feat(canvas): 画布工作台 - 新增生成副本并修复大型画布性能、素材同步与删除校验

统一图片与视频副本的生成落点并保留参考连接。\n优化大型画布节点虚拟化、媒体懒加载、拖拽与详情进入路径。\n修复素材主版本 ID 同步及删除时的画布独立引用校验。
This commit is contained in:
ddCat
2026-09-01 14:57:05 +08:00
parent 434f63e153
commit 3f4a9ea3d0
45 changed files with 1348 additions and 286 deletions

View File

@@ -260,6 +260,7 @@ function pendingGenerationAttempt(scope: string, projectId: string, effectKeys?:
}
function ordinaryCanvasProjectSnapshot(scope: string, project: CanvasProject, durableProject: CanvasProject | undefined) {
if (!hasGenerationEffectKeys(project) && !hasGenerationEffectKeys(durableProject)) return project;
const durableNodes = new Map((durableProject?.nodes || []).map((node) => [node.id, node]));
const durableSessions = new Map((durableProject?.chatSessions || []).map((session) => [session.id, session]));
let changed = false;
@@ -288,7 +289,7 @@ function ordinaryCanvasProjectSnapshot(scope: string, project: CanvasProject, du
}
continue;
}
if (JSON.stringify(localKeys) === JSON.stringify(durableKeys)) {
if (sameGenerationEffectKeys(localKeys, durableKeys)) {
nodes.push(node);
continue;
}
@@ -318,7 +319,7 @@ function ordinaryCanvasProjectSnapshot(scope: string, project: CanvasProject, du
}
continue;
}
if (JSON.stringify(session.generationEffectKeys) === JSON.stringify(durableKeys)) {
if (sameGenerationEffectKeys(session.generationEffectKeys, durableKeys)) {
chatSessions.push(session);
continue;
}
@@ -341,6 +342,19 @@ function ordinaryCanvasProjectSnapshot(scope: string, project: CanvasProject, du
return changed ? { ...project, nodes, chatSessions } : project;
}
function hasGenerationEffectKeys(value: CanvasProject | undefined) {
if (!value) return false;
return value.nodes.some((node) => Boolean(node.metadata?.generationEffectKeys?.length))
|| value.chatSessions.some((session) => Boolean(session.generationEffectKeys?.length));
}
function sameGenerationEffectKeys(left?: readonly string[], right?: readonly string[]) {
if (left === right) return true;
if (!left?.length && !right?.length) return true;
if (!left || !right || left.length !== right.length) return false;
return left.every((value, index) => value === right[index]);
}
function ordinaryCanvasPersistenceState(scope: string, state: PersistedCanvasState, durableProjects: CanvasProject[]) {
const durableById = new Map(durableProjects.map((project) => [project.id, project]));
return {