feat(*): 模型协议 - 升级声明式协议、扩充官方插件并完善生成任务恢复
This commit is contained in:
1 parent
3f4a9ea3d0
commit
4724f0767b
132 files changed
+4576
-1420
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { canvasGenerationRequestFingerprint, runCanvasGenerationSubmissionOnce } from "@/lib/canvas/canvas-generation-submission";
|
||||
import { canvasGenerationPromptMetadata, canvasGenerationRequestFingerprint, runCanvasGenerationSubmissionOnce } from "@/lib/canvas/canvas-generation-submission";
|
||||
|
||||
function fingerprint(overrides: Partial<Parameters<typeof canvasGenerationRequestFingerprint>[0]> = {}) {
|
||||
return canvasGenerationRequestFingerprint({
|
||||
@@ -22,6 +22,13 @@ function fingerprint(overrides: Partial<Parameters<typeof canvasGenerationReques
|
||||
}
|
||||
|
||||
describe("canvas generation submission", () => {
|
||||
test("持久化时分离编辑器槽位提示词和模型提示词", () => {
|
||||
expect(canvasGenerationPromptMetadata("自我介绍 @图片1", "电影感:自我介绍 @图片1")).toEqual({
|
||||
composerContent: "自我介绍 @图片1",
|
||||
prompt: "电影感:自我介绍 @图片1",
|
||||
});
|
||||
});
|
||||
|
||||
test("同一节点的并发提交只执行一次", async () => {
|
||||
const locks = new Map<string, Promise<unknown>>();
|
||||
let executions = 0;
|
||||
|
||||
@@ -14,6 +14,7 @@ const canvasNodeContentSource = readFileSync(resolve(import.meta.dir, "../src/co
|
||||
const canvasAudioPlayerSource = readFileSync(resolve(import.meta.dir, "../src/components/canvas/canvas-audio-player.tsx"), "utf8");
|
||||
const canvasMentionSource = readFileSync(resolve(import.meta.dir, "../src/components/canvas/canvas-resource-mention-textarea.tsx"), "utf8");
|
||||
const canvasNodeSource = readFileSync(resolve(import.meta.dir, "../src/components/canvas/canvas-node.tsx"), "utf8");
|
||||
const canvasVideoPreviewSource = readFileSync(resolve(import.meta.dir, "../src/services/canvas-video-preview.ts"), "utf8");
|
||||
const videoPlayerSource = readFileSync(resolve(import.meta.dir, "../src/components/video-player.tsx"), "utf8");
|
||||
const canvasProjectSource = readFileSync(resolve(import.meta.dir, "../src/pages/canvas/project.tsx"), "utf8");
|
||||
const globalStylesSource = readFileSync(resolve(import.meta.dir, "../src/styles/globals.css"), "utf8");
|
||||
@@ -67,6 +68,20 @@ describe("large canvas media rendering", () => {
|
||||
test("keeps node shells visible while panning and zooming", () => {
|
||||
expect(globalStylesSource).not.toMatch(/\[data-canvas-viewport-interacting="true"\]\s+\.canvas-node-shell\s*\{[^}]*content-visibility:\s*hidden/);
|
||||
});
|
||||
|
||||
test("lets inactive video nodes hydrate their own first-frame preview", () => {
|
||||
expect(canvasNodeContentSource).toContain("if (previewUrl || !node.metadata?.content || !updateMetadataRef.current)");
|
||||
expect(canvasNodeContentSource).not.toContain("hydrateMediaPreview");
|
||||
expect(canvasNodeContentSource).toContain("mediaActive || !hasPassivePreview");
|
||||
expect(canvasNodeContentSource).toContain('muted\n playsInline\n preload="auto"');
|
||||
expect(canvasNodeContentSource).toContain("onLoadedMetadata={(event) => primePassiveVideoFrame(event.currentTarget)}");
|
||||
expect(canvasNodeContentSource).toContain("video.currentTime = Math.min(0.001, video.duration)");
|
||||
});
|
||||
|
||||
test("allows failed or empty first-frame requests to retry", () => {
|
||||
expect(canvasVideoPreviewSource).toContain("if (!preview) previewRequests.delete(requestKey)");
|
||||
expect(canvasVideoPreviewSource).toContain("previewRequests.delete(requestKey);");
|
||||
});
|
||||
});
|
||||
|
||||
describe("audio canvas interaction", () => {
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("canvas node generation position mentions", () => {
|
||||
|
||||
expect(context.referenceImages.map((image) => image.id)).toEqual([source.id]);
|
||||
expect(context.imageCount).toBe(1);
|
||||
expect(context.prompt).toBe("将 图片1 图片变清晰");
|
||||
expect(context.prompt).toBe("将 @图片1 图片变清晰");
|
||||
});
|
||||
|
||||
test("已有图片节点未显式引用自身时不自动退化为图生图", () => {
|
||||
@@ -67,8 +67,8 @@ describe("canvas node generation position mentions", () => {
|
||||
|
||||
expect(before.referenceImages.map((image) => image.id)).toEqual(["image-a"]);
|
||||
expect(after.referenceImages.map((image) => image.id)).toEqual(["image-b"]);
|
||||
expect(before.prompt).toBe("让 图片1 进入画面");
|
||||
expect(after.prompt).toBe("让 图片1 进入画面");
|
||||
expect(before.prompt).toBe("让 @图片1 进入画面");
|
||||
expect(after.prompt).toBe("让 @图片1 进入画面");
|
||||
});
|
||||
|
||||
test("按类型位置选择资源,提示词出现顺序不会改变槽位含义", () => {
|
||||
@@ -81,7 +81,7 @@ describe("canvas node generation position mentions", () => {
|
||||
|
||||
expect(context.referenceImages.map((image) => image.id)).toEqual(["image-b"]);
|
||||
expect(context.referenceAudios.map((audio) => audio.id)).toEqual(["audio-a"]);
|
||||
expect(context.prompt).toBe("让 图片1 配合 音频1");
|
||||
expect(context.prompt).toBe("让 @图片1 配合 @音频1");
|
||||
});
|
||||
|
||||
test("旧节点 token 只做读取迁移,不再进入生成提示词", () => {
|
||||
@@ -90,7 +90,7 @@ describe("canvas node generation position mentions", () => {
|
||||
const context = buildNodeGenerationContext(target.id, [image, target], [connection(image.id)], "让 @[node:image-a] 进入画面", []);
|
||||
|
||||
expect(context.referenceImages.map((item) => item.id)).toEqual(["image-a"]);
|
||||
expect(context.prompt).toBe("让 图片1 进入画面");
|
||||
expect(context.prompt).toBe("让 @图片1 进入画面");
|
||||
expect(context.prompt).not.toContain("@[node:");
|
||||
});
|
||||
});
|
||||
@@ -55,6 +55,19 @@ describe("canvas resource mention editor", () => {
|
||||
expect(project).not.toContain("removeCanvasResourceMention");
|
||||
});
|
||||
|
||||
test("keeps editable mention prompts separate from normalized generation prompts", () => {
|
||||
const imageExecutor = source("../src/pages/canvas/canvas-image-generation-executor.ts");
|
||||
const mediaExecutors = source("../src/pages/canvas/canvas-media-generation-executors.ts");
|
||||
const textExecutor = source("../src/pages/canvas/canvas-text-generation-executor.ts");
|
||||
const generationExecutor = source("../src/pages/canvas/use-canvas-generation-executor.ts");
|
||||
|
||||
expect(imageExecutor.match(/canvasGenerationPromptMetadata\(prompt, effectivePrompt\)/g)?.length).toBeGreaterThanOrEqual(3);
|
||||
expect(mediaExecutors.match(/canvasGenerationPromptMetadata\(prompt, effectivePrompt\)/g)?.length).toBe(2);
|
||||
expect(textExecutor.match(/canvasGenerationPromptMetadata\(prompt, effectivePrompt\)/g)?.length).toBe(2);
|
||||
expect(generationExecutor).toContain("composerContent: prompt");
|
||||
expect(generationExecutor).toContain("canvasGenerationPromptMetadata(prompt, statusPrompt)");
|
||||
});
|
||||
|
||||
test("anchors the mention menu to the caret instead of the textarea edge", () => {
|
||||
const component = source("../src/components/canvas/canvas-resource-mention-textarea.tsx");
|
||||
|
||||
|
||||
Reference in new issue
Block a user