diff --git a/packages/chess/src/app/App.tsx b/packages/chess/src/app/App.tsx index 862417f..6ee56c1 100644 --- a/packages/chess/src/app/App.tsx +++ b/packages/chess/src/app/App.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { Routes, Route, useNavigate } from 'react-router-dom' import { GameView } from '../ui/GameView' import { RulesView } from '../ui/RulesView' @@ -5,11 +6,23 @@ import { SavePanel } from '../ui/SavePanel' import { ImportExport } from '../ui/ImportExport' import { useChessEngine } from '../hooks/useChessEngine' import { ChessEngine } from '../engine' +import { loadAutoSave } from '../persist/autosave.js' import type { AttrKey, FactValue, EntityId } from '@paratype/rete' export function App() { const chessState = useChessEngine() - + + // Restore autosave on mount — [] deps is intentional (run once, avoid infinite loop) + useEffect(() => { + const saved = loadAutoSave(); + if (!saved) return; + const newEngine = new ChessEngine(); + const oldFacts = newEngine.session.allFacts(); + for (const f of oldFacts) newEngine.session.retract(f.id, f.attr as AttrKey); + for (const f of saved) newEngine.session.insert(f.id as EntityId, f.attr as AttrKey, f.value as FactValue); + chessState.loadEngine(newEngine); + }, []); // intentional: run once on mount, chessState ref is stable + return (
diff --git a/packages/chess/src/hooks/useChessEngine.ts b/packages/chess/src/hooks/useChessEngine.ts index 056f3b1..2e3c4b9 100644 --- a/packages/chess/src/hooks/useChessEngine.ts +++ b/packages/chess/src/hooks/useChessEngine.ts @@ -1,6 +1,7 @@ import { useState, useRef, useCallback } from 'react'; import { ChessEngine, type GameResult } from '../engine'; import type { PieceType } from '../schema'; +import { saveAutoSave } from '../persist/autosave.js'; export function useChessEngine() { const engineRef = useRef(null); @@ -19,6 +20,7 @@ export function useChessEngine() { const loadEngine = useCallback((newEngine: ChessEngine) => { engineRef.current = newEngine; moveHistoryRef.current = []; + saveAutoSave(newEngine.session.allFacts()); setTick(t => t + 1); }, []); @@ -43,6 +45,7 @@ export function useChessEngine() { if (move) { moveHistoryRef.current.push({ from, to, promoteTo }); const result = engine.applyMove(move, promoteTo); + saveAutoSave(engine.session.allFacts()); setTick(t => t + 1); // trigger re-render return result; } @@ -60,6 +63,7 @@ export function useChessEngine() { } } engineRef.current = newEngine; + saveAutoSave(newEngine.session.allFacts()); setTick(t => t + 1); } }, []); diff --git a/packages/chess/src/persist/autosave.ts b/packages/chess/src/persist/autosave.ts new file mode 100644 index 0000000..eaeeb49 --- /dev/null +++ b/packages/chess/src/persist/autosave.ts @@ -0,0 +1,23 @@ +import { exportGame, importGame } from './io.js'; + +const AUTOSAVE_KEY = 'paratype-chess:v1:autosave'; + +export function saveAutoSave(facts: Array<{ id: number; attr: string; value: unknown }>): void { + try { + localStorage.setItem(AUTOSAVE_KEY, exportGame(facts)); + } catch { /* ignore quota errors */ } +} + +export function loadAutoSave(): Array<{ id: number; attr: string; value: unknown }> | null { + try { + const raw = localStorage.getItem(AUTOSAVE_KEY); + if (!raw) return null; + return importGame(raw); + } catch { + return null; // silently discard invalid/stale/version-mismatched data + } +} + +export function clearAutoSave(): void { + localStorage.removeItem(AUTOSAVE_KEY); +} diff --git a/packages/rete/tsconfig.json b/packages/rete/tsconfig.json index dbe5244..c49dd21 100644 --- a/packages/rete/tsconfig.json +++ b/packages/rete/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", + "rootDir": "src", "composite": true }, - "include": ["src/**/*", "tests/**/*"] + "include": ["src/**/*"] }