Skip to content

Paste into a Bot's browser on a layout that does not write Latin letters - #596

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:paste-non-latin-layout
Sep 18, 2026
Merged

davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:paste-non-latin-layout

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

What this changes

When somebody takes the wheel of a Bot's browser, LiveScreen sends their keystrokes to it, except Ctrl+V / Cmd+V. #422 leaves that shortcut to the local page, so the browser raises a paste event, and onPaste sends the clipboard text across as { type: "text" }.

isPasteShortcut recognised the shortcut with event.key.toLowerCase() === "v". That is the same comparison #580 fixed for Shift+N. key is the character the layout writes, and a layout that writes another script has no key that writes a V:

Layout Ctrl + the V key key code Before After
Russian м "м" KeyV sent to the Bot's browser, local paste prevented stays local, clipboard text sent
Greek ω "ω" KeyV sent to the Bot's browser, local paste prevented stays local, clipboard text sent
Dvorak V "v" Period stays local stays local (unchanged)
Dvorak K "k" KeyV sent as Ctrl+K sent as Ctrl+K (unchanged)

On those layouts the keydown was forwarded as Ctrl+"м" and preventDefault was called, so the local paste event never fired. The person's clipboard never reached the Bot's browser, and pasting a one-time code or a long address while driving did nothing.

The fix exports keyOf from lib/hotkeys/hotkeys.ts, the function #580 added, and uses it in isPasteShortcut. So both places read the key by one rule: key as before, and the physical key from code only when key is a single character outside ASCII. The modifier check, the keyup bookkeeping and onPaste are unchanged.

Where it runs

  • New state that outlives a request? None.
  • What happens on the second replica? Not applicable. This is keyboard handling in the browser.
  • Anything serialised? No.
  • Anything fanned out to a browser? No new messages. The same text frame is sent that a paste on a Latin layout already sends.
  • New listener, port, or schedule? No.

Boundary and audit

  • Every acting call still goes through the gateway: resolve, decide, audit, then act. Unchanged. Input is still sent only while the person holds the wheel.
  • New refusals and new failures each write a row. None are added.
  • Nothing new is trusted from the client that the server can resolve itself.

Changelog

  • A line in CHANGELOG.md under Unreleased.

Proof

Three tests are added to app/tests/live-screen-keyboard.test.tsx, using its existing SocketDouble:

  • Ctrl+V keydown and keyup on a Russian layout (key: "м") and on a Greek layout (key: "ω"), both with code: "KeyV". Each asserts that nothing is sent and the keydown is not prevented.
  • Dvorak: Ctrl on the key that writes V (code: "Period") is still paste, and Ctrl on the QWERTY V key, which writes K, is still sent as Ctrl+K. This one passes on main too and is there to keep the Latin behaviour pinned.

With live-screen.tsx from main (only the tests added):

error: expect(received).toEqual(expected)

- []
+ [
+   {
+     "code": "KeyV",
+     "event": "down",
+     "key": "м",
+     "modifiers": 2,
+     "text": "м",
+     "type": "key",
+     "windowsVirtualKeyCode": 86,
+   },
+   {
+     "code": "KeyV",
+     "event": "up",
+     "key": "м",
+     "modifiers": 0,
+     "type": "key",
+     "windowsVirtualKeyCode": 86,
+   },
+ ]
(fail) Ctrl+V on a Russian layout stays in the local page too
(fail) Ctrl+V on a Greek layout stays in the local page too
 7 pass
 2 fail

With the fix:

  • bun test --coverage tests/live-screen-keyboard.test.tsx tests/hotkey-layouts.test.ts: 12 pass, 0 fail. The changed lines in live-screen.tsx and hotkeys.ts are covered, and Start a new chat with Shift+N on a layout that does not write Latin letters #580's layout tests still pass against the now exported keyOf.
  • The whole app suite on this Windows machine: 873 pass, 44 fail with the fix, and 870 pass, 44 fail on main. The 44 failures are the same tests on both runs (serve ports and WebSockets, path handling, locale).
  • bun run typecheck (app, server, worker): exit 0. bunx biome format and bunx biome lint --error-on-warnings on the changed files: clean.

The keystrokes in the tests have the shape a browser reports: key from the layout and code from the physical key. I did not drive a real Bot browser with a Russian layout selected.

This PR adds its entry at the top of ## Unreleased, where other open PRs add theirs, so the changelog may conflict. I can rebase if it does.

🤖 Generated with Claude Code

While a person drives a Bot's browser, Ctrl+V or Cmd+V stays in the
local page so its paste event can carry the clipboard text across.
isPasteShortcut compared KeyboardEvent.key with "v", and on Russian or
Greek the V key writes "м" or "ω", so the keystroke was sent to the Bot's
browser, the local paste was prevented, and nothing was pasted. It now
reads the key through keyOf, the rule the app's shortcuts already use
for those layouts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code-verified clean; CI green on this sha.

@davidmckayv
davidmckayv merged commit e9e0e72 into CopilotKit:main Sep 18, 2026
17 checks passed
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