From 159b7ecd6e5d37b8b425c5ec749581db4f3b4abe Mon Sep 17 00:00:00 2001 From: naps62-yolo Date: Mon, 22 Jun 2026 09:20:57 +0000 Subject: [PATCH] =?UTF-8?q?feat(maestro):=20multi-view=20web=20UI=20?= =?UTF-8?q?=E2=80=94=20splits,=20tabs,=20drag-dock,=20highlights?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render the content pane as tabs of nested split trees, each session leaf a live attach mounted through the multi-mount pool: - #30 arbitrarily-nested H/V splits with mouse-resizable dividers; per-pane ⏻/▥/▤/✕ tools; closing a pane collapses its parent split. - #31 tabs, each holding its own tree; add / switch / close. - #32 drag-to-dock: drag a pane title or a sidebar session onto a pane for a 5-zone dock (edges split, centre swaps/replaces); a sidebar drag moves an already-open session. - #33 focused pane at full strength, others dim; sidebar shows focused (loud) vs open-elsewhere (quiet) with a stable per-session colour mapping rows to panes. Opening a session fills the focused pane (single-pane parity by default); multi-pane is opt-in via split / drag / tabs. Panes auto-close when their session ends (guarded against eviction and intentional close). The steady "connected" label is hidden as noise. Closes #30, #31, #32, #33. Part of epic #37. Co-Authored-By: Claude Opus 4.8 --- web/src/attach.ts | 7 + web/src/catalog.ts | 73 ++++-- web/src/connections.ts | 8 +- web/src/layout.ts | 104 +++++++- web/src/main.ts | 109 ++++---- web/src/multiview.ts | 536 ++++++++++++++++++++++++++++++++++++++++ web/src/sessioncolor.ts | 23 ++ web/src/styles.css | 318 ++++++++++++++++++++++++ 8 files changed, 1100 insertions(+), 78 deletions(-) create mode 100644 web/src/multiview.ts create mode 100644 web/src/sessioncolor.ts diff --git a/web/src/attach.ts b/web/src/attach.ts index e019e57..ef7c4b5 100644 --- a/web/src/attach.ts +++ b/web/src/attach.ts @@ -30,6 +30,10 @@ export interface AttachConnection { export interface AttachOptions { /** Invoked when the user clicks the view's explicit "disconnect" control. */ onRequestClose?: (sessionId: Uuid) => void; + /** Invoked when the socket closes on its own — the session ended or the link + * dropped — but NOT on an intentional `dispose()` (eviction / user close). The + * shell uses this to auto-close the pane rather than leave a dead one. */ + onClosed?: (sessionId: Uuid) => void; } /** What the header shows about the session (name / command / host). Optional — @@ -242,6 +246,9 @@ export function createAttachConnection( ws.onclose = (ev) => { setState(`closed (${ev.code})`, "closed"); + // Only bubble up server/network closes — an intentional dispose has already + // set `disposed`, and eviction/user-close handle their own pane teardown. + if (!disposed) opts.onClosed?.(sessionId); }; // PTY input: every keystroke goes out as a binary frame. A pending key-bar diff --git a/web/src/catalog.ts b/web/src/catalog.ts index 07ef274..3adc557 100644 --- a/web/src/catalog.ts +++ b/web/src/catalog.ts @@ -337,12 +337,24 @@ export function renderTopbar(store: CatalogStore): HTMLElement { return header; } +/** Two-tier sidebar highlight state (issue #33): which sessions are open in some + * pane (quiet), which one is focused (loud), and a stable per-session colour so + * a row maps to its pane. */ +export interface SidebarHighlights { + open: Set; + focused: string | null; + colors: Map; +} + /** Live handle to the persistent sidebar (built once, survives navigation). */ export interface SidebarHandle { /** The `