Several windows at once - #3
Open
auslander969 wants to merge 1 commit into
Open
auslander969 wants to merge 1 commit into
auslander969 wants to merge 1 commit into
Conversation
`Ctrl+Shift+N` opens another window on the same folder with nothing in it, `Ctrl+Shift+W` closes one, and `file → exit` still ends the app: every window gets the unsaved-changes question and the app goes when the last of them has closed itself. A window is its own webview with its own store, so most of this is already true — two windows share nothing, and neither has to know what the other is showing. What had to be settled is the four things they would otherwise fight over. They live in src-tauri/src/windows.rs, with the front end's half in src/app/windows.ts. Which window a file belongs to. A path from Finder, from a second launch or from the command line is parked on the window the user was last in, and only that window is nudged — rather than every window racing to drain one queue. One file, one window. Two windows on one document would be two buffers, two drafts under the same name and two saves racing each other (spec §8), so opening a file another window already has brings that window forward instead. Unless this window opened something of its own: then the focus stays here and the status bar says where the other one went. state.json. Version 2 is a list with one entry per window; version 1 was the single object one window wrote, and still reads as the one window it was. No window can see the others' entries, so the file is assembled and written in Rust. Closing a window drops its entry; quitting keeps them all, and so does closing the last window, because that is how the app is quit on Windows. settings.json. One file, so a theme changed in one window changes in all of them. Without that the other windows would carry on with the old value and write it back over the new one on the way out. The watcher is now one per window rather than one shared: a window only hears about its own files, and a window opening a document does not make every other window's library be indexed again. Starting one walks the whole tree, which is long enough to be seen on a large library, so `watch` is `async` and does that off the main thread — on the main thread it is the window not drawing for as long as it takes. Recovery drafts are the app's, not a window's, so only the first window of a run offers them. Three windows each offering to restore the same file would be three chances to answer one question, and two of them wrong. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gregmos
added a commit
that referenced
this pull request
Sep 12, 2026
`Ctrl+Shift+N` opens another window on the same library, empty and cascaded from this one; `Ctrl+Shift+W` closes it. Windows live as long as the app runs and do not come back after a restart — the files do, through `recent`, which is now shared between windows. state.json stays one snapshot written by the first window alone. What the windows share is kept small: which window a path from outside goes to (the focused one), which window already holds a file (one file, one buffer — checked on the canonical path after the read), the settings (a change in one window applies in all), and `recent`. Each window has its own watcher; quitting asks every window about its own unsaved work, and the app goes when the last one has closed itself. Window creation and watcher setup are async commands — a synchronous command that builds a webview deadlocks WebView2 on Windows. The close scenario is single-entry, so a quit arriving over an open dialog neither doubles it nor destroys the window twice. Grew out of PR #3 by @auslander969 (Several windows at once); the session-restore half of it was dropped by agreement, and its Windows defects are excluded by construction. Spec: review/waves/w13-windows-spec.md. Co-Authored-By: auslander969 <134359370+auslander969@users.noreply.github.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E9xq5TL7X83gzMZHauso67
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.
Opens more than one window at a time. Built on 0.2.2, so it sits on top of the macOS window work in #2 rather than around it.
Ctrl+Shift+Nopens another window on the same folder with nothing in it.Ctrl+Shift+Wcloses one.Ctrl+Wstill closes the document, and the native Window submenu is untouched —Close Windowstays absent, as review #5 has it.What had to be settled
A window is its own webview with its own store, so most of this was already true: two windows share nothing and neither has to know what the other is showing. Four things they would otherwise fight over now live in
src-tauri/src/windows.rs, with the front end's half insrc/app/windows.ts.Which window a file belongs to. A path from Finder, a second launch or the command line is parked on the window the user was last in, and only that window is nudged. The old single queue would have had every window racing to drain it.
PendingPaths/PendingFoldersbecame one parcel per window label, drained bytake_opening.One file, one window. Two windows on one document would be two buffers, two drafts under the same name and two saves racing each other — §8 territory. Opening a file another window already has brings that window forward instead. Unless this window opened something of its own: then the focus stays put and the status bar says where the other one went, because being thrown into another window over one of three files would lose the other two.
state.json. Version 2 is a list with one entry per window. Version 1 was the single object one window wrote and still reads as the one window it was, so an existing file upgrades rather than disappearing. No window can see the others' entries, so the file is assembled and written in Rust;
parseSessionis unchanged and still parses one entry, which is why its tests are untouched.Closing a window drops its entry. Quitting keeps them all — and so does closing the last window, because that is how the app is quit on Windows and how
file → exitends everywhere, and quitting means coming back where you left off.settings.json. One file, so a theme changed in one window changes in all of them. Without it the other windows carry on with the old value and write it back over the new one on the way out.
Quitting
⌘Q, the Dock's Quit andfile → exitall emitplain:quit-requestedto every window; each runs the close scenario it already had and destroys itself, and the app goes when the last one has.exit_appis gone — the exit guard from #2 just neededwebview_windows().is_empty()instead of asking formain.One consequence worth naming: cancelling the unsaved-changes question in the third window leaves the first two closed. Nothing unsaved is lost — those two were saved or explicitly discarded — but it is not the "all or nothing" some apps do. Happy to do the two-phase version if you would rather.
The watcher
One watcher per window rather than one shared. A window now only hears about its own files, and a window opening a document no longer makes every other window's library be indexed again. The shared version I tried first was worse than the original in both respects.
watchis alsoasyncnow, so the tree walk that starting a watcher does happens off the main thread. That one is a pre-existing stall rather than something multi-window introduced — with a large library the window does not draw until the walk finishes — but two windows made it easy to hit. Splitting it into its own commit is no trouble if you would prefer it separately.Also
Recovery drafts belong to the app, not a window, so only the first window of a run offers them; three windows each offering the same file would be three chances to answer one question, and two of them wrong. Nameless buffers carry the window label in their id so two windows counting from one each cannot collide on a draft name.
Tested
npx vitest run(673 pass, 52 files) andcargo test(90 pass) on macOS. New cases cover version 1/version 2state.jsonand the one-file-one-window rule, including that a window still opens the file if the registry cannot be reached.By hand on macOS 15, debug build under a separate bundle identifier so it could run alongside a released Plain:
Ctrl+Shift+N→ a second, empty, same librarystate.jsonv2 with both windows and what each had openNot verified, and worth your eye: Windows — I have no machine for it, and the fs-scope and association paths there are yours. Closing one window of several and confirming its entry goes: the path is small and reviewed, but I did not exercise it. Cascade placement for a new window is best-effort and looked like the OS was choosing anyway.
🤖 Generated with Claude Code