diff --git a/src/game.css b/src/game.css index ceb4988..16965b6 100644 --- a/src/game.css +++ b/src/game.css @@ -255,7 +255,7 @@ } } -/* Cursor highlight */ +/* Cursor highlight - Normal mode */ .sudoku-cell.cursor { background-color: rgba(59, 130, 246, 0.3); outline: 3px solid rgb(59, 130, 246); @@ -264,6 +264,22 @@ position: relative; } +/* Cursor highlight - Edit mode */ +.sudoku-cell.cursor-edit { + background-color: rgba(34, 197, 94, 0.3); + outline: 3px solid rgb(34, 197, 94); + animation: edit-pulse 1.5s ease-in-out infinite; +} + +@keyframes edit-pulse { + 0%, 100% { + outline-color: rgb(34, 197, 94); + } + 50% { + outline-color: rgb(74, 222, 128); + } +} + /* Invalid move flash */ .sudoku-cell.invalid-move { background-color: rgba(239, 68, 68, 0.4); diff --git a/src/useCursor/index.ts b/src/useCursor/index.ts index 71dcc07..9ae9ed5 100644 --- a/src/useCursor/index.ts +++ b/src/useCursor/index.ts @@ -1,2 +1,2 @@ export { useCursor } from './useCursor'; -export type { CursorManager, Coord } from './types'; +export type { CursorManager, Coord, CursorMode } from './types'; diff --git a/src/useCursor/types.ts b/src/useCursor/types.ts index 061c288..96f0c5d 100644 --- a/src/useCursor/types.ts +++ b/src/useCursor/types.ts @@ -1,5 +1,7 @@ export type Coord = { row: number; col: number }; +export type CursorMode = 'normal' | 'edit'; + export type Motion = { dr: number; dc: number; steps: number }; export type VimMotionSystem = { @@ -13,6 +15,7 @@ export type VimMotionSystem = { export type CursorManager = { position: () => Coord; + mode: () => CursorMode; moveLeft: (count?: number) => void; moveRight: (count?: number) => void; moveUp: (count?: number) => void; diff --git a/src/useCursor/useCursor.ts b/src/useCursor/useCursor.ts index d455eff..7a8cd48 100644 --- a/src/useCursor/useCursor.ts +++ b/src/useCursor/useCursor.ts @@ -1,10 +1,11 @@ import { useState, useEffect } from 'react'; -import type { Coord, CursorManager } from './types'; +import type { Coord, CursorManager, CursorMode } from './types'; import type { GameStatusManager } from '../useGameStatus/types'; import { useVimMotions } from './useVimMotions'; export function useCursor(gameStatusManager: GameStatusManager, containerRef: React.RefObject) { const [position, setPosition] = useState({ row: 0, col: 0 }); + const [mode, setMode] = useState('normal'); const { gameStatus } = gameStatusManager; const vimMotions = useVimMotions(); @@ -92,6 +93,19 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re function handleKeyDown(e: KeyboardEvent) { const key = e.key; + + if (mode === 'edit') { + if (key === 'Escape') { + setMode('normal'); + } + return; + } + + if (key === 'i') { + setMode('edit'); + return; + } + const result = vimMotions.processKey(key); if (!result) return; @@ -131,10 +145,11 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, [gameStatus]); + }, [gameStatus, mode]); const cursor: CursorManager = { position: () => position, + mode: () => mode, moveLeft, moveRight, moveUp, diff --git a/src/useCursor/useCursorRender.ts b/src/useCursor/useCursorRender.ts index ebd0f9e..244a9b2 100644 --- a/src/useCursor/useCursorRender.ts +++ b/src/useCursor/useCursorRender.ts @@ -1,11 +1,12 @@ import { useEffect } from 'react'; -import type { Coord } from './types'; +import type { Coord, CursorMode } from './types'; import type { BoardManager } from '../useBoard/types'; import type { GameStatus } from '../useGameStatus/types'; export function useCursorRender( containerRef: BoardManager['containerRef'], position: Coord, + mode: CursorMode, gameStatus: GameStatus, boardRendered: number ) { @@ -18,14 +19,18 @@ export function useCursorRender( const cells = container.querySelectorAll('.sudoku-cell'); cells.forEach(cell => { - cell.classList.remove('cursor'); + cell.classList.remove('cursor', 'cursor-edit'); }); const index = position.row * 9 + position.col; const targetCell = cells[index]; if (targetCell) { - targetCell.classList.add('cursor'); + if (mode === 'edit') { + targetCell.classList.add('cursor', 'cursor-edit'); + } else { + targetCell.classList.add('cursor'); + } } - }, [position, containerRef, gameStatus, boardRendered]); + }, [position, mode, containerRef, gameStatus, boardRendered]); } diff --git a/src/useGame.ts b/src/useGame.ts index 757c063..9f576a7 100644 --- a/src/useGame.ts +++ b/src/useGame.ts @@ -18,8 +18,9 @@ export function useGame(options: GameOptions = {}, platformHook?: unknown): Game const cursor = useCursor(gameStatusManager, containerRef); const cursorPosition = cursor.position(); + const cursorMode = cursor.mode(); - useCursorRender(containerRef, cursorPosition, gameStatus, boardRendered); + useCursorRender(containerRef, cursorPosition, cursorMode, gameStatus, boardRendered); useEffect(() => { renderBoard();