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

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,42 @@ 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){
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 fraction=total?Math.max(0,(visualPage-1)/total):0;
20538
+ 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:Boolean(raw.atStart),atEnd:Boolean(raw.atEnd)};
20539
+ }
20540
+ function install(contents,config){
20541
+ var doc=contents.document,win=contents.window,start=null,blocked=false;
20542
+ 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;}
20543
+ function selected(){return Boolean(String(win.getSelection()||'').trim()) || config.blocked();}
20544
+ function interactive(target){return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,[contenteditable="true"],[data-papyrus-overlay]'));}
20545
+ function begin(e){start=point(e);blocked=e.touches.length!==1 || selected() || interactive(e.target);}
20546
+ function move(e){if(e.touches.length!==1)blocked=true;}
20547
+ function end(e){var action=gesture(start,point(e),{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);}
20548
+ function cancel(){start=null;blocked=true;}
20549
+ doc.addEventListener('touchstart',begin,{passive:true});doc.addEventListener('touchmove',move,{passive:true});doc.addEventListener('touchend',end,{passive:true});doc.addEventListener('touchcancel',cancel,{passive:true});
20550
+ return function(){doc.removeEventListener('touchstart',begin);doc.removeEventListener('touchmove',move);doc.removeEventListener('touchend',end);doc.removeEventListener('touchcancel',cancel);};
20551
+ }
20552
+ root.PapyrusEpubReader={mode:mode,validCfi:validCfi,options:options,gesture:gesture,location:location,install:install};
20553
+ })(typeof window==='undefined'?globalThis:window);
20554
+
20555
+ /* @papyrus-epub-reader:end */
20520
20556
  /* @papyrus-epub-annotations:start */
20521
20557
  (function (root, factory) {
20522
20558
  if (typeof module === 'object' && module.exports) module.exports = factory();
@@ -20595,6 +20631,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20595
20631
  var color = /^#[0-9a-f]{6}$/i.test(annotation.color) ? annotation.color : '#FFC928';
20596
20632
  var style = annotation.markupStyle || annotation.type;
20597
20633
  var opacity = Number.isFinite(annotation.opacity) ? Math.max(0,Math.min(1,annotation.opacity)) : 0.35;
20634
+ if(style === 'highlight')opacity=Math.min(0.35,opacity);
20598
20635
  rects.forEach(function(rect) {
20599
20636
  var mark = doc.createElement('div');
20600
20637
  var bodyRect=doc.body.getBoundingClientRect();
@@ -20607,8 +20644,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20607
20644
  if (hasNote) {
20608
20645
  var last = rects[rects.length-1], button = doc.createElement('button');
20609
20646
  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);
20647
+ var pageWidth=Math.max(44, options.pageWidth ? options.pageWidth() : win.innerWidth);
20648
+ var bodyRect=doc.body.getBoundingClientRect(), column=Math.max(0,Math.floor((last.left-bodyRect.left)/pageWidth));
20649
+ 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';
20650
+ var key = column+":"+Math.floor(last.top/44);
20612
20651
  var neighbors=noteGroups.get(key);
20613
20652
  if(!neighbors){neighbors={button:button,ids:[]};noteGroups.set(key,neighbors);group.appendChild(button);}
20614
20653
  neighbors.ids.push(annotation.id);
@@ -20629,10 +20668,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20629
20668
  doc.addEventListener('click',tapMarkup);
20630
20669
  var observer = typeof win.ResizeObserver === 'function' ? new win.ResizeObserver(schedule) : null;
20631
20670
  if (observer) observer.observe(doc.body);
20632
- win.addEventListener('resize',schedule); doc.addEventListener('load',schedule,true);
20671
+ win.addEventListener('resize',schedule);win.addEventListener('scroll',schedule); doc.addEventListener('load',schedule,true);
20633
20672
  if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(schedule);
20634
20673
  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();}};
20674
+ 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
20675
  }
20637
20676
  return {validAnchor:validAnchor,recoverRange:recoverRange,resolveRange:resolveRange,selection:selection,install:install};
20638
20677
  });
@@ -21713,26 +21752,55 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21713
21752
  pageCount,
21714
21753
  });
21715
21754
 
21755
+ if (book.locations?.generate) {
21756
+ const activeBook=book;
21757
+ activeBook.locations.generate(150).then(() => {
21758
+ if (isCurrentLoad() && activeBook===book && epubLocation) {
21759
+ epubLocation.progress=activeBook.locations.percentageFromCfi(epubLocation.cfi);
21760
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21761
+ }
21762
+ }).catch(() => {});
21763
+ }
21764
+ return renderEpubRendition(loadId,generation,null);
21765
+ };
21766
+
21767
+ const renderEpubRendition = async (loadId,generation,target) => {
21768
+ const diagnosticPayload={loadId};
21769
+ const isCurrentLoad=()=>generation===epubLoadGeneration;
21770
+ if(epubInteractionCleanup)epubInteractionCleanup();
21771
+ if(rendition)rendition.destroy();
21716
21772
  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
- });
21773
+ rendition = book.renderTo(viewer, window.PapyrusEpubReader.options(epubViewMode === 'single' ? 'paged' : 'chapter-scroll'));
21725
21774
  sendEpubDiagnostic('epub.rendition.created', diagnosticPayload);
21726
21775
 
