# Vim-Maze Architecture Refactoring Plan ## Current Problems Identified ### 1. Dual Position System - `useCursor` maintains logical cursor position - `useHero` maintains actual hero position - They sync but can diverge during animations - Creates confusion about single source of truth ### 2. Mixed Responsibilities - **useCursor**: Input abstraction + maze geometry + event dispatching - **useHero**: Game logic + DOM manipulation + animation + visual feedback - **useHeroRender**: Hero rendering + coordinate system + DOM querying ### 3. Anti-Patterns - Global events for component communication (`maze-invalid`) - Direct DOM manipulation breaking React abstraction - Hard-coded CSS selectors and maze structure assumptions - Debug code left in production (`console.log`) ## Target Architecture ### Clear Separation of Concerns ``` useCursor → Pure input abstraction (Vim motions → coordinates) useHero → Pure game logic (position validation, state transitions) useHeroRender → Pure visual layer (CSS classes, coordinate display) ``` ### Single Source of Truth - Hero position = game state (authoritative) - Cursor position = input intent (derived from hero) - Render position = derived from hero position ### React-First Architecture - State-driven rendering instead of DOM manipulation - Callback props instead of global events - Ref-based access instead of direct queries ## Detailed Refactoring Steps ### Phase 1: Extract Specialized Hooks #### 1.1 Create `useMazeNavigation` **Location:** `src/useBoard/useMazeNavigation.ts` **Responsibilities:** - Wall detection and boundary finding - Path validation for movements - Anchor target detection (line start/end, top/bottom) - Pure functions, no side effects **Interface:** ```tsx type MazeNavigator = { isValidMove: (from: Coord, to: Coord) => boolean; findAnchorTarget: (from: Coord, direction: 'left'|'right'|'top'|'bottom') => Coord | null; validatePath: (from: Coord, direction: Coord, steps: number) => boolean; } ``` #### 1.2 Create `useVimMotions` **Location:** `src/useCursor/useVimMotions.ts` **Responsibilities:** - Parse Vim-style input (hjkl, counts, gg, G, ^, $, .) - Convert motions to coordinate changes - Manage count accumulation and last motion tracking - Pure input parsing, no game logic **Interface:** ```tsx type VimMotionSystem = { processKey: (key: string) => { type: 'move'|'anchor'|'repeat', data: any } | null; resetCount: () => void; getCount: () => number; setLastMotion: (motion: Motion) => void; repeatLastMotion: () => Motion | null; } ``` #### 1.3 Create `useAnimation` **Location:** `src/useCursor/useAnimation.ts` **Responsibilities:** - Smooth interpolated movement for multi-step actions - Animation timing and frame management - Cancellation handling (Escape key) - Trail effects during animation **Interface:** ```tsx type AnimationSystem = { animateMovement: (from: Coord, to: Coord, steps: number, onComplete: () => void) => void; cancelAnimation: () => void; isAnimating: () => boolean; } ``` ### Phase 2: Refactor Core Hooks #### 2.1 Simplify `useCursor` **New Responsibilities:** - Coordinate input from Vim motions - Delegate navigation to `useMazeNavigation` - Delegate animation to `useAnimation` - Maintain cursor state (synced with hero) **New Interface:** ```tsx type CursorManager = { position: () => Coord; mode: () => CursorMode; move: (direction: Coord, count?: number) => boolean; moveToAnchor: (target: 'start'|'end'|'top'|'bottom') => boolean; repeatLastMotion: () => boolean; } ``` #### 2.2 Refactor `useHero` **New Responsibilities:** - Pure game logic and state management - Position validation and game rule enforcement - Game state transitions (key pickup, victory) - No DOM manipulation or visual effects **New Interface:** ```tsx type HeroManager = { position: Coord | null; canMoveTo: (coord: Coord) => boolean; moveTo: (coord: Coord) => void; pickupKey: () => void; reachExit: () => void; reset: () => void; } ``` #### 2.3 Refactor `useHeroRender` **New Responsibilities:** - Pure visual presentation - CSS class management for hero position - Coordinate system display - No game logic or state management **New Interface:** ```tsx type HeroRenderer = { render: () => void; updatePosition: (coord: Coord) => void; showCoordinates: (show: boolean) => void; } ``` ### Phase 3: Fix Communication Patterns #### 3.1 Replace Global Events **Current:** `window.dispatchEvent(new Event("maze-invalid"))` **New:** Callback props and return values ```tsx // Instead of events, use return values const canMove = cursor.move(direction, count); if (!canMove) { // Handle invalid move in caller flashInvalid(); } ``` #### 3.2 Remove Direct DOM Manipulation **Current:** `getCellEl().classList.add("hero")` **New:** State-driven rendering ```tsx // In render component