Desktop apps built with a web view tend to fight their own navigation. A note editor wants a main window, a floating quick-capture panel, a dock that stays on top of everything, and a kanban board that can be pinned underneath the desktop icons. In a browser those are four pages. In a desktop app they are four windows, and the naive answer is four routes.
Four routes means four bundles, four sets of stores, and four places where the same SQLite file gets opened. It also means every shared component has to be reachable from each of them.
The route that branches on its role
The approach that held up was to keep one route and let the window tell it what to be. Each window is created with a role, the frontend reads that role on load, and a single root layout renders the right shell for it.
const role = await getCurrentWindowRole();
// "workspace" | "dock" | "board" | "note" | "task"
The Rust side owns the part the frontend cannot: window lifecycle, the tray, global shortcuts, and the desktop-underlay flag for the board. The frontend owns the part Rust should not: which panel is open, what the note currently says, whether the dock is collapsed.
What this buys is that every window shares one build. A component change reaches the dock and the board in the same rebuild, and there is exactly one place where the database connection is configured.
One database, five readers
The harder problem is concurrency. Five windows writing to one SQLite file is a recipe for database is locked if each opens its own connection and each decides when to write.
Migrations run once, on startup, from the Rust side, before any window exists. Windows then read and write through a single plugin connection with a busy timeout, and the stores treat writes as the exception rather than the default. The board does not poll; it listens for a change event and reloads the one column that changed.
There is one deliberate fallback. Outside Tauri, during vite dev in a browser or in a component preview, the data layer serves seed content instead of failing. That keeps the UI workable without a desktop shell, which matters more than it sounds: it is the difference between being able to iterate on the board layout in a browser and having to rebuild the whole app for every CSS change.
What I would keep
The single-route decision is the one worth carrying forward. It is tempting to reach for separate entry points because the windows look unrelated, but they are not: they are five views of one local dataset. Treating them that way removed a whole class of bugs before they existed.



