From 9dc86aea1fc9eab25f69e08beb775a6ec95d3a13 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sat, 7 Feb 2026 20:24:06 -0500 Subject: [PATCH 1/2] fix(ui): support cmd-click links in inline code --- packages/ui/src/components/markdown.css | 6 ++ packages/ui/src/components/markdown.tsx | 74 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css index 68ae93bda4ae..4fa79610e805 100644 --- a/packages/ui/src/components/markdown.css +++ b/packages/ui/src/components/markdown.css @@ -209,3 +209,9 @@ display: block; } } + +:root[data-link-modifier="true"] [data-component="markdown"] code[data-link-url]:hover { + text-decoration: underline; + text-underline-offset: 2px; + cursor: pointer; +} diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 608db818f549..a396c0e5e62a 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -49,6 +49,51 @@ type CopyLabels = { copied: string } +const urlPattern = /^https?:\/\/[^\s<>()`"']+$/ +const linkModifierAttribute = "data-link-modifier" + +let linkModifierInstalled = false +let linkModifierOn = false + +function codeUrl(text: string) { + const href = text.trim().replace(/[),.;!?]+$/, "") + if (!urlPattern.test(href)) return + try { + const url = new URL(href) + return url.toString() + } catch { + return + } +} + +function setLinkModifier(next: boolean) { + if (linkModifierOn === next) return + linkModifierOn = next + if (next) { + document.documentElement.setAttribute(linkModifierAttribute, "true") + return + } + document.documentElement.removeAttribute(linkModifierAttribute) +} + +function updateLinkModifier(event: Pick) { + setLinkModifier(event.metaKey || event.ctrlKey) +} + +const handleModifierKeyDown = (event: KeyboardEvent) => updateLinkModifier(event) + +const handleModifierKeyUp = (event: KeyboardEvent) => updateLinkModifier(event) + +const handleModifierBlur = () => setLinkModifier(false) + +function ensureLinkModifier() { + if (linkModifierInstalled) return + linkModifierInstalled = true + document.addEventListener("keydown", handleModifierKeyDown, true) + document.addEventListener("keyup", handleModifierKeyUp, true) + window.addEventListener("blur", handleModifierBlur) +} + function createIcon(path: string, slot: string) { const icon = document.createElement("div") icon.setAttribute("data-component", "icon") @@ -110,9 +155,36 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { wrapper.appendChild(createCopyButton(labels)) } + const markCodeLinks = () => { + const codeNodes = Array.from(root.querySelectorAll(":not(pre) > code")) + for (const code of codeNodes) { + const href = codeUrl(code.textContent ?? "") + if (!href) { + code.removeAttribute("data-link-url") + continue + } + code.setAttribute("data-link-url", href) + } + } + const handleClick = async (event: MouseEvent) => { const target = event.target if (!(target instanceof Element)) return + + const codeEl = target.closest("code[data-link-url]") + if (codeEl instanceof HTMLElement) { + if (event.defaultPrevented) return + if (event.button !== 0) return + if (!event.metaKey && !event.ctrlKey) return + if (event.altKey || event.shiftKey) return + const href = codeEl.getAttribute("data-link-url") + if (!href) return + event.preventDefault() + event.stopPropagation() + window.open(href, "_blank", "noopener,noreferrer") + return + } + const button = target.closest('[data-slot="markdown-copy-button"]') if (!(button instanceof HTMLButtonElement)) return const code = button.closest('[data-component="markdown-code"]')?.querySelector("code") @@ -132,12 +204,14 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { for (const block of blocks) { ensureWrapper(block) } + markCodeLinks() const buttons = Array.from(root.querySelectorAll('[data-slot="markdown-copy-button"]')) for (const button of buttons) { if (button instanceof HTMLButtonElement) updateLabel(button) } + ensureLinkModifier() root.addEventListener("click", handleClick) return () => { From 7f6cc4f9abdd9163ec2be85b81d354e3e50db6b1 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 13 Feb 2026 11:35:02 -0500 Subject: [PATCH 2/2] fix(ui): make inline code URLs clickable without modifier key Wrap URL-containing inline code in tags instead of using a custom cmd-click handler with window.open. This uses the existing external-link infrastructure so the desktop app routes through platform.openLink. --- packages/ui/src/components/markdown.css | 3 +- packages/ui/src/components/markdown.tsx | 68 +++++++------------------ 2 files changed, 20 insertions(+), 51 deletions(-) diff --git a/packages/ui/src/components/markdown.css b/packages/ui/src/components/markdown.css index 4fa79610e805..27c8f238d519 100644 --- a/packages/ui/src/components/markdown.css +++ b/packages/ui/src/components/markdown.css @@ -210,8 +210,7 @@ } } -:root[data-link-modifier="true"] [data-component="markdown"] code[data-link-url]:hover { +[data-component="markdown"] a.external-link:hover > code { text-decoration: underline; text-underline-offset: 2px; - cursor: pointer; } diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index a396c0e5e62a..4c3d5628418b 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -50,10 +50,6 @@ type CopyLabels = { } const urlPattern = /^https?:\/\/[^\s<>()`"']+$/ -const linkModifierAttribute = "data-link-modifier" - -let linkModifierInstalled = false -let linkModifierOn = false function codeUrl(text: string) { const href = text.trim().replace(/[),.;!?]+$/, "") @@ -66,34 +62,6 @@ function codeUrl(text: string) { } } -function setLinkModifier(next: boolean) { - if (linkModifierOn === next) return - linkModifierOn = next - if (next) { - document.documentElement.setAttribute(linkModifierAttribute, "true") - return - } - document.documentElement.removeAttribute(linkModifierAttribute) -} - -function updateLinkModifier(event: Pick) { - setLinkModifier(event.metaKey || event.ctrlKey) -} - -const handleModifierKeyDown = (event: KeyboardEvent) => updateLinkModifier(event) - -const handleModifierKeyUp = (event: KeyboardEvent) => updateLinkModifier(event) - -const handleModifierBlur = () => setLinkModifier(false) - -function ensureLinkModifier() { - if (linkModifierInstalled) return - linkModifierInstalled = true - document.addEventListener("keydown", handleModifierKeyDown, true) - document.addEventListener("keyup", handleModifierKeyUp, true) - window.addEventListener("blur", handleModifierBlur) -} - function createIcon(path: string, slot: string) { const icon = document.createElement("div") icon.setAttribute("data-component", "icon") @@ -159,11 +127,28 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { const codeNodes = Array.from(root.querySelectorAll(":not(pre) > code")) for (const code of codeNodes) { const href = codeUrl(code.textContent ?? "") + const parentLink = + code.parentElement instanceof HTMLAnchorElement && code.parentElement.classList.contains("external-link") + ? code.parentElement + : null + if (!href) { - code.removeAttribute("data-link-url") + if (parentLink) parentLink.replaceWith(code) + continue + } + + if (parentLink) { + parentLink.href = href continue } - code.setAttribute("data-link-url", href) + + const link = document.createElement("a") + link.href = href + link.className = "external-link" + link.target = "_blank" + link.rel = "noopener noreferrer" + code.parentNode?.replaceChild(link, code) + link.appendChild(code) } } @@ -171,20 +156,6 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { const target = event.target if (!(target instanceof Element)) return - const codeEl = target.closest("code[data-link-url]") - if (codeEl instanceof HTMLElement) { - if (event.defaultPrevented) return - if (event.button !== 0) return - if (!event.metaKey && !event.ctrlKey) return - if (event.altKey || event.shiftKey) return - const href = codeEl.getAttribute("data-link-url") - if (!href) return - event.preventDefault() - event.stopPropagation() - window.open(href, "_blank", "noopener,noreferrer") - return - } - const button = target.closest('[data-slot="markdown-copy-button"]') if (!(button instanceof HTMLButtonElement)) return const code = button.closest('[data-component="markdown-code"]')?.querySelector("code") @@ -211,7 +182,6 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { if (button instanceof HTMLButtonElement) updateLabel(button) } - ensureLinkModifier() root.addEventListener("click", handleClick) return () => {