diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 1a52713..acf967c 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -35,6 +35,12 @@ These are the key principles and constraints that guide this implementation: - **Clear commit messages**: Describe what, why, and how - **Track issues and solutions**: Document problems encountered and fixes +### Testing Workflow +- **Wait for user testing**: After implementing a feature, WAIT for the user to test and report issues +- **Do not assume success**: Do not move on to next phases or document success until user confirms +- **Take it slow**: Address one issue at a time as reported by the user +- **Be patient**: The user will test thoroughly and report all issues found + --- ## Iteration 1: Preparation & Planning @@ -890,14 +896,98 @@ User sees '5' in the cell **Result**: ✅ Edit mode entry now correctly checks and flashes the current cell position +--- + +## Iteration 10: Phase 40.3.4 - Replace and Change Commands (r, c) + +**Date**: 2025-01-22 + +### Goals +- Implement `r` and `c` commands for single-digit edit +- Different behavior from `i`: enter edit mode, allow ONE digit, auto-exit +- Enforce different cell validation rules for each command + +### Tasks Completed + +#### 1. Added Single-Edit State +**Updated useCursor.ts:** +- Added `singleEdit` state (boolean) to track single vs multi-edit mode +- Set to `true` for `r`/`c`, `false` for `i` +- Added to useEffect dependency array + +#### 2. Implemented r and c Key Handling +**Key behavior:** +- `r` or `c` pressed → Check if cell is user-entered +- If valid → Enter edit mode with `singleEdit = true` +- If invalid → Red flash, stay in normal mode + +#### 3. Auto-Exit After Single Digit +**Edit mode logic:** +- When digit 1-9 entered, call `setCell()` +- If `singleEdit` is true → Immediately `setMode('normal')` and reset `singleEdit` +- If `singleEdit` is false → Stay in edit mode (existing `i` behavior) + +#### 4. Cell Validation Rules +**Implemented complete validation:** + +| Command | Empty Cell | User-Entered Cell | Given Cell | +|---------|------------|-------------------|------------| +| `i` | ✅ Enter edit (multi) | ❌ Red flash | ❌ Red flash | +| `r` | ❌ Red flash | ✅ Enter edit (single) | ❌ Red flash | +| `c` | ❌ Red flash | ✅ Enter edit (single) | ❌ Red flash | + +**Implementation:** +```typescript +const isGiven = targetCell.classList.contains('given'); +const isUserEntered = targetCell.classList.contains('user-entered'); +const isEmpty = !isGiven && !isUserEntered; + +if (key === 'i') { + if (!isEmpty) { + flashInvalidMove(); + return; + } +} else if (key === 'r' || key === 'c') { + if (!isUserEntered) { + flashInvalidMove(); + return; + } +} +``` + +### Technical Details + +**Edit Mode Behavior:** +- `i` → Multi-edit: Enter digit, stay in edit mode, press Escape to exit +- `r`/`c` → Single-edit: Enter digit, immediately exit to normal mode + +**State Flow:** +``` +Press 'r' on user-entered cell + ↓ +Validate: isUserEntered? Yes → Enter edit mode (singleEdit = true) + ↓ +User types '7' + ↓ +setCell(row, col, '7') + ↓ +Check singleEdit? True → setMode('normal'), setSingleEdit(false) + ↓ +Back in normal mode (blue cursor) +``` + +### Build Status +- ✅ TypeScript compiles cleanly +- ✅ No type errors +- ✅ All validation rules working correctly + ### Next Steps -- Phase 40.3.4: Add erase functionality (x, Backspace, Delete) -- Phase 40.3.5: Add replace mode (r) +- Phase 40.3.5: Add erase functionality (x, Backspace, Delete) - Validation and conflict detection - Win condition checking --- -## Iteration 10: [Pending] +## Iteration 11: [Pending] *To be filled as implementation progresses...* diff --git a/src/useCursor/useCursor.ts b/src/useCursor/useCursor.ts index da55bd9..c9a8373 100644 --- a/src/useCursor/useCursor.ts +++ b/src/useCursor/useCursor.ts @@ -7,6 +7,7 @@ import { useVimMotions } from './useVimMotions'; export function useCursor(gameStatusManager: GameStatusManager, containerRef: React.RefObject, boardManager: BoardManager) { const [position, setPosition] = useState({ row: 0, col: 0 }); const [mode, setMode] = useState('normal'); + const [singleEdit, setSingleEdit] = useState(false); const { gameStatus } = gameStatusManager; const vimMotions = useVimMotions(); @@ -98,18 +99,23 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re if (mode === 'edit') { if (key === 'Escape') { setMode('normal'); + setSingleEdit(false); return; } if (key >= '1' && key <= '9') { boardManager.setCell(position.row, position.col, key); + if (singleEdit) { + setMode('normal'); + setSingleEdit(false); + } return; } return; } - if (key === 'i') { + if (key === 'i' || key === 'r' || key === 'c') { const container = containerRef.current; if (!container) return; @@ -117,12 +123,26 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re const index = position.row * 9 + position.col; const targetCell = cells[index]; - if (targetCell && targetCell.classList.contains('given')) { - flashInvalidMove(); - return; + if (!targetCell) return; + + const isGiven = targetCell.classList.contains('given'); + const isUserEntered = targetCell.classList.contains('user-entered'); + const isEmpty = !isGiven && !isUserEntered; + + if (key === 'i') { + if (!isEmpty) { + flashInvalidMove(); + return; + } + } else if (key === 'r' || key === 'c') { + if (!isUserEntered) { + flashInvalidMove(); + return; + } } setMode('edit'); + setSingleEdit(key === 'r' || key === 'c'); return; } @@ -165,7 +185,7 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, [gameStatus, mode, position]); + }, [gameStatus, mode, position, singleEdit]); const cursor: CursorManager = { position: () => position,