选择性整合 #547,保留主分支节点与工具条设计。新增默认折叠的节点、资产、任务和历史面板,风格与预设保留在节点内,移除重复入口。 补齐工具权限、提示词解析、资源引用保护及数据库迁移,修复提示词清空和连续编辑后的生成配置同步。 验证:前端全量 1925 项测试、构建、类型检查及 lint 通过;后端 go test ./... 通过。真实模型、PostgreSQL 实库与登录态页面验收仍待进行。 Refs #547 Co-authored-by: bensharp <bensharp@qq.com>
73 lines
4.5 KiB
TypeScript
73 lines
4.5 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { applyToolMention, parseToolMentionTokens, removeToolMentions } from "../src/lib/canvas/canvas-resource-references";
|
|
import { createNineGridNode } from "../src/lib/canvas/canvas-image-source";
|
|
import { CanvasNodeType } from "../src/types/canvas";
|
|
import type { CanvasNodeData } from "../src/types/canvas";
|
|
import "../src/lib/canvas/node-registry/definitions/index";
|
|
import { writeCanvasNodePrompt } from "../src/lib/canvas/canvas-node-prompt";
|
|
import { canonicalGenerationMetadata } from "../src/lib/canvas/generation-contract";
|
|
|
|
for (const type of ["style", "nine_grid"]) {
|
|
test(`${type} selection, replacement, removal and clearing persist across prompt panel reads`, () => {
|
|
for (const content of [undefined, "/generated-image.png"]) {
|
|
let node: CanvasNodeData = {
|
|
id: "image",
|
|
type: CanvasNodeType.Image,
|
|
title: "图片",
|
|
position: { x: 0, y: 0 },
|
|
width: 320,
|
|
height: 180,
|
|
metadata: { content, prompt: "原始生成记录" },
|
|
};
|
|
const editAndRead = (prompt: string) => {
|
|
node = writeCanvasNodePrompt(node, prompt);
|
|
node = JSON.parse(JSON.stringify(node)) as CanvasNodeData;
|
|
const metadata = canonicalGenerationMetadata(node, "image");
|
|
expect(metadata.composerContent).toBe(prompt);
|
|
expect(metadata.generationSpec?.prompt).toBe(prompt);
|
|
if (content) expect(node.metadata?.prompt).toBe("原始生成记录");
|
|
return metadata.composerContent!;
|
|
};
|
|
let prompt = editAndRead(applyToolMention("场景描述", { id: 1, type, label: "初始选择" }));
|
|
prompt = editAndRead(applyToolMention(prompt, { id: 2, type, label: "替换选择" }));
|
|
expect(parseToolMentionTokens(prompt).map((token) => token.toolId)).toEqual([2]);
|
|
prompt = editAndRead(`${prompt}\n连续编辑`);
|
|
prompt = editAndRead(removeToolMentions(prompt, type));
|
|
expect(prompt).toBe("场景描述 \n连续编辑");
|
|
expect(parseToolMentionTokens(prompt)).toEqual([]);
|
|
editAndRead(applyToolMention(prompt, { id: 3, type, label: "再次选择" }));
|
|
expect(parseToolMentionTokens(editAndRead(""))).toEqual([]);
|
|
editAndRead("清空后的新内容");
|
|
}
|
|
});
|
|
}
|
|
|
|
test("tool labels round trip; style replaces, motion deduplicates; clear preserves prose", () => {
|
|
const style = { id: 1, type: "style", label: "风格: [a] 中文" };
|
|
const first = applyToolMention("第一行\n第二行", style);
|
|
expect(parseToolMentionTokens(first)[0].label).toBe(style.label);
|
|
const changed = applyToolMention(first, { ...style, id: 2 });
|
|
expect(parseToolMentionTokens(changed).map((t) => t.toolId)).toEqual([2]);
|
|
expect(removeToolMentions(changed, "style")).toBe("第一行\n第二行");
|
|
const motion = { id: 46, type: "motion", label: "推镜" };
|
|
const combined = applyToolMention(changed, motion);
|
|
expect(applyToolMention(combined, motion)).toBe(combined);
|
|
expect(parseToolMentionTokens(combined)).toHaveLength(2);
|
|
});
|
|
|
|
test("nine grid creates idle child using stable source token, not an auto generation", () => {
|
|
const child = createNineGridNode({ id: "source", type: CanvasNodeType.Image, title: "图", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { content: "/image.png" } }, "child", 79, "九宫格", "nine_grid", "Grid3x3");
|
|
expect(child.metadata?.prompt).toContain("@[node:source]");
|
|
expect(parseToolMentionTokens(child.metadata?.prompt ?? "")[0].toolId).toBe(79);
|
|
expect(child.metadata?.content).toBeFalsy();
|
|
});
|
|
|
|
test("node style overrides project style and removing it restores inheritance", async () => {
|
|
const { resolveCanvasStyleExecution } = await import("../src/lib/canvas/canvas-style-execution");
|
|
const { defaultConfig } = await import("../src/stores/use-config-store");
|
|
const styleNode = { id: "style", type: CanvasNodeType.Config, title: "画风", position: { x: 0, y: 0 }, width: 300, height: 200, metadata: { workflowKind: "styleboard" as const, stylePresetId: "test", prompt: "project style" } };
|
|
const prompt = applyToolMention("scene", { id: 1, type: "style", label: "custom" });
|
|
expect(resolveCanvasStyleExecution([styleNode], undefined, prompt, defaultConfig, "image")).toBeNull();
|
|
expect(resolveCanvasStyleExecution([styleNode], undefined, removeToolMentions(prompt, "style"), defaultConfig, "image")).not.toBeNull();
|
|
});
|