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 () => {