Skip to content

fix(app): persist & restore preview tabs across reload and tab switches #1640

Description

@jacktrnr

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-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.

Activity

  1. self-assigned this
    on Sep 28, 2026
  2. 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
  3. jacktrnr commented on Sep 29, 2026

    @jacktrnr
    ContributorAuthor

    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.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions