# S02: Design Canvas Core (View 2)

**Goal:** Build the Konva.js-powered 2D design canvas (View 2) with artboard shapes, basic shapes (rect/circle/ellipse/line), imported SVG rendering, selection handles, multi-select, object/layer panel with reorder, alignment tools, and undo/redo.
**Demo:** After this: Create artboards in each shape, add/move/resize shapes, multi-select with alignment, undo/redo through history

## Tasks
- [x] **T01: Built canvas type system, useCanvasState hook with undo/redo, artboard shapes, alignment utils, ArtboardSetup component, and 48 new tests passing** — Establish the foundation that every other task depends on: canvas type definitions (discriminated union for canvas objects, artboard config, canvas state), the central useCanvasState hook with full CRUD + selection + undo/redo, artboard shape utilities, the ArtboardSetup component, and canvas mock setup for Vitest. This is the riskiest piece — the state model shape affects every downstream component.

The useCanvasState hook is a useReducer + useRef pattern: current state in a reducer (triggers re-renders), history stack in a useRef (avoids re-renders on history push). Every mutation dispatches through the reducer which also pushes a history snapshot. Undo/redo pops from history.

The ArtboardSetup component renders a modal overlay on first entering the canvas view with shape picker (rect, square, circle, oval, shield, pennant, custom), width/height number inputs, and inches/mm units toggle. On confirm, it produces an ArtboardConfig that initializes the canvas.

Install konva, react-konva, and jest-canvas-mock as dependencies. Set up canvas mock in test-setup.ts so Konva components don't crash in jsdom.
  - Estimate: 2h
  - Files: app/src/types/canvas.ts, app/src/hooks/useCanvasState.ts, app/src/hooks/__tests__/useCanvasState.test.ts, app/src/utils/artboardShapes.ts, app/src/utils/__tests__/artboardShapes.test.ts, app/src/utils/alignment.ts, app/src/utils/__tests__/alignment.test.ts, app/src/components/canvas/ArtboardSetup.tsx, app/src/test-setup.ts, app/package.json
  - Verify: cd app && npx vitest run --reporter=verbose && npx tsc --noEmit
- [x] **T02: Built KonvaStage with artboard rendering, shape tools, selection handles, rubber-band multi-select, SVG import, and DesignCanvas view container** — Build the core Konva canvas rendering layer and wire it into the app. This task creates the KonvaStage component (Konva Stage + Layer with artboard background, all canvas objects, Transformer for selection handles, rubber-band selection rectangle), the DesignCanvas view container, and shape creation tools.

Key implementation details:
- DesignCanvas is the View 2 container. Layout: top toolbar area, left canvas with KonvaStage, right panel area (wired in T03). Receives svgData and traceMetadata props from App.tsx.
- Wire DesignCanvas into App.tsx replacing the placeholder div. Remove underscore prefixes from _svgResult and _traceMetadata variables.
- KonvaStage renders: (1) artboard background shape using Konva.Rect/Path/Circle based on ArtboardConfig, (2) all canvas objects from useCanvasState mapped to Konva components, (3) Transformer attached to selected nodes, (4) rubber-band selection rectangle for multi-select.
- Imported SVG from View 1: convert SVG string → Blob URL → Image element → Konva.Image. Auto-add to canvas objects on mount if svgData is provided.
- Shape tools: rect, circle, ellipse, line creation. Click canvas in tool mode to place shape at click position with default dimensions. Tool state tracked in DesignCanvas.
- Selection: click shape to select (single), shift-click to add to selection, click empty area to deselect, rubber-band rectangle selects all intersecting shapes.
- Transformer: on onTransformEnd, reset scaleX/scaleY to 1 and multiply into width/height to keep state model clean.
- Line objects support dash arrays for style variants (solid, dashed, dotted).
- Canvas view should fill available width. Override #root width constraint for canvas view or make canvas flex within it.

