swarm-ui: hives are a folded card on the agents page, and hide developer details also hides the components tab
The hives nav tab and route go, with no redirect; the roster is a card above the agents panel that starts folded and is read on the first open. The settings toggle is renamed from hide debug output to hide developer details, as it now also drops the components tab (the page still answers at /components); the stored preference keeps its key.
This commit is contained in:
parent
0cbfa806d3
commit
70917b7dd0
1 changed files with 110 additions and 49 deletions
|
|
@ -31,6 +31,7 @@ import type { ComponentChildren } from "preact";
|
|||
import { Link, useLocation } from "wouter-preact";
|
||||
import { LinksMenu } from "./LinksMenu.js";
|
||||
import { UserMenu } from "./UserMenu.js";
|
||||
import { useHideDebug } from "./useHideDebug.js";
|
||||
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";
|
||||
|
|
@ -49,7 +50,6 @@ const THEME_KEY = "swarm-ui:theme-override";
|
|||
const MOTION_KEY = "swarm-ui:motion-override";
|
||||
|
||||
const NAV_ITEMS: { href: string; label: string; accent: string }[] = [
|
||||
{ href: "/hives", label: "hives", accent: "var(--purple)" },
|
||||
{ href: "/agents", label: "agents", accent: "var(--green)" },
|
||||
{ href: "/alerts", label: "alerts", accent: "var(--red)" },
|
||||
{ href: "/jobs", label: "jobs", accent: "var(--pink)" },
|
||||
|
|
@ -209,6 +209,13 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
// rather than reusing `NavLink`.
|
||||
const { tabs, close: closeTab } = useDynamicTabs();
|
||||
|
||||
// The components tab is a developer page: it goes while "hide developer
|
||||
// details" is on. The page itself still answers at `/components`.
|
||||
const hideDeveloper = useHideDebug();
|
||||
const navItems = hideDeveloper
|
||||
? NAV_ITEMS.filter((item) => item.href !== "/components")
|
||||
: NAV_ITEMS;
|
||||
|
||||
// Applied once here, not inside `SettingsMenu` — every route mounts
|
||||
// through this one `<Shell>`, so the override takes effect regardless
|
||||
// of which page is showing or whether the menu's ever been opened,
|
||||
|
|
@ -226,7 +233,7 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
// scroll-offset bookkeeping needed.
|
||||
function measureIndex(index: number): IndicatorRect | null {
|
||||
const navEl = navRef.current;
|
||||
const item = NAV_ITEMS[index];
|
||||
const item = navItems[index];
|
||||
const textEl = item ? textRefs.current[item.href] : null;
|
||||
if (!navEl || !item || !textEl) return null;
|
||||
const navRect = navEl.getBoundingClientRect();
|
||||
|
|
@ -245,8 +252,14 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
// [c's colour]"). Re-runs on navigation; a separate resize listener
|
||||
// below re-measures the already-settled position without re-hopping
|
||||
// (a reflow isn't a navigation).
|
||||
// The list changed (the components tab came or went), so the settled index
|
||||
// no longer names the same item: land directly on the active one again.
|
||||
useEffect(() => {
|
||||
const targetIndex = NAV_ITEMS.findIndex((item) => item.href === location);
|
||||
settledIndexRef.current = -1;
|
||||
}, [hideDeveloper]);
|
||||
|
||||
useEffect(() => {
|
||||
const targetIndex = navItems.findIndex((item) => item.href === location);
|
||||
if (targetIndex === -1) {
|
||||
settledIndexRef.current = -1;
|
||||
setIndicator(null);
|
||||
|
|
@ -278,7 +291,7 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
cancelled = true;
|
||||
if (timeoutId !== undefined) window.clearTimeout(timeoutId);
|
||||
};
|
||||
}, [location]);
|
||||
}, [location, hideDeveloper]);
|
||||
|
||||
// Fires the render *after* the indicator's first non-null commit —
|
||||
// deliberately one tick behind, not folded into the effect above,
|
||||
|
|
@ -323,7 +336,7 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
// label its page registered as a dynamic tab. An unmatched path gets
|
||||
// the bare brand.
|
||||
const pageLabel =
|
||||
NAV_ITEMS.find((item) => item.href === location)?.label ??
|
||||
navItems.find((item) => item.href === location)?.label ??
|
||||
tabs.find((tab) => tab.href === location)?.label;
|
||||
useEffect(() => {
|
||||
document.title = pageLabel ? `${pageLabel} · ${brand}` : brand;
|
||||
|
|
@ -355,7 +368,7 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
</span>
|
||||
</Link>
|
||||
<nav class="shell-nav" ref={navRef}>
|
||||
{NAV_ITEMS.map((item) => (
|
||||
{navItems.map((item) => (
|
||||
<NavLink
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
|
|
|
|||
Loading…
Reference in a new issue