diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index 51f30877..6275e3a7 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -9,8 +9,7 @@ domain, covers host-level detail for one hive. | route | what | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| `/` | the start page: the swarm's name over a digital-rain backdrop; the swarm name in the header links here | -| `/hives` | the hive directory, each hive with its last reported status | +| `/` | the hive directory, each hive with its last reported status | | `/agents` | every agent: status, config PR, wanted state, inference settings, resource caps; create agents, link forge, matrix and GitHub accounts | | `/agents//terminal` | one agent's live terminal | | `/agents//subagents//terminal` | one of that agent's subagents' live terminal | diff --git a/frontend/packages/swarm-ui/src/App.tsx b/frontend/packages/swarm-ui/src/App.tsx index b6c34ec2..d6432f10 100644 --- a/frontend/packages/swarm-ui/src/App.tsx +++ b/frontend/packages/swarm-ui/src/App.tsx @@ -11,7 +11,6 @@ import { IssueReportPage } from "./pages/IssueReportPage.js"; import { ReviewRunsPage } from "./pages/review-runs/ReviewRunsPage.js"; import { ReviewTryOutPage } from "./pages/review-tryout/ReviewTryOutPage.js"; import { SchedulesPage } from "./pages/schedules/SchedulesPage.js"; -import { StartPage } from "./pages/StartPage.js"; import { Panel } from "./ui/panel/Panel.js"; function NotFound() { @@ -26,8 +25,7 @@ export function App() { return ( - - + — the hive roster, `/hives`' content. Fetches +// — the hive roster, `/`'s content. Fetches // swarm-controller's `GET /api/hives/status`, the swarm-wide status // aggregate: one row per roster hive, freshness derived at read time // from the status bucket. Supersedes the earlier `GET /api/hives` + diff --git a/frontend/packages/swarm-ui/src/pages/StartPage.css b/frontend/packages/swarm-ui/src/pages/StartPage.css deleted file mode 100644 index d7d045cd..00000000 --- a/frontend/packages/swarm-ui/src/pages/StartPage.css +++ /dev/null @@ -1,23 +0,0 @@ -/* The page fills the full-bleed body; the rain canvas fills the page and the - * name sits centred over it. */ -.start-page { - position: relative; - flex: 1; - min-height: 0; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} -.start-page-name { - position: relative; - margin: 0; - padding: 0 1em; - text-align: center; - font-size: clamp(2em, 8vw, 5em); - font-weight: 600; - color: var(--purple); - text-shadow: - 0 0 0.4em color-mix(in srgb, var(--purple) 55%, transparent), - 0 0 1.2em var(--bg); -} diff --git a/frontend/packages/swarm-ui/src/pages/StartPage.tsx b/frontend/packages/swarm-ui/src/pages/StartPage.tsx deleted file mode 100644 index 1f0162bc..00000000 --- a/frontend/packages/swarm-ui/src/pages/StartPage.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// — `/`, the swarm's landing page: the swarm's name over the -// digital-rain backdrop, and nothing else. The header's swarm name links -// here. The hive roster that used to be `/` is now `/hives`. -import { MatrixRain } from "../shell/MatrixRain.js"; -import { useSwarmName } from "../shell/useSwarmName.js"; -import "./StartPage.css"; - -// Same fallback as the header and the page title, so an unnamed swarm shows -// one label everywhere. -const DEFAULT_NAME = "hyperhive swarm"; - -export function StartPage() { - const name = useSwarmName() ?? DEFAULT_NAME; - return ( -
- -

{name}

-
- ); -} diff --git a/frontend/packages/swarm-ui/src/shell/MatrixRain.css b/frontend/packages/swarm-ui/src/shell/MatrixRain.css deleted file mode 100644 index 12ef9a37..00000000 --- a/frontend/packages/swarm-ui/src/shell/MatrixRain.css +++ /dev/null @@ -1,9 +0,0 @@ -/* Fills the nearest positioned ancestor, dimmed so it reads as backdrop. */ -.matrix-rain { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - opacity: 0.32; - pointer-events: none; -} diff --git a/frontend/packages/swarm-ui/src/shell/MatrixRain.tsx b/frontend/packages/swarm-ui/src/shell/MatrixRain.tsx deleted file mode 100644 index da960376..00000000 --- a/frontend/packages/swarm-ui/src/shell/MatrixRain.tsx +++ /dev/null @@ -1,133 +0,0 @@ -// — the falling-glyph "digital rain" backdrop, drawn on a canvas -// that fills its positioned parent. Purely decorative, so it is hidden from -// assistive tech and takes no pointer events. The glyphs take the live -// `--green` and the fade trail the live `--bg`, so a theme swap recolours it. -// Stepped on an interval (the steppy cadence is the look, and it costs -// little), paused while the tab is hidden, and stopped (and cleared) when -// motion is reduced: the settings menu's `data-motion` override wins over -// `prefers-reduced-motion`, and is followed live. The same effect as the hive dashboard's home -// page, which is plain JS over a fixed canvas. -import { useEffect, useRef } from "preact/hooks"; -import { - CELL, - columnsFor, - fadeColorOf, - glyphFor, - rainAllowed, - startDrops, - stepDrops, -} from "./rain.js"; -import "./MatrixRain.css"; - -const STEP_MS = 55; - -// `getComputedStyle` on a custom property returns its declared value -// unresolved (`var(--x)`), so resolve a concrete rgb() by computing a real -// `color` on a probe element instead. -function resolveColor(varName: string, fallback: string): string { - const probe = document.createElement("span"); - probe.style.color = `var(${varName})`; - probe.style.display = "none"; - document.body.appendChild(probe); - const c = getComputedStyle(probe).color; - probe.remove(); - return /^rgb/.test(c) ? c : fallback; -} - -export function MatrixRain() { - const ref = useRef(null); - - useEffect(() => { - const canvas = ref.current; - const ctx = canvas?.getContext("2d"); - if (!canvas || !ctx) return; - - // Resolved again whenever the theme or motion setting can have changed: - // the shell applies its overrides (`data-theme`, `data-motion`) from an - // effect that runs after this one, and a later switch from the settings - // menu changes them too. - let glyphColor = ""; - let fadeColor = ""; - function resolveColors() { - glyphColor = resolveColor("--green", "rgb(166, 227, 161)"); - fadeColor = fadeColorOf(resolveColor("--bg", "rgb(30, 30, 46)")); - } - resolveColors(); - const motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); - const schemeQuery = window.matchMedia("(prefers-color-scheme: dark)"); - - let cols = 0; - let drops: number[] = []; - - function resize() { - if (!canvas || !ctx) return; - canvas.width = canvas.offsetWidth; - canvas.height = canvas.offsetHeight; - cols = columnsFor(canvas.width); - drops = startDrops(cols, Math.random); - ctx.font = `${CELL}px monospace`; // a canvas resize resets the font - } - - function tick() { - if (!canvas || !ctx) return; - ctx.fillStyle = fadeColor; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = glyphColor; - for (let i = 0; i < cols; i++) { - ctx.fillText(glyphFor(Math.random), i * CELL, (drops[i] ?? 0) * CELL); - } - drops = stepDrops(drops, canvas.height, Math.random); - } - - const allowed = () => - rainAllowed(document.documentElement.dataset.motion, motionQuery.matches); - - let timer: number | null = null; - const play = () => { - if (timer === null) timer = window.setInterval(tick, STEP_MS); - }; - const pause = () => { - if (timer !== null) { - window.clearInterval(timer); - timer = null; - } - }; - - // Runs the rain when motion is allowed and the tab is visible; otherwise - // stops it and clears what was drawn. Safe to call on any change. - function sync() { - if (allowed() && !document.hidden) { - play(); - } else { - pause(); - if (!allowed() && canvas && ctx) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - } - } - } - const onSettingsChange = () => { - resolveColors(); - sync(); - }; - - const watch = new MutationObserver(onSettingsChange); - watch.observe(document.documentElement, { attributes: true }); - schemeQuery.addEventListener("change", onSettingsChange); - motionQuery.addEventListener("change", onSettingsChange); - - resize(); - window.addEventListener("resize", resize); - document.addEventListener("visibilitychange", sync); - sync(); - return () => { - pause(); - watch.disconnect(); - schemeQuery.removeEventListener("change", onSettingsChange); - motionQuery.removeEventListener("change", onSettingsChange); - window.removeEventListener("resize", resize); - document.removeEventListener("visibilitychange", sync); - }; - }, []); - - return