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 : (