diff --git a/ui/src/pages/chat/background-tasks.e2e.test.ts b/ui/src/pages/chat/background-tasks.e2e.test.ts index ee97ed3cd668..4d238cd80951 100644 --- a/ui/src/pages/chat/background-tasks.e2e.test.ts +++ b/ui/src/pages/chat/background-tasks.e2e.test.ts @@ -273,6 +273,27 @@ describeControlUiE2e("Control UI chat background-tasks rail mocked Gateway E2E", expect(await page.locator(".chat-tasks-status__link").textContent()).toContain( "1 running task", ); + const statusLink = page.locator(".chat-tasks-status__link"); + await statusLink.hover(); + const previewBody = page.locator( + "openclaw-tooltip.chat-tasks-status__preview wa-tooltip[open] .body", + ); + await previewBody.waitFor({ state: "visible" }); + const linkBox = await statusLink.boundingBox(); + const previewBox = await previewBody.boundingBox(); + expect(linkBox).not.toBeNull(); + expect(previewBox).not.toBeNull(); + if (!linkBox || !previewBox) { + throw new Error("expected running-task link and preview geometry"); + } + const linkCenter = linkBox.x + linkBox.width / 2; + const previewCenter = previewBox.x + previewBox.width / 2; + expect(Math.abs(previewCenter - linkCenter)).toBeLessThanOrEqual(2); + expect(previewBox.y + previewBox.height).toBeLessThanOrEqual(linkBox.y); + await page.screenshot({ + path: path.join(artifactDir, "05-running-task-popover-centered.png"), + fullPage: true, + }); await page.getByRole("button", { name: "Show background tasks" }).click(); const row = page.locator('[data-task-id="task-exec"]'); @@ -280,7 +301,7 @@ describeControlUiE2e("Control UI chat background-tasks rail mocked Gateway E2E", expect(await row.textContent()).toContain("CLI command"); expect(await row.textContent()).toContain("Command running"); await page.screenshot({ - path: path.join(artifactDir, "05-one-background-exec.png"), + path: path.join(artifactDir, "06-one-background-exec.png"), fullPage: true, }); } finally { diff --git a/ui/src/pages/chat/components/chat-background-tasks-status.ts b/ui/src/pages/chat/components/chat-background-tasks-status.ts index f80e56c80ed0..41faa8484b61 100644 --- a/ui/src/pages/chat/components/chat-background-tasks-status.ts +++ b/ui/src/pages/chat/components/chat-background-tasks-status.ts @@ -110,25 +110,25 @@ export function renderBackgroundTasksStatusRow( backgroundTasks.onToggleCollapsed(); } }; - // The preview tooltip anchors the whole row (not the link button), so the - // ticking preview content stays outside the polite live region. + // Keep the live announcement separate from the tooltip: rich preview + // content must not enter the polite region, while the popup must anchor to + // the link itself or its center drifts with the claw and elapsed time. return html` - -
- - ${status.startedMs !== null - ? html` - - - - ` - : nothing} +
+ + ${status.startedMs !== null + ? html` + + + ` + : nothing} + ${label} + -
- ${renderStatusPreview(backgroundTasks)} - + ${renderStatusPreview(backgroundTasks)} + +
`; } diff --git a/ui/src/pages/chat/components/chat-background-tasks.test.ts b/ui/src/pages/chat/components/chat-background-tasks.test.ts index 2248155aa746..5500ba21f54d 100644 --- a/ui/src/pages/chat/components/chat-background-tasks.test.ts +++ b/ui/src/pages/chat/components/chat-background-tasks.test.ts @@ -761,10 +761,9 @@ describe("running-tasks status row", () => { const row = container.querySelector(".chat-tasks-status"); expect(row).not.toBeNull(); expect(row?.querySelector("openclaw-elapsed-time")).not.toBeNull(); - // The ticking timer must stay outside the polite live region. - expect(row?.querySelector(".chat-tasks-status__time")?.getAttribute("aria-hidden")).toBe( - "true", - ); + const liveStatus = row?.querySelector('[role="status"]'); + expect(liveStatus?.textContent?.trim()).toBe("1 running task"); + expect(liveStatus?.querySelector("openclaw-elapsed-time")).toBeNull(); const link = row?.querySelector(".chat-tasks-status__link"); expect(link?.textContent?.trim()).toBe("1 running task"); link?.click(); @@ -812,7 +811,8 @@ describe("running-tasks status row", () => { ); const preview = container.querySelector("openclaw-tooltip.chat-tasks-status__preview"); - expect(preview?.querySelector(".chat-tasks-status")?.id).toBe("chat-tasks-status-test"); + expect(preview?.firstElementChild?.classList.contains("chat-tasks-status__link")).toBe(true); + expect(container.querySelector(".chat-tasks-status")?.id).toBe("chat-tasks-status-test"); expect(preview?.querySelector('.chat-tasks-preview[slot="content"]')).not.toBeNull(); const titles = [...container.querySelectorAll(".chat-tasks-preview__title")].map((el) => el.textContent?.trim(),