55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
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<Terminal, "hideCursor" | "showCursor" | "write">,
|
|
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;
|
|
}
|