diff --git a/apps/web/src/pages/PlaygroundPage.tsx b/apps/web/src/pages/PlaygroundPage.tsx index cd26a68..c59f931 100644 --- a/apps/web/src/pages/PlaygroundPage.tsx +++ b/apps/web/src/pages/PlaygroundPage.tsx @@ -1,27 +1,8 @@ -import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'; -import { - AssistantRuntimeProvider, - ComposerPrimitive, - ErrorPrimitive, - MessagePrimitive, - ThreadPrimitive, - useMessage, - useMessagePartText, - useLocalRuntime, - useThread, - type ChatModelAdapter, - type ThreadMessage, - type ThreadMessageLike, -} from '@assistant-ui/react'; -import { StreamdownTextPrimitive } from '@assistant-ui/react-streamdown'; -import { cjk } from '@streamdown/cjk'; -import { code } from '@streamdown/code'; -import { math } from '@streamdown/math'; -import { mermaid } from '@streamdown/mermaid'; +import { useEffect, useMemo, useRef, useState } from 'react'; import type { GatewayApiKey, GatewayTask, PlatformModel } from '@easyai-ai-gateway/contracts'; -import { Bot, ChevronDown, FileText, Image as ImageIcon, LoaderCircle, MessageSquarePlus, Music2, Paperclip, Plus, Repeat2, Send, Sparkles, Video, X } from 'lucide-react'; +import { ChevronDown, MessageSquarePlus, Send, Sparkles } from 'lucide-react'; import { Badge, Button, Select, Textarea } from '../components/ui'; -import { GatewayApiError, createImageEditTask, createImageGenerationTask, createVideoGenerationTask, pollTaskUntilSettled, streamChatCompletionText, taskIsPending, uploadFileToStorage } from '../api'; +import { createImageEditTask, createImageGenerationTask, createVideoGenerationTask, pollTaskUntilSettled, taskIsPending } from '../api'; import type { PlaygroundMode } from '../types'; import { PlaygroundPromptMentionInput, @@ -40,96 +21,40 @@ import { type MediaGenerationSettings, type MediaModelCapabilities, } from './playground-media'; - -type VideoCreateMode = 'text_to_video' | 'first_last_frame' | 'omni_reference'; +import { + ComposerUploadButton as SharedComposerUploadButton, + mediaUploadAccept as sharedMediaUploadAccept, + mediaUploadAcceptForMode as sharedMediaUploadAcceptForMode, + mediaUploadRequestPayload as sharedMediaUploadRequestPayload, + mediaUploadSummaryMessage as sharedMediaUploadSummaryMessage, + mergeMediaUploadsForMode as sharedMergeMediaUploadsForMode, + normalizeFirstLastFrameUploads as sharedNormalizeFirstLastFrameUploads, + PlaygroundReferencePicker, + swapFirstLastFrameUploads as sharedSwapFirstLastFrameUploads, + uploadPlaygroundFiles as sharedUploadPlaygroundFiles, + UploadAttachmentList as SharedUploadAttachmentList, + allowedMediaUploadKinds as sharedAllowedMediaUploadKinds, + type PlaygroundUpload, + type PlaygroundUploadRole, +} from './playground-upload'; +import { AssistantChatPlayground, clearStoredChatMessages } from './playground-chat'; +import { + ApiKeySelect, + ModeSwitch, + PlaygroundGreeting, + apiKeyNoticeText, + modeOptions, + modelOptionLabel, + placeholderByMode, + quickPrompts, + resolveSelectedApiKeyId, + videoModeOptions, + type ModelOption, + type VideoCreateMode, +} from './playground-shared'; const MEDIA_RUNS_STORAGE_KEY = 'easyai:playground:media-runs:v1'; const MEDIA_RUNS_STORAGE_LIMIT = 50; -const CHAT_MESSAGES_STORAGE_KEY = 'easyai:playground:chat-messages:v1'; -const CHAT_MESSAGES_STORAGE_LIMIT = 100; - -interface StoredChatMessage { - content: string; - createdAt: string; - id: string; - role: 'assistant' | 'user'; -} - -interface ModelOption { - count: number; - label: string; - models: PlatformModel[]; - provider: string; - value: string; -} - -type PlaygroundUploadKind = 'audio' | 'file' | 'image' | 'video'; -type PlaygroundUploadRole = 'first_frame' | 'last_frame'; - -interface PlaygroundUpload { - contentType: string; - id: string; - kind: PlaygroundUploadKind; - name: string; - raw: Record; - role?: PlaygroundUploadRole; - size: number; - url: string; -} - -const modeOptions: Array<{ description: string; icon: ReactNode; label: string; value: PlaygroundMode }> = [ - { value: 'chat', label: '大模型对话', description: '对话、推理、结构化输出', icon: }, - { value: 'image', label: '图像生成', description: '文生图、图像编辑参数预览', icon: }, - { value: 'video', label: '视频生成', description: '图生视频、文生视频任务测试', icon: