fix(workbench): align investigation diagram controls

This commit is contained in:
2026-10-10 16:30:20 +08:00
parent ba78a24384
commit e0eb6a721e
2 changed files with 83 additions and 2 deletions
+37 -1
View File
@@ -1353,6 +1353,12 @@ button.agent-history-session-trigger:not(:disabled):active {
.investigation-diagram .node { cursor: pointer; }
.investigation-diagram .node:hover { filter: brightness(0.97); }
.investigation-diagram [data-streamdown="mermaid-block"] {
--diagram-control-rail-inset: 1.0625rem;
--diagram-control-group-gap: 0.5rem;
--diagram-zoom-rail-height: 7.625rem;
}
/* Match Button's compact icon-sm size, including controls portalled to body. */
button[type="button"]:has(> .investigation-diagram-control-icon) {
--diagram-control-size: 2rem;
@@ -1398,14 +1404,37 @@ div:has(> button > [data-investigation-diagram-control="zoom-in"]),
.investigation-diagram [data-streamdown="mermaid-block"] > div:first-child { display: none; }
.investigation-diagram [data-streamdown="mermaid-block"] > div:has(> [data-streamdown="mermaid-block-actions"]) {
position: absolute;
z-index: 20;
inset-block-start: auto;
inset-block-end: calc(
var(--diagram-control-rail-inset) + var(--diagram-zoom-rail-height) +
var(--diagram-control-group-gap)
);
inset-inline-start: auto;
inset-inline-end: var(--diagram-control-rail-inset);
height: auto;
margin-top: 0;
}
.investigation-diagram div:has(> button > [data-investigation-diagram-control="zoom-in"]) {
inset-inline-start: auto;
inset-inline-end: 0.5rem;
}
button[type="button"]:has(> [data-investigation-diagram-control="close"]) {
position: absolute;
border-color: var(--surface-border);
inset-block-start: 1.3125rem;
inset-inline-end: 1.3125rem;
border-color: transparent;
background: var(--surface-control);
box-shadow:
0 0 0 4px var(--surface-control),
0 0 0 5px var(--surface-border);
}
button[type="button"]:has(> [data-investigation-diagram-control="close"])::after {
inset: -5px;
}
@media (hover: hover) {
@@ -1424,6 +1453,13 @@ button[type="button"]:has(> .investigation-diagram-control-icon):active:not(:dis
transform: scale(0.98);
}
button[type="button"]:has(> [data-investigation-diagram-control="close"]):active:not(:disabled) {
box-shadow:
0 0 0 4px var(--surface-control),
0 0 0 5px var(--action-border-pressed),
var(--action-shadow-secondary-pressed);
}
button[type="button"]:has(> .investigation-diagram-control-icon):focus-visible {
outline: 2px solid var(--action-focus-ring);
outline-offset: 2px;
@@ -202,7 +202,34 @@ test("调查报告展示 SVG 脉络、处置进展和报告版本,缺数正常
});
}
};
const assertInlineControlRail = async (surface: typeof diagram) => {
const mermaidBlock = surface.locator('[data-streamdown="mermaid-block"]');
const graph = surface.locator('[data-streamdown="mermaid"]');
const fullscreenButton = surface.getByRole("button", { name: "全屏查看", exact: true });
const zoomInButton = surface.getByRole("button", { name: "Zoom in", exact: true });
const fullscreenGroup = surface.locator('[data-streamdown="mermaid-block-actions"]');
const zoomGroup = zoomInButton.locator("..");
const [blockBox, graphBox, fullscreenBox, zoomInBox, fullscreenGroupBox, zoomGroupBox] = await Promise.all([
mermaidBlock.boundingBox(), graph.boundingBox(), fullscreenButton.boundingBox(), zoomInButton.boundingBox(),
fullscreenGroup.boundingBox(), zoomGroup.boundingBox()
]);
expect(blockBox).not.toBeNull();
expect(graphBox).not.toBeNull();
expect(fullscreenBox).not.toBeNull();
expect(zoomInBox).not.toBeNull();
expect(fullscreenGroupBox).not.toBeNull();
expect(zoomGroupBox).not.toBeNull();
expect(Math.round(graphBox!.y - blockBox!.y)).toBeLessThanOrEqual(10);
expect(Math.round(fullscreenBox!.x)).toBe(Math.round(zoomInBox!.x));
expect(Math.round(zoomInBox!.y - fullscreenBox!.y - fullscreenBox!.height)).toBe(18);
expect(Math.round(fullscreenGroupBox!.x)).toBe(Math.round(zoomGroupBox!.x));
expect(Math.round(fullscreenGroupBox!.width)).toBe(Math.round(zoomGroupBox!.width));
expect(Math.round(zoomGroupBox!.y - fullscreenGroupBox!.y - fullscreenGroupBox!.height)).toBe(8);
expect(Math.round(blockBox!.x + blockBox!.width - fullscreenGroupBox!.x - fullscreenGroupBox!.width)).toBe(18);
expect(Math.round(graphBox!.x + graphBox!.width - zoomGroupBox!.x - zoomGroupBox!.width)).toBe(8);
};
assertControlGeometry(inlineControls);
await assertInlineControlRail(diagram);
const zoomIn = diagram.getByRole("button", { name: "Zoom in", exact: true });
const zoomOut = diagram.getByRole("button", { name: "Zoom out", exact: true });
const resetZoom = diagram.getByRole("button", { name: "Reset zoom and pan", exact: true });
@@ -237,6 +264,21 @@ test("调查报告展示 SVG 脉络、处置进展和报告版本,缺数正常
await expect(fullscreen.locator("svg[id]")).toHaveCount(1);
const fullscreenControls = await captureDiagramControls(fullscreen);
assertControlGeometry(fullscreenControls);
const assertFullscreenCloseFrame = async () => {
const close = fullscreen.getByRole("button", { name: "退出全屏", exact: true });
const frame = await close.evaluate((button) => {
const rect = button.getBoundingClientRect();
return {
top: rect.top,
right: window.innerWidth - rect.right,
boxShadow: getComputedStyle(button).boxShadow
};
});
expect(Math.round(frame.top)).toBe(21);
expect(Math.round(frame.right)).toBe(21);
expect(frame.boxShadow).not.toBe("none");
};
await assertFullscreenCloseFrame();
await writeFile(testInfo.outputPath("diagram-control-geometry.json"), JSON.stringify({ viewport: page.viewportSize(), inline: inlineControls, fullscreen: fullscreenControls }, null, 2));
await testInfo.attach("styled-investigation-fullscreen", { body: await page.screenshot({ path: testInfo.outputPath("styled-investigation-fullscreen.png") }), contentType: "image/png" });
await fullscreen.getByRole("button", { name: "退出全屏", exact: true }).click();
@@ -345,12 +387,15 @@ test("调查报告展示 SVG 脉络、处置进展和报告版本,缺数正常
await expect(mobilePanel.getByLabel("调查脉络", { exact: true }).locator("svg[id]")).toHaveCount(1);
const mobileDiagram = mobilePanel.getByLabel("调查脉络", { exact: true });
assertControlGeometry(await captureDiagramControls(mobileDiagram));
await assertInlineControlRail(mobileDiagram);
if (width === 320) {
await mobileDiagram.getByRole("button", { name: "全屏查看", exact: true }).click();
await expect(fullscreen.locator("svg[id]")).toHaveCount(1);
assertControlGeometry(await captureDiagramControls(fullscreen));
await assertFullscreenCloseFrame();
await testInfo.attach("diagram-fullscreen-320", { body: await page.screenshot({ path: testInfo.outputPath("diagram-fullscreen-320.png") }), contentType: "image/png" });
await fullscreen.getByRole("button", { name: "退出全屏", exact: true }).click();
await fullscreen.getByRole("button", { name: "退出全屏", exact: true }).evaluate((button) => button.click());
await expect(fullscreen).toHaveCount(0);
}
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
await mobilePanel.getByLabel("任务报告", { exact: true }).scrollIntoViewIfNeeded();