@@ -439,6 +439,39 @@ function ImagePreview({ file }: { file: WorkspaceFileRecord }) {
439439 )
440440}
441441
442+ const pptxSlideCache = new Map < string , string [ ] > ( )
443+
444+ function pptxCacheKey ( fileId : string , dataUpdatedAt : number ) : string {
445+ return `${ fileId } :${ dataUpdatedAt } `
446+ }
447+
448+ async function renderPptxSlides (
449+ data : Uint8Array ,
450+ onSlide : ( src : string , index : number ) => void ,
451+ cancelled : ( ) => boolean
452+ ) : Promise < void > {
453+ const { PPTXViewer } = await import ( 'pptxviewjs' )
454+ if ( cancelled ( ) ) return
455+
456+ const W = 1920
457+ const H = 1080
458+
459+ const canvas = document . createElement ( 'canvas' )
460+ canvas . width = W
461+ canvas . height = H
462+ const viewer = new PPTXViewer ( { canvas } )
463+ await viewer . loadFile ( data )
464+ const count = viewer . getSlideCount ( )
465+ if ( cancelled ( ) || count === 0 ) return
466+
467+ for ( let i = 0 ; i < count ; i ++ ) {
468+ if ( cancelled ( ) ) break
469+ if ( i === 0 ) await viewer . render ( )
470+ else await viewer . goToSlide ( i )
471+ onSlide ( canvas . toDataURL ( 'image/jpeg' , 0.85 ) , i )
472+ }
473+ }
474+
442475function PptxPreview ( {
443476 file,
444477 workspaceId,
@@ -455,11 +488,19 @@ function PptxPreview({
455488 dataUpdatedAt,
456489 } = useWorkspaceFileBinary ( workspaceId , file . id , file . key )
457490
458- const [ slides , setSlides ] = useState < string [ ] > ( [ ] )
491+ const cacheKey = pptxCacheKey ( file . id , dataUpdatedAt )
492+ const cached = pptxSlideCache . get ( cacheKey )
493+
494+ const [ slides , setSlides ] = useState < string [ ] > ( cached ?? [ ] )
459495 const [ rendering , setRendering ] = useState ( false )
460496 const [ renderError , setRenderError ] = useState < string | null > ( null )
461497
462498 useEffect ( ( ) => {
499+ if ( cached ) {
500+ setSlides ( cached )
501+ return
502+ }
503+
463504 let cancelled = false
464505
465506 async function render ( ) {
@@ -474,61 +515,33 @@ function PptxPreview({
474515 await fn ( pptx )
475516 const arrayBuffer = ( await pptx . write ( { outputType : 'arraybuffer' } ) ) as ArrayBuffer
476517 if ( cancelled ) return
477- const { PPTXViewer } = await import ( 'pptxviewjs' )
478518 const data = new Uint8Array ( arrayBuffer )
479- const probe = document . createElement ( 'canvas' )
480- const probeViewer = new PPTXViewer ( { canvas : probe } )
481- await probeViewer . loadFile ( data )
482- const count = probeViewer . getSlideCount ( )
483- if ( cancelled || count === 0 ) return
484- const dpr = window . devicePixelRatio || 1
485- const W = Math . round ( 1920 * dpr )
486- const H = Math . round ( 1080 * dpr )
487519 const images : string [ ] = [ ]
488- for ( let i = 0 ; i < count ; i ++ ) {
489- if ( cancelled ) break
490- const canvas = document . createElement ( 'canvas' )
491- canvas . width = W
492- canvas . height = H
493- const viewer = new PPTXViewer ( { canvas } )
494- await viewer . loadFile ( data )
495- if ( i > 0 ) await viewer . goToSlide ( i )
496- else await viewer . render ( )
497- images . push ( canvas . toDataURL ( 'image/png' ) )
498- }
499- if ( ! cancelled ) setSlides ( images )
520+ await renderPptxSlides (
521+ data ,
522+ ( src ) => {
523+ images . push ( src )
524+ if ( ! cancelled ) setSlides ( [ ...images ] )
525+ } ,
526+ ( ) => cancelled
527+ )
500528 return
501529 }
502530
503531 if ( ! fileData ) return
504- const { PPTXViewer } = await import ( 'pptxviewjs' )
505- if ( cancelled ) return
506-
507- const data = new Uint8Array ( fileData ! )
508- const probe = document . createElement ( 'canvas' )
509- const probeViewer = new PPTXViewer ( { canvas : probe } )
510- await probeViewer . loadFile ( data )
511- const count = probeViewer . getSlideCount ( )
512- if ( cancelled || count === 0 ) return
513-
514- const dpr = window . devicePixelRatio || 1
515- const W = Math . round ( 1920 * dpr )
516- const H = Math . round ( 1080 * dpr )
532+ const data = new Uint8Array ( fileData )
517533 const images : string [ ] = [ ]
518-
519- for ( let i = 0 ; i < count ; i ++ ) {
520- if ( cancelled ) break
521- const canvas = document . createElement ( 'canvas' )
522- canvas . width = W
523- canvas . height = H
524- const viewer = new PPTXViewer ( { canvas } )
525- await viewer . loadFile ( data )
526- if ( i > 0 ) await viewer . goToSlide ( i )
527- else await viewer . render ( )
528- images . push ( canvas . toDataURL ( 'image/png' ) )
534+ await renderPptxSlides (
535+ data ,
536+ ( src ) => {
537+ images . push ( src )
538+ if ( ! cancelled ) setSlides ( [ ...images ] )
539+ } ,
540+ ( ) => cancelled
541+ )
542+ if ( ! cancelled && images . length > 0 ) {
543+ pptxSlideCache . set ( cacheKey , images )
529544 }
530-
531- if ( ! cancelled ) setSlides ( images )
532545 } catch ( err ) {
533546 if ( ! cancelled ) {
534547 const msg = err instanceof Error ? err . message : 'Failed to render presentation'
@@ -544,7 +557,7 @@ function PptxPreview({
544557 return ( ) => {
545558 cancelled = true
546559 }
547- } , [ fileData , dataUpdatedAt , streamingContent ] )
560+ } , [ fileData , dataUpdatedAt , streamingContent , cacheKey , cached ] )
548561
549562 const error = fetchError
550563 ? fetchError instanceof Error
0 commit comments