IMPORTANT tsconfig constraints: noUnusedLocals and noUnusedParameters are strict. All variables must be used or prefixed with underscore.
  - Estimate: 2h30m
  - Files: app/src/views/DesignCanvas.tsx, app/src/views/DesignCanvas.module.css, app/src/components/canvas/KonvaStage.tsx, app/src/App.tsx, app/src/App.css
  - Verify: cd app && npx tsc --noEmit && npx vitest run --reporter=verbose
- [x] **T03: Built four canvas UI panels (ObjectPanel, AlignmentBar, CanvasToolbar, ShapeProperties) with full state wiring into DesignCanvas view** — Build the UI panels that surround the canvas: the right-side ObjectPanel for layer management, the AlignmentBar for spatial arrangement, the CanvasToolbar for tool switching and canvas controls, and the ShapeProperties panel for editing selected objects.

ObjectPanel:
- Lists canvas objects by z-order (top of list = frontmost layer)
- Each row shows: drag handle, object type icon, name (double-click to rename), visibility toggle (eye icon), lock toggle (lock icon)
- Click row to select object on canvas, shift-click for multi-select
- Drag-to-reorder changes z-order in state (which changes render order in KonvaStage)
- Wire to useCanvasState: reorderObject, toggleVisibility, toggleLock, selectObjects

AlignmentBar:
- Appears when 1+ objects selected
- Buttons: align left, align center, align right, align top, align middle, align bottom
- When 2+ selected: distribute horizontally, distribute vertically
- Center on artboard button (works with 1+ selected)
- Consumes alignment utility functions from utils/alignment.ts
- Dispatches batch updateObject calls through useCanvasState

CanvasToolbar:
- Tool buttons: pointer/select (default), rectangle, circle, ellipse, line
- Active tool highlighted with accent color
- Undo/redo buttons (disabled when stack empty) — connected to useCanvasState undo/redo
- Grid toggle button
- Zoom controls (zoom in, zoom out, fit to artboard)

ShapeProperties:
- Shown when exactly 1 shape selected
- Fields: stroke color (color input), stroke weight (number input), fill color (color input with toggle), width/height display
- For line objects: line style dropdown (solid, dashed, dotted)
- Changes dispatch updateObject through useCanvasState

Wire all panels into DesignCanvas.tsx layout slots.
  - Estimate: 2h
  - Files: app/src/components/canvas/ObjectPanel.tsx, app/src/components/canvas/AlignmentBar.tsx, app/src/components/canvas/CanvasToolbar.tsx, app/src/components/canvas/ShapeProperties.tsx, app/src/views/DesignCanvas.tsx, app/src/views/DesignCanvas.module.css, app/src/App.css
  - Verify: cd app && npx tsc --noEmit && npx vitest run --reporter=verbose
- [x] **T04: Add canvas keyboard shortcuts (undo/redo/delete/select-all/deselect) and verify full integration — 71 tests pass, zero TypeScript errors** — Wire keyboard shortcuts for the canvas, perform final integration checks, and ensure all tests pass.

Keyboard shortcuts:
- Ctrl+Z: undo (calls useCanvasState.undo)
- Ctrl+Shift+Z (or Ctrl+Y): redo (calls useCanvasState.redo)
- Delete/Backspace: remove selected objects (calls useCanvasState.removeObject for each selected ID)
- Escape: deselect all (calls useCanvasState.deselectAll)
- Ctrl+A: select all objects

Implement via useEffect with keydown listener on the DesignCanvas container div (or window). Ensure shortcuts only fire when canvas view is active and no text input is focused (check activeElement).

Final integration:
- Verify the full flow: View 1 → Use This → Artboard Setup → Canvas with imported SVG
- Verify undo/redo buttons in toolbar reflect stack state (disabled when empty)
- Verify all panel components render and dispatch correctly
- Run full test suite: all existing 23 tests + all new tests must pass
- Run TypeScript compiler: zero errors
- Ensure no unused imports/variables (strict tsconfig)

This task closes the slice by ensuring everything works together as an integrated whole.
  - Estimate: 1h
  - Files: app/src/views/DesignCanvas.tsx, app/src/App.css
  - Verify: cd app && npx vitest run --reporter=verbose && npx tsc --noEmit
