@@ -17,7 +17,7 @@ With build controls at the top (build, force build, clean, etc.)
17
17
*/
18
18
19
19
import type { TabsProps } from "antd" ;
20
- import { Spin , Tabs , Tag } from "antd" ;
20
+ import { List as AntdList , Spin , Tabs , Tag } from "antd" ;
21
21
import { List } from "immutable" ;
22
22
import { useCallback , useMemo , useState } from "react" ;
23
23
import { useIntl } from "react-intl" ;
@@ -58,6 +58,12 @@ interface OutputProps {
58
58
59
59
type TabType = "pdf" | "contents" | "files" | "build" | "errors" ;
60
60
61
+ interface FileListItem {
62
+ path : string ;
63
+ isMain : boolean ;
64
+ summary : string ;
65
+ }
66
+
61
67
export function Output ( props : OutputProps ) {
62
68
const {
63
69
id,
@@ -120,6 +126,13 @@ export function Output(props: OutputProps) {
120
126
// List of LaTeX files in the project
121
127
const switch_to_files : List < string > = useRedux ( [ name , "switch_to_files" ] ) ;
122
128
129
+ // File summaries state with caching (1 minute max)
130
+ const [ fileSummaries , setFileSummaries ] = useState < Record < string , string > > (
131
+ { } ,
132
+ ) ;
133
+ const [ lastSummariesFetch , setLastSummariesFetch ] = useState < number > ( 0 ) ;
134
+ const [ summariesLoading , setSummariesLoading ] = useState < boolean > ( false ) ;
135
+
123
136
// Update table of contents when component mounts
124
137
useEffect ( ( ) => {
125
138
// We have to do this update
@@ -232,6 +245,37 @@ export function Output(props: OutputProps) {
232
245
return { errors, warnings, typesetting } ;
233
246
} , [ build_logs , knitr ] ) ;
234
247
248
+ // Function to generate basic file summaries (placeholder for future implementation)
249
+ const generateFileSummaries = useCallback ( ( ) => {
250
+ if ( ! switch_to_files || switch_to_files . size === 0 ) return ;
251
+
252
+ const now = Date . now ( ) ;
253
+ const oneMinute = 60 * 1000 ;
254
+
255
+ // Only update if it's been more than 1 minute since last fetch
256
+ if ( now - lastSummariesFetch < oneMinute ) return ;
257
+
258
+ setSummariesLoading ( true ) ;
259
+
260
+ // Generate basic summaries - this is scaffolding for future implementation
261
+ const summaries : Record < string , string > = { } ;
262
+ switch_to_files . forEach ( ( filePath ) => {
263
+ // Basic placeholder - can be enhanced with actual file content analysis
264
+ summaries [ filePath ] = "LaTeX document" ;
265
+ } ) ;
266
+
267
+ setFileSummaries ( summaries ) ;
268
+ setLastSummariesFetch ( now ) ;
269
+ setSummariesLoading ( false ) ;
270
+ } , [ switch_to_files , lastSummariesFetch ] ) ;
271
+
272
+ // Generate file summaries when files change
273
+ React . useEffect ( ( ) => {
274
+ if ( switch_to_files && switch_to_files . size > 1 ) {
275
+ generateFileSummaries ( ) ;
276
+ }
277
+ } , [ switch_to_files , generateFileSummaries ] ) ;
278
+
235
279
// No automatic tab switching - let user control tabs manually
236
280
// Errors are indicated with red exclamation icon only
237
281
@@ -324,31 +368,59 @@ export function Output(props: OutputProps) {
324
368
return a . localeCompare ( b ) ;
325
369
} ) ;
326
370
371
+ const listData = sortedFiles . toJS ( ) . map ( ( filePath : string ) => ( {
372
+ path : filePath ,
373
+ isMain : filePath === path ,
374
+ summary : fileSummaries [ filePath ] || "Loading..." ,
375
+ } ) ) ;
376
+
327
377
return {
328
378
key : "files" ,
329
379
label : (
330
380
< span style = { { display : "flex" , alignItems : "center" , gap : "2px" } } >
331
381
< Icon name = "file" />
332
382
Files
383
+ { summariesLoading && < Spin size = "small" /> }
333
384
</ span >
334
385
) ,
335
386
children : (
336
- < div className = "smc-vfill" style = { { padding : "10px" } } >
337
- { sortedFiles . map ( ( filePath ) => (
338
- < div
339
- key = { filePath }
340
- style = { {
341
- padding : "8px" ,
342
- cursor : "pointer" ,
343
- borderBottom : `1px solid ${ COLORS . GRAY_LL } ` ,
344
- fontFamily : "monospace" ,
345
- fontSize : "12px" ,
346
- } }
347
- onClick = { ( ) => actions . switch_to_file ( filePath ) }
348
- >
349
- { path === filePath ? < b > { filePath } (main)</ b > : filePath }
350
- </ div >
351
- ) ) }
387
+ < div
388
+ className = "smc-vfill"
389
+ style = { { padding : "10px" , overflowY : "auto" } }
390
+ >
391
+ < AntdList
392
+ size = "small"
393
+ dataSource = { listData }
394
+ renderItem = { ( item : FileListItem ) => (
395
+ < AntdList . Item
396
+ style = { {
397
+ cursor : "pointer" ,
398
+ padding : "12px 8px" ,
399
+ } }
400
+ onClick = { ( ) => actions . switch_to_file ( item . path ) }
401
+ >
402
+ < AntdList . Item . Meta
403
+ title = {
404
+ < div style = { { fontFamily : "monospace" , fontSize : "13px" } } >
405
+ { item . isMain ? < b > { item . path } (main)</ b > : item . path }
406
+ </ div >
407
+ }
408
+ description = {
409
+ < div
410
+ style = { {
411
+ fontSize : "12px" ,
412
+ color : COLORS . GRAY_D ,
413
+ marginTop : "4px" ,
414
+ whiteSpace : "pre-wrap" ,
415
+ } }
416
+ >
417
+ { item . summary }
418
+ </ div >
419
+ }
420
+ />
421
+ </ AntdList . Item >
422
+ ) }
423
+ />
352
424
</ div >
353
425
) ,
354
426
} ;
0 commit comments