summaryrefslogtreecommitdiff
path: root/src/utils/grid.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <me@liz.coffee>2025-10-05 16:42:02 -0700
committerElizabeth Hunt <me@liz.coffee>2025-10-05 23:11:41 -0700
commitde43eb05d2e43ab31effce3dcca62ad91a556b26 (patch)
tree47a62b61bfc97dda639dea70627ecf3005ba7b02 /src/utils/grid.ts
parent35add63ec4dce39710095f17abd86777de9e5b49 (diff)
downloadansicolor-de43eb05d2e43ab31effce3dcca62ad91a556b26.tar.gz
ansicolor-de43eb05d2e43ab31effce3dcca62ad91a556b26.zip
Diffstat (limited to 'src/utils/grid.ts')
-rw-r--r--src/utils/grid.ts83
1 files changed, 82 insertions, 1 deletions
diff --git a/src/utils/grid.ts b/src/utils/grid.ts
index 71370da..1a2aa29 100644
--- a/src/utils/grid.ts
+++ b/src/utils/grid.ts
@@ -1,7 +1,88 @@
import type { AnsiTermColor, Grid } from '@/types/grid';
-import { getAnsiColorEscape, getAnsiEscapeCodeFromDiff } from './ansi';
+import { getAnsiColorEscape, getAnsiEscapeCodeFromDiff, parseAnsiColorCode } from './ansi';
const defaultColor: AnsiTermColor = { foreground: null, background: null };
+
+export const gridFromAnsi = (ansiText: string): Grid => {
+ const lines = ansiText.split('\n');
+ const grid: Grid = [];
+
+ for (let y = 0; y < lines.length; y++) {
+ const line = lines[y];
+ const row: Grid[0] = [];
+ let x = 0;
+ let currentColor: AnsiTermColor = { ...defaultColor };
+
+ // Regex to match ANSI escape codes
+ const ansiRegex = /\x1b\[([0-9;]+)m/g;
+ let lastIndex = 0;
+ let match;
+
+ while ((match = ansiRegex.exec(line)) !== null) {
+ // Add characters before this escape code
+ const text = line.slice(lastIndex, match.index);
+ for (const char of text) {
+ row.push({ char, color: { ...currentColor }, x: x++, y });
+ }
+
+ // Parse escape code
+ const codes = match[1].split(';').map(Number);
+ let i = 0;
+ while (i < codes.length) {
+ const code = codes[i];
+
+ if (code === 38 && codes[i + 1] === 5) {
+ // Foreground color: ESC[38;5;{code}m
+ const colorCode = codes[i + 2];
+ currentColor.foreground = parseAnsiColorCode(colorCode);
+ i += 3;
+ } else if (code === 48 && codes[i + 1] === 5) {
+ // Background color: ESC[48;5;{code}m
+ const colorCode = codes[i + 2];
+ currentColor.background = parseAnsiColorCode(colorCode);
+ i += 3;
+ } else if (code === 39) {
+ // Reset foreground
+ currentColor.foreground = null;
+ i++;
+ } else if (code === 49) {
+ // Reset background
+ currentColor.background = null;
+ i++;
+ } else {
+ i++;
+ }
+ }
+
+ lastIndex = ansiRegex.lastIndex;
+ }
+
+ // Add remaining characters
+ const remainingText = line.slice(lastIndex);
+ for (const char of remainingText) {
+ row.push({ char, color: { ...currentColor }, x: x++, y });
+ }
+
+ grid.push(row);
+ }
+
+ // Normalize grid width
+ const maxWidth = Math.max(...grid.map(row => row.length));
+ for (let y = 0; y < grid.length; y++) {
+ while (grid[y].length < maxWidth) {
+ const x = grid[y].length;
+ grid[y].push({
+ char: ' ',
+ color: { ...defaultColor },
+ x,
+ y,
+ });
+ }
+ }
+
+ return grid;
+};
+
export const gridFromAscii = (
ascii: string,
color: AnsiTermColor = defaultColor,