11import { useEffect , useId , useMemo , useRef , useState } from "react" ;
22import { defaultParseSearch } from "@tanstack/react-router" ;
3- import { ArrowLeft , Circle , FlaskConical , GitBranch , RefreshCw , Sparkles } from "lucide-react" ;
3+ import { ArrowLeft , Circle , FlaskConical , GitBranch , RefreshCw } from "lucide-react" ;
4+ import { ResearchAnalysisButton , ResearchAnalysisPanel , ResearchAnalysisScope , ResearchProgressSummary , useResearchAnalysis } from "./goal-research-analysis" ;
45import { buildExploreTimeline , parseExploreFindingSections } from "../../../../../../loopx/control_plane/presentation/explore_timeline.js" ;
56import { GoalResearchTimeline } from "./goal-research-timeline" ;
67import { requestJson } from "../../data/chat" ;
@@ -25,6 +26,7 @@ const selectedFromUrl = (goal: string): Selection => {
2526/** An effect-free history consumer. Its source is Explore, not raw sessions or
2627 * a judge. All recorded updates survive; unsupported analysis stays unavailable. */
2728export function GoalResearchHistory ( { goalId, active, zh, refreshRevision = 0 } : { goalId : string ; active : boolean ; zh : boolean ; refreshRevision ?: number } ) {
29+ const analysis = useResearchAnalysis ( goalId , active , zh ) ;
2830 const [ read , setRead ] = useState < Read > ( { events : [ ] , total : null , complete : false } ) ;
2931 const [ busy , setBusy ] = useState ( true ) ;
3032 const [ error , setError ] = useState ( "" ) ;
@@ -43,7 +45,6 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0}: {
4345 title : "研究历程" , loading : "正在读取完整记录" , reading : "读取中" , refresh : "刷新记录" ,
4446 all : "回到全图" , scope : "Explore 已记录历史" , coverage : "其他来源覆盖、尝试发生时序尚未核验" ,
4547 partial : "历史尚未完整载入" , failure : "无法继续读取历史。已读记录保留;请重试。" ,
46- analysis : "深度分析" , unavailable : "尚未接入轨迹评测;浏览历史不会调用 Agent。" ,
4748 time : "记录时间(本地)" , unknown : "时间未记录" , records : "条记录" , evidence : "已记录依据" ,
4849 interpretation : "独立检查与实际采用尚未核验" , versions : "来源与版本引用" , updates : "历史记录" ,
4950 changes : "次记录" , findings : "条结论" , links : "显式关联" , missing : "部分关联的来源节点尚未记录" ,
@@ -52,7 +53,6 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0}: {
5253 title : "Research history" , loading : "Reading the full recorded history" , reading : "Reading" , refresh : "Refresh history" ,
5354 all : "Whole graph" , scope : "Recorded Explore history" , coverage : "Other-source coverage and attempt-time coverage are unverified" ,
5455 partial : "History is not fully loaded" , failure : "Cannot continue reading history. Loaded records remain; retry." ,
55- analysis : "Deep analysis" , unavailable : "Trajectory assessment is not connected. Reading history never invokes an Agent." ,
5656 time : "Recorded time (local)" , unknown : "Time not recorded" , records : "records" , evidence : "Recorded evidence" ,
5757 interpretation : "Independent checking and actual adoption are unverified" , versions : "Source and version references" , updates : "History" ,
5858 changes : "records" , findings : "findings" , links : "Explicit relationships" , missing : "Some linked source nodes are not recorded" ,
@@ -165,20 +165,23 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0}: {
165165 } ) ;
166166 } ;
167167 const choose = ( id : string , focus = true ) => select ( { nodes : id ? [ id ] : [ ] , day : null } , focus ) ;
168- if ( ! busy && ! error && ! read . events . length ) return null ;
168+ if ( ! busy && ! error && ! read . events . length && ! analysis . pack && ! analysis . snapshot ?. catalog . packs . length ) return null ;
169169 return < section className = "research-history" aria-label = { copy . title } aria-busy = { busy } >
170170 < header className = "research-history-heading" > < div > < h2 > { copy . title } </ h2 >
171171 < p > { copy . scope } { value ?. startAt ? < > · { copy . time } < time > { timeLabel ( value . startAt ) } </ time > — < time > { timeLabel ( value . endAt ) } </ time > </ > : null } </ p > </ div >
172172 < div className = "research-history-controls" >
173173 { selection . nodes . length || selection . day !== null ? < button type = "button" onClick = { ( ) => { choose ( "" , false ) ; graph . current ?. focus ( ) ; } } > < ArrowLeft size = { 14 } aria-hidden /> { copy . all } </ button > : null }
174174 < div className = "research-lens" role = "group" aria-label = { zh ? "视图" : "View" } > { ( [ "timeline" , "graph" ] as const ) . map ( item => < button key = { item } type = "button" aria-pressed = { lens === item } onClick = { ( ) => chooseLens ( item ) } > { item === "timeline" ? ( zh ? "时间线" : "Timeline" ) : ( zh ? "关系图" : "Graph" ) } </ button > ) } </ div >
175- < button type = "button" disabled title = { copy . unavailable } aria-describedby = { ` ${ presentationId } - analysis` } > < Sparkles size = { 14 } aria-hidden /> { copy . analysis } </ button >
175+ < ResearchAnalysisButton analysis = { analysis } / >
176176 < button type = "button" aria-label = { copy . refresh } disabled = { busy } onClick = { ( ) => setRevision ( n => n + 1 ) } > < RefreshCw size = { 15 } aria-hidden /> </ button >
177177 </ div > </ header >
178178 < p className = "research-history-coverage" role = "status" > { busy ? `${ copy . reading } ${ loaded } ${ read . total ? ` / ${ read . total } ` : "" } `
179179 : read . total === null ? ( zh ? "来源尚未读到" : "Source has not been read" )
180180 : `${ read . events . length } / ${ read . total } ${ copy . records } ${ read . complete ? "" : ` · ${ copy . partial } ` } ` } · { copy . coverage } </ p >
181181 { error || history . error ? < p role = "alert" className = "research-history-error" > { copy . failure } < small > { error || history . error } </ small > </ p > : null }
182+ < ResearchAnalysisScope analysis = { analysis } />
183+ < ResearchProgressSummary analysis = { analysis } />
184+ < ResearchAnalysisPanel analysis = { analysis } />
182185 { busy && ! read . events . length ? < div className = "research-history-loading" role = "status" > { copy . loading } …</ div > : null }
183186 { value ?. nodes . length ? < div className = { `research-history-layout${ selected || selectedMembers ?. length ? " has-selection" : "" } ` } >
184187 { lens === "timeline" && timeline ? < div className = "research-history-canvas is-timeline" ref = { graph } tabIndex = { - 1 } >
@@ -249,6 +252,5 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0}: {
249252 { recordsOpen ? < > < ol className = "research-history-records" > { value . events . slice ( 0 , recordLimit ) . map ( event => < li key = { event . event_id } > < time > { timeLabel ( event . recorded_at ) } </ time > < span > { event . title } · { stateLabel ( event . status ) } { event . summary ? ` — ${ event . summary } ` : "" } </ span > { event . parent_id ? < code > { copy . group } : { event . parent_id } </ code > : null } { event . evidence_refs . map ( ref => < code key = { ref } > { ref } </ code > ) } { event . run_id ? < code > { event . run_id } </ code > : null } </ li > ) } </ ol >
250253 { recordLimit < value . events . length ? < button type = "button" onClick = { ( ) => setRecordLimit ( n => n + 50 ) } > { zh ? "继续展开记录" : "Show more records" } · { Math . min ( recordLimit , value . events . length ) } / { value . events . length } </ button > : null } </ > : null }
251254 </ details > : null }
252- < p className = "research-history-note" id = { `${ presentationId } -analysis` } > { copy . unavailable } </ p >
253255 </ section > ;
254256}
0 commit comments