@@ -6,7 +6,7 @@ import { recalcTarget } from "../recalcRefresh";
66import { CatalogSidebar } from "../components/CatalogSidebar" ;
77import { BuckarooEmbed } from "../components/BuckarooEmbed" ;
88import { VegaChart } from "../components/VegaChart" ;
9- import type { Entry , AppError , EntryDetail , EntryCache } from "../types" ;
9+ import type { Entry , AppError , EntryDetail , EntryCache , SessionResult } from "../types" ;
1010
1111// ── Error banner ──────────────────────────────────────────────────────────────
1212
@@ -91,11 +91,16 @@ function EntryDetailPane({ project, hash }: { project: string; hash: string }) {
9191 . catch ( ( ) => setLoading ( false ) ) ;
9292 } , [ project , hash ] ) ;
9393
94- if ( loading ) return < div className = "meta" > loading…</ div > ;
94+ if ( loading )
95+ return (
96+ < div className = "spinner-row" >
97+ < span className = "spinner" /> loading…
98+ </ div >
99+ ) ;
95100 if ( ! detail ) return < div className = "meta" > entry not found</ div > ;
96101
97102 const { manifest, alias, version, forensic_history, prompt_history, chart_spec,
98- display_config, build_artifacts, total_rows, buckaroo_session , buckaroo_ws_base , code } = detail ;
103+ display_config, build_artifacts, total_rows, code } = detail ;
99104 const diffProvenance = display_config ?. diff_provenance ;
100105
101106 const handleSaveCode = async ( ) => {
@@ -109,9 +114,6 @@ function EntryDetailPane({ project, hash }: { project: string; hash: string }) {
109114 }
110115 } ;
111116
112- const wsUrl =
113- buckaroo_session && buckaroo_ws_base ? `${ buckaroo_ws_base } /ws/${ buckaroo_session } ` : null ;
114-
115117 return (
116118 < div className = "detail entry-detail" >
117119 < div className = "entry-header" >
@@ -234,15 +236,7 @@ function EntryDetailPane({ project, hash }: { project: string; hash: string }) {
234236 { " " } matched
235237 </ div >
236238 ) }
237- { wsUrl ? (
238- < BuckarooEmbed wsUrl = { wsUrl } className = "buckaroo-embed" />
239- ) : (
240- < div className = "meta" >
241- { total_rows > 0
242- ? `${ total_rows } rows (Buckaroo not available — run tallyman with --buckaroo)`
243- : "no rows" }
244- </ div >
245- ) }
239+ < BuckarooDataPane project = { project } hash = { manifest . content_hash } totalRows = { total_rows } />
246240 </ div >
247241
248242 { tab === "code" && (
@@ -306,6 +300,85 @@ function EntryDetailPane({ project, hash }: { project: string; hash: string }) {
306300 ) ;
307301}
308302
303+ // ── Lazy Buckaroo grid: spinner → grid, or a precise error + retry (#133) ─────
304+
305+ function BuckarooDataPane ( { project, hash, totalRows } : { project : string ; hash : string ; totalRows : number } ) {
306+ const [ result , setResult ] = useState < SessionResult | null > ( null ) ;
307+ const [ attempt , setAttempt ] = useState ( 0 ) ;
308+
309+ useEffect ( ( ) => {
310+ let cancelled = false ;
311+ setResult ( null ) ;
312+ api
313+ . session ( project , hash )
314+ . then ( ( d ) => {
315+ if ( ! cancelled ) setResult ( d ) ;
316+ } )
317+ . catch ( ( e ) => {
318+ if ( ! cancelled )
319+ setResult ( { status : "error" , ws_url : null , detail : e instanceof Error ? e . message : String ( e ) } ) ;
320+ } ) ;
321+ return ( ) => {
322+ cancelled = true ;
323+ } ;
324+ } , [ project , hash , attempt ] ) ;
325+
326+ if ( result === null ) {
327+ return (
328+ < div className = "buckaroo-loading" >
329+ < span className = "spinner" /> loading data…
330+ </ div >
331+ ) ;
332+ }
333+ if ( result . status === "ok" && result . ws_url ) {
334+ return < BuckarooEmbed wsUrl = { result . ws_url } className = "buckaroo-embed" /> ;
335+ }
336+
337+ const retry = ( ) => setAttempt ( ( n ) => n + 1 ) ;
338+
339+ if ( result . status === "unavailable" || result . status === "no_build" ) {
340+ return (
341+ < div className = "meta" >
342+ { result . status === "no_build"
343+ ? "no build for this entry"
344+ : totalRows > 0
345+ ? `${ totalRows } rows (Buckaroo not available — run tallyman with --buckaroo)`
346+ : "no rows" }
347+ { result . status === "unavailable" && (
348+ < >
349+ { " · " }
350+ < button type = "button" className = "link-btn" onClick = { retry } >
351+ retry
352+ </ button >
353+ </ >
354+ ) }
355+ </ div >
356+ ) ;
357+ }
358+
359+ // timeout | error — surface what went wrong and a way out.
360+ return (
361+ < div className = "buckaroo-error" >
362+ < strong >
363+ { result . status === "timeout"
364+ ? "Buckaroo timed out loading this entry."
365+ : "Buckaroo couldn’t load this entry." }
366+ </ strong >
367+ { result . detail && < div className = "meta detail" > { result . detail } </ div > }
368+ < div className = "meta" >
369+ < button type = "button" className = "link-btn" onClick = { retry } >
370+ retry
371+ </ button >
372+ { " · to debug, check " }
373+ < code > ~/.tallyman-notebooks/projects/{ project } /buckaroo.log</ code >
374+ { " and the companion log, or run the " }
375+ < code > debug-tallyman</ code >
376+ { " skill." }
377+ </ div >
378+ </ div >
379+ ) ;
380+ }
381+
309382// ── Per-expression cache footprint (metadata tab) ─────────────────────────────
310383
311384function ShareBar ( { value, total } : { value : number ; total : number } ) {
0 commit comments