import { useEffect, useState } from "react"; import { Platform } from "react-native"; import { useAggregatedAgents } from "./use-aggregated-agents"; type FaviconStatus = "none" | "running" | "attention"; type ColorScheme = "dark" | "light"; /* eslint-disable @typescript-eslint/no-require-imports */ const FAVICON_IMAGES: Record> = { dark: { none: require("../../assets/images/favicon-dark.png"), running: require("../../assets/images/favicon-dark-running.png"), attention: require("../../assets/images/favicon-dark-attention.png"), }, light: { none: require("../../assets/images/favicon-light.png"), running: require("../../assets/images/favicon-light-running.png"), attention: require("../../assets/images/favicon-light-attention.png"), }, }; /* eslint-enable @typescript-eslint/no-require-imports */ function deriveFaviconStatus( agents: ReturnType["agents"] ): FaviconStatus { const hasRunning = agents.some((agent) => agent.status === "running"); if (hasRunning) { return "running"; } const hasAttention = agents.some((agent) => agent.requiresAttention); if (hasAttention) { return "attention"; } return "none"; } function getFaviconUri(status: FaviconStatus, colorScheme: ColorScheme): string { const image = FAVICON_IMAGES[colorScheme][status]; if (typeof image === "object" && "uri" in image) { return image.uri; } const suffix = status === "none" ? "" : `-${status}`; return `/assets/images/favicon-${colorScheme}${suffix}.png`; } function getOrCreateFaviconLink(): HTMLLinkElement | null { if (typeof document === "undefined") return null; let link = document.querySelector('link[rel="icon"]'); if (!link) { link = document.createElement("link"); link.rel = "icon"; link.type = "image/png"; document.head.appendChild(link); } return link; } function updateFavicon(status: FaviconStatus, colorScheme: ColorScheme) { const link = getOrCreateFaviconLink(); if (!link) return; const newHref = getFaviconUri(status, colorScheme); if (link.href !== newHref) { link.href = newHref; } } function getSystemColorScheme(): ColorScheme { if (Platform.OS !== "web" || typeof window === "undefined" || typeof window.matchMedia !== "function") { return "dark"; } return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } export function useFaviconStatus() { const { agents } = useAggregatedAgents(); const [colorScheme, setColorScheme] = useState(getSystemColorScheme); // Listen for system color scheme changes useEffect(() => { if (Platform.OS !== "web" || typeof window === "undefined") return; const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handler = (e: MediaQueryListEvent) => { setColorScheme(e.matches ? "dark" : "light"); }; mediaQuery.addEventListener("change", handler); return () => mediaQuery.removeEventListener("change", handler); }, []); // Update favicon when agents or color scheme changes useEffect(() => { if (Platform.OS !== "web") return; const status = deriveFaviconStatus(agents); updateFavicon(status, colorScheme); }, [agents, colorScheme]); }