-
-
- {item.label}
-
- {functionKindLabels[item.kind]}
-
-
- {item.route}
-
-
-
{item.description}
+
+
+
功能预设提示词
+
+ 视频画布运行时会通过 /api/video-canvas/config 读取这里保存的提示词。比如“多机位九宫格”的分镜规则、黑色分割线要求和镜头说明,都由管理员在这里维护,前端不再写死覆盖。
+
+
+
+
+ {functions.map((item) => (
+
+
+
+
+
+ {item.label}
+
+ {functionKindLabels[item.kind]}
+
+
+ {item.route}
+
+
+ {item.description}
+
+
+
+ updateFunction(item.key, { enabled: checked })}
+ />
+
-
-
-
updateFunction(item.key, { enabled: checked })}
- />
+
-
-
-
-
-
-
-
- ))}
-
+
+
+
+
+
+ ))}
+
+
);
}
diff --git a/components/canvas/LiblibVideoCanvasReplica.tsx b/components/canvas/LiblibVideoCanvasReplica.tsx
index 7e0c18ea9..0c8616c22 100644
--- a/components/canvas/LiblibVideoCanvasReplica.tsx
+++ b/components/canvas/LiblibVideoCanvasReplica.tsx
@@ -387,13 +387,10 @@ const OFFICIAL_ZOOM_DOCK_SCALE = 0.65;
const OFFICIAL_DROPDOWN_SCALE = 0.7;
const OFFICIAL_NINE_GRID_WIDTH = 930;
const OFFICIAL_NINE_GRID_HEIGHT = 520;
+const MULTI_VIEW_NINE_GRID_FUNCTION_KEY = "multi-view-nine-grid";
const PROMPT_COMPOSER_BASE_WIDTH = 895;
const PROMPT_COMPOSER_BASE_HEIGHT = 286;
const PRESET_COMPOSER_BASE_HEIGHT = 238;
-const LEGACY_MULTI_VIEW_NINE_GRID_PROMPT =
- "以选中图片为角色/主体参考,生成多视角 3x3 九宫格分镜图。保持主体一致,九个镜头包含远景、全身、侧身、背面、半身、近景、局部细节等视角,使用清晰黑色分割线分隔每一格,白色或干净背景。";
-const MULTI_VIEW_NINE_GRID_PROMPT =
- "以选中图片作为角色、服装、发型、场景光线的唯一参考,生成一张 3x3 多机位分镜合成图。必须是 9 个明显不同的镜头画面,不是同一张图裁切或重复人脸。九格分别包含:1 森林环境远景/主体很小,2 全身远景,3 中景正面,4 侧面半身,5 低机位或俯拍,6 面部近景,7 手部/衣袖/动作细节,8 背面或侧背,9 仰视或情绪特写。保持同一角色身份、绿色服装、自然森林光线一致,每格构图、景别、机位和主体大小必须不同。输出为单张 16:9 图片,九格之间使用清晰黑色分割线,不能出现文字、水印、编号,不能白底棚拍。";
const NINE_GRID_CELL_VISUALS: GridCellVisual[] = [
{ fit: "contain", objectPosition: "50% 50%", scale: 0.62 },
{ fit: "contain", objectPosition: "58% 50%", scale: 0.74, translateX: -10 },
@@ -1016,6 +1013,7 @@ function createMediaResourceNode({
function createNineGridCompositeNode({
anchor,
columns,
+ gridPrompt,
option,
rows,
sourceUrl,
@@ -1024,6 +1022,7 @@ function createNineGridCompositeNode({
}: {
anchor: CloneNode;
columns: number;
+ gridPrompt?: string;
option?: string;
rows: number;
sourceUrl: string;
@@ -1035,10 +1034,11 @@ function createNineGridCompositeNode({
cellCount === 9
? "多机位九宫格"
: option?.trim() || `${cellCount}宫格`;
- const gridPrompt =
- cellCount === 9 && (!option || /多|9|九/.test(option))
- ? MULTI_VIEW_NINE_GRID_PROMPT
- : `基于选中图片生成 ${columns}x${rows} 宫格分镜图,使用黑色分割线分隔每一格。`;
+ const resolvedGridPrompt =
+ gridPrompt?.trim() ||
+ (cellCount === 9 && (!option || /多|9|九/.test(option))
+ ? getDefaultFunctionPrompt(MULTI_VIEW_NINE_GRID_FUNCTION_KEY)
+ : `基于选中图片生成 ${columns}x${rows} 宫格分镜图,使用黑色分割线分隔每一格。`);
return {
id: makeId("image"),
@@ -1055,7 +1055,7 @@ function createNineGridCompositeNode({
action: "IMAGE_GENERATE_NINE_GRID",
generated: true,
gridColumns: columns,
- gridPrompt,
+ gridPrompt: resolvedGridPrompt,
gridRows: rows,
mediaType: "image",
nineGridCells: Array.from({ length: cellCount }).map((_, index) => getGridCellVisual(index)),
@@ -1066,7 +1066,7 @@ function createNineGridCompositeNode({
columns,
mode: "grid",
option: option || "",
- prompt: gridPrompt,
+ prompt: resolvedGridPrompt,
rows,
sourceNodeId: anchor.id,
sourceUrl,
@@ -1174,6 +1174,10 @@ function getConfiguredFunction(
return config.functions[key] || defaultVideoCanvasFunctionConfig.functions[key];
}
+function getDefaultFunctionPrompt(key: string) {
+ return defaultVideoCanvasFunctionConfig.functions[key]?.prompt || "";
+}
+
function getFunctionModel(
config: VideoCanvasFunctionConfig,
definition: VideoCanvasFunctionDefinition,
@@ -1203,16 +1207,10 @@ function composeMultiViewNineGridPrompt(
definition: VideoCanvasFunctionDefinition,
option?: string,
) {
- const configuredPrompt = definition.prompt.trim();
+ const configuredPrompt = definition.prompt.trim() || getDefaultFunctionPrompt(MULTI_VIEW_NINE_GRID_FUNCTION_KEY);
const selectedOption = String(option || "").trim();
- const parts = [MULTI_VIEW_NINE_GRID_PROMPT];
- const isLegacyPrompt =
- configuredPrompt === LEGACY_MULTI_VIEW_NINE_GRID_PROMPT ||
- (configuredPrompt.includes("以选中图片为角色/主体参考") && configuredPrompt.includes("白色或干净背景"));
+ const parts = [configuredPrompt];
- if (configuredPrompt && configuredPrompt !== MULTI_VIEW_NINE_GRID_PROMPT && !isLegacyPrompt) {
- parts.push(`后台补充:${configuredPrompt}`);
- }
if (selectedOption) {
parts.push(`用户选择:${selectedOption}`);
}
@@ -1544,7 +1542,7 @@ function normalizeRestoredNode(node: CloneNode, fallbackIndex: number): CloneNod
draftValues: {
...(node.draftValues || {}),
action: "IMAGE_GENERATE_NINE_GRID",
- gridPrompt: getDraftString(node, "gridPrompt") || MULTI_VIEW_NINE_GRID_PROMPT,
+ gridPrompt: getDraftString(node, "gridPrompt") || getDefaultFunctionPrompt(MULTI_VIEW_NINE_GRID_FUNCTION_KEY),
nineGridComposite: true,
outputSizeLabel: getDraftString(node, "outputSizeLabel") || "2752 × 1536",
},
@@ -2578,10 +2576,12 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
const title = labels[action];
if (action === "grid") {
const { columns, rows } = normalizeGridOption(option || "9宫格");
- const gridDefinition = getConfiguredFunction(videoCanvasFunctionConfig, "multi-view-nine-grid");
+ const gridDefinition = getConfiguredFunction(videoCanvasFunctionConfig, MULTI_VIEW_NINE_GRID_FUNCTION_KEY);
+ const prompt = composeMultiViewNineGridPrompt(gridDefinition, option);
const gridNode = createNineGridCompositeNode({
anchor,
columns,
+ gridPrompt: prompt,
option,
rows,
sourceUrl,
@@ -2613,7 +2613,6 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
setActivePanel(null);
if (videoCanvasFunctionConfig.enabled && gridDefinition?.enabled) {
- const prompt = composeMultiViewNineGridPrompt(gridDefinition, option);
const response = await imageGenerationService.generateImage({
aspectRatio: "16:9",
imageCount: 1,
@@ -2852,7 +2851,7 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
const starterMode = getPresetMode(anchor);
const functionKey =
payload.slashScene && /九宫格|宫格|多机位/.test(payload.slashScene)
- ? "multi-view-nine-grid"
+ ? MULTI_VIEW_NINE_GRID_FUNCTION_KEY
: starterMode === "character-three-view"
? "character-three-view"
: "image-repaint";
@@ -2862,7 +2861,7 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
payload.prompt.trim() || payload.slashScene || "生成角色三视图",
);
const finalPrompt =
- functionKey === "multi-view-nine-grid"
+ functionKey === MULTI_VIEW_NINE_GRID_FUNCTION_KEY
? composeMultiViewNineGridPrompt(definition, payload.prompt.trim() || payload.slashScene || "多机位九宫格")
: prompt;
const count = Math.max(1, Math.min(4, Number.parseInt(payload.settings.count, 10) || 1));
@@ -2885,14 +2884,14 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
const generatedNode = generatedUrl
? createMediaResourceNode({
action: "IMAGE_RESOURCE",
- height: functionKey === "multi-view-nine-grid" ? OFFICIAL_NINE_GRID_HEIGHT : 280,
+ height: functionKey === MULTI_VIEW_NINE_GRID_FUNCTION_KEY ? OFFICIAL_NINE_GRID_HEIGHT : 280,
mediaType: "image",
mediaUrl: generatedUrl,
prompt: finalPrompt,
title: payload.slashScene || definition.label || "图片生成",
- width: functionKey === "multi-view-nine-grid" ? OFFICIAL_NINE_GRID_WIDTH : 420,
+ width: functionKey === MULTI_VIEW_NINE_GRID_FUNCTION_KEY ? OFFICIAL_NINE_GRID_WIDTH : 420,
x: rightX,
- y: Math.round(anchor.y + anchor.height / 2 - (functionKey === "multi-view-nine-grid" ? OFFICIAL_NINE_GRID_HEIGHT : 280) / 2),
+ y: Math.round(anchor.y + anchor.height / 2 - (functionKey === MULTI_VIEW_NINE_GRID_FUNCTION_KEY ? OFFICIAL_NINE_GRID_HEIGHT : 280) / 2),
})
: null;
const created = generatedNode || createTargetModuleNode("image", rightX, 0, nodes.length + 1);
@@ -3118,13 +3117,16 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
if (node.inputType === "image" || /IMAGE_GENERATE/.test(action)) {
const functionKey =
action === "IMAGE_GENERATE_NINE_GRID"
- ? "multi-view-nine-grid"
+ ? MULTI_VIEW_NINE_GRID_FUNCTION_KEY
: node.draftValues?.generatorType === "ENHANCE"
? "hd-upscale"
: "image-repaint";
const definition = getConfiguredFunction(videoCanvasFunctionConfig, functionKey);
const sourceUrl = imageUrls[0] || getNodeSourceMediaUrl(node);
- const prompt = composeFunctionPrompt(definition, rawPrompt || String(params.option || node.title));
+ const prompt =
+ functionKey === MULTI_VIEW_NINE_GRID_FUNCTION_KEY
+ ? composeMultiViewNineGridPrompt(definition, String(params.option || node.title))
+ : composeFunctionPrompt(definition, rawPrompt || String(params.option || node.title));
let generatedUrl = "";
if (definition.route === "enhance" && sourceUrl) {