Repository navigation
feat(windows): record every window's session as it changes; treat windows the same - #994
Merged
Merged
Conversation
"main" is now only the name of the window Tauri builds at launch. The frontend has no branch on it; Rust tells the launch window apart in one constant, LAUNCH_LABEL, and only at startup. - Startup adoption: before window-state loads its file, a launch window with no snapshot takes over the first remembered window's snapshot, breadcrumb, unsaved backup and geometry, so a launch never opens an extra empty window. The snapshot moves last, so a launch killed partway adopts it again. An existing backup of the launch window is never overwritten; the other one stays an orphan. - Orphan backups (no open window, no snapshot) are handed out by claim_unsaved_recovery, once, to the first window that asks. - Every window runs the same startup: restore, reopen_remembered_windows (only the first call of a launch does anything), claim backups, ask for startup files. send_markdown_path answers only the launch window, once; a file macOS opens before that joins the launch's files instead of also going to whichever window has focus. - A cancelled quit forgets the windows it already closed. Their backups stay and are adopted as orphans. - Each window keeps its sidebar folder in its own snapshot. The install-wide folder.lastRoot is read once, by the first window to start, and removed. - Forgetting a window drops its breadcrumb whatever its label.
A window's restore snapshot is written whenever its tabs, their order, the active tab, a tab's view (edit, split, ratio, scroll sync), the tag or the folder change, through the bounded, serialized writer #962 uses for unsaved backups: at most one write per 500 ms, latest state wins. Scroll and editor position are not tracked, so scrolling writes nothing; they go out with the next tracked change and on focus loss, hide, close and quit. So after a crash or kill every window comes back with its own tabs and backup instead of being merged into the launch window, and an update restart finds the other windows already current. Every window writes a snapshot once its restore has run, an empty one included: the set of snapshot files is the window list. Recording starts only after the window's own restore, so its empty starting state never replaces what it is about to read. Forgetting a window waits for a write in flight and stops the writer first, so the file cannot come back. createUnsavedRecovery becomes createBoundedWriter, with the value already on disk as `initial`, and dispose resolves once the last write landed.
tauri-plugin-window-state saves only on a clean exit, so after a crash or kill every window came back on the built-in display at its default size, though its tabs were restored. A move or resize now schedules one save of every window's geometry 500 ms later, through the existing coalesced helper. The plugin and this save share geometry_flags.
Its only caller is the macOS RunEvent::Opened handler, so clippy's dead-code lint failed the Linux and Windows builds.
PathGao
added a commit
that referenced
this pull request
Oct 20, 2026
… bits (#996) * docs: drop Live Mode from comments about the always-on watcher Since #986 the file watcher runs whether or not Live Mode is on, and #987 turned Live Mode on by default. Comments and test names still said the watcher was Live Mode's, or that Live Mode was off by default. * docs: bring comments in line with continuous snapshots and the JS menu #994 writes each window's snapshot as it changes, not once at close, and calls the first window the launch window rather than main. #993 builds the menu in the frontend, so its Copy item is no longer PredefinedMenuItem::copy. * chore(folder): drop the folder.lastRoot migration The install-wide key only existed between #964 and #994, both merged the same day, and never shipped in a release. * chore: drop the menu-close-file listener Nothing has emitted the event since 715e1b6.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Follows #990. Windows are now equal, and each one's session is recorded as it changes rather than only at quit:
kill -9, every window comes back with its own tabs, its own unsaved tabs (feat(editor): keep unsaved changes when quitting #962), and its own position and size. Before, the window list was only written on quit, and unsaved tabs from other windows landed in the launch window.mainspecially any more, except that it is the window Tauri creates at launch.Mechanism
Continuous session. Each window writes its own
window-state/<label>.jsonwithin 500 ms of a change to its tabs, active tab, view mode, split, tag or sidebar folder. It uses #962's bounded, serialized writer, renamedcreateBoundedWriter. Scroll and cursor positions don't trigger writes; they ride along with the next write, and are also written on blur, when the window is hidden, on close and on quit. The set of snapshot files is the window list. Window geometry is saved through the window-state plugin 500 ms after a move or resize (coalesced), because the plugin itself only saves on exit.Remember or forget is unchanged from #990, with two gaps closed:
Mutex<Option<Vec<label>>>instead of a bool, andset_quitting(false)drops their snapshots. Their unsaved backups become orphans.Former main-window privileges:
claim_unsaved_recovery), once each, to whichever window asks firstfolder.lastRootkey is migrated oncescripts/windowsAreEqual.test.tsfails if the frontend compares against'main'or mentionsisMain. In Rust non-test code,"main"appears only asLAUNCH_LABEL.Startup adoption. A small plugin runs before the window-state plugin loads. If the launch window has no snapshot of its own but other windows are remembered, it adopts the first of them: snapshot, crash guard, unsaved backup and geometry. So a relaunch after closing the launch window opens no extra empty window. The snapshot is moved last, so a kill midway re-adopts the same window. An existing launch-window backup is never overwritten; the adopted one becomes an orphan instead.
Behaviour changes
Scope
fs::write, not an atomic rename. A kill during that write loses every window's geometry once. Tabs and unsaved changes are unaffected. This is markedponytail:in the code.Tests
disposewaits for an in-flight write. Each was confirmed to fail with the code broken.isMainWindowoption.Verification
npm run check0 errors,npm test1142 pass,npx vitest run523 pass,cargo test206 pass,cargo fmt --checkandcargo clippy --all-targets -- -D warningsclean.macOS 27, release test bundle:
kill -9, both came back, each with its own tab and text.kill -9, both reopened at exactly those positions and sizes.