import { useState } from 'react'; import type { ExecuteMatch3DActionRequest, Match3DAgentSessionSnapshot, Match3DAnchorItemResponse, SendMatch3DMessageRequest, } from '../../../packages/shared/src/contracts/match3dAgent'; import { buildCreationAgentChatMessage, createCreationAgentChatQuickActions, createCreationAgentClientMessageId, resolveCreationAgentQuickActionMessage, } from '../../services/creation-agent'; import { type CreationAgentAnchorView, type CreationAgentSessionView, type CreationAgentTheme, CreationAgentWorkspace, } from '../creation-agent'; type Match3DAgentWorkspaceProps = { session: Match3DAgentSessionSnapshot | null; streamingReplyText?: string; isStreamingReply?: boolean; isBusy?: boolean; error?: string | null; onBack: () => void; onSubmitMessage: (payload: SendMatch3DMessageRequest) => void; onExecuteAction: (payload: ExecuteMatch3DActionRequest) => void; }; type Match3DReferenceImageState = { src: string; label: string; }; const MATCH3D_AGENT_THEME: CreationAgentTheme = { accentTextClass: 'text-lime-100/86', accentBgClass: 'bg-lime-200', accentButtonClass: 'bg-lime-200 shadow-emerald-950/20', userBubbleClass: 'bg-emerald-600 text-white', heroClass: 'border border-lime-100/18 bg-[radial-gradient(circle_at_top_left,rgba(190,242,100,0.24),transparent_34%),radial-gradient(circle_at_bottom_right,rgba(251,146,60,0.2),transparent_32%),linear-gradient(135deg,rgba(20,83,45,0.96),rgba(39,39,42,0.96))]', anchorGridClass: 'grid gap-2 sm:grid-cols-3', }; const MATCH3D_QUICK_ACTIONS = [ ...createCreationAgentChatQuickActions(), { key: 'match3d-auto-config', label: '自动配置', }, ]; function readMatch3DReferenceImageAsDataUrl(file: File) { return new Promise((resolve, reject) => { if (!file.type.startsWith('image/')) { reject(new Error('请选择图片文件。')); return; } const reader = new FileReader(); reader.onerror = () => reject(new Error('参考图读取失败,请重试。')); reader.onload = () => resolve(String(reader.result || '')); reader.readAsDataURL(file); }); } function mapMatch3DAnchor( anchor: Match3DAnchorItemResponse, ): CreationAgentAnchorView { return { key: anchor.key, label: anchor.label, value: anchor.value, status: anchor.status, }; } function mapMatch3DSession( session: Match3DAgentSessionSnapshot, ): CreationAgentSessionView { // 中文注释:抓大鹅 F1 只展示聊天与配置锚点,草稿结果交给后续结果页承接。 const chatMessages = session.messages.filter( (message) => message.kind === 'chat' || message.kind === 'summary' || message.kind === 'warning', ); return { sessionId: session.sessionId, title: null, assistantSummary: null, currentTurn: session.currentTurn, progressPercent: session.progressPercent, anchors: [ session.anchorPack.theme, session.anchorPack.clearCount, session.anchorPack.difficulty, ].map(mapMatch3DAnchor), messages: chatMessages, recommendedReplies: [], }; } function buildMatch3DChatPayload({ text, quickFillRequested = false, referenceImageSrc, }: { text: string; quickFillRequested?: boolean; referenceImageSrc?: string | null; }) { return buildCreationAgentChatMessage<{ referenceImageSrc?: string | null; }>({ clientMessageId: createCreationAgentClientMessageId('match3d'), text, quickFillRequested, extraPayload: { referenceImageSrc: referenceImageSrc || null, }, }); } export function Match3DAgentWorkspace({ session, streamingReplyText = '', isStreamingReply = false, isBusy = false, error = null, onBack, onSubmitMessage, onExecuteAction, }: Match3DAgentWorkspaceProps) { const [referenceImage, setReferenceImage] = useState(null); const [referenceImageError, setReferenceImageError] = useState( null, ); return ( { onSubmitMessage( buildMatch3DChatPayload({ text, referenceImageSrc: referenceImage?.src ?? null, }), ); }} onPrimaryAction={() => { onExecuteAction({ action: 'match3d_compile_draft' }); }} onQuickAction={(action) => { const quickActionMessage = action.key === 'match3d-auto-config' ? { text: '自动配置', quickFillRequested: true, } : resolveCreationAgentQuickActionMessage( action.key, '请总结一下当前抓大鹅设定。', ); onSubmitMessage( buildMatch3DChatPayload({ ...quickActionMessage, referenceImageSrc: referenceImage?.src ?? null, }), ); }} onReferenceImageChange={async (file) => { try { const dataUrl = await readMatch3DReferenceImageAsDataUrl(file); setReferenceImage({ src: dataUrl, label: file.name.trim() || '本地参考图', }); setReferenceImageError(null); } catch (caughtError) { setReferenceImageError( caughtError instanceof Error ? caughtError.message : '参考图读取失败,请重试。', ); } }} onClearReferenceImage={() => { setReferenceImage(null); setReferenceImageError(null); }} /> ); } export default Match3DAgentWorkspace;