import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ApiError, buildMindSpaceConversationPackageManifestDownloadUrl, getMindSpaceConversationPackage, } from '../api/client'; import type { MindSpaceConversationArtifact, MindSpaceConversationPackage, } from '../types'; function packageArtifactLabel(kind: MindSpaceConversationArtifact['kind']) { switch (kind) { case 'input_image': return '输入图片'; case 'input_file': return '输入文件'; case 'generated_image': return '生成图片'; case 'generated_file': return '生成文件'; case 'page': return '页面草稿'; case 'public_html': return '公开页面'; case 'long_image': return '长图'; case 'thumbnail': return '缩略图'; case 'docx': return 'Word 文档'; case 'pdf': return 'PDF'; default: return '文件'; } } function formatPackageArtifactSize(sizeBytes?: number) { if (!Number.isFinite(sizeBytes)) return null; const bytes = Number(sizeBytes); if (bytes < 1024) return `${bytes}B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)}KB`; return `${(bytes / 1024 / 1024).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)}MB`; } function groupPackageArtifacts(artifacts: MindSpaceConversationArtifact[]) { const groups = new Map(); artifacts.forEach((artifact) => { const label = packageArtifactLabel(artifact.kind); groups.set(label, [...(groups.get(label) ?? []), artifact]); }); return Array.from(groups.entries()); } function resolveConversationPackageName( pkg?: MindSpaceConversationPackage | null, sessionName?: string | null, ) { return pkg?.displayFolderName || pkg?.title || sessionName || '本次对话文件夹'; } export function ConversationPackagePanel({ sessionId, sessionName, open, onClose, }: { sessionId: string | null; sessionName?: string | null; open: boolean; onClose: () => void; }) { const [packageLoading, setPackageLoading] = useState(false); const [conversationPackage, setConversationPackage] = useState(null); const [packageError, setPackageError] = useState(null); const [packageFilter, setPackageFilter] = useState('all'); const [packageMessageFilter, setPackageMessageFilter] = useState('all'); const packageCacheRef = useRef(new Map()); useEffect(() => { const cachedPackage = sessionId ? packageCacheRef.current.get(sessionId) ?? null : null; setPackageLoading(false); setConversationPackage(cachedPackage); setPackageError(null); setPackageFilter('all'); setPackageMessageFilter('all'); }, [sessionId]); const loadConversationPackage = useCallback(async () => { if (!sessionId) return; const cachedPackage = packageCacheRef.current.get(sessionId) ?? null; setPackageLoading(true); setPackageError(null); try { const data = await getMindSpaceConversationPackage(sessionId); packageCacheRef.current.set(sessionId, data); setConversationPackage(data); setPackageFilter((current) => current === 'all' || data.artifacts.some((artifact) => artifact.kind === current) ? current : 'all', ); setPackageMessageFilter((current) => current === 'all' || (data.messages ?? []).some((message) => message.messageId === current) ? current : 'all', ); } catch (err) { if (!cachedPackage) setConversationPackage(null); if (err instanceof ApiError && err.status === 404) { setPackageError('这个对话还没有形成文件夹内容'); } else { setPackageError(err instanceof Error ? err.message : '对话文件夹加载失败'); } } finally { setPackageLoading(false); } }, [sessionId]); useEffect(() => { if (open) void loadConversationPackage(); }, [loadConversationPackage, open]); const packageArtifacts = conversationPackage?.artifacts ?? []; const conversationPackageName = resolveConversationPackageName(conversationPackage, sessionName ?? null); const packageFilterOptions = Array.from( packageArtifacts.reduce((counts, artifact) => { counts.set(artifact.kind, (counts.get(artifact.kind) ?? 0) + 1); return counts; }, new Map()), ).sort(([left], [right]) => packageArtifactLabel(left).localeCompare(packageArtifactLabel(right), 'zh-CN')); const packageMessageOptions = (conversationPackage?.messages ?? []) .filter((message) => message.artifactIds.length > 0) .map((message, index) => ({ id: message.messageId, label: `消息 ${index + 1}`, count: message.artifactIds.length, artifactIds: new Set(message.artifactIds), })); const selectedMessageArtifacts = packageMessageFilter === 'all' ? null : packageMessageOptions.find((message) => message.id === packageMessageFilter)?.artifactIds ?? null; const filteredPackageArtifacts = packageArtifacts.filter((artifact) => { const kindMatched = packageFilter === 'all' || artifact.kind === packageFilter; const messageMatched = !selectedMessageArtifacts || selectedMessageArtifacts.has(artifact.artifactId); return kindMatched && messageMatched; }); const packageGroups = useMemo(() => groupPackageArtifacts(filteredPackageArtifacts), [filteredPackageArtifacts]); if (!open) return null; return (
event.stopPropagation()} >

MindSpace 对话包

{conversationPackageName}

{packageArtifacts.length} 个项目 文件夹名:{conversationPackageName}
{packageLoading &&
正在整理这个对话里的文件…
} {!packageLoading && packageError && (
对话包暂时打不开 {packageError}
)} {!packageLoading && !packageError && conversationPackage && packageArtifacts.length === 0 && (
这个对话还没有文件 发送图片、保存页面、生成公开页或下载文档后,这里会自动聚合成一个对话文件夹。
)} {!packageLoading && !packageError && conversationPackage && packageArtifacts.length > 0 && ( <>
{packageFilterOptions.map(([kind, count]) => ( ))}
{packageMessageOptions.length > 0 && (
按消息 {packageMessageOptions.map((message) => ( ))}
)} {filteredPackageArtifacts.length === 0 ? (
当前筛选没有项目 切换到“全部”或“全部消息”可以查看当前对话包里的其它产物。
) : ( )} )}
{sessionId && conversationPackage && ( 下载清单 )}
); }