Skip to content

feat(desktop): chip a link pasted or typed into the composer - #72201

Merged
OutThisLife merged 3 commits into
mainfrom
bb/composer-url-chips
Jul 26, 2026
Merged

feat(desktop): chip a link pasted or typed into the composer#72201
OutThisLife merged 3 commits into
mainfrom
bb/composer-url-chips

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

A link pasted into the composer went in as raw URL text — wrapping across two or three lines, inert, and unrelated to the @url: reference the "+ → Add URL" dialog produces for the exact same thing. Now both paths land the same chip.

What changed

Paste. handlePaste runs the clipboard text through a new linkifyUrls() that rewrites bare http(s)://… into @url: directives, then inserts through a chip-aware caret insert instead of the plain-text one. The rewrite is positional, so a link mid-paragraph stays where it was, the punctuation that ended the sentence stays outside the chip, a link already written as a directive is skipped, and an unbalanced ) that closed a parenthetical is trimmed while …/wiki/Foo_(bar) keeps its own.

Typing. A plain space finishing a typed link commits the same chip. It bails on a textContent.includes('://') read before touching the caret range, so it costs one native string scan on every other space — the same guard shape refreshTrigger uses.

Both editors. The composer and the message-edit box. The latter's paste went through document.execCommand('insertText', …), which cannot produce a chip at all, so it now uses the same insert the composer does. Consequence worth naming: execCommand fed the browser's native undo stack and a manual DOM insert doesn't, so Cmd+Z after a paste in the edit box now behaves the way it already did in the composer.

The label

URL chips were unreadable. The composer labeled them with refLabel, which takes the last path segment, so three PR links all read as their number. The transcript had a second 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 at max-w-56 to cut, and the full value on the chip's title so a cut-off link stays readable on hover — which the transcript side already did.

What this means for a sent message

@url: is a real context reference: the gateway fetches the page through web_extract when the turn runs. That is already what the Add URL dialog does, so a pasted link now behaves like a dialog-added one rather than like inert text. Backspace right after the chip removes it as one unit if that isn't wanted.

Tests

url-refs.test.ts covers the rewrite (mid-sentence position, trailing punctuation, balanced parens, several links in one paste, already-a-directive, no scheme) and the typed-space commit including the modified-space no-op. rich-editor.test.ts gains coverage for directives landing as chips through the caret insert, and for replaceBeforeCaret declining a caret with no room for the token. 323 tests pass across src/app/chat/composer and src/components/assistant-ui.

Not yet looked at in a running app — the chip styling is the existing DIRECTIVE_CHIP_CLASS that @file: chips already use, so this only changes what fills it.

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.
`insertPlainTextAtCaret` dropped its text in verbatim, so a caller with
directives in hand had no way to land them as chips. It becomes
`insertComposerContentsAtCaret`, sharing the parse `renderComposerContents`
already uses, and gains a sibling `replaceBeforeCaret` for swapping a
just-typed token for a chip.
A pasted link went in as raw URL text, wrapping across the composer and
staying inert. It now becomes the same `@url:` reference the "+ → Add URL"
dialog inserts — parsed in place, so a link mid-sentence keeps its position
and the punctuation that ended the sentence stays outside the chip. Typing
one and pressing space commits it the same way.

Both rich-editor surfaces get it: the composer and the message-edit box,
whose paste went through `execCommand` and could not produce a chip at all.
@OutThisLife
OutThisLife enabled auto-merge July 26, 2026 20:28
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) needs-decision Awaiting maintainer decision before any implementation labels Jul 26, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #54662 addressed native paste undo via execCommand. This PR needs manual DOM insertion to create URL chips and intentionally changes that undo behavior. The URL-chip capability is distinct, but maintainers should choose the desired editor contract.

@OutThisLife
OutThisLife merged commit 6fc571a into main Jul 26, 2026
34 checks passed
@OutThisLife
OutThisLife deleted the bb/composer-url-chips branch July 26, 2026 20:33
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on adfaa95

all good!

randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…rl-chips

feat(desktop): chip a link pasted or typed into the composer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants