From ee09162e4529c019889ad1e74129b0cd1d230f23 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sun, 26 Jul 2026 15:23:35 -0500 Subject: [PATCH] fix(desktop): label a url chip with its host and path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The composer labeled a `@url:` chip with `refLabel`, which takes the last path segment — three PR links all read as their number. The transcript had its own better labeler that stopped at the hostname, so the same reference read differently before and after send. One `refChipLabel` now serves both: host without `www.`, path riding along for the chip's existing truncate to cut, and the full value on the chip's title so a cut-off link is still readable on hover. --- .../src/app/chat/composer/rich-editor.ts | 13 +++++------- .../assistant-ui/directive-text.tsx | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/desktop/src/app/chat/composer/rich-editor.ts b/apps/desktop/src/app/chat/composer/rich-editor.ts index 21f0286c9f8..8978d6f1520 100644 --- a/apps/desktop/src/app/chat/composer/rich-editor.ts +++ b/apps/desktop/src/app/chat/composer/rich-editor.ts @@ -11,11 +11,11 @@ import { directiveIconElement, directiveIconSvg, formatRefValue, + refChipLabel, slashChipClass, type SlashChipKind, slashIconElement } from '@/components/assistant-ui/directive-text' -import { sessionRefFallbackLabel } from '@/lib/session-refs' export const RICH_INPUT_SLOT = 'composer-rich-input' @@ -35,10 +35,6 @@ export function unquoteRef(raw: string) { return quoted ? raw.slice(1, -1) : raw.replace(/[,.;!?]+$/, '') } -export function refLabel(id: string) { - return id.split(/[\\/]/).filter(Boolean).pop() || id -} - /** Always-quote variant of formatRefValue — chips need a fence even for safe values. */ export function quoteRefValue(value: string) { if (!value.includes('`')) { @@ -60,9 +56,9 @@ export function refChipHtml(kind: string, rawValue: string, displayLabel?: strin const id = unquoteRef(rawValue) const text = `@${kind}:${quoteRefValue(id)}` - const label = displayLabel || (kind === 'session' ? sessionRefFallbackLabel(id) : refLabel(id)) + const label = displayLabel || refChipLabel(kind, id) - return `${directiveIconSvg(kind)}${escapeHtml(label)}` + return `${directiveIconSvg(kind)}${escapeHtml(label)}` } export function refChipElement(kind: string, rawValue: string, displayLabel?: string) { @@ -72,12 +68,13 @@ export function refChipElement(kind: string, rawValue: string, displayLabel?: st const label = document.createElement('span') chip.contentEditable = 'false' + chip.title = id chip.dataset.refText = text chip.dataset.refId = id chip.dataset.refKind = kind chip.className = DIRECTIVE_CHIP_CLASS label.className = 'truncate' - label.textContent = displayLabel || (kind === 'session' ? sessionRefFallbackLabel(id) : refLabel(id)) + label.textContent = displayLabel || refChipLabel(kind, id) chip.append(directiveIconElement(kind), label) return chip diff --git a/apps/desktop/src/components/assistant-ui/directive-text.tsx b/apps/desktop/src/components/assistant-ui/directive-text.tsx index 317489a96f8..312c7d9414d 100644 --- a/apps/desktop/src/components/assistant-ui/directive-text.tsx +++ b/apps/desktop/src/components/assistant-ui/directive-text.tsx @@ -277,7 +277,7 @@ function parseDirectiveText(text: string): Unstable_DirectiveSegment[] { start: match.index ?? 0, end: (match.index ?? 0) + match[0].length, type: match[1] || 'file', - label: shortLabel(match[1] as HermesRefType, id), + label: refChipLabel(match[1] || 'file', id), id } }), @@ -320,25 +320,30 @@ function parseDirectiveText(text: string): Unstable_DirectiveSegment[] { return segments } -function shortLabel(type: HermesRefType, id: string): string { +/** Display text for a `@kind:value` chip. Shared with the composer's + * contenteditable chips so a link reads the same before and after send: the + * host leads (scheme and `www.` are noise) and the path rides along for the + * chip's `truncate` to cut — a bare hostname can't tell two links apart. */ +export function refChipLabel(type: string, id: string): string { if (type === 'terminal') { return id || 'terminal' } + if (type === 'session') { + return sessionRefFallbackLabel(id) + } + if (type === 'url') { try { - const parsed = new URL(id) + const { hostname, pathname, search } = new URL(id) + const path = `${pathname}${search}`.replace(/\/$/, '') - return parsed.hostname || id + return `${hostname.replace(/^www\./i, '')}${path}` || id } catch { return id } } - if (type === 'session') { - return sessionRefFallbackLabel(id) - } - const tail = id.split(/[\\/]/).filter(Boolean).pop() return tail || id