Files
web-cad/phase3-task-spec.md
T

154 lines
7.5 KiB
Markdown
Raw Normal View History

# Phase 3: Drawing Tools — React Wiring & State Management
## Goal
Wire Phase 1 canvas/interaction engines to React state so drawing tools actually create, render, and manage elements. Implement undo/redo, command-line shortcuts, and drawing flow improvements.
## Requirements Covered
- F-CAD-01: Zeichnen von Grundelementen (Linie, Kreis, Bogen, Rechteck, Polygon, Polylinie)
- F-CAD-03: Bemaßung
- F-CAD-05: Command Line (L, C, PL, R, A, T, DIM shortcuts)
- F-CAD-09: Undo/Redo mit History
- F-CAD-11: Text
## Current State
- `src/interaction/index.ts` (591 lines): InteractionEngine with drawing logic for all types, callbacks (onElementCreated, onElementsDeleted, onToolStateChanged, onCursorMoved, onCommandTrigger)
- `src/canvas/RenderEngine.ts` (588 lines): Renders all element types
- `src/components/CanvasArea.tsx` (103 lines): Creates engine instances in useEffect but does NOT wire callbacks to React state
- `src/App.tsx` (223 lines): Has mock state but no element management, no engine ref, no undo/redo
## Tasks
### Task 1: Refactor CanvasArea to expose engine instances
File: `src/components/CanvasArea.tsx`
Current: engines created in useEffect, no external access.
Required:
- Use `useRef` to hold engine instances (InteractionEngine, RenderEngine, ZoomPanController, etc.)
- Accept new props: `onElementCreated`, `onElementsDeleted`, `onCursorMoved`, `onToolStateChanged`, `onToolChange` (tool from App → engine), `elements` (element array → engine.setElements), `snapEnabled`, `orthoEnabled`, `gridEnabled` (from App → engine state)
- In useEffect: create engines, wire all callbacks to props, call `interaction.setCallbacks({...})`
- Add a separate useEffect for `elements` changes → `interaction.setElements(elements)` + `spatialIndex.bulkInsert(elements)` + `renderEngine.render()`
- Add a separate useEffect for `snapEnabled/orthoEnabled/gridEnabled` → update engine state + renderEngine options
- Add a separate useEffect for `activeTool``interaction.setTool(tool)`
- Expose zoomPan via ref callback prop `onZoomPanReady` so App can call zoomFit
- Add `onZoomIn`, `onZoomOut`, `onZoomFit` to actually call zoomPan methods (not just log)
- Add double-click handler: on dblclick, if drawing polyline/polygon → call confirmDraw via interaction
New CanvasAreaProps (add to `src/types/ui.types.ts`):
```typescript
export interface CanvasAreaProps {
cursorPos: CursorPos;
viewMode: ViewMode;
onViewChange: (mode: ViewMode) => void;
gridEnabled: boolean;
orthoEnabled: boolean;
snapEnabled: boolean;
activeTool: string;
elements: CADElement[];
onElementCreated: (el: CADElement) => void;
onElementsDeleted: (ids: string[]) => void;
onCursorMoved: (x: number, y: number) => void;
onToolStateChanged: (state: ToolState) => void;
onToggleGrid: () => void;
onToggleOrtho: () => void;
onToggleSnap: () => void;
onZoomIn: () => void;
onZoomOut: () => void;
onZoomFit: () => void;
}
```
### Task 2: Implement undo/redo stack in App.tsx
File: `src/App.tsx`
Required:
- Add `elements` state: `useState<CADElement[]>([])`
- Add `undoStack` and `redoStack` as `useState<CADElement[][]>`
- `handleElementCreated`: push current elements to undoStack, add new element, clear redoStack
- `handleElementsDeleted`: push current elements to undoStack, remove deleted, clear redoStack
- `handleUndo`: if undoStack not empty, push current to redoStack, pop undoStack → set elements
- `handleRedo`: if redoStack not empty, push current to undoStack, pop redoStack → set elements
- Pass `elements` to CanvasArea
- Pass `onElementCreated`, `onElementsDeleted` to CanvasArea
- Wire `onCursorMoved` to update `cursorPos` state
- Wire `onToolStateChanged` to update any relevant UI state
### Task 3: Wire tool selection from LeftSidebar to CanvasArea
File: `src/App.tsx`
Required:
- `activeTool` state already exists in App.tsx
- Pass `activeTool` to CanvasArea as prop
- CanvasArea useEffect watches `activeTool` → calls `interaction.setTool(tool as ToolType)`
- LeftSidebar `onToolChange` already updates `activeTool` in App
### Task 4: Command line shortcuts (F-CAD-05)
File: `src/App.tsx`
Required:
- In `handleCommand(cmd)`, parse command and map to tool:
- `L` or `LINE` → setTool('line')
- `C` or `CIRCLE` → setTool('circle')
- `A` or `ARC` → setTool('arc')
- `R` or `RECT` or `RECTANGLE` → setTool('rect')
- `PL` or `POLYLINE` → setTool('polyline')
- `POL` or `POLYGON` → setTool('polygon')
- `T` or `TEXT` → setTool('text')
- `DIM` or `DIMENSION` → setTool('dimension')
- `V` or `SELECT` → setTool('select')
- `P` or `PAN` → setTool('pan')
- `M` or `MOVE` → setTool('move')
- `CO` or `COPY` → setTool('copy')
- `RO` or `ROTATE` → setTool('rotate')
- `SC` or `SCALE` → setTool('scale')
- `MI` or `MIRROR` → setTool('mirror')
- `DEL` or `ERASE` → setTool('delete')
- `UNDO` or `U` → handleUndo()
- `REDO` → handleRedo()
- Add command to history with prefix ''
- Add response to history with prefix '·' (e.g. 'Linie-Werkzeug aktiv · Klicken zum Starten')
### Task 5: Drawing flow improvements
File: `src/interaction/index.ts`
Required:
- **Polyline/Polygon**: Add `handleDoubleClick` method — if activeTool is polyline/polygon and phase is drawing, call confirmDraw()
- Wire dblclick event in `attach()`: `this.addListener('dblclick', this.onDoubleClick.bind(this))`
- **Arc**: Improve 3-point arc — first click = center, second click = start point (sets radius + startAngle), third click = end point (sets endAngle). Update handleDrawDown to handle multi-point arc.
- **Text**: After placing text element, trigger an inline edit prompt. For now, use a simple `prompt()` dialog or add `onTextEdit` callback.
### Task 6: Grid/Snap/Ortho toggle wiring
File: `src/components/CanvasArea.tsx`
Required:
- When `gridEnabled` prop changes: `renderEngine.setOptions({ showGrid: gridEnabled })` + re-render
- When `snapEnabled` prop changes: `interaction.state.snapEnabled = snapEnabled` + `renderEngine.setOptions({ showSnapPoints: snapEnabled })` + re-render
- When `orthoEnabled` prop changes: `interaction.state.ortho = orthoEnabled` + `renderEngine.setOptions({ showOrtho: orthoEnabled })` + re-render
## Constraints
- TypeScript functional components, no inline styles (except where mockup uses style)
- No styled-components, no Tailwind
- `npx tsc --noEmit` must pass with 0 errors
- `npx vite build` must succeed
- Minimal changes — extend existing files, don't rewrite
- Keep all existing CSS class names
- Import ToolState type from interaction/index.ts where needed
## File Summary
1. `src/types/ui.types.ts` — update CanvasAreaProps
2. `src/components/CanvasArea.tsx` — expose engines, wire callbacks, add useEffects
3. `src/App.tsx` — element state, undo/redo, command shortcuts, wire all callbacks
4. `src/interaction/index.ts` — double-click handler, arc 3-point, text edit callback
## Test Criteria
1. `npx tsc --noEmit` — 0 errors
2. `npx vite build` — success
3. Drawing a line: select Line tool → click start → move mouse → Enter → element appears
4. Drawing a rect: select Rect tool → click corner → move mouse → Enter → element appears
5. Drawing a circle: select Circle tool → click center → move mouse → Enter → element appears
6. Polyline: select Polyline → click multiple points → double-click → element appears
7. Command line: type 'L' → line tool activates
8. Undo/Redo: draw element → Ctrl+Z → element disappears → Ctrl+Y → element reappears
9. Grid toggle: click grid button → grid appears/disappears
10. Snap toggle: click snap button → snap points appear/disappear on hover