[BUG] Korean/CJK IME preedit composes at parked cursor (bottom-left) in `claude agents` view; CLAUDE_CODE_NATIVE_CURSOR=1 is silently negated
Summary
In the background agents view (claude agents), Korean (Hangul) IME composition is broken: the in-progress composition syllable (preedit) is rendered at the terminal's parked cursor location (bottom-left of the screen) instead of at the input caret. The regular interactive REPL on the same machine/terminal composes Korean correctly inline.
Setting CLAUDE_CODE_NATIVE_CURSOR=1 does not fix the agents view (see root-cause analysis below).
Environment
- Claude Code: 2.1.157
- OS: macOS (Darwin 25.5.0)
- Terminal: Ghostty
- Shell: fish / zsh
- IME: macOS 2-set Korean (두벌식)
Steps to reproduce
- Launch the background agents view:
claude agents - Switch the system input source to Korean.
- In the "type a prompt to create an agent" input, type
안녕.
Expected
안녕 composes inline inside the input box, at the caret — same as the regular claude REPL.
Actual
- The committed syllable (
안) appears in the input box with a drawn cursor block. - The still-composing syllable (
녕) appears outside the input box, at the bottom-left of the screen, where the terminal's real native cursor is parked.
So the drawn cursor block (input box) and the real native cursor (where macOS attaches the IME preedit) are in two different places. The preedit follows the real cursor, which is parked.
Root-cause analysis (from the minified 2.1.157 bundle)
The fullscreen/alt-screen renderer parks the native cursor at a fixed location (altScreenParkPatch) and draws its own cursor block. macOS IME attaches the preedit to the real (parked) cursor, hence the bottom-left composition.
The renderer can position the real native cursor at the caret when options.nativeCursor is true, but in the agents view it resolves to false. The default is computed by (deobfuscated names):
function nativeCursorDefault() {
if (cached !== undefined) return cached;
if (parseBool(process.env.CLAUDE_CODE_ACCESSIBILITY)) return cached = true;
if (parseBool(process.env.CLAUDE_CODE_NATIVE_CURSOR)) return cached = !usesParkedCursor();
return cached = false;
}
function usesParkedCursor() {
if (cached2 !== undefined) return cached2;
if (!process.stdout.isTTY) return cached2 = false;
if (/* ... */) return cached2 = false;
if (parseBool(process.env.CLAUDE_CODE_DECSTBM)) return cached2 = true;
return cached2 = featureFlag("tengu_marlin_porch", false);
}
So even when the user sets CLAUDE_CODE_NATIVE_CURSOR=1, the value becomes !usesParkedCursor(). On setups where usesParkedCursor() is true (the tengu_marlin_porch flag, or CLAUDE_CODE_DECSTBM), native cursor stays off and the manual override is silently negated. This is why CLAUDE_CODE_NATIVE_CURSOR=1 has no effect in the agents view.
Net effect: in the agents view there is no user-facing way to make the native cursor track the input caret, so CJK IME preedit can't compose inline.
Workarounds tried
CLAUDE_CODE_NATIVE_CURSOR=1 claude agents— no effect (negated as above).- Switching the renderer to the classic main-screen mode (
/tui default/CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1) avoids the parked cursor, but loses the flicker-free fullscreen scrollback — and it's unclear whether the agents view honors thetuisetting at all.
Suggested fix
In the agents view (and any alt-screen TUI with a focused text input), emit the real cursor-position escape to the input caret so the terminal's IME composes there — i.e. treat the focused input like the REPL does — rather than parking the native cursor. At minimum, let CLAUDE_CODE_NATIVE_CURSOR=1 actually force native cursor on without being negated by usesParkedCursor().
Related
- #63091 — IME candidate window at corner instead of caret (Windows CMD): same class (preedit not at caret).
- #47962 — IME pre-edit buffer cleared by UI redraws (macOS, iTerm2).
- #39112 — docs gap on native terminal IME composition / caret tracking.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