Add performant ANSI module

This commit is contained in:
themodernhakr 2025-05-19 11:01:02 -05:00
parent e925828328
commit bcdbf8f7c6

131
src/lib/util/ansi.ts Normal file
View File

@ -0,0 +1,131 @@
import { tryCatch, type Result } from '@maxmorozoff/try-catch-tuple';
const encoder = new TextEncoder();
export const ANSI = {
// Text Styles
RESET: '\x1B[0m',
BOLD: '\x1B[1m',
DIM: '\x1B[2m',
ITALIC: '\x1B[3m',
UNDERLINE: '\x1B[4m',
BLINK: '\x1B[5m',
REVERSE: '\x1B[7m',
HIDDEN: '\x1B[8m',
// Foreground Colors
BLACK: '\x1B[30m',
RED: '\x1B[31m',
GREEN: '\x1B[32m',
YELLOW: '\x1B[33m',
BLUE: '\x1B[34m',
MAGENTA: '\x1B[35m',
CYAN: '\x1B[36m',
WHITE: '\x1B[37m',
// Background Colors
BG_BLACK: '\x1B[40m',
BG_RED: '\x1B[41m',
BG_GREEN: '\x1B[42m',
BG_YELLOW: '\x1B[43m',
BG_BLUE: '\x1B[44m',
BG_MAGENTA: '\x1B[45m',
BG_CYAN: '\x1B[46m',
BG_WHITE: '\x1B[47m',
// Cursor Control
SAVE_CURSOR: '\x1B[s',
RESTORE_CURSOR: '\x1B[u',
CURSOR_TO: (x: number, y: number) => `\x1B[${y};${x}H`,
CURSOR_UP: (n = 1) => `\x1B[${n}A`,
CURSOR_DOWN: (n = 1) => `\x1B[${n}B`,
CURSOR_FORWARD: (n = 1) => `\x1B[${n}C`,
CURSOR_BACK: (n = 1) => `\x1B[${n}D`,
// Screen Control
CLEAR_SCREEN: '\x1B[2J',
CLEAR_LINE: '\x1B[2K',
CLEAR_END_LINE: '\x1B[0K',
CLEAR_START_LINE: '\x1B[1K',
CLEAR_BELOW: '\x1B[J',
};
export const ANSI_BUFFERS = {
// Static code buffers
RESET: encoder.encode(ANSI.RESET),
BOLD: encoder.encode(ANSI.BOLD),
DIM: encoder.encode(ANSI.DIM),
ITALIC: encoder.encode(ANSI.ITALIC),
UNDERLINE: encoder.encode(ANSI.UNDERLINE),
BLINK: encoder.encode(ANSI.BLINK),
REVERSE: encoder.encode(ANSI.REVERSE),
HIDDEN: encoder.encode(ANSI.HIDDEN),
// Colors
BLACK: encoder.encode(ANSI.BLACK),
RED: encoder.encode(ANSI.RED),
GREEN: encoder.encode(ANSI.GREEN),
YELLOW: encoder.encode(ANSI.YELLOW),
BLUE: encoder.encode(ANSI.BLUE),
MAGENTA: encoder.encode(ANSI.MAGENTA),
CYAN: encoder.encode(ANSI.CYAN),
WHITE: encoder.encode(ANSI.WHITE),
// Backgrounds
BG_BLACK: encoder.encode(ANSI.BG_BLACK),
BG_RED: encoder.encode(ANSI.BG_RED),
BG_GREEN: encoder.encode(ANSI.BG_GREEN),
BG_YELLOW: encoder.encode(ANSI.BG_YELLOW),
BG_BLUE: encoder.encode(ANSI.BG_BLUE),
BG_MAGENTA: encoder.encode(ANSI.BG_MAGENTA),
BG_CYAN: encoder.encode(ANSI.BG_CYAN),
BG_WHITE: encoder.encode(ANSI.BG_WHITE),
// Cursor Control
SAVE_CURSOR: encoder.encode(ANSI.SAVE_CURSOR),
RESTORE_CURSOR: encoder.encode(ANSI.RESTORE_CURSOR),
CLEAR_SCREEN: encoder.encode(ANSI.CLEAR_SCREEN),
CLEAR_LINE: encoder.encode(ANSI.CLEAR_LINE),
CLEAR_BELOW: encoder.encode(ANSI.CLEAR_BELOW),
};
type AnsiBufferKey = keyof typeof ANSI_BUFFERS;
export async function writeAnsi(
codes: AnsiBufferKey[],
text?: string,
autoReset: boolean = true
): Promise<Result<number, Error>> {
const buffers: Uint8Array[] = [];
// Add requested ANSI codes
for (const code of codes) {
buffers.push(ANSI_BUFFERS[code]);
}
// Add optional text
if (text) {
buffers.push(encoder.encode(text));
}
// Auto-reset if requested
if (autoReset && !codes.includes('RESET')) {
buffers.push(ANSI_BUFFERS.RESET);
}
// Combine all buffers into single write
const combined = new Uint8Array(
buffers.reduce((acc, buf) => acc + buf.length, 0)
);
let offset = 0;
for (const buf of buffers) {
combined.set(buf, offset);
offset += buf.length;
}
return tryCatch(() => Bun.write(Bun.stdout, combined));
}
// For CommonJS compatibility
export default { ANSI, ANSI_BUFFERS, writeAnsi };