@papyrus-sdk/ui-react-native 0.2.35-beta.11 → 0.2.35-beta.11.epub.2

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.
@@ -20483,6 +20483,67 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20483
20483
  <!-- @papyrus-comic-runtime:end -->
20484
20484
  <script>
20485
20485
  (function () {
20486
+ /* @papyrus-epub-reader:start */
20487
+ (function(root){
20488
+ function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
20489
+ function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\([^\n<>]+\)$/.test(value);}
20490
+ function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
20491
+ function gesture(start,end,state){
20492
+ if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
20493
+ var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
20494
+ if(state.mode==='paged' && Math.abs(dx)>=48 && Math.abs(dx)>Math.abs(dy)*1.5 && elapsed>=0 && elapsed<1500)return (dx<0)!==Boolean(state.rtl)?'next':'prev';
20495
+ if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
20496
+ return null;
20497
+ }
20498
+ function location(raw,count,readingMode,geometry){
20499
+ var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
20500
+ var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
20501
+ var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
20502
+ var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
20503
+ var atStart=Boolean(raw.atStart),atEnd=Boolean(raw.atEnd);
20504
+ // Chromium may round a column offset down by a fraction of a CSS pixel.
20505
+ // Keep epub.js authoritative outside a 1px boundary, and for RTL layouts.
20506
+ if(visualPage!==null && total>0 && geometry && geometry.direction==='ltr' && geometry.pageWidth>0 && Number.isFinite(geometry.offset)){
20507
+ var boundary=Math.round(geometry.offset/geometry.pageWidth);
20508
+ if(Math.abs(geometry.offset-boundary*geometry.pageWidth)<=1){
20509
+ visualPage=Math.min(total,Math.max(1,boundary+1));
20510
+ atStart=chapter===(geometry.firstIndex ?? 0)+1 && visualPage===1;
20511
+ atEnd=chapter===(geometry.lastIndex ?? count-1)+1 && visualPage===total;
20512
+ }
20513
+ }
20514
+ var fraction=total?Math.max(0,(visualPage-1)/total):0;
20515
+ return {version:1,cfi:start.cfi,href:start.href||'',chapter:chapter,chapterCount:count,visualPage:visualPage,visualPageCount:total,progress:Math.min(1,Math.max(0,(chapter-1+fraction)/Math.max(1,count))),mode:mode(readingMode),atStart:atStart,atEnd:atEnd};
20516
+ }
20517
+ function installResizeAnchor(manager,readCfi){
20518
+ var original=manager.resize,anchor=null;
20519
+ function resize(width,height,target){
20520
+ if(validCfi(target))anchor=target;
20521
+ if(!anchor){var current=readCfi();if(validCfi(current))anchor=current;}
20522
+ // epub.js accepts a logical target as its third argument. Keep it across
20523
+ // keyboard/dialog reflows instead of adopting each new viewport start.
20524
+ return original.call(this,width,height,anchor || target);
20525
+ }
20526
+ manager.resize=resize;
20527
+ return {cfi:function(){return anchor;},invalidate:function(){anchor=null;},destroy:function(){if(manager.resize===resize)manager.resize=original;anchor=null;}};
20528
+ }
20529
+ function install(contents,config){
20530
+ var doc=contents.document,win=contents.window,start=null,blocked=false;
20531
+ function point(e){var t=e.changedTouches&&e.changedTouches[0]||e.touches&&e.touches[0];return t?{x:t.clientX,y:t.clientY,time:Date.now()}:null;}
20532
+ function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
20533
+ function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
20534
+ function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
20535
+ function invalidateScroll(finish){if(config.current() && start && finish && config.mode()==='chapter-scroll' && !blocked && !selected() && Math.abs(finish.y-start.y)>12 && Math.abs(finish.y-start.y)>Math.abs(finish.x-start.x) && config.interact)config.interact();}
20536
+ function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
20537
+ function end(e){var finish=point(e);invalidateScroll(finish);var action=gesture(start,finish,{mode:config.mode(),rtl:config.rtl(),selected:selected(),interactive:blocked||interactive(e.target)||Boolean(config.annotationHit && config.annotationHit(point(e))),multitouch:blocked});start=null;if(!config.current())return;if(action==='tap')config.tap();else if(action)config.turn(action==='next'?1:-1);}
20538
+ function cancel(){start=null;blocked=true;}
20539
+ function link(e){if(config.current() && e.target?.closest?.('a') && config.interact)config.interact();}
20540
+ doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});doc.addEventListener('click',link);
20541
+ return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);doc.removeEventListener('click',link);};
20542
+ }
20543
+ root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,installResizeAnchor:installResizeAnchor,install:install};
20544
+ })(typeof window==='undefined'?globalThis:window);
20545
+
20546
+ /* @papyrus-epub-reader:end */
20486
20547
  /* @papyrus-epub-annotations:start */
