From de43eb05d2e43ab31effce3dcca62ad91a556b26 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 5 Oct 2025 16:42:02 -0700 Subject: Init --- src/pages/Paint.tsx | 156 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 37 deletions(-) (limited to 'src/pages/Paint.tsx') diff --git a/src/pages/Paint.tsx b/src/pages/Paint.tsx index 136a347..995bcc3 100644 --- a/src/pages/Paint.tsx +++ b/src/pages/Paint.tsx @@ -1,16 +1,34 @@ import { GridComponent } from '@/components/grid/GridComponent'; +import { ColorSwatch } from '@/components/toolbar/ColorSwatch'; +import { Toolbar } from '@/components/toolbar/Toolbar'; +import { ToolbarItem } from '@/components/toolbar/ToolbarItem'; +import { SaveModal } from '@/components/SaveModal'; import type { AnsiTermColor, Grid, GridCell } from '@/types/grid'; -import { - type IZipper, - ListZipper, -} from '@emprespresso/pengueno'; -import { useCallback, useState } from 'react'; +import { gridToAnsi } from '@/utils/grid'; +import { saveArt } from '@/utils/storage'; +import { type IZipper, ListZipper } from '@emprespresso/pengueno'; +import { useCallback, useEffect, useState } from 'react'; +import { flushSync } from 'react-dom'; export interface ChooseArtProps { grid: Grid; + onGoHome?: () => void; } -export const Paint: React.FC = ({ grid }) => { +// Gruvbox theme colors converted to ANSI RGB values +const defaultColors: AnsiTermColor[] = [ + { foreground: { r: 5, g: 5, b: 5 }, background: null }, // bright7 #ebdbb2 + { foreground: { r: 5, g: 1, b: 1 }, background: null }, // regular1 #cc241d + { foreground: { r: 4, g: 4, b: 1 }, background: null }, // regular2 #98971a + { foreground: { r: 5, g: 4, b: 1 }, background: null }, // regular3 #d79921 + { foreground: { r: 2, g: 3, b: 4 }, background: null }, // regular4 #458588 + { foreground: { r: 4, g: 2, b: 3 }, background: null }, // regular5 #b16286 + { foreground: { r: 3, g: 4, b: 3 }, background: null }, // regular6 #689d6a + { foreground: { r: 4, g: 4, b: 3 }, background: null }, // regular7 #a89984 +]; + +export const Paint: React.FC = ({ grid, onGoHome }) => { + const [showSaveModal, setShowSaveModal] = useState(false); const [selectedColor, setSelectedColor] = useState({ foreground: { r: 5, g: 5, b: 5 }, background: null, @@ -18,49 +36,113 @@ export const Paint: React.FC = ({ grid }) => { const [history, setHistory] = useState>( ListZipper.from([grid]), ); + const [isDragging, setIsDragging] = useState(false); + const [workingGrid, setWorkingGrid] = useState(grid); + + const handleDragStart = useCallback(() => { + setIsDragging(true); + // Don't reset workingGrid here - let it accumulate changes + }, []); + + const handleDragEnd = useCallback(() => { + if (isDragging) { + // Commit the working grid to history as a single atomic operation + setHistory((currentHistory) => { + return currentHistory.prepend(workingGrid).previous().get(); + }); + setIsDragging(false); + } + }, [isDragging, workingGrid]); const cellInteractionCallback = useCallback( (cell: GridCell) => { - setHistory((currentHistory) => { - const currentGrid = currentHistory.read().get(); - const newGrid = currentGrid.map((row) => [...row]); // Deep copy for current state - newGrid[cell.y][cell.x] = { ...cell, color: selectedColor }; - return currentHistory.prepend(newGrid).previous().get(); + flushSync(() => { + setWorkingGrid((currentGrid) => { + const newGrid = currentGrid.map((row) => [...row]); + newGrid[cell.y][cell.x] = { ...cell, color: selectedColor }; + return newGrid; + }); }); }, [selectedColor], ); + const handleSave = (name: string) => { + const currentGrid = history.read().get(); + saveArt(name, currentGrid); + }; + + const currentGrid = workingGrid; + const ansiOutput = gridToAnsi(currentGrid); + return ( -
+
+ {showSaveModal && ( + setShowSaveModal(false)} + /> + )} + + ( + + )} + > + 🎨 + + it.read()) + .present() + } + onClick={() => { + setHistory((history) => { + const newHistory = history.previous().get(); + setWorkingGrid(newHistory.read().get()); + return newHistory; + }); + }} + > + ↷ + + it.read()) + .present() + } + onClick={() => { + setHistory((history) => { + const newHistory = history.next().get(); + setWorkingGrid(newHistory.read().get()); + return newHistory; + }); + }} + > + ↶ + + setShowSaveModal(true)}> + 💾 + + + 🏠 + + - -
); }; -- cgit v1.2.3-70-g09d2