diff --git a/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.css b/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.css index c6ea8838..396efc59 100644 --- a/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.css +++ b/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.css @@ -8,16 +8,11 @@ margin-block-start: 1rem; } -/* Holds the connection-status badge + (unless `fullHeight`) the - expand-to-fullscreen trigger — `margin-left: auto` targets the expand - trigger by its OWN class, not `:last-child` (tried that first; it - silently broke when `fullHeight` hides the expand trigger, since the - status badge then becomes the `:last-child` too and gets pushed to - the right edge instead of staying flush left — caught by actually - rendering the `fullHeight` case, not just reasoning about the CSS). - Same split-row intent `Shell.css`'s `.shell-header-actions` already - uses for its own pair of triggers, just addressed directly instead of - positionally. */ +/* Holds the connection-status badge and the expand-to-fullscreen + trigger (absent from the `fullHeight` mount, which floats the status + badge inside the terminal instead). `margin-left: auto` targets the + expand trigger by its own class rather than `:last-child`, same + split-row intent `Shell.css`'s `.shell-header-actions` uses. */ .ui-agent-term-preview-toolbar { display: flex; align-items: center; @@ -51,6 +46,7 @@ .ui-agent-term-preview-full { display: flex; flex-direction: column; + margin-block-start: 0; } .ui-agent-term-preview-full .ui-agent-term-preview-wrap.terminal-wrap { max-height: none; @@ -61,6 +57,14 @@ max-height: none; height: 100%; } +/* Flush to the header bar and the viewport sides; the input row below + gets its own padding so it doesn't touch the screen edge. */ +.ui-agent-term-preview-full .ui-agent-term-preview-wrap.terminal-wrap { + margin: 0; +} +.ui-agent-term-preview-full .ui-agent-input { + padding: 0.4em 0.75em 0.6em; +} /* `box-sizing: border-box`, not the browser's default `content-box` — `.live.terminal`'s own `padding: 0.8em 1em 0.4em` (terminal.css) is @@ -105,3 +109,12 @@ gap: 0.25em; max-width: calc(100% - 0.8em); } + +/* Full-tab mount's connection badge — top-left floating twin of + `.ui-agent-term-preview-header-badges` (top-right), same anchor. */ +.ui-agent-term-preview-live { + position: absolute; + top: 0.4em; + left: 0.4em; + z-index: 1; +} diff --git a/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.tsx b/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.tsx index 7d0f54e1..49c02c7d 100644 --- a/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.tsx +++ b/frontend/packages/swarm-ui/src/pages/agents/AgentTermPreview.tsx @@ -175,13 +175,16 @@ export function AgentTermPreview({ (fullHeight ? " ui-agent-term-preview-full" : "") } > -
- - {!fullHeight && ( + {/* The full-tab mount has no toolbar row: the terminal takes the + whole page below the shell header, and the connection badge + floats inside it with the other badges (below). */} + {!fullHeight && ( +
+ openTab({ href: paths.page, label: paths.label })} /> - )} -
+
+ )}
+ {/* Full-tab mount only: the connection badge floats in the + terminal's top-left corner (the other badges own the + top-right), instead of sitting in a toolbar row above it. */} + {fullHeight && ( + + )} {header && showHeaderBadges && (
— gives `AgentTermPreview`'s `fullHeight` flex - chain (`.ui-agent-term-preview-full`, AgentTermPreview.css) something - real to fill. `Shell.css`'s `.shell-body` has no fixed height of its - own (it's a plain flow column sized by content), so without an - explicit height here the chain's `flex: 1` would resolve against - nothing and the terminal would collapse to its content's own height - instead of filling the viewport — the opposite of what "full-height" - mount means for this page specifically (the 12em-capped preview - inside `AgentsPage` deliberately has no such wrapper; this is the one - place that needs it). - - `100dvh`, not `100vh` — `100vh` on a mobile browser includes the - address-bar chrome even while it's visibly on-screen, so the terminal - would render taller than the actually-visible viewport there; - `100dvh` tracks the real visible area instead. The subtraction below - is a fixed estimate (Shell.css's own `.shell-header` padding + - `.shell-body`'s), not a measured value — an exact fit isn't worth a - ResizeObserver for a single full-bleed page; a few pixels short of - the viewport edge reads as intentional breathing room, not a bug. */ +/* — fills `.shell-body-full` (Shell.css), which is + already everything below the header bar, full width and height. This + wrapper only has to pass that height down so `AgentTermPreview`'s + `fullHeight` flex chain (`.ui-agent-term-preview-full`) has a real box + to fill. */ .ui-agent-terminal-page { + flex: 1; + min-height: 0; display: flex; flex-direction: column; - height: calc(100dvh - 4.5em - 3em); } .ui-agent-terminal-page .ui-agent-term-preview-full { diff --git a/frontend/packages/swarm-ui/src/shell/Shell.css b/frontend/packages/swarm-ui/src/shell/Shell.css index 0393f5bc..56bee244 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.css +++ b/frontend/packages/swarm-ui/src/shell/Shell.css @@ -242,6 +242,27 @@ .shell-body-wide { max-width: 90em; } +/* Full-bleed route (the agent terminal): the shell becomes a viewport-high + column, the header keeps its natural height, and the body takes every + remaining pixel with no width cap or padding. `100dvh`, not `100vh` — + `vh` includes mobile browser chrome that is on screen. */ +.shell-full { + display: flex; + flex-direction: column; + height: 100dvh; +} +.shell-full .shell-header { + flex-shrink: 0; +} +.shell-body-full { + flex: 1; + min-height: 0; + max-width: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; +} @keyframes shell-page-enter { from { opacity: 0; diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index 6459c0b8..e8305219 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -63,19 +63,19 @@ const NAV_ITEMS: { href: string; label: string; accent: string }[] = [ const WIDE_BODY_ROUTES = new Set(["/issues", "/agents"]); // `/agents/:name/terminal` (and a subagent's, under -// `/agents/:name/subagents/:subagent/`) needs the same widening but can't join -// the `Set` above — it's a parameterized path, one literal string can't -// match every agent name. A 60em-capped terminal pane reads as cramped -// (this is a full tab, not a sidebar preview), same reasoning as the -// table routes above just for a different reason (a wide pane, not a -// wide table). -const WIDE_BODY_ROUTE_PATTERN = +// `/agents/:name/subagents/:subagent/`) is full-bleed: the terminal fills +// the whole viewport below the header bar, edge to edge (`shell-full` / +// `shell-body-full` in Shell.css). It can't join the `Set` above — it's a +// parameterized path, one literal string can't match every agent name. +const FULL_BLEED_ROUTE_PATTERN = /^\/agents\/[^/]+(\/subagents\/[^/]+)?\/terminal$/; +function isFullBleedRoute(location: string): boolean { + return FULL_BLEED_ROUTE_PATTERN.test(location); +} + function isWideBodyRoute(location: string): boolean { - return ( - WIDE_BODY_ROUTES.has(location) || WIDE_BODY_ROUTE_PATTERN.test(location) - ); + return WIDE_BODY_ROUTES.has(location); } // Static fallback — matches `index.html`'s `` default, so a page @@ -360,7 +360,7 @@ export function Shell({ children }: { children: ComponentChildren }) { const navAccent = indicator?.accent ?? "var(--purple)"; return ( - <div class="shell"> + <div class={"shell" + (isFullBleedRoute(location) ? " shell-full" : "")}> <header class="shell-header"> <span class="shell-brand" @@ -447,7 +447,9 @@ export function Shell({ children }: { children: ComponentChildren }) { apply, only the max-width cap changes. */} <div class={ - "shell-body" + (isWideBodyRoute(location) ? " shell-body-wide" : "") + "shell-body" + + (isWideBodyRoute(location) ? " shell-body-wide" : "") + + (isFullBleedRoute(location) ? " shell-body-full" : "") } key={location} >