@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.js CHANGED
@@ -20517,6 +20517,67 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20517
20517
  <!-- @papyrus-comic-runtime:end -->
20518
20518
  <script>
20519
20519
  (function () {
20520
+ /* @papyrus-epub-reader:start */
20521
+ (function(root){
20522
+ function mode(value){return value==='chapter-scroll'?'chapter-scroll':'paged';}
20523
+ function validCfi(value){return typeof value==='string' && value.length<=4096 && /^epubcfi\\([^\\n<>]+\\)$/.test(value);}
20524
+ function options(value){return {manager:'default',flow:mode(value)==='paged'?'paginated':'scrolled-doc',spread:'none',width:'100%',height:'100%',allowScriptedContent:false};}
20525
+ function gesture(start,end,state){
20526
+ if(!start || !end || state.selected || state.interactive || state.multitouch)return null;
20527
+ var dx=end.x-start.x,dy=end.y-start.y,elapsed=end.time-start.time;
20528
+ 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';
20529
+ if(elapsed>=0 && elapsed<450 && Math.hypot(dx,dy)<=12)return 'tap';
20530
+ return null;
20531
+ }
20532
+ function location(raw,count,readingMode,geometry){
20533
+ var start=raw && raw.start;if(!start || !validCfi(start.cfi))return null;
20534
+ var displayed=start.displayed||{},chapter=Math.max(1,(Number(start.index)||0)+1);
20535
+ var visualPage=mode(readingMode)==='paged' && Number.isFinite(displayed.page)?displayed.page:null;
20536
+ var total=mode(readingMode)==='paged' && Number.isFinite(displayed.total)?displayed.total:null;
20537
+ var atStart=Boolean(raw.atStart),atEnd=Boolean(raw.atEnd);
20538
+ // Chromium may round a column offset down by a fraction of a CSS pixel.
20539
+ // Keep epub.js authoritative outside a 1px boundary, and for RTL layouts.
20540
+ if(visualPage!==null && total>0 && geometry && geometry.direction==='ltr' && geometry.pageWidth>0 && Number.isFinite(geometry.offset)){
20541
+ var boundary=Math.round(geometry.offset/geometry.pageWidth);
20542
+ if(Math.abs(geometry.offset-boundary*geometry.pageWidth)<=1){
20543
+ visualPage=Math.min(total,Math.max(1,boundary+1));
20544
+ atStart=chapter===(geometry.firstIndex ?? 0)+1 && visualPage===1;
20545
+ atEnd=chapter===(geometry.lastIndex ?? count-1)+1 && visualPage===total;
20546
+ }
20547
+ }
20548
+ var fraction=total?Math.max(0,(visualPage-1)/total):0;
20549
+ 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};
20550
+ }
20551
+ function installResizeAnchor(manager,readCfi){
20552
+ var original=manager.resize,anchor=null;
20553
+ function resize(width,height,target){
20554
+ if(validCfi(target))anchor=target;
20555
+ if(!anchor){var current=readCfi();if(validCfi(current))anchor=current;}
20556
+ // epub.js accepts a logical target as its third argument. Keep it across
20557
+ // keyboard/dialog reflows instead of adopting each new viewport start.
20558
+ return original.call(this,width,height,anchor || target);
20559
+ }
20560
+ manager.resize=resize;
20561
+ return {cfi:function(){return anchor;},invalidate:function(){anchor=null;},destroy:function(){if(manager.resize===resize)manager.resize=original;anchor=null;}};
20562
+ }
20563
+ function install(contents,config){
20564
+ var doc=contents.document,win=contents.window,start=null,blocked=false;
20565
+ 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;}
20566
+ function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
20567
+ function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
20568
+ function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
20569
+ 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();}
20570
+ function move(e){if(e.touches.length!==1)blocked=true;invalidateScroll(point(e));}
20571
+ 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);}
20572
+ function cancel(){start=null;blocked=true;}
20573
+ function link(e){if(config.current() && e.target?.closest?.('a') && config.interact)config.interact();}
20574
+ 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);
20575
+ return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);doc.removeEventListener('click',link);};
20576
+ }
20577
+ root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,installResizeAnchor:installResizeAnchor,install:install};
20578
+ })(typeof window==='undefined'?globalThis:window);
20579
+
20580
+ /* @papyrus-epub-reader:end */
20520
20581
  /* @papyrus-epub-annotations:start */
20521
20582
  (function (root, factory) {
20522
20583
  if (typeof module === 'object' && module.exports) module.exports = factory();
@@ -20595,6 +20656,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20595
20656
  var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
20596
20657
  var style = annotation.markupStyle || annotation.type;
20597
20658
  var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
20659
+ if(style === 'highlight')opacity=Math.min(0.35,opacity);
20598
20660
  rects.forEach(function(rect) {
20599
20661
  var mark = doc.createElement('div');
20600
20662
  var bodyRect=doc.body.getBoundingClientRect();
@@ -20607,8 +20669,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20607
20669
  if (hasNote) {
20608
20670
  var last = rects[rects.length-1], button = doc.createElement('button');
20609
20671
  button.textContent = '\u25CF'; button.setAttribute('aria-label',options.labels().comment || '');
20610
- 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';
20611
- var key = Math.floor(last.top/44);
20672
+ var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
20673
+ var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
20674
+ 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';
20675
+ var key = column+":"+Math.floor(last.top/44);
20612
20676
  var neighbors=noteGroups.get(key);
20613
20677
  if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
20614
20678
  neighbors.ids.push(annotation.id);
@@ -20629,10 +20693,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20629
20693
  doc.addEventListener('click',tapMarkup);
20630
20694
  var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20631
20695
  if (observer) observer.observe(doc.body);
20632
- win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
20696
+ win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
20633
20697
  if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
20634
20698
  schedule();
20635
- 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();}};
20699
+ 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();}};
20636
20700
  }
20637
20701
  return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20638
20702
  });
@@ -20645,6 +20709,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20645
20709
  let currentType = null;
20646
20710
  let book = null;
20647
20711
  let rendition = null;
20712
+ let epubResizeAnchor = null;
20648
20713
  let documentSessionId = null;
20649
20714
  let annotationDocumentId;
20650
20715
  let annotationLabels = {};
@@ -21057,6 +21122,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21057
21122
  };
21058
21123
 
21059
21124
  const clearViewer = () => {
21125
+ if(epubResizeAnchor){epubResizeAnchor.destroy();epubResizeAnchor=null;}
21060
21126
  epubAnnotationRenderers.forEach(renderer => renderer.destroy());
21061
21127
  epubAnnotationRenderers.clear();
21062
21128
  if (epubInteractionCleanup) {
@@ -21713,26 +21779,57 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21713
21779
  pageCount,
21714
21780
  });
21715
21781
 
21782
+ if (book.locations?.generate) {
21783
+ const activeBook=book;
21784
+ activeBook.locations.generate(150).then(() => {
21785
+ if (isCurrentLoad() && activeBook===book && epubLocation) {
21786
+ epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
21787
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21788
+ }
21789
+ }).catch(() => {});
21790
+ }
21791
+ return renderEpubRendition(loadId,generation,null);
21792
+ };
21793
+
21794
+ const renderEpubRendition = async (loadId,generation,target) => {
21795
+ const diagnosticPayload={loadId};
21796
+ const isCurrentLoad=()=>generation===epubLoadGeneration;
21797
+ if(epubInteractionCleanup)epubInteractionCleanup();
21798
+ if(rendition)rendition.destroy();
21716
21799
  clearViewer();
21717
- rendition = book.renderTo(viewer, {
21718
- manager: 'continuous',
21719
- allowScriptedContent: false,
21720
- width: '100%',
21721
- height: '100%',
21722
- flow: 'scrolled-continuous',
21723
- spread: 'none',
21724
- });
21800
+ rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
21725
21801
  sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
21726
21802
 
21727
21803
  if (rendition && rendition.hooks && rendition.hooks.content) {
21728
21804
  const session = documentSessionId;
21729
21805
  const activeRendition = rendition;
21730
21806
  rendition.hooks.content.register((contents) => {
21807
+ const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
21808
+ mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
21809
+ rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
21810
+ blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
21811
+ annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
21812
+ current: () => session === documentSessionId && activeRendition === rendition,
21813
+ interact: () => epubResizeAnchor?.invalidate(),
21814
+ tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
21815
+ turn: direction => {
21816
+ epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
21817
+ if(session !== documentSessionId || activeRendition !== rendition)return;
21818
+ epubResizeAnchor?.invalidate();
21819
+ return direction<0?activeRendition.prev():activeRendition.next();
21820
+ });
21821
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
21822
+ },
21823
+ });
21824
+ const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
21825
+ epubSelectionCleanups.add(gestureCleanup);
21826
+ contents.__papyrusGestureCleanup=gestureCleanup;
21731
21827
  const href = spineItems[contents.sectionIndex]?.href;
21732
21828
  if (href && window.PapyrusEpubAnnotations) {
21733
21829
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21734
21830
  href, get documentId() { return annotationDocumentId; },
21735
21831
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21832
+ pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21736
21833
  annotations: () => epubAnnotations, labels: () => annotationLabels,
21737
21834
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21738
21835
  });
@@ -21751,8 +21848,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21751
21848
  typeof selectionWindow.getSelection === 'function'
