import { useEffect, useMemo, useRef, useState, type KeyboardEvent, } from 'react'; import { Bot, Check, Clock3, Cloud, Film, ImageIcon, Loader2, Plus, RefreshCw, Send, Sparkles, WandSparkles, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { IMAGE_WORKSPACE_CREATE_PROJECT_EVENT, resolveImageWorkspaceAssetUrl, } from '@/lib/image-workspace'; import { cn } from '@/lib/utils'; import { useImageWorkspaceStore } from '@/stores/image-workspace'; import { useAuthStore } from '@/stores/auth'; import type { DesignAsset, DesignGenerationQuote, DesignGenerationTask, DesignMessage, DesignTaskStatus, } from '../../../shared/image-workspace'; const ACTIVE_TASK_STATUSES = new Set(['queued', 'running']); function taskStatusLabel(status: DesignTaskStatus): string { if (status === 'queued') return '排队中'; if (status === 'running') return '生成中'; if (status === 'succeeded') return '已完成'; if (status === 'cancelled') return '已取消'; return '失败'; } function mediumLabel(medium: 'image' | 'video'): string { return medium === 'video' ? '视频' : '图片'; } function activeQuote(messages: DesignMessage[]): DesignGenerationQuote | null { for (let index = messages.length - 1; index >= 0; index -= 1) { const quote = messages[index].generationQuote; if (quote?.status === 'active') return quote; } return null; } type RenderedDesignMessage = DesignMessage & { streaming?: boolean }; function AssetPreview({ asset }: { asset: DesignAsset }) { const [url, setUrl] = useState(null); useEffect(() => { let cancelled = false; void resolveImageWorkspaceAssetUrl(asset.contentPath).then((nextUrl) => { if (!cancelled) setUrl(nextUrl); }); return () => { cancelled = true; }; }, [asset.contentPath]); if (!url) { return (
); } if (asset.mimeType.startsWith('video/')) { return (