import { Check, ImageIcon, Music, Search, Video } from 'lucide-react'; import { type ReactNode, useEffect, useMemo, useState } from 'react'; import { PlatformActionButton } from '../../../packages/shared/src/components/PlatformActionButton'; import { PlatformResourceFilterBar } from '../../../packages/shared/src/components/PlatformResourceFilterBar'; import { PlatformStatusMessage } from '../../../packages/shared/src/components/PlatformStatusMessage'; import { UnifiedModal } from '../common/UnifiedModal'; import type { EditorAsset } from './ImageCanvasEditorTypes'; import { PROJECT_ASSET_PICKER_CATEGORY_OPTIONS, projectAssetMatchesPickerFilter, type ProjectAssetPickerCategory, projectAssetPickerCategory, } from './projectAssetReferencePickerModel'; type ImageCanvasProjectAssetPickerDialogProps = { open: boolean; /** 项目已登记素材;弹窗只负责选择,不改素材本身。 */ assets: readonly EditorAsset[]; /** 打开时的初始选择,取消时原选择保持不变。 */ selectedAssetIds: readonly string[]; onCancel: () => void; onConfirm: (assetIds: string[]) => void; /** * 单选模式:点击即整组替换当前选择,不再渲染「已选」chip 行。 * 默认 `false`,网页端美术画布的参考图多选行为逐字不变。 */ singleSelect?: boolean; /** * 被禁用的素材 id → 用户可见的禁用原因。默认空,即所有素材都可选。 * * 禁用项仍然渲染(不隐藏):隐藏会让用户以为"素材不存在",而真实原因是它不可替换。 */ assetBlockedReasons?: Readonly>; /** * 可选素材 id → 非阻断提示(例如"格式与源素材不同")。默认空,即不显示任何提示。 * * 与 `assetBlockedReasons` 的区别:提示不改变可点性,只把差异说清楚。 */ assetHints?: Readonly>; /** * 素材缩略图渲染器。默认 `undefined` → 沿用 ``。 * * 宿主(如 AGC 资源工作台)没有同步 `src` 时必须传它:AGC 的预览读取走带 scope 的 * 原生调度器 + Blob URL,弹窗内取不到,直接给空 `src` 会挂破图。 */ renderAssetMedia?: (asset: EditorAsset) => ReactNode; /** * 选择对象的中文名词,用于拼弹窗标题与可访问名称。默认「参考图」, * 即网页端美术画布的现有文案逐字不变。 */ selectionNoun?: string; /** * 宿主的失败原因(例如后端拒绝了这次替换)。默认 `undefined` → 不渲染。 * * 用于在弹窗内说明"为什么这次操作没成功",而不是静默关闭弹窗让用户以为成功了。 */ errorMessage?: string | null; }; function assetIcon(asset: EditorAsset) { const category = projectAssetPickerCategory(asset); if (category === 'video') { return