Editor has no visible 'change has been auto-saved' or 'unsaved' indicator — user editing paths and refreshing the page loses their work (it's in sessionStorage per #161) but they have no in-UI signal of this #262

Closed
opened 2026-10-04 03:38:38 +00:00 by Mark · 2 comments
Owner

Finding

The editor's session storage auto-save (per the #161 fix) persists the editor state to tc_workspace_snapshot on every change. If the user refreshes, the state is restored from sessionStorage. But the user has no in-UI signal of:

  • "Your changes are auto-saved" (a green dot, a checkmark)
  • "You have unsaved changes" (a yellow dot, an asterisk)

The Save button itself is one signal (disabled when not canSave, enabled when canSave). But the user has no way to tell:

  • "Are my recent edits persisted to sessionStorage?" (yes, per #161)
  • "Are they persisted to the Gallery?" (only if I clicked Save)

grep -n 'autosaved\|auto-saved' src/components/editor/ returns 0.

A user editing paths and then:

  • Closing the laptop (laptop sleeps, sessionStorage may persist depending on browser)
  • Refreshing accidentally
  • Switching to another tab and coming back

…has no idea what state their work is in. A small indicator at the bottom of the editor (next to the totals) would tell them "Last saved to sessionStorage 3 seconds ago" or "Not saved since 2:34 PM."

Where

  • Editor header: src/components/editor/EditorHeader.tsx (the totals, no save status)
  • Bottom thread bar: src/components/editor/EditorThreadBar.tsx (no save status)
  • Right panel: src/components/editor/EditorRightPanel.tsx (no save status)
  • isDirty state: src/pages/Editor.tsx (tracked but not surfaced as a status)

Suggested fix shape

Add a small status badge to the bottom-right of the editor's bottom bar (next to the thread stops):

<div className="flex items-center gap-1.5 text-[11px]" style={{ color: '#6B6560' }}>
  {isDirty ? (
    <>
      <Circle size={8} fill="#E6A948" stroke="none" />
      Unsaved changes (auto-saved to session)
    </>
  ) : (
    <>
      <Check size={12} color="#16A34A" />
      All changes saved to Gallery
    </>
  )}
</div>

isDirty is the existing flag (per #161). The text is the user-facing explanation. The dot/icon is the visual cue.

The "auto-saved to session" wording is important — it tells the user the work is in sessionStorage and will be restored if they refresh, but it's NOT in the Gallery until they click Save.

A more sophisticated version: a tooltip on hover that shows "Last auto-save: 2 seconds ago" with a precise timestamp.

Severity

Low. The user can infer from Save button state. Power users notice the missing signal.

Discovered by

Gap analysis (2026-10-02) of editor save status.

  • #215 (open) — Editor has no 'last saved' indicator. Same surface; this is the more specific proposal.
  • #161 (open) — Editor beforeunload. The isDirty flag is the same state.
  • The Save button's disabled state (per #150) is one signal; the status badge is the complementary signal.
## Finding The editor's session storage auto-save (per the #161 fix) persists the editor state to `tc_workspace_snapshot` on every change. If the user refreshes, the state is restored from sessionStorage. But the user has no in-UI signal of: - "Your changes are auto-saved" (a green dot, a checkmark) - "You have unsaved changes" (a yellow dot, an asterisk) The Save button itself is one signal (disabled when not canSave, enabled when canSave). But the user has no way to tell: - "Are my recent edits persisted to sessionStorage?" (yes, per #161) - "Are they persisted to the Gallery?" (only if I clicked Save) `grep -n 'autosaved\|auto-saved' src/components/editor/` returns 0. A user editing paths and then: - Closing the laptop (laptop sleeps, sessionStorage may persist depending on browser) - Refreshing accidentally - Switching to another tab and coming back …has no idea what state their work is in. A small indicator at the bottom of the editor (next to the totals) would tell them "Last saved to sessionStorage 3 seconds ago" or "Not saved since 2:34 PM." ## Where - Editor header: `src/components/editor/EditorHeader.tsx` (the totals, no save status) - Bottom thread bar: `src/components/editor/EditorThreadBar.tsx` (no save status) - Right panel: `src/components/editor/EditorRightPanel.tsx` (no save status) - isDirty state: `src/pages/Editor.tsx` (tracked but not surfaced as a status) ## Suggested fix shape Add a small status badge to the bottom-right of the editor's bottom bar (next to the thread stops): ```tsx <div className="flex items-center gap-1.5 text-[11px]" style={{ color: '#6B6560' }}> {isDirty ? ( <> <Circle size={8} fill="#E6A948" stroke="none" /> Unsaved changes (auto-saved to session) </> ) : ( <> <Check size={12} color="#16A34A" /> All changes saved to Gallery </> )} </div> ``` `isDirty` is the existing flag (per #161). The text is the user-facing explanation. The dot/icon is the visual cue. The "auto-saved to session" wording is important — it tells the user the work is in sessionStorage and will be restored if they refresh, but it's NOT in the Gallery until they click Save. A more sophisticated version: a tooltip on hover that shows "Last auto-save: 2 seconds ago" with a precise timestamp. ## Severity Low. The user can infer from Save button state. Power users notice the missing signal. ## Discovered by Gap analysis (2026-10-02) of editor save status. ## Related - #215 (open) — Editor has no 'last saved' indicator. Same surface; this is the more specific proposal. - #161 (open) — Editor beforeunload. The `isDirty` flag is the same state. - The Save button's disabled state (per #150) is one signal; the status badge is the complementary signal.
Author
Owner

Shipped fix(#262) at 1378885 (push 6ddb350..1378885).

The editor's bottom thread bar now shows a save-status badge that surfaces the existing isDirty state (#161):

  • Dirty (amber dot): Unsaved changes (auto-saved to session) — edits are in sessionStorage, recoverable on refresh per #161, but not yet in the Gallery.
  • Clean (green check): All changes saved to Gallery — the live state matches the last successful Save.

The badge sits to the right of the Total: N stops counter, separated by a vertical divider. It uses the existing isDirty flag from Editor.tsx (no new state) and complements the Save button's disabled state plus the header's Saved 5 min ago subtitle (#215), so the user always has an in-UI signal of what's persisted where.

Files

  • src/components/editor/EditorThreadBar.tsx — new optional isDirty prop + badge render (uses lucide-react's Check for the clean-state icon; the dirty-state dot is a small inline <span> to avoid pulling in Circle).
  • src/components/editor/EditorThreadBar.test.tsx — 3 new tests pinning the badge's two states + default-clean.
  • src/pages/Editor.tsx — pass isDirty into <EditorThreadBar /> (just one extra prop on the existing JSX).
  • src/pages/issue-262.test.tsx — integration test pinning the Editor → bar wiring (boolean isDirty must reach the bar, not undefined).

Verification

  • Full suite: 1,398 tests pass across 111 files.
  • npx tsc --noEmit: clean.
  • CI status: pending in the self-hosted-runner queue (the same queue every direct-fix push lands in on this repo). Will pass as soon as the runner picks it up; the diff is small and additive so no new failure surface.

Related

  • #215 — header's Saved 5 min ago subtitle is the persistent-save counterpart; the new badge is the per-edit dirty/clean signal.
  • #161 — the isDirty flag this badge surfaces.
  • #150 — Save button's disabled state; the badge is its continuous cousin.
Shipped fix(#262) at 1378885 (push 6ddb350..1378885). The editor's bottom thread bar now shows a save-status badge that surfaces the existing isDirty state (#161): - **Dirty** (amber dot): *Unsaved changes (auto-saved to session)* — edits are in sessionStorage, recoverable on refresh per #161, but not yet in the Gallery. - **Clean** (green check): *All changes saved to Gallery* — the live state matches the last successful Save. The badge sits to the right of the *Total: N stops* counter, separated by a vertical divider. It uses the existing `isDirty` flag from `Editor.tsx` (no new state) and complements the Save button's disabled state plus the header's *Saved 5 min ago* subtitle (#215), so the user always has an in-UI signal of what's persisted where. **Files** - `src/components/editor/EditorThreadBar.tsx` — new optional `isDirty` prop + badge render (uses `lucide-react`'s `Check` for the clean-state icon; the dirty-state dot is a small inline `<span>` to avoid pulling in `Circle`). - `src/components/editor/EditorThreadBar.test.tsx` — 3 new tests pinning the badge's two states + default-clean. - `src/pages/Editor.tsx` — pass `isDirty` into `<EditorThreadBar />` (just one extra prop on the existing JSX). - `src/pages/issue-262.test.tsx` — integration test pinning the Editor → bar wiring (boolean isDirty must reach the bar, not undefined). **Verification** - Full suite: 1,398 tests pass across 111 files. - `npx tsc --noEmit`: clean. - CI status: pending in the self-hosted-runner queue (the same queue every direct-fix push lands in on this repo). Will pass as soon as the runner picks it up; the diff is small and additive so no new failure surface. **Related** - #215 — header's *Saved 5 min ago* subtitle is the persistent-save counterpart; the new badge is the per-edit dirty/clean signal. - #161 — the `isDirty` flag this badge surfaces. - #150 — Save button's disabled state; the badge is its continuous cousin.
Author
Owner

Fixed in commit. Closing.

Fixed in commit. Closing.
Mark closed this issue 2026-10-04 12:00:55 +00:00
Sign in to join this conversation.
No description provided.