import { useEffect, useMemo, useState } from 'react' import { ActivityCardView, Collapsible, activitySummary, groupActivityCards, pendingCount, type ActivityCard } from '@ui' import { store, useStore } from '../store' import { docBridge } from './Workbench' import type { WorkCardAction } from '../contracts' import { WorkExecutionRecord } from './WorkExecutionRecord' /** 对话与辅助工作栏共享对象、动作和唯一文件预览,不另持有异步状态。 */ export function ConversationWork() { const cards = useStore(s => s.cards) const conversationId = useStore(s => s.currentConvId) const projectId = useStore(s => s.currentProjectId) const scratch = useStore(s => !!s.tasks.find(t => t.id === s.currentConvId)?.scratch) const messages = useStore(s => s.messages) const actionStates = useStore(s => s.cardActionStates) const rightCollapsed = useStore(s => s.ui.rightCollapsed) const rightTab = useStore(s => s.ui.rightTab) const record = useStore(s => s.workRecord) const detailHere = rightCollapsed || rightTab !== 'work' const [summaryOpen, setSummaryOpen] = useState(detailHere) useEffect(() => setSummaryOpen(detailHere), [detailHere]) const { current, required, focused, history } = useMemo(() => { const current = cards.filter(card => card.status !== 'dismissed' && card.conversationId === conversationId && card.projectId === projectId) const required: ActivityCard[] = [] const remaining: ActivityCard[] = [] for (const card of current) { // A confirmation already visible at its user message has one main action // path in the conversation; assistant-anchored cards still need this path. if (card.kind === 'confirm' && card.anchorMessageId && messages.some(message => message.role === 'user' && message.id === card.anchorMessageId)) continue const summary = activitySummary(card) const taskNotice = card.task?.limitations?.length || card.task?.phase.id === 'correction' || card.task?.findings?.some(f => f.status === 'active' && (f.kind === 'needs_user' || f.corrects)) if (taskNotice || card.status === 'action_required' || card.work?.decision?.state === 'open' || summary.failed || (card.status === 'done' && summary.remaining)) required.push(card) else remaining.push(card) } // Preserve the host's newest-first order for completed results and history. const results = groupActivityCards(remaining).find(group => group.key === 'results')?.cards ?? [] const focused = remaining.find(card => card.pinned && card.status === 'active') ?? remaining.find(card => card.status === 'active') ?? results[0] return { current, required, focused, history: remaining.filter(card => card !== focused).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)) } }, [cards, messages, conversationId, projectId]) if (!detailHere) return null if (record) return