) : null}
+ {isPickingCharacterReferenceFromCanvas ? (
+
+ 请选择画布中的图片作为常规参考图,按 Esc 退出
+
+ ) : null}
{isPickingIconSpecFromCanvas ? (
请选择画布中的图标素材规范,按 Esc 退出
diff --git a/src/components/image-editor/ImageCanvasGenerationModel.test.ts b/src/components/image-editor/ImageCanvasGenerationModel.test.ts
index 59286b2d..b6c565ad 100644
--- a/src/components/image-editor/ImageCanvasGenerationModel.test.ts
+++ b/src/components/image-editor/ImageCanvasGenerationModel.test.ts
@@ -104,10 +104,46 @@ describe('ImageCanvasGenerationModel', () => {
.toBe('自定义');
expect(buildQuickEditModelOptions('nano-banana')).toEqual([
{ label: 'nano-banana', value: 'nano-banana' },
- { label: 'GPT Image', value: DEFAULT_IMAGE_MODEL },
+ { label: 'nanobanana2', value: DEFAULT_IMAGE_MODEL },
+ { label: 'gpt-image-2', value: 'gpt-image-2' },
]);
});
+ it('adds reference image semantics and snapshots for spec generation references', () => {
+ const prompt = buildSpecPrompt(
+ 'ui',
+ {
+ playSetting: '消除玩法',
+ artStyle: '清爽卡通',
+ bodyRatio: '3',
+ characterView: '',
+ customPrompt: '',
+ },
+ true,
+ );
+
+ expect(prompt).toContain('参考图生成规范');
+ expect(prompt).toContain('参考图1');
+ expect(prompt).toContain('生成一张完整游戏UI规范汇总设定展板');
+ expect(
+ buildSpecPrompt(
+ 'custom',
+ { ...blankSpecValues, customPrompt: '生成一张怪兽规范图' },
+ true,
+ ),
+ ).toContain('生成一张怪兽规范图');
+ expect(
+ buildSpecGenerationInputs(
+ 'custom',
+ { ...blankSpecValues, customPrompt: '生成一张怪兽规范图' },
+ { id: 'spec-ref', label: '参考.png', src: '/ref.png' },
+ ),
+ ).toEqual({
+ fields: [{ title: '自定义规范提示词', value: '生成一张怪兽规范图' }],
+ references: [{ title: '参考图', label: '参考.png', src: '/ref.png' }],
+ });
+ });
+
it('uses objectKey for character animation references before falling back to src', () => {
expect(
resolveCharacterAnimationSourceImageSrc({
diff --git a/src/components/image-editor/ImageCanvasGenerationModel.ts b/src/components/image-editor/ImageCanvasGenerationModel.ts
index 88ee13b4..7a119efc 100644
--- a/src/components/image-editor/ImageCanvasGenerationModel.ts
+++ b/src/components/image-editor/ImageCanvasGenerationModel.ts
@@ -23,7 +23,9 @@ export const CHARACTER_FRAME_ORIGINAL_SIZE = { width: 2048, height: 2048 };
export const CHARACTER_FRAME_DISPLAY_SIZE = { width: 420, height: 420 };
export const ICON_FRAME_ORIGINAL_SIZE = { width: 512, height: 512 };
export const ICON_FRAME_DISPLAY_SIZE = { width: 360, height: 360 };
-export const DEFAULT_IMAGE_MODEL = 'gpt-image-2';
+export const IMAGE_MODEL_GPT_IMAGE_2 = 'gpt-image-2';
+export const IMAGE_MODEL_NANOBANANA2 = 'gemini-3.1-flash-image-preview';
+export const DEFAULT_IMAGE_MODEL = IMAGE_MODEL_NANOBANANA2;
export const ICON_DESCRIPTION_LIMIT = 100;
// 图标素材面板按描述项扩宽,避免在画布子面板里做滑动列表。
export const ICON_DESCRIPTION_CARD_WIDTH_REM = 8.4;
@@ -44,8 +46,20 @@ export const QUICK_EDIT_SIZE_PRESETS = [
'1024x1536',
] as const;
export const QUICK_EDIT_MODEL_OPTIONS = [
- { label: 'GPT Image', value: DEFAULT_IMAGE_MODEL },
+ { label: 'nanobanana2', value: IMAGE_MODEL_NANOBANANA2 },
+ { label: 'gpt-image-2', value: IMAGE_MODEL_GPT_IMAGE_2 },
] as const;
+export const EDITOR_IMAGE_MODEL_OPTIONS = QUICK_EDIT_MODEL_OPTIONS;
+export const EDITOR_IMAGE_DIMENSION_OPTIONS = {
+ [IMAGE_MODEL_NANOBANANA2]: {
+ aspectRatios: ['1:1', '2:3', '3:2', '9:16', '16:9'],
+ imageSizes: ['0.5K', '1K', '2K'],
+ },
+ [IMAGE_MODEL_GPT_IMAGE_2]: {
+ aspectRatios: ['1:1', '2:3', '3:2', '9:16', '16:9'],
+ imageSizes: ['1K', '2K'],
+ },
+} as const;
export const CHARACTER_ANIMATION_MODEL = 'seedance2.0';
export const CHARACTER_ANIMATION_ACTION_PROMPTS = [
{ label: '待机', text: '待机动作,轻微呼吸起伏。' },
@@ -169,17 +183,23 @@ export function buildIconSpecPrompt(values: SpecFormValues) {
export function buildSpecPrompt(
type: SpecGenerationType,
values: SpecFormValues,
+ hasReferenceImage = false,
) {
- if (type === 'character') {
- return buildCharacterSpecPrompt(values);
+ const prompt =
+ type === 'character'
+ ? buildCharacterSpecPrompt(values)
+ : type === 'ui'
+ ? buildUiSpecPrompt(values)
+ : type === 'icon'
+ ? buildIconSpecPrompt(values)
+ : values.customPrompt.trim();
+ if (!hasReferenceImage) {
+ return prompt;
}
- if (type === 'ui') {
- return buildUiSpecPrompt(values);
- }
- if (type === 'icon') {
- return buildIconSpecPrompt(values);
- }
- return values.customPrompt.trim();
+ return [
+ '参考图生成规范:严格参考图1的构图、风格、材质、色彩、形状语言和视觉层级生成本次规范图;参考图只作为美术方向和规范语义依据,不要直接复制参考图中的文字、水印或无关背景。',
+ prompt,
+ ].join('\n');
}
export function getLayerKindLabel(layer: CanvasLayer) {
@@ -254,11 +274,15 @@ export function buildImageGenerationInputs(prompt: string): CanvasGenerationInpu
export function buildSpecGenerationInputs(
specType: SpecGenerationType,
values: SpecFormValues,
+ reference?: CharacterReferenceImage | null,
): CanvasGenerationInputs {
+ const references = reference
+ ? [{ title: '参考图', label: reference.label, src: reference.src }]
+ : [];
if (specType === 'custom') {
return {
fields: createGenerationInputField('自定义规范提示词', values.customPrompt),
- references: [],
+ references,
};
}
@@ -274,7 +298,7 @@ export function buildSpecGenerationInputs(
}
return {
fields: baseFields,
- references: [],
+ references,
};
}
diff --git a/src/components/image-editor/useImageCanvasGenerationWorkflow.ts b/src/components/image-editor/useImageCanvasGenerationWorkflow.ts
index f321d1d2..c36313ac 100644
--- a/src/components/image-editor/useImageCanvasGenerationWorkflow.ts
+++ b/src/components/image-editor/useImageCanvasGenerationWorkflow.ts
@@ -30,6 +30,7 @@ import {
DEFAULT_ICON_DESCRIPTIONS,
DEFAULT_IMAGE_MODEL,
DEFAULT_SPEC_FORM_VALUES,
+ EDITOR_IMAGE_DIMENSION_OPTIONS,
ICON_DESCRIPTION_LIMIT,
ICON_FRAME_DISPLAY_SIZE,
ICON_FRAME_ORIGINAL_SIZE,
@@ -154,10 +155,16 @@ export function useImageCanvasGenerationWorkflow({
const [isSpecMenuOpen, setIsSpecMenuOpen] = useState(false);
const [isCharacterSpecMenuOpen, setIsCharacterSpecMenuOpen] =
useState(false);
+ const [isCharacterReferenceMenuOpen, setIsCharacterReferenceMenuOpen] =
+ useState(false);
const [
isPickingCharacterSpecFromCanvas,
setIsPickingCharacterSpecFromCanvas,
] = useState(false);
+ const [
+ isPickingCharacterReferenceFromCanvas,
+ setIsPickingCharacterReferenceFromCanvas,
+ ] = useState(false);
const [isIconSpecMenuOpen, setIsIconSpecMenuOpen] = useState(false);
const [isPickingIconSpecFromCanvas, setIsPickingIconSpecFromCanvas] =
useState(false);
@@ -165,6 +172,7 @@ export function useImageCanvasGenerationWorkflow({
useState(null);
const [characterAnimationPanel, setCharacterAnimationPanel] =
useState(null);
+ const [lastImageModel, setLastImageModel] = useState(DEFAULT_IMAGE_MODEL);
const quickEditSourceLayer = quickEditPanel
? (layers.find((layer) => layer.id === quickEditPanel.sourceLayerId) ??
@@ -278,7 +286,13 @@ export function useImageCanvasGenerationWorkflow({
const openCharacterGenerationDialog = useCallback(() => {
const worldCenter = getViewportWorldCenter({ canvasSize, viewport });
setIsSpecMenuOpen(false);
+ setIsCharacterReferenceMenuOpen(false);
setIsPickingCharacterSpecFromCanvas(false);
+ setIsPickingCharacterReferenceFromCanvas(false);
+ const dimensionOptions =
+ EDITOR_IMAGE_DIMENSION_OPTIONS[
+ lastImageModel as keyof typeof EDITOR_IMAGE_DIMENSION_OPTIONS
+ ] ?? EDITOR_IMAGE_DIMENSION_OPTIONS[DEFAULT_IMAGE_MODEL];
openCanvasGenerationDialog({
mode: 'character',
prompt: '',
@@ -286,6 +300,11 @@ export function useImageCanvasGenerationWorkflow({
composerOpen: true,
characterSpecReference: null,
characterReferences: [],
+ imageModel: lastImageModel,
+ aspectRatio: dimensionOptions.aspectRatios[0],
+ imageSize:
+ dimensionOptions.imageSizes.find((size) => size === '1K') ??
+ dimensionOptions.imageSizes[0],
placeholder: {
x: worldCenter.x - CHARACTER_FRAME_DISPLAY_SIZE.width / 2,
y: worldCenter.y - CHARACTER_FRAME_DISPLAY_SIZE.height / 2,
@@ -300,6 +319,7 @@ export function useImageCanvasGenerationWorkflow({
setQuickEditPanel(null);
}, [
canvasSize,
+ lastImageModel,
openCanvasGenerationDialog,
selectSingleLayer,
setActiveTool,
@@ -309,8 +329,14 @@ export function useImageCanvasGenerationWorkflow({
const openIconGenerationDialog = useCallback(() => {
const worldCenter = getViewportWorldCenter({ canvasSize, viewport });
setIsSpecMenuOpen(false);
+ setIsCharacterReferenceMenuOpen(false);
setIsPickingCharacterSpecFromCanvas(false);
+ setIsPickingCharacterReferenceFromCanvas(false);
setIsPickingIconSpecFromCanvas(false);
+ const dimensionOptions =
+ EDITOR_IMAGE_DIMENSION_OPTIONS[
+ lastImageModel as keyof typeof EDITOR_IMAGE_DIMENSION_OPTIONS
+ ] ?? EDITOR_IMAGE_DIMENSION_OPTIONS[DEFAULT_IMAGE_MODEL];
openCanvasGenerationDialog({
mode: 'icon',
prompt: '',
@@ -318,6 +344,11 @@ export function useImageCanvasGenerationWorkflow({
composerOpen: true,
iconSpecReference: null,
iconDescriptions: [...DEFAULT_ICON_DESCRIPTIONS],
+ imageModel: lastImageModel,
+ aspectRatio: dimensionOptions.aspectRatios[0],
+ imageSize:
+ dimensionOptions.imageSizes.find((size) => size === '1K') ??
+ dimensionOptions.imageSizes[0],
placeholder: {
x: worldCenter.x - ICON_FRAME_DISPLAY_SIZE.width / 2,
y: worldCenter.y - ICON_FRAME_DISPLAY_SIZE.height / 2,
@@ -333,6 +364,7 @@ export function useImageCanvasGenerationWorkflow({
setCharacterAnimationPanel(null);
}, [
canvasSize,
+ lastImageModel,
openCanvasGenerationDialog,
selectSingleLayer,
setActiveTool,
@@ -543,6 +575,26 @@ export function useImageCanvasGenerationWorkflow({
[setGenerateDialog, setImageContextMenu],
);
+ const pickCharacterReferenceFromLayer = useCallback(
+ (layer: CanvasLayer) => {
+ setGenerateDialog((currentDialog) =>
+ currentDialog?.mode === 'character'
+ ? {
+ ...setFailedCharacterGenerationIdle(currentDialog),
+ characterReferences: [
+ ...(currentDialog.characterReferences ?? []),
+ createCanvasLayerReference(layer),
+ ],
+ composerOpen: true,
+ }
+ : currentDialog,
+ );
+ setIsPickingCharacterReferenceFromCanvas(false);
+ setImageContextMenu(null);
+ },
+ [setGenerateDialog, setImageContextMenu],
+ );
+
const pickIconSpecFromLayer = useCallback(
(layer: CanvasLayer) => {
if (layer.assetKind !== 'icon-spec') {
@@ -654,7 +706,11 @@ export function useImageCanvasGenerationWorkflow({
const generated = await generateEditorIconSpritesheet({
referenceImageSrc: dialog.iconSpecReference.src,
iconDescriptions,
+ model: dialog.imageModel ?? DEFAULT_IMAGE_MODEL,
+ aspectRatio: dialog.aspectRatio ?? '1:1',
+ imageSize: dialog.imageSize ?? '1K',
});
+ setLastImageModel(dialog.imageModel ?? DEFAULT_IMAGE_MODEL);
addIconSpritesheetResultLayers(
generated,
generated.iconImageSrcs,
@@ -795,8 +851,12 @@ export function useImageCanvasGenerationWorkflow({
const generated = await generateEditorImage({
prompt: normalizedPrompt,
kind: 'character',
+ model: dialog.imageModel ?? DEFAULT_IMAGE_MODEL,
+ aspectRatio: dialog.aspectRatio ?? '1:1',
+ imageSize: dialog.imageSize ?? '1K',
...(referenceImageSrcs.length ? { referenceImageSrcs } : {}),
});
+ setLastImageModel(dialog.imageModel ?? DEFAULT_IMAGE_MODEL);
addGeneratedResultLayer(generated, {
frame: getGeneratingDialogPlaceholder(dialog),
assetKind: 'character',
@@ -1021,8 +1081,12 @@ export function useImageCanvasGenerationWorkflow({
setIsSpecMenuOpen,
isCharacterSpecMenuOpen,
setIsCharacterSpecMenuOpen,
+ isCharacterReferenceMenuOpen,
+ setIsCharacterReferenceMenuOpen,
isPickingCharacterSpecFromCanvas,
setIsPickingCharacterSpecFromCanvas,
+ isPickingCharacterReferenceFromCanvas,
+ setIsPickingCharacterReferenceFromCanvas,
isIconSpecMenuOpen,
setIsIconSpecMenuOpen,
isPickingIconSpecFromCanvas,
@@ -1035,6 +1099,7 @@ export function useImageCanvasGenerationWorkflow({
openEditDialog,
openQuickEditPanel,
pickCharacterSpecFromLayer,
+ pickCharacterReferenceFromLayer,
pickIconSpecFromLayer,
submitIconSpritesheetGeneration,
submitQuickEdit,
@@ -1043,6 +1108,7 @@ export function useImageCanvasGenerationWorkflow({
updateIconDescription,
addIconDescription,
updateCharacterAnimationDuration,
+ rememberImageModel: setLastImageModel,
submitCharacterAnimation,
hideGeneratedLayerPanelAfterBlur,
closeGenerateComposer,
@@ -1057,8 +1123,10 @@ export function useImageCanvasGenerationWorkflow({
closeGenerateComposer,
hideGeneratedLayerPanelAfterBlur,
iconDescriptionValues,
+ isCharacterReferenceMenuOpen,
isCharacterSpecMenuOpen,
isIconSpecMenuOpen,
+ isPickingCharacterReferenceFromCanvas,
isPickingCharacterSpecFromCanvas,
isPickingIconSpecFromCanvas,
isSpecMenuOpen,
@@ -1069,6 +1137,7 @@ export function useImageCanvasGenerationWorkflow({
openIconGenerationDialog,
openQuickEditPanel,
openSpecDialog,
+ pickCharacterReferenceFromLayer,
pickCharacterSpecFromLayer,
pickIconSpecFromLayer,
quickEditModelOptions,
diff --git a/src/components/image-editor/useImageCanvasKeyboardShortcuts.test.tsx b/src/components/image-editor/useImageCanvasKeyboardShortcuts.test.tsx
index 5071864f..1f44b871 100644
--- a/src/components/image-editor/useImageCanvasKeyboardShortcuts.test.tsx
+++ b/src/components/image-editor/useImageCanvasKeyboardShortcuts.test.tsx
@@ -79,8 +79,14 @@ function KeyboardShortcutsHarness({
const [contextMenuOpen, setContextMenuOpen] = useState(true);
const [isSpecMenuOpen, setIsSpecMenuOpen] = useState(true);
const [isCharacterSpecMenuOpen, setIsCharacterSpecMenuOpen] = useState(true);
+ const [isCharacterReferenceMenuOpen, setIsCharacterReferenceMenuOpen] =
+ useState(true);
const [isPickingCharacterSpecFromCanvas, setIsPickingCharacterSpecFromCanvas] =
useState(true);
+ const [
+ isPickingCharacterReferenceFromCanvas,
+ setIsPickingCharacterReferenceFromCanvas,
+ ] = useState(true);
const [isIconSpecMenuOpen, setIsIconSpecMenuOpen] = useState(true);
const [isPickingIconSpecFromCanvas, setIsPickingIconSpecFromCanvas] =
useState(true);
@@ -111,7 +117,9 @@ function KeyboardShortcutsHarness({
closeEditorChromePanels,
setIsSpecMenuOpen,
setIsCharacterSpecMenuOpen,
+ setIsCharacterReferenceMenuOpen,
setIsPickingCharacterSpecFromCanvas,
+ setIsPickingCharacterReferenceFromCanvas,
setIsIconSpecMenuOpen,
setIsPickingIconSpecFromCanvas,
setIsSpacePanning,
@@ -138,9 +146,15 @@ function KeyboardShortcutsHarness({
{String(isCharacterSpecMenuOpen)}
+
+ {String(isCharacterReferenceMenuOpen)}
+
{String(isPickingCharacterSpecFromCanvas)}
+
+ {String(isPickingCharacterReferenceFromCanvas)}
+
{String(isIconSpecMenuOpen)}
{String(isPickingIconSpecFromCanvas)}
diff --git a/src/components/image-editor/useImageCanvasKeyboardShortcuts.ts b/src/components/image-editor/useImageCanvasKeyboardShortcuts.ts
index eca38901..ed2fd479 100644
--- a/src/components/image-editor/useImageCanvasKeyboardShortcuts.ts
+++ b/src/components/image-editor/useImageCanvasKeyboardShortcuts.ts
@@ -31,7 +31,9 @@ type UseImageCanvasKeyboardShortcutsOptions = {
closeEditorChromePanels: () => void;
setIsSpecMenuOpen: (open: boolean) => void;
setIsCharacterSpecMenuOpen: (open: boolean) => void;
+ setIsCharacterReferenceMenuOpen: (open: boolean) => void;
setIsPickingCharacterSpecFromCanvas: (picking: boolean) => void;
+ setIsPickingCharacterReferenceFromCanvas: (picking: boolean) => void;
setIsIconSpecMenuOpen: (open: boolean) => void;
setIsPickingIconSpecFromCanvas: (picking: boolean) => void;
setIsSpacePanning: (panning: boolean) => void;
@@ -77,7 +79,9 @@ export function useImageCanvasKeyboardShortcuts({
closeEditorChromePanels,
setIsSpecMenuOpen,
setIsCharacterSpecMenuOpen,
+ setIsCharacterReferenceMenuOpen,
setIsPickingCharacterSpecFromCanvas,
+ setIsPickingCharacterReferenceFromCanvas,
setIsIconSpecMenuOpen,
setIsPickingIconSpecFromCanvas,
setIsSpacePanning,
@@ -93,7 +97,9 @@ export function useImageCanvasKeyboardShortcuts({
currentPanel?.status === 'generating' ? currentPanel : null,
);
setIsCharacterSpecMenuOpen(false);
+ setIsCharacterReferenceMenuOpen(false);
setIsPickingCharacterSpecFromCanvas(false);
+ setIsPickingCharacterReferenceFromCanvas(false);
setIsIconSpecMenuOpen(false);
setIsPickingIconSpecFromCanvas(false);
setGenerateDialog((currentDialog) => {
@@ -149,7 +155,9 @@ export function useImageCanvasKeyboardShortcuts({
setGenerateDialog(null);
setActiveTool('select');
setIsCharacterSpecMenuOpen(false);
+ setIsCharacterReferenceMenuOpen(false);
setIsPickingCharacterSpecFromCanvas(false);
+ setIsPickingCharacterReferenceFromCanvas(false);
setIsIconSpecMenuOpen(false);
setIsPickingIconSpecFromCanvas(false);
return;
@@ -193,7 +201,9 @@ export function useImageCanvasKeyboardShortcuts({
setGenerateDialog,
setImageContextMenu,
setIsCharacterSpecMenuOpen,
+ setIsCharacterReferenceMenuOpen,
setIsIconSpecMenuOpen,
+ setIsPickingCharacterReferenceFromCanvas,
setIsPickingCharacterSpecFromCanvas,
setIsPickingIconSpecFromCanvas,
setIsSpacePanning,
diff --git a/src/components/image-editor/useImageCanvasStageInteractions.test.tsx b/src/components/image-editor/useImageCanvasStageInteractions.test.tsx
index 2d89bcfc..df67f505 100644
--- a/src/components/image-editor/useImageCanvasStageInteractions.test.tsx
+++ b/src/components/image-editor/useImageCanvasStageInteractions.test.tsx
@@ -175,6 +175,7 @@ function StageInteractionsHarness({
generateDialog,
setGenerateDialog,
isPickingCharacterSpecFromCanvas: false,
+ isPickingCharacterReferenceFromCanvas: false,
isPickingIconSpecFromCanvas: false,
clearCanvasFocus: () => {
setSelectedLayerId(null);
@@ -182,6 +183,7 @@ function StageInteractionsHarness({
setClearCount((currentCount) => currentCount + 1);
},
pickCharacterSpecFromLayer,
+ pickCharacterReferenceFromLayer: vi.fn(),
pickIconSpecFromLayer,
activateCanvasGenerationDialog,
updateCanvasGenerationDialogById: (dialogId, updater) => {
diff --git a/src/components/image-editor/useImageCanvasStageInteractions.ts b/src/components/image-editor/useImageCanvasStageInteractions.ts
index 2507f19f..46dbf0fd 100644
--- a/src/components/image-editor/useImageCanvasStageInteractions.ts
+++ b/src/components/image-editor/useImageCanvasStageInteractions.ts
@@ -45,9 +45,11 @@ type UseImageCanvasStageInteractionsOptions = {
generateDialog: GenerateDialogState | null;
setGenerateDialog: Dispatch>;
isPickingCharacterSpecFromCanvas: boolean;
+ isPickingCharacterReferenceFromCanvas: boolean;
isPickingIconSpecFromCanvas: boolean;
clearCanvasFocus: () => void;
pickCharacterSpecFromLayer: (layer: CanvasLayer) => void;
+ pickCharacterReferenceFromLayer: (layer: CanvasLayer) => void;
pickIconSpecFromLayer: (layer: CanvasLayer) => void;
activateCanvasGenerationDialog: (
dialog: CanvasGenerationDialogState,
@@ -126,9 +128,11 @@ export function useImageCanvasStageInteractions({
generateDialog,
setGenerateDialog,
isPickingCharacterSpecFromCanvas,
+ isPickingCharacterReferenceFromCanvas,
isPickingIconSpecFromCanvas,
clearCanvasFocus,
pickCharacterSpecFromLayer,
+ pickCharacterReferenceFromLayer,
pickIconSpecFromLayer,
activateCanvasGenerationDialog,
updateCanvasGenerationDialogById,
@@ -139,6 +143,7 @@ export function useImageCanvasStageInteractions({
}: UseImageCanvasStageInteractionsOptions) {
const dragStateRef = useRef(null);
const isShiftPressedRef = useRef(false);
+ const suppressNextLayerClickRef = useRef(false);
const [canvasMarquee, setCanvasMarquee] = useState(
null,
);
@@ -230,12 +235,24 @@ export function useImageCanvasStageInteractions({
) {
event.preventDefault();
event.stopPropagation();
+ suppressNextLayerClickRef.current = true;
pickCharacterSpecFromLayer(layer);
return;
}
+ if (
+ isPickingCharacterReferenceFromCanvas &&
+ generateDialog?.mode === 'character'
+ ) {
+ event.preventDefault();
+ event.stopPropagation();
+ suppressNextLayerClickRef.current = true;
+ pickCharacterReferenceFromLayer(layer);
+ return;
+ }
if (isPickingIconSpecFromCanvas && generateDialog?.mode === 'icon') {
event.preventDefault();
event.stopPropagation();
+ suppressNextLayerClickRef.current = true;
pickIconSpecFromLayer(layer);
return;
}
@@ -302,8 +319,10 @@ export function useImageCanvasStageInteractions({
effectiveTool,
generateDialog?.mode,
isPickingCharacterSpecFromCanvas,
+ isPickingCharacterReferenceFromCanvas,
isPickingIconSpecFromCanvas,
layers,
+ pickCharacterReferenceFromLayer,
pickCharacterSpecFromLayer,
pickIconSpecFromLayer,
selectedLayerIds,
@@ -320,9 +339,16 @@ export function useImageCanvasStageInteractions({
// 测试环境和辅助技术可能只触发 click;
// 用 click 兜底选中,真实拖拽仍由 pointerDown 负责。
event.stopPropagation();
+ if (suppressNextLayerClickRef.current) {
+ suppressNextLayerClickRef.current = false;
+ return;
+ }
if (isPickingCharacterSpecFromCanvas) {
return;
}
+ if (isPickingCharacterReferenceFromCanvas) {
+ return;
+ }
if (isPickingIconSpecFromCanvas) {
return;
}
@@ -346,6 +372,7 @@ export function useImageCanvasStageInteractions({
},
[
isPickingCharacterSpecFromCanvas,
+ isPickingCharacterReferenceFromCanvas,
isPickingIconSpecFromCanvas,
onCloseImageContextMenu,
setGenerateDialog,