From 548abf4e92c34a3ce97e2b02a643205401508c84 Mon Sep 17 00:00:00 2001 From: Val Alexander <68980965+BunsDev@users.noreply.github.com> Date: Mon, 11 May 2026 07:34:12 -0500 Subject: [PATCH] fix(control-ui): polish mount fallback recovery --- ui/index.html | 40 ++++++++++++++++++++++---------- ui/src/ui/mount-fallback.test.ts | 13 +++++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/ui/index.html b/ui/index.html index 2a9e32ba07f..9277fa45171 100644 --- a/ui/index.html +++ b/ui/index.html @@ -164,6 +164,10 @@ background: #8bd3ff; } + .mount-fallback__panel:focus { + outline: none; + } + .mount-fallback__button:focus-visible, .mount-fallback__panel a:focus-visible { outline: 3px solid #f9c74f; @@ -202,7 +206,7 @@ aria-labelledby="openclaw-mount-fallback-title" hidden > -
+

OpenClaw Control UI

Control UI did not start

@@ -218,7 +222,7 @@ Control UI troubleshooting. @@ -231,8 +235,8 @@ > Try again -

@@ -244,11 +248,12 @@ var fallback = document.getElementById("openclaw-mount-fallback"); if (!app || !fallback) return; - var dismissed = false; + var panel = fallback.querySelector(".mount-fallback__panel"); var retry = document.getElementById("openclaw-mount-retry"); - var dismiss = document.getElementById("openclaw-mount-dismiss"); + 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 timer; function appMounted() { try { @@ -269,12 +274,24 @@ } function showFallback() { - if (dismissed || appMounted()) return; + if (appMounted()) return; 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(); + } + } } - var timer = window.setTimeout(showFallback, delay); + function armFallbackTimer() { + window.clearTimeout(timer); + timer = window.setTimeout(showFallback, delay); + } + + armFallbackTimer(); if (window.customElements && typeof window.customElements.whenDefined === "function") { window.customElements.whenDefined(tagName).then( @@ -291,11 +308,10 @@ window.location.reload(); }); } - if (dismiss) { - dismiss.addEventListener("click", function () { - dismissed = true; - window.clearTimeout(timer); + if (wait) { + wait.addEventListener("click", function () { hideFallback(); + armFallbackTimer(); }); } })(); diff --git a/ui/src/ui/mount-fallback.test.ts b/ui/src/ui/mount-fallback.test.ts index f1a65ee2989..c7ddfefabb3 100644 --- a/ui/src/ui/mount-fallback.test.ts +++ b/ui/src/ui/mount-fallback.test.ts @@ -59,6 +59,19 @@ describe("Control UI mount fallback", () => { ); expect(fallback?.textContent).toContain("Control UI did not start"); expect(fallback?.textContent).toContain("Control UI troubleshooting"); + expect(frameWindow.document.activeElement?.classList.contains("mount-fallback__panel")).toBe( + true, + ); + + const waitButton = frameWindow.document.getElementById("openclaw-mount-wait"); + waitButton?.click(); + expect(fallback?.hidden).toBe(true); + expect(frameWindow.document.body.classList.contains("openclaw-mount-fallback-active")).toBe( + false, + ); + + await waitForWindowTimeout(frameWindow, 10); + expect(fallback?.hidden).toBe(false); }); it("keeps the fallback hidden when the app element registers before the timeout", async () => {