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.
This commit is contained in:
2026-07-28 17:24:32 +08:00
parent f4318a9b9c
commit 73896a893f
2 changed files with 77 additions and 10 deletions
+9 -3
View File
@@ -140,6 +140,7 @@
--acrylic-highlight: rgba(255, 255, 255, 0.68); --acrylic-highlight: rgba(255, 255, 255, 0.68);
--shadow-navigation: 0 1px 0 rgba(48, 65, 85, 0.14); --shadow-navigation: 0 1px 0 rgba(48, 65, 85, 0.14);
--shadow-panel: 0 12px 32px rgba(15, 33, 55, 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-control: 0 6px 18px rgba(15, 33, 55, 0.1);
--shadow-transient: 0 14px 34px rgba(15, 33, 55, 0.18); --shadow-transient: 0 14px 34px rgba(15, 33, 55, 0.18);
--shadow-agent-floating: --shadow-agent-floating:
@@ -244,6 +245,7 @@
--glass-transient: rgba(232, 242, 250, 0.76); --glass-transient: rgba(232, 242, 250, 0.76);
--acrylic-outline: rgba(43, 63, 86, 0.28); --acrylic-outline: rgba(43, 63, 86, 0.28);
--shadow-panel: 0 14px 36px rgba(7, 18, 32, 0.22); --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-control: 0 7px 20px rgba(7, 18, 32, 0.16);
--shadow-transient: 0 16px 38px rgba(7, 18, 32, 0.26); --shadow-transient: 0 16px 38px rgba(7, 18, 32, 0.26);
--shadow-agent-floating: --shadow-agent-floating:
@@ -681,14 +683,18 @@ button:disabled {
z-index: 2; z-index: 2;
border-color: var(--acrylic-outline); border-color: var(--acrylic-outline);
border-style: solid; border-style: solid;
border-width: 0 1px 1px; border-width: 0 0 1px;
border-bottom-right-radius: var(--radius-panel); border-bottom-right-radius: var(--radius-panel);
border-bottom-left-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 { .agent-panel-header-mobile {
inset-inline: -0.25rem;
top: -2rem; top: -2rem;
padding-top: 2rem; padding-top: 2rem;
} }
+68 -7
View File
@@ -117,6 +117,7 @@ test("desktop Agent header expands into one attached acrylic history surface", a
const headerBefore = await agentHeader.boundingBox(); const headerBefore = await agentHeader.boundingBox();
const conversationBefore = await agentConversation.boundingBox(); const conversationBefore = await agentConversation.boundingBox();
const historyButtonPresentationBefore = await getSurfacePresentation(historyButton); const historyButtonPresentationBefore = await getSurfacePresentation(historyButton);
const agentPanelFrameBefore = await expectAcrylicOutline(agentPanel);
await historyButton.click(); 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 page.mouse.move(720, 320);
await expectAttachedHistoryExtension(history); await expectAttachedHistoryExtension(history);
await expectTransparentExpandedHeader(agentHeader, agentPanel); await expectTransparentExpandedHeader(agentHeader, agentPanel);
await expectUnifiedHistoryStructure(history);
await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore);
await expect await expect
.poll(() => getSurfacePresentation(historyButton)) .poll(() => getSurfacePresentation(historyButton))
.toEqual(historyButtonPresentationBefore); .toEqual(historyButtonPresentationBefore);
@@ -144,9 +147,11 @@ test("desktop Agent header expands into one attached acrylic history surface", a
expectRoundedValue(headerBox!.y, headerBefore!.y); expectRoundedValue(headerBox!.y, headerBefore!.y);
expect(headerBox!.height).toBeGreaterThan(headerBefore!.height); expect(headerBox!.height).toBeGreaterThan(headerBefore!.height);
expectRoundedValue(historyBox!.y, headerBefore!.y + headerBefore!.height); expectRoundedValue(historyBox!.y, headerBefore!.y + headerBefore!.height);
expectRoundedValue(historyBox!.x, headerBox!.x + 1); expectRoundedValue(headerBox!.x, agentPanelBox!.x + 1);
expectRoundedValue(historyBox!.width, headerBox!.width - 2); expectRoundedValue(headerBox!.y, agentPanelBox!.y + 1);
expect(headerBox!.width).toBeLessThanOrEqual(agentPanelBox!.width); expectRoundedValue(headerBox!.width, agentPanelBox!.width - 2);
expectRoundedValue(historyBox!.x, headerBox!.x);
expectRoundedValue(historyBox!.width, headerBox!.width);
expectRoundedBox(conversationAfter!, conversationBefore!); expectRoundedBox(conversationAfter!, conversationBefore!);
await history.getByRole("button", { name: "重命名对话" }).first().click(); 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(history).toBeHidden();
await expect(historyButton).toBeFocused(); await expect(historyButton).toBeFocused();
await expect(historyButton).toHaveAttribute("aria-expanded", "false"); await expect(historyButton).toHaveAttribute("aria-expanded", "false");
await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore);
await historyButton.click(); await historyButton.click();
await expect(history).toBeVisible(); await expect(history).toBeVisible();
@@ -193,6 +199,7 @@ test("mobile Agent mirrors the desktop floating history surface", async ({
const headerBefore = await agentHeader.boundingBox(); const headerBefore = await agentHeader.boundingBox();
const conversationBefore = await agentConversation.boundingBox(); const conversationBefore = await agentConversation.boundingBox();
const historyButtonPresentationBefore = await getSurfacePresentation(historyButton); const historyButtonPresentationBefore = await getSurfacePresentation(historyButton);
const mobileSheetFrameBefore = await expectAcrylicOutline(mobileSheet);
await historyButton.click(); await historyButton.click();
@@ -202,6 +209,9 @@ test("mobile Agent mirrors the desktop floating history surface", async ({
await page.mouse.move(10, 100); await page.mouse.move(10, 100);
await expectAttachedHistoryExtension(history); await expectAttachedHistoryExtension(history);
await expectTransparentExpandedHeader(agentHeader, mobileSheet); await expectTransparentExpandedHeader(agentHeader, mobileSheet);
await expectUnifiedHistoryStructure(history);
await expect.poll(() => getAcrylicFrame(mobileSheet)).toEqual(mobileSheetFrameBefore);
await expect(agentPanel).toHaveCSS("box-shadow", "none");
await expect await expect
.poll(() => getSurfacePresentation(historyButton)) .poll(() => getSurfacePresentation(historyButton))
.toEqual(historyButtonPresentationBefore); .toEqual(historyButtonPresentationBefore);
@@ -223,7 +233,7 @@ test("mobile Agent mirrors the desktop floating history surface", async ({
expectRoundedValue(headerAfter!.width, panelBox!.width); expectRoundedValue(headerAfter!.width, panelBox!.width);
expect(headerAfter!.height).toBeGreaterThan(headerBefore!.height); expect(headerAfter!.height).toBeGreaterThan(headerBefore!.height);
expectRoundedValue(historyBox!.y, headerBefore!.y + 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); expect(headerAfter!.height).toBeLessThan(panelBox!.height);
expectRoundedBox(conversationAfter!, conversationBefore!); expectRoundedBox(conversationAfter!, conversationBefore!);
await expect(agentConversation).toHaveClass(/agent-panel-conversation-canvas/); await expect(agentConversation).toHaveClass(/agent-panel-conversation-canvas/);
@@ -344,6 +354,57 @@ async function expectAttachedHistoryExtension(history: Locator) {
expect(presentation.boxShadow).toBe("none"); 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) { async function expectTransparentExpandedHeader(header: Locator, agentSurface: Locator) {
const [presentation, agentOutlineColor] = await Promise.all([ const [presentation, agentOutlineColor] = await Promise.all([
header.evaluate((element) => { header.evaluate((element) => {
@@ -371,10 +432,10 @@ async function expectTransparentExpandedHeader(header: Locator, agentSurface: Lo
expect(presentation.borderBottomLeftRadius).toBe("16px"); expect(presentation.borderBottomLeftRadius).toBe("16px");
expect(presentation.borderBottomRightRadius).toBe("16px"); expect(presentation.borderBottomRightRadius).toBe("16px");
expect(presentation.borderBottomWidth).toBe("1px"); expect(presentation.borderBottomWidth).toBe("1px");
expect(presentation.borderLeftWidth).toBe("1px"); expect(presentation.borderLeftWidth).toBe("0px");
expect(presentation.borderRightWidth).toBe("1px"); expect(presentation.borderRightWidth).toBe("0px");
expect(presentation.borderTopWidth).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) { async function getSurfacePresentation(surface: Locator) {