diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2025-10-04 18:36:10 -0700 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2025-10-04 18:36:10 -0700 |
| commit | 35add63ec4dce39710095f17abd86777de9e5b49 (patch) | |
| tree | 1afdf952f310e09a663e85541474efdc95155a73 /src/utils/ansi.ts | |
| parent | 507c972ecafeceaf4f8962ad881f8fb50c9b86c1 (diff) | |
| download | ansicolor-35add63ec4dce39710095f17abd86777de9e5b49.tar.gz ansicolor-35add63ec4dce39710095f17abd86777de9e5b49.zip | |
Working history state
Diffstat (limited to 'src/utils/ansi.ts')
| -rw-r--r-- | src/utils/ansi.ts | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/utils/ansi.ts b/src/utils/ansi.ts new file mode 100644 index 0000000..551a365 --- /dev/null +++ b/src/utils/ansi.ts @@ -0,0 +1,68 @@ +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, + }; +}; |
