Skip to content

Commit 548abf4

Browse files
committed
fix(control-ui): polish mount fallback recovery
1 parent a719e9e commit 548abf4

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

ui/index.html

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@
164164
background: #8bd3ff;
165165
}
166166

167+
.mount-fallback__panel:focus {
168+
outline: none;
169+
}
170+
167171
.mount-fallback__button:focus-visible,
168172
.mount-fallback__panel a:focus-visible {
169173
outline: 3px solid #f9c74f;
@@ -202,7 +206,7 @@
202206
aria-labelledby="openclaw-mount-fallback-title"
203207
hidden
204208
>
205-
<div class="mount-fallback__panel">
209+
<div class="mount-fallback__panel" tabindex="-1">
206210
<p class="mount-fallback__eyebrow">OpenClaw Control UI</p>
207211
<h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
208212
<p>
@@ -218,7 +222,7 @@ <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
218222
<a
219223
href="https://docs.openclaw.ai/web/control-ui#blank-control-ui-page"
220224
target="_blank"
221-
rel="noreferrer"
225+
rel="noopener noreferrer"
222226
>Control UI troubleshooting</a
223227
>.
224228
</li>
@@ -231,8 +235,8 @@ <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
231235
>
232236
Try again
233237
</button>
234-
<button type="button" id="openclaw-mount-dismiss" class="mount-fallback__button">
235-
Hide message
238+
<button type="button" id="openclaw-mount-wait" class="mount-fallback__button">
239+
Keep waiting
236240
</button>
237241
</div>
238242
</div>
@@ -244,11 +248,12 @@ <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
244248
var fallback = document.getElementById("openclaw-mount-fallback");
245249
if (!app || !fallback) return;
246250

247-
var dismissed = false;
251+
var panel = fallback.querySelector(".mount-fallback__panel");
248252
var retry = document.getElementById("openclaw-mount-retry");
249-
var dismiss = document.getElementById("openclaw-mount-dismiss");
253+
var wait = document.getElementById("openclaw-mount-wait");
250254
var rawDelay = Number(fallback.getAttribute("data-openclaw-mount-timeout-ms"));
251255
var delay = Number.isFinite(rawDelay) && rawDelay > 0 ? rawDelay : 12000;
256+
var timer;
252257

253258
function appMounted() {
254259
try {
@@ -269,12 +274,24 @@ <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
269274
}
270275

271276
function showFallback() {
272-
if (dismissed || appMounted()) return;
277+
if (appMounted()) return;
273278
fallback.hidden = false;
274279
document.body.classList.add("openclaw-mount-fallback-active");
280+
if (panel && typeof panel.focus === "function") {
281+
try {
282+
panel.focus({ preventScroll: true });
283+
} catch (e) {
284+
panel.focus();
285+
}
286+
}
287+
}
288+
289+
function armFallbackTimer() {
290+
window.clearTimeout(timer);
291+
timer = window.setTimeout(showFallback, delay);
275292
}
276293

277-
var timer = window.setTimeout(showFallback, delay);
294+
armFallbackTimer();
278295

279296
if (window.customElements && typeof window.customElements.whenDefined === "function") {
280297
window.customElements.whenDefined(tagName).then(
@@ -291,11 +308,10 @@ <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1>
291308
window.location.reload();
292309
});
293310
}
294-
if (dismiss) {
295-
dismiss.addEventListener("click", function () {
296-
dismissed = true;
297-
window.clearTimeout(timer);
311+
if (wait) {
312+
wait.addEventListener("click", function () {
298313
hideFallback();
314+
armFallbackTimer();
299315
});
300316
}
301317
})();

ui/src/ui/mount-fallback.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ describe("Control UI mount fallback", () => {
5959
);
6060
expect(fallback?.textContent).toContain("Control UI did not start");
6161
expect(fallback?.textContent).toContain("Control UI troubleshooting");
62+
expect(frameWindow.document.activeElement?.classList.contains("mount-fallback__panel")).toBe(
63+
true,
64+
);
65+
66+
const waitButton = frameWindow.document.getElementById("openclaw-mount-wait");
67+
waitButton?.click();
68+
expect(fallback?.hidden).toBe(true);
69+
expect(frameWindow.document.body.classList.contains("openclaw-mount-fallback-active")).toBe(
70+
false,
71+
);
72+
73+
await waitForWindowTimeout(frameWindow, 10);
74+
expect(fallback?.hidden).toBe(false);
6275
});
6376

6477
it("keeps the fallback hidden when the app element registers before the timeout", async () => {

0 commit comments

Comments
 (0)