21727
21776
  if (rendition && rendition.hooks && rendition.hooks.content) {
21728
21777
  const session = documentSessionId;
21729
21778
  const activeRendition = rendition;
21730
21779
  rendition.hooks.content.register((contents) => {
21780
+ const removeGestureListeners = window.PapyrusEpubReader.install(contents, {
21781
+ mode: () => epubViewMode === 'single' ? 'paged' : 'chapter-scroll',
21782
+ rtl: () => book.package?.metadata?.direction === 'rtl' || book.spine?.direction === 'rtl',
21783
+ blocked: () => Boolean(nativeMenuSelection && String(nativeMenuSelection.contents?.window?.getSelection() || '').trim()),
21784
+ annotationHit: point => epubAnnotationRenderers.get(contents)?.hit(point.x,point.y),
21785
+ current: () => session === documentSessionId && activeRendition === rendition,
21786
+ tap: () => sendEvent('EPUB_CONTENT_TAP',{documentSessionId:session}),
21787
+ turn: direction => {
21788
+ epubNavigationQueue = epubNavigationQueue.catch(()=>{}).then(() => {
21789
+ if(session !== documentSessionId || activeRendition !== rendition)return;
21790
+ return direction<0?activeRendition.prev():activeRendition.next();
21791
+ });
21792
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR',{message:String(error)}));
21793
+ },
21794
+ });
21795
+ const gestureCleanup = () => {removeGestureListeners();epubSelectionCleanups.delete(gestureCleanup);};
21796
+ epubSelectionCleanups.add(gestureCleanup);
21797
+ contents.__papyrusGestureCleanup=gestureCleanup;
21731
21798
  const href = spineItems[contents.sectionIndex]?.href;
21732
21799
  if (href && window.PapyrusEpubAnnotations) {
21733
21800
  const renderer = window.PapyrusEpubAnnotations.install(contents, {
21734
21801
  href, get documentId() { return annotationDocumentId; },
21735
21802
  isCurrent: () => session === documentSessionId && activeRendition === rendition,
21803
+ pageWidth: () => activeRendition.layout?.width || viewer.clientWidth,
21736
21804
  annotations: () => epubAnnotations, labels: () => annotationLabels,
21737
21805
  onTap: (id, ids) => sendEvent("EPUB_ANNOTATION_TAP", {documentSessionId: session, id, ids}),
21738
21806
  });
@@ -21751,8 +21819,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21751
21819
  typeof selectionWindow.getSelection === 'function'
21752
21820
  ) {
21753
21821
  let selectionTimer = null;
21754
- let lastSelectionKey = '';
21755
- let lastSelectionAt = 0;
21822
+
21756
21823
  const reportSelection = () => {
21757
21824
  selectionTimer = null;
21758
21825
  if (session !== documentSessionId || activeRendition !== rendition) return;
@@ -21767,11 +21834,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21767
21834
  try { cfi = contents.cfiFromRange(range); } catch (_) { return; }
21768
21835
  const anchor = window.PapyrusEpubAnnotations?.selection(contents, cfi, href, annotationDocumentId);
21769
21836
  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;
21837
+ if (nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21775
21838
  const payload = {documentSessionId: session, text: anchor.quote,
21776
21839
  pageIndex: contents.sectionIndex + 1, anchor};
21777
21840
  nativeMenuSelection = {contents, payload};
@@ -21786,6 +21849,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21786
21849
  // The content document still emits selectionchange, so resolve its
21787
21850
  // live Range to a CFI before the native menu action can fire.
21788
21851
  if (text) reportSelection();
21852
+ else if (nativeMenuSelection?.contents === contents) nativeMenuSelection = null;
21789
21853
  };
21790
21854
  selectionDocument.addEventListener('selectionchange', handleSelectionChange);
21791
21855
  const selectionCleanup = () => {
@@ -21808,6 +21872,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21808
21872
  typeof rendition.hooks.unloaded.register === 'function'
21809
21873
  ) {
21810
21874
  rendition.hooks.unloaded.register((view) => {
21875
+ view?.contents?.__papyrusGestureCleanup?.();
21811
21876
  const cleanup = view && view.contents && view.contents.__papyrusSelectionCleanup;
21812
21877
  if (typeof cleanup === 'function') cleanup();
21813
21878
  const renderer = epubAnnotationRenderers.get(view?.contents);
@@ -21823,21 +21888,27 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21823
21888
  if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21824
21889
  const href = spineItems[contents.sectionIndex]?.href;
21825
21890
  const anchor = href && window.PapyrusEpubAnnotations?.selection(contents,cfiRange,href,annotationDocumentId);
21826
- if (!anchor) return;
21891
+ if (!anchor || nativeMenuSelection?.payload?.anchor?.cfiRange === anchor.cfiRange && nativeMenuSelection?.payload?.anchor?.quote === anchor.quote) return;
21827
21892
  epubScrollDiagnostics && epubScrollDiagnostics.setSelectionActive(true);
21828
21893
  const payload = {documentSessionId: selectionSession, text: anchor.quote, pageIndex: contents.sectionIndex+1, anchor};
21829
21894
  nativeMenuSelection = {contents, payload};
21830
21895
  sendEvent('EPUB_TEXT_SELECTED', payload);
21831
21896
  });
21832
- ['rendered','resized','layout'].forEach(name => rendition.on(name, () => {
21897
+ ['rendered','resized','layout','relocated'].forEach(name => rendition.on(name, () => {
21833
21898
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
21834
21899
  }));
21835
21900
  rendition.on('relocated', (location) => {
21901
+ if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21836
21902
  const start = location && location.start;
21837
21903
  let index = start && Number.isInteger(start.index) ? start.index : -1;
21838
21904
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
21839
21905
  if (index < 0 || index >= spineItems.length) return;
21840
21906
  epubScrollDiagnostics && epubScrollDiagnostics.relocated(location);
21907
+ epubLocation=window.PapyrusEpubReader.location(location,spineItems.length,epubViewMode==='single'?'paged':'chapter-scroll');
21908
+ if(epubLocation){
21909
+ if(book.locations?.length?.()>0)epubLocation.progress=book.locations.percentageFromCfi(epubLocation.cfi);
21910
+ sendEvent('EPUB_LOCATION',{documentSessionId,location:epubLocation});
21911
+ }
21841
21912
  const nextPage = index + 1;
21842
21913
  if (nextPage === currentPage) return;
21843
21914
  currentPage = nextPage;
@@ -21845,9 +21916,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21845
21916
  });
21846
21917
  }
21847
21918
 
21919
+ applyEpubZoom();
21848
21920
  sendEpubDiagnostic('epub.display.start', diagnosticPayload);
21849
21921
  await withEpubStageTimeout(
21850
- displayEpubPage(0),
21922
+ rendition.display(target || spineItems[0]?.href),
21851
21923
  'rendition.display',
21852
21924
  diagnosticPayload,
21853
21925
  );
@@ -21859,7 +21931,6 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21859
21931
  typeof rendition.manager.on === 'function' &&
21860
21932
  typeof rendition.on === 'function'
21861
21933
  ) {
21862
- let touchStart = null;
21863
21934
  let lastScrollOffset = -1;
21864
21935
  const manager = rendition.manager;
21865
21936
  epubScrollDiagnostics = createEpubScrollDiagnostics(manager);
@@ -21934,48 +22005,19 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21934
22005
  return result;
21935
22006
  };
21936
22007
  }
21937
- const getTouchPoint = (event) => {
21938
- const touch =
21939
- (event && event.changedTouches && event.changedTouches[0]) ||
21940
- (event && event.touches && event.touches[0]);
21941
- if (!touch) return null;
21942
- const x = Number(touch.screenX ?? touch.clientX);
21943
- const y = Number(touch.screenY ?? touch.clientY);
21944
- return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
21945
- };
21946
- const handleScroll = (event) => {
22008
+ const handleScroll = event => {
21947
22009
  const offsetY = Math.max(0, Number(event && event.top) || 0);
21948
22010
  epubScrollDiagnostics && epubScrollDiagnostics.scroll(event);
21949
- if (Math.abs(offsetY - lastScrollOffset) < 1) return;
21950
- lastScrollOffset = offsetY;
21951
- sendEvent('VIEWER_SCROLL', { offsetY });
21952
- };
21953
- const handleTouchStart = (event) => {
21954
- touchStart = getTouchPoint(event);
21955
- epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
21956
- };
21957
- const handleTouchMove = (event) => {
21958
- epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
22011
+ if (Math.abs(offsetY-lastScrollOffset)<1) return;
22012
+ lastScrollOffset=offsetY;
22013
+ sendEvent('VIEWER_SCROLL',{offsetY});
21959
22014
  };
21960
- const handleTouchEnd = (event) => {
21961
- const end = getTouchPoint(event);
21962
- const start = touchStart;
21963
- touchStart = null;
21964
- const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
21965
- if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21966
- sendEvent('VIEWER_TAP', {});
21967
- }
21968
- epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
21969
- };
21970
-
21971
22015
  manager.on('scroll', handleScroll);
21972
22016
  const handleScrolled = (event) => {
21973
22017
  epubScrollDiagnostics && epubScrollDiagnostics.settled(event);
21974
22018
  };
21975
22019
  manager.on('scrolled', handleScrolled);
21976
- rendition.on('touchstart', handleTouchStart);
21977
- rendition.on('touchmove', handleTouchMove);
21978
- rendition.on('touchend', handleTouchEnd);
22020
+
21979
22021
  epubInteractionCleanup = () => {
21980
22022
  epubSelectionCleanups.forEach((cleanup) => cleanup());
21981
22023
  epubSelectionCleanups.clear();
@@ -21984,16 +22026,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21984
22026
  if (originalEnqueue) queue.enqueue = originalEnqueue;
21985
22027
  if (typeof manager.off === 'function') manager.off('scroll', handleScroll);
21986
22028
  if (typeof manager.off === 'function') manager.off('scrolled', handleScrolled);
21987
- if (typeof rendition.off === 'function') {
21988
- rendition.off('touchstart', handleTouchStart);
21989
- rendition.off('touchmove', handleTouchMove);
21990
- rendition.off('touchend', handleTouchEnd);
21991
- }
21992
22029
  epubScrollDiagnostics = null;
21993
22030
  };
21994
22031
  }
21995
- applyEpubZoom();
21996
-
21997
22032
  const outline = await withEpubStageTimeout(
21998
22033
  buildOutline(),
21999
22034
  'outline',
@@ -22007,6 +22042,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22007
22042
  return { pageCount, outline };
22008
22043
  };
22009
22044
 
22045
+ let epubNavigationQueue = Promise.resolve();
22046
+ let epubNavigationRequest = 0;
22010
22047
  const displayEpubPage = async (pageIndex) => {
22011
22048
  if (!rendition) return;
22012
22049
  const item = spineItems[pageIndex];
@@ -22016,9 +22053,18 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22016
22053
  target,
22017
22054
  pageIndex,
22018
22055
  });
22019
- await rendition.display(target);
22020
- currentPage = pageIndex + 1;
22021
- sendState();
22056
+ const activeRendition = rendition;
22057
+ const session = documentSessionId;
22058
+ const request = ++epubNavigationRequest;
22059
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22060
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22061
+ await activeRendition.display(target);
22062
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22063
+ currentPage = pageIndex + 1;
22064
+ sendState();
22065
+ });
22066
+ epubNavigationQueue = navigation;
22067
+ await navigation;
22022
22068
  };
22023
22069
 
22024
22070
  const getTextContent = async (pageIndex) => {
@@ -22120,6 +22166,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22120
22166
  };
22121
22167
 
22122
22168
  let nativeMenuSelection = null;
22169
+ let epubViewMode = "single";
22170
+ let epubLocation = null;
22123
22171
 
22124
22172
  const handleCommand = async (message) => {
22125
22173
  const { id, kind, payload } = message;
@@ -22132,6 +22180,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22132
22180
  epubAnnotations = [];
22133
22181
  annotationDocumentId = undefined;
22134
22182
  zoom = 1.0;
22183
+ epubViewMode = payload.epubMode === "chapter-scroll" ? "continuous" : "single";
22184
+ epubLocation = null;
22185
+ nativeMenuSelection = null;
22186
+ epubNavigationRequest += 1;
22187
+ epubNavigationQueue = Promise.resolve();
22135
22188
 
22136
22189
  if (currentType === 'text') {
22137
22190
  const result = await loadText(
@@ -22186,17 +22239,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22186
22239
  epubAnnotationRenderers.forEach(renderer => renderer.schedule());
22187
22240
  } else {
22188
22241
  const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
22189
- if (!annotation) throw new Error('Annotation not available');
22242
+ if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
22190
22243
  // Navigation uses the CFI, never a synthetic percentage/page rectangle.
22191
22244
  const navigationSession = documentSessionId;
22192
22245
  const activeRendition = rendition;
22193
22246
  // Open the confirmed chapter first, resolve its quote, then navigate its actual range.
22194
- await activeRendition.display(annotation.anchor.href);
22195
- if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22196
- const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
22197
- const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
22198
- if(!range)throw new Error('EPUB anchor unresolved');
22199
- await activeRendition.display(contents.cfiFromRange(range));
22247
+ epubNavigationRequest += 1;
22248
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22249
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22250
+ await activeRendition.display(annotation.anchor.href);
22251
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22252
+ const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
22253
+ const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
22254
+ if(!range)throw new Error('EPUB anchor unresolved');
22255
+ await activeRendition.display(contents.cfiFromRange(range));
22256
+ });
22257
+ epubNavigationQueue = navigation;
22258
+ await navigation;
22200
22259
  }
22201
22260
  sendResponse(id,true,null); return;
22202
22261
  }
@@ -22223,12 +22282,54 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22223
22282
  return;
22224
22283
  }
22225
22284
 
22285
+ if (kind === 'epub-turn-page') {
22286
+ if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
22287
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
22288
+ const active = rendition;
22289
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22290
+ if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22291
+ return payload.direction < 0 ? active.prev() : active.next();
22292
+ });
22293
+ await epubNavigationQueue;
22294
+ sendResponse(id,true,null); return;
22295
+ }
22296
+
22297
+ if (kind === 'set-epub-view-mode') {
22298
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
22299
+ const next=payload.epubMode ? (payload.epubMode==='paged'?'single':'continuous') : (payload.viewMode==='single'?'single':'continuous');
22300
+ {
22301
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22302
+ if(payload.documentSessionId!==documentSessionId || next===epubViewMode)return;
22303
+ const target=epubLocation?.cfi;
22304
+ epubViewMode=next;
22305
+ await renderEpubRendition('layout',epubLoadGeneration,target);
22306
+ });
22307
+ await epubNavigationQueue;
22308
+ }
22309
+ sendResponse(id,true,null);return;
22310
+ }
22311
+ if(kind === 'epub-go-to-location'){
22312
+ if(currentType!=='epub' || payload.documentSessionId!==documentSessionId || !window.PapyrusEpubReader.validCfi(payload.cfi))throw new Error('Invalid EPUB location');
22313
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(()=>payload.documentSessionId===documentSessionId?rendition.display(payload.cfi):undefined);
22314
+ await epubNavigationQueue;sendResponse(id,true,null);return;
22315
+ }
22316
+
22226
22317
  if (kind === 'set-zoom') {
22227
- zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
22228
- applyZoom();
22229
- sendState();
22230
- sendResponse(id, true, { zoom });
22231
- return;
22318
+ const requestedZoom=Math.max(0.5,Math.min(4.0,payload.zoom || 1.0));
22319
+ if(currentType === 'epub') {
22320
+ const session=documentSessionId;
22321
+ epubNavigationQueue=epubNavigationQueue.catch(()=>{}).then(async()=>{
22322
+ if(session!==documentSessionId)return;
22323
+ const active=rendition;
22324
+ const cfi=epubLocation?.cfi;
22325
+ zoom=requestedZoom;applyEpubZoom();
22326
+ // Let the content CSS update before redisplaying the logical anchor.
22327
+ await new Promise(resolve=>(window.requestAnimationFrame || (callback=>setTimeout(callback,0)))(resolve));
22328
+ if(cfi && active===rendition && session===documentSessionId)await active.display(cfi);
22329
+ });
22330
+ await epubNavigationQueue;
22331
+ } else {zoom=requestedZoom;applyZoom();}
22332
+ sendState();sendResponse(id,true,{zoom});return;
22232
22333
  }
22233
22334
 
22234
22335
  if (kind === 'set-rotation') {
@@ -23391,6 +23492,7 @@ var IconToolStrikeout = ({
23391
23492
  ),
23392
23493
  /* @__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 })
23393
23494
  ] });
23495
+ 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" }) });
23394
23496
  var IconCopy = ({
23395
23497
  size = 20,
23396
23498
  color = defaultColor,
@@ -23455,6 +23557,39 @@ var IconToolNote = ({
23455
23557
  /* @__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" }),
23456
23558
  /* @__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" })
23457
23559
  ] });
23560
+ var IconHighlight = ({
23561
+ size = 24,
23562
+ color = "#fbbf24"
23563
+ }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_svg.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
23564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23565
+ import_react_native_svg.Path,
23566
+ {
23567
+ d: "M4 15h16v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3Z",
23568
+ fill: color,
23569
+ opacity: "0.85"
23570
+ }
23571
+ ),
23572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23573
+ import_react_native_svg.Path,
23574
+ {
23575
+ d: "M6 5h12v10H6V5Z",
23576
+ stroke: color,
23577
+ strokeWidth: "2",
23578
+ strokeLinecap: "round",
23579
+ strokeLinejoin: "round",
23580
+ fill: "none"
23581
+ }
23582
+ ),
23583
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
23584
+ import_react_native_svg.Path,
23585
+ {
23586
+ d: "M8 8h8M8 11h5",
23587
+ stroke: color,
23588
+ strokeWidth: "2",
23589
+ strokeLinecap: "round"
23590
+ }
23591
+ )
23592
+ ] });
23458
23593
  var IconUnderline = ({
23459
23594
  size = 24,
23460
23595
  color = "#60a5fa",
@@ -25883,8 +26018,38 @@ var styles = import_react_native.StyleSheet.create({
25883
26018
  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;
25884
26019
  var PageRenderer_default = (0, import_react2.memo)(PageRenderer, arePageRendererPropsEqual);
25885
26020
 
26021
+ // components/PapyrusSafeArea.tsx
26022
+ var import_react3 = __toESM(require("react"));
26023
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
26024
+ var import_jsx_runtime4 = require("react/jsx-runtime");
26025
+ var papyrusSafeAreaContext = import_react_native_safe_area_context.SafeAreaInsetsContext;
26026
+ var ZERO_SAFE_AREA_INSETS = {
26027
+ top: 0,
26028
+ bottom: 0,
26029
+ left: 0,
26030
+ right: 0
26031
+ };
26032
+ var usePapyrusSafeAreaInsets = () => (0, import_react3.useContext)(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
26033
+ function PapyrusSafeAreaBoundary({
26034
+ children
26035
+ }) {
26036
+ const insets = (0, import_react3.useContext)(papyrusSafeAreaContext);
26037
+ if (insets) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
26038
+ return import_react3.default.createElement(import_react_native_safe_area_context.SafeAreaProvider, null, children);
26039
+ }
26040
+
26041
+ // components/epubReadingLocation.ts
26042
+ function parseEpubReadingLocation(raw) {
26043
+ if (!raw || typeof raw !== "object") return null;
26044
+ const p = raw;
26045
+ 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;
26046
+ if (p.mode === "chapter-scroll") return p.visualPage === null && p.visualPageCount === null ? p : null;
26047
+ if (p.mode !== "paged" || !Number.isSafeInteger(p.visualPage) || (p.visualPage ?? 0) < 1 || !Number.isSafeInteger(p.visualPageCount) || (p.visualPageCount ?? 0) < (p.visualPage ?? 0)) return null;
26048
+ return p;
26049
+ }
26050
+
25886
26051
  // components/WebViewViewer.tsx
25887
- var import_react3 = require("react");
26052
+ var import_react4 = require("react");
25888
26053
  var import_react_native2 = require("react-native");
25889
26054
  var import_clipboard3 = __toESM(require("@react-native-clipboard/clipboard"));
25890
26055
  var import_react_native_webview = __toESM(require("react-native-webview"));
@@ -25952,7 +26117,7 @@ function parseEpubAnnotationEvent(raw, session, documentId) {
25952
26117
  }
25953
26118
 
25954
26119
  // components/WebViewViewer.tsx
25955
- var import_jsx_runtime4 = require("react/jsx-runtime");
26120
+ var import_jsx_runtime5 = require("react/jsx-runtime");
25956
26121
  var runtimeAsset = require_runtime();
25957
26122
  var resolveRuntimeSource = (asset) => {
25958
26123
  if (typeof asset === "string") {
@@ -25972,6 +26137,7 @@ var resolveRuntimeSource = (asset) => {
25972
26137
  };
25973
26138
  var WebViewViewer = ({
25974
26139
  engine,
26140
+ documentType,
25975
26141
  documentId,
25976
26142
  onDefineSelection,
25977
26143
  defineSelectionMode,
@@ -25979,19 +26145,20 @@ var WebViewViewer = ({
25979
26145
  onScrollOffset,
25980
26146
  onTap
25981
26147
  }) => {
25982
- const webViewRef = (0, import_react3.useRef)(null);
25983
- const { pageTheme, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = (0, import_core4.useViewerStore)();
25984
- const [selection, setSelection] = (0, import_react3.useState)(null);
26148
+ const webViewRef = (0, import_react4.useRef)(null);
26149
+ const insets = usePapyrusSafeAreaInsets();
26150
+ const { pageTheme, epubReadingMode, setDocumentState, annotations, isLoaded, locale, annotationColor, annotationOpacity, addAnnotation, beginAnnotationDraft, setSelectedAnnotation } = (0, import_core4.useViewerStore)();
26151
+ const [selection, setSelection] = (0, import_react4.useState)(null);
25985
26152
  const t = getStrings(locale);
25986
26153
  const bridgeEngine = engine;
25987
- const runtimeSource = (0, import_react3.useMemo)(
26154
+ const runtimeSource = (0, import_react4.useMemo)(
25988
26155
  () => resolveRuntimeSource(
25989
26156
  bridgeEngine.getWebViewRuntimeSource?.() ?? runtimeAsset
25990
26157
  ),
25991
26158
  [bridgeEngine]
25992
26159
  );
25993
- const [runtimeHtml, setRuntimeHtml] = (0, import_react3.useState)(null);
25994
- (0, import_react3.useEffect)(() => {
26160
+ const [runtimeHtml, setRuntimeHtml] = (0, import_react4.useState)(null);
26161
+ (0, import_react4.useEffect)(() => {
25995
26162
  let active = true;
25996
26163
  if ("html" in runtimeSource) {
25997
26164
  setRuntimeHtml(runtimeSource.html);
@@ -26012,7 +26179,7 @@ var WebViewViewer = ({
26012
26179
  active = false;
26013
26180
  };
26014
26181
  }, [runtimeSource]);
26015
- const webViewSource = (0, import_react3.useMemo)(() => {
26182
+ const webViewSource = (0, import_react4.useMemo)(() => {
26016
26183
  if (runtimeHtml !== null) {
26017
26184
  return {
26018
26185
  html: runtimeHtml,
@@ -26023,13 +26190,13 @@ var WebViewViewer = ({
26023
26190
  return runtimeSource;
26024
26191
  }, [runtimeHtml, runtimeSource]);
26025
26192
  const runtimeConfig = bridgeEngine.getWebViewRuntimeConfig?.();
26026
- const runtimeConfigScript = (0, import_react3.useMemo)(() => {
26193
+ const runtimeConfigScript = (0, import_react4.useMemo)(() => {
26027
26194
  if (!runtimeConfig) return void 0;
26028
26195
  return `window.__PAPYRUS_RUNTIME_CONFIG__=${JSON.stringify(
26029
26196
  runtimeConfig
26030
26197
  )};true;`;
26031
26198
  }, [runtimeConfig]);
26032
- (0, import_react3.useEffect)(() => {
26199
+ (0, import_react4.useEffect)(() => {
26033
26200
  bridgeEngine.attachWebView?.({
26034
26201
  postMessage: (message) => {
26035
26202
  if (__DEV__) {
@@ -26043,10 +26210,10 @@ var WebViewViewer = ({
26043
26210
  bridgeEngine.detachWebView?.();
26044
26211
  };
26045
26212
  }, [bridgeEngine]);
26046
- (0, import_react3.useEffect)(() => {
26213
+ (0, import_react4.useEffect)(() => {
26047
26214
  setSelection(null);
26048
26215
  }, [engine, documentId]);
26049
- (0, import_react3.useEffect)(() => {
26216
+ (0, import_react4.useEffect)(() => {
26050
26217
  if (!isLoaded) return;
26051
26218
  let active = true;
26052
26219
  void bridgeEngine.syncEpubAnnotations?.(annotations, documentId, { comment: t.annotationNote }).catch(() => {
@@ -26061,6 +26228,10 @@ var WebViewViewer = ({
26061
26228
  (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 }));
26062
26229
  setSelection(null);
26063
26230
  };
26231
+ (0, import_react4.useEffect)(() => {
26232
+ if (!isLoaded) return;
26233
+ webViewRef.current?.postMessage(JSON.stringify({ id: "epub-layout", kind: "set-epub-view-mode", payload: { epubMode: epubReadingMode, documentSessionId: bridgeEngine.getWebViewDocumentSessionId?.() } }));
26234
+ }, [isLoaded, epubReadingMode, bridgeEngine]);
26064
26235
  const sendSelectionCommand = (key) => {
26065
26236
  webViewRef.current?.postMessage(JSON.stringify({
26066
26237
  type: "epub-selection-action",
@@ -26084,6 +26255,11 @@ var WebViewViewer = ({
26084
26255
  let annotationRaw = raw;
26085
26256
  try {
26086
26257
  const message = JSON.parse(raw);
26258
+ if (message.type === "event" && message.name === "EPUB_LOCATION" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) {
26259
+ const location = parseEpubReadingLocation(message.payload.location);
26260
+ if (location) setDocumentState({ epubLocation: location });
26261
+ }
26262
+ if (message.type === "event" && message.name === "EPUB_CONTENT_TAP" && message.payload?.documentSessionId === bridgeEngine.getWebViewDocumentSessionId?.()) onTap?.();
26087
26263
  if (message.type === "event" && message.name === "EPUB_SELECTION_ACTION") {
26088
26264
  action = message.payload?.action;
26089
26265
  annotationRaw = JSON.stringify({ ...message, name: "EPUB_TEXT_SELECTED" });
@@ -26147,7 +26323,7 @@ var WebViewViewer = ({
26147
26323
  console.warn("[Papyrus WebView] error", event.nativeEvent);
26148
26324
  }
26149
26325
  };
26150
- const themeOverlayStyle = (0, import_react3.useMemo)(() => {
26326
+ const themeOverlayStyle = (0, import_react4.useMemo)(() => {
26151
26327
  switch (pageTheme) {
26152
26328
  case "sepia":
26153
26329
  return styles2.themeSepia;
@@ -26159,7 +26335,7 @@ var WebViewViewer = ({
26159
26335
  return styles2.themeNone;
26160
26336
  }
26161
26337
  }, [pageTheme]);
26162
- const allowingReadAccessToURL = (0, import_react3.useMemo)(() => {
26338
+ const allowingReadAccessToURL = (0, import_react4.useMemo)(() => {
26163
26339
  if (!runtimeSource || typeof runtimeSource !== "object") return void 0;
26164
26340
  if ("uri" in runtimeSource && runtimeSource.uri) {
26165
26341
  return runtimeSource.uri;
@@ -26172,8 +26348,8 @@ var WebViewViewer = ({
26172
26348
  maxWidth: resolvedMaxPageWidth,
26173
26349
  alignSelf: "center"
26174
26350
  } : void 0;
26175
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native2.View, { style: styles2.container, children: [
26176
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
26351
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native2.View, { style: [styles2.container, documentType === "epub" && { paddingTop: insets.top, paddingBottom: insets.bottom }], children: [
26352
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
26177
26353
  import_react_native_webview.default,
26178
26354
  {
26179
26355
  ref: webViewRef,
@@ -26197,7 +26373,7 @@ var WebViewViewer = ({
26197
26373
  style: [styles2.webview, cappedWebViewStyle]
26198
26374
  }
26199
26375
  ),
26200
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
26376
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
26201
26377
  import_react_native2.View,
26202
26378
  {
26203
26379
  pointerEvents: "none",
@@ -26299,22 +26475,22 @@ function getNearbyPdfNotes(annotations, id) {
26299
26475
  // components/PdfNoteChooser.tsx
26300
26476
  var import_react_native3 = require("react-native");
26301
26477
  var import_core5 = require("@papyrus-sdk/core");
26302
- var import_jsx_runtime5 = require("react/jsx-runtime");
26478
+ var import_jsx_runtime6 = require("react/jsx-runtime");
26303
26479
  function PdfNoteChooser({ notes, title, cancel, onSelect, onClose }) {
26304
- 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: [
26305
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.Text, { style: styles3.title, children: title }),
26306
- /* @__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: [
26480
+ 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: [
26481
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native3.Text, { style: styles3.title, children: title }),
26482
+ /* @__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: [
26307
26483
  index + 1,
26308
26484
  ". ",
26309
26485
  (0, import_core5.getAnnotationNote)(item) || (0, import_core5.getAnnotationQuote)(item)
26310
26486
  ] }) }) }),
26311
- /* @__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 }) })
26487
+ /* @__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 }) })
26312
26488
  ] }) }) });
26313
26489
  }
26314
26490
  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 } });
26315
26491
 
26316
26492
  // components/DedicatedAndroidPdfViewer.tsx
26317
- var import_react4 = require("react");
26493
+ var import_react5 = require("react");
26318
26494
  var import_react_native4 = require("react-native");
26319
26495
  var import_core6 = require("@papyrus-sdk/core");
26320
26496
  var import_engine_native2 = require("@papyrus-sdk/engine-native");
@@ -26335,7 +26511,7 @@ var resolvePageTapChromeVisibility = ({
26335
26511
  };
26336
26512
 
26337
26513
  // components/DedicatedAndroidPdfViewer.tsx
26338
- var import_jsx_runtime6 = require("react/jsx-runtime");
26514
+ var import_jsx_runtime7 = require("react/jsx-runtime");
26339
26515
  var TEXT_MARKUP_TOOLS2 = /* @__PURE__ */ new Set(["highlight", "underline", "squiggly", "strikeout"]);
26340
26516
  var MOBILE_CHROME_HIDE_DELTA = 28;
26341
26517
  var MOBILE_CHROME_SHOW_DELTA = 22;
@@ -26364,7 +26540,7 @@ function DedicatedAndroidPdfViewer({
26364
26540
  const searchResults = (0, import_core6.useViewerStore)((state) => state.searchResults);
26365
26541
  const annotationNavigationRequest = (0, import_core6.useViewerStore)((state) => state.annotationNavigationRequest);
26366
26542
  const annotations = (0, import_core6.useViewerStore)((state) => state.annotations);
26367
- const [pendingNotes, setPendingNotes] = (0, import_react4.useState)([]);
26543
+ const [pendingNotes, setPendingNotes] = (0, import_react5.useState)([]);
26368
26544
  const handleAnnotationTap = (id) => {
26369
26545
  const nearby = getNearbyPdfNotes(annotations, id);
26370
26546
  if (nearby.length < 2) {
@@ -26392,30 +26568,30 @@ function DedicatedAndroidPdfViewer({
26392
26568
  maxWidth: resolvedMaxPageWidth,
26393
26569
  alignSelf: "center"
26394
26570
  } : void 0;
26395
- const [selection, setSelection] = (0, import_react4.useState)(null);
26396
- const selectionRef = (0, import_react4.useRef)(null);
26397
- const lastVisiblePagesKeyRef = (0, import_react4.useRef)("");
26398
- const lastScrollOffsetYRef = (0, import_react4.useRef)(0);
26399
- const scrollDownAccumRef = (0, import_react4.useRef)(0);
26400
- const scrollUpAccumRef = (0, import_react4.useRef)(0);
26401
- const pendingChromeShowTimeoutRef = (0, import_react4.useRef)(null);
26402
- const chromeVisibleRef = (0, import_react4.useRef)(true);
26403
- const lastZoomChangedAtRef = (0, import_react4.useRef)(null);
26404
- (0, import_react4.useEffect)(() => {
26571
+ const [selection, setSelection] = (0, import_react5.useState)(null);
26572
+ const selectionRef = (0, import_react5.useRef)(null);
26573
+ const lastVisiblePagesKeyRef = (0, import_react5.useRef)("");
26574
+ const lastScrollOffsetYRef = (0, import_react5.useRef)(0);
26575
+ const scrollDownAccumRef = (0, import_react5.useRef)(0);
26576
+ const scrollUpAccumRef = (0, import_react5.useRef)(0);
26577
+ const pendingChromeShowTimeoutRef = (0, import_react5.useRef)(null);
26578
+ const chromeVisibleRef = (0, import_react5.useRef)(true);
26579
+ const lastZoomChangedAtRef = (0, import_react5.useRef)(null);
26580
+ (0, import_react5.useEffect)(() => {
26405
26581
  chromeVisibleRef.current = mobileChromeVisible;
26406
26582
  }, [mobileChromeVisible]);
26407
- const clearPendingChromeShow = (0, import_react4.useCallback)(() => {
26583
+ const clearPendingChromeShow = (0, import_react5.useCallback)(() => {
26408
26584
  if (pendingChromeShowTimeoutRef.current) {
26409
26585
  clearTimeout(pendingChromeShowTimeoutRef.current);
26410
26586
  pendingChromeShowTimeoutRef.current = null;
26411
26587
  }
26412
26588
  }, []);
26413
- const setMobileChromeVisible = (0, import_react4.useCallback)((visible, reason) => {
26589
+ const setMobileChromeVisible = (0, import_react5.useCallback)((visible, reason) => {
26414
26590
  if (chromeVisibleRef.current === visible) return;
26415
26591
  chromeVisibleRef.current = visible;
26416
26592
  setDocumentState({ mobileChromeVisible: visible });
26417
26593
  }, [setDocumentState]);
26418
- const trackMobileChromeByOffset = (0, import_react4.useCallback)((offsetY, reasonPrefix) => {
26594
+ const trackMobileChromeByOffset = (0, import_react5.useCallback)((offsetY, reasonPrefix) => {
26419
26595
  const safeOffset = Math.max(0, offsetY);
26420
26596
  const delta = safeOffset - lastScrollOffsetYRef.current;
26421
26597
  lastScrollOffsetYRef.current = safeOffset;
@@ -26451,12 +26627,12 @@ function DedicatedAndroidPdfViewer({
26451
26627
  }
26452
26628
  }
26453
26629
  }, [clearPendingChromeShow, setMobileChromeVisible]);
26454
- (0, import_react4.useEffect)(() => {
26630
+ (0, import_react5.useEffect)(() => {
26455
26631
  return () => {
26456
26632
  clearPendingChromeShow();
26457
26633
  };
26458
26634
  }, [clearPendingChromeShow]);
26459
- const applySelection = (0, import_react4.useCallback)((type) => {
26635
+ const applySelection = (0, import_react5.useCallback)((type) => {
26460
26636
  const sel = selectionRef.current;
26461
26637
  if (!sel || !sel.rects || sel.rects.length === 0) return;
26462
26638
  const bounds = sel.rects.reduce((acc, r) => ({
@@ -26478,12 +26654,12 @@ function DedicatedAndroidPdfViewer({
26478
26654
  setSelection(null);
26479
26655
  selectionRef.current = null;
26480
26656
  }, [addAnnotation, beginAnnotationDraft, annotationColor, documentId]);
26481
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native4.View, { style: styles4.container, children: [
26482
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26657
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native4.View, { style: styles4.container, children: [
26658
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26483
26659
  setPendingNotes([]);
26484
26660
  setSelectedAnnotation(id);
26485
26661
  } }),
26486
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
26662
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
26487
26663
  import_engine_native2.PapyrusPdfDocumentView,
26488
26664
  {
26489
26665
  style: [styles4.viewer, cappedViewerStyle],
@@ -26597,7 +26773,7 @@ var styles4 = import_react_native4.StyleSheet.create({
26597
26773
  });
26598
26774
 
26599
26775
  // components/DedicatedIosPdfViewer.tsx
26600
- var import_react5 = require("react");
26776
+ var import_react6 = require("react");
26601
26777
  var import_react_native5 = require("react-native");
26602
26778
  var import_core7 = require("@papyrus-sdk/core");
26603
26779
  var import_engine_native3 = require("@papyrus-sdk/engine-native");
@@ -26694,7 +26870,7 @@ var resolveNativePdfVisiblePages = (pages) => (pages ?? []).flatMap((page) => {
26694
26870
  });
26695
26871
 
26696
26872
  // components/DedicatedIosPdfViewer.tsx
26697
- var import_jsx_runtime7 = require("react/jsx-runtime");
26873
+ var import_jsx_runtime8 = require("react/jsx-runtime");
26698
26874
  var MOBILE_CHROME_HIDE_DELTA2 = 28;
26699
26875
  var MOBILE_CHROME_SHOW_DELTA2 = 22;
26700
26876
  var MOBILE_CHROME_SHOW_DELAY_MS2 = 180;
@@ -26726,7 +26902,7 @@ function DedicatedIosPdfViewer({
26726
26902
  const inkStrokeWidth = (0, import_core7.useViewerStore)((state) => state.inkStrokeWidth);
26727
26903
  const annotationNavigationRequest = (0, import_core7.useViewerStore)((state) => state.annotationNavigationRequest);
26728
26904
  const annotations = (0, import_core7.useViewerStore)((state) => state.annotations);
26729
- const [pendingNotes, setPendingNotes] = (0, import_react5.useState)([]);
26905
+ const [pendingNotes, setPendingNotes] = (0, import_react6.useState)([]);
26730
26906
  const handleAnnotationTap = (id) => {
26731
26907
  const nearby = getNearbyPdfNotes(annotations, id);
26732
26908
  if (nearby.length < 2) {
@@ -26762,16 +26938,16 @@ function DedicatedIosPdfViewer({
26762
26938
  supportsNativeEditMenu
26763
26939
  );
26764
26940
  const resolvedMaxPageWidth = typeof maxPageWidth === "number" && Number.isFinite(maxPageWidth) && maxPageWidth > 0 ? maxPageWidth : void 0;
26765
- const chromeVisibleRef = (0, import_react5.useRef)(mobileChromeVisible);
26766
- const lastScrollOffsetYRef = (0, import_react5.useRef)(0);
26767
- const scrollDownAccumRef = (0, import_react5.useRef)(0);
26768
- const scrollUpAccumRef = (0, import_react5.useRef)(0);
26769
- const pendingChromeShowTimeoutRef = (0, import_react5.useRef)(null);
26770
- const lastZoomChangedAtRef = (0, import_react5.useRef)(null);
26771
- const lastVisiblePagesKeyRef = (0, import_react5.useRef)("");
26772
- const [selection, setSelection] = (0, import_react5.useState)(null);
26773
- const selectionRef = (0, import_react5.useRef)(null);
26774
- (0, import_react5.useEffect)(() => {
26941
+ const chromeVisibleRef = (0, import_react6.useRef)(mobileChromeVisible);
26942
+ const lastScrollOffsetYRef = (0, import_react6.useRef)(0);
26943
+ const scrollDownAccumRef = (0, import_react6.useRef)(0);
26944
+ const scrollUpAccumRef = (0, import_react6.useRef)(0);
26945
+ const pendingChromeShowTimeoutRef = (0, import_react6.useRef)(null);
26946
+ const lastZoomChangedAtRef = (0, import_react6.useRef)(null);
26947
+ const lastVisiblePagesKeyRef = (0, import_react6.useRef)("");
26948
+ const [selection, setSelection] = (0, import_react6.useState)(null);
26949
+ const selectionRef = (0, import_react6.useRef)(null);
26950
+ (0, import_react6.useEffect)(() => {
26775
26951
  const pickerActive = supportsNativeEditMenu && activeTool === "ink";
26776
26952
  setDocumentState({ nativeInkToolPickerActive: pickerActive });
26777
26953
  return () => {
@@ -26780,15 +26956,15 @@ function DedicatedIosPdfViewer({
26780
26956
  }
26781
26957
  };
26782
26958
  }, [activeTool, setDocumentState]);
26783
- (0, import_react5.useEffect)(() => {
26959
+ (0, import_react6.useEffect)(() => {
26784
26960
  chromeVisibleRef.current = mobileChromeVisible;
26785
26961
  }, [mobileChromeVisible]);
26786
- const clearPendingChromeShow = (0, import_react5.useCallback)(() => {
26962
+ const clearPendingChromeShow = (0, import_react6.useCallback)(() => {
26787
26963
  if (!pendingChromeShowTimeoutRef.current) return;
26788
26964
  clearTimeout(pendingChromeShowTimeoutRef.current);
26789
26965
  pendingChromeShowTimeoutRef.current = null;
26790
26966
  }, []);
26791
- const setMobileChromeVisible = (0, import_react5.useCallback)(
26967
+ const setMobileChromeVisible = (0, import_react6.useCallback)(
26792
26968
  (visible) => {
26793
26969
  if (chromeVisibleRef.current === visible) return;
26794
26970
  chromeVisibleRef.current = visible;
@@ -26796,14 +26972,14 @@ function DedicatedIosPdfViewer({
26796
26972
  },
26797
26973
  [setDocumentState]
26798
26974
  );
26799
- const updateSelection = (0, import_react5.useCallback)(
26975
+ const updateSelection = (0, import_react6.useCallback)(
26800
26976
  (nextSelection) => {
26801
26977
  selectionRef.current = nextSelection;
26802
26978
  setSelection(nextSelection);
26803
26979
  },
26804
26980
  []
26805
26981
  );
26806
- const handleTextSelectionChange = (0, import_react5.useCallback)(
26982
+ const handleTextSelectionChange = (0, import_react6.useCallback)(
26807
26983
  (event) => {
26808
26984
  const nextSelection = resolveNativePdfTextSelection(event.nativeEvent);
26809
26985
  updateSelection(nextSelection);
@@ -26816,7 +26992,7 @@ function DedicatedIosPdfViewer({
26816
26992
  },
26817
26993
  [onTextSelected, setSelectedAnnotation, updateSelection]
26818
26994
  );
26819
- const defineSelection = (0, import_react5.useCallback)(() => {
26995
+ const defineSelection = (0, import_react6.useCallback)(() => {
26820
26996
  const currentSelection = selectionRef.current;
26821
26997
  if (!currentSelection) return;
26822
26998
  onDefineSelection?.({
@@ -26825,7 +27001,7 @@ function DedicatedIosPdfViewer({
26825
27001
  });
26826
27002
  updateSelection(null);
26827
27003
  }, [onDefineSelection, updateSelection]);
26828
- const handleNativeDefineSelection = (0, import_react5.useCallback)(
27004
+ const handleNativeDefineSelection = (0, import_react6.useCallback)(
26829
27005
  (event) => {
26830
27006
  const text = event.nativeEvent?.text;
26831
27007
  const pageIndex = event.nativeEvent?.pageIndex;
@@ -26837,7 +27013,7 @@ function DedicatedIosPdfViewer({
26837
27013
  },
26838
27014
  [onDefineSelection, updateSelection]
26839
27015
  );
26840
- const handleInkToolPickerVisibilityChange = (0, import_react5.useCallback)(
27016
+ const handleInkToolPickerVisibilityChange = (0, import_react6.useCallback)(
26841
27017
  (event) => {
26842
27018
  handleNativeInkPickerVisibility(
26843
27019
  event.nativeEvent?.visible === true,
@@ -26846,7 +27022,7 @@ function DedicatedIosPdfViewer({
26846
27022
  },
26847
27023
  []
26848
27024
  );
26849
- const trackMobileChromeByOffset = (0, import_react5.useCallback)(
27025
+ const trackMobileChromeByOffset = (0, import_react6.useCallback)(
26850
27026
  (offsetY) => {
26851
27027
  if (pageCount <= 0) return;
26852
27028
  const safeOffset = Math.max(0, offsetY);
@@ -26882,11 +27058,11 @@ function DedicatedIosPdfViewer({
26882
27058
  },
26883
27059
  [clearPendingChromeShow, pageCount, setMobileChromeVisible]
26884
27060
  );
26885
- (0, import_react5.useEffect)(
27061
+ (0, import_react6.useEffect)(
26886
27062
  () => () => clearPendingChromeShow(),
26887
27063
  [clearPendingChromeShow]
26888
27064
  );
26889
- const handleVisiblePagesChange = (0, import_react5.useCallback)(
27065
+ const handleVisiblePagesChange = (0, import_react6.useCallback)(
26890
27066
  (event) => {
26891
27067
  const pages = resolveNativePdfVisiblePages(
26892
27068
  event.nativeEvent?.pages
@@ -26901,13 +27077,13 @@ function DedicatedIosPdfViewer({
26901
27077
  },
26902
27078
  [setDocumentState]
26903
27079
  );
26904
- const handleScroll = (0, import_react5.useCallback)(
27080
+ const handleScroll = (0, import_react6.useCallback)(
26905
27081
  (event) => {
26906
27082
  trackMobileChromeByOffset(event.nativeEvent?.offsetY ?? 0);
26907
27083
  },
26908
27084
  [trackMobileChromeByOffset]
26909
27085
  );
26910
- const handleTap = (0, import_react5.useCallback)(() => {
27086
+ const handleTap = (0, import_react6.useCallback)(() => {
26911
27087
  setSelectedAnnotation(null);
26912
27088
  const nextVisible = resolvePageTapChromeVisibility({
26913
27089
  chromeVisible: chromeVisibleRef.current,
@@ -26917,7 +27093,7 @@ function DedicatedIosPdfViewer({
26917
27093
  });
26918
27094
  if (nextVisible !== null) setMobileChromeVisible(nextVisible);
26919
27095
  }, [activeTool, selectionActive, setMobileChromeVisible, setSelectedAnnotation]);
26920
- const handleDeleteAnnotation = (0, import_react5.useCallback)(
27096
+ const handleDeleteAnnotation = (0, import_react6.useCallback)(
26921
27097
  (annotationId) => {
26922
27098
  deleteAnnotationAndClearSelection(
26923
27099
  annotationId,
@@ -26932,12 +27108,12 @@ function DedicatedIosPdfViewer({
26932
27108
  maxWidth: resolvedMaxPageWidth,
26933
27109
  alignSelf: "center"
26934
27110
  } : void 0;
26935
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native5.View, { style: styles5.container, children: [
26936
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
27111
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native5.View, { style: styles5.container, children: [
27112
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PdfNoteChooser, { notes: pendingNotes, title: t.annotationNote, cancel: t.cancel, onClose: () => setPendingNotes([]), onSelect: (id) => {
26937
27113
  setPendingNotes([]);
26938
27114
  setSelectedAnnotation(id);
26939
27115
  } }),
26940
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27116
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
26941
27117
  import_engine_native3.PapyrusPdfDocumentView,
26942
27118
  {
26943
27119
  style: [styles5.viewer, cappedViewerStyle],
@@ -27006,22 +27182,22 @@ function DedicatedIosPdfViewer({
27006
27182
  onInkToolPickerVisibilityChange: handleInkToolPickerVisibilityChange
27007
27183
  }
27008
27184
  ),
27009
- 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)(
27185
+ 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)(
27010
27186
  import_react_native5.Pressable,
27011
27187
  {
27012
27188
  onPress: defineSelection,
27013
27189
  style: styles5.fallbackButton,
27014
27190
  accessibilityRole: "button",
27015
27191
  accessibilityLabel: t.define,
27016
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native5.Text, { style: styles5.fallbackButtonText, children: t.define })
27192
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.Text, { style: styles5.fallbackButtonText, children: t.define })
27017
27193
  }
27018
27194
  ) }),
27019
- fallbackDeleteAnnotation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27195
+ fallbackDeleteAnnotation && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27020
27196
  import_react_native5.View,
27021
27197
  {
27022
27198
  style: styles5.annotationDeleteFallback,
27023
27199
  pointerEvents: "box-none",
27024
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
27200
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27025
27201
  import_react_native5.Pressable,
27026
27202
  {
27027
27203
  onPress: () => handleDeleteAnnotation(fallbackDeleteAnnotation.id),
@@ -27029,7 +27205,7 @@ function DedicatedIosPdfViewer({
27029
27205
  accessibilityRole: "button",
27030
27206
  accessibilityLabel: t.deleteAnnotation,
27031
27207
  hitSlop: 8,
27032
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native5.Text, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
27208
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native5.Text, { style: styles5.annotationDeleteButtonText, children: t.deleteAnnotation })
27033
27209
  }
27034
27210
  )
27035
27211
  }
@@ -27092,7 +27268,7 @@ var styles5 = import_react_native5.StyleSheet.create({
27092
27268
  });
27093
27269
 
27094
27270
  // components/NativePdfDocumentViewer.tsx
27095
- var import_jsx_runtime8 = require("react/jsx-runtime");
27271
+ var import_jsx_runtime9 = require("react/jsx-runtime");
27096
27272
  function NativePdfDocumentViewer({
27097
27273
  engine,
27098
27274
  documentId,
@@ -27105,7 +27281,7 @@ function NativePdfDocumentViewer({
27105
27281
  import_react_native6.Platform.OS
27106
27282
  );
27107
27283
  if (implementation === "android") {
27108
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27284
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
27109
27285
  DedicatedAndroidPdfViewer,
27110
27286
  {
27111
27287
  engine,
@@ -27118,7 +27294,7 @@ function NativePdfDocumentViewer({
27118
27294
  );
27119
27295
  }
27120
27296
  if (implementation === "ios") {
27121
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
27297
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
27122
27298
  DedicatedIosPdfViewer,
27123
27299
  {
27124
27300
  engine,
@@ -27172,26 +27348,6 @@ var resolveMobileChromeOffsets = (insets) => ({
27172
27348
  right: MOBILE_CHROME_METRICS.screenPadding + Math.max(0, insets.right)
27173
27349
  });
27174
27350
 
27175
- // components/PapyrusSafeArea.tsx
27176
- var import_react6 = __toESM(require("react"));
27177
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
27178
- var import_jsx_runtime9 = require("react/jsx-runtime");
27179
- var papyrusSafeAreaContext = import_react_native_safe_area_context.SafeAreaInsetsContext;
27180
- var ZERO_SAFE_AREA_INSETS = {
27181
- top: 0,
27182
- bottom: 0,
27183
- left: 0,
27184
- right: 0
27185
- };
27186
- var usePapyrusSafeAreaInsets = () => (0, import_react6.useContext)(papyrusSafeAreaContext) ?? ZERO_SAFE_AREA_INSETS;
27187
- function PapyrusSafeAreaBoundary({
27188
- children
27189
- }) {
27190
- const insets = (0, import_react6.useContext)(papyrusSafeAreaContext);
27191
- if (insets) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children });
27192
- return import_react6.default.createElement(import_react_native_safe_area_context.SafeAreaProvider, null, children);
27193
- }
27194
-
27195
27351
  // components/NativeTextDocumentViewer.tsx
27196
27352
  var import_jsx_runtime10 = require("react/jsx-runtime");
27197
27353
  function NativeTextDocumentViewer({
@@ -27740,6 +27896,7 @@ var scrollViewerListToOffset = (list, offset, animated) => {
27740
27896
  list.scrollToOffset?.({ offset, animated });
27741
27897
  };
27742
27898
  var Viewer = ({
27899
+ documentType,
27743
27900
  documentId,
27744
27901
  engine,
27745
27902
  maxPageWidth,
@@ -29267,14 +29424,8 @@ var Viewer = ({
29267
29424
  },
29268
29425
  [mobilePerf, perfEnabled, scrollPerfSession]
29269
29426
  );
29270
- const handleWebViewScroll = (0, import_react9.useCallback)(
29271
- (_offsetY) => {
29272
- if (!chromeVisibleRef.current) {
29273
- setMobileChromeVisible(true, "scroll.webview.recover");
29274
- }
29275
- },
29276
- [setMobileChromeVisible]
29277
- );
29427
+ const handleWebViewScroll = (0, import_react9.useCallback)((_offsetY) => {
29428
+ }, []);
29278
29429
  const handlePageTap = (0, import_react9.useCallback)(() => {
29279
29430
  const nextVisible = resolvePageTapChromeVisibility({
29280
29431
  chromeVisible: chromeVisibleRef.current,
@@ -29496,6 +29647,7 @@ var Viewer = ({
29496
29647
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native9.View, { style: [styles8.container, isDark && styles8.containerDark], children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
29497
29648
  WebViewViewer_default,
29498
29649
  {
29650
+ documentType,
29499
29651
  engine,
29500
29652
  documentId,
29501
29653
  onDefineSelection,
@@ -30042,6 +30194,7 @@ var styles9 = import_react_native11.StyleSheet.create({
30042
30194
  // components/PageJumpModal.tsx
30043
30195
  var import_jsx_runtime14 = require("react/jsx-runtime");
30044
30196
  function PageJumpModal({
30197
+ documentType,
30045
30198
  visible,
30046
30199
  currentPage,
30047
30200
  pageCount,
@@ -30068,8 +30221,8 @@ function PageJumpModal({
30068
30221
  isDark,
30069
30222
  maxHeight: 300,
30070
30223
  showHeader: true,
30071
- title: "Ir para p\xE1gina",
30072
- closeAccessibilityLabel: "Close page jump",
30224
+ title: documentType === "epub" ? "Ir para cap\xEDtulo" : "Ir para p\xE1gina",
30225
+ closeAccessibilityLabel: "Fechar navega\xE7\xE3o",
30073
30226
  sheetStyle: {
30074
30227
  paddingHorizontal: 18,
30075
30228
  paddingBottom: 18,
@@ -30097,7 +30250,7 @@ function PageJumpModal({
30097
30250
  }
30098
30251
  ],
30099
30252
  onSubmitEditing: confirmJump,
30100
- accessibilityLabel: "Page jump input",
30253
+ accessibilityLabel: documentType === "epub" ? "N\xFAmero do cap\xEDtulo" : "N\xFAmero da p\xE1gina",
30101
30254
  testID: "papyrus-page-jump-input"
30102
30255
  }
30103
30256
  ),
@@ -30152,6 +30305,7 @@ var styles10 = import_react_native12.StyleSheet.create({
30152
30305
  var import_jsx_runtime15 = require("react/jsx-runtime");
30153
30306
  var Topbar = ({
30154
30307
  engine,
30308
+ documentType,
30155
30309
  onOpenSettings,
30156
30310
  onOpenOverflow,
30157
30311
  title,
@@ -30172,6 +30326,12 @@ var Topbar = ({
30172
30326
  locale
30173
30327
  } = (0, import_core12.useViewerStore)();
30174
30328
  const inkSession = useNativeInkSession();
30329
+ const chromeVisible = mobileChromeVisible || inkSession.active;
30330
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
30331
+ const chromeMotion = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
30332
+ "worklet";
30333
+ return { opacity: chromeProgress.value };
30334
+ });
30175
30335
  const t = getStrings(locale);
30176
30336
  const progress = useNativeInkMotion(inkSession.active, inkSession.supported);
30177
30337
  const settingsMotion = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
@@ -30227,26 +30387,30 @@ var Topbar = ({
30227
30387
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSettings, { size: MOBILE_CHROME_METRICS.iconSize, color: isDark ? "#e5e7eb" : "#111827" })
30228
30388
  }
30229
30389
  );
30230
- if (!mobileChromeVisible && !inkSession.active) return null;
30390
+ if (!chromeVisible && documentType !== "epub") return null;
30231
30391
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
30232
30392
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
30233
- import_react_native13.View,
30393
+ import_react_native_reanimated2.default.View,
30234
30394
  {
30235
30395
  style: [
30236
30396
  styles11.chromeFrame,
30397
+ chromeMotion,
30237
30398
  {
30238
30399
  paddingTop: insets.top,
30239
30400
  paddingLeft: MOBILE_CHROME_METRICS.screenPadding + insets.left,
30240
30401
  paddingRight: MOBILE_CHROME_METRICS.screenPadding + insets.right
30241
30402
  }
30242
30403
  ],
30243
- pointerEvents: "box-none",
30404
+ pointerEvents: chromeVisible ? "box-none" : "none",
30405
+ accessibilityElementsHidden: !chromeVisible,
30406
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
30244
30407
  testID: "papyrus-floating-top-controls",
30245
30408
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
30246
30409
  import_react_native13.View,
30247
30410
  {
30248
30411
  style: [
30249
30412
  styles11.container,
30413
+ import_react_native13.Platform.OS === "ios" && styles11.containerIos,
30250
30414
  isDark && styles11.containerDark,
30251
30415
  isLandscape && styles11.containerLandscape
30252
30416
  ],
@@ -30386,6 +30550,7 @@ var Topbar = ({
30386
30550
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
30387
30551
  PageJumpModal,
30388
30552
  {
30553
+ documentType,
30389
30554
  visible: jumpModalOpen,
30390
30555
  currentPage,
30391
30556
  pageCount,
@@ -30403,7 +30568,8 @@ var styles11 = import_react_native13.StyleSheet.create({
30403
30568
  maxWidth: MOBILE_CHROME_METRICS.maxFloatingWidth,
30404
30569
  marginTop: 10,
30405
30570
  paddingHorizontal: 14,
30406
- paddingVertical: 10,
30571
+ height: MOBILE_CHROME_METRICS.topbarHeight,
30572
+ paddingVertical: 0,
30407
30573
  backgroundColor: "rgba(255,255,255,0.88)",
30408
30574
  borderWidth: 1,
30409
30575
  borderColor: "rgba(255,255,255,0.72)",
@@ -30420,6 +30586,10 @@ var styles11 = import_react_native13.StyleSheet.create({
30420
30586
  backgroundColor: "rgba(15,17,21,0.88)",
30421
30587
  borderColor: "rgba(71,85,105,0.48)"
30422
30588
  },
30589
+ containerIos: {
30590
+ height: MOBILE_CHROME_METRICS.topbarHeight,
30591
+ paddingVertical: 0
30592
+ },
30423
30593
  containerLandscape: {
30424
30594
  maxWidth: void 0,
30425
30595
  marginTop: 0
@@ -32556,10 +32726,10 @@ var import_react_native16 = require("react-native");
32556
32726
  var import_core15 = require("@papyrus-sdk/core");
32557
32727
  var import_jsx_runtime18 = require("react/jsx-runtime");
32558
32728
  var COLORS = ["#fbbf24", "#fb7185", "#60a5fa", "#34d399", "#c084fc"];
32559
- var AnnotationEditor = () => {
32729
+ var AnnotationEditor = ({ documentId }) => {
32560
32730
  const { annotations, annotationDraft, clearAnnotationDraft, addAnnotation, selectedAnnotationId, updateAnnotation, removeAnnotation, setSelectedAnnotation, uiTheme, locale, accentColor } = (0, import_core15.useViewerStore)();
32561
32731
  const annotation = annotationDraft?.id === selectedAnnotationId ? annotationDraft : annotations.find((a) => a.id === selectedAnnotationId);
32562
- const supported = annotation && annotation.type !== "ink";
32732
+ const supported = annotation && annotation.type !== "ink" && (!documentId || !annotation.anchor?.documentId || annotation.anchor.documentId === documentId);
32563
32733
  const [editing, setEditing] = (0, import_react16.useState)(false);
32564
32734
  const [draft, setDraft] = (0, import_react16.useState)("");
32565
32735
  const [color, setColor] = (0, import_react16.useState)(COLORS[0]);
@@ -32624,14 +32794,34 @@ var AnnotationEditor = () => {
32624
32794
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native16.Animated.View, { accessibilityViewIsModal: true, style: [styles14.card, { backgroundColor: dark ? "#17191f" : "#fff", marginBottom: Math.max(16, insets.bottom), opacity: progress, transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [12, 0] }) }] }], children: [
32625
32795
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native16.View, { style: styles14.header, children: [
32626
32796
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.title, textStyle], children: editing ? t.editNote : t.annotationNote }),
32627
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: t.close, style: styles14.button, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: textStyle, children: "\xD7" }) })
32797
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Pressable, { onPress: close, accessibilityRole: "button", accessibilityLabel: t.close, style: [styles14.button, { borderRadius: 22, backgroundColor: dark ? "#242936" : "#f1f5f9" }], children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconClose, { size: 20, color: textStyle.color }) })
32628
32798
  ] }),
32629
32799
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native16.ScrollView, { style: styles14.content, keyboardShouldPersistTaps: "handled", children: [
32630
32800
  quote ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.quote, textStyle], selectable: true, children: quote }) : null,
32631
32801
  editing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
32632
32802
  /* @__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" }] }),
32633
32803
  /* @__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)) }),
32634
- /* @__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)) })
32804
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.View, { style: styles14.markupOptions, children: [
32805
+ ["highlight", t.annotationHighlight, IconHighlight],
32806
+ ["underline", t.annotationUnderline, IconUnderline],
32807
+ ["strikeout", t.annotationStrikeout, IconStrikeout],
32808
+ ["none", t.annotationIndicatorOnly, IconComment]
32809
+ ].map(([style, label, Icon]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
32810
+ import_react_native16.Pressable,
32811
+ {
32812
+ onPress: () => setMarkup(style),
32813
+ accessibilityRole: "button",
32814
+ accessibilityLabel: label,
32815
+ accessibilityState: { selected: markup === style },
32816
+ testID: `annotation-style-${style}`,
32817
+ style: [styles14.markupButton, { backgroundColor: dark ? "#242936" : "#f1f5f9", borderColor: markup === style ? accentColor : "transparent" }],
32818
+ children: [
32819
+ /* @__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 }) }),
32820
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.markupLabel, textStyle], children: style === "none" ? locale === "pt-BR" ? "Indicador" : "Indicator" : label })
32821
+ ]
32822
+ },
32823
+ style
32824
+ )) })
32635
32825
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native16.Text, { style: [styles14.note, textStyle], selectable: true, children: (0, import_core15.getAnnotationNote)(annotation) || t.notePlaceholder })
32636
32826
  ] }),
32637
32827
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native16.View, { style: styles14.actions, children: [
@@ -32654,7 +32844,7 @@ var AnnotationEditor = () => {
32654
32844
  ] })
32655
32845
  ] }) });
32656
32846
  };
32657
- 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 } });
32847
+ 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 } });
32658
32848
  var AnnotationEditor_default = AnnotationEditor;
32659
32849
 
32660
32850
  // components/BottomBar.tsx
@@ -32717,10 +32907,19 @@ var BottomBar = ({
32717
32907
  documentType,
32718
32908
  onOpenInfo,
32719
32909
  onOpenSettings,
32720
- onOpenDestination
32910
+ onOpenDestination,
32911
+ onTurnPage,
32912
+ onNavigateToPage,
32913
+ onOpenPages
32721
32914
  }) => {
32722
32915
  const {
32723
32916
  activeMobileDestination,
32917
+ viewMode,
32918
+ epubReadingMode,
32919
+ epubLocation,
32920
+ comicLayoutMode,
32921
+ currentPage,
32922
+ pageCount,
32724
32923
  mobileDockVisible,
32725
32924
  setDocumentState,
32726
32925
  uiTheme,
@@ -32735,7 +32934,7 @@ var BottomBar = ({
32735
32934
  const supportsInkChrome = documentType === "pdf" && inkSession.supported;
32736
32935
  const drawing = supportsInkChrome && inkSession.active;
32737
32936
  const dockVisible = mobileChromeVisible && mobileDockVisible && !drawing;
32738
- const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome);
32937
+ const dockProgress = useNativeInkMotion(dockVisible, supportsInkChrome || documentType === "epub");
32739
32938
  const dockMotion = (0, import_react_native_reanimated3.useAnimatedStyle)(() => {
32740
32939
  "worklet";
32741
32940
  return {
@@ -32757,6 +32956,15 @@ var BottomBar = ({
32757
32956
  activeMobileDestination,
32758
32957
  toolDockOpen
32759
32958
  });
32959
+ const paginated = documentType !== "text" && (documentType === "comic" ? comicLayoutMode === "single" : documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single");
32960
+ if (paginated) {
32961
+ layout.leftSlots.push(...layout.rightSlots.filter((slot) => slot.key === "search"));
32962
+ layout.rightSlots = [];
32963
+ }
32964
+ const turnPage = (direction) => {
32965
+ if (onTurnPage) onTurnPage(direction);
32966
+ else onNavigateToPage?.(currentPage + direction);
32967
+ };
32760
32968
  const slotMeta = {
32761
32969
  annotate: {
32762
32970
  label: t.tools,
@@ -32817,7 +33025,7 @@ var BottomBar = ({
32817
33025
  onPress: onOpenSettings
32818
33026
  }
32819
33027
  };
32820
- if (!dockVisible && !supportsInkChrome) return null;
33028
+ if (!dockVisible && !supportsInkChrome && documentType !== "epub") return null;
32821
33029
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
32822
33030
  import_react_native_reanimated3.default.View,
32823
33031
  {
@@ -32882,7 +33090,11 @@ var BottomBar = ({
32882
33090
  isDark && styles15.islandDark
32883
33091
  ],
32884
33092
  testID: "papyrus-floating-bottom-dock",
32885
- children: layout.rightSlots.map((slot) => {
33093
+ children: paginated ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
33094
+ /* @__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) }) }),
33095
+ /* @__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}` }) }),
33096
+ /* @__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) }) })
33097
+ ] }) : layout.rightSlots.map((slot) => {
32886
33098
  const meta = slotMeta[slot.key];
32887
33099
  const Icon = meta.icon;
32888
33100
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -32945,8 +33157,9 @@ var styles15 = import_react_native17.StyleSheet.create({
32945
33157
  island: {
32946
33158
  flexDirection: "row",
32947
33159
  alignItems: "center",
32948
- paddingVertical: 8,
32949
- paddingHorizontal: 10,
33160
+ height: MOBILE_CHROME_METRICS.bottomBarVisualHeight,
33161
+ paddingVertical: 0,
33162
+ paddingHorizontal: 6,
32950
33163
  backgroundColor: "rgba(255,255,255,0.9)",
32951
33164
  borderWidth: 1,
32952
33165
  borderColor: "rgba(255,255,255,0.72)",
@@ -32972,6 +33185,8 @@ var styles15 = import_react_native17.StyleSheet.create({
32972
33185
  borderColor: "rgba(71,85,105,0.48)"
32973
33186
  },
32974
33187
  iconOnlyItem: {
33188
+ minWidth: 44,
33189
+ height: 44,
32975
33190
  alignItems: "center",
32976
33191
  justifyContent: "center",
32977
33192
  paddingHorizontal: MOBILE_CHROME_METRICS.bottomBarItemPaddingHorizontal,
@@ -33048,10 +33263,12 @@ var SettingsSheet = ({
33048
33263
  engine,
33049
33264
  visible,
33050
33265
  onClose,
33051
- documentType = "pdf"
33266
+ documentType = "pdf",
33267
+ onOpenInfo
33052
33268
  }) => {
33053
33269
  const {
33054
33270
  viewMode,
33271
+ epubReadingMode,
33055
33272
  uiTheme,
33056
33273
  zoom,
33057
33274
  setDocumentState,
@@ -33063,12 +33280,16 @@ var SettingsSheet = ({
33063
33280
  comicReadingDirection
33064
33281
  } = (0, import_core17.useViewerStore)();
33065
33282
  const isDark = uiTheme === "dark";
33066
- const isPaged = viewMode === "single";
33283
+ const isPaged = documentType === "epub" ? epubReadingMode === "paged" : viewMode === "single";
33067
33284
  const isDouble = viewMode === "double";
33068
33285
  const t = getStrings(locale);
33069
33286
  const { height: windowHeight } = (0, import_react_native18.useWindowDimensions)();
33070
33287
  const sheetMaxHeight = getSettingsSheetMaxHeight(windowHeight);
33071
33288
  const handleTransition = (mode) => {
33289
+ if (documentType === "epub") {
33290
+ setDocumentState({ epubReadingMode: mode === "paged" ? "paged" : "chapter-scroll" });
33291
+ return;
33292
+ }
33072
33293
  if (mode === "paged") {
33073
33294
  setDocumentState({ viewMode: "single" });
33074
33295
  return;
@@ -33113,6 +33334,7 @@ var SettingsSheet = ({
33113
33334
  contentContainerStyle: styles16.sheetContent,
33114
33335
  showsVerticalScrollIndicator: false,
33115
33336
  children: [
33337
+ onOpenInfo ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native18.Pressable, { accessibilityRole: "button", onPress: onOpenInfo, style: { minHeight: 44, justifyContent: "center", marginBottom: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native18.Text, { style: [styles16.sectionTitle, isDark && styles16.sectionTitleDark], children: t.info }) }) : null,
33116
33338
  (documentType === "text" || documentType === "epub") && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: styles16.section, children: [
33117
33339
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native18.Text, { style: [styles16.sectionTitle, isDark && styles16.sectionTitleDark], children: locale === "pt-BR" ? "Tamanho da fonte" : "Font size" }),
33118
33340
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
@@ -33291,7 +33513,7 @@ var SettingsSheet = ({
33291
33513
  isDark && styles16.optionTextDark,
33292
33514
  (!isPaged || isDouble) && styles16.optionTextActive
33293
33515
  ],
33294
- children: t.continuous
33516
+ children: documentType === "epub" && locale === "pt-BR" ? "Rolagem por cap\xEDtulo" : t.continuous
33295
33517
  }
33296
33518
  )
33297
33519
  }
@@ -33321,7 +33543,7 @@ var SettingsSheet = ({
33321
33543
  )
33322
33544
  ] })
33323
33545
  ] }),
33324
- documentType !== "comic" && documentType !== "text" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: styles16.section, children: [
33546
+ documentType === "pdf" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native18.View, { style: styles16.section, children: [
33325
33547
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
33326
33548
  import_react_native18.Text,
33327
33549
  {
@@ -34199,6 +34421,7 @@ var styles20 = import_react_native22.StyleSheet.create({
34199
34421
  // components/ProgressPill.tsx
34200
34422
  var import_react18 = require("react");
34201
34423
  var import_react_native23 = require("react-native");
34424
+ var import_react_native_reanimated4 = __toESM(require("react-native-reanimated"));
34202
34425
  var import_core21 = require("@papyrus-sdk/core");
34203
34426
 
34204
34427
  // components/progressPillMode.ts
@@ -34218,9 +34441,11 @@ var import_jsx_runtime25 = require("react/jsx-runtime");
34218
34441
  var clampPercent = (value) => Math.max(0, Math.min(100, Math.round(value)));
34219
34442
  function ProgressPill({
34220
34443
  documentType,
34444
+ navigationInDock = false,
34221
34445
  onPress,
34222
34446
  onOpenPageJump,
34223
34447
  onNavigateToPage,
34448
+ onTurnPage,
34224
34449
  onScrubbingChange
34225
34450
  }) {
34226
34451
  const {
@@ -34230,12 +34455,20 @@ function ProgressPill({
34230
34455
  textLength,
34231
34456
  comicLayoutMode,
34232
34457
  viewMode,
34458
+ epubReadingMode,
34459
+ epubLocation,
34233
34460
  locale,
34234
34461
  uiTheme,
34235
34462
  accentColor,
34236
34463
  mobileChromeVisible,
34237
34464
  mobileProgressPillVisible
34238
34465
  } = (0, import_core21.useViewerStore)();
34466
+ const chromeVisible = mobileChromeVisible && mobileProgressPillVisible;
34467
+ const chromeProgress = useNativeInkMotion(chromeVisible, documentType === "epub");
34468
+ const chromeMotion = (0, import_react_native_reanimated4.useAnimatedStyle)(() => {
34469
+ "worklet";
34470
+ return { opacity: chromeProgress.value };
34471
+ });
34239
34472
  const t = getStrings(locale);
34240
34473
  const isDark = uiTheme === "dark";
34241
34474
  const offsets = resolveMobileChromeOffsets(usePapyrusSafeAreaInsets());
@@ -34280,10 +34513,10 @@ function ProgressPill({
34280
34513
  return `${displayedPage}/${pageCount || 0}`;
34281
34514
  }
34282
34515
  if (documentType === "epub") {
34283
- return `Cap. ${displayedPage} \xB7 ${percent}%`;
34516
+ return `Cap. ${displayedPage} \xB7 ${scrubPreviewPage === null && epubLocation ? clampPercent(epubLocation.progress * 100) : percent}%`;
34284
34517
  }
34285
34518
  return `${percent}%`;
34286
- }, [displayedPage, documentType, pageCount]);
34519
+ }, [displayedPage, documentType, pageCount, epubLocation, scrubPreviewPage]);
34287
34520
  const thumbTop = resolvePageScrubberThumbTop({
34288
34521
  currentPage,
34289
34522
  trackHeight,
@@ -34325,7 +34558,7 @@ function ProgressPill({
34325
34558
  },
34326
34559
  []
34327
34560
  );
34328
- if (!shouldRenderPageScrubber({
34561
+ if (!(documentType === "epub" && mobileProgressPillVisible) && !shouldRenderPageScrubber({
34329
34562
  mobileChromeVisible,
34330
34563
  mobileProgressPillVisible,
34331
34564
  isScrubbing: isScrubbingRef.current
@@ -34356,16 +34589,17 @@ function ProgressPill({
34356
34589
  }
34357
34590
  );
34358
34591
  }
34359
- const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : viewMode;
34592
+ const progressViewMode = documentType === "comic" ? comicLayoutMode === "single" ? "single" : "continuous" : documentType === "epub" ? epubReadingMode === "paged" ? "single" : "continuous" : viewMode;
34360
34593
  const pillMode = resolveProgressPillMode(progressViewMode, currentPage, pageCount);
34594
+ if (pillMode.kind === "navigation" && navigationInDock) return null;
34361
34595
  if (pillMode.kind === "navigation") {
34362
- const previousDisabled = pillMode.previousPage === null || !onNavigateToPage;
34363
- const nextDisabled = pillMode.nextPage === null || !onNavigateToPage;
34596
+ const previousDisabled = !onTurnPage && (pillMode.previousPage === null || !onNavigateToPage);
34597
+ const nextDisabled = !onTurnPage && (pillMode.nextPage === null || !onNavigateToPage);
34364
34598
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
34365
34599
  import_react_native23.View,
34366
34600
  {
34367
34601
  pointerEvents: "box-none",
34368
- style: [styles21.frame, styles21.singleFrame, { top: offsets.progress, right: offsets.right }],
34602
+ style: [{ position: "absolute", zIndex: 18, bottom: offsets.bottom + MOBILE_CHROME_METRICS.bottomBarVisualHeight + 12, left: offsets.left, right: offsets.right, alignItems: "center" }],
34369
34603
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native23.View, { style: [styles21.singlePill, isDark && styles21.pillDark], children: [
34370
34604
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
34371
34605
  import_react_native23.Pressable,
@@ -34374,7 +34608,7 @@ function ProgressPill({
34374
34608
  accessibilityLabel: t.previousPage,
34375
34609
  accessibilityState: { disabled: previousDisabled },
34376
34610
  disabled: previousDisabled,
34377
- onPress: () => pillMode.previousPage !== null && onNavigateToPageRef.current?.(pillMode.previousPage),
34611
+ onPress: () => onTurnPage ? onTurnPage(-1) : pillMode.previousPage !== null && onNavigateToPageRef.current?.(pillMode.previousPage),
34378
34612
  style: styles21.pageArrowHit,
34379
34613
  testID: "papyrus-progress-page-previous",
34380
34614
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native23.Text, { style: [styles21.pageArrow, isDark && styles21.labelDark, previousDisabled && styles21.pageArrowDisabled], children: "\u2039" })
@@ -34402,7 +34636,7 @@ function ProgressPill({
34402
34636
  accessibilityLabel: t.nextPage,
34403
34637
  accessibilityState: { disabled: nextDisabled },
34404
34638
  disabled: nextDisabled,
34405
- onPress: () => pillMode.nextPage !== null && onNavigateToPageRef.current?.(pillMode.nextPage),
34639
+ onPress: () => onTurnPage ? onTurnPage(1) : pillMode.nextPage !== null && onNavigateToPageRef.current?.(pillMode.nextPage),
34406
34640
  style: styles21.pageArrowHit,
34407
34641
  testID: "papyrus-progress-page-next",
34408
34642
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native23.Text, { style: [styles21.pageArrow, isDark && styles21.labelDark, nextDisabled && styles21.pageArrowDisabled], children: "\u203A" })
@@ -34546,11 +34780,14 @@ function ProgressPill({
34546
34780
  }
34547
34781
  const responderPanHandlers = scrubberResponderRef.current?.panHandlers ?? {};
34548
34782
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
34549
- import_react_native23.View,
34783
+ import_react_native_reanimated4.default.View,
34550
34784
  {
34551
- pointerEvents: "box-none",
34785
+ pointerEvents: chromeVisible ? "box-none" : "none",
34786
+ accessibilityElementsHidden: !chromeVisible,
34787
+ importantForAccessibility: chromeVisible ? "auto" : "no-hide-descendants",
34552
34788
  style: [
34553
34789
  styles21.frame,
34790
+ chromeMotion,
34554
34791
  resolvePageScrubberOverlayStyle(),
34555
34792
  {
34556
34793
  top: offsets.progress,
@@ -35279,15 +35516,10 @@ function ReadingShell({
35279
35516
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35280
35517
  Topbar_default,
35281
35518
  {
35519
+ documentType,
35282
35520
  engine,
35283
35521
  title,
35284
- onOpenOverflow: () => {
35285
- closeMobileDestination();
35286
- setInfoOpen(false);
35287
- setActionsOpen(false);
35288
- setSettingsOpen(false);
35289
- setOverflowOpen(true);
35290
- },
35522
+ onOpenSettings: () => openDestination("display"),
35291
35523
  onOpenPageJump: () => setPageJumpOpen(true),
35292
35524
  showPageNavigationControls: false
35293
35525
  }
@@ -35295,6 +35527,7 @@ function ReadingShell({
35295
35527
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native26.View, { style: styles24.viewerStage, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35296
35528
  Viewer_default,
35297
35529
  {
35530
+ documentType,
35298
35531
  engine,
35299
35532
  ...viewerProps,
35300
35533
  pageScrubActive
@@ -35303,7 +35536,13 @@ function ReadingShell({
35303
35536
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35304
35537
  ProgressPill,
35305
35538
  {
35539
+ navigationInDock: true,
35306
35540
  documentType,
35541
+ onTurnPage: documentType === "epub" ? (direction) => {
35542
+ const paginatedEngine = engine;
35543
+ void paginatedEngine.turnEpubPage?.(direction).catch(() => {
35544
+ });
35545
+ } : void 0,
35307
35546
  onNavigateToPage: navigateToPage,
35308
35547
  onPress: () => openDestination(
35309
35548
  documentType === "pdf" ? "pages" : documentType === "epub" ? "contents" : "progress"
@@ -35315,6 +35554,12 @@ function ReadingShell({
35315
35554
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35316
35555
  BottomBar_default,
35317
35556
  {
35557
+ onNavigateToPage: navigateToPage,
35558
+ onOpenPages: () => setPageJumpOpen(true),
35559
+ onTurnPage: documentType === "epub" ? (direction) => {
35560
+ const paginatedEngine = engine;
35561
+ void paginatedEngine.turnEpubPage?.(direction).catch((error) => console.warn("[Papyrus] page navigation failed", error));
35562
+ } : void 0,
35318
35563
  documentType,
35319
35564
  onOpenDestination: openDestination,
35320
35565
  onOpenInfo: () => openDestination("info"),
@@ -35394,6 +35639,7 @@ function ReadingShell({
35394
35639
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35395
35640
  SettingsSheet_default,
35396
35641
  {
35642
+ onOpenInfo: () => openDestination("info"),
35397
35643
  engine,
35398
35644
  documentType,
35399
35645
  visible: settingsOpen,
@@ -35406,6 +35652,7 @@ function ReadingShell({
35406
35652
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
35407
35653
  PageJumpModal,
35408
35654
  {
35655
+ documentType,
35409
35656
  visible: pageJumpOpen,
35410
35657
  currentPage,
35411
35658
  pageCount,