@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.
package/dist/index.mjs CHANGED
@@ -20512,6 +20512,67 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20512
20512
  <!-- @papyrus-comic-runtime:end -->
20513
20513
  <script>
20514
20514
  (function () {
20515
+ /* @papyrus-epub-reader:start */
20516
+ (function(root){
20517
+ function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
20518
+ function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\\([^\\n<>]+\\)$/.test(value);}
20519
+ function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
20520
+ function gesture(start,end,state){
20521
+ if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
20522
+ var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
20523
+ 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';
20524
+ if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
20525
+ return null;
20526
+ }
20527
+ function location(raw,count,readingMode,geometry){
20528
+ var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
20529
+ var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
20530
+ var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
20531
+ var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
20532
+ var atStart=Boolean(raw.atStart),atEnd=Boolean(raw.atEnd);
20533
+ // Chromium may round a column offset down by a fraction of a CSS pixel.
20534
+ // Keep epub.js authoritative outside a 1px boundary, and for RTL layouts.
20535
+ if(visualPage!==null && total>0 && geometry && geometry.direction==='ltr' && geometry.pageWidth>0 && Number.isFinite(geometry.offset)){
20536
+ var boundary=Math.round(geometry.offset/geometry.pageWidth);
20537
+ if(Math.abs(geometry.offset-boundary*geometry.pageWidth)<=1){
20538
+ visualPage=Math.min(total,Math.max(1,boundary+1));
20539
+ atStart=chapter===(geometry.firstIndex ?? 0)+1 && visualPage===1;
20540
+ atEnd=chapter===(geometry.lastIndex ?? count-1)+1 && visualPage===total;
20541
+ }
20542
+ }
20543
+ var fraction=total?Math.max(0,(visualPage-1)/total):0;
20544
+ 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};
20545
+ }
20546
+ function installResizeAnchor(manager,readCfi){
20547
+ var original=manager.resize,anchor=null;
20548
+ function resize(width,height,target){
20549
+ if(validCfi(target))anchor=target;
20550
+ if(!anchor){var current=readCfi();if(validCfi(current))anchor=current;}
20551
+ // epub.js accepts a logical target as its third argument. Keep it across
20552
+ // keyboard/dialog reflows instead of adopting each new viewport start.
20553
+ return original.call(this,width,height,anchor || target);
20554
+ }
20555
+ manager.resize=resize;
20556
+ return {cfi:function(){return anchor;},invalidate:function(){anchor=null;},destroy:function(){if(manager.resize===resize)manager.resize=original;anchor=null;}};
20557
+ }
20558
+ function install(contents,config){
20559
+ var doc=contents.document,win=contents.window,start=null,blocked=false;
20560
+ 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;}
20561
+ function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
20562
+ function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
20563
+ function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
20564
+ 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();}
20565
+ function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
20566
+ 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);}
20567
+ function cancel(){start=null;blocked=true;}
20568
+ function link(e){if(config.current() && e.target?.closest?.('a') && config.interact)config.interact();}
20569
+ 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);
20570
+ return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);doc.removeEventListener('click',link);};
20571
+ }
20572
+ root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,installResizeAnchor:installResizeAnchor,install:install};
20573
+ })(typeof window==='undefined'?globalThis:window);
20574
+
20575
+ /* @papyrus-epub-reader:end */
20515
20576
  /* @papyrus-epub-annotations:start */
20516
20577
  (function (root, factory) {
20517
20578
  if (typeof module === 'object' && module.exports) module.exports = factory();
@@ -20590,6 +20651,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20590
20651
  var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
20591
20652
  var style = annotation.markupStyle || annotation.type;
20592
20653
  var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
20654
+ if(style === 'highlight')opacity=Math.min(0.35,opacity);
20593
20655
  rects.forEach(function(rect) {
20594
20656
  var mark = doc.createElement('div');
20595
20657
  var bodyRect=doc.body.getBoundingClientRect();
@@ -20602,8 +20664,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20602
20664
  if (hasNote) {
20603
20665
  var last = rects[rects.length-1], button = doc.createElement('button');
20604
20666
  button.textContent = '\u25CF'; button.setAttribute('aria-label',options.labels().comment || '');
20605
- 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';
20606
- var key = Math.floor(last.top/44);
20667
+ var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
20668
+ var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
20669
+ 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';
20670
+ var key = column+":"+Math.floor(last.top/44);
20607
20671
  var neighbors=noteGroups.get(key);
20608
20672
  if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
20609
20673
  neighbors.ids.push(annotation.id);
@@ -20624,10 +20688,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20624
20688
  doc.addEventListener('click',tapMarkup);
20625
20689
  var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20626
20690
  if (observer) observer.observe(doc.body);
20627
- win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
20691
+ win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
20628
20692
  if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
20629
20693
  schedule();
20630
- 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();}};
20694
+ 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();}};
20631
20695
  }
20632
20696
  return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20633
20697
  });
@@ -20640,6 +20704,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20640
20704
  let currentType = null;
20641
20705
  let book = null;
20642
20706
  let rendition = null;
20707
+ let epubResizeAnchor = null;
20643
20708
  let documentSessionId = null;
20644
20709
  let annotationDocumentId;
20645
20710
  let annotationLabels = {};
@@ -21052,6 +21117,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21052
21117
  };
21053
21118
 
21054
21119
  const clearViewer = () => {
21120
+ if(epubResizeAnchor){epubResizeAnchor.destroy();epubResizeAnchor=null;}
21055
21121
  epubAnnotationRenderers.forEach(renderer => renderer.destroy());
21056
21122
  epubAnnotationRenderers.clear();
21057
21123
  if (epubInteractionCleanup) {
@@ -21708,26 +21774,57 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21708
21774
  pageCount,
21709
21775
  });
21710
21776
 
21777
+ if (book.locations?.generate) {
21778
+ const activeBook=book;
21779
+ activeBook.locations.generate(150).then(() => {
21780
+ if (isCurrentLoad() && activeBook===book && epubLocation) {
21781
+ epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
21782
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21783
+ }
21784
+ }).catch(() => {});
21785
+ }
21786
+ return renderEpubRendition(loadId,generation,null);
21787
+ };
21788
+
21789
+ const renderEpubRendition = async (loadId,generation,target) => {
21790
+ const diagnosticPayload={loadId};
21791
+ const isCurrentLoad=()=>generation===epubLoadGeneration;
21792
+ if(epubInteractionCleanup)epubInteractionCleanup();
21793
+ if(rendition)rendition.destroy();
21711
21794
  clearViewer();
21712
- rendition = book.renderTo(viewer, {
21713
- manager: 'continuous',
21714
- allowScriptedContent: false,
21715
- width: '100%',
21716
- height: '100%',
21717
- flow: 'scrolled-continuous',
21718
- spread: 'none',
21719
- });
21795
+ rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
21720
21796
  sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
21721
21797
 
21722
21798
  if (rendition && rendition.hooks && rendition.hooks.content) {
21723
21799
  const session = documentSessionId;
21724
21800
  const activeRendition = rendition;
21725
21801
  rendition.hooks.content.register((contents) => {
21802
+ const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
21803
+ mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
21804
+ rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
21805
+ blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
21806
+ annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
21807
+ current: () => session === documentSessionId && activeRendition === rendition,
21808
+ interact: () => epubResizeAnchor?.invalidate(),
21809
+ tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
21810
+ turn: direction => {
21811
+ epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
21812
+ if(session !== documentSessionId || activeRendition !== rendition)return;
21813
+ epubResizeAnchor?.invalidate();
21814
+ return direction<0?activeRendition.prev():activeRendition.next();
21815
+ });
21816
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
21817
+ },
21818
+ });
21819
+ const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
21820
+ epubSelectionCleanups.add(gestureCleanup);
21821
+ contents.__papyrusGestureCleanup=gestureCleanup;
21726
21822
  const href = spineItems[contents.sectionIndex]?.href;
21727
21823
  if (href && window.PapyrusEpubAnnotations) {
21728
21824
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21729
21825
  href, get documentId() { return annotationDocumentId; },
21730
21826
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21827
+ pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21731
21828
  annotations: () => epubAnnotations, labels: () => annotationLabels,
21732
21829
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21733
21830
  });
