Skip to content

fix(web-desktop): copy messages to the browser's machine, not the host#1143

Open
chr1syy wants to merge 2 commits into
RunMaestro:rcfrom
chr1syy:fix/web-desktop-clipboard-copy
Open

fix(web-desktop): copy messages to the browser's machine, not the host#1143
chr1syy wants to merge 2 commits into
RunMaestro:rcfrom
chr1syy:fix/web-desktop-clipboard-copy

Conversation

@chr1syy

@chr1syy chr1syy commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Problem

Clicking Copy on a message while accessing Maestro through web-desktop (the browser interface) copied the text to the clipboard of the machine running the Electron host, not the PC the user is browsing from.

Root cause

safeClipboardWrite() in src/renderer/utils/clipboard.ts always preferred window.maestro.shell.copyTextToClipboard, which routes over IPC to the host's Electron clipboard.writeText(). In web-desktop the renderer runs in the browser but the preload shim still exposes that method, so the browser-native fallback was never reached. Result: the copy landed on the server's machine.

Fix

  1. Route web-desktop through the browser clipboard. Guard the Electron IPC path with the existing isWebDesktop() helper (src/renderer/utils/runtimeContext.ts) so web-desktop uses navigator.clipboard instead. Applied to all three helpers that had the same flaw: safeClipboardWrite (text), safeClipboardWriteImage, and safeClipboardReadImage.

  2. Insecure-context fallback. The async Clipboard API is gated to secure contexts, so navigator.clipboard is undefined when web-desktop is served over plain HTTP (e.g. a Tailscale/LAN IP without TLS). Added a legacy execCommand('copy') fallback for the text path that runs when navigator.clipboard is missing or the async write throws, so the copy still lands on the user's own machine.

Behavior matrix (text copy)

Context Path used
Electron desktop app Electron IPC -> host clipboard (unchanged)
web-desktop over HTTPS / localhost navigator.clipboard.writeText (browser)
web-desktop over plain HTTP (Tailscale/LAN) execCommand('copy') fallback (browser)

Tests

Added src/__tests__/renderer/utils/clipboard.test.ts (5 cases, all passing): desktop uses IPC; web-desktop uses the browser API; insecure context falls back to execCommand; thrown async write falls back to execCommand; returns false only when even the legacy copy fails.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved clipboard read/write behavior across desktop app and web-desktop environments.
    • Added/strengthened legacy copy fallback (execCommand) and retry behavior when browser clipboard access fails.
    • Ensured text and image clipboard operations route correctly per environment.
  • Tests

    • Added Vitest coverage to verify environment-specific clipboard pathways and fallback scenarios for both text and images.

Clicking Copy on a message in web-desktop wrote to the clipboard of the
machine running the Electron host, not the browser the user is on. The
shared clipboard helper always preferred window.maestro.shell.copyText-
ToClipboard, which routes over IPC to the host's Electron clipboard, and
that path is always present in web-desktop via the preload shim.

Guard the Electron IPC path with isWebDesktop() so web-desktop uses the
browser Clipboard API instead. Applied to text, image-write and image-
read helpers.

Add a legacy execCommand('copy') fallback for the text path: the async
Clipboard API is gated to secure contexts, so it is undefined when web-
desktop is served over plain HTTP (e.g. a Tailscale/LAN IP without TLS).
The fallback runs when navigator.clipboard is missing or the async write
throws, so the copy still lands on the user's own machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0770857c-4eee-445f-88df-8ab69f54a48f

📥 Commits

Reviewing files that changed from the base of the PR and between a69ab91 and ac5e9a2.

📒 Files selected for processing (2)
  • src/__tests__/renderer/utils/clipboard.test.ts
  • src/renderer/utils/clipboard.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/utils/clipboard.ts

📝 Walkthrough

Walkthrough

safeClipboardWrite now uses Electron IPC only outside web-desktop mode and falls back to a legacy execCommand('copy') path on clipboard failures. The image clipboard helpers also guard their IPC branches with !isWebDesktop(). A new Vitest suite covers the routing behavior.

