fix: links in chat could not be opened#8544
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f22b0da57
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b263ba9c7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c4d608ca85
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", | ||
| className, | ||
| )} | ||
| linkSafety={linkSafetyConfig} |
There was a problem hiding this comment.
Preserve local artifact link routing when enabling linkSafety
Enabling linkSafety here changes Streamdown’s link renderer to its intercepted link flow, so markdown links are no longer handled as plain anchors. useArtifactLinkHandler still only matches closest("a"), which means assistant markdown links stop going through resolveMarkdownHref/openResolvedPath; internal artifact paths are then routed through the modal’s openUrl path (or fail), breaking artifact navigation and bypassing the allowed-roots policy.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c02f12a0c3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| @@ -0,0 +1,89 @@ | |||
| import { useCallback, useEffect, useRef, useState } from "react"; | |||
| import { openUrl } from "@tauri-apps/plugin-opener"; | |||
There was a problem hiding this comment.
Export openUrl in plugin-opener test mocks
Introducing the openUrl import here means any Vitest mock of @tauri-apps/plugin-opener must now provide that named export; existing mocks like src/features/chat/ui/__tests__/MessageBubble.test.tsx currently define only openPath, so module loading will fail with a missing-export error before assertions run. Please update affected mocks (or use a partial mock of the real module) so chat-related test suites keep running.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a8cc9f0326
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| "title": "Environment Variables", | ||
| "toggleVisibility": "Toggle value visibility" | ||
| }, | ||
| "linkSafety": { |
There was a problem hiding this comment.
Localize new link-safety keys in Spanish locale
LinkSafetyModal now reads common:components.linkSafety.*, but this commit only adds those keys to locales/en/common.json; locales/es/common.json is still missing them. In Spanish sessions the modal will fall back to English for every new label, creating a visible localization regression on a surface that was previously fully translated.
Useful? React with 👍 / 👎.
| <button | ||
| className="wrap-anywhere appearance-none text-left font-medium text-primary underline" | ||
| data-streamdown="link" | ||
| onClick={() => openModal?.(href ?? "")} | ||
| type="button" | ||
| > |
There was a problem hiding this comment.
Keep external markdown links as anchors for accessibility
Replacing external markdown links with <button> removes link semantics, so assistive tech users can no longer navigate these items via link lists and users lose standard link affordances (e.g., link context actions). This is an accessibility regression versus the previous anchor-based rendering; keep an <a href> element and intercept click behavior to open the safety modal instead of changing the role to a button.
Useful? React with 👍 / 👎.
|
Looks good! I left some comments. Before merging this PR, would you please address these including the i18n one from codex. Also one thought: would it be possible to keep external markdown links as something like That would keep the external-link flow the same, while preserving normal link semantics and accessibility. Since the modal and openUrl() handling are already in place, it seems like a pretty small change from the current implementation. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c314e8219f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| vi.mock("@tauri-apps/plugin-opener", () => ({ | ||
| openPath: vi.fn(), | ||
| openUrl: vi.fn(), | ||
| })); |
There was a problem hiding this comment.
Mock revealItemInDir in plugin-opener test setup
The global @tauri-apps/plugin-opener mock now exports only openPath and openUrl, but src/shared/lib/fileManager.ts imports revealItemInDir; any test that exercises revealInFileManager (for example via FileContextMenu) will get an undefined function from this mock and fail at runtime. Add revealItemInDir: vi.fn() to this shared mock so plugin-opener consumers have a complete mocked surface.
Useful? React with 👍 / 👎.
External links in chat messages were silently ignored in Tauri's
WKWebView. Add a custom MarkdownLink component that splits link
behavior by type: external links show a LinkSafetyModal confirmation
dialog before opening via @tauri-apps/plugin-opener, while internal
links render as plain <a> elements so useArtifactLinkHandler can
intercept them for artifact navigation.
This replaces Streamdown's built-in linkSafety which converts all
links to <button> elements, breaking the artifact policy layer that
relies on closest("a") to resolve internal paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move isExternalHref to shared/lib/ to fix dependency inversion where shared/ui imported from features/chat - Reset isCopied state when LinkSafetyModal reopens via useEffect on isOpen, preventing stale 'Copied!' text on reopen - Await openUrl before closing LinkSafetyModal so the modal stays visible if the open fails - Lift modal state to MessageResponse level with a single LinkSafetyModal instance and React context, instead of mounting one Dialog per external link in the message - Update useArtifactLinkHandler doc comment to reference MarkdownLink instead of the now-disabled Streamdown linkSafety Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
- Update test description from 'Streamdown linkSafety modal' to 'MarkdownLink'\''s LinkSafetyModal' to match actual implementation - Clean up isExternalHref import/re-export in artifactPathPolicyCore: import once at top for local use, re-export the local binding instead of a separate re-export-from-source statement - Move hardcoded English strings in LinkSafetyModal to react-i18next under components.linkSafety namespace for i18n consistency - Add comment on MessageResponse memo comparator explaining that internal state (modalUrl) is intentionally outside the comparator Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
The LinkSafetyModal introduced in c02f12a imports openUrl from @tauri-apps/plugin-opener. Test files that mock this module only provided openPath, causing module loading to fail with a missing export error. Add openUrl: vi.fn() to all four affected mocks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the re-export shim of isExternalHref through artifactPathPolicy and artifactPathPolicyCore. Consumers now import directly from @/shared/lib/isExternalHref, eliminating unnecessary indirection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Centralise the @tauri-apps/plugin-opener vi.mock in the goose2 test setup file so each test suite no longer duplicates it. Tests that assert on openPath now import it from the mocked module and use vi.mocked() instead of a local mock variable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change MarkdownLink to render external links as <a> elements with preventDefault + modal open on click, instead of <button> elements. This improves accessibility (screen readers, right-click copy, status bar preview) and makes all chat links semantically consistent. useArtifactLinkHandler already has an isExternalHref early return so there is no conflict with the delegated click handler. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The linkSafety i18n keys were added to en/common.json but not es/common.json, causing Spanish sessions to fall back to English for the external link confirmation modal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove extra blank lines in artifactPathPolicy.ts and artifactPathPolicyCore.ts, and wrap long expect call in FilesList.test.tsx to satisfy biome's formatter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* main: feat(hooks): add Husky git hooks for ui/goose2 (#8577) fix: links in chat could not be opened (#8544) fix: run setup before dev and dev-debug in goose2 justfile (#8718) Manage skills as sources over ACP (#8675) handle full node paths in goose2 kill recipe (#8709) overhaul provider inventory and agent/model selection (#8652) Remove unused import (#8676) delete the goose2 migration plan prompt (#8678) Add health score badge to README (#8677) feat(goose2): voice dictation via direct-ACP pattern (#8609) consistently use actions-rust-lang/setup-rust-toolchain (#8671) fix(developer): run shell tool under bash/sh regardless of login shell (#8659) refactor(providers): extract shared OAuth device-flow helper (#8619) Add a goose2 release workflow (#8629) chore(deps): bump ncipollo/release-action from 1.20.0 to 1.21.0 (#8664) docs: add blog post about Mesh LLM provider option (#8655) fix: append /chat/completions for prefixed v1 base URLs (#8521) Reset ChatGPT Codex auth during OAuth setup (#8569) chore(deps): bump EmbarkStudios/cargo-deny-action from 2.0.15 to 2.0.17 (#8665) Add dependabot config for pnpm workspace, cargo, and actions (#8660)
Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* main: (41 commits) removed the specific code owner for documentation change (#8749) fix(providers): handle missing delta field in streaming chunks (#8700) refactor(providers): extract http_status module and rename handle_status_openai_compat (#8620) fix(providers/openai): accept streaming chunks with both reasoning fields (#8715) feat: associate threads with projects (#8745) upgrade goose sdk and tui to be compatible with the latest agentclientprotocol/sdk package (#8667) feat: extend goose2 context window ux with auto-compaction (#8721) improve goose2 agent management flows (#8737) alexhancock/tui-improvements (#8736) fix: add strict:false to Responses API tools and gpt-5.4 to known models (#8636) persist and reliably apply chat model selection (#8734) merge goose-acp crate into goose (#8726) docs: AGENTS.md section on goose2 desktop backend architecture (#8732) feat: goose2 message bubble + action tray (#8720) consolidate provider ACP methods onto inventory (#8710) ci: declare and enforce MSRV of 1.91.1 (#8670) fix(ui): correct grammar in apps view description (#8668) (#8679) Stop load openai fast model for openapi compatible custom endpoint (#8644) feat(hooks): add Husky git hooks for ui/goose2 (#8577) fix: links in chat could not be opened (#8544) ...
Summary