21752
21849
  ) {
21753
21850
  let selectionTimer = null;
21754
- let lastSelectionKey = '';
21755
- let lastSelectionAt = 0;
21851
+
21756
21852
  const reportSelection = () => {
21757
21853
  selectionTimer = null;
21758
21854
  if (session !== documentSessionId || activeRendition !== rendition) return;
@@ -21767,11 +21863,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21767
21863
  try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
21768
21864
  const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
21769
21865
  if (!anchor) return;
21770
- const key = anchor.cfiRange + ':' + anchor.quote;
21771
- const now = Date.now();
21772
- if (key === lastSelectionKey && now - lastSelectionAt < 750) return;
21773
- lastSelectionKey = key;
21774
- lastSelectionAt = now;
21866
+ if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21775
21867
  const payload = {documentSessionId: session, text: anchor.quote,
21776
21868
  pageIndex: contents.sectionIndex + 1, anchor};
21777
21869
  nativeMenuSelection = {contents, payload};
@@ -21786,6 +21878,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21786
21878
  // The content document still emits selectionchange, so resolve its
21787
21879
  // live Range to a CFI before the native menu action can fire.
21788
21880
  if (text) reportSelection();
21881
+ else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
21789
21882
  };
21790
21883
  selectionDocument.addEventListener('selectionchange', handleSelectionChange);
21791
21884
  const selectionCleanup = () => {
@@ -21808,6 +21901,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21808
21901
  typeof rendition.hooks.unloaded.register === 'function'
21809
21902
  ) {
21810
21903
  rendition.hooks.unloaded.register((view) => {
21904
+ view?.contents?.__papyrusGestureCleanup?.();
21811
21905
  const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
21812
21906
  if (typeof cleanup === 'function') cleanup();
21813
21907
  const renderer = epubAnnotationRenderers.get(view?.contents);
@@ -21823,13 +21917,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21823
21917
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21824
21918
  const href = spineItems[contents.sectionIndex]?.href;
21825
21919
  const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
21826
- if (!anchor) return;
21920
+ if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21827
21921
  epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
21828
21922
  const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
21829
21923
  nativeMenuSelection = {contents, payload};
21830
21924
  sendEvent('EPUB_TEXT_SELECTED', payload);
21831
21925
  });
21832
- ['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
21926
+ ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21833
21927
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
21834
21928
  }));
21835
21929
  rendition.on('relocated', (location) => {
@@ -21839,6 +21933,21 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21839
21933
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
21840
21934
  if (index < 0 || index >= spineItems.length) return;
21841
21935
  epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
21936
+ let pageGeometry;
21937
+ const manager=rendition.manager,layout=manager?.layout;
21938
+ if(epubViewMode==='single' && layout?.divisor===1 && !manager?.settings?.fullsize && manager?.settings?.direction==='ltr'){
21939
+ const visible=manager.visible?.();
21940
+ const view=visible?.find(view=>view.index===index);
21941
+ if(view && manager.container){
21942
+ pageGeometry={direction:'ltr',firstIndex:book.spine.first()?.index,lastIndex:book.spine.last()?.index,pageWidth:layout.pageWidth,offset:manager.container.getBoundingClientRect().left-view.position().left};
21943
+ }
21944
+ }
21945
+ epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll',pageGeometry);
21946
+ if(epubLocation){
21947
+ epubLocation.cfi=epubResizeAnchor?.cfi() || epubLocation.cfi;
21948
+ if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
21949
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21950
+ }
21842
21951
  const nextPage = index + 1;
21843
21952
  if (nextPage === currentPage) return;
21844
21953
  currentPage = nextPage;
@@ -21846,21 +21955,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21846
21955
  });
21847
21956
  }
21848
21957
 
21958
+ applyEpubZoom();
21849
21959
  sendEpubDiagnostic('epub.display.start', diagnosticPayload);
21850
21960
  await withEpubStageTimeout(
21851
- displayEpubPage(0),
21961
+ rendition.display(target || spineItems[0]?.href),
21852
21962
  'rendition.display',
21853
21963
  diagnosticPayload,
21854
21964
  );
21855
21965
  if (!isCurrentLoad()) throw new Error('EPUB load superseded by a newer load');
21856
21966
  sendEpubDiagnostic('epub.display.end', diagnosticPayload);
21967
+ const resizeRendition=rendition;
21968
+ epubResizeAnchor=window.PapyrusEpubReader.installResizeAnchor(resizeRendition.manager,()=>resizeRendition.location?.start?.cfi);
21857
21969
  if (
21858
21970
  rendition &&
21859
21971
  rendition.manager &&
21860
21972
  typeof rendition.manager.on === 'function' &&
21861
21973
  typeof rendition.on === 'function'
21862
21974
  ) {
21863
- let touchStart = null;
21864
21975
  let lastScrollOffset = -1;
21865
21976
  const manager = rendition.manager;
21866
21977
  epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
@@ -21935,57 +22046,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21935
22046
  return result;
21936
22047
  };
21937
22048
  }
21938
- const getTouchPoint = (event) => {
21939
- const touch =
21940
- (event && event.changedTouches && event.changedTouches[0]) ||
21941
- (event && event.touches && event.touches[0]);
21942
- if (!touch) return null;
21943
- const x = Number(touch.screenX ?? touch.clientX);
21944
- const y = Number(touch.screenY ?? touch.clientY);
21945
- return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
21946
- };
21947
- const handleScroll = (event) => {
22049
+ const handleScroll = event => {
21948
22050
  const offsetY = Math.max(0, Number(event && event.top) || 0);
21949
22051
  epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
21950
- if (Math.abs(offsetY - lastScrollOffset) < 1) return;
21951
- lastScrollOffset = offsetY;
21952
- sendEvent('VIEWER_SCROLL', { offsetY });
22052
+ if (Math.abs(offsetY-lastScrollOffset)<1) return;
22053
+ lastScrollOffset=offsetY;
22054
+ sendEvent('VIEWER_SCROLL',{offsetY});
21953
22055
  };
21954
- const handleTouchStart = (event) => {
21955
- touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
21956
- epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
21957
- };
21958
- const handleTouchMove = (event) => {
21959
- if (event?.touches?.length > 1) touchStart = null;
21960
- epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
21961
- };
21962
- const handleTouchEnd = (event) => {
21963
- const end = getTouchPoint(event);
21964
- const start = touchStart;
21965
- touchStart = null;
21966
- const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
21967
- 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) {
21968
- const active = rendition;
21969
- const interactionSession = documentSessionId;
21970
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
21971
- if (active !== rendition || interactionSession !== documentSessionId) return;
21972
- return end.x < start.x ? active.next() : active.prev();
21973
- });
21974
- epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
21975
- } else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21976
- sendEvent('VIEWER_TAP', {});
21977
- }
21978
- epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
21979
- };
21980
-
21981
22056
  manager.on('scroll', handleScroll);
21982
22057
  const handleScrolled = (event) => {
21983
22058
  epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
21984
22059
  };
21985
22060
  manager.on('scrolled', handleScrolled);
21986
- rendition.on('touchstart', handleTouchStart);
21987
- rendition.on('touchmove', handleTouchMove);
21988
- rendition.on('touchend', handleTouchEnd);
22061
+
21989
22062
  epubInteractionCleanup = () => {
21990
22063
  epubSelectionCleanups.forEach((cleanup) => cleanup());
21991
22064
  epubSelectionCleanups.clear();
@@ -21994,16 +22067,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21994
22067
  if (originalEnqueue) queue.enqueue = originalEnqueue;
21995
22068
  if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
21996
22069
  if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
21997
- if (typeof rendition.off === 'function') {
21998
- rendition.off('touchstart', handleTouchStart);
21999
- rendition.off('touchmove', handleTouchMove);
22000
- rendition.off('touchend', handleTouchEnd);
22001
- }
22002
22070
  epubScrollDiagnostics = null;
22003
22071
  };
22004
22072
  }
22005
- applyEpubZoom();
22006
-
22007
22073
  const outline = await withEpubStageTimeout(
22008
22074
  buildOutline(),
22009
22075
  'outline',
@@ -22033,6 +22099,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22033
22099
  const request = ++epubNavigationRequest;
22034
22100
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22035
22101
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22102
+ epubResizeAnchor?.invalidate();
22036
22103
  await activeRendition.display(target);
22037
22104
  if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22038
22105
  currentPage = pageIndex + 1;
@@ -22141,7 +22208,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22141
22208
  };
22142
22209
 
22143
22210
  let nativeMenuSelection = null;
22144
- let epubViewMode = "continuous";
22211
+ let epubViewMode = "single";
22212
+ let epubLocation = null;
22145
22213
 
22146
22214
  const handleCommand = async (message) => {
22147
22215
  const { id, kind, payload } = message;
@@ -22154,7 +22222,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22154
22222
  epubAnnotations = [];
22155
22223
  annotationDocumentId = undefined;
22156
22224
  zoom = 1.0;
22157
- epubViewMode = "continuous";
22225
+ epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22226
+ epubLocation = null;
22227
+ nativeMenuSelection = null;
22158
22228
  epubNavigationRequest += 1;
22159
22229
  epubNavigationQueue = Promise.resolve();
22160
22230
 
@@ -22219,6 +22289,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22219
22289
  epubNavigationRequest += 1;
22220
22290
  const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22221
22291
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22292
+ epubResizeAnchor?.invalidate();
22222
22293
  await activeRendition.display(annotation.anchor.href);
22223
22294
  if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22224
22295
  const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
@@ -22260,6 +22331,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22260
22331
  const active = rendition;
22261
22332
  epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22262
22333
  if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22334
+ epubResizeAnchor?.invalidate();
22263
22335
  return payload.direction < 0 ? active.prev() : active.next();
22264
22336
  });
22265
22337
  await epubNavigationQueue;
@@ -22268,25 +22340,40 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22268
22340
 
