---
estimated_steps: 23
estimated_files: 2
skills_used: []
---

# T03: Build text properties panel and convert-to-paths action in ShapeProperties

Add text-specific property controls to ShapeProperties and implement the 'Convert to Paths' action that replaces a TextObject with path-based objects using fontService.textToPathData().

## Text Property Controls (in ShapeProperties)
When the selected object has type 'text', show:
1. **Text content** — textarea input bound to obj.text
2. **Font family** — dropdown/select populated from fontService.getAvailableFonts(), bound to obj.fontFamily
3. **Font size** — number input (min 8, max 200, step 1), bound to obj.fontSize
4. **Letter spacing** — number input (min -5, max 20, step 0.5), bound to obj.letterSpacing
5. **Line height** — number input (min 0.5, max 3, step 0.1), bound to obj.lineHeight
6. **Fill color** — color input (reuse existing pattern from hasFill), bound to obj.fill
7. **Stroke color + weight** — reuse existing stroke controls
8. **Convert to Paths button** — calls fontService.textToPathData(), then replaces the text object with a new path object (type 'image' with SVG data URL, or a new 'path' type if simpler)

## Convert to Paths Implementation
- Show confirmation dialog (window.confirm) since this is destructive
- Call `textToPathData(obj.text, obj.fontFamily, obj.fontSize, obj.letterSpacing)` from fontService
- Create SVG string from returned path data: `<svg xmlns=... viewBox=...><path d="{pathData}" fill="{obj.fill}" stroke="{obj.stroke}" stroke-width="{obj.strokeWidth}"/></svg>`
- Convert SVG to Blob URL, create ImageObject replacing the TextObject at same x,y position
- Use onUpdate to remove old text object and add new image object (or dispatch through a callback prop)

## Integration Notes
- fontService.ts from T01 provides loadFont(), getAvailableFonts(), textToPathData()
- The text property controls should only show when object.type === 'text'
- Existing stroke/fill/opacity/rotation controls still show for text objects
- ShapeProperties receives onUpdate callback from DesignCanvas — convert-to-paths needs onUpdate + onAddObject + onRemoveObject, so ShapeProperties props may need extending, or the convert action can be handled via a new callback prop
- DesignCanvas.tsx needs to pass the additional callback(s) to ShapeProperties

## Inputs

- ``app/src/utils/fontService.ts` — font loading and text-to-path conversion from T01`
- ``app/src/types/canvas.ts` — TextObject type from T02`
- ``app/src/components/canvas/ShapeProperties.tsx` — existing property panel to extend with text controls from T02`
- ``app/src/views/DesignCanvas.tsx` — existing view container that passes props to ShapeProperties`

## Expected Output

- ``app/src/components/canvas/ShapeProperties.tsx` — updated with text property controls (font picker, size, spacing, content) and Convert to Paths button`
- ``app/src/views/DesignCanvas.tsx` — updated to pass additional callbacks to ShapeProperties for convert-to-paths action`

## Verification

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