UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

576 lines (519 loc) 18.9 kB
<!doctype html> <html data-openclaw-control-ui-build-id="2026.9.2-release-3928bad9badf-2026-09-05T15-22-41.651Z-71b2645fc29849d3cc27b9ef88a1c066f48136ed05c7478b4dc737076b890f9a" lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" /> <title>OpenClaw Control</title> <meta name="color-scheme" content="dark light" /> <!-- These mirror the claw theme's --bg tokens for first paint; runtime theme application keeps them synchronized. --> <meta name="theme-color" media="(prefers-color-scheme: light)" content="#faf9f7" /> <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0e1015" /> <link rel="icon" type="image/svg+xml" href="./favicon.svg" /> <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" /> <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" /> <link rel="manifest" href="./manifest.webmanifest" crossorigin="use-credentials" /> <script data-cfasync="false"> // Configure Zod before module evaluation so its JIT probe cannot violate strict CSP. globalThis.__zod_globalConfig ??= {}; globalThis.__zod_globalConfig.jitless = true; (function () { var THEMES = { claw: 1, knot: 1, dash: 1, absolutely: 1, tide: 1, beacon: 1, phosphor: 1, crt: 1, manuscript: 1, rose: 1, miami: 1, }; var MODES = { system: 1, light: 1, dark: 1 }; var LEGACY = { dark: "claw:dark", light: "claw:light", openknot: "knot:dark", fieldmanual: "dash:dark", clawdash: "dash:light", system: "claw:system", }; try { var keys = Object.keys(localStorage); var raw; for (var i = 0; i < keys.length; i++) { if (keys[i].indexOf("openclaw.control.settings.v1") === 0) { raw = localStorage.getItem(keys[i]); if (raw) break; } } var s = raw ? JSON.parse(raw) : null; var t = s && s.theme; var m = s && s.themeMode; if (typeof t !== "string") t = ""; if (typeof m !== "string") m = ""; var legacy = LEGACY[t]; var theme = THEMES[t] ? t : legacy ? legacy.split(":")[0] : "claw"; var mode = MODES[m] ? m : legacy ? legacy.split(":")[1] : "system"; if (mode === "system") { mode = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; } // family -> [dark, light] resolved names. Kept in lockstep with // resolveTheme() in app/theme.ts and the files in public/themes. var FAMILIES = { knot: ["openknot", "openknot-light"], dash: ["dash", "dash-light"], absolutely: ["absolutely", "absolutely-light"], tide: ["tide", "tide-light"], beacon: ["beacon", "beacon-light"], phosphor: ["phosphor", "phosphor-light"], crt: ["crt", "crt-light"], manuscript: ["manuscript", "manuscript-light"], rose: ["rose", "rose-light"], miami: ["miami", "miami-light"], }; var pair = FAMILIES[theme]; var resolved = pair ? pair[mode === "light" ? 1 : 0] : mode === "light" ? "light" : "dark"; if (pair) { // Script-created links need explicit render blocking before first paint. // The gateway stamps the mount prefix and asset version before this runs. var base = document.documentElement.getAttribute("data-openclaw-control-ui-base-path") || ""; var buildId = document.documentElement.getAttribute( "data-openclaw-control-ui-build-id", ); var version = buildId ? "?v=" + encodeURIComponent(buildId) : ""; var palette = document.createElement("link"); palette.rel = "stylesheet"; palette.setAttribute("blocking", "render"); palette.id = "openclaw-theme-palette-" + theme; palette.onerror = function () { palette.remove(); }; palette.href = base + "/themes/" + theme + ".css" + version; document.head.appendChild(palette); } document.documentElement.setAttribute("data-theme", resolved); var resolvedMode = resolved.includes("light") ? "light" : "dark"; document.documentElement.setAttribute("data-theme-mode", resolvedMode); // Carapace CSS selects on [data-theme-resolved]; keep in lockstep. document.documentElement.setAttribute("data-theme-resolved", resolvedMode); } catch (e) {} })(); </script> <style> /* Paint before the app stylesheet arrives so WebKit never exposes a white document between its native dark canvas and the persisted UI theme. */ html, body { min-height: 100%; } html { background: #0e1015; color-scheme: dark; } html[data-theme-mode="light"] { background: #faf9f7; color-scheme: light; } html[data-theme="openknot"] { background: #080808; } html[data-theme="openknot-light"] { background: #f9f9fb; } html[data-theme="dash"] { background: #1a1210; } html[data-theme="dash-light"] { background: #f7f2ec; } html[data-theme="absolutely"] { background: #1c1c1a; } html[data-theme="absolutely-light"] { background: #faf9f5; } html[data-theme="tide"] { background: #10151b; } html[data-theme="tide-light"] { background: #f7f9fb; } html[data-theme="beacon"] { background: #000000; } html[data-theme="beacon-light"] { background: #ffffff; } html[data-theme="phosphor"] { background: #0a0f0a; } html[data-theme="phosphor-light"] { background: #f4f7f4; } html[data-theme="crt"] { background: #090a09; } html[data-theme="crt-light"] { background: #f5f5f4; } html[data-theme="manuscript"] { background: #211e18; } html[data-theme="manuscript-light"] { background: #f6f1e4; } html[data-theme="rose"] { background: #191724; } html[data-theme="rose-light"] { background: #faf4ed; } html[data-theme="miami"] { background: #140f1e; } html[data-theme="miami-light"] { background: #f7f3f6; } body { margin: 0; min-height: 100%; background: inherit; color-scheme: inherit; } body.openclaw-mount-fallback-active { margin: 0; min-width: 320px; color: #eef4f8; background: #101418; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } body.openclaw-mount-fallback-active openclaw-app { display: none; } .mount-fallback { box-sizing: border-box; /* The app stylesheet clips the root scrollers, so this pre-app error surface must own its scrolling on short windows. dvh keeps the scroll range inside the visible mobile viewport; vh is the fallback. */ height: 100vh; height: 100dvh; overflow-y: auto; padding: 24px; place-items: center; place-items: safe center; } .mount-fallback:not([hidden]) { display: grid; } .mount-fallback__panel { box-sizing: border-box; width: min(100%, 640px); border: 1px solid rgba(148, 163, 184, 0.28); border-radius: 8px; background: #151b21; box-shadow: 0 24px 80px rgba(0, 0, 0, 0.36); padding: 28px; } .mount-fallback__eyebrow { margin: 0 0 10px; color: #9fb0bd; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; } .mount-fallback__panel h1 { margin: 0; color: inherit; font-size: clamp(1.5rem, 3vw, 2rem); line-height: 1.15; } .mount-fallback__panel p { margin: 16px 0 0; color: #c8d3da; font-size: 1rem; line-height: 1.6; } .mount-fallback__panel ul { margin: 18px 0 0; padding-left: 1.2rem; color: #c8d3da; line-height: 1.6; } .mount-fallback__panel a { color: #8bd3ff; text-decoration-thickness: 0.08em; text-underline-offset: 0.18em; } .mount-fallback__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; } .mount-fallback__button { min-height: 42px; border: 1px solid rgba(148, 163, 184, 0.36); border-radius: 6px; padding: 0 16px; color: inherit; background: transparent; font: inherit; font-weight: 700; } .mount-fallback__button--primary { border-color: #66c2ff; color: #061019; background: #8bd3ff; } .mount-fallback__panel:focus { outline: none; } .mount-fallback__button:focus-visible, .mount-fallback__panel a:focus-visible { outline: 3px solid #f9c74f; outline-offset: 3px; } html[data-theme-mode="light"] body.openclaw-mount-fallback-active { color: #151b21; background: #f5f7fa; } html[data-theme-mode="light"] .mount-fallback__panel { border-color: rgba(71, 85, 105, 0.22); background: #ffffff; box-shadow: 0 24px 80px rgba(15, 23, 42, 0.14); } html[data-theme-mode="light"] .mount-fallback__eyebrow, html[data-theme-mode="light"] .mount-fallback__panel p, html[data-theme-mode="light"] .mount-fallback__panel ul { color: #4b5963; } html[data-theme-mode="light"] .mount-fallback__panel a { color: #0369a1; } </style> <script data-cfasync="false" type="module" crossorigin src="./assets/index-1fvEwqai.js"></script> <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-DkW27tQK.js"> <link rel="modulepreload" crossorigin href="./assets/lit-runtime-CV_MH99K.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-core-vvcQQV2m.js"> <link rel="modulepreload" crossorigin href="./assets/gateway-runtime-Bqqcp9-I.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-core-YphAl4aM.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-core-Rwzhj6nO.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-foundation-Cydpkyqa.js"> <link rel="stylesheet" crossorigin href="./assets/control-ui-core-ClxHIvNM.css"> </head> <body> <openclaw-app></openclaw-app> <section id="openclaw-mount-fallback" class="mount-fallback" data-openclaw-mount-timeout-ms="12000" role="alert" aria-labelledby="openclaw-mount-fallback-title" hidden > <div class="mount-fallback__panel" tabindex="-1"> <p class="mount-fallback__eyebrow">OpenClaw Control UI</p> <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1> <p id="openclaw-mount-fallback-summary"> The browser loaded the static page, but the app bundle did not start. The gateway may be restarting, or this page may reference assets from a different OpenClaw version. </p> <ul> <li>OpenClaw will retry the current app bundle automatically.</li> <li>If this persists, reload or try a clean browser profile.</li> <li> See <a href="https://docs.openclaw.ai/web/control-ui#blank-control-ui-page" target="_blank" rel="noopener noreferrer" >Control UI troubleshooting</a >. </li> </ul> <div class="mount-fallback__actions"> <button type="button" id="openclaw-mount-retry" class="mount-fallback__button mount-fallback__button--primary" > Try again </button> <button type="button" id="openclaw-mount-wait" class="mount-fallback__button"> Keep waiting </button> </div> </div> </section> <script data-cfasync="false"> (function () { var app = document.querySelector("openclaw-app"); var fallback = document.getElementById("openclaw-mount-fallback"); if (!app || !fallback) return; var panel = fallback.querySelector(".mount-fallback__panel"); var summary = document.getElementById("openclaw-mount-fallback-summary"); var retry = document.getElementById("openclaw-mount-retry"); var wait = document.getElementById("openclaw-mount-wait"); var rawDelay = Number(fallback.getAttribute("data-openclaw-mount-timeout-ms")); var delay = Number.isFinite(rawDelay) && rawDelay > 0 ? rawDelay : 12000; var maxRecoveryAttempts = 6; var timer; var recoveryTimer; var recoveryAttempt = 0; var recoveryInFlight = false; var recoveryNavigation = false; var appStarted = false; try { var initialUrl = new URL(window.location.href); recoveryNavigation = initialUrl.searchParams.has("openclaw_mount_recovery"); if (recoveryNavigation) { initialUrl.searchParams.delete("openclaw_mount_recovery"); window.history.replaceState(null, "", initialUrl.href); } } catch (e) {} function hideFallback() { fallback.hidden = true; document.body.classList.remove("openclaw-mount-fallback-active"); } function setSummary(text) { if (summary) summary.textContent = text; } function scheduleRecovery() { window.clearTimeout(recoveryTimer); if (appStarted || recoveryAttempt >= maxRecoveryAttempts) return; var retryDelay = Math.min(delay, 1000 * Math.pow(2, recoveryAttempt)); recoveryTimer = window.setTimeout(retryCurrentDocument, retryDelay); } function finishRecoveryAttempt() { recoveryInFlight = false; if (appStarted) return; if (recoveryAttempt >= maxRecoveryAttempts) { setSummary( "The gateway is still unavailable. Try again, then check the troubleshooting guide if the problem persists.", ); return; } setSummary( "The gateway is not reachable yet. OpenClaw will keep retrying while it restarts.", ); scheduleRecovery(); } function unregisterServiceWorkers() { if (!navigator.serviceWorker?.getRegistrations) return; // Recovery must not depend on a possibly broken worker. Navigations bypass it, // so a reload can self-heal after the registrations are removed. void navigator.serviceWorker .getRegistrations() .then(function (registrations) { return Promise.all( registrations.map(function (registration) { return registration.unregister(); }), ); }) .catch(function () {}); } function retryCurrentDocument() { if ( appStarted || recoveryInFlight || recoveryAttempt >= maxRecoveryAttempts || typeof window.fetch !== "function" ) return; var documentUrl = new URL(window.location.href); if (recoveryNavigation) { setSummary( "A fresh page still could not start the Control UI. Try again, then check the troubleshooting guide if the problem persists.", ); return; } recoveryInFlight = true; recoveryAttempt += 1; documentUrl.searchParams.set("openclaw_mount_recovery", String(Date.now())); var recoveryController = new AbortController(); var requestTimer = window.setTimeout(function () { recoveryController.abort(); }, delay); window .fetch(documentUrl.href, { cache: "no-store", credentials: "same-origin", signal: recoveryController.signal, }) .then(function (response) { if (!response.ok) throw new Error("gateway unavailable"); if (!appStarted) window.location.replace(documentUrl.href); }) .catch(function () { unregisterServiceWorkers(); finishRecoveryAttempt(); }) .finally(function () { window.clearTimeout(requestTimer); }); } function showFallback() { if (appStarted) return; retryCurrentDocument(); fallback.hidden = false; document.body.classList.add("openclaw-mount-fallback-active"); if (panel && typeof panel.focus === "function") { try { panel.focus({ preventScroll: true }); } catch (e) { panel.focus(); } } } function armFallbackTimer() { window.clearTimeout(timer); timer = window.setTimeout(showFallback, delay); } armFallbackTimer(); window.addEventListener( "openclaw-control-ui-rendered", function () { appStarted = true; window.clearTimeout(timer); window.clearTimeout(recoveryTimer); hideFallback(); }, { once: true }, ); if (retry) { retry.addEventListener("click", function () { unregisterServiceWorkers(); window.location.reload(); }); } if (wait) { wait.addEventListener("click", function () { hideFallback(); recoveryAttempt = 0; retryCurrentDocument(); armFallbackTimer(); }); } })(); </script> </body> </html>