diff --git a/src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx b/src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx
index 1b9739b81..abf08dfb1 100644
--- a/src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx
+++ b/src/components/image-editor/ImageCanvasBasicGenerationComposerView.test.tsx
@@ -156,6 +156,49 @@ describe('ImageCanvasBasicGenerationComposerView', () => {
expect(screen.queryByText('参考图A')).toBeNull();
});
+ it('removes only the selected reference when multiple references are present', () => {
+ const submitGeneration = vi.fn();
+ render(
+ ,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name: '删除参考图B' }));
+ expect(screen.queryByText('参考图B')).toBeNull();
+ expect(screen.getByText('参考图A')).toBeTruthy();
+ expect(screen.getByText('参考图C')).toBeTruthy();
+
+ fireEvent.click(screen.getByRole('button', { name: '生成' }));
+ expect(submitGeneration).toHaveBeenCalledWith(
+ expect.objectContaining({
+ generationReferences: [
+ expect.objectContaining({ id: 'ref-a' }),
+ expect.objectContaining({ id: 'ref-c' }),
+ ],
+ }),
+ );
+ });
+
it('shows only current image options and opens upward option panels', () => {
render();
diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx
index 3ff601c19..047f5d0fe 100644
--- a/src/components/image-editor/ImageCanvasEditorView.tsx
+++ b/src/components/image-editor/ImageCanvasEditorView.tsx
@@ -1666,8 +1666,14 @@ export function ImageCanvasEditorView({
return generateDialog?.mode === 'spec'
? {
label: '规范参考图',
- currentCount: 0,
- limit: 1,
+ currentCount:
+ generateDialog.specType === 'icon'
+ ? 0
+ : imageReferenceCount(generateDialog.generationReferences),
+ limit:
+ generateDialog.specType === 'icon'
+ ? 1
+ : resolveDialogExtraImageReferenceLimit(generateDialog),
contextId: generateDialogContextId,
}
: null;
diff --git a/src/components/image-editor/ImageCanvasGenerationDialogModel.ts b/src/components/image-editor/ImageCanvasGenerationDialogModel.ts
index 9c2bcf69e..85910c912 100644
--- a/src/components/image-editor/ImageCanvasGenerationDialogModel.ts
+++ b/src/components/image-editor/ImageCanvasGenerationDialogModel.ts
@@ -1017,12 +1017,22 @@ function createNormalizedGenerationDialogDraft({
characterView: getNormalizedString(sourceLayer, 'characterView'),
customPrompt: getNormalizedString(sourceLayer, 'customPrompt'),
},
- specReference:
- resolveNormalizedReferences(
- sourceLayer,
- 'reference',
- availableLayers,
- )[0] ?? null,
+ ...(specType === 'icon'
+ ? {
+ specReference:
+ resolveNormalizedReferences(
+ sourceLayer,
+ 'reference',
+ availableLayers,
+ )[0] ?? null,
+ }
+ : {
+ generationReferences: resolveNormalizedReferences(
+ sourceLayer,
+ 'reference',
+ availableLayers,
+ ),
+ }),
},
sourceLayer,
);
@@ -2104,7 +2114,15 @@ export function appendGenerationReference(
}
return {
...resetFailedGenerationDialog(dialog),
- specReference: createCanvasLayerReference(layer),
+ ...(dialog.specType === 'icon' || dialog.specType === undefined
+ ? { specReference: createCanvasLayerReference(layer) }
+ : {
+ generationReferences: appendLimitedImageReferences(
+ dialog.generationReferences,
+ [createCanvasLayerReference(layer)],
+ resolveDialogExtraImageReferenceLimit(dialog),
+ ),
+ }),
composerOpen: true,
};
}
diff --git a/src/components/image-editor/ImageCanvasGenerationModel.ts b/src/components/image-editor/ImageCanvasGenerationModel.ts
index 409572301..724ff72b1 100644
--- a/src/components/image-editor/ImageCanvasGenerationModel.ts
+++ b/src/components/image-editor/ImageCanvasGenerationModel.ts
@@ -1875,12 +1875,18 @@ export function buildSpecGenerationInputs(
specType: SpecGenerationType,
values: SpecFormValues,
reference?: CharacterReferenceImage | null,
+ extraReferences: CharacterReferenceImage[] = [],
): CanvasGenerationInputs {
- const references = reference
- ? createGenerationInputReference('参考图', reference, {
- id: 'reference',
- })
- : [];
+ const references = [
+ ...(reference ? [reference] : []),
+ ...extraReferences,
+ ].flatMap((item, index) =>
+ createGenerationInputReference(
+ index === 0 ? '参考图' : `参考图${index + 1}`,
+ item,
+ { id: index === 0 ? 'reference' : `reference-${index + 1}` },
+ ),
+ );
if (specType === 'custom') {
return {
version: 2,
diff --git a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts
index 14edd6af1..7068dab9a 100644
--- a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts
+++ b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts
@@ -756,6 +756,11 @@ export function buildImageGenerationSubmissionPlan({
},
};
}
+ const specReferences = dialog.generationReferences?.length
+ ? dialog.generationReferences
+ : dialog.specReference
+ ? [dialog.specReference]
+ : [];
return {
kind: 'image',
normalizedPrompt,
@@ -763,18 +768,18 @@ export function buildImageGenerationSubmissionPlan({
prompt: buildSpecPrompt(
specType,
specValues,
- Boolean(dialog.specReference?.src),
+ specReferences.some((reference) => Boolean(reference.src)),
),
size: SPEC_GENERATION_SIZE,
model: SPEC_GENERATION_MODEL,
aspectRatio: SPEC_GENERATION_ASPECT_RATIO,
imageSize: SPEC_GENERATION_IMAGE_SIZE,
kind: 'spec',
- ...(dialog.specReference?.src
+ ...(specReferences.length
? {
- referenceImageSrcs: [
- resolveImageReferenceSubmissionSource(dialog.specReference),
- ],
+ referenceImageSrcs: specReferences.map(
+ resolveImageReferenceSubmissionSource,
+ ),
}
: {}),
},
@@ -787,7 +792,8 @@ export function buildImageGenerationSubmissionPlan({
generationInputs: buildSpecGenerationInputs(
specType,
specValues,
- dialog.specReference,
+ specReferences[0],
+ specReferences.slice(1),
),
},
};
diff --git a/src/components/image-editor/ImageCanvasSpecGenerationPanelView.tsx b/src/components/image-editor/ImageCanvasSpecGenerationPanelView.tsx
index 19e960295..a06c11152 100644
--- a/src/components/image-editor/ImageCanvasSpecGenerationPanelView.tsx
+++ b/src/components/image-editor/ImageCanvasSpecGenerationPanelView.tsx
@@ -144,7 +144,13 @@ export function ImageCanvasSpecGenerationPanelView({
: 'spec-reference';
const reference = isUiDesignDialog
? dialog.uiDesignSpecReference
- : dialog.specReference;
+ : dialog.specType === 'icon'
+ ? dialog.specReference
+ : null;
+ const extraSpecReferences =
+ !isUiDesignDialog && dialog.specType !== 'icon'
+ ? (dialog.generationReferences ?? [])
+ : [];
const isGenerating = dialog.status === 'generating';
const isIconSpec = dialog.mode === 'spec' && dialog.specType === 'icon';
const [playSettingOptimization, setPlaySettingOptimization] =
@@ -278,7 +284,7 @@ export function ImageCanvasSpecGenerationPanelView({
onPointerDown={(event) => event.stopPropagation()}
>
- {isUiDesignDialog || dialog.specType ? (
+ {isUiDesignDialog || dialog.specType === 'icon' ? (
) : null}
+ {extraSpecReferences.map((item, index) => (
+ }
+ imageSrc={item.src}
+ objectKey={item.objectKey}
+ label={`参考图${index + 1}`}
+ ariaLabel={item.label || `参考图${index + 1}`}
+ title={item.label}
+ disabled={isGenerating}
+ onRemove={
+ setGenerateDialog
+ ? () =>
+ setGenerateDialog((currentDialog) =>
+ currentDialog?.mode === 'spec'
+ ? {
+ ...currentDialog,
+ status:
+ currentDialog.status === 'failed'
+ ? 'idle'
+ : currentDialog.status,
+ errorMessage:
+ currentDialog.status === 'failed'
+ ? undefined
+ : currentDialog.errorMessage,
+ generationReferences: (
+ currentDialog.generationReferences ?? []
+ ).filter(
+ (referenceItem) =>
+ referenceItem.id !== item.id,
+ ),
+ }
+ : currentDialog,
+ )
+ : undefined
+ }
+ removeLabel={`删除${item.label || `参考图${index + 1}`}`}
+ />
+ ))}
+ {!isUiDesignDialog && dialog.specType !== 'icon' ? (
+ }
+ label="参考图"
+ ariaLabel={referenceLabel}
+ disabled={isGenerating}
+ isAdd
+ onClick={openReferenceMenu}
+ />
+ ) : null}
{isUiDesignDialog
? (dialog.generationReferences ?? []).map((item, index) => (
({
+ file,
+ index,
+ imageSrc: await readImageFileAsDataUrl(file),
+ })),
+ );
if (!validateImageReferenceUploadReservation(reservation)) {
return;
}
const references = await persistReferenceUploads(
- [
- {
- idPrefix: 'upload-spec-reference',
- file: imageFile,
- fallbackLabel: '参考图',
- imageSrc,
- },
- ],
+ fileReferences.map(({ file, imageSrc, index }) => ({
+ idPrefix: 'upload-spec-reference',
+ index,
+ file,
+ fallbackLabel: `参考图${index + 1}`,
+ imageSrc,
+ })),
reservation,
);
if (
diff --git a/src/index.css b/src/index.css
index 560074278..34850e1bc 100644
--- a/src/index.css
+++ b/src/index.css
@@ -7033,6 +7033,14 @@ button.image-canvas-editor__reference-chip:disabled {
transform: scale(1);
}
+@media (hover: none) {
+ .image-canvas-editor__reference-chip-remove {
+ opacity: 1;
+ pointer-events: auto;
+ transform: scale(1);
+ }
+}
+
.image-canvas-editor__reference-chip-remove:disabled {
cursor: not-allowed;
opacity: 0;