Skip to content

fix(ai-edition): copy chat messages via main-process clipboard on Windows - #21

Closed
My-Denia wants to merge 1 commit into
mainfrom
fix/738-copy-message-windows-clipboard
Closed

My-Denia wants to merge 1 commit into
mainfrom
fix/738-copy-message-windows-clipboard

Conversation

@My-Denia

@My-Denia My-Denia commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes getopenscreen#738. The AI chat panel's Copy message button always reported "Couldn't copy" on Windows: Electron denies the renderer's navigator.clipboard.writeText (the renderer never holds the clipboard-sanitized-write permission), so the write was rejected with NotAllowedError: Write permission denied and the handler mapped every rejection to the generic error toast while the clipboard kept its old content. The behavior is identical in dev and packaged builds; on the packaged build the reporter saw the same failure.

The fix

A narrow clipboard-write bridge following the repo's existing preload/IPC patterns:

  • electron/ipc/handlers.ts: ipcMain.handle("clipboard:write-text", …) — string-checked, writes with Electron's main-process clipboard module (no permission gate there; main.ts already uses this API for the update dialog).
  • electron/preload.ts: copyToClipboard(text) exposed through the existing electronAPI bridge.
  • electron/electron-env.d.ts: optional copyToClipboard? declaration, mirroring benchFinished?.
  • src/components/ai-edition/LeftPanel.tsx: the copy handler prefers the bridge and keeps the navigator.clipboard.writeText fallback for shim/web contexts without it. Toasts and semantics are unchanged.

No clipboard framework, no chat data-model change, no UI redesign. The only other clipboard paths (Edit-menu roles, the update-dialog copy) are untouched.

Regression evidence (old FAIL / new PASS)

src/components/ai-edition/LeftPanel.copyMessage.test.tsx stubs the navigator clipboard to reject exactly the way Electron's does, with the bridge available, and asserts the bridge receives the exact message content plus the success toast:

  • on upstream/main (commit cef9be0): the distinguishing test fails — the click routes to the denied navigator and shows the error toast;
  • with this fix: passes; a companion test pins the fallback behavior when the bridge is absent.

Checks on the branch: npx tsc --noEmit and npx tsc -p tsconfig.test.json --noEmit both clean; Biome clean on the touched files (repo-root npm run lint hits a pre-existing nested-root configuration error on this machine, reproducible on pristine main); the full unit suite shows exactly the same set of pre-existing local-environment failures with and without this change — the branch adds the regression test and zero new failures.

Real-machine verification (Windows 11, computer-use driven)

  • Baseline reproduction on current main (dev build): Copy message click → the localized "Couldn't copy" error toast, clipboard unchanged (sentinel read-back), root cause captured as the NotAllowedError above via a temporary uncommitted diagnostic.
  • Fixed dev build: assistant and user messages copy with the localized "Copied to clipboard" success toast; clipboard content byte-compared with the exact stub reply (unicode, CJK, emoji, newlines, trailing tab all intact); copying different messages in sequence leaves no residue; composer select + Ctrl+C still works.
  • Fixed packaged build (electron-builder --win --dir, Openscreen.exe, file:// renderer): provider connected through the settings UI, chat turn completed, Copy message → "Copied to clipboard" toast and byte-exact clipboard for both messages.

One honest limit: an assistant message with empty content renders a zero-height bubble whose footer buttons are not exposed to the accessibility tree, so the empty-content case could not be clicked end to end. Its semantics ride the same unconditional write path (no content-dependent branch) and the unit test pins exact content pass-through; the baseline has the identical rendering behavior.

Intent

Opened against the fork for review; ready for an owner decision before any upstream submission (per the standing rule, no upstream PR will be created without explicit approval).

Copilot AI lite review requested due to automatic review settings September 25, 2026 14:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: ed08c606-bb7b-4651-b85a-50feb4c21079


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T16:39:58.753934Z a129322 New commits
🔒 Security Review ✅ Completed 2026-09-25T14:20:53.981605Z 6c7b282 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@My-Denia
My-Denia force-pushed the fix/738-copy-message-windows-clipboard branch 2 times, most recently from f701ab6 to f74e0ad Compare September 25, 2026 15:41
…dows

Electron denies the renderer's navigator.clipboard.writeText
(NotAllowedError: Write permission denied — the renderer never holds
clipboard-sanitized-write), so the chat panel's Copy message button
always fell to its error toast and the clipboard kept its old content,
in dev and packaged builds alike (issue getopenscreen#738).

Route the write through a narrow preload bridge into main's clipboard
module, which has no such permission gate: ipcMain.handle receives the
text (string-checked), the preload exposes copyToClipboard, and the
panel prefers the bridge while keeping the navigator path for shim/web
contexts without one. Toasts and message content are unchanged.

The regression test pins the distinguishing case: a navigator clipboard
that rejects exactly the way Electron's does, with the bridge available
— the old handler fails it, the new one copies the full message and
reports success.
@My-Denia
My-Denia force-pushed the fix/738-copy-message-windows-clipboard branch from f74e0ad to a129322 Compare September 25, 2026 16:37
@My-Denia My-Denia closed this Sep 26, 2026
@cursor
cursor Bot deleted the fix/738-copy-message-windows-clipboard branch September 26, 2026 12:08
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.

[Bug]: AI chat message copy button always reports "Couldn't copy" on Windows

2 participants