@
feat(圆桌): Step3 三产物并行 + 大屏多页模式 + 后台挂起查看恢复流程 后台挂起 Step3: - engine 总结报告/结果绘制/大屏数据 asyncio.gather 三流并行,故障隔离 - request.state 回归修复(进程内虚拟 request 补 state),恢复后台报告 - ROUNDTABLE_JOB_GATEWAY 默认 inprocess;产物自愈重试(截断/缺失纠偏) - 大屏后端只产 report-json(dashboardJson),前端按当前主题渲染 结果绘制大屏多页模式(模式二): - report-template 模板渲染(report-json→大屏HTML),零截断、主题跟随 - 与模式一同款交互(流式+沙箱+可修改);沙箱新窗口打开按钮 最终方案展示:左侧席位栏+总控头像、交付/文件合并、收展动画、内联沙箱 后台挂起查看=恢复实时流程: - handleLoadDraft 返回 Promise,await hydrate 后订阅 streamJob SSE 跟随 - saveDraftNow 强制存(发起前+查看前各一次)防丢 第二步优化:总控判断串/并行(并行单列可写)、席位上下文+交付模板、 peerDeliveries、失败重试、共识标记、流程图小窗(缩放/拖动/并行批) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> @
This commit is contained in:
1 parent
a7596001c5
commit
d5f319d9cb
51 files changed
+5016
-1186
No files matched your search
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Step 3「结果绘制」模式。
|
||||
*
|
||||
* 布局对齐第 2 步:
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Download,
|
||||
Eye,
|
||||
FileText,
|
||||
LayoutDashboard,
|
||||
ListOrdered,
|
||||
Loader2,
|
||||
PanelLeftClose,
|
||||
@@ -45,11 +46,14 @@ 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 { Step3DashboardMode } from "@/roundtable-planning/components/Step3DashboardMode";
|
||||
import { useAutoScroll } from "@/roundtable-planning/hooks/useAutoScroll";
|
||||
import { useDialogueSpeedMetrics } from "@/roundtable-planning/hooks/useDialogueSpeedMetrics";
|
||||
import {
|
||||
useStep3Report,
|
||||
type ReportIntent,
|
||||
import { isDashboardHtml } from "@/roundtable-planning/lib/report-template";
|
||||
import type { UseStep3DashboardReturn } from "@/roundtable-planning/hooks/useStep3Dashboard";
|
||||
import type {
|
||||
ReportIntent,
|
||||
UseStep3ReportReturn,
|
||||
} from "@/roundtable-planning/hooks/useStep3Report";
|
||||
|
||||
interface SeatDelivery {
|
||||
@@ -77,8 +81,10 @@ interface Step3ReportModeProps {
|
||||
setSelectedModel: (model: string) => void;
|
||||
/** 草稿里已存的上次绘制结果 —— 有则进入时恢复展示,不重新绘制。 */
|
||||
savedReport?: DraftStep3Snapshot | null;
|
||||
/** 一次绘制完成时回调,把结果交给上层存进草稿 step3。 */
|
||||
onReportSaved?: (snapshot: DraftStep3Snapshot) => void;
|
||||
/** 页面层常驻的经典绘制流(2026-06-13 提升:切走不断流,进入续看)。 */
|
||||
report: UseStep3ReportReturn;
|
||||
/** 页面层常驻的大屏多页流(模式二)。 */
|
||||
dashboard: UseStep3DashboardReturn;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
@@ -97,7 +103,8 @@ export function Step3ReportMode({
|
||||
modelsLoading,
|
||||
setSelectedModel,
|
||||
savedReport,
|
||||
onReportSaved,
|
||||
report,
|
||||
dashboard,
|
||||
onBack,
|
||||
}: Step3ReportModeProps) {
|
||||
const reportSeatDeliveries = useMemo(
|
||||
@@ -112,6 +119,7 @@ export function Step3ReportMode({
|
||||
return `圆桌总控结论-${short}`;
|
||||
}, [intentReady?.objective]);
|
||||
|
||||
// 生成流来自页面层常驻 hook(切走不断流,进入本视图续看进行中过程/结果)。
|
||||
const {
|
||||
phase,
|
||||
error,
|
||||
@@ -125,17 +133,9 @@ export function Step3ReportMode({
|
||||
pause,
|
||||
interject,
|
||||
themeMismatch,
|
||||
abort,
|
||||
openArtifactFromStep,
|
||||
openArtifactPreview,
|
||||
} = useStep3Report({
|
||||
intentReady,
|
||||
seatDeliveries: reportSeatDeliveries,
|
||||
consensusText,
|
||||
selectedModel,
|
||||
savedReport,
|
||||
onReportSaved,
|
||||
});
|
||||
} = report;
|
||||
|
||||
const [modal, setModal] = useState<SourceModal>(null);
|
||||
|
||||
@@ -151,18 +151,31 @@ export function Step3ReportMode({
|
||||
}
|
||||
}, [artifactsOpen]);
|
||||
|
||||
// 进入即自动开跑;卸载(含返回普通视图)中断在途请求。
|
||||
// 不加"永久闸门"——否则 StrictMode 的 mount→unmount→remount 会在 cleanup 里 abort
|
||||
// 掉首次 run,remount 又因闸门跳过重启,导致永远不绘制。run() 自带 runningRef 去重,
|
||||
// remount 重新 start() 即可(开发期至多多建一个空线程,生产无 StrictMode 只跑一次)。
|
||||
// 若草稿里已存上次绘制结果(savedReport)→ 直接恢复展示,不重新跑模型;用户可点「重新绘制」覆盖。
|
||||
// ── 绘制模式(2026-06-13):classic = 经典单页看板(模型自由写整页 HTML);
|
||||
// dashboard = 大屏多页(模型只出 report-json,模板渲染)。两条流现在**并行常驻**
|
||||
// (页面进入第三步即三流齐跑),进入本页默认落经典视图;仅旧版草稿「只有大屏 HTML
|
||||
// 存在 html 槽位(带 marker)」时直接落大屏。
|
||||
const [dashboardMode, setDashboardMode] = useState(() => isDashboardHtml(savedReport?.html));
|
||||
|
||||
// 预览开关:经典绘制视图在前台(非大屏模式)时本流才允许自动打开沙箱(并行流防抢);
|
||||
// 进入时若已有产物/进行中会立即接管沙箱续看。
|
||||
useEffect(() => {
|
||||
if (savedReport?.html) {
|
||||
if (dashboardMode) return;
|
||||
report.setPreviewEnabled(true);
|
||||
return () => report.setPreviewEnabled(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dashboardMode]);
|
||||
|
||||
// 续看语义(2026-06-13):hook 常驻页面层,进入第三步时页面已统一编排(总结 +
|
||||
// 经典绘制并行启动)。这里只兜底"从未启动过"(idle)的场景,**卸载不再 abort**——
|
||||
// 切回总结报告 / 回第二步,绘制流照跑,回头继续看。
|
||||
useEffect(() => {
|
||||
if (dashboardMode || phase !== "idle") return;
|
||||
if (savedReport?.html && !isDashboardHtml(savedReport.html)) {
|
||||
hydrateSaved(savedReport);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
return () => abort();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -188,6 +201,33 @@ export function Step3ReportMode({
|
||||
? "已暂停"
|
||||
: "待绘制";
|
||||
|
||||
// ── 模式二:大屏多页(模板渲染)。经典模式的所有 hooks 已在上方调用完毕,
|
||||
// 这里早退渲染不违反 hooks 规则。 ──
|
||||
if (dashboardMode) {
|
||||
return (
|
||||
<Step3DashboardMode
|
||||
selectedModel={selectedModel}
|
||||
availableModels={availableModels}
|
||||
modelsLoading={modelsLoading}
|
||||
setSelectedModel={setSelectedModel}
|
||||
savedReport={savedReport}
|
||||
dashboard={dashboard}
|
||||
onSwitchClassic={() => {
|
||||
// 切回经典:**不打断**大屏流(切走不断流);经典从未启动则补一次。
|
||||
setDashboardMode(false);
|
||||
if (phase === "idle") {
|
||||
if (savedReport?.html && !isDashboardHtml(savedReport.html)) {
|
||||
hydrateSaved(savedReport);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
}}
|
||||
onBack={onBack}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col text-slate-800">
|
||||
{/* 顶栏 */}
|
||||
@@ -224,6 +264,15 @@ export function Step3ReportMode({
|
||||
<RefreshCw className="w-3.5 h-3.5 text-slate-500" />
|
||||
重新绘制
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDashboardMode(true)}
|
||||
title="大屏多页模式:模型只输出结构化数据、模板渲染总览页 + 每席位一页(更快,支持席位间跳转)。经典绘制若在进行中不会被打断,切回可续看。"
|
||||
className="bg-primary text-white hover:bg-[var(--brand-900)] text-[10.5px] font-bold px-3 py-1.5 rounded-xl flex items-center gap-1 transition shadow-sm shadow-primary/10"
|
||||
>
|
||||
<LayoutDashboard className="w-3.5 h-3.5" />
|
||||
大屏多页
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
@@ -463,14 +512,14 @@ function AgentProgressPanel({
|
||||
modelsLoading,
|
||||
setSelectedModel,
|
||||
}: {
|
||||
phase: ReturnType<typeof useStep3Report>["phase"];
|
||||
dialogues: ReturnType<typeof useStep3Report>["dialogues"];
|
||||
phase: UseStep3ReportReturn["phase"];
|
||||
dialogues: UseStep3ReportReturn["dialogues"];
|
||||
error: string | null;
|
||||
themeMismatch: boolean;
|
||||
onRetry: () => void;
|
||||
onRevisionSubmit: (revisionInstruction: string) => void;
|
||||
onArtifactStepClick: ReturnType<typeof useStep3Report>["openArtifactFromStep"];
|
||||
onArtifactPreview: ReturnType<typeof useStep3Report>["openArtifactPreview"];
|
||||
onArtifactStepClick: UseStep3ReportReturn["openArtifactFromStep"];
|
||||
onArtifactPreview: UseStep3ReportReturn["openArtifactPreview"];
|
||||
selectedModel: string;
|
||||
availableModels: ReadonlyArray<ModelOption>;
|
||||
modelsLoading: boolean;
|
||||
|
||||
Reference in new issue
Block a user