Skip to content

feat: open more than one window - #689

Merged
Azganoth merged 1 commit into
mainfrom
feature/multiple-windows
Oct 12, 2026
Merged

Azganoth merged 1 commit into
mainfrom
feature/multiple-windows

Conversation

@Azganoth

Copy link
Copy Markdown
Owner

Summary

Each Leafdown process had one window, and Mod+Q closed it. File > New window (Mod+Shift+N) now opens another window in the same process, on the welcome screen. Each window has its own tabs, folder context, article navigator, folder search, title, and zoom. Settings and recent items are shared through the persisted stores, which already synchronize every window, so sidebar visibility, status bar visibility, and Always on top apply to every window. With one window, everything behaves as before except Mod+Q.

  • A saved file is open in at most one tab across windows. The backend records each window's saved documents and whether each has unsaved changes, comparing paths by a Rust port of the frontend's path identity rules. A window claims a path before it reads the file or saves to it, and the claim checks and records in one step. When another window holds the path, that window activates its tab and comes forward instead, and the file is still recorded as recent.
  • Opening a folder whose index file another window holds opens the folder without it and leaves the other window where it is, since the folder, not the file, was asked for. A Find in folder match in a file another window holds brings that window forward with its tab active, without choosing the match there.
  • Save as onto a path another window holds is refused. A rename in the navigator moves the affected tabs in every window, keeping their unsaved changes. A deletion is forwarded to the other windows, because a document watch does not observe a deleted parent folder; their tabs keep their text and show File missing, with a warning that another Leafdown window deleted the file.
  • Replace in folder skips and names a file with unsaved changes in another window, and plans it again once those changes are saved or discarded there. A file that gains such changes after the plan is left as it was, and the report says why.
  • Close window (Alt+F4, the close button) asks only about that window's unsaved documents, and closing the last window quits. A window with unsaved documents unminimizes and comes forward before it asks.
  • Quit (Mod+Q, taken from Close window) closes the windows one at a time, the caller first and then the others in the order they were first seen, each through its own close request. A destroyed window moves the quit on, and Cancel ends it, leaving that window and every window not yet asked open. A window whose close request is still unanswered is not asked again, since a second request closes a window without asking.
  • The first window keeps its size and position between launches; the window-state plugin is now filtered to it. Later windows open 32 logical pixels down and right of the window they were opened from, at its work area's top-left corner when they would not fit, or centered when that window is maximized, fullscreen, or minimized. open_new_window is an async command because creating a window from a synchronous command deadlocks on Windows.
  • The default capability adds core:window:allow-unminimize. The test-only WebDriver capability covers every window rather than main, so the desktop suite can drive the new ones.
  • Specification, Architecture, Reference (New window, Close window, Quit), Help, and the changelog describe windows.

Related Issue

Closes #642
Refs #639

Verification

  • Rust window tests: windows close in turn during a quit; a quit declined in its second window leaves that window and the rest open; a decline from a window not being closed, a window closed out of turn, and a second quit while one is under way leave the sequence intact; new windows get distinct labels and cascade, falling back to the work-area corner.
  • Rust window_documents and path_utils tests: a claim succeeds on a path no window holds and is refused for one another window holds under any spelling, lasts until the window reports its documents, and is freed when its window is destroyed; windows keep first-seen order; relative and empty paths are refused; Rust path identity keys match the frontend's for Windows and POSIX paths.
  • openSession, windowDocumentClaims, useWindowDocuments, crossWindowDocuments, and otherWindowDocuments: opening a file another window holds shows it there without reading it, a failed open gives its claim back, a folder opens without an index file held elsewhere, a window reports its documents and follows the others', and a rename or deletion in another window moves this window's tabs or marks them missing with their text kept.
  • documentWorkflows, folderEntryWorkflows, folderSearchEngine, folderReplaceWorkflows, and folderSearchWorkflows: the Save as refusal, rename and delete forwarding, the Replace in folder skip and its re-plan, a file that gains unsaved changes elsewhere during a replacement, and a match in another window's file.
  • app, file actions, shortcutReference, and command-menubar: the close dialog brings its window forward, Always on top follows a change made in another window, New window and Quit reach the backend, and Mod+Q belongs to Quit.
  • pnpm check and pnpm test:e2e:desktop:runner passed.

Not verified: the new multiple-windows desktop scenario and the rest of the desktop E2E suite, which CI runs. Outside that scenario, no native window behavior was exercised in the running app: opening and placing windows, focus across windows, the quit sequence, the filtered window state, and settings, including Always on top, changing in every window.

Notes

  • A list a window sent just before a claim can reach the backend after it and drop the claim until the window's next report, which follows at once. Another window could claim the same file in that gap.
  • Quit waits on each window's frontend, so a window whose frontend stops responding stalls it until that window's close button is clicked twice, which closes it without asking.
  • A folder context that another window renames or deletes is not followed; only the documents under it are.
  • The scenario types only in a window just opened or brought forward, because WebDriver's synthetic input does not reliably reach a background window.
  • Opening a file from the operating system still starts a separate instance that knows nothing of this one's windows; that is Open files from File Explorer in the running Leafdown #643. Moving tabs between windows and restoring windows after a restart are out of scope.

@Azganoth Azganoth added the Feature New feature or request label Oct 12, 2026
@Azganoth Azganoth self-assigned this Oct 12, 2026
@Azganoth
Azganoth enabled auto-merge (squash) October 12, 2026 01:57
@Azganoth
Azganoth merged commit 5d70729 into main Oct 12, 2026
3 checks passed
@Azganoth
Azganoth deleted the feature/multiple-windows branch October 12, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Open more than one Leafdown window

1 participant