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

154 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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