修复图标生成审查中的轻量问题

图标规范输出校验失败时携带无效响应和修正指令重试。
图标图集队列在定价和持久化前归一化描述数量。
手工编辑规范字段后清除失效撤销快照。
收口项目生成对话框的重复复位逻辑并补充测试和文档。
This commit is contained in:
2026-08-06 16:19:48 +08:00
parent 3fdbe81bfe
commit 0b3475ef98
8 changed files with 136 additions and 65 deletions
@@ -520,7 +520,30 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
).toBe('手动修改玩法');
});
it('leaves text and the existing undo snapshot unchanged when optimization fails', async () => {
it('clears the field undo snapshot after a manual edit', async () => {
iconSpecClientMocks.refineGamePlay.mockResolvedValueOnce('成功优化');
render(<IconSpecHarness />);
fireEvent.click(screen.getByRole('button', { name: '一键优化玩法设定' }));
await waitFor(() =>
expect(
(screen.getByLabelText('玩法设定') as HTMLTextAreaElement).value,
).toBe('成功优化'),
);
expect(
screen.getByRole('button', { name: '撤销玩法设定优化' }),
).toBeTruthy();
fireEvent.change(screen.getByLabelText('玩法设定'), {
target: { value: '手动修改玩法' },
});
expect(
screen.queryByRole('button', { name: '撤销玩法设定优化' }),
).toBeNull();
});
it('leaves text and the existing valid undo snapshot unchanged when optimization fails', async () => {
iconSpecClientMocks.refineGamePlay
.mockResolvedValueOnce('成功优化')
.mockRejectedValueOnce(new Error('服务暂不可用'));
@@ -532,9 +555,6 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
(screen.getByLabelText('玩法设定') as HTMLTextAreaElement).value,
).toBe('成功优化'),
);
fireEvent.change(screen.getByLabelText('玩法设定'), {
target: { value: '失败前文本' },
});
fireEvent.click(screen.getByRole('button', { name: '一键优化玩法设定' }));
await waitFor(() =>
@@ -542,7 +562,7 @@ describe('ImageCanvasSpecGenerationPanelView', () => {
);
expect(
(screen.getByLabelText('玩法设定') as HTMLTextAreaElement).value,
).toBe('失败前文本');
).toBe('成功优化');
fireEvent.click(screen.getByRole('button', { name: '撤销玩法设定优化' }));
expect(
(screen.getByLabelText('玩法设定') as HTMLTextAreaElement).value,
@@ -489,12 +489,18 @@ export function ImageCanvasSpecGenerationPanelView({
placeholder={item.placeholder}
disabled={isGenerating || item.optimization.optimizing}
className="image-canvas-editor__generation-prompt image-canvas-editor__spec-textarea"
onChange={(event) =>
onChange={(event) => {
onUpdateSpecFormValue(
item.key,
limitIconSpecPrompt(event.target.value),
)
}
);
(item.field === 'playSetting'
? setPlaySettingOptimization
: setArtStyleOptimization)(
INITIAL_ICON_SPEC_OPTIMIZATION_STATE,
);
setOptimizationError(null);
}}
/>
<div className="flex items-center gap-1">
<PlatformActionButton
@@ -1772,6 +1772,26 @@ export function useImageCanvasGenerationSubmissionWorkflow({
onWalletBalanceMayHaveChanged,
]);
const resetProjectBackedGenerationDialog = useCallback(
(canvasDialog: CanvasGenerationDialogState | null) => {
if (!canvasDialog || !projectId) {
return false;
}
updateCanvasGenerationDialogById(canvasDialog.id, (currentDialog) =>
currentDialog.generatedLayerId
? currentDialog
: {
...currentDialog,
status: 'idle',
composerOpen: true,
errorMessage: undefined,
},
);
return true;
},
[projectId, updateCanvasGenerationDialogById],
);
const submitImageGeneration = useCallback(
async (dialog: GenerateDialogState) => {
const normalizedPrompt =
@@ -1985,20 +2005,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
}
return;
}
// TODO refactor here
if (canvasDialog && projectId) {
updateCanvasGenerationDialogById(
canvasDialog.id,
(currentDialog) =>
currentDialog.generatedLayerId
? currentDialog
: {
...currentDialog,
status: 'idle',
composerOpen: true,
errorMessage: undefined,
},
);
if (resetProjectBackedGenerationDialog(canvasDialog)) {
return;
}
addGeneratedResultLayer(generated, {
@@ -2056,19 +2063,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
}
return;
}
if (canvasDialog && projectId) {
updateCanvasGenerationDialogById(
canvasDialog.id,
(currentDialog) =>
currentDialog.generatedLayerId
? currentDialog
: {
...currentDialog,
status: 'idle',
composerOpen: true,
errorMessage: undefined,
},
);
if (resetProjectBackedGenerationDialog(canvasDialog)) {
return;
}
addVideoResultLayer(
@@ -2135,19 +2130,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
}
return;
}
if (canvasDialog && projectId) {
updateCanvasGenerationDialogById(
canvasDialog.id,
(currentDialog) =>
currentDialog.generatedLayerId
? currentDialog
: {
...currentDialog,
status: 'idle',
composerOpen: true,
errorMessage: undefined,
},
);
if (resetProjectBackedGenerationDialog(canvasDialog)) {
return;
}
addAudioResultLayer(
@@ -2214,19 +2197,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
}
return;
}
if (canvasDialog && projectId) {
updateCanvasGenerationDialogById(
canvasDialog.id,
(currentDialog) =>
currentDialog.generatedLayerId
? currentDialog
: {
...currentDialog,
status: 'idle',
composerOpen: true,
errorMessage: undefined,
},
);
if (resetProjectBackedGenerationDialog(canvasDialog)) {
return;
}
addGeneratedResultLayer(generated, {
@@ -2267,6 +2238,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
layers,
quickEditSelectionState,
rememberImageModel,
resetProjectBackedGenerationDialog,
projectId,
assetFolderId,
applyProjectSnapshot,