Skip to content

refactor: deliver watcher events and close requests only to their own window - #687

Merged
Azganoth merged 1 commit into
mainfrom
chore/window-scoped-watchers
Oct 11, 2026
Merged

Azganoth merged 1 commit into
mainfrom
chore/window-scoped-watchers

Conversation

@Azganoth

Copy link
Copy Markdown
Owner

Summary

The backend assumed one window. It held one native watcher per kind for the whole application, broadcast watcher events and close requests to every window, and tracked a single pending close request, so a second window's document watch replaced the first's and closing one of two windows destroyed both (#633). Watches and close requests now belong to their window, as groundwork for tabs (#641) and more windows (#642). With one window and one document, watching and closing behave as before.

  • Watches are kept per window label. A window holds at most one folder watch, which a newer scope replaces as before, and any number of document watches, each started and stopped on its own by scope ID; the document watch commands no longer take a generation. Change and watch-error events are emitted only to the owning window, and destroying a window stops its watches.
  • A document watch's stop can reach the backend before its start, so the backend keeps that stop until the start arrives and refuses it. The frontend therefore stops only a watch it asked to start; a stop without a start would otherwise stay until the window closes.
  • A close request goes only to the window it names, and the guard tracks one pending request per window, so an unanswered request lets the next one close that window alone. The frontend declines through a decline_window_close command instead of a broadcast event, so the backend learns the window from the caller rather than from the message. Destroying a window clears its pending request.
  • The default capability covers every window (*) rather than main; every window loads the local frontend.
  • Architecture describes window-owned watches and close requests.

Related Issue

Closes #640

Verification

  • watch_scope Rust tests: several document watches in one window start and stop independently, including a stop that arrives before or during its start; two windows' folder and document scopes do not displace each other, whether their numbers match or one window's are lower; stopping a window drops only its watches.
  • window Rust tests: the close guard asks each window's frontend separately and closes only the window whose request went unanswered.
  • useActiveDocumentWatcher: a watch closed before it was requested is never stopped, and a failed start is still stopped. The close-request handler and both watchers listen through their own window, which Tauri limits to events addressed to it; app tests cover declining through the command.
  • pnpm check:frontend and pnpm check:backend passed.

Manually verified in a Windows 11 debug build with a temporary command that opened a second window, driven over the WebView2 DevTools protocol with an event recorder in each window:

  1. With one document open in each window, each in its own folder, an external change to either document reached and reloaded only the window holding it.
  2. Closing the second window with unsaved changes prompted only there. Declining kept it open and the next close prompted again; discarding closed it while the first window and the process stayed.
  3. A new window reusing the closed window's label received no events when the closed window's document changed, and its first close request prompted, so the closed window's watches and pending request had been cleared.
  4. Closing the last window quit.

Not verified: the desktop E2E suite locally, which CI runs. No scenario was added, since the E2E capability grants WebDriver access to main only.

Notes

A webview reload restarts the window's scope numbering while the backend still holds the old page's scopes, so a watch whose ID collides is refused until the window closes. Generations had the same limit before this change; it is left as is.

@Azganoth Azganoth added the Maintenance Chore, refactor, dependency, or test work without user-facing change label Oct 11, 2026
@Azganoth Azganoth self-assigned this Oct 11, 2026
@Azganoth
Azganoth enabled auto-merge (squash) October 11, 2026 07:10
@Azganoth
Azganoth disabled auto-merge October 11, 2026 07:16
@Azganoth
Azganoth enabled auto-merge (squash) October 11, 2026 07:16
@Azganoth
Azganoth merged commit a50dd67 into main Oct 11, 2026
3 checks passed
@Azganoth
Azganoth deleted the chore/window-scoped-watchers branch October 11, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Maintenance Chore, refactor, dependency, or test work without user-facing change

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Deliver watcher events and close requests only to the window that owns them

1 participant