Repository navigation
Agent chat: copy-to-clipboard button in left gutter per output block (reusable JS/CSS component) #63
Description
Summary
Add a copy-to-clipboard button to each output block in the agent chat view. The button appears in the left margin when the mouse is nearby and disappears when it moves away. Implement as a reusable JavaScript/CSS component so it can be applied to any HTML-rendered view.
Behaviour
- Each logical output block (assistant message, tool result, reasoning block, etc.) gets its own copy button.
- The button is positioned off to the left of the block, in a reserved left gutter.
- It is invisible by default; it fades/appears when the mouse enters a proximity zone around the block (or its gutter area), and fades out when the mouse leaves.
- Clicking the button copies the plain-text content of that block to the clipboard (
navigator.clipboard.writeText). - After a successful copy, the button briefly shows a ✓ confirmation, then reverts.
- The button does not shift layout — it overlays the gutter so surrounding content never reflows.
Implementation: reusable JS/CSS component
Create a self-contained component (e.g. copy-gutter.js + copy-gutter.css) in the shared web assets, usable by any HTML view:
CSS sketch
.copy-gutter-host {
position: relative; /* establishes gutter coordinate space */
}
.copy-gutter-btn {
position: absolute;
left: -2.2em; /* sits in the left gutter, outside the content flow */
top: 0;
opacity: 0;
transition: opacity 120ms ease;
/* icon button styling */
}
.copy-gutter-host:hover .copy-gutter-btn,
.copy-gutter-btn:focus-visible {
opacity: 1;
}
.copy-gutter-btn.confirmed {
/* brief ✓ state */
}JS sketch
// CopyGutter.init(root)
// Finds all [data-copy-target] elements inside `root`, wraps each in a
// .copy-gutter-host, injects a .copy-gutter-btn, and wires click → clipboard.
export function init(root = document) {
root.querySelectorAll('[data-copy-target]').forEach(el => {
// inject button, wire hover/click
});
}Blocks opt in by carrying a data-copy-target attribute. The attribute value can be:
- empty/omitted → copy the element's
innerText - a CSS selector → copy the matching child's text
Usage in the agent chat HTML view
When the agent chat HTML renderer (see HTML rendering design) emits a message or tool-result block, it adds data-copy-target to the block's root element. CopyGutter.init() is called once after the initial render and again incrementally as streaming adds new blocks (or new blocks are detected via a MutationObserver attached by CopyGutter.init).
Reuse in other views
Any HTML view that includes copy-gutter.js + copy-gutter.css and marks elements with data-copy-target automatically gets copy buttons — no per-view code required. Candidate views: tool result browser, entity note viewer, code blocks in markdown renders.
Acceptance criteria
- Every assistant message block and tool result block has a copy button in the left gutter.
- The button is not visible unless the mouse is near the block.
- Clicking copies the block's plain text to the clipboard.
- A brief ✓ confirmation is shown after a successful copy.
- The component is in shared web assets and requires no per-view JS to activate beyond
CopyGutter.init(root). - No layout shift occurs when the button appears or disappears.
Activity
JoshuaRowePhantom commentedon Jun 27, 2026
Design
Approach: Self-contained copy-gutter component inlined in chat-output-shell.html (CSS + JS), with data-copy-target attributes added to each chat-content block by the C# renderer.
Root cause / chosen approach:
Design decisions:
avigator.clipboard.writeText. After resolve, button briefly shows ✓ via .confirmed class, then reverts after 1200 ms via setTimeout.
Files that will change:
JoshuaRowePhantom commentedon Jun 27, 2026
Tests
Phantom.Workspaces.Agent.Gui.Tests (ChatOutputHtmlRendererTests.cs — new file)
Phantom.Workspaces.Agent.Gui.WebViewTests (ChatOutputBrowserIntegrationTests.cs — new tests added)
Fix #63: add copy-gutter component with per-block copy buttons in age…
JoshuaRowePhantom commentedon Jun 30, 2026
Verification passed
Checked: Commit \73420a8\ (Fix #63) on the \eatures\ branch, touching four files:
Result: Implementation found, all verification criteria satisfied, tests present and green.
avigator.clipboard.writeText, shows .confirmed\ state briefly after copy