22269
22341
  if (kind === 'set-epub-view-mode') {
22270
22342
  if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
22271
- epubViewMode = payload.viewMode === 'single' ? 'single' : 'continuous';
22272
- const active = rendition;
22273
- epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22274
- if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22275
- const flow = epubViewMode === 'single' ? 'paginated' : 'scrolled-continuous';
22276
- if (active.settings.flow === flow) return;
22277
- active.flow(flow);
22278
- return active.q.enqueue(() => Promise.resolve());
22279
- });
22280
- await epubNavigationQueue;
22281
- sendResponse(id,true,null); return;
22343
+ const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
22344
+ {
22345
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22346
+ if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
22347
+ const target=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22348
+ epubViewMode=next;
22349
+ await renderEpubRendition('layout',epubLoadGeneration,target);
22350
+ });
22351
+ await epubNavigationQueue;
22352
+ }
22353
+ sendResponse(id,true,null);return;
22354
+ }
22355
+ if(kind === 'epub-go-to-location'){
22356
+ if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22357
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>{if(payload.documentSessionId!==documentSessionId)return;epubResizeAnchor?.invalidate();return rendition.display(payload.cfi);});
22358
+ await epubNavigationQueue;sendResponse(id,true,null);return;
22282
22359
  }
22283
22360
 
22284
22361
  if (kind === 'set-zoom') {
22285
- zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
22286
- applyZoom();
22287
- sendState();
22288
- sendResponse(id, true, { zoom });
22289
- return;
22362
+ const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
22363
+ if(currentType === 'epub') {
22364
+ const session=documentSessionId;
22365
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22366
+ if(session!==documentSessionId)return;
22367
+ const active=rendition;
22368
+ const cfi=epubResizeAnchor?.cfi() || epubLocation?.cfi;
22369
+ zoom=requestedZoom;applyEpubZoom();
22370
+ // Let the content CSS update before redisplaying the logical anchor.
22371
+ await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
22372
+ if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
22373
+ });
22374
+ await epubNavigationQueue;
22375
+ } else {zoom=requestedZoom;applyZoom();}
22376
+ sendState();sendResponse(id,true,{zoom});return;
22290
22377
  }
22291
22378
 
22292
22379
  if (kind === 'set-rotation') {
@@ -23449,6 +23536,7 @@ var IconToolStrikeout = ({
23449
23536
  ),
23450
23537
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Rect, { x: "10.9", y: "17.1", width: "14.2", height: "2.4", rx: "1.2", fill: color })
23451
23538
  ] });
23539
+ var IconStrikeout = ({ size = 22, color = defaultColor, strokeWidth = 1.8 }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.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" }) });
23452
23540
  var IconCopy = ({
23453
23541
  size = 20,
23454
23542
  color = defaultColor,
@@ -23513,6 +23601,39 @@ var IconToolNote = ({
23513
23601
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Line, { x1: "15", y1: "14.8", x2: "21.6", y2: "14.8", stroke: color, strokeWidth: "1.8", strokeLinecap: "round" }),
23514
23602
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_svg.Line, { x1: "15", y1: "18.2", x2: "19.2", y2: "18.2", stroke: color, strokeWidth: "1.8", strokeLinecap: "round" })
23515
23603
  ] });