@@ -21746,8 +21843,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21746
21843
  typeof selectionWindow.getSelection === 'function'
21747
21844
  ) {
21748
21845
  let selectionTimer = null;
21749
- let lastSelectionKey = '';
21750
- let lastSelectionAt = 0;
21846
+
21751
21847
  const reportSelection = () => {
21752
21848
  selectionTimer = null;
21753
21849
  if (session !== documentSessionId || activeRendition !== rendition) return;
@@ -21762,11 +21858,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21762
21858
  try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
21763
21859
  const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
21764
21860
  if (!anchor) return;
21765
- const key = anchor.cfiRange + ':' + anchor.quote;
21766
- const now = Date.now();
21767
- if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
21768
- lastSelectionKey = key;
21769
- lastSelectionAt = now;
21861
+ if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21770
21862
  const payload = {documentSessionId: session, text: anchor.quote,
21771
21863
  pageIndex: contents.sectionIndex + 1, anchor};
21772
21864
  nativeMenuSelection = {contents, payload};
@@ -21781,6 +21873,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21781
21873
  // The content document still emits selectionchange, so resolve its
21782
21874
  // live Range to a CFI before the native menu action can fire.
21783
21875
  if (text) reportSelection();
21876
+ else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
21784
21877
  };
21785
21878
  selectionDocument.addEventListener('selectionchange', handleSelectionChange);
21786
21879
  const selectionCleanup = () => {
@@ -21803,6 +21896,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21803
21896
  typeof rendition.hooks.unloaded.register === 'function'
21804
21897
  ) {
21805
21898
  rendition.hooks.unloaded.register((view) => {
21899
+ view?.contents?.__papyrusGestureCleanup?.();
21806
21900
  const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
21807
21901
  if (typeof cleanup === 'function') cleanup();
21808
21902
  const renderer = epubAnnotationRenderers.get(view?.contents);
@@ -21818,13 +21912,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21818
21912
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21819
21913
  const href = spineItems[contents.sectionIndex]?.href;
21820
21914
  const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
21821
- if (!anchor) return;
21915
+ if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21822
21916
  epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
21823
21917
  const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
21824
21918
  nativeMenuSelection = {contents, payload};
21825
21919
  sendEvent('EPUB_TEXT_SELECTED', payload);
21826
21920
  });
21827
- ['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
21921
+ ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21828
21922
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
21829
21923
  }));
21830
21924
  rendition.on('relocated', (location) => {
@@ -21834,6 +21928,21 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21834
21928
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
21835
21929
  if (index < 0 || index >= spineItems.length) return;
21836
21930
  epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
21931
+ let pageGeometry;
21932
+ const manager=rendition.manager,layout=manager?.layout;
21933
+ if(epubViewMode==='single' && layout?.divisor===1 && !manager?.settings?.fullsize && manager?.settings?.direction==='ltr'){
21934
+ const visible=manager.visible?.();
21935
+ const view=visible?.find(view=>view.index===index);
21936
+ if(view && manager.container){
21937
+ pageGeometry={direction:'ltr',firstIndex:book.spine.first()?.index,lastIndex:book.spine.last()?.index,pageWidth:layout.pageWidth,offset:manager.container.getBoundingClientRect().left-view.position().left};
21938
+ }
21939
+ }
21940
+ epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll',pageGeometry);
21941
+ if(epubLocation){
21942
+ epubLocation.cfi=epubResizeAnchor?.cfi() || epubLocation.cfi;
21943
+ if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
21944
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21945
+ }
21837
21946
  const nextPage = index + 1;
21838
21947
  if (nextPage === currentPage) return;
21839
21948
  currentPage = nextPage;
@@ -21841,21 +21950,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21841
21950
  });
21842
21951
  }
21843
21952
 
21953
+ applyEpubZoom();
21844
21954
  sendEpubDiagnostic('epub.display.start', diagnosticPayload);
21845
21955
  await withEpubStageTimeout(
21846
- displayEpubPage(0),
21956
+ rendition.display(target || spineItems[0]?.href),
21847
21957
  'rendition.display',
21848
21958
  diagnosticPayload,
21849
21959
  );
21850
21960
  if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
21851
21961
  sendEpubDiagnostic('epub.display.end', diagnosticPayload);
21962
+ const resizeRendition=rendition;
21963
+ epubResizeAnchor=window.PapyrusEpubReader.installResizeAnchor(resizeRendition.manager,()=>resizeRendition.location?.start?.cfi);
21852
21964
  if (
21853
21965
  rendition &&
21854
21966
  rendition.manager &&
21855
21967
  typeof rendition.manager.on === 'function' &&
21856
21968
  typeof rendition.on === 'function'
21857
21969
  ) {
21858
- let touchStart = null;
21859
21970
  let lastScrollOffset = -1;
21860
21971
  const manager = rendition.manager;
21861
21972
  epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
@@ -21930,57 +22041,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21930
22041
  return result;
21931
22042
  };
21932
22043
  }
21933
- const getTouchPoint = (event) => {
21934
- const touch =
21935
- (event && event.changedTouches && event.changedTouches[0]) ||
21936
- (event && event.touches && event.touches[0]);
21937
- if (!touch) return null;
21938
- const x = Number(touch.screenX ?? touch.clientX);
21939
- const y = Number(touch.screenY ?? touch.clientY);
21940
- return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
21941
- };
21942
- const handleScroll = (event) => {
22044
+ const handleScroll = event => {
21943
22045
  const offsetY = Math.max(0, Number(event && event.top) || 0);
21944
22046
  epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
21945
- if (Math.abs(offsetY - lastScrollOffset) < 1) return;
21946
- lastScrollOffset = offsetY;
21947
- sendEvent('VIEWER_SCROLL', { offsetY });
21948
- };
21949
- const handleTouchStart = (event) => {
21950
- touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
21951
- epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
21952
- };
21953
- const handleTouchMove = (event) => {
21954
- if (event?.touches?.length > 1) touchStart = null;
21955
- epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
21956
- };
21957
- const handleTouchEnd = (event) => {
21958
- const end = getTouchPoint(event);
21959
- const start = touchStart;
21960
- touchStart = null;
21961
- const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
21962
- 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) {
21963
- const active = rendition;
21964
- const interactionSession = documentSessionId;
21965
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
21966
- if (active !== rendition || interactionSession !== documentSessionId) return;
21967
- return end.x < start.x ? active.next() : active.prev();
21968
- });
21969
- epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
21970
- } else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21971
- sendEvent('VIEWER_TAP', {});
21972
- }
21973
- epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
22047
+ if (Math.abs(offsetY-lastScrollOffset)<1) return;
22048
+ lastScrollOffset=offsetY;
22049
+ sendEvent('VIEWER_SCROLL',{offsetY});
21974
22050
  };
21975
-
21976
22051
  manager.on('scroll', handleScroll);
21977
22052
  const handleScrolled = (event) => {
21978
22053
  epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
21979
22054
  };
21980
22055
  manager.on('scrolled', handleScrolled);
21981
- rendition.on('touchstart', handleTouchStart);
21982
- rendition.on('touchmove', handleTouchMove);
21983
- rendition.on('touchend', handleTouchEnd);
22056
+
21984
22057
  epubInteractionCleanup = () => {
21985
22058
  epubSelectionCleanups.forEach((cleanup) => cleanup());
21986
22059
  epubSelectionCleanups.clear();
@@ -21989,16 +22062,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21989
22062
  if (originalEnqueue) queue.enqueue = originalEnqueue;
21990
22063
  if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
21991
22064
  if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
21992
- if (typeof rendition.off === 'function') {
21993
- rendition.off('touchstart', handleTouchStart);
21994
- rendition.off('touchmove', handleTouchMove);
21995
- rendition.off('touchend', handleTouchEnd);
21996
- }
21997
22065
  epubScrollDiagnostics = null;
21998
22066
  };
21999
22067
  }
22000
- applyEpubZoom();
22001
-
22002
22068
  const outline = await withEpubStageTimeout(
22003
22069
  buildOutline(),
22004
22070
  'outline',
@@ -22028,6 +22094,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22028
22094
  const request = ++epubNavigationRequest;
22029
22095
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22030
22096
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22097
+ epubResizeAnchor?.invalidate();
22031
22098
  await activeRendition.display(target);
22032
22099
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22033
22100
  currentPage = pageIndex + 1;
@@ -22136,7 +22203,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22136
22203
  };
22137
22204
 
22138
22205
  let nativeMenuSelection = null;
22139
- let epubViewMode = "continuous";
22206
+ let epubViewMode = "single";
22207
+ let epubLocation = null;
22140
22208
 
22141
22209
  const handleCommand = async (message) => {
22142
22210
  const { id, kind, payload } = message;
@@ -22149,7 +22217,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22149
22217
  epubAnnotations = [];
22150
22218
  annotationDocumentId = undefined;
22151
22219
  zoom = 1.0;
22152
- epubViewMode = "continuous";
22220
+ epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22221
+ epubLocation = null;
22222
+ nativeMenuSelection = null;
22153
22223
  epubNavigationRequest += 1;
22154
22224
  epubNavigationQueue = Promise.resolve();
22155
22225
 
@@ -22214,6 +22284,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22214
22284
  epubNavigationRequest += 1;
22215
22285
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22216
22286
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22287
+ epubResizeAnchor?.invalidate();
22217
22288
  await activeRendition.display(annotation.anchor.href);
22218
22289
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22219
22290
  const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
@@ -22255,6 +22326,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22255
22326
  const active = rendition;
22256
22327
  epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22257
22328
  if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22329
+ epubResizeAnchor?.invalidate();
22258
22330
  return payload.direction < 0 ? active.prev() : active.next();
22259
22331
  });
22260
22332
  await epubNavigationQueue;
@@ -22263,25 +22335,40 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22263
22335
 
22264
22336
  if (kind === 'set-epub-view-mode') {
22265
22337
  if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
22266
- epubViewMode = payload.viewMode === 'single' ? 'single' : 'continuous';
22267
- const active = rendition;
22268
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22269
- if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22270
- const flow = epubViewMode === 'single' ? 'paginated' : 'scrolled-continuous';
22271
- if (active.settings.flow === flow) return;
22272
- active.flow(flow);
22273
- return active.q.enqueue(() => Promise.resolve());
22274
- });
22275
- await epubNavigationQueue;
22276
- sendResponse(id,true,null); return;
22338
+ const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
22339
+ {
22340
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22341
+ if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
22342
+ const target=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22343
+ epubViewMode=next;
22344
+ await renderEpubRendition('layout',epubLoadGeneration,target);
22345
+ });
22346
+ await epubNavigationQueue;
22347
+ }
22348
+ sendResponse(id,true,null);return;
22349
+ }
22350
+ if(kind === 'epub-go-to-location'){
22351
+ if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22352
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});
22353
+ await epubNavigationQueue;sendResponse(id,true,null);return;
22277
22354
  }
22278
22355
 
22279
22356
  if (kind === 'set-zoom') {
22280
- zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
22281
- applyZoom();
22282
- sendState();
22283
- sendResponse(id, true, { zoom });
22284
- return;
22357
+ const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
22358
+ if(currentType === 'epub') {
22359
+ const session=documentSessionId;
22360
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22361
+ if(session!==documentSessionId)return;
22362
+ const active=rendition;
22363
+ const cfi=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22364
+ zoom=requestedZoom;applyEpubZoom();
22365
+ // Let the content CSS update before redisplaying the logical anchor.
22366
+ await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
22367
+ if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
22368
+ });
22369
+ await epubNavigationQueue;
22370
+ } else {zoom=requestedZoom;applyZoom();}
22371
+ sendState();sendResponse(id,true,{zoom});return;
22285
22372
  }
22286
22373
 
22287
22374
  if (kind === 'set-rotation') {
@@ -23457,6 +23544,7 @@ var IconToolStrikeout = ({
23457
23544
  ),
23458
23545
  /* @__PURE__ */ jsx2(Rect, { x: "10.9", y: "17.1", width: "14.2", height: "2.4", rx: "1.2", fill: color })
23459
23546
  ] });
