perf: 优化智能体与工作流幕布渲染性能

- 分阶段加载智能体配置并按需缓存 MCP 工具

- 合并画布状态更新与节点尺寸监听,启用大图可视区域渲染和静态连线

- 隔离 Tinyflow Store 实例并补充数据同步与回归测试
This commit is contained in:
2026-07-27 18:27:01 +08:00
parent dc7e46260b
commit aedefe6b5e
39 changed files with 1349 additions and 408 deletions

View File

@@ -0,0 +1,49 @@
import type { Node } from '@xyflow/svelte';
import { describe, expect, it } from 'vitest';
import {
createTinyflowNodeNormalizer,
DEFAULT_NODE_DRAG_HANDLE,
} from './nodeInteraction';
function createNode(patch: Partial<Node> = {}): Node {
return {
data: {},
id: 'node-1',
position: { x: 0, y: 0 },
type: 'customNode',
...patch,
};
}
describe('createTinyflowNodeNormalizer', () => {
it('节点已经规范化时保留引用', () => {
const normalizer = createTinyflowNodeNormalizer();
const node = createNode({ dragHandle: DEFAULT_NODE_DRAG_HANDLE });
expect(normalizer(node)).toBe(node);
});
it('需要补齐交互属性时采用写时复制且不修改输入', () => {
const normalizer = createTinyflowNodeNormalizer();
const node = createNode();
const normalized = normalizer(node);
expect(normalized).not.toBe(node);
expect(normalized.dragHandle).toBe(DEFAULT_NODE_DRAG_HANDLE);
expect(node.dragHandle).toBeUndefined();
});
it('只读节点已经包含限制属性时保留引用', () => {
const normalizer = createTinyflowNodeNormalizer({ readonly: true } as any);
const node = createNode({
connectable: false,
draggable: false,
selectable: false,
});
expect(normalizer(node)).toBe(node);
});
});

View File

@@ -25,33 +25,45 @@ export function createTinyflowNodeNormalizer(
const elementsSelectable = options?.elementsSelectable ?? !readonly;
return (node) => {
const nextNode = { ...node };
let nextNode = node;
const mutableNode = () => {
if (nextNode === node) {
nextNode = { ...node };
}
return nextNode;
};
if (nodesDraggable) {
if (nextNode.draggable === false) {
delete nextNode.draggable;
delete mutableNode().draggable;
}
if (shouldUseDefaultDragHandle(nextNode, options?.customNodes)) {
nextNode.dragHandle = DEFAULT_NODE_DRAG_HANDLE;
mutableNode().dragHandle = DEFAULT_NODE_DRAG_HANDLE;
}
} else {
nextNode.draggable = false;
if (nextNode.draggable !== false) {
mutableNode().draggable = false;
}
}
if (nodesConnectable) {
if (nextNode.connectable === false) {
delete nextNode.connectable;
delete mutableNode().connectable;
}
} else {
nextNode.connectable = false;
if (nextNode.connectable !== false) {
mutableNode().connectable = false;
}
}
if (elementsSelectable) {
if (nextNode.selectable === false) {
delete nextNode.selectable;
delete mutableNode().selectable;
}
} else {
nextNode.selectable = false;
if (nextNode.selectable !== false) {
mutableNode().selectable = false;
}
}
return nextNode;