调整图片编辑器参考图选择交互

- 常规参考图入口改为先弹出来源菜单,支持从画布选择和上传图片。

- 角色规范、图标规范和常规参考图来源菜单统一向上弹出。

- 画布参考图选择拦截普通图层选中逻辑,保持生成面板不隐藏。

- 补充图片编辑器交互测试与技术文档说明。
This commit is contained in:
2026-06-17 14:08:26 +08:00
parent d0ad8402de
commit e970d34574
12 changed files with 602 additions and 76 deletions

View File

@@ -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<QuickEditPanelState | null>(null);
const [characterAnimationPanel, setCharacterAnimationPanel] =
useState<CharacterAnimationPanelState | null>(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,