20487
20548
  (function (root, factory) {
20488
20549
  if (typeof module === 'object' && module.exports) module.exports = factory();
@@ -20561,6 +20622,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20561
20622
  var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
20562
20623
  var style = annotation.markupStyle || annotation.type;
20563
20624
  var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
20625
+ if(style === 'highlight')opacity=Math.min(0.35,opacity);
20564
20626
  rects.forEach(function(rect) {
20565
20627
  var mark = doc.createElement('div');
20566
20628
  var bodyRect=doc.body.getBoundingClientRect();
@@ -20573,8 +20635,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20573
20635
  if (hasNote) {
20574
20636
  var last = rects[rects.length-1], button = doc.createElement('button');
20575
20637
  button.textContent = '●'; button.setAttribute('aria-label',options.labels().comment || '');
20576
- button.style.cssText = 'position:absolute;pointer-events:auto;border:0;background:transparent;color:'+color+';width:44px;height:44px;left:'+Math.max(0,Math.min(doc.documentElement.clientWidth-44,last.right-doc.body.getBoundingClientRect().left))+'px;top:'+(Math.floor(last.top/44)*44-doc.body.getBoundingClientRect().top)+'px';
20577
- var key = Math.floor(last.top/44);
20638
+ var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
20639
+ var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
20640
+ button.style.cssText = 'position:absolute;pointer-events:auto;border:0;background:transparent;color:'+color+';width:44px;height:44px;left:'+((column+1)*pageWidth-44)+'px;top:'+(Math.floor(last.top/44)*44-doc.body.getBoundingClientRect().top)+'px';
20641
+ var key = column+":"+Math.floor(last.top/44);
20578
20642
  var neighbors=noteGroups.get(key);
20579
20643
  if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
20580
20644
  neighbors.ids.push(annotation.id);
@@ -20595,10 +20659,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20595
20659
  doc.addEventListener('click',tapMarkup);
20596
20660
  var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20597
20661
  if (observer) observer.observe(doc.body);
20598
- win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
20662
+ win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
20599
20663
  if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
20600
20664
  schedule();
20601
- return {schedule:schedule, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',schedule);doc.removeEventListener('load',schedule,true);doc.removeEventListener('click',tapMarkup);overlay.remove();doc.body.style.position=originalPosition;registry.clear();}};
20665
+ return {schedule:schedule, hit:function(x,y){return hitRegions.some(function(item){return item.rects.some(function(r){return x>=r.left && x<=r.right && y>=r.top && y<=r.bottom;});});}, range:function(anchor){return resolveRange(contents,anchor);}, destroy:function(){alive=false;if(frame)win.cancelAnimationFrame(frame);if(observer)observer.disconnect();win.removeEventListener('resize',schedule);win.removeEventListener('scroll',schedule);doc.removeEventListener('load',schedule,true);doc.removeEventListener('click',tapMarkup);overlay.remove();doc.body.style.position=originalPosition;registry.clear();}};
20602
20666
  }
20603
20667
  return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20604
20668
  });
@@ -20611,6 +20675,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20611
20675
  let currentType = null;
20612
20676
  let book = null;
20613
20677
  let rendition = null;
20678
+ let epubResizeAnchor = null;
20614
20679
  let documentSessionId = null;
20615
20680
  let annotationDocumentId;
20616
20681
  let annotationLabels = {};
@@ -21023,6 +21088,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21023
21088
  };
21024
21089
 
21025
21090
  const clearViewer = () => {
21091
+ if(epubResizeAnchor){epubResizeAnchor.destroy();epubResizeAnchor=null;}
21026
21092
  epubAnnotationRenderers.forEach(renderer => renderer.destroy());
21027
21093
  epubAnnotationRenderers.clear();
21028
21094
  if (epubInteractionCleanup) {
@@ -21679,26 +21745,57 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21679
21745
  pageCount,
21680
21746
  });
21681
21747
 
21748
+ if (book.locations?.generate) {
21749
+ const activeBook=book;
21750
+ activeBook.locations.generate(150).then(() => {
21751
+ if (isCurrentLoad() && activeBook===book && epubLocation) {
21752
+ epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
21753
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21754
+ }
21755
+ }).catch(() => {});
21756
+ }
21757
+ return renderEpubRendition(loadId,generation,null);
21758
+ };
21759
+
21760
+ const renderEpubRendition = async (loadId,generation,target) => {
21761
+ const diagnosticPayload={loadId};
21762
+ const isCurrentLoad=()=>generation===epubLoadGeneration;
21763
+ if(epubInteractionCleanup)epubInteractionCleanup();
21764
+ if(rendition)rendition.destroy();
21682
21765
  clearViewer();
21683
- rendition = book.renderTo(viewer, {
21684
- manager: 'continuous',
21685
- allowScriptedContent: false,
21686
- width: '100%',
21687
- height: '100%',
21688
- flow: 'scrolled-continuous',
21689
- spread: 'none',
21690
- });
21766
+ rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
21691
21767
  sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
21692
21768
 
21693
21769
  if (rendition && rendition.hooks && rendition.hooks.content) {
21694
21770
  const session = documentSessionId;
21695
21771
  const activeRendition = rendition;
21696
21772
  rendition.hooks.content.register((contents) => {
21773
+ const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
21774
+ mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
21775
+ rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
21776
+ blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
21777
+ annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
21778
+ current: () => session === documentSessionId && activeRendition === rendition,
21779
+ interact: () => epubResizeAnchor?.invalidate(),
21780
+ tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
21781
+ turn: direction => {
21782
+ epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
21783
+ if(session !== documentSessionId || activeRendition !== rendition)return;
21784
+ epubResizeAnchor?.invalidate();
21785
+ return direction<0?activeRendition.prev():activeRendition.next();
21786
+ });
21787
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
21788
+ },
21789
+ });
21790
+ const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
21791
+ epubSelectionCleanups.add(gestureCleanup);
21792
+ contents.__papyrusGestureCleanup=gestureCleanup;
21697
21793
  const href = spineItems[contents.sectionIndex]?.href;
21698
21794
  if (href && window.PapyrusEpubAnnotations) {
21699
21795
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21700
21796
  href, get documentId() { return annotationDocumentId; },
21701
21797
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21798
+ pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21702
21799
  annotations: () => epubAnnotations, labels: () => annotationLabels,
21703
21800
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21704
21801
  });
