@@ -38,8 +38,7 @@ function uniqueLayers(layers) {
3838}
3939
4040/**
41- * Every dense per-page array that must stay index-aligned with `pageMetrics`.
42- * Delete/move apply the same splice to each. (Derived image caches are handled separately by `clearImageCaches`.)
41+ * Every dense per-page array that must stay index-aligned with `pageMetrics` under page delete/move.
4342 * @param {ScribeDoc } doc
4443 * @returns {Array<Array<any>> }
4544 */
@@ -58,7 +57,8 @@ function densePageArrays(doc) {
5857}
5958
6059/**
61- * Drop the derived image caches so they re-render against the edited order (via `sourcePageN`).
60+ * Drop the full-resolution render caches so they re-render against the edited page order.
61+ * Thumbnails are deliberately not among these; page ops reorder that cache in place via `remapThumbnails` instead of re-rendering it.
6262 * @param {ScribeDoc } doc
6363 */
6464function clearImageCaches ( doc ) {
@@ -67,7 +67,6 @@ function clearImageCaches(doc) {
6767 images . binary . length = 0 ;
6868 images . nativeProps . length = 0 ;
6969 images . binaryProps . length = 0 ;
70- images . thumbnails . length = 0 ;
7170}
7271
7372/**
@@ -192,6 +191,19 @@ function remapOutlineByTags(doc, tags) {
192191 setDocOutline ( doc , remapOutline ( doc . outline , ( old ) => ( newByOld . has ( old ) ? newByOld . get ( old ) : null ) ) ) ;
193192}
194193
194+ /**
195+ * Reorder the retained thumbnail cache to match a page-order edit instead of dropping it:
196+ * a reorder leaves each thumbnail's pixels unchanged, so re-rendering would be waste.
197+ * @param {ScribeDoc } doc
198+ * @param {Array<?number> } tags - tags[newPos] is the pre-edit index of the page now at newPos, or null for a freshly inserted page.
199+ */
200+ function remapThumbnails ( doc , tags ) {
201+ const { thumbnails } = doc . images ;
202+ const remapped = tags . map ( ( old ) => ( old == null ? undefined : thumbnails [ old ] ) ) ;
203+ thumbnails . length = 0 ;
204+ for ( const t of remapped ) thumbnails . push ( t ) ;
205+ }
206+
195207/**
196208 * Re-align the document's FOREIGN render sources (the pools of pages copied in from other documents) with a restored page state,
197209 * so undo/redo of a cross-document paste tracks its source rather than leaking it.
@@ -228,6 +240,8 @@ function capturePageState(doc) {
228240 // Foreign (cross-document) render sources this state references, captured so undo/redo can re-register or release them.
229241 // The primary source is excluded because page ops never add or remove it.
230242 foreignSources : new Map ( [ ...doc . images . sources ] . filter ( ( [ id ] ) => id !== primaryId ) ) ,
243+ // The retained thumbnail cache, so undo/redo restore its slot alignment too (no preview re-render on either).
244+ thumbnails : [ ...doc . images . thumbnails ] ,
231245 } ;
232246}
233247
@@ -244,6 +258,10 @@ function restorePageState(doc, snap) {
244258 setDocOutline ( doc , cloneOutline ( snap . outline ) ) ;
245259 renumberPages ( doc ) ;
246260 clearImageCaches ( doc ) ;
261+ // Restore the retained thumbnail cache in lockstep with the page order (clearImageCaches no longer drops it).
262+ const { thumbnails } = doc . images ;
263+ thumbnails . length = 0 ;
264+ for ( const t of snap . thumbnails ) thumbnails . push ( t ) ;
247265 doc . inputData . pageCount = snap . pageCount ;
248266 doc . images . pageCount = snap . pageCount ;
249267 reconcileForeignSources ( doc , snap . foreignSources ) ;
@@ -423,6 +441,7 @@ export class ScribeDoc {
423441 const tags = this . pageMetrics . map ( ( _ , k ) => k ) ;
424442 for ( const arr of [ ...densePageArrays ( this ) , tags ] ) if ( i < arr . length ) arr . splice ( i , 1 ) ;
425443 remapOutlineByTags ( this , tags ) ;
444+ remapThumbnails ( this , tags ) ;
426445 clearImageCaches ( this ) ;
427446 renumberPages ( this ) ;
428447 this . inputData . pageCount = this . pageMetrics . length ;
@@ -447,6 +466,7 @@ export class ScribeDoc {
447466 arr . splice ( to , 0 , item ) ;
448467 }
449468 remapOutlineByTags ( this , tags ) ;
469+ remapThumbnails ( this , tags ) ;
450470 clearImageCaches ( this ) ;
451471 renumberPages ( this ) ;
452472 } ) ;
@@ -467,6 +487,7 @@ export class ScribeDoc {
467487 for ( const i of sorted ) if ( i < arr . length ) arr . splice ( i , 1 ) ;
468488 }
469489 remapOutlineByTags ( this , tags ) ;
490+ remapThumbnails ( this , tags ) ;
470491 clearImageCaches ( this ) ;
471492 renumberPages ( this ) ;
472493 this . inputData . pageCount = this . pageMetrics . length ;
@@ -494,6 +515,7 @@ export class ScribeDoc {
494515 arr . splice ( Math . max ( 0 , Math . min ( to , arr . length ) ) , 0 , ...pulled ) ;
495516 }
496517 remapOutlineByTags ( this , tags ) ;
518+ remapThumbnails ( this , tags ) ;
497519 clearImageCaches ( this ) ;
498520 renumberPages ( this ) ;
499521 } ) ;
@@ -560,6 +582,7 @@ export class ScribeDoc {
560582 spliceFull ( this . inputData . ocrApplied , bundles . map ( ( b ) => b . ocrApplied ) ) ;
561583 spliceFull ( tags , bundles . map ( ( ) => null ) ) ;
562584 remapOutlineByTags ( this , tags ) ;
585+ remapThumbnails ( this , tags ) ;
563586
564587 // Register a copied page's foreign render source so it keeps rendering and subsetting from its origin.
565588 for ( const b of bundles ) {
0 commit comments