Skip to content

Commit 0a4b5a2

Browse files
jimgqyuclaude
andcommitted
fix: enable terminal cursor blinking via DEC ?12h for IME input
Ink v7's buildCursorSuffix only emits DECTCEM show/hide (ESC[?25h/l) without any blink sequence. Add ESC[?12h (DEC Start Blinking Cursor) once per mount so the hardware cursor blinks in the text input area. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent d420c65 commit 0a4b5a2

1 file changed

Lines changed: 15 additions & 0 deletions

File tree

packages/tui/src/compat/cursor-hooks.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,25 @@ export function useDeclaredCursor(
9797

9898
const { setCursorPosition } = useCursor();
9999

100+
// Track whether we have emitted the blink-enable sequence.
101+
// \x1b[?12h is a DEC private mode that persists across cursor hide/show
102+
// cycles, so we only need to emit it once per mount.
103+
const blinkEmittedRef = useRef(false);
104+
100105
useEffect(() => {
101106
const opts = optsRef.current;
102107
if (!process.stdout.isTTY) return;
103108

109+
// Enable cursor blinking on the first successful render frame.
110+
// Ink v7's buildCursorSuffix only emits \x1b[?25h (DECTCEM show cursor),
111+
// never a blink sequence. ?12h is the DEC "Start Blinking Cursor" mode
112+
// and is widely supported (xterm, iTerm2, Windows Terminal). Terminals
113+
// that ignore ?12h (kitty, Alacritty) control blinking via user config.
114+
if (!blinkEmittedRef.current) {
115+
process.stdout.write('\x1b[?12h');
116+
blinkEmittedRef.current = true;
117+
}
118+
104119
const el = elRef.current;
105120
if (!el?.yogaNode) return;
106121

0 commit comments

Comments
 (0)