mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
feat(desktop): show open-with affordance on URLs rendered as inline code
Extend the injection effect in AssistantMessage to also append a ▾ trigger after inline <code> elements whose text is a browser-localhost or remote URL, so AI-written run URLs like \`http://localhost:9527/\` get the same open-with menu that markdown <a> links already receive. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3f1617d139
commit
4f03b8f6e6
@ -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(
|
||||
<AssistantMessage
|
||||
sessionId="s1"
|
||||
content={'运行地址 `http://localhost:9527/`'}
|
||||
isStreaming={false}
|
||||
/>,
|
||||
)
|
||||
// The trigger comes from the <code> path (no <a> 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(
|
||||
<AssistantMessage
|
||||
sessionId="s1"
|
||||
content={'运行地址 `http://localhost:9527/`'}
|
||||
isStreaming={false}
|
||||
/>,
|
||||
)
|
||||
|
||||
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(
|
||||
<AssistantMessage
|
||||
sessionId="s1"
|
||||
content={'装一下 `npm install`'}
|
||||
isStreaming={false}
|
||||
/>,
|
||||
)
|
||||
expect(screen.queryByLabelText('打开方式')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@ -52,10 +52,8 @@ export const AssistantMessage = memo(function AssistantMessage({ content, isStre
|
||||
useEffect(() => {
|
||||
const root = contentRef.current
|
||||
if (!root || !sessionId || isStreaming) return
|
||||
root.querySelectorAll<HTMLAnchorElement>('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<HTMLAnchorElement>('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<HTMLElement>('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])
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user