feat: implement edit mode state machine (i and Escape)

Added mode state machine with visual feedback:

Mode State:
- CursorMode type: 'normal' | 'edit'
- Tracked in useCursor with useState
- Starts in 'normal' mode

Key Bindings:
- Press 'i' in normal mode → Enter edit mode
- Press 'Escape' in edit mode → Return to normal mode
- All vim motions (hjkl, anchors, etc.) only work in normal mode
- Edit mode blocks all movement keys

Visual Feedback:
- Normal mode: Blue outline (rgb(59, 130, 246))
- Edit mode: Green pulsing outline (rgb(34, 197, 94))
- Smooth pulse animation (1.5s ease-in-out infinite)
- cursor-edit CSS class applied in edit mode

Flow:
1. Game starts in normal mode (blue cursor)
2. Press 'i' → Green pulsing cursor (edit mode)
3. Press 'Escape' → Blue cursor (normal mode)

No digit entry yet - just state machine and visual indicators.
Phase 40.3.1 complete.
This commit is contained in:
2025-10-22 19:22:15 +01:00
parent 8ac008aeac
commit 0ce1d7fe65
6 changed files with 49 additions and 9 deletions
+17 -2
View File
@@ -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<HTMLDivElement | null>) {
const [position, setPosition] = useState<Coord>({ row: 0, col: 0 });
const [mode, setMode] = useState<CursorMode>('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,