summaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/ansi.ts12
-rw-r--r--src/utils/grid.ts83
-rw-r--r--src/utils/storage.ts42
3 files changed, 136 insertions, 1 deletions
diff --git a/src/utils/ansi.ts b/src/utils/ansi.ts
index 551a365..e57c075 100644
--- a/src/utils/ansi.ts
+++ b/src/utils/ansi.ts
@@ -66,3 +66,15 @@ export const getStyleForAnsiColor = (color: AnsiTermColor): CSSProperties => {
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 };
+};
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,
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
new file mode 100644
index 0000000..ee862ae
--- /dev/null
+++ b/src/utils/storage.ts
@@ -0,0 +1,42 @@
+import type { Grid } from '@/types/grid';
+
+export interface SavedArt {
+ id: string;
+ name: string;
+ grid: Grid;
+ timestamp: number;
+}
+
+const STORAGE_KEY = 'ansicolor-saved-art';
+const MAX_SAVES = 8;
+
+export const saveArt = (name: string, grid: Grid): void => {
+ const saves = getSavedArt();
+ const newSave: SavedArt = {
+ id: Date.now().toString(),
+ name,
+ grid,
+ timestamp: Date.now(),
+ };
+
+ const updatedSaves = [newSave, ...saves].slice(0, MAX_SAVES);
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedSaves));
+};
+
+export const getSavedArt = (): SavedArt[] => {
+ const saved = localStorage.getItem(STORAGE_KEY);
+ if (!saved) return [];
+
+ try {
+ return JSON.parse(saved);
+ } catch (e) {
+ console.error('Failed to parse saved art', e);
+ return [];
+ }
+};
+
+export const deleteSavedArt = (id: string): void => {
+ const saves = getSavedArt();
+ const filtered = saves.filter(save => save.id !== id);
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(filtered));
+};