swarm-ui: give every tab the wide body
The shell had two body widths, a 60em readable column and a 90em wide body that a short list of table routes opted into. Every route now gets the 90em body, and the opt-in route list, its modifier class and its test go away.
This commit is contained in:
parent
0e7aa74d00
commit
89efda5ea2
4 changed files with 14 additions and 51 deletions
|
|
@ -229,8 +229,10 @@
|
||||||
background: color-mix(in srgb, var(--fg) 10%, transparent);
|
background: color-mix(in srgb, var(--fg) 10%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* One width for every tab's contents: 90em still centers with visible margins
|
||||||
|
on a typical desktop viewport; it isn't full-bleed. */
|
||||||
.shell-body {
|
.shell-body {
|
||||||
max-width: 60em;
|
max-width: 90em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1.5em 1.25em;
|
padding: 1.5em 1.25em;
|
||||||
/* Plain fade, deliberately — mara's call on review: a scale+overshoot
|
/* Plain fade, deliberately — mara's call on review: a scale+overshoot
|
||||||
|
|
@ -239,15 +241,6 @@
|
||||||
having shipped something to walk back first. */
|
having shipped something to walk back first. */
|
||||||
animation: shell-page-enter 160ms ease;
|
animation: shell-page-enter 160ms ease;
|
||||||
}
|
}
|
||||||
/* Opt-in widening for a route that genuinely needs more than a
|
|
||||||
readable-line-length column (see Shell.tsx's `WIDE_BODY_ROUTES`).
|
|
||||||
Only overrides `max-width` — margin/padding/animation stay
|
|
||||||
`.shell-body`'s, so this reads as "the same body, just wider" rather
|
|
||||||
than a parallel layout to keep in sync. 90em still centers with
|
|
||||||
visible margins on a typical desktop viewport; it isn't full-bleed. */
|
|
||||||
.shell-body-wide {
|
|
||||||
max-width: 90em;
|
|
||||||
}
|
|
||||||
/* Full-bleed route (the agent terminal): the shell becomes a viewport-high
|
/* Full-bleed route (the agent terminal): the shell becomes a viewport-high
|
||||||
column, the header keeps its natural height, and the body takes every
|
column, the header keeps its natural height, and the body takes every
|
||||||
remaining pixel with no width cap or padding. `100dvh`, not `100vh` —
|
remaining pixel with no width cap or padding. `100dvh`, not `100vh` —
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import { SettingsMenu } from "@hive/shared/settings-menu.js";
|
||||||
import { HideDebugSetting } from "@hive/shared/hide-debug-setting.js";
|
import { HideDebugSetting } from "@hive/shared/hide-debug-setting.js";
|
||||||
import { useApplyThemeOverride } from "@hive/shared/theme-apply.js";
|
import { useApplyThemeOverride } from "@hive/shared/theme-apply.js";
|
||||||
import { useApplyMotionOverride } from "@hive/shared/motion-apply.js";
|
import { useApplyMotionOverride } from "@hive/shared/motion-apply.js";
|
||||||
import { isFullBleedRoute, isWideBodyRoute } from "./routes.js";
|
import { isFullBleedRoute } from "./routes.js";
|
||||||
import { useDynamicTabs } from "./useDynamicTabs.js";
|
import { useDynamicTabs } from "./useDynamicTabs.js";
|
||||||
import { useSwarmName } from "./useSwarmName.js";
|
import { useSwarmName } from "./useSwarmName.js";
|
||||||
import "./Shell.css";
|
import "./Shell.css";
|
||||||
|
|
@ -136,8 +136,7 @@ function NavLink({
|
||||||
// One open agent-terminal tab (`useDynamicTabs`) — deliberately
|
// One open agent-terminal tab (`useDynamicTabs`) — deliberately
|
||||||
// its own small component, not folded into `NavLink` above: dynamic tabs
|
// its own small component, not folded into `NavLink` above: dynamic tabs
|
||||||
// don't ride the sliding `.shell-nav-indicator` (there's no fixed set to
|
// don't ride the sliding `.shell-nav-indicator` (there's no fixed set to
|
||||||
// hop between — see `WIDE_BODY_ROUTE_PATTERN`'s own comment on why they
|
// hop between, so they can't share `NAV_ITEMS`'s fixed route list either), so "active"
|
||||||
// can't share `NAV_ITEMS`'s Set-based route list either), so "active"
|
|
||||||
// here is a plain class toggle, not a measured position. The close ×
|
// here is a plain class toggle, not a measured position. The close ×
|
||||||
// is a real sibling button, not nested inside the `<Link>` — a button
|
// is a real sibling button, not nested inside the `<Link>` — a button
|
||||||
// inside an `<a>` is invalid HTML and (worse) means a click anywhere on
|
// inside an `<a>` is invalid HTML and (worse) means a click anywhere on
|
||||||
|
|
@ -418,15 +417,10 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{/* Keyed by route so it remounts (and replays its entrance
|
{/* Keyed by route so it remounts (and replays its entrance
|
||||||
animation) on every navigation — see the file-top comment.
|
animation) on every navigation — see the file-top comment. */}
|
||||||
`shell-body-wide` is additive (see `WIDE_BODY_ROUTES` above),
|
|
||||||
not a swap — `.shell-body`'s padding/animation rules still
|
|
||||||
apply, only the max-width cap changes. */}
|
|
||||||
<div
|
<div
|
||||||
class={
|
class={
|
||||||
"shell-body" +
|
"shell-body" + (isFullBleedRoute(location) ? " shell-body-full" : "")
|
||||||
(isWideBodyRoute(location) ? " shell-body-wide" : "") +
|
|
||||||
(isFullBleedRoute(location) ? " shell-body-full" : "")
|
|
||||||
}
|
}
|
||||||
key={location}
|
key={location}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,12 @@
|
||||||
// Which layout a route gets inside the shell: the readable-width cap, the
|
// Which routes are full-bleed inside the shell; every other route gets the
|
||||||
// wide body, or full-bleed. Kept apart from `Shell.tsx` so it is plain
|
// same centred body. Kept apart from `Shell.tsx` so it is a plain function of
|
||||||
// functions of a path, with no DOM or CSS behind it, and can be tested.
|
// 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"]);
|
|
||||||
|
|
||||||
// `/agents/:name/terminal` (and a subagent's, under
|
// `/agents/:name/terminal` (and a subagent's, under
|
||||||
// `/agents/:name/subagents/:subagent/`) is full-bleed: the terminal fills
|
// `/agents/:name/subagents/:subagent/`) is full-bleed: the terminal fills
|
||||||
// the whole viewport below the header bar, edge to edge (`shell-full` /
|
// 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
|
// `shell-body-full` in Shell.css). It's a parameterized path, so one literal
|
||||||
// parameterized path, one literal string can't match every agent name.
|
// string can't match every agent name.
|
||||||
const FULL_BLEED_ROUTE_PATTERN =
|
const FULL_BLEED_ROUTE_PATTERN =
|
||||||
/^\/agents\/[^/]+(\/subagents\/[^/]+)?\/terminal$/;
|
/^\/agents\/[^/]+(\/subagents\/[^/]+)?\/terminal$/;
|
||||||
|
|
||||||
|
|
@ -24,7 +15,3 @@ const FULL_BLEED_ROUTE_PATTERN =
|
||||||
export function isFullBleedRoute(location: string): boolean {
|
export function isFullBleedRoute(location: string): boolean {
|
||||||
return location === "/" || FULL_BLEED_ROUTE_PATTERN.test(location);
|
return location === "/" || FULL_BLEED_ROUTE_PATTERN.test(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWideBodyRoute(location: string): boolean {
|
|
||||||
return WIDE_BODY_ROUTES.has(location);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// The shell's per-route layout: which paths are full-bleed, which get the
|
// The shell's per-route layout: which paths are full-bleed. Run by `npm test`.
|
||||||
// wide body. Run by `npm test`.
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { test } from "node:test";
|
import { test } from "node:test";
|
||||||
import { isFullBleedRoute, isWideBodyRoute } from "../src/shell/routes.ts";
|
import { isFullBleedRoute } from "../src/shell/routes.ts";
|
||||||
|
|
||||||
test("the start page is full-bleed", () => {
|
test("the start page is full-bleed", () => {
|
||||||
assert.equal(isFullBleedRoute("/"), true);
|
assert.equal(isFullBleedRoute("/"), true);
|
||||||
|
|
@ -10,7 +9,6 @@ test("the start page is full-bleed", () => {
|
||||||
|
|
||||||
test("the hive directory moved off / and is not full-bleed", () => {
|
test("the hive directory moved off / and is not full-bleed", () => {
|
||||||
assert.equal(isFullBleedRoute("/hives"), false);
|
assert.equal(isFullBleedRoute("/hives"), false);
|
||||||
assert.equal(isWideBodyRoute("/hives"), false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("an agent's terminal and a subagent's are full-bleed", () => {
|
test("an agent's terminal and a subagent's are full-bleed", () => {
|
||||||
|
|
@ -31,12 +29,3 @@ test("a terminal path with extra segments is not full-bleed", () => {
|
||||||
assert.equal(isFullBleedRoute("/agents/iris/terminal/extra"), false);
|
assert.equal(isFullBleedRoute("/agents/iris/terminal/extra"), false);
|
||||||
assert.equal(isFullBleedRoute("/agents//terminal"), false);
|
assert.equal(isFullBleedRoute("/agents//terminal"), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("the wide-body routes are exactly the table pages", () => {
|
|
||||||
for (const path of ["/issues", "/agents", "/review"]) {
|
|
||||||
assert.equal(isWideBodyRoute(path), true, path);
|
|
||||||
}
|
|
||||||
for (const path of ["/", "/jobs", "/schedules", "/components"]) {
|
|
||||||
assert.equal(isWideBodyRoute(path), false, path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue