diff --git a/desktop/src/components/chat/AssistantMessage.linkrouting.test.tsx b/desktop/src/components/chat/AssistantMessage.linkrouting.test.tsx
index c0de7ea7..e5ec5927 100644
--- a/desktop/src/components/chat/AssistantMessage.linkrouting.test.tsx
+++ b/desktop/src/components/chat/AssistantMessage.linkrouting.test.tsx
@@ -130,3 +130,46 @@ describe('AssistantMessage open-with trigger injection', () => {
expect(screen.getByText('openWith.systemBrowser')).toBeInTheDocument()
})
})
+
+describe('AssistantMessage open-with trigger injection — inline code URLs', () => {
+ it('injects a ▾ trigger next to an inline-code localhost URL', async () => {
+ render(
+ ,
+ )
+ // The trigger comes from the path (no in this content)
+ expect(screen.getByLabelText('打开方式')).toBeInTheDocument()
+ })
+
+ it('clicking the trigger next to inline-code URL opens menu with in-app-browser and system-browser items', async () => {
+ render(
+ ,
+ )
+
+ const trigger = screen.getByLabelText('打开方式')
+ fireEvent.click(trigger)
+
+ await waitFor(() => {
+ expect(screen.getByText('openWith.inAppBrowser')).toBeInTheDocument()
+ })
+ expect(screen.getByText('openWith.systemBrowser')).toBeInTheDocument()
+ })
+
+ it('does NOT inject a trigger for non-URL inline code', () => {
+ render(
+ ,
+ )
+ expect(screen.queryByLabelText('打开方式')).toBeNull()
+ })
+})
diff --git a/desktop/src/components/chat/AssistantMessage.tsx b/desktop/src/components/chat/AssistantMessage.tsx
index 4b72cd72..24b285f5 100644
--- a/desktop/src/components/chat/AssistantMessage.tsx
+++ b/desktop/src/components/chat/AssistantMessage.tsx
@@ -52,10 +52,8 @@ export const AssistantMessage = memo(function AssistantMessage({ content, isStre
useEffect(() => {
const root = contentRef.current
if (!root || !sessionId || isStreaming) return
- root.querySelectorAll('a[href]').forEach((link) => {
- const href = link.getAttribute('href') ?? ''
- if (classifyPreviewLink(href).kind === 'ignored') return
- if (link.nextElementSibling instanceof HTMLElement && link.nextElementSibling.classList.contains('md-open-with')) return
+
+ function makeTrigger(href: string): HTMLButtonElement {
const trigger = document.createElement('button')
trigger.type = 'button'
trigger.className = 'md-open-with'
@@ -64,7 +62,23 @@ export const AssistantMessage = memo(function AssistantMessage({ content, isStre
trigger.tabIndex = -1
trigger.textContent = '▾'
trigger.style.cssText = 'margin-left:3px;padding:0 3px;border:none;background:transparent;color:var(--color-text-tertiary);cursor:pointer;font-size:11px;line-height:1;vertical-align:middle'
- link.after(trigger)
+ return trigger
+ }
+
+ root.querySelectorAll('a[href]').forEach((link) => {
+ const href = link.getAttribute('href') ?? ''
+ if (classifyPreviewLink(href).kind === 'ignored') return
+ if (link.nextElementSibling instanceof HTMLElement && link.nextElementSibling.classList.contains('md-open-with')) return
+ link.after(makeTrigger(href))
+ })
+
+ root.querySelectorAll('code').forEach((code) => {
+ if (code.closest('pre')) return
+ const text = (code.textContent ?? '').trim()
+ const kind = classifyPreviewLink(text).kind
+ if (kind !== 'browser-localhost' && kind !== 'remote') return
+ if (code.nextElementSibling instanceof HTMLElement && code.nextElementSibling.classList.contains('md-open-with')) return
+ code.after(makeTrigger(text))
})
}, [content, isStreaming, sessionId])