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) =>