Korean IME composition window appears at wrong position (bottom-left of terminal)
Summary
When typing Korean (or other CJK languages) in Claude Code, the IME composition/candidate window renders at the bottom-left corner of the terminal instead of at the cursor position. This makes Korean input extremely difficult — the composing character appears far from where text is being typed.
Environment
- OS: macOS (Darwin 25.2.0)
- Claude Code model: Opus 4.6
- Input method: macOS default Korean input (2-set / du-beol-sik)
- Terminal: iTerm2 / Terminal.app (reproducible in both)
Steps to Reproduce
- Launch Claude Code in a terminal on macOS.
- Switch to Korean input method.
- Type Korean text in the input prompt (e.g., keystrokes for "한글").
- Observe where the IME composition window appears.
Expected Behavior
The IME composition window should appear directly adjacent to the text cursor in the input field.
Actual Behavior
The IME composition window appears at the bottom-left corner of the terminal, completely detached from the input area:
- Split attention: The composing syllable (ㅎ → 하 → 한) appears far from where text is inserted.
- Disorienting feel: Characters feel "pushed away" or "lagging" due to spatial disconnect.
- Affects all Korean input: Korean users all experience this.
Screenshot
Technical Analysis
Root Cause
Claude Code uses React Ink, which hides the real terminal cursor and renders a fake cursor via chalk.inverse(). macOS IME relies on the real terminal cursor position to place the composition window. Since the real cursor is hidden, the IME window falls back to position (0,0).
The Fix: Ink's useCursor API
Ink 6.7.0 introduced useCursor (PR #866) which moves the real terminal cursor to specified coordinates after each render:
import { useCursor } from 'ink';
import stringWidth from 'string-width';
const { setCursorPosition } = useCursor();
setCursorPosition({
x: stringWidth(prompt + textBeforeCursor),
y: inputRow,
});
Local verification: Tested with ink@6.7.0 + useCursor — IME composition window appears at the correct position. Korean input works as expected.
Related Issues
Proposed Fix
- Integrate
useCursor from ink >=6.7.0 into Claude Code's text input component
- Use
string-width for CJK-aware cursor column calculation
- Show the real terminal cursor alongside the existing visual cursor
Upstream PRs
Reproduction
Available at korean-terminal:
| Script |
Description |
IME behavior |
npm run test1 |
Fake cursor (current behavior) |
IME at bottom-left (broken) |
npm run test2 |
useCursor with real cursor |
IME at correct position (fixed) |
npm run test3 |
Full TextInput + useCursor |
IME at correct position (fixed) |
Korean IME composition window appears at wrong position (bottom-left of terminal)
Summary
When typing Korean (or other CJK languages) in Claude Code, the IME composition/candidate window renders at the bottom-left corner of the terminal instead of at the cursor position. This makes Korean input extremely difficult — the composing character appears far from where text is being typed.
Environment
Steps to Reproduce
Expected Behavior
The IME composition window should appear directly adjacent to the text cursor in the input field.
Actual Behavior
The IME composition window appears at the bottom-left corner of the terminal, completely detached from the input area:
Screenshot
Technical Analysis
Root Cause
Claude Code uses React Ink, which hides the real terminal cursor and renders a fake cursor via
chalk.inverse(). macOS IME relies on the real terminal cursor position to place the composition window. Since the real cursor is hidden, the IME window falls back to position (0,0).The Fix: Ink's
useCursorAPIInk 6.7.0 introduced
useCursor(PR #866) which moves the real terminal cursor to specified coordinates after each render:Local verification: Tested with
ink@6.7.0+useCursor— IME composition window appears at the correct position. Korean input works as expected.Related Issues
Proposed Fix
useCursorfrom ink >=6.7.0 into Claude Code's text input componentstring-widthfor CJK-aware cursor column calculationUpstream PRs
ink-text-input#93: Add IME cursor positioning viauseCursor@inkjs/ui#24: Add IME cursor positioning to TextInputink#876: Improve cursor-ime example with full TextInputink#866:useCursorhook (merged)ink#872:<Cursor>component (open)Reproduction
Available at korean-terminal:
npm run test1npm run test2useCursorwith real cursornpm run test3useCursor