收窄改造缺失来源拒绝范围

可补选的必填与可选引用统一按最佳努力恢复
仅对三个无来源替换入口的 action 拒绝改造
细化快速编辑与来源绑定错误提示
补充权威设计说明和回归测试
This commit is contained in:
2026-08-07 14:34:24 +08:00
parent 130dc8ef9a
commit 0b44253c9d
3 changed files with 99 additions and 9 deletions
File diff suppressed because one or more lines are too long
@@ -4942,6 +4942,47 @@ describe('useImageCanvasGenerationWorkflow', () => {
);
});
it('best-effort restores a required reference that the target panel can replace', () => {
render(
<GenerationWorkflowHarness
initialLayers={[
createLayer({
sourceType: 'generated',
assetKind: 'character',
generationInputs: {
version: 2,
action: 'character.generate',
fields: [
{ id: 'prompt', title: '角色设定', value: '红发骑士' },
{ id: 'model', title: '模型', value: DEFAULT_IMAGE_MODEL },
{ id: 'style', title: '风格', value: 'none' },
{ id: 'aspectRatio', title: '比例', value: '1:1' },
{ id: 'imageSize', title: '尺寸', value: '1K' },
],
references: [
{
id: 'specReference',
title: '角色规范',
refType: 'project-resource',
refId: 'resource-missing-character-spec',
},
],
},
}),
]}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '打开图片改造' }));
expect(screen.getByTestId('dialog').textContent).toContain(
'character:idle:open',
);
expect(screen.getByTestId('reference-pick-warning').textContent).toBe(
'部分原参考素材不在当前画布或来自面板上传,未恢复,请重新选择。',
);
});
it('warns and submits current defaults when V2 parameters are invalid', async () => {
generateEditorImageMock.mockResolvedValueOnce(
createGenerated({ prompt: '森林场景' }),
@@ -5055,7 +5096,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
);
});
it('does not legacy-fallback when a V2 required source is unavailable', () => {
it('refuses redraw only when quick edit cannot replace its missing source', () => {
render(
<GenerationWorkflowHarness
initialLayers={[
@@ -5085,7 +5126,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
expect(screen.getByTestId('dialog').textContent).toBe('-');
expect(screen.getByTestId('reference-pick-warning').textContent).toBe(
'原必需参考素材不在当前画布或来自面板上传,无法改造。',
'原编辑目标不在当前画布,当前快速编辑面板无法重新选择编辑目标,无法改造。',
);
});
@@ -34,8 +34,10 @@ import {
PERFECT_PIXEL_RECONCILIATION_WINDOW_MS,
} from './ImageCanvasEditorModel';
import type {
CanvasGenerationAction,
CanvasGenerationDialogState,
CanvasHistoryAction,
CanvasGenerationInputReference,
CanvasLayer,
CanvasTool,
CanvasViewport,
@@ -167,6 +169,44 @@ function getCanvasGenerationRedrawWarning({
return null;
}
const NON_REPLACEABLE_SOURCE_WARNING_BY_ACTION: Partial<
Record<CanvasGenerationAction, string>
> = {
'image.edit':
'原编辑目标不在当前画布,当前快速编辑面板无法重新选择编辑目标,无法改造。',
'character-animation.generate':
'原角色图层不在当前画布,当前角色动作面板无法重新选择来源角色,无法改造。',
'ui-design.extract-assets':
'原 UI 设计图不在当前画布,当前素材提取面板无法重新选择来源设计图,无法改造。',
};
function getMissingNonReplaceableSourceWarning({
action,
references,
unavailableReferences,
}: {
action: CanvasGenerationAction;
references: CanvasGenerationInputReference[];
unavailableReferences: CanvasGenerationInputReference[];
}) {
const warning = NON_REPLACEABLE_SOURCE_WARNING_BY_ACTION[action];
if (!warning) {
return null;
}
const unavailableSourceKeys = new Set(
unavailableReferences
.filter((reference) => reference.id === 'source')
.map((reference) => `${reference.refType}:${reference.refId}`),
);
const hasAvailableSource = references
.filter((reference) => reference.id === 'source')
.some(
(reference) =>
!unavailableSourceKeys.has(`${reference.refType}:${reference.refId}`),
);
return hasAvailableSource ? null : warning;
}
type CropExpandResizeDragState = {
pointerId: number;
handle: CropExpandResizeHandle;
@@ -1819,6 +1859,17 @@ export function useImageCanvasGenerationWorkflow({
layers,
)
: [];
const missingNonReplaceableSourceWarning = normalizedGenerationInputs
? getMissingNonReplaceableSourceWarning({
action: normalizedGenerationInputs.action,
references: normalizedGenerationInputs.references,
unavailableReferences,
})
: null;
if (missingNonReplaceableSourceWarning) {
showGenerationWarning(missingNonReplaceableSourceWarning);
return;
}
if (
isNormalizedGenerationInputs &&
normalizedGenerationInputs.action === 'ui-design.extract-assets'
@@ -1835,7 +1886,9 @@ export function useImageCanvasGenerationWorkflow({
: null;
if (!extractionSource) {
showGenerationWarning(
'原 UI 设计图不在当前画布或来自面板上传,无法改造。',
NON_REPLACEABLE_SOURCE_WARNING_BY_ACTION[
'ui-design.extract-assets'
]!,
);
return;
}
@@ -1908,11 +1961,7 @@ export function useImageCanvasGenerationWorkflow({
return;
}
if (isNormalizedGenerationInputs) {
showGenerationWarning(
unavailableReferences.length
? '原必需参考素材不在当前画布或来自面板上传,无法改造。'
: '该生成配方当前无法恢复,无法改造。',
);
showGenerationWarning('该生成配方当前无法恢复,无法改造。');
return;
}
if (sourceLayer.mediaType === 'audio') {