Files
Genarrative/src/components/image-editor/ImageCanvasSelectedLayerToolbarView.tsx
T
kdletters 44f47e5db1
Project CI / Repository checks (push) Successful in 1m0s
Project CI / Frontend tests (push) Successful in 2m5s
Project CI / Native shell tests (push) Successful in 2m40s
Project CI / Backend tests (push) Successful in 3m16s
修复图集素材标签持久化与拆分校验
项目资源复用增加素材类型匹配,避免同源旧类型资源回流
标签保存期间禁用拆分并在服务端确认后回写资源引用
补齐乱序请求、快照刷新、失败回滚和撤销历史保护
新增前后端回归测试并同步图集入口设计文档
2026-07-29 11:06:56 +08:00

206 lines
6.5 KiB
TypeScript

import {
Crop,
Download,
ImageOff,
Loader2,
PersonStanding,
Scissors,
Sparkles,
WandSparkles,
} from 'lucide-react';
import type { CSSProperties } from 'react';
import { PlatformIconButton } from '../common/PlatformIconButton';
import { EditorIconButton } from './ImageCanvasEditorPrimitives';
import type { CanvasLayer } from './ImageCanvasEditorTypes';
import { isQuickEditUnsupportedAssetKind } from './ImageCanvasGenerationModel';
type ImageCanvasSelectedLayerToolbarViewProps = {
selectedLayer: CanvasLayer | null;
selectedToolbarStyle: CSSProperties | null;
onOpenQuickEditPanel: (layer: CanvasLayer) => void;
onOpenRedrawPanel: (layer: CanvasLayer) => void;
onOpenCropExpandPanel: (layer: CanvasLayer) => void;
onRemoveBackground: (layer: CanvasLayer) => void;
isSplittingIconSpritesheet: boolean;
isPersistingAssetKind: boolean;
onSplitIconSpritesheet: (layer: CanvasLayer) => void;
onExtractUiDesignAssets: (layer: CanvasLayer) => void;
onOpenCharacterAnimationPanel: (layer: CanvasLayer) => void;
onDownloadLayer: (layer: CanvasLayer) => void;
};
export function ImageCanvasSelectedLayerToolbarView({
selectedLayer,
selectedToolbarStyle,
onOpenQuickEditPanel,
onOpenRedrawPanel,
onOpenCropExpandPanel,
onRemoveBackground,
isSplittingIconSpritesheet,
isPersistingAssetKind,
onSplitIconSpritesheet,
onExtractUiDesignAssets,
onOpenCharacterAnimationPanel,
onDownloadLayer,
}: ImageCanvasSelectedLayerToolbarViewProps) {
if (!selectedLayer || !selectedToolbarStyle) {
return null;
}
if (selectedLayer.mediaType === 'audio') {
return (
<div
className="image-canvas-editor__floating-toolbar"
style={selectedToolbarStyle}
role="toolbar"
aria-label="素材工具栏"
onPointerDown={(event) => event.stopPropagation()}
>
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label="改造"
title="改造"
icon={<WandSparkles className="h-4 w-4" />}
onClick={() => onOpenRedrawPanel(selectedLayer)}
>
<span>改造</span>
</PlatformIconButton>
<EditorIconButton
label="下载按钮"
title="下载按钮"
icon={Download}
onClick={() => onDownloadLayer(selectedLayer)}
/>
</div>
);
}
const canRasterEdit =
selectedLayer.mediaType !== 'video' &&
selectedLayer.mediaType !== 'image-sequence' &&
selectedLayer.assetKind !== 'character-animation';
return (
<div
className="image-canvas-editor__floating-toolbar"
style={selectedToolbarStyle}
role="toolbar"
aria-label="图片工具栏"
onPointerDown={(event) => event.stopPropagation()}
>
{!isQuickEditUnsupportedAssetKind(selectedLayer) ? (
<>
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label="快速编辑"
title="快速编辑"
icon={<Sparkles className="h-4 w-4" />}
onClick={() => onOpenQuickEditPanel(selectedLayer)}
>
<span>快速编辑</span>
</PlatformIconButton>
<span
aria-hidden="true"
className="image-canvas-editor__floating-toolbar-divider"
/>
</>
) : null}
{canRasterEdit ? (
<>
<EditorIconButton
label="裁扩按钮"
title="裁扩按钮"
icon={Crop}
onClick={() => onOpenCropExpandPanel(selectedLayer)}
/>
<EditorIconButton
label="去除背景按钮"
title="去除背景按钮"
icon={ImageOff}
onClick={() => onRemoveBackground(selectedLayer)}
/>
</>
) : null}
{selectedLayer.assetKind === 'icon-spritesheet' ? (
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label={
isPersistingAssetKind
? '素材类型保存中'
: isSplittingIconSpritesheet
? '拆图中'
: '拆分图集'
}
title={
isPersistingAssetKind
? '素材类型保存中'
: isSplittingIconSpritesheet
? '拆图中'
: '拆分图集'
}
icon={
isPersistingAssetKind || isSplittingIconSpritesheet ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Scissors className="h-4 w-4" />
)
}
disabled={isPersistingAssetKind || isSplittingIconSpritesheet}
aria-busy={isPersistingAssetKind || isSplittingIconSpritesheet}
onClick={() => onSplitIconSpritesheet(selectedLayer)}
>
<span>
{isPersistingAssetKind
? '保存中'
: isSplittingIconSpritesheet
? '拆图中'
: '拆分图集'}
</span>
</PlatformIconButton>
) : null}
{selectedLayer.assetKind === 'ui-design' ? (
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label="提取素材"
title="提取素材"
icon={<Scissors className="h-4 w-4" />}
onClick={() => onExtractUiDesignAssets(selectedLayer)}
>
<span>提取素材</span>
</PlatformIconButton>
) : null}
{selectedLayer.assetKind === 'character' ? (
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label="生成动画"
title="生成动画"
icon={<PersonStanding className="h-4 w-4" />}
onClick={() => onOpenCharacterAnimationPanel(selectedLayer)}
>
<span>生成动画</span>
</PlatformIconButton>
) : null}
<span
aria-hidden="true"
className="image-canvas-editor__floating-toolbar-divider"
/>
<PlatformIconButton
className="image-canvas-editor__floating-toolbar-text-button"
label="改造"
title="改造"
icon={<WandSparkles className="h-4 w-4" />}
onClick={() => onOpenRedrawPanel(selectedLayer)}
>
<span>改造</span>
</PlatformIconButton>
<EditorIconButton
label="下载按钮"
title="下载按钮"
icon={Download}
onClick={() => onDownloadLayer(selectedLayer)}
/>
</div>
);
}