From de43eb05d2e43ab31effce3dcca62ad91a556b26 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 5 Oct 2025 16:42:02 -0700 Subject: Init --- src/components/toolbar/ColorSwatch.tsx | 227 +++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 src/components/toolbar/ColorSwatch.tsx (limited to 'src/components/toolbar/ColorSwatch.tsx') diff --git a/src/components/toolbar/ColorSwatch.tsx b/src/components/toolbar/ColorSwatch.tsx new file mode 100644 index 0000000..6f33483 --- /dev/null +++ b/src/components/toolbar/ColorSwatch.tsx @@ -0,0 +1,227 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import type { AnsiTermColor, Grid } from '@/types/grid'; +import { hexToAnsi } from '@/utils/ansi'; +import { gridFromAscii } from '@/utils/grid'; +import { HexColorPicker } from 'react-colorful'; +import { GridComponent } from '../grid/GridComponent'; + +interface AnsiColorSwatchProps { + onSelect: (color: AnsiTermColor) => void; + defaultColors: AnsiTermColor[]; + onClose?: () => void; +} + +const STORAGE_KEY = 'ansicolor-history'; + +export const ColorSwatch: React.FC = ({ + onSelect, + defaultColors, + onClose, +}) => { + const [foregroundColor, setForegroundColor] = useState(null); + const [backgroundColor, setBackgroundColor] = useState(null); + + // Initialize history from localStorage or defaults + const [history, setHistory] = useState(() => { + const savedHistory = localStorage.getItem(STORAGE_KEY); + if (savedHistory) { + try { + return JSON.parse(savedHistory); + } catch (e) { + console.error( + 'Failed to parse color history from localStorage', + e, + ); + } + } + return defaultColors; + }); + useEffect(() => { + localStorage.setItem(STORAGE_KEY, JSON.stringify(history)); + }, [history]); + + const [selectedHistory, setSelectedHistory] = useState(0); + + const handleHistorySelection = (selected: number) => { + const color = history.at(selected); + if (color === undefined) return; + setSelectedHistory(selected); + + // Update the hex color pickers + if (color.foreground) { + const hex = `#${[color.foreground.r, color.foreground.g, color.foreground.b] + .map(c => Math.round(c * 51).toString(16).padStart(2, '0')) + .join('')}`; + setForegroundColor(hex); + } else { + setForegroundColor(null); + } + + if (color.background) { + const hex = `#${[color.background.r, color.background.g, color.background.b] + .map(c => Math.round(c * 51).toString(16).padStart(2, '0')) + .join('')}`; + setBackgroundColor(hex); + } else { + setBackgroundColor(null); + } + + onSelect(color); + }; + + const handleColorSelect = () => { + // Only add to history if it has at least one non-null color + const hasColor = ansiColor.foreground !== null || ansiColor.background !== null; + + if (hasColor) { + // Add to history if not already present + const colorExists = history.some( + (c) => + JSON.stringify(c.foreground) === + JSON.stringify(ansiColor.foreground) && + JSON.stringify(c.background) === + JSON.stringify(ansiColor.background), + ); + if (!colorExists) { + setHistory((prev) => [ansiColor, ...prev]); + } + } + + onSelect(ansiColor); + }; + + const ansiColor = useMemo((): AnsiTermColor => { + const [fg, bg] = [foregroundColor, backgroundColor].map((h) => + h ? hexToAnsi(h) : null, + ); + return { foreground: fg, background: bg }; + }, [foregroundColor, backgroundColor]); + const previewGrid = useMemo( + (): Grid => gridFromAscii(butterfly, ansiColor), + [ansiColor], + ); + + return ( +
+
+
+ {history.map((color, idx) => { + const hasForeground = color.foreground !== null; + const hasBackground = color.background !== null; + + return ( +
handleHistorySelection(idx)} + style={{ + cursor: 'pointer', + border: + selectedHistory === idx + ? '2px solid var(--regular6)' + : '1px solid var(--regular3)', + borderRadius: '3px', + padding: '4px', + width: '28px', + height: '28px', + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontSize: '16px', + lineHeight: 1, + overflow: 'hidden', + }} + > + {hasForeground && hasBackground ? ( + <> +
+
+ + ) : hasForeground ? ( +
+ ) : hasBackground ? ( +
+ ) : ( +
+ )} +
+ ); + })} +
+
+
+
+ +
+ +
+
+ +
+ +
+
+ + +
+ ); +}; + +const butterfly = `| | +| ⠀⠀⠀⠀⊹ | +| ⢶⢻⣑⣒⢤⡀⠀⢄⠀⠀⡠⠀⢀⡤⣆⣊⡿⡷ | +| ⠀⠹⠹⣚⣣⠻⣦⡀⠀⠀⢀⣴⠟⣸⢓⢎⠏⠀ | +| ⠀⠀⢡⣱⣖⣢⡾⢿⣾⣷⡿⢷⣖⣒⣎⡎⠀⠀ | +| ⠀⠀⠀⣠⠓⢬⠅⡺⢻⡟⢗⠨⡥⠚⣄⠀⠀⠀ | +| ⠀⠀⠀⣿⡆⠘⠆⢇⢸⡇⠸⠰⠃⢰⣿⠀⠀⠀ | +| ⠀⠀⠀⠐⡻⣮⣬⠞⠈⠁⠳⣤⣴⢿⠂⠀⠀⠀ | +| ⠀⠀⠀⡜⠀⠁⠉⠀⠀⠀⠀⠈⠈⠀⢣⠀⠀⠀ | +| ⊹ | +| |`; -- cgit v1.2.3-70-g09d2