@@ -141,14 +141,14 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
141141 ( f ) =>
142142 f . includes ( "comp_" ) ||
143143 f . includes ( ".svg" ) ||
144- f === "report.txt" ||
144+ f . endsWith ( "report.txt" ) ||
145145 ( f . includes ( "_metrics.tsv" ) && ! f . toLowerCase ( ) . includes ( "pca" ) ) ||
146- ( f . startsWith ( "tedana_20" ) && f . endsWith ( ".tsv" ) ) ||
146+ ( f . startsWith ( "tedana_20" ) || f . endsWith ( "_tedana_log .tsv" ) ) ||
147147 ( f . includes ( "_mixing.tsv" ) && ! f . toLowerCase ( ) . includes ( "pca" ) && ! f . toLowerCase ( ) . includes ( "orth" ) ) ||
148148 ( f . includes ( "_components.nii.gz" ) && f . toLowerCase ( ) . includes ( "ica" ) && ! f . includes ( "stat-z" ) && ! f . includes ( "echo-" ) ) ||
149149 f === "betas_OC.nii.gz" ||
150150 f . includes ( "_mask.nii" ) ||
151- f . includes ( "CrossComponent_metrics.json" ) ||
151+ ( f . includes ( "CrossComponent_metrics.json" ) && ! f . toLowerCase ( ) . includes ( "pca" ) ) ||
152152 ( f . includes ( "cross_component_metrics.json" ) && ! f . toLowerCase ( ) . includes ( "pca" ) ) ||
153153 f === "manual_classification.tsv" ||
154154 // QC NIfTI files
@@ -190,8 +190,9 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
190190 // Repetition time from registry
191191 let repetitionTime = null ;
192192
193- // Process files via HTTP fetch
194- for ( const filepath of relevantFiles ) {
193+ // Process files via HTTP fetch (parallel)
194+ try {
195+ const filePromises = relevantFiles . map ( async ( filepath ) => {
195196 const filename = filepath . split ( "/" ) . pop ( ) ;
196197
197198 try {
@@ -221,7 +222,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
221222 }
222223
223224 // Report info
224- if ( filename === "report.txt" ) {
225+ if ( filename . endsWith ( "report.txt" ) ) {
225226 const response = await fetch ( `/${ filepath } ` ) ;
226227 info = await response . text ( ) ;
227228 setLoadingProgress ( ( prev ) => ( { ...prev , current : prev . current + 1 } ) ) ;
@@ -243,7 +244,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
243244 }
244245
245246 // Dataset path
246- if ( filename . startsWith ( "tedana_20" ) && filename . endsWith ( ".tsv" ) ) {
247+ if ( filename . startsWith ( "tedana_20" ) || filename . endsWith ( "_tedana_log .tsv" ) ) {
247248 const response = await fetch ( `/${ filepath } ` ) ;
248249 const text = await response . text ( ) ;
249250 const lines = text . split ( "\n" ) ;
@@ -271,12 +272,14 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
271272 if ( ( filename . includes ( "_components.nii.gz" ) && filename . toLowerCase ( ) . includes ( "ica" ) && ! filename . includes ( "stat-z" ) && ! filename . includes ( "echo-" ) ) || filename === "betas_OC.nii.gz" ) {
272273 // Extract TR from NIfTI header using a Range request (first 4KB is enough to
273274 // decompress the header from gzip), independent of loading the full file.
275+ // Only trust 206 Partial Content — if the proxy doesn't forward Range headers,
276+ // the server returns 200 with the full file, which would hang on arrayBuffer().
274277 if ( ! repetitionTime ) {
275278 try {
276279 const headerResponse = await fetch ( `/${ filepath } ` , {
277280 headers : { Range : "bytes=0-4095" } ,
278281 } ) ;
279- if ( headerResponse . ok || headerResponse . status === 206 ) {
282+ if ( headerResponse . status === 206 ) {
280283 const headerBuffer = await headerResponse . arrayBuffer ( ) ;
281284 const tr = await extractTRFromNifti ( headerBuffer ) ;
282285 if ( tr ) {
@@ -288,15 +291,9 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
288291 // Range requests not supported; TR won't be extracted from header
289292 }
290293 }
291- // Always set URL so BrainViewer can load even if buffer fails
294+ // Use URL — BrainViewer prefers URL over buffer anyway, and skipping the
295+ // full download avoids hanging Promise.all on large files through the proxy.
292296 niftiUrl = `/${ filepath } ` ;
293- // Also try loading the full buffer (fails gracefully for very large files)
294- try {
295- const response = await fetch ( `/${ filepath } ` ) ;
296- niftiBuffer = await response . arrayBuffer ( ) ;
297- } catch {
298- console . warn ( "[Rica] NIfTI too large for ArrayBuffer, Niivue will load from URL" ) ;
299- }
300297 setLoadingProgress ( ( prev ) => ( { ...prev , current : prev . current + 1 } ) ) ;
301298 }
302299
@@ -308,7 +305,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
308305 }
309306
310307 // Cross-component metrics (for elbow thresholds)
311- if ( filename . includes ( "CrossComponent_metrics.json" ) ||
308+ if ( ( filename . includes ( "CrossComponent_metrics.json" ) && ! filename . toLowerCase ( ) . includes ( "pca" ) ) ||
312309 ( filename . includes ( "cross_component_metrics.json" ) && ! filename . toLowerCase ( ) . includes ( "pca" ) ) ) {
313310 const response = await fetch ( `/${ filepath } ` ) ;
314311 crossComponentMetrics = await response . json ( ) ;
@@ -385,7 +382,8 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
385382 } catch ( error ) {
386383 console . error ( `Error fetching file ${ filepath } :` , error ) ;
387384 }
388- }
385+ } ) ;
386+ await Promise . all ( filePromises ) ;
389387
390388 // Sort component figures by name
391389 compFigures . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
@@ -419,6 +417,29 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
419417 statusTableData,
420418 repetitionTime,
421419 } ) ;
420+ } catch ( err ) {
421+ console . error ( "[Rica] loadFromServer failed:" , err ) ;
422+ onDataLoad ( {
423+ componentFigures : compFigures ,
424+ carpetFigures,
425+ diagnosticFigures,
426+ components : [ components ] ,
427+ info,
428+ originalData : [ originalData ] ,
429+ dirPath,
430+ mixingMatrix,
431+ niftiBuffer,
432+ niftiUrl,
433+ maskBuffer,
434+ crossComponentMetrics,
435+ qcNiftiBuffers,
436+ externalRegressorsFigure,
437+ hasManualClassifications : manualClassificationData && manualClassificationData . length > 0 ,
438+ decisionTreeData,
439+ statusTableData,
440+ repetitionTime,
441+ } ) ;
442+ }
422443 } ,
423444 [ onDataLoad , onLoadingStart ]
424445 ) ;
@@ -457,15 +478,15 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
457478 ( f ) =>
458479 f . name . includes ( "comp_" ) ||
459480 f . name . includes ( ".svg" ) ||
460- f . name === "report.txt" ||
481+ f . name . endsWith ( "report.txt" ) ||
461482 ( f . name . includes ( "_metrics.tsv" ) && ! f . name . toLowerCase ( ) . includes ( "pca" ) ) ||
462- ( f . name . startsWith ( "tedana_20" ) && f . name . endsWith ( ".tsv" ) ) ||
483+ ( f . name . startsWith ( "tedana_20" ) || f . name . endsWith ( "_tedana_log .tsv" ) ) ||
463484 // New files for Niivue integration
464485 ( f . name . includes ( "_mixing.tsv" ) && ! f . name . toLowerCase ( ) . includes ( "pca" ) && ! f . name . toLowerCase ( ) . includes ( "orth" ) ) ||
465486 ( f . name . includes ( "_components.nii.gz" ) && f . name . toLowerCase ( ) . includes ( "ica" ) && ! f . name . includes ( "stat-z" ) && ! f . name . includes ( "echo-" ) ) ||
466487 f . name === "betas_OC.nii.gz" ||
467488 f . name . includes ( "_mask.nii" ) ||
468- f . name . includes ( "CrossComponent_metrics.json" ) ||
489+ ( f . name . includes ( "CrossComponent_metrics.json" ) && ! f . name . toLowerCase ( ) . includes ( "pca" ) ) ||
469490 ( f . name . includes ( "cross_component_metrics.json" ) && ! f . name . toLowerCase ( ) . includes ( "pca" ) ) ||
470491 f . name === "manual_classification.tsv" ||
471492 // QC NIfTI files
@@ -534,7 +555,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
534555 }
535556
536557 // Report info
537- if ( filename === "report.txt" ) {
558+ if ( filename . endsWith ( "report.txt" ) ) {
538559 info = await readFileAsText ( file ) ;
539560 setLoadingProgress ( ( prev ) => ( { ...prev , current : prev . current + 1 } ) ) ;
540561 }
@@ -554,7 +575,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
554575 }
555576
556577 // Dataset path
557- if ( filename . startsWith ( "tedana_20" ) && filename . endsWith ( ".tsv" ) ) {
578+ if ( filename . startsWith ( "tedana_20" ) || filename . endsWith ( "_tedana_log .tsv" ) ) {
558579 const text = await readFileAsText ( file ) ;
559580 // Look for the line containing "Using output directory:"
560581 const lines = text . split ( "\n" ) ;
@@ -607,7 +628,7 @@ function IntroPopup({ onDataLoad, onLoadingStart, closePopup, isLoading, isDark
607628 }
608629
609630 // Cross-component metrics (for elbow thresholds)
610- if ( filename . includes ( "CrossComponent_metrics.json" ) ||
631+ if ( ( filename . includes ( "CrossComponent_metrics.json" ) && ! filename . toLowerCase ( ) . includes ( "pca" ) ) ||
611632 ( filename . includes ( "cross_component_metrics.json" ) && ! filename . toLowerCase ( ) . includes ( "pca" ) ) ) {
612633 const text = await readFileAsText ( file ) ;
613634 crossComponentMetrics = JSON . parse ( text ) ;
0 commit comments