Skip to content

Persist the workbench session across the editor/processor switch - #7654

Open
EthanHealy01 wants to merge 4 commits into
mainfrom
feature/persist-workbench-session
Open

Persist the workbench session across the editor/processor switch#7654
EthanHealy01 wants to merge 4 commits into
mainfrom
feature/persist-workbench-session

Conversation

@EthanHealy01

@EthanHealy01 EthanHealy01 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

What

Switching editor -> processor (or reloading) unmounts every editor provider, which emptied the workbench. This PR mirrors the workbench into per-tab sessionStorage and refills an empty one from that record on the next mount:

  • Files, selection, view and active document survive the shell switch and reloads. Each recorded file is resolved to its current leaf version on restore, so a file versioned by a policy or another tab comes back at its latest state.
  • The switch back lands where the user left: the processor sidebar's "editor" button consumes a one-shot return path saved at switch time.
  • The app switch respects unsaved changes: useOtherAppSwitch (proprietary + saas) now routes through requestNavigation, so the same warning guards it as any other navigation.
  • Desktop shadows WorkbenchSessionPersistence with a stub (OS-launched files own boot there).

How to test

I've run through each of these manually:

  • Upload several PDFs in the editor, select a couple, and switch to the Active Files grid. Click "Open PDF Processor" in the sidebar footer, then switch back to the editor. The same files, selection and view should return, and you should land on the editor page you left.
  • Open a document in the viewer, then reload the tab. The workbench should refill and come back on the viewer with the same document active.
  • With unsaved changes in a tool, click the processor switch. The unsaved-changes warning should appear, and the switch should only proceed if you confirm.
  • Open a second browser tab with different files. Each tab should restore its own workbench independently (the record is per-tab sessionStorage).
  • While in the processor, delete one of the open files from storage, then switch back. The remaining files should restore and a warning toast should report "Restored X of Y files".

Mirror the open files, selection, active document and current view into
per-tab sessionStorage, and refill an empty workbench from that record on
the next mount, so switching to the processor (or reloading) no longer
costs the user their workbench. The processor sidebar's switch back lands
on the editor tool the user left, and the app switch now routes through
the unsaved-changes guard.

Restored files re-enter through the same FileContext entry point My Files
uses, resolved to their current leaf version, so policy auto-run's
persisted dispatch record keeps upload policies (and their billing) from
firing a second time.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@EthanHealy01
EthanHealy01 requested review from a team and balazs-szucs as code owners August 23, 2026 14:17
@dosubot dosubot Bot added size:L This PR changes 100-499 lines ignoring generated files. enhancement New feature or request labels Aug 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌐 TOML Translation Verification Summary

🔄 Reference Branch: pr-branch

📃 File Check: en-US/translation.toml

  1. Test Status:Passed
  2. Test Status:Passed
  3. Test Status:Passed

✅ Overall Check Status: Success

Thanks @EthanHealy01 for your help in keeping the translations up to date.

@github-actions github-actions Bot added Front End Issues or pull requests related to front-end development Translation Issues or pull requests related to translation and removed enhancement New feature or request labels Aug 23, 2026
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

📦 Tauri Desktop Builds Ready!

The desktop applications have been built and are ready for testing.

Download Artifacts:

🍎 macOS Universal: Download Stirling-PDF-macos-universal (.dmg) - 340.4 MB
🪟 Windows x64: Download Stirling-PDF-windows-x86_64 (.exe, .msi) - 264.4 MB


Built from commit 6af209f
Artifacts expire in 7 days

Two fallout fixes from the workbench session restore:

- reopenView wrote the recorded view once and then re-asserted it after the
  files hydrated, which also overwrote a view picked in between. Holding the
  restore guard is what keeps HomePage's defaults off it, so drop the second
  write.
- engine-capabilities reopened the file from the library after a reload, but
  the restore now leaves it already open, and that button toggles an open
  file back out. The viewer already shows it, so assert the tile directly.

The round-trip spec asserted the editor's file list emptied on the switch;
the processor shell does not mount in the default build, so assert the URL
and the recorded session instead, then load the editor cold for the return.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ConnorYoh

Copy link
Copy Markdown
Member

Some edge cases AI picked up on:
Edge cases it doesn't cover

