feat: implement erase functionality (Phase 40.3.5)

- Add x, d, Delete in normal mode to erase user-entered cells
- Add Backspace in edit mode to erase and stay in edit mode
- Add Backspace in normal mode as invalid move (red flash)
- All erase commands respect given cell protection
- Empty cells flash red on erase attempt in normal mode
This commit is contained in:
2025-10-22 20:15:00 +01:00
parent bd3616b472
commit 710ec4b83b
2 changed files with 149 additions and 2 deletions
+105 -2
View File
@@ -982,12 +982,115 @@ Back in normal mode (blue cursor)
- ✅ All validation rules working correctly
### Next Steps
- Phase 40.3.5: Add erase functionality (x, Backspace, Delete)
- Validation and conflict detection
- Win condition checking
---
## Iteration 11: [Pending]
## Iteration 11: Phase 40.3.5 - Erase Functionality
**Date**: 2025-01-22
### Goals
- Implement erase commands to clear user-entered digits
- Different behavior in normal vs edit mode
- Always respect given cell protection
### Tasks Completed
#### 1. Normal Mode Erase (x, d, Delete)
**Keys: `x`, `d`, `Delete`**
- Only work on user-entered cells
- Set cell value to '.' (empty)
- Flash red if cell is given or already empty
**Validation Logic:**
```typescript
if (key === 'x' || key === 'd' || key === 'Delete') {
const isGiven = targetCell.classList.contains('given');
const isUserEntered = targetCell.classList.contains('user-entered');
if (isGiven || !isUserEntered) {
flashInvalidMove();
return;
}
boardManager.setCell(position.row, position.col, '.');
return;
}
```
#### 2. Edit Mode Erase (Backspace)
**Key: `Backspace`**
- Works in edit mode only
- Erases current cell value
- Flash red if cell is given
- Stay in edit mode after erasing
**Implementation:**
```typescript
if (key === 'Backspace') {
const targetCell = cells[index];
if (targetCell && targetCell.classList.contains('given')) {
flashInvalidMove();
return;
}
boardManager.setCell(position.row, position.col, '.');
return;
}
```
#### 3. Normal Mode Backspace Protection
**Key: `Backspace` in normal mode**
- Always triggers red flash (invalid move)
- Prevents accidental cell clearing in normal mode
- User must use x, d, or Delete explicitly
### Erase Command Rules
| Mode | Key | Empty Cell | User-Entered Cell | Given Cell |
|------|-----|------------|-------------------|------------|
| Normal | `x`, `d`, `Delete` | ❌ Red flash | ✅ Erase to '.' | ❌ Red flash |
| Normal | `Backspace` | ❌ Red flash | ❌ Red flash | ❌ Red flash |
| Edit | `Backspace` | ✅ Erase (noop) | ✅ Erase to '.' | ❌ Red flash |
### Technical Details
**Cell State After Erase:**
- Board string updated with '.' at cell index
- Cell loses `.user-entered` class on next render
- Cell becomes empty and eligible for `i` command again
**Flow Example:**
```
User enters '5' in cell with 'i'
↓
Cell has .user-entered class, displays '5'
↓
Press Escape → normal mode
↓
Press 'x' or 'd' or Delete
↓
boardManager.setCell(row, col, '.')
↓
Board re-renders, cell is empty
↓
Cell loses .user-entered, can use 'i' again
```
### Build Status
- ✅ TypeScript compiles cleanly
- ✅ No type errors
- ✅ All erase commands working correctly
### Next Steps
- Validation and conflict detection
- Win condition checking
---
## Iteration 12: [Pending]
*To be filled as implementation progresses...*
+44
View File
@@ -112,6 +112,50 @@ export function useCursor(gameStatusManager: GameStatusManager, containerRef: Re
return;
}
if (key === 'Backspace') {
const container = containerRef.current;
if (!container) return;
const cells = container.querySelectorAll('.sudoku-cell');
const index = position.row * 9 + position.col;
const targetCell = cells[index];
if (targetCell && targetCell.classList.contains('given')) {
flashInvalidMove();
return;
}
boardManager.setCell(position.row, position.col, '.');
return;
}
return;
}
if (key === 'x' || key === 'd' || key === 'Delete') {
const container = containerRef.current;
if (!container) return;
const cells = container.querySelectorAll('.sudoku-cell');
const index = position.row * 9 + position.col;
const targetCell = cells[index];
if (!targetCell) return;
const isGiven = targetCell.classList.contains('given');
const isUserEntered = targetCell.classList.contains('user-entered');
if (isGiven || !isUserEntered) {
flashInvalidMove();
return;
}
boardManager.setCell(position.row, position.col, '.');
return;
}
if (key === 'Backspace') {
flashInvalidMove();
return;
}