Skip to content

Report a clipboard write that fails both ways - #1091

Merged
nedtwigg merged 2 commits into
mainfrom
copy-failures
Oct 9, 2026
Merged

nedtwigg merged 2 commits into
mainfrom
copy-failures

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Oct 9, 2026

Copy link
Copy Markdown
Member

Copy actions, such as Copy surface:N in a terminal's right-click context, sometimes fail with "Could not copy to clipboard", and a retry usually works. We could not reproduce this, so this PR collects evidence from the field (#1090).

In WebKit, writeText is refused when the click does not count as recent activation. The fallback, execCommand('copy'), then runs after the await and fails too.

The failure did not reproduce in either engine:

  • Chromium (innerdogfood harness): 25 of 25 copies succeeded.
  • Playwright WebKit: 12 of 12 succeeded. That engine does refuse writeText once activation has lapsed, so the test was meaningful.
  • Blocking the main thread for up to 8 s before the click still let every copy succeed.

Change

  • lib/src/lib/clipboard-failure.ts captures each write's state synchronously at the click or key that asked for it. It also notes the outcome of each fallback step. The report never includes the copied text.
  • writeTextToClipboard publishes a report when both paths fail. Callers can opt out with reportFailure: false; the dialog's own "Copy details" does, so a retry cannot replace the evidence on screen.
  • ClipboardFailureDialog is mounted in App, so it covers the desktop and VS Code hosts. It shows the details with instructions to post them to Copy to clipboard sometimes fails (collecting diagnostics) #1090, a Copy details button, and the failure count.
  • Spec rule added in docs/specs/mouse-and-clipboard.md §4.5. The word budget is ratcheted.
  • Tests: a refused write produces a report without the text, and a successful fallback or an opt-out produces none. Storybook adds Modals/ClipboardFailureDialog.

Fixes nothing yet; it collects evidence for #1090.

🤖 Generated with Claude Code

A copy that `navigator.clipboard.writeText` refuses and the `execCommand`
fallback cannot rescue now opens a "Copy failed" dialog in the desktop and
VS Code hosts. The dialog shows diagnostics for the attempt and asks the user
to post them to #1090. The diagnostics are the activation state, the
triggering event, time since the last trusted press and key, focus, and each
fallback step. They never include the copied text. The failure has not
reproduced in Chromium or Playwright WebKit, so field reports are the way to
find it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3a1ce20
Status: ✅  Deploy successful!
Preview URL: https://c6e8c9f2.mouseterm.pages.dev
Branch Preview URL: https://copy-failures.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue link breaks in the VS Code host, which is one of the two hosts this dialog targets. getPlatformOrNull()?.openExternal detaches the method from its adapter, and VSCodeAdapter.openExternal reads this.vscode.postMessage(...). Clicking the link therefore throws a TypeError instead of opening #1090. The Tauri and fake adapters don't read this, which is why the link works in standalone and Storybook.

ExternalTextLink already provides this link: a text-link underline button that calls getPlatform().openExternal?.(href) on the adapter. The suggestions below switch the dialog to it. App always has a platform, so the <a> fallback for a null platform has no caller.

Comment thread lib/src/components/ClipboardFailureDialog.tsx Outdated
Comment thread lib/src/components/ClipboardFailureDialog.tsx Outdated
Comment thread lib/src/components/ClipboardFailureDialog.tsx Outdated
Reading `openExternal` off the adapter detached it from `this`, so the VS Code
adapter threw on click; ExternalTextLink calls it on the adapter. The issue URL
gets its outbound-lint entries as a user-clicked link. The
paste-control-characters canary seed is regenerated over clipboard.ts's new
import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg
nedtwigg merged commit 4bdda59 into main Oct 9, 2026
14 checks passed
@nedtwigg
nedtwigg deleted the copy-failures branch October 9, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants