---
id: S02
parent: M002
milestone: M002
provides:
  - CanvasObject type system (types/canvas.ts) — discriminated union ready for 'text' type extension in S03
  - useCanvasState hook with full CRUD, selection, undo/redo, reorder, visibility/lock toggle
  - KonvaStage component rendering artboard + all object types + selection/transform
  - DesignCanvas view container wired into App.tsx with View 1 → View 2 data flow
  - Artboard shape utilities (shield, pennant paths, presets, unit conversion)
  - Alignment/distribute utility functions
  - Canvas panel system (ObjectPanel, AlignmentBar, CanvasToolbar, ShapeProperties)
requires:
  []
affects:
  - S03
key_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/components/canvas/KonvaStage.tsx
  - 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.tsx
  - app/src/App.css
key_decisions:
  - D007: Canvas state uses useReducer + useRef pattern — reducer for renders, ref for undo/redo history. Select/deselect excluded from undo stack. History capped at 50.
  - D008: Canvas objects use TypeScript discriminated union on type field (rect|circle|ellipse|line|image) with shared BaseCanvasObject.
  - Artboard centered on stage via computed offsets; object positions stored relative to artboard origin
  - SVG import from View 1 uses Blob URL → Image element → Konva.Image with auto-scale to fit artboard
  - ObjectPanel displays objects in reverse z-order (frontmost at top) matching standard design tool UX
  - Keyboard shortcuts use window-level keydown with activeElement tag guard to skip input/textarea/select
patterns_established:
  - Discriminated union CanvasObject type system — add new object types by extending the union and adding case handling
  - useCanvasState hook as single source of truth for all canvas mutations — all components dispatch through it
  - Panel components receive state + dispatch callbacks from DesignCanvas — no direct hook usage in panels
  - vitest-canvas-mock for Konva/canvas testing in Vitest (not jest-canvas-mock)
observability_surfaces:
  - none
drill_down_paths:
  - .gsd/milestones/M002/slices/S02/tasks/T01-SUMMARY.md
  - .gsd/milestones/M002/slices/S02/tasks/T02-SUMMARY.md
  - .gsd/milestones/M002/slices/S02/tasks/T03-SUMMARY.md
  - .gsd/milestones/M002/slices/S02/tasks/T04-SUMMARY.md
duration: ""
verification_result: passed
completed_at: 2026-03-26T05:44:01.051Z
blocker_discovered: false
---

# S02: Design Canvas Core (View 2)

**Built the Konva.js-powered 2D design canvas with artboard setup, shape tools, selection/transform, layer management, alignment, properties editing, keyboard shortcuts, and undo/redo — 48 new tests (71 total), zero TypeScript errors.**

## What Happened

This slice delivered the complete Design Canvas (View 2) for the Kerf Engine app across four tasks.

**T01 — Foundation:** Established the canvas type system (`types/canvas.ts`) with a discriminated union for five object types (rect, circle, ellipse, line, image) sharing a `BaseCanvasObject`. Built the central `useCanvasState` hook using a useReducer + useRef pattern — reducer drives re-renders for current state, ref holds undo/redo history stacks without triggering renders. Select/deselect are excluded from the undo stack (UI-only concern). History is capped at 50 entries. Created artboard shape utilities (shield/pennant SVG paths, dimension presets, unit conversion), 9 pure alignment/distribute functions, and the `ArtboardSetup` modal component. Installed konva, react-konva, and vitest-canvas-mock. 48 new tests passing.

**T02 — Rendering Layer:** Created `KonvaStage` rendering artboard backgrounds (Rect/Circle/Ellipse/Path based on shape config), mapping all canvas objects to Konva primitives with drag/transform handlers, Transformer synced to selected nodes, and rubber-band multi-select rectangle. Built `DesignCanvas` view container with ResizeObserver-driven sizing, artboard setup flow, and SVG import (Blob URL → Image element → Konva.Image with auto-scale). Wired into `App.tsx` to receive real svgData/traceMetadata props from View 1.

**T03 — Panel System:** Built four panel components: `ObjectPanel` (layer list in reverse z-order with drag reorder, visibility/lock toggles, rename), `AlignmentBar` (6 alignment + 2 distribute + center-on-artboard), `CanvasToolbar` (tool switcher, undo/redo, grid toggle, zoom controls), and `ShapeProperties` (stroke/fill/dimensions/line-style editing with discriminated union narrowing). All wired to useCanvasState in DesignCanvas.

**T04 — Shortcuts & Integration:** Added window-level keyboard shortcuts (Ctrl+Z undo, Ctrl+Shift+Z redo, Delete/Backspace remove, Escape deselect, Ctrl+A select all) with activeElement guard to skip input fields. Verified full integration: View 1 → Use This → Artboard Setup → Canvas with imported SVG. All 71 tests pass, zero TypeScript errors.

## Verification

Ran `cd app && npx vitest run --reporter=verbose` — 71 tests pass across 6 test files, 0 failures (exit 0). Ran `cd app && npx tsc --noEmit` — zero type errors under strict mode with noUnusedLocals/noUnusedParameters (exit 0).

## Requirements Advanced

None.

## Requirements Validated

None.

## New Requirements Surfaced

None.

## Requirements Invalidated or Re-scoped

None.

## Deviations

Used vitest-canvas-mock instead of jest-canvas-mock (plan specified jest-canvas-mock but it crashes in Vitest because it internally calls jest.fn()). This was identified in T01 and recorded as L010 in KNOWLEDGE.md.

## Known Limitations

Canvas components (KonvaStage, DesignCanvas panels) do not have dedicated unit tests — they were verified via TypeScript compilation and manual integration. Konva rendering in jsdom is limited since canvas operations are mocked. Drag-to-reorder in ObjectPanel uses simple index swapping, not full drag-and-drop library.

## Follow-ups

S03 will add 'text' type to the CanvasObject discriminated union and integrate opentype.js for font loading and text-to-path conversion. ShapeProperties panel will need a text-specific section. The canvas object type system (D008) was designed to accommodate this extension.

## Files Created/Modified

- `app/src/types/canvas.ts` — New — discriminated union type system for canvas objects (rect, circle, ellipse, line, image), ArtboardConfig, CanvasState, and CanvasAction types
- `app/src/hooks/useCanvasState.ts` — New — central state management hook with useReducer+useRef for CRUD, selection, undo/redo, reorder, visibility/lock
- `app/src/hooks/__tests__/useCanvasState.test.ts` — New — 20 tests covering all useCanvasState operations including undo/redo edge cases
- `app/src/utils/artboardShapes.ts` — New — artboard shape presets, shield/pennant SVG path generators, toPx/fromPx unit conversion, artboardClipPath
- `app/src/utils/__tests__/artboardShapes.test.ts` — New — 15 tests for artboard presets, path generation, unit conversion, clip paths
- `app/src/utils/alignment.ts` — New — 9 pure alignment/distribute/center functions for canvas objects
- `app/src/utils/__tests__/alignment.test.ts` — New — 13 tests for alignment, distribution, and center-on-artboard
- `app/src/components/canvas/ArtboardSetup.tsx` — New — modal for artboard shape/size/unit selection on entering canvas view
- `app/src/components/canvas/KonvaStage.tsx` — New — Konva Stage+Layer rendering artboard, all object types, Transformer, rubber-band selection
- `app/src/components/canvas/ObjectPanel.tsx` — New — layer management panel with z-order list, reorder, visibility/lock toggles, rename
- `app/src/components/canvas/AlignmentBar.tsx` — New — alignment and distribution toolbar consuming alignment utils
- `app/src/components/canvas/CanvasToolbar.tsx` — New — tool switcher, undo/redo, grid toggle, zoom controls
- `app/src/components/canvas/ShapeProperties.tsx` — New — property editor for selected shape (stroke, fill, dimensions, line style, opacity)
- `app/src/views/DesignCanvas.tsx` — New — View 2 container wiring KonvaStage + all panels to useCanvasState, SVG import, keyboard shortcuts
- `app/src/views/DesignCanvas.module.css` — New — CSS module for DesignCanvas layout
- `app/src/App.tsx` — Modified — wired DesignCanvas with svgData/traceMetadata props, removed underscore prefixes
- `app/src/App.css` — Modified — added comprehensive canvas UI styles (toolbar, panels, artboard setup, shape properties, alignment bar)
- `app/src/test-setup.ts` — Modified — added vitest-canvas-mock import for Konva testing
- `app/package.json` — Modified — added konva, react-konva, vitest-canvas-mock dependencies
