diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index 19057f59..1f1b7746 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -35,6 +35,7 @@ import { SettingsMenu } from "@hive/shared/settings-menu.js"; import { HideDebugSetting } from "@hive/shared/hide-debug-setting.js"; import { useApplyThemeOverride } from "@hive/shared/theme-apply.js"; import { useApplyMotionOverride } from "@hive/shared/motion-apply.js"; +import { isFullBleedRoute, isWideBodyRoute } from "./routes.js"; import { useDynamicTabs } from "./useDynamicTabs.js"; import { useSwarmName } from "./useSwarmName.js"; import "./Shell.css"; @@ -58,38 +59,6 @@ const NAV_ITEMS: { href: string; label: string; accent: string }[] = [ { href: "/components", label: "components", accent: "var(--blue)" }, ]; -// Routes that opt out of `.shell-body`'s 60em readable-line-length cap -// — mara screenshotted the issue report's 8-column table getting -// clipped; that width is deliberate for the rest of the UI's cards/ -// forms, but too narrow for a wide table. `.shell-body-wide` (Shell.css) is a -// modifier alongside the base class, not a replacement, so every other -// route's layout is untouched. `/agents` joined for the same reason — -// its own table view (plus the list+detail split) hits the same cap. -const WIDE_BODY_ROUTES = new Set([ - "/issues", - "/agents", - "/review-runs", - "/review-tryout", -]); - -// `/agents/:name/terminal` (and a subagent's, under -// `/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$/; - -// The start page (`/`) is full-bleed too: its rain backdrop fills the whole -// body below the header. -function isFullBleedRoute(location: string): boolean { - return location === "/" || FULL_BLEED_ROUTE_PATTERN.test(location); -} - -function isWideBodyRoute(location: string): boolean { - return WIDE_BODY_ROUTES.has(location); -} - // Static fallback — matches `index.html`'s `` default, so a page // never flashes something else before the fetch below resolves, and an // operator who never set `services.hyperhive.swarm.name` sees the exact diff --git a/frontend/packages/swarm-ui/src/shell/routes.ts b/frontend/packages/swarm-ui/src/shell/routes.ts new file mode 100644 index 00000000..e897e126 --- /dev/null +++ b/frontend/packages/swarm-ui/src/shell/routes.ts @@ -0,0 +1,35 @@ +// Which layout a route gets inside the shell: the readable-width cap, the +// wide body, or full-bleed. Kept apart from `Shell.tsx` so it is plain +// functions of a path, with no DOM or CSS behind it, and can be tested. + +// Routes that opt out of `.shell-body`'s 60em readable-line-length cap +// — mara screenshotted the issue report's 8-column table getting +// clipped; that width is deliberate for the rest of the UI's cards/ +// forms, but too narrow for a wide table. `.shell-body-wide` (Shell.css) is a +// modifier alongside the base class, not a replacement, so every other +// route's layout is untouched. `/agents` joined for the same reason — +// its own table view (plus the list+detail split) hits the same cap. +const WIDE_BODY_ROUTES = new Set([ + "/issues", + "/agents", + "/review-runs", + "/review-tryout", +]); + +// `/agents/:name/terminal` (and a subagent's, under +// `/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$/; + +// The start page (`/`) is full-bleed too: its rain backdrop fills the whole +// body below the header. +export function isFullBleedRoute(location: string): boolean { + return location === "/" || FULL_BLEED_ROUTE_PATTERN.test(location); +} + +export function isWideBodyRoute(location: string): boolean { + return WIDE_BODY_ROUTES.has(location); +} diff --git a/frontend/packages/swarm-ui/test/routes.test.mjs b/frontend/packages/swarm-ui/test/routes.test.mjs new file mode 100644 index 00000000..ce10142c --- /dev/null +++ b/frontend/packages/swarm-ui/test/routes.test.mjs @@ -0,0 +1,42 @@ +// The shell's per-route layout: which paths are full-bleed, which get the +// wide body. Run by `npm test`. +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { isFullBleedRoute, isWideBodyRoute } from "../src/shell/routes.ts"; + +test("the start page is full-bleed", () => { + assert.equal(isFullBleedRoute("/"), true); +}); + +test("the hive directory moved off / and is not full-bleed", () => { + assert.equal(isFullBleedRoute("/hives"), false); + assert.equal(isWideBodyRoute("/hives"), false); +}); + +test("an agent's terminal and a subagent's are full-bleed", () => { + assert.equal(isFullBleedRoute("/agents/iris/terminal"), true); + assert.equal( + isFullBleedRoute("/agents/iris/subagents/scout-1/terminal"), + true, + ); +}); + +test("the agents page and other routes are not full-bleed", () => { + for (const path of ["/agents", "/agents/iris", "/jobs", "/issues"]) { + assert.equal(isFullBleedRoute(path), false, path); + } +}); + +test("a terminal path with extra segments is not full-bleed", () => { + assert.equal(isFullBleedRoute("/agents/iris/terminal/extra"), false); + assert.equal(isFullBleedRoute("/agents//terminal"), false); +}); + +test("the wide-body routes are exactly the table pages", () => { + for (const path of ["/issues", "/agents", "/review-runs", "/review-tryout"]) { + assert.equal(isWideBodyRoute(path), true, path); + } + for (const path of ["/", "/jobs", "/schedules", "/components"]) { + assert.equal(isWideBodyRoute(path), false, path); + } +});