23547
+ var IconStrikeout = ({ size = 22, color = defaultColor, strokeWidth = 1.8 }) => /* @__PURE__ */ jsx2(Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx2(Path, { d: "M17 5c-1-1-3-2-5-2-3 0-5 2-5 4 0 2 2 3 5 4M17 15c0 2-2 4-5 4-2 0-4-1-5-2M4 12h16", stroke: color, strokeWidth, strokeLinecap: "round", strokeLinejoin: "round" }) });
23460
23548
  var IconCopy = ({
23461
23549
  size = 20,
23462
23550
  color = defaultColor,
@@ -23521,6 +23609,39 @@ var IconToolNote = ({
23521
23609
  /* @__PURE__ */ jsx2(Line, { x1: "15", y1: "14.8", x2: "21.6", y2: "14.8", stroke: color, strokeWidth: "1.8", strokeLinecap: "round" }),
23522
23610
  /* @__PURE__ */ jsx2(Line, { x1: "15", y1: "18.2", x2: "19.2", y2: "18.2", stroke: color, strokeWidth: "1.8", strokeLinecap: "round" })
23523
23611
  ] });
23612
+ var IconHighlight = ({
23613
+ size = 24,
23614
+ color = "#fbbf24"
23615
+ }) => /* @__PURE__ */ jsxs(Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
23616
+ /* @__PURE__ */ jsx2(
23617
+ Path,
23618
+ {
23619
+ d: "M4 15h16v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3Z",
23620
+ fill: color,
23621
+ opacity: "0.85"
23622
+ }
23623
+ ),
23624
+ /* @__PURE__ */ jsx2(
23625
+ Path,
23626
+ {
23627
+ d: "M6 5h12v10H6V5Z",
23628
+ stroke: color,
23629
+ strokeWidth: "2",
23630
+ strokeLinecap: "round",
23631
+ strokeLinejoin: "round",
23632
+ fill: "none"
23633
+ }
23634
+ ),
23635
+ /* @__PURE__ */ jsx2(
23636
+ Path,
23637
+ {
23638
+ d: "M8 8h8M8 11h5",
23639
+ stroke: color,
23640
+ strokeWidth: "2",
23641
+ strokeLinecap: "round"
23642
+ }
23643
+ )
23644
+ ] });
23524
23645
  var IconUnderline = ({
23525
23646
  size = 24,
23526
23647
  color = "#60a5fa",
@@ -25731,6 +25852,39 @@ var styles = StyleSheet.create({
25731
25852
  var arePageRendererPropsEqual = (previous, next) => previous.documentId === next.documentId && previous.engine === next.engine && previous.pageIndex === next.pageIndex && previous.scale === next.scale && previous.PageViewComponent === next.PageViewComponent && previous.availableWidth === next.availableWidth && previous.maxPageWidth === next.maxPageWidth && previous.maxPageHeight === next.maxPageHeight && previous.pageViewportWidth === next.pageViewportWidth && previous.horizontalPadding === next.horizontalPadding && previous.spacing === next.spacing && previous.onSelectionDragActiveChange === next.onSelectionDragActiveChange && previous.gestureScrollLockActive === next.gestureScrollLockActive && previous.lastPinchEndedAt === next.lastPinchEndedAt && previous.requestSelectionVerticalAutoscroll === next.requestSelectionVerticalAutoscroll;
25732
25853
  var PageRenderer_default = memo(PageRenderer, arePageRendererPropsEqual);
25733
25854
 
25855
+ // components/PapyrusSafeArea.tsx
25856
+ import React3, { useContext as useContext2 } from "react";
25857
+ import {
25858
+ SafeAreaInsetsContext,
25859
+ SafeAreaProvider
25860
+ } from "react-native-safe-area-context";
25861
+ import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
25862
+ var papyrusSafeAreaContext = SafeAreaInsetsContext;
25863
+ var ZERO_SAFE_AREA_INSETS = {
25864
+ top: 0,
25865
+ bottom: 0,
25866
+ left: 0,
25867
+ right: 0
25868
+ };
25869
+ var usePapyrusSafeAreaInsets = () => useContext2(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
25870
+ function PapyrusSafeAreaBoundary({
25871
+ children
25872
+ }) {
25873
+ const insets = useContext2(papyrusSafeAreaContext);
25874
+ if (insets) return /* @__PURE__ */ jsx4(Fragment, { children });
25875
+ return React3.createElement(SafeAreaProvider, null, children);
25876
+ }
25877
+
25878
+ // components/epubReadingLocation.ts
25879
+ function parseEpubReadingLocation(raw) {
25880
+ if (!raw || typeof raw !== "object") return null;
25881
+ const p = raw;
25882
+ if (p.version !== 1 || typeof p.cfi !== "string" || p.cfi.length > 4096 || !/^epubcfi\([^\n<>]+\)$/.test(p.cfi) || typeof p.href !== "string" || !Number.isSafeInteger(p.chapter) || p.chapter < 1 || !Number.isSafeInteger(p.chapterCount) || p.chapterCount < p.chapter || !Number.isFinite(p.progress) || p.progress < 0 || p.progress > 1 || typeof p.atStart !== "boolean" || typeof p.atEnd !== "boolean") return null;
25883
+ if (p.mode === "chapter-scroll") return p.visualPage === null && p.visualPageCount === null ? p : null;
25884
+ if (p.mode !== "paged" || !Number.isSafeInteger(p.visualPage) || (p.visualPage ?? 0) < 1 || !Number.isSafeInteger(p.visualPageCount) || (p.visualPageCount ?? 0) < (p.visualPage ?? 0)) return null;
25885
+ return p;
25886
+ }
25887
+
25734
25888
  // components/WebViewViewer.tsx
25735
25889
  import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
25736
25890
  import { Image, StyleSheet as StyleSheet2, View as View2, Alert } from "react-native";
@@ -25800,7 +25954,7 @@ function parseEpubAnnotationEvent(raw, session, documentId) {
25800
25954
  }
25801
25955
 
25802
25956
  // components/WebViewViewer.tsx
25803
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
25957
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
25804
25958
  var runtimeAsset = require_runtime();
25805
25959
  var resolveRuntimeSource = (asset) => {
25806
25960
  if (typeof asset === "string") {
@@ -25820,6 +25974,7 @@ var resolveRuntimeSource = (asset) => {
25820
25974
  };
25821
25975
  var WebViewViewer = ({
25822
25976
  engine,
25977
+ documentType,
25823
25978
  documentId,
25824
25979
  onDefineSelection,
25825
25980
  defineSelectionMode,
@@ -25828,7 +25983,8 @@ var WebViewViewer = ({
25828
25983
  onTap
25829
25984
  }) => {
25830
25985
  const webViewRef = useRef2(null);
25831
- const { pageTheme, viewMode, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = useViewerStore2();
25986
+ const insets = usePapyrusSafeAreaInsets();
25987
+ const { pageTheme, epubReadingMode, setDocumentState, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = useViewerStore2();
25832
25988
  const [selection, setSelection] = useState2(null);
25833
25989
  const t = getStrings(locale);
25834
25990
  const bridgeEngine = engine;
@@ -25911,8 +26067,8 @@ var WebViewViewer = ({
25911
26067
  };
25912
26068
  useEffect2(() => {
25913
26069
  if (!isLoaded) return;
25914
- webViewRef.current?.postMessage(JSON.stringify({ id: "epub-layout", kind: "set-epub-view-mode", payload: { viewMode, documentSessionId: bridgeEngine.getWebViewDocumentSessionId?.() } }));
25915
- }, [isLoaded, viewMode, bridgeEngine]);
26070
+ webViewRef.current?.postMessage(JSON.stringify({ id: "epub-layout", kind: "set-epub-view-mode", payload: { epubMode: epubReadingMode, documentSessionId: bridgeEngine.getWebViewDocumentSessionId?.() } }));
26071
+ }, [isLoaded, epubReadingMode, bridgeEngine]);
25916
26072
  const sendSelectionCommand = (key) => {
25917
26073
  webViewRef.current?.postMessage(JSON.stringify({
25918
26074
  type: "epub-selection-action",
@@ -25936,6 +26092,11 @@ var WebViewViewer = ({
25936
26092
  let annotationRaw = raw;
25937
26093
  try {
25938
26094
  const message = JSON.parse(raw);
26095
+ if (message.type === "event" && message.name === "EPUB_LOCATION" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) {
26096
+ const location = parseEpubReadingLocation(message.payload.location);
26097
+ if (location) setDocumentState({ epubLocation: location });
26098
+ }
26099
+ if (message.type === "event" && message.name === "EPUB_CONTENT_TAP" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) onTap?.();
25939
26100
  if (message.type === "event" && message.name === "EPUB_SELECTION_ACTION") {
25940
26101
  action = message.payload?.action;
25941
26102
  annotationRaw = JSON.stringify({ ...message, name: "EPUB_TEXT_SELECTED" });
@@ -26024,8 +26185,8 @@ var WebViewViewer = ({
26024
26185
  maxWidth: resolvedMaxPageWidth,
26025
26186
  alignSelf: "center"
26026
26187
  } : void 0;
26027
- return /* @__PURE__ */ jsxs3(View2, { style: styles2.container, children: [
26028
- /* @__PURE__ */ jsx4(
26188
+ return /* @__PURE__ */ jsxs3(View2, { style: [styles2.container, documentType === "epub" && { paddingTop: insets.top, paddingBottom: insets.bottom }], children: [
26189
+ /* @__PURE__ */ jsx5(
26029
26190
  WebView,
26030
26191
  {
26031
26192
  ref: webViewRef,
@@ -26049,7 +26210,7 @@ var WebViewViewer = ({
26049
26210
  style: [styles2.webview, cappedWebViewStyle]
26050
26211
  }
26051
26212
  ),
26052
- /* @__PURE__ */ jsx4(
26213
+ /* @__PURE__ */ jsx5(
26053
26214
  View2,
26054
26215
  {
26055
26216
  pointerEvents: "none",
@@ -26151,16 +26312,16 @@ function getNearbyPdfNotes(annotations, id) {
26151
26312
  // components/PdfNoteChooser.tsx
26152
26313
  import { FlatList, Modal, Pressable as Pressable2, StyleSheet as StyleSheet3, Text, View as View3 } from "react-native";
26153
26314
  import { getAnnotationNote, getAnnotationQuote } from "@papyrus-sdk/core";
26154
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
26315
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
26155
26316
  function PdfNoteChooser({ notes, title, cancel, onSelect, onClose }) {
26156
- return /* @__PURE__ */ jsx5(Modal, { visible: notes.length > 1, transparent: true, animationType: "none", onRequestClose: onClose, children: /* @__PURE__ */ jsx5(View3, { style: styles3.backdrop, children: /* @__PURE__ */ jsxs4(View3, { style: styles3.sheet, accessibilityViewIsModal: true, children: [
26157
- /* @__PURE__ */ jsx5(Text, { style: styles3.title, children: title }),
26158
- /* @__PURE__ */ jsx5(FlatList, { data: notes, keyExtractor: (a) => a.id, renderItem: ({ item, index }) => /* @__PURE__ */ jsx5(Pressable2, { accessibilityRole: "button", style: styles3.row, onPress: () => onSelect(item.id), children: /* @__PURE__ */ jsxs4(Text, { children: [
26317
+ return /* @__PURE__ */ jsx6(Modal, { visible: notes.length > 1, transparent: true, animationType: "none", onRequestClose: onClose, children: /* @__PURE__ */ jsx6(View3, { style: styles3.backdrop, children: /* @__PURE__ */ jsxs4(View3, { style: styles3.sheet, accessibilityViewIsModal: true, children: [
26318
+ /* @__PURE__ */ jsx6(Text, { style: styles3.title, children: title }),
26319
+ /* @__PURE__ */ jsx6(FlatList, { data: notes, keyExtractor: (a) => a.id, renderItem: ({ item, index }) => /* @__PURE__ */ jsx6(Pressable2, { accessibilityRole: "button", style: styles3.row, onPress: () => onSelect(item.id), children: /* @__PURE__ */ jsxs4(Text, { children: [
26159
26320
  index + 1,
26160
26321
  ". ",
26161
26322
  getAnnotationNote(item) || getAnnotationQuote(item)
26162
26323
  ] }) }) }),
26163
- /* @__PURE__ */ jsx5(Pressable2, { accessibilityRole: "button", style: styles3.row, onPress: onClose, children: /* @__PURE__ */ jsx5(Text, { children: cancel }) })
26324
+ /* @__PURE__ */ jsx6(Pressable2, { accessibilityRole: "button", style: styles3.row, onPress: onClose, children: /* @__PURE__ */ jsx6(Text, { children: cancel }) })
26164
26325
  ] }) }) });
26165
26326
  }
26166
26327
  var styles3 = StyleSheet3.create({ backdrop: { flex: 1, backgroundColor: "#0008", justifyContent: "center", padding: 24 }, sheet: { backgroundColor: "#fff", borderRadius: 16, padding: 16, maxHeight: "75%" }, title: { fontSize: 18, fontWeight: "600", marginBottom: 12 }, row: { minHeight: 44, padding: 12 } });
@@ -26187,7 +26348,7 @@ var resolvePageTapChromeVisibility = ({
26187
26348
  };
26188
26349
 
26189
26350
  // components/DedicatedAndroidPdfViewer.tsx
26190
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
26351
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
26191
26352
  var TEXT_MARKUP_TOOLS2 = /* @__PURE__ */ new Set(["highlight", "underline", "squiggly", "strikeout"]);
26192
26353
  var MOBILE_CHROME_HIDE_DELTA = 28;
26193
26354
  var MOBILE_CHROME_SHOW_DELTA = 22;
@@ -26331,11 +26492,11 @@ function DedicatedAndroidPdfViewer({
26331
26492
  selectionRef.current = null;
26332
26493
  }, [addAnnotation, beginAnnotationDraft, annotationColor, documentId]);
26333
26494
  return /* @__PURE__ */ jsxs5(View4, { style: styles4.container, children: [
26334
- /* @__PURE__ */ jsx6(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26495
+ /* @__PURE__ */ jsx7(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26335
26496
  setPendingNotes([]);
26336
26497
  setSelectedAnnotation(id);
26337
26498
  } }),
26338
- /* @__PURE__ */ jsx6(
26499
+ /* @__PURE__ */ jsx7(
26339
26500
  PapyrusPdfDocumentView,
26340
26501
  {
26341
26502
  style: [styles4.viewer, cappedViewerStyle],
@@ -26546,7 +26707,7 @@ var resolveNativePdfVisiblePages = (pages) => (pages ?? []).flatMap((page) => {
26546
26707
  });
26547
26708
 
26548
26709
  // components/DedicatedIosPdfViewer.tsx
26549
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
26710
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
26550
26711
  var MOBILE_CHROME_HIDE_DELTA2 = 28;
26551
26712
  var MOBILE_CHROME_SHOW_DELTA2 = 22;
26552
26713
  var MOBILE_CHROME_SHOW_DELAY_MS2 = 180;
@@ -26785,11 +26946,11 @@ function DedicatedIosPdfViewer({
26785
26946
  alignSelf: "center"
26786
26947
  } : void 0;
26787
26948
  return /* @__PURE__ */ jsxs6(View5, { style: styles5.container, children: [
26788
- /* @__PURE__ */ jsx7(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26949
+ /* @__PURE__ */ jsx8(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26789
26950
  setPendingNotes([]);
26790
26951
  setSelectedAnnotation(id);
26791
26952
  } }),
26792
- /* @__PURE__ */ jsx7(
26953
+ /* @__PURE__ */ jsx8(
26793
26954
  PapyrusPdfDocumentView2,
26794
26955
  {
26795
26956
  style: [styles5.viewer, cappedViewerStyle],
@@ -26858,22 +27019,22 @@ function DedicatedIosPdfViewer({
26858
27019
  onInkToolPickerVisibilityChange: handleInkToolPickerVisibilityChange
26859
27020
  }
26860
27021
  ),
26861
- selection && !supportsNativeEditMenu && onDefineSelection && shouldShowDefineSelection(selection.text, defineSelectionMode) && /* @__PURE__ */ jsx7(View5, { style: styles5.selectionFallback, pointerEvents: "box-none", children: /* @__PURE__ */ jsx7(
27022
+ selection && !supportsNativeEditMenu && onDefineSelection && shouldShowDefineSelection(selection.text, defineSelectionMode) && /* @__PURE__ */ jsx8(View5, { style: styles5.selectionFallback, pointerEvents: "box-none", children: /* @__PURE__ */ jsx8(
26862
27023
  Pressable3,
26863
27024
  {
26864
27025
  onPress: defineSelection,
26865
27026
  style: styles5.fallbackButton,
26866
27027
  accessibilityRole: "button",
26867
27028
  accessibilityLabel: t.define,
26868
- children: /* @__PURE__ */ jsx7(Text2, { style: styles5.fallbackButtonText, children: t.define })
27029
+ children: /* @__PURE__ */ jsx8(Text2, { style: styles5.fallbackButtonText, children: t.define })
26869
27030
  }
26870
27031
  ) }),
26871
- fallbackDeleteAnnotation && /* @__PURE__ */ jsx7(
27032
+ fallbackDeleteAnnotation && /* @__PURE__ */ jsx8(
26872
27033
  View5,
26873
27034
  {
26874
27035
  style: styles5.annotationDeleteFallback,
26875
27036
  pointerEvents: "box-none",
26876
- children: /* @__PURE__ */ jsx7(
27037
+ children: /* @__PURE__ */ jsx8(
26877
27038
  Pressable3,
26878
27039
  {
26879
27040
  onPress: () => handleDeleteAnnotation(fallbackDeleteAnnotation.id),
@@ -26881,7 +27042,7 @@ function DedicatedIosPdfViewer({
26881
27042
  accessibilityRole: "button",
26882
27043
  accessibilityLabel: t.deleteAnnotation,
26883
27044
  hitSlop: 8,
26884
- children: /* @__PURE__ */ jsx7(Text2, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
27045
+ children: /* @__PURE__ */ jsx8(Text2, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
26885
27046
  }
26886
27047
  )
26887
27048
  }
@@ -26944,7 +27105,7 @@ var styles5 = StyleSheet5.create({
26944
27105
  });
26945
27106
 
26946
27107
  // components/NativePdfDocumentViewer.tsx
26947
- import { jsx as jsx8 } from "react/jsx-runtime";
27108
+ import { jsx as jsx9 } from "react/jsx-runtime";
26948
27109
  function NativePdfDocumentViewer({
26949
27110
  engine,
26950
27111
  documentId,
@@ -26957,7 +27118,7 @@ function NativePdfDocumentViewer({
26957
27118
  Platform3.OS
26958
27119
  );
26959
27120
  if (implementation === "android") {
26960
- return /* @__PURE__ */ jsx8(
27121
+ return /* @__PURE__ */ jsx9(
26961
27122
  DedicatedAndroidPdfViewer,
26962
27123
  {
26963
27124
  engine,
@@ -26970,7 +27131,7 @@ function NativePdfDocumentViewer({
26970
27131
  );
26971
27132
  }
26972
27133
  if (implementation === "ios") {
26973
- return /* @__PURE__ */ jsx8(
27134
+ return /* @__PURE__ */ jsx9(
26974
27135
  DedicatedIosPdfViewer,
26975
27136
  {
26976
27137
  engine,
@@ -27026,29 +27187,6 @@ var resolveMobileChromeOffsets = (insets) => ({
27026
27187
  right: MOBILE_CHROME_METRICS.screenPadding + Math.max(0, insets.right)
27027
27188
  });
27028
27189
 
27029
- // components/PapyrusSafeArea.tsx
27030
- import React6, { useContext as useContext2 } from "react";
27031
- import {
27032
- SafeAreaInsetsContext,
27033
- SafeAreaProvider
27034
- } from "react-native-safe-area-context";
27035
- import { Fragment, jsx as jsx9 } from "react/jsx-runtime";
27036
- var papyrusSafeAreaContext = SafeAreaInsetsContext;
27037
- var ZERO_SAFE_AREA_INSETS = {
27038
- top: 0,
27039
- bottom: 0,
27040
- left: 0,
27041
- right: 0
27042
- };
27043
- var usePapyrusSafeAreaInsets = () => useContext2(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
27044
- function PapyrusSafeAreaBoundary({
27045
- children
27046
- }) {
27047
- const insets = useContext2(papyrusSafeAreaContext);
27048
- if (insets) return /* @__PURE__ */ jsx9(Fragment, { children });
27049
- return React6.createElement(SafeAreaProvider, null, children);
27050
- }
27051
-
27052
27190
  // components/NativeTextDocumentViewer.tsx
27053
27191
  import { jsx as jsx10 } from "react/jsx-runtime";
27054
27192
  function NativeTextDocumentViewer({
@@ -27599,6 +27737,7 @@ var scrollViewerListToOffset = (list, offset, animated) => {
27599
27737
  list.scrollToOffset?.({ offset, animated });
27600
27738
  };
27601
27739
  var Viewer = ({
27740
+ documentType,
27602
27741
  documentId,
27603
27742
  engine,
27604
27743
  maxPageWidth,
@@ -29126,14 +29265,8 @@ var Viewer = ({
29126
29265
  },
29127
29266
  [mobilePerf, perfEnabled, scrollPerfSession]
29128
29267
  );
29129
- const handleWebViewScroll = useCallback4(
29130
- (_offsetY) => {
29131
- if (!chromeVisibleRef.current) {
29132
- setMobileChromeVisible(true, "scroll.webview.recover");
29133
- }
29134
- },
29135
- [setMobileChromeVisible]
29136
- );
29268
+ const handleWebViewScroll = useCallback4((_offsetY) => {
29269
+ }, []);
29137
29270
  const handlePageTap = useCallback4(() => {
29138
29271
  const nextVisible = resolvePageTapChromeVisibility({
29139
29272
  chromeVisible: chromeVisibleRef.current,
@@ -29355,6 +29488,7 @@ var Viewer = ({
29355
29488
  return /* @__PURE__ */ jsx12(View8, { style: [styles8.container, isDark && styles8.containerDark], children: /* @__PURE__ */ jsx12(
29356
29489
  WebViewViewer_default,
29357
29490
  {
29491
+ documentType,
29358
29492
  engine,
29359
29493
  documentId,
29360
29494
  onDefineSelection,
@@ -29925,6 +30059,7 @@ var styles9 = StyleSheet9.create({
29925
30059
  // components/PageJumpModal.tsx
29926
30060
  import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
29927
30061
  function PageJumpModal({
30062
+ documentType,
29928
30063
  visible,
29929
30064
  currentPage,
29930
30065
  pageCount,
@@ -29951,8 +30086,8 @@ function PageJumpModal({
29951
30086
  isDark,
29952
30087
  maxHeight: 300,
29953
30088
  showHeader: true,
29954
- title: "Ir para p\xE1gina",
29955
- closeAccessibilityLabel: "Close page jump",
30089
+ title: documentType === "epub" ? "Ir para cap\xEDtulo" : "Ir para p\xE1gina",
30090
+ closeAccessibilityLabel: "Fechar navega\xE7\xE3o",
29956
30091
  sheetStyle: {
29957
30092
  paddingHorizontal: 18,
29958
30093
  paddingBottom: 18,
@@ -29980,7 +30115,7 @@ function PageJumpModal({
29980
30115
  }
29981
30116
  ],
29982
30117
  onSubmitEditing: confirmJump,
29983
- accessibilityLabel: "Page jump input",
30118
+ accessibilityLabel: documentType === "epub" ? "N\xFAmero do cap\xEDtulo" : "N\xFAmero da p\xE1gina",
29984
30119
  testID: "papyrus-page-jump-input"
29985
30120
  }
29986
30121
  ),
@@ -30035,6 +30170,7 @@ var styles10 = StyleSheet10.create({
30035
30170
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
30036
30171
  var Topbar = ({
30037
30172
  engine,
30173
+ documentType,
30038
30174
  onOpenSettings,
30039
30175
  onOpenOverflow,
30040
30176
  title,
@@ -30055,6 +30191,12 @@ var Topbar = ({
30055
30191
  locale
30056
30192
  } = useViewerStore9();
30057
30193
  const inkSession = useNativeInkSession();
30194
+ const chromeVisible = mobileChromeVisible || inkSession.active;
30195
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
30196
+ const chromeMotion = useAnimatedStyle(() => {
30197
+ "worklet";
30198
+ return { opacity: chromeProgress.value };
30199
+ });
30058
30200
  const t = getStrings(locale);
30059
30201
  const progress = useNativeInkMotion(inkSession.active, inkSession.supported);
30060
30202
  const settingsMotion = useAnimatedStyle(() => {
@@ -30110,20 +30252,23 @@ var Topbar = ({
30110
30252
  children: /* @__PURE__ */ jsx15(IconSettings, { size: MOBILE_CHROME_METRICS.iconSize, color: isDark ? "#e5e7eb" : "#111827" })
30111
30253
  }
30112
30254
  );
30113
- if (!mobileChromeVisible && !inkSession.active) return null;
30255
+ if (!chromeVisible && documentType !== "epub") return null;
30114
30256
  return /* @__PURE__ */ jsxs10(Fragment2, { children: [
30115
30257
  /* @__PURE__ */ jsx15(
30116
- View11,
30258
+ Animated3.View,
30117
30259
  {
30118
30260
  style: [
30119
30261
  styles11.chromeFrame,
30262
+ chromeMotion,
30120
30263
  {
30121
30264
  paddingTop: insets.top,
30122
30265
  paddingLeft: MOBILE_CHROME_METRICS.screenPadding + insets.left,
30123
30266
  paddingRight: MOBILE_CHROME_METRICS.screenPadding + insets.right
30124
30267
  }
30125
30268
  ],
30126
- pointerEvents: "box-none",
30269
+ pointerEvents: chromeVisible ? "box-none" : "none",
30270
+ accessibilityElementsHidden: !chromeVisible,
30271
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
30127
30272
  testID: "papyrus-floating-top-controls",
30128
30273
  children: /* @__PURE__ */ jsxs10(
30129
30274
  View11,
@@ -30270,6 +30415,7 @@ var Topbar = ({
30270
30415
  /* @__PURE__ */ jsx15(
30271
30416
  PageJumpModal,
30272
30417
  {
30418
+ documentType,
30273
30419
  visible: jumpModalOpen,
30274
30420
  currentPage,
30275
30421
  pageCount,
@@ -32543,7 +32689,27 @@ var AnnotationEditor = ({ documentId }) => {
32543
32689
  editing ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
32544
32690
  /* @__PURE__ */ jsx18(TextInput2, { accessibilityLabel: t.editNote, value: draft, onChangeText: setDraft, multiline: true, autoFocus: true, placeholder: t.notePlaceholder, placeholderTextColor: dark ? "#9ca3af" : "#6b7280", style: [styles14.input, textStyle, { borderColor: dark ? "#374151" : "#d1d5db" }] }),
32545
32691
  /* @__PURE__ */ jsx18(View14, { style: styles14.options, children: COLORS.map((value) => /* @__PURE__ */ jsx18(Pressable9, { accessibilityRole: "button", accessibilityLabel: `${t.annotationColor} ${value}`, accessibilityState: { selected: color === value }, onPress: () => setColor(value), style: [styles14.color, { backgroundColor: value, borderColor: color === value ? accentColor : "transparent" }] }, value)) }),
32546
- /* @__PURE__ */ jsx18(View14, { style: styles14.options, children: [["highlight", t.annotationHighlight], ["underline", t.annotationUnderline], ["strikeout", t.annotationStrikeout], ["none", t.annotationIndicatorOnly]].map(([style, label]) => /* @__PURE__ */ jsx18(Pressable9, { onPress: () => setMarkup(style), accessibilityRole: "button", accessibilityState: { selected: markup === style }, style: [styles14.button, markup === style && { borderColor: accentColor, borderWidth: 1 }], children: /* @__PURE__ */ jsx18(Text8, { style: textStyle, children: label }) }, style)) })
32692
+ /* @__PURE__ */ jsx18(View14, { style: styles14.markupOptions, children: [
32693
+ ["highlight", t.annotationHighlight, IconHighlight],
32694
+ ["underline", t.annotationUnderline, IconUnderline],
32695
+ ["strikeout", t.annotationStrikeout, IconStrikeout],
32696
+ ["none", t.annotationIndicatorOnly, IconComment]
32697
+ ].map(([style, label, Icon]) => /* @__PURE__ */ jsxs13(
32698
+ Pressable9,
32699
+ {
32700
+ onPress: () => setMarkup(style),
32701
+ accessibilityRole: "button",
32702
+ accessibilityLabel: label,
32703
+ accessibilityState: { selected: markup === style },
32704
+ testID: `annotation-style-${style}`,
32705
+ style: [styles14.markupButton, { backgroundColor: dark ? "#242936" : "#f1f5f9", borderColor: markup === style ? accentColor : "transparent" }],
32706
+ children: [
32707
+ /* @__PURE__ */ jsx18(View14, { accessible: false, importantForAccessibility: "no-hide-descendants", children: /* @__PURE__ */ jsx18(Icon, { size: 22, color: markup === style ? accentColor : textStyle.color }) }),
32708
+ /* @__PURE__ */ jsx18(Text8, { style: [styles14.markupLabel, textStyle], children: style === "none" ? locale === "pt-BR" ? "Indicador" : "Indicator" : label })
32709
+ ]
32710
+ },
32711
+ style
32712
+ )) })
32547
32713
  ] }) : /* @__PURE__ */ jsx18(Text8, { style: [styles14.note, textStyle], selectable: true, children: getAnnotationNote2(annotation) || t.notePlaceholder })
32548
32714
  ] }),
32549
32715
  /* @__PURE__ */ jsxs13(View14, { style: styles14.actions, children: [
@@ -32566,7 +32732,7 @@ var AnnotationEditor = ({ documentId }) => {
32566
32732
  ] })
32567
32733
  ] }) });
32568
32734
  };
32569
- var styles14 = StyleSheet14.create({ host: { flex: 1, justifyContent: "flex-end", backgroundColor: "rgba(0,0,0,.18)", paddingHorizontal: 12 }, card: { borderRadius: 20, padding: 16, maxHeight: "72%", elevation: 12 }, header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, title: { fontSize: 17, fontWeight: "700" }, content: { flexGrow: 0 }, quote: { fontSize: 14, lineHeight: 21, borderLeftWidth: 3, borderLeftColor: "#9ca3af", paddingLeft: 10, marginVertical: 12 }, note: { fontSize: 16, lineHeight: 24, paddingVertical: 12 }, input: { minHeight: 112, maxHeight: 240, borderWidth: 1, borderRadius: 12, padding: 12, fontSize: 16, textAlignVertical: "top" }, button: { minWidth: 44, minHeight: 44, paddingHorizontal: 10, borderRadius: 10, alignItems: "center", justifyContent: "center" }, actions: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", marginTop: 12 }, options: { flexDirection: "row", flexWrap: "wrap", gap: 8, marginTop: 12 }, color: { width: 44, height: 44, borderRadius: 22, borderWidth: 3 } });
32735
+ var styles14 = StyleSheet14.create({ host: { flex: 1, justifyContent: "flex-end", backgroundColor: "rgba(0,0,0,.18)", paddingHorizontal: 12 }, card: { borderRadius: 20, padding: 16, maxHeight: "72%", elevation: 12 }, header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, title: { fontSize: 17, fontWeight: "700" }, content: { flexGrow: 0 }, quote: { fontSize: 14, lineHeight: 21, borderLeftWidth: 3, borderLeftColor: "#9ca3af", paddingLeft: 10, marginVertical: 12 }, note: { fontSize: 16, lineHeight: 24, paddingVertical: 12 }, input: { minHeight: 112, maxHeight: 240, borderWidth: 1, borderRadius: 12, padding: 12, fontSize: 16, textAlignVertical: "top" }, button: { minWidth: 44, minHeight: 44, paddingHorizontal: 10, borderRadius: 10, alignItems: "center", justifyContent: "center" }, actions: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", marginTop: 12 }, markupOptions: { flexDirection: "row", flexWrap: "wrap", gap: 8, marginTop: 12 }, markupButton: { flex: 1, minWidth: 64, minHeight: 64, borderWidth: 1, borderRadius: 12, paddingHorizontal: 4, paddingVertical: 8, alignItems: "center", justifyContent: "center", gap: 6 }, markupLabel: { fontSize: 11, lineHeight: 15, textAlign: "center" }, options: { flexDirection: "row", flexWrap: "wrap", gap: 8, marginTop: 12 }, color: { width: 44, height: 44, borderRadius: 22, borderWidth: 3 } });
32570
32736
  var AnnotationEditor_default = AnnotationEditor;
32571
32737
 
32572
32738
  // components/BottomBar.tsx
@@ -32637,6 +32803,8 @@ var BottomBar = ({
32637
32803
  const {
32638
32804
  activeMobileDestination,
32639
32805
  viewMode,
32806
+ epubReadingMode,
32807
+ epubLocation,
32640
32808
  comicLayoutMode,
32641
32809
  currentPage,
32642
32810
  pageCount,
@@ -32654,7 +32822,7 @@ var BottomBar = ({
32654
32822
  const supportsInkChrome = documentType === "pdf" && inkSession.supported;
32655
32823
  const drawing = supportsInkChrome && inkSession.active;
32656
32824
  const dockVisible = mobileChromeVisible && mobileDockVisible && !drawing;
32657
- const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome);
32825
+ const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome || documentType === "epub");
32658
32826
  const dockMotion = useAnimatedStyle2(() => {
32659
32827
  "worklet";
32660
32828
  return {
@@ -32676,7 +32844,7 @@ var BottomBar = ({
32676
32844
  activeMobileDestination,
32677
32845
  toolDockOpen
32678
32846
  });
32679
- const paginated = documentType !== "text" && (documentType === "comic" ? comicLayoutMode === "single" : viewMode === "single");
32847
+ const paginated = documentType !== "text" && (documentType === "comic" ? comicLayoutMode === "single" : documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single");
32680
32848
  if (paginated) {
32681
32849
  layout.leftSlots.push(...layout.rightSlots.filter((slot) => slot.key === "search"));
32682
32850
  layout.rightSlots = [];
@@ -32745,7 +32913,7 @@ var BottomBar = ({
32745
32913
  onPress: onOpenSettings
32746
32914
  }
32747
32915
  };
32748
- if (!dockVisible && !supportsInkChrome) return null;
32916
+ if (!dockVisible && !supportsInkChrome && documentType !== "epub") return null;
32749
32917
  return /* @__PURE__ */ jsx19(
32750
32918
  Animated5.View,
32751
32919
  {
@@ -32811,13 +32979,9 @@ var BottomBar = ({
32811
32979
  ],
32812
32980
  testID: "papyrus-floating-bottom-dock",
32813
32981
  children: paginated ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
32814
- /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "P\xE1gina anterior" : "Previous page", disabled: !dockVisible || !onTurnPage && currentPage <= 1, onPress: () => turnPage(-1), style: styles15.iconOnlyItem, children: /* @__PURE__ */ jsx19(IconChevronLeft, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) }),
32815
- /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Ir para p\xE1gina" : "Go to page", onPress: onOpenPages, style: [styles15.iconOnlyItem, { minWidth: 56 }], children: /* @__PURE__ */ jsxs14(Text9, { style: { color: iconColor(false), fontWeight: "700", fontSize: 13, lineHeight: 18, includeFontPadding: false }, children: [
32816
- currentPage,
32817
- "/",
32818
- pageCount
32819
- ] }) }),
32820
- /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Pr\xF3xima p\xE1gina" : "Next page", disabled: !dockVisible || !onTurnPage && currentPage >= pageCount, onPress: () => turnPage(1), style: styles15.iconOnlyItem, children: /* @__PURE__ */ jsx19(IconChevronRight, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) })
32982
+ /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "P\xE1gina anterior" : "Previous page", disabled: !dockVisible || (documentType === "epub" ? epubLocation?.atStart : !onTurnPage && currentPage <= 1), onPress: () => turnPage(-1), style: styles15.iconOnlyItem, children: /* @__PURE__ */ jsx19(IconChevronLeft, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) }),
32983
+ /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Ir para p\xE1gina" : "Go to page", onPress: onOpenPages, style: [styles15.iconOnlyItem, { minWidth: 56 }], children: /* @__PURE__ */ jsx19(Text9, { style: { color: iconColor(false), fontWeight: "700", fontSize: 13, lineHeight: 18, includeFontPadding: false }, children: documentType === "epub" ? epubLocation?.visualPage ? `Cap. ${epubLocation.chapter} \xB7 ${epubLocation.visualPage}/${epubLocation.visualPageCount}` : `Cap. ${currentPage}` : `${currentPage}/${pageCount}` }) }),
32984
+ /* @__PURE__ */ jsx19(Pressable10, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Pr\xF3xima p\xE1gina" : "Next page", disabled: !dockVisible || (documentType === "epub" ? epubLocation?.atEnd : !onTurnPage && currentPage >= pageCount), onPress: () => turnPage(1), style: styles15.iconOnlyItem, children: /* @__PURE__ */ jsx19(IconChevronRight, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) })
32821
32985
  ] }) : layout.rightSlots.map((slot) => {
32822
32986
  const meta = slotMeta[slot.key];
32823
32987
  const Icon = meta.icon;
@@ -32999,6 +33163,7 @@ var SettingsSheet = ({
32999
33163
  }) => {
33000
33164
  const {
33001
33165
  viewMode,
33166
+ epubReadingMode,
33002
33167
  uiTheme,
33003
33168
  zoom,
33004
33169
  setDocumentState,
@@ -33010,12 +33175,16 @@ var SettingsSheet = ({
33010
33175
  comicReadingDirection
33011
33176
  } = useViewerStore14();
33012
33177
  const isDark = uiTheme === "dark";
33013
- const isPaged = viewMode === "single";
33178
+ const isPaged = documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single";
33014
33179
  const isDouble = viewMode === "double";
33015
33180
  const t = getStrings(locale);
33016
33181
  const { height: windowHeight } = useWindowDimensions8();
33017
33182
  const sheetMaxHeight = getSettingsSheetMaxHeight(windowHeight);
33018
33183
  const handleTransition = (mode) => {
33184
+ if (documentType === "epub") {
33185
+ setDocumentState({ epubReadingMode: mode === "paged" ? "paged" : "chapter-scroll" });
33186
+ return;
33187
+ }
33019
33188
  if (mode === "paged") {
33020
33189
  setDocumentState({ viewMode: "single" });
33021
33190
  return;
@@ -33239,7 +33408,7 @@ var SettingsSheet = ({
33239
33408
  isDark && styles16.optionTextDark,
33240
33409
  (!isPaged || isDouble) && styles16.optionTextActive
33241
33410
  ],
33242
- children: t.continuous
33411
+ children: documentType === "epub" && locale === "pt-BR" ? "Rolagem por cap\xEDtulo" : t.continuous
33243
33412
  }
33244
33413
  )
33245
33414
  }
@@ -33269,7 +33438,7 @@ var SettingsSheet = ({
33269
33438
  )
33270
33439
  ] })
33271
33440
  ] }),
33272
- documentType !== "comic" && documentType !== "text" && /* @__PURE__ */ jsxs15(View16, { style: styles16.section, children: [
33441
+ documentType === "pdf" && /* @__PURE__ */ jsxs15(View16, { style: styles16.section, children: [
33273
33442
  /* @__PURE__ */ jsx20(
33274
33443
  Text10,
33275
33444
  {
@@ -34162,6 +34331,7 @@ import {
34162
34331
  View as View21,
34163
34332
  useWindowDimensions as useWindowDimensions9
34164
34333
  } from "react-native";
34334
+ import Reanimated, { useAnimatedStyle as useAnimatedStyle3 } from "react-native-reanimated";
34165
34335
  import { useViewerStore as useViewerStore18 } from "@papyrus-sdk/core";
34166
34336
 
34167
34337
  // components/progressPillMode.ts
@@ -34195,12 +34365,20 @@ function ProgressPill({
34195
34365
  textLength,
34196
34366
  comicLayoutMode,
34197
34367
  viewMode,
34368
+ epubReadingMode,
34369
+ epubLocation,
34198
34370
  locale,
34199
34371
  uiTheme,
34200
34372
  accentColor,
34201
34373
  mobileChromeVisible,
34202
34374
  mobileProgressPillVisible
34203
34375
  } = useViewerStore18();
34376
+ const chromeVisible = mobileChromeVisible && mobileProgressPillVisible;
34377
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
34378
+ const chromeMotion = useAnimatedStyle3(() => {
34379
+ "worklet";
34380
+ return { opacity: chromeProgress.value };
34381
+ });
34204
34382
  const t = getStrings(locale);
34205
34383
  const isDark = uiTheme === "dark";
34206
34384
  const offsets = resolveMobileChromeOffsets(usePapyrusSafeAreaInsets());
@@ -34245,10 +34423,10 @@ function ProgressPill({
34245
34423
  return `${displayedPage}/${pageCount || 0}`;
34246
34424
  }
34247
34425
  if (documentType === "epub") {
34248
- return `Cap. ${displayedPage} \xB7 ${percent}%`;
34426
+ return `Cap. ${displayedPage} \xB7 ${scrubPreviewPage === null && epubLocation ? clampPercent(epubLocation.progress * 100) : percent}%`;
34249
34427
  }
34250
34428
  return `${percent}%`;
34251
- }, [displayedPage, documentType, pageCount]);
34429
+ }, [displayedPage, documentType, pageCount, epubLocation, scrubPreviewPage]);
34252
34430
  const thumbTop = resolvePageScrubberThumbTop({
34253
34431
  currentPage,
34254
34432
  trackHeight,
@@ -34290,7 +34468,7 @@ function ProgressPill({
34290
34468
  },
34291
34469
  []
34292
34470
  );
34293
- if (!shouldRenderPageScrubber({
34471
+ if (!(documentType === "epub" && mobileProgressPillVisible) && !shouldRenderPageScrubber({
34294
34472
  mobileChromeVisible,
34295
34473
  mobileProgressPillVisible,
34296
34474
  isScrubbing: isScrubbingRef.current
@@ -34321,7 +34499,7 @@ function ProgressPill({
34321
34499
  }
34322
34500
  );
34323
34501
  }
34324
- const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : viewMode;
34502
+ const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : documentType === "epub" ? epubReadingMode === "paged" ? "single" : "continuous" : viewMode;
34325
34503
  const pillMode = resolveProgressPillMode(progressViewMode, currentPage, pageCount);
34326
34504
  if (pillMode.kind === "navigation" && navigationInDock) return null;
34327
34505
  if (pillMode.kind === "navigation") {
@@ -34512,11 +34690,14 @@ function ProgressPill({
34512
34690
  }
34513
34691
  const responderPanHandlers = scrubberResponderRef.current?.panHandlers ?? {};
34514
34692
  return /* @__PURE__ */ jsx25(
34515
- View21,
34693
+ Reanimated.View,
34516
34694
  {
34517
- pointerEvents: "box-none",
34695
+ pointerEvents: chromeVisible ? "box-none" : "none",
34696
+ accessibilityElementsHidden: !chromeVisible,
34697
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
34518
34698
  style: [
34519
34699
  styles21.frame,
34700
+ chromeMotion,
34520
34701
  resolvePageScrubberOverlayStyle(),
34521
34702
  {
34522
34703
  top: offsets.progress,
@@ -35253,6 +35434,7 @@ function ReadingShell({
35253
35434
  /* @__PURE__ */ jsx28(
35254
35435
  Topbar_default,
35255
35436
  {
35437
+ documentType,
35256
35438
  engine,
35257
35439
  title,
35258
35440
  onOpenSettings: () => openDestination("display"),
@@ -35263,6 +35445,7 @@ function ReadingShell({
35263
35445
  /* @__PURE__ */ jsx28(View23, { style: styles24.viewerStage, children: /* @__PURE__ */ jsx28(
35264
35446
  Viewer_default,
35265
35447
  {
35448
+ documentType,
35266
35449
  engine,
35267
35450
  ...viewerProps,
35268
35451
  pageScrubActive
@@ -35387,6 +35570,7 @@ function ReadingShell({
35387
35570
  /* @__PURE__ */ jsx28(
35388
35571
  PageJumpModal,
35389
35572
  {
35573
+ documentType,
35390
35574
  visible: pageJumpOpen,
35391
35575
  currentPage,
35392
35576
  pageCount,