(function () { "use strict"; const PLUGIN = "digital-pet"; const apiBase = "/api/plugins/digital-pet"; function getSdk() { return window.__HERMES_PLUGIN_SDK__ || {}; } function fetchJson(path, options) { const sdk = getSdk(); const url = apiBase + path; const opts = Object.assign({ headers: { "Content-Type": "application/json" } }, options || {}); const doFetch = sdk.authedFetch || fetch; return doFetch(url, opts).then(function (res) { if (!res.ok) throw new Error("digital pet request failed: " + res.status); return res.json(); }); } function DigitalPetOverlay() { const sdk = getSdk(); const React = sdk.React || window.React; const hooks = sdk.hooks || React; const useEffect = hooks.useEffect; const useRef = hooks.useRef; const useState = hooks.useState; const h = React.createElement; const [state, setState] = useState(null); const [error, setError] = useState(null); const mounted = useRef(false); const hoverTimer = useRef(null); const transitionTimer = useRef(null); function clearHoverTimer() { if (hoverTimer.current) window.clearTimeout(hoverTimer.current); hoverTimer.current = null; } function clearTransitionTimer() { if (transitionTimer.current) window.clearTimeout(transitionTimer.current); transitionTimer.current = null; } function applyRemoteState(data) { if (!mounted.current) return null; setError(null); setState(data.state); return data.state; } function applyRemoteError(err) { if (!mounted.current) return undefined; setError(err.message || String(err)); return undefined; } function refresh() { return fetchJson("/state") .then(applyRemoteState) .catch(applyRemoteError); } function send(type, metadata) { return fetchJson("/event", { method: "POST", body: JSON.stringify({ type: type, metadata: metadata || {} }) }) .then(applyRemoteState) .catch(applyRemoteError); } useEffect(function () { mounted.current = true; return function () { mounted.current = false; clearHoverTimer(); clearTransitionTimer(); }; }, []); useEffect(function () { refresh(); const id = window.setInterval(refresh, 30000); return function () { window.clearInterval(id); }; }, []); useEffect(function () { if (!state) return undefined; clearTransitionTimer(); const completion = state.mode === "reacting" ? "reaction_complete" : state.mode === "prompting" ? "prompt_complete" : state.mode === "waking" ? "wake_complete" : null; if (!completion) return undefined; transitionTimer.current = window.setTimeout(function () { transitionTimer.current = null; send(completion); }, 2400); return clearTransitionTimer; }, [state && state.mode, state && state.revision]); function onClick() { if (!state || state.mode === "hidden") send("summon"); else if (state.mode === "sleeping") send("wake"); else send("click"); } function startHover() { clearHoverTimer(); if (!state || state.mode === "hidden" || state.mode === "sleeping") return; hoverTimer.current = window.setTimeout(function () { hoverTimer.current = null; send("hover_hold"); }, 1000); } function stopHover() { clearHoverTimer(); } if (!state) return h("div", { className: "digital-pet-root digital-pet-loading", "aria-live": "polite" }, error || ""); if (state.mode === "hidden" || state.enabled === false) { return h("button", { type: "button", className: "digital-pet-summon", onClick: onClick, title: "Summon station companion", "aria-label": "Summon station companion" }, "◆"); } const mood = state.mood || "calm"; const mode = state.mode || "idle"; const face = mode === "sleeping" ? "– –" : mood === "pleased" ? "•‿•" : mood === "curious" ? "•?•" : mood === "tired" ? "-_-" : "•_•"; return h("div", { className: "digital-pet-root", "data-mode": mode, "data-mood": mood }, state.message ? h("div", { className: "digital-pet-bubble", role: "status", "aria-live": "polite" }, state.message) : null, h("button", { type: "button", className: "digital-pet-avatar", onClick: onClick, onMouseEnter: startHover, onMouseLeave: stopHover, onFocus: stopHover, title: mode === "sleeping" ? "Click to wake Signal" : "Click Signal; hover to sleep", "aria-label": mode === "sleeping" ? "Wake digital pet" : "Interact with digital pet" }, h("span", { className: "digital-pet-antenna", "aria-hidden": "true" }, "⌁"), h("span", { className: "digital-pet-face", "aria-hidden": "true" }, face), h("span", { className: "digital-pet-name" }, state.name || "Signal") ) ); } function registerWhenReady() { if (!window.__HERMES_PLUGINS__ || !window.__HERMES_PLUGIN_SDK__ || !window.__HERMES_PLUGIN_SDK__.React) { window.setTimeout(registerWhenReady, 50); return; } window.__HERMES_PLUGINS__.registerSlot(PLUGIN, "overlay", DigitalPetOverlay); } registerWhenReady(); })();