hyperhive/frontend/packages/swarm-ui/src/shell/Shell.tsx
iris 527ed8f3e2 swarm-ui: add a jobs tab, and type JobqGraph as real TypeScript
New /jobs route in swarm-ui, reusing the shared JobqGraph component
against swarm-controller's own GET /api/jobq/graph (same wire shape
hive-c0re's dashboard already consumes, different endpoint, no fork).

Converted JobqGraph.jsx to JobqGraph.tsx with real prop/wire types
(mirrors hive_jobq_wire's GraphNode/GraphDep/State by hand) instead of
a hand-maintained ambient .d.ts at the swarm-ui consumer side — the
.d.ts would duplicate the prop list and drift from the source the
moment the component's signature changes without the declaration being
touched. Both dashboard (untyped consumer, esbuild strips types) and
swarm-ui (tsc --noEmit) build/typecheck clean off the one file.
2026-08-16 17:30:06 +02:00

50 lines
1.9 KiB
TypeScript

// <Shell> — page chrome every swarm-ui route mounts inside: a header
// bar (swarm branding) plus a nav row linking the app's real routes.
// Preact-native styling — `./Shell.css` imported right here, not
// wired centrally, so the component and its styles travel together
// (esbuild folds every imported `.css` reachable from `main.tsx` into
// one `main.css` companion output, see build.mjs) — deliberately not
// @hive/shared's chrome.css page-header pattern, which is the per-hive
// MPA dashboard's visual language. This package is a clean field, not
// an inheritor of that look.
//
// Route list lives here, not prop-drilled from App — one small SPA
// has exactly one place that needs to know its own nav, and this is
// it. Grows additively as real routes land (a swarm-wide agent roster
// is next); no speculative entries.
import type { ComponentChildren } from 'preact';
import { Link, useRoute } from 'wouter-preact';
import { LinksMenu } from './LinksMenu.js';
import './Shell.css';
const NAV_ITEMS: { href: string; label: string }[] = [
{ href: '/', label: 'overview' },
{ href: '/jobs', label: 'jobs' },
{ href: '/components', label: 'components' },
];
function NavLink({ href, label }: { href: string; label: string }) {
const [active] = useRoute(href);
return (
<Link href={href} className={'shell-nav-link' + (active ? ' shell-nav-link-active' : '')}>
{label}
</Link>
);
}
export function Shell({ children }: { children: ComponentChildren }) {
return (
<div class="shell">
<header class="shell-header">
<span class="shell-brand">hyperhive swarm</span>
<nav class="shell-nav">
{NAV_ITEMS.map((item) => (
<NavLink key={item.href} href={item.href} label={item.label} />
))}
</nav>
<LinksMenu />
</header>
<div class="shell-body">{children}</div>
</div>
);
}