From be6036b2165e9ea08499ee0685cc4b1ebfac4b0c Mon Sep 17 00:00:00 2001 From: iris Date: Mon, 12 Oct 2026 20:15:56 +0200 Subject: [PATCH] swarm-ui: a hide debug output toggle in the header's settings menu The row moves from the per-agent page into @hive/shared so both pages mount the same one. The terminal's debug rows now follow the preference as it changes, so rows already on screen hide or show at once instead of only the rows that arrive after the toggle. --- docs/swarm/ui.md | 6 +++++ docs/web-ui/terminal-rendering.md | 7 +++-- frontend/packages/agent/src/Root.tsx | 2 +- .../agent/src/components/HideDebugSetting.tsx | 25 ------------------ frontend/packages/shared/package.json | 1 + frontend/packages/shared/src/prefs.ts | 26 ++++++++++++++++--- .../shared/src/settings/HideDebugSetting.tsx | 26 +++++++++++++++++++ frontend/packages/shared/src/terminal/Row.tsx | 26 ++++++++++++++++--- .../packages/swarm-ui/src/shell/Shell.tsx | 5 +++- 9 files changed, 88 insertions(+), 36 deletions(-) delete mode 100644 frontend/packages/agent/src/components/HideDebugSetting.tsx create mode 100644 frontend/packages/shared/src/settings/HideDebugSetting.tsx diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index e6c3ce37..71258581 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -35,6 +35,12 @@ An agent's terminal opens with its last 100 rows from the past 24 hours, then follows new ones; a reconnect picks up after the last row it showed. A subagent terminal shows rows published while it's open, and has no turn-state badges. +The settings menu in the header has a **hide debug output** toggle. It drops +the muted debug rows (thinking, coalesced ticks, harness chatter) from every +terminal, the preview and the tabs, and rows already on screen hide or show as +you flip it. The browser keeps the choice for the swarm UI, apart from the +per-agent pages'. + A terminal's own tab takes the agent's icon as its browser-tab icon, so tabs of different agents tell apart. A subagent's tab shows its agent's icon. An agent with no icon keeps the hyperhive mark. diff --git a/docs/web-ui/terminal-rendering.md b/docs/web-ui/terminal-rendering.md index 1eb32c31..aae8b9af 100644 --- a/docs/web-ui/terminal-rendering.md +++ b/docs/web-ui/terminal-rendering.md @@ -53,8 +53,11 @@ that motivated the change. The settings menu's "hide debug output" toggle (`getHideDebugPref()`, `@hive/shared/prefs.js`) skips `debug`-level rows entirely rather than -muting them further — client-side only, same live-read-no-reload shape -as the expand-tool-output preference above. +muting them further — client-side only, no reload. The per-agent page and +the swarm UI both have the toggle, as one shared row +(`@hive/shared/hide-debug-setting.js`). Each browser origin keeps its own +setting. A `debug` row follows the toggle as it changes, so rows already +on screen hide or show at once, not only the rows that arrive after it. Per-tool icon + summary formatting (what a `Read`/`Edit`/`send` call's row actually says) lives in `stream_enrich.rs`'s `fmt_tool_use()` family — diff --git a/frontend/packages/agent/src/Root.tsx b/frontend/packages/agent/src/Root.tsx index bf98c97c..f4eec760 100644 --- a/frontend/packages/agent/src/Root.tsx +++ b/frontend/packages/agent/src/Root.tsx @@ -12,7 +12,7 @@ import { useApplyMotionOverride } from "@hive/shared/motion-apply.js"; import { Header } from "./components/Header.js"; import { MetaNav } from "./components/MetaNav.js"; import { ExpandDetailsSetting } from "./components/ExpandDetailsSetting.js"; -import { HideDebugSetting } from "./components/HideDebugSetting.js"; +import { HideDebugSetting } from "@hive/shared/hide-debug-setting.js"; import { StatusChips } from "./components/StatusChips.js"; import { LiveStream, type LiveStreamHandle } from "./components/LiveStream.js"; import { HeaderPill } from "./components/HeaderPill.js"; diff --git a/frontend/packages/agent/src/components/HideDebugSetting.tsx b/frontend/packages/agent/src/components/HideDebugSetting.tsx deleted file mode 100644 index bff063ae..00000000 --- a/frontend/packages/agent/src/components/HideDebugSetting.tsx +++ /dev/null @@ -1,25 +0,0 @@ -// Agent-terminal-only settings-menu row: "hide debug output". Same shape -// as `ExpandDetailsSetting` (its own file's header comment explains why -// this lives here rather than as a `SettingsMenu` prop) — mara: "add a -// setting to not display verbose output... like the grey colored debug -// stuff." -import { useState } from "preact/hooks"; -import { getHideDebugPref, setHideDebugPref } from "@hive/shared/prefs.js"; - -export function HideDebugSetting() { - const [hideDebug, setHideDebugState] = useState(() => getHideDebugPref()); - return ( - - ); -} diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 2e98b15a..47a8ba34 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -52,6 +52,7 @@ "./theme-apply.js": "./src/settings/theme-apply.ts", "./motion-apply.js": "./src/settings/motion-apply.ts", "./settings-menu.js": "./src/settings/SettingsMenu.tsx", + "./hide-debug-setting.js": "./src/settings/HideDebugSetting.tsx", "./settings-menu.css": "./src/settings/SettingsMenu.css" }, "files": [ diff --git a/frontend/packages/shared/src/prefs.ts b/frontend/packages/shared/src/prefs.ts index 4ba17362..c542e1df 100644 --- a/frontend/packages/shared/src/prefs.ts +++ b/frontend/packages/shared/src/prefs.ts @@ -2,9 +2,9 @@ // so a future second consumer (e.g. swarm-ui's own terminal) gets the // exact same key name for free via the browser's shared per-origin // storage, rather than an independent copy that could drift. -// `ExpandDetailsSetting` / `HideDebugSetting` (the per-agent page's own -// settings popover rows) are the only writers today; `Row.tsx` is the -// only reader of either. +// `ExpandDetailsSetting` / `HideDebugSetting` (the settings popover rows +// of the per-agent page and of the swarm UI) are the only writers today; +// `Row.tsx` is the only reader of either. const EXPAND_DETAILS_KEY = "hive-agent-expand-details"; @@ -53,4 +53,24 @@ export function setHideDebugPref(v: boolean): void { } catch { /* localStorage unavailable — preference is session-only */ } + for (const listener of hideDebugListeners) listener(); +} + +const hideDebugListeners = new Set<() => void>(); + +// Calls `listener` whenever the preference changes: in this tab through +// `setHideDebugPref`, in another tab of the same origin through the +// browser's `storage` event. Returns the unsubscribe. This is what lets a +// terminal that is already showing rows follow the toggle, instead of only +// the rows that arrive after it. +export function subscribeHideDebugPref(listener: () => void): () => void { + hideDebugListeners.add(listener); + const onStorage = (e: StorageEvent) => { + if (e.key === HIDE_DEBUG_KEY || e.key === null) listener(); + }; + window.addEventListener("storage", onStorage); + return () => { + hideDebugListeners.delete(listener); + window.removeEventListener("storage", onStorage); + }; } diff --git a/frontend/packages/shared/src/settings/HideDebugSetting.tsx b/frontend/packages/shared/src/settings/HideDebugSetting.tsx new file mode 100644 index 00000000..b0c98025 --- /dev/null +++ b/frontend/packages/shared/src/settings/HideDebugSetting.tsx @@ -0,0 +1,26 @@ +// Settings-menu row: "hide debug output". Rendered as a `` +// child (`./SettingsMenu.js`) by each page that has a terminal, the +// per-agent page and the swarm UI, rather than as a prop on the menu: the +// caller owns its row. Both read the same preference (`../prefs.js`), which +// `../terminal/Row.tsx` follows as it changes — mara: "add a setting to not +// display verbose output... like the grey colored debug stuff." +import { useState } from "preact/hooks"; +import { getHideDebugPref, setHideDebugPref } from "../prefs.js"; + +export function HideDebugSetting() { + const [hideDebug, setHideDebugState] = useState(() => getHideDebugPref()); + return ( + + ); +} diff --git a/frontend/packages/shared/src/terminal/Row.tsx b/frontend/packages/shared/src/terminal/Row.tsx index 8109213e..a199365d 100644 --- a/frontend/packages/shared/src/terminal/Row.tsx +++ b/frontend/packages/shared/src/terminal/Row.tsx @@ -10,11 +10,15 @@ // Shared between `@hive/agent`'s local stream and `@hive/swarm-ui`'s // relay consumer (see `./termMsg.js`'s doc) — both hand this the same // `TermRow` shape regardless of which wire framing they read it off of. -import { useEffect, useRef } from "preact/hooks"; +import { useEffect, useRef, useState } from "preact/hooks"; import type { TermRow } from "./termMsg.js"; import { linkifyToNodes } from "./linkify.js"; import { renderMarkdown } from "./markdown.js"; -import { getExpandDetailsPref, getHideDebugPref } from "../prefs.js"; +import { + getExpandDetailsPref, + getHideDebugPref, + subscribeHideDebugPref, +} from "../prefs.js"; function MarkdownBody({ text }: { text: string }) { const ref = useRef(null); @@ -54,13 +58,27 @@ function DiffBody({ text }: { text: string }) { ); } -export function Row({ row }: { row: TermRow }) { +// A `debug` row, which follows the "hide debug output" preference as it +// changes. Only debug rows subscribe, so a long terminal does not hold a +// listener per row. +function DebugRow({ row }: { row: TermRow }) { + const [hidden, setHidden] = useState(() => getHideDebugPref()); + useEffect( + () => subscribeHideDebugPref(() => setHidden(getHideDebugPref())), + [], + ); // "hide debug output" is a skip, not a dimmer — a hidden debug row // still occupies no DOM node at all, same as it never streamed, rather // than a CSS `display: none` that would keep it in the layout/DOM for // no benefit. - if (row.level === "debug" && getHideDebugPref()) return null; + return hidden ? null : ; +} +export function Row({ row }: { row: TermRow }) { + return row.level === "debug" ? : ; +} + +function RowBody({ row }: { row: TermRow }) { const cssClass = "level-" + row.level; const icon = row.icon != null && row.icon !== "" && ( {row.icon} diff --git a/frontend/packages/swarm-ui/src/shell/Shell.tsx b/frontend/packages/swarm-ui/src/shell/Shell.tsx index 324d7ad9..7cab64c1 100644 --- a/frontend/packages/swarm-ui/src/shell/Shell.tsx +++ b/frontend/packages/swarm-ui/src/shell/Shell.tsx @@ -32,6 +32,7 @@ import { Link, useLocation } from "wouter-preact"; import { LinksMenu } from "./LinksMenu.js"; import { UserMenu } from "./UserMenu.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"; import { useApplyMotionOverride } from "@hive/shared/motion-apply.js"; import { useDynamicTabs } from "./useDynamicTabs.js"; @@ -444,7 +445,9 @@ export function Shell({ children }: { children: ComponentChildren }) { `margin-left: auto` split the available space between them instead of sitting flush together at the right edge. */}
- + + +