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 @@ -2,10 +2,12 @@
* These labels do not classify evidence or schedule verification. */
export function exploreFindingCopy(zh: boolean) {
return zh ? {
count: (value: number) => `${value} 条结论`,
labels: {tentative: "暂定结论", confirmed: "来源记为确认", refuted: "已反驳"},
help: "结论状态说明",
meaning: "暂定结论表示来源尚未确认或反驳该结论,可能已有观测与证据;不表示有检查任务排队。确认与反驳也仅表示来源在所述适用范围内的判断,不等于独立验证或目标完成。研究问题是否收束、下一步继续或转向,分别记录。",
} : {
count: (value: number) => `${value} ${value === 1 ? "finding" : "findings"}`,
labels: {tentative: "Tentative", confirmed: "Recorded confirmation", refuted: "Refuted"},
help: "About finding states",
meaning: "Tentative means the source has not confirmed or refuted the finding; observations and evidence may already exist. It does not mean a check is queued. Confirmation and refutation are source judgments within the stated scope, not independent verification or Goal completion. Question closure and the decision to continue or replan are recorded separately.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,17 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
title: "研究历程", loading: "正在读取完整记录", reading: "读取中", refresh: "刷新记录",
all: "回到全图", scope: "Explore 已记录历史", coverage: "其他来源覆盖、尝试发生时序尚未核验",
partial: "历史尚未完整载入", failure: "无法继续读取历史。已读记录保留;请重试。",
time: "记录时间(本地)", unknown: "时间未记录", records: "条记录", evidence: "已记录依据",
time: "记录时间(本地)", unknown: "时间未记录", record: "条记录", records: "条记录", evidence: "已记录依据",
interpretation: "独立检查与实际采用尚未核验", versions: "来源与版本引用", updates: "历史记录",
changes: "次记录", findings: "条结论", links: "显式关联", missing: "部分关联的来源节点尚未记录",
changes: "次记录", question: "个问题", questions: "个问题", links: "显式关联", missing: "部分关联的来源节点尚未记录",
group: "来源分组", lost: "当前记录中找不到所选节点,其他历史仍可浏览。",
} : {
title: "Research history", loading: "Reading the full recorded history", reading: "Reading", refresh: "Refresh history",
all: "Whole graph", scope: "Recorded Explore history", coverage: "Other-source coverage and attempt-time coverage are unverified",
partial: "History is not fully loaded", failure: "Cannot continue reading history. Loaded records remain; retry.",
time: "Recorded time (local)", unknown: "Time not recorded", records: "records", evidence: "Recorded evidence",
time: "Recorded time (local)", unknown: "Time not recorded", record: "record", records: "records", evidence: "Recorded evidence",
interpretation: "Independent checking and actual adoption are unverified", versions: "Source and version references", updates: "History",
changes: "records", findings: "findings", links: "Explicit relationships", missing: "Some linked source nodes are not recorded",
changes: "records", question: "question", questions: "questions", links: "Explicit relationships", missing: "Some linked source nodes are not recorded",
group: "Source grouping", lost: "The selected node is absent from this snapshot. Other history remains readable.",
};
// Labels reuse Explore's existing vocabulary; they confer no verification state.
Expand Down Expand Up @@ -415,12 +415,12 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
<span className="research-node-meta">{node.latest.node_kind === "experiment" ? <FlaskConical size={13} aria-hidden/> : <Circle size={12} aria-hidden/>}{kindLabel(node.latest.node_kind)}<small>{stateLabel(node.latest.status)}</small></span>
<strong>{node.latest.title}</strong>{measured ? <span className="research-node-count" title={`${measured.metric} (${measured.unit}) · ${measured.comparison_scope} · ${measured.evaluation_ref}`}>
{measured.metric} {formatExploreMeasurementValue(measured.latest.measurement.value, zh)} · best {formatExploreMeasurementValue(measured.best?.value ?? null, zh)} {measured.higher_is_better ? "↑" : "↓"}
</span> : <span className="research-node-count"><time>{timeLabel(node.updates[0]?.recorded_at)}</time>{node.updates.length > 1 ? ` · ${node.updates.length} ${copy.changes}` : ""}{node.findings.length ? ` · ${node.findings.length} ${copy.findings}` : ""}</span>}
</span> : <span className="research-node-count"><time>{timeLabel(node.updates[0]?.recorded_at)}</time>{node.updates.length > 1 ? ` · ${node.updates.length} ${copy.changes}` : ""}{node.findings.length ? ` · ${findingCopy.count(node.findings.length)}` : ""}</span>}
</button>})}
{grouped.groups.map((group, index) => <button type="button" key={`day:${group.day}`} className={`research-history-node research-history-group${selectedGroup === group ? " is-selected" : ""}`} style={{left: (index % 4) * 230 + 24, top: clusterY + Math.floor(index / 4) * 124}}
data-research-day={group.day} aria-pressed={selectedGroup === group} onClick={() => select({nodes: [], day: group.day})}>
<span className="research-node-meta"><Circle size={12} aria-hidden/>{zh ? "未关联问题" : "Unlinked questions"}</span>
<strong>{group.day ? `${group.day} UTC` : copy.unknown}</strong><span className="research-node-count">{group.nodes.length} {zh ? "个问题 · 展开全部" : "questions · read all"}</span>
<strong>{group.day ? `${group.day} UTC` : copy.unknown}</strong><span className="research-node-count">{group.nodes.length} {group.nodes.length === 1 ? copy.question : copy.questions} · {zh ? "展开全部" : "read all"}</span>
</button>)}
</div>
</div>;
Expand Down Expand Up @@ -475,7 +475,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
<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}` : ""}`
: read.total === null ? (zh ? "来源尚未读到" : "Source has not been read")
: `${read.events.length} / ${read.total} ${copy.records}${read.complete ? "" : ` · ${copy.partial}`}`}</p>
: `${read.events.length} / ${read.total} ${read.total === 1 ? copy.record : copy.records}${read.complete ? "" : ` · ${copy.partial}`}`}</p>
{!readingSelection ? displayControl : null}
</div>
{readFailure ? <div className="research-history-read-error" role="alert">
Expand Down Expand Up @@ -570,7 +570,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
{displayControl}
<ol className="research-question-index">{selectedMembers.map(node => <li key={node.id} data-research-node={node.id}>
<button type="button" onClick={() => {collectionOpener.current = opener.current; chooseQuestion(node, {nodes: selection.nodes, day: selection.day});}}>
<strong>{node.latest.title}</strong><span>{stateLabel(node.findings.at(-1)?.status ?? node.latest.status)}{node.latest.status === "blocked" && node.findings.length ? ` · ${stateLabel(node.latest.status)}` : ""}{node.findings.length ? ` · ${node.findings.length} ${copy.findings}` : ""}</span>
<strong>{node.latest.title}</strong><span>{stateLabel(node.findings.at(-1)?.status ?? node.latest.status)}{node.latest.status === "blocked" && node.findings.length ? ` · ${stateLabel(node.latest.status)}` : ""}{node.findings.length ? ` · ${findingCopy.count(node.findings.length)}` : ""}</span>
{node.latest.blocked_reason ? <span className="research-history-error">{node.latest.blocked_reason}</span> : null}
</button>
</li>)}</ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,23 @@ export function GoalResearchTimeline({timeline, zh, selectedIds, selectedEventId
}, [timeline.start, timeline.end]);
const findingCopy = exploreFindingCopy(zh);
const copy: Copy = zh ? {
early: "前段", middle: "中段", late: "后段", items: "研究项", findings: "结论",
early: "前段", middle: "中段", late: "后段", item: "研究项", items: "研究项", question: "个问题", questions: "个问题",
decisions: "显式决策", none: "未记录显式决策", changed: "变化", stopped: "停止", retained: "保留",
unstructured: "决策未结构化", unstructuredHint: "结论未按 writeback 结构记录,无法拆出依据与决策。",
density: "记录密度", densityHint: "柱高为事件数;浅红为无记录空档", ticks: "转向 / 继续",
routes: "研究路线", routeHint: "仅由显式关联或父子分组相连", singles: "未关联研究项",
trail: "决策轨迹", more: "显示更早记录", moreQuestions: "显示更多问题", records: "条决策记录", unknownTime: "时间未知", all: "全部时段",
trail: "决策轨迹", more: "显示更早记录", moreQuestions: "显示更多问题", record: "条决策记录", records: "条决策记录", unknownTime: "时间未知", all: "全部时段",
span: "记录跨度", start: "起点", end: "末端", replan: "转向", continue: "继续", stop: "停止", wait: "等待", no_change: "无变化", ask_human: "请人决定",
...findingCopy.labels, unlabeled: "无结论", phaseNote: "按记录时间三等分,仅为阅读分组,不是预声明的评估窗口。",
noTime: "没有可用的记录时间,无法放上时间轴;下方记录仍可完整阅读。",
whole: "全程",
} : {
early: "Early", middle: "Middle", late: "Late", items: "Research items", findings: "Findings",
early: "Early", middle: "Middle", late: "Late", item: "Research item", items: "Research items", question: "question", questions: "questions",
decisions: "Explicit decisions", none: "No explicit decision recorded", changed: "Changed", stopped: "Stopped", retained: "Retained",
unstructured: "Unstructured decisions", unstructuredHint: "Findings were not recorded in writeback sections; evidence and decisions cannot be separated.",
density: "Recording density", densityHint: "Bar height is events; pale red is a recording gap", ticks: "Replan / continue",
routes: "Research routes", routeHint: "Connected only by recorded relationships or parent grouping", singles: "Unlinked research items",
trail: "Decision history", more: "Show earlier records", moreQuestions: "Show more questions", records: "decision records", unknownTime: "Unknown time", all: "Whole history",
trail: "Decision history", more: "Show earlier records", moreQuestions: "Show more questions", record: "decision record", records: "decision records", unknownTime: "Unknown time", all: "Whole history",
span: "Recorded span", start: "Start", end: "End", replan: "Replan", continue: "Continue", stop: "Stop", wait: "Wait", no_change: "No change", ask_human: "Ask human",
...findingCopy.labels, unlabeled: "No finding", phaseNote: "Equal thirds of record time, for reading only; not a pre-declared evaluation window.",
noTime: "No usable record time; the history cannot be placed on a time axis. All records remain readable below.",
Expand Down Expand Up @@ -96,7 +96,7 @@ export function GoalResearchTimeline({timeline, zh, selectedIds, selectedEventId
const counts = Object.entries(item.decisions);
return [
`${timeLabel(item.start)} — ${timeLabel(item.end)}`,
`${item.nodes} ${copy.items} · ${item.findings} ${copy.findings}`,
`${item.nodes} ${item.nodes === 1 ? copy.item : copy.items} · ${findingCopy.count(item.findings)}`,
LABELS.map(key => `${label(key)} ${item.labels[key] ?? 0}`).join(" · "),
timeline.decisions.length ? (counts.length ? `${copy.decisions} · ${counts.map(([key, value]) => `${label(key)} ${value}`).join(" · ")}` : (timeline.structuredCount ? copy.none : copy.unstructured)) : null,
].filter(Boolean).join(" · ");
Expand All @@ -105,7 +105,7 @@ export function GoalResearchTimeline({timeline, zh, selectedIds, selectedEventId
return <button key={item.index} type="button" aria-pressed={phase === item.index} onClick={() => onPhase(phase === item.index ? null : item.index)}
aria-label={`${phaseName(item)} · ${phaseDescription(item)}`} title={`${phaseName(item)} · ${phaseDescription(item)}`}
className={`research-phase${phase === item.index ? " is-active" : ""}`}>
<span className="research-phase-head"><strong>{phaseName(item)}</strong><span>{item.findings} {copy.findings}</span></span>
<span className="research-phase-head"><strong>{phaseName(item)}</strong><span>{findingCopy.count(item.findings)}</span></span>
<span className="research-phase-bar" aria-label={LABELS.map(key => `${label(key)} ${item.labels[key] ?? 0}`).join(" · ")}>
{LABELS.map(key => item.labels[key] ? <span key={key} className={tone(key)} style={{flexGrow: item.labels[key]}}>{item.labels[key]}</span> : null)}
</span>
Expand Down Expand Up @@ -151,7 +151,7 @@ export function GoalResearchTimeline({timeline, zh, selectedIds, selectedEventId
.map(edge => ({key: edge.event_id, from: edge.from_node, to: edge.to_node, kind: edge.edge_type === "refutes" ? "is-refutation" : "", title: `${edge.edge_type}: ${edge.summary}`})),
];
return <div className="research-route" key={route.id} role="group" aria-label={`${members.length} · ${first.node.latest.title}`}>
<div className="research-row-label"><strong>{members.length} {zh ? "项研究" : "research items"}</strong>
<div className="research-row-label"><strong>{members.length} {zh ? "项研究" : members.length === 1 ? "research item" : "research items"}</strong>
<small>{dayLabel(route.first!)} – {dayLabel(route.last!)} · {copy.span} {hours(route.first!, route.last!)}</small>
<small>{tally.map(([key, value]) => `${label(key)} ${value}`).join(" · ") || copy.unlabeled}</small></div>
<div className="research-row-plot">
Expand Down Expand Up @@ -182,7 +182,7 @@ export function GoalResearchTimeline({timeline, zh, selectedIds, selectedEventId
<div className="research-row-label"><strong>{copy.singles} · {groupedIds.size}</strong></div>
<div className="research-unlinked-days">{dayGroups.map(group => <button type="button" key={group.day} data-research-day={group.day} aria-pressed={selectedDay === group.day}
className={`${selectedDay === group.day ? "is-selected" : ""}${group.nodes.some(node => inPhase(timeline.nodes.get(node.id)!.first)) ? "" : " is-dimmed"}`}
onClick={() => onGroupSelect(group.day)}><time>{group.day || (zh ? "时间未知" : "Unknown time")}</time><strong>{group.nodes.length} {zh ? "个问题" : "questions"}</strong></button>)}</div>
onClick={() => onGroupSelect(group.day)}><time>{group.day || (zh ? "时间未知" : "Unknown time")}</time><strong>{group.nodes.length} {group.nodes.length === 1 ? copy.question : copy.questions}</strong></button>)}</div>
</> : null}
{swarm.length ? <>
<div className="research-row-label"><strong>{copy.singles} · {swarm.length}</strong></div>
Expand Down Expand Up @@ -233,7 +233,7 @@ function TrailQuestion({group, copy, label, timeLabel, selectedEventId, onSelect
return <li className="research-trail-question">
<header>
<h4>{group.title}</h4>
<div className="research-trail-meta"><span>{group.items.length} {copy.records}</span>
<div className="research-trail-meta"><span>{group.items.length} {group.items.length === 1 ? copy.record : copy.records}</span>
{knownTimes.length ? <span>{timeLabel(knownTimes.at(-1)!)}{knownTimes.length > 1 ? ` — ${timeLabel(knownTimes[0]!)}` : ""}</span> : null}
{knownTimes.length < group.items.length ? <span>{copy.unknownTime} · {group.items.length - knownTimes.length}</span> : null}
{exceptions.map(({status, count}) => <span className={tone(status)} key={status}>{label(status)} {count}</span>)}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading