a5906ac26e
统一图片模型的业务交付像素并保留 GPT provider 合法请求尺寸 禁止欠交付图片被放大并返回尺寸降级提示 重置相同 warning 的三秒自动关闭计时 补充前后端回归测试与编辑器契约文档
557 lines
19 KiB
TypeScript
557 lines
19 KiB
TypeScript
import { X } from 'lucide-react';
|
|
import {
|
|
type CSSProperties,
|
|
type Dispatch,
|
|
type MutableRefObject,
|
|
type ReactNode,
|
|
type RefObject,
|
|
type SetStateAction,
|
|
useCallback,
|
|
useEffect,
|
|
useRef,
|
|
} from 'react';
|
|
|
|
import type {
|
|
EditorAssetSnapshot,
|
|
EditorProjectSnapshot,
|
|
} from '../../services/image-editor/editorProjectClient';
|
|
import {
|
|
PlatformFloatingMenu,
|
|
PlatformFloatingMenuItem,
|
|
} from '../common/PlatformFloatingMenu';
|
|
import { PlatformRuntimeStatusToast } from '../common/PlatformRuntimeStatusToast';
|
|
import { ImageCanvasEditorPortal } from './ImageCanvasEditorPortal';
|
|
import type {
|
|
CanvasGenerationDialogState,
|
|
CanvasHistoryAction,
|
|
CanvasLayer,
|
|
CanvasTool,
|
|
CanvasViewport,
|
|
GenerateDialogState,
|
|
ImageContextMenuState,
|
|
PublicationMaterialsWorkflowId,
|
|
SidebarPanel,
|
|
UploadTarget,
|
|
} from './ImageCanvasEditorTypes';
|
|
import { ImageCanvasGenerationComposerView } from './ImageCanvasGenerationComposerView';
|
|
import {
|
|
resolveCharacterAnimationPanelStyle,
|
|
resolveCropExpandPanelStyle,
|
|
resolveGenerationAnchor,
|
|
resolveGenerationComposerStyle,
|
|
resolveIconComposerStyle,
|
|
resolveQuickEditPanelStyle,
|
|
} from './ImageCanvasOverlayModel';
|
|
import { PUBLICATION_MATERIALS_WORKFLOWS } from './ImageCanvasPublicationMaterialsModel';
|
|
import { useImageCanvasGenerationWorkflow } from './useImageCanvasGenerationWorkflow';
|
|
|
|
type CanvasGenerationDialogUpdater = (
|
|
dialog: CanvasGenerationDialogState,
|
|
) => CanvasGenerationDialogState | null;
|
|
|
|
type ToolbarOptionTool = Extract<CanvasTool, 'music' | 'spec' | 'publication'>;
|
|
|
|
type ImageCanvasGenerationSurfaceOptions = {
|
|
layers: CanvasLayer[];
|
|
canvasSize: { width: number; height: number };
|
|
viewport: CanvasViewport;
|
|
setViewport: Dispatch<SetStateAction<CanvasViewport>>;
|
|
canvasViewportRef: RefObject<HTMLDivElement | null>;
|
|
setLayers: Dispatch<SetStateAction<CanvasLayer[]>>;
|
|
layerCounterRef: MutableRefObject<number>;
|
|
specToolWrapRef: RefObject<HTMLSpanElement | null>;
|
|
musicToolWrapRef: RefObject<HTMLSpanElement | null>;
|
|
publicationToolWrapRef: RefObject<HTMLSpanElement | null>;
|
|
characterSpecButtonRef: RefObject<HTMLButtonElement | null>;
|
|
characterReferenceButtonRef: RefObject<HTMLButtonElement | null>;
|
|
iconSpecButtonRef: RefObject<HTMLButtonElement | null>;
|
|
generationReferenceButtonRef: RefObject<HTMLButtonElement | null>;
|
|
publicationReferenceButtonRef: RefObject<HTMLButtonElement | null>;
|
|
generateDialog: GenerateDialogState | null;
|
|
setGenerateDialog: Dispatch<SetStateAction<GenerateDialogState | null>>;
|
|
activeCanvasGenerationDialog: CanvasGenerationDialogState | null;
|
|
canvasGenerationDialogs: CanvasGenerationDialogState[];
|
|
openCanvasGenerationDialog: (
|
|
dialog: Omit<CanvasGenerationDialogState, 'id'>,
|
|
) => string;
|
|
updateCanvasGenerationDialogById: (
|
|
dialogId: string,
|
|
updater: CanvasGenerationDialogUpdater,
|
|
) => void;
|
|
hasCanvasGenerationDialogById: (dialogId: string) => boolean;
|
|
archiveActiveCanvasGenerationDialog: () => void;
|
|
removeCanvasGenerationDialogsByLayerId: (targetLayerId: string) => void;
|
|
getGeneratingDialogPlaceholder: (
|
|
dialog: GenerateDialogState,
|
|
) => GenerateDialogState['placeholder'];
|
|
appendCanvasLayersWithResources: (nextLayers: CanvasLayer[]) => void;
|
|
selectSingleLayer: (layerId: string | null) => void;
|
|
fitLayers: (
|
|
targetLayers?: CanvasLayer[],
|
|
options?: { captureHistory?: boolean },
|
|
) => void;
|
|
captureCanvasHistory: (action: CanvasHistoryAction) => void;
|
|
setActiveTool: Dispatch<SetStateAction<CanvasTool>>;
|
|
setActiveSidebarPanel: Dispatch<SetStateAction<SidebarPanel | null>>;
|
|
setMetadataLayer: Dispatch<SetStateAction<CanvasLayer | null>>;
|
|
setImageContextMenu: Dispatch<SetStateAction<ImageContextMenuState | null>>;
|
|
requestUpload: (target: UploadTarget) => void;
|
|
persistGeneratedAsset?: (layer: CanvasLayer) => void;
|
|
persistUpdatedLayerResource?: (layer: CanvasLayer) => void;
|
|
projectId?: string | null;
|
|
currentUserId?: string | null;
|
|
assetFolderId?: string | null;
|
|
upsertGeneratedAsset?: (asset: EditorAssetSnapshot) => void;
|
|
applyProjectSnapshot?: (
|
|
project: EditorProjectSnapshot,
|
|
action?: CanvasHistoryAction,
|
|
) => void;
|
|
onWalletBalanceMayHaveChanged?: () => void;
|
|
};
|
|
|
|
function renderEditorPortal(node: ReactNode) {
|
|
return <ImageCanvasEditorPortal>{node}</ImageCanvasEditorPortal>;
|
|
}
|
|
|
|
function buildToolbarPortalMenuStyle(
|
|
anchor: HTMLElement | null,
|
|
): CSSProperties {
|
|
const rect = anchor?.getBoundingClientRect();
|
|
if (!rect) {
|
|
return {
|
|
position: 'fixed',
|
|
left: 0,
|
|
top: 0,
|
|
right: 'auto',
|
|
bottom: 'auto',
|
|
zIndex: 70,
|
|
};
|
|
}
|
|
|
|
return {
|
|
position: 'fixed',
|
|
left: Math.round(rect.left),
|
|
top: Math.round(rect.top),
|
|
right: 'auto',
|
|
bottom: 'auto',
|
|
zIndex: 70,
|
|
transform: 'translateY(calc(-100% - 0.45rem))',
|
|
};
|
|
}
|
|
|
|
const TOOLBAR_OPTION_CLOSE_DELAY_MS = 160;
|
|
|
|
export function useImageCanvasGenerationSurface({
|
|
layers,
|
|
canvasSize,
|
|
viewport,
|
|
setViewport,
|
|
setLayers,
|
|
layerCounterRef,
|
|
specToolWrapRef,
|
|
musicToolWrapRef,
|
|
publicationToolWrapRef,
|
|
characterSpecButtonRef,
|
|
characterReferenceButtonRef,
|
|
iconSpecButtonRef,
|
|
generationReferenceButtonRef,
|
|
publicationReferenceButtonRef,
|
|
generateDialog,
|
|
setGenerateDialog,
|
|
activeCanvasGenerationDialog,
|
|
canvasGenerationDialogs,
|
|
openCanvasGenerationDialog,
|
|
updateCanvasGenerationDialogById,
|
|
hasCanvasGenerationDialogById,
|
|
archiveActiveCanvasGenerationDialog,
|
|
removeCanvasGenerationDialogsByLayerId,
|
|
getGeneratingDialogPlaceholder,
|
|
appendCanvasLayersWithResources,
|
|
selectSingleLayer,
|
|
fitLayers,
|
|
captureCanvasHistory,
|
|
setActiveTool,
|
|
setActiveSidebarPanel,
|
|
setMetadataLayer,
|
|
setImageContextMenu,
|
|
requestUpload,
|
|
persistGeneratedAsset,
|
|
persistUpdatedLayerResource,
|
|
projectId,
|
|
currentUserId,
|
|
assetFolderId,
|
|
upsertGeneratedAsset,
|
|
applyProjectSnapshot,
|
|
onWalletBalanceMayHaveChanged,
|
|
}: ImageCanvasGenerationSurfaceOptions) {
|
|
const toolbarOptionCloseTimerRef = useRef<ReturnType<
|
|
typeof setTimeout
|
|
> | null>(null);
|
|
const generationWorkflow = useImageCanvasGenerationWorkflow({
|
|
layers,
|
|
canvasSize,
|
|
viewport,
|
|
setViewport,
|
|
setLayers,
|
|
canvasGenerationDialogs,
|
|
layerCounterRef,
|
|
generateDialog,
|
|
setGenerateDialog,
|
|
openCanvasGenerationDialog,
|
|
updateCanvasGenerationDialogById,
|
|
hasCanvasGenerationDialogById,
|
|
archiveActiveCanvasGenerationDialog,
|
|
removeCanvasGenerationDialogsByLayerId,
|
|
getGeneratingDialogPlaceholder,
|
|
appendCanvasLayersWithResources,
|
|
selectSingleLayer,
|
|
fitLayers,
|
|
captureCanvasHistory,
|
|
setActiveTool,
|
|
setActiveSidebarPanel,
|
|
setMetadataLayer,
|
|
setImageContextMenu,
|
|
persistGeneratedAsset,
|
|
persistUpdatedLayerResource,
|
|
projectId,
|
|
currentUserId,
|
|
assetFolderId,
|
|
upsertGeneratedAsset,
|
|
applyProjectSnapshot,
|
|
onWalletBalanceMayHaveChanged,
|
|
});
|
|
|
|
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,
|
|
});
|
|
const quickEditPanelStyle = resolveQuickEditPanelStyle({
|
|
panel: generationWorkflow.quickEditPanel,
|
|
sourceLayer: generationWorkflow.quickEditSourceLayer,
|
|
viewport,
|
|
canvasSize,
|
|
});
|
|
const cropExpandPanelStyle = resolveCropExpandPanelStyle({
|
|
panel: generationWorkflow.cropExpandPanel,
|
|
sourceLayer: generationWorkflow.cropExpandSourceLayer,
|
|
viewport,
|
|
canvasSize,
|
|
});
|
|
const characterAnimationPanelStyle =
|
|
activeCanvasGenerationDialog?.mode === 'character-animation'
|
|
? generationComposerStyle
|
|
: resolveCharacterAnimationPanelStyle({
|
|
panel: generationWorkflow.characterAnimationPanel,
|
|
sourceLayer: generationWorkflow.characterAnimationSourceLayer,
|
|
viewport,
|
|
canvasSize,
|
|
});
|
|
const clearToolbarOptionCloseTimer = useCallback(() => {
|
|
if (!toolbarOptionCloseTimerRef.current) {
|
|
return;
|
|
}
|
|
clearTimeout(toolbarOptionCloseTimerRef.current);
|
|
toolbarOptionCloseTimerRef.current = null;
|
|
}, []);
|
|
|
|
useEffect(
|
|
() => () => {
|
|
clearToolbarOptionCloseTimer();
|
|
},
|
|
[clearToolbarOptionCloseTimer],
|
|
);
|
|
|
|
const openToolbarOptionMenu = useCallback(
|
|
(tool: ToolbarOptionTool) => {
|
|
clearToolbarOptionCloseTimer();
|
|
generationWorkflow.setIsMusicMenuOpen(tool === 'music');
|
|
generationWorkflow.setIsSpecMenuOpen(tool === 'spec');
|
|
generationWorkflow.setIsPublicationMenuOpen(tool === 'publication');
|
|
},
|
|
[clearToolbarOptionCloseTimer, generationWorkflow],
|
|
);
|
|
|
|
const closeToolbarOptionMenu = useCallback(
|
|
(tool: ToolbarOptionTool) => {
|
|
clearToolbarOptionCloseTimer();
|
|
toolbarOptionCloseTimerRef.current = setTimeout(() => {
|
|
if (tool === 'music') {
|
|
generationWorkflow.setIsMusicMenuOpen(false);
|
|
return;
|
|
}
|
|
if (tool === 'publication') {
|
|
generationWorkflow.setIsPublicationMenuOpen(false);
|
|
return;
|
|
}
|
|
generationWorkflow.setIsSpecMenuOpen(false);
|
|
}, TOOLBAR_OPTION_CLOSE_DELAY_MS);
|
|
},
|
|
[clearToolbarOptionCloseTimer, generationWorkflow],
|
|
);
|
|
|
|
const switchGenerationTool = useCallback(
|
|
(tool: CanvasTool) => {
|
|
if (tool === 'generate') {
|
|
generationWorkflow.openGenerateDialog();
|
|
return true;
|
|
}
|
|
if (tool === 'video') {
|
|
generationWorkflow.openVideoGenerationDialog();
|
|
return true;
|
|
}
|
|
if (tool === 'music') {
|
|
openToolbarOptionMenu('music');
|
|
setActiveTool('music');
|
|
return true;
|
|
}
|
|
if (tool === 'spec') {
|
|
openToolbarOptionMenu('spec');
|
|
setActiveTool('spec');
|
|
return true;
|
|
}
|
|
if (tool === 'publication') {
|
|
openToolbarOptionMenu('publication');
|
|
setActiveTool('publication');
|
|
return true;
|
|
}
|
|
if (tool === 'character') {
|
|
generationWorkflow.openCharacterGenerationDialog();
|
|
return true;
|
|
}
|
|
if (tool === 'icon') {
|
|
generationWorkflow.openIconGenerationDialog();
|
|
return true;
|
|
}
|
|
if (tool === 'ui-design') {
|
|
generationWorkflow.openUiDesignGenerationDialog();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
[generationWorkflow, openToolbarOptionMenu, setActiveTool],
|
|
);
|
|
|
|
const generationComposerNode = (
|
|
<>
|
|
{generationWorkflow.generationWarning ? (
|
|
<PlatformRuntimeStatusToast
|
|
key={generationWorkflow.generationWarningVersion}
|
|
tone="warning"
|
|
surface="solid"
|
|
size="sm"
|
|
className="image-canvas-editor__reference-pick-toast"
|
|
role="alert"
|
|
>
|
|
<span>{generationWorkflow.generationWarning}</span>
|
|
<button
|
|
type="button"
|
|
className="image-canvas-editor__reference-pick-toast-close"
|
|
aria-label="关闭提示"
|
|
onClick={generationWorkflow.clearGenerationWarning}
|
|
>
|
|
<X aria-hidden="true" size={14} strokeWidth={2.4} />
|
|
</button>
|
|
</PlatformRuntimeStatusToast>
|
|
) : null}
|
|
<ImageCanvasGenerationComposerView
|
|
specToolWrapRef={specToolWrapRef}
|
|
publicationReferenceButtonRef={publicationReferenceButtonRef}
|
|
characterSpecButtonRef={characterSpecButtonRef}
|
|
characterReferenceButtonRef={characterReferenceButtonRef}
|
|
generationReferenceButtonRef={generationReferenceButtonRef}
|
|
iconSpecButtonRef={iconSpecButtonRef}
|
|
isPublicationReferenceMenuOpen={
|
|
generationWorkflow.isPublicationReferenceMenuOpen
|
|
}
|
|
isSpecMenuOpen={generationWorkflow.isSpecMenuOpen}
|
|
isGenerationReferenceMenuOpen={
|
|
generationWorkflow.isGenerationReferenceMenuOpen
|
|
}
|
|
isCharacterSpecMenuOpen={generationWorkflow.isCharacterSpecMenuOpen}
|
|
isCharacterReferenceMenuOpen={
|
|
generationWorkflow.isCharacterReferenceMenuOpen
|
|
}
|
|
isIconSpecMenuOpen={generationWorkflow.isIconSpecMenuOpen}
|
|
isUiDesignSpecMenuOpen={generationWorkflow.isUiDesignSpecMenuOpen}
|
|
isPickingPublicationReferenceFromCanvas={
|
|
generationWorkflow.isPickingPublicationReferenceFromCanvas
|
|
}
|
|
isPickingGenerationReferenceFromCanvas={
|
|
generationWorkflow.isPickingGenerationReferenceFromCanvas
|
|
}
|
|
isPickingQuickEditReferenceFromCanvas={
|
|
generationWorkflow.isPickingQuickEditReferenceFromCanvas
|
|
}
|
|
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}
|
|
cropExpandPanel={generationWorkflow.cropExpandPanel}
|
|
cropExpandSourceLayer={generationWorkflow.cropExpandSourceLayer}
|
|
cropExpandPanelStyle={cropExpandPanelStyle}
|
|
characterAnimationPanel={generationWorkflow.characterAnimationPanel}
|
|
characterAnimationSourceLayer={
|
|
generationWorkflow.characterAnimationSourceLayer
|
|
}
|
|
characterAnimationPanelStyle={characterAnimationPanelStyle}
|
|
characterAnimationPrice={generationWorkflow.characterAnimationPrice}
|
|
setGenerateDialog={setGenerateDialog}
|
|
setQuickEditPanel={generationWorkflow.setQuickEditPanel}
|
|
setCropExpandPanel={generationWorkflow.setCropExpandPanel}
|
|
setCharacterAnimationPanel={
|
|
generationWorkflow.setCharacterAnimationPanel
|
|
}
|
|
setIsCharacterSpecMenuOpen={
|
|
generationWorkflow.setIsCharacterSpecMenuOpen
|
|
}
|
|
setIsCharacterReferenceMenuOpen={
|
|
generationWorkflow.setIsCharacterReferenceMenuOpen
|
|
}
|
|
setIsGenerationReferenceMenuOpen={
|
|
generationWorkflow.setIsGenerationReferenceMenuOpen
|
|
}
|
|
setIsPublicationReferenceMenuOpen={
|
|
generationWorkflow.setIsPublicationReferenceMenuOpen
|
|
}
|
|
setIsIconSpecMenuOpen={generationWorkflow.setIsIconSpecMenuOpen}
|
|
setIsUiDesignSpecMenuOpen={generationWorkflow.setIsUiDesignSpecMenuOpen}
|
|
setIsPickingGenerationReferenceFromCanvas={
|
|
generationWorkflow.setIsPickingGenerationReferenceFromCanvas
|
|
}
|
|
setIsPickingQuickEditReferenceFromCanvas={
|
|
generationWorkflow.setIsPickingQuickEditReferenceFromCanvas
|
|
}
|
|
setIsPickingPublicationReferenceFromCanvas={
|
|
generationWorkflow.setIsPickingPublicationReferenceFromCanvas
|
|
}
|
|
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()}
|
|
onSubmitCropExpand={() => void generationWorkflow.submitCropExpand()}
|
|
onSubmitCharacterAnimation={() =>
|
|
void generationWorkflow.submitCharacterAnimation()
|
|
}
|
|
onUpdateSpecFormValue={generationWorkflow.updateSpecFormValue}
|
|
onUpdateIconDescriptionText={
|
|
generationWorkflow.updateIconDescriptionsText
|
|
}
|
|
onUpdateCharacterAnimationDuration={
|
|
generationWorkflow.updateCharacterAnimationDuration
|
|
}
|
|
onRememberImageModel={generationWorkflow.rememberImageModel}
|
|
onSpecMenuPointerEnter={() => openToolbarOptionMenu('spec')}
|
|
onSpecMenuPointerLeave={() => closeToolbarOptionMenu('spec')}
|
|
/>
|
|
{generationWorkflow.isPublicationMenuOpen
|
|
? renderEditorPortal(
|
|
<PlatformFloatingMenu
|
|
className="image-canvas-editor__spec-menu image-canvas-editor__portal-menu image-canvas-editor__publication-menu"
|
|
label="宣发素材类型"
|
|
placement="top-start"
|
|
style={buildToolbarPortalMenuStyle(
|
|
publicationToolWrapRef.current,
|
|
)}
|
|
onPointerEnter={() => openToolbarOptionMenu('publication')}
|
|
onPointerLeave={() => closeToolbarOptionMenu('publication')}
|
|
onFocus={() => openToolbarOptionMenu('publication')}
|
|
onBlur={() => closeToolbarOptionMenu('publication')}
|
|
>
|
|
{PUBLICATION_MATERIALS_WORKFLOWS.map((workflow) => (
|
|
<PlatformFloatingMenuItem
|
|
key={workflow.id}
|
|
onClick={() =>
|
|
generationWorkflow.openPublicationGenerationDialog(
|
|
workflow.id as PublicationMaterialsWorkflowId,
|
|
)
|
|
}
|
|
>
|
|
{workflow.label}
|
|
</PlatformFloatingMenuItem>
|
|
))}
|
|
</PlatformFloatingMenu>,
|
|
)
|
|
: null}
|
|
{generationWorkflow.isMusicMenuOpen
|
|
? renderEditorPortal(
|
|
<PlatformFloatingMenu
|
|
className="image-canvas-editor__spec-menu image-canvas-editor__portal-menu image-canvas-editor__music-menu"
|
|
label="生成音乐类型"
|
|
placement="top-start"
|
|
style={buildToolbarPortalMenuStyle(musicToolWrapRef.current)}
|
|
onPointerEnter={() => openToolbarOptionMenu('music')}
|
|
onPointerLeave={() => closeToolbarOptionMenu('music')}
|
|
onFocus={() => openToolbarOptionMenu('music')}
|
|
onBlur={() => closeToolbarOptionMenu('music')}
|
|
>
|
|
<PlatformFloatingMenuItem
|
|
onClick={generationWorkflow.openSoundEffectGenerationDialog}
|
|
>
|
|
生成游戏音效
|
|
</PlatformFloatingMenuItem>
|
|
<PlatformFloatingMenuItem
|
|
onClick={generationWorkflow.openBackgroundMusicGenerationDialog}
|
|
>
|
|
生成游戏背景音乐
|
|
</PlatformFloatingMenuItem>
|
|
</PlatformFloatingMenu>,
|
|
)
|
|
: null}
|
|
</>
|
|
);
|
|
|
|
return {
|
|
...generationWorkflow,
|
|
generationComposerStyle,
|
|
generationComposerNode,
|
|
openToolbarOptionMenu,
|
|
closeToolbarOptionMenu,
|
|
switchGenerationTool,
|
|
};
|
|
}
|