---
estimated_steps: 19
estimated_files: 6
skills_used: []
---

# T02: Build app shell with view routing, file upload zone, and preset selector

Build the first user-facing components: the app shell with view-state routing (Import → Canvas → Export), a drag-and-drop file upload zone, and a preset selector that fetches presets from the engine and populates parameter defaults.

## Steps

1. Create `app/src/views/ImportConvert.tsx` — the main View 1 container. Layout: left panel (upload + controls) and right panel (preview area, placeholder for now). Use CSS modules or a single `app/src/views/ImportConvert.module.css`.
2. Update `app/src/App.tsx` with view-state routing using React useState:
   - `type ViewState = 'import' | 'canvas' | 'export'`
   - Render `ImportConvert` when state is 'import', placeholder `<div>View 2: Design Canvas</div>` for 'canvas', placeholder for 'export'
   - Pass a callback `onUseThis(svgOutput: string, metadata: TraceMetadata)` that transitions to 'canvas'
3. Create `app/src/components/FileUpload.tsx`:
   - Drag-and-drop zone with `onDragOver`/`onDrop` handlers + hidden file `<input>` fallback
   - Accept: `.png,.jpg,.jpeg,.bmp,.tiff,.webp,.svg` via accept attribute
   - On file selection: store `File` in parent state, show filename + file size, render thumbnail via `URL.createObjectURL()`
   - Detect SVG uploads (check file type or extension) and surface this to parent for mode switching
4. Create `app/src/components/PresetSelector.tsx`:
   - On mount: call `getPresets()` from `app/src/api/engine.ts`
   - Render preset cards — each shows name and description from the preset config
   - Clicking a card selects it (visual highlight) and calls `onPresetSelect(presetName, presetConfig)` callback
   - Default selection: 'sign' preset
5. Create `app/src/App.css` (or global styles) with minimal layout: flexbox two-column layout for View 1, card styles for presets, drop zone styling with dashed border + hover state.
6. Wire components together in `ImportConvert.tsx`: FileUpload at top, PresetSelector below it, both in the left panel. Store selected file and preset in local state.

## Inputs

- `app/src/App.tsx`
- `app/src/main.tsx`
- `app/src/types/engine.ts`
- `app/src/api/engine.ts`

## Expected Output

- `app/src/views/ImportConvert.tsx`
- `app/src/views/ImportConvert.module.css`
- `app/src/components/FileUpload.tsx`
- `app/src/components/PresetSelector.tsx`
- `app/src/App.tsx`
- `app/src/App.css`

## Verification

cd app && npx vitest run --reporter=verbose 2>&1 | tail -20 && npx tsc --noEmit 2>&1 | tail -10
