31300cc2a2
The previous commit referenced shared/ansi.ts, shared/format.ts, and shared/ctx.ts but those files were filtered by the default-deny .gitignore. Adding !/shared/ to the allowlist so the imports actually resolve in a fresh clone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
/**
|
|
* ANSI escape-sequence helpers shared across extensions.
|
|
*
|
|
* Kept free of relative imports so Node's --experimental-strip-types can load
|
|
* this directly in tests.
|
|
*/
|
|
|
|
export const ANSI_RESET_FG = "\x1b[39m";
|
|
export const ANSI_RESET_ALL = "\x1b[0m";
|
|
|
|
/**
|
|
* Convert `#rrggbb` (or `#rgb`) to [r, g, b] bytes. Throws on malformed input
|
|
* rather than silently producing NaN — previously the callers built broken
|
|
* ANSI codes out of `\x1b[38;2;NaN;NaN;NaNm` which some terminals render as
|
|
* bright cyan, giving mysterious color bugs.
|
|
*/
|
|
export function hexToRgb(hex: string): [number, number, number] {
|
|
const m = hex.trim().replace(/^#/, "");
|
|
const expanded =
|
|
m.length === 3 ? m.split("").map((c) => c + c).join("") : m;
|
|
if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {
|
|
throw new Error(`Invalid hex color: ${hex}`);
|
|
}
|
|
const n = parseInt(expanded, 16);
|
|
return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];
|
|
}
|
|
|
|
/** Build a 24-bit foreground ANSI escape for an `#rrggbb` hex color. */
|
|
export function fgFromHex(hex: string): string {
|
|
const [r, g, b] = hexToRgb(hex);
|
|
return `\x1b[38;2;${r};${g};${b}m`;
|
|
}
|
|
|
|
/**
|
|
* Strip every ANSI CSI escape sequence from a string. Useful when the
|
|
* renderer needs to know the visible width of a line that contains color
|
|
* codes.
|
|
*/
|
|
export function stripAnsi(s: string): string {
|
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: CSI stripping is the point
|
|
return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
}
|
|
|
|
/** Count visible grapheme-less characters. Mirrors pi-tui's visibleWidth. */
|
|
export function visibleWidth(s: string): number {
|
|
return stripAnsi(s).length;
|
|
}
|