@embedpdf/plugin-scroll 2.0.0-next.1 → 2.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +13 -1
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),e=require("@embedpdf/models");var a=(t=>(t.Vertical="vertical",t.Horizontal="horizontal",t))(a||{});class i{constructor(t){this.pageGap=t.pageGap??20,this.viewportGap=t.viewportGap??20,this.bufferSize=t.bufferSize??2}getVisibleRange(t,e,a){const i=this.getScrollOffset(t),o=i,r=i+this.getClientSize(t);let s=0;for(;s<e.length&&(e[s].offset+e[s].height)*a<=o;)s++;let n=s;for(;n<e.length&&e[n].offset*a<=r;)n++;return{start:Math.max(0,s-this.bufferSize),end:Math.min(e.length-1,n+this.bufferSize-1)}}handleScroll(t,e,a){const i=this.getVisibleRange(t,e,a),o=e.slice(i.start,i.end+1),r=this.calculatePageVisibility(o,t,a),s=r.map(t=>t.pageNumber),n=e.slice(i.start,i.end+1).flatMap(t=>t.index),g=this.determineCurrentPage(r),c=e[i.start],h=e[i.end],l=c?c.offset*a:0,u=h?(e[e.length-1].offset+e[e.length-1].height)*a-(h.offset+h.height)*a:0;return{currentPage:g,visiblePages:s,pageVisibilityMetrics:r,renderedPageIndexes:n,scrollOffset:{x:t.scrollLeft,y:t.scrollTop},startSpacing:l,endSpacing:u}}calculatePageVisibility(t,e,a){const i=[];return t.forEach(t=>{t.pageLayouts.forEach(o=>{const r=t.x*a,s=t.y*a,n=r+o.x*a,g=s+o.y*a,c=o.rotatedWidth*a,h=o.rotatedHeight*a,l=e.scrollLeft,u=e.scrollTop,d=l+e.clientWidth,m=u+e.clientHeight,p=Math.max(n,l),S=Math.max(g,u),y=Math.min(n+c,d),f=Math.min(g+h,m);if(p<y&&S<f){const t=y-p,e=f-S,r=c*h,s=t*e;i.push({pageNumber:o.pageNumber,viewportX:p-l,viewportY:S-u,visiblePercentage:s/r*100,original:{pageX:(p-n)/a,pageY:(S-g)/a,visibleWidth:t/a,visibleHeight:e/a,scale:1},scaled:{pageX:p-n,pageY:S-g,visibleWidth:t,visibleHeight:e,scale:a}})}})}),i}determineCurrentPage(t){if(0===t.length)return 1;const e=Math.max(...t.map(t=>t.visiblePercentage)),a=t.filter(t=>t.visiblePercentage===e);return 1===a.length?a[0].pageNumber:a.sort((t,e)=>t.pageNumber-e.pageNumber)[0].pageNumber}getRectLocationForPage(t,e,a){const i=e.find(e=>e.pageNumbers.includes(t));if(!i)return null;const o=i.pageLayouts.find(e=>e.pageNumber===t);if(!o)return null;let r=0;if(a){const t=a.width;i.width<t&&(r=(t-i.width)/2)}return{origin:{x:i.x+o.x+r,y:i.y+o.y},size:{width:o.width,height:o.height}}}getScrollPositionForPage(t,a,i,o,r){const s=this.getTotalContentSize(a),n=this.getRectLocationForPage(t,a,s);if(!n)return null;const g=e.scalePosition(n.origin,i);if(r){const t=e.transformPosition({width:n.size.width,height:n.size.height},{x:r.x,y:r.y},o,i);return{x:g.x+t.x+this.viewportGap,y:g.y+t.y+this.viewportGap}}return{x:g.x+this.viewportGap,y:g.y+this.viewportGap}}getRectPositionForPage(t,a,i,o,r){const s=this.getTotalContentSize(a),n=this.getRectLocationForPage(t,a,s);if(!n)return null;const g=e.scalePosition(n.origin,i),c=e.transformRect({width:n.size.width,height:n.size.height},r,o,i);return{origin:{x:g.x+c.origin.x,y:g.y+c.origin.y},size:c.size}}}class o extends i{constructor(t){super(t)}createVirtualItems(t){let e=0;return t.map((t,a)=>{let i=0;const o=t.map(t=>{const e={pageNumber:t.index+1,pageIndex:t.index,x:i,y:0,width:t.size.width,height:t.size.height,rotatedWidth:t.rotatedSize.width,rotatedHeight:t.rotatedSize.height};return i+=t.rotatedSize.width+this.pageGap,e}),r=t.reduce((e,a,i)=>e+a.rotatedSize.width+(i<t.length-1?this.pageGap:0),0),s=Math.max(...t.map(t=>t.rotatedSize.height)),n={id:`item-${a}`,x:0,y:e,offset:e,width:r,height:s,pageLayouts:o,pageNumbers:t.map(t=>t.index+1),index:a};return e+=s+this.pageGap,n})}getTotalContentSize(t){if(0===t.length)return{width:0,height:0};return{width:Math.max(...t.map(t=>t.width)),height:t[t.length-1].y+t[t.length-1].height}}getScrollOffset(t){return t.scrollTop}getClientSize(t){return t.clientHeight}}class r extends i{constructor(t){super(t)}createVirtualItems(t){let e=0;return t.map((t,a)=>{let i=0;const o=t.map(t=>{const e={pageNumber:t.index+1,pageIndex:t.index,x:i,y:0,width:t.size.width,height:t.size.height,rotatedWidth:t.rotatedSize.width,rotatedHeight:t.rotatedSize.height};return i+=t.rotatedSize.width+this.pageGap,e}),r=t.reduce((e,a,i)=>e+a.rotatedSize.width+(i<t.length-1?this.pageGap:0),0),s=Math.max(...t.map(t=>t.rotatedSize.height)),n={id:`item-${a}`,x:e,y:0,offset:e,width:r,height:s,pageLayouts:o,pageNumbers:t.map(t=>t.index+1),index:a};return e+=r+this.pageGap,n})}getTotalContentSize(t){if(0===t.length)return{width:0,height:0};return{width:t[t.length-1].x+t[t.length-1].width,height:Math.max(...t.map(t=>t.height))}}getScrollOffset(t){return t.scrollLeft}getClientSize(t){return t.clientWidth}}const s="INIT_SCROLL_STATE",n="CLEANUP_SCROLL_STATE",g="UPDATE_DOCUMENT_SCROLL_STATE",c="SET_SCROLL_STRATEGY";function h(t,e){return{type:g,payload:{documentId:t,state:e}}}const l={isChanging:!1,targetPage:1,fromPage:1,startTime:0},u=(t,e)=>({startSpacing:t.startSpacing,endSpacing:t.endSpacing,totalWidth:t.totalContentSize.width*e,totalHeight:t.totalContentSize.height*e,pageGap:t.pageGap*e,strategy:t.strategy,items:t.renderedPageIndexes.map(a=>({...t.virtualItems[a],pageLayouts:t.virtualItems[a].pageLayouts.map(t=>({...t,rotatedWidth:t.rotatedWidth*e,rotatedHeight:t.rotatedHeight*e,width:t.width*e,height:t.height*e}))}))}),d=class extends t.BasePlugin{constructor(e,a,i){var o,r;super(e,a),this.id=e,this.config=i,this.strategies=new Map,this.layoutReady=new Set,this.initialLayoutFired=new Set,this.scrollerLayoutEmitters=new Map,this.pageChange$=t.createBehaviorEmitter(),this.scroll$=t.createBehaviorEmitter(),this.layoutChange$=t.createBehaviorEmitter(),this.pageChangeState$=t.createBehaviorEmitter(),this.layoutReady$=t.createBehaviorEmitter(),this.state$=t.createBehaviorEmitter(),this.viewport=this.registry.getPlugin("viewport").provides(),this.spread=(null==(o=this.registry.getPlugin("spread"))?void 0:o.provides())??null,this.viewport.onScrollActivity(t=>{const e=this.getDocumentState(t.documentId);(null==e?void 0:e.pageChangeState.isChanging)&&!t.activity.isSmoothScrolling&&this.completePageChange(t.documentId)}),null==(r=this.spread)||r.onSpreadChange(t=>{this.refreshDocumentLayout(t.documentId)}),this.viewport.onViewportChange(t=>{const e=this.getDocumentState(t.documentId);if(!e)return;const a=this.computeMetrics(t.documentId,t.metrics);this.layoutReady.has(t.documentId)?this.commitMetrics(t.documentId,a):this.commitMetrics(t.documentId,{...a,scrollOffset:e.scrollOffset})})}onDocumentLoadingStarted(e){const a=this.getCoreDocument(e);if(!a)return;const i=this.createDocumentState(a);this.dispatch(function(t,e){return{type:s,payload:{documentId:t,state:e}}}(e,i));const o=this.createStrategy(i.strategy);this.strategies.set(e,o),this.scrollerLayoutEmitters.set(e,t.createBehaviorEmitter())}onDocumentLoaded(t){var e;const a=this.getCoreDocument(t);a&&(this.dispatch(h(t,{totalPages:(null==(e=a.document)?void 0:e.pageCount)??0})),this.refreshDocumentLayout(t),this.logger.debug("ScrollPlugin","DocumentOpened",`Initialized scroll state for document: ${t}`))}onDocumentClosed(t){this.strategies.delete(t),this.layoutReady.delete(t),this.initialLayoutFired.delete(t);const e=this.scrollerLayoutEmitters.get(t);e&&(e.clear(),this.scrollerLayoutEmitters.delete(t)),this.dispatch(function(t){return{type:n,payload:t}}(t)),this.logger.debug("ScrollPlugin","DocumentClosed",`Cleaned up scroll state for document: ${t}`)}onScaleChanged(t){const e=this.coreState.core.documents[t];if(!e||"loaded"!==e.status)return;const a=this.viewport.forDocument(t),i=this.computeMetrics(t,a.getMetrics());this.commitMetrics(t,i)}onRotationChanged(t){this.refreshDocumentLayout(t)}onScrollerData(t,e){const a=this.scrollerLayoutEmitters.get(t);if(!a)throw new Error(`No scroller layout emitter found for document: ${t}`);return a.on(e)}getScrollerLayout(t){const e=this.getDocumentState(t),a=this.getCoreDocumentOrThrow(t);if(!e||!a)throw new Error(`Cannot get scroller layout for document: ${t}`);return u(e,a.scale)}setLayoutReady(t){if(this.layoutReady.has(t))return;const e=this.getDocumentState(t);if(!e)return;this.layoutReady.add(t);const a=!this.initialLayoutFired.has(t);a&&this.initialLayoutFired.add(t);this.viewport.forDocument(t).scrollTo({...e.scrollOffset,behavior:"instant"}),this.layoutReady$.emit({documentId:t,isInitial:a})}clearLayoutReady(t){this.layoutReady.delete(t)}buildCapability(){return{getCurrentPage:()=>this.getCurrentPage(),getTotalPages:()=>this.getTotalPages(),getPageChangeState:()=>this.getPageChangeState(),scrollToPage:t=>this.scrollToPage(t),scrollToNextPage:t=>this.scrollToNextPage(t),scrollToPreviousPage:t=>this.scrollToPreviousPage(t),getMetrics:t=>this.getMetrics(t),getLayout:()=>this.getLayout(),getRectPositionForPage:(t,e,a,i)=>this.getRectPositionForPage(t,e,a,i),forDocument:t=>this.createScrollScope(t),setScrollStrategy:(t,e)=>this.setScrollStrategyForDocument(t,e),getPageGap:()=>this.state.defaultPageGap,onPageChange:this.pageChange$.on,onScroll:this.scroll$.on,onLayoutChange:this.layoutChange$.on,onLayoutReady:this.layoutReady$.on,onPageChangeState:this.pageChangeState$.on,onStateChange:this.state$.on}}createScrollScope(t){return{getCurrentPage:()=>this.getCurrentPage(t),getTotalPages:()=>this.getTotalPages(t),getPageChangeState:()=>this.getPageChangeState(t),scrollToPage:e=>this.scrollToPage(e,t),scrollToNextPage:e=>this.scrollToNextPage(e,t),scrollToPreviousPage:e=>this.scrollToPreviousPage(e,t),getSpreadPagesWithRotatedSize:()=>this.getSpreadPagesWithRotatedSize(t),getMetrics:e=>this.getMetrics(e,t),getLayout:()=>this.getLayout(t),getRectPositionForPage:(e,a,i,o)=>this.getRectPositionForPage(e,a,i,o,t),setScrollStrategy:e=>this.setScrollStrategyForDocument(e,t),onPageChange:e=>this.pageChange$.on(a=>{a.documentId===t&&e(a)}),onScroll:e=>this.scroll$.on(a=>{a.documentId===t&&e(a.metrics)}),onLayoutChange:e=>this.layoutChange$.on(a=>{a.documentId===t&&e(a.layout)})}}getDocumentState(t){const e=t??this.getActiveDocumentId();return this.state.documents[e]??null}getDocumentStateOrThrow(t){const e=this.getDocumentState(t);if(!e)throw new Error(`Scroll state not found for document: ${t??"active"}`);return e}getStrategy(t){const e=t??this.getActiveDocumentId(),a=this.strategies.get(e);if(!a)throw new Error(`Strategy not found for document: ${e}`);return a}createStrategy(t){const e={pageGap:this.state.defaultPageGap,viewportGap:this.viewport.getViewportGap(),bufferSize:this.state.defaultBufferSize};return t===a.Horizontal?new r(e):new o(e)}createDocumentState(t){var e;return{virtualItems:[],totalPages:(null==(e=t.document)?void 0:e.pageCount)??0,currentPage:1,totalContentSize:{width:0,height:0},strategy:this.state.defaultStrategy,pageGap:this.state.defaultPageGap,visiblePages:[],pageVisibilityMetrics:[],renderedPageIndexes:[],scrollOffset:{x:0,y:0},startSpacing:0,endSpacing:0,pageChangeState:l}}startPageChange(t,e,a="smooth"){const i=this.getDocumentState(t);if(!i)return;const o={isChanging:!0,targetPage:e,fromPage:i.currentPage,startTime:Date.now()};this.dispatch(h(t,{pageChangeState:o})),"instant"===a&&this.completePageChange(t)}completePageChange(t){const e=this.getDocumentState(t);if(!e||!e.pageChangeState.isChanging)return;const a={isChanging:!1,targetPage:e.pageChangeState.targetPage,fromPage:e.pageChangeState.fromPage,startTime:e.pageChangeState.startTime};this.dispatch(h(t,{pageChangeState:a}))}computeLayout(t,e){const a=this.getStrategy(t),i=a.createVirtualItems(e);return{virtualItems:i,totalContentSize:a.getTotalContentSize(i)}}computeMetrics(t,e,a){const i=this.getCoreDocumentOrThrow(t),o=this.getDocumentState(t),r=this.getStrategy(t);if(!o)throw new Error(`Document state not found: ${t}`);return r.handleScroll(e,a??o.virtualItems,i.scale)}commitMetrics(t,e){const a=this.getDocumentState(t);a&&(this.dispatch(h(t,e)),this.scroll$.emit({documentId:t,metrics:e}),e.currentPage!==a.currentPage&&this.pageChange$.emit({documentId:t,pageNumber:e.currentPage,totalPages:a.totalPages}),this.pushScrollerLayout(t))}pushScrollerLayout(t){const e=this.scrollerLayoutEmitters.get(t);if(e)try{const a=this.getScrollerLayout(t);e.emit(a)}catch(a){}}refreshDocumentLayout(t){const e=this.coreState.core.documents[t],a=this.getDocumentState(t);if(!e||!a||"loaded"!==e.status)return;const i=this.getSpreadPagesWithRotatedSize(t),o=this.computeLayout(t,i),r=this.viewport.forDocument(t),s=this.computeMetrics(t,r.getMetrics(),o.virtualItems);this.dispatch(h(t,{...o,...s})),this.layoutChange$.emit({documentId:t,layout:o}),this.pushScrollerLayout(t)}getSpreadPagesWithRotatedSize(t){var a,i;const o=t??this.getActiveDocumentId(),r=this.coreState.core.documents[o];if(!r)throw new Error(`Document ${o} not loaded`);return((null==(a=this.spread)?void 0:a.forDocument(o).getSpreadPages())||(null==(i=r.document)?void 0:i.pages.map(t=>[t]))||[]).map(t=>t.map(t=>({...t,rotatedSize:e.transformSize(t.size,r.rotation,1)})))}getCurrentPage(t){return this.getDocumentStateOrThrow(t).currentPage}getTotalPages(t){return this.getDocumentStateOrThrow(t).totalPages}getPageChangeState(t){return this.getDocumentStateOrThrow(t).pageChangeState}scrollToPage(t,e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.getCoreDocumentOrThrow(a),{pageNumber:s,behavior:n="smooth",pageCoordinates:g,center:c=!1}=t;this.startPageChange(a,s,n);const h=o.getScrollPositionForPage(s,i.virtualItems,r.scale,r.rotation,g);if(h){this.viewport.forDocument(a).scrollTo({...h,behavior:n,center:c})}else this.completePageChange(a)}scrollToNextPage(t="smooth",e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.getCoreDocumentOrThrow(a),s=i.virtualItems.findIndex(t=>t.pageNumbers.includes(i.currentPage));if(s>=0&&s<i.virtualItems.length-1){const e=i.virtualItems[s+1].pageNumbers[0];this.startPageChange(a,e,t);const n=o.getScrollPositionForPage(e,i.virtualItems,r.scale,r.rotation);if(n){this.viewport.forDocument(a).scrollTo({...n,behavior:t})}else this.completePageChange(a)}}scrollToPreviousPage(t="smooth",e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.coreState.core.documents[a],s=i.virtualItems.findIndex(t=>t.pageNumbers.includes(i.currentPage));if(s>0){const e=i.virtualItems[s-1].pageNumbers[0];this.startPageChange(a,e,t);const n=o.getScrollPositionForPage(e,i.virtualItems,r.scale,r.rotation);if(n){this.viewport.forDocument(a).scrollTo({...n,behavior:t})}else this.completePageChange(a)}}getMetrics(t,e){const a=e??this.getActiveDocumentId();if(t)return this.computeMetrics(a,t);const i=this.viewport.forDocument(a);return this.computeMetrics(a,i.getMetrics())}getLayout(t){const e=this.getDocumentStateOrThrow(t);return{virtualItems:e.virtualItems,totalContentSize:e.totalContentSize}}getRectPositionForPage(t,e,a,i,o){const r=o??this.getActiveDocumentId(),s=this.getDocumentStateOrThrow(r),n=this.getStrategy(r),g=this.getCoreDocumentOrThrow(r);return n.getRectPositionForPage(t+1,s.virtualItems,a??g.scale,i??g.rotation,e)}setScrollStrategyForDocument(t,e){const a=e??this.getActiveDocumentId(),i=this.getDocumentState(a);if(!i||i.strategy===t)return;const o=this.createStrategy(t);this.strategies.set(a,o),this.dispatch(function(t,e){return{type:c,payload:{documentId:t,strategy:e}}}(a,t)),this.refreshDocumentLayout(a)}onStoreUpdated(t,e){for(const a in e.documents){const i=t.documents[a],o=e.documents[a];i!==o&&(this.state$.emit(o),(null==i?void 0:i.pageChangeState)!==o.pageChangeState&&this.pageChangeState$.emit({documentId:a,state:o.pageChangeState}),this.pushScrollerLayout(a))}}async initialize(){this.logger.info("ScrollPlugin","Initialize","Scroll plugin initialized")}async destroy(){this.strategies.clear(),this.layoutReady.clear(),this.initialLayoutFired.clear();for(const t of this.scrollerLayoutEmitters.values())t.clear();this.scrollerLayoutEmitters.clear(),this.pageChange$.clear(),this.scroll$.clear(),this.layoutChange$.clear(),this.pageChangeState$.clear(),this.layoutReady$.clear(),this.state$.clear(),super.destroy()}};d.id="scroll";let m=d;const p="scroll",S={id:p,name:"Scroll Plugin",version:"1.0.0",provides:["scroll"],requires:["viewport"],optional:["spread"],defaultConfig:{enabled:!0,defaultPageGap:10,defaultBufferSize:4,defaultStrategy:a.Vertical}},y={manifest:S,create:(t,e)=>new m(p,t,e),reducer:(t,e)=>{switch(e.type){case s:{const{documentId:a,state:i}=e.payload;return{...t,documents:{...t.documents,[a]:i}}}case n:{const{[e.payload]:a,...i}=t.documents;return{...t,documents:i}}case g:{const{documentId:a,state:i}=e.payload,o=t.documents[a];return o?{...t,documents:{...t.documents,[a]:{...o,...i}}}:t}case c:{const{documentId:a,strategy:i}=e.payload,o=t.documents[a];return o?{...t,documents:{...t.documents,[a]:{...o,strategy:i}}}:t}default:return t}},initialState:(t,e)=>((t,e)=>({defaultStrategy:e.defaultStrategy??a.Vertical,defaultPageGap:e.defaultPageGap??10,defaultBufferSize:e.defaultBufferSize??2,documents:{}}))(0,e)};exports.SCROLL_PLUGIN_ID=p,exports.ScrollPlugin=m,exports.ScrollPluginPackage=y,exports.ScrollStrategy=a,exports.getScrollerLayout=u,exports.manifest=S;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),e=require("@embedpdf/models");var a=(t=>(t.Vertical="vertical",t.Horizontal="horizontal",t))(a||{});class i{constructor(t){this.pageGap=t.pageGap??20,this.viewportGap=t.viewportGap??20,this.bufferSize=t.bufferSize??2}getVisibleRange(t,e,a){const i=this.getScrollOffset(t),o=i,r=i+this.getClientSize(t);let s=0;for(;s<e.length&&(e[s].offset+e[s].height)*a<=o;)s++;let n=s;for(;n<e.length&&e[n].offset*a<=r;)n++;return{start:Math.max(0,s-this.bufferSize),end:Math.min(e.length-1,n+this.bufferSize-1)}}handleScroll(t,e,a){const i=this.getVisibleRange(t,e,a),o=e.slice(i.start,i.end+1),r=this.calculatePageVisibility(o,t,a),s=r.map(t=>t.pageNumber),n=e.slice(i.start,i.end+1).flatMap(t=>t.index),g=this.determineCurrentPage(r),c=e[i.start],h=e[i.end],l=c?c.offset*a:0,u=h?(e[e.length-1].offset+e[e.length-1].height)*a-(h.offset+h.height)*a:0;return{currentPage:g,visiblePages:s,pageVisibilityMetrics:r,renderedPageIndexes:n,scrollOffset:{x:t.scrollLeft,y:t.scrollTop},startSpacing:l,endSpacing:u}}calculatePageVisibility(t,e,a){const i=[];return t.forEach(t=>{t.pageLayouts.forEach(o=>{const r=t.x*a,s=t.y*a,n=r+o.x*a,g=s+o.y*a,c=o.rotatedWidth*a,h=o.rotatedHeight*a,l=e.scrollLeft,u=e.scrollTop,d=l+e.clientWidth,m=u+e.clientHeight,p=Math.max(n,l),S=Math.max(g,u),y=Math.min(n+c,d),f=Math.min(g+h,m);if(p<y&&S<f){const t=y-p,e=f-S,r=c*h,s=t*e;i.push({pageNumber:o.pageNumber,viewportX:p-l,viewportY:S-u,visiblePercentage:s/r*100,original:{pageX:(p-n)/a,pageY:(S-g)/a,visibleWidth:t/a,visibleHeight:e/a,scale:1},scaled:{pageX:p-n,pageY:S-g,visibleWidth:t,visibleHeight:e,scale:a}})}})}),i}determineCurrentPage(t){if(0===t.length)return 1;const e=Math.max(...t.map(t=>t.visiblePercentage)),a=t.filter(t=>t.visiblePercentage===e);return 1===a.length?a[0].pageNumber:a.sort((t,e)=>t.pageNumber-e.pageNumber)[0].pageNumber}getRectLocationForPage(t,e,a){const i=e.find(e=>e.pageNumbers.includes(t));if(!i)return null;const o=i.pageLayouts.find(e=>e.pageNumber===t);if(!o)return null;let r=0;if(a){const t=a.width;i.width<t&&(r=(t-i.width)/2)}return{origin:{x:i.x+o.x+r,y:i.y+o.y},size:{width:o.width,height:o.height}}}getScrollPositionForPage(t,a,i,o,r){const s=this.getTotalContentSize(a),n=this.getRectLocationForPage(t,a,s);if(!n)return null;const g=e.scalePosition(n.origin,i);if(r){const t=e.transformPosition({width:n.size.width,height:n.size.height},{x:r.x,y:r.y},o,i);return{x:g.x+t.x+this.viewportGap,y:g.y+t.y+this.viewportGap}}return{x:g.x+this.viewportGap,y:g.y+this.viewportGap}}getRectPositionForPage(t,a,i,o,r){const s=this.getTotalContentSize(a),n=this.getRectLocationForPage(t,a,s);if(!n)return null;const g=e.scalePosition(n.origin,i),c=e.transformRect({width:n.size.width,height:n.size.height},r,o,i);return{origin:{x:g.x+c.origin.x,y:g.y+c.origin.y},size:c.size}}}class o extends i{constructor(t){super(t)}createVirtualItems(t){let e=0;return t.map((t,a)=>{let i=0;const o=t.map(t=>{const e={pageNumber:t.index+1,pageIndex:t.index,x:i,y:0,width:t.size.width,height:t.size.height,rotatedWidth:t.rotatedSize.width,rotatedHeight:t.rotatedSize.height};return i+=t.rotatedSize.width+this.pageGap,e}),r=t.reduce((e,a,i)=>e+a.rotatedSize.width+(i<t.length-1?this.pageGap:0),0),s=Math.max(...t.map(t=>t.rotatedSize.height)),n={id:`item-${a}`,x:0,y:e,offset:e,width:r,height:s,pageLayouts:o,pageNumbers:t.map(t=>t.index+1),index:a};return e+=s+this.pageGap,n})}getTotalContentSize(t){if(0===t.length)return{width:0,height:0};return{width:Math.max(...t.map(t=>t.width)),height:t[t.length-1].y+t[t.length-1].height}}getScrollOffset(t){return t.scrollTop}getClientSize(t){return t.clientHeight}}class r extends i{constructor(t){super(t)}createVirtualItems(t){let e=0;return t.map((t,a)=>{let i=0;const o=t.map(t=>{const e={pageNumber:t.index+1,pageIndex:t.index,x:i,y:0,width:t.size.width,height:t.size.height,rotatedWidth:t.rotatedSize.width,rotatedHeight:t.rotatedSize.height};return i+=t.rotatedSize.width+this.pageGap,e}),r=t.reduce((e,a,i)=>e+a.rotatedSize.width+(i<t.length-1?this.pageGap:0),0),s=Math.max(...t.map(t=>t.rotatedSize.height)),n={id:`item-${a}`,x:e,y:0,offset:e,width:r,height:s,pageLayouts:o,pageNumbers:t.map(t=>t.index+1),index:a};return e+=r+this.pageGap,n})}getTotalContentSize(t){if(0===t.length)return{width:0,height:0};return{width:t[t.length-1].x+t[t.length-1].width,height:Math.max(...t.map(t=>t.height))}}getScrollOffset(t){return t.scrollLeft}getClientSize(t){return t.clientWidth}}const s="INIT_SCROLL_STATE",n="CLEANUP_SCROLL_STATE",g="UPDATE_DOCUMENT_SCROLL_STATE",c="SET_SCROLL_STRATEGY";function h(t,e){return{type:g,payload:{documentId:t,state:e}}}const l={isChanging:!1,targetPage:1,fromPage:1,startTime:0},u=(t,e)=>({startSpacing:t.startSpacing,endSpacing:t.endSpacing,totalWidth:t.totalContentSize.width*e,totalHeight:t.totalContentSize.height*e,pageGap:t.pageGap*e,strategy:t.strategy,items:t.renderedPageIndexes.map(a=>({...t.virtualItems[a],pageLayouts:t.virtualItems[a].pageLayouts.map(t=>({...t,rotatedWidth:t.rotatedWidth*e,rotatedHeight:t.rotatedHeight*e,width:t.width*e,height:t.height*e}))}))}),d=class extends t.BasePlugin{constructor(e,a,i){var o,r;super(e,a),this.id=e,this.config=i,this.strategies=new Map,this.layoutReady=new Set,this.initialLayoutFired=new Set,this.scrollerLayoutEmitters=new Map,this.pageChange$=t.createBehaviorEmitter(),this.scroll$=t.createBehaviorEmitter(),this.layoutChange$=t.createBehaviorEmitter(),this.pageChangeState$=t.createBehaviorEmitter(),this.layoutReady$=t.createBehaviorEmitter(),this.state$=t.createBehaviorEmitter(),this.viewport=this.registry.getPlugin("viewport").provides(),this.spread=(null==(o=this.registry.getPlugin("spread"))?void 0:o.provides())??null,this.viewport.onScrollActivity(t=>{const e=this.getDocumentState(t.documentId);(null==e?void 0:e.pageChangeState.isChanging)&&!t.activity.isSmoothScrolling&&this.completePageChange(t.documentId)}),null==(r=this.spread)||r.onSpreadChange(t=>{this.refreshDocumentLayout(t.documentId)}),this.viewport.onViewportChange(t=>{const e=this.getDocumentState(t.documentId);if(!e)return;const a=this.computeMetrics(t.documentId,t.metrics);this.layoutReady.has(t.documentId)?this.commitMetrics(t.documentId,a):this.commitMetrics(t.documentId,{...a,scrollOffset:e.scrollOffset})})}onDocumentLoadingStarted(e){const a=this.getCoreDocument(e);if(!a)return;const i=this.createDocumentState(a);this.dispatch(function(t,e){return{type:s,payload:{documentId:t,state:e}}}(e,i));const o=this.createStrategy(i.strategy);this.strategies.set(e,o),this.scrollerLayoutEmitters.set(e,t.createBehaviorEmitter())}onDocumentLoaded(t){var e;const a=this.getCoreDocument(t);a&&(this.dispatch(h(t,{totalPages:(null==(e=a.document)?void 0:e.pageCount)??0})),this.refreshDocumentLayout(t),this.logger.debug("ScrollPlugin","DocumentOpened",`Initialized scroll state for document: ${t}`))}onDocumentClosed(t){this.strategies.delete(t),this.layoutReady.delete(t),this.initialLayoutFired.delete(t);const e=this.scrollerLayoutEmitters.get(t);e&&(e.clear(),this.scrollerLayoutEmitters.delete(t)),this.dispatch(function(t){return{type:n,payload:t}}(t)),this.logger.debug("ScrollPlugin","DocumentClosed",`Cleaned up scroll state for document: ${t}`)}onScaleChanged(t){const e=this.coreState.core.documents[t];if(!e||"loaded"!==e.status)return;const a=this.viewport.forDocument(t),i=this.computeMetrics(t,a.getMetrics());this.commitMetrics(t,i)}onRotationChanged(t){this.refreshDocumentLayout(t)}onScrollerData(t,e){const a=this.scrollerLayoutEmitters.get(t);if(!a)throw new Error(`No scroller layout emitter found for document: ${t}`);return a.on(e)}getScrollerLayout(t){const e=this.getDocumentState(t),a=this.getCoreDocumentOrThrow(t);if(!e||!a)throw new Error(`Cannot get scroller layout for document: ${t}`);return u(e,a.scale)}setLayoutReady(t){if(this.layoutReady.has(t))return;const e=this.getDocumentState(t);if(!e)return;this.layoutReady.add(t);const a=!this.initialLayoutFired.has(t);a&&this.initialLayoutFired.add(t);this.viewport.forDocument(t).scrollTo({...e.scrollOffset,behavior:"instant"}),this.layoutReady$.emit({documentId:t,isInitial:a,pageNumber:e.currentPage,totalPages:e.totalPages})}clearLayoutReady(t){this.layoutReady.delete(t)}buildCapability(){return{getCurrentPage:()=>this.getCurrentPage(),getTotalPages:()=>this.getTotalPages(),getPageChangeState:()=>this.getPageChangeState(),scrollToPage:t=>this.scrollToPage(t),scrollToNextPage:t=>this.scrollToNextPage(t),scrollToPreviousPage:t=>this.scrollToPreviousPage(t),getMetrics:t=>this.getMetrics(t),getLayout:()=>this.getLayout(),getRectPositionForPage:(t,e,a,i)=>this.getRectPositionForPage(t,e,a,i),forDocument:t=>this.createScrollScope(t),setScrollStrategy:(t,e)=>this.setScrollStrategyForDocument(t,e),getPageGap:()=>this.state.defaultPageGap,onPageChange:this.pageChange$.on,onScroll:this.scroll$.on,onLayoutChange:this.layoutChange$.on,onLayoutReady:this.layoutReady$.on,onPageChangeState:this.pageChangeState$.on,onStateChange:this.state$.on}}createScrollScope(t){return{getCurrentPage:()=>this.getCurrentPage(t),getTotalPages:()=>this.getTotalPages(t),getPageChangeState:()=>this.getPageChangeState(t),scrollToPage:e=>this.scrollToPage(e,t),scrollToNextPage:e=>this.scrollToNextPage(e,t),scrollToPreviousPage:e=>this.scrollToPreviousPage(e,t),getSpreadPagesWithRotatedSize:()=>this.getSpreadPagesWithRotatedSize(t),getMetrics:e=>this.getMetrics(e,t),getLayout:()=>this.getLayout(t),getRectPositionForPage:(e,a,i,o)=>this.getRectPositionForPage(e,a,i,o,t),setScrollStrategy:e=>this.setScrollStrategyForDocument(e,t),onPageChange:e=>this.pageChange$.on(a=>{a.documentId===t&&e(a)}),onScroll:e=>this.scroll$.on(a=>{a.documentId===t&&e(a.metrics)}),onLayoutChange:e=>this.layoutChange$.on(a=>{a.documentId===t&&e(a.layout)})}}getDocumentState(t){const e=t??this.getActiveDocumentId();return this.state.documents[e]??null}getDocumentStateOrThrow(t){const e=this.getDocumentState(t);if(!e)throw new Error(`Scroll state not found for document: ${t??"active"}`);return e}getStrategy(t){const e=t??this.getActiveDocumentId(),a=this.strategies.get(e);if(!a)throw new Error(`Strategy not found for document: ${e}`);return a}createStrategy(t){const e={pageGap:this.state.defaultPageGap,viewportGap:this.viewport.getViewportGap(),bufferSize:this.state.defaultBufferSize};return t===a.Horizontal?new r(e):new o(e)}createDocumentState(t){var e;return{virtualItems:[],totalPages:(null==(e=t.document)?void 0:e.pageCount)??0,currentPage:1,totalContentSize:{width:0,height:0},strategy:this.state.defaultStrategy,pageGap:this.state.defaultPageGap,visiblePages:[],pageVisibilityMetrics:[],renderedPageIndexes:[],scrollOffset:{x:0,y:0},startSpacing:0,endSpacing:0,pageChangeState:l}}startPageChange(t,e,a="smooth"){const i=this.getDocumentState(t);if(!i)return;const o={isChanging:!0,targetPage:e,fromPage:i.currentPage,startTime:Date.now()};this.dispatch(h(t,{pageChangeState:o})),"instant"===a&&this.completePageChange(t)}completePageChange(t){const e=this.getDocumentState(t);if(!e||!e.pageChangeState.isChanging)return;const a={isChanging:!1,targetPage:e.pageChangeState.targetPage,fromPage:e.pageChangeState.fromPage,startTime:e.pageChangeState.startTime};this.dispatch(h(t,{pageChangeState:a}))}computeLayout(t,e){const a=this.getStrategy(t),i=a.createVirtualItems(e);return{virtualItems:i,totalContentSize:a.getTotalContentSize(i)}}computeMetrics(t,e,a){const i=this.getCoreDocumentOrThrow(t),o=this.getDocumentState(t),r=this.getStrategy(t);if(!o)throw new Error(`Document state not found: ${t}`);return r.handleScroll(e,a??o.virtualItems,i.scale)}commitMetrics(t,e){const a=this.getDocumentState(t);a&&(this.dispatch(h(t,e)),this.scroll$.emit({documentId:t,metrics:e}),e.currentPage!==a.currentPage&&this.pageChange$.emit({documentId:t,pageNumber:e.currentPage,totalPages:a.totalPages}),this.pushScrollerLayout(t))}pushScrollerLayout(t){const e=this.scrollerLayoutEmitters.get(t);if(e)try{const a=this.getScrollerLayout(t);e.emit(a)}catch(a){}}refreshDocumentLayout(t){const e=this.coreState.core.documents[t],a=this.getDocumentState(t);if(!e||!a||"loaded"!==e.status)return;const i=this.getSpreadPagesWithRotatedSize(t),o=this.computeLayout(t,i),r=this.viewport.forDocument(t),s=this.computeMetrics(t,r.getMetrics(),o.virtualItems);this.dispatch(h(t,{...o,...s})),this.layoutChange$.emit({documentId:t,layout:o}),this.pushScrollerLayout(t)}getSpreadPagesWithRotatedSize(t){var a,i;const o=t??this.getActiveDocumentId(),r=this.coreState.core.documents[o];if(!r)throw new Error(`Document ${o} not loaded`);return((null==(a=this.spread)?void 0:a.forDocument(o).getSpreadPages())||(null==(i=r.document)?void 0:i.pages.map(t=>[t]))||[]).map(t=>t.map(t=>({...t,rotatedSize:e.transformSize(t.size,r.rotation,1)})))}getCurrentPage(t){return this.getDocumentStateOrThrow(t).currentPage}getTotalPages(t){return this.getDocumentStateOrThrow(t).totalPages}getPageChangeState(t){return this.getDocumentStateOrThrow(t).pageChangeState}scrollToPage(t,e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.getCoreDocumentOrThrow(a),{pageNumber:s,behavior:n="smooth",pageCoordinates:g,alignX:c,alignY:h}=t;this.startPageChange(a,s,n);const l=o.getScrollPositionForPage(s,i.virtualItems,r.scale,r.rotation,g);if(l){this.viewport.forDocument(a).scrollTo({...l,behavior:n,alignX:c,alignY:h})}else this.completePageChange(a)}scrollToNextPage(t="smooth",e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.getCoreDocumentOrThrow(a),s=i.virtualItems.findIndex(t=>t.pageNumbers.includes(i.currentPage));if(s>=0&&s<i.virtualItems.length-1){const e=i.virtualItems[s+1].pageNumbers[0];this.startPageChange(a,e,t);const n=o.getScrollPositionForPage(e,i.virtualItems,r.scale,r.rotation);if(n){this.viewport.forDocument(a).scrollTo({...n,behavior:t})}else this.completePageChange(a)}}scrollToPreviousPage(t="smooth",e){const a=e??this.getActiveDocumentId(),i=this.getDocumentStateOrThrow(a),o=this.getStrategy(a),r=this.coreState.core.documents[a],s=i.virtualItems.findIndex(t=>t.pageNumbers.includes(i.currentPage));if(s>0){const e=i.virtualItems[s-1].pageNumbers[0];this.startPageChange(a,e,t);const n=o.getScrollPositionForPage(e,i.virtualItems,r.scale,r.rotation);if(n){this.viewport.forDocument(a).scrollTo({...n,behavior:t})}else this.completePageChange(a)}}getMetrics(t,e){const a=e??this.getActiveDocumentId();if(t)return this.computeMetrics(a,t);const i=this.viewport.forDocument(a);return this.computeMetrics(a,i.getMetrics())}getLayout(t){const e=this.getDocumentStateOrThrow(t);return{virtualItems:e.virtualItems,totalContentSize:e.totalContentSize}}getRectPositionForPage(t,e,a,i,o){const r=o??this.getActiveDocumentId(),s=this.getDocumentStateOrThrow(r),n=this.getStrategy(r),g=this.getCoreDocumentOrThrow(r);return n.getRectPositionForPage(t+1,s.virtualItems,a??g.scale,i??g.rotation,e)}setScrollStrategyForDocument(t,e){const a=e??this.getActiveDocumentId(),i=this.getDocumentState(a);if(!i||i.strategy===t)return;const o=this.createStrategy(t);this.strategies.set(a,o),this.dispatch(function(t,e){return{type:c,payload:{documentId:t,strategy:e}}}(a,t)),this.refreshDocumentLayout(a)}onStoreUpdated(t,e){for(const a in e.documents){const i=t.documents[a],o=e.documents[a];i!==o&&(this.state$.emit(o),(null==i?void 0:i.pageChangeState)!==o.pageChangeState&&this.pageChangeState$.emit({documentId:a,state:o.pageChangeState}),this.pushScrollerLayout(a))}}async initialize(){this.logger.info("ScrollPlugin","Initialize","Scroll plugin initialized")}async destroy(){this.strategies.clear(),this.layoutReady.clear(),this.initialLayoutFired.clear();for(const t of this.scrollerLayoutEmitters.values())t.clear();this.scrollerLayoutEmitters.clear(),this.pageChange$.clear(),this.scroll$.clear(),this.layoutChange$.clear(),this.pageChangeState$.clear(),this.layoutReady$.clear(),this.state$.clear(),super.destroy()}};d.id="scroll";let m=d;const p="scroll",S={id:p,name:"Scroll Plugin",version:"1.0.0",provides:["scroll"],requires:["viewport"],optional:["spread"],defaultConfig:{defaultPageGap:10,defaultBufferSize:4,defaultStrategy:a.Vertical}},y={manifest:S,create:(t,e)=>new m(p,t,e),reducer:(t,e)=>{switch(e.type){case s:{const{documentId:a,state:i}=e.payload;return{...t,documents:{...t.documents,[a]:i}}}case n:{const{[e.payload]:a,...i}=t.documents;return{...t,documents:i}}case g:{const{documentId:a,state:i}=e.payload,o=t.documents[a];return o?{...t,documents:{...t.documents,[a]:{...o,...i}}}:t}case c:{const{documentId:a,strategy:i}=e.payload,o=t.documents[a];return o?{...t,documents:{...t.documents,[a]:{...o,strategy:i}}}:t}default:return t}},initialState:(t,e)=>((t,e)=>({defaultStrategy:e.defaultStrategy??a.Vertical,defaultPageGap:e.defaultPageGap??10,defaultBufferSize:e.defaultBufferSize??2,documents:{}}))(0,e)};exports.SCROLL_PLUGIN_ID=p,exports.ScrollPlugin=m,exports.ScrollPluginPackage=y,exports.ScrollStrategy=a,exports.getScrollerLayout=u,exports.manifest=S;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/lib/types.ts","../src/lib/strategies/base-strategy.ts","../src/lib/strategies/vertical-strategy.ts","../src/lib/strategies/horizontal-strategy.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/scroll-plugin.ts","../src/lib/manifest.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig, EventHook } from '@embedpdf/core';\nimport { PdfPageObject, PdfPageObjectWithRotatedSize, Rect, Rotation } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from './types/virtual-item';\n\nexport type ScrollBehavior = 'instant' | 'smooth' | 'auto';\n\nexport interface PageChangeState {\n isChanging: boolean;\n targetPage: number;\n fromPage: number;\n startTime: number;\n}\n\n// Per-document scroll state\nexport interface ScrollDocumentState {\n virtualItems: VirtualItem[];\n totalPages: number;\n currentPage: number;\n totalContentSize: { width: number; height: number };\n strategy: ScrollStrategy;\n pageGap: number;\n\n // Scroll metrics\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n pageChangeState: PageChangeState;\n}\n\n// Plugin state\nexport interface ScrollState {\n // Global defaults (applied to new documents)\n defaultStrategy: ScrollStrategy;\n defaultPageGap: number;\n defaultBufferSize: number;\n\n // Per-document states\n documents: Record<string, ScrollDocumentState>;\n}\n\nexport interface ScrollerLayout {\n startSpacing: number;\n endSpacing: number;\n totalWidth: number;\n totalHeight: number;\n pageGap: number;\n strategy: ScrollStrategy;\n items: VirtualItem[];\n}\n\nexport enum ScrollStrategy {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\nexport interface PageVisibilityMetrics {\n pageNumber: number;\n viewportX: number;\n viewportY: number;\n visiblePercentage: number;\n original: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n scaled: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n}\n\nexport interface ScrollMetrics {\n currentPage: number;\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n}\n\nexport interface ScrollPluginConfig extends BasePluginConfig {\n defaultStrategy?: ScrollStrategy;\n defaultPageGap?: number;\n defaultBufferSize?: number;\n}\n\nexport type LayoutChangePayload = Pick<ScrollDocumentState, 'virtualItems' | 'totalContentSize'>;\n\nexport interface ScrollToPageOptions {\n pageNumber: number;\n pageCoordinates?: { x: number; y: number };\n behavior?: ScrollBehavior;\n center?: boolean;\n}\n\n// Events include documentId\nexport interface PageChangeEvent {\n documentId: string;\n pageNumber: number;\n totalPages: number;\n}\n\nexport interface ScrollEvent {\n documentId: string;\n metrics: ScrollMetrics;\n}\n\nexport interface LayoutChangeEvent {\n documentId: string;\n layout: LayoutChangePayload;\n}\n\nexport interface PageChangeStateEvent {\n documentId: string;\n state: PageChangeState;\n}\n\nexport interface LayoutReadyEvent {\n documentId: string;\n /** True only on the first layout ready after document load, false on subsequent (e.g., tab switches) */\n isInitial: boolean;\n}\n\n// Scoped scroll capability\nexport interface ScrollScope {\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getSpreadPagesWithRotatedSize(): PdfPageObjectWithRotatedSize[][];\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n setScrollStrategy(strategy: ScrollStrategy): void;\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollMetrics>;\n onLayoutChange: EventHook<LayoutChangePayload>;\n}\n\nexport interface ScrollCapability {\n // Active document operations (defaults to active document)\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n\n // Document-scoped operations\n forDocument(documentId: string): ScrollScope;\n\n // Global settings\n setScrollStrategy(strategy: ScrollStrategy, documentId?: string): void;\n getPageGap(): number;\n\n // Events (all include documentId)\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollEvent>;\n onLayoutChange: EventHook<LayoutChangeEvent>;\n onLayoutReady: EventHook<LayoutReadyEvent>;\n onPageChangeState: EventHook<PageChangeStateEvent>;\n onStateChange: EventHook<ScrollDocumentState>;\n}\n","import {\n PdfPageObjectWithRotatedSize,\n Position,\n Rect,\n Rotation,\n scalePosition,\n Size,\n transformPosition,\n transformRect,\n} from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport interface ScrollStrategyConfig {\n pageGap?: number;\n viewportGap?: number;\n bufferSize?: number;\n}\n\nexport abstract class BaseScrollStrategy {\n protected pageGap: number;\n protected viewportGap: number;\n protected bufferSize: number;\n\n constructor(config: ScrollStrategyConfig) {\n this.pageGap = config.pageGap ?? 20;\n this.viewportGap = config.viewportGap ?? 20;\n this.bufferSize = config.bufferSize ?? 2;\n }\n\n abstract createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[];\n abstract getTotalContentSize(virtualItems: VirtualItem[]): Size;\n protected abstract getScrollOffset(viewport: ViewportMetrics): number;\n protected abstract getClientSize(viewport: ViewportMetrics): number;\n\n protected getVisibleRange(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): { start: number; end: number } {\n const scrollOffset = this.getScrollOffset(viewport);\n const clientSize = this.getClientSize(viewport);\n const viewportStart = scrollOffset;\n const viewportEnd = scrollOffset + clientSize;\n\n let startIndex = 0;\n while (\n startIndex < virtualItems.length &&\n (virtualItems[startIndex].offset + virtualItems[startIndex].height) * scale <= viewportStart\n ) {\n startIndex++;\n }\n\n let endIndex = startIndex;\n while (endIndex < virtualItems.length && virtualItems[endIndex].offset * scale <= viewportEnd) {\n endIndex++;\n }\n\n return {\n start: Math.max(0, startIndex - this.bufferSize),\n end: Math.min(virtualItems.length - 1, endIndex + this.bufferSize - 1),\n };\n }\n\n handleScroll(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): ScrollMetrics {\n const range = this.getVisibleRange(viewport, virtualItems, scale);\n const visibleItems = virtualItems.slice(range.start, range.end + 1);\n const pageVisibilityMetrics = this.calculatePageVisibility(visibleItems, viewport, scale);\n const visiblePages = pageVisibilityMetrics.map((m) => m.pageNumber);\n const renderedPageIndexes = virtualItems\n .slice(range.start, range.end + 1)\n .flatMap((item) => item.index);\n const currentPage = this.determineCurrentPage(pageVisibilityMetrics);\n const first = virtualItems[range.start];\n const last = virtualItems[range.end];\n const startSpacing = first ? first.offset * scale : 0;\n const endSpacing = last\n ? (virtualItems[virtualItems.length - 1].offset + // end of content\n virtualItems[virtualItems.length - 1].height) *\n scale - // minus\n (last.offset + last.height) * scale // end of last rendered\n : 0;\n\n return {\n currentPage,\n visiblePages,\n pageVisibilityMetrics,\n renderedPageIndexes,\n scrollOffset: { x: viewport.scrollLeft, y: viewport.scrollTop },\n startSpacing,\n endSpacing,\n };\n }\n\n protected calculatePageVisibility(\n virtualItems: VirtualItem[],\n viewport: ViewportMetrics,\n scale: number,\n ): ScrollMetrics['pageVisibilityMetrics'] {\n const visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'] = [];\n\n virtualItems.forEach((item) => {\n item.pageLayouts.forEach((page) => {\n const itemX = item.x * scale;\n const itemY = item.y * scale;\n const pageX = itemX + page.x * scale;\n const pageY = itemY + page.y * scale;\n const pageWidth = page.rotatedWidth * scale;\n const pageHeight = page.rotatedHeight * scale;\n\n const viewportLeft = viewport.scrollLeft;\n const viewportTop = viewport.scrollTop;\n const viewportRight = viewportLeft + viewport.clientWidth;\n const viewportBottom = viewportTop + viewport.clientHeight;\n\n const intersectionLeft = Math.max(pageX, viewportLeft);\n const intersectionTop = Math.max(pageY, viewportTop);\n const intersectionRight = Math.min(pageX + pageWidth, viewportRight);\n const intersectionBottom = Math.min(pageY + pageHeight, viewportBottom);\n\n if (intersectionLeft < intersectionRight && intersectionTop < intersectionBottom) {\n const visibleWidth = intersectionRight - intersectionLeft;\n const visibleHeight = intersectionBottom - intersectionTop;\n const totalArea = pageWidth * pageHeight;\n const visibleArea = visibleWidth * visibleHeight;\n\n visibilityMetrics.push({\n pageNumber: page.pageNumber,\n viewportX: intersectionLeft - viewportLeft,\n viewportY: intersectionTop - viewportTop,\n visiblePercentage: (visibleArea / totalArea) * 100,\n original: {\n pageX: (intersectionLeft - pageX) / scale,\n pageY: (intersectionTop - pageY) / scale,\n visibleWidth: visibleWidth / scale,\n visibleHeight: visibleHeight / scale,\n scale: 1,\n },\n scaled: {\n pageX: intersectionLeft - pageX,\n pageY: intersectionTop - pageY,\n visibleWidth,\n visibleHeight,\n scale,\n },\n });\n }\n });\n });\n\n return visibilityMetrics;\n }\n\n protected determineCurrentPage(\n visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'],\n ): number {\n if (visibilityMetrics.length === 0) return 1;\n\n const maxVisibility = Math.max(...visibilityMetrics.map((m) => m.visiblePercentage));\n const mostVisiblePages = visibilityMetrics.filter((m) => m.visiblePercentage === maxVisibility);\n\n return mostVisiblePages.length === 1\n ? mostVisiblePages[0].pageNumber\n : mostVisiblePages.sort((a, b) => a.pageNumber - b.pageNumber)[0].pageNumber;\n }\n\n private getRectLocationForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n totalContentSize?: Size,\n ): Rect | null {\n // Find the virtual item containing the page\n const item = virtualItems.find((item) => item.pageNumbers.includes(pageNumber));\n if (!item) return null;\n\n // Find the specific page layout for the requested page number\n const pageLayout = item.pageLayouts.find((layout) => layout.pageNumber === pageNumber);\n if (!pageLayout) return null;\n\n // Calculate centering offset for items that are narrower than the maximum width\n let centeringOffsetX = 0;\n if (totalContentSize) {\n const maxWidth = totalContentSize.width;\n if (item.width < maxWidth) {\n centeringOffsetX = (maxWidth - item.width) / 2;\n }\n }\n\n return {\n origin: {\n x: item.x + pageLayout.x + centeringOffsetX,\n y: item.y + pageLayout.y,\n },\n size: {\n width: pageLayout.width,\n height: pageLayout.height,\n },\n };\n }\n\n getScrollPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n pageCoordinates?: { x: number; y: number },\n ): Position | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n // If specific page coordinates are provided, add them to the base position\n if (pageCoordinates) {\n const rotatedSize = transformPosition(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n {\n x: pageCoordinates.x,\n y: pageCoordinates.y,\n },\n rotation,\n scale,\n );\n\n return {\n x: scaledBasePosition.x + rotatedSize.x + this.viewportGap,\n y: scaledBasePosition.y + rotatedSize.y + this.viewportGap,\n };\n }\n\n return {\n x: scaledBasePosition.x + this.viewportGap,\n y: scaledBasePosition.y + this.viewportGap,\n };\n }\n\n getRectPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n rect: Rect,\n ): Rect | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n const rotatedSize = transformRect(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n rect,\n rotation,\n scale,\n );\n\n return {\n origin: {\n x: scaledBasePosition.x + rotatedSize.origin.x,\n y: scaledBasePosition.y + rotatedSize.origin.y,\n },\n size: rotatedSize.size,\n };\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport class VerticalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let yOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: 0,\n y: yOffset,\n offset: yOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n yOffset += height + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const maxWidth = Math.max(...virtualItems.map((item) => item.width));\n const totalHeight =\n virtualItems[virtualItems.length - 1].y + virtualItems[virtualItems.length - 1].height;\n return {\n width: maxWidth,\n height: totalHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollTop;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientHeight;\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\n\nexport class HorizontalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let xOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: xOffset,\n y: 0,\n offset: xOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n xOffset += width + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const totalWidth =\n virtualItems[virtualItems.length - 1].x + virtualItems[virtualItems.length - 1].width;\n const maxHeight = Math.max(...virtualItems.map((item) => item.height));\n return {\n width: totalWidth,\n height: maxHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollLeft;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientWidth;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { ScrollDocumentState, ScrollStrategy } from './types';\n\n// Document lifecycle\nexport const INIT_SCROLL_STATE = 'INIT_SCROLL_STATE';\nexport const CLEANUP_SCROLL_STATE = 'CLEANUP_SCROLL_STATE';\nexport const UPDATE_DOCUMENT_SCROLL_STATE = 'UPDATE_DOCUMENT_SCROLL_STATE';\nexport const SET_SCROLL_STRATEGY = 'SET_SCROLL_STRATEGY';\n\nexport interface InitScrollStateAction extends Action {\n type: typeof INIT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: ScrollDocumentState;\n };\n}\n\nexport interface CleanupScrollStateAction extends Action {\n type: typeof CLEANUP_SCROLL_STATE;\n payload: string; // documentId\n}\n\nexport interface UpdateDocumentScrollStateAction extends Action {\n type: typeof UPDATE_DOCUMENT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: Partial<ScrollDocumentState>;\n };\n}\n\nexport interface SetScrollStrategyAction extends Action {\n type: typeof SET_SCROLL_STRATEGY;\n payload: {\n documentId: string;\n strategy: ScrollStrategy;\n };\n}\n\nexport type ScrollAction =\n | InitScrollStateAction\n | CleanupScrollStateAction\n | UpdateDocumentScrollStateAction\n | SetScrollStrategyAction;\n\nexport function initScrollState(\n documentId: string,\n state: ScrollDocumentState,\n): InitScrollStateAction {\n return { type: INIT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function cleanupScrollState(documentId: string): CleanupScrollStateAction {\n return { type: CLEANUP_SCROLL_STATE, payload: documentId };\n}\n\nexport function updateDocumentScrollState(\n documentId: string,\n state: Partial<ScrollDocumentState>,\n): UpdateDocumentScrollStateAction {\n return { type: UPDATE_DOCUMENT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function setScrollStrategy(\n documentId: string,\n strategy: ScrollStrategy,\n): SetScrollStrategyAction {\n return { type: SET_SCROLL_STRATEGY, payload: { documentId, strategy } };\n}\n","import { Reducer, CoreState } from '@embedpdf/core';\nimport { ScrollState, ScrollStrategy, ScrollPluginConfig, PageChangeState } from './types';\nimport {\n ScrollAction,\n INIT_SCROLL_STATE,\n CLEANUP_SCROLL_STATE,\n UPDATE_DOCUMENT_SCROLL_STATE,\n SET_SCROLL_STRATEGY,\n} from './actions';\n\nexport const defaultPageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: 1,\n fromPage: 1,\n startTime: 0,\n};\n\nexport const initialState: (coreState: CoreState, config: ScrollPluginConfig) => ScrollState = (\n _coreState,\n config,\n) => ({\n defaultStrategy: config.defaultStrategy ?? ScrollStrategy.Vertical,\n defaultPageGap: config.defaultPageGap ?? 10,\n defaultBufferSize: config.defaultBufferSize ?? 2,\n documents: {},\n});\n\nexport const scrollReducer: Reducer<ScrollState, ScrollAction> = (state, action) => {\n switch (action.type) {\n case INIT_SCROLL_STATE: {\n const { documentId, state: docState } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n };\n }\n\n case CLEANUP_SCROLL_STATE: {\n const { [action.payload]: removed, ...remaining } = state.documents;\n return {\n ...state,\n documents: remaining,\n };\n }\n\n case UPDATE_DOCUMENT_SCROLL_STATE: {\n const { documentId, state: updates } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n ...updates,\n },\n },\n };\n }\n\n case SET_SCROLL_STRATEGY: {\n const { documentId, strategy } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n strategy,\n },\n },\n };\n }\n\n default:\n return state;\n }\n};\n","import { ScrollerLayout, ScrollDocumentState } from './types';\n\nexport const getScrollerLayout = (\n documentState: ScrollDocumentState,\n scale: number,\n): ScrollerLayout => {\n return {\n startSpacing: documentState.startSpacing,\n endSpacing: documentState.endSpacing,\n totalWidth: documentState.totalContentSize.width * scale,\n totalHeight: documentState.totalContentSize.height * scale,\n pageGap: documentState.pageGap * scale,\n strategy: documentState.strategy,\n items: documentState.renderedPageIndexes.map((idx) => {\n return {\n ...documentState.virtualItems[idx],\n pageLayouts: documentState.virtualItems[idx].pageLayouts.map((layout) => {\n return {\n ...layout,\n rotatedWidth: layout.rotatedWidth * scale,\n rotatedHeight: layout.rotatedHeight * scale,\n width: layout.width * scale,\n height: layout.height * scale,\n };\n }),\n };\n }),\n };\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createBehaviorEmitter,\n DocumentState,\n Unsubscribe,\n Listener,\n SET_PAGES,\n} from '@embedpdf/core';\nimport { PdfPageObjectWithRotatedSize, Rect, Rotation, transformSize } from '@embedpdf/models';\nimport { ViewportCapability, ViewportMetrics, ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { SpreadCapability, SpreadPlugin } from '@embedpdf/plugin-spread';\n\nimport {\n ScrollCapability,\n ScrollScope,\n ScrollPluginConfig,\n ScrollStrategy,\n ScrollMetrics,\n ScrollState,\n ScrollDocumentState,\n LayoutChangePayload,\n ScrollerLayout,\n ScrollToPageOptions,\n PageChangeEvent,\n ScrollEvent,\n LayoutChangeEvent,\n PageChangeStateEvent,\n LayoutReadyEvent,\n ScrollBehavior,\n PageChangeState,\n} from './types';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './strategies/base-strategy';\nimport { VerticalScrollStrategy } from './strategies/vertical-strategy';\nimport { HorizontalScrollStrategy } from './strategies/horizontal-strategy';\nimport {\n ScrollAction,\n initScrollState,\n cleanupScrollState,\n updateDocumentScrollState,\n setScrollStrategy,\n} from './actions';\nimport { VirtualItem } from './types/virtual-item';\nimport { defaultPageChangeState } from './reducer';\nimport { getScrollerLayout } from './selectors';\n\nexport class ScrollPlugin extends BasePlugin<\n ScrollPluginConfig,\n ScrollCapability,\n ScrollState,\n ScrollAction\n> {\n static readonly id = 'scroll' as const;\n\n private viewport: ViewportCapability;\n private spread: SpreadCapability | null;\n\n // Strategies per document\n private strategies = new Map<string, BaseScrollStrategy>();\n\n // Layout ready tracking per document\n private layoutReady = new Set<string>();\n\n // Tracks documents that have had their initial layout ready (cleared only on document close)\n private initialLayoutFired = new Set<string>();\n\n // Per-document scroller layout emitters (for real-time scroll updates)\n private scrollerLayoutEmitters = new Map<\n string,\n ReturnType<typeof createBehaviorEmitter<ScrollerLayout>>\n >();\n\n // Event emitters (include documentId)\n private readonly pageChange$ = createBehaviorEmitter<PageChangeEvent>();\n private readonly scroll$ = createBehaviorEmitter<ScrollEvent>();\n private readonly layoutChange$ = createBehaviorEmitter<LayoutChangeEvent>();\n private readonly pageChangeState$ = createBehaviorEmitter<PageChangeStateEvent>();\n private readonly layoutReady$ = createBehaviorEmitter<LayoutReadyEvent>();\n private readonly state$ = createBehaviorEmitter<ScrollDocumentState>();\n\n constructor(\n public readonly id: string,\n registry: PluginRegistry,\n private config?: ScrollPluginConfig,\n ) {\n super(id, registry);\n\n this.viewport = this.registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.spread = this.registry.getPlugin<SpreadPlugin>('spread')?.provides() ?? null;\n\n // Subscribe to viewport scroll activity (per document)\n this.viewport.onScrollActivity((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (docState?.pageChangeState.isChanging && !event.activity.isSmoothScrolling) {\n this.completePageChange(event.documentId);\n }\n });\n\n this.spread?.onSpreadChange((event) => {\n this.refreshDocumentLayout(event.documentId);\n });\n\n // Subscribe to viewport changes (per document) with throttling\n this.viewport.onViewportChange((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (!docState) return;\n\n // Compute the metrics based on the incoming event\n const computedMetrics = this.computeMetrics(event.documentId, event.metrics);\n\n // THE GUARD: Only update the scrollOffset if the layout is already \"ready\".\n if (this.layoutReady.has(event.documentId)) {\n // Layout is ready, so this is a real scroll event from the user.\n // Commit all metrics, including the new scrollOffset.\n this.commitMetrics(event.documentId, computedMetrics);\n } else {\n // Layout is NOT ready. This is the initial, premature event.\n // We must commit the other metrics (like visible pages for rendering)\n // but EXCLUDE the incorrect scrollOffset to protect our persisted state.\n this.commitMetrics(event.documentId, {\n ...computedMetrics,\n scrollOffset: docState.scrollOffset,\n });\n }\n });\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle Hooks (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n protected override onDocumentLoadingStarted(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n // Initialize scroll state for this document\n const docState = this.createDocumentState(coreDoc);\n this.dispatch(initScrollState(documentId, docState));\n\n // Create strategy for this document\n const strategy = this.createStrategy(docState.strategy);\n this.strategies.set(documentId, strategy);\n\n // Create scroller layout emitter for this document\n this.scrollerLayoutEmitters.set(documentId, createBehaviorEmitter<ScrollerLayout>());\n }\n\n protected override onDocumentLoaded(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n\n this.dispatch(\n updateDocumentScrollState(documentId, { totalPages: coreDoc.document?.pageCount ?? 0 }),\n );\n // Initial layout computation\n this.refreshDocumentLayout(documentId);\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentOpened',\n `Initialized scroll state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup strategy\n this.strategies.delete(documentId);\n\n // Cleanup layout ready tracking\n this.layoutReady.delete(documentId);\n this.initialLayoutFired.delete(documentId);\n\n // Cleanup scroller layout emitter\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (emitter) {\n emitter.clear();\n this.scrollerLayoutEmitters.delete(documentId);\n }\n\n // Cleanup state\n this.dispatch(cleanupScrollState(documentId));\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentClosed',\n `Cleaned up scroll state for document: ${documentId}`,\n );\n }\n\n protected override onScaleChanged(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n if (!coreDoc || coreDoc.status !== 'loaded') return;\n\n const viewportScope = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewportScope.getMetrics());\n\n // Use the canonical path so scroll/pageChange events and scroller layout\n // updates all flow through the same place.\n this.commitMetrics(documentId, metrics);\n }\n\n protected override onRotationChanged(documentId: string): void {\n this.refreshDocumentLayout(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Public API for Components (Scroller Layout)\n // ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to scroller layout updates for a specific document\n * This is the key method for the Scroller component to stay reactive\n */\n public onScrollerData(\n documentId: string,\n callback: (layout: ScrollerLayout) => void,\n ): Unsubscribe {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) {\n throw new Error(`No scroller layout emitter found for document: ${documentId}`);\n }\n return emitter.on(callback);\n }\n\n /**\n * Get current scroller layout for a document\n */\n public getScrollerLayout(documentId: string): ScrollerLayout {\n const docState = this.getDocumentState(documentId);\n const coreDoc = this.getCoreDocumentOrThrow(documentId);\n\n if (!docState || !coreDoc) {\n throw new Error(`Cannot get scroller layout for document: ${documentId}`);\n }\n\n return getScrollerLayout(docState, coreDoc.scale);\n }\n\n public setLayoutReady(documentId: string): void {\n // This guard logic is now reliable because the flag gets reset correctly.\n if (this.layoutReady.has(documentId)) {\n return;\n }\n\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n this.layoutReady.add(documentId);\n\n // Determine if this is the initial layout for this document\n const isInitial = !this.initialLayoutFired.has(documentId);\n if (isInitial) {\n this.initialLayoutFired.add(documentId);\n }\n\n // Restore the persisted scroll position\n const viewport = this.viewport.forDocument(documentId);\n viewport.scrollTo({ ...docState.scrollOffset, behavior: 'instant' });\n\n this.layoutReady$.emit({ documentId, isInitial });\n }\n\n public clearLayoutReady(documentId: string): void {\n this.layoutReady.delete(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): ScrollCapability {\n return {\n // Active document operations\n getCurrentPage: () => this.getCurrentPage(),\n getTotalPages: () => this.getTotalPages(),\n getPageChangeState: () => this.getPageChangeState(),\n scrollToPage: (options) => this.scrollToPage(options),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior),\n getMetrics: (viewport) => this.getMetrics(viewport),\n getLayout: () => this.getLayout(),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation),\n\n // Document-scoped operations\n forDocument: (documentId) => this.createScrollScope(documentId),\n\n // Global settings\n setScrollStrategy: (strategy, documentId) =>\n this.setScrollStrategyForDocument(strategy, documentId),\n getPageGap: () => this.state.defaultPageGap,\n\n // Events\n onPageChange: this.pageChange$.on,\n onScroll: this.scroll$.on,\n onLayoutChange: this.layoutChange$.on,\n onLayoutReady: this.layoutReady$.on,\n onPageChangeState: this.pageChangeState$.on,\n onStateChange: this.state$.on,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createScrollScope(documentId: string): ScrollScope {\n return {\n getCurrentPage: () => this.getCurrentPage(documentId),\n getTotalPages: () => this.getTotalPages(documentId),\n getPageChangeState: () => this.getPageChangeState(documentId),\n scrollToPage: (options) => this.scrollToPage(options, documentId),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior, documentId),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior, documentId),\n getSpreadPagesWithRotatedSize: () => this.getSpreadPagesWithRotatedSize(documentId),\n getMetrics: (viewport) => this.getMetrics(viewport, documentId),\n getLayout: () => this.getLayout(documentId),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation, documentId),\n setScrollStrategy: (strategy) => this.setScrollStrategyForDocument(strategy, documentId),\n onPageChange: (listener: Listener<PageChangeEvent>) =>\n this.pageChange$.on((event) => {\n if (event.documentId === documentId) listener(event);\n }),\n onScroll: (listener: Listener<ScrollMetrics>) =>\n this.scroll$.on((event) => {\n if (event.documentId === documentId) listener(event.metrics);\n }),\n onLayoutChange: (listener: Listener<LayoutChangePayload>) =>\n this.layoutChange$.on((event) => {\n if (event.documentId === documentId) listener(event.layout);\n }),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // State Helpers\n // ─────────────────────────────────────────────────────────\n private getDocumentState(documentId?: string): ScrollDocumentState | null {\n const id = documentId ?? this.getActiveDocumentId();\n return this.state.documents[id] ?? null;\n }\n\n private getDocumentStateOrThrow(documentId?: string): ScrollDocumentState {\n const state = this.getDocumentState(documentId);\n if (!state) {\n throw new Error(`Scroll state not found for document: ${documentId ?? 'active'}`);\n }\n return state;\n }\n\n private getStrategy(documentId?: string): BaseScrollStrategy {\n const id = documentId ?? this.getActiveDocumentId();\n const strategy = this.strategies.get(id);\n if (!strategy) {\n throw new Error(`Strategy not found for document: ${id}`);\n }\n return strategy;\n }\n\n private createStrategy(strategyType: ScrollStrategy): BaseScrollStrategy {\n const config: ScrollStrategyConfig = {\n pageGap: this.state.defaultPageGap,\n viewportGap: this.viewport.getViewportGap(),\n bufferSize: this.state.defaultBufferSize,\n };\n\n return strategyType === ScrollStrategy.Horizontal\n ? new HorizontalScrollStrategy(config)\n : new VerticalScrollStrategy(config);\n }\n\n private createDocumentState(coreDoc: DocumentState): ScrollDocumentState {\n return {\n virtualItems: [],\n totalPages: coreDoc.document?.pageCount ?? 0,\n currentPage: 1,\n totalContentSize: { width: 0, height: 0 },\n strategy: this.state.defaultStrategy,\n pageGap: this.state.defaultPageGap,\n visiblePages: [],\n pageVisibilityMetrics: [],\n renderedPageIndexes: [],\n scrollOffset: { x: 0, y: 0 },\n startSpacing: 0,\n endSpacing: 0,\n pageChangeState: defaultPageChangeState,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Page Change Management\n // ─────────────────────────────────────────────────────────\n\n private startPageChange(\n documentId: string,\n targetPage: number,\n behavior: ScrollBehavior = 'smooth',\n ): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: true,\n targetPage,\n fromPage: docState.currentPage,\n startTime: Date.now(),\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n\n if (behavior === 'instant') {\n this.completePageChange(documentId);\n }\n }\n\n private completePageChange(documentId: string): void {\n const docState = this.getDocumentState(documentId);\n if (!docState || !docState.pageChangeState.isChanging) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: docState.pageChangeState.targetPage,\n fromPage: docState.pageChangeState.fromPage,\n startTime: docState.pageChangeState.startTime,\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n }\n\n // ─────────────────────────────────────────────────────────\n // Layout & Metrics Computation\n // ─────────────────────────────────────────────────────────\n\n private computeLayout(\n documentId: string,\n pages: PdfPageObjectWithRotatedSize[][],\n ): {\n virtualItems: VirtualItem[];\n totalContentSize: { width: number; height: number };\n } {\n const strategy = this.getStrategy(documentId);\n const virtualItems = strategy.createVirtualItems(pages);\n const totalContentSize = strategy.getTotalContentSize(virtualItems);\n return { virtualItems, totalContentSize };\n }\n\n private computeMetrics(\n documentId: string,\n vp: ViewportMetrics,\n items?: VirtualItem[],\n ): ScrollMetrics {\n const coreDocState = this.getCoreDocumentOrThrow(documentId);\n const docState = this.getDocumentState(documentId);\n const strategy = this.getStrategy(documentId);\n if (!docState) throw new Error(`Document state not found: ${documentId}`);\n\n return strategy.handleScroll(vp, items ?? docState.virtualItems, coreDocState.scale);\n }\n\n // ─────────────────────────────────────────────────────────\n // Commit (Single Source of Truth)\n // ─────────────────────────────────────────────────────────\n\n private commitMetrics(documentId: string, metrics: ScrollMetrics): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n // Update state\n this.dispatch(updateDocumentScrollState(documentId, metrics));\n\n // Emit scroll event\n this.scroll$.emit({ documentId, metrics });\n\n // Emit page change if current page changed\n if (metrics.currentPage !== docState.currentPage) {\n this.pageChange$.emit({\n documentId,\n pageNumber: metrics.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n // CRITICAL: Push updated scroller layout (for spacing/visible items reactivity)\n this.pushScrollerLayout(documentId);\n }\n\n private pushScrollerLayout(documentId: string): void {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) return;\n\n try {\n const layout = this.getScrollerLayout(documentId);\n emitter.emit(layout);\n } catch (error) {\n // Document might be closing, ignore\n }\n }\n\n private refreshDocumentLayout(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n const docState = this.getDocumentState(documentId);\n\n if (!coreDoc || !docState || coreDoc.status !== 'loaded') return;\n\n const pages = this.getSpreadPagesWithRotatedSize(documentId);\n const layout = this.computeLayout(documentId, pages);\n // Get viewport metrics for this document\n const viewport = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewport.getMetrics(), layout.virtualItems);\n\n // Update state with layout + metrics\n this.dispatch(\n updateDocumentScrollState(documentId, {\n ...layout,\n ...metrics,\n }),\n );\n // Emit layout change event\n this.layoutChange$.emit({ documentId, layout });\n\n // Push updated scroller layout\n this.pushScrollerLayout(documentId);\n }\n\n private getSpreadPagesWithRotatedSize(documentId?: string): PdfPageObjectWithRotatedSize[][] {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n if (!coreDoc) throw new Error(`Document ${id} not loaded`);\n\n const spreadPages =\n this.spread?.forDocument(id).getSpreadPages() ||\n coreDoc.document?.pages.map((page) => [page]) ||\n [];\n return spreadPages.map((spread) =>\n spread.map((page) => ({\n ...page,\n rotatedSize: transformSize(page.size, coreDoc.rotation, 1),\n })),\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private getCurrentPage(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).currentPage;\n }\n\n private getTotalPages(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).totalPages;\n }\n\n private getPageChangeState(documentId?: string): PageChangeState {\n return this.getDocumentStateOrThrow(documentId).pageChangeState;\n }\n\n private scrollToPage(options: ScrollToPageOptions, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const { pageNumber, behavior = 'smooth', pageCoordinates, center = false } = options;\n\n this.startPageChange(id, pageNumber, behavior);\n\n const position = strategy.getScrollPositionForPage(\n pageNumber,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n pageCoordinates,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior, center });\n } else {\n this.completePageChange(id);\n }\n }\n\n private scrollToNextPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex >= 0 && currentItemIndex < docState.virtualItems.length - 1) {\n const nextItem = docState.virtualItems[currentItemIndex + 1];\n const targetPage = nextItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private scrollToPreviousPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.coreState.core.documents[id];\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex > 0) {\n const prevItem = docState.virtualItems[currentItemIndex - 1];\n const targetPage = prevItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private getMetrics(viewport?: ViewportMetrics, documentId?: string): ScrollMetrics {\n const id = documentId ?? this.getActiveDocumentId();\n\n if (viewport) {\n return this.computeMetrics(id, viewport);\n }\n\n const viewportScope = this.viewport.forDocument(id);\n return this.computeMetrics(id, viewportScope.getMetrics());\n }\n\n private getLayout(documentId?: string): LayoutChangePayload {\n const docState = this.getDocumentStateOrThrow(documentId);\n return {\n virtualItems: docState.virtualItems,\n totalContentSize: docState.totalContentSize,\n };\n }\n\n private getRectPositionForPage(\n pageIndex: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n documentId?: string,\n ): Rect | null {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n return strategy.getRectPositionForPage(\n pageIndex + 1,\n docState.virtualItems,\n scale ?? coreDoc.scale,\n rotation ?? coreDoc.rotation,\n rect,\n );\n }\n\n private setScrollStrategyForDocument(newStrategy: ScrollStrategy, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n\n if (!docState || docState.strategy === newStrategy) return;\n\n // Create new strategy\n const strategy = this.createStrategy(newStrategy);\n this.strategies.set(id, strategy);\n\n // Update state\n this.dispatch(setScrollStrategy(id, newStrategy));\n\n // Recalculate layout\n this.refreshDocumentLayout(id);\n }\n\n // ─────────────────────────────────────────────────────────\n // Store Update Handlers\n // ─────────────────────────────────────────────────────────\n\n override onStoreUpdated(prevState: ScrollState, newState: ScrollState): void {\n // Emit state changes and push scroller layout for each changed document\n for (const documentId in newState.documents) {\n const prevDoc = prevState.documents[documentId];\n const newDoc = newState.documents[documentId];\n\n if (prevDoc !== newDoc) {\n this.state$.emit(newDoc);\n\n if (prevDoc?.pageChangeState !== newDoc.pageChangeState) {\n this.pageChangeState$.emit({\n documentId,\n state: newDoc.pageChangeState,\n });\n }\n\n // Push scroller layout on any state change\n this.pushScrollerLayout(documentId);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(): Promise<void> {\n this.logger.info('ScrollPlugin', 'Initialize', 'Scroll plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.strategies.clear();\n this.layoutReady.clear();\n this.initialLayoutFired.clear();\n\n // Clear all scroller layout emitters\n for (const emitter of this.scrollerLayoutEmitters.values()) {\n emitter.clear();\n }\n this.scrollerLayoutEmitters.clear();\n\n this.pageChange$.clear();\n this.scroll$.clear();\n this.layoutChange$.clear();\n this.pageChangeState$.clear();\n this.layoutReady$.clear();\n this.state$.clear();\n\n super.destroy();\n }\n}\n","import { PluginManifest } from '@embedpdf/core';\nimport { ScrollPluginConfig, ScrollStrategy } from './types';\n\nexport const SCROLL_PLUGIN_ID = 'scroll';\n\nexport const manifest: PluginManifest<ScrollPluginConfig> = {\n id: SCROLL_PLUGIN_ID,\n name: 'Scroll Plugin',\n version: '1.0.0',\n provides: ['scroll'],\n requires: ['viewport'],\n optional: ['spread'],\n defaultConfig: {\n enabled: true,\n defaultPageGap: 10,\n defaultBufferSize: 4,\n defaultStrategy: ScrollStrategy.Vertical,\n },\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { ScrollPlugin } from './scroll-plugin';\nimport { manifest, SCROLL_PLUGIN_ID } from './manifest';\nimport { ScrollPluginConfig, ScrollState } from './types';\nimport { scrollReducer, initialState } from './reducer';\nimport { ScrollAction } from './actions';\n\nexport const ScrollPluginPackage: PluginPackage<\n ScrollPlugin,\n ScrollPluginConfig,\n ScrollState,\n ScrollAction\n> = {\n manifest,\n create: (registry, config) => new ScrollPlugin(SCROLL_PLUGIN_ID, registry, config),\n reducer: scrollReducer,\n initialState: (coreState, config) => initialState(coreState, config),\n};\n\nexport * from './scroll-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './types/virtual-item';\nexport * from './selectors';\n"],"names":["ScrollStrategy","BaseScrollStrategy","constructor","config","this","pageGap","viewportGap","bufferSize","getVisibleRange","viewport","virtualItems","scale","scrollOffset","getScrollOffset","viewportStart","viewportEnd","getClientSize","startIndex","length","offset","height","endIndex","start","Math","max","end","min","handleScroll","range","visibleItems","slice","pageVisibilityMetrics","calculatePageVisibility","visiblePages","map","m","pageNumber","renderedPageIndexes","flatMap","item","index","currentPage","determineCurrentPage","first","last","startSpacing","endSpacing","x","scrollLeft","y","scrollTop","visibilityMetrics","forEach","pageLayouts","page","itemX","itemY","pageX","pageY","pageWidth","rotatedWidth","pageHeight","rotatedHeight","viewportLeft","viewportTop","viewportRight","clientWidth","viewportBottom","clientHeight","intersectionLeft","intersectionTop","intersectionRight","intersectionBottom","visibleWidth","visibleHeight","totalArea","visibleArea","push","viewportX","viewportY","visiblePercentage","original","scaled","maxVisibility","mostVisiblePages","filter","sort","a","b","getRectLocationForPage","totalContentSize","find","pageNumbers","includes","pageLayout","layout","centeringOffsetX","maxWidth","width","origin","size","getScrollPositionForPage","rotation","pageCoordinates","getTotalContentSize","pageRect","scaledBasePosition","scalePosition","rotatedSize","transformPosition","getRectPositionForPage","rect","transformRect","VerticalScrollStrategy","super","createVirtualItems","pdfPageObject","yOffset","pagesInSpread","pageIndex","reduce","sum","i","p","id","HorizontalScrollStrategy","xOffset","INIT_SCROLL_STATE","CLEANUP_SCROLL_STATE","UPDATE_DOCUMENT_SCROLL_STATE","SET_SCROLL_STRATEGY","updateDocumentScrollState","documentId","state","type","payload","defaultPageChangeState","isChanging","targetPage","fromPage","startTime","getScrollerLayout","documentState","totalWidth","totalHeight","strategy","items","idx","_ScrollPlugin","BasePlugin","registry","strategies","Map","layoutReady","Set","initialLayoutFired","scrollerLayoutEmitters","pageChange$","createBehaviorEmitter","scroll$","layoutChange$","pageChangeState$","layoutReady$","state$","getPlugin","provides","spread","_a","onScrollActivity","event","docState","getDocumentState","pageChangeState","activity","isSmoothScrolling","completePageChange","_b","onSpreadChange","refreshDocumentLayout","onViewportChange","computedMetrics","computeMetrics","metrics","has","commitMetrics","onDocumentLoadingStarted","coreDoc","getCoreDocument","createDocumentState","dispatch","initScrollState","createStrategy","set","onDocumentLoaded","totalPages","document","pageCount","logger","debug","onDocumentClosed","delete","emitter","get","clear","cleanupScrollState","onScaleChanged","coreState","core","documents","status","viewportScope","forDocument","getMetrics","onRotationChanged","onScrollerData","callback","Error","on","getCoreDocumentOrThrow","setLayoutReady","add","isInitial","scrollTo","behavior","emit","clearLayoutReady","buildCapability","getCurrentPage","getTotalPages","getPageChangeState","scrollToPage","options","scrollToNextPage","scrollToPreviousPage","getLayout","createScrollScope","setScrollStrategy","setScrollStrategyForDocument","getPageGap","defaultPageGap","onPageChange","onScroll","onLayoutChange","onLayoutReady","onPageChangeState","onStateChange","getSpreadPagesWithRotatedSize","listener","getActiveDocumentId","getDocumentStateOrThrow","getStrategy","strategyType","getViewportGap","defaultBufferSize","Horizontal","defaultStrategy","startPageChange","Date","now","computeLayout","pages","vp","coreDocState","pushScrollerLayout","error","getSpreadPages","transformSize","center","position","currentItemIndex","findIndex","newStrategy","onStoreUpdated","prevState","newState","prevDoc","newDoc","initialize","info","destroy","values","ScrollPlugin","SCROLL_PLUGIN_ID","manifest","name","version","requires","optional","defaultConfig","enabled","Vertical","ScrollPluginPackage","create","reducer","action","removed","remaining","updates","initialState","_coreState"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/lib/types.ts","../src/lib/strategies/base-strategy.ts","../src/lib/strategies/vertical-strategy.ts","../src/lib/strategies/horizontal-strategy.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/scroll-plugin.ts","../src/lib/manifest.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig, EventHook } from '@embedpdf/core';\nimport { PdfPageObject, PdfPageObjectWithRotatedSize, Rect, Rotation } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from './types/virtual-item';\n\nexport type ScrollBehavior = 'instant' | 'smooth' | 'auto';\n\nexport interface PageChangeState {\n isChanging: boolean;\n targetPage: number;\n fromPage: number;\n startTime: number;\n}\n\n// Per-document scroll state\nexport interface ScrollDocumentState {\n virtualItems: VirtualItem[];\n totalPages: number;\n currentPage: number;\n totalContentSize: { width: number; height: number };\n strategy: ScrollStrategy;\n pageGap: number;\n\n // Scroll metrics\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n pageChangeState: PageChangeState;\n}\n\n// Plugin state\nexport interface ScrollState {\n // Global defaults (applied to new documents)\n defaultStrategy: ScrollStrategy;\n defaultPageGap: number;\n defaultBufferSize: number;\n\n // Per-document states\n documents: Record<string, ScrollDocumentState>;\n}\n\nexport interface ScrollerLayout {\n startSpacing: number;\n endSpacing: number;\n totalWidth: number;\n totalHeight: number;\n pageGap: number;\n strategy: ScrollStrategy;\n items: VirtualItem[];\n}\n\nexport enum ScrollStrategy {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\nexport interface PageVisibilityMetrics {\n pageNumber: number;\n viewportX: number;\n viewportY: number;\n visiblePercentage: number;\n original: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n scaled: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n}\n\nexport interface ScrollMetrics {\n currentPage: number;\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n}\n\nexport interface ScrollPluginConfig extends BasePluginConfig {\n defaultStrategy?: ScrollStrategy;\n defaultPageGap?: number;\n defaultBufferSize?: number;\n}\n\nexport type LayoutChangePayload = Pick<ScrollDocumentState, 'virtualItems' | 'totalContentSize'>;\n\nexport interface ScrollToPageOptions {\n pageNumber: number;\n pageCoordinates?: { x: number; y: number };\n behavior?: ScrollBehavior;\n /**\n * Horizontal alignment as a percentage (0-100).\n * 0 = target at left edge, 50 = centered, 100 = target at right edge.\n */\n alignX?: number;\n /**\n * Vertical alignment as a percentage (0-100).\n * 0 = target at top edge, 50 = centered, 100 = target at bottom edge.\n * Useful for mobile where UI overlays may cover part of the screen (e.g., alignY: 25 for top quarter).\n */\n alignY?: number;\n}\n\n// Events include documentId\nexport interface PageChangeEvent {\n documentId: string;\n pageNumber: number;\n totalPages: number;\n}\n\nexport interface ScrollEvent {\n documentId: string;\n metrics: ScrollMetrics;\n}\n\nexport interface LayoutChangeEvent {\n documentId: string;\n layout: LayoutChangePayload;\n}\n\nexport interface PageChangeStateEvent {\n documentId: string;\n state: PageChangeState;\n}\n\nexport interface LayoutReadyEvent {\n documentId: string;\n /** True only on the first layout ready after document load, false on subsequent (e.g., tab switches) */\n isInitial: boolean;\n pageNumber: number;\n totalPages: number;\n}\n\n// Scoped scroll capability\nexport interface ScrollScope {\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getSpreadPagesWithRotatedSize(): PdfPageObjectWithRotatedSize[][];\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n setScrollStrategy(strategy: ScrollStrategy): void;\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollMetrics>;\n onLayoutChange: EventHook<LayoutChangePayload>;\n}\n\nexport interface ScrollCapability {\n // Active document operations (defaults to active document)\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n\n // Document-scoped operations\n forDocument(documentId: string): ScrollScope;\n\n // Global settings\n setScrollStrategy(strategy: ScrollStrategy, documentId?: string): void;\n getPageGap(): number;\n\n // Events (all include documentId)\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollEvent>;\n onLayoutChange: EventHook<LayoutChangeEvent>;\n onLayoutReady: EventHook<LayoutReadyEvent>;\n onPageChangeState: EventHook<PageChangeStateEvent>;\n onStateChange: EventHook<ScrollDocumentState>;\n}\n","import {\n PdfPageObjectWithRotatedSize,\n Position,\n Rect,\n Rotation,\n scalePosition,\n Size,\n transformPosition,\n transformRect,\n} from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport interface ScrollStrategyConfig {\n pageGap?: number;\n viewportGap?: number;\n bufferSize?: number;\n}\n\nexport abstract class BaseScrollStrategy {\n protected pageGap: number;\n protected viewportGap: number;\n protected bufferSize: number;\n\n constructor(config: ScrollStrategyConfig) {\n this.pageGap = config.pageGap ?? 20;\n this.viewportGap = config.viewportGap ?? 20;\n this.bufferSize = config.bufferSize ?? 2;\n }\n\n abstract createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[];\n abstract getTotalContentSize(virtualItems: VirtualItem[]): Size;\n protected abstract getScrollOffset(viewport: ViewportMetrics): number;\n protected abstract getClientSize(viewport: ViewportMetrics): number;\n\n protected getVisibleRange(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): { start: number; end: number } {\n const scrollOffset = this.getScrollOffset(viewport);\n const clientSize = this.getClientSize(viewport);\n const viewportStart = scrollOffset;\n const viewportEnd = scrollOffset + clientSize;\n\n let startIndex = 0;\n while (\n startIndex < virtualItems.length &&\n (virtualItems[startIndex].offset + virtualItems[startIndex].height) * scale <= viewportStart\n ) {\n startIndex++;\n }\n\n let endIndex = startIndex;\n while (endIndex < virtualItems.length && virtualItems[endIndex].offset * scale <= viewportEnd) {\n endIndex++;\n }\n\n return {\n start: Math.max(0, startIndex - this.bufferSize),\n end: Math.min(virtualItems.length - 1, endIndex + this.bufferSize - 1),\n };\n }\n\n handleScroll(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): ScrollMetrics {\n const range = this.getVisibleRange(viewport, virtualItems, scale);\n const visibleItems = virtualItems.slice(range.start, range.end + 1);\n const pageVisibilityMetrics = this.calculatePageVisibility(visibleItems, viewport, scale);\n const visiblePages = pageVisibilityMetrics.map((m) => m.pageNumber);\n const renderedPageIndexes = virtualItems\n .slice(range.start, range.end + 1)\n .flatMap((item) => item.index);\n const currentPage = this.determineCurrentPage(pageVisibilityMetrics);\n const first = virtualItems[range.start];\n const last = virtualItems[range.end];\n const startSpacing = first ? first.offset * scale : 0;\n const endSpacing = last\n ? (virtualItems[virtualItems.length - 1].offset + // end of content\n virtualItems[virtualItems.length - 1].height) *\n scale - // minus\n (last.offset + last.height) * scale // end of last rendered\n : 0;\n\n return {\n currentPage,\n visiblePages,\n pageVisibilityMetrics,\n renderedPageIndexes,\n scrollOffset: { x: viewport.scrollLeft, y: viewport.scrollTop },\n startSpacing,\n endSpacing,\n };\n }\n\n protected calculatePageVisibility(\n virtualItems: VirtualItem[],\n viewport: ViewportMetrics,\n scale: number,\n ): ScrollMetrics['pageVisibilityMetrics'] {\n const visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'] = [];\n\n virtualItems.forEach((item) => {\n item.pageLayouts.forEach((page) => {\n const itemX = item.x * scale;\n const itemY = item.y * scale;\n const pageX = itemX + page.x * scale;\n const pageY = itemY + page.y * scale;\n const pageWidth = page.rotatedWidth * scale;\n const pageHeight = page.rotatedHeight * scale;\n\n const viewportLeft = viewport.scrollLeft;\n const viewportTop = viewport.scrollTop;\n const viewportRight = viewportLeft + viewport.clientWidth;\n const viewportBottom = viewportTop + viewport.clientHeight;\n\n const intersectionLeft = Math.max(pageX, viewportLeft);\n const intersectionTop = Math.max(pageY, viewportTop);\n const intersectionRight = Math.min(pageX + pageWidth, viewportRight);\n const intersectionBottom = Math.min(pageY + pageHeight, viewportBottom);\n\n if (intersectionLeft < intersectionRight && intersectionTop < intersectionBottom) {\n const visibleWidth = intersectionRight - intersectionLeft;\n const visibleHeight = intersectionBottom - intersectionTop;\n const totalArea = pageWidth * pageHeight;\n const visibleArea = visibleWidth * visibleHeight;\n\n visibilityMetrics.push({\n pageNumber: page.pageNumber,\n viewportX: intersectionLeft - viewportLeft,\n viewportY: intersectionTop - viewportTop,\n visiblePercentage: (visibleArea / totalArea) * 100,\n original: {\n pageX: (intersectionLeft - pageX) / scale,\n pageY: (intersectionTop - pageY) / scale,\n visibleWidth: visibleWidth / scale,\n visibleHeight: visibleHeight / scale,\n scale: 1,\n },\n scaled: {\n pageX: intersectionLeft - pageX,\n pageY: intersectionTop - pageY,\n visibleWidth,\n visibleHeight,\n scale,\n },\n });\n }\n });\n });\n\n return visibilityMetrics;\n }\n\n protected determineCurrentPage(\n visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'],\n ): number {\n if (visibilityMetrics.length === 0) return 1;\n\n const maxVisibility = Math.max(...visibilityMetrics.map((m) => m.visiblePercentage));\n const mostVisiblePages = visibilityMetrics.filter((m) => m.visiblePercentage === maxVisibility);\n\n return mostVisiblePages.length === 1\n ? mostVisiblePages[0].pageNumber\n : mostVisiblePages.sort((a, b) => a.pageNumber - b.pageNumber)[0].pageNumber;\n }\n\n private getRectLocationForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n totalContentSize?: Size,\n ): Rect | null {\n // Find the virtual item containing the page\n const item = virtualItems.find((item) => item.pageNumbers.includes(pageNumber));\n if (!item) return null;\n\n // Find the specific page layout for the requested page number\n const pageLayout = item.pageLayouts.find((layout) => layout.pageNumber === pageNumber);\n if (!pageLayout) return null;\n\n // Calculate centering offset for items that are narrower than the maximum width\n let centeringOffsetX = 0;\n if (totalContentSize) {\n const maxWidth = totalContentSize.width;\n if (item.width < maxWidth) {\n centeringOffsetX = (maxWidth - item.width) / 2;\n }\n }\n\n return {\n origin: {\n x: item.x + pageLayout.x + centeringOffsetX,\n y: item.y + pageLayout.y,\n },\n size: {\n width: pageLayout.width,\n height: pageLayout.height,\n },\n };\n }\n\n getScrollPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n pageCoordinates?: { x: number; y: number },\n ): Position | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n // If specific page coordinates are provided, add them to the base position\n if (pageCoordinates) {\n const rotatedSize = transformPosition(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n {\n x: pageCoordinates.x,\n y: pageCoordinates.y,\n },\n rotation,\n scale,\n );\n\n return {\n x: scaledBasePosition.x + rotatedSize.x + this.viewportGap,\n y: scaledBasePosition.y + rotatedSize.y + this.viewportGap,\n };\n }\n\n return {\n x: scaledBasePosition.x + this.viewportGap,\n y: scaledBasePosition.y + this.viewportGap,\n };\n }\n\n getRectPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n rect: Rect,\n ): Rect | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n const rotatedSize = transformRect(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n rect,\n rotation,\n scale,\n );\n\n return {\n origin: {\n x: scaledBasePosition.x + rotatedSize.origin.x,\n y: scaledBasePosition.y + rotatedSize.origin.y,\n },\n size: rotatedSize.size,\n };\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport class VerticalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let yOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: 0,\n y: yOffset,\n offset: yOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n yOffset += height + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const maxWidth = Math.max(...virtualItems.map((item) => item.width));\n const totalHeight =\n virtualItems[virtualItems.length - 1].y + virtualItems[virtualItems.length - 1].height;\n return {\n width: maxWidth,\n height: totalHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollTop;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientHeight;\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\n\nexport class HorizontalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let xOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: xOffset,\n y: 0,\n offset: xOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n xOffset += width + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const totalWidth =\n virtualItems[virtualItems.length - 1].x + virtualItems[virtualItems.length - 1].width;\n const maxHeight = Math.max(...virtualItems.map((item) => item.height));\n return {\n width: totalWidth,\n height: maxHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollLeft;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientWidth;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { ScrollDocumentState, ScrollStrategy } from './types';\n\n// Document lifecycle\nexport const INIT_SCROLL_STATE = 'INIT_SCROLL_STATE';\nexport const CLEANUP_SCROLL_STATE = 'CLEANUP_SCROLL_STATE';\nexport const UPDATE_DOCUMENT_SCROLL_STATE = 'UPDATE_DOCUMENT_SCROLL_STATE';\nexport const SET_SCROLL_STRATEGY = 'SET_SCROLL_STRATEGY';\n\nexport interface InitScrollStateAction extends Action {\n type: typeof INIT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: ScrollDocumentState;\n };\n}\n\nexport interface CleanupScrollStateAction extends Action {\n type: typeof CLEANUP_SCROLL_STATE;\n payload: string; // documentId\n}\n\nexport interface UpdateDocumentScrollStateAction extends Action {\n type: typeof UPDATE_DOCUMENT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: Partial<ScrollDocumentState>;\n };\n}\n\nexport interface SetScrollStrategyAction extends Action {\n type: typeof SET_SCROLL_STRATEGY;\n payload: {\n documentId: string;\n strategy: ScrollStrategy;\n };\n}\n\nexport type ScrollAction =\n | InitScrollStateAction\n | CleanupScrollStateAction\n | UpdateDocumentScrollStateAction\n | SetScrollStrategyAction;\n\nexport function initScrollState(\n documentId: string,\n state: ScrollDocumentState,\n): InitScrollStateAction {\n return { type: INIT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function cleanupScrollState(documentId: string): CleanupScrollStateAction {\n return { type: CLEANUP_SCROLL_STATE, payload: documentId };\n}\n\nexport function updateDocumentScrollState(\n documentId: string,\n state: Partial<ScrollDocumentState>,\n): UpdateDocumentScrollStateAction {\n return { type: UPDATE_DOCUMENT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function setScrollStrategy(\n documentId: string,\n strategy: ScrollStrategy,\n): SetScrollStrategyAction {\n return { type: SET_SCROLL_STRATEGY, payload: { documentId, strategy } };\n}\n","import { Reducer, CoreState } from '@embedpdf/core';\nimport { ScrollState, ScrollStrategy, ScrollPluginConfig, PageChangeState } from './types';\nimport {\n ScrollAction,\n INIT_SCROLL_STATE,\n CLEANUP_SCROLL_STATE,\n UPDATE_DOCUMENT_SCROLL_STATE,\n SET_SCROLL_STRATEGY,\n} from './actions';\n\nexport const defaultPageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: 1,\n fromPage: 1,\n startTime: 0,\n};\n\nexport const initialState: (coreState: CoreState, config: ScrollPluginConfig) => ScrollState = (\n _coreState,\n config,\n) => ({\n defaultStrategy: config.defaultStrategy ?? ScrollStrategy.Vertical,\n defaultPageGap: config.defaultPageGap ?? 10,\n defaultBufferSize: config.defaultBufferSize ?? 2,\n documents: {},\n});\n\nexport const scrollReducer: Reducer<ScrollState, ScrollAction> = (state, action) => {\n switch (action.type) {\n case INIT_SCROLL_STATE: {\n const { documentId, state: docState } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n };\n }\n\n case CLEANUP_SCROLL_STATE: {\n const { [action.payload]: removed, ...remaining } = state.documents;\n return {\n ...state,\n documents: remaining,\n };\n }\n\n case UPDATE_DOCUMENT_SCROLL_STATE: {\n const { documentId, state: updates } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n ...updates,\n },\n },\n };\n }\n\n case SET_SCROLL_STRATEGY: {\n const { documentId, strategy } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n strategy,\n },\n },\n };\n }\n\n default:\n return state;\n }\n};\n","import { ScrollerLayout, ScrollDocumentState } from './types';\n\nexport const getScrollerLayout = (\n documentState: ScrollDocumentState,\n scale: number,\n): ScrollerLayout => {\n return {\n startSpacing: documentState.startSpacing,\n endSpacing: documentState.endSpacing,\n totalWidth: documentState.totalContentSize.width * scale,\n totalHeight: documentState.totalContentSize.height * scale,\n pageGap: documentState.pageGap * scale,\n strategy: documentState.strategy,\n items: documentState.renderedPageIndexes.map((idx) => {\n return {\n ...documentState.virtualItems[idx],\n pageLayouts: documentState.virtualItems[idx].pageLayouts.map((layout) => {\n return {\n ...layout,\n rotatedWidth: layout.rotatedWidth * scale,\n rotatedHeight: layout.rotatedHeight * scale,\n width: layout.width * scale,\n height: layout.height * scale,\n };\n }),\n };\n }),\n };\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createBehaviorEmitter,\n DocumentState,\n Unsubscribe,\n Listener,\n SET_PAGES,\n} from '@embedpdf/core';\nimport { PdfPageObjectWithRotatedSize, Rect, Rotation, transformSize } from '@embedpdf/models';\nimport { ViewportCapability, ViewportMetrics, ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { SpreadCapability, SpreadPlugin } from '@embedpdf/plugin-spread';\n\nimport {\n ScrollCapability,\n ScrollScope,\n ScrollPluginConfig,\n ScrollStrategy,\n ScrollMetrics,\n ScrollState,\n ScrollDocumentState,\n LayoutChangePayload,\n ScrollerLayout,\n ScrollToPageOptions,\n PageChangeEvent,\n ScrollEvent,\n LayoutChangeEvent,\n PageChangeStateEvent,\n LayoutReadyEvent,\n ScrollBehavior,\n PageChangeState,\n} from './types';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './strategies/base-strategy';\nimport { VerticalScrollStrategy } from './strategies/vertical-strategy';\nimport { HorizontalScrollStrategy } from './strategies/horizontal-strategy';\nimport {\n ScrollAction,\n initScrollState,\n cleanupScrollState,\n updateDocumentScrollState,\n setScrollStrategy,\n} from './actions';\nimport { VirtualItem } from './types/virtual-item';\nimport { defaultPageChangeState } from './reducer';\nimport { getScrollerLayout } from './selectors';\n\nexport class ScrollPlugin extends BasePlugin<\n ScrollPluginConfig,\n ScrollCapability,\n ScrollState,\n ScrollAction\n> {\n static readonly id = 'scroll' as const;\n\n private viewport: ViewportCapability;\n private spread: SpreadCapability | null;\n\n // Strategies per document\n private strategies = new Map<string, BaseScrollStrategy>();\n\n // Layout ready tracking per document\n private layoutReady = new Set<string>();\n\n // Tracks documents that have had their initial layout ready (cleared only on document close)\n private initialLayoutFired = new Set<string>();\n\n // Per-document scroller layout emitters (for real-time scroll updates)\n private scrollerLayoutEmitters = new Map<\n string,\n ReturnType<typeof createBehaviorEmitter<ScrollerLayout>>\n >();\n\n // Event emitters (include documentId)\n private readonly pageChange$ = createBehaviorEmitter<PageChangeEvent>();\n private readonly scroll$ = createBehaviorEmitter<ScrollEvent>();\n private readonly layoutChange$ = createBehaviorEmitter<LayoutChangeEvent>();\n private readonly pageChangeState$ = createBehaviorEmitter<PageChangeStateEvent>();\n private readonly layoutReady$ = createBehaviorEmitter<LayoutReadyEvent>();\n private readonly state$ = createBehaviorEmitter<ScrollDocumentState>();\n\n constructor(\n public readonly id: string,\n registry: PluginRegistry,\n private config?: ScrollPluginConfig,\n ) {\n super(id, registry);\n\n this.viewport = this.registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.spread = this.registry.getPlugin<SpreadPlugin>('spread')?.provides() ?? null;\n\n // Subscribe to viewport scroll activity (per document)\n this.viewport.onScrollActivity((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (docState?.pageChangeState.isChanging && !event.activity.isSmoothScrolling) {\n this.completePageChange(event.documentId);\n }\n });\n\n this.spread?.onSpreadChange((event) => {\n this.refreshDocumentLayout(event.documentId);\n });\n\n // Subscribe to viewport changes (per document) with throttling\n this.viewport.onViewportChange((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (!docState) return;\n\n // Compute the metrics based on the incoming event\n const computedMetrics = this.computeMetrics(event.documentId, event.metrics);\n\n // THE GUARD: Only update the scrollOffset if the layout is already \"ready\".\n if (this.layoutReady.has(event.documentId)) {\n // Layout is ready, so this is a real scroll event from the user.\n // Commit all metrics, including the new scrollOffset.\n this.commitMetrics(event.documentId, computedMetrics);\n } else {\n // Layout is NOT ready. This is the initial, premature event.\n // We must commit the other metrics (like visible pages for rendering)\n // but EXCLUDE the incorrect scrollOffset to protect our persisted state.\n this.commitMetrics(event.documentId, {\n ...computedMetrics,\n scrollOffset: docState.scrollOffset,\n });\n }\n });\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle Hooks (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n protected override onDocumentLoadingStarted(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n // Initialize scroll state for this document\n const docState = this.createDocumentState(coreDoc);\n this.dispatch(initScrollState(documentId, docState));\n\n // Create strategy for this document\n const strategy = this.createStrategy(docState.strategy);\n this.strategies.set(documentId, strategy);\n\n // Create scroller layout emitter for this document\n this.scrollerLayoutEmitters.set(documentId, createBehaviorEmitter<ScrollerLayout>());\n }\n\n protected override onDocumentLoaded(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n\n this.dispatch(\n updateDocumentScrollState(documentId, { totalPages: coreDoc.document?.pageCount ?? 0 }),\n );\n // Initial layout computation\n this.refreshDocumentLayout(documentId);\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentOpened',\n `Initialized scroll state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup strategy\n this.strategies.delete(documentId);\n\n // Cleanup layout ready tracking\n this.layoutReady.delete(documentId);\n this.initialLayoutFired.delete(documentId);\n\n // Cleanup scroller layout emitter\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (emitter) {\n emitter.clear();\n this.scrollerLayoutEmitters.delete(documentId);\n }\n\n // Cleanup state\n this.dispatch(cleanupScrollState(documentId));\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentClosed',\n `Cleaned up scroll state for document: ${documentId}`,\n );\n }\n\n protected override onScaleChanged(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n if (!coreDoc || coreDoc.status !== 'loaded') return;\n\n const viewportScope = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewportScope.getMetrics());\n\n // Use the canonical path so scroll/pageChange events and scroller layout\n // updates all flow through the same place.\n this.commitMetrics(documentId, metrics);\n }\n\n protected override onRotationChanged(documentId: string): void {\n this.refreshDocumentLayout(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Public API for Components (Scroller Layout)\n // ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to scroller layout updates for a specific document\n * This is the key method for the Scroller component to stay reactive\n */\n public onScrollerData(\n documentId: string,\n callback: (layout: ScrollerLayout) => void,\n ): Unsubscribe {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) {\n throw new Error(`No scroller layout emitter found for document: ${documentId}`);\n }\n return emitter.on(callback);\n }\n\n /**\n * Get current scroller layout for a document\n */\n public getScrollerLayout(documentId: string): ScrollerLayout {\n const docState = this.getDocumentState(documentId);\n const coreDoc = this.getCoreDocumentOrThrow(documentId);\n\n if (!docState || !coreDoc) {\n throw new Error(`Cannot get scroller layout for document: ${documentId}`);\n }\n\n return getScrollerLayout(docState, coreDoc.scale);\n }\n\n public setLayoutReady(documentId: string): void {\n // This guard logic is now reliable because the flag gets reset correctly.\n if (this.layoutReady.has(documentId)) {\n return;\n }\n\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n this.layoutReady.add(documentId);\n\n // Determine if this is the initial layout for this document\n const isInitial = !this.initialLayoutFired.has(documentId);\n if (isInitial) {\n this.initialLayoutFired.add(documentId);\n }\n\n // Restore the persisted scroll position\n const viewport = this.viewport.forDocument(documentId);\n viewport.scrollTo({ ...docState.scrollOffset, behavior: 'instant' });\n\n this.layoutReady$.emit({\n documentId,\n isInitial,\n pageNumber: docState.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n public clearLayoutReady(documentId: string): void {\n this.layoutReady.delete(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): ScrollCapability {\n return {\n // Active document operations\n getCurrentPage: () => this.getCurrentPage(),\n getTotalPages: () => this.getTotalPages(),\n getPageChangeState: () => this.getPageChangeState(),\n scrollToPage: (options) => this.scrollToPage(options),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior),\n getMetrics: (viewport) => this.getMetrics(viewport),\n getLayout: () => this.getLayout(),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation),\n\n // Document-scoped operations\n forDocument: (documentId) => this.createScrollScope(documentId),\n\n // Global settings\n setScrollStrategy: (strategy, documentId) =>\n this.setScrollStrategyForDocument(strategy, documentId),\n getPageGap: () => this.state.defaultPageGap,\n\n // Events\n onPageChange: this.pageChange$.on,\n onScroll: this.scroll$.on,\n onLayoutChange: this.layoutChange$.on,\n onLayoutReady: this.layoutReady$.on,\n onPageChangeState: this.pageChangeState$.on,\n onStateChange: this.state$.on,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createScrollScope(documentId: string): ScrollScope {\n return {\n getCurrentPage: () => this.getCurrentPage(documentId),\n getTotalPages: () => this.getTotalPages(documentId),\n getPageChangeState: () => this.getPageChangeState(documentId),\n scrollToPage: (options) => this.scrollToPage(options, documentId),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior, documentId),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior, documentId),\n getSpreadPagesWithRotatedSize: () => this.getSpreadPagesWithRotatedSize(documentId),\n getMetrics: (viewport) => this.getMetrics(viewport, documentId),\n getLayout: () => this.getLayout(documentId),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation, documentId),\n setScrollStrategy: (strategy) => this.setScrollStrategyForDocument(strategy, documentId),\n onPageChange: (listener: Listener<PageChangeEvent>) =>\n this.pageChange$.on((event) => {\n if (event.documentId === documentId) listener(event);\n }),\n onScroll: (listener: Listener<ScrollMetrics>) =>\n this.scroll$.on((event) => {\n if (event.documentId === documentId) listener(event.metrics);\n }),\n onLayoutChange: (listener: Listener<LayoutChangePayload>) =>\n this.layoutChange$.on((event) => {\n if (event.documentId === documentId) listener(event.layout);\n }),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // State Helpers\n // ─────────────────────────────────────────────────────────\n private getDocumentState(documentId?: string): ScrollDocumentState | null {\n const id = documentId ?? this.getActiveDocumentId();\n return this.state.documents[id] ?? null;\n }\n\n private getDocumentStateOrThrow(documentId?: string): ScrollDocumentState {\n const state = this.getDocumentState(documentId);\n if (!state) {\n throw new Error(`Scroll state not found for document: ${documentId ?? 'active'}`);\n }\n return state;\n }\n\n private getStrategy(documentId?: string): BaseScrollStrategy {\n const id = documentId ?? this.getActiveDocumentId();\n const strategy = this.strategies.get(id);\n if (!strategy) {\n throw new Error(`Strategy not found for document: ${id}`);\n }\n return strategy;\n }\n\n private createStrategy(strategyType: ScrollStrategy): BaseScrollStrategy {\n const config: ScrollStrategyConfig = {\n pageGap: this.state.defaultPageGap,\n viewportGap: this.viewport.getViewportGap(),\n bufferSize: this.state.defaultBufferSize,\n };\n\n return strategyType === ScrollStrategy.Horizontal\n ? new HorizontalScrollStrategy(config)\n : new VerticalScrollStrategy(config);\n }\n\n private createDocumentState(coreDoc: DocumentState): ScrollDocumentState {\n return {\n virtualItems: [],\n totalPages: coreDoc.document?.pageCount ?? 0,\n currentPage: 1,\n totalContentSize: { width: 0, height: 0 },\n strategy: this.state.defaultStrategy,\n pageGap: this.state.defaultPageGap,\n visiblePages: [],\n pageVisibilityMetrics: [],\n renderedPageIndexes: [],\n scrollOffset: { x: 0, y: 0 },\n startSpacing: 0,\n endSpacing: 0,\n pageChangeState: defaultPageChangeState,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Page Change Management\n // ─────────────────────────────────────────────────────────\n\n private startPageChange(\n documentId: string,\n targetPage: number,\n behavior: ScrollBehavior = 'smooth',\n ): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: true,\n targetPage,\n fromPage: docState.currentPage,\n startTime: Date.now(),\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n\n if (behavior === 'instant') {\n this.completePageChange(documentId);\n }\n }\n\n private completePageChange(documentId: string): void {\n const docState = this.getDocumentState(documentId);\n if (!docState || !docState.pageChangeState.isChanging) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: docState.pageChangeState.targetPage,\n fromPage: docState.pageChangeState.fromPage,\n startTime: docState.pageChangeState.startTime,\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n }\n\n // ─────────────────────────────────────────────────────────\n // Layout & Metrics Computation\n // ─────────────────────────────────────────────────────────\n\n private computeLayout(\n documentId: string,\n pages: PdfPageObjectWithRotatedSize[][],\n ): {\n virtualItems: VirtualItem[];\n totalContentSize: { width: number; height: number };\n } {\n const strategy = this.getStrategy(documentId);\n const virtualItems = strategy.createVirtualItems(pages);\n const totalContentSize = strategy.getTotalContentSize(virtualItems);\n return { virtualItems, totalContentSize };\n }\n\n private computeMetrics(\n documentId: string,\n vp: ViewportMetrics,\n items?: VirtualItem[],\n ): ScrollMetrics {\n const coreDocState = this.getCoreDocumentOrThrow(documentId);\n const docState = this.getDocumentState(documentId);\n const strategy = this.getStrategy(documentId);\n if (!docState) throw new Error(`Document state not found: ${documentId}`);\n\n return strategy.handleScroll(vp, items ?? docState.virtualItems, coreDocState.scale);\n }\n\n // ─────────────────────────────────────────────────────────\n // Commit (Single Source of Truth)\n // ─────────────────────────────────────────────────────────\n\n private commitMetrics(documentId: string, metrics: ScrollMetrics): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n // Update state\n this.dispatch(updateDocumentScrollState(documentId, metrics));\n\n // Emit scroll event\n this.scroll$.emit({ documentId, metrics });\n\n // Emit page change if current page changed\n if (metrics.currentPage !== docState.currentPage) {\n this.pageChange$.emit({\n documentId,\n pageNumber: metrics.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n // CRITICAL: Push updated scroller layout (for spacing/visible items reactivity)\n this.pushScrollerLayout(documentId);\n }\n\n private pushScrollerLayout(documentId: string): void {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) return;\n\n try {\n const layout = this.getScrollerLayout(documentId);\n emitter.emit(layout);\n } catch (error) {\n // Document might be closing, ignore\n }\n }\n\n private refreshDocumentLayout(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n const docState = this.getDocumentState(documentId);\n\n if (!coreDoc || !docState || coreDoc.status !== 'loaded') return;\n\n const pages = this.getSpreadPagesWithRotatedSize(documentId);\n const layout = this.computeLayout(documentId, pages);\n // Get viewport metrics for this document\n const viewport = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewport.getMetrics(), layout.virtualItems);\n\n // Update state with layout + metrics\n this.dispatch(\n updateDocumentScrollState(documentId, {\n ...layout,\n ...metrics,\n }),\n );\n // Emit layout change event\n this.layoutChange$.emit({ documentId, layout });\n\n // Push updated scroller layout\n this.pushScrollerLayout(documentId);\n }\n\n private getSpreadPagesWithRotatedSize(documentId?: string): PdfPageObjectWithRotatedSize[][] {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n if (!coreDoc) throw new Error(`Document ${id} not loaded`);\n\n const spreadPages =\n this.spread?.forDocument(id).getSpreadPages() ||\n coreDoc.document?.pages.map((page) => [page]) ||\n [];\n return spreadPages.map((spread) =>\n spread.map((page) => ({\n ...page,\n rotatedSize: transformSize(page.size, coreDoc.rotation, 1),\n })),\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private getCurrentPage(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).currentPage;\n }\n\n private getTotalPages(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).totalPages;\n }\n\n private getPageChangeState(documentId?: string): PageChangeState {\n return this.getDocumentStateOrThrow(documentId).pageChangeState;\n }\n\n private scrollToPage(options: ScrollToPageOptions, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const { pageNumber, behavior = 'smooth', pageCoordinates, alignX, alignY } = options;\n\n this.startPageChange(id, pageNumber, behavior);\n\n const position = strategy.getScrollPositionForPage(\n pageNumber,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n pageCoordinates,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior, alignX, alignY });\n } else {\n this.completePageChange(id);\n }\n }\n\n private scrollToNextPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex >= 0 && currentItemIndex < docState.virtualItems.length - 1) {\n const nextItem = docState.virtualItems[currentItemIndex + 1];\n const targetPage = nextItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private scrollToPreviousPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.coreState.core.documents[id];\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex > 0) {\n const prevItem = docState.virtualItems[currentItemIndex - 1];\n const targetPage = prevItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private getMetrics(viewport?: ViewportMetrics, documentId?: string): ScrollMetrics {\n const id = documentId ?? this.getActiveDocumentId();\n\n if (viewport) {\n return this.computeMetrics(id, viewport);\n }\n\n const viewportScope = this.viewport.forDocument(id);\n return this.computeMetrics(id, viewportScope.getMetrics());\n }\n\n private getLayout(documentId?: string): LayoutChangePayload {\n const docState = this.getDocumentStateOrThrow(documentId);\n return {\n virtualItems: docState.virtualItems,\n totalContentSize: docState.totalContentSize,\n };\n }\n\n private getRectPositionForPage(\n pageIndex: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n documentId?: string,\n ): Rect | null {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n return strategy.getRectPositionForPage(\n pageIndex + 1,\n docState.virtualItems,\n scale ?? coreDoc.scale,\n rotation ?? coreDoc.rotation,\n rect,\n );\n }\n\n private setScrollStrategyForDocument(newStrategy: ScrollStrategy, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n\n if (!docState || docState.strategy === newStrategy) return;\n\n // Create new strategy\n const strategy = this.createStrategy(newStrategy);\n this.strategies.set(id, strategy);\n\n // Update state\n this.dispatch(setScrollStrategy(id, newStrategy));\n\n // Recalculate layout\n this.refreshDocumentLayout(id);\n }\n\n // ─────────────────────────────────────────────────────────\n // Store Update Handlers\n // ─────────────────────────────────────────────────────────\n\n override onStoreUpdated(prevState: ScrollState, newState: ScrollState): void {\n // Emit state changes and push scroller layout for each changed document\n for (const documentId in newState.documents) {\n const prevDoc = prevState.documents[documentId];\n const newDoc = newState.documents[documentId];\n\n if (prevDoc !== newDoc) {\n this.state$.emit(newDoc);\n\n if (prevDoc?.pageChangeState !== newDoc.pageChangeState) {\n this.pageChangeState$.emit({\n documentId,\n state: newDoc.pageChangeState,\n });\n }\n\n // Push scroller layout on any state change\n this.pushScrollerLayout(documentId);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(): Promise<void> {\n this.logger.info('ScrollPlugin', 'Initialize', 'Scroll plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.strategies.clear();\n this.layoutReady.clear();\n this.initialLayoutFired.clear();\n\n // Clear all scroller layout emitters\n for (const emitter of this.scrollerLayoutEmitters.values()) {\n emitter.clear();\n }\n this.scrollerLayoutEmitters.clear();\n\n this.pageChange$.clear();\n this.scroll$.clear();\n this.layoutChange$.clear();\n this.pageChangeState$.clear();\n this.layoutReady$.clear();\n this.state$.clear();\n\n super.destroy();\n }\n}\n","import { PluginManifest } from '@embedpdf/core';\nimport { ScrollPluginConfig, ScrollStrategy } from './types';\n\nexport const SCROLL_PLUGIN_ID = 'scroll';\n\nexport const manifest: PluginManifest<ScrollPluginConfig> = {\n id: SCROLL_PLUGIN_ID,\n name: 'Scroll Plugin',\n version: '1.0.0',\n provides: ['scroll'],\n requires: ['viewport'],\n optional: ['spread'],\n defaultConfig: {\n defaultPageGap: 10,\n defaultBufferSize: 4,\n defaultStrategy: ScrollStrategy.Vertical,\n },\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { ScrollPlugin } from './scroll-plugin';\nimport { manifest, SCROLL_PLUGIN_ID } from './manifest';\nimport { ScrollPluginConfig, ScrollState } from './types';\nimport { scrollReducer, initialState } from './reducer';\nimport { ScrollAction } from './actions';\n\nexport const ScrollPluginPackage: PluginPackage<\n ScrollPlugin,\n ScrollPluginConfig,\n ScrollState,\n ScrollAction\n> = {\n manifest,\n create: (registry, config) => new ScrollPlugin(SCROLL_PLUGIN_ID, registry, config),\n reducer: scrollReducer,\n initialState: (coreState, config) => initialState(coreState, config),\n};\n\nexport * from './scroll-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './types/virtual-item';\nexport * from './selectors';\n"],"names":["ScrollStrategy","BaseScrollStrategy","constructor","config","this","pageGap","viewportGap","bufferSize","getVisibleRange","viewport","virtualItems","scale","scrollOffset","getScrollOffset","viewportStart","viewportEnd","getClientSize","startIndex","length","offset","height","endIndex","start","Math","max","end","min","handleScroll","range","visibleItems","slice","pageVisibilityMetrics","calculatePageVisibility","visiblePages","map","m","pageNumber","renderedPageIndexes","flatMap","item","index","currentPage","determineCurrentPage","first","last","startSpacing","endSpacing","x","scrollLeft","y","scrollTop","visibilityMetrics","forEach","pageLayouts","page","itemX","itemY","pageX","pageY","pageWidth","rotatedWidth","pageHeight","rotatedHeight","viewportLeft","viewportTop","viewportRight","clientWidth","viewportBottom","clientHeight","intersectionLeft","intersectionTop","intersectionRight","intersectionBottom","visibleWidth","visibleHeight","totalArea","visibleArea","push","viewportX","viewportY","visiblePercentage","original","scaled","maxVisibility","mostVisiblePages","filter","sort","a","b","getRectLocationForPage","totalContentSize","find","pageNumbers","includes","pageLayout","layout","centeringOffsetX","maxWidth","width","origin","size","getScrollPositionForPage","rotation","pageCoordinates","getTotalContentSize","pageRect","scaledBasePosition","scalePosition","rotatedSize","transformPosition","getRectPositionForPage","rect","transformRect","VerticalScrollStrategy","super","createVirtualItems","pdfPageObject","yOffset","pagesInSpread","pageIndex","reduce","sum","i","p","id","HorizontalScrollStrategy","xOffset","INIT_SCROLL_STATE","CLEANUP_SCROLL_STATE","UPDATE_DOCUMENT_SCROLL_STATE","SET_SCROLL_STRATEGY","updateDocumentScrollState","documentId","state","type","payload","defaultPageChangeState","isChanging","targetPage","fromPage","startTime","getScrollerLayout","documentState","totalWidth","totalHeight","strategy","items","idx","_ScrollPlugin","BasePlugin","registry","strategies","Map","layoutReady","Set","initialLayoutFired","scrollerLayoutEmitters","pageChange$","createBehaviorEmitter","scroll$","layoutChange$","pageChangeState$","layoutReady$","state$","getPlugin","provides","spread","_a","onScrollActivity","event","docState","getDocumentState","pageChangeState","activity","isSmoothScrolling","completePageChange","_b","onSpreadChange","refreshDocumentLayout","onViewportChange","computedMetrics","computeMetrics","metrics","has","commitMetrics","onDocumentLoadingStarted","coreDoc","getCoreDocument","createDocumentState","dispatch","initScrollState","createStrategy","set","onDocumentLoaded","totalPages","document","pageCount","logger","debug","onDocumentClosed","delete","emitter","get","clear","cleanupScrollState","onScaleChanged","coreState","core","documents","status","viewportScope","forDocument","getMetrics","onRotationChanged","onScrollerData","callback","Error","on","getCoreDocumentOrThrow","setLayoutReady","add","isInitial","scrollTo","behavior","emit","clearLayoutReady","buildCapability","getCurrentPage","getTotalPages","getPageChangeState","scrollToPage","options","scrollToNextPage","scrollToPreviousPage","getLayout","createScrollScope","setScrollStrategy","setScrollStrategyForDocument","getPageGap","defaultPageGap","onPageChange","onScroll","onLayoutChange","onLayoutReady","onPageChangeState","onStateChange","getSpreadPagesWithRotatedSize","listener","getActiveDocumentId","getDocumentStateOrThrow","getStrategy","strategyType","getViewportGap","defaultBufferSize","Horizontal","defaultStrategy","startPageChange","Date","now","computeLayout","pages","vp","coreDocState","pushScrollerLayout","error","getSpreadPages","transformSize","alignX","alignY","position","currentItemIndex","findIndex","newStrategy","onStoreUpdated","prevState","newState","prevDoc","newDoc","initialize","info","destroy","values","ScrollPlugin","SCROLL_PLUGIN_ID","manifest","name","version","requires","optional","defaultConfig","Vertical","ScrollPluginPackage","create","reducer","action","removed","remaining","updates","initialState","_coreState"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -532,7 +532,12 @@ const _ScrollPlugin = class _ScrollPlugin extends BasePlugin {
|
|
|
532
532
|
}
|
|
533
533
|
const viewport = this.viewport.forDocument(documentId);
|
|
534
534
|
viewport.scrollTo({ ...docState.scrollOffset, behavior: "instant" });
|
|
535
|
-
this.layoutReady$.emit({
|
|
535
|
+
this.layoutReady$.emit({
|
|
536
|
+
documentId,
|
|
537
|
+
isInitial,
|
|
538
|
+
pageNumber: docState.currentPage,
|
|
539
|
+
totalPages: docState.totalPages
|
|
540
|
+
});
|
|
536
541
|
}
|
|
537
542
|
clearLayoutReady(documentId) {
|
|
538
543
|
this.layoutReady.delete(documentId);
|
|
@@ -758,7 +763,7 @@ const _ScrollPlugin = class _ScrollPlugin extends BasePlugin {
|
|
|
758
763
|
const docState = this.getDocumentStateOrThrow(id);
|
|
759
764
|
const strategy = this.getStrategy(id);
|
|
760
765
|
const coreDoc = this.getCoreDocumentOrThrow(id);
|
|
761
|
-
const { pageNumber, behavior = "smooth", pageCoordinates,
|
|
766
|
+
const { pageNumber, behavior = "smooth", pageCoordinates, alignX, alignY } = options;
|
|
762
767
|
this.startPageChange(id, pageNumber, behavior);
|
|
763
768
|
const position = strategy.getScrollPositionForPage(
|
|
764
769
|
pageNumber,
|
|
@@ -769,7 +774,7 @@ const _ScrollPlugin = class _ScrollPlugin extends BasePlugin {
|
|
|
769
774
|
);
|
|
770
775
|
if (position) {
|
|
771
776
|
const viewport = this.viewport.forDocument(id);
|
|
772
|
-
viewport.scrollTo({ ...position, behavior,
|
|
777
|
+
viewport.scrollTo({ ...position, behavior, alignX, alignY });
|
|
773
778
|
} else {
|
|
774
779
|
this.completePageChange(id);
|
|
775
780
|
}
|
|
@@ -916,7 +921,6 @@ const manifest = {
|
|
|
916
921
|
requires: ["viewport"],
|
|
917
922
|
optional: ["spread"],
|
|
918
923
|
defaultConfig: {
|
|
919
|
-
enabled: true,
|
|
920
924
|
defaultPageGap: 10,
|
|
921
925
|
defaultBufferSize: 4,
|
|
922
926
|
defaultStrategy: ScrollStrategy.Vertical
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/lib/types.ts","../src/lib/strategies/base-strategy.ts","../src/lib/strategies/vertical-strategy.ts","../src/lib/strategies/horizontal-strategy.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/scroll-plugin.ts","../src/lib/manifest.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig, EventHook } from '@embedpdf/core';\nimport { PdfPageObject, PdfPageObjectWithRotatedSize, Rect, Rotation } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from './types/virtual-item';\n\nexport type ScrollBehavior = 'instant' | 'smooth' | 'auto';\n\nexport interface PageChangeState {\n isChanging: boolean;\n targetPage: number;\n fromPage: number;\n startTime: number;\n}\n\n// Per-document scroll state\nexport interface ScrollDocumentState {\n virtualItems: VirtualItem[];\n totalPages: number;\n currentPage: number;\n totalContentSize: { width: number; height: number };\n strategy: ScrollStrategy;\n pageGap: number;\n\n // Scroll metrics\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n pageChangeState: PageChangeState;\n}\n\n// Plugin state\nexport interface ScrollState {\n // Global defaults (applied to new documents)\n defaultStrategy: ScrollStrategy;\n defaultPageGap: number;\n defaultBufferSize: number;\n\n // Per-document states\n documents: Record<string, ScrollDocumentState>;\n}\n\nexport interface ScrollerLayout {\n startSpacing: number;\n endSpacing: number;\n totalWidth: number;\n totalHeight: number;\n pageGap: number;\n strategy: ScrollStrategy;\n items: VirtualItem[];\n}\n\nexport enum ScrollStrategy {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\nexport interface PageVisibilityMetrics {\n pageNumber: number;\n viewportX: number;\n viewportY: number;\n visiblePercentage: number;\n original: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n scaled: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n}\n\nexport interface ScrollMetrics {\n currentPage: number;\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n}\n\nexport interface ScrollPluginConfig extends BasePluginConfig {\n defaultStrategy?: ScrollStrategy;\n defaultPageGap?: number;\n defaultBufferSize?: number;\n}\n\nexport type LayoutChangePayload = Pick<ScrollDocumentState, 'virtualItems' | 'totalContentSize'>;\n\nexport interface ScrollToPageOptions {\n pageNumber: number;\n pageCoordinates?: { x: number; y: number };\n behavior?: ScrollBehavior;\n center?: boolean;\n}\n\n// Events include documentId\nexport interface PageChangeEvent {\n documentId: string;\n pageNumber: number;\n totalPages: number;\n}\n\nexport interface ScrollEvent {\n documentId: string;\n metrics: ScrollMetrics;\n}\n\nexport interface LayoutChangeEvent {\n documentId: string;\n layout: LayoutChangePayload;\n}\n\nexport interface PageChangeStateEvent {\n documentId: string;\n state: PageChangeState;\n}\n\nexport interface LayoutReadyEvent {\n documentId: string;\n /** True only on the first layout ready after document load, false on subsequent (e.g., tab switches) */\n isInitial: boolean;\n}\n\n// Scoped scroll capability\nexport interface ScrollScope {\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getSpreadPagesWithRotatedSize(): PdfPageObjectWithRotatedSize[][];\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n setScrollStrategy(strategy: ScrollStrategy): void;\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollMetrics>;\n onLayoutChange: EventHook<LayoutChangePayload>;\n}\n\nexport interface ScrollCapability {\n // Active document operations (defaults to active document)\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n\n // Document-scoped operations\n forDocument(documentId: string): ScrollScope;\n\n // Global settings\n setScrollStrategy(strategy: ScrollStrategy, documentId?: string): void;\n getPageGap(): number;\n\n // Events (all include documentId)\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollEvent>;\n onLayoutChange: EventHook<LayoutChangeEvent>;\n onLayoutReady: EventHook<LayoutReadyEvent>;\n onPageChangeState: EventHook<PageChangeStateEvent>;\n onStateChange: EventHook<ScrollDocumentState>;\n}\n","import {\n PdfPageObjectWithRotatedSize,\n Position,\n Rect,\n Rotation,\n scalePosition,\n Size,\n transformPosition,\n transformRect,\n} from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport interface ScrollStrategyConfig {\n pageGap?: number;\n viewportGap?: number;\n bufferSize?: number;\n}\n\nexport abstract class BaseScrollStrategy {\n protected pageGap: number;\n protected viewportGap: number;\n protected bufferSize: number;\n\n constructor(config: ScrollStrategyConfig) {\n this.pageGap = config.pageGap ?? 20;\n this.viewportGap = config.viewportGap ?? 20;\n this.bufferSize = config.bufferSize ?? 2;\n }\n\n abstract createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[];\n abstract getTotalContentSize(virtualItems: VirtualItem[]): Size;\n protected abstract getScrollOffset(viewport: ViewportMetrics): number;\n protected abstract getClientSize(viewport: ViewportMetrics): number;\n\n protected getVisibleRange(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): { start: number; end: number } {\n const scrollOffset = this.getScrollOffset(viewport);\n const clientSize = this.getClientSize(viewport);\n const viewportStart = scrollOffset;\n const viewportEnd = scrollOffset + clientSize;\n\n let startIndex = 0;\n while (\n startIndex < virtualItems.length &&\n (virtualItems[startIndex].offset + virtualItems[startIndex].height) * scale <= viewportStart\n ) {\n startIndex++;\n }\n\n let endIndex = startIndex;\n while (endIndex < virtualItems.length && virtualItems[endIndex].offset * scale <= viewportEnd) {\n endIndex++;\n }\n\n return {\n start: Math.max(0, startIndex - this.bufferSize),\n end: Math.min(virtualItems.length - 1, endIndex + this.bufferSize - 1),\n };\n }\n\n handleScroll(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): ScrollMetrics {\n const range = this.getVisibleRange(viewport, virtualItems, scale);\n const visibleItems = virtualItems.slice(range.start, range.end + 1);\n const pageVisibilityMetrics = this.calculatePageVisibility(visibleItems, viewport, scale);\n const visiblePages = pageVisibilityMetrics.map((m) => m.pageNumber);\n const renderedPageIndexes = virtualItems\n .slice(range.start, range.end + 1)\n .flatMap((item) => item.index);\n const currentPage = this.determineCurrentPage(pageVisibilityMetrics);\n const first = virtualItems[range.start];\n const last = virtualItems[range.end];\n const startSpacing = first ? first.offset * scale : 0;\n const endSpacing = last\n ? (virtualItems[virtualItems.length - 1].offset + // end of content\n virtualItems[virtualItems.length - 1].height) *\n scale - // minus\n (last.offset + last.height) * scale // end of last rendered\n : 0;\n\n return {\n currentPage,\n visiblePages,\n pageVisibilityMetrics,\n renderedPageIndexes,\n scrollOffset: { x: viewport.scrollLeft, y: viewport.scrollTop },\n startSpacing,\n endSpacing,\n };\n }\n\n protected calculatePageVisibility(\n virtualItems: VirtualItem[],\n viewport: ViewportMetrics,\n scale: number,\n ): ScrollMetrics['pageVisibilityMetrics'] {\n const visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'] = [];\n\n virtualItems.forEach((item) => {\n item.pageLayouts.forEach((page) => {\n const itemX = item.x * scale;\n const itemY = item.y * scale;\n const pageX = itemX + page.x * scale;\n const pageY = itemY + page.y * scale;\n const pageWidth = page.rotatedWidth * scale;\n const pageHeight = page.rotatedHeight * scale;\n\n const viewportLeft = viewport.scrollLeft;\n const viewportTop = viewport.scrollTop;\n const viewportRight = viewportLeft + viewport.clientWidth;\n const viewportBottom = viewportTop + viewport.clientHeight;\n\n const intersectionLeft = Math.max(pageX, viewportLeft);\n const intersectionTop = Math.max(pageY, viewportTop);\n const intersectionRight = Math.min(pageX + pageWidth, viewportRight);\n const intersectionBottom = Math.min(pageY + pageHeight, viewportBottom);\n\n if (intersectionLeft < intersectionRight && intersectionTop < intersectionBottom) {\n const visibleWidth = intersectionRight - intersectionLeft;\n const visibleHeight = intersectionBottom - intersectionTop;\n const totalArea = pageWidth * pageHeight;\n const visibleArea = visibleWidth * visibleHeight;\n\n visibilityMetrics.push({\n pageNumber: page.pageNumber,\n viewportX: intersectionLeft - viewportLeft,\n viewportY: intersectionTop - viewportTop,\n visiblePercentage: (visibleArea / totalArea) * 100,\n original: {\n pageX: (intersectionLeft - pageX) / scale,\n pageY: (intersectionTop - pageY) / scale,\n visibleWidth: visibleWidth / scale,\n visibleHeight: visibleHeight / scale,\n scale: 1,\n },\n scaled: {\n pageX: intersectionLeft - pageX,\n pageY: intersectionTop - pageY,\n visibleWidth,\n visibleHeight,\n scale,\n },\n });\n }\n });\n });\n\n return visibilityMetrics;\n }\n\n protected determineCurrentPage(\n visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'],\n ): number {\n if (visibilityMetrics.length === 0) return 1;\n\n const maxVisibility = Math.max(...visibilityMetrics.map((m) => m.visiblePercentage));\n const mostVisiblePages = visibilityMetrics.filter((m) => m.visiblePercentage === maxVisibility);\n\n return mostVisiblePages.length === 1\n ? mostVisiblePages[0].pageNumber\n : mostVisiblePages.sort((a, b) => a.pageNumber - b.pageNumber)[0].pageNumber;\n }\n\n private getRectLocationForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n totalContentSize?: Size,\n ): Rect | null {\n // Find the virtual item containing the page\n const item = virtualItems.find((item) => item.pageNumbers.includes(pageNumber));\n if (!item) return null;\n\n // Find the specific page layout for the requested page number\n const pageLayout = item.pageLayouts.find((layout) => layout.pageNumber === pageNumber);\n if (!pageLayout) return null;\n\n // Calculate centering offset for items that are narrower than the maximum width\n let centeringOffsetX = 0;\n if (totalContentSize) {\n const maxWidth = totalContentSize.width;\n if (item.width < maxWidth) {\n centeringOffsetX = (maxWidth - item.width) / 2;\n }\n }\n\n return {\n origin: {\n x: item.x + pageLayout.x + centeringOffsetX,\n y: item.y + pageLayout.y,\n },\n size: {\n width: pageLayout.width,\n height: pageLayout.height,\n },\n };\n }\n\n getScrollPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n pageCoordinates?: { x: number; y: number },\n ): Position | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n // If specific page coordinates are provided, add them to the base position\n if (pageCoordinates) {\n const rotatedSize = transformPosition(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n {\n x: pageCoordinates.x,\n y: pageCoordinates.y,\n },\n rotation,\n scale,\n );\n\n return {\n x: scaledBasePosition.x + rotatedSize.x + this.viewportGap,\n y: scaledBasePosition.y + rotatedSize.y + this.viewportGap,\n };\n }\n\n return {\n x: scaledBasePosition.x + this.viewportGap,\n y: scaledBasePosition.y + this.viewportGap,\n };\n }\n\n getRectPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n rect: Rect,\n ): Rect | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n const rotatedSize = transformRect(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n rect,\n rotation,\n scale,\n );\n\n return {\n origin: {\n x: scaledBasePosition.x + rotatedSize.origin.x,\n y: scaledBasePosition.y + rotatedSize.origin.y,\n },\n size: rotatedSize.size,\n };\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport class VerticalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let yOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: 0,\n y: yOffset,\n offset: yOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n yOffset += height + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const maxWidth = Math.max(...virtualItems.map((item) => item.width));\n const totalHeight =\n virtualItems[virtualItems.length - 1].y + virtualItems[virtualItems.length - 1].height;\n return {\n width: maxWidth,\n height: totalHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollTop;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientHeight;\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\n\nexport class HorizontalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let xOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: xOffset,\n y: 0,\n offset: xOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n xOffset += width + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const totalWidth =\n virtualItems[virtualItems.length - 1].x + virtualItems[virtualItems.length - 1].width;\n const maxHeight = Math.max(...virtualItems.map((item) => item.height));\n return {\n width: totalWidth,\n height: maxHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollLeft;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientWidth;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { ScrollDocumentState, ScrollStrategy } from './types';\n\n// Document lifecycle\nexport const INIT_SCROLL_STATE = 'INIT_SCROLL_STATE';\nexport const CLEANUP_SCROLL_STATE = 'CLEANUP_SCROLL_STATE';\nexport const UPDATE_DOCUMENT_SCROLL_STATE = 'UPDATE_DOCUMENT_SCROLL_STATE';\nexport const SET_SCROLL_STRATEGY = 'SET_SCROLL_STRATEGY';\n\nexport interface InitScrollStateAction extends Action {\n type: typeof INIT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: ScrollDocumentState;\n };\n}\n\nexport interface CleanupScrollStateAction extends Action {\n type: typeof CLEANUP_SCROLL_STATE;\n payload: string; // documentId\n}\n\nexport interface UpdateDocumentScrollStateAction extends Action {\n type: typeof UPDATE_DOCUMENT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: Partial<ScrollDocumentState>;\n };\n}\n\nexport interface SetScrollStrategyAction extends Action {\n type: typeof SET_SCROLL_STRATEGY;\n payload: {\n documentId: string;\n strategy: ScrollStrategy;\n };\n}\n\nexport type ScrollAction =\n | InitScrollStateAction\n | CleanupScrollStateAction\n | UpdateDocumentScrollStateAction\n | SetScrollStrategyAction;\n\nexport function initScrollState(\n documentId: string,\n state: ScrollDocumentState,\n): InitScrollStateAction {\n return { type: INIT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function cleanupScrollState(documentId: string): CleanupScrollStateAction {\n return { type: CLEANUP_SCROLL_STATE, payload: documentId };\n}\n\nexport function updateDocumentScrollState(\n documentId: string,\n state: Partial<ScrollDocumentState>,\n): UpdateDocumentScrollStateAction {\n return { type: UPDATE_DOCUMENT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function setScrollStrategy(\n documentId: string,\n strategy: ScrollStrategy,\n): SetScrollStrategyAction {\n return { type: SET_SCROLL_STRATEGY, payload: { documentId, strategy } };\n}\n","import { Reducer, CoreState } from '@embedpdf/core';\nimport { ScrollState, ScrollStrategy, ScrollPluginConfig, PageChangeState } from './types';\nimport {\n ScrollAction,\n INIT_SCROLL_STATE,\n CLEANUP_SCROLL_STATE,\n UPDATE_DOCUMENT_SCROLL_STATE,\n SET_SCROLL_STRATEGY,\n} from './actions';\n\nexport const defaultPageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: 1,\n fromPage: 1,\n startTime: 0,\n};\n\nexport const initialState: (coreState: CoreState, config: ScrollPluginConfig) => ScrollState = (\n _coreState,\n config,\n) => ({\n defaultStrategy: config.defaultStrategy ?? ScrollStrategy.Vertical,\n defaultPageGap: config.defaultPageGap ?? 10,\n defaultBufferSize: config.defaultBufferSize ?? 2,\n documents: {},\n});\n\nexport const scrollReducer: Reducer<ScrollState, ScrollAction> = (state, action) => {\n switch (action.type) {\n case INIT_SCROLL_STATE: {\n const { documentId, state: docState } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n };\n }\n\n case CLEANUP_SCROLL_STATE: {\n const { [action.payload]: removed, ...remaining } = state.documents;\n return {\n ...state,\n documents: remaining,\n };\n }\n\n case UPDATE_DOCUMENT_SCROLL_STATE: {\n const { documentId, state: updates } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n ...updates,\n },\n },\n };\n }\n\n case SET_SCROLL_STRATEGY: {\n const { documentId, strategy } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n strategy,\n },\n },\n };\n }\n\n default:\n return state;\n }\n};\n","import { ScrollerLayout, ScrollDocumentState } from './types';\n\nexport const getScrollerLayout = (\n documentState: ScrollDocumentState,\n scale: number,\n): ScrollerLayout => {\n return {\n startSpacing: documentState.startSpacing,\n endSpacing: documentState.endSpacing,\n totalWidth: documentState.totalContentSize.width * scale,\n totalHeight: documentState.totalContentSize.height * scale,\n pageGap: documentState.pageGap * scale,\n strategy: documentState.strategy,\n items: documentState.renderedPageIndexes.map((idx) => {\n return {\n ...documentState.virtualItems[idx],\n pageLayouts: documentState.virtualItems[idx].pageLayouts.map((layout) => {\n return {\n ...layout,\n rotatedWidth: layout.rotatedWidth * scale,\n rotatedHeight: layout.rotatedHeight * scale,\n width: layout.width * scale,\n height: layout.height * scale,\n };\n }),\n };\n }),\n };\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createBehaviorEmitter,\n DocumentState,\n Unsubscribe,\n Listener,\n SET_PAGES,\n} from '@embedpdf/core';\nimport { PdfPageObjectWithRotatedSize, Rect, Rotation, transformSize } from '@embedpdf/models';\nimport { ViewportCapability, ViewportMetrics, ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { SpreadCapability, SpreadPlugin } from '@embedpdf/plugin-spread';\n\nimport {\n ScrollCapability,\n ScrollScope,\n ScrollPluginConfig,\n ScrollStrategy,\n ScrollMetrics,\n ScrollState,\n ScrollDocumentState,\n LayoutChangePayload,\n ScrollerLayout,\n ScrollToPageOptions,\n PageChangeEvent,\n ScrollEvent,\n LayoutChangeEvent,\n PageChangeStateEvent,\n LayoutReadyEvent,\n ScrollBehavior,\n PageChangeState,\n} from './types';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './strategies/base-strategy';\nimport { VerticalScrollStrategy } from './strategies/vertical-strategy';\nimport { HorizontalScrollStrategy } from './strategies/horizontal-strategy';\nimport {\n ScrollAction,\n initScrollState,\n cleanupScrollState,\n updateDocumentScrollState,\n setScrollStrategy,\n} from './actions';\nimport { VirtualItem } from './types/virtual-item';\nimport { defaultPageChangeState } from './reducer';\nimport { getScrollerLayout } from './selectors';\n\nexport class ScrollPlugin extends BasePlugin<\n ScrollPluginConfig,\n ScrollCapability,\n ScrollState,\n ScrollAction\n> {\n static readonly id = 'scroll' as const;\n\n private viewport: ViewportCapability;\n private spread: SpreadCapability | null;\n\n // Strategies per document\n private strategies = new Map<string, BaseScrollStrategy>();\n\n // Layout ready tracking per document\n private layoutReady = new Set<string>();\n\n // Tracks documents that have had their initial layout ready (cleared only on document close)\n private initialLayoutFired = new Set<string>();\n\n // Per-document scroller layout emitters (for real-time scroll updates)\n private scrollerLayoutEmitters = new Map<\n string,\n ReturnType<typeof createBehaviorEmitter<ScrollerLayout>>\n >();\n\n // Event emitters (include documentId)\n private readonly pageChange$ = createBehaviorEmitter<PageChangeEvent>();\n private readonly scroll$ = createBehaviorEmitter<ScrollEvent>();\n private readonly layoutChange$ = createBehaviorEmitter<LayoutChangeEvent>();\n private readonly pageChangeState$ = createBehaviorEmitter<PageChangeStateEvent>();\n private readonly layoutReady$ = createBehaviorEmitter<LayoutReadyEvent>();\n private readonly state$ = createBehaviorEmitter<ScrollDocumentState>();\n\n constructor(\n public readonly id: string,\n registry: PluginRegistry,\n private config?: ScrollPluginConfig,\n ) {\n super(id, registry);\n\n this.viewport = this.registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.spread = this.registry.getPlugin<SpreadPlugin>('spread')?.provides() ?? null;\n\n // Subscribe to viewport scroll activity (per document)\n this.viewport.onScrollActivity((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (docState?.pageChangeState.isChanging && !event.activity.isSmoothScrolling) {\n this.completePageChange(event.documentId);\n }\n });\n\n this.spread?.onSpreadChange((event) => {\n this.refreshDocumentLayout(event.documentId);\n });\n\n // Subscribe to viewport changes (per document) with throttling\n this.viewport.onViewportChange((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (!docState) return;\n\n // Compute the metrics based on the incoming event\n const computedMetrics = this.computeMetrics(event.documentId, event.metrics);\n\n // THE GUARD: Only update the scrollOffset if the layout is already \"ready\".\n if (this.layoutReady.has(event.documentId)) {\n // Layout is ready, so this is a real scroll event from the user.\n // Commit all metrics, including the new scrollOffset.\n this.commitMetrics(event.documentId, computedMetrics);\n } else {\n // Layout is NOT ready. This is the initial, premature event.\n // We must commit the other metrics (like visible pages for rendering)\n // but EXCLUDE the incorrect scrollOffset to protect our persisted state.\n this.commitMetrics(event.documentId, {\n ...computedMetrics,\n scrollOffset: docState.scrollOffset,\n });\n }\n });\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle Hooks (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n protected override onDocumentLoadingStarted(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n // Initialize scroll state for this document\n const docState = this.createDocumentState(coreDoc);\n this.dispatch(initScrollState(documentId, docState));\n\n // Create strategy for this document\n const strategy = this.createStrategy(docState.strategy);\n this.strategies.set(documentId, strategy);\n\n // Create scroller layout emitter for this document\n this.scrollerLayoutEmitters.set(documentId, createBehaviorEmitter<ScrollerLayout>());\n }\n\n protected override onDocumentLoaded(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n\n this.dispatch(\n updateDocumentScrollState(documentId, { totalPages: coreDoc.document?.pageCount ?? 0 }),\n );\n // Initial layout computation\n this.refreshDocumentLayout(documentId);\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentOpened',\n `Initialized scroll state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup strategy\n this.strategies.delete(documentId);\n\n // Cleanup layout ready tracking\n this.layoutReady.delete(documentId);\n this.initialLayoutFired.delete(documentId);\n\n // Cleanup scroller layout emitter\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (emitter) {\n emitter.clear();\n this.scrollerLayoutEmitters.delete(documentId);\n }\n\n // Cleanup state\n this.dispatch(cleanupScrollState(documentId));\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentClosed',\n `Cleaned up scroll state for document: ${documentId}`,\n );\n }\n\n protected override onScaleChanged(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n if (!coreDoc || coreDoc.status !== 'loaded') return;\n\n const viewportScope = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewportScope.getMetrics());\n\n // Use the canonical path so scroll/pageChange events and scroller layout\n // updates all flow through the same place.\n this.commitMetrics(documentId, metrics);\n }\n\n protected override onRotationChanged(documentId: string): void {\n this.refreshDocumentLayout(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Public API for Components (Scroller Layout)\n // ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to scroller layout updates for a specific document\n * This is the key method for the Scroller component to stay reactive\n */\n public onScrollerData(\n documentId: string,\n callback: (layout: ScrollerLayout) => void,\n ): Unsubscribe {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) {\n throw new Error(`No scroller layout emitter found for document: ${documentId}`);\n }\n return emitter.on(callback);\n }\n\n /**\n * Get current scroller layout for a document\n */\n public getScrollerLayout(documentId: string): ScrollerLayout {\n const docState = this.getDocumentState(documentId);\n const coreDoc = this.getCoreDocumentOrThrow(documentId);\n\n if (!docState || !coreDoc) {\n throw new Error(`Cannot get scroller layout for document: ${documentId}`);\n }\n\n return getScrollerLayout(docState, coreDoc.scale);\n }\n\n public setLayoutReady(documentId: string): void {\n // This guard logic is now reliable because the flag gets reset correctly.\n if (this.layoutReady.has(documentId)) {\n return;\n }\n\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n this.layoutReady.add(documentId);\n\n // Determine if this is the initial layout for this document\n const isInitial = !this.initialLayoutFired.has(documentId);\n if (isInitial) {\n this.initialLayoutFired.add(documentId);\n }\n\n // Restore the persisted scroll position\n const viewport = this.viewport.forDocument(documentId);\n viewport.scrollTo({ ...docState.scrollOffset, behavior: 'instant' });\n\n this.layoutReady$.emit({ documentId, isInitial });\n }\n\n public clearLayoutReady(documentId: string): void {\n this.layoutReady.delete(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): ScrollCapability {\n return {\n // Active document operations\n getCurrentPage: () => this.getCurrentPage(),\n getTotalPages: () => this.getTotalPages(),\n getPageChangeState: () => this.getPageChangeState(),\n scrollToPage: (options) => this.scrollToPage(options),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior),\n getMetrics: (viewport) => this.getMetrics(viewport),\n getLayout: () => this.getLayout(),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation),\n\n // Document-scoped operations\n forDocument: (documentId) => this.createScrollScope(documentId),\n\n // Global settings\n setScrollStrategy: (strategy, documentId) =>\n this.setScrollStrategyForDocument(strategy, documentId),\n getPageGap: () => this.state.defaultPageGap,\n\n // Events\n onPageChange: this.pageChange$.on,\n onScroll: this.scroll$.on,\n onLayoutChange: this.layoutChange$.on,\n onLayoutReady: this.layoutReady$.on,\n onPageChangeState: this.pageChangeState$.on,\n onStateChange: this.state$.on,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createScrollScope(documentId: string): ScrollScope {\n return {\n getCurrentPage: () => this.getCurrentPage(documentId),\n getTotalPages: () => this.getTotalPages(documentId),\n getPageChangeState: () => this.getPageChangeState(documentId),\n scrollToPage: (options) => this.scrollToPage(options, documentId),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior, documentId),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior, documentId),\n getSpreadPagesWithRotatedSize: () => this.getSpreadPagesWithRotatedSize(documentId),\n getMetrics: (viewport) => this.getMetrics(viewport, documentId),\n getLayout: () => this.getLayout(documentId),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation, documentId),\n setScrollStrategy: (strategy) => this.setScrollStrategyForDocument(strategy, documentId),\n onPageChange: (listener: Listener<PageChangeEvent>) =>\n this.pageChange$.on((event) => {\n if (event.documentId === documentId) listener(event);\n }),\n onScroll: (listener: Listener<ScrollMetrics>) =>\n this.scroll$.on((event) => {\n if (event.documentId === documentId) listener(event.metrics);\n }),\n onLayoutChange: (listener: Listener<LayoutChangePayload>) =>\n this.layoutChange$.on((event) => {\n if (event.documentId === documentId) listener(event.layout);\n }),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // State Helpers\n // ─────────────────────────────────────────────────────────\n private getDocumentState(documentId?: string): ScrollDocumentState | null {\n const id = documentId ?? this.getActiveDocumentId();\n return this.state.documents[id] ?? null;\n }\n\n private getDocumentStateOrThrow(documentId?: string): ScrollDocumentState {\n const state = this.getDocumentState(documentId);\n if (!state) {\n throw new Error(`Scroll state not found for document: ${documentId ?? 'active'}`);\n }\n return state;\n }\n\n private getStrategy(documentId?: string): BaseScrollStrategy {\n const id = documentId ?? this.getActiveDocumentId();\n const strategy = this.strategies.get(id);\n if (!strategy) {\n throw new Error(`Strategy not found for document: ${id}`);\n }\n return strategy;\n }\n\n private createStrategy(strategyType: ScrollStrategy): BaseScrollStrategy {\n const config: ScrollStrategyConfig = {\n pageGap: this.state.defaultPageGap,\n viewportGap: this.viewport.getViewportGap(),\n bufferSize: this.state.defaultBufferSize,\n };\n\n return strategyType === ScrollStrategy.Horizontal\n ? new HorizontalScrollStrategy(config)\n : new VerticalScrollStrategy(config);\n }\n\n private createDocumentState(coreDoc: DocumentState): ScrollDocumentState {\n return {\n virtualItems: [],\n totalPages: coreDoc.document?.pageCount ?? 0,\n currentPage: 1,\n totalContentSize: { width: 0, height: 0 },\n strategy: this.state.defaultStrategy,\n pageGap: this.state.defaultPageGap,\n visiblePages: [],\n pageVisibilityMetrics: [],\n renderedPageIndexes: [],\n scrollOffset: { x: 0, y: 0 },\n startSpacing: 0,\n endSpacing: 0,\n pageChangeState: defaultPageChangeState,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Page Change Management\n // ─────────────────────────────────────────────────────────\n\n private startPageChange(\n documentId: string,\n targetPage: number,\n behavior: ScrollBehavior = 'smooth',\n ): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: true,\n targetPage,\n fromPage: docState.currentPage,\n startTime: Date.now(),\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n\n if (behavior === 'instant') {\n this.completePageChange(documentId);\n }\n }\n\n private completePageChange(documentId: string): void {\n const docState = this.getDocumentState(documentId);\n if (!docState || !docState.pageChangeState.isChanging) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: docState.pageChangeState.targetPage,\n fromPage: docState.pageChangeState.fromPage,\n startTime: docState.pageChangeState.startTime,\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n }\n\n // ─────────────────────────────────────────────────────────\n // Layout & Metrics Computation\n // ─────────────────────────────────────────────────────────\n\n private computeLayout(\n documentId: string,\n pages: PdfPageObjectWithRotatedSize[][],\n ): {\n virtualItems: VirtualItem[];\n totalContentSize: { width: number; height: number };\n } {\n const strategy = this.getStrategy(documentId);\n const virtualItems = strategy.createVirtualItems(pages);\n const totalContentSize = strategy.getTotalContentSize(virtualItems);\n return { virtualItems, totalContentSize };\n }\n\n private computeMetrics(\n documentId: string,\n vp: ViewportMetrics,\n items?: VirtualItem[],\n ): ScrollMetrics {\n const coreDocState = this.getCoreDocumentOrThrow(documentId);\n const docState = this.getDocumentState(documentId);\n const strategy = this.getStrategy(documentId);\n if (!docState) throw new Error(`Document state not found: ${documentId}`);\n\n return strategy.handleScroll(vp, items ?? docState.virtualItems, coreDocState.scale);\n }\n\n // ─────────────────────────────────────────────────────────\n // Commit (Single Source of Truth)\n // ─────────────────────────────────────────────────────────\n\n private commitMetrics(documentId: string, metrics: ScrollMetrics): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n // Update state\n this.dispatch(updateDocumentScrollState(documentId, metrics));\n\n // Emit scroll event\n this.scroll$.emit({ documentId, metrics });\n\n // Emit page change if current page changed\n if (metrics.currentPage !== docState.currentPage) {\n this.pageChange$.emit({\n documentId,\n pageNumber: metrics.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n // CRITICAL: Push updated scroller layout (for spacing/visible items reactivity)\n this.pushScrollerLayout(documentId);\n }\n\n private pushScrollerLayout(documentId: string): void {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) return;\n\n try {\n const layout = this.getScrollerLayout(documentId);\n emitter.emit(layout);\n } catch (error) {\n // Document might be closing, ignore\n }\n }\n\n private refreshDocumentLayout(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n const docState = this.getDocumentState(documentId);\n\n if (!coreDoc || !docState || coreDoc.status !== 'loaded') return;\n\n const pages = this.getSpreadPagesWithRotatedSize(documentId);\n const layout = this.computeLayout(documentId, pages);\n // Get viewport metrics for this document\n const viewport = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewport.getMetrics(), layout.virtualItems);\n\n // Update state with layout + metrics\n this.dispatch(\n updateDocumentScrollState(documentId, {\n ...layout,\n ...metrics,\n }),\n );\n // Emit layout change event\n this.layoutChange$.emit({ documentId, layout });\n\n // Push updated scroller layout\n this.pushScrollerLayout(documentId);\n }\n\n private getSpreadPagesWithRotatedSize(documentId?: string): PdfPageObjectWithRotatedSize[][] {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n if (!coreDoc) throw new Error(`Document ${id} not loaded`);\n\n const spreadPages =\n this.spread?.forDocument(id).getSpreadPages() ||\n coreDoc.document?.pages.map((page) => [page]) ||\n [];\n return spreadPages.map((spread) =>\n spread.map((page) => ({\n ...page,\n rotatedSize: transformSize(page.size, coreDoc.rotation, 1),\n })),\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private getCurrentPage(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).currentPage;\n }\n\n private getTotalPages(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).totalPages;\n }\n\n private getPageChangeState(documentId?: string): PageChangeState {\n return this.getDocumentStateOrThrow(documentId).pageChangeState;\n }\n\n private scrollToPage(options: ScrollToPageOptions, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const { pageNumber, behavior = 'smooth', pageCoordinates, center = false } = options;\n\n this.startPageChange(id, pageNumber, behavior);\n\n const position = strategy.getScrollPositionForPage(\n pageNumber,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n pageCoordinates,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior, center });\n } else {\n this.completePageChange(id);\n }\n }\n\n private scrollToNextPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex >= 0 && currentItemIndex < docState.virtualItems.length - 1) {\n const nextItem = docState.virtualItems[currentItemIndex + 1];\n const targetPage = nextItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private scrollToPreviousPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.coreState.core.documents[id];\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex > 0) {\n const prevItem = docState.virtualItems[currentItemIndex - 1];\n const targetPage = prevItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private getMetrics(viewport?: ViewportMetrics, documentId?: string): ScrollMetrics {\n const id = documentId ?? this.getActiveDocumentId();\n\n if (viewport) {\n return this.computeMetrics(id, viewport);\n }\n\n const viewportScope = this.viewport.forDocument(id);\n return this.computeMetrics(id, viewportScope.getMetrics());\n }\n\n private getLayout(documentId?: string): LayoutChangePayload {\n const docState = this.getDocumentStateOrThrow(documentId);\n return {\n virtualItems: docState.virtualItems,\n totalContentSize: docState.totalContentSize,\n };\n }\n\n private getRectPositionForPage(\n pageIndex: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n documentId?: string,\n ): Rect | null {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n return strategy.getRectPositionForPage(\n pageIndex + 1,\n docState.virtualItems,\n scale ?? coreDoc.scale,\n rotation ?? coreDoc.rotation,\n rect,\n );\n }\n\n private setScrollStrategyForDocument(newStrategy: ScrollStrategy, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n\n if (!docState || docState.strategy === newStrategy) return;\n\n // Create new strategy\n const strategy = this.createStrategy(newStrategy);\n this.strategies.set(id, strategy);\n\n // Update state\n this.dispatch(setScrollStrategy(id, newStrategy));\n\n // Recalculate layout\n this.refreshDocumentLayout(id);\n }\n\n // ─────────────────────────────────────────────────────────\n // Store Update Handlers\n // ─────────────────────────────────────────────────────────\n\n override onStoreUpdated(prevState: ScrollState, newState: ScrollState): void {\n // Emit state changes and push scroller layout for each changed document\n for (const documentId in newState.documents) {\n const prevDoc = prevState.documents[documentId];\n const newDoc = newState.documents[documentId];\n\n if (prevDoc !== newDoc) {\n this.state$.emit(newDoc);\n\n if (prevDoc?.pageChangeState !== newDoc.pageChangeState) {\n this.pageChangeState$.emit({\n documentId,\n state: newDoc.pageChangeState,\n });\n }\n\n // Push scroller layout on any state change\n this.pushScrollerLayout(documentId);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(): Promise<void> {\n this.logger.info('ScrollPlugin', 'Initialize', 'Scroll plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.strategies.clear();\n this.layoutReady.clear();\n this.initialLayoutFired.clear();\n\n // Clear all scroller layout emitters\n for (const emitter of this.scrollerLayoutEmitters.values()) {\n emitter.clear();\n }\n this.scrollerLayoutEmitters.clear();\n\n this.pageChange$.clear();\n this.scroll$.clear();\n this.layoutChange$.clear();\n this.pageChangeState$.clear();\n this.layoutReady$.clear();\n this.state$.clear();\n\n super.destroy();\n }\n}\n","import { PluginManifest } from '@embedpdf/core';\nimport { ScrollPluginConfig, ScrollStrategy } from './types';\n\nexport const SCROLL_PLUGIN_ID = 'scroll';\n\nexport const manifest: PluginManifest<ScrollPluginConfig> = {\n id: SCROLL_PLUGIN_ID,\n name: 'Scroll Plugin',\n version: '1.0.0',\n provides: ['scroll'],\n requires: ['viewport'],\n optional: ['spread'],\n defaultConfig: {\n enabled: true,\n defaultPageGap: 10,\n defaultBufferSize: 4,\n defaultStrategy: ScrollStrategy.Vertical,\n },\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { ScrollPlugin } from './scroll-plugin';\nimport { manifest, SCROLL_PLUGIN_ID } from './manifest';\nimport { ScrollPluginConfig, ScrollState } from './types';\nimport { scrollReducer, initialState } from './reducer';\nimport { ScrollAction } from './actions';\n\nexport const ScrollPluginPackage: PluginPackage<\n ScrollPlugin,\n ScrollPluginConfig,\n ScrollState,\n ScrollAction\n> = {\n manifest,\n create: (registry, config) => new ScrollPlugin(SCROLL_PLUGIN_ID, registry, config),\n reducer: scrollReducer,\n initialState: (coreState, config) => initialState(coreState, config),\n};\n\nexport * from './scroll-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './types/virtual-item';\nexport * from './selectors';\n"],"names":["ScrollStrategy","item"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/lib/types.ts","../src/lib/strategies/base-strategy.ts","../src/lib/strategies/vertical-strategy.ts","../src/lib/strategies/horizontal-strategy.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/selectors.ts","../src/lib/scroll-plugin.ts","../src/lib/manifest.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig, EventHook } from '@embedpdf/core';\nimport { PdfPageObject, PdfPageObjectWithRotatedSize, Rect, Rotation } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from './types/virtual-item';\n\nexport type ScrollBehavior = 'instant' | 'smooth' | 'auto';\n\nexport interface PageChangeState {\n isChanging: boolean;\n targetPage: number;\n fromPage: number;\n startTime: number;\n}\n\n// Per-document scroll state\nexport interface ScrollDocumentState {\n virtualItems: VirtualItem[];\n totalPages: number;\n currentPage: number;\n totalContentSize: { width: number; height: number };\n strategy: ScrollStrategy;\n pageGap: number;\n\n // Scroll metrics\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n pageChangeState: PageChangeState;\n}\n\n// Plugin state\nexport interface ScrollState {\n // Global defaults (applied to new documents)\n defaultStrategy: ScrollStrategy;\n defaultPageGap: number;\n defaultBufferSize: number;\n\n // Per-document states\n documents: Record<string, ScrollDocumentState>;\n}\n\nexport interface ScrollerLayout {\n startSpacing: number;\n endSpacing: number;\n totalWidth: number;\n totalHeight: number;\n pageGap: number;\n strategy: ScrollStrategy;\n items: VirtualItem[];\n}\n\nexport enum ScrollStrategy {\n Vertical = 'vertical',\n Horizontal = 'horizontal',\n}\n\nexport interface PageVisibilityMetrics {\n pageNumber: number;\n viewportX: number;\n viewportY: number;\n visiblePercentage: number;\n original: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n scaled: {\n pageX: number;\n pageY: number;\n visibleWidth: number;\n visibleHeight: number;\n scale: number;\n };\n}\n\nexport interface ScrollMetrics {\n currentPage: number;\n visiblePages: number[];\n pageVisibilityMetrics: PageVisibilityMetrics[];\n renderedPageIndexes: number[];\n scrollOffset: { x: number; y: number };\n startSpacing: number;\n endSpacing: number;\n}\n\nexport interface ScrollPluginConfig extends BasePluginConfig {\n defaultStrategy?: ScrollStrategy;\n defaultPageGap?: number;\n defaultBufferSize?: number;\n}\n\nexport type LayoutChangePayload = Pick<ScrollDocumentState, 'virtualItems' | 'totalContentSize'>;\n\nexport interface ScrollToPageOptions {\n pageNumber: number;\n pageCoordinates?: { x: number; y: number };\n behavior?: ScrollBehavior;\n /**\n * Horizontal alignment as a percentage (0-100).\n * 0 = target at left edge, 50 = centered, 100 = target at right edge.\n */\n alignX?: number;\n /**\n * Vertical alignment as a percentage (0-100).\n * 0 = target at top edge, 50 = centered, 100 = target at bottom edge.\n * Useful for mobile where UI overlays may cover part of the screen (e.g., alignY: 25 for top quarter).\n */\n alignY?: number;\n}\n\n// Events include documentId\nexport interface PageChangeEvent {\n documentId: string;\n pageNumber: number;\n totalPages: number;\n}\n\nexport interface ScrollEvent {\n documentId: string;\n metrics: ScrollMetrics;\n}\n\nexport interface LayoutChangeEvent {\n documentId: string;\n layout: LayoutChangePayload;\n}\n\nexport interface PageChangeStateEvent {\n documentId: string;\n state: PageChangeState;\n}\n\nexport interface LayoutReadyEvent {\n documentId: string;\n /** True only on the first layout ready after document load, false on subsequent (e.g., tab switches) */\n isInitial: boolean;\n pageNumber: number;\n totalPages: number;\n}\n\n// Scoped scroll capability\nexport interface ScrollScope {\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getSpreadPagesWithRotatedSize(): PdfPageObjectWithRotatedSize[][];\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n setScrollStrategy(strategy: ScrollStrategy): void;\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollMetrics>;\n onLayoutChange: EventHook<LayoutChangePayload>;\n}\n\nexport interface ScrollCapability {\n // Active document operations (defaults to active document)\n getCurrentPage(): number;\n getTotalPages(): number;\n getPageChangeState(): PageChangeState;\n scrollToPage(options: ScrollToPageOptions): void;\n scrollToNextPage(behavior?: ScrollBehavior): void;\n scrollToPreviousPage(behavior?: ScrollBehavior): void;\n getMetrics(viewport?: ViewportMetrics): ScrollMetrics;\n getLayout(): LayoutChangePayload;\n getRectPositionForPage(\n page: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n ): Rect | null;\n\n // Document-scoped operations\n forDocument(documentId: string): ScrollScope;\n\n // Global settings\n setScrollStrategy(strategy: ScrollStrategy, documentId?: string): void;\n getPageGap(): number;\n\n // Events (all include documentId)\n onPageChange: EventHook<PageChangeEvent>;\n onScroll: EventHook<ScrollEvent>;\n onLayoutChange: EventHook<LayoutChangeEvent>;\n onLayoutReady: EventHook<LayoutReadyEvent>;\n onPageChangeState: EventHook<PageChangeStateEvent>;\n onStateChange: EventHook<ScrollDocumentState>;\n}\n","import {\n PdfPageObjectWithRotatedSize,\n Position,\n Rect,\n Rotation,\n scalePosition,\n Size,\n transformPosition,\n transformRect,\n} from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { VirtualItem } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport interface ScrollStrategyConfig {\n pageGap?: number;\n viewportGap?: number;\n bufferSize?: number;\n}\n\nexport abstract class BaseScrollStrategy {\n protected pageGap: number;\n protected viewportGap: number;\n protected bufferSize: number;\n\n constructor(config: ScrollStrategyConfig) {\n this.pageGap = config.pageGap ?? 20;\n this.viewportGap = config.viewportGap ?? 20;\n this.bufferSize = config.bufferSize ?? 2;\n }\n\n abstract createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[];\n abstract getTotalContentSize(virtualItems: VirtualItem[]): Size;\n protected abstract getScrollOffset(viewport: ViewportMetrics): number;\n protected abstract getClientSize(viewport: ViewportMetrics): number;\n\n protected getVisibleRange(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): { start: number; end: number } {\n const scrollOffset = this.getScrollOffset(viewport);\n const clientSize = this.getClientSize(viewport);\n const viewportStart = scrollOffset;\n const viewportEnd = scrollOffset + clientSize;\n\n let startIndex = 0;\n while (\n startIndex < virtualItems.length &&\n (virtualItems[startIndex].offset + virtualItems[startIndex].height) * scale <= viewportStart\n ) {\n startIndex++;\n }\n\n let endIndex = startIndex;\n while (endIndex < virtualItems.length && virtualItems[endIndex].offset * scale <= viewportEnd) {\n endIndex++;\n }\n\n return {\n start: Math.max(0, startIndex - this.bufferSize),\n end: Math.min(virtualItems.length - 1, endIndex + this.bufferSize - 1),\n };\n }\n\n handleScroll(\n viewport: ViewportMetrics,\n virtualItems: VirtualItem[],\n scale: number,\n ): ScrollMetrics {\n const range = this.getVisibleRange(viewport, virtualItems, scale);\n const visibleItems = virtualItems.slice(range.start, range.end + 1);\n const pageVisibilityMetrics = this.calculatePageVisibility(visibleItems, viewport, scale);\n const visiblePages = pageVisibilityMetrics.map((m) => m.pageNumber);\n const renderedPageIndexes = virtualItems\n .slice(range.start, range.end + 1)\n .flatMap((item) => item.index);\n const currentPage = this.determineCurrentPage(pageVisibilityMetrics);\n const first = virtualItems[range.start];\n const last = virtualItems[range.end];\n const startSpacing = first ? first.offset * scale : 0;\n const endSpacing = last\n ? (virtualItems[virtualItems.length - 1].offset + // end of content\n virtualItems[virtualItems.length - 1].height) *\n scale - // minus\n (last.offset + last.height) * scale // end of last rendered\n : 0;\n\n return {\n currentPage,\n visiblePages,\n pageVisibilityMetrics,\n renderedPageIndexes,\n scrollOffset: { x: viewport.scrollLeft, y: viewport.scrollTop },\n startSpacing,\n endSpacing,\n };\n }\n\n protected calculatePageVisibility(\n virtualItems: VirtualItem[],\n viewport: ViewportMetrics,\n scale: number,\n ): ScrollMetrics['pageVisibilityMetrics'] {\n const visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'] = [];\n\n virtualItems.forEach((item) => {\n item.pageLayouts.forEach((page) => {\n const itemX = item.x * scale;\n const itemY = item.y * scale;\n const pageX = itemX + page.x * scale;\n const pageY = itemY + page.y * scale;\n const pageWidth = page.rotatedWidth * scale;\n const pageHeight = page.rotatedHeight * scale;\n\n const viewportLeft = viewport.scrollLeft;\n const viewportTop = viewport.scrollTop;\n const viewportRight = viewportLeft + viewport.clientWidth;\n const viewportBottom = viewportTop + viewport.clientHeight;\n\n const intersectionLeft = Math.max(pageX, viewportLeft);\n const intersectionTop = Math.max(pageY, viewportTop);\n const intersectionRight = Math.min(pageX + pageWidth, viewportRight);\n const intersectionBottom = Math.min(pageY + pageHeight, viewportBottom);\n\n if (intersectionLeft < intersectionRight && intersectionTop < intersectionBottom) {\n const visibleWidth = intersectionRight - intersectionLeft;\n const visibleHeight = intersectionBottom - intersectionTop;\n const totalArea = pageWidth * pageHeight;\n const visibleArea = visibleWidth * visibleHeight;\n\n visibilityMetrics.push({\n pageNumber: page.pageNumber,\n viewportX: intersectionLeft - viewportLeft,\n viewportY: intersectionTop - viewportTop,\n visiblePercentage: (visibleArea / totalArea) * 100,\n original: {\n pageX: (intersectionLeft - pageX) / scale,\n pageY: (intersectionTop - pageY) / scale,\n visibleWidth: visibleWidth / scale,\n visibleHeight: visibleHeight / scale,\n scale: 1,\n },\n scaled: {\n pageX: intersectionLeft - pageX,\n pageY: intersectionTop - pageY,\n visibleWidth,\n visibleHeight,\n scale,\n },\n });\n }\n });\n });\n\n return visibilityMetrics;\n }\n\n protected determineCurrentPage(\n visibilityMetrics: ScrollMetrics['pageVisibilityMetrics'],\n ): number {\n if (visibilityMetrics.length === 0) return 1;\n\n const maxVisibility = Math.max(...visibilityMetrics.map((m) => m.visiblePercentage));\n const mostVisiblePages = visibilityMetrics.filter((m) => m.visiblePercentage === maxVisibility);\n\n return mostVisiblePages.length === 1\n ? mostVisiblePages[0].pageNumber\n : mostVisiblePages.sort((a, b) => a.pageNumber - b.pageNumber)[0].pageNumber;\n }\n\n private getRectLocationForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n totalContentSize?: Size,\n ): Rect | null {\n // Find the virtual item containing the page\n const item = virtualItems.find((item) => item.pageNumbers.includes(pageNumber));\n if (!item) return null;\n\n // Find the specific page layout for the requested page number\n const pageLayout = item.pageLayouts.find((layout) => layout.pageNumber === pageNumber);\n if (!pageLayout) return null;\n\n // Calculate centering offset for items that are narrower than the maximum width\n let centeringOffsetX = 0;\n if (totalContentSize) {\n const maxWidth = totalContentSize.width;\n if (item.width < maxWidth) {\n centeringOffsetX = (maxWidth - item.width) / 2;\n }\n }\n\n return {\n origin: {\n x: item.x + pageLayout.x + centeringOffsetX,\n y: item.y + pageLayout.y,\n },\n size: {\n width: pageLayout.width,\n height: pageLayout.height,\n },\n };\n }\n\n getScrollPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n pageCoordinates?: { x: number; y: number },\n ): Position | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n // If specific page coordinates are provided, add them to the base position\n if (pageCoordinates) {\n const rotatedSize = transformPosition(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n {\n x: pageCoordinates.x,\n y: pageCoordinates.y,\n },\n rotation,\n scale,\n );\n\n return {\n x: scaledBasePosition.x + rotatedSize.x + this.viewportGap,\n y: scaledBasePosition.y + rotatedSize.y + this.viewportGap,\n };\n }\n\n return {\n x: scaledBasePosition.x + this.viewportGap,\n y: scaledBasePosition.y + this.viewportGap,\n };\n }\n\n getRectPositionForPage(\n pageNumber: number,\n virtualItems: VirtualItem[],\n scale: number,\n rotation: Rotation,\n rect: Rect,\n ): Rect | null {\n const totalContentSize = this.getTotalContentSize(virtualItems);\n const pageRect = this.getRectLocationForPage(pageNumber, virtualItems, totalContentSize);\n if (!pageRect) return null;\n\n const scaledBasePosition = scalePosition(pageRect.origin, scale);\n\n const rotatedSize = transformRect(\n {\n width: pageRect.size.width,\n height: pageRect.size.height,\n },\n rect,\n rotation,\n scale,\n );\n\n return {\n origin: {\n x: scaledBasePosition.x + rotatedSize.origin.x,\n y: scaledBasePosition.y + rotatedSize.origin.y,\n },\n size: rotatedSize.size,\n };\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\nimport { ScrollMetrics } from '../types';\n\nexport class VerticalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let yOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: 0,\n y: yOffset,\n offset: yOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n yOffset += height + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const maxWidth = Math.max(...virtualItems.map((item) => item.width));\n const totalHeight =\n virtualItems[virtualItems.length - 1].y + virtualItems[virtualItems.length - 1].height;\n return {\n width: maxWidth,\n height: totalHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollTop;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientHeight;\n }\n}\n","import { PdfPageObjectWithRotatedSize } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './base-strategy';\nimport { VirtualItem, PageLayout } from '../types/virtual-item';\n\nexport class HorizontalScrollStrategy extends BaseScrollStrategy {\n constructor(config: ScrollStrategyConfig) {\n super(config);\n }\n\n createVirtualItems(pdfPageObject: PdfPageObjectWithRotatedSize[][]): VirtualItem[] {\n let xOffset = 0;\n return pdfPageObject.map((pagesInSpread, index) => {\n let pageX = 0;\n const pageLayouts: PageLayout[] = pagesInSpread.map((page) => {\n const layout: PageLayout = {\n pageNumber: page.index + 1,\n pageIndex: page.index,\n x: pageX,\n y: 0,\n width: page.size.width,\n height: page.size.height,\n rotatedWidth: page.rotatedSize.width,\n rotatedHeight: page.rotatedSize.height,\n };\n pageX += page.rotatedSize.width + this.pageGap;\n return layout;\n });\n const width = pagesInSpread.reduce(\n (sum, page, i) =>\n sum + page.rotatedSize.width + (i < pagesInSpread.length - 1 ? this.pageGap : 0),\n 0,\n );\n const height = Math.max(...pagesInSpread.map((p) => p.rotatedSize.height));\n const item: VirtualItem = {\n id: `item-${index}`,\n x: xOffset,\n y: 0,\n offset: xOffset,\n width,\n height,\n pageLayouts,\n pageNumbers: pagesInSpread.map((p) => p.index + 1),\n index,\n };\n xOffset += width + this.pageGap;\n return item;\n });\n }\n\n getTotalContentSize(virtualItems: VirtualItem[]): { width: number; height: number } {\n if (virtualItems.length === 0) return { width: 0, height: 0 };\n const totalWidth =\n virtualItems[virtualItems.length - 1].x + virtualItems[virtualItems.length - 1].width;\n const maxHeight = Math.max(...virtualItems.map((item) => item.height));\n return {\n width: totalWidth,\n height: maxHeight,\n };\n }\n\n protected getScrollOffset(viewport: ViewportMetrics): number {\n return viewport.scrollLeft;\n }\n\n protected getClientSize(viewport: ViewportMetrics): number {\n return viewport.clientWidth;\n }\n}\n","import { Action } from '@embedpdf/core';\nimport { ScrollDocumentState, ScrollStrategy } from './types';\n\n// Document lifecycle\nexport const INIT_SCROLL_STATE = 'INIT_SCROLL_STATE';\nexport const CLEANUP_SCROLL_STATE = 'CLEANUP_SCROLL_STATE';\nexport const UPDATE_DOCUMENT_SCROLL_STATE = 'UPDATE_DOCUMENT_SCROLL_STATE';\nexport const SET_SCROLL_STRATEGY = 'SET_SCROLL_STRATEGY';\n\nexport interface InitScrollStateAction extends Action {\n type: typeof INIT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: ScrollDocumentState;\n };\n}\n\nexport interface CleanupScrollStateAction extends Action {\n type: typeof CLEANUP_SCROLL_STATE;\n payload: string; // documentId\n}\n\nexport interface UpdateDocumentScrollStateAction extends Action {\n type: typeof UPDATE_DOCUMENT_SCROLL_STATE;\n payload: {\n documentId: string;\n state: Partial<ScrollDocumentState>;\n };\n}\n\nexport interface SetScrollStrategyAction extends Action {\n type: typeof SET_SCROLL_STRATEGY;\n payload: {\n documentId: string;\n strategy: ScrollStrategy;\n };\n}\n\nexport type ScrollAction =\n | InitScrollStateAction\n | CleanupScrollStateAction\n | UpdateDocumentScrollStateAction\n | SetScrollStrategyAction;\n\nexport function initScrollState(\n documentId: string,\n state: ScrollDocumentState,\n): InitScrollStateAction {\n return { type: INIT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function cleanupScrollState(documentId: string): CleanupScrollStateAction {\n return { type: CLEANUP_SCROLL_STATE, payload: documentId };\n}\n\nexport function updateDocumentScrollState(\n documentId: string,\n state: Partial<ScrollDocumentState>,\n): UpdateDocumentScrollStateAction {\n return { type: UPDATE_DOCUMENT_SCROLL_STATE, payload: { documentId, state } };\n}\n\nexport function setScrollStrategy(\n documentId: string,\n strategy: ScrollStrategy,\n): SetScrollStrategyAction {\n return { type: SET_SCROLL_STRATEGY, payload: { documentId, strategy } };\n}\n","import { Reducer, CoreState } from '@embedpdf/core';\nimport { ScrollState, ScrollStrategy, ScrollPluginConfig, PageChangeState } from './types';\nimport {\n ScrollAction,\n INIT_SCROLL_STATE,\n CLEANUP_SCROLL_STATE,\n UPDATE_DOCUMENT_SCROLL_STATE,\n SET_SCROLL_STRATEGY,\n} from './actions';\n\nexport const defaultPageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: 1,\n fromPage: 1,\n startTime: 0,\n};\n\nexport const initialState: (coreState: CoreState, config: ScrollPluginConfig) => ScrollState = (\n _coreState,\n config,\n) => ({\n defaultStrategy: config.defaultStrategy ?? ScrollStrategy.Vertical,\n defaultPageGap: config.defaultPageGap ?? 10,\n defaultBufferSize: config.defaultBufferSize ?? 2,\n documents: {},\n});\n\nexport const scrollReducer: Reducer<ScrollState, ScrollAction> = (state, action) => {\n switch (action.type) {\n case INIT_SCROLL_STATE: {\n const { documentId, state: docState } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n };\n }\n\n case CLEANUP_SCROLL_STATE: {\n const { [action.payload]: removed, ...remaining } = state.documents;\n return {\n ...state,\n documents: remaining,\n };\n }\n\n case UPDATE_DOCUMENT_SCROLL_STATE: {\n const { documentId, state: updates } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n ...updates,\n },\n },\n };\n }\n\n case SET_SCROLL_STRATEGY: {\n const { documentId, strategy } = action.payload;\n const docState = state.documents[documentId];\n if (!docState) return state;\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: {\n ...docState,\n strategy,\n },\n },\n };\n }\n\n default:\n return state;\n }\n};\n","import { ScrollerLayout, ScrollDocumentState } from './types';\n\nexport const getScrollerLayout = (\n documentState: ScrollDocumentState,\n scale: number,\n): ScrollerLayout => {\n return {\n startSpacing: documentState.startSpacing,\n endSpacing: documentState.endSpacing,\n totalWidth: documentState.totalContentSize.width * scale,\n totalHeight: documentState.totalContentSize.height * scale,\n pageGap: documentState.pageGap * scale,\n strategy: documentState.strategy,\n items: documentState.renderedPageIndexes.map((idx) => {\n return {\n ...documentState.virtualItems[idx],\n pageLayouts: documentState.virtualItems[idx].pageLayouts.map((layout) => {\n return {\n ...layout,\n rotatedWidth: layout.rotatedWidth * scale,\n rotatedHeight: layout.rotatedHeight * scale,\n width: layout.width * scale,\n height: layout.height * scale,\n };\n }),\n };\n }),\n };\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createBehaviorEmitter,\n DocumentState,\n Unsubscribe,\n Listener,\n SET_PAGES,\n} from '@embedpdf/core';\nimport { PdfPageObjectWithRotatedSize, Rect, Rotation, transformSize } from '@embedpdf/models';\nimport { ViewportCapability, ViewportMetrics, ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { SpreadCapability, SpreadPlugin } from '@embedpdf/plugin-spread';\n\nimport {\n ScrollCapability,\n ScrollScope,\n ScrollPluginConfig,\n ScrollStrategy,\n ScrollMetrics,\n ScrollState,\n ScrollDocumentState,\n LayoutChangePayload,\n ScrollerLayout,\n ScrollToPageOptions,\n PageChangeEvent,\n ScrollEvent,\n LayoutChangeEvent,\n PageChangeStateEvent,\n LayoutReadyEvent,\n ScrollBehavior,\n PageChangeState,\n} from './types';\nimport { BaseScrollStrategy, ScrollStrategyConfig } from './strategies/base-strategy';\nimport { VerticalScrollStrategy } from './strategies/vertical-strategy';\nimport { HorizontalScrollStrategy } from './strategies/horizontal-strategy';\nimport {\n ScrollAction,\n initScrollState,\n cleanupScrollState,\n updateDocumentScrollState,\n setScrollStrategy,\n} from './actions';\nimport { VirtualItem } from './types/virtual-item';\nimport { defaultPageChangeState } from './reducer';\nimport { getScrollerLayout } from './selectors';\n\nexport class ScrollPlugin extends BasePlugin<\n ScrollPluginConfig,\n ScrollCapability,\n ScrollState,\n ScrollAction\n> {\n static readonly id = 'scroll' as const;\n\n private viewport: ViewportCapability;\n private spread: SpreadCapability | null;\n\n // Strategies per document\n private strategies = new Map<string, BaseScrollStrategy>();\n\n // Layout ready tracking per document\n private layoutReady = new Set<string>();\n\n // Tracks documents that have had their initial layout ready (cleared only on document close)\n private initialLayoutFired = new Set<string>();\n\n // Per-document scroller layout emitters (for real-time scroll updates)\n private scrollerLayoutEmitters = new Map<\n string,\n ReturnType<typeof createBehaviorEmitter<ScrollerLayout>>\n >();\n\n // Event emitters (include documentId)\n private readonly pageChange$ = createBehaviorEmitter<PageChangeEvent>();\n private readonly scroll$ = createBehaviorEmitter<ScrollEvent>();\n private readonly layoutChange$ = createBehaviorEmitter<LayoutChangeEvent>();\n private readonly pageChangeState$ = createBehaviorEmitter<PageChangeStateEvent>();\n private readonly layoutReady$ = createBehaviorEmitter<LayoutReadyEvent>();\n private readonly state$ = createBehaviorEmitter<ScrollDocumentState>();\n\n constructor(\n public readonly id: string,\n registry: PluginRegistry,\n private config?: ScrollPluginConfig,\n ) {\n super(id, registry);\n\n this.viewport = this.registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.spread = this.registry.getPlugin<SpreadPlugin>('spread')?.provides() ?? null;\n\n // Subscribe to viewport scroll activity (per document)\n this.viewport.onScrollActivity((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (docState?.pageChangeState.isChanging && !event.activity.isSmoothScrolling) {\n this.completePageChange(event.documentId);\n }\n });\n\n this.spread?.onSpreadChange((event) => {\n this.refreshDocumentLayout(event.documentId);\n });\n\n // Subscribe to viewport changes (per document) with throttling\n this.viewport.onViewportChange((event) => {\n const docState = this.getDocumentState(event.documentId);\n if (!docState) return;\n\n // Compute the metrics based on the incoming event\n const computedMetrics = this.computeMetrics(event.documentId, event.metrics);\n\n // THE GUARD: Only update the scrollOffset if the layout is already \"ready\".\n if (this.layoutReady.has(event.documentId)) {\n // Layout is ready, so this is a real scroll event from the user.\n // Commit all metrics, including the new scrollOffset.\n this.commitMetrics(event.documentId, computedMetrics);\n } else {\n // Layout is NOT ready. This is the initial, premature event.\n // We must commit the other metrics (like visible pages for rendering)\n // but EXCLUDE the incorrect scrollOffset to protect our persisted state.\n this.commitMetrics(event.documentId, {\n ...computedMetrics,\n scrollOffset: docState.scrollOffset,\n });\n }\n });\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle Hooks (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n protected override onDocumentLoadingStarted(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n // Initialize scroll state for this document\n const docState = this.createDocumentState(coreDoc);\n this.dispatch(initScrollState(documentId, docState));\n\n // Create strategy for this document\n const strategy = this.createStrategy(docState.strategy);\n this.strategies.set(documentId, strategy);\n\n // Create scroller layout emitter for this document\n this.scrollerLayoutEmitters.set(documentId, createBehaviorEmitter<ScrollerLayout>());\n }\n\n protected override onDocumentLoaded(documentId: string): void {\n const coreDoc = this.getCoreDocument(documentId);\n if (!coreDoc) return;\n\n this.dispatch(\n updateDocumentScrollState(documentId, { totalPages: coreDoc.document?.pageCount ?? 0 }),\n );\n // Initial layout computation\n this.refreshDocumentLayout(documentId);\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentOpened',\n `Initialized scroll state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup strategy\n this.strategies.delete(documentId);\n\n // Cleanup layout ready tracking\n this.layoutReady.delete(documentId);\n this.initialLayoutFired.delete(documentId);\n\n // Cleanup scroller layout emitter\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (emitter) {\n emitter.clear();\n this.scrollerLayoutEmitters.delete(documentId);\n }\n\n // Cleanup state\n this.dispatch(cleanupScrollState(documentId));\n\n this.logger.debug(\n 'ScrollPlugin',\n 'DocumentClosed',\n `Cleaned up scroll state for document: ${documentId}`,\n );\n }\n\n protected override onScaleChanged(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n if (!coreDoc || coreDoc.status !== 'loaded') return;\n\n const viewportScope = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewportScope.getMetrics());\n\n // Use the canonical path so scroll/pageChange events and scroller layout\n // updates all flow through the same place.\n this.commitMetrics(documentId, metrics);\n }\n\n protected override onRotationChanged(documentId: string): void {\n this.refreshDocumentLayout(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Public API for Components (Scroller Layout)\n // ─────────────────────────────────────────────────────────\n\n /**\n * Subscribe to scroller layout updates for a specific document\n * This is the key method for the Scroller component to stay reactive\n */\n public onScrollerData(\n documentId: string,\n callback: (layout: ScrollerLayout) => void,\n ): Unsubscribe {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) {\n throw new Error(`No scroller layout emitter found for document: ${documentId}`);\n }\n return emitter.on(callback);\n }\n\n /**\n * Get current scroller layout for a document\n */\n public getScrollerLayout(documentId: string): ScrollerLayout {\n const docState = this.getDocumentState(documentId);\n const coreDoc = this.getCoreDocumentOrThrow(documentId);\n\n if (!docState || !coreDoc) {\n throw new Error(`Cannot get scroller layout for document: ${documentId}`);\n }\n\n return getScrollerLayout(docState, coreDoc.scale);\n }\n\n public setLayoutReady(documentId: string): void {\n // This guard logic is now reliable because the flag gets reset correctly.\n if (this.layoutReady.has(documentId)) {\n return;\n }\n\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n this.layoutReady.add(documentId);\n\n // Determine if this is the initial layout for this document\n const isInitial = !this.initialLayoutFired.has(documentId);\n if (isInitial) {\n this.initialLayoutFired.add(documentId);\n }\n\n // Restore the persisted scroll position\n const viewport = this.viewport.forDocument(documentId);\n viewport.scrollTo({ ...docState.scrollOffset, behavior: 'instant' });\n\n this.layoutReady$.emit({\n documentId,\n isInitial,\n pageNumber: docState.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n public clearLayoutReady(documentId: string): void {\n this.layoutReady.delete(documentId);\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): ScrollCapability {\n return {\n // Active document operations\n getCurrentPage: () => this.getCurrentPage(),\n getTotalPages: () => this.getTotalPages(),\n getPageChangeState: () => this.getPageChangeState(),\n scrollToPage: (options) => this.scrollToPage(options),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior),\n getMetrics: (viewport) => this.getMetrics(viewport),\n getLayout: () => this.getLayout(),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation),\n\n // Document-scoped operations\n forDocument: (documentId) => this.createScrollScope(documentId),\n\n // Global settings\n setScrollStrategy: (strategy, documentId) =>\n this.setScrollStrategyForDocument(strategy, documentId),\n getPageGap: () => this.state.defaultPageGap,\n\n // Events\n onPageChange: this.pageChange$.on,\n onScroll: this.scroll$.on,\n onLayoutChange: this.layoutChange$.on,\n onLayoutReady: this.layoutReady$.on,\n onPageChangeState: this.pageChangeState$.on,\n onStateChange: this.state$.on,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createScrollScope(documentId: string): ScrollScope {\n return {\n getCurrentPage: () => this.getCurrentPage(documentId),\n getTotalPages: () => this.getTotalPages(documentId),\n getPageChangeState: () => this.getPageChangeState(documentId),\n scrollToPage: (options) => this.scrollToPage(options, documentId),\n scrollToNextPage: (behavior) => this.scrollToNextPage(behavior, documentId),\n scrollToPreviousPage: (behavior) => this.scrollToPreviousPage(behavior, documentId),\n getSpreadPagesWithRotatedSize: () => this.getSpreadPagesWithRotatedSize(documentId),\n getMetrics: (viewport) => this.getMetrics(viewport, documentId),\n getLayout: () => this.getLayout(documentId),\n getRectPositionForPage: (page, rect, scale, rotation) =>\n this.getRectPositionForPage(page, rect, scale, rotation, documentId),\n setScrollStrategy: (strategy) => this.setScrollStrategyForDocument(strategy, documentId),\n onPageChange: (listener: Listener<PageChangeEvent>) =>\n this.pageChange$.on((event) => {\n if (event.documentId === documentId) listener(event);\n }),\n onScroll: (listener: Listener<ScrollMetrics>) =>\n this.scroll$.on((event) => {\n if (event.documentId === documentId) listener(event.metrics);\n }),\n onLayoutChange: (listener: Listener<LayoutChangePayload>) =>\n this.layoutChange$.on((event) => {\n if (event.documentId === documentId) listener(event.layout);\n }),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // State Helpers\n // ─────────────────────────────────────────────────────────\n private getDocumentState(documentId?: string): ScrollDocumentState | null {\n const id = documentId ?? this.getActiveDocumentId();\n return this.state.documents[id] ?? null;\n }\n\n private getDocumentStateOrThrow(documentId?: string): ScrollDocumentState {\n const state = this.getDocumentState(documentId);\n if (!state) {\n throw new Error(`Scroll state not found for document: ${documentId ?? 'active'}`);\n }\n return state;\n }\n\n private getStrategy(documentId?: string): BaseScrollStrategy {\n const id = documentId ?? this.getActiveDocumentId();\n const strategy = this.strategies.get(id);\n if (!strategy) {\n throw new Error(`Strategy not found for document: ${id}`);\n }\n return strategy;\n }\n\n private createStrategy(strategyType: ScrollStrategy): BaseScrollStrategy {\n const config: ScrollStrategyConfig = {\n pageGap: this.state.defaultPageGap,\n viewportGap: this.viewport.getViewportGap(),\n bufferSize: this.state.defaultBufferSize,\n };\n\n return strategyType === ScrollStrategy.Horizontal\n ? new HorizontalScrollStrategy(config)\n : new VerticalScrollStrategy(config);\n }\n\n private createDocumentState(coreDoc: DocumentState): ScrollDocumentState {\n return {\n virtualItems: [],\n totalPages: coreDoc.document?.pageCount ?? 0,\n currentPage: 1,\n totalContentSize: { width: 0, height: 0 },\n strategy: this.state.defaultStrategy,\n pageGap: this.state.defaultPageGap,\n visiblePages: [],\n pageVisibilityMetrics: [],\n renderedPageIndexes: [],\n scrollOffset: { x: 0, y: 0 },\n startSpacing: 0,\n endSpacing: 0,\n pageChangeState: defaultPageChangeState,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Page Change Management\n // ─────────────────────────────────────────────────────────\n\n private startPageChange(\n documentId: string,\n targetPage: number,\n behavior: ScrollBehavior = 'smooth',\n ): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: true,\n targetPage,\n fromPage: docState.currentPage,\n startTime: Date.now(),\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n\n if (behavior === 'instant') {\n this.completePageChange(documentId);\n }\n }\n\n private completePageChange(documentId: string): void {\n const docState = this.getDocumentState(documentId);\n if (!docState || !docState.pageChangeState.isChanging) return;\n\n const pageChangeState: PageChangeState = {\n isChanging: false,\n targetPage: docState.pageChangeState.targetPage,\n fromPage: docState.pageChangeState.fromPage,\n startTime: docState.pageChangeState.startTime,\n };\n\n this.dispatch(updateDocumentScrollState(documentId, { pageChangeState }));\n }\n\n // ─────────────────────────────────────────────────────────\n // Layout & Metrics Computation\n // ─────────────────────────────────────────────────────────\n\n private computeLayout(\n documentId: string,\n pages: PdfPageObjectWithRotatedSize[][],\n ): {\n virtualItems: VirtualItem[];\n totalContentSize: { width: number; height: number };\n } {\n const strategy = this.getStrategy(documentId);\n const virtualItems = strategy.createVirtualItems(pages);\n const totalContentSize = strategy.getTotalContentSize(virtualItems);\n return { virtualItems, totalContentSize };\n }\n\n private computeMetrics(\n documentId: string,\n vp: ViewportMetrics,\n items?: VirtualItem[],\n ): ScrollMetrics {\n const coreDocState = this.getCoreDocumentOrThrow(documentId);\n const docState = this.getDocumentState(documentId);\n const strategy = this.getStrategy(documentId);\n if (!docState) throw new Error(`Document state not found: ${documentId}`);\n\n return strategy.handleScroll(vp, items ?? docState.virtualItems, coreDocState.scale);\n }\n\n // ─────────────────────────────────────────────────────────\n // Commit (Single Source of Truth)\n // ─────────────────────────────────────────────────────────\n\n private commitMetrics(documentId: string, metrics: ScrollMetrics): void {\n const docState = this.getDocumentState(documentId);\n if (!docState) return;\n\n // Update state\n this.dispatch(updateDocumentScrollState(documentId, metrics));\n\n // Emit scroll event\n this.scroll$.emit({ documentId, metrics });\n\n // Emit page change if current page changed\n if (metrics.currentPage !== docState.currentPage) {\n this.pageChange$.emit({\n documentId,\n pageNumber: metrics.currentPage,\n totalPages: docState.totalPages,\n });\n }\n\n // CRITICAL: Push updated scroller layout (for spacing/visible items reactivity)\n this.pushScrollerLayout(documentId);\n }\n\n private pushScrollerLayout(documentId: string): void {\n const emitter = this.scrollerLayoutEmitters.get(documentId);\n if (!emitter) return;\n\n try {\n const layout = this.getScrollerLayout(documentId);\n emitter.emit(layout);\n } catch (error) {\n // Document might be closing, ignore\n }\n }\n\n private refreshDocumentLayout(documentId: string): void {\n const coreDoc = this.coreState.core.documents[documentId];\n const docState = this.getDocumentState(documentId);\n\n if (!coreDoc || !docState || coreDoc.status !== 'loaded') return;\n\n const pages = this.getSpreadPagesWithRotatedSize(documentId);\n const layout = this.computeLayout(documentId, pages);\n // Get viewport metrics for this document\n const viewport = this.viewport.forDocument(documentId);\n const metrics = this.computeMetrics(documentId, viewport.getMetrics(), layout.virtualItems);\n\n // Update state with layout + metrics\n this.dispatch(\n updateDocumentScrollState(documentId, {\n ...layout,\n ...metrics,\n }),\n );\n // Emit layout change event\n this.layoutChange$.emit({ documentId, layout });\n\n // Push updated scroller layout\n this.pushScrollerLayout(documentId);\n }\n\n private getSpreadPagesWithRotatedSize(documentId?: string): PdfPageObjectWithRotatedSize[][] {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n if (!coreDoc) throw new Error(`Document ${id} not loaded`);\n\n const spreadPages =\n this.spread?.forDocument(id).getSpreadPages() ||\n coreDoc.document?.pages.map((page) => [page]) ||\n [];\n return spreadPages.map((spread) =>\n spread.map((page) => ({\n ...page,\n rotatedSize: transformSize(page.size, coreDoc.rotation, 1),\n })),\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private getCurrentPage(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).currentPage;\n }\n\n private getTotalPages(documentId?: string): number {\n return this.getDocumentStateOrThrow(documentId).totalPages;\n }\n\n private getPageChangeState(documentId?: string): PageChangeState {\n return this.getDocumentStateOrThrow(documentId).pageChangeState;\n }\n\n private scrollToPage(options: ScrollToPageOptions, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const { pageNumber, behavior = 'smooth', pageCoordinates, alignX, alignY } = options;\n\n this.startPageChange(id, pageNumber, behavior);\n\n const position = strategy.getScrollPositionForPage(\n pageNumber,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n pageCoordinates,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior, alignX, alignY });\n } else {\n this.completePageChange(id);\n }\n }\n\n private scrollToNextPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex >= 0 && currentItemIndex < docState.virtualItems.length - 1) {\n const nextItem = docState.virtualItems[currentItemIndex + 1];\n const targetPage = nextItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private scrollToPreviousPage(behavior: ScrollBehavior = 'smooth', documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.coreState.core.documents[id];\n\n const currentItemIndex = docState.virtualItems.findIndex((item) =>\n item.pageNumbers.includes(docState.currentPage),\n );\n\n if (currentItemIndex > 0) {\n const prevItem = docState.virtualItems[currentItemIndex - 1];\n const targetPage = prevItem.pageNumbers[0];\n\n this.startPageChange(id, targetPage, behavior);\n\n const position = strategy.getScrollPositionForPage(\n targetPage,\n docState.virtualItems,\n coreDoc.scale,\n coreDoc.rotation,\n );\n\n if (position) {\n const viewport = this.viewport.forDocument(id);\n viewport.scrollTo({ ...position, behavior });\n } else {\n this.completePageChange(id);\n }\n }\n }\n\n private getMetrics(viewport?: ViewportMetrics, documentId?: string): ScrollMetrics {\n const id = documentId ?? this.getActiveDocumentId();\n\n if (viewport) {\n return this.computeMetrics(id, viewport);\n }\n\n const viewportScope = this.viewport.forDocument(id);\n return this.computeMetrics(id, viewportScope.getMetrics());\n }\n\n private getLayout(documentId?: string): LayoutChangePayload {\n const docState = this.getDocumentStateOrThrow(documentId);\n return {\n virtualItems: docState.virtualItems,\n totalContentSize: docState.totalContentSize,\n };\n }\n\n private getRectPositionForPage(\n pageIndex: number,\n rect: Rect,\n scale?: number,\n rotation?: Rotation,\n documentId?: string,\n ): Rect | null {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentStateOrThrow(id);\n const strategy = this.getStrategy(id);\n const coreDoc = this.getCoreDocumentOrThrow(id);\n\n return strategy.getRectPositionForPage(\n pageIndex + 1,\n docState.virtualItems,\n scale ?? coreDoc.scale,\n rotation ?? coreDoc.rotation,\n rect,\n );\n }\n\n private setScrollStrategyForDocument(newStrategy: ScrollStrategy, documentId?: string): void {\n const id = documentId ?? this.getActiveDocumentId();\n const docState = this.getDocumentState(id);\n\n if (!docState || docState.strategy === newStrategy) return;\n\n // Create new strategy\n const strategy = this.createStrategy(newStrategy);\n this.strategies.set(id, strategy);\n\n // Update state\n this.dispatch(setScrollStrategy(id, newStrategy));\n\n // Recalculate layout\n this.refreshDocumentLayout(id);\n }\n\n // ─────────────────────────────────────────────────────────\n // Store Update Handlers\n // ─────────────────────────────────────────────────────────\n\n override onStoreUpdated(prevState: ScrollState, newState: ScrollState): void {\n // Emit state changes and push scroller layout for each changed document\n for (const documentId in newState.documents) {\n const prevDoc = prevState.documents[documentId];\n const newDoc = newState.documents[documentId];\n\n if (prevDoc !== newDoc) {\n this.state$.emit(newDoc);\n\n if (prevDoc?.pageChangeState !== newDoc.pageChangeState) {\n this.pageChangeState$.emit({\n documentId,\n state: newDoc.pageChangeState,\n });\n }\n\n // Push scroller layout on any state change\n this.pushScrollerLayout(documentId);\n }\n }\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(): Promise<void> {\n this.logger.info('ScrollPlugin', 'Initialize', 'Scroll plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.strategies.clear();\n this.layoutReady.clear();\n this.initialLayoutFired.clear();\n\n // Clear all scroller layout emitters\n for (const emitter of this.scrollerLayoutEmitters.values()) {\n emitter.clear();\n }\n this.scrollerLayoutEmitters.clear();\n\n this.pageChange$.clear();\n this.scroll$.clear();\n this.layoutChange$.clear();\n this.pageChangeState$.clear();\n this.layoutReady$.clear();\n this.state$.clear();\n\n super.destroy();\n }\n}\n","import { PluginManifest } from '@embedpdf/core';\nimport { ScrollPluginConfig, ScrollStrategy } from './types';\n\nexport const SCROLL_PLUGIN_ID = 'scroll';\n\nexport const manifest: PluginManifest<ScrollPluginConfig> = {\n id: SCROLL_PLUGIN_ID,\n name: 'Scroll Plugin',\n version: '1.0.0',\n provides: ['scroll'],\n requires: ['viewport'],\n optional: ['spread'],\n defaultConfig: {\n defaultPageGap: 10,\n defaultBufferSize: 4,\n defaultStrategy: ScrollStrategy.Vertical,\n },\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { ScrollPlugin } from './scroll-plugin';\nimport { manifest, SCROLL_PLUGIN_ID } from './manifest';\nimport { ScrollPluginConfig, ScrollState } from './types';\nimport { scrollReducer, initialState } from './reducer';\nimport { ScrollAction } from './actions';\n\nexport const ScrollPluginPackage: PluginPackage<\n ScrollPlugin,\n ScrollPluginConfig,\n ScrollState,\n ScrollAction\n> = {\n manifest,\n create: (registry, config) => new ScrollPlugin(SCROLL_PLUGIN_ID, registry, config),\n reducer: scrollReducer,\n initialState: (coreState, config) => initialState(coreState, config),\n};\n\nexport * from './scroll-plugin';\nexport * from './types';\nexport * from './manifest';\nexport * from './types/virtual-item';\nexport * from './selectors';\n"],"names":["ScrollStrategy","item"],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -94,7 +94,17 @@ export interface ScrollToPageOptions {
|
|
|
94
94
|
y: number;
|
|
95
95
|
};
|
|
96
96
|
behavior?: ScrollBehavior;
|
|
97
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Horizontal alignment as a percentage (0-100).
|
|
99
|
+
* 0 = target at left edge, 50 = centered, 100 = target at right edge.
|
|
100
|
+
*/
|
|
101
|
+
alignX?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Vertical alignment as a percentage (0-100).
|
|
104
|
+
* 0 = target at top edge, 50 = centered, 100 = target at bottom edge.
|
|
105
|
+
* Useful for mobile where UI overlays may cover part of the screen (e.g., alignY: 25 for top quarter).
|
|
106
|
+
*/
|
|
107
|
+
alignY?: number;
|
|
98
108
|
}
|
|
99
109
|
export interface PageChangeEvent {
|
|
100
110
|
documentId: string;
|
|
@@ -117,6 +127,8 @@ export interface LayoutReadyEvent {
|
|
|
117
127
|
documentId: string;
|
|
118
128
|
/** True only on the first layout ready after document load, false on subsequent (e.g., tab switches) */
|
|
119
129
|
isInitial: boolean;
|
|
130
|
+
pageNumber: number;
|
|
131
|
+
totalPages: number;
|
|
120
132
|
}
|
|
121
133
|
export interface ScrollScope {
|
|
122
134
|
getCurrentPage(): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/plugin-scroll",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@embedpdf/models": "2.0.0-next.
|
|
38
|
+
"@embedpdf/models": "2.0.0-next.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/react": "^18.2.0",
|
|
42
42
|
"typescript": "^5.0.0",
|
|
43
|
-
"@embedpdf/
|
|
44
|
-
"@embedpdf/
|
|
45
|
-
"@embedpdf/plugin-spread": "2.0.0-next.
|
|
46
|
-
"@embedpdf/
|
|
43
|
+
"@embedpdf/core": "2.0.0-next.3",
|
|
44
|
+
"@embedpdf/plugin-viewport": "2.0.0-next.3",
|
|
45
|
+
"@embedpdf/plugin-spread": "2.0.0-next.3",
|
|
46
|
+
"@embedpdf/build": "1.1.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"preact": "^10.26.4",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"react-dom": ">=16.8.0",
|
|
52
52
|
"vue": ">=3.2.0",
|
|
53
53
|
"svelte": ">=5 <6",
|
|
54
|
-
"@embedpdf/core": "2.0.0-next.
|
|
55
|
-
"@embedpdf/plugin-viewport": "2.0.0-next.
|
|
54
|
+
"@embedpdf/core": "2.0.0-next.3",
|
|
55
|
+
"@embedpdf/plugin-viewport": "2.0.0-next.3"
|
|
56
56
|
},
|
|
57
57
|
"files": [
|
|
58
58
|
"dist",
|