Skip to content

feat: open several documents as tabs in a window - #688

Merged
Azganoth merged 3 commits into
mainfrom
feature/document-tabs
Oct 11, 2026
Merged

Azganoth merged 3 commits into
mainfrom
feature/document-tabs

Conversation

@Azganoth

@Azganoth Azganoth commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

A window held one document, so opening another file replaced it after asking about unsaved changes. A window now holds several documents as tabs, under the ownership model in the #633 Outcome, and switching between them keeps each one's editor state. With one tab, everything behaves as before except that opening no longer replaces the document.

  • Opening a file from Open, recent files, the article navigator, a link, a drop, or a Find in folder result adds a tab after the active one, or activates the tab already holding that file. New adds an untitled tab, numbered from the lowest free number. Opening never replaces a document, so it no longer asks about unsaved changes.
  • The session store holds the tabs and the active tab ID, and keeps activeDocument as a mirror of the active tab for consumers that only follow it. Async workflows check a tab ID plus a generation that every document replacement bumps, so a read, save, reload, or prompt that settles after its tab closed or changed does nothing. Save and Save As target the tab that was active when the command was given, not whichever tab is active when a queued save runs.
  • Inactive tabs keep their editors mounted in hidden panels, which keeps undo history, caret, scroll, and find-and-replace state. The editor bridge holds every mounted editor and names the shown one; outline navigation, search-match selection, and focus requests wait until their editor is shown.
  • Each saved tab has its own file watch through Deliver watcher events and close requests only to the window that owns them #640's per-document watches, so an external change reloads a clean tab or is recorded for an unsaved one whether or not it is active.
  • Close document (Mod+W), a tab's close button, and a middle click close one tab, asking about its unsaved changes by name. Closing the window, Close folder, or deleting an entry that holds several unsaved tabs shows one dialog naming them, with Discard all and Cancel.
  • Link resolution spells a target inside the folder context from the folder's own path, as folder scans already do, instead of returning its canonical path. When the folder was opened through a short name, a junction, or a symbolic link, the canonical path differed from the one the open tab held, so following a link to an open document opened it again in a second tab. Architecture records the contract.
  • Open folder keeps the tabs and opens the index file as a tab. Save as onto a path open in another tab is refused. A rename in the navigator moves every tab under the renamed path, keeping unsaved changes.
  • Find in folder and Replace in folder read every open tab from its editor. Replace all asks about each unsaved tab its plan includes, in tab order and without switching tabs.
  • The strip sits above the document beside the navigator, shrinks its tabs and then scrolls, and ends with a + button that runs New. A tab shows the file name, adding the parent folder when names clash and the full path on hover; an unsaved tab shows a dot that becomes a close button on hover. Mod+Tab/Mod+Shift+Tab and Mod+PageDown/Mod+PageUp move through the strip with wrap, Mod+1 to Mod+8 pick a tab, and Mod+9 picks the last. The window title follows the active tab as name - Leafdown, which adds the core:window:allow-set-title permission.
  • Specification, Architecture, Reference, Help, and the changelog describe tabs. Getting started no longer says the close prompt offers to save.

Related Issue

Closes #641
Refs #639

Verification

  • session store tests: opening places a tab after the active one or reuses the tab holding the path, closing activates the right neighbor or else the left, and replacing a document bumps its generation.
  • Workflow tests (openSession, documentWorkflows, unsavedChanges, externalChanges, useDocumentWatchers, folderEntryWorkflows, folderContextWorkflows, folderSearchWorkflows, folderReplaceWorkflows): opening into a new or existing tab, per-tab unsaved and external-change state including reloads of inactive tabs, closing a tab, the several-tab dialog, Open folder and Close folder with tabs, the Save as refusal, rename and delete across tabs, and folder search and replace over several tabs including inactive unsaved ones. A regression test saves the tab the command was given on while another tab is active by the time the queued save runs; it fails without the fix.
  • documentEditorBridge: heading, search-match, and focus requests wait for their editor to be shown and are dropped when another is shown first.
  • document-tabs, shell, app, useAppCommands, and view tests: the strip's order, names that clash, switching, closing without switching, and New button; the window title; the window-close dialog; and the tab shortcuts' routing and wrap.
  • link Rust tests: a target in a folder reached through a symbolic link keeps the folder's spelling, which fails without the change. The command contract test that link and folder-scan paths agree now starts from TEMP as the host spells it, an 8.3 short path on the Windows CI runner, instead of canonicalizing it first.
  • pnpm check:frontend and pnpm check:backend passed.

Manually verified in headless Microsoft Edge 154 on Windows 11, the engine WebView2 uses, with the tab strip and real editors mounted on a temporary Vite page and driven over the DevTools protocol:

  1. A hidden tab kept its scroll position, so switching back needs no restore.
  2. Clicking a tab focused its editor without scrolling it.
  3. A trusted Ctrl+Tab reached the page and wrapped from the last tab to the first.
  4. A context popup open in a tab that became hidden hid with it.

Not verified: the desktop E2E suite and the new document-tabs scenario locally, which CI runs; the tab shortcuts in the Tauri window itself.

Notes

@Azganoth Azganoth added the Feature New feature or request label Oct 11, 2026
@Azganoth Azganoth self-assigned this Oct 11, 2026
@Azganoth
Azganoth merged commit cfc97e3 into main Oct 11, 2026
3 checks passed
@Azganoth
Azganoth deleted the feature/document-tabs branch October 11, 2026 15:04
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 several documents as tabs in a window

1 participant