fix(transcript): show Copied only when error or diff copy succeeds
Why
HQ #4203 found that transcript Copy controls could say Copied when the async clipboard was refused and the legacy document.execCommand("copy") fallback returned false.
The current code has already improved the lower-level helper: copyTextResult(text) returns a real boolean, preserves the fallback's false/throw result, removes its temporary textarea and restores focus. Fenced code and card corners already consume that result through CopyButton. Two surviving direct consumers still use the legacy copyText(text): Promise<void> wrapper, which awaits the boolean but discards it:
- failed-turn card's Copy error, in
TurnRows.tsx; - edit diff's Copy, in
EditDiff.tsx.
Both treat the wrapper's fulfilled void promise as proof the clipboard changed, and present Copied after a false fallback.
Fix
- Switch those two call sites to the existing
copyTextResulthelper. - Drive their existing
copiedstate from the actualboolean. Keep their rejection callback defensive. Successful fallback and later retry still work. - Leave the shared legacy wrapper and unrelated callers alone; no new copy service, global feedback API, or UI changes.
Validation and tests
- Added
webviews/test/agent-pane-copy-refusal.test.tsxwith a controllable async clipboard andexecCommandfallback:- async success, fallback true and explicit false;
- thrown fallback and removal of the temporary textarea;
- the real failed-turn Copy error control stays in Copy on refusal and recovers after a successful retry;
- the edit-diff Copy control does the same.
- Source-level verification: Executed the exact GitHub-saved body of
copyTextResult(only its TypeScript signature erased) in a JS test harness: refusedfalse, accepted fallbacktrue, thrown fallbackfalse, async successtrue; 3 fallbacks appended and cleaned up 3 temporary nodes, with focus restored. Also inspected the two committed callers for use of the boolean. - Not run here: the actual Bun/JSDOM/React tests, web lint, a tagged WKWebView app, or clipboard paste in native. CI/review must confirm them.
Ownership/overlap
- HQ #4203 is the existing source-audited owner issue. Its CodeBlock finding is already addressed by a different, newer
CopyButtonpath; this PR fixes only the remaining direct consumers. - Recent relevant copy/card PR #28057 has merged and its
CopyButtonremains unchanged; no open competing PR was identified for these exact controls. - No clipboard schema, permission, platform protocol, backend or unrelated UI file changed.
Acceptance
A fallback false or throw keeps Copy error and Copy diff in their original state; success changes to Copied; a subsequent successful retry recovers without an app restart.
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes transcript Copy controls showing Copied when the clipboard was refused and the legacy execCommand("copy") fallback returned false or threw.
The failed-turn card's Copy error and the edit diff's Copy now use copyTextResult and update their copied state from the actual boolean, so a refused copy keeps the button unchanged and a successful retry recovers normally.
- Adds
webviews/test/agent-pane-copy-refusal.test.tsxcovering refusal, fallback success, thrown fallback, and retry behavior for both controls; the edit-diff case renders the real lazy-loaded hunks with a valid edit fixture.