copy button on every code block
the bubble's copy button copies the whole reply, which is the wrong unit when the reply is prose ending in one command to run. fenced blocks get their own button; inline code doesn't. text read from textContent at click time rather than the markdown ast, trailing newline stripped so a pasted command doesn't run itself. the positioned wrapper takes the vertical margin, or the pre's own margin collapses through it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -491,6 +491,34 @@ reasoned, not observed. Typecheck and the sidecar tests are clean.
|
||||
|
||||
---
|
||||
|
||||
## 18. Every code block has its own copy button
|
||||
|
||||
**Where:** any reply containing a fenced block — a command to run, a snippet to paste.
|
||||
|
||||
The bubble's copy button copies the _whole reply_. When the reply is prose ending in one command you're
|
||||
meant to run, that's the wrong unit, and you end up selecting the line by hand — the one chore a command
|
||||
in a chat exists to save you. Fenced blocks now carry a button in their top-right corner. Hover-revealed
|
||||
on a pointer device, always visible on touch, because there is no hover there to reveal it with.
|
||||
|
||||
Inline `` `code` `` deliberately gets nothing: it's short enough to select, and a button per backticked
|
||||
word would be noise.
|
||||
|
||||
Two details. The text is read from the rendered DOM (`textContent`) at click time rather than
|
||||
reconstructed from the markdown AST — react-markdown hands the `pre` override a `<code>` element whose
|
||||
children are strings, elements or nested arrays depending on which plugins ran, and reassembling that is
|
||||
guesswork; `textContent` is exactly what's on screen. And the trailing newline is stripped, because it
|
||||
belongs to the fence, not the command — pasted into a shell it would _run_ the thing rather than leave it
|
||||
on the prompt for you to look at.
|
||||
|
||||
The block is wrapped in a positioned div, so `prose.css` moved the vertical margin onto the wrapper;
|
||||
otherwise the `pre`'s own margin collapses through it and the `:first-child`/`:last-child` reset stops
|
||||
working. The streaming bubble gets the same treatment, so a block doesn't gain a button when the turn
|
||||
ends.
|
||||
|
||||
**Not verified:** the browser.
|
||||
|
||||
---
|
||||
|
||||
## Things noticed and deliberately left alone
|
||||
|
||||
- **`useChatWebSocket` silently ignores unparseable frames.** That one is intentional and the comment
|
||||
|
||||
Reference in New Issue
Block a user