代码修改

This commit is contained in:
董宏凯 committed 2026-05-27 15:30:51 +08:00
1 parent 8f93606c81
commit ff90cac755
2 files changed
+432 -143

No files matched your search

@@ -2,7 +2,7 @@
* Step 2 主面板 —— 多智能体圆桌研讨。
*
* 包含 4 块:
* 1) 顶部 Headline + 挂起/恢复按钮
* 1) 顶部 Headline + 推理模型下拉 + 挂起/恢复按钮
* 2) Persona 抽屉(点击席位头像后展开,见 `PersonaDrawer.tsx`)
* 3) 对话流(初始化占位 + 气泡列表 + ChainOfThought 折叠卡 + markdown)
* 4) 底部澄清条 + 用户干预输入 + 「达成共识」入口
@@ -68,6 +68,7 @@ export interface Step2PanelProps {
selectedModel: string;
availableModels: ModelOption[];
modelsLoading: boolean;
setSelectedModel: React.Dispatch<React.SetStateAction<string>>;
// ─── Setters ──────────────────────────────────────────────────
setIsRoundTableRunning: React.Dispatch<React.SetStateAction<boolean>>;
setClarificationDraft: React.Dispatch<React.SetStateAction<string>>;
@@ -100,6 +101,7 @@ export function Step2Panel(props: Step2PanelProps) {
selectedModel,
availableModels,
modelsLoading,
setSelectedModel,
setIsRoundTableRunning,
setClarificationDraft,
setCustomAgentInput,
@@ -129,7 +131,32 @@ export function Step2Panel(props: Step2PanelProps) {
</p>
</div>
</div>
<div className="flex items-center gap-2.5">
<div className="flex items-center gap-2.5 shrink-0">
<select
id="step2-global-model-select"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
disabled={modelsLoading || availableModels.length === 0}
title={
modelsLoading
? "正在加载模型列表…"
: availableModels.length === 0
? "未在 config.yaml 中配置任何模型"
: "切换全局推理模型"
}
className="max-w-[220px] bg-slate-50/50 border border-slate-200 rounded-xl px-3 py-1.5 text-xs font-bold text-slate-700 cursor-pointer focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-secondary dark:text-foreground dark:border-border"
>
{availableModels.length === 0 && (
<option value="">{modelsLoading ? "加载中…" : "未配置模型"}</option>
)}
{availableModels.map((m) => (
<option key={m.name} value={m.name}>
{m.display_name && m.display_name !== m.name
? `${m.display_name} (${m.name})`
: m.name}
</option>
))}
</select>
{isRoundTableRunning && isOrchestrating ? (
<button
onClick={() => {
@@ -3,9 +3,9 @@
*
* 重构后的主文件只负责"组合 + 编排":
* - 顶栏(步骤切换 + 草稿菜单 + 新建任务 + 设置)
* - 左侧栏(任务概览 + 角色面板 + 人工参与)
* - 中间视区:Step1Panel / Step2Panel / Step3Panel(`currentStep` 切换)
* - 右侧栏(推理模型 / 当前意图摘要 / 澄清记录)
* - 左侧栏:Step1/2 为任务概览 + 角色 + 人工参与;Step2 侧栏可整体收起为图标条,含可收起的意图理解
* - 中间视区:Step1Panel / Step2Panel / Step3Panel(`currentStep` 切换);Step2 顶栏含推理模型下拉
* - 右侧栏:仅 Step1(推理模型 / 当前意图摘要 / 澄清记录);Step2/3 隐藏以让中间区自适应
* - 推荐弹窗 + Toast
*
* 业务状态全部下沉到 hook:
@@ -27,6 +27,10 @@ import {
Check,
CheckCircle,
ChevronRight,
ChevronDown,
ChevronUp,
PanelLeftClose,
PanelLeftOpen,
Clipboard,
Clock,
Cpu,
@@ -423,6 +427,7 @@ export default function RoundtablePlanningPage() {
agentNameToRole={step2.agentNameToRole}
selectedAgents={step2.selectedAgents}
budgetLimit={step2.budgetLimit}
intentReady={step1.intentReady}
/>
{/* MIDDLE VIEWPORT */}
@@ -472,6 +477,7 @@ export default function RoundtablePlanningPage() {
selectedModel={selectedModel}
availableModels={availableModels}
modelsLoading={modelsLoading}
setSelectedModel={setSelectedModel}
setIsRoundTableRunning={step2.setIsRoundTableRunning}
setClarificationDraft={step2.setClarificationDraft}
setCustomAgentInput={step2.setCustomAgentInput}
@@ -787,6 +793,7 @@ interface LeftSidebarProps {
>;
selectedAgents: SelectedAgent[];
budgetLimit: number;
intentReady: ReturnType<typeof useStep1Intent>["intentReady"];
}
function LeftSidebar({
@@ -795,12 +802,31 @@ function LeftSidebar({
agentNameToRole,
selectedAgents,
budgetLimit: _budgetLimit,
intentReady,
}: LeftSidebarProps) {
const [sidebarExpanded, setSidebarExpanded] = useState(true);
const isStep2 = currentStep === 2;
return (
<section
id="left-sidebar"
className="w-[280px] shrink-0 flex flex-col gap-4 overflow-y-auto pr-1"
className={cn(
"shrink-0 flex flex-col gap-3 overflow-y-auto pr-1 transition-[width] duration-200 ease-out",
isStep2 && !sidebarExpanded ? "w-[56px]" : "w-[280px]",
)}
>
{isStep2 && !sidebarExpanded && (
<button
type="button"
onClick={() => setSidebarExpanded(true)}
title="展开侧栏"
aria-expanded={false}
className="w-full shrink-0 flex h-10 items-center justify-center rounded-2xl border border-slate-200/50 bg-white text-slate-500 hover:text-primary hover:border-primary/30 transition-colors shadow-[0_2px_8px_rgba(0,0,0,0.01)]"
>
<PanelLeftOpen className="h-4 w-4" />
</button>
)}
{currentStep === 3 ? (
<>
{/* Step 3 时只保留「参与与结论贡献」面板;原文件里其它两个 hidden 块未启用,
@@ -849,41 +875,67 @@ function LeftSidebar({
</div>
</div>
</>
) : (
<>
{/* 任务概览 */}
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Clipboard className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">任务概览</h2>
</div>
<div className="space-y-3">
<div>
<span className="text-[10px] uppercase tracking-wider text-slate-400 font-bold block mb-1">
当前节点
</span>
<div className="flex items-center justify-between gap-2.5">
<span className="text-sm font-bold text-slate-800">初始任务</span>
<span
className={`text-[10px] font-bold px-2 py-0.5 rounded-md border shrink-0 ${
currentStep === 1
? "text-[#ff9500] bg-orange-50 border-orange-200/50"
: currentStep === 2
? "text-[var(--brand-900)] bg-[var(--brand-200)] border-red-200/50"
: "text-emerald-600 bg-emerald-50 border-emerald-200/50"
}`}
>
{currentStep === 1 ? "待澄清" : currentStep === 2 ? "研讨中" : "已规划交付"}
</span>
</div>
<p className="text-xs text-slate-500 mt-2.5 leading-relaxed bg-slate-50 border border-slate-100 p-2 rounded-lg">
围绕某复杂任务制定规划方案
</p>
) : currentStep === 2 ? (
sidebarExpanded ? (
<>
<IntentSummaryCard
intentReady={intentReady}
title="意图理解"
collapsible
sidebarToggle={{
expanded: sidebarExpanded,
onToggle: () => setSidebarExpanded((v) => !v),
}}
/>
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex-1 flex flex-col min-h-[240px]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-3">
<Users className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">参与角色</h2>
</div>
<div className="space-y-2.5 overflow-y-auto flex-1 pr-1 custom-scrollbar">
{roles.map((role) => (
<RoleRow key={role.id} role={role} agentNameToRole={agentNameToRole} />
))}
</div>
</div>
</div>
{/* 参与角色 */}
<HumanParticipationCard />
<TaskOverviewCard currentStep={currentStep} />
</>
) : (
<>
<CollapsedSidebarSection title="意图理解">
<IntentSectionIcon className="h-5 w-5" />
</CollapsedSidebarSection>
<CollapsedSidebarSection title="参与角色" className="flex-1 min-h-[120px]">
<div className="flex flex-col items-center gap-2 overflow-y-auto max-h-[320px] custom-scrollbar">
{roles.map((role) => (
<RoleAvatarBadge
key={role.id}
role={role}
agentNameToRole={agentNameToRole}
size="h-8 w-8"
showSpeakingAnimation
/>
))}
</div>
</CollapsedSidebarSection>
<CollapsedSidebarSection title="人工参与">
<Man1Avatar size="h-8 w-8" />
</CollapsedSidebarSection>
<CollapsedSidebarSection title="任务概览">
<Clipboard className="h-5 w-5 text-primary" strokeWidth={2.5} />
</CollapsedSidebarSection>
</>
)
) : (
<>
<TaskOverviewCard currentStep={currentStep} />
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex-1 flex flex-col min-h-[300px]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-3">
<Users className="w-4 h-4 text-primary" strokeWidth={2.5} />
@@ -896,32 +948,317 @@ function LeftSidebar({
</div>
</div>
{/* 人工参与 */}
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Activity className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">人工参与</h2>
</div>
<div className="flex items-center justify-between p-2 rounded-xl bg-slate-50 border border-slate-100">
<div className="flex items-center gap-2.5">
<Man1Avatar size="h-7 w-7" />
<span className="text-xs font-bold text-slate-800">用户</span>
</div>
<div className="flex items-center gap-1.5">
<span className="flex h-1.5 w-1.5 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-500"></span>
</span>
<span className="text-[10px] font-bold text-emerald-600">在线</span>
</div>
</div>
</div>
<HumanParticipationCard />
</>
)}
</section>
);
}
function TaskOverviewCard({ currentStep }: { currentStep: number }) {
return (
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Clipboard className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">任务概览</h2>
</div>
<div className="space-y-3">
<div>
<span className="text-[10px] uppercase tracking-wider text-slate-400 font-bold block mb-1">
当前节点
</span>
<div className="flex items-center justify-between gap-2.5">
<span className="text-sm font-bold text-slate-800">初始任务</span>
<span
className={`text-[10px] font-bold px-2 py-0.5 rounded-md border shrink-0 ${
currentStep === 1
? "text-[#ff9500] bg-orange-50 border-orange-200/50"
: currentStep === 2
? "text-[var(--brand-900)] bg-[var(--brand-200)] border-red-200/50"
: "text-emerald-600 bg-emerald-50 border-emerald-200/50"
}`}
>
{currentStep === 1 ? "待澄清" : currentStep === 2 ? "研讨中" : "已规划交付"}
</span>
</div>
<p className="text-xs text-slate-500 mt-2.5 leading-relaxed bg-slate-50 border border-slate-100 p-2 rounded-lg">
围绕某复杂任务制定规划方案
</p>
</div>
</div>
</div>
);
}
function HumanParticipationCard() {
return (
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Activity className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">人工参与</h2>
</div>
<div className="flex items-center justify-between p-2 rounded-xl bg-slate-50 border border-slate-100">
<div className="flex items-center gap-2.5">
<Man1Avatar size="h-7 w-7" />
<span className="text-xs font-bold text-slate-800">用户</span>
</div>
<div className="flex items-center gap-1.5">
<span className="flex h-1.5 w-1.5 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-500"></span>
</span>
<span className="text-[10px] font-bold text-emerald-600">在线</span>
</div>
</div>
</div>
);
}
interface ModelSelectorCardProps {
selectedModel: string;
setSelectedModel: React.Dispatch<React.SetStateAction<string>>;
availableModels: ReadonlyArray<{ name: string; display_name?: string }>;
modelsLoading: boolean;
}
function ModelSelectorCard({
selectedModel,
setSelectedModel,
availableModels,
modelsLoading,
}: ModelSelectorCardProps) {
return (
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Cpu className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">推理模型</h2>
</div>
<select
id="global-model-select"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
disabled={modelsLoading || availableModels.length === 0}
title={
modelsLoading
? "正在加载模型列表…"
: availableModels.length === 0
? "未在 config.yaml 中配置任何模型"
: "切换全局推理模型;不影响 Persona 抽屉里已设置的 per-seat 覆盖"
}
className="w-full bg-slate-50/50 border border-slate-100 rounded-xl px-3 py-2 text-xs font-bold text-slate-700 cursor-pointer focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-secondary dark:text-foreground dark:border-border"
>
{availableModels.length === 0 && (
<option value="">{modelsLoading ? "加载中…" : "未配置模型"}</option>
)}
{availableModels.map((m) => (
<option key={m.name} value={m.name}>
{m.display_name && m.display_name !== m.name
? `${m.display_name} (${m.name})`
: m.name}
</option>
))}
</select>
</div>
);
}
interface IntentSummaryCardProps {
intentReady: ReturnType<typeof useStep1Intent>["intentReady"];
title?: string;
/** Step 2 侧栏: 收起时仅展示目标,展开后显示约束与假设。 */
collapsible?: boolean;
/** 侧栏整体收起按钮,放在标题栏右侧。 */
sidebarToggle?: {
expanded: boolean;
onToggle: () => void;
};
}
function IntentSummaryCard({
intentReady,
title = "当前意图摘要",
collapsible = false,
sidebarToggle,
}: IntentSummaryCardProps) {
const [expanded, setExpanded] = useState(!collapsible);
const showFullDetail = !collapsible || expanded;
return (
<div className="bg-white border border-slate-200/50 rounded-2xl p-4.5 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center justify-between gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<div className="flex items-center gap-2 min-w-0">
<IntentSectionIcon className="w-4 h-4" />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest truncate">{title}</h2>
</div>
{sidebarToggle && (
<button
type="button"
onClick={sidebarToggle.onToggle}
title={sidebarToggle.expanded ? "收起侧栏" : "展开侧栏"}
aria-expanded={sidebarToggle.expanded}
className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg text-slate-500 hover:text-primary hover:bg-slate-50 transition-colors"
>
{sidebarToggle.expanded ? (
<PanelLeftClose className="h-4 w-4" />
) : (
<PanelLeftOpen className="h-4 w-4" />
)}
</button>
)}
</div>
<div className="p-3 bg-slate-50/50 border border-slate-100 rounded-xl space-y-3 mr-0.5">
{intentReady ? (
<>
<ul className="text-xs text-slate-700 space-y-2.5 font-normal">
<li className="flex items-baseline gap-2 leading-relaxed">
<span className="text-primary text-base leading-none">•</span>
<span>
<b className="text-slate-900 font-bold">目标</b>: {intentReady.objective}
</span>
</li>
</ul>
{collapsible && (
<button
type="button"
onClick={() => setExpanded((v) => !v)}
className="flex items-center gap-0.5 text-[10px] font-bold text-slate-500 hover:text-primary transition-colors"
aria-expanded={expanded}
>
{expanded ? (
<>
收起
<ChevronUp className="w-3.5 h-3.5" />
</>
) : (
<>
展开
<ChevronDown className="w-3.5 h-3.5" />
</>
)}
</button>
)}
{showFullDetail && (
<ul className="text-xs text-slate-700 space-y-2.5 font-normal">
{intentReady.constraints.map((c, i) => (
<li key={`c-${i}`} className="flex items-baseline gap-2 leading-relaxed">
<span className="text-primary text-base leading-none">•</span>
<span>
<b className="text-slate-900 font-bold">约束</b>: {c}
</span>
</li>
))}
{(intentReady.assumptions ?? []).map((a, i) => (
<li key={`a-${i}`} className="flex items-baseline gap-2 leading-relaxed">
<span className="text-slate-400 text-base leading-none">•</span>
<span className="text-slate-500">
<b className="text-slate-700 font-bold">假设</b>: {a}
</span>
</li>
))}
</ul>
)}
</>
) : (
<p className="text-xs text-slate-400 italic leading-relaxed">
等待任务理解智能体澄清完成...
</p>
)}
</div>
</div>
);
}
function IntentSectionIcon({ className }: { className?: string }) {
return (
<svg
className={cn("text-primary shrink-0", className)}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
>
<circle cx="12" cy="12" r="10" />
<circle cx="12" cy="12" r="6" />
<circle cx="12" cy="12" r="2" />
</svg>
);
}
function CollapsedSidebarSection({
title,
children,
className,
}: {
title: string;
children: React.ReactNode;
className?: string;
}) {
return (
<div
title={title}
className={cn(
"w-full bg-white border border-slate-200/50 rounded-2xl p-2 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center",
className,
)}
>
{children}
</div>
);
}
function renderRoleAvatar(
role: RoleEntry,
agentNameToRole: Record<
string,
{ speakerId: number; avatarType: string; displayName: string }
>,
size: string,
) {
if (role.type === "human") {
return <Man1Avatar size={size} />;
}
return getAvatar(
role.agentId ? agentNameToRole[role.agentId]?.avatarType ?? "blue" : "blue",
size,
);
}
function RoleAvatarBadge({
role,
agentNameToRole,
size = "h-7 w-7",
showSpeakingAnimation = false,
}: {
role: RoleEntry;
agentNameToRole: Record<
string,
{ speakerId: number; avatarType: string; displayName: string }
>;
size?: string;
showSpeakingAnimation?: boolean;
}) {
const isOngoing = role.status === "研讨中";
const animate = showSpeakingAnimation && isOngoing;
return (
<div
title={`${role.name} · ${role.status}`}
className={cn(
"relative shrink-0 rounded-full",
animate && "ring-2 ring-primary/50 ring-offset-2 ring-offset-white",
)}
>
{animate && (
<span className="pointer-events-none absolute inset-0 rounded-full border-2 border-primary/35 animate-ping" />
)}
<div className={cn("relative rounded-full", animate && "animate-pulse")}>
{renderRoleAvatar(role, agentNameToRole, size)}
</div>
</div>
);
}
function RoleRow({
role,
agentNameToRole,
@@ -949,14 +1286,7 @@ function RoleRow({
}`}
>
<div className="flex items-center gap-2.5">
{role.type === "human" ? (
<Man1Avatar size="h-7 w-7" />
) : (
getAvatar(
role.agentId ? agentNameToRole[role.agentId]?.avatarType ?? "blue" : "blue",
"h-7 w-7",
)
)}
<RoleAvatarBadge role={role} agentNameToRole={agentNameToRole} />
<div className="flex flex-col">
<span className="text-xs font-bold text-slate-800 tracking-tight">{role.name}</span>
{role.type === "agent" && (
@@ -1024,89 +1354,21 @@ function RightSidebar({
timelineEntries,
triggerToast,
}: RightSidebarProps) {
if (currentStep === 3) return null;
// Step 2 侧栏内容已迁至左侧;Step 3 沿用原逻辑不展示右侧栏。
if (currentStep !== 1) return null;
return (
<section
id="right-sidebar"
className="w-[300px] shrink-0 flex flex-col gap-4 overflow-y-auto pl-1"
>
{/* 推理模型选择器 —— 全局,与 Persona 抽屉的 per-seat 覆盖叠加(后者优先)。 */}
<div className="bg-white border border-slate-200/50 rounded-2xl p-4 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<Cpu className="w-4 h-4 text-primary" strokeWidth={2.5} />
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">推理模型</h2>
</div>
<select
id="global-model-select"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
disabled={modelsLoading || availableModels.length === 0}
title={
modelsLoading
? "正在加载模型列表…"
: availableModels.length === 0
? "未在 config.yaml 中配置任何模型"
: "切换全局推理模型;不影响 Persona 抽屉里已设置的 per-seat 覆盖"
}
className="w-full bg-slate-50/50 border border-slate-100 rounded-xl px-3 py-2 text-xs font-bold text-slate-700 cursor-pointer focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-secondary dark:text-foreground dark:border-border"
>
{availableModels.length === 0 && (
<option value="">{modelsLoading ? "加载中…" : "未配置模型"}</option>
)}
{availableModels.map((m) => (
<option key={m.name} value={m.name}>
{m.display_name && m.display_name !== m.name
? `${m.display_name} (${m.name})`
: m.name}
</option>
))}
</select>
</div>
<ModelSelectorCard
selectedModel={selectedModel}
setSelectedModel={setSelectedModel}
availableModels={availableModels}
modelsLoading={modelsLoading}
/>
{/* 当前意图摘要 —— 由 intent agent 输出的结构化 summary 驱动。 */}
<div className="bg-white border border-slate-200/50 rounded-2xl p-4.5 shadow-[0_2px_8px_rgba(0,0,0,0.01)]">
<div className="flex items-center gap-2 border-b border-slate-100 pb-2.5 mb-2.5">
<svg className="w-4 h-4 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<circle cx="12" cy="12" r="10" />
<circle cx="12" cy="12" r="6" />
<circle cx="12" cy="12" r="2" />
</svg>
<h2 className="text-xs font-bold text-slate-900 uppercase tracking-widest">当前意图摘要</h2>
</div>
<div className="p-3 bg-slate-50/50 border border-slate-100 rounded-xl space-y-3 mr-0.5">
{intentReady ? (
<ul className="text-xs text-slate-700 space-y-2.5 font-normal">
<li className="flex items-baseline gap-2 leading-relaxed">
<span className="text-primary text-base leading-none">•</span>
<span>
<b className="text-slate-900 font-bold">目标</b>: {intentReady.objective}
</span>
</li>
{intentReady.constraints.map((c, i) => (
<li key={`c-${i}`} className="flex items-baseline gap-2 leading-relaxed">
<span className="text-primary text-base leading-none">•</span>
<span>
<b className="text-slate-900 font-bold">约束</b>: {c}
</span>
</li>
))}
{(intentReady.assumptions ?? []).map((a, i) => (
<li key={`a-${i}`} className="flex items-baseline gap-2 leading-relaxed">
<span className="text-slate-400 text-base leading-none">•</span>
<span className="text-slate-500">
<b className="text-slate-700 font-bold">假设</b>: {a}
</span>
</li>
))}
</ul>
) : (
<p className="text-xs text-slate-400 italic leading-relaxed">
等待任务理解智能体澄清完成...
</p>
)}
</div>
</div>
<IntentSummaryCard intentReady={intentReady} />
{/* 澄清记录 Timeline */}
<div className="bg-white border border-slate-200/50 rounded-2xl p-4.5 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex-1 flex flex-col min-h-[300px]">