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

7.5 KiB
Raw Permalink Blame 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 activeToolinteraction.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):

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