1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
import type { AnsiTermColor, AnsiColorVal, AnsiRgb } from '@/types/grid';
import type { NumericRange } from '@/types/num';
import type { CSSProperties } from 'react';
const byteToAnsiValRatio = 255 / 5;
export const ansiRgbToHex = ({ r, g, b }: AnsiRgb): string => {
// Scale ANSI colors (0-5) to RGB range (0-255)
const scaleToRgb = (val: number) => Math.round(val * byteToAnsiValRatio);
const rHex = scaleToRgb(r).toString(16).padStart(2, '0');
const gHex = scaleToRgb(g).toString(16).padStart(2, '0');
const bHex = scaleToRgb(b).toString(16).padStart(2, '0');
return `#${rHex}${gHex}${bHex}`;
};
export const hexToAnsi = (_hex: string): AnsiRgb => {
const hex = _hex.split('#').at(-1)!;
const rgb = [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map(
(val) => parseInt(val, 16) || 0,
);
const [r, g, b] = rgb.map(
(value) =>
Math.floor(
Math.min(value, 255) * (1 / byteToAnsiValRatio),
) as AnsiColorVal,
);
return { r, g, b };
};
export const getAnsiColorEscape = (ansi: AnsiTermColor) => {
const [fg, bg] = [ansi.foreground, ansi.background].map((color, i) => {
if (color === null) return `\x1b[39;49m`;
const { r, g, b } = color;
const id = (16 + 36 * r + 6 * g + b) as NumericRange<16, 231>;
return `\x1b[${3 + i}8;5;${id}m`;
});
return { fg, bg };
};
export const getAnsiEscapeCodeFromDiff = (
currentColor: AnsiTermColor,
newColor: AnsiTermColor,
): string => {
const { fg: currentFg, bg: currentBg } = getAnsiColorEscape(currentColor);
const { fg: newFg, bg: newBg } = getAnsiColorEscape(newColor);
const fg = currentFg === newFg ? '' : newFg;
const bg = currentBg === newBg ? '' : newBg;
return `${fg}${bg}`;
};
export const ansiColorsEqual = (
color1: AnsiTermColor,
color2: AnsiTermColor,
): boolean => {
// eh.
return JSON.stringify(color1) === JSON.stringify(color2);
};
export const getStyleForAnsiColor = (color: AnsiTermColor): CSSProperties => {
const { background, foreground } = color;
return {
backgroundColor: background ? ansiRgbToHex(background) : undefined,
color: foreground ? ansiRgbToHex(foreground) : undefined,
};
};
export const parseAnsiColorCode = (code: number): AnsiRgb | null => {
// Only handle 216 color cube (16-231)
if (code < 16 || code > 231) return null;
const adjusted = code - 16;
const r = Math.floor(adjusted / 36) as AnsiColorVal;
const g = Math.floor((adjusted % 36) / 6) as AnsiColorVal;
const b = (adjusted % 6) as AnsiColorVal;
return { r, g, b };
};
|