diff --git a/frontend-web/src/roundtable-planning/components/Step3SummaryMode.tsx b/frontend-web/src/roundtable-planning/components/Step3SummaryMode.tsx index 349f0045..739eae69 100644 --- a/frontend-web/src/roundtable-planning/components/Step3SummaryMode.tsx +++ b/frontend-web/src/roundtable-planning/components/Step3SummaryMode.tsx @@ -21,6 +21,8 @@ import { Loader2, PanelLeftClose, PanelLeftOpen, + Paperclip, + Pause, RefreshCw, SendHorizontal, Sparkles, @@ -113,11 +115,12 @@ export function Step3SummaryMode({ dialogues, seatStubMessages, activeArtifactThreadId, - canAsk, + busy, start, hydrateSaved, redraw, ask, + pause, abort, openArtifactFromStep, openArtifactPreview, @@ -303,9 +306,10 @@ export function Step3SummaryMode({ phase={phase} dialogues={dialogues} error={error} - canAsk={canAsk} + busy={busy} onRetry={redraw} onAsk={ask} + onPause={pause} onArtifactStepClick={openArtifactFromStep} onArtifactPreview={openArtifactPreview} selectedModel={selectedModel} @@ -422,9 +426,10 @@ function AgentProgressPanel({ phase, dialogues, error, - canAsk, + busy, onRetry, onAsk, + onPause, onArtifactStepClick, onArtifactPreview, selectedModel, @@ -435,9 +440,10 @@ function AgentProgressPanel({ phase: ReturnType["phase"]; dialogues: ReturnType["dialogues"]; error: string | null; - canAsk: boolean; + busy: boolean; onRetry: () => void; - onAsk: (message: string) => void; + onAsk: (message: string, files?: File[]) => void; + onPause: () => void; onArtifactStepClick: ReturnType["openArtifactFromStep"]; onArtifactPreview: ReturnType["openArtifactPreview"]; selectedModel: string; @@ -445,17 +451,33 @@ function AgentProgressPanel({ modelsLoading: boolean; setSelectedModel: (model: string) => void; }) { - const busy = phase === "creating" || phase === "streaming"; const scrollRef = useRef(null); const { atBottom, scrollToBottom } = useAutoScroll(scrollRef, [dialogues]); const [askText, setAskText] = useState(""); + // 输入框携带的本地附件(点发送时连同指令一起上传到总结线程)。 + const [askFiles, setAskFiles] = useState([]); + const fileInputRef = useRef(null); + + const pickFiles = (e: React.ChangeEvent) => { + const picked = Array.from(e.target.files ?? []); + if (picked.length > 0) { + setAskFiles((prev) => { + const seen = new Set(prev.map((f) => `${f.name}:${f.size}`)); + return [...prev, ...picked.filter((f) => !seen.has(`${f.name}:${f.size}`))]; + }); + } + e.target.value = ""; + }; + const removeFile = (idx: number) => setAskFiles((prev) => prev.filter((_, i) => i !== idx)); const handleAskSubmit = (e: FormEvent) => { e.preventDefault(); const text = askText.trim(); - if (!text || !canAsk) return; + const hasFiles = askFiles.length > 0; + if (!text && !hasFiles) return; // 允许「随时」发送(含生成中);空内容不发 setAskText(""); - onAsk(text); + setAskFiles([]); + onAsk(text, hasFiles ? askFiles : undefined); }; return ( @@ -551,6 +573,31 @@ function AgentProgressPanel({ {!atBottom && }
+ {/* 已选附件预览(发送时随指令上传到总结线程,供智能体读取) */} + {askFiles.length > 0 && ( +
+ {askFiles.map((file, idx) => ( + + + + {file.name} + + {formatBytes(file.size)} + + + ))} +
+ )}