Sign-out doesn't clear the record, so the next user in that tab gets the previous user's workbench (Med). Nothing removes stirling.workbench.session anywhere, and fileStorage.clearAll() exists but isn't called on sign-out either. User A works, signs out, user B signs in in the same tab, and B's editor opens with A's documents loaded. Those files were already reachable via My Files since IndexedDB is per-origin — so it's not a new data path, but it goes from "B could go looking" to "B's workbench opens with them in it", which is a different conversation with a customer. One line to clear the key.

A failed write leaves a stale record rather than none (Low). setItem is atomic, so on quota exhaustion nothing is written and the previous record survives — the next restore rebuilds an older file set. Silently wrong rather than absent. A removeItem in the catch makes the failure mode "no restore" instead of "wrong restore".

The 5-second settle timeout is a guess, and losing it loses the view, not the files (Low). Past SETTLE_TIMEOUT_MS the guard releases and HomePage's default-view heuristic can overwrite the recorded view. On a slow device with large PDFs you get your files back on the wrong screen, which reads as half-broken.

Plus two nits: no schema version (a future shape change would silently restore zero files rather than being discarded), and the "Restored 0 of 5 files. The rest are no longer stored" toast is reachable in the total-loss case, where "the rest" is all of them.

One piece of context that raises the stakes on the first item: WORKBENCH_SESSION_RESTORE is true in both core and proprietary, so this ships enabled rather than dark. The sign-out case is live on day one.

CONNOR COMMENTS:

  • really neat that this works with duplicate tab. Its a bit clunky as it needs to reauth (auto but shows a few intermediary states) but will open the opened files
  • soon we should follow this up with viewer session storage for files (scroll position, rotations applied etc) it could live in a similar schema and has been asked for a lot.

Review feedback on the session persistence PR. A tab outlives a session,
so the record needed an owner and a way to disappear:

- Records carry the signed-in user, and a restore refuses (and clears) one
  left by someone else - a 401 bounce or an expiry drops the next person on
  /login in the same tab, which clearing on the logout button alone misses.
- Sign-out is detected from the identity going away rather than from any
  particular button, because SaaS signs out straight from the settings modal
  and several other paths never reach accountLogout. It suspends recording
  as well as clearing: signing out unmounts the editor, and that teardown
  flush would otherwise write the workbench straight back.
- The editor's providers also mount on /login, so this records and restores
  nothing there instead of rebuilding the workbench behind the login form.
- A failed write clears rather than leaving an older record to be restored.
- The record is versioned, so a shape this build no longer trusts is
  discarded instead of half-read.
- Total loss gets its own message rather than 'Restored 0 of N ... the rest'.
- The view guard is held per hydrated file rather than one flat deadline, so
  a slow device does not get its files back on the wrong screen.
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines ignoring generated files. and removed size:L This PR changes 100-499 lines ignoring generated files. labels Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚀 V2 Auto-Deployment Complete!

🔗 Direct Test URL (non-SSL) http://54.175.155.236:7654

🧩 Admin portal included - try it at http://54.175.155.236:7654/portal.

This deployment will be automatically cleaned up when the PR is closed.

🔄 Auto-deployed for approved V2 contributors.

@EthanHealy01
EthanHealy01 enabled auto-merge August 24, 2026 19:05

@jbrunton96 jbrunton96 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me. Did an AI review of it and it brought up a couple of things which I've commented on the files

useNavigationState,
useNavigationActions,
} from "@app/contexts/NavigationContext";
import { isApplyingRestoredView } from "@app/services/workbenchSession";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review:
HomePage.tsx:153 — the new leftFiles gate drops a guard unrelated to this feature, leaving the workbench stuck on the My Files grid at /editor after any HomePage remount (login bounce, share link).

@@ -0,0 +1,284 @@
// The editor/processor shell switch unmounts every editor provider, and a reload starts from nothing:
// this mirrors the workbench into sessionStorage and refills an empty one from that record on mount.
import { useCallback, useContext, useEffect, useRef, useState } from "react";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review:

WorkbenchSessionPersistence.tsx:181 — a failed refreshSession() (SaaS team switch, password change) looks like a sign-out: the record is wiped and writes stay suspended for the rest of the page, since nothing resumes them when the identity returns.

@reecebrowne

Copy link
Copy Markdown
Contributor

A blip on the /auth/me check gets treated the same as a sign out, so the
restore info is wiped immediately so state gets lost when it shouldn't be. This coule happen with a dodgy wifi connection, on a backend upgrade or redeploy etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Front End Issues or pull requests related to front-end development size:XL This PR changes 500-999 lines ignoring generated files. Translation Issues or pull requests related to translation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants