[BUG] Korean/CJK IME preedit composes at parked cursor (bottom-left) in `claude agents` view; CLAUDE_CODE_NATIVE_CURSOR=1 is silently negated

Resolved 💬 2 comments Opened May 30, 2026 by shinnamu Closed Jun 2, 2026

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

  1. Launch the background agents view: claude agents
  2. Switch the system input source to Korean.
  3. 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 agentsno 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 the tui setting 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