Editor has no way to import a SVG file as editable paths — user with an SVG file (e.g., a logo) on their disk wants to bring it in as vector paths, can only screenshot and re-digitize via the workspace #283

Closed
opened 2026-10-04 03:43:03 +00:00 by Mark · 1 comment
Owner

Finding

grep -rn 'import.*svg\|svg.*import' src/components/editor/ returns 0. The editor has no SVG import.

A user with an SVG file (e.g., a logo from Adobe Illustrator, an icon from a website, an SVG they made in Inkscape):

  • Wants to bring it into the editor
  • Currently: has to screenshot the SVG, paste/upload to the workspace, run auto-digitize
  • Loses fidelity (the screenshot is raster, the digitize approximates)

A direct SVG import would parse the SVG's <path> elements and create editor paths directly.

Where

  • Editor's file import: grep -rn 'importFile\|importFile' src/components/editor/ returns 0
  • The workspace has image import (PNG/JPG/SVG via loadDesign), but it's not the editor
  • The SVG path data is just a string of M/L/C commands — easy to parse

Suggested fix shape

  1. File input: add an "Import SVG" button to the editor's toolbar (or File menu):
<button onClick={onImportSvg} data-testid="import-svg">
  <FileUp size={14} /> Import SVG
</button>

onImportSvg opens a file picker for .svg files. The user selects one, the file is read as text.

  1. Parse: use a library like svg-path-parser or write a simple parser to extract <path d="..."> elements and convert to PathNode[]:
import { parseSVGPath } from 'svg-path-parser';

const parsed = parseSVGPath(svgPathData);
// parsed is an array of { command, x, y, ... }
const nodes: PathNode[] = parsed
  .filter((cmd) => cmd.command === 'M' || cmd.command === 'L')
  .map((cmd, i) => ({ id: `n${i}`, x: cmd.x, y: cmd.y, type: 'corner' }));
  1. Create a path for each <path> element in the SVG.

  2. Thread colors: try to match each SVG path's fill or stroke to the closest catalog color. The user can adjust.

  3. History: the import is a single undoable action.

Severity

Medium. The user can re-digitize via the workspace, but loses fidelity. Power users with vector source files notice.

Discovered by

