From 73896a893ffb76d00034cc13ca0671843007381f Mon Sep 17 00:00:00 2001 From: Huarch Date: Tue, 28 Jul 2026 17:24:32 +0800 Subject: [PATCH] fix: unify Agent history panel framing The regression came from applying history-open styling to the outer acrylic shell during iteration. Keep the shell frame unchanged, confine the bottom edge treatment to the expanded header, and cover desktop and mobile geometry in Playwright. --- src/styles.css | 12 +++-- tests/browser/agent-acrylic.e2e.ts | 75 +++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/styles.css b/src/styles.css index 1b3ca65..9eb9182 100644 --- a/src/styles.css +++ b/src/styles.css @@ -140,6 +140,7 @@ --acrylic-highlight: rgba(255, 255, 255, 0.68); --shadow-navigation: 0 1px 0 rgba(48, 65, 85, 0.14); --shadow-panel: 0 12px 32px rgba(15, 33, 55, 0.14); + --shadow-agent-history-bottom: 0 12px 12px -12px rgba(15, 33, 55, 0.32); --shadow-control: 0 6px 18px rgba(15, 33, 55, 0.1); --shadow-transient: 0 14px 34px rgba(15, 33, 55, 0.18); --shadow-agent-floating: @@ -244,6 +245,7 @@ --glass-transient: rgba(232, 242, 250, 0.76); --acrylic-outline: rgba(43, 63, 86, 0.28); --shadow-panel: 0 14px 36px rgba(7, 18, 32, 0.22); + --shadow-agent-history-bottom: 0 14px 14px -14px rgba(7, 18, 32, 0.4); --shadow-control: 0 7px 20px rgba(7, 18, 32, 0.16); --shadow-transient: 0 16px 38px rgba(7, 18, 32, 0.26); --shadow-agent-floating: @@ -681,14 +683,18 @@ button:disabled { z-index: 2; border-color: var(--acrylic-outline); border-style: solid; - border-width: 0 1px 1px; + border-width: 0 0 1px; border-bottom-right-radius: var(--radius-panel); border-bottom-left-radius: var(--radius-panel); - box-shadow: var(--shadow-panel); + box-shadow: var(--shadow-agent-history-bottom); +} + +.agent-panel-integrated-header { + inset-inline: -0.25rem; + top: -0.25rem; } .agent-panel-header-mobile { - inset-inline: -0.25rem; top: -2rem; padding-top: 2rem; } diff --git a/tests/browser/agent-acrylic.e2e.ts b/tests/browser/agent-acrylic.e2e.ts index ea1717e..ad16c69 100644 --- a/tests/browser/agent-acrylic.e2e.ts +++ b/tests/browser/agent-acrylic.e2e.ts @@ -117,6 +117,7 @@ test("desktop Agent header expands into one attached acrylic history surface", a const headerBefore = await agentHeader.boundingBox(); const conversationBefore = await agentConversation.boundingBox(); const historyButtonPresentationBefore = await getSurfacePresentation(historyButton); + const agentPanelFrameBefore = await expectAcrylicOutline(agentPanel); await historyButton.click(); @@ -127,6 +128,8 @@ test("desktop Agent header expands into one attached acrylic history surface", a await page.mouse.move(720, 320); await expectAttachedHistoryExtension(history); await expectTransparentExpandedHeader(agentHeader, agentPanel); + await expectUnifiedHistoryStructure(history); + await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore); await expect .poll(() => getSurfacePresentation(historyButton)) .toEqual(historyButtonPresentationBefore); @@ -144,9 +147,11 @@ test("desktop Agent header expands into one attached acrylic history surface", a expectRoundedValue(headerBox!.y, headerBefore!.y); expect(headerBox!.height).toBeGreaterThan(headerBefore!.height); expectRoundedValue(historyBox!.y, headerBefore!.y + headerBefore!.height); - expectRoundedValue(historyBox!.x, headerBox!.x + 1); - expectRoundedValue(historyBox!.width, headerBox!.width - 2); - expect(headerBox!.width).toBeLessThanOrEqual(agentPanelBox!.width); + expectRoundedValue(headerBox!.x, agentPanelBox!.x + 1); + expectRoundedValue(headerBox!.y, agentPanelBox!.y + 1); + expectRoundedValue(headerBox!.width, agentPanelBox!.width - 2); + expectRoundedValue(historyBox!.x, headerBox!.x); + expectRoundedValue(historyBox!.width, headerBox!.width); expectRoundedBox(conversationAfter!, conversationBefore!); await history.getByRole("button", { name: "重命名对话" }).first().click(); @@ -164,6 +169,7 @@ test("desktop Agent header expands into one attached acrylic history surface", a await expect(history).toBeHidden(); await expect(historyButton).toBeFocused(); await expect(historyButton).toHaveAttribute("aria-expanded", "false"); + await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore); await historyButton.click(); await expect(history).toBeVisible(); @@ -193,6 +199,7 @@ test("mobile Agent mirrors the desktop floating history surface", async ({ const headerBefore = await agentHeader.boundingBox(); const conversationBefore = await agentConversation.boundingBox(); const historyButtonPresentationBefore = await getSurfacePresentation(historyButton); + const mobileSheetFrameBefore = await expectAcrylicOutline(mobileSheet); await historyButton.click(); @@ -202,6 +209,9 @@ test("mobile Agent mirrors the desktop floating history surface", async ({ await page.mouse.move(10, 100); await expectAttachedHistoryExtension(history); await expectTransparentExpandedHeader(agentHeader, mobileSheet); + await expectUnifiedHistoryStructure(history); + await expect.poll(() => getAcrylicFrame(mobileSheet)).toEqual(mobileSheetFrameBefore); + await expect(agentPanel).toHaveCSS("box-shadow", "none"); await expect .poll(() => getSurfacePresentation(historyButton)) .toEqual(historyButtonPresentationBefore); @@ -223,7 +233,7 @@ test("mobile Agent mirrors the desktop floating history surface", async ({ expectRoundedValue(headerAfter!.width, panelBox!.width); expect(headerAfter!.height).toBeGreaterThan(headerBefore!.height); expectRoundedValue(historyBox!.y, headerBefore!.y + headerBefore!.height); - expectRoundedValue(historyBox!.width, headerAfter!.width - 2); + expectRoundedValue(historyBox!.width, headerAfter!.width); expect(headerAfter!.height).toBeLessThan(panelBox!.height); expectRoundedBox(conversationAfter!, conversationBefore!); await expect(agentConversation).toHaveClass(/agent-panel-conversation-canvas/); @@ -344,6 +354,57 @@ async function expectAttachedHistoryExtension(history: Locator) { expect(presentation.boxShadow).toBe("none"); } +async function getAcrylicFrame(surface: Locator) { + return surface.evaluate((element) => { + const style = getComputedStyle(element); + + return { + borderBottomColor: style.borderBottomColor, + borderLeftColor: style.borderLeftColor, + borderRightColor: style.borderRightColor, + borderTopColor: style.borderTopColor, + boxShadow: style.boxShadow + }; + }); +} + +async function expectAcrylicOutline(surface: Locator) { + const frame = await getAcrylicFrame(surface); + + expect(frame.borderTopColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(frame.borderRightColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(frame.borderBottomColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(frame.borderLeftColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(frame.boxShadow).toContain("0px 1px 0px 0px inset"); + expect(frame.boxShadow).toContain("0px 0px 0px 1px"); + + return frame; +} + +async function expectUnifiedHistoryStructure(history: Locator) { + const structure = await history.evaluate((element) => { + const paragraphs = Array.from(element.querySelectorAll("p")); + const heading = paragraphs.find((paragraph) => paragraph.textContent?.trim() === "历史记录"); + const count = paragraphs.find((paragraph) => + /^(?:\d+ 个会话|暂无历史会话)$/.test(paragraph.textContent?.trim() ?? "") + ); + + return { + countTagName: count?.tagName ?? null, + headingTagName: heading?.tagName ?? null, + siblings: Boolean(heading && count && heading.parentElement === count.parentElement) + }; + }); + + expect(structure).toEqual({ + countTagName: "P", + headingTagName: "P", + siblings: true + }); + await expect(history.getByRole("button", { name: "重命名对话" }).first()).toBeVisible(); + await expect(history.getByRole("button", { name: "删除对话" }).first()).toBeVisible(); +} + async function expectTransparentExpandedHeader(header: Locator, agentSurface: Locator) { const [presentation, agentOutlineColor] = await Promise.all([ header.evaluate((element) => { @@ -371,10 +432,10 @@ async function expectTransparentExpandedHeader(header: Locator, agentSurface: Lo expect(presentation.borderBottomLeftRadius).toBe("16px"); expect(presentation.borderBottomRightRadius).toBe("16px"); expect(presentation.borderBottomWidth).toBe("1px"); - expect(presentation.borderLeftWidth).toBe("1px"); - expect(presentation.borderRightWidth).toBe("1px"); + expect(presentation.borderLeftWidth).toBe("0px"); + expect(presentation.borderRightWidth).toBe("0px"); expect(presentation.borderTopWidth).toBe("0px"); - expect(presentation.boxShadow).not.toBe("none"); + expect(presentation.boxShadow).toContain("0px 12px 12px -12px"); } async function getSurfacePresentation(surface: Locator) {