You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(app): persist & restore preview tabs across reload and tab switches #1640
Problem — The Preview tab loses its open files whenever the session view re-instantiates — a window reload OR any VS Code tab switch (the session view is disposable by design, #1288). Symptoms seen live: the set collapses to one file, closed files reopen, and files shift between different sessions' previews. Root cause: the open set lives only in SessionPreviewTab's component-local workspace signal, destroyed on every re-instantiation; and a reactive "open on previewFile change" effect re-fires on each re-instantiation, re-opening stale/active files. Approach — Persist the open set (previewPaths) + focused file (previewActive) per session in the serverGlobal layout store, and restore via ONE signature-guarded reactive merge that opens the persisted set idempotently — once per distinct value — so it survives async hydration and late session-key resolution and never loops or re-opens on a tab switch. Writes happen only on explicit open/close. The legacy reactive on(previewFile)→open effect is deleted (it was the reopen/shift engine); its jobs fold into the existing message listener (live opens) and the restore target (net-new opens). Approaches Considered — (A, chosen) minimal core — signature-guarded restore + delete the reopen effect. (B) invert to a single source of truth where previewPaths is the open set and the workspace projects it — more robust but a workspace refactor. (C) persist only the single active file — lowest risk, drops multi-file. Chose A: reuses the one primitive proven to work, no big refactor, robust to the breaking points below. Scope — in: restoring the SET of open preview files + focus, across window reload AND tab switches, per session. out: split-pane/zoom/scroll fidelity (#981); editor/PDF cursor/scroll on tab switch (#1366/#1367); consolidating the two force-mounted call sites (follow-up). Assumptions — the serverGlobal layout store persists across reload (proven by session-tab restoration); SessionView is per-session-keyed; the store's ready signal gates the first write.
Acceptance Criteria
Open N files in Preview, reload the window → all N return, correct file focused.
Close a tab, switch to another VS Code tab and back → it stays closed (no reopen/shift).
Close all tabs, reload → Preview is empty (nothing reopens).
Two sessions with different previews → switching between them shows each session's own set (no cross-contamination).
Open the first file into a closed Preview (this mounts the tab) → it shows AND persists.
A reactive unit test simulates async-hydrate / re-instantiation / close-then-switch / net-new-open and asserts the persisted set.
Key Decisions
Restore is a single signature-guarded reactive merge — idempotent; opens missing paths only; writes at most once per distinct value (deduped) to persist a net-new open.
No on(previewFile)→open effect. Live opens arrive via the mount message listener; a first-open-that-mounts-the-tab is caught because previewActive is in the restore target.
Writes only on user open/close; close reassigns previewActive to a remaining tab or null, so a closed file is never the active pointer to be re-opened.
Additive optional SessionView fields (previewPaths, previewActive); no schema-version bump.
Constraints & Invariants
Must not loop (signature guard + write-only-on-change).
Must not re-add a closed file (close writes the shrunk list + reassigns active; restore target excludes it).
Robust if two preview instances co-mount (idempotent restore + deduped writes) — verify/consolidate as a follow-up, not blocking.
Dirty .tex close still shows the save dialog (unchanged, expected — not a persistence bug).
Breaking points addressed
BP1 async hydration · BP2 late session-key resolution · BP3 re-instantiation reopen · BP4 dual-role previewFile · BP5 two force-mounted instances · BP6 write→read loops · BP7 close persistence · BP9 cross-session contamination. (BP8 dirty-close is out of scope, documented.)
Source
Part of #1252. Supersedes the in-memory approach in #1398 (the persisted restore covers tab-switch survival too). Related: #1365 (active-state plumbing), #1366/#1367 (tab-switch view-state), #981 (full state codec). PR: #1641.
changed the title [-]fix(app): preview tabs persist across editor reload (restore open files on webview reload)[/-][+]fix(app): persist & restore preview tabs across reload and tab switches[/+]on Sep 29, 2026
Implemented in PR #1641 (ready for review). Verified working on a live build for the reload + same-session tab-switch restore.
Known limitation, tracked separately in #1648 (not introduced by this work): opening a file in one session's Preview can bleed into another session's Preview — the preview-file bridge message is an un-targeted window broadcast and the prewarmer keeps backgrounded session views alive, so every live SessionPreviewTab handles it. Non-catastrophic; needs a foreground-session gate or a session-targeted message.
Important
Problem — The Preview tab loses its open files whenever the session view re-instantiates — a window reload OR any VS Code tab switch (the session view is disposable by design, #1288). Symptoms seen live: the set collapses to one file, closed files reopen, and files shift between different sessions' previews. Root cause: the open set lives only in
SessionPreviewTab's component-localworkspacesignal, destroyed on every re-instantiation; and a reactive "open onpreviewFilechange" effect re-fires on each re-instantiation, re-opening stale/active files.Approach — Persist the open set (
previewPaths) + focused file (previewActive) per session in theserverGloballayout store, and restore via ONE signature-guarded reactive merge that opens the persisted set idempotently — once per distinct value — so it survives async hydration and late session-key resolution and never loops or re-opens on a tab switch. Writes happen only on explicit open/close. The legacy reactiveon(previewFile)→open effect is deleted (it was the reopen/shift engine); its jobs fold into the existing message listener (live opens) and the restore target (net-new opens).Approaches Considered — (A, chosen) minimal core — signature-guarded restore + delete the reopen effect. (B) invert to a single source of truth where
previewPathsis the open set and the workspace projects it — more robust but a workspace refactor. (C) persist only the single active file — lowest risk, drops multi-file. Chose A: reuses the one primitive proven to work, no big refactor, robust to the breaking points below.Scope — in: restoring the SET of open preview files + focus, across window reload AND tab switches, per session. out: split-pane/zoom/scroll fidelity (#981); editor/PDF cursor/scroll on tab switch (#1366/#1367); consolidating the two force-mounted call sites (follow-up).
Assumptions — the
serverGloballayout store persists across reload (proven by session-tab restoration);SessionViewis per-session-keyed; the store'sreadysignal gates the first write.Acceptance Criteria
Key Decisions
on(previewFile)→open effect. Live opens arrive via the mount message listener; a first-open-that-mounts-the-tab is caught becausepreviewActiveis in the restore target.previewActiveto a remaining tab ornull, so a closed file is never the active pointer to be re-opened.SessionViewfields (previewPaths,previewActive); no schema-version bump.Constraints & Invariants
.texclose still shows the save dialog (unchanged, expected — not a persistence bug).Breaking points addressed
BP1 async hydration · BP2 late session-key resolution · BP3 re-instantiation reopen · BP4 dual-role
previewFile· BP5 two force-mounted instances · BP6 write→read loops · BP7 close persistence · BP9 cross-session contamination. (BP8 dirty-close is out of scope, documented.)Source
Part of #1252. Supersedes the in-memory approach in #1398 (the persisted restore covers tab-switch survival too). Related: #1365 (active-state plumbing), #1366/#1367 (tab-switch view-state), #981 (full state codec). PR: #1641.