Gap analysis (2026-10-02) of editor file import.

  • The workspace's image upload (per #148) handles raster files. The editor's SVG import is the vector version.
  • The reference image backdrop (per #245) is a different surface (visual reference, not editable paths).
  • Inkscape and Illustrator both import SVG natively. The editor's lack of this is a gap.
## Finding `grep -rn 'import.*svg\|svg.*import' src/components/editor/` returns 0. The editor has no SVG import. A user with an SVG file (e.g., a logo from Adobe Illustrator, an icon from a website, an SVG they made in Inkscape): - Wants to bring it into the editor - Currently: has to screenshot the SVG, paste/upload to the workspace, run auto-digitize - Loses fidelity (the screenshot is raster, the digitize approximates) A direct SVG import would parse the SVG's `<path>` elements and create editor paths directly. ## Where - Editor's file import: `grep -rn 'importFile\|importFile' src/components/editor/` returns 0 - The workspace has image import (PNG/JPG/SVG via `loadDesign`), but it's not the editor - The SVG path data is just a string of M/L/C commands — easy to parse ## Suggested fix shape 1. **File input**: add an "Import SVG" button to the editor's toolbar (or File menu): ```tsx <button onClick={onImportSvg} data-testid="import-svg"> <FileUp size={14} /> Import SVG </button> ``` `onImportSvg` opens a file picker for `.svg` files. The user selects one, the file is read as text. 2. **Parse**: use a library like `svg-path-parser` or write a simple parser to extract `<path d="...">` elements and convert to `PathNode[]`: ```ts import { parseSVGPath } from 'svg-path-parser'; const parsed = parseSVGPath(svgPathData); // parsed is an array of { command, x, y, ... } const nodes: PathNode[] = parsed .filter((cmd) => cmd.command === 'M' || cmd.command === 'L') .map((cmd, i) => ({ id: `n${i}`, x: cmd.x, y: cmd.y, type: 'corner' })); ``` 3. **Create a path** for each `<path>` element in the SVG. 4. **Thread colors**: try to match each SVG path's `fill` or `stroke` to the closest catalog color. The user can adjust. 5. **History**: the import is a single undoable action. ## Severity Medium. The user can re-digitize via the workspace, but loses fidelity. Power users with vector source files notice. ## Discovered by Gap analysis (2026-10-02) of editor file import. ## Related - The workspace's image upload (per #148) handles raster files. The editor's SVG import is the vector version. - The reference image backdrop (per #245) is a different surface (visual reference, not editable paths). - Inkscape and Illustrator both import SVG natively. The editor's lack of this is a gap.
Mark closed this issue 2026-10-04 16:34:34 +00:00
Author
Owner

Shipped fix at e321fb9 on origin/main.

Added an 'Import SVG' button to the editor toolbar (FileUp icon, sits next to the reference-image button) that opens a .svg file picker. The handler parses each <path> element into a real StitchPath row — distinct from the existing reference-image picker, which rasterizes an SVG into a backdrop.

Implementation lives in src/components/editor/svgImport.ts:

  • Pure-JS SVG path tokenizer (M/L/H/V/C/S/Q/T/A/Z, absolute + relative, shorthand repetition). Self-contained — no svg-path-parser runtime dep, no reliance on SVGPathElement.getTotalLength (jsdom doesn't implement those).
  • Cubic / quadratic Béziers + elliptical arcs evaluated by hand; straight segments contribute one node per vertex.
  • Cap: 12 samples per curve segment, 200 nodes per path.

Color mapping: closest-by-RGB against the user's current thread catalog. Fill wins over stroke; outline-only art falls back to the catalog's first entry.

History contract: single pushHistory(paths) covers the whole import — one undo undoes the entire file. Selection is promoted to the new paths so the user can immediately re-color / reposition. A editor:fit-all-to-view event is dispatched post-import so a tiny SVG dropped into an empty editor is visible.

Tests: 22 unit tests (svgImport.test.ts) + 7 page-level integration tests (pages/issue-283.test.tsx) covering toolbar wiring, single + multi-path import, malformed / empty / non-SVG error paths, color matching by fill / stroke / fallback, history push, fit-to-view dispatch, and selection promote. All 537 editor tests + the full project build + lint stay green.

Shipped fix at e321fb9 on origin/main. Added an 'Import SVG' button to the editor toolbar (FileUp icon, sits next to the reference-image button) that opens a .svg file picker. The handler parses each `<path>` element into a real `StitchPath` row — distinct from the existing reference-image picker, which rasterizes an SVG into a backdrop. Implementation lives in `src/components/editor/svgImport.ts`: - Pure-JS SVG path tokenizer (M/L/H/V/C/S/Q/T/A/Z, absolute + relative, shorthand repetition). Self-contained — no `svg-path-parser` runtime dep, no reliance on `SVGPathElement.getTotalLength` (jsdom doesn't implement those). - Cubic / quadratic Béziers + elliptical arcs evaluated by hand; straight segments contribute one node per vertex. - Cap: 12 samples per curve segment, 200 nodes per path. Color mapping: closest-by-RGB against the user's current thread catalog. Fill wins over stroke; outline-only art falls back to the catalog's first entry. History contract: single `pushHistory(paths)` covers the whole import — one undo undoes the entire file. Selection is promoted to the new paths so the user can immediately re-color / reposition. A `editor:fit-all-to-view` event is dispatched post-import so a tiny SVG dropped into an empty editor is visible. Tests: 22 unit tests (svgImport.test.ts) + 7 page-level integration tests (pages/issue-283.test.tsx) covering toolbar wiring, single + multi-path import, malformed / empty / non-SVG error paths, color matching by fill / stroke / fallback, history push, fit-to-view dispatch, and selection promote. All 537 editor tests + the full project build + lint stay green.
Sign in to join this conversation.
No description provided.