---
id: S01
parent: M002
milestone: M002
provides:
  - Typed API client (getPresets, traceImage, simplifyVector) with AbortSignal support
  - ViewState routing in App.tsx — S02 adds 'canvas' view rendering
  - onUseThis callback passes SVG string + TraceMetadata from View 1 to View 2
  - useDebouncedTrace hook pattern reusable for any debounced API interaction
  - TypeScript interfaces for all engine API types (PresetConfig, TraceResponse, TraceMetadata)
requires:
  []
affects:
  - S02
key_files:
  - app/src/api/engine.ts
  - app/src/types/engine.ts
  - app/src/hooks/useDebouncedTrace.ts
  - app/src/views/ImportConvert.tsx
  - app/src/components/FileUpload.tsx
  - app/src/components/PresetSelector.tsx
  - app/src/components/ParameterSliders.tsx
  - app/src/components/SvgPreview.tsx
  - app/src/components/OutputInfoBar.tsx
  - app/src/App.tsx
  - app/vite.config.ts
  - engine/main.py
key_decisions:
  - D005: Vite + React + TS with plain CSS modules, Vitest, raw fetch — minimal dependency surface
  - D006: JSON.stringify params stabilization in useDebouncedTrace to avoid infinite re-render loops
  - CORS allows all origins for dev with comment to restrict in production
  - Use This button always visible but disabled when no SVG — avoids layout shift
patterns_established:
  - P006: JSON.stringify for React hook dependency stabilization of object params
  - P007: Vite dev proxy for engine API — relative URLs, no CORS issues in dev
  - P008: CSS modules for views, global App.css for shared component styles
  - ViewState routing via useState in App.tsx — simple state machine (import → canvas → export)
  - Mode-aware UI: components inspect preset vectorization mode to show/hide relevant controls
observability_surfaces:
  - OutputInfoBar color coding: green=normal, yellow=high node count (>5000), red=open paths (>0)
  - TraceMetadata.warnings array surfaced in OutputInfoBar when present
  - Processing time displayed in stats bar
drill_down_paths:
  - .gsd/milestones/M002/slices/S01/tasks/T01-SUMMARY.md
  - .gsd/milestones/M002/slices/S01/tasks/T02-SUMMARY.md
  - .gsd/milestones/M002/slices/S01/tasks/T03-SUMMARY.md
  - .gsd/milestones/M002/slices/S01/tasks/T04-SUMMARY.md
duration: ""
verification_result: passed
completed_at: 2026-03-26T05:20:14.695Z
blocker_discovered: false
---

# S01: Import & Convert UI (View 1)

**Built the complete Import & Convert view (View 1) — file upload, preset selection, debounced live vectorization preview with parameter sliders, output stats bar, and Use This button to advance to canvas.**

## What Happened

This slice delivered the full View 1 of the Kerf App: a React SPA that integrates with the Engine API for interactive vectorization.

