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:
+105
-2
@@ -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...*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user