Changes

Clipboard Routing and Fallback

Layer / File(s) Summary
safeClipboardWrite routing and image guards
src/renderer/utils/clipboard.ts
Adds legacyExecCommandCopy, rewires safeClipboardWrite to use IPC only when !isWebDesktop() and fall back to execCommand on error, and adds !isWebDesktop() guards to safeClipboardWriteImage and safeClipboardReadImage.
safeClipboardWrite test suite
src/__tests__/renderer/utils/clipboard.test.ts
Adds Vitest coverage for desktop vs web-desktop routing, clipboard API fallback behavior, and image copy/read paths with mocked browser and IPC clipboard APIs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • RunMaestro/Maestro#481: Directly modifies the same clipboard utility area and related clipboard routing/error-handling behavior.

Poem

🐇 Hop hop, the clipboard sways,
IPC or browser in different ways.
If a write goes soft and shy,
execCommand gives it one more try.
Web-desktop gets the proper trail—
A rabbit left a tidy tale. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main web-desktop clipboard routing fix and is concise and specific.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed due to a network error.


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.

@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR changes clipboard routing so web-desktop copies use the browser machine. The main changes are:

  • Added web-desktop checks before Electron clipboard IPC calls.
  • Added a legacy text copy fallback for insecure browser contexts.
  • Added tests for text clipboard routing and fallback behavior.

Confidence Score: 4/5

The changed clipboard flow needs fixes for image copy and image read over plain HTTP web-desktop.

  • Text copy routing is covered and has a browser fallback.
  • Image copy and image read now skip IPC in web-desktop but have no insecure-context fallback.
  • The legacy text fallback can leave focus away from the active terminal or input.

src/renderer/utils/clipboard.ts

Important Files Changed

Filename Overview
src/renderer/utils/clipboard.ts Changes text and image clipboard routing for web-desktop, with remaining issues in insecure-context image paths and legacy focus handling.
src/tests/renderer/utils/clipboard.test.ts Adds useful coverage for text clipboard routing, but image clipboard behavior is not covered.

Reviews (1): Last reviewed commit: "fix(web-desktop): copy to the browser's ..." | Re-trigger Greptile

Comment thread src/renderer/utils/clipboard.ts
Comment thread src/renderer/utils/clipboard.ts
Comment thread src/renderer/utils/clipboard.ts

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
src/__tests__/renderer/utils/clipboard.test.ts (1)

21-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add regression coverage for the image routing guards.

Lines 100-123 in src/renderer/utils/clipboard.ts also changed safeClipboardWriteImage and safeClipboardReadImage to skip host IPC in web-desktop, but this suite only exercises safeClipboardWrite. Please add one web-desktop case for each image helper so a future regression there does not silently copy images on the Electron host again.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/renderer/utils/clipboard.test.ts` around lines 21 - 92, Add
regression coverage in the clipboard tests for the image routing guards
introduced in safeClipboardWriteImage and safeClipboardReadImage. Extend the
existing safeClipboardWrite suite with one web-desktop test for each helper that
verifies mockIsWebDesktop() routes to the browser path and does not call the
host IPC clipboard on window.maestro.shell. Keep the tests close to the existing
safeClipboardWrite cases so the behavior of safeClipboardWriteImage and
safeClipboardReadImage is locked down against future regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/__tests__/renderer/utils/clipboard.test.ts`:
- Around line 21-92: Add regression coverage in the clipboard tests for the
image routing guards introduced in safeClipboardWriteImage and
safeClipboardReadImage. Extend the existing safeClipboardWrite suite with one
web-desktop test for each helper that verifies mockIsWebDesktop() routes to the
browser path and does not call the host IPC clipboard on window.maestro.shell.
Keep the tests close to the existing safeClipboardWrite cases so the behavior of
safeClipboardWriteImage and safeClipboardReadImage is locked down against future
regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55632a28-2348-4211-8133-19d221ca438f

📥 Commits

Reviewing files that changed from the base of the PR and between b330223 and a69ab91.

