#!/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 { 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 { return TerminalUtils.cursor.getCursorPosition(); }, /** Restores the passed cursor position */ restorePosition: (pos: CursorPosition) => TerminalUtils.cursor.moveCursor(pos.row, pos.col), }, };