Add performant ANSI module
This commit is contained in:
parent
e925828328
commit
bcdbf8f7c6
131
src/lib/util/ansi.ts
Normal file
131
src/lib/util/ansi.ts
Normal 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 };
|
||||||
Loading…
Reference in New Issue
Block a user