From ec51ec924ace5017f59c5201759cb69877945c29 Mon Sep 17 00:00:00 2001 From: iris Date: Fri, 11 Sep 2026 16:57:15 +0200 Subject: [PATCH] agent: add ?hide= URL param to suppress header/composer chrome For embedding companions (e.g. trollshell WebKitGTK) that supply their own header/input chrome around the per-agent page and only want the raw terminal feed. Comma-separated so both axes can hide in one param: ?hide=header,input. Zeroes --agent-header-h/--agent-composer-h via body classes rather than conditionally computing padding, so every dependent calc() (overlay offset, terminal scroll padding) collapses in one place. --- frontend/packages/agent/src/Root.tsx | 145 +++++++++++++++----------- frontend/packages/agent/src/agent.css | 13 +++ 2 files changed, 97 insertions(+), 61 deletions(-) diff --git a/frontend/packages/agent/src/Root.tsx b/frontend/packages/agent/src/Root.tsx index 23d7fed4..dacd3424 100644 --- a/frontend/packages/agent/src/Root.tsx +++ b/frontend/packages/agent/src/Root.tsx @@ -75,6 +75,14 @@ function tokenTotal(u: TokenUsage | null): number | null { ); } +// `?hide=header,input` — an embedding companion window (e.g. a native +// desktop shell) supplies its own header/composer chrome around this +// page, so it asks for just the terminal feed. Read once per load (the +// value can't change without a navigation); comma-separated so both +// axes can be hidden in one param. +const hideParam = new URLSearchParams(window.location.search).get("hide"); +const HIDDEN = new Set(hideParam ? hideParam.split(",").filter(Boolean) : []); + export function Root() { useApplyThemeOverride(THEME_KEY); useApplyMotionOverride(MOTION_KEY); @@ -82,6 +90,17 @@ export function Root() { const { todos, refresh: refreshTodos } = useTodos(); const [openPanel, setOpenPanel] = useState(null); const liveStreamRef = useRef(null); + const hideHeader = HIDDEN.has("header"); + const hideInput = HIDDEN.has("input"); + // Body classes, not inline styles: agent.css's `--agent-header-h`/ + // `--agent-header-real-h`/`--agent-composer-h` custom properties already + // drive every dependent calc() (overlay offset, terminal scroll padding) + // — zeroing them here collapses all of that in one place instead of + // touching each rule. + useEffect(() => { + document.body.classList.toggle("agent-hide-header", hideHeader); + document.body.classList.toggle("agent-hide-input", hideInput); + }, [hideHeader, hideInput]); // Ticks the status badge's "thinking Xm Ys" age once a second, // independent of the ~4s `/api/state` poll cadence — without its own @@ -111,7 +130,7 @@ export function Root() { ? `${state.label} // ${state.hive_name}` : `${tab} // hyperhive`; }, [state?.label, state?.qualified_label, state?.hive_name]); - const termInput = ( + const termInput = hideInput ? null : (