feat: prepare vim-sudoku for unified API implementation
- Archive previous implementation attempt (src -> src_failed) - Create comprehensive IMPLEMENTATION_PLAN.md with phased approach - Create IMPLEMENTATION.md to track implementation progress - Update example app to match vim-maze patterns: - Simplify UI to desktop-only (no mobile support) - Add score metrics display (time, keystrokes, final score) - Update instructions for sudoku-specific keys (hjkl, i for edit) - Remove unnecessary mobile hooks Phase 5 complete. Ready to begin Phase 10 (Setup & Core Types).
This commit is contained in:
+49
-9
@@ -1,17 +1,57 @@
|
||||
import { useGame } from "../../src/useGame";
|
||||
import { useKeyBindings } from './useKeyBindings';
|
||||
import "../../src/game.css";
|
||||
import { useKeyBindings } from "./useKeyBindings";
|
||||
import '../../src/game.css';
|
||||
|
||||
export default function App() {
|
||||
const { containerRef } = useGame({}, useKeyBindings);
|
||||
function App() {
|
||||
const gameManager = useGame({ difficulty: 'medium' }, useKeyBindings);
|
||||
const { containerRef, gameStatus, scoreManager } = gameManager;
|
||||
const { timeValue, keystrokes, finalScore } = scoreManager;
|
||||
|
||||
const formatTime = (ms: number) => {
|
||||
const seconds = Math.floor(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: 16 }}>
|
||||
<h1>VIMazing Sudoku</h1>
|
||||
<div className="relative w-fit mx-auto">
|
||||
<div ref={containerRef} />
|
||||
<div className="flex flex-col items-center justify-around min-h-[100vh]">
|
||||
<h1 className="text-6xl">VIMazing vim-sudoku example</h1>
|
||||
<div className="mx-auto my-4 w-fit bg-muted rounded-lg p-4">
|
||||
{(gameStatus === "started" || gameStatus === "game-won") && (
|
||||
<div className="flex gap-8 text-sm font-mono items-center">
|
||||
<div>
|
||||
<div className="text-muted-foreground">Time</div>
|
||||
<div className="text-lg font-bold">{formatTime(timeValue)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground">Keystrokes</div>
|
||||
<div className="text-lg font-bold">{keystrokes}</div>
|
||||
</div>
|
||||
{gameStatus === "game-won" && finalScore && (
|
||||
<>
|
||||
<div className="w-px h-12 bg-muted-foreground/20" />
|
||||
<div className="text-center">
|
||||
<div className="text-muted-foreground">Final Score</div>
|
||||
<div className="text-3xl font-bold bg-gradient-to-r from-green-400 to-emerald-500 bg-clip-text text-transparent">{finalScore}</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative mx-auto my-4 w-fit" id="sudoku_container">
|
||||
<div ref={containerRef} className="relative" />
|
||||
</div>
|
||||
<div className="text-center text-2xl text-muted-foreground">
|
||||
{gameStatus === "waiting" && <p>Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to start</p>}
|
||||
{gameStatus === "started" && <p>Use <kbd className="px-2 py-1 bg-muted rounded">hjkl</kbd> to navigate • Press <kbd className="px-2 py-1 bg-muted rounded">i</kbd> to edit • Press <kbd className="px-2 py-1 bg-muted rounded">q</kbd> to quit</p>}
|
||||
{gameStatus === "game-over" && <p>Game Over! Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to restart</p>}
|
||||
{gameStatus === "game-won" && <p>You Won! Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to play again</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import * as React from "react";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean>(
|
||||
typeof window !== "undefined"
|
||||
? window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`).matches
|
||||
: false
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
|
||||
const onChange = (event: MediaQueryListEvent) => {
|
||||
setIsMobile(event.matches);
|
||||
};
|
||||
|
||||
// Set initial value
|
||||
setIsMobile(mql.matches);
|
||||
|
||||
// Add listener
|
||||
mql.addEventListener("change", onChange);
|
||||
|
||||
// Cleanup
|
||||
return () => mql.removeEventListener("change", onChange);
|
||||
}, []);
|
||||
|
||||
return isMobile;
|
||||
}
|
||||
Reference in New Issue
Block a user