From f244deffee34870207bf2c910225c7fe17518867 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sat, 25 Jul 2026 22:52:34 -0500 Subject: [PATCH] fix(tui,desktop): let authored link text outrank the fetched page title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both chat renderers resolve link titles over the network and render them in place of the link's text, unconditionally — so they also replaced text the agent deliberately wrote. `[#71706](url)` rendered as the whole GitHub page title, and prose labels were swapped out mid-sentence. The TUI ordered `fetched || label` and always passed the URL to useLinkTitle. Desktop looked guarded but wasn't: chat markdown hands authored text to PrettyLink as `fallbackLabel`, not `label`, so `useLinkTitle(label ? null : target)` never skipped the fetch and `fetched || label || fallbackLabel` still let the title win. Treat an authored label as the intent: it wins, and it skips the fetch. A label that is just the URL still resolves, since `[url](url)` and `` are bare links wearing markdown syntax — desktop already applied that same URL-equality rule before handing the label over. Co-authored-by: Kinkoolino-Hermes <297364961+Kinkoolino-Hermes@users.noreply.github.com> --- apps/desktop/src/lib/external-link.tsx | 7 +++++-- ui-tui/src/components/markdown.tsx | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/lib/external-link.tsx b/apps/desktop/src/lib/external-link.tsx index ebdee577ac27..570106f44c3a 100644 --- a/apps/desktop/src/lib/external-link.tsx +++ b/apps/desktop/src/lib/external-link.tsx @@ -251,10 +251,13 @@ interface PrettyLinkProps extends Omit, 'href' | 'target'> { fallbackLabel?: string } +// Title resolution is a fallback, not an override. Both props carry authored +// text — chat markdown passes `fallbackLabel` — so either one skips the fetch. export function PrettyLink({ className, fallbackLabel, href, label, ...rest }: PrettyLinkProps) { const target = useMemo(() => normalizeExternalUrl(href), [href]) - const fetched = useLinkTitle(label ? null : target) - const display = fetched || label?.trim() || fallbackLabel?.trim() || urlSlugTitleLabel(target) + const authoredLabel = label?.trim() || fallbackLabel?.trim() + const fetched = useLinkTitle(authoredLabel ? null : target) + const display = authoredLabel || fetched || urlSlugTitleLabel(target) return ( diff --git a/ui-tui/src/components/markdown.tsx b/ui-tui/src/components/markdown.tsx index fb7fafd73c58..1c24a8d7bffa 100644 --- a/ui-tui/src/components/markdown.tsx +++ b/ui-tui/src/components/markdown.tsx @@ -153,25 +153,27 @@ const autolinkUrl = (raw: string) => const defaultLinkLabel = (url: string) => url.startsWith('mailto:') ? url.replace(/^mailto:/, '') : /^https?:\/\//i.test(url) ? urlSlugTitleLabel(url) : url -const pickFallbackLabel = (label: string | undefined, target: string): string | undefined => { +// A label only counts as authored if it says something the URL doesn't: +// `[https://example.com](https://example.com)` and `` +// are bare links wearing markdown syntax, so they still want a page title. +const pickAuthoredLabel = (label: string | undefined, target: string): string | undefined => { const trimmed = label?.trim() - if (!trimmed) { - return undefined - } - - return normalizeExternalUrl(trimmed) === target ? undefined : trimmed + return trimmed && normalizeExternalUrl(trimmed) !== target ? trimmed : undefined } interface ResolvedLinkProps { - fallbackLabel?: string + authoredLabel?: string t: Theme url: string } -function ResolvedLink({ fallbackLabel, t, url }: ResolvedLinkProps) { - const fetched = useLinkTitle(url) - const display = fetched || fallbackLabel || defaultLinkLabel(url) +// Title resolution is a fallback for links with no text of their own, not an +// override — replacing `[Read the RFC](url)` with the page title throws away +// better wording than we can derive, and mangles labels like `#71706`. +function ResolvedLink({ authoredLabel, t, url }: ResolvedLinkProps) { + const fetched = useLinkTitle(authoredLabel ? null : url) + const display = authoredLabel || fetched || defaultLinkLabel(url) return ( @@ -185,7 +187,7 @@ function ResolvedLink({ fallbackLabel, t, url }: ResolvedLinkProps) { const renderResolvedLink = (k: number, t: Theme, rawUrl: string, label?: string) => { const target = normalizeExternalUrl(rawUrl) - return + return } export const stripInlineMarkup = (v: string) =>