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.
This commit is contained in:
parent
5af1f6a8e5
commit
ec51ec924a
2 changed files with 97 additions and 61 deletions
|
|
@ -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() {
|
export function Root() {
|
||||||
useApplyThemeOverride(THEME_KEY);
|
useApplyThemeOverride(THEME_KEY);
|
||||||
useApplyMotionOverride(MOTION_KEY);
|
useApplyMotionOverride(MOTION_KEY);
|
||||||
|
|
@ -82,6 +90,17 @@ export function Root() {
|
||||||
const { todos, refresh: refreshTodos } = useTodos();
|
const { todos, refresh: refreshTodos } = useTodos();
|
||||||
const [openPanel, setOpenPanel] = useState<OpenPanel>(null);
|
const [openPanel, setOpenPanel] = useState<OpenPanel>(null);
|
||||||
const liveStreamRef = useRef<LiveStreamHandle>(null);
|
const liveStreamRef = useRef<LiveStreamHandle>(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,
|
// Ticks the status badge's "thinking Xm Ys" age once a second,
|
||||||
// independent of the ~4s `/api/state` poll cadence — without its own
|
// independent of the ~4s `/api/state` poll cadence — without its own
|
||||||
|
|
@ -111,7 +130,7 @@ export function Root() {
|
||||||
? `${state.label} // ${state.hive_name}`
|
? `${state.label} // ${state.hive_name}`
|
||||||
: `${tab} // hyperhive`;
|
: `${tab} // hyperhive`;
|
||||||
}, [state?.label, state?.qualified_label, state?.hive_name]);
|
}, [state?.label, state?.qualified_label, state?.hive_name]);
|
||||||
const termInput = (
|
const termInput = hideInput ? null : (
|
||||||
<footer class="agent-composer">
|
<footer class="agent-composer">
|
||||||
<TermInput
|
<TermInput
|
||||||
label={state?.label ?? "…"}
|
label={state?.label ?? "…"}
|
||||||
|
|
@ -191,6 +210,7 @@ export function Root() {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{hideHeader ? null : (
|
||||||
<Header label="…" pills={pills}>
|
<Header label="…" pills={pills}>
|
||||||
<StatusChips
|
<StatusChips
|
||||||
statusLabel="… connecting"
|
statusLabel="… connecting"
|
||||||
|
|
@ -207,6 +227,7 @@ export function Root() {
|
||||||
onCancelTurn={() => Promise.resolve()}
|
onCancelTurn={() => Promise.resolve()}
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
|
)}
|
||||||
<main className="agent-main">
|
<main className="agent-main">
|
||||||
<LiveStream ref={liveStreamRef} />
|
<LiveStream ref={liveStreamRef} />
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -266,6 +287,7 @@ export function Root() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{hideHeader ? null : (
|
||||||
<Header
|
<Header
|
||||||
label={state.label}
|
label={state.label}
|
||||||
hiveLabel={
|
hiveLabel={
|
||||||
|
|
@ -311,6 +333,7 @@ export function Root() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
|
)}
|
||||||
<main className="agent-main">
|
<main className="agent-main">
|
||||||
{state.status === "needs_login_idle" ||
|
{state.status === "needs_login_idle" ||
|
||||||
state.status === "needs_login_in_progress" ? (
|
state.status === "needs_login_in_progress" ? (
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,19 @@
|
||||||
--agent-frost-blur: blur(12px) saturate(140%);
|
--agent-frost-blur: blur(12px) saturate(140%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* `?hide=header,input` (per-agent-page embed mode): `Root.tsx` toggles
|
||||||
|
these body classes when it omits `<Header>`/the composer footer.
|
||||||
|
Zeroing the custom properties here — rather than overriding each
|
||||||
|
dependent rule — collapses every calc() that reads them (overlay
|
||||||
|
offset, terminal scroll padding) in one place. */
|
||||||
|
body.agent-hide-header {
|
||||||
|
--agent-header-h: 0px;
|
||||||
|
--agent-header-real-h: 0px;
|
||||||
|
}
|
||||||
|
body.agent-hide-input {
|
||||||
|
--agent-composer-h: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue