---
estimated_steps: 13
estimated_files: 5
skills_used: []
---

# T02: Add TextObject type and wire text tool into canvas rendering

Extend the CanvasObject discriminated union with a TextObject type. Wire text objects into all canvas components: KonvaStage (render via Konva Text + case in getObjWidth/getObjHeight + text tool creation in handleStageMouseDown), CanvasToolbar (add 'text' to CanvasTool union and TOOLS array), ObjectPanel (add 'text' icon to TYPE_ICONS), and DesignCanvas (pass through). This follows the exact pattern established in S02 for adding object types.

## Key Implementation Details
- TextObject interface: extends BaseCanvasObject with type: 'text', text: string, fontFamily: string, fontSize: number, letterSpacing: number, lineHeight: number, fill: string, stroke: string, strokeWidth: number, width: number (for wrapping)
- CanvasTool union in KonvaStage.tsx must add 'text' — this is imported by CanvasToolbar
- KonvaStage renderObject switch: case 'text' renders <Text> from react-konva with fontFamily, fontSize, fill, stroke, strokeWidth, width, letterSpacing, lineHeight props
- KonvaStage handleStageMouseDown switch: case 'text' creates a TextObject with defaults (text: 'Text', fontFamily: 'Roboto', fontSize: 24, letterSpacing: 0, lineHeight: 1.2)
- KonvaStage getObjWidth for text: use obj.width (wrapping width) or estimate from fontSize * text.length * 0.6
- KonvaStage getObjHeight for text: obj.fontSize * obj.lineHeight
- KonvaStage onTransformEnd for text: scale width, keep fontSize unchanged (text wraps to new width)
- ObjectPanel TYPE_ICONS: text → 'T'
- ShapeProperties getWidth/getHeight: add case 'text' returning obj.width and obj.fontSize * obj.lineHeight
- All switch statements must be exhaustive — TypeScript noFallthroughCasesInSwitch enforces this
- TypeScript strict mode — no unused locals/params, erasableSyntaxOnly, use import type

## Inputs

- ``app/src/types/canvas.ts` — existing CanvasObject discriminated union to extend`
- ``app/src/components/canvas/KonvaStage.tsx` — existing renderObject/getObjWidth/getObjHeight/handleStageMouseDown to extend`
- ``app/src/components/canvas/CanvasToolbar.tsx` — existing TOOLS array and CanvasTool import`
- ``app/src/components/canvas/ObjectPanel.tsx` — existing TYPE_ICONS map`
- ``app/src/components/canvas/ShapeProperties.tsx` — existing getWidth/getHeight helpers`

## Expected Output

- ``app/src/types/canvas.ts` — updated with TextObject interface and 'text' in CanvasObject union`
- ``app/src/components/canvas/KonvaStage.tsx` — updated with text rendering, text tool creation, 'text' in CanvasTool union, text cases in getObjWidth/getObjHeight`
- ``app/src/components/canvas/CanvasToolbar.tsx` — updated with text tool button in TOOLS array`
- ``app/src/components/canvas/ObjectPanel.tsx` — updated with text icon in TYPE_ICONS`
- ``app/src/components/canvas/ShapeProperties.tsx` — updated with text cases in getWidth/getHeight`

## Verification

cd app && npx tsc --noEmit && npx vitest run --reporter=verbose