@@ -21717,8 +21814,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21717
21814
  typeof selectionWindow.getSelection === 'function'
21718
21815
  ) {
21719
21816
  let selectionTimer = null;
21720
- let lastSelectionKey = '';
21721
- let lastSelectionAt = 0;
21817
+
21722
21818
  const reportSelection = () => {
21723
21819
  selectionTimer = null;
21724
21820
  if (session !== documentSessionId || activeRendition !== rendition) return;
@@ -21733,11 +21829,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21733
21829
  try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
21734
21830
  const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
21735
21831
  if (!anchor) return;
21736
- const key = anchor.cfiRange + ':' + anchor.quote;
21737
- const now = Date.now();
21738
- if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
21739
- lastSelectionKey = key;
21740
- lastSelectionAt = now;
21832
+ if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21741
21833
  const payload = {documentSessionId: session, text: anchor.quote,
21742
21834
  pageIndex: contents.sectionIndex + 1, anchor};
21743
21835
  nativeMenuSelection = {contents, payload};
@@ -21752,6 +21844,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21752
21844
  // The content document still emits selectionchange, so resolve its
21753
21845
  // live Range to a CFI before the native menu action can fire.
21754
21846
  if (text) reportSelection();
21847
+ else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
21755
21848
  };
21756
21849
  selectionDocument.addEventListener('selectionchange', handleSelectionChange);
21757
21850
  const selectionCleanup = () => {
@@ -21774,6 +21867,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21774
21867
  typeof rendition.hooks.unloaded.register === 'function'
21775
21868
  ) {
21776
21869
  rendition.hooks.unloaded.register((view) => {
21870
+ view?.contents?.__papyrusGestureCleanup?.();
21777
21871
  const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
21778
21872
  if (typeof cleanup === 'function') cleanup();
21779
21873
  const renderer = epubAnnotationRenderers.get(view?.contents);
@@ -21789,13 +21883,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21789
21883
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21790
21884
  const href = spineItems[contents.sectionIndex]?.href;
21791
21885
  const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
21792
- if (!anchor) return;
21886
+ if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21793
21887
  epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
21794
21888
  const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
21795
21889
  nativeMenuSelection = {contents, payload};
21796
21890
  sendEvent('EPUB_TEXT_SELECTED', payload);
21797
21891
  });
21798
- ['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
21892
+ ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21799
21893
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
21800
21894
  }));
