ai-chatbot/lib/client/canvas-toolbar-capability.ts
WuYongJie 6b7b24a86e 修复图片工具提交前的画布同步与结果合并
新导入或新生成的图片在自动保存前点击去水印,会因服务端缺少节点而失败。工具栏统一先保存当前画布,保存成功后再调用处理能力;处理与自动保存共用队列,结果合并保留等待期间的本地编辑并更新保存版本。适用于去水印、抠图、增强、扩图和图层拆分。

验证:14 项画布能力与提交同步测试通过,覆盖保存失败不提交、执行期间编辑保留、上游失败和切换画布;TypeScript 检查通过。
2026-11-03 14:23:59 +08:00

41 lines
1.8 KiB
TypeScript

import type { CanvasCapabilityClientCommand } from "@/lib/canvas/capabilities/client-command";
import { mergeCanvasSnapshots } from "@/lib/video-canvas/merge-snapshot";
import { canvasService } from "@/services/canvas";
import { useCanvasStore } from "@/store/canvas";
/** Runs after the editor has saved and while its save queue is reserved. */
export async function executeCanvasToolbarCapability(
command: CanvasCapabilityClientCommand,
) {
const projectId = command.projectId!;
const before = useCanvasStore.getState();
if (before.projectId !== projectId) throw new Error("画布已切换,请重新选择图片");
const base = JSON.stringify({ canvasData: before.getCanvasState() });
const response = await fetch(`/api/canvas/${projectId}/capabilities`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(command),
});
const payload = await response.json().catch(() => ({}));
if (!response.ok || payload?.success !== true) {
throw new Error(payload?.error || "图片处理失败");
}
const result = await canvasService.getProject(projectId);
if (!result.success || !result.data?.canvasData) {
throw new Error(result.error || "结果已生成,但画布同步失败");
}
const current = useCanvasStore.getState();
if (current.projectId !== projectId) return;
const merged = mergeCanvasSnapshots(
base,
JSON.stringify({ canvasData: current.getCanvasState() }),
JSON.stringify({ canvasData: result.data.canvasData }),
);
if (merged.conflicts.length > 0) {
throw new Error("结果已生成,但画布有冲突;请保留本地改动后刷新查看结果");
}
current.loadCanvas(JSON.parse(merged.snapshot).canvasData);
current.setCanvasRevision(String(result.data.updatedAt), String(result.data.updatedAt));
}