diff --git a/ui/src/e2e/sidebar-customization.e2e.test.ts b/ui/src/e2e/sidebar-customization.e2e.test.ts index 01cdc0c7e902..c0a92d757e7b 100644 --- a/ui/src/e2e/sidebar-customization.e2e.test.ts +++ b/ui/src/e2e/sidebar-customization.e2e.test.ts @@ -691,6 +691,94 @@ suite.define(() => { ); }); + it("moves Home activity clear of the aligned Pages editor", async () => { + await suite.withPage( + { + locale: "en-US", + serviceWorkers: "block", + viewport: { height: 900, width: 1440 }, + }, + async ({ page }) => { + await installMockGateway(page, { + methodResponses: { + "sessions.list": { + count: 1, + defaults: { + contextTokens: null, + model: "gpt-5.5", + modelProvider: "openai", + }, + path: "", + sessions: [ + { + hasActiveRun: true, + key: "main", + kind: "direct", + status: "running", + updatedAt: 100, + }, + ], + ts: 100, + }, + }, + }); + + await page.goto(`${suite.server.baseUrl}chat`); + const sidebar = page.locator("openclaw-app-sidebar"); + const home = sidebar.locator(".nav-item--home"); + await expect.poll(() => home.isVisible()).toBe(true); + await sidebar.evaluate(async (element) => { + const host = element as HTMLElement & { + requestUpdate(): void; + updateComplete: Promise; + }; + // The shell refreshes this callback whenever its lazy outbox runtime + // loads. Keep the warning fixture stable until geometry is measured. + Object.defineProperty(host, "outboxAttentionCountForSession", { + configurable: true, + get: () => () => 1, + set: () => undefined, + }); + host.requestUpdate(); + await host.updateComplete; + }); + + const activity = home.locator(".sidebar-home-session-states"); + const editor = sidebar.locator(".sidebar-nav__head-action"); + await expect.poll(() => activity.locator(".session-run-spinner").count()).toBe(1); + await expect.poll(() => activity.locator(".session-row-badge--attention").count()).toBe(1); + + await page.mouse.move(900, 400); + const restingActivity = await activity.boundingBox(); + expect(restingActivity).not.toBeNull(); + await sidebar.locator(".sidebar-nav").hover(); + await expect + .poll(() => editor.evaluate((element) => getComputedStyle(element).opacity)) + .toBe("1"); + await expect + .poll(async () => { + const [homeBox, activityBox, editorBox] = await Promise.all([ + home.boundingBox(), + activity.boundingBox(), + editor.boundingBox(), + ]); + if (!homeBox || !activityBox || !editorBox || !restingActivity) { + return null; + } + return { + activityShift: Math.round(restingActivity.x - activityBox.x), + centerDelta: Math.abs( + editorBox.y + editorBox.height / 2 - (homeBox.y + homeBox.height / 2), + ), + gap: Math.round(editorBox.x - (activityBox.x + activityBox.width)), + }; + }) + .toEqual({ activityShift: 25, centerDelta: 0, gap: 4 }); + await captureUiProof(page, "07-home-activity-editor.png"); + }, + ); + }); + it("keeps mobile attention in the drawer while desktop remains unchanged", async () => { await suite.withPage( { diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css index fd14aad45ccc..0acd4e81fa30 100644 --- a/ui/src/styles/layout.css +++ b/ui/src/styles/layout.css @@ -3402,12 +3402,12 @@ wa-dropdown-item.session-menu__item::part(submenu-icon) { .sidebar-nav__head { position: absolute; z-index: 1; - top: 0; + top: 5px; right: 0; display: flex; align-items: center; justify-content: flex-end; - padding: 2px 8px 2px 12px; + padding: 0 8px 0 12px; } .sidebar-nav__head-action { @@ -3440,6 +3440,10 @@ wa-dropdown-item.session-menu__item::part(submenu-icon) { .sidebar-nav__head-action { opacity: 1; } + + .sidebar-home-session-states { + transform: translateX(-25px); + } } .sidebar-nav__head-action:hover, @@ -3468,6 +3472,14 @@ wa-dropdown-item.session-menu__item::part(submenu-icon) { display: inline-flex; align-items: center; gap: 6px; + transition: transform var(--duration-fast) ease; +} + +/* The Pages editor shares Home's row. Move every trailing state together so + its hover/focus hitbox never covers progress, warning, or draft status. */ +.sidebar-nav:hover .sidebar-home-session-states, +.sidebar-nav:focus-within .sidebar-home-session-states { + transform: translateX(-25px); } /* Zone 5: product chrome in one slim footer bar. The shell footer's top