Editor → Workspace round-trip silently drops user edits: Editor never writes its mutated paths back to sessionStorage.tc_workspace_snapshot or to the Workspace state #110

Closed
opened 2026-09-25 17:50:22 +00:00 by Mark · 1 comment
Owner

Bug: Editor → Workspace round-trip loses user edits — Editor never writes its mutated paths back to sessionStorage

Reproduction

  1. cd ~/Projects/Kimi_Agent_Brother_10_Digitizing_App && npm run dev
  2. Open http://localhost:3000/.
  3. Upload an image in the Workspace, click "Run Auto-Digitize" (requires backend; will fail with "Service unavailable" — for the purposes of this bug, you can simulate the post-digitize state by pasting a snapshot into sessionStorage, see below).
  4. Click "Confirm & Edit" to navigate to /editor.
  5. Editor reads the workspace state from sessionStorage.tc_workspace_snapshot and loads the paths.
  6. Edit the paths: change colors, move nodes, delete a node, whatever.
  7. Click "Back to Workspace" (or any link back to /workspace).
  8. Workspace loads with the original state — none of your Editor edits are visible.
  9. Click "Save to Gallery" — the saved design has the original state, not your edits.

Expected

Either of:

  • Editor writes its paths back to sessionStorage.tc_workspace_snapshot on every change, so returning to Workspace re-hydrates the latest state.
  • Editor exposes a "Save" / "Apply" button that explicitly persists back to the workspace state.
  • Workspace reads from the same sessionStorage key on remount, not from a local React useState.

