Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: a terminal tab takes its agent's icon as the browser-tab icon

The agent terminal page swaps the page's favicon for the agent's icon while it is mounted, so tabs of different agents tell apart. A subagent terminal shows its agent's icon. An agent with no icon, or an unreachable controller, keeps the hyperhive mark; leaving the page restores the page's own icon.
This commit is contained in:
iris 2026-10-11 23:47:33 +02:00 • committed by mara
commit 9761ffa24a
3 changed files with 45 additions and 0 deletions

View file

@ -0,0 +1,38 @@
// useAgentFavicon — while the calling page is mounted, the browser tab's icon
// is this agent's icon, so tabs of different agents tell apart at a glance.
//
// The icon comes from swarm-controller's `GET /api/agents/{name}/icon`, an
// SVG that is a 404 for an agent with none. A `<link>` can't report a failed
// load, so an off-screen image loads it first and the tab icon switches only
// once that succeeds: an agent with no icon, or an unreachable controller,
// keeps the hyperhive mark. Leaving the page puts the page's own icon back.
import { useEffect } from "preact/hooks";
export function useAgentFavicon(agent: string): void {
useEffect(() => {
const link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
if (link === null) return;
const original = {
href: link.getAttribute("href"),
type: link.getAttribute("type"),
};
const url = `/api/agents/${encodeURIComponent(agent)}/icon`;
let cancelled = false;
const probe = new Image();
probe.onload = () => {
if (cancelled) return;
link.setAttribute("type", "image/svg+xml");
link.setAttribute("href", url);
};
probe.src = url;
return () => {
cancelled = true;
for (const [attr, value] of Object.entries(original)) {
if (value === null) link.removeAttribute(attr);
else link.setAttribute(attr, value);
}
};
}, [agent]);
}