**T01 — Foundation:** Added CORSMiddleware to engine/main.py for browser API access. Scaffolded the app/ directory as a Vite + React + TypeScript project with a dev proxy forwarding /engine/* requests to localhost:8000. Built typed API client (getPresets, traceImage, simplifyVector) with AbortSignal support and error handling. Established the test infrastructure with Vitest + jsdom + testing-library. 9 tests pass.

**T02 — UI Shell:** Created the two-column ImportConvert layout (left: controls, right: preview). Built FileUpload component with drag-and-drop + file input fallback, thumbnail preview via URL.createObjectURL, and SVG file detection for mode switching. Built PresetSelector that fetches presets from the engine API on mount with AbortController cleanup, renders preset cards with selection highlighting, and auto-selects 'sign' as default. Set up ViewState routing in App.tsx (import → canvas → export).

**T03 — Core Interaction Loop (highest risk):** Created the useDebouncedTrace custom hook — the central piece — which debounces API calls on params/preset/file change, aborts in-flight requests via AbortController, and routes SVG uploads to simplifyVector() instead of traceImage(). Solved infinite re-render loops by stabilizing params via JSON.stringify in the useEffect dependency array. Built ParameterSliders with mode-aware controls (potrace shows epsilon/turdsize/alphamax; vtracer shows epsilon/filter_speckle/corner_threshold). Built SvgPreview with responsive SVG rendering (strips width/height, preserves viewBox), loading spinner, and error states. Wired the complete live re-trace loop in ImportConvert. 7 hook tests pass.

**T04 — Polish & Completion:** Created OutputInfoBar with color-coded stats (green normal, yellow >5000 nodes, red open paths), warnings display, and processing time. Wired the Use This button (disabled when no SVG output) that passes SVG + metadata to App.tsx for View 2 consumption. 7 component tests pass.

All 23 tests pass. Zero TypeScript errors.

## Verification

All slice-level verification checks pass:
- `cd app && npx vitest run --reporter=verbose`: 23/23 tests pass across 3 test files (9 API client, 7 OutputInfoBar, 7 useDebouncedTrace)
- `cd app && npx tsc --noEmit`: zero TypeScript errors
- All planned files exist: API client, types, 6 components, 1 custom hook, 2 views, CSS modules, 3 test files
- CORS middleware confirmed in engine/main.py
- Vite dev proxy confirmed in vite.config.ts

## Requirements Advanced

None.

## Requirements Validated

None.

## New Requirements Surfaced

None.

## Requirements Invalidated or Re-scoped

None.

## Deviations

Minor deviations, none plan-breaking:
- T01: Added @types/node and test-setup.ts (not in plan) for proper type resolution and jest-dom matchers
- T03: Use This button was wired in T03 instead of T04 — T04 retained it and added disabled state logic
- T03: Hook architecture changed from useCallback-based trigger to single-useEffect with JSON.stringify params stabilization to avoid infinite re-render loops

## Known Limitations

- CORS allows all origins (appropriate for dev, needs restriction for production)
- SVG preview uses dangerouslySetInnerHTML — acceptable for engine-generated SVG but should not be used for untrusted SVG in production
- No end-to-end integration test with a running engine (unit tests mock fetch)

## Follow-ups

- S02 consumes the SVG string and TraceMetadata passed via onUseThis callback in App.tsx
- Production CORS configuration should be added when deployment is set up (M003)
- Consider adding SVG sanitization if untrusted input becomes possible

## Files Created/Modified

- `engine/main.py` — Added CORSMiddleware allowing all origins for dev
- `app/vite.config.ts` — Vite config with React plugin, dev proxy to engine, Vitest config
- `app/src/types/engine.ts` — TypeScript interfaces for PresetConfig, TraceResponse, TraceMetadata, PresetsResponse
- `app/src/api/engine.ts` — Typed API client: getPresets(), traceImage(), simplifyVector() with AbortSignal
- `app/src/api/__tests__/engine.test.ts` — 9 unit tests for API client covering URL/method/FormData/AbortSignal/errors
- `app/src/hooks/useDebouncedTrace.ts` — Custom hook: debounced trace with AbortController, SVG mode detection, params stabilization
- `app/src/hooks/__tests__/useDebouncedTrace.test.ts` — 7 tests for debounce, abort, SVG routing, error handling, cleanup
- `app/src/views/ImportConvert.tsx` — View 1 container: two-column layout wiring FileUpload, PresetSelector, ParameterSliders, SvgPreview, OutputInfoBar, Use This button
- `app/src/views/ImportConvert.module.css` — CSS module for ImportConvert layout
- `app/src/components/FileUpload.tsx` — Drag-and-drop file upload with thumbnail preview and SVG detection
- `app/src/components/PresetSelector.tsx` — Fetches presets from engine, renders cards with selection state, auto-selects sign
- `app/src/components/ParameterSliders.tsx` — Mode-aware range sliders for vectorization parameters
- `app/src/components/SvgPreview.tsx` — Responsive SVG rendering with loading/error states
- `app/src/components/OutputInfoBar.tsx` — Color-coded stats bar with warnings display
- `app/src/components/__tests__/OutputInfoBar.test.tsx` — 7 tests for color coding logic and edge cases
- `app/src/App.tsx` — ViewState routing, SVG/metadata state for View 2 handoff
- `app/src/App.css` — Global styles for shared components
- `app/src/test-setup.ts` — Vitest setup with jest-dom matchers
