Skip to content

IMPLEMENT: fix visualisation reloading to empty state screen (#233)#241

Merged
DaveHudson merged 6 commits intomainfrom
sandcastle/issue-233-visualisation-reloads-to-empty-state-screen
May 1, 2026
Merged

IMPLEMENT: fix visualisation reloading to empty state screen (#233)#241
DaveHudson merged 6 commits intomainfrom
sandcastle/issue-233-visualisation-reloads-to-empty-state-screen

Conversation

@DaveHudson
Copy link
Copy Markdown
Collaborator

Summary

Closes #233

DaveHudson added 6 commits May 1, 2026 12:03
Root cause: useUndoStore always initialises with an empty schema. When a
dev-server HMR reload or a macOS window-close + dock-reopen creates a fresh
renderer context, any unsaved in-memory schema is lost and App.tsx shows the
EmptyState because schema.types.length === 0.

Fix: add useSessionPersistence hook that debounce-saves unsaved schemas
(filePath === null) to an injectable SessionStorage backend (defaults to
window.localStorage) and restores on mount when the schema is still empty.
Clears the session when a file path is set (file opened/saved) or the schema
is replaced with an empty one (File > New).

Key decisions:
- Storage is injected so tests use a plain Map-backed mock; no globals needed
- Only persists unsaved state — project auto-save and manual save already handle
  files that have a path
- Schema + canvas positions are persisted; chat history is not (chat can be
  rebuilt; schema disappearing is the reported regression)

Files changed:
- apps/desktop/src/renderer/src/hooks/useSessionPersistence.ts (new)
- apps/desktop/src/renderer/src/App.tsx (wire hook after useProjectAutoSave)
- apps/desktop/tests/hooks/useSessionPersistence.test.tsx (8 tests, all green)

Closes #233
…test

- Wrap `setItem` in `flush` with try/catch so QuotaExceededError (private
  browsing, full storage) doesn't produce an unhandled error in a setTimeout
  callback — silent skip is the right behaviour for a best-effort cache
- Remove multi-line JSDoc blocks from hook and test files (coding standards:
  no multi-line comment blocks)
- Add test: 'does not crash when storage write throws (e.g. private browsing
  quota)'; use explicit unmount() to guarantee effect-cleanup order in React 19
  and prevent test-order contamination
The session-persistence fix from #233 restores any unsaved schema from
localStorage on mount, which carries across Electron launches inside a
single Playwright run. The import/export and CRUD specs both expect the
"Load allotment sample" button (which only renders in the empty state),
so the second one to run finds the button missing.

Clear `contexture:session:v1` and reload the page in `beforeAll` so each
spec starts from a clean empty schema.
Two loss vectors made the untitled-session restore flaky in real use:

1. The 300ms debounce dropped the last write if the renderer was killed
   before the timer fired (e.g. stopping the dev server right after a
   chat turn settled). Add a `pagehide` (and `beforeunload`) listener
   that synchronously flushes any pending state — Electron fires these
   on dev-server reload and on window close.

2. Pure layout changes (a node drag with no schema mutation) never
   triggered a write because the only subscription was on the undo
   store. Take `layout` as a reactive prop and schedule a debounced
   flush whenever its identity changes, so dragging a node also lands
   in storage.

API change: `useSessionPersistence` now accepts a `layout: Layout`
value instead of a `getLayout` getter. Two new tests cover the pagehide
flush and the layout-only persistence path.
The previous attempt cleared `localStorage` then called `page.reload()`,
but the reload itself triggers `pagehide` which the new persistence
flush uses to write any non-empty schema back to storage. The next
mount then restores it, so the empty state never shows.

Reset the in-memory schema to empty via the exposed undo store before
clearing the session key. The persistence subscriber sees an empty
schema and removes the key on its own; we still belt-and-brace it.
@DaveHudson DaveHudson merged commit d24a817 into main May 1, 2026
3 checks passed
@DaveHudson DaveHudson deleted the sandcastle/issue-233-visualisation-reloads-to-empty-state-screen branch May 1, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Visualisation reloads to empty state screen

1 participant