@taml/encoder
Version:
Convert ANSI escape sequences to TAML (Terminal ANSI Markup Language) tags
227 lines (202 loc) • 5.72 kB
text/typescript
import type { BackgroundColor, ColorRGB, StandardColor } from "./types.js";
// RGB values for the 16 standard colors
const STANDARD_COLORS: Record<StandardColor, ColorRGB> = {
black: { r: 0, g: 0, b: 0 },
red: { r: 128, g: 0, b: 0 },
green: { r: 0, g: 128, b: 0 },
yellow: { r: 128, g: 128, b: 0 },
blue: { r: 0, g: 0, b: 128 },
magenta: { r: 128, g: 0, b: 128 },
cyan: { r: 0, g: 128, b: 128 },
white: { r: 192, g: 192, b: 192 },
brightBlack: { r: 128, g: 128, b: 128 },
brightRed: { r: 255, g: 0, b: 0 },
brightGreen: { r: 0, g: 255, b: 0 },
brightYellow: { r: 255, g: 255, b: 0 },
brightBlue: { r: 0, g: 0, b: 255 },
brightMagenta: { r: 255, g: 0, b: 255 },
brightCyan: { r: 0, g: 255, b: 255 },
brightWhite: { r: 255, g: 255, b: 255 },
};
/**
* Convert a 256-color index to RGB values
*/
function convert256ToRGB(colorIndex: number): ColorRGB {
if (colorIndex < 0 || colorIndex > 255) {
throw new Error(
`Invalid color index: ${colorIndex}. Must be between 0-255.`,
);
}
// Standard colors (0-15)
if (colorIndex < 16) {
const standardColorNames: StandardColor[] = [
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"brightBlack",
"brightRed",
"brightGreen",
"brightYellow",
"brightBlue",
"brightMagenta",
"brightCyan",
"brightWhite",
];
const colorName = standardColorNames[colorIndex];
if (!colorName) {
throw new Error(`Invalid standard color index: ${colorIndex}`);
}
return STANDARD_COLORS[colorName];
}
// 216-color cube (16-231)
if (colorIndex >= 16 && colorIndex <= 231) {
const index = colorIndex - 16;
const r = Math.floor(index / 36);
const g = Math.floor((index % 36) / 6);
const b = index % 6;
const toRGB = (value: number) => (value === 0 ? 0 : 55 + value * 40);
return {
r: toRGB(r),
g: toRGB(g),
b: toRGB(b),
};
}
// Grayscale colors (232-255)
if (colorIndex >= 232 && colorIndex <= 255) {
const gray = 8 + (colorIndex - 232) * 10;
return { r: gray, g: gray, b: gray };
}
throw new Error(`Unexpected color index: ${colorIndex}`);
}
/**
* Calculate Euclidean distance between two RGB colors
*/
function calculateColorDistance(color1: ColorRGB, color2: ColorRGB): number {
const rDiff = color1.r - color2.r;
const gDiff = color1.g - color2.g;
const bDiff = color1.b - color2.b;
return Math.sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff);
}
/**
* Find the nearest standard color to the given RGB values
*/
function findNearestStandardColor(rgb: ColorRGB): StandardColor {
let nearestColor: StandardColor = "white";
let minDistance = Number.POSITIVE_INFINITY;
for (const [colorName, colorRGB] of Object.entries(STANDARD_COLORS) as [
StandardColor,
ColorRGB,
][]) {
const distance = calculateColorDistance(rgb, colorRGB);
if (distance < minDistance) {
minDistance = distance;
nearestColor = colorName;
}
}
return nearestColor;
}
/**
* Convert a 256-color index to the nearest standard color
*/
export function convert256ToStandardColor(colorIndex: number): StandardColor {
const rgb = convert256ToRGB(colorIndex);
return findNearestStandardColor(rgb);
}
/**
* Convert RGB values to the nearest standard color
*/
export function convertRGBToStandardColor(
r: number,
g: number,
b: number,
): StandardColor {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
throw new Error(
`Invalid RGB values: (${r}, ${g}, ${b}). Values must be between 0-255.`,
);
}
return findNearestStandardColor({ r, g, b });
}
/**
* Convert basic ANSI color code (30-37, 90-97) to standard color name
*/
export function convertBasicAnsiToStandardColor(
ansiCode: number,
): StandardColor {
const colorMap: Record<number, StandardColor> = {
30: "black",
31: "red",
32: "green",
33: "yellow",
34: "blue",
35: "magenta",
36: "cyan",
37: "white",
90: "brightBlack",
91: "brightRed",
92: "brightGreen",
93: "brightYellow",
94: "brightBlue",
95: "brightMagenta",
96: "brightCyan",
97: "brightWhite",
};
const color = colorMap[ansiCode];
if (!color) {
throw new Error(`Invalid ANSI color code: ${ansiCode}`);
}
return color;
}
/**
* Convert basic ANSI background color code (40-47, 100-107) to background color name
*/
export function convertBasicAnsiToBackgroundColor(
ansiCode: number,
): BackgroundColor {
const colorMap: Record<number, BackgroundColor> = {
40: "bgBlack",
41: "bgRed",
42: "bgGreen",
43: "bgYellow",
44: "bgBlue",
45: "bgMagenta",
46: "bgCyan",
47: "bgWhite",
100: "bgBrightBlack",
101: "bgBrightRed",
102: "bgBrightGreen",
103: "bgBrightYellow",
104: "bgBrightBlue",
105: "bgBrightMagenta",
106: "bgBrightCyan",
107: "bgBrightWhite",
};
const color = colorMap[ansiCode];
if (!color) {
throw new Error(`Invalid ANSI background color code: ${ansiCode}`);
}
return color;
}
/**
* Convert 256-color index to background color name
*/
export function convert256ToBackgroundColor(colorIndex: number): string {
const standardColor = convert256ToStandardColor(colorIndex);
return `bg${standardColor.charAt(0).toUpperCase()}${standardColor.slice(1)}`;
}
/**
* Convert RGB values to background color name
*/
export function convertRGBToBackgroundColor(
r: number,
g: number,
b: number,
): string {
const standardColor = convertRGBToStandardColor(r, g, b);
return `bg${standardColor.charAt(0).toUpperCase()}${standardColor.slice(1)}`;
}