---
id: T03
parent: S01
milestone: M002
provides: []
requires: []
affects: []
key_files: ["app/src/hooks/useDebouncedTrace.ts", "app/src/components/ParameterSliders.tsx", "app/src/components/SvgPreview.tsx", "app/src/views/ImportConvert.tsx", "app/src/hooks/__tests__/useDebouncedTrace.test.ts", "app/src/App.css", "app/src/App.tsx"]
key_decisions: ["Stabilize params in hook via JSON.stringify instead of requiring callers to memoize", "Use real timers with short debounce in hook tests instead of fake timers"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "Ran cd app && npx vitest run --reporter=verbose — all 16 tests pass (9 API client + 7 useDebouncedTrace hook). Ran cd app && npx tsc --noEmit — zero TypeScript errors."
completed_at: 2026-03-26T05:15:38.801Z
blocker_discovered: false
---

# T03: Built useDebouncedTrace hook with AbortController cancellation, ParameterSliders with mode-aware controls, SvgPreview with responsive rendering, and wired the complete live re-trace loop in ImportConvert

> Built useDebouncedTrace hook with AbortController cancellation, ParameterSliders with mode-aware controls, SvgPreview with responsive rendering, and wired the complete live re-trace loop in ImportConvert

## What Happened
---
id: T03
parent: S01
milestone: M002
key_files:
  - app/src/hooks/useDebouncedTrace.ts
  - app/src/components/ParameterSliders.tsx
  - app/src/components/SvgPreview.tsx
  - app/src/views/ImportConvert.tsx
  - app/src/hooks/__tests__/useDebouncedTrace.test.ts
  - app/src/App.css
  - app/src/App.tsx
key_decisions:
  - Stabilize params in hook via JSON.stringify instead of requiring callers to memoize
  - Use real timers with short debounce in hook tests instead of fake timers
duration: ""
verification_result: passed
completed_at: 2026-03-26T05:15:38.812Z
blocker_discovered: false
---

# T03: Built useDebouncedTrace hook with AbortController cancellation, ParameterSliders with mode-aware controls, SvgPreview with responsive rendering, and wired the complete live re-trace loop in ImportConvert

**Built useDebouncedTrace hook with AbortController cancellation, ParameterSliders with mode-aware controls, SvgPreview with responsive rendering, and wired the complete live re-trace loop in ImportConvert**

## What Happened

Created the useDebouncedTrace custom hook that encapsulates the core interaction loop: on file/preset/params change, it clears any pending debounce timeout, aborts any in-flight fetch via AbortController, and schedules a new trace request after debounceMs. SVG file uploads route to simplifyVector() instead of traceImage(). Parameters are stabilized internally via JSON.stringify to prevent infinite re-render loops from new object references in React effects. Built ParameterSliders component that dynamically renders labeled range sliders based on the active vectorization mode — potrace shows Detail Level (epsilon), Noise Filter (turdsize), and Smooth Curves (alphamax); vtracer shows Detail Level, Noise Filter (filter_speckle), and Corner Threshold. Built SvgPreview component that renders three states: loading (animated spinner), error (message with retry hint), and ready (responsive SVG via dangerouslySetInnerHTML with width/height attributes stripped for viewBox-based scaling). Wired everything in ImportConvert.tsx with currentParams state, preset-change resets, slider-change updates, and Use This button. Wrote 7 tests for the hook.

## Verification

Ran cd app && npx vitest run --reporter=verbose — all 16 tests pass (9 API client + 7 useDebouncedTrace hook). Ran cd app && npx tsc --noEmit — zero TypeScript errors.

## Verification Evidence

| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `cd app && npx vitest run --reporter=verbose` | 0 | ✅ pass | 1820ms |
| 2 | `cd app && npx tsc --noEmit` | 0 | ✅ pass | 2800ms |


## Deviations

Restructured hook to use single useEffect with JSON.stringify(params) in deps instead of useCallback-based triggerTrace approach — the callback approach caused infinite re-render loops. Added Use This button to ImportConvert in this task rather than T04.

## Known Issues

None.

## Files Created/Modified

- `app/src/hooks/useDebouncedTrace.ts`
- `app/src/components/ParameterSliders.tsx`
- `app/src/components/SvgPreview.tsx`
- `app/src/views/ImportConvert.tsx`
- `app/src/hooks/__tests__/useDebouncedTrace.test.ts`
- `app/src/App.css`
- `app/src/App.tsx`


## Deviations
Restructured hook to use single useEffect with JSON.stringify(params) in deps instead of useCallback-based triggerTrace approach — the callback approach caused infinite re-render loops. Added Use This button to ImportConvert in this task rather than T04.

## Known Issues
None.
