feat: add adaptive agent workspace windows

This commit is contained in:
2026-08-19 12:57:37 +08:00
parent d08cf2abc1
commit f58e0a70f4
18 changed files with 1797 additions and 309 deletions
@@ -0,0 +1,88 @@
import { describe, expect, it } from "vitest";
import {
MAX_ANALYSIS_WINDOWS,
closeAnalysisWindow,
createInitialWorkspaceState,
maximizeWorkspaceWindow,
minimizeWorkspaceWindow,
openAnalysisDocument,
resizeWorkspace,
restoreWorkspaceWindow,
updateWorkspaceWindowRect,
type AnalysisDocument
} from "./workspace-model";
const bounds = { width: 1180, height: 760 };
function documentAt(index: number): AnalysisDocument {
return {
id: `analysis-${index}`,
title: `分析 ${index}`,
subtitle: "测试",
generatedAt: "10:40:00",
blocks: []
};
}
describe("workspace window lifecycle", () => {
it("minimizes the map for the first analysis and restores it after the last window closes", () => {
const initial = createInitialWorkspaceState(bounds);
const opened = openAnalysisDocument(initial, documentAt(1), bounds);
expect(opened.map.mode).toBe("minimized");
expect(opened.analyses).toHaveLength(1);
const closed = closeAnalysisWindow(opened, "analysis-1");
expect(closed.analyses).toHaveLength(0);
expect(closed.map.mode).toBe("docked");
expect(closed.activeWindowId).toBe("workspace-map");
});
it("supports minimize, maximize and bounded restore", () => {
const opened = openAnalysisDocument(createInitialWorkspaceState(bounds), documentAt(1), bounds);
const minimized = minimizeWorkspaceWindow(opened, "analysis-1");
expect(minimized.analyses[0]?.mode).toBe("minimized");
const restored = restoreWorkspaceWindow(minimized, "analysis-1", bounds);
expect(restored.analyses[0]?.mode).toBe("floating");
const maximized = maximizeWorkspaceWindow(restored, "analysis-1");
expect(maximized.analyses[0]?.mode).toBe("maximized");
const resized = updateWorkspaceWindowRect(
maximized,
"analysis-1",
{ x: -100, y: -80, width: 4000, height: 3000 },
bounds
);
expect(resized.analyses[0]?.rect).toEqual({ x: 12, y: 12, width: 1156, height: 736 });
});
it("caps transient analysis windows without deleting an existing result", () => {
const state = Array.from({ length: MAX_ANALYSIS_WINDOWS }, (_, index) => index + 1).reduce(
(current, index) => openAnalysisDocument(current, documentAt(index), bounds),
createInitialWorkspaceState(bounds)
);
const overflow = openAnalysisDocument(state, documentAt(99), bounds);
expect(overflow).toBe(state);
expect(overflow.analyses).toHaveLength(MAX_ANALYSIS_WINDOWS);
});
it("reclamps current and restore rectangles when the workspace shrinks", () => {
const opened = openAnalysisDocument(createInitialWorkspaceState(bounds), documentAt(1), bounds);
const moved = updateWorkspaceWindowRect(
opened,
"analysis-1",
{ x: 500, y: 260, width: 660, height: 480 },
bounds
);
const resized = resizeWorkspace(moved, { width: 800, height: 600 });
const analysis = resized.analyses[0];
expect(analysis?.rect).toEqual({ x: 128, y: 108, width: 660, height: 480 });
expect(analysis?.restoreRect).toEqual(analysis?.rect);
expect(resized.map.restoreRect.x + resized.map.restoreRect.width).toBeLessThanOrEqual(788);
expect(resized.map.restoreRect.y + resized.map.restoreRect.height).toBeLessThanOrEqual(588);
});
});