0015 — A modular, single-process, multi-window web UI
Status: Accepted (built, including in-app window management — 2026-07-29 note below) Date: 2026-06-29
Context
A real front-of-house setup is not one screen. The operator wants the fader wall on the main display, a plugin editor on a second monitor, meters and latency on a tablet, and a phone on stage for monitor mixes — several windows and screens, several devices, all live and all agreeing. The question is how to structure the UI so those views are independent without becoming independent applications that drift apart.
Decision
Build the UI as modular view components over one shared, server-synced state, so a window is a composition of modules, not a separate app. Because the server is the single source of truth (0003), every window — same device or another — is just one more thin client that sends intents and re-renders from the broadcast. Synchronisation across windows is therefore free: there is nothing to reconcile between them, only between each window and the server.
The web UI is already factored this way: a fader wall, a per-strip insert rack, an
auto-generated plugin editor, and meter widgets are independent components driven by
module-scoped composables (useMixer, useInserts, useCatalog, useTheme) over the
server's REST entity API (with ?watch=1 SSE for live rows), plus the legacy WebSocket
for what has not yet moved off it — either way, one connection per client to the one
authoritative server.
Today, "multi-window" is achieved by opening the surface on several windows, tabs, or devices at once — each an independent client the server keeps in sync. An in-app window/screen manager (assign which modules show on which screen, save that layout per-show) is designed but not yet built; the modular component structure and the single-source-of-truth server are the substrate it will sit on.
Consequences
- Multiple synchronised views work today with no extra machinery — open more clients.
- Windows stay in agreement by construction, because none of them is authoritative; the server is.
- The path to a real multi-window manager is additive: compose the existing modules into named layouts and persist them, not re-architect the UI.
- A single shared connection and module-scoped state keep memory and message traffic reasonable as windows multiply; meters are a separate low-rate stream so extra views do not multiply per-control chatter.
- The same modules are reused by the live-gig view, the plugin configurator, and (designed) the surface-assignment UI — one component set, many compositions.
Note — 2026-07-29: the in-app window manager is built
"An in-app window/screen manager (assign which modules show on which screen,
save that layout per-show) is designed but not yet built" now describes shipped
code, point for point:
packages/web-ui/app/modules/LayoutManager.vue composes, saves, switches,
duplicates and pops out window layouts; app/utils/layout.ts is the pure data model
and useLayout (app/composables/useLayout.ts) persists a layout per show with the
session; the dock substrate is useLayout.ts, useDockContext.ts,
DockShell.vue, DockRegion.vue, DockPanel.vue, WindowView.vue and
utils/dock.ts.