接入画板生成视频功能

新增画板底部生成视频入口、Lovart 风格面板、视频图层渲染与元数据展示。

接入 /api/editor/videos/generations 契约与后端 Ark/VectorEngine 视频任务链路。

统一编辑器生成类泥点配置,并补充 UI 设计图、参考图与生成面板结构测试。

更新编辑器技术方案、生成类面板方案和 Hermes 共享决策/踩坑记录。
This commit is contained in:
2026-06-17 20:47:27 +08:00
parent d1cd300695
commit b2fd5574db
39 changed files with 3390 additions and 238 deletions
@@ -12,6 +12,7 @@ import {
generateEditorCharacterAnimation,
generateEditorIconSpritesheet,
generateEditorImage,
generateEditorVideo,
listEditorProjects,
loadEditorAssetLibrary,
loadEditorProject,
@@ -760,6 +761,58 @@ describe('editorProjectClient', () => {
);
});
it('generates editor videos through the backend BFF', async () => {
requestJsonMock.mockResolvedValueOnce({
videoSrc: '/generated-editor-videos/video-task-1/preview.mp4',
width: 1280,
height: 720,
sourceType: 'generated',
prompt: '让角色向镜头挥手',
actualPrompt: '让角色向镜头挥手',
model: 'kling3.0-omni',
provider: 'VectorEngine',
taskId: 'video-task-1',
durationSeconds: 5,
resolution: '480p',
priceMudPoints: 50,
});
const result = await generateEditorVideo({
prompt: '让角色向镜头挥手',
model: 'kling3.0-omni',
aspectRatio: '16:9',
durationSeconds: 5,
resolution: '480p',
mode: 'std',
sound: 'off',
priceMudPoints: 50,
});
expect(result.taskId).toBe('video-task-1');
expect(requestJsonMock).toHaveBeenCalledWith(
'/api/editor/videos/generations',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt: '让角色向镜头挥手',
model: 'kling3.0-omni',
aspectRatio: '16:9',
durationSeconds: 5,
resolution: '480p',
mode: 'std',
sound: 'off',
priceMudPoints: 50,
}),
}),
'生成视频失败',
expect.objectContaining({
timeoutMs: 1_200_000,
retry: { maxRetries: 0 },
}),
);
});
it('edits editor images through the backend BFF', async () => {
requestJsonMock.mockResolvedValueOnce({
imageSrc: 'data:image/png;base64,edited',
@@ -8,6 +8,7 @@ const EDITOR_ICON_SPRITESHEET_GENERATION_API =
'/api/editor/icon-spritesheets/generations';
const EDITOR_CHARACTER_ANIMATION_GENERATION_API =
'/api/editor/character-animations/generations';
const EDITOR_VIDEO_GENERATION_API = '/api/editor/videos/generations';
const EDITOR_IMAGE_MODEL_NANOBANANA2 = 'gemini-3.1-flash-image-preview';
const DEFAULT_PROJECT_TITLE = '未命名画布';
@@ -83,7 +84,7 @@ export type EditorAssetLibrarySnapshot = {
export type EditorImageGenerationInput = {
prompt: string;
size?: string;
kind?: 'spec' | 'character' | 'quick-edit';
kind?: 'spec' | 'character' | 'quick-edit' | 'ui-design';
model?: string;
aspectRatio?: string;
imageSize?: string;
@@ -185,6 +186,38 @@ export type EditorCharacterAnimationGenerationResult = {
priceMudPoints: number;
};
export type EditorVideoModel =
| 'seedance2.0'
| 'seedance2.0-fast'
| 'kling3.0'
| 'kling3.0-omni'
| 'veo3.1'
| 'veo3.1-fast';
export type EditorVideoGenerationInput = {
prompt: string;
model: EditorVideoModel;
aspectRatio: '16:9';
durationSeconds: 4 | 5;
resolution: '480p' | '720p';
mode: 'std';
sound: 'off';
priceMudPoints: number;
};
export type EditorVideoGenerationResult = {
videoSrc: string;
width: number;
height: number;
sourceType: 'generated';
prompt: string;
actualPrompt?: string | null;
model: string;
provider: string;
taskId: string;
priceMudPoints?: number;
};
export type EditorProjectSnapshot = {
projectId: string;
title: string;
@@ -281,6 +314,7 @@ type EditorAssetResponse = {
type EditorImageGenerationResponse = EditorImageGenerationResult;
type EditorIconSpritesheetGenerationResponse =
EditorIconSpritesheetGenerationResult;
type EditorVideoGenerationResponse = EditorVideoGenerationResult;
function jsonRequest(method: 'POST' | 'PATCH', body: Record<string, unknown>) {
return {
@@ -534,3 +568,17 @@ export async function generateEditorCharacterAnimation(
},
);
}
export async function generateEditorVideo(input: EditorVideoGenerationInput) {
return requestJson<EditorVideoGenerationResponse>(
EDITOR_VIDEO_GENERATION_API,
jsonRequest('POST', input),
'生成视频失败',
{
timeoutMs: 1_200_000,
retry: {
maxRetries: 0,
},
},
);
}