fix(transcript): show Copied only when error or diff copy succeeds

TE
5d ago
+169-6

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 copyTextResult helper.
  • Drive their existing copied state from the actual boolean. 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.tsx with a controllable async clipboard and execCommand fallback:
    • 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: refused false, accepted fallback true, thrown fallback false, async success true; 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 CopyButton path; this PR fixes only the remaining direct consumers.
  • Recent relevant copy/card PR #28057 has merged and its CopyButton remains 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 Autofix with [code]smith
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.tsx covering 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.