fix(canvas): 画布批量生成 - 修复节点丢失、资源引用及刷新状态不一致

This commit is contained in:
ddCat committed 2026-09-21 21:13:29 +08:00
1 parent dc1ad68015
commit 6f45240599
26 files changed
+533 -179

No files matched your search

+12 -21
View File
@@ -7,6 +7,7 @@ import { canvasNodeRenderBudget, canvasNodeRenderPadding, CANVAS_MAX_RENDERED_CO
import { buildCanvasNodeMentionReferenceMap, buildCanvasResourceReferences, buildToolMentionReference, parseToolMentionTokens } from "@/lib/canvas/canvas-resource-references";
import { buildSkillMentionReferences } from "@/lib/canvas/canvas-skill-mentions";
import { buildCanvasSpatialIndex, canvasNodeBounds, type CanvasSpatialIndex, type CanvasSpatialIndexEntry } from "@/lib/canvas/canvas-spatial-index";
import { selectCanvasVisibleNodes } from "@/lib/canvas/canvas-node-visibility";
import type { Skill } from "@/services/api/skills";
import type { Asset, ImageAsset } from "@/stores/use-asset-store";
import type { DirectorScene } from "@/types/director";
@@ -154,6 +155,7 @@ export function useCanvasRenderModel({
const viewWidth = viewportSize.width / viewport.k;
const viewHeight = viewportSize.height / viewport.k;
return {
view: { left: viewLeft, top: viewTop, right: viewLeft + viewWidth, bottom: viewTop + viewHeight },
enter: { left: viewLeft - enterPadding, top: viewTop - enterPadding, right: viewLeft + viewWidth + enterPadding, bottom: viewTop + viewHeight + enterPadding },
retain: { left: viewLeft - retainPadding, top: viewTop - retainPadding, right: viewLeft + viewWidth + retainPadding, bottom: viewTop + viewHeight + retainPadding },
};
@@ -177,30 +179,19 @@ export function useCanvasRenderModel({
const visibleNodes = useMemo(() => {
const frames: CanvasNodeData[] = [];
const regular: CanvasNodeData[] = [];
const renderedNodeIds = renderedNodeIdsRef.current;
const renderBudget = canvasNodeRenderBudget(viewport.k);
const forcedNodeIds = new Set([...selectedNodeIds, ...(dragPreview?.nodeIds || [])].slice(0, renderBudget));
const candidates = nodeSpatialIndex
.query(renderBounds.retain, renderBudget + forcedNodeIds.size)
.map((nodeId) => nodeById.get(nodeId))
.filter((node): node is CanvasNodeData => Boolean(node));
const candidateIds = new Set(candidates.map((node) => node.id));
for (const nodeId of forcedNodeIds) {
if (candidateIds.has(nodeId)) continue;
const node = nodeById.get(nodeId);
if (node) candidates.push(node);
}
const prioritized = candidates.filter((node) => forcedNodeIds.has(node.id));
const remaining = candidates.filter((node) => !forcedNodeIds.has(node.id)).slice(0, Math.max(0, renderBudget - prioritized.length));
[...prioritized, ...remaining].forEach((node) => {
if (renderHiddenNodeIds.has(node.id)) return;
const retained = forcedNodeIds.has(node.id) || renderedNodeIds.has(node.id);
const insideEnterBounds = node.position.x + node.width > renderBounds.enter.left && node.position.x < renderBounds.enter.right && node.position.y + node.height > renderBounds.enter.top && node.position.y < renderBounds.enter.bottom;
if (!retained && !insideEnterBounds) return;
selectCanvasVisibleNodes({
index: nodeSpatialIndex,
nodeById,
...renderBounds,
hiddenIds: renderHiddenNodeIds,
retainedIds: renderedNodeIdsRef.current,
forcedIds: new Set([...selectedNodeIds, ...(dragPreview?.nodeIds || [])]),
budget: canvasNodeRenderBudget(viewport.k),
}).forEach((node) => {
(isFrameNode(node) ? frames : regular).push(node);
});
return [...frames, ...regular];
}, [dragPreview, nodeById, nodeSpatialIndex, renderBounds, renderHiddenNodeIds, selectedNodeIds]);
}, [dragPreview, nodeById, nodeSpatialIndex, renderBounds, renderHiddenNodeIds, selectedNodeIds, viewport.k]);
useEffect(() => {
renderedNodeIdsRef.current = new Set(visibleNodes.map((node) => node.id));
}, [visibleNodes]);