完善快速编辑参考图与生成占位

- 快速编辑支持最多8张额外参考图,并将原图自动作为最后一张参考图提交

- 快速编辑尺寸和模型沿用原图配置,按禁用态参数胶囊展示

- 快速编辑提交前同步改写原图引用为实际图序号

- 快速编辑生成改为创建独立画布生成占位,并支持键盘删除生成中对象

- 对齐生成类面板参考图图标、规范类固定参数和相关文档测试
This commit is contained in:
2026-06-18 22:39:03 +08:00
parent b04b6cee2b
commit ea29887d9c
65 changed files with 2819 additions and 531 deletions
@@ -17,6 +17,7 @@ import {
CHARACTER_ANIMATION_DURATION_OPTIONS,
CHARACTER_FRAME_DISPLAY_SIZE,
CHARACTER_FRAME_ORIGINAL_SIZE,
appendLimitedQuickEditReferences,
createCanvasLayerReference,
DEFAULT_ICON_DESCRIPTIONS,
DEFAULT_IMAGE_MODEL,
@@ -63,6 +64,14 @@ function resetFailedGenerationDialog(dialog: GenerateDialogState) {
};
}
function resetFailedGenerationPanel(panel: QuickEditPanelState) {
return {
...panel,
status: panel.status === 'failed' ? 'idle' : panel.status,
errorMessage: panel.status === 'failed' ? undefined : panel.errorMessage,
};
}
export function parseIconDescriptionsText(text: string): string[] {
return text
.split(/[\r\n,,、;;/|]+/u)
@@ -86,6 +95,24 @@ function getReferenceMediaType(layer: CanvasLayer) {
: 'image';
}
function isIconSpecReferenceLayer(layer: CanvasLayer) {
if (layer.assetKind === 'icon-spec') {
return true;
}
// 中文注释:历史“UI素材规范”图层以普通 spec 保存,但语义上就是图标规范,允许继续作为图标生成参考。
if (layer.assetKind !== 'spec') {
return false;
}
return (
layer.title.includes('图标规范') ||
layer.title.includes('UI素材规范') ||
layer.generationInputs?.fields.some(
(field) =>
field.value.includes('游戏UI规范') || field.value.includes('图标规范'),
) === true
);
}
function appendLimitedSeedanceCanvasReference(
references: NonNullable<GenerateDialogState['generationReferences']>,
layer: CanvasLayer,
@@ -374,10 +401,45 @@ export function createQuickEditPanelDraft(
sourceLayer.originalHeight,
),
model: sourceLayer.model?.trim() || DEFAULT_IMAGE_MODEL,
quickEditReferences: [],
status: 'idle',
};
}
export function createQuickEditGenerationDialogDraft({
sourceLayer,
prompt,
status = 'idle',
references = [],
}: {
sourceLayer: CanvasLayer;
prompt: string;
status?: CanvasGenerationDialogState['status'];
references?: NonNullable<QuickEditPanelState['quickEditReferences']>;
}): Omit<CanvasGenerationDialogState, 'id'> {
return {
mode: 'quick-edit',
prompt,
status,
composerOpen: false,
sourceLayerId: sourceLayer.id,
generationReferences: references.slice(0, 8),
imageModel: sourceLayer.model?.trim() || DEFAULT_IMAGE_MODEL,
imageSize: formatImageSizeValue(
sourceLayer.originalWidth,
sourceLayer.originalHeight,
),
placeholder: {
x: sourceLayer.x + sourceLayer.width + 32,
y: sourceLayer.y,
width: sourceLayer.width,
height: sourceLayer.height,
originalWidth: sourceLayer.originalWidth,
originalHeight: sourceLayer.originalHeight,
},
};
}
export function createRedrawPanelDraft(
sourceLayer: CanvasLayer,
): QuickEditPanelState {
@@ -517,7 +579,7 @@ export function assignIconSpecReference(
dialog: GenerateDialogState | null,
layer: CanvasLayer,
): GenerateDialogState | null {
if (layer.assetKind !== 'icon-spec') {
if (!isIconSpecReferenceLayer(layer)) {
return dialog;
}
return dialog?.mode === 'icon'
@@ -533,7 +595,7 @@ export function assignUiDesignSpecReference(
dialog: GenerateDialogState | null,
layer: CanvasLayer,
): GenerateDialogState | null {
if (layer.assetKind !== 'icon-spec') {
if (!isIconSpecReferenceLayer(layer)) {
return dialog;
}
return dialog?.mode === 'ui-design'
@@ -577,6 +639,22 @@ export function updateIconDescriptionsTextInDialog(
: dialog;
}
export function appendQuickEditReference(
panel: QuickEditPanelState | null,
layer: CanvasLayer,
): QuickEditPanelState | null {
if (!panel || panel.mode === 'redraw' || getReferenceMediaType(layer) !== 'image') {
return panel;
}
return {
...resetFailedGenerationPanel(panel),
quickEditReferences: appendLimitedQuickEditReferences(
panel.quickEditReferences,
[createCanvasLayerReference(layer)],
),
};
}
export function updateCharacterAnimationDurationPanel(
panel: CharacterAnimationPanelState | null,
frameCountValue: string,
@@ -604,6 +682,7 @@ export function hideGeneratedLayerComposerAfterBlur(
dialog?.mode === 'character' ||
dialog?.mode === 'icon' ||
dialog?.mode === 'ui-design' ||
dialog?.mode === 'quick-edit' ||
dialog?.mode === 'character-animation' ||
dialog?.mode === 'video' ||
dialog?.mode === 'audio-sound-effect' ||
@@ -624,6 +703,7 @@ export function closeGenerateComposerDialog(
dialog?.mode === 'character' ||
dialog?.mode === 'icon' ||
dialog?.mode === 'ui-design' ||
dialog?.mode === 'quick-edit' ||
dialog?.mode === 'character-animation' ||
dialog?.mode === 'video' ||
dialog?.mode === 'audio-sound-effect' ||