📒 Files selected for processing (2)
  • src/__tests__/renderer/utils/clipboard.test.ts
  • src/renderer/utils/clipboard.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a69ab91add

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/__tests__/renderer/utils/clipboard.test.ts Outdated
@pedramamini

Copy link
Copy Markdown
Collaborator

Hi @chr1syy, thanks a lot for this fix! 🙏 Routing web-desktop clipboard writes to the browser instead of the Electron host is exactly the right call, and the insecure-context execCommand fallback plus the new test suite are a really nice touch.

Before we merge, there are a few things worth addressing. These surfaced in the automated reviews (Greptile / CodeRabbit / Codex) and I agree they're worth handling:

Must fix

  • Em-dash in a code comment (src/__tests__/renderer/utils/clipboard.test.ts, line 6): the JSDoc reads the HOST machine's clipboard — not the browser. Our writing rule forbids em/en-dashes anywhere, including code comments (see CLAUDE.md, "No Em-Dashes or En-Dashes"). Please swap it for a comma or a plain hyphen.

Should fix (this is the plain-HTTP case the PR targets)

  • Image copy silently fails over plain HTTP (clipboard.ts, safeClipboardWriteImage): you now correctly skip the host IPC path in web-desktop, but navigator.clipboard.write() is unavailable in an insecure context, so image copy returns false with no user-visible signal. That's the exact Tailscale/LAN scenario this PR is about. There's no clean execCommand equivalent for images, so at minimum let's make the failure visible (a toast or center-flash) rather than a silent no-op, or explicitly note the limitation.
  • Image read misreports an empty clipboard over plain HTTP (clipboard.ts, safeClipboardReadImage): same root cause. navigator.clipboard.read() is blocked in insecure contexts, so the image editor reports "No image found in clipboard" even when one is present. Worth handling the same way.

Nice to have

  • Restore focus in the legacy copy path (clipboard.ts, legacyExecCommandCopy): the hidden textarea grabs focus and is then removed without restoring the previously focused element, so the next keystrokes can land off the original input/terminal. Saving document.activeElement and re-focusing it in the finally block handles it.
  • Test coverage for the image guards (CodeRabbit nit): the suite only exercises safeClipboardWrite. A web-desktop case each for safeClipboardWriteImage / safeClipboardReadImage would lock in the routing you added.

Good news: no merge conflicts, the branch is clean against rc, so no rebase needed. Once these are addressed I'll take another pass. Thanks again for the contribution!

Address PR review feedback on the web-desktop clipboard fix:
- legacyExecCommandCopy now restores focus to the previously focused
  element after the hidden textarea is removed, so the next keystroke
  returns to the control that initiated the copy.
- Add web-desktop regression tests for safeClipboardWriteImage and
  safeClipboardReadImage proving they route to the browser API and never
  hit the host IPC clipboard.
- Replace an em-dash in the test header comment per the repo writing rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chr1syy

chr1syy commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Addressed the image-routing test coverage nitpick in ac5e9a2: added two new web-desktop regression suites in clipboard.test.ts for safeClipboardWriteImage and safeClipboardReadImage that assert each routes to the browser Clipboard API and never calls the host IPC clipboard (window.maestro.shell.*). Full suite is 9 passing tests.

Also addressed greptile's P2 (legacy copy now restores focus to the previously focused element) and codex's em-dash nit in the test comment. The two greptile P1 image threads are inherent insecure-context browser limitations (no browser API for binary clipboard data over plain HTTP); replied inline with rationale.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@chr1syy Thanks, that addresses my concern on the image-routing coverage. The added web-desktop regression tests for safeClipboardWriteImage and safeClipboardReadImage sound like the right lock-in for this behavior, and the focus-restore + comment cleanup are nice follow-ups as well.

Your note on the remaining plain-HTTP image clipboard limitations also makes sense. Given the browser restrictions there, an inline rationale is a reasonable way to document the constraint.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@chr1syy chr1syy added the ready to merge This PR is ready to merge label Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge This PR is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants