Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
control?.querySelector<HTMLElement>("button:not(:disabled)")?.focus({preventScroll: true});
};
const readFailure = error || history.error;
const emptyHistory = !busy && !readFailure && read.total === 0;
const emptyWithAnalysis = emptyHistory && Boolean(analysis.pack?.result || analysis.pack?.native_outcomes);
const emptyHistoryMessage = zh ? "这个 Goal 尚无研究历程" : "No research history for this Goal yet";
return <section className="research-history" aria-label={copy.title} aria-busy={busy}>
{progressiveOverview ? <>
<nav className="research-reading-order" aria-label={zh ? "研究阅读" : "Research reading"}>
Expand Down Expand Up @@ -480,9 +483,11 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
<button type="button" aria-label={copy.refresh} disabled={busy} onClick={refresh}><RefreshCw size={15} aria-hidden/></button>
</div></header>
<div className="research-history-meta">
<p className="research-history-coverage" role="status">{copy.time}{value?.startAt ? <> · <time>{timeLabel(value.startAt)}</time> — <time>{timeLabel(value.endAt)}</time></> : null} · {busy ? `${copy.reading} ${loaded}${read.total ? ` / ${read.total}` : ""}`
<div className="research-history-coverage" role="status">{emptyWithAnalysis ? <>{emptyHistoryMessage} · {read.events.length} / {read.total}<ContextHelp icon={FlaskConical} label={zh ? "研究历程来源" : "Research history source"}>
<p>{zh ? "已保存的分析、原生测量与 Explore 历程分别读取。这个 Goal 没有已记录的 Explore 历程;已保存的结果仍可在下方阅读。" : "Saved analysis, native measurements and Explore history are read separately. This Goal has no recorded Explore history; its saved results remain readable below."}</p>
</ContextHelp></> : <>{copy.time}{value?.startAt ? <> · <time>{timeLabel(value.startAt)}</time> — <time>{timeLabel(value.endAt)}</time></> : null} · {busy ? `${copy.reading} ${loaded}${read.total ? ` / ${read.total}` : ""}`
: read.total === null ? (zh ? "来源尚未读到" : "Source has not been read")
: `${read.events.length} / ${read.total} ${read.total === 1 ? copy.record : copy.records}${read.complete ? "" : ` · ${copy.partial}`}`}</p>
: `${read.events.length} / ${read.total} ${read.total === 1 ? copy.record : copy.records}${read.complete ? "" : ` · ${copy.partial}`}`}</>}</div>
{!readingSelection ? displayControl : null}
</div>
{readFailure ? <div className="research-history-read-error" role="alert">
Expand All @@ -492,8 +497,8 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
</div>
<button type="button" disabled={busy} onClick={refresh}><RefreshCw size={14} aria-hidden/>{zh ? "重试" : "Retry"}</button>
</div> : null}
{!busy && !readFailure && read.total === 0 ? <div className="research-history-empty">
<div role="status"><strong>{zh ? "这个 Goal 尚无研究历程" : "No research history for this Goal yet"}</strong><ContextHelp icon={FlaskConical} label={zh ? "研究历程来源" : "Research history source"}><p>{zh ? "开启研究模式后,后续研究问题、尝试与证据会在这里形成可回读的历程。" : "Enable research mode to build a readable history of questions, attempts and evidence from later work."}</p></ContextHelp></div>
{emptyHistory && !emptyWithAnalysis ? <div className="research-history-empty">
<div role="status"><strong>{emptyHistoryMessage}</strong><ContextHelp icon={FlaskConical} label={zh ? "研究历程来源" : "Research history source"}><p>{zh ? "开启研究模式后,后续研究问题、尝试与证据会在这里形成可回读的历程。" : "Enable research mode to build a readable history of questions, attempts and evidence from later work."}</p></ContextHelp></div>
<button type="button" onClick={focusResearchMode}>{zh ? "查看研究模式" : "Review research mode"}</button>
</div> : null}
{!progressiveOverview ? <><ResearchAnalysisStatus analysis={analysis}/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ArrowUpRight, ChevronDown, CircleHelp, FileText, TriangleAlert, X} from "lucide-react";
import {useId, useRef, useState} from "react";
import {useId, useLayoutEffect, useRef, useState} from "react";
import {TRAJECTORY_METRICS} from "../../../../../../loopx/control_plane/capabilities/trajectory_assessment_types.js";
import type {AssessmentCitation, AssessmentVerdict, TrajectoryAssessment, TrajectoryEpisode, TrajectoryInput, TrajectoryMetric, TrajectorySolverUsage} from "../../../../../../loopx/control_plane/capabilities/trajectory_assessment_types.js";
import type {TrajectoryNativeOutcomes} from "../../../../../../loopx/control_plane/capabilities/trajectory_native_outcomes.js";
Expand Down Expand Up @@ -133,7 +133,7 @@ function RetainedImprovement({native, zh, onCitation}: {native: NonNullable<Rese
{native.applicability === "historical" ? <p className="research-progress-warning" role="status">{zh ? "历史捕获 · 当前结果未核验" : "Historical capture · current result unverified"}</p> : null}
<details><summary aria-label={`${label}: ${improvement.value === null ? (zh ? "未知" : "Unknown") : `${format(improvement.value)} ${outcomes.metric.unit}`}`}>
<strong data-retained-improvement>{improvement.value === null ? "—" : format(improvement.value)}</strong>
<span>{improvement.value === null ? (zh ? "未知" : "Unknown") : outcomes.metric.unit} · {outcomes.metric.name}{improvement.baseline ? ` · ${zh ? "基线" : "baseline"} ${formatNativeMeasurement(improvement.baseline.value, zh)}` : ""}</span><ChevronDown size={14} aria-hidden/>
<span>{improvement.value === null ? (zh ? "未知" : "Unknown") : outcomes.metric.unit}</span><ChevronDown size={14} aria-hidden/>
</summary><dl>
{([[improvement.baseline, zh ? "比较基线" : "Comparison baseline"], [outcomes.retained, zh ? "实际保留" : "Actually retained"]] as const).map(([point, title]) => <div key={title}><dt>{title}</dt><dd>{point ? <button type="button" onClick={event => onCitation(point.citation, event.currentTarget)}>{String(point.value)} · {point.candidate_ref}<ArrowUpRight size={13} aria-hidden/></button> : (zh ? "未知" : "Unknown")}</dd></div>)}
<div><dt>{zh ? "保留依据" : "Retention evidence"}</dt><dd>{improvement.retention_citation ? <button type="button" onClick={event => onCitation(improvement.retention_citation!, event.currentTarget)}>{zh ? "查看原文" : "Read original evidence"}<ArrowUpRight size={13} aria-hidden/></button> : (zh ? "未采集" : "Uncaptured")}</dd></div>
Expand Down Expand Up @@ -196,6 +196,14 @@ function ResearchMetrics({pack: p, zh, onCitation}: {pack: ResearchProgressPack
const [selected, setSelected] = useState<TrajectoryMetric | null>(null);
const panelId = useId(), panel = useRef<HTMLDivElement>(null);
const origin = useRef<HTMLButtonElement | null>(null);
// Focus the committed region before the next interaction, even when the
// animation clock is suspended. A later frame must not steal a row's Enter.
useLayoutEffect(() => {
if (selected && panel.current) {
panel.current.focus({preventScroll: true});
panel.current.scrollIntoView({block: "nearest"});
}
}, [selected]);
const result = p.result;
const number = (n: number) => new Intl.NumberFormat(zh ? "zh-CN" : "en", {maximumFractionDigits: 1}).format(n);
const percent = (n: number) => new Intl.NumberFormat(zh ? "zh-CN" : "en", {style: "percent", maximumFractionDigits: 0}).format(n);
Expand All @@ -219,7 +227,6 @@ function ResearchMetrics({pack: p, zh, onCitation}: {pack: ResearchProgressPack
onClick={event => {
if (selected === metric) {close(); return;}
origin.current = event.currentTarget; setSelected(metric);
requestAnimationFrame(() => {panel.current?.focus({preventScroll: true}); panel.current?.scrollIntoView({block: "nearest"});});
}}>
<span className="research-effect-title">{label[zh ? 0 : 1]}<ChevronDown size={13} aria-hidden/></span>
<strong className="research-effect-number">{row?.eligible ? <>{number(row.yes)}<span> / {number(row.eligible)}</span></> : "—"}</strong>
Expand Down
Loading
Loading