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 = `| | | ⠀⠀⠀⠀⊹ | | ⢶⢻⣑⣒⢤⡀⠀⢄⠀⠀⡠⠀⢀⡤⣆⣊⡿⡷ | | ⠀⠹⠹⣚⣣⠻⣦⡀⠀⠀⢀⣴⠟⣸⢓⢎⠏⠀ | | ⠀⠀⢡⣱⣖⣢⡾⢿⣾⣷⡿⢷⣖⣒⣎⡎⠀⠀ | | ⠀⠀⠀⣠⠓⢬⠅⡺⢻⡟⢗⠨⡥⠚⣄⠀⠀⠀ | | ⠀⠀⠀⣿⡆⠘⠆⢇⢸⡇⠸⠰⠃⢰⣿⠀⠀⠀ | | ⠀⠀⠀⠐⡻⣮⣬⠞⠈⠁⠳⣤⣴⢿⠂⠀⠀⠀ | | ⠀⠀⠀⡜⠀⠁⠉⠀⠀⠀⠀⠈⠈⠀⢣⠀⠀⠀ | | ⊹ | | |`;