Actual

  • src/pages/Editor.tsx:29 defines SNAPSHOT_STORAGE_KEY = 'tc_workspace_snapshot'.
  • src/pages/Editor.tsx:32-72 reads from it on mount via loadInitialPaths().
  • src/pages/Editor.tsx never calls sessionStorage.setItem(SNAPSHOT_STORAGE_KEY, ...) anywhere. Verified by grep -nE 'setItem.*SNAPSHOT|setItem.*workspace' src/pages/Editor.tsx src/components/editor/*.tsx — zero hits.
  • src/components/editor/EditorHeader.tsx has "Export PES", "Undo", "Redo", "Settings — coming soon" — no "Save" or "Apply" button.
  • grep -rnE 'addDesign|saveDesigns' src/components/editor/ returns zero hits — Editor never persists to Gallery.
  • Workspace's useWorkspace.ts:764 saveCurrentDesign reads from state.stitchObjects (React useState), not from sessionStorage. So Editor's edits are invisible to the Save flow even if you go back to Workspace first.

Why this matters

The user-story is: upload → digitize → refine in editor → save. The middle step (refine in editor) is silently dropped. A user who spends 10 minutes tweaking stitch colors and node positions in the Editor loses everything when they return to the Workspace to save. The only way to persist Editor edits is to click "Export PES" inside the Editor, which is a separate action that doesn't touch the Workspace's Gallery save flow.

  • #69 (closed): "Editor is initialized with DEMO_PATHS instead of workspace state — no Workspace → Editor bridge". Closed silently. The current code (Editor.tsx:32-72) now reads workspace state via sessionStorage on mount — so half of the bridge exists (read direction).
  • #74 (closed): "No 'Open in Workspace' / load-back action on Gallery designs". The fix added addDesign({ ... snapshot }) so a Gallery save persists the geometry that Workspace can reload. So the Gallery → Workspace and Workspace → Editor bridges exist; only Editor → Workspace (the write-back half) is missing.

Suggested fix scope

Option A (minimum invasive): Editor writes to sessionStorage on every path mutation:

// Editor.tsx
useEffect(() => {
  try {
    sessionStorage.setItem('tc_workspace_snapshot', JSON.stringify({
      stitchObjects: pathsToStitchObjects(paths),
      threadStops: pathsToThreadStops(paths),
      designSettings: { fileName: filename },
    }));
  } catch {}
}, [paths, filename]);

Then in useWorkspace.ts, on mount, hydrate state.stitchObjects from sessionStorage.tc_workspace_snapshot if present (similar to how Gallery's LOAD_DESIGN_STORAGE_KEY is handled in useWorkspace.ts:loadDesignFromSessionStorage).

Option B (preferred): Move paths state to a shared Zustand/context/store that both Editor and Workspace read from. Add a real "Apply" button in the Editor that explicitly writes the merged state back to the workspace store, and a clear UX signal ("Edits applied to workspace at 2:34pm").

Labels

  • bug
  • data-loss
  • usability
  • triage-approved

Severity

High. User-data loss in the primary workflow. A user refining a digitized design in the Editor and clicking "Save to Gallery" loses every edit. This breaks the core "upload → digitize → refine → save → export" user story. Every embroiderer who uses the Editor will hit this.

## Bug: Editor → Workspace round-trip loses user edits — Editor never writes its mutated `paths` back to sessionStorage ### Reproduction 1. `cd ~/Projects/Kimi_Agent_Brother_10_Digitizing_App && npm run dev` 2. Open `http://localhost:3000/`. 3. Upload an image in the Workspace, click "Run Auto-Digitize" (requires backend; will fail with "Service unavailable" — for the purposes of this bug, you can simulate the post-digitize state by pasting a snapshot into sessionStorage, see below). 4. Click "Confirm & Edit" to navigate to `/editor`. 5. Editor reads the workspace state from `sessionStorage.tc_workspace_snapshot` and loads the paths. 6. Edit the paths: change colors, move nodes, delete a node, whatever. 7. Click "Back to Workspace" (or any link back to `/workspace`). 8. Workspace loads with the **original** state — none of your Editor edits are visible. 9. Click "Save to Gallery" — the saved design has the **original** state, not your edits. ### Expected Either of: - Editor writes its `paths` back to `sessionStorage.tc_workspace_snapshot` on every change, so returning to Workspace re-hydrates the latest state. - Editor exposes a "Save" / "Apply" button that explicitly persists back to the workspace state. - Workspace reads from the same sessionStorage key on remount, not from a local React `useState`. ### Actual - `src/pages/Editor.tsx:29` defines `SNAPSHOT_STORAGE_KEY = 'tc_workspace_snapshot'`. - `src/pages/Editor.tsx:32-72` reads from it on mount via `loadInitialPaths()`. - `src/pages/Editor.tsx` never calls `sessionStorage.setItem(SNAPSHOT_STORAGE_KEY, ...)` anywhere. Verified by `grep -nE 'setItem.*SNAPSHOT|setItem.*workspace' src/pages/Editor.tsx src/components/editor/*.tsx` — zero hits. - `src/components/editor/EditorHeader.tsx` has "Export PES", "Undo", "Redo", "Settings — coming soon" — **no "Save" or "Apply" button**. - `grep -rnE 'addDesign|saveDesigns' src/components/editor/` returns **zero hits** — Editor never persists to Gallery. - Workspace's `useWorkspace.ts:764` `saveCurrentDesign` reads from `state.stitchObjects` (React useState), not from sessionStorage. So Editor's edits are invisible to the Save flow even if you go back to Workspace first. ### Why this matters The user-story is: upload → digitize → refine in editor → save. The middle step (refine in editor) is silently dropped. A user who spends 10 minutes tweaking stitch colors and node positions in the Editor loses everything when they return to the Workspace to save. The only way to persist Editor edits is to click "Export PES" inside the Editor, which is a separate action that doesn't touch the Workspace's Gallery save flow. ### Related prior art - **#69** (closed): "Editor is initialized with DEMO_PATHS instead of workspace state — no Workspace → Editor bridge". Closed silently. The current code (Editor.tsx:32-72) now reads workspace state via sessionStorage on mount — so half of the bridge exists (read direction). - **#74** (closed): "No 'Open in Workspace' / load-back action on Gallery designs". The fix added `addDesign({ ... snapshot })` so a Gallery save persists the geometry that Workspace can reload. So the *Gallery → Workspace* and *Workspace → Editor* bridges exist; only **Editor → Workspace** (the write-back half) is missing. ### Suggested fix scope Option A (minimum invasive): Editor writes to sessionStorage on every path mutation: ```ts // Editor.tsx useEffect(() => { try { sessionStorage.setItem('tc_workspace_snapshot', JSON.stringify({ stitchObjects: pathsToStitchObjects(paths), threadStops: pathsToThreadStops(paths), designSettings: { fileName: filename }, })); } catch {} }, [paths, filename]); ``` Then in `useWorkspace.ts`, on mount, hydrate `state.stitchObjects` from `sessionStorage.tc_workspace_snapshot` if present (similar to how Gallery's `LOAD_DESIGN_STORAGE_KEY` is handled in `useWorkspace.ts:loadDesignFromSessionStorage`). Option B (preferred): Move `paths` state to a shared Zustand/context/store that both Editor and Workspace read from. Add a real "Apply" button in the Editor that explicitly writes the merged state back to the workspace store, and a clear UX signal ("Edits applied to workspace at 2:34pm"). ### Labels - bug - data-loss - usability - triage-approved ### Severity **High.** User-data loss in the primary workflow. A user refining a digitized design in the Editor and clicking "Save to Gallery" loses every edit. This breaks the core "upload → digitize → refine → save → export" user story. Every embroiderer who uses the Editor will hit this.
Author
Owner

Fixed in commit ce57a37 on main.

Root cause: Editor.tsx mutated paths in local React state but never wrote back to sessionStorage.tc_workspace_snapshot, so returning to /workspace silently reverted to the pre-editor geometry. Additionally, Workspace.tsx never re-hydrated from that key on mount even if a write existed.

Fix:

  • src/pages/Editor.tsx: added a useEffect that serializes paths + filename into tc_workspace_snapshot on every change. Shape matches commitDigitizedResult's payload.
  • src/components/workspace/useWorkspace.ts: added a mount-time effect that consumes tc_workspace_snapshot if present and dispatches LOAD_DESIGN, mirroring the Gallery → Workspace bridge for tc_load_design_snapshot. Snapshot is not removed on consume so re-entering the Editor still sees the latest state.

Tests (all 367 pass; lint + tsc -b + vite build clean):

  • Editor.test.tsx — drag a node, assert sessionStorage reflects the new coords.
  • useWorkspace.test.ts — pre-seed the snapshot, assert mount hydrates stitchObjects/threadStops/designSettings; corrupt payload leaves state at initial.
  • issue-96.test.ts + repro-64.test.ts — added sessionStorage.clear() to beforeEach; the new mount effect exposed a latent order-dependency.

Repro now passes: editing in /editor → "Back to Workspace" re-hydrates the latest paths; "Save to Gallery" captures the edited state, not the original.

Fixed in commit ce57a37 on main. Root cause: Editor.tsx mutated `paths` in local React state but never wrote back to `sessionStorage.tc_workspace_snapshot`, so returning to /workspace silently reverted to the pre-editor geometry. Additionally, Workspace.tsx never re-hydrated from that key on mount even if a write existed. Fix: - src/pages/Editor.tsx: added a useEffect that serializes `paths` + `filename` into `tc_workspace_snapshot` on every change. Shape matches `commitDigitizedResult`'s payload. - src/components/workspace/useWorkspace.ts: added a mount-time effect that consumes `tc_workspace_snapshot` if present and dispatches LOAD_DESIGN, mirroring the Gallery → Workspace bridge for `tc_load_design_snapshot`. Snapshot is not removed on consume so re-entering the Editor still sees the latest state. Tests (all 367 pass; lint + tsc -b + vite build clean): - Editor.test.tsx — drag a node, assert sessionStorage reflects the new coords. - useWorkspace.test.ts — pre-seed the snapshot, assert mount hydrates stitchObjects/threadStops/designSettings; corrupt payload leaves state at initial. - issue-96.test.ts + repro-64.test.ts — added sessionStorage.clear() to beforeEach; the new mount effect exposed a latent order-dependency. Repro now passes: editing in /editor → "Back to Workspace" re-hydrates the latest paths; "Save to Gallery" captures the edited state, not the original.
Mark closed this issue 2026-09-25 17:58:19 +00:00
Sign in to join this conversation.
No description provided.