Repository navigation
macOS: editing keys for Claude Code cards (newline, select-all, copy/cut/paste) and UTF-8 locale - #145
Closed
mrcertis wants to merge 4 commits into
Closed
macOS: editing keys for Claude Code cards (newline, select-all, copy/cut/paste) and UTF-8 locale#145mrcertis wants to merge 4 commits into
mrcertis wants to merge 4 commits into
Conversation
- Enter, Shift+Enter and Command+Enter reach Claude Code as distinct sequences, as they already do for Codex. - Command+A selects the whole Claude draft (shown as a layer over the prompt rows); Command+C/X copy/cut it, typing, paste or Backspace replace or clear it. Actions run through Claude's Ctrl+G external editor round trip with a bundled $VISUAL helper; input is held while it runs and a Ctrl+G picked up late is cancelled, never opening an editor. - macOS launches without any locale get LC_CTYPE=UTF-8 so pbcopy and friends stop turning Cyrillic into mojibake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Requests are files req.<n> claimed or cancelled by an atomic rename, so a Ctrl+G Claude never handles no longer blocks later actions and a request can't be both acted on and cancelled. - A session's actions run one after another; input typed meanwhile keeps its place, and held input still settles answered prompts. - Only a Claude launched with the helper (SessionMetadata.claudeDraft) gets the whole-draft keys; other Claude cards keep xterm's own Command+A. - Command+A selects only a prompt visible on screen; a dialog in its place or a restarted Claude drops the selection. - The helper keeps the draft when it cannot save a copy and ignores unknown requests; the helper path may not contain shell syntax. - A localeless macOS launch gets LANG set to the system's UTF-8 locale (en_US.UTF-8 when the system lacks it), a name ssh can pass on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pass the draft helper and the locale through Claude's --settings env as well: a background session (/bg, /resume, agent view) runs under Claude's supervisor and gets this launch's --settings, not its environment. - The helper answers a request in another card's folder after its own, so a background session shown by another card still works; it acts only on Claude's own prompt file (claude-prompt-*.md), never on a file a program Claude runs opens (git commit, crontab -e). - A cancel the helper cannot remove is not used up: that Ctrl+G opens the editor. - Agent isolation: a Claude card's own draft folder is writable and readable again; the draft root is private app data, hidden from other launches. - Command+V over a selected draft reads the clipboard when its turn comes, so keys pressed meanwhile stay after the paste. - Every launch resets the card's draft queue; leftovers of an earlier run are removed at startup; Command+A over a dialog drops an earlier selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tor and locale - Inside agent isolation the shared draft root is hidden, so the helper no longer requires it: it looks in its own card's folder, then in whichever other card folders it can see. The seatbelt test now runs the real helper. - A cancel is used up only from the card's own folder: another card's timed-out action never swallows an ordinary Ctrl+G here. - A plugin that sets VISUAL or EDITOR in its own --settings env keeps Ctrl+G (no helper), and a locale a plugin sets is not replaced in the --settings env that background sessions get. - A request that cannot be claimed but is still there (also a dangling link) stops the helper instead of looping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 tasks done
Contributor
Author
|
Superseded by #146: the complete, tested version of this work (this branch was an early state with bugs we have since fixed). |
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Claude Code cards on macOS get the editing keys people expect from a text field:
\r,CSI 13;2u,CSI 13;9u), as they already do for Codex. What each one does stays Claude's choice: with~/.claude/keybindings.jsonmappingenter/shift+entertochat:newlineandcmd+entertochat:submit, Enter writes a new line and Command+Enter sends.LANGset to the system's UTF-8 locale (en_US.UTF-8 when the system lacks it), sopbcopy,viand friends stop turning Cyrillic into mojibake.How the draft actions work
Claude Code has no select-all, so CanvasTTY acts on the draft through Claude's own external-editor round trip (Ctrl+G) with a bundled
$VISUALhelper (src/agent-runtime/draft-editor.sh):req.<n>into the card's folder (<userData>/claude-draft/<session>), sends^G, and holds the card's input meanwhile, so keys typed during the round trip arrive after it;^Guses the cancel up: it never opens an editor;VISUAL/EDITORopens (arguments kept);claude-prompt-*.md):git commitorcrontab -erun by Claude's tools get the person's editor.Only local macOS Claude launches get the helper. A plugin or a remote environment that sets its own editor (in the environment or in its
--settingsenv) keeps it.Claude Code 2.1 background sessions
From Claude Code 2.1,
/bg,/resumeinside a session and the agent view move a conversation into a background session that runs under Claude's own supervisor (claude daemon run), not in the card's process. Such a session gets the launch's--settingsbut not its environment. So the helper variables and the locale are also passed through--settingsenv, merged with CanvasTTY's other inline settings. Verified live: they reach a background session. A background session may be shown by another card than the one that started it, so the helper also answers a request in another card's folder after its own. A cancel is only ever the card's own.Agent isolation
The draft root is private app data, hidden from isolated launches. Each Claude card may write and read only its own draft folder (
ownWritable, derived from the session, not from the environment). A real-seatbelt test runs the helper inside the layer.Notes for maintainers
CANVASTTY_TERMINAL_SESSION_IDand the browser/runtime gateway variables. So background sessions shown in other cards talk to the starting card's gateways, which may be gone. Whether background sessions stay inside agent isolation depends on which launch started the supervisor; not verified here. Possible directions: per-card values through--settings(secrets not in argv), orCLAUDE_CODE_DISABLE_AGENT_VIEW=1for isolated cards.CSI 13;9uon macOS Claude cards. Without acmd+enterbinding in Claude's keybindings, what Claude does with it is Claude's default.Test plan
npm test: 2155 pass; the one failure, "real CLIs start inside the layer" (opencodeJSON Parse error), fails the same way on cleanmainnpm run typecheck--settingsenv, plugin editor and locale; seatbelt with the real helper🤖 Generated with Claude Code