统一快速编辑/改造参数继承逻辑
- 快速编辑与改造打开时优先复用对应生成器最近一次 imageModel/aspectRatio/imageSize 参数。 - 统一创建草稿函数 createQuickEditPanelDraft / createRedrawPanelDraft / createEditDialogDraft 的参数注入与默认回退。 - 编辑生成弹窗价格与参数展示改为使用当前 dialog 的图片模型/尺寸参数。 - 调整工作流测试为当前统一提交链路并新增参数继承回归用例。
This commit is contained in:
@@ -15,6 +15,8 @@ function createDialog(
|
||||
prompt: '旧修改提示',
|
||||
status: 'idle',
|
||||
sourceLayerId: 'layer-a',
|
||||
imageModel: 'gpt-image-2',
|
||||
imageSize: '1K',
|
||||
...patch,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { type Dispatch, type SetStateAction } from 'react';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
import {
|
||||
calculateEditorImageGenerationPrice,
|
||||
IMAGE_MODEL_GPT_IMAGE_2,
|
||||
} from './ImageCanvasGenerationModel';
|
||||
import { ImageCanvasBasicGenerationComposerView } from './ImageCanvasBasicGenerationComposerView';
|
||||
import type { GenerateDialogState } from './ImageCanvasEditorTypes';
|
||||
@@ -20,10 +19,12 @@ export function ImageCanvasEditGenerationModalView({
|
||||
onSubmit,
|
||||
}: ImageCanvasEditGenerationModalViewProps) {
|
||||
const isOpen = dialog?.mode === 'edit' && dialog.status !== 'generating';
|
||||
const dialogImageModel = dialog?.imageModel ?? 'gpt-image-2';
|
||||
const dialogImageSize = dialog?.imageSize ?? '1K';
|
||||
const editPrice = calculateEditorImageGenerationPrice({
|
||||
kind: 'quick-edit',
|
||||
model: IMAGE_MODEL_GPT_IMAGE_2,
|
||||
imageSize: '1K',
|
||||
model: dialogImageModel,
|
||||
imageSize: dialogImageSize,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -42,9 +43,9 @@ export function ImageCanvasEditGenerationModalView({
|
||||
dialog={{
|
||||
...dialog,
|
||||
mode: 'edit',
|
||||
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
|
||||
imageModel: dialogImageModel,
|
||||
aspectRatio: dialog.aspectRatio ?? '1:1',
|
||||
imageSize: dialog.imageSize ?? '1K',
|
||||
imageSize: dialogImageSize,
|
||||
generationReferences: [],
|
||||
}}
|
||||
style={{}}
|
||||
|
||||
@@ -381,6 +381,9 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
sourceLayerId: 'layer-source',
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '4:3',
|
||||
imageSize: '1K',
|
||||
});
|
||||
expect(createQuickEditPanelDraft(sourceLayer)).toEqual({
|
||||
mode: 'quick-edit',
|
||||
|
||||
@@ -633,26 +633,53 @@ export function createUiDesignGenerationDialogDraft({
|
||||
|
||||
export function createEditDialogDraft(
|
||||
sourceLayer: CanvasLayer,
|
||||
options: {
|
||||
imageModel?: string;
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
} = {},
|
||||
): GenerateDialogState {
|
||||
const imageModel = normalizeEditorImageModel(
|
||||
options.imageModel ?? sourceLayer.model,
|
||||
);
|
||||
const aspectRatio =
|
||||
options.aspectRatio ??
|
||||
inferEditorImageAspectRatio(sourceLayer.originalWidth, sourceLayer.originalHeight);
|
||||
const imageSize =
|
||||
options.imageSize ??
|
||||
inferEditorImageSizeLabel(sourceLayer.originalWidth, sourceLayer.originalHeight);
|
||||
return {
|
||||
mode: 'edit',
|
||||
prompt: resolveUserGenerationPromptSnapshot(sourceLayer),
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
sourceLayerId: sourceLayer.id,
|
||||
imageModel,
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
};
|
||||
}
|
||||
|
||||
export function createQuickEditPanelDraft(
|
||||
sourceLayer: CanvasLayer,
|
||||
options: {
|
||||
imageModel?: string;
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
} = {},
|
||||
): QuickEditPanelState {
|
||||
const aspectRatio = inferEditorImageAspectRatio(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
);
|
||||
const imageSize = inferEditorImageSizeLabel(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
const aspectRatio = options.aspectRatio ??
|
||||
inferEditorImageAspectRatio(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
);
|
||||
const imageSize = options.imageSize ??
|
||||
inferEditorImageSizeLabel(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
);
|
||||
const model = normalizeEditorImageModel(
|
||||
options.imageModel ?? sourceLayer.model,
|
||||
);
|
||||
return {
|
||||
mode: 'quick-edit',
|
||||
@@ -664,7 +691,7 @@ export function createQuickEditPanelDraft(
|
||||
),
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
model: normalizeEditorImageModel(sourceLayer.model),
|
||||
model,
|
||||
quickEditReferences: [],
|
||||
status: 'idle',
|
||||
};
|
||||
@@ -714,9 +741,14 @@ export function createQuickEditGenerationDialogDraft({
|
||||
|
||||
export function createRedrawPanelDraft(
|
||||
sourceLayer: CanvasLayer,
|
||||
options: {
|
||||
imageModel?: string;
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
} = {},
|
||||
): QuickEditPanelState {
|
||||
return {
|
||||
...createQuickEditPanelDraft(sourceLayer),
|
||||
...createQuickEditPanelDraft(sourceLayer, options),
|
||||
mode: 'redraw',
|
||||
prompt: resolveUserGenerationPromptSnapshot(sourceLayer),
|
||||
};
|
||||
|
||||
@@ -248,6 +248,23 @@ function GenerationWorkflowHarness({
|
||||
<button type="button" onClick={workflow.openGenerateDialog}>
|
||||
打开生成
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
dialogs.openCanvasGenerationDialog({
|
||||
mode: 'generate',
|
||||
prompt: '',
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
sourceLayerId: 'layer-source',
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
})
|
||||
}
|
||||
>
|
||||
写入源图生成参数快照
|
||||
</button>
|
||||
<button type="button" onClick={workflow.openCharacterGenerationDialog}>
|
||||
打开角色生成
|
||||
</button>
|
||||
@@ -405,6 +422,21 @@ function GenerationWorkflowHarness({
|
||||
>
|
||||
填写生成提示词
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
dialogs.setGenerateDialog((currentDialog) =>
|
||||
currentDialog
|
||||
? {
|
||||
...currentDialog,
|
||||
prompt: '快速修图',
|
||||
}
|
||||
: currentDialog,
|
||||
)
|
||||
}
|
||||
>
|
||||
填写快速编辑提示词
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
@@ -797,12 +829,12 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开生成' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
|
||||
expect(screen.getByTestId('dialog').textContent).toBe('-');
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:-',
|
||||
expect(screen.getByTestId('dialog').textContent).toBe(
|
||||
'quick-edit:idle:open:-:placeholder',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
|
||||
expect(screen.getByTestId('generation-dialogs').textContent).toBe(
|
||||
'generation-dialog-1:generate:closed',
|
||||
'generation-dialog-1:generate:closed|generation-dialog-2:quick-edit:open',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -822,6 +854,58 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps source image model and dimensions when opening quick edit', () => {
|
||||
render(
|
||||
<GenerationWorkflowHarness
|
||||
initialLayers={[
|
||||
{
|
||||
...createLayer(),
|
||||
originalWidth: 1537,
|
||||
originalHeight: 1024,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '写入源图生成参数快照' }),
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
|
||||
expect(screen.getByTestId('dialog').textContent).toBe(
|
||||
'quick-edit:idle:open:-:placeholder',
|
||||
);
|
||||
expect(screen.getByTestId('dialog-image-options').textContent).toBe(
|
||||
'quick-edit:gpt-image-2:2:3:2K',
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps target generator options when opening redraw', () => {
|
||||
render(
|
||||
<GenerationWorkflowHarness
|
||||
initialLayers={[
|
||||
{
|
||||
...createLayer(),
|
||||
originalWidth: 1537,
|
||||
originalHeight: 1024,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '写入源图生成参数快照' }),
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开图片改造' }));
|
||||
|
||||
expect(screen.getByTestId('dialog').textContent).toBe(
|
||||
'quick-edit:idle:open:-:placeholder',
|
||||
);
|
||||
expect(screen.getByTestId('dialog-image-options').textContent).toBe(
|
||||
'quick-edit:gpt-image-2:2:3:2K',
|
||||
);
|
||||
});
|
||||
|
||||
it('submits audio remodel with restored sound parameters and appends the new audio layer', async () => {
|
||||
generateEditorSoundEffectMock.mockResolvedValueOnce({
|
||||
audioSrc: 'data:audio/mpeg;base64,generated',
|
||||
@@ -1207,9 +1291,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:-',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
|
||||
expect(screen.getByTestId('quick-edit-selection').textContent).toBe(
|
||||
'layer-source:none:0',
|
||||
);
|
||||
@@ -1251,7 +1333,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
'layer-source:rect:1',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:对1号红色圈选框里的内容做以下修改:',
|
||||
'-',
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
@@ -1263,7 +1345,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
'layer-source:ellipse:2',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:对1号红色圈选框里的内容做以下修改:\n对2号红色圈选框里的内容做以下修改:',
|
||||
'-',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1274,18 +1356,8 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
render(<GenerationWorkflowHarness />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:-',
|
||||
);
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: '填写快速编辑' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:快速修图',
|
||||
);
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交快速编辑' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '填写快速编辑提示词' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交生成' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(generateEditorImageMock).toHaveBeenCalledWith({
|
||||
@@ -1385,7 +1457,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'layer-source:idle:-',
|
||||
'-',
|
||||
);
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开修改状态' }));
|
||||
|
||||
@@ -895,6 +895,24 @@ export function useImageCanvasGenerationWorkflow({
|
||||
],
|
||||
);
|
||||
|
||||
const createQuickEditModeDraft = useCallback(
|
||||
(sourceLayer: CanvasLayer) => {
|
||||
const sourceDialog = [...canvasGenerationDialogs]
|
||||
.reverse()
|
||||
.find(
|
||||
(dialog) =>
|
||||
dialog.generatedLayerId === sourceLayer.id ||
|
||||
(!dialog.generatedLayerId && dialog.sourceLayerId === sourceLayer.id),
|
||||
);
|
||||
return createQuickEditPanelDraft(sourceLayer, {
|
||||
imageModel: sourceDialog?.imageModel,
|
||||
aspectRatio: sourceDialog?.aspectRatio,
|
||||
imageSize: sourceDialog?.imageSize,
|
||||
});
|
||||
},
|
||||
[canvasGenerationDialogs],
|
||||
);
|
||||
|
||||
const openQuickEditPanel = useCallback(
|
||||
(sourceLayer: CanvasLayer) => {
|
||||
setImageContextMenu(null);
|
||||
@@ -902,7 +920,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setUiAssetExtractionState(null);
|
||||
setCropExpandPanel(null);
|
||||
setCharacterAnimationPanel(null);
|
||||
const quickEditDraft = createQuickEditPanelDraft(sourceLayer);
|
||||
const quickEditDraft = createQuickEditModeDraft(sourceLayer);
|
||||
const quickEditSize = resolveEditorImageGenerationPixelSize({
|
||||
model: quickEditDraft.model,
|
||||
aspectRatio: quickEditDraft.aspectRatio,
|
||||
@@ -936,6 +954,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
},
|
||||
[
|
||||
canvasSize,
|
||||
createQuickEditModeDraft,
|
||||
openPlacedCanvasGenerationDialog,
|
||||
selectSingleLayer,
|
||||
setActiveTool,
|
||||
@@ -1006,7 +1025,19 @@ export function useImageCanvasGenerationWorkflow({
|
||||
sourceLayer.mediaType === undefined ||
|
||||
sourceLayer.mediaType === 'image'
|
||||
) {
|
||||
const redrawPanel = createRedrawPanelDraft(sourceLayer);
|
||||
const sourceDialog = [...canvasGenerationDialogs]
|
||||
.reverse()
|
||||
.find(
|
||||
(dialog) =>
|
||||
dialog.generatedLayerId === sourceLayer.id ||
|
||||
(!dialog.generatedLayerId &&
|
||||
dialog.sourceLayerId === sourceLayer.id),
|
||||
);
|
||||
const redrawPanel = createRedrawPanelDraft(sourceLayer, {
|
||||
imageModel: sourceDialog?.imageModel,
|
||||
aspectRatio: sourceDialog?.aspectRatio,
|
||||
imageSize: sourceDialog?.imageSize,
|
||||
});
|
||||
const redrawSize = resolveEditorImageGenerationPixelSize({
|
||||
model: redrawPanel.model,
|
||||
aspectRatio: redrawPanel.aspectRatio,
|
||||
@@ -1039,6 +1070,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setActiveTool,
|
||||
setImageContextMenu,
|
||||
setMetadataLayer,
|
||||
canvasGenerationDialogs,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user