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