fix(workbench): refine agent dock and smooth map resizing

Unify dock surfaces, headers and dividers, and remove the visible context strip.
Limit pointer resizing to 400-640px and reveal the edge grip on hover.
Resize and redraw the map in the same layout cycle to prevent blanking and lag.
Preserve horizontal centering during conversation control press feedback.

Validation: isolated lint, typecheck, 21 focused unit tests, and 10 relevant
browser checks passed. Snapshot baselines regenerated from the isolated change.
The existing mobile alert summary test also fails on unchanged HEAD because
the expected demo alert source is unavailable in this environment.
This commit is contained in:
2026-10-08 17:34:42 +08:00
parent 7c6d06cb43
commit d2d705ed4c
19 changed files with 680 additions and 170 deletions
+114 -4
View File
@@ -53,12 +53,25 @@ test("desktop Agent docks beside the workspace with a solid reading surface", as
await waitForAnimations(agentPanel);
await expectAcrylicSurface(topBar);
await expect(agentPanel).toHaveCSS("background-color", "oklch(0.965 0.012 248)");
await expect(agentPanel).toHaveCSS("background-color", "oklch(0.972 0.005 250)");
await expect(agentPanel).toHaveCSS("backdrop-filter", "none");
await expect(agentPanel).toHaveCSS("border-top-left-radius", "0px");
await expect(agentPanel).toHaveCSS("border-top-right-radius", "16px");
await expect(agentPanel).not.toHaveCSS("box-shadow", "none");
await expect(agentPanel).toHaveCSS("border-radius", "0px");
await expect(agentPanel).toHaveCSS("border-top-width", "0px");
await expect(agentPanel).toHaveCSS("border-bottom-width", "0px");
await expect(agentPanel).toHaveCSS("border-left-width", "0px");
await expect(agentPanel).toHaveCSS("border-right-width", "1px");
await expect(agentPanel).toHaveCSS("border-right-color", "oklch(0.905 0.009 250)");
await expect(agentPanel).toHaveCSS("box-shadow", "none");
await expect(agentControls).toHaveCSS("border-top-width", "1px");
await expect(agentControls).toHaveCSS("background-color", "oklch(0.985 0.003 250)");
await expect(agentPanel.locator("form.agent-panel-control")).toHaveCSS("background-color", "rgb(255, 255, 255)");
const capabilities = agentPanel.getByRole("list", { name: "Agent 分析能力" });
await expect(capabilities.getByRole("listitem")).toHaveCount(4);
await expect(capabilities).toHaveCSS("box-shadow", "none");
for (const item of await capabilities.getByRole("listitem").all()) {
await expect(item).toHaveCSS("border-right-width", "0px");
await expect(item).toHaveCSS("border-bottom-width", "0px");
}
await expect(
agentPanel.getByText("直接说出你想调查的问题,我会整理监测与空间证据,并将分析结果带回地图。")
).toHaveCSS("color", "rgb(51, 65, 85)");
@@ -72,8 +85,21 @@ test("desktop Agent docks beside the workspace with a solid reading surface", as
expect(conversationBox).not.toBeNull();
expect(scrollBox).not.toBeNull();
expect(panelBox!.x).toBe(0);
const topBarBox = await topBar.boundingBox();
expect(panelBox!.y).toBe(topBarBox!.y + topBarBox!.height);
expect(panelBox!.y + panelBox!.height).toBe(page.viewportSize()!.height);
expect(panelBox!.width).toBe(500);
await expect.poll(async () => (await workspace.boundingBox())?.x).toBe(500);
const headerBox = await agentHeader.boundingBox();
const workspaceBox = await workspace.boundingBox();
expect(headerBox!.height).toBe(topBarBox!.height);
expect(headerBox!.y).toBe(workspaceBox!.y);
await expect(topBar).toHaveCSS("box-shadow", "none");
await expect(agentHeader).toHaveCSS("box-shadow", "none");
await expect(agentHeader.locator(":scope > div").first()).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
expect(await topBar.evaluate((element) => getComputedStyle(element).borderBottomColor)).toBe(
await agentPanel.evaluate((element) => getComputedStyle(element).borderRightColor)
);
expect(Math.abs(conversationBox!.x - panelBox!.x)).toBeLessThanOrEqual(1);
await expect(agentConversationScroll).toHaveCSS("scrollbar-gutter", "stable both-edges");
@@ -81,6 +107,57 @@ test("desktop Agent docks beside the workspace with a solid reading surface", as
await page.getByPlaceholder("输入调度问题,Agent 将通过后端会话流式响应").focus();
await expect.poll(() => getSurfacePresentation(agentPanel)).toEqual(beforeFocus);
await page.screenshot({ path: "test-results/agent-history-ui/desktop-dock.png" });
for (const viewport of [
{ width: 1024, height: 768, panelWidth: 500 },
{ width: 1920, height: 1080, panelWidth: 540 }
]) {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await expect(agentPanel).toHaveCSS("width", `${viewport.panelWidth}px`);
await expect.poll(async () => (await workspace.boundingBox())?.x).toBe(viewport.panelWidth);
const resizedPanelBox = await agentPanel.boundingBox();
expect(resizedPanelBox!.x).toBe(0);
expect(resizedPanelBox!.y).toBe(topBarBox!.y + topBarBox!.height);
expect(resizedPanelBox!.y + resizedPanelBox!.height).toBe(viewport.height);
await page.screenshot({
path: `test-results/agent-history-ui/desktop-dock-${viewport.width}.png`
});
}
});
test("desktop Agent portal menus keep local material and respect reduced transparency", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: /展开 Agent 助手面板/ }).click();
const panel = page.getByRole("complementary", { name: "Agent 命令面板" });
await waitForAnimations(panel);
for (const name of ["选择 Agent 模型", "权限批准模式"]) {
const trigger = panel.getByRole("button", { name });
await trigger.click();
const menu = page.getByRole("menu");
await expect(menu).toHaveAttribute("data-agent-presentation", "desktop-dock");
expect(await menu.evaluate((element) => element.closest("aside"))).toBeNull();
await expect(menu).toHaveCSS("background-color", "rgba(255, 255, 255, 0.94)");
await expect(menu).toHaveCSS("backdrop-filter", "blur(12px) saturate(1.03)");
await expect(menu).toHaveCSS("background-image", "none");
await page.keyboard.press("Escape");
await expect(trigger).toBeFocused();
}
const cdp = await page.context().newCDPSession(page);
await cdp.send("Emulation.setEmulatedMedia", {
features: [{ name: "prefers-reduced-transparency", value: "reduce" }]
});
expect(await page.evaluate(() => matchMedia("(prefers-reduced-transparency: reduce)").matches)).toBe(true);
await panel.getByRole("button", { name: "选择 Agent 模型" }).click();
const menu = page.getByRole("menu");
await expect(menu).toHaveCSS("background-color", "rgb(255, 255, 255)");
await expect(menu).toHaveCSS("backdrop-filter", "none");
await page.keyboard.press("Escape");
await panel.getByRole("button", { name: "打开 Agent 历史记录" }).click();
const history = panel.getByRole("region", { name: "Agent 历史记录" });
await expect(history).toHaveCSS("background-color", "rgb(255, 255, 255)");
await expect(history).toHaveCSS("backdrop-filter", "none");
});
test("desktop Agent history remains usable inside the docked navigation pane", async ({ page }) => {
@@ -99,6 +176,8 @@ test("desktop Agent history remains usable inside the docked navigation pane", a
await waitForAnimations(history);
await expect(historyButton).toHaveAttribute("aria-expanded", "true");
await expectUnifiedHistoryStructure(history);
await expect(history).toHaveCSS("background-color", "rgba(255, 255, 255, 0.94)");
await expect(history).toHaveCSS("backdrop-filter", "blur(12px) saturate(1.03)");
await history.getByRole("button", { name: "重命名对话" }).first().click();
const renameInput = history.locator("input").first();
@@ -118,6 +197,35 @@ test("desktop Agent history remains usable inside the docked navigation pane", a
await expect(historyButton).toHaveAttribute("aria-expanded", "false");
});
test("desktop Agent press feedback keeps pointer clicks and keyboard activation stable", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: /展开 Agent 助手面板/ }).click();
const panel = page.getByRole("complementary", { name: "Agent 命令面板" });
await waitForAnimations(panel);
const trigger = panel.getByRole("button", { name: "打开 Agent 历史记录" });
const history = panel.getByRole("region", { name: "Agent 历史记录" });
const before = await trigger.boundingBox();
await page.mouse.move(before!.x + before!.width / 2, before!.y + before!.height / 2);
await page.mouse.down();
const pressed = await trigger.boundingBox();
expect(pressed!.y).toBe(before!.y + 1);
expect(pressed!.width).toBe(before!.width);
await page.mouse.up();
await expect(trigger).toHaveAttribute("aria-expanded", "true");
await expect(history).toBeVisible();
await waitForAnimations(panel);
await page.keyboard.press("Tab");
await trigger.focus();
expect(await trigger.evaluate((element) => element.matches(":focus-visible"))).toBe(true);
const focused = await trigger.boundingBox();
await page.keyboard.down("Space");
expect(await trigger.boundingBox()).toEqual(focused);
await page.keyboard.up("Space");
await expect(history).toBeHidden();
await expect(trigger).toBeFocused();
});
test("mobile Agent mirrors the desktop floating history surface", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/", { waitUntil: "domcontentloaded" });
@@ -192,6 +300,8 @@ test("docked Agent respects forced color mode", async ({ page }) => {
await expect(navigation).toBeVisible();
await expect(navigation).toHaveCSS("backdrop-filter", "none");
await expect(navigation).toHaveCSS("box-shadow", "none");
await expect(navigation).toHaveCSS("border-right-width", "1px");
await expect(navigation).toHaveCSS("border-right-color", "rgb(0, 0, 0)");
await expect(workspace).toBeVisible();
await expect(page.getByRole("toolbar", { name: "工作区窗口任务栏" })).toHaveCount(0);
await page.screenshot({ path: "test-results/agent-history-ui/forced-colors.png" });