拆分编辑器高级生成提交模型
抽出图标素材生成校验和请求参数组装 抽出角色动画生成请求参数组装 补充高级生成提交模型单测 更新 TRACKING.md 记录第三十八阶段验证
This commit is contained in:
@@ -154,3 +154,4 @@
|
||||
- 2026-06-17 前端拆分第三十五阶段:继续收口 `useImageCanvasGenerationWorkflow`,新增 `ImageCanvasGenerationSubmissionModel`,把普通生图、修改图片、规范生成和角色形象生成的请求 payload、标准化 prompt、结果图层标题 / assetKind 和 generationInputs 快照构建从 workflow hook 中抽成纯模型;workflow hook 保留对话状态、真实 API 调用、图片引用解析、结果落图、选中和 fit 副作用,避免拆散生成生命周期。新增模型单测覆盖普通生图、修改图、带参考图的规范生成、带规范 / 常规参考图的角色生成和缺失源图异常;`useImageCanvasGenerationWorkflow` 从 1167 行降至 1104 行。统一验证命令:`npm run test -- src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasAssetRowView.test.tsx src/components/image-editor/ImageCanvasSidebarView.test.tsx src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasCharacterGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasEditGenerationModalView.test.tsx src/components/image-editor/ImageCanvasEditorShellView.test.tsx src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx src/components/image-editor/ImageCanvasPanelDockView.test.tsx src/components/image-editor/ImageCanvasContextMenusView.test.tsx src/components/image-editor/ImageCanvasSelectedLayerToolbarView.test.tsx src/components/image-editor/ImageCanvasIconSpritesheetComposerView.test.tsx src/components/image-editor/ImageCanvasSpecGenerationPanelView.test.tsx src/components/image-editor/ImageCanvasGenerationImageOptionsView.test.tsx src/components/image-editor/ImageCanvasQuickEditPanelView.test.tsx src/components/image-editor/ImageCanvasCharacterAnimationPanelView.test.tsx src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 临时 `XDG_CONFIG_HOME` 下 Chrome headless 打开成功,未登录显示 `账号入口`;关闭后 `画布背景设置` 可打开,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`;点击 `生成工具` 后 `Image Generator`、`生成图片` 对话框和 `AI画布工具栏` 均可见,控制台仅有预期的未登录 `/api/auth/refresh` 401。
|
||||
- 2026-06-17 前端拆分第三十六阶段:继续收口 `useImageCanvasUploadWorkflow`,新增 `ImageCanvasUploadModel`,把上传目标文件夹解析、上传中素材占位卡、上传到画布的临时图层、无效 drop 坐标兜底和图片真实尺寸回填坐标计算从 hook 中抽成纯模型;upload workflow hook 保留登录恢复、文件读取、真实素材创建 API、上传进度状态和生成面板参考图写入副作用。新增模型单测覆盖文件夹兜底、占位素材、画布落点、非法坐标兜底和真实尺寸修正;`useImageCanvasUploadWorkflow` 从 546 行降至 510 行。统一验证命令:`npm run test -- src/components/image-editor/ImageCanvasUploadModel.test.ts src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasUploadModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasAssetRowView.test.tsx src/components/image-editor/ImageCanvasSidebarView.test.tsx src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasCharacterGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasEditGenerationModalView.test.tsx src/components/image-editor/ImageCanvasEditorShellView.test.tsx src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx src/components/image-editor/ImageCanvasPanelDockView.test.tsx src/components/image-editor/ImageCanvasContextMenusView.test.tsx src/components/image-editor/ImageCanvasSelectedLayerToolbarView.test.tsx src/components/image-editor/ImageCanvasIconSpritesheetComposerView.test.tsx src/components/image-editor/ImageCanvasSpecGenerationPanelView.test.tsx src/components/image-editor/ImageCanvasGenerationImageOptionsView.test.tsx src/components/image-editor/ImageCanvasQuickEditPanelView.test.tsx src/components/image-editor/ImageCanvasCharacterAnimationPanelView.test.tsx src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 临时 `XDG_CONFIG_HOME` 下 Chrome headless 打开成功,未登录显示 `账号入口`;关闭后 `画布背景设置` 可打开,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`;点击 `生成工具` 后 `Image Generator`、`生成图片` 对话框、`上传到项目素材` 入口和 `AI画布工具栏` 均可见,控制台仅有预期的未登录 `/api/auth/refresh` 401。
|
||||
- 2026-06-17 前端拆分第三十七阶段:继续收口 `useImageCanvasAssetLibrary`,新增 `ImageCanvasAssetLibraryModel`,把素材分组、可选择素材筛选、全选状态、素材 / 文件夹重命名、文件夹折叠、本地新建文件夹占位、持久化文件夹替换、本地删除素材、删除文件夹回默认文件夹、选择集合切换、批量删除和本地移动素材到文件夹从 hook 中抽成纯模型;asset library hook 继续保留加载账号素材库、后端 CRUD 调用、登录弹窗、DOM 框选和素材拖拽命中生命周期。新增模型单测覆盖分组 / 选择、重命名 / 折叠 / 本地文件夹、本地文件夹持久化替换、删除文件夹回默认文件夹、全选 / 批量删除和本地移动;`useImageCanvasAssetLibrary` 从 609 行降至 573 行。统一验证命令:`npm run test -- src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts src/components/image-editor/useImageCanvasAssetLibrary.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts src/components/image-editor/ImageCanvasUploadModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasAssetRowView.test.tsx src/components/image-editor/ImageCanvasSidebarView.test.tsx src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasCharacterGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasEditGenerationModalView.test.tsx src/components/image-editor/ImageCanvasEditorShellView.test.tsx src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx src/components/image-editor/ImageCanvasPanelDockView.test.tsx src/components/image-editor/ImageCanvasContextMenusView.test.tsx src/components/image-editor/ImageCanvasSelectedLayerToolbarView.test.tsx src/components/image-editor/ImageCanvasIconSpritesheetComposerView.test.tsx src/components/image-editor/ImageCanvasSpecGenerationPanelView.test.tsx src/components/image-editor/ImageCanvasGenerationImageOptionsView.test.tsx src/components/image-editor/ImageCanvasQuickEditPanelView.test.tsx src/components/image-editor/ImageCanvasCharacterAnimationPanelView.test.tsx src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/useImageCanvasAssetLibrary.test.tsx src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 临时 `XDG_CONFIG_HOME` 下 Chrome headless 打开成功,未登录显示 `账号入口`;关闭后 `画布背景设置` 可打开,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`;`打开图层` 切换后侧栏显示 `图层`,点击 `生成工具` 后 `Image Generator`、`生成图片` 对话框和 `AI画布工具栏` 均可见;切回 `打开素材` 后侧栏显示 `素材` 且 `上传到项目素材` 入口可见,控制台仅有预期的未登录 `/api/auth/refresh` 401。
|
||||
- 2026-06-17 前端拆分第三十八阶段:继续收口 `useImageCanvasGenerationWorkflow`,扩展 `ImageCanvasGenerationSubmissionModel`,把图标素材批量生成的规范校验 / 描述清洗 / 请求 payload / generationInputs,以及角色动画生成的 prompt 清洗 / objectKey 优先源图 / 尺寸 / 价格 / 模型参数从 workflow hook 中抽成纯模型;workflow hook 继续保留对话状态、真实 API 调用、生成结果落图、失败恢复和角色动画面板生命周期。新增模型单测覆盖图标缺少规范、图标空描述、图标描述 trim / 参考快照,以及角色动画 trim、objectKey 源图和价格计算;`useImageCanvasGenerationWorkflow` 从 1104 行降至 1075 行。统一验证命令:`npm run test -- src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts src/components/image-editor/ImageCanvasUploadModel.test.ts src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasAssetRowView.test.tsx src/components/image-editor/ImageCanvasSidebarView.test.tsx src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasCharacterGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasEditGenerationModalView.test.tsx src/components/image-editor/ImageCanvasEditorShellView.test.tsx src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx src/components/image-editor/ImageCanvasPanelDockView.test.tsx src/components/image-editor/ImageCanvasContextMenusView.test.tsx src/components/image-editor/ImageCanvasSelectedLayerToolbarView.test.tsx src/components/image-editor/ImageCanvasIconSpritesheetComposerView.test.tsx src/components/image-editor/ImageCanvasSpecGenerationPanelView.test.tsx src/components/image-editor/ImageCanvasGenerationImageOptionsView.test.tsx src/components/image-editor/ImageCanvasQuickEditPanelView.test.tsx src/components/image-editor/ImageCanvasCharacterAnimationPanelView.test.tsx src/components/image-editor/ImageCanvasWorldView.test.tsx src/components/image-editor/useImageCanvasAssetLibrary.test.tsx src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 临时 `XDG_CONFIG_HOME` 下 Chrome headless 打开成功,未登录显示 `账号入口`;关闭后 `画布背景设置` 可打开,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`;`打开图层` 切换后侧栏显示 `图层`,点击 `生成工具` 后 `Image Generator`、`生成图片` 对话框和 `AI画布工具栏` 均可见;点击 `生成图标素材` 后 `Icon Generator` 占位和 `生成图标素材` 面板可见,控制台仅有预期的未登录 `/api/auth/refresh` 401。
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { CanvasLayer } from './ImageCanvasEditorTypes';
|
||||
import { buildImageGenerationSubmissionPlan } from './ImageCanvasGenerationSubmissionModel';
|
||||
import {
|
||||
buildCharacterAnimationSubmissionPlan,
|
||||
buildIconSpritesheetGenerationSubmissionPlan,
|
||||
buildImageGenerationSubmissionPlan,
|
||||
} from './ImageCanvasGenerationSubmissionModel';
|
||||
|
||||
function createLayer(overrides: Partial<CanvasLayer> = {}): CanvasLayer {
|
||||
return {
|
||||
@@ -209,4 +213,121 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
|
||||
}),
|
||||
).toThrow('未找到要修改的图片');
|
||||
});
|
||||
|
||||
it('returns an icon spritesheet error when the spec reference is missing', () => {
|
||||
const plan = buildIconSpritesheetGenerationSubmissionPlan({
|
||||
mode: 'icon',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
iconDescriptions: ['返回按钮'],
|
||||
});
|
||||
|
||||
expect(plan).toEqual({
|
||||
ok: false,
|
||||
errorMessage: '请选择图标素材规范',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an icon spritesheet error when descriptions are empty', () => {
|
||||
const plan = buildIconSpritesheetGenerationSubmissionPlan({
|
||||
mode: 'icon',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
iconSpecReference: {
|
||||
id: 'icon-spec',
|
||||
label: '图标规范',
|
||||
src: 'data:image/png;base64,spec',
|
||||
},
|
||||
iconDescriptions: [' ', '\n'],
|
||||
});
|
||||
|
||||
expect(plan).toEqual({
|
||||
ok: false,
|
||||
errorMessage: '请填写素材描述',
|
||||
});
|
||||
});
|
||||
|
||||
it('builds icon spritesheet plans with trimmed descriptions and references', () => {
|
||||
const plan = buildIconSpritesheetGenerationSubmissionPlan({
|
||||
mode: 'icon',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '3:2',
|
||||
imageSize: '2K',
|
||||
iconSpecReference: {
|
||||
id: 'icon-spec',
|
||||
label: '图标规范',
|
||||
src: 'data:image/png;base64,spec',
|
||||
},
|
||||
iconDescriptions: [' 返回按钮 ', '', '设置按钮'],
|
||||
});
|
||||
|
||||
expect(plan).toEqual({
|
||||
ok: true,
|
||||
iconDescriptions: ['返回按钮', '设置按钮'],
|
||||
input: {
|
||||
referenceImageSrc: 'data:image/png;base64,spec',
|
||||
iconDescriptions: ['返回按钮', '设置按钮'],
|
||||
model: 'gpt-image-2',
|
||||
aspectRatio: '3:2',
|
||||
imageSize: '2K',
|
||||
},
|
||||
generationInputs: {
|
||||
fields: [
|
||||
{ title: '素材描述 1', value: '返回按钮' },
|
||||
{ title: '素材描述 2', value: '设置按钮' },
|
||||
],
|
||||
references: [
|
||||
{
|
||||
title: '图标素材规范',
|
||||
label: '图标规范',
|
||||
src: 'data:image/png;base64,spec',
|
||||
},
|
||||
],
|
||||
},
|
||||
rememberImageModel: 'gpt-image-2',
|
||||
});
|
||||
});
|
||||
|
||||
it('builds character animation plans with trimmed prompt and object key source', () => {
|
||||
const sourceLayer = createLayer({
|
||||
id: 'character-layer',
|
||||
title: '角色图',
|
||||
objectKey: 'generated/character.png',
|
||||
assetKind: 'character',
|
||||
originalWidth: 960,
|
||||
originalHeight: 1280,
|
||||
});
|
||||
|
||||
const plan = buildCharacterAnimationSubmissionPlan({
|
||||
panel: {
|
||||
sourceLayerId: 'character-layer',
|
||||
promptText: ' 循环奔跑动作 ',
|
||||
resolution: '720p',
|
||||
ratio: 'same',
|
||||
frameCount: 48,
|
||||
durationSeconds: 6,
|
||||
status: 'idle',
|
||||
},
|
||||
sourceLayer,
|
||||
});
|
||||
|
||||
expect(plan).toEqual({
|
||||
promptText: '循环奔跑动作',
|
||||
input: {
|
||||
sourceLayerId: 'character-layer',
|
||||
sourceImageSrc: 'generated/character.png',
|
||||
sourceWidth: 960,
|
||||
sourceHeight: 1280,
|
||||
promptText: '循环奔跑动作',
|
||||
resolution: '720p',
|
||||
ratio: 'same',
|
||||
frameCount: 48,
|
||||
durationSeconds: 6,
|
||||
priceMudPoints: 120,
|
||||
model: 'seedance2.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
import type {
|
||||
EditorCharacterAnimationGenerationInput,
|
||||
EditorIconSpritesheetGenerationInput,
|
||||
EditorImageGenerationInput,
|
||||
} from '../../services/image-editor/editorProjectClient';
|
||||
import type {
|
||||
CanvasGenerationInputs,
|
||||
CanvasLayer,
|
||||
CharacterAnimationPanelState,
|
||||
GenerateDialogState,
|
||||
} from './ImageCanvasEditorTypes';
|
||||
import {
|
||||
CHARACTER_ANIMATION_MODEL,
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
DEFAULT_ICON_DESCRIPTIONS,
|
||||
DEFAULT_SPEC_FORM_VALUES,
|
||||
SPEC_GENERATION_SIZE,
|
||||
SPEC_TYPE_LABEL,
|
||||
buildIconGenerationInputs,
|
||||
buildCharacterGenerationInputs,
|
||||
buildEditGenerationInputs,
|
||||
buildImageGenerationInputs,
|
||||
buildSpecGenerationInputs,
|
||||
buildSpecPrompt,
|
||||
calculateCharacterAnimationPrice,
|
||||
resolveCharacterAnimationSourceImageSrc,
|
||||
} from './ImageCanvasGenerationModel';
|
||||
|
||||
type ImageGenerationSubmissionOptions = {
|
||||
@@ -43,6 +51,24 @@ export type ImageGenerationSubmissionPlan =
|
||||
rememberImageModel?: string;
|
||||
};
|
||||
|
||||
export type IconSpritesheetGenerationSubmissionPlan =
|
||||
| {
|
||||
ok: false;
|
||||
errorMessage: string;
|
||||
}
|
||||
| {
|
||||
ok: true;
|
||||
iconDescriptions: string[];
|
||||
input: EditorIconSpritesheetGenerationInput;
|
||||
generationInputs: CanvasGenerationInputs;
|
||||
rememberImageModel: string;
|
||||
};
|
||||
|
||||
export type CharacterAnimationSubmissionPlan = {
|
||||
promptText: string;
|
||||
input: EditorCharacterAnimationGenerationInput;
|
||||
};
|
||||
|
||||
export function buildImageGenerationSubmissionPlan({
|
||||
dialog,
|
||||
layers,
|
||||
@@ -141,3 +167,72 @@ export function buildImageGenerationSubmissionPlan({
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildIconSpritesheetGenerationSubmissionPlan(
|
||||
dialog: GenerateDialogState,
|
||||
): IconSpritesheetGenerationSubmissionPlan {
|
||||
const iconDescriptions = (dialog.iconDescriptions ?? DEFAULT_ICON_DESCRIPTIONS)
|
||||
.map((description) => description.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!dialog.iconSpecReference) {
|
||||
return {
|
||||
ok: false,
|
||||
errorMessage: '请选择图标素材规范',
|
||||
};
|
||||
}
|
||||
|
||||
if (!iconDescriptions.length) {
|
||||
return {
|
||||
ok: false,
|
||||
errorMessage: '请填写素材描述',
|
||||
};
|
||||
}
|
||||
|
||||
const rememberImageModel = dialog.imageModel ?? DEFAULT_IMAGE_MODEL;
|
||||
return {
|
||||
ok: true,
|
||||
iconDescriptions,
|
||||
input: {
|
||||
referenceImageSrc: dialog.iconSpecReference.src,
|
||||
iconDescriptions,
|
||||
model: rememberImageModel,
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
},
|
||||
generationInputs: buildIconGenerationInputs(
|
||||
iconDescriptions,
|
||||
dialog.iconSpecReference,
|
||||
),
|
||||
rememberImageModel,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildCharacterAnimationSubmissionPlan({
|
||||
panel,
|
||||
sourceLayer,
|
||||
}: {
|
||||
panel: CharacterAnimationPanelState;
|
||||
sourceLayer: CanvasLayer;
|
||||
}): CharacterAnimationSubmissionPlan {
|
||||
const promptText = panel.promptText.trim();
|
||||
return {
|
||||
promptText,
|
||||
input: {
|
||||
sourceLayerId: sourceLayer.id,
|
||||
sourceImageSrc: resolveCharacterAnimationSourceImageSrc(sourceLayer),
|
||||
sourceWidth: sourceLayer.originalWidth,
|
||||
sourceHeight: sourceLayer.originalHeight,
|
||||
promptText,
|
||||
resolution: panel.resolution,
|
||||
ratio: panel.ratio,
|
||||
frameCount: panel.frameCount,
|
||||
durationSeconds: panel.durationSeconds,
|
||||
priceMudPoints: calculateCharacterAnimationPrice(
|
||||
panel.resolution,
|
||||
panel.durationSeconds,
|
||||
),
|
||||
model: CHARACTER_ANIMATION_MODEL,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
} from './ImageCanvasGenerationLayerModel';
|
||||
import {
|
||||
CHARACTER_ANIMATION_DURATION_OPTIONS,
|
||||
CHARACTER_ANIMATION_MODEL,
|
||||
CHARACTER_FRAME_DISPLAY_SIZE,
|
||||
CHARACTER_FRAME_ORIGINAL_SIZE,
|
||||
DEFAULT_ICON_DESCRIPTIONS,
|
||||
@@ -37,16 +36,18 @@ import {
|
||||
SPEC_FRAME_DISPLAY_SIZE,
|
||||
SPEC_FRAME_ORIGINAL_SIZE,
|
||||
buildEditGenerationInputs,
|
||||
buildIconGenerationInputs,
|
||||
buildQuickEditModelOptions,
|
||||
buildQuickEditSizeOptions,
|
||||
calculateCharacterAnimationPrice,
|
||||
createCanvasLayerReference,
|
||||
isCanvasGenerationDialog,
|
||||
resolveCharacterAnimationSourceImageSrc,
|
||||
resolveImageGenerationErrorMessage,
|
||||
} from './ImageCanvasGenerationModel';
|
||||
import { buildImageGenerationSubmissionPlan } from './ImageCanvasGenerationSubmissionModel';
|
||||
import {
|
||||
buildCharacterAnimationSubmissionPlan,
|
||||
buildIconSpritesheetGenerationSubmissionPlan,
|
||||
buildImageGenerationSubmissionPlan,
|
||||
} from './ImageCanvasGenerationSubmissionModel';
|
||||
import { formatImageSizeValue } from './ImageCanvasEditorModel';
|
||||
import type {
|
||||
CanvasGenerationDialogState,
|
||||
@@ -657,29 +658,15 @@ export function useImageCanvasGenerationWorkflow({
|
||||
) => {
|
||||
updateCanvasGenerationDialogById(nextDialog.id, () => nextDialog);
|
||||
};
|
||||
const iconDescriptions = (
|
||||
dialog.iconDescriptions ?? DEFAULT_ICON_DESCRIPTIONS
|
||||
)
|
||||
.map((description) => description.trim())
|
||||
.filter(Boolean);
|
||||
if (!dialog.iconSpecReference) {
|
||||
const submissionPlan =
|
||||
buildIconSpritesheetGenerationSubmissionPlan(dialog);
|
||||
if (!submissionPlan.ok) {
|
||||
if (canvasDialog) {
|
||||
setSubmittingIconDialog({
|
||||
...canvasDialog,
|
||||
status: 'failed',
|
||||
composerOpen: true,
|
||||
errorMessage: '请选择图标素材规范',
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!iconDescriptions.length) {
|
||||
if (canvasDialog) {
|
||||
setSubmittingIconDialog({
|
||||
...canvasDialog,
|
||||
status: 'failed',
|
||||
composerOpen: true,
|
||||
errorMessage: '请填写素材描述',
|
||||
errorMessage: submissionPlan.errorMessage,
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -691,32 +678,28 @@ export function useImageCanvasGenerationWorkflow({
|
||||
|
||||
setSubmittingIconDialog({
|
||||
...canvasDialog,
|
||||
iconDescriptions,
|
||||
iconDescriptions: submissionPlan.iconDescriptions,
|
||||
status: 'generating',
|
||||
composerOpen: false,
|
||||
errorMessage: undefined,
|
||||
});
|
||||
|
||||
try {
|
||||
const generated = await generateEditorIconSpritesheet({
|
||||
referenceImageSrc: dialog.iconSpecReference.src,
|
||||
iconDescriptions,
|
||||
model: dialog.imageModel ?? DEFAULT_IMAGE_MODEL,
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
});
|
||||
setLastImageModel(dialog.imageModel ?? DEFAULT_IMAGE_MODEL);
|
||||
const generated = await generateEditorIconSpritesheet(
|
||||
submissionPlan.input,
|
||||
);
|
||||
setLastImageModel(submissionPlan.rememberImageModel);
|
||||
addIconSpritesheetResultLayers(
|
||||
generated,
|
||||
generated.iconImageSrcs,
|
||||
buildIconGenerationInputs(iconDescriptions, dialog.iconSpecReference),
|
||||
submissionPlan.generationInputs,
|
||||
getGeneratingDialogPlaceholder(dialog),
|
||||
canvasDialog.id,
|
||||
);
|
||||
} catch (error) {
|
||||
setSubmittingIconDialog({
|
||||
...canvasDialog,
|
||||
iconDescriptions,
|
||||
iconDescriptions: submissionPlan.iconDescriptions,
|
||||
status: 'failed',
|
||||
composerOpen: true,
|
||||
errorMessage: resolveImageGenerationErrorMessage(error),
|
||||
@@ -913,10 +896,13 @@ export function useImageCanvasGenerationWorkflow({
|
||||
if (!characterAnimationPanel || !characterAnimationSourceLayer) {
|
||||
return;
|
||||
}
|
||||
const promptText = characterAnimationPanel.promptText.trim();
|
||||
const submissionPlan = buildCharacterAnimationSubmissionPlan({
|
||||
panel: characterAnimationPanel,
|
||||
sourceLayer: characterAnimationSourceLayer,
|
||||
});
|
||||
const nextPanel = {
|
||||
...characterAnimationPanel,
|
||||
promptText,
|
||||
promptText: submissionPlan.promptText,
|
||||
status: 'generating' as const,
|
||||
errorMessage: undefined,
|
||||
result: undefined,
|
||||
@@ -924,24 +910,9 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setCharacterAnimationPanel(nextPanel);
|
||||
|
||||
try {
|
||||
const result = await generateEditorCharacterAnimation({
|
||||
sourceLayerId: characterAnimationSourceLayer.id,
|
||||
sourceImageSrc: resolveCharacterAnimationSourceImageSrc(
|
||||
characterAnimationSourceLayer,
|
||||
),
|
||||
sourceWidth: characterAnimationSourceLayer.originalWidth,
|
||||
sourceHeight: characterAnimationSourceLayer.originalHeight,
|
||||
promptText,
|
||||
resolution: nextPanel.resolution,
|
||||
ratio: nextPanel.ratio,
|
||||
frameCount: nextPanel.frameCount,
|
||||
durationSeconds: nextPanel.durationSeconds,
|
||||
priceMudPoints: calculateCharacterAnimationPrice(
|
||||
nextPanel.resolution,
|
||||
nextPanel.durationSeconds,
|
||||
),
|
||||
model: CHARACTER_ANIMATION_MODEL,
|
||||
});
|
||||
const result = await generateEditorCharacterAnimation(
|
||||
submissionPlan.input,
|
||||
);
|
||||
setCharacterAnimationPanel((currentPanel) =>
|
||||
currentPanel
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user