-
{children}
+ {active ? (
+
+
+
+
+
+
+
+ 最小化
+
+ {mode === "maximized" || mode === "docked" ? (
+
+
+ 恢复为浮动窗口
+
+ ) : (
+
+
+ 最大化
+
+ )}
+ {onClose ? (
+ <>
+
+
+
+ 关闭并销毁
+
+ >
+ ) : null}
+
+
+ ) : null}
{swapTarget ? (
void; children: ReactNode }) {
- return (
-
- );
-}
-
function resizeRect(rect: WorkspaceRect, direction: "move" | ResizeDirection, deltaX: number, deltaY: number): WorkspaceRect {
if (direction === "move") return { ...rect, x: rect.x + deltaX, y: rect.y + deltaY };
const west = direction.includes("w");
diff --git a/tests/browser/workspace-main-frame.e2e.ts b/tests/browser/workspace-main-frame.e2e.ts
index 9ed0c80..2d7a6ec 100644
--- a/tests/browser/workspace-main-frame.e2e.ts
+++ b/tests/browser/workspace-main-frame.e2e.ts
@@ -1,7 +1,7 @@
import { expect, test, type Page } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
-test("temporary Agent analysis windows replace and restore the map workspace", async ({ page }) => {
+test("temporary Agent analysis windows float above the persistent map workspace", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
@@ -12,22 +12,29 @@ test("temporary Agent analysis windows replace and restore the map workspace", a
await expect(frame).toBeVisible();
await expect(mapWindow).toBeVisible();
await expect(testButton).toBeVisible();
+ await expect(page.getByLabel("工作台导航")).toHaveCount(0);
+ await expect(page.getByLabel("Agent 命令面板")).toBeVisible();
+ await expect(mapWindow.getByRole("navigation", { name: "地图工具" })).toBeVisible();
+ await expect(page.locator(".workspace-window-titlebar")).toHaveCount(0);
await testButton.click();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toBeVisible();
- await expect(mapWindow).toBeHidden();
+ await expect(mapWindow).toBeVisible();
await testButton.click();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeVisible();
await expect(page.getByText("2 / 6 个临时分析窗")).toBeVisible();
- await page.getByRole("button", { name: "最小化供水服务分区平衡分析" }).click();
+ await openWindowOperationMenu(page, "供水服务分区平衡分析");
+ await page.getByRole("menuitem", { name: "最小化" }).click();
await expect(page.getByRole("button", { name: "恢复供水服务分区平衡分析" })).toBeVisible();
await page.getByRole("button", { name: "恢复供水服务分区平衡分析" }).click();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeVisible();
- await page.getByRole("button", { name: "关闭并销毁供水服务分区平衡分析" }).click();
- await page.getByRole("button", { name: "关闭并销毁北辰分区压力异常诊断" }).click();
+ await openWindowOperationMenu(page, "供水服务分区平衡分析");
+ await page.getByRole("menuitem", { name: "关闭并销毁" }).click();
+ await openWindowOperationMenu(page, "北辰分区压力异常诊断");
+ await page.getByRole("menuitem", { name: "关闭并销毁" }).click();
await expect(mapWindow).toBeVisible();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toHaveCount(0);
await expect(page.getByText("0 / 6 个临时分析窗")).toBeVisible();
@@ -42,21 +49,27 @@ test("the map document floats and minimizes to its taskbar entry", async ({ page
const dockedBox = await workspace.boundingBox();
expect(dockedBox).not.toBeNull();
- await page.getByRole("button", { name: "浮动供水管网地图" }).click();
+ await openWindowOperationMenu(page, "供水管网地图");
+ await page.getByRole("menuitem", { name: "恢复为浮动窗口" }).click();
const floatingBox = await mapWindow.boundingBox();
expect(floatingBox).not.toBeNull();
expect(floatingBox!.width).toBeLessThan(dockedBox!.width);
expect(floatingBox!.height).toBeLessThan(dockedBox!.height);
- await expect(page.getByRole("button", { name: "最大化供水管网地图" })).toBeVisible();
+ await expect(mapWindow).toHaveCSS("border-radius", "0px");
+ const floatingClassName = await mapWindow.getAttribute("class");
+ expect(floatingClassName).not.toContain("ring-1");
+ expect(await mapWindow.evaluate((element) => getComputedStyle(element).boxShadow)).not.toBe("none");
+ await expect(page.getByRole("button", { name: "打开供水管网地图窗口操作" })).toBeAttached();
- await page.getByRole("button", { name: "最小化供水管网地图" }).click();
+ await openWindowOperationMenu(page, "供水管网地图");
+ await page.getByRole("menuitem", { name: "最小化" }).click();
await expect(mapWindow).toBeHidden();
const restoreTask = page.getByRole("button", { name: "恢复供水管网地图" });
await expect(restoreTask).toBeVisible();
await restoreTask.click();
await expect(mapWindow).toBeVisible();
- await expect(page.getByRole("button", { name: "最大化供水管网地图" })).toBeVisible();
+ await expect(page.getByRole("button", { name: "打开供水管网地图窗口操作" })).toBeAttached();
});
test("the test control cycles through three trusted document profiles", async ({ page }) => {
@@ -89,7 +102,7 @@ test("temporary documents survive a mobile breakpoint round trip", async ({ page
await page.setViewportSize({ width: 1440, height: 900 });
await expect(analysisHeading).toBeVisible();
- await expect(page.locator("#workspace-map")).toBeHidden();
+ await expect(page.locator("#workspace-map")).toBeVisible();
});
test("layout presets arrange transient windows and keep the map visible", async ({ page }) => {
@@ -108,6 +121,7 @@ test("layout presets arrange transient windows and keep the map visible", async
await expect(page.locator("#workspace-map")).toBeVisible();
await expect(page.locator("section[aria-label='低压事件调度处置流程']")).toBeVisible();
await expect(page.locator("section[aria-label='北辰分区压力异常诊断']")).toBeHidden();
+ await expect(page.locator("section[aria-label='低压事件调度处置流程']")).toHaveCSS("border-radius", "0px");
const workspaceBox = await workspace.boundingBox();
const primaryBox = await page.locator("section[aria-label='低压事件调度处置流程']").boundingBox();
@@ -138,6 +152,7 @@ test("dragging a small window onto the primary window swaps their complete slots
expect(mapBefore!.width).toBeLessThan(analysisBefore!.width);
await page.mouse.move(mapBefore!.x + 72, mapBefore!.y + 22);
+ await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(
analysisBefore!.x + analysisBefore!.width / 2,
@@ -146,6 +161,7 @@ test("dragging a small window onto the primary window swaps their complete slots
);
await expect(page.getByText("释放以交换")).toBeVisible();
await page.mouse.up();
+ await page.keyboard.up("Alt");
const mapAfter = await mapWindow.boundingBox();
const analysisAfter = await analysisWindow.boundingBox();
@@ -172,21 +188,32 @@ test("dropping on blank workspace exits auto layout and Escape cancels a drag",
expect(workspaceBox).not.toBeNull();
await page.mouse.move(before!.x + 80, before!.y + 22);
+ await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(workspaceBox!.x + 4, workspaceBox!.y + 4, { steps: 8 });
await page.keyboard.press("Escape");
+ await page.keyboard.up("Alt");
await expect(workspace).toHaveAttribute("data-layout-mode", "split");
const afterCancel = await analysisWindow.boundingBox();
expect(Math.abs(afterCancel!.x - before!.x)).toBeLessThanOrEqual(2);
await page.mouse.move(before!.x + 80, before!.y + 22);
+ await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(workspaceBox!.x + 4, workspaceBox!.y + 4, { steps: 8 });
await page.mouse.up();
+ await page.keyboard.up("Alt");
await expect(workspace).toHaveAttribute("data-layout-mode", "free");
await expect(page.getByText("北辰分区压力异常诊断已转为自由排列")).toBeAttached();
});
+async function openWindowOperationMenu(page: Page, title: string) {
+ const window = page.locator(`section[aria-label='${title}']`);
+ await page.getByRole("button", { name: `切换到${title}` }).click();
+ await window.hover();
+ await page.getByRole("button", { name: `打开${title}窗口操作` }).click();
+}
+
async function prepareMainFrame(page: Page) {
await mockAgentApi(page);
await page.route("**/runtime-config.js", async (route) => {