feat(canvas): 交互可读性 - 优化连线、字号与工作条

This commit is contained in:
ddCat
2026-09-19 22:29:41 +08:00
parent 04a1e19def
commit 33908ed117
12 changed files with 210 additions and 57 deletions

View File

@@ -0,0 +1,25 @@
import { describe, expect, test } from "bun:test";
import { filterCanvasDisplayConnections } from "@/lib/canvas/canvas-connection-visibility";
import { CanvasNodeType, type CanvasDisplayConnection } from "@/types/canvas";
function connection(id: string, fromNodeId: string, toNodeId: string): CanvasDisplayConnection {
const node = (nodeId: string) => ({ id: nodeId, type: CanvasNodeType.Text, title: nodeId, position: { x: 0, y: 0 }, width: 100, height: 80, metadata: {} });
return { connection: { id, fromNodeId, toNodeId }, from: node(fromNodeId), to: node(toNodeId) };
}
describe("canvas connection visibility", () => {
const connections = [connection("a", "one", "two"), connection("b", "two", "three"), connection("c", "four", "five")];
test("keeps all connections when disabled", () => {
expect(filterCanvasDisplayConnections(connections, { enabled: false })).toEqual(connections);
});
test("shows only direct connections for hovered and selected nodes", () => {
expect(filterCanvasDisplayConnections(connections, { enabled: true, hoveredNodeId: "three", selectedNodeIds: new Set(["one"]) }).map((item) => item.connection.id)).toEqual(["a", "b"]);
});
test("keeps an explicitly selected connection visible", () => {
expect(filterCanvasDisplayConnections(connections, { enabled: true, selectedConnectionId: "c" }).map((item) => item.connection.id)).toEqual(["c"]);
});
});

View File

@@ -0,0 +1,23 @@
import { describe, expect, test } from "bun:test";
import { canvasTextFontSize } from "@/lib/canvas/canvas-text-scale";
describe("canvas text font scaling", () => {
test("keeps the configured base size at the default node size", () => {
expect(canvasTextFontSize(340, 240, 14)).toBe(14);
});
test("scales proportionally when the node grows in both directions", () => {
expect(canvasTextFontSize(680, 480, 14)).toBeCloseTo(28);
});
test("responds when only one dimension changes", () => {
expect(canvasTextFontSize(680, 240, 14)).toBeCloseTo(14 * Math.sqrt(2));
});
test("uses metadata font size as the base and clamps extremes", () => {
expect(canvasTextFontSize(340, 240, 20)).toBe(20);
expect(canvasTextFontSize(1, 1, 2)).toBe(8);
expect(canvasTextFontSize(10_000, 10_000, 20)).toBe(72);
});
});

View File

@@ -27,6 +27,24 @@ function createMainContext(canvasTool: "move" | "box-select" = "box-select"): To
}
describe("canvas toolbar mode switch", () => {
test.each(["default", "compact"] as const)("renders %s canvas modes as an icon-only sliding switch", (size) => {
const entries = resolveToolbarEntries("main", createMainContext(), null);
const modeSwitch = entries.find((entry) => entry.kind === "switch");
if (!modeSwitch) throw new Error("Missing canvas mode switch");
const html = renderToStaticMarkup(<FloatingDock size={size} items={[modeSwitch]} />);
const diameter = size === "compact" ? 24 : 26;
expect(html).toContain(`width:${diameter}px;height:${diameter}px`);
expect(html.match(/aceternity-dock-switch-thumb/g)).toHaveLength(1);
expect(html).toContain("absolute top-1/2 rounded-full");
expect(html).toContain("grid size-4 shrink-0 place-items-center");
expect(html).not.toContain("is-labeled");
expect(html).not.toContain("选择</span>");
expect(html).not.toContain("移动</span>");
expect(html).toContain('aria-label="区域选择"');
expect(html).toContain('aria-label="抓手工具"');
});
test("renders grab and box-select as one dock switch", () => {
const entries = resolveToolbarEntries("main", createMainContext("box-select"), null);
const modeSwitch = entries.find((entry) => entry.kind === "switch" && entry.id === CANVAS_MODE_TOOL_ID);