修复 embed 跨域并持久化圆桌 Step3 结果
- Gateway 默认 CORS 白名单增加 5174,embed 文档补充跨域配置说明 - 圆桌草稿新增 step3 快照字段与迁移,结果绘制 HTML 可随草稿恢复 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
40ba81ee6a
commit
aea7724cdb
15 files changed
+527
-89
No files matched your search
@@ -20,11 +20,15 @@ import {
|
||||
FileText,
|
||||
ListOrdered,
|
||||
Loader2,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
RefreshCw,
|
||||
Sparkles,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
||||
import { useArtifacts } from "@/components/workspace/artifacts";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
buildArtifactDownloadUrl,
|
||||
listSessionArtifacts,
|
||||
@@ -36,6 +40,7 @@ import { ScrollToBottomButton } from "@/roundtable-planning/components/ScrollToB
|
||||
import { Step2Message } from "@/roundtable-planning/components/Step2Panel";
|
||||
import { Step2SandboxLayout } from "@/roundtable-planning/components/Step2SandboxLayout";
|
||||
import { markdownComponents } from "@/roundtable-planning/components/markdown-components";
|
||||
import type { DraftStep3Snapshot } from "@/roundtable-planning/api/drafts";
|
||||
import { useAutoScroll } from "@/roundtable-planning/hooks/useAutoScroll";
|
||||
import {
|
||||
useStep3Report,
|
||||
@@ -65,6 +70,10 @@ interface Step3ReportModeProps {
|
||||
availableModels: ReadonlyArray<ModelOption>;
|
||||
modelsLoading: boolean;
|
||||
setSelectedModel: (model: string) => void;
|
||||
/** 草稿里已存的上次绘制结果 —— 有则进入时恢复展示,不重新绘制。 */
|
||||
savedReport?: DraftStep3Snapshot | null;
|
||||
/** 一次绘制完成时回调,把结果交给上层存进草稿 step3。 */
|
||||
onReportSaved?: (snapshot: DraftStep3Snapshot) => void;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
@@ -82,6 +91,8 @@ export function Step3ReportMode({
|
||||
availableModels,
|
||||
modelsLoading,
|
||||
setSelectedModel,
|
||||
savedReport,
|
||||
onReportSaved,
|
||||
onBack,
|
||||
}: Step3ReportModeProps) {
|
||||
const reportSeatDeliveries = useMemo(
|
||||
@@ -97,6 +108,7 @@ export function Step3ReportMode({
|
||||
seatStubMessages,
|
||||
activeArtifactThreadId,
|
||||
start,
|
||||
hydrateSaved,
|
||||
redraw,
|
||||
abort,
|
||||
openArtifactFromStep,
|
||||
@@ -106,16 +118,35 @@ export function Step3ReportMode({
|
||||
seatDeliveries: reportSeatDeliveries,
|
||||
consensusText,
|
||||
selectedModel,
|
||||
savedReport,
|
||||
onReportSaved,
|
||||
});
|
||||
|
||||
const [modal, setModal] = useState<SourceModal>(null);
|
||||
|
||||
// 左栏「研讨原料」收起态:打开沙箱时默认收起成图标条(对齐第 2 步侧栏收起),
|
||||
// 关闭沙箱时自动展开;中途用户可手动切换(effect 只在沙箱开关变化的边沿触发)。
|
||||
const { open: artifactsOpen } = useArtifacts();
|
||||
const [leftCollapsed, setLeftCollapsed] = useState(false);
|
||||
const prevArtifactsOpenRef = useRef(artifactsOpen);
|
||||
useEffect(() => {
|
||||
if (artifactsOpen !== prevArtifactsOpenRef.current) {
|
||||
prevArtifactsOpenRef.current = artifactsOpen;
|
||||
setLeftCollapsed(artifactsOpen);
|
||||
}
|
||||
}, [artifactsOpen]);
|
||||
|
||||
// 进入即自动开跑;卸载(含返回普通视图)中断在途请求。
|
||||
// 不加"永久闸门"——否则 StrictMode 的 mount→unmount→remount 会在 cleanup 里 abort
|
||||
// 掉首次 run,remount 又因闸门跳过重启,导致永远不绘制。run() 自带 runningRef 去重,
|
||||
// remount 重新 start() 即可(开发期至多多建一个空线程,生产无 StrictMode 只跑一次)。
|
||||
// 若草稿里已存上次绘制结果(savedReport)→ 直接恢复展示,不重新跑模型;用户可点「重新绘制」覆盖。
|
||||
useEffect(() => {
|
||||
start();
|
||||
if (savedReport?.html) {
|
||||
hydrateSaved(savedReport);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
return () => abort();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -178,34 +209,86 @@ export function Step3ReportMode({
|
||||
|
||||
{/* 主体:左源卡 + 右沙箱 */}
|
||||
<div className="flex-1 min-h-0 flex gap-3 p-3">
|
||||
{/* 左栏:收起的源内容 */}
|
||||
<aside className="w-[280px] shrink-0 flex flex-col gap-3 overflow-y-auto pr-0.5">
|
||||
<SourceCard
|
||||
icon={<CheckCircle className="w-4 h-4 text-emerald-500" />}
|
||||
title="总控最终结论"
|
||||
hint={consensusText ? "点击查看结论全文" : "暂无结论"}
|
||||
badge={hasConsensus ? "已共识" : undefined}
|
||||
disabled={!consensusText}
|
||||
onClick={() => setModal("consensus")}
|
||||
/>
|
||||
<SourceCard
|
||||
icon={<FileText className="w-4 h-4 text-primary" />}
|
||||
title="各席位最终交付"
|
||||
hint="点击逐席位查看交付"
|
||||
badge={String(seatDeliveries.length)}
|
||||
disabled={seatDeliveries.length === 0}
|
||||
onClick={() => setModal("seats")}
|
||||
/>
|
||||
<SourceCard
|
||||
icon={<Download className="w-4 h-4 text-primary" />}
|
||||
title="各席位生成文件"
|
||||
hint="点击查看/预览产出文件"
|
||||
onClick={() => setModal("files")}
|
||||
/>
|
||||
{/* 左栏:研讨原料源卡。打开沙箱时收起成 52px 图标条(对齐第 2 步侧栏收起样式)。 */}
|
||||
<aside
|
||||
className={cn(
|
||||
"shrink-0 flex flex-col gap-3 overflow-y-auto pr-0.5 transition-[width] duration-200 ease-out",
|
||||
leftCollapsed ? "w-[52px]" : "w-[280px]",
|
||||
)}
|
||||
>
|
||||
{leftCollapsed ? (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLeftCollapsed(false)}
|
||||
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>
|
||||
<CollapsedSourceCard
|
||||
icon={<CheckCircle className="w-4 h-4 text-emerald-500" />}
|
||||
title="总控最终结论"
|
||||
disabled={!consensusText}
|
||||
onClick={() => setModal("consensus")}
|
||||
/>
|
||||
<CollapsedSourceCard
|
||||
icon={<FileText className="w-4 h-4 text-primary" />}
|
||||
title="各席位最终交付"
|
||||
disabled={seatDeliveries.length === 0}
|
||||
onClick={() => setModal("seats")}
|
||||
/>
|
||||
<CollapsedSourceCard
|
||||
icon={<Download className="w-4 h-4 text-primary" />}
|
||||
title="各席位生成文件"
|
||||
onClick={() => setModal("files")}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="shrink-0 flex items-center justify-between gap-2 px-1">
|
||||
<span className="text-[11px] font-bold text-slate-500 uppercase tracking-widest">
|
||||
研讨原料
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLeftCollapsed(true)}
|
||||
title="收起研讨原料"
|
||||
aria-expanded
|
||||
className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200/50 bg-white text-slate-500 hover:text-primary hover:border-primary/30 transition-colors"
|
||||
>
|
||||
<PanelLeftClose className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<SourceCard
|
||||
icon={<CheckCircle className="w-4 h-4 text-emerald-500" />}
|
||||
title="总控最终结论"
|
||||
hint={consensusText ? "点击查看结论全文" : "暂无结论"}
|
||||
badge={hasConsensus ? "已共识" : undefined}
|
||||
disabled={!consensusText}
|
||||
onClick={() => setModal("consensus")}
|
||||
/>
|
||||
<SourceCard
|
||||
icon={<FileText className="w-4 h-4 text-primary" />}
|
||||
title="各席位最终交付"
|
||||
hint="点击逐席位查看交付"
|
||||
badge={String(seatDeliveries.length)}
|
||||
disabled={seatDeliveries.length === 0}
|
||||
onClick={() => setModal("seats")}
|
||||
/>
|
||||
<SourceCard
|
||||
icon={<Download className="w-4 h-4 text-primary" />}
|
||||
title="各席位生成文件"
|
||||
hint="点击查看/预览产出文件"
|
||||
onClick={() => setModal("files")}
|
||||
/>
|
||||
|
||||
<div className="mt-1 rounded-2xl border border-dashed border-slate-200 bg-slate-50/60 p-3 text-[11px] leading-relaxed text-slate-400">
|
||||
原研讨内容已收起到这里,随时点开查阅。右侧由「方案总结智能体」据此绘制可视化看板。
|
||||
</div>
|
||||
<div className="mt-1 rounded-2xl border border-dashed border-slate-200 bg-slate-50/60 p-3 text-[11px] leading-relaxed text-slate-400">
|
||||
原研讨内容已收起到这里,随时点开查阅。右侧由「方案总结智能体」据此绘制可视化汇报。
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</aside>
|
||||
|
||||
{/* 右栏:智能体进度 + 沙箱(复用第 2 步) */}
|
||||
@@ -300,6 +383,31 @@ function SourceCard({
|
||||
);
|
||||
}
|
||||
|
||||
// 收起态的源卡:仅图标,居中,点击仍打开对应弹窗(对齐第 2 步 CollapsedSidebarSection 样式)。
|
||||
function CollapsedSourceCard({
|
||||
icon,
|
||||
title,
|
||||
disabled,
|
||||
onClick,
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className="w-full shrink-0 flex items-center justify-center rounded-2xl border border-slate-200/50 bg-white p-2.5 text-slate-500 shadow-[0_2px_8px_rgba(0,0,0,0.01)] transition hover:text-primary hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
{icon}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// ── 右栏:智能体进度面板(沙箱的 60% 区域)────────────────────────────────
|
||||
|
||||
function AgentProgressPanel({
|
||||
|
||||
Reference in new issue
Block a user