23604
+ var IconHighlight = ({
23605
+ size = 24,
23606
+ color = "#fbbf24"
23607
+ }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
23608
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23609
+ import_react_native_svg.Path,
23610
+ {
23611
+ d: "M4 15h16v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3Z",
23612
+ fill: color,
23613
+ opacity: "0.85"
23614
+ }
23615
+ ),
23616
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23617
+ import_react_native_svg.Path,
23618
+ {
23619
+ d: "M6 5h12v10H6V5Z",
23620
+ stroke: color,
23621
+ strokeWidth: "2",
23622
+ strokeLinecap: "round",
23623
+ strokeLinejoin: "round",
23624
+ fill: "none"
23625
+ }
23626
+ ),
23627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23628
+ import_react_native_svg.Path,
23629
+ {
23630
+ d: "M8 8h8M8 11h5",
23631
+ stroke: color,
23632
+ strokeWidth: "2",
23633
+ strokeLinecap: "round"
23634
+ }
23635
+ )
23636
+ ] });
23516
23637
  var IconUnderline = ({
23517
23638
  size = 24,
23518
23639
  color = "#60a5fa",
@@ -25941,8 +26062,38 @@ var styles = import_react_native.StyleSheet.create({
25941
26062
  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;
25942
26063
  var PageRenderer_default = (0, import_react2.memo)(PageRenderer, arePageRendererPropsEqual);
25943
26064
 
26065
+ // components/PapyrusSafeArea.tsx
26066
+ var import_react3 = __toESM(require("react"));
26067
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
26068
+ var import_jsx_runtime4 = require("react/jsx-runtime");
26069
+ var papyrusSafeAreaContext = import_react_native_safe_area_context.SafeAreaInsetsContext;
26070
+ var ZERO_SAFE_AREA_INSETS = {
26071
+ top: 0,
26072
+ bottom: 0,
26073
+ left: 0,
26074
+ right: 0
26075
+ };
26076
+ var usePapyrusSafeAreaInsets = () => (0, import_react3.useContext)(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
26077
+ function PapyrusSafeAreaBoundary({
26078
+ children
26079
+ }) {
26080
+ const insets = (0, import_react3.useContext)(papyrusSafeAreaContext);
26081
+ if (insets) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
26082
+ return import_react3.default.createElement(import_react_native_safe_area_context.SafeAreaProvider, null, children);
26083
+ }
26084
+
26085
+ // components/epubReadingLocation.ts
26086
+ function parseEpubReadingLocation(raw) {
26087
+ if (!raw || typeof raw !== "object") return null;
26088
+ const p = raw;
26089
+ 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;
26090
+ if (p.mode === "chapter-scroll") return p.visualPage === null && p.visualPageCount === null ? p : null;
26091
+ if (p.mode !== "paged" || !Number.isSafeInteger(p.visualPage) || (p.visualPage ?? 0) < 1 || !Number.isSafeInteger(p.visualPageCount) || (p.visualPageCount ?? 0) < (p.visualPage ?? 0)) return null;
26092
+ return p;
26093
+ }
26094
+
25944
26095
  // components/WebViewViewer.tsx
25945
- var import_react3 = require("react");
26096
+ var import_react4 = require("react");
25946
26097
  var import_react_native2 = require("react-native");
25947
26098
  var import_clipboard3 = __toESM(require("@react-native-clipboard/clipboard"));
25948
26099
  var import_react_native_webview = __toESM(require("react-native-webview"));
@@ -26010,7 +26161,7 @@ function parseEpubAnnotationEvent(raw, session, documentId) {
26010
26161
  }
26011
26162
 
26012
26163
  // components/WebViewViewer.tsx
26013
- var import_jsx_runtime4 = require("react/jsx-runtime");
26164
+ var import_jsx_runtime5 = require("react/jsx-runtime");
26014
26165
  var runtimeAsset = require_runtime();
26015
26166
  var resolveRuntimeSource = (asset) => {
26016
26167
  if (typeof asset === "string") {
@@ -26030,6 +26181,7 @@ var resolveRuntimeSource = (asset) => {
26030
26181
  };
26031
26182
  var WebViewViewer = ({
26032
26183
  engine,
26184
+ documentType,
26033
26185
  documentId,
26034
26186
  onDefineSelection,
26035
26187
  defineSelectionMode,
@@ -26037,19 +26189,20 @@ var WebViewViewer = ({
26037
26189
  onScrollOffset,
26038
26190
  onTap
26039
26191
  }) => {
26040
- const webViewRef = (0, import_react3.useRef)(null);
26041
- const { pageTheme, viewMode, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = (0, import_core4.useViewerStore)();
26042
- const [selection, setSelection] = (0, import_react3.useState)(null);
26192
+ const webViewRef = (0, import_react4.useRef)(null);
26193
+ const insets = usePapyrusSafeAreaInsets();
26194
+ const { pageTheme, epubReadingMode, setDocumentState, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = (0, import_core4.useViewerStore)();
26195
+ const [selection, setSelection] = (0, import_react4.useState)(null);
26043
26196
  const t = getStrings(locale);
26044
26197
  const bridgeEngine = engine;
26045
- const runtimeSource = (0, import_react3.useMemo)(
26198
+ const runtimeSource = (0, import_react4.useMemo)(
26046
26199
  () => resolveRuntimeSource(
26047
26200
  bridgeEngine.getWebViewRuntimeSource?.() ?? runtimeAsset
26048
26201
  ),
26049
26202
  [bridgeEngine]
26050
26203
  );
26051
- const [runtimeHtml, setRuntimeHtml] = (0, import_react3.useState)(null);
26052
- (0, import_react3.useEffect)(() => {
26204
+ const [runtimeHtml, setRuntimeHtml] = (0, import_react4.useState)(null);
26205
+ (0, import_react4.useEffect)(() => {
26053
26206
  let active = true;
26054
26207
  if ("html" in runtimeSource) {
26055
26208
  setRuntimeHtml(runtimeSource.html);
@@ -26070,7 +26223,7 @@ var WebViewViewer = ({
26070
26223
  active = false;
26071
26224
  };
26072
26225
  }, [runtimeSource]);
26073
- const webViewSource = (0, import_react3.useMemo)(() => {
26226
+ const webViewSource = (0, import_react4.useMemo)(() => {
26074
26227
  if (runtimeHtml !== null) {
26075
26228
  return {
26076
26229
  html: runtimeHtml,
@@ -26081,13 +26234,13 @@ var WebViewViewer = ({
26081
26234
  return runtimeSource;
26082
26235
  }, [runtimeHtml, runtimeSource]);
26083
26236
  const runtimeConfig = bridgeEngine.getWebViewRuntimeConfig?.();
26084
- const runtimeConfigScript = (0, import_react3.useMemo)(() => {
26237
+ const runtimeConfigScript = (0, import_react4.useMemo)(() => {
26085
26238
  if (!runtimeConfig) return void 0;
26086
26239
  return `window.__PAPYRUS_RUNTIME_CONFIG__=${JSON.stringify(
26087
26240
  runtimeConfig
26088
26241
  )};true;`;
26089
26242
  }, [runtimeConfig]);
26090
- (0, import_react3.useEffect)(() => {
26243
+ (0, import_react4.useEffect)(() => {
26091
26244
  bridgeEngine.attachWebView?.({
26092
26245
  postMessage: (message) => {
26093
26246
  if (__DEV__) {
@@ -26101,10 +26254,10 @@ var WebViewViewer = ({
26101
26254
  bridgeEngine.detachWebView?.();
26102
26255
  };
26103
26256
  }, [bridgeEngine]);
26104
- (0, import_react3.useEffect)(() => {
26257
+ (0, import_react4.useEffect)(() => {
26105
26258
  setSelection(null);
26106
26259
  }, [engine, documentId]);
26107
- (0, import_react3.useEffect)(() => {
26260
+ (0, import_react4.useEffect)(() => {
26108
26261
  if (!isLoaded) return;
26109
26262
  let active = true;
26110
26263
  void bridgeEngine.syncEpubAnnotations?.(annotations, documentId, { comment: t.annotationNote }).catch(() => {
@@ -26119,10 +26272,10 @@ var WebViewViewer = ({
26119
26272
  (note !== void 0 ? beginAnnotationDraft : addAnnotation)((0, import_core4.createContextualAnnotation)({ id: `annotation-${Date.now()}-${Math.random().toString(36).slice(2)}`, anchor: selected.anchor, pageIndex: selected.pageIndex, style, color: annotationColor, opacity: annotationOpacity, note }));
26120
26273
  setSelection(null);
26121
26274
  };
26122
- (0, import_react3.useEffect)(() => {
26275
+ (0, import_react4.useEffect)(() => {
26123
26276
  if (!isLoaded) return;
26124
- webViewRef.current?.postMessage(JSON.stringify({ id: "epub-layout", kind: "set-epub-view-mode", payload: { viewMode, documentSessionId: bridgeEngine.getWebViewDocumentSessionId?.() } }));
26125
- }, [isLoaded, viewMode, bridgeEngine]);
26277
+ webViewRef.current?.postMessage(JSON.stringify({ id: "epub-layout", kind: "set-epub-view-mode", payload: { epubMode: epubReadingMode, documentSessionId: bridgeEngine.getWebViewDocumentSessionId?.() } }));
26278
+ }, [isLoaded, epubReadingMode, bridgeEngine]);
26126
26279
  const sendSelectionCommand = (key) => {
26127
26280
  webViewRef.current?.postMessage(JSON.stringify({
26128
26281
  type: "epub-selection-action",
@@ -26146,6 +26299,11 @@ var WebViewViewer = ({
26146
26299
  let annotationRaw = raw;
26147
26300
  try {
26148
26301
  const message = JSON.parse(raw);
26302
+ if (message.type === "event" && message.name === "EPUB_LOCATION" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) {
26303
+ const location = parseEpubReadingLocation(message.payload.location);
26304
+ if (location) setDocumentState({ epubLocation: location });
26305
+ }
26306
+ if (message.type === "event" && message.name === "EPUB_CONTENT_TAP" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) onTap?.();
26149
26307
  if (message.type === "event" && message.name === "EPUB_SELECTION_ACTION") {
26150
26308
  action = message.payload?.action;
26151
26309
  annotationRaw = JSON.stringify({ ...message, name: "EPUB_TEXT_SELECTED" });
@@ -26209,7 +26367,7 @@ var WebViewViewer = ({
26209
26367
  console.warn("[Papyrus WebView] error", event.nativeEvent);
26210
26368
  }
26211
26369
  };
26212
- const themeOverlayStyle = (0, import_react3.useMemo)(() => {
26370
+ const themeOverlayStyle = (0, import_react4.useMemo)(() => {
26213
26371
  switch (pageTheme) {
26214
26372
  case "sepia":
26215
26373
  return styles2.themeSepia;
@@ -26221,7 +26379,7 @@ var WebViewViewer = ({
26221
26379
  return styles2.themeNone;
26222
26380
  }
26223
26381
  }, [pageTheme]);
26224
- const allowingReadAccessToURL = (0, import_react3.useMemo)(() => {
26382
+ const allowingReadAccessToURL = (0, import_react4.useMemo)(() => {
26225
26383
  if (!runtimeSource || typeof runtimeSource !== "object") return void 0;
26226
26384
  if ("uri" in runtimeSource && runtimeSource.uri) {
26227
26385
  return runtimeSource.uri;
@@ -26234,8 +26392,8 @@ var WebViewViewer = ({
26234
26392
  maxWidth: resolvedMaxPageWidth,
26235
26393
  alignSelf: "center"
26236
26394
  } : void 0;
26237
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.container, children: [
26238
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
26395
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native2.View, { style: [styles2.container, documentType === "epub" && { paddingTop: insets.top, paddingBottom: insets.bottom }], children: [
26396
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
26239
26397
  import_react_native_webview.default,
26240
26398
  {
26241
26399
  ref: webViewRef,
@@ -26259,7 +26417,7 @@ var WebViewViewer = ({
26259
26417
  style: [styles2.webview, cappedWebViewStyle]
26260
26418
  }
26261
26419
  ),
26262
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
26420
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
26263
26421
  import_react_native2.View,
26264
26422
  {
26265
26423
  pointerEvents: "none",
@@ -26361,22 +26519,22 @@ function getNearbyPdfNotes(annotations, id) {
26361
26519
  // components/PdfNoteChooser.tsx
26362
26520
  var import_react_native3 = require("react-native");
26363
26521
  var import_core5 = require("@papyrus-sdk/core");
26364
- var import_jsx_runtime5 = require("react/jsx-runtime");
26522
+ var import_jsx_runtime6 = require("react/jsx-runtime");
26365
26523
  function PdfNoteChooser({ notes, title, cancel, onSelect, onClose }) {
26366
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Modal, { visible: notes.length > 1, transparent: true, animationType: "none", onRequestClose: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: styles3.backdrop, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.View, { style: styles3.sheet, accessibilityViewIsModal: true, children: [
26367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Text, { style: styles3.title, children: title }),
26368
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.FlatList, { data: notes, keyExtractor: (a) => a.id, renderItem: ({ item, index }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Pressable, { accessibilityRole: "button", style: styles3.row, onPress: () => onSelect(item.id), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native3.Text, { children: [
26524
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Modal, { visible: notes.length > 1, transparent: true, animationType: "none", onRequestClose: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.View, { style: styles3.backdrop, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native3.View, { style: styles3.sheet, accessibilityViewIsModal: true, children: [
26525
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Text, { style: styles3.title, children: title }),
26526
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.FlatList, { data: notes, keyExtractor: (a) => a.id, renderItem: ({ item, index }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Pressable, { accessibilityRole: "button", style: styles3.row, onPress: () => onSelect(item.id), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native3.Text, { children: [
26369
26527
  index + 1,
26370
26528
  ". ",
26371
26529
  (0, import_core5.getAnnotationNote)(item) || (0, import_core5.getAnnotationQuote)(item)
26372
26530
  ] }) }) }),
26373
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Pressable, { accessibilityRole: "button", style: styles3.row, onPress: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Text, { children: cancel }) })
26531
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Pressable, { accessibilityRole: "button", style: styles3.row, onPress: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Text, { children: cancel }) })
26374
26532
  ] }) }) });
26375
26533
  }
26376
26534
  var styles3 = import_react_native3.StyleSheet.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 } });
26377
26535
 
26378
26536
  // components/DedicatedAndroidPdfViewer.tsx
26379
- var import_react4 = require("react");
26537
+ var import_react5 = require("react");
26380
26538
  var import_react_native4 = require("react-native");
26381
26539
  var import_core6 = require("@papyrus-sdk/core");
26382
26540
  var import_engine_native2 = require("@papyrus-sdk/engine-native");
@@ -26397,7 +26555,7 @@ var resolvePageTapChromeVisibility = ({
26397
26555
  };
26398
26556
 
26399
26557
  // components/DedicatedAndroidPdfViewer.tsx
26400
- var import_jsx_runtime6 = require("react/jsx-runtime");
26558
+ var import_jsx_runtime7 = require("react/jsx-runtime");
26401
26559
  var TEXT_MARKUP_TOOLS2 = /* @__PURE__ */ new Set(["highlight", "underline", "squiggly", "strikeout"]);
26402
26560
  var MOBILE_CHROME_HIDE_DELTA = 28;
26403
26561
  var MOBILE_CHROME_SHOW_DELTA = 22;
@@ -26426,7 +26584,7 @@ function DedicatedAndroidPdfViewer({
26426
26584
  const searchResults = (0, import_core6.useViewerStore)((state) => state.searchResults);
26427
26585
  const annotationNavigationRequest = (0, import_core6.useViewerStore)((state) => state.annotationNavigationRequest);
26428
26586
  const annotations = (0, import_core6.useViewerStore)((state) => state.annotations);
26429
- const [pendingNotes, setPendingNotes] = (0, import_react4.useState)([]);
26587
+ const [pendingNotes, setPendingNotes] = (0, import_react5.useState)([]);
26430
26588
  const handleAnnotationTap = (id) => {
26431
26589
  const nearby = getNearbyPdfNotes(annotations, id);
26432
26590
  if (nearby.length < 2) {
@@ -26454,30 +26612,30 @@ function DedicatedAndroidPdfViewer({
26454
26612
  maxWidth: resolvedMaxPageWidth,
26455
26613
  alignSelf: "center"
26456
26614
  } : void 0;
26457
- const [selection, setSelection] = (0, import_react4.useState)(null);
26458
- const selectionRef = (0, import_react4.useRef)(null);
26459
- const lastVisiblePagesKeyRef = (0, import_react4.useRef)("");
26460
- const lastScrollOffsetYRef = (0, import_react4.useRef)(0);
26461
- const scrollDownAccumRef = (0, import_react4.useRef)(0);
26462
- const scrollUpAccumRef = (0, import_react4.useRef)(0);
26463
- const pendingChromeShowTimeoutRef = (0, import_react4.useRef)(null);
26464
- const chromeVisibleRef = (0, import_react4.useRef)(true);
26465
- const lastZoomChangedAtRef = (0, import_react4.useRef)(null);
26466
- (0, import_react4.useEffect)(() => {
26615
+ const [selection, setSelection] = (0, import_react5.useState)(null);
26616
+ const selectionRef = (0, import_react5.useRef)(null);
26617
+ const lastVisiblePagesKeyRef = (0, import_react5.useRef)("");
26618
+ const lastScrollOffsetYRef = (0, import_react5.useRef)(0);
26619
+ const scrollDownAccumRef = (0, import_react5.useRef)(0);
26620
+ const scrollUpAccumRef = (0, import_react5.useRef)(0);
26621
+ const pendingChromeShowTimeoutRef = (0, import_react5.useRef)(null);
26622
+ const chromeVisibleRef = (0, import_react5.useRef)(true);
26623
+ const lastZoomChangedAtRef = (0, import_react5.useRef)(null);
26624
+ (0, import_react5.useEffect)(() => {
26467
26625
  chromeVisibleRef.current = mobileChromeVisible;
26468
26626
  }, [mobileChromeVisible]);
26469
- const clearPendingChromeShow = (0, import_react4.useCallback)(() => {
26627
+ const clearPendingChromeShow = (0, import_react5.useCallback)(() => {
26470
26628
  if (pendingChromeShowTimeoutRef.current) {
26471
26629
  clearTimeout(pendingChromeShowTimeoutRef.current);
26472
26630
  pendingChromeShowTimeoutRef.current = null;
26473
26631
  }
26474
26632
  }, []);
26475
- const setMobileChromeVisible = (0, import_react4.useCallback)((visible, reason) => {
26633
+ const setMobileChromeVisible = (0, import_react5.useCallback)((visible, reason) => {
26476
26634
  if (chromeVisibleRef.current === visible) return;
26477
26635
  chromeVisibleRef.current = visible;
26478
26636
  setDocumentState({ mobileChromeVisible: visible });
26479
26637
  }, [setDocumentState]);
26480
- const trackMobileChromeByOffset = (0, import_react4.useCallback)((offsetY, reasonPrefix) => {
26638
+ const trackMobileChromeByOffset = (0, import_react5.useCallback)((offsetY, reasonPrefix) => {
26481
26639
  const safeOffset = Math.max(0, offsetY);
26482
26640
  const delta = safeOffset - lastScrollOffsetYRef.current;
26483
26641
  lastScrollOffsetYRef.current = safeOffset;
@@ -26513,12 +26671,12 @@ function DedicatedAndroidPdfViewer({
26513
26671
  }
26514
26672
  }
26515
26673
  }, [clearPendingChromeShow, setMobileChromeVisible]);
26516
- (0, import_react4.useEffect)(() => {
26674
+ (0, import_react5.useEffect)(() => {
26517
26675
  return () => {
26518
26676
  clearPendingChromeShow();
26519
26677
  };
26520
26678
  }, [clearPendingChromeShow]);
26521
- const applySelection = (0, import_react4.useCallback)((type) => {
26679
+ const applySelection = (0, import_react5.useCallback)((type) => {
26522
26680
  const sel = selectionRef.current;
26523
26681
  if (!sel || !sel.rects || sel.rects.length === 0) return;
26524
26682
  const bounds = sel.rects.reduce((acc, r) => ({
@@ -26540,12 +26698,12 @@ function DedicatedAndroidPdfViewer({
26540
26698
  setSelection(null);
26541
26699
  selectionRef.current = null;
26542
26700
  }, [addAnnotation, beginAnnotationDraft, annotationColor, documentId]);
26543
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native4.View, { style: styles4.container, children: [
26544
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26701
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native4.View, { style: styles4.container, children: [
26702
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26545
26703
  setPendingNotes([]);
26546
26704
  setSelectedAnnotation(id);
26547
26705
  } }),
26548
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
26706
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
26549
26707
  import_engine_native2.PapyrusPdfDocumentView,
26550
26708
  {
26551
26709
  style: [styles4.viewer, cappedViewerStyle],
@@ -26659,7 +26817,7 @@ var styles4 = import_react_native4.StyleSheet.create({
26659
26817
  });
26660
26818
 
26661
26819
  // components/DedicatedIosPdfViewer.tsx
26662
- var import_react5 = require("react");
26820
+ var import_react6 = require("react");
26663
26821
  var import_react_native5 = require("react-native");
26664
26822
  var import_core7 = require("@papyrus-sdk/core");
26665
26823
  var import_engine_native3 = require("@papyrus-sdk/engine-native");
@@ -26756,7 +26914,7 @@ var resolveNativePdfVisiblePages = (pages) => (pages ?? []).flatMap((page) => {
26756
26914
  });
26757
26915
 
26758
26916
  // components/DedicatedIosPdfViewer.tsx
26759
- var import_jsx_runtime7 = require("react/jsx-runtime");
26917
+ var import_jsx_runtime8 = require("react/jsx-runtime");
26760
26918
  var MOBILE_CHROME_HIDE_DELTA2 = 28;
26761
26919
  var MOBILE_CHROME_SHOW_DELTA2 = 22;
26762
26920
  var MOBILE_CHROME_SHOW_DELAY_MS2 = 180;
@@ -26788,7 +26946,7 @@ function DedicatedIosPdfViewer({
26788
26946
  const inkStrokeWidth = (0, import_core7.useViewerStore)((state) => state.inkStrokeWidth);
26789
26947
  const annotationNavigationRequest = (0, import_core7.useViewerStore)((state) => state.annotationNavigationRequest);
26790
26948
  const annotations = (0, import_core7.useViewerStore)((state) => state.annotations);
26791
- const [pendingNotes, setPendingNotes] = (0, import_react5.useState)([]);
26949
+ const [pendingNotes, setPendingNotes] = (0, import_react6.useState)([]);
26792
26950
  const handleAnnotationTap = (id) => {
26793
26951
  const nearby = getNearbyPdfNotes(annotations, id);
26794
26952
  if (nearby.length < 2) {
@@ -26824,16 +26982,16 @@ function DedicatedIosPdfViewer({
26824
26982
  supportsNativeEditMenu
26825
26983
  );
26826
26984
  const resolvedMaxPageWidth = typeof maxPageWidth === "number" && Number.isFinite(maxPageWidth) && maxPageWidth > 0 ? maxPageWidth : void 0;
26827
- const chromeVisibleRef = (0, import_react5.useRef)(mobileChromeVisible);
26828
- const lastScrollOffsetYRef = (0, import_react5.useRef)(0);
26829
- const scrollDownAccumRef = (0, import_react5.useRef)(0);
26830
- const scrollUpAccumRef = (0, import_react5.useRef)(0);
26831
- const pendingChromeShowTimeoutRef = (0, import_react5.useRef)(null);
26832
- const lastZoomChangedAtRef = (0, import_react5.useRef)(null);
26833
- const lastVisiblePagesKeyRef = (0, import_react5.useRef)("");
26834
- const [selection, setSelection] = (0, import_react5.useState)(null);
26835
- const selectionRef = (0, import_react5.useRef)(null);
26836
- (0, import_react5.useEffect)(() => {
26985
+ const chromeVisibleRef = (0, import_react6.useRef)(mobileChromeVisible);
26986
+ const lastScrollOffsetYRef = (0, import_react6.useRef)(0);
26987
+ const scrollDownAccumRef = (0, import_react6.useRef)(0);
26988
+ const scrollUpAccumRef = (0, import_react6.useRef)(0);
26989
+ const pendingChromeShowTimeoutRef = (0, import_react6.useRef)(null);
26990
+ const lastZoomChangedAtRef = (0, import_react6.useRef)(null);
26991
+ const lastVisiblePagesKeyRef = (0, import_react6.useRef)("");
26992
+ const [selection, setSelection] = (0, import_react6.useState)(null);
26993
+ const selectionRef = (0, import_react6.useRef)(null);
26994
+ (0, import_react6.useEffect)(() => {
26837
26995
  const pickerActive = supportsNativeEditMenu && activeTool === "ink";
26838
26996
  setDocumentState({ nativeInkToolPickerActive: pickerActive });
26839
26997
  return () => {
@@ -26842,15 +27000,15 @@ function DedicatedIosPdfViewer({
26842
27000
  }
26843
27001
  };
26844
27002
  }, [activeTool, setDocumentState]);
26845
- (0, import_react5.useEffect)(() => {
27003
+ (0, import_react6.useEffect)(() => {
26846
27004
  chromeVisibleRef.current = mobileChromeVisible;
26847
27005
  }, [mobileChromeVisible]);
26848
- const clearPendingChromeShow = (0, import_react5.useCallback)(() => {
27006
+ const clearPendingChromeShow = (0, import_react6.useCallback)(() => {
26849
27007
  if (!pendingChromeShowTimeoutRef.current) return;
26850
27008
  clearTimeout(pendingChromeShowTimeoutRef.current);
26851
27009
  pendingChromeShowTimeoutRef.current = null;
26852
27010
  }, []);
26853
- const setMobileChromeVisible = (0, import_react5.useCallback)(
27011
+ const setMobileChromeVisible = (0, import_react6.useCallback)(
26854
27012
  (visible) => {
26855
27013
  if (chromeVisibleRef.current === visible) return;
26856
27014
  chromeVisibleRef.current = visible;
@@ -26858,14 +27016,14 @@ function DedicatedIosPdfViewer({
26858
27016
  },
26859
27017
  [setDocumentState]
26860
27018
  );
26861
- const updateSelection = (0, import_react5.useCallback)(
27019
+ const updateSelection = (0, import_react6.useCallback)(
26862
27020
  (nextSelection) => {
26863
27021
  selectionRef.current = nextSelection;
26864
27022
  setSelection(nextSelection);
26865
27023
  },
26866
27024
  []
26867
27025
  );
26868
- const handleTextSelectionChange = (0, import_react5.useCallback)(
27026
+ const handleTextSelectionChange = (0, import_react6.useCallback)(
26869
27027
  (event) => {
26870
27028
  const nextSelection = resolveNativePdfTextSelection(event.nativeEvent);
26871
27029
  updateSelection(nextSelection);
@@ -26878,7 +27036,7 @@ function DedicatedIosPdfViewer({
26878
27036
  },
26879
27037
  [onTextSelected, setSelectedAnnotation, updateSelection]
26880
27038
  );
26881
- const defineSelection = (0, import_react5.useCallback)(() => {
27039
+ const defineSelection = (0, import_react6.useCallback)(() => {
26882
27040
  const currentSelection = selectionRef.current;
26883
27041
  if (!currentSelection) return;
26884
27042
  onDefineSelection?.({
@@ -26887,7 +27045,7 @@ function DedicatedIosPdfViewer({
26887
27045
  });
26888
27046
  updateSelection(null);
26889
27047
  }, [onDefineSelection, updateSelection]);
26890
- const handleNativeDefineSelection = (0, import_react5.useCallback)(
27048
+ const handleNativeDefineSelection = (0, import_react6.useCallback)(
26891
27049
  (event) => {
26892
27050
  const text = event.nativeEvent?.text;
26893
27051
  const pageIndex = event.nativeEvent?.pageIndex;
@@ -26899,7 +27057,7 @@ function DedicatedIosPdfViewer({
26899
27057
  },
26900
27058
  [onDefineSelection, updateSelection]
26901
27059
  );
26902
- const handleInkToolPickerVisibilityChange = (0, import_react5.useCallback)(
27060
+ const handleInkToolPickerVisibilityChange = (0, import_react6.useCallback)(
26903
27061
  (event) => {
26904
27062
  handleNativeInkPickerVisibility(
26905
27063
  event.nativeEvent?.visible === true,
@@ -26908,7 +27066,7 @@ function DedicatedIosPdfViewer({
26908
27066
  },
26909
27067
  []
26910
27068
  );
26911
- const trackMobileChromeByOffset = (0, import_react5.useCallback)(
27069
+ const trackMobileChromeByOffset = (0, import_react6.useCallback)(
26912
27070
  (offsetY) => {
26913
27071
  if (pageCount <= 0) return;
26914
27072
  const safeOffset = Math.max(0, offsetY);
@@ -26944,11 +27102,11 @@ function DedicatedIosPdfViewer({
26944
27102
  },
26945
27103
  [clearPendingChromeShow, pageCount, setMobileChromeVisible]
26946
27104
  );
26947
- (0, import_react5.useEffect)(
27105
+ (0, import_react6.useEffect)(
26948
27106
  () => () => clearPendingChromeShow(),
26949
27107
  [clearPendingChromeShow]
26950
27108
  );
26951
- const handleVisiblePagesChange = (0, import_react5.useCallback)(
27109
+ const handleVisiblePagesChange = (0, import_react6.useCallback)(
26952
27110
  (event) => {
26953
27111
  const pages = resolveNativePdfVisiblePages(
26954
27112
  event.nativeEvent?.pages
@@ -26963,13 +27121,13 @@ function DedicatedIosPdfViewer({
26963
27121
  },
26964
27122
  [setDocumentState]
26965
27123
  );
26966
- const handleScroll = (0, import_react5.useCallback)(
27124
+ const handleScroll = (0, import_react6.useCallback)(
26967
27125
  (event) => {
26968
27126
  trackMobileChromeByOffset(event.nativeEvent?.offsetY ?? 0);
26969
27127
  },
26970
27128
  [trackMobileChromeByOffset]
26971
27129
  );
26972
- const handleTap = (0, import_react5.useCallback)(() => {
27130
+ const handleTap = (0, import_react6.useCallback)(() => {
26973
27131
  setSelectedAnnotation(null);
26974
27132
  const nextVisible = resolvePageTapChromeVisibility({
26975
27133
  chromeVisible: chromeVisibleRef.current,
@@ -26979,7 +27137,7 @@ function DedicatedIosPdfViewer({
26979
27137
  });
26980
27138
  if (nextVisible !== null) setMobileChromeVisible(nextVisible);
26981
27139
  }, [activeTool, selectionActive, setMobileChromeVisible, setSelectedAnnotation]);
26982
- const handleDeleteAnnotation = (0, import_react5.useCallback)(
27140
+ const handleDeleteAnnotation = (0, import_react6.useCallback)(
26983
27141
  (annotationId) => {
26984
27142
  deleteAnnotationAndClearSelection(
26985
27143
  annotationId,
@@ -26994,12 +27152,12 @@ function DedicatedIosPdfViewer({
26994
27152
  maxWidth: resolvedMaxPageWidth,
26995
27153
  alignSelf: "center"
26996
27154
  } : void 0;
26997
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native5.View, { style: styles5.container, children: [
26998
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
27155
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.container, children: [
27156
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26999
27157
  setPendingNotes([]);
27000
27158
  setSelectedAnnotation(id);
27001
27159
  } }),
27002
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27160
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27003
27161
  import_engine_native3.PapyrusPdfDocumentView,
27004
27162
  {
27005
27163
  style: [styles5.viewer, cappedViewerStyle],
@@ -27068,22 +27226,22 @@ function DedicatedIosPdfViewer({
27068
27226
  onInkToolPickerVisibilityChange: handleInkToolPickerVisibilityChange
27069
27227
  }
27070
27228
  ),
27071
- selection && !supportsNativeEditMenu && onDefineSelection && shouldShowDefineSelection(selection.text, defineSelectionMode) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native5.View, { style: styles5.selectionFallback, pointerEvents: "box-none", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27229
+ selection && !supportsNativeEditMenu && onDefineSelection && shouldShowDefineSelection(selection.text, defineSelectionMode) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.View, { style: styles5.selectionFallback, pointerEvents: "box-none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27072
27230
  import_react_native5.Pressable,
27073
27231
  {
27074
27232
  onPress: defineSelection,
27075
27233
  style: styles5.fallbackButton,
27076
27234
  accessibilityRole: "button",
27077
27235
  accessibilityLabel: t.define,
27078
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native5.Text, { style: styles5.fallbackButtonText, children: t.define })
27236
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.Text, { style: styles5.fallbackButtonText, children: t.define })
27079
27237
  }
27080
27238
  ) }),
27081
- fallbackDeleteAnnotation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27239
+ fallbackDeleteAnnotation && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27082
27240
  import_react_native5.View,
27083
27241
  {
27084
27242
  style: styles5.annotationDeleteFallback,
27085
27243
  pointerEvents: "box-none",
27086
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27244
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27087
27245
  import_react_native5.Pressable,
27088
27246
  {
27089
27247
  onPress: () => handleDeleteAnnotation(fallbackDeleteAnnotation.id),
@@ -27091,7 +27249,7 @@ function DedicatedIosPdfViewer({
27091
27249
  accessibilityRole: "button",
27092
27250
  accessibilityLabel: t.deleteAnnotation,
27093
27251
  hitSlop: 8,
27094
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native5.Text, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
27252
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.Text, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
27095
27253
  }
27096
27254
  )
27097
27255
  }
@@ -27154,7 +27312,7 @@ var styles5 = import_react_native5.StyleSheet.create({
27154
27312
  });
27155
27313
 
27156
27314
  // components/NativePdfDocumentViewer.tsx
27157
- var import_jsx_runtime8 = require("react/jsx-runtime");
27315
+ var import_jsx_runtime9 = require("react/jsx-runtime");
27158
27316
  function NativePdfDocumentViewer({
27159
27317
  engine,
27160
27318
  documentId,
@@ -27167,7 +27325,7 @@ function NativePdfDocumentViewer({
27167
27325
  import_react_native6.Platform.OS
27168
27326
  );
27169
27327
  if (implementation === "android") {
27170
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27328
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
27171
27329
  DedicatedAndroidPdfViewer,
27172
27330
  {
27173
27331
  engine,
@@ -27180,7 +27338,7 @@ function NativePdfDocumentViewer({
27180
27338
  );
27181
27339
  }
27182
27340
  if (implementation === "ios") {
27183
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27341
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
27184
27342
  DedicatedIosPdfViewer,
27185
27343
  {
27186
27344
  engine,
@@ -27234,26 +27392,6 @@ var resolveMobileChromeOffsets = (insets) => ({
27234
27392
  right: MOBILE_CHROME_METRICS.screenPadding + Math.max(0, insets.right)
27235
27393
  });
27236
27394
 
27237
- // components/PapyrusSafeArea.tsx
27238
- var import_react6 = __toESM(require("react"));
27239
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
27240
- var import_jsx_runtime9 = require("react/jsx-runtime");
27241
- var papyrusSafeAreaContext = import_react_native_safe_area_context.SafeAreaInsetsContext;
27242
- var ZERO_SAFE_AREA_INSETS = {
27243
- top: 0,
27244
- bottom: 0,
27245
- left: 0,
27246
- right: 0
27247
- };
27248
- var usePapyrusSafeAreaInsets = () => (0, import_react6.useContext)(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
27249
- function PapyrusSafeAreaBoundary({
27250
- children
27251
- }) {
27252
- const insets = (0, import_react6.useContext)(papyrusSafeAreaContext);
27253
- if (insets) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
27254
- return import_react6.default.createElement(import_react_native_safe_area_context.SafeAreaProvider, null, children);
27255
- }
27256
-
27257
27395
  // components/NativeTextDocumentViewer.tsx
27258
27396
  var import_jsx_runtime10 = require("react/jsx-runtime");
27259
27397
  function NativeTextDocumentViewer({
@@ -27802,6 +27940,7 @@ var scrollViewerListToOffset = (list, offset, animated) => {
27802
27940
  list.scrollToOffset?.({ offset, animated });
27803
27941
  };
27804
27942
  var Viewer = ({
27943
+ documentType,
27805
27944
  documentId,
27806
27945
  engine,
27807
27946
  maxPageWidth,
@@ -29329,14 +29468,8 @@ var Viewer = ({
29329
29468
  },
29330
29469
  [mobilePerf, perfEnabled, scrollPerfSession]
29331
29470
  );
29332
- const handleWebViewScroll = (0, import_react9.useCallback)(
29333
- (_offsetY) => {
29334
- if (!chromeVisibleRef.current) {
29335
- setMobileChromeVisible(true, "scroll.webview.recover");
29336
- }
29337
- },
29338
- [setMobileChromeVisible]
29339
- );
29471
+ const handleWebViewScroll = (0, import_react9.useCallback)((_offsetY) => {
29472
+ }, []);
29340
29473
  const handlePageTap = (0, import_react9.useCallback)(() => {
29341
29474
  const nextVisible = resolvePageTapChromeVisibility({
29342
29475
  chromeVisible: chromeVisibleRef.current,
@@ -29558,6 +29691,7 @@ var Viewer = ({
29558
29691
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native9.View, { style: [styles8.container, isDark && styles8.containerDark], children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
29559
29692
  WebViewViewer_default,
29560
29693
  {
29694
+ documentType,
29561
29695
  engine,
29562
29696
  documentId,
29563
29697
  onDefineSelection,
@@ -30104,6 +30238,7 @@ var styles9 = import_react_native11.StyleSheet.create({
30104
30238
  // components/PageJumpModal.tsx
30105
30239
  var import_jsx_runtime14 = require("react/jsx-runtime");
30106
30240
  function PageJumpModal({
30241
+ documentType,
30107
30242
  visible,
30108
30243
  currentPage,
30109
30244
  pageCount,
@@ -30130,8 +30265,8 @@ function PageJumpModal({
30130
30265
  isDark,
30131
30266
  maxHeight: 300,
30132
30267
  showHeader: true,
30133
- title: "Ir para p\xE1gina",
30134
- closeAccessibilityLabel: "Close page jump",
30268
+ title: documentType === "epub" ? "Ir para cap\xEDtulo" : "Ir para p\xE1gina",
30269
+ closeAccessibilityLabel: "Fechar navega\xE7\xE3o",
30135
30270
  sheetStyle: {
30136
30271
  paddingHorizontal: 18,
30137
30272
  paddingBottom: 18,
@@ -30159,7 +30294,7 @@ function PageJumpModal({
30159
30294
  }
30160
30295
  ],
30161
30296
  onSubmitEditing: confirmJump,
30162
- accessibilityLabel: "Page jump input",
30297
+ accessibilityLabel: documentType === "epub" ? "N\xFAmero do cap\xEDtulo" : "N\xFAmero da p\xE1gina",
30163
30298
  testID: "papyrus-page-jump-input"
30164
30299
  }
30165
30300
  ),
@@ -30214,6 +30349,7 @@ var styles10 = import_react_native12.StyleSheet.create({
30214
30349
  var import_jsx_runtime15 = require("react/jsx-runtime");
30215
30350
  var Topbar = ({
30216
30351
  engine,
30352
+ documentType,
30217
30353
  onOpenSettings,
30218
30354
  onOpenOverflow,
30219
30355
  title,
@@ -30234,6 +30370,12 @@ var Topbar = ({
30234
30370
  locale
30235
30371
  } = (0, import_core12.useViewerStore)();
30236
30372
  const inkSession = useNativeInkSession();
30373
+ const chromeVisible = mobileChromeVisible || inkSession.active;
30374
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
30375
+ const chromeMotion = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
30376
+ "worklet";
30377
+ return { opacity: chromeProgress.value };
30378
+ });
30237
30379
  const t = getStrings(locale);
30238
30380
  const progress = useNativeInkMotion(inkSession.active, inkSession.supported);
30239
30381
  const settingsMotion = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
@@ -30289,20 +30431,23 @@ var Topbar = ({
30289
30431
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSettings, { size: MOBILE_CHROME_METRICS.iconSize, color: isDark ? "#e5e7eb" : "#111827" })
30290
30432
  }
30291
30433
  );
30292
- if (!mobileChromeVisible && !inkSession.active) return null;
30434
+ if (!chromeVisible && documentType !== "epub") return null;
30293
30435
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
30294
30436
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
30295
- import_react_native13.View,
30437
+ import_react_native_reanimated2.default.View,
30296
30438
  {
30297
30439
  style: [
30298
30440
  styles11.chromeFrame,
30441
+ chromeMotion,
30299
30442
  {
30300
30443
  paddingTop: insets.top,
30301
30444
  paddingLeft: MOBILE_CHROME_METRICS.screenPadding + insets.left,
30302
30445
  paddingRight: MOBILE_CHROME_METRICS.screenPadding + insets.right
30303
30446
  }
30304
30447
  ],
30305
- pointerEvents: "box-none",
30448
+ pointerEvents: chromeVisible ? "box-none" : "none",
30449
+ accessibilityElementsHidden: !chromeVisible,
30450
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
30306
30451
  testID: "papyrus-floating-top-controls",
30307
30452
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
30308
30453
  import_react_native13.View,
@@ -30449,6 +30594,7 @@ var Topbar = ({
30449
30594
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
30450
30595
  PageJumpModal,
30451
30596
  {
30597
+ documentType,
30452
30598
  visible: jumpModalOpen,
30453
30599
  currentPage,
30454
30600
  pageCount,
@@ -32699,7 +32845,27 @@ var AnnotationEditor = ({ documentId }) => {
32699
32845
  editing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
32700
32846
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.TextInput, { 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" }] }),
32701
32847
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.View, { style: styles14.options, children: COLORS.map((value) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Pressable, { accessibilityRole: "button", accessibilityLabel: `${t.annotationColor} ${value}`, accessibilityState: { selected: color === value }, onPress: () => setColor(value), style: [styles14.color, { backgroundColor: value, borderColor: color === value ? accentColor : "transparent" }] }, value)) }),
32702
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.View, { style: styles14.options, children: [["highlight", t.annotationHighlight], ["underline", t.annotationUnderline], ["strikeout", t.annotationStrikeout], ["none", t.annotationIndicatorOnly]].map(([style, label]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Pressable, { onPress: () => setMarkup(style), accessibilityRole: "button", accessibilityState: { selected: markup === style }, style: [styles14.button, markup === style && { borderColor: accentColor, borderWidth: 1 }], children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: textStyle, children: label }) }, style)) })
32848
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.View, { style: styles14.markupOptions, children: [
32849
+ ["highlight", t.annotationHighlight, IconHighlight],
32850
+ ["underline", t.annotationUnderline, IconUnderline],
32851
+ ["strikeout", t.annotationStrikeout, IconStrikeout],
32852
+ ["none", t.annotationIndicatorOnly, IconComment]
32853
+ ].map(([style, label, Icon]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
32854
+ import_react_native16.Pressable,
32855
+ {
32856
+ onPress: () => setMarkup(style),
32857
+ accessibilityRole: "button",
32858
+ accessibilityLabel: label,
32859
+ accessibilityState: { selected: markup === style },
32860
+ testID: `annotation-style-${style}`,
32861
+ style: [styles14.markupButton, { backgroundColor: dark ? "#242936" : "#f1f5f9", borderColor: markup === style ? accentColor : "transparent" }],
32862
+ children: [
32863
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.View, { accessible: false, importantForAccessibility: "no-hide-descendants", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { size: 22, color: markup === style ? accentColor : textStyle.color }) }),
32864
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.markupLabel, textStyle], children: style === "none" ? locale === "pt-BR" ? "Indicador" : "Indicator" : label })
32865
+ ]
32866
+ },
32867
+ style
32868
+ )) })
32703
32869
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.note, textStyle], selectable: true, children: (0, import_core15.getAnnotationNote)(annotation) || t.notePlaceholder })
32704
32870
  ] }),
32705
32871
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native16.View, { style: styles14.actions, children: [
@@ -32722,7 +32888,7 @@ var AnnotationEditor = ({ documentId }) => {
32722
32888
  ] })
32723
32889
  ] }) });
32724
32890
  };
32725
- var styles14 = import_react_native16.StyleSheet.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 } });
32891
+ var styles14 = import_react_native16.StyleSheet.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 } });
32726
32892
  var AnnotationEditor_default = AnnotationEditor;
32727
32893
 
32728
32894
  // components/BottomBar.tsx
@@ -32793,6 +32959,8 @@ var BottomBar = ({
32793
32959
  const {
32794
32960
  activeMobileDestination,
32795
32961
  viewMode,
32962
+ epubReadingMode,
32963
+ epubLocation,
32796
32964
  comicLayoutMode,
32797
32965
  currentPage,
32798
32966
  pageCount,
@@ -32810,7 +32978,7 @@ var BottomBar = ({
32810
32978
  const supportsInkChrome = documentType === "pdf" && inkSession.supported;
32811
32979
  const drawing = supportsInkChrome && inkSession.active;
32812
32980
  const dockVisible = mobileChromeVisible && mobileDockVisible && !drawing;
32813
- const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome);
32981
+ const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome || documentType === "epub");
32814
32982
  const dockMotion = (0, import_react_native_reanimated3.useAnimatedStyle)(() => {
32815
32983
  "worklet";
32816
32984
  return {
@@ -32832,7 +33000,7 @@ var BottomBar = ({
32832
33000
  activeMobileDestination,
32833
33001
  toolDockOpen
32834
33002
  });
32835
- const paginated = documentType !== "text" && (documentType === "comic" ? comicLayoutMode === "single" : viewMode === "single");
33003
+ const paginated = documentType !== "text" && (documentType === "comic" ? comicLayoutMode === "single" : documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single");
32836
33004
  if (paginated) {
32837
33005
  layout.leftSlots.push(...layout.rightSlots.filter((slot) => slot.key === "search"));
32838
33006
  layout.rightSlots = [];
@@ -32901,7 +33069,7 @@ var BottomBar = ({
32901
33069
  onPress: onOpenSettings
32902
33070
  }
32903
33071
  };
32904
- if (!dockVisible && !supportsInkChrome) return null;
33072
+ if (!dockVisible && !supportsInkChrome && documentType !== "epub") return null;
32905
33073
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
32906
33074
  import_react_native_reanimated3.default.View,
32907
33075
  {
@@ -32967,13 +33135,9 @@ var BottomBar = ({
32967
33135
  ],
32968
33136
  testID: "papyrus-floating-bottom-dock",
32969
33137
  children: paginated ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
32970
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "P\xE1gina anterior" : "Previous page", disabled: !dockVisible || !onTurnPage && currentPage <= 1, onPress: () => turnPage(-1), style: styles15.iconOnlyItem, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconChevronLeft, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) }),
32971
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Ir para p\xE1gina" : "Go to page", onPress: onOpenPages, style: [styles15.iconOnlyItem, { minWidth: 56 }], children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native17.Text, { style: { color: iconColor(false), fontWeight: "700", fontSize: 13, lineHeight: 18, includeFontPadding: false }, children: [
32972
- currentPage,
32973
- "/",
32974
- pageCount
32975
- ] }) }),
32976
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { 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__ */ (0, import_jsx_runtime19.jsx)(IconChevronRight, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) })
33138
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { 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__ */ (0, import_jsx_runtime19.jsx)(IconChevronLeft, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) }),
33139
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { accessibilityRole: "button", accessibilityLabel: locale === "pt-BR" ? "Ir para p\xE1gina" : "Go to page", onPress: onOpenPages, style: [styles15.iconOnlyItem, { minWidth: 56 }], children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Text, { 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}` }) }),
33140
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.Pressable, { 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__ */ (0, import_jsx_runtime19.jsx)(IconChevronRight, { size: MOBILE_CHROME_METRICS.iconSize, color: iconColor(false) }) })
32977
33141
  ] }) : layout.rightSlots.map((slot) => {
32978
33142
  const meta = slotMeta[slot.key];
32979
33143
  const Icon = meta.icon;
@@ -33148,6 +33312,7 @@ var SettingsSheet = ({
33148
33312
  }) => {
33149
33313
  const {
33150
33314
  viewMode,
33315
+ epubReadingMode,
33151
33316
  uiTheme,
33152
33317
  zoom,
33153
33318
  setDocumentState,
@@ -33159,12 +33324,16 @@ var SettingsSheet = ({
33159
33324
  comicReadingDirection
33160
33325
  } = (0, import_core17.useViewerStore)();
33161
33326
  const isDark = uiTheme === "dark";
33162
- const isPaged = viewMode === "single";
33327
+ const isPaged = documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single";
33163
33328
  const isDouble = viewMode === "double";
33164
33329
  const t = getStrings(locale);
33165
33330
  const { height: windowHeight } = (0, import_react_native18.useWindowDimensions)();
33166
33331
  const sheetMaxHeight = getSettingsSheetMaxHeight(windowHeight);
33167
33332
  const handleTransition = (mode) => {
33333
+ if (documentType === "epub") {
33334
+ setDocumentState({ epubReadingMode: mode === "paged" ? "paged" : "chapter-scroll" });
33335
+ return;
33336
+ }
33168
33337
  if (mode === "paged") {
33169
33338
  setDocumentState({ viewMode: "single" });
33170
33339
  return;
@@ -33388,7 +33557,7 @@ var SettingsSheet = ({
33388
33557
  isDark && styles16.optionTextDark,
33389
33558
  (!isPaged || isDouble) && styles16.optionTextActive
33390
33559
  ],
33391
- children: t.continuous
33560
+ children: documentType === "epub" && locale === "pt-BR" ? "Rolagem por cap\xEDtulo" : t.continuous
33392
33561
  }
33393
33562
  )
33394
33563
  }
@@ -33418,7 +33587,7 @@ var SettingsSheet = ({
33418
33587
  )
33419
33588
  ] })
33420
33589
  ] }),
33421
- documentType !== "comic" && documentType !== "text" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: styles16.section, children: [
33590
+ documentType === "pdf" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: styles16.section, children: [
33422
33591
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
33423
33592
  import_react_native18.Text,
33424
33593
  {
@@ -34296,6 +34465,7 @@ var styles20 = import_react_native22.StyleSheet.create({
34296
34465
  // components/ProgressPill.tsx
34297
34466
  var import_react18 = require("react");
34298
34467
  var import_react_native23 = require("react-native");
34468
+ var import_react_native_reanimated4 = __toESM(require("react-native-reanimated"));
34299
34469
  var import_core21 = require("@papyrus-sdk/core");
34300
34470
 
34301
34471
  // components/progressPillMode.ts
@@ -34329,12 +34499,20 @@ function ProgressPill({
34329
34499
  textLength,
34330
34500
  comicLayoutMode,
34331
34501
  viewMode,
34502
+ epubReadingMode,
34503
+ epubLocation,
34332
34504
  locale,
34333
34505
  uiTheme,
34334
34506
  accentColor,
34335
34507
  mobileChromeVisible,
34336
34508
  mobileProgressPillVisible
34337
34509
  } = (0, import_core21.useViewerStore)();
34510
+ const chromeVisible = mobileChromeVisible && mobileProgressPillVisible;
34511
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
34512
+ const chromeMotion = (0, import_react_native_reanimated4.useAnimatedStyle)(() => {
34513
+ "worklet";
34514
+ return { opacity: chromeProgress.value };
34515
+ });
34338
34516
  const t = getStrings(locale);
34339
34517
  const isDark = uiTheme === "dark";
34340
34518
  const offsets = resolveMobileChromeOffsets(usePapyrusSafeAreaInsets());
@@ -34379,10 +34557,10 @@ function ProgressPill({
34379
34557
  return `${displayedPage}/${pageCount || 0}`;
34380
34558
  }
34381
34559
  if (documentType === "epub") {
34382
- return `Cap. ${displayedPage} \xB7 ${percent}%`;
34560
+ return `Cap. ${displayedPage} \xB7 ${scrubPreviewPage === null && epubLocation ? clampPercent(epubLocation.progress * 100) : percent}%`;
34383
34561
  }
34384
34562
  return `${percent}%`;
34385
- }, [displayedPage, documentType, pageCount]);
34563
+ }, [displayedPage, documentType, pageCount, epubLocation, scrubPreviewPage]);
34386
34564
  const thumbTop = resolvePageScrubberThumbTop({
34387
34565
  currentPage,
34388
34566
  trackHeight,
@@ -34424,7 +34602,7 @@ function ProgressPill({
34424
34602
  },
34425
34603
  []
34426
34604
  );
34427
- if (!shouldRenderPageScrubber({
34605
+ if (!(documentType === "epub" && mobileProgressPillVisible) && !shouldRenderPageScrubber({
34428
34606
  mobileChromeVisible,
34429
34607
  mobileProgressPillVisible,
34430
34608
  isScrubbing: isScrubbingRef.current
@@ -34455,7 +34633,7 @@ function ProgressPill({
34455
34633
  }
34456
34634
  );
34457
34635
  }
34458
- const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : viewMode;
34636
+ const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : documentType === "epub" ? epubReadingMode === "paged" ? "single" : "continuous" : viewMode;
34459
34637
  const pillMode = resolveProgressPillMode(progressViewMode, currentPage, pageCount);
34460
34638
  if (pillMode.kind === "navigation" && navigationInDock) return null;
34461
34639
  if (pillMode.kind === "navigation") {
@@ -34646,11 +34824,14 @@ function ProgressPill({
34646
34824
  }
34647
34825
  const responderPanHandlers = scrubberResponderRef.current?.panHandlers ?? {};
34648
34826
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
34649
- import_react_native23.View,
34827
+ import_react_native_reanimated4.default.View,
34650
34828
  {
34651
- pointerEvents: "box-none",
34829
+ pointerEvents: chromeVisible ? "box-none" : "none",
34830
+ accessibilityElementsHidden: !chromeVisible,
34831
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
34652
34832
  style: [
34653
34833
  styles21.frame,
34834
+ chromeMotion,
34654
34835
  resolvePageScrubberOverlayStyle(),
34655
34836
  {
34656
34837
  top: offsets.progress,
@@ -35379,6 +35560,7 @@ function ReadingShell({
35379
35560
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35380
35561
  Topbar_default,
35381
35562
  {
35563
+ documentType,
35382
35564
  engine,
35383
35565
  title,
35384
35566
  onOpenSettings: () => openDestination("display"),
@@ -35389,6 +35571,7 @@ function ReadingShell({
35389
35571
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native26.View, { style: styles24.viewerStage, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35390
35572
  Viewer_default,
35391
35573
  {
35574
+ documentType,
35392
35575
  engine,
35393
35576
  ...viewerProps,
35394
35577
  pageScrubActive
@@ -35513,6 +35696,7 @@ function ReadingShell({
35513
35696
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35514
35697
  PageJumpModal,
35515
35698
  {
35699
+ documentType,
35516
35700
  visible: pageJumpOpen,
35517
35701
  currentPage,
35518
35702
  pageCount,