21801
21895
  rendition.on('relocated', (location) => {
@@ -21805,6 +21899,21 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21805
21899
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
21806
21900
  if (index < 0 || index >= spineItems.length) return;
21807
21901
  epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
21902
+ let pageGeometry;
21903
+ const manager=rendition.manager,layout=manager?.layout;
21904
+ if(epubViewMode==='single' && layout?.divisor===1 && !manager?.settings?.fullsize && manager?.settings?.direction==='ltr'){
21905
+ const visible=manager.visible?.();
21906
+ const view=visible?.find(view=>view.index===index);
21907
+ if(view && manager.container){
21908
+ pageGeometry={direction:'ltr',firstIndex:book.spine.first()?.index,lastIndex:book.spine.last()?.index,pageWidth:layout.pageWidth,offset:manager.container.getBoundingClientRect().left-view.position().left};
21909
+ }
21910
+ }
21911
+ epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll',pageGeometry);
21912
+ if(epubLocation){
21913
+ epubLocation.cfi=epubResizeAnchor?.cfi() || epubLocation.cfi;
21914
+ if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
21915
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21916
+ }
21808
21917
  const nextPage = index + 1;
21809
21918
  if (nextPage === currentPage) return;
21810
21919
  currentPage = nextPage;
@@ -21812,21 +21921,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21812
21921
  });
21813
21922
  }
21814
21923
 
21924
+ applyEpubZoom();
21815
21925
  sendEpubDiagnostic('epub.display.start', diagnosticPayload);
21816
21926
  await withEpubStageTimeout(
21817
- displayEpubPage(0),
21927
+ rendition.display(target || spineItems[0]?.href),
21818
21928
  'rendition.display',
21819
21929
  diagnosticPayload,
21820
21930
  );
21821
21931
  if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
21822
21932
  sendEpubDiagnostic('epub.display.end', diagnosticPayload);
21933
+ const resizeRendition=rendition;
21934
+ epubResizeAnchor=window.PapyrusEpubReader.installResizeAnchor(resizeRendition.manager,()=>resizeRendition.location?.start?.cfi);
21823
21935
  if (
21824
21936
  rendition &&
21825
21937
  rendition.manager &&
21826
21938
  typeof rendition.manager.on === 'function' &&
21827
21939
  typeof rendition.on === 'function'
21828
21940
  ) {
21829
- let touchStart = null;
21830
21941
  let lastScrollOffset = -1;
21831
21942
  const manager = rendition.manager;
21832
21943
  epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
@@ -21901,57 +22012,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21901
22012
  return result;
21902
22013
  };
21903
22014
  }
21904
- const getTouchPoint = (event) => {
21905
- const touch =
21906
- (event && event.changedTouches && event.changedTouches[0]) ||
21907
- (event && event.touches && event.touches[0]);
21908
- if (!touch) return null;
21909
- const x = Number(touch.screenX ?? touch.clientX);
21910
- const y = Number(touch.screenY ?? touch.clientY);
21911
- return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
21912
- };
21913
- const handleScroll = (event) => {
22015
+ const handleScroll = event => {
21914
22016
  const offsetY = Math.max(0, Number(event && event.top) || 0);
21915
22017
  epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
21916
- if (Math.abs(offsetY - lastScrollOffset) < 1) return;
21917
- lastScrollOffset = offsetY;
21918
- sendEvent('VIEWER_SCROLL', { offsetY });
22018
+ if (Math.abs(offsetY-lastScrollOffset)<1) return;
22019
+ lastScrollOffset=offsetY;
22020
+ sendEvent('VIEWER_SCROLL',{offsetY});
21919
22021
  };
21920
- const handleTouchStart = (event) => {
21921
- touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
21922
- epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
21923
- };
21924
- const handleTouchMove = (event) => {
21925
- if (event?.touches?.length > 1) touchStart = null;
21926
- epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
21927
- };
21928
- const handleTouchEnd = (event) => {
21929
- const end = getTouchPoint(event);
21930
- const start = touchStart;
21931
- touchStart = null;
21932
- const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
21933
- if (!selectedText && start && end && epubViewMode === 'single' && Math.abs(end.x-start.x) >= 48 && Math.abs(end.x-start.x) > Math.abs(end.y-start.y)*1.5) {
21934
- const active = rendition;
21935
- const interactionSession = documentSessionId;
21936
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
21937
- if (active !== rendition || interactionSession !== documentSessionId) return;
21938
- return end.x < start.x ? active.next() : active.prev();
21939
- });
21940
- epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
21941
- } else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21942
- sendEvent('VIEWER_TAP', {});
21943
- }
21944
- epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
21945
- };
21946
-
21947
22022
  manager.on('scroll', handleScroll);
21948
22023
  const handleScrolled = (event) => {
21949
22024
  epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
21950
22025
  };
21951
22026
  manager.on('scrolled', handleScrolled);
21952
- rendition.on('touchstart', handleTouchStart);
21953
- rendition.on('touchmove', handleTouchMove);
21954
- rendition.on('touchend', handleTouchEnd);
22027
+
21955
22028
  epubInteractionCleanup = () => {
21956
22029
  epubSelectionCleanups.forEach((cleanup) => cleanup());
21957
22030
  epubSelectionCleanups.clear();
@@ -21960,16 +22033,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21960
22033
  if (originalEnqueue) queue.enqueue = originalEnqueue;
21961
22034
  if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
21962
22035
  if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
21963
- if (typeof rendition.off === 'function') {
21964
- rendition.off('touchstart', handleTouchStart);
21965
- rendition.off('touchmove', handleTouchMove);
21966
- rendition.off('touchend', handleTouchEnd);
21967
- }
21968
22036
  epubScrollDiagnostics = null;
21969
22037
  };
21970
22038
  }
21971
- applyEpubZoom();
21972
-
21973
22039
  const outline = await withEpubStageTimeout(
21974
22040
  buildOutline(),
21975
22041
  'outline',
@@ -21999,6 +22065,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21999
22065
  const request = ++epubNavigationRequest;
22000
22066
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22001
22067
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22068
+ epubResizeAnchor?.invalidate();
22002
22069
  await activeRendition.display(target);
22003
22070
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22004
22071
  currentPage = pageIndex + 1;
@@ -22107,7 +22174,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22107
22174
  };
22108
22175
 
22109
22176
  let nativeMenuSelection = null;
22110
- let epubViewMode = "continuous";
22177
+ let epubViewMode = "single";
22178
+ let epubLocation = null;
22111
22179
 
22112
22180
  const handleCommand = async (message) => {
22113
22181
  const { id, kind, payload } = message;
@@ -22120,7 +22188,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22120
22188
  epubAnnotations = [];
22121
22189
  annotationDocumentId = undefined;
22122
22190
  zoom = 1.0;
22123
- epubViewMode = "continuous";
22191
+ epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22192
+ epubLocation = null;
22193
+ nativeMenuSelection = null;
22124
22194
  epubNavigationRequest += 1;
22125
22195
  epubNavigationQueue = Promise.resolve();
22126
22196
 
@@ -22185,6 +22255,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22185
22255
  epubNavigationRequest += 1;
22186
22256
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22187
22257
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22258
+ epubResizeAnchor?.invalidate();
22188
22259
  await activeRendition.display(annotation.anchor.href);
22189
22260
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22190
22261
  const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
@@ -22226,6 +22297,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22226
22297
  const active = rendition;
22227
22298
  epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22228
22299
  if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22300
+ epubResizeAnchor?.invalidate();
22229
22301
  return payload.direction < 0 ? active.prev() : active.next();
22230
22302
  });
22231
22303
  await epubNavigationQueue;
@@ -22234,25 +22306,40 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22234
22306
 
22235
22307
  if (kind === 'set-epub-view-mode') {
22236
22308
  if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
22237
- epubViewMode = payload.viewMode === 'single' ? 'single' : 'continuous';
22238
- const active = rendition;
22239
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22240
- if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22241
- const flow = epubViewMode === 'single' ? 'paginated' : 'scrolled-continuous';
22242
- if (active.settings.flow === flow) return;
22243
- active.flow(flow);
22244
- return active.q.enqueue(() => Promise.resolve());
22245
- });
22246
- await epubNavigationQueue;
22247
- sendResponse(id,true,null); return;
22309
+ const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
22310
+ {
22311
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22312
+ if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
22313
+ const target=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22314
+ epubViewMode=next;
22315
+ await renderEpubRendition('layout',epubLoadGeneration,target);
22316
+ });
22317
+ await epubNavigationQueue;
22318
+ }
22319
+ sendResponse(id,true,null);return;
22320
+ }
22321
+ if(kind === 'epub-go-to-location'){
22322
+ if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22323
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});
22324
+ await epubNavigationQueue;sendResponse(id,true,null);return;
22248
22325
  }
22249
22326
 
22250
22327
  if (kind === 'set-zoom') {
22251
- zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
22252
- applyZoom();
22253
- sendState();
22254
- sendResponse(id, true, { zoom });
22255
- return;
22328
+ const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
22329
+ if(currentType === 'epub') {
22330
+ const session=documentSessionId;
22331
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22332
+ if(session!==documentSessionId)return;
22333
+ const active=rendition;
22334
+ const cfi=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22335
+ zoom=requestedZoom;applyEpubZoom();
22336
+ // Let the content CSS update before redisplaying the logical anchor.
22337
+ await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
22338
+ if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
22339
+ });
22340
+ await epubNavigationQueue;
22341
+ } else {zoom=requestedZoom;applyZoom();}
22342
+ sendState();sendResponse(id,true,{zoom});return;
22256
22343
  }
22257
22344
 
22258
22345
  if (kind === 'set-rotation') {