cli-framework/src/lib/util/terminal.ts
2025-05-19 22:23:28 -05:00

56 lines
1.7 KiB
TypeScript

#!/usr/bin/env bun
export type CursorPosition = { row: number; col: number };
export const TerminalUtils = {
clear: {
screen: () => process.stdout.write('\x1Bc'),
content: () => process.stdout.write('\x1B[2J'),
cursorHome: () => process.stdout.write('\x1B[H'),
line: () => process.stdout.write('\x1B[K'),
below: () => process.stdout.write('\x1B[J'),
above: () => process.stdout.write('\x1B[1J'),
clear: () => console.clear(),
},
cursor: {
/** Move cursor to a specific row & column */
moveCursor: (row: number, col: number) =>
process.stdout.write(`\x1B[${row};${col}H`),
/** Queries the terminal for the current cursor position */
async getCursorPosition(): Promise<CursorPosition> {
return new Promise((resolve, reject) => {
if (!process.stdin.isTTY || !process.stdout.isTTY)
reject(new Error('Not running in a terminal environment!'));
const stdin = process.stdin;
stdin.setRawMode(true);
stdin.resume();
stdin.setEncoding('utf8');
stdin.once('data', (data) => {
const match = /\[(\d+);(\d+)R/.exec(data.toString());
resolve({
row: parseInt(match?.[1] ?? '0', 10),
col: parseInt(match?.[2] ?? '0', 10),
});
stdin.setRawMode(false);
stdin.pause();
});
process.stdout.write('\x1B[6n'); // Ask terminal for cursor position
});
},
/** Stores detected cursor position in a constant */
async storePosition(): Promise<CursorPosition> {
return TerminalUtils.cursor.getCursorPosition();
},
/** Restores the passed cursor position */
restorePosition: (pos: CursorPosition) =>
TerminalUtils.cursor.moveCursor(pos.row, pos.col),
},
};