import type { Terminal } from "./terminal.js"; /** Logical cursor row/col extracted from overlay-stripped rendered lines */ export type HardwareCursorLogicalPos = { row: number; col: number }; /** * Move the terminal cursor so IME anchors to the rendered logical cursor (`CURSOR_MARKER`). * * Purpose: IME placement stays testable without the differential render pipeline. * * Consumer: `TUI.doRender` after marker strip (`extractCursorPosition`). * * Returns the next logical hardware cursor row used for incremental cursor-move math. */ export function syncHardwareCursorForIME( terminal: Pick, options: { cursorPos: HardwareCursorLogicalPos | null; totalLines: number; hardwareCursorRow: number; showHardwareCursor: boolean; }, ): number { const { cursorPos, totalLines, hardwareCursorRow, showHardwareCursor } = options; if (!cursorPos || totalLines <= 0) { terminal.hideCursor(); return hardwareCursorRow; } const targetRow = Math.max(0, Math.min(cursorPos.row, totalLines - 1)); const targetCol = Math.max(0, cursorPos.col); const rowDelta = targetRow - hardwareCursorRow; let buffer = ""; if (rowDelta > 0) { buffer += `\x1b[${rowDelta}B`; } else if (rowDelta < 0) { buffer += `\x1b[${-rowDelta}A`; } buffer += `\x1b[${targetCol + 1}G`; if (buffer) { terminal.write(buffer); } if (showHardwareCursor) { terminal.showCursor(); } else { terminal.hideCursor(); } return targetRow; }