Workspace has no 'replace image' affordance — once an image is loaded and digitize has run, the user has to start a fresh session to use a different image; no way to swap images in place #229

Closed
opened 2026-10-04 02:13:18 +00:00 by Mark · 1 comment
Owner

Finding

src/components/workspace/useWorkspace.ts's uploadedImage is set by LOAD_DESIGN (loading a saved design) or by the file dropzone handler (loading from a file). Once set, the only way to clear it is to navigate away and come back.

grep -n 'REPLACE_IMAGE\|replaceImage\|clearImage' src/components/workspace/useWorkspace.ts returns 0 hits. There's no action to swap the loaded image for a different one.

A user who:

  • Loads a JPEG
  • Runs auto-digitize
  • Reviews the result and decides "actually, the original PNG was better — let me re-do this with the PNG"
  • Has to navigate Gallery → click the original design → open in Workspace
  • Or: drag the PNG to the dropzone (which might be wired to "add" rather than "replace")

grep -n 'onDrop\|onDragOver' src/components/workspace/CanvasArea.tsx — let me check the dropzone behavior.

Where

  • uploadedImage state: src/components/workspace/useWorkspace.ts (set once)
  • No REPLACE_IMAGE: grep -n 'REPLACE_IMAGE' src/components/workspace/useWorkspace.ts returns 0
  • Dropzone: src/components/workspace/CanvasArea.tsx (need to verify if it adds or replaces)
  • Toolbar upload: src/components/workspace/AppHeader.tsx (the upload button)

Suggested fix shape

Add a REPLACE_IMAGE action to the workspace reducer:

case 'REPLACE_IMAGE': {
  return {
    ...state,
    uploadedImage: action.image,
    stitchObjects: [],
    colorRegions: [],
    threadStops: [],
    // Reset related state — replacing the image invalidates the
    // auto-digitize output.
  };
}

Plumb the action to the file dropzone and the toolbar's upload button:

  1. File dropzone (the canvas): the dropzone currently calls LOAD_DESIGN (for .pes/.dst files) and probably has a separate path for image files. The image-file path should dispatch REPLACE_IMAGE instead of "add image."
  2. Toolbar upload button: the button has the same flow.
  3. Confirmation modal when there's an existing image AND the result of auto-digitize: "Replacing this image will clear the current stitch objects. Continue?"

The confirmation prevents accidental loss of the user's digitize work.

Severity

Medium. The user can navigate Gallery → Workspace to swap; the in-place replace is a polish improvement.

Discovered by

Gap analysis (2026-10-02) of workspace image affordances.

  • #186 (closed) — Workspace clipboard paste. Same image-input family.
  • The Gallery's "Open in Workspace" action loads a design (with image + geometry); this issue is about image-only replace.
## Finding `src/components/workspace/useWorkspace.ts`'s `uploadedImage` is set by `LOAD_DESIGN` (loading a saved design) or by the file dropzone handler (loading from a file). Once set, the only way to clear it is to navigate away and come back. `grep -n 'REPLACE_IMAGE\|replaceImage\|clearImage' src/components/workspace/useWorkspace.ts` returns 0 hits. There's no action to swap the loaded image for a different one. A user who: - Loads a JPEG - Runs auto-digitize - Reviews the result and decides "actually, the original PNG was better — let me re-do this with the PNG" - Has to navigate Gallery → click the original design → open in Workspace - Or: drag the PNG to the dropzone (which might be wired to "add" rather than "replace") `grep -n 'onDrop\|onDragOver' src/components/workspace/CanvasArea.tsx` — let me check the dropzone behavior. ## Where - uploadedImage state: `src/components/workspace/useWorkspace.ts` (set once) - No REPLACE_IMAGE: `grep -n 'REPLACE_IMAGE' src/components/workspace/useWorkspace.ts` returns 0 - Dropzone: `src/components/workspace/CanvasArea.tsx` (need to verify if it adds or replaces) - Toolbar upload: `src/components/workspace/AppHeader.tsx` (the upload button) ## Suggested fix shape Add a `REPLACE_IMAGE` action to the workspace reducer: ```ts case 'REPLACE_IMAGE': { return { ...state, uploadedImage: action.image, stitchObjects: [], colorRegions: [], threadStops: [], // Reset related state — replacing the image invalidates the // auto-digitize output. }; } ``` Plumb the action to the file dropzone and the toolbar's upload button: 1. **File dropzone** (the canvas): the dropzone currently calls `LOAD_DESIGN` (for `.pes`/`.dst` files) and probably has a separate path for image files. The image-file path should dispatch `REPLACE_IMAGE` instead of "add image." 2. **Toolbar upload button**: the button has the same flow. 3. **Confirmation modal** when there's an existing image AND the result of auto-digitize: "Replacing this image will clear the current stitch objects. Continue?" The confirmation prevents accidental loss of the user's digitize work. ## Severity Medium. The user can navigate Gallery → Workspace to swap; the in-place replace is a polish improvement. ## Discovered by Gap analysis (2026-10-02) of workspace image affordances. ## Related - #186 (closed) — Workspace clipboard paste. Same image-input family. - The Gallery's "Open in Workspace" action loads a design (with image + geometry); this issue is about image-only replace.
Author
Owner

Shipped in commit cb25f59 on main.

What landed

  • New REPLACE_IMAGE reducer action: takes a fresh UploadedImage, resets every artifact derived from the previous image (stitchObjects / colorRegions / threadStops / threadMatches / selectedIds / processingStep / loadedDesignId), preserves designSettings (hoop / fabric / stabilizer aren't image-specific), wrapped in pushHistory so undo round-trips the swap.
  • New useWorkspace.handleReplaceImage(file): same MIME/size validation + filename derivation as handleFileUpload, but dispatches REPLACE_IMAGE on success. Prompts via window.confirm when there's derived work (auto-digitize output or drawn regions), matching the editor's #184/#228 unsaved-changes guard pattern.
  • ImageEditToolbar: new Replace button (refresh icon, sits left of the ×). Renders only when onReplace is wired.
  • CanvasArea: corner "Replace image" button alongside "Adjust Settings" so the affordance is reachable from the digitizing / digitized states too (the image-edit toolbar only renders in image-uploaded). Dedicated hidden file input so the OS picker re-opens for same-file re-picks.

Tests: 19 new cases in src/components/workspace/issue-229.test.tsx covering the reducer semantics, the confirm-prompt branches, the toolbar button, and the CanvasArea wiring. Full repo suite: 1152 passing, 1 skipped (was 1133 → 1152).

Why a confirm prompt and not an undo-only solution: the user can always ⌘Z, but window.confirm makes the consequence of clicking the new affordance visible before the stitch work is destroyed — the editor's back-nav and Load Design dropdown already use this same shape.

Shipped in commit cb25f59 on `main`. **What landed** - New `REPLACE_IMAGE` reducer action: takes a fresh `UploadedImage`, resets every artifact derived from the previous image (stitchObjects / colorRegions / threadStops / threadMatches / selectedIds / processingStep / loadedDesignId), preserves designSettings (hoop / fabric / stabilizer aren't image-specific), wrapped in pushHistory so undo round-trips the swap. - New `useWorkspace.handleReplaceImage(file)`: same MIME/size validation + filename derivation as `handleFileUpload`, but dispatches `REPLACE_IMAGE` on success. Prompts via `window.confirm` when there's derived work (auto-digitize output or drawn regions), matching the editor's #184/#228 unsaved-changes guard pattern. - `ImageEditToolbar`: new Replace button (refresh icon, sits left of the ×). Renders only when `onReplace` is wired. - `CanvasArea`: corner "Replace image" button alongside "Adjust Settings" so the affordance is reachable from the `digitizing` / `digitized` states too (the image-edit toolbar only renders in `image-uploaded`). Dedicated hidden file input so the OS picker re-opens for same-file re-picks. **Tests**: 19 new cases in `src/components/workspace/issue-229.test.tsx` covering the reducer semantics, the confirm-prompt branches, the toolbar button, and the CanvasArea wiring. Full repo suite: 1152 passing, 1 skipped (was 1133 → 1152). **Why a confirm prompt and not an undo-only solution**: the user can always ⌘Z, but `window.confirm` makes the consequence of clicking the new affordance visible *before* the stitch work is destroyed — the editor's back-nav and Load Design dropdown already use this same shape.
Mark closed this issue 2026-10-04 06:09:48 +00:00
Sign in to join this conversation.
No description provided.