stablyai / stablyai/orca

[Bug]: macOS — Option-composed non-ASCII letters (ç, ñ, å, ø) are swallowed in kitty-protocol TUIs even with "Option as Alt" disabled

Open
#20,171 17 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
71.3k
Forks
4.7k
Avg merge
14h 54m
Merged PRs (30d)
520

Description

## Problem

On macOS, `Option+C` (→ `ç`) does not reach a TUI that has negotiated the kitty keyboard
protocol. Instead of the composed character, the pane sends the physical chord `alt+c`
encoded as `CSI 99;3u`, and the character is lost entirely.

This happens **even though the pane is configured with Option explicitly NOT acting as
Alt** (`terminalMacOptionAsAlt: "false"`, Settings → Terminal → macOS keyboard). That
setting is the user stating "Option composes characters here", so a composed character
should be delivered verbatim.

It affects every letter macOS composes with Option outside ASCII: `ç ñ å ø œ æ ¡ ¿ …`
For pt-BR / es / da / no / sv users, `ç` is a plain letter typed dozens of times a day,
so a kitty-protocol agent pane is effectively unusable for writing prose.

No pane setting avoids it: none of the four `macOptionAsAlt` values
(`true` / `false` / `left` / `right`) reaches a code path that sends the character, and
switching the macOS input source between `U.S.` and `U.S. International – PC` does not
change the Option+C path either.

What *does* work under the very same kitty flags is the dead-key route on
`U.S. International – PC` (`'` then `c`, no Option held), which never enters this policy.
That isolates the defect precisely to the Option keydown path — the pane, the encoder and
the PTY all deliver `ç` fine.

## Environment

- Orca 1.4.199, macOS 26.5 (arm64)
- macOS input source: `U.S.` (also reproduced on `U.S. International – PC`)
- Settings → Terminal → macOS keyboard: Option is **not** Alt (`terminalMacOptionAsAlt = "false"`)
- Affected TUI: Claude Code 2.1.269 (enables the kitty keyboard protocol)
- **Not** affected: Codex, Grok — neither negotiates the protocol, and Orca already
force-disables kitty for `tuiAgent === 'grok'`

## Reproduce (no agent needed)

In any Orca terminal pane, with `terminalMacOptionAsAlt = "false"`:

```sh
printf '\033[5u'; cat -v # flags 5 = exactly what Claude Code sends
# now press Option+C
```

| kitty flags | bytes received on Option+C |
| --- | --- |
| protocol off | `ç` :white_check_mark: |
| `CSI > 1 u` | `ç` :white_check_mark: |
| `CSI > 5 u` | `^[[99;3u` :x: — character lost |
| `CSI > 29 u` | `^[[99;3;231u` :white_check_mark: (survives only as associated text) |

Same pane, same `CSI > 5 u`, but typing `ç` as `'`+`c` on `U.S. International – PC`
(no Option): `ç` arrives correctly.

Injecting `ç` as text with no Alt modifier works under any flag set, so the pane and the
PTY are fine — only the Option keydown path is.

## Root cause

`src/renderer/src/components/terminal-pane/terminal-option-shortcut-policy.ts`

`resolveTerminalOptionShortcutAction` guards the "send the composed text" branch with
`isLayoutComposedAsciiCharacter`, which accepts only code points `0x21…0x7e`:

```ts
// Why ASCII-only: the protocol says a text-producing key sends its text, but #8031 needs Option
// hotkeys to still reach kitty TUIs. ASCII splits the two — layouts hide `@ $ # [ ] { } \ |` behind
// Option with no other way to type them, while the glyphs on TUI-bound keys (π, ƒ, ∫) never are.
```

`ç` is U+00E7, so it fails the guard, falls through to `encodeTerminalOptionKittyEvent`,
and is emitted as the `alt+c` chord instead of as text.

That ASCII line was drawn in #14743 (fixing #14024, `@` / `$` on the Turkish layout). It
separates symbols from TUI glyphs correctly, but puts **accented letters on the wrong
side**: `ç`, `ñ`, `å` are letters the layout composes — not `π` / `ƒ` / `∫` glyphs that
happen to sit on TUI-bound keys.

## Suggested fix

When `canSendComposedText` is true — i.e. the user configured `macOptionAsAlt` so that
this Option side does **not** act as Alt — send the composed character regardless of its
code point. The user has already declared Option's role; the ASCII heuristic is only
needed where that role is ambiguous. Users who want Option to reach kitty TUIs as a
modifier (the #8031 case) set `macOptionAsAlt: "true"`, which this leaves untouched.

If a narrower change is preferred, at minimum widen the guard to Unicode letters whose
NFD decomposition starts with the key's unmodified base character (`ç` → `c`, `ñ` → `n`,
`å` → `a`), which keeps `π` / `ƒ` / `∫` on the chord path.

## Why it surfaced now

Claude Code ≤ 2.1.268 enabled the kitty protocol only for an allowlist of terminals
(`iTerm.app`, `kitty`, `WezTerm`, `ghostty`, `tmux`, `windows-terminal`, `WarpTerminal`)
— Orca was not in it, so the protocol stayed off and `ç` worked. 2.1.269 added a
`CSI ? u` probe; Orca answers it, so Claude Code now enables `CSI > 5 u` and the latent
bug became reachable. This also explains the "Not affected: Claude" note in #14024 back
in August.

Contributor guide

Open the contributing guide

Research direction

Start with `src/renderer/src/components/terminal-pane/terminal-option-shortcut-policy.ts` and read `resolveTerminalOptionShortcutAction`, especially the composed-text guard and the kitty-event fallback. Check for tests covering this policy and the existing Option-as-Alt behavior. Reproduce with kitty flags 5 and Option+C; done means the composed character reaches the TUI when Option is configured not to act as Alt, while the Alt-modifier behavior remains available when configured as Alt.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.