Files
Genarrative/src/components/image-editor/useImageCanvasGenerationSurface.tsx
T
kdletters 6d964937db 完善画布生成面板交互
补齐普通生图参考图来源菜单和画布选择流程

接入UI设计图与视频生成面板的提交链路

让生成引用上传目标支持多种生成面板

统一图片信息弹窗断言并补充相关测试

修复图标按钮浮层锚点ref与视频生成类型契约
2026-06-17 21:49:32 +08:00

288 lines
10 KiB
TypeScript

import {
type Dispatch,
type MutableRefObject,
type RefObject,
type SetStateAction,
useCallback,
} from 'react';
import { ImageCanvasGenerationComposerView } from './ImageCanvasGenerationComposerView';
import {
resolveCharacterAnimationPanelStyle,
resolveGenerationAnchor,
resolveGenerationComposerStyle,
resolveIconComposerStyle,
resolveQuickEditPanelStyle,
} from './ImageCanvasOverlayModel';
import type {
CanvasGenerationDialogState,
CanvasLayer,
CanvasTool,
CanvasViewport,
GenerateDialogState,
ImageContextMenuState,
SidebarPanel,
UploadTarget,
} from './ImageCanvasEditorTypes';
import { useImageCanvasGenerationWorkflow } from './useImageCanvasGenerationWorkflow';
type CanvasGenerationDialogUpdater = (
dialog: CanvasGenerationDialogState,
) => CanvasGenerationDialogState | null;
type ImageCanvasGenerationSurfaceOptions = {
layers: CanvasLayer[];
canvasSize: { width: number; height: number };
viewport: CanvasViewport;
setViewport: Dispatch<SetStateAction<CanvasViewport>>;
layerCounterRef: MutableRefObject<number>;
specToolWrapRef: RefObject<HTMLSpanElement | null>;
characterSpecButtonRef: RefObject<HTMLButtonElement | null>;
characterReferenceButtonRef: RefObject<HTMLButtonElement | null>;
iconSpecButtonRef: RefObject<HTMLButtonElement | null>;
generationReferenceButtonRef: RefObject<HTMLButtonElement | null>;
generateDialog: GenerateDialogState | null;
setGenerateDialog: Dispatch<SetStateAction<GenerateDialogState | null>>;
activeCanvasGenerationDialog: CanvasGenerationDialogState | null;
canvasGenerationDialogs: CanvasGenerationDialogState[];
openCanvasGenerationDialog: (
dialog: Omit<CanvasGenerationDialogState, 'id'>,
) => void;
updateCanvasGenerationDialogById: (
dialogId: string,
updater: CanvasGenerationDialogUpdater,
) => void;
removeCanvasGenerationDialogById: (dialogId: string) => void;
removeCanvasGenerationDialogsByLayerId: (targetLayerId: string) => void;
getGeneratingDialogPlaceholder: (
dialog: GenerateDialogState,
) => GenerateDialogState['placeholder'];
appendCanvasLayersWithResources: (nextLayers: CanvasLayer[]) => void;
selectSingleLayer: (layerId: string | null) => void;
fitLayers: (targetLayers?: CanvasLayer[]) => void;
setActiveTool: Dispatch<SetStateAction<CanvasTool>>;
setActiveSidebarPanel: Dispatch<SetStateAction<SidebarPanel | null>>;
setMetadataLayer: Dispatch<SetStateAction<CanvasLayer | null>>;
setImageContextMenu: Dispatch<SetStateAction<ImageContextMenuState | null>>;
requestUpload: (target: UploadTarget) => void;
};
export function useImageCanvasGenerationSurface({
layers,
canvasSize,
viewport,
setViewport,
layerCounterRef,
specToolWrapRef,
characterSpecButtonRef,
characterReferenceButtonRef,
iconSpecButtonRef,
generationReferenceButtonRef,
generateDialog,
setGenerateDialog,
activeCanvasGenerationDialog,
canvasGenerationDialogs,
openCanvasGenerationDialog,
updateCanvasGenerationDialogById,
removeCanvasGenerationDialogById,
removeCanvasGenerationDialogsByLayerId,
getGeneratingDialogPlaceholder,
appendCanvasLayersWithResources,
selectSingleLayer,
fitLayers,
setActiveTool,
setActiveSidebarPanel,
setMetadataLayer,
setImageContextMenu,
requestUpload,
}: ImageCanvasGenerationSurfaceOptions) {
const generationWorkflow = useImageCanvasGenerationWorkflow({
layers,
canvasSize,
viewport,
setViewport,
canvasGenerationDialogs,
layerCounterRef,
generateDialog,
setGenerateDialog,
openCanvasGenerationDialog,
updateCanvasGenerationDialogById,
removeCanvasGenerationDialogById,
removeCanvasGenerationDialogsByLayerId,
getGeneratingDialogPlaceholder,
appendCanvasLayersWithResources,
selectSingleLayer,
fitLayers,
setActiveTool,
setActiveSidebarPanel,
setMetadataLayer,
setImageContextMenu,
});
const activeGenerationLayer =
activeCanvasGenerationDialog?.generatedLayerId
? (layers.find(
(layer) => layer.id === activeCanvasGenerationDialog.generatedLayerId,
) ?? null)
: null;
const generationAnchor = resolveGenerationAnchor({
dialog: activeCanvasGenerationDialog,
generatedLayer: activeGenerationLayer,
});
const generationComposerStyle = resolveGenerationComposerStyle({
dialog: activeCanvasGenerationDialog,
anchor: generationAnchor,
viewport,
});
const iconComposerStyle = resolveIconComposerStyle({
dialog: activeCanvasGenerationDialog,
composerStyle: generationComposerStyle,
iconDescriptionCount: generationWorkflow.iconDescriptionValues.length,
});
const quickEditPanelStyle = resolveQuickEditPanelStyle({
panel: generationWorkflow.quickEditPanel,
sourceLayer: generationWorkflow.quickEditSourceLayer,
viewport,
canvasSize,
});
const characterAnimationPanelStyle = resolveCharacterAnimationPanelStyle({
panel: generationWorkflow.characterAnimationPanel,
sourceLayer: generationWorkflow.characterAnimationSourceLayer,
viewport,
canvasSize,
});
const switchGenerationTool = useCallback(
(tool: CanvasTool) => {
if (tool === 'generate') {
generationWorkflow.openGenerateDialog();
return true;
}
if (tool === 'spec') {
generationWorkflow.setIsSpecMenuOpen((open) => !open);
setActiveTool('spec');
return true;
}
if (tool === 'character') {
generationWorkflow.openCharacterGenerationDialog();
return true;
}
if (tool === 'icon') {
generationWorkflow.openIconGenerationDialog();
return true;
}
return false;
},
[generationWorkflow, setActiveTool],
);
const generationComposerNode = (
<ImageCanvasGenerationComposerView
specToolWrapRef={specToolWrapRef}
characterSpecButtonRef={characterSpecButtonRef}
characterReferenceButtonRef={characterReferenceButtonRef}
generationReferenceButtonRef={generationReferenceButtonRef}
iconSpecButtonRef={iconSpecButtonRef}
isSpecMenuOpen={generationWorkflow.isSpecMenuOpen}
isGenerationReferenceMenuOpen={
generationWorkflow.isGenerationReferenceMenuOpen
}
isCharacterSpecMenuOpen={generationWorkflow.isCharacterSpecMenuOpen}
isCharacterReferenceMenuOpen={
generationWorkflow.isCharacterReferenceMenuOpen
}
isIconSpecMenuOpen={generationWorkflow.isIconSpecMenuOpen}
isUiDesignSpecMenuOpen={generationWorkflow.isUiDesignSpecMenuOpen}
isPickingGenerationReferenceFromCanvas={
generationWorkflow.isPickingGenerationReferenceFromCanvas
}
isPickingCharacterSpecFromCanvas={
generationWorkflow.isPickingCharacterSpecFromCanvas
}
isPickingCharacterReferenceFromCanvas={
generationWorkflow.isPickingCharacterReferenceFromCanvas
}
isPickingIconSpecFromCanvas={
generationWorkflow.isPickingIconSpecFromCanvas
}
isPickingUiDesignSpecFromCanvas={
generationWorkflow.isPickingUiDesignSpecFromCanvas
}
generateDialog={generateDialog}
generationComposerStyle={generationComposerStyle}
iconComposerStyle={iconComposerStyle}
quickEditPanel={generationWorkflow.quickEditPanel}
quickEditSourceLayer={generationWorkflow.quickEditSourceLayer}
quickEditPanelStyle={quickEditPanelStyle}
quickEditSizeOptions={generationWorkflow.quickEditSizeOptions}
quickEditModelOptions={generationWorkflow.quickEditModelOptions}
characterAnimationPanel={generationWorkflow.characterAnimationPanel}
characterAnimationSourceLayer={
generationWorkflow.characterAnimationSourceLayer
}
characterAnimationPanelStyle={characterAnimationPanelStyle}
characterAnimationPrice={generationWorkflow.characterAnimationPrice}
setGenerateDialog={setGenerateDialog}
setQuickEditPanel={generationWorkflow.setQuickEditPanel}
setCharacterAnimationPanel={
generationWorkflow.setCharacterAnimationPanel
}
setIsCharacterSpecMenuOpen={
generationWorkflow.setIsCharacterSpecMenuOpen
}
setIsCharacterReferenceMenuOpen={
generationWorkflow.setIsCharacterReferenceMenuOpen
}
setIsGenerationReferenceMenuOpen={
generationWorkflow.setIsGenerationReferenceMenuOpen
}
setIsIconSpecMenuOpen={generationWorkflow.setIsIconSpecMenuOpen}
setIsUiDesignSpecMenuOpen={
generationWorkflow.setIsUiDesignSpecMenuOpen
}
setIsPickingGenerationReferenceFromCanvas={
generationWorkflow.setIsPickingGenerationReferenceFromCanvas
}
setIsPickingCharacterSpecFromCanvas={
generationWorkflow.setIsPickingCharacterSpecFromCanvas
}
setIsPickingCharacterReferenceFromCanvas={
generationWorkflow.setIsPickingCharacterReferenceFromCanvas
}
setIsPickingIconSpecFromCanvas={
generationWorkflow.setIsPickingIconSpecFromCanvas
}
setIsPickingUiDesignSpecFromCanvas={
generationWorkflow.setIsPickingUiDesignSpecFromCanvas
}
onOpenSpecDialog={generationWorkflow.openSpecDialog}
onRequestUpload={requestUpload}
onSubmitImageGeneration={(dialog) =>
void generationWorkflow.submitImageGeneration(dialog)
}
onSubmitIconSpritesheetGeneration={(dialog) =>
void generationWorkflow.submitIconSpritesheetGeneration(dialog)
}
onSubmitQuickEdit={() => void generationWorkflow.submitQuickEdit()}
onSubmitCharacterAnimation={() =>
void generationWorkflow.submitCharacterAnimation()
}
onCloseGenerateComposer={generationWorkflow.closeGenerateComposer}
onUpdateSpecFormValue={generationWorkflow.updateSpecFormValue}
onUpdateIconDescription={generationWorkflow.updateIconDescription}
onAddIconDescription={generationWorkflow.addIconDescription}
onUpdateCharacterAnimationDuration={
generationWorkflow.updateCharacterAnimationDuration
}
onRememberImageModel={generationWorkflow.rememberImageModel}
/>
);
return {
...generationWorkflow,
generationComposerStyle,
generationComposerNode,
switchGenerationTool,
};
}