ai-chatbot/lib/client/canvas-toolbar-capability.ts

67 lines
3.1 KiB
TypeScript

import type { CanvasCapabilityClientCommand } from "@/lib/canvas/capabilities/client-command";
import { mergeCanvasSnapshots } from "@/lib/video-canvas/merge-snapshot";
import { useCanvasStore } from "@/store/canvas";
import { formatRequestFailure, getRequestFailureDetails } from "@/lib/request-error";
async function requestCapabilityResult(command: CanvasCapabilityClientCommand) {
const endpoint = `/api/canvas/${command.projectId}/capabilities`;
let payload: any = null;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(command),
});
payload = await response.json().catch(() => null);
if (payload?.success === false || (!response.ok && ![502, 503, 504, 524].includes(response.status))) {
throw Object.assign(new Error(payload?.error || "图片处理请求未通过"), { confirmedRejection: true });
}
if (!response.ok) payload = null;
} catch (error) {
if ((error as { confirmedRejection?: boolean })?.confirmedRejection) throw error;
}
const deadline = Date.now() + 20 * 60 * 1000;
while (Date.now() < deadline) {
const task = payload?.task;
if (task?.status === "succeeded") return payload;
if (task?.status === "failed") throw new Error(task.error || "图片处理失败,请联系管理员核对任务结果");
await new Promise(resolve => setTimeout(resolve, 1500));
try {
const response = await fetch(`${endpoint}?requestId=${encodeURIComponent(command.requestId)}`, { cache: "no-store", signal: AbortSignal.timeout(12_000) });
payload = response.ok ? await response.json() : null;
} catch {
payload = null;
}
}
throw new Error(formatRequestFailure(getRequestFailureDetails({ providerOutcomeUnknown: true }), command.requestId));
}
/** 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() });
await requestCapabilityResult(command);
const projectResponse = await fetch(`/api/canvas/${projectId}`);
const result = await projectResponse.json();
if (!projectResponse.ok || !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));
}