@papyrus-sdk/ui-react-native 0.2.20 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -20395,9 +20395,121 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20395
20395
  ]).default;
20396
20396
  });
20397
20397
  </script>
20398
+ <!-- @papyrus-comic-runtime:start -->
20399
+ <script>
20400
+ (function (root, factory) {
20401
+ if (typeof module === "object" && module.exports) {
20402
+ module.exports = factory();
20403
+ } else {
20404
+ root.PapyrusComicRuntime = Object.assign(
20405
+ factory(),
20406
+ root.PapyrusComicRuntime || {}
20407
+ );
20408
+ }
20409
+ })(typeof self === "object" ? self : this, function () {
20410
+ var IMAGE_EXTENSIONS = /\\.(?:gif|jpe?g|png|svg|webp)$/i;
20411
+
20412
+ function isComicImageName(name) {
20413
+ return typeof name === "string" && IMAGE_EXTENSIONS.test(name);
20414
+ }
20415
+
20416
+ function naturalCompare(left, right) {
20417
+ var leftParts = String(left).split(/(\\d+)/);
20418
+ var rightParts = String(right).split(/(\\d+)/);
20419
+ var length = Math.max(leftParts.length, rightParts.length);
20420
+
20421
+ for (var index = 0; index < length; index += 1) {
20422
+ var leftPart = leftParts[index] || "";
20423
+ var rightPart = rightParts[index] || "";
20424
+ if (leftPart === rightPart) continue;
20425
+
20426
+ var leftNumber = /^\\d+$/.test(leftPart) ? Number(leftPart) : null;
20427
+ var rightNumber = /^\\d+$/.test(rightPart) ? Number(rightPart) : null;
20428
+ if (leftNumber !== null && rightNumber !== null) {
20429
+ return leftNumber - rightNumber;
20430
+ }
20431
+ return leftPart.localeCompare(rightPart, undefined, {
20432
+ numeric: true,
20433
+ sensitivity: "base",
20434
+ });
20435
+ }
20436
+
20437
+ return 0;
20438
+ }
20439
+
20440
+ function sortComicPageNames(names) {
20441
+ return names.slice().sort(naturalCompare);
20442
+ }
20443
+
20444
+ function patchCbrWorkerSource(workerSource, wasmUrl) {
20445
+ return workerSource.replace(
20446
+ /new URL\\("libarchive\\.wasm",import\\.meta\\.url\\)\\.href/g,
20447
+ JSON.stringify(wasmUrl)
20448
+ );
20449
+ }
20450
+
20451
+ function getComicPreviewSize(width, height, maxWidth, maxHeight) {
20452
+ var safeWidth = Math.max(1, Number(width) || 1);
20453
+ var safeHeight = Math.max(1, Number(height) || 1);
20454
+ var widthLimit = maxWidth || 240;
20455
+ var heightLimit = maxHeight || 360;
20456
+ var scale = Math.min(1, widthLimit / safeWidth, heightLimit / safeHeight);
20457
+ return {
20458
+ width: Math.max(1, Math.round(safeWidth * scale)),
20459
+ height: Math.max(1, Math.round(safeHeight * scale)),
20460
+ };
20461
+ }
20462
+
20463
+ function getComicPageAspectRatio(width, height) {
20464
+ var safeWidth = Math.max(1, Number(width) || 1);
20465
+ var safeHeight = Math.max(1, Number(height) || 1);
20466
+ return safeWidth + " / " + safeHeight;
20467
+ }
20468
+
20469
+ function getProtectedComicPageIndexes(
20470
+ currentPage,
20471
+ visiblePageIndexes,
20472
+ loadingPageIndexes
20473
+ ) {
20474
+ var protectedIndexes = new Set([
20475
+ currentPage - 1,
20476
+ currentPage,
20477
+ currentPage - 2,
20478
+ ]);
20479
+ (visiblePageIndexes || []).forEach(function (pageIndex) {
20480
+ protectedIndexes.add(pageIndex);
20481
+ });
20482
+ (loadingPageIndexes || []).forEach(function (pageIndex) {
20483
+ protectedIndexes.add(pageIndex);
20484
+ });
20485
+ return protectedIndexes;
20486
+ }
20487
+
20488
+ function isCurrentComicPageLoad(
20489
+ loadGeneration,
20490
+ currentGeneration,
20491
+ entry,
20492
+ currentEntry
20493
+ ) {
20494
+ return loadGeneration === currentGeneration && entry === currentEntry;
20495
+ }
20496
+
20497
+ return {
20498
+ isComicImageName: isComicImageName,
20499
+ sortComicPageNames: sortComicPageNames,
20500
+ patchCbrWorkerSource: patchCbrWorkerSource,
20501
+ getComicPreviewSize: getComicPreviewSize,
20502
+ getComicPageAspectRatio: getComicPageAspectRatio,
20503
+ getProtectedComicPageIndexes: getProtectedComicPageIndexes,
20504
+ isCurrentComicPageLoad: isCurrentComicPageLoad,
20505
+ };
20506
+ });
20507
+
20508
+ </script>
20509
+ <!-- @papyrus-comic-runtime:end -->
20398
20510
  <script>
20399
20511
  (function () {
20400
- const viewer = document.getElementById("viewer");
20512
+ const viewer = document.getElementById('viewer');
20401
20513
  const DEFAULT_FONT_SIZE = 16;
20402
20514
  const TEXT_PAGE_CHUNK = 1600;
20403
20515
 
@@ -20410,27 +20522,116 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20410
20522
  let currentPage = 1;
20411
20523
  let pageCount = 0;
20412
20524
  let zoom = 1.0;
20525
+ let rotation = 0;
20526
+ let comicArchive = null;
20527
+ let comicEntries = [];
20528
+ let comicContainer = null;
20529
+ let comicPages = [];
20530
+ let comicObserver = null;
20531
+ let comicScrollFrame = null;
20532
+ const comicObjectUrls = new Map();
20533
+ const comicLoading = new Map();
20534
+ const comicDimensions = new Map();
20535
+ const comicPreviewCache = new Map();
20536
+ const comicPreviewLoading = new Map();
20537
+ const COMIC_CACHE_LIMIT = 16;
20538
+ const COMIC_PREVIEW_CACHE_LIMIT = 8;
20539
+ const comicHelpers = window.PapyrusComicRuntime || {};
20540
+ const runtimeConfig = window.__PAPYRUS_RUNTIME_CONFIG__ || {};
20541
+ let comicDispose = null;
20542
+ let comicGeneration = 0;
20413
20543
 
20414
20544
  const sendMessage = (payload) => {
20415
- if (
20416
- window.ReactNativeWebView &&
20417
- window.ReactNativeWebView.postMessage
20418
- ) {
20545
+ if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
20419
20546
  window.ReactNativeWebView.postMessage(JSON.stringify(payload));
20420
20547
  return;
20421
20548
  }
20422
20549
  if (window.parent && window.parent !== window) {
20423
- window.parent.postMessage(payload, "*");
20550
+ window.parent.postMessage(payload, '*');
20424
20551
  }
20425
20552
  };
20426
20553
 
20427
20554
  const sendResponse = (id, ok, data, error) => {
20428
- sendMessage({ type: "response", id, ok, data, error });
20555
+ sendMessage({ type: 'response', id, ok, data, error });
20556
+ };
20557
+
20558
+ const runtimeAssetRequests = new Map();
20559
+ const fileChunkRequests = new Map();
20560
+
20561
+ const requestRuntimeAsset = (url, encoding) =>
20562
+ new Promise((resolve, reject) => {
20563
+ const id = \`runtime-asset-\${Date.now()}-\${Math.random()}\`;
20564
+ const timeout = setTimeout(() => {
20565
+ runtimeAssetRequests.delete(id);
20566
+ reject(new Error(\`Tempo esgotado ao carregar asset CBR: \${url}\`));
20567
+ }, 15000);
20568
+ runtimeAssetRequests.set(id, {resolve, reject, timeout});
20569
+ sendMessage({type: 'asset-request', id, url, encoding});
20570
+ });
20571
+
20572
+ const encodeArrayBufferAsDataUrl = async (buffer) => {
20573
+ const bytes = new Uint8Array(buffer);
20574
+ let binary = '';
20575
+ const chunkSize = 0x8000;
20576
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
20577
+ binary += String.fromCharCode(
20578
+ ...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)),
20579
+ );
20580
+ }
20581
+ return \`data:application/wasm;base64,\${btoa(binary)}\`;
20582
+ };
20583
+
20584
+ const fetchRuntimeAsset = async (url, encoding) => {
20585
+ try {
20586
+ const response = await fetch(url);
20587
+ if (!response.ok) throw new Error(\`status \${response.status}\`);
20588
+ if (encoding === 'text') return await response.text();
20589
+ return await encodeArrayBufferAsDataUrl(await response.arrayBuffer());
20590
+ } catch {
20591
+ return await requestRuntimeAsset(url, encoding);
20592
+ }
20593
+ };
20594
+
20595
+ const requestLocalFileChunk = (uri, offset, length) =>
20596
+ new Promise((resolve, reject) => {
20597
+ const id = \`file-chunk-\${Date.now()}-\${Math.random()}\`;
20598
+ const timeout = setTimeout(() => {
20599
+ fileChunkRequests.delete(id);
20600
+ reject(new Error(\`Tempo esgotado ao ler arquivo local: \${uri}\`));
20601
+ }, 30000);
20602
+ fileChunkRequests.set(id, {resolve, reject, timeout});
20603
+ sendMessage({type: 'file-chunk-request', id, uri, offset, length});
20604
+ });
20605
+
20606
+ const readLocalFile = async (uri) => {
20607
+ const chunkSize = 1024 * 1024;
20608
+ const chunks = [];
20609
+ let offset = 0;
20610
+ let totalLength = 0;
20611
+ while (true) {
20612
+ const result = await requestLocalFileChunk(uri, offset, chunkSize);
20613
+ const bytes = decodeBase64(result.data || '');
20614
+ if (bytes.length === 0 && !result.done) {
20615
+ throw new Error('Leitura nativa retornou um chunk vazio.');
20616
+ }
20617
+ chunks.push(bytes);
20618
+ totalLength += bytes.length;
20619
+ offset += bytes.length;
20620
+ if (result.done) break;
20621
+ }
20622
+
20623
+ const output = new Uint8Array(totalLength);
20624
+ let outputOffset = 0;
20625
+ chunks.forEach((chunk) => {
20626
+ output.set(chunk, outputOffset);
20627
+ outputOffset += chunk.length;
20628
+ });
20629
+ return output.buffer;
20429
20630
  };
20430
20631
 
20431
20632
  const sendState = (extra) => {
20432
20633
  sendMessage({
20433
- type: "state",
20634
+ type: 'state',
20434
20635
  payload: {
20435
20636
  pageCount,
20436
20637
  currentPage,
@@ -20441,70 +20642,66 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20441
20642
  };
20442
20643
 
20443
20644
  const sendEvent = (name, payload) => {
20444
- sendMessage({ type: "event", name, payload });
20445
- };
20446
-
20447
- const log = (...args) => {
20448
- sendEvent("RUNTIME_LOG", {
20449
- message: args.map((value) => String(value)).join(" "),
20450
- });
20645
+ sendMessage({ type: 'event', name, payload });
20451
20646
  };
20452
20647
 
20453
20648
  const reportError = (error, context) => {
20454
- const message =
20455
- error && error.message ? error.message : String(error);
20649
+ const message = error && error.message ? error.message : String(error);
20456
20650
  const stack = error && error.stack ? error.stack : null;
20457
- sendEvent("RUNTIME_ERROR", { message, context, stack });
20458
- };
20459
-
20460
- const EPUB_OPEN_TIMEOUT_MS = 180000;
20461
- const EPUB_READY_TIMEOUT_MS = 180000;
20462
- const EPUB_DISPLAY_TIMEOUT_MS = 30000;
20463
-
20464
- const withTimeout = async (promise, ms, label) => {
20465
- let timer = null;
20466
- try {
20467
- return await Promise.race([
20468
- promise,
20469
- new Promise((_, reject) => {
20470
- timer = setTimeout(
20471
- () => reject(new Error(\`Timeout: \${label}\`)),
20472
- ms
20473
- );
20474
- }),
20475
- ]);
20476
- } finally {
20477
- if (timer) {
20478
- clearTimeout(timer);
20479
- }
20480
- }
20651
+ sendEvent('RUNTIME_ERROR', { message, context, stack });
20481
20652
  };
20482
20653
 
20483
20654
  const shouldIgnoreError = (error) => {
20484
- const message =
20485
- error && error.message ? error.message : String(error || "");
20486
- return message.includes("ResizeObserver loop");
20655
+ const message = error && error.message ? error.message : String(error || '');
20656
+ return message.includes('ResizeObserver loop');
20487
20657
  };
20488
20658
 
20489
- window.addEventListener("error", (event) => {
20659
+ window.addEventListener('error', (event) => {
20490
20660
  const error = event.error || event.message;
20491
20661
  if (shouldIgnoreError(error)) return;
20492
- reportError(error, "window.error");
20662
+ reportError(error, 'window.error');
20493
20663
  });
20494
20664
 
20495
- window.addEventListener("unhandledrejection", (event) => {
20665
+ window.addEventListener('unhandledrejection', (event) => {
20496
20666
  if (shouldIgnoreError(event.reason)) return;
20497
- reportError(event.reason, "unhandledrejection");
20667
+ reportError(event.reason, 'unhandledrejection');
20498
20668
  });
20499
20669
 
20670
+ const clearComicState = () => {
20671
+ comicGeneration += 1;
20672
+ if (comicObserver) {
20673
+ comicObserver.disconnect();
20674
+ comicObserver = null;
20675
+ }
20676
+ if (comicScrollFrame !== null) {
20677
+ cancelAnimationFrame(comicScrollFrame);
20678
+ comicScrollFrame = null;
20679
+ }
20680
+ comicObjectUrls.forEach((url) => URL.revokeObjectURL(url));
20681
+ comicObjectUrls.clear();
20682
+ comicLoading.clear();
20683
+ comicDimensions.clear();
20684
+ comicPreviewCache.clear();
20685
+ comicPreviewLoading.clear();
20686
+ if (comicDispose) {
20687
+ void comicDispose();
20688
+ comicDispose = null;
20689
+ }
20690
+ comicArchive = null;
20691
+ comicEntries = [];
20692
+ comicContainer = null;
20693
+ comicPages = [];
20694
+ };
20695
+
20500
20696
  const clearViewer = () => {
20697
+ clearComicState();
20501
20698
  while (viewer.firstChild) {
20502
20699
  viewer.removeChild(viewer.firstChild);
20503
20700
  }
20504
20701
  };
20505
20702
 
20506
20703
  const decodeBase64 = (value) => {
20507
- const clean = value.replace(/\\s/g, "");
20704
+ const clean = value.replace(/\\s/g, '');
20508
20705
  const binary = atob(clean);
20509
20706
  const len = binary.length;
20510
20707
  const bytes = new Uint8Array(len);
@@ -20516,41 +20713,438 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20516
20713
 
20517
20714
  const decodeBase64ToText = (value) => {
20518
20715
  const bytes = decodeBase64(value);
20519
- return new TextDecoder("utf-8").decode(bytes);
20716
+ return new TextDecoder('utf-8').decode(bytes);
20717
+ };
20718
+
20719
+ const sourceToArrayBuffer = async (source) => {
20720
+ if (source.kind === 'uri') {
20721
+ if (/^(?:file|content):\\/\\//i.test(source.uri)) {
20722
+ return readLocalFile(source.uri);
20723
+ }
20724
+ const response = await fetch(source.uri);
20725
+ if (!response.ok && response.status !== 0) {
20726
+ throw new Error(\`Falha ao carregar quadrinho (\${response.status})\`);
20727
+ }
20728
+ return response.arrayBuffer();
20729
+ }
20730
+ if (source.kind === 'base64') {
20731
+ return decodeBase64(source.data).buffer;
20732
+ }
20733
+ throw new Error('Fonte de quadrinho inv\xE1lida.');
20734
+ };
20735
+
20736
+ const comicMimeType = (name) => {
20737
+ const extension = name.split('.').pop()?.toLowerCase();
20738
+ if (extension === 'png') return 'image/png';
20739
+ if (extension === 'gif') return 'image/gif';
20740
+ if (extension === 'svg') return 'image/svg+xml';
20741
+ if (extension === 'webp') return 'image/webp';
20742
+ return 'image/jpeg';
20743
+ };
20744
+
20745
+ const updateComicCurrentPage = () => {
20746
+ if (!comicContainer || comicPages.length === 0) return;
20747
+ const viewportTop = comicContainer.scrollTop;
20748
+ let closestIndex = currentPage - 1;
20749
+ let closestDistance = Number.POSITIVE_INFINITY;
20750
+ comicPages.forEach((page, index) => {
20751
+ const distance = Math.abs(page.offsetTop - viewportTop);
20752
+ if (distance < closestDistance) {
20753
+ closestDistance = distance;
20754
+ closestIndex = index;
20755
+ }
20756
+ });
20757
+ const nextPage = Math.max(1, Math.min(pageCount, closestIndex + 1));
20758
+ if (nextPage !== currentPage) {
20759
+ currentPage = nextPage;
20760
+ sendState();
20761
+ }
20520
20762
  };
20521
20763
 
20522
- const fetchUriAsArrayBuffer = async (uri) => {
20523
- const response = await fetch(uri);
20524
- const ok = response.ok || response.status === 0;
20525
- if (!ok) {
20526
- throw new Error(
20527
- "Failed to fetch epub uri (" + response.status + ")"
20764
+ const scheduleComicCurrentPage = () => {
20765
+ if (comicScrollFrame !== null) return;
20766
+ comicScrollFrame = requestAnimationFrame(() => {
20767
+ comicScrollFrame = null;
20768
+ updateComicCurrentPage();
20769
+ });
20770
+ };
20771
+
20772
+ const evictComicUrls = () => {
20773
+ if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) return;
20774
+ const protectedIndexes = new Set([
20775
+ currentPage - 1,
20776
+ currentPage,
20777
+ currentPage - 2,
20778
+ ]);
20779
+ for (const [index, url] of comicObjectUrls) {
20780
+ if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) break;
20781
+ if (protectedIndexes.has(index)) continue;
20782
+ URL.revokeObjectURL(url);
20783
+ comicObjectUrls.delete(index);
20784
+ const image = comicPages[index]?.querySelector('img');
20785
+ const page = comicPages[index];
20786
+ const dimensions = comicDimensions.get(index);
20787
+ const width = dimensions?.width || image?.naturalWidth || 0;
20788
+ const height = dimensions?.height || image?.naturalHeight || 0;
20789
+ if (page && width > 0 && height > 0) {
20790
+ page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
20791
+ width,
20792
+ height
20793
+ );
20794
+ }
20795
+ if (image) image.removeAttribute('src');
20796
+ }
20797
+ };
20798
+
20799
+ const ensureComicPage = async (pageIndex) => {
20800
+ if (comicObjectUrls.has(pageIndex)) {
20801
+ const image = comicPages[pageIndex]?.querySelector('img');
20802
+ if (image && !image.src) image.src = comicObjectUrls.get(pageIndex);
20803
+ return comicObjectUrls.get(pageIndex);
20804
+ }
20805
+ if (comicLoading.has(pageIndex)) return comicLoading.get(pageIndex);
20806
+
20807
+ const entry = comicEntries[pageIndex];
20808
+ if (!entry) throw new Error(\`P\xE1gina de quadrinho inv\xE1lida: \${pageIndex}\`);
20809
+ const loadGeneration = comicGeneration;
20810
+ const pending = entry.async('blob').then((blob) => {
20811
+ if (
20812
+ !comicHelpers.isCurrentComicPageLoad(
20813
+ loadGeneration,
20814
+ comicGeneration,
20815
+ entry,
20816
+ comicEntries[pageIndex]
20817
+ )
20818
+ ) {
20819
+ return null;
20820
+ }
20821
+ const url = URL.createObjectURL(
20822
+ new Blob([blob], { type: comicMimeType(entry.name) })
20528
20823
  );
20824
+ comicObjectUrls.set(pageIndex, url);
20825
+ const image = comicPages[pageIndex]?.querySelector('img');
20826
+ if (image) image.src = url;
20827
+ evictComicUrls();
20828
+ return url;
20829
+ });
20830
+ comicLoading.set(pageIndex, pending);
20831
+ try {
20832
+ return await pending;
20833
+ } finally {
20834
+ if (comicLoading.get(pageIndex) === pending) {
20835
+ comicLoading.delete(pageIndex);
20836
+ }
20529
20837
  }
20530
- if (typeof response.arrayBuffer === "function") {
20531
- return response.arrayBuffer();
20838
+ };
20839
+
20840
+ const renderComicPage = (pageIndex) => {
20841
+ const image = comicPages[pageIndex]?.querySelector('img');
20842
+ if (!image) return;
20843
+ image.style.width = \`\${Math.max(40, Math.round(100 * zoom))}%\`;
20844
+ image.style.transform = \`rotate(\${rotation}deg)\`;
20845
+ };
20846
+
20847
+ const applyComicLayout = () => {
20848
+ comicPages.forEach((_, pageIndex) => renderComicPage(pageIndex));
20849
+ };
20850
+
20851
+ const loadComic = async (source, format) => {
20852
+ clearViewer();
20853
+ let entries;
20854
+ if (format === 'cbr') {
20855
+ const clientUrl = runtimeConfig.cbrClientUrl;
20856
+ const workerUrl = runtimeConfig.cbrWorkerUrl;
20857
+ const wasmUrl = runtimeConfig.cbrWasmUrl;
20858
+ if (!clientUrl || !workerUrl || !wasmUrl) {
20859
+ throw new Error(
20860
+ 'CBR no mobile exige o pacote opcional de runtime libarchive.'
20861
+ );
20862
+ }
20863
+
20864
+ const [clientSource, workerSource, wasmSource] = await Promise.all([
20865
+ fetchRuntimeAsset(clientUrl, 'text'),
20866
+ fetchRuntimeAsset(workerUrl, 'text'),
20867
+ runtimeConfig.cbrWasmDataUrl ||
20868
+ fetchRuntimeAsset(wasmUrl, 'base64'),
20869
+ ]);
20870
+ const patchedWorkerSource = comicHelpers.patchCbrWorkerSource(
20871
+ workerSource,
20872
+ wasmSource
20873
+ );
20874
+ const workerObjectUrl = URL.createObjectURL(
20875
+ new Blob([patchedWorkerSource], {type: 'text/javascript'})
20876
+ );
20877
+ const clientObjectUrl = URL.createObjectURL(
20878
+ new Blob([clientSource], { type: 'text/javascript' })
20879
+ );
20880
+ try {
20881
+ const archiveModule = await import(clientObjectUrl);
20882
+ const createCbrWorker = () => {
20883
+ const worker = new Worker(workerObjectUrl, {type: 'module'});
20884
+ worker.addEventListener('error', (event) => {
20885
+ sendEvent('RUNTIME_ERROR', {
20886
+ message: \`CBR worker: \${event?.message || 'falha ao executar o worker'}\`,
20887
+ context: 'comic.worker',
20888
+ });
20889
+ });
20890
+ worker.addEventListener('messageerror', () => {
20891
+ sendEvent('RUNTIME_ERROR', {
20892
+ message: 'CBR worker: falha ao transferir mensagem',
20893
+ context: 'comic.worker',
20894
+ });
20895
+ });
20896
+ return worker;
20897
+ };
20898
+ archiveModule.Archive.init({getWorker: createCbrWorker});
20899
+ const bytes = await sourceToArrayBuffer(source);
20900
+ const file = new Blob([bytes], {
20901
+ type: 'application/vnd.comicbook-rar',
20902
+ });
20903
+ const archive = await archiveModule.Archive.open(file);
20904
+ const files = await archive.getFilesArray();
20905
+ entries = files
20906
+ .filter(
20907
+ (item) => {
20908
+ const name = \`\${item?.path || ''}\${item?.file?.name || ''}\`;
20909
+ return item?.file && comicHelpers.isComicImageName(name);
20910
+ }
20911
+ )
20912
+ .map((item) => {
20913
+ const name = \`\${item.path || ''}\${item.file.name}\`;
20914
+ return {
20915
+ name,
20916
+ async: async () => item.file.extract(),
20917
+ };
20918
+ });
20919
+ comicDispose = async () => {
20920
+ await archive.close();
20921
+ URL.revokeObjectURL(workerObjectUrl);
20922
+ URL.revokeObjectURL(clientObjectUrl);
20923
+ };
20924
+ } catch (error) {
20925
+ URL.revokeObjectURL(workerObjectUrl);
20926
+ URL.revokeObjectURL(clientObjectUrl);
20927
+ throw error;
20928
+ }
20929
+ } else {
20930
+ if (!window.JSZip) {
20931
+ throw new Error('O runtime ZIP do leitor mobile n\xE3o foi carregado.');
20932
+ }
20933
+
20934
+ const bytes = await sourceToArrayBuffer(source);
20935
+ comicArchive = await window.JSZip.loadAsync(bytes);
20936
+ entries = Object.keys(comicArchive.files)
20937
+ .filter((name) => {
20938
+ const entry = comicArchive.files[name];
20939
+ return !entry.dir && comicHelpers.isComicImageName(name);
20940
+ })
20941
+ .map((name) => comicArchive.files[name]);
20942
+ }
20943
+
20944
+ const names = comicHelpers.sortComicPageNames(
20945
+ entries.map((entry) => entry.name)
20946
+ );
20947
+ const entriesByName = new Map(entries.map((entry) => [entry.name, entry]));
20948
+ comicEntries = names.map((name) => entriesByName.get(name));
20949
+ if (comicEntries.length === 0) {
20950
+ if (comicDispose) {
20951
+ await comicDispose();
20952
+ comicDispose = null;
20953
+ }
20954
+ throw new Error('O arquivo n\xE3o cont\xE9m p\xE1ginas de imagem.');
20532
20955
  }
20533
- const blob = await response.blob();
20534
- return new Promise((resolve, reject) => {
20956
+
20957
+ comicContainer = document.createElement('div');
20958
+ comicContainer.style.width = '100%';
20959
+ comicContainer.style.height = '100%';
20960
+ comicContainer.style.overflow = 'auto';
20961
+ comicContainer.style.padding = '16px 0 120px';
20962
+ comicContainer.style.boxSizing = 'border-box';
20963
+ comicContainer.style.background = '#ffffff';
20964
+ comicContainer.addEventListener('scroll', scheduleComicCurrentPage, {
20965
+ passive: true,
20966
+ });
20967
+ viewer.style.overflow = 'hidden';
20968
+ viewer.appendChild(comicContainer);
20969
+
20970
+ comicPages = comicEntries.map((entry, pageIndex) => {
20971
+ const page = document.createElement('div');
20972
+ page.dataset.pageIndex = String(pageIndex);
20973
+ page.style.width = '100%';
20974
+ page.style.minHeight = '80px';
20975
+ page.style.display = 'flex';
20976
+ page.style.justifyContent = 'center';
20977
+ page.style.alignItems = 'flex-start';
20978
+ page.style.padding = '0 12px 20px';
20979
+ page.style.boxSizing = 'border-box';
20980
+
20981
+ const image = document.createElement('img');
20982
+ image.alt = \`P\xE1gina \${pageIndex + 1}\`;
20983
+ image.draggable = false;
20984
+ image.decoding = 'async';
20985
+ image.loading = 'lazy';
20986
+ image.style.display = 'block';
20987
+ image.style.maxWidth = 'none';
20988
+ image.style.height = 'auto';
20989
+ image.style.userSelect = 'none';
20990
+ image.addEventListener('load', () => {
20991
+ if (image.naturalWidth > 0 && image.naturalHeight > 0) {
20992
+ comicDimensions.set(pageIndex, {
20993
+ width: image.naturalWidth,
20994
+ height: image.naturalHeight,
20995
+ });
20996
+ page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
20997
+ image.naturalWidth,
20998
+ image.naturalHeight
20999
+ );
21000
+ }
21001
+ });
21002
+ page.appendChild(image);
21003
+ comicContainer.appendChild(page);
21004
+ return page;
21005
+ });
21006
+
21007
+ if (typeof IntersectionObserver === 'function') {
21008
+ comicObserver = new IntersectionObserver(
21009
+ (observations) => {
21010
+ observations.forEach((observation) => {
21011
+ if (!observation.isIntersecting) return;
21012
+ const pageIndex = Number(observation.target.dataset.pageIndex);
21013
+ void ensureComicPage(pageIndex).catch((error) =>
21014
+ sendEvent('RUNTIME_ERROR', {
21015
+ message: error?.message || String(error),
21016
+ context: 'comic.page',
21017
+ })
21018
+ );
21019
+ });
21020
+ scheduleComicCurrentPage();
21021
+ },
21022
+ { root: comicContainer, rootMargin: '600px 0px' }
21023
+ );
21024
+ comicPages.forEach((page) => comicObserver.observe(page));
21025
+ } else {
21026
+ for (let pageIndex = 0; pageIndex < Math.min(3, comicPages.length); pageIndex += 1) {
21027
+ void ensureComicPage(pageIndex);
21028
+ }
21029
+ }
21030
+ pageCount = comicPages.length;
21031
+ currentPage = 1;
21032
+ await ensureComicPage(0);
21033
+ applyComicLayout();
21034
+ return { pageCount };
21035
+ };
21036
+
21037
+ const readComicBlobAsDataUrl = (blob, mimeType) =>
21038
+ new Promise((resolve, reject) => {
20535
21039
  const reader = new FileReader();
20536
- reader.onerror = () =>
20537
- reject(new Error("Failed to read epub blob"));
21040
+ reader.onerror = () => reject(new Error('Falha ao gerar preview.'));
20538
21041
  reader.onload = () => resolve(reader.result);
20539
- reader.readAsArrayBuffer(blob);
21042
+ reader.readAsDataURL(new Blob([blob], { type: mimeType }));
20540
21043
  });
21044
+
21045
+ const createComicPagePreview = async (pageIndex) => {
21046
+ const entry = comicEntries[pageIndex];
21047
+ if (!entry) return null;
21048
+ const mimeType = comicMimeType(entry.name);
21049
+ const sourceUrl = await ensureComicPage(pageIndex);
21050
+ if (!sourceUrl) return null;
21051
+ try {
21052
+ const image = new Image();
21053
+ image.decoding = 'async';
21054
+ await new Promise((resolve, reject) => {
21055
+ image.onload = resolve;
21056
+ image.onerror = () => reject(new Error('Falha ao decodificar preview.'));
21057
+ image.src = sourceUrl;
21058
+ });
21059
+ const size = comicHelpers.getComicPreviewSize(
21060
+ image.naturalWidth || image.width,
21061
+ image.naturalHeight || image.height
21062
+ );
21063
+ const canvas = document.createElement('canvas');
21064
+ canvas.width = size.width;
21065
+ canvas.height = size.height;
21066
+ const context = canvas.getContext('2d');
21067
+ if (!context) throw new Error('Canvas indispon\xEDvel para preview.');
21068
+ context.fillStyle = '#ffffff';
21069
+ context.fillRect(0, 0, size.width, size.height);
21070
+ context.drawImage(image, 0, 0, size.width, size.height);
21071
+ return canvas.toDataURL('image/jpeg', 0.78);
21072
+ } catch {
21073
+ try {
21074
+ const response = await fetch(sourceUrl);
21075
+ const blob = await response.blob();
21076
+ return await readComicBlobAsDataUrl(blob, mimeType);
21077
+ } catch {
21078
+ return null;
21079
+ }
21080
+ }
21081
+ };
21082
+
21083
+ const getComicPagePreview = async (pageIndex) => {
21084
+ const entry = comicEntries[pageIndex];
21085
+ if (!entry) return null;
21086
+ if (comicPreviewCache.has(pageIndex)) {
21087
+ return comicPreviewCache.get(pageIndex);
21088
+ }
21089
+ if (comicPreviewLoading.has(pageIndex)) {
21090
+ return comicPreviewLoading.get(pageIndex);
21091
+ }
21092
+ const loadGeneration = comicGeneration;
21093
+ const pending = createComicPagePreview(pageIndex).then((preview) => {
21094
+ if (
21095
+ !comicHelpers.isCurrentComicPageLoad(
21096
+ loadGeneration,
21097
+ comicGeneration,
21098
+ entry,
21099
+ comicEntries[pageIndex]
21100
+ )
21101
+ ) {
21102
+ return null;
21103
+ }
21104
+ comicPreviewCache.set(pageIndex, preview);
21105
+ while (comicPreviewCache.size > COMIC_PREVIEW_CACHE_LIMIT) {
21106
+ comicPreviewCache.delete(comicPreviewCache.keys().next().value);
21107
+ }
21108
+ return preview;
21109
+ });
21110
+ comicPreviewLoading.set(pageIndex, pending);
21111
+ try {
21112
+ return await pending;
21113
+ } finally {
21114
+ if (comicPreviewLoading.get(pageIndex) === pending) {
21115
+ comicPreviewLoading.delete(pageIndex);
21116
+ }
21117
+ }
21118
+ };
21119
+
21120
+ const getComicPageDimensions = async (pageIndex) => {
21121
+ const known = comicDimensions.get(pageIndex);
21122
+ if (known) return known;
21123
+ await ensureComicPage(pageIndex);
21124
+ const image = comicPages[pageIndex]?.querySelector('img');
21125
+ if (image && !image.complete) {
21126
+ await new Promise((resolve) => {
21127
+ image.addEventListener('load', resolve, { once: true });
21128
+ image.addEventListener('error', resolve, { once: true });
21129
+ });
21130
+ }
21131
+ return (
21132
+ comicDimensions.get(pageIndex) || {
21133
+ width: image?.naturalWidth || 0,
21134
+ height: image?.naturalHeight || 0,
21135
+ }
21136
+ );
20541
21137
  };
20542
21138
 
20543
21139
  const normalizeHref = (href) => {
20544
- if (!href) return "";
20545
- return href.split("#")[0];
21140
+ if (!href) return '';
21141
+ return href.split('#')[0];
20546
21142
  };
20547
21143
 
20548
21144
  const getSpineIndexByHref = (href) => {
20549
21145
  const normalized = normalizeHref(href);
20550
21146
  if (!normalized) return -1;
20551
- return spineItems.findIndex(
20552
- (item) => normalizeHref(item.href) === normalized
20553
- );
21147
+ return spineItems.findIndex((item) => normalizeHref(item.href) === normalized);
20554
21148
  };
20555
21149
 
20556
21150
  const buildOutline = async () => {
@@ -20559,11 +21153,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20559
21153
  const toc = nav && nav.toc ? nav.toc : [];
20560
21154
 
20561
21155
  const mapItem = (item) => {
20562
- const title = item.label || item.title || "";
20563
- const pageIndex = getSpineIndexByHref(item.href || "");
20564
- const children = Array.isArray(item.subitems)
20565
- ? item.subitems.map(mapItem)
20566
- : [];
21156
+ const title = item.label || item.title || '';
21157
+ const pageIndex = getSpineIndexByHref(item.href || '');
21158
+ const children = Array.isArray(item.subitems) ? item.subitems.map(mapItem) : [];
20567
21159
  const outlineItem = { title, pageIndex };
20568
21160
  if (children.length > 0) outlineItem.children = children;
20569
21161
  return outlineItem;
@@ -20575,10 +21167,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20575
21167
  const applyEpubZoom = () => {
20576
21168
  if (!rendition || !rendition.themes) return;
20577
21169
  const fontSize = \`\${Math.round(zoom * 100)}%\`;
20578
- if (typeof rendition.themes.fontSize === "function") {
21170
+ if (typeof rendition.themes.fontSize === 'function') {
20579
21171
  rendition.themes.fontSize(fontSize);
20580
- } else if (typeof rendition.themes.override === "function") {
20581
- rendition.themes.override("font-size", fontSize);
21172
+ } else if (typeof rendition.themes.override === 'function') {
21173
+ rendition.themes.override('font-size', fontSize);
20582
21174
  }
20583
21175
  };
20584
21176
 
@@ -20589,14 +21181,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20589
21181
  };
20590
21182
 
20591
21183
  const renderTextPage = (pageIndex) => {
20592
- const text = textPages[pageIndex] || "";
21184
+ const text = textPages[pageIndex] || '';
20593
21185
  if (!textContainer) {
20594
- textContainer = document.createElement("div");
20595
- textContainer.style.padding = "24px";
20596
- textContainer.style.lineHeight = "1.6";
20597
- textContainer.style.whiteSpace = "pre-wrap";
20598
- textContainer.style.fontFamily =
20599
- 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
21186
+ textContainer = document.createElement('div');
21187
+ textContainer.style.padding = '24px';
21188
+ textContainer.style.lineHeight = '1.6';
21189
+ textContainer.style.whiteSpace = 'pre-wrap';
21190
+ textContainer.style.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
20600
21191
  viewer.appendChild(textContainer);
20601
21192
  }
20602
21193
  textContainer.textContent = text;
@@ -20608,18 +21199,18 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20608
21199
  for (let i = 0; i < text.length; i += TEXT_PAGE_CHUNK) {
20609
21200
  pages.push(text.slice(i, i + TEXT_PAGE_CHUNK));
20610
21201
  }
20611
- return pages.length > 0 ? pages : [""];
21202
+ return pages.length > 0 ? pages : [''];
20612
21203
  };
20613
21204
 
20614
21205
  const loadText = async (source) => {
20615
- let text = "";
20616
- if (source.kind === "uri") {
21206
+ let text = '';
21207
+ if (source.kind === 'uri') {
20617
21208
  const res = await fetch(source.uri);
20618
21209
  text = await res.text();
20619
- } else if (source.kind === "base64") {
21210
+ } else if (source.kind === 'base64') {
20620
21211
  text = decodeBase64ToText(source.data);
20621
- } else if (source.kind === "text") {
20622
- text = source.text || "";
21212
+ } else if (source.kind === 'text') {
21213
+ text = source.text || '';
20623
21214
  }
20624
21215
 
20625
21216
  clearViewer();
@@ -20632,44 +21223,25 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20632
21223
  };
20633
21224
 
20634
21225
  const loadEpub = async (source) => {
20635
- log("epub.load:start", source.kind);
20636
- if (rendition && typeof rendition.destroy === "function") {
21226
+ if (rendition && typeof rendition.destroy === 'function') {
20637
21227
  rendition.destroy();
20638
21228
  }
20639
- if (book && typeof book.destroy === "function") {
21229
+ if (book && typeof book.destroy === 'function') {
20640
21230
  book.destroy();
20641
21231
  }
20642
21232
 
20643
- if (source.kind === "base64") {
20644
- book = ePub();
20645
- await withTimeout(
20646
- book.open(source.data, "base64"),
20647
- EPUB_OPEN_TIMEOUT_MS,
20648
- "epub.open"
20649
- );
20650
- } else if (source.kind === "uri") {
20651
- log("epub.load:fetch-uri");
20652
- const data = await withTimeout(
20653
- fetchUriAsArrayBuffer(source.uri),
20654
- EPUB_OPEN_TIMEOUT_MS,
20655
- "epub.fetch"
20656
- );
20657
- const input = data instanceof Uint8Array ? data.buffer : data;
20658
- book = ePub(input, { openAs: "epub" });
20659
- } else {
20660
- let data = null;
20661
- if (source.kind === "text") {
20662
- const encoder = new TextEncoder();
20663
- data = encoder.encode(source.text || "").buffer;
20664
- }
20665
- const input = data instanceof Uint8Array ? data.buffer : data;
20666
- book = ePub(input, { openAs: "epub" });
20667
- }
20668
- if (book && book.opened) {
20669
- await withTimeout(book.opened, EPUB_OPEN_TIMEOUT_MS, "epub.opened");
21233
+ let data = null;
21234
+ if (source.kind === 'uri') {
21235
+ data = source.uri;
21236
+ } else if (source.kind === 'base64') {
21237
+ data = decodeBase64(source.data);
21238
+ } else if (source.kind === 'text') {
21239
+ const encoder = new TextEncoder();
21240
+ data = encoder.encode(source.text || '').buffer;
20670
21241
  }
20671
- await withTimeout(book.ready, EPUB_READY_TIMEOUT_MS, "epub.ready");
20672
- log("epub.load:ready");
21242
+
21243
+ book = ePub(data);
21244
+ await book.ready;
20673
21245
 
20674
21246
  spineItems = book.spine && book.spine.items ? book.spine.items : [];
20675
21247
  pageCount = spineItems.length;
@@ -20677,64 +21249,38 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20677
21249
 
20678
21250
  clearViewer();
20679
21251
  rendition = book.renderTo(viewer, {
20680
- width: "100%",
20681
- height: "100%",
20682
- flow: "paginated",
20683
- spread: "none",
21252
+ width: '100%',
21253
+ height: '100%',
21254
+ flow: 'paginated',
21255
+ spread: 'none',
20684
21256
  });
20685
21257
 
20686
21258
  if (rendition && rendition.hooks && rendition.hooks.content) {
20687
21259
  rendition.hooks.content.register((contents) => {
20688
- const frame =
20689
- contents && contents.document
20690
- ? contents.document.defaultView.frameElement
20691
- : null;
21260
+ const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
20692
21261
  if (frame) {
20693
- frame.setAttribute(
20694
- "sandbox",
20695
- "allow-scripts allow-same-origin"
20696
- );
21262
+ frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
20697
21263
  }
20698
21264
  });
20699
21265
  }
20700
21266
 
20701
- if (rendition && typeof rendition.on === "function") {
20702
- rendition.on("selected", (cfiRange, contents) => {
20703
- const selection =
20704
- contents && contents.window
20705
- ? contents.window.getSelection()
20706
- : null;
20707
- const text = selection ? selection.toString().trim() : "";
21267
+ if (rendition && typeof rendition.on === 'function') {
21268
+ rendition.on('selected', (cfiRange, contents) => {
21269
+ const selection = contents && contents.window ? contents.window.getSelection() : null;
21270
+ const text = selection ? selection.toString().trim() : '';
20708
21271
  if (text) {
20709
- sendEvent("TEXT_SELECTED", {
20710
- text,
20711
- pageIndex: Math.max(0, currentPage - 1),
20712
- });
21272
+ sendEvent('TEXT_SELECTED', { text, pageIndex: Math.max(0, currentPage - 1) });
20713
21273
  }
20714
- if (
20715
- rendition &&
20716
- rendition.annotations &&
20717
- typeof rendition.annotations.remove === "function"
20718
- ) {
20719
- rendition.annotations.remove(cfiRange, "highlight");
21274
+ if (rendition && rendition.annotations && typeof rendition.annotations.remove === 'function') {
21275
+ rendition.annotations.remove(cfiRange, 'highlight');
20720
21276
  }
20721
21277
  });
20722
21278
  }
20723
21279
 
20724
- try {
20725
- await withTimeout(
20726
- displayEpubPage(0),
20727
- EPUB_DISPLAY_TIMEOUT_MS,
20728
- "epub.display"
20729
- );
20730
- log("epub.load:displayed");
20731
- } catch (err) {
20732
- reportError(err, "epub.display");
20733
- }
21280
+ await displayEpubPage(0);
20734
21281
  applyEpubZoom();
20735
21282
 
20736
21283
  const outline = await buildOutline();
20737
- log("epub.load:outline", outline.length);
20738
21284
  return { pageCount, outline };
20739
21285
  };
20740
21286
 
@@ -20749,41 +21295,34 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20749
21295
  };
20750
21296
 
20751
21297
  const getTextContent = async (pageIndex) => {
20752
- if (currentType === "text") {
20753
- const text = textPages[pageIndex] || "";
20754
- return [
20755
- {
20756
- str: text,
20757
- dir: "ltr",
20758
- width: 0,
20759
- height: 0,
20760
- transform: [1, 0, 0, 1, 0, 0],
20761
- fontName: "default",
20762
- },
20763
- ];
21298
+ if (currentType === 'text') {
21299
+ const text = textPages[pageIndex] || '';
21300
+ return [{
21301
+ str: text,
21302
+ dir: 'ltr',
21303
+ width: 0,
21304
+ height: 0,
21305
+ transform: [1, 0, 0, 1, 0, 0],
21306
+ fontName: 'default',
21307
+ }];
20764
21308
  }
20765
21309
 
20766
- if (currentType === "epub") {
21310
+ if (currentType === 'epub') {
20767
21311
  if (!book) return [];
20768
21312
  const item = spineItems[pageIndex];
20769
21313
  if (!item) return [];
20770
21314
  try {
20771
21315
  const section = book.spine.get(item.idref || item.href);
20772
- const text =
20773
- section && typeof section.text === "function"
20774
- ? await section.text()
20775
- : "";
21316
+ const text = section && typeof section.text === 'function' ? await section.text() : '';
20776
21317
  if (!text) return [];
20777
- return [
20778
- {
20779
- str: text,
20780
- dir: "ltr",
20781
- width: 0,
20782
- height: 0,
20783
- transform: [1, 0, 0, 1, 0, 0],
20784
- fontName: "default",
20785
- },
20786
- ];
21318
+ return [{
21319
+ str: text,
21320
+ dir: 'ltr',
21321
+ width: 0,
21322
+ height: 0,
21323
+ transform: [1, 0, 0, 1, 0, 0],
21324
+ fontName: 'default',
21325
+ }];
20787
21326
  } catch (err) {
20788
21327
  return [];
20789
21328
  }
@@ -20794,7 +21333,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20794
21333
 
20795
21334
  const getPageText = async (pageIndex) => {
20796
21335
  const items = await getTextContent(pageIndex);
20797
- return items.map((item) => item.str).join(" ");
21336
+ return items.map((item) => item.str).join(' ');
20798
21337
  };
20799
21338
 
20800
21339
  const searchText = async (query) => {
@@ -20810,11 +21349,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20810
21349
  while (pos !== -1) {
20811
21350
  const start = Math.max(0, pos - 20);
20812
21351
  const end = Math.min(text.length, pos + query.length + 20);
20813
- results.push({
20814
- pageIndex: i,
20815
- text: text.substring(start, end),
20816
- matchIndex,
20817
- });
21352
+ results.push({ pageIndex: i, text: text.substring(start, end), matchIndex });
20818
21353
  matchIndex += 1;
20819
21354
  pos = lower.indexOf(normalized, pos + 1);
20820
21355
  }
@@ -20830,11 +21365,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20830
21365
 
20831
21366
  const getPageIndex = (dest) => {
20832
21367
  if (!dest) return null;
20833
- if (typeof dest === "string") return getSpineIndexByHref(dest);
20834
- if (typeof dest !== "object") return null;
20835
- if (dest.kind === "href") return getSpineIndexByHref(dest.value);
20836
- if (dest.kind === "pageIndex") return dest.value;
20837
- if (dest.kind === "pageNumber") return Math.max(0, dest.value - 1);
21368
+ if (typeof dest === 'string') return getSpineIndexByHref(dest);
21369
+ if (typeof dest !== 'object') return null;
21370
+ if (dest.kind === 'href') return getSpineIndexByHref(dest.value);
21371
+ if (dest.kind === 'pageIndex') return dest.value;
21372
+ if (dest.kind === 'pageNumber') return Math.max(0, dest.value - 1);
20838
21373
  return null;
20839
21374
  };
20840
21375
 
@@ -20848,53 +21383,70 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20848
21383
  };
20849
21384
 
20850
21385
  const applyZoom = () => {
20851
- if (currentType === "text") {
21386
+ if (currentType === 'text') {
20852
21387
  applyTextZoom();
20853
- } else if (currentType === "epub") {
21388
+ } else if (currentType === 'epub') {
20854
21389
  applyEpubZoom();
21390
+ } else if (currentType === 'comic') {
21391
+ applyComicLayout();
20855
21392
  }
20856
21393
  };
20857
21394
 
20858
21395
  const handleCommand = async (message) => {
20859
21396
  const { id, kind, payload } = message;
20860
- log("command", kind);
20861
21397
 
20862
21398
  try {
20863
- if (kind === "load") {
21399
+ if (kind === 'load') {
20864
21400
  currentType = payload.type;
20865
21401
  zoom = 1.0;
20866
21402
 
20867
- if (currentType === "text") {
21403
+ if (currentType === 'text') {
20868
21404
  const result = await loadText(payload.source);
20869
21405
  sendState();
20870
21406
  sendResponse(id, true, result);
20871
21407
  return;
20872
21408
  }
20873
21409
 
20874
- if (currentType === "epub") {
21410
+ if (currentType === 'epub') {
20875
21411
  const result = await loadEpub(payload.source);
20876
21412
  sendState({ outline: result.outline || [] });
20877
21413
  sendResponse(id, true, result);
20878
21414
  return;
20879
21415
  }
20880
21416
 
20881
- throw new Error("Unsupported document type");
21417
+ if (currentType === 'comic') {
21418
+ const result = await loadComic(payload.source, payload.format || 'cbz');
21419
+ sendState();
21420
+ sendResponse(id, true, result);
21421
+ return;
21422
+ }
21423
+
21424
+ throw new Error('Unsupported document type');
20882
21425
  }
20883
21426
 
20884
- if (kind === "go-to-page") {
21427
+ if (kind === 'go-to-page') {
20885
21428
  const page = Math.max(1, payload.page || 1);
20886
- if (currentType === "text") {
21429
+ if (currentType === 'text') {
20887
21430
  currentPage = page;
20888
21431
  renderTextPage(page - 1);
20889
21432
  sendState();
20890
- } else if (currentType === "epub") {
21433
+ } else if (currentType === 'epub') {
20891
21434
  await displayEpubPage(page - 1);
21435
+ } else if (currentType === 'comic') {
21436
+ currentPage = page;
21437
+ const target = comicPages[page - 1];
21438
+ if (target) {
21439
+ await ensureComicPage(page - 1);
21440
+ target.scrollIntoView({ block: 'start' });
21441
+ renderComicPage(page - 1);
21442
+ sendState();
21443
+ }
20892
21444
  }
20893
21445
  sendResponse(id, true, { currentPage });
20894
21446
  return;
20895
21447
  }
20896
21448
 
20897
- if (kind === "set-zoom") {
21449
+ if (kind === 'set-zoom') {
20898
21450
  zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
20899
21451
  applyZoom();
20900
21452
  sendState();
@@ -20902,103 +21454,132 @@ https://github.com/nodeca/pako/blob/main/LICENSE
20902
21454
  return;
20903
21455
  }
20904
21456
 
20905
- if (kind === "set-rotation") {
21457
+ if (kind === 'set-rotation') {
21458
+ rotation = ((payload.rotation || 0) % 360 + 360) % 360;
21459
+ if (currentType === 'comic') applyComicLayout();
20906
21460
  sendResponse(id, true, {});
20907
21461
  return;
20908
21462
  }
20909
21463
 
20910
- if (kind === "get-text-content") {
21464
+ if (kind === 'get-text-content') {
20911
21465
  const items = await getTextContent(payload.pageIndex || 0);
20912
21466
  sendResponse(id, true, items);
20913
21467
  return;
20914
21468
  }
20915
21469
 
20916
- if (kind === "get-page-dimensions") {
20917
- sendResponse(id, true, getPageDimensions());
21470
+ if (kind === 'get-page-dimensions') {
21471
+ if (currentType === 'comic') {
21472
+ sendResponse(
21473
+ id,
21474
+ true,
21475
+ await getComicPageDimensions(payload.pageIndex || 0)
21476
+ );
21477
+ } else {
21478
+ sendResponse(id, true, getPageDimensions());
21479
+ }
21480
+ return;
21481
+ }
21482
+
21483
+ if (kind === 'get-page-preview') {
21484
+ if (currentType !== 'comic') {
21485
+ sendResponse(id, true, null);
21486
+ return;
21487
+ }
21488
+ sendResponse(
21489
+ id,
21490
+ true,
21491
+ await getComicPagePreview(payload.pageIndex || 0)
21492
+ );
20918
21493
  return;
20919
21494
  }
20920
21495
 
20921
- if (kind === "search-text") {
20922
- const results = await searchText(payload.query || "");
21496
+ if (kind === 'search-text') {
21497
+ const results = await searchText(payload.query || '');
20923
21498
  sendResponse(id, true, results);
20924
21499
  return;
20925
21500
  }
20926
21501
 
20927
- if (kind === "select-text") {
21502
+ if (kind === 'select-text') {
20928
21503
  const selection = await selectText(payload.pageIndex || 0);
20929
21504
  sendResponse(id, true, selection);
20930
21505
  return;
20931
21506
  }
20932
21507
 
20933
- if (kind === "get-outline") {
21508
+ if (kind === 'get-outline') {
20934
21509
  const outline = await buildOutline();
20935
21510
  sendResponse(id, true, outline);
20936
21511
  return;
20937
21512
  }
20938
21513
 
20939
- if (kind === "get-page-index") {
21514
+ if (kind === 'get-page-index') {
20940
21515
  const index = getPageIndex(payload.dest);
20941
21516
  sendResponse(id, true, index);
20942
21517
  return;
20943
21518
  }
20944
21519
 
20945
- if (kind === "destroy") {
21520
+ if (kind === 'destroy') {
20946
21521
  clearViewer();
20947
21522
  sendResponse(id, true, {});
20948
21523
  return;
20949
21524
  }
20950
21525
 
20951
- sendResponse(id, false, null, "Unknown command");
21526
+ sendResponse(id, false, null, 'Unknown command');
20952
21527
  } catch (err) {
20953
- sendResponse(
20954
- id,
20955
- false,
20956
- null,
20957
- err && err.message ? err.message : String(err)
20958
- );
21528
+ sendResponse(id, false, null, err && err.message ? err.message : String(err));
20959
21529
  }
20960
21530
  };
20961
21531
 
20962
21532
  const onMessage = (event) => {
20963
21533
  let message = null;
20964
- const raw = event && typeof event === "object" ? event.data : null;
21534
+ const raw = event && typeof event === 'object' ? event.data : null;
20965
21535
 
20966
- if (raw && typeof raw === "object" && raw.kind && raw.id) {
21536
+ if (raw && typeof raw === 'object' && (raw.kind || raw.type) && raw.id) {
20967
21537
  message = raw;
20968
- } else if (typeof raw === "string") {
20969
- if (raw.startsWith("setImmediate$")) {
20970
- return;
20971
- }
21538
+ } else if (typeof raw === 'string') {
21539
+ if (raw.startsWith('setImmediate$')) return;
20972
21540
  const trimmed = raw.trim();
20973
- if (
20974
- !trimmed ||
20975
- (!trimmed.startsWith("{") && !trimmed.startsWith("["))
20976
- ) {
21541
+ if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) {
20977
21542
  return;
20978
21543
  }
20979
21544
  try {
20980
21545
  message = JSON.parse(trimmed);
20981
21546
  } catch (err) {
20982
- sendEvent("RUNTIME_ERROR", {
20983
- message: "Failed to parse message",
20984
- context: "runtime.onMessage",
20985
- stack: err && err.stack ? err.stack : null,
20986
- preview: trimmed.slice(0, 200),
20987
- });
20988
21547
  return;
20989
21548
  }
20990
21549
  } else {
20991
21550
  return;
20992
21551
  }
20993
21552
 
20994
- if (!message || !message.kind || message.id == null) return;
20995
- handleCommand(message);
21553
+ if (!message || message.id == null) return;
21554
+
21555
+ if (message.type === 'asset-response') {
21556
+ const pending = runtimeAssetRequests.get(message.id);
21557
+ if (!pending) return;
21558
+ runtimeAssetRequests.delete(message.id);
21559
+ clearTimeout(pending.timeout);
21560
+ if (message.ok) pending.resolve(message.data);
21561
+ else pending.reject(new Error(message.error || 'Falha ao carregar asset CBR.'));
21562
+ return;
21563
+ }
21564
+
21565
+ if (message.type === 'file-chunk-response') {
21566
+ const pending = fileChunkRequests.get(message.id);
21567
+ if (!pending) return;
21568
+ fileChunkRequests.delete(message.id);
21569
+ clearTimeout(pending.timeout);
21570
+ if (message.ok) pending.resolve(message);
21571
+ else pending.reject(new Error(message.error || 'Falha ao ler arquivo local.'));
21572
+ return;
21573
+ }
21574
+
21575
+ if (!message.kind) return;
21576
+ handleCommand(message);
20996
21577
  };
20997
21578
 
20998
- window.addEventListener("message", onMessage);
20999
- document.addEventListener("message", onMessage);
21579
+ window.addEventListener('message', onMessage);
21580
+ document.addEventListener('message', onMessage);
21000
21581
 
21001
- sendMessage({ type: "ready" });
21582
+ sendMessage({ type: 'ready' });
21002
21583
  })();
21003
21584
  </script>
21004
21585
  </body>
@@ -21013,7 +21594,7 @@ import {
21013
21594
  useRef as useRef4,
21014
21595
  useEffect as useEffect4,
21015
21596
  useCallback as useCallback3,
21016
- useState as useState3
21597
+ useState as useState4
21017
21598
  } from "react";
21018
21599
  import {
21019
21600
  FlatList,
@@ -21036,6 +21617,7 @@ import {
21036
21617
  } from "react";
21037
21618
  import {
21038
21619
  View,
21620
+ Text,
21039
21621
  StyleSheet,
21040
21622
  Pressable,
21041
21623
  PanResponder,
@@ -21045,7 +21627,10 @@ import {
21045
21627
  } from "react-native";
21046
21628
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
21047
21629
  import Svg, { Path as SvgPath } from "react-native-svg";
21048
- import { useViewerStore } from "@papyrus-sdk/core";
21630
+ import { papyrusEvents, useViewerStore } from "@papyrus-sdk/core";
21631
+ import {
21632
+ PapyrusEventType
21633
+ } from "@papyrus-sdk/types";
21049
21634
  import {
21050
21635
  PapyrusPageView
21051
21636
  } from "@papyrus-sdk/engine-native";
@@ -21316,6 +21901,14 @@ var resolvePdfBasePageWidth = ({
21316
21901
  horizontalPadding
21317
21902
  }) => Math.max(0, viewportWidth - horizontalPadding * 2);
21318
21903
 
21904
+ // components/selectionEvent.ts
21905
+ var notifyTextSelection = (handler, payload) => {
21906
+ const text = payload.text.replace(/\s+/g, " ").trim();
21907
+ if (!text) return false;
21908
+ handler?.({ ...payload, text });
21909
+ return Boolean(handler);
21910
+ };
21911
+
21319
21912
  // components/PageRenderer.tsx
21320
21913
  import { jsx, jsxs } from "react/jsx-runtime";
21321
21914
  var TEXT_MARKUP_TOOLS = /* @__PURE__ */ new Set([
@@ -21390,6 +21983,8 @@ var PageRenderer = ({
21390
21983
  horizontalPadding = 16,
21391
21984
  spacing = 24,
21392
21985
  onSelectionDragActiveChange,
21986
+ onTextSelected,
21987
+ onDefineSelection,
21393
21988
  gestureScrollLockActive = false,
21394
21989
  lastPinchEndedAt = null,
21395
21990
  requestSelectionVerticalAutoscroll
@@ -21735,6 +22330,17 @@ var PageRenderer = ({
21735
22330
  return;
21736
22331
  }
21737
22332
  const selectedText = selection.text || "";
22333
+ const normalizedText = selectedText.replace(/\s+/g, " ").trim();
22334
+ if (normalizedText) {
22335
+ papyrusEvents.emit(PapyrusEventType.TEXT_SELECTED, {
22336
+ text: normalizedText,
22337
+ pageIndex
22338
+ });
22339
+ }
22340
+ notifyTextSelection(onTextSelected, {
22341
+ text: normalizedText,
22342
+ pageIndex
22343
+ });
21738
22344
  logSelectionPerf("selection.ready", {
21739
22345
  tool: activeTool,
21740
22346
  sourceRectCount: selection.rects.length,
@@ -22670,6 +23276,20 @@ var PageRenderer = ({
22670
23276
  }
22671
23277
  ],
22672
23278
  children: [
23279
+ onDefineSelection ? /* @__PURE__ */ jsx(
23280
+ Pressable,
23281
+ {
23282
+ onPress: (event) => {
23283
+ stopPressPropagation(event);
23284
+ onDefineSelection({
23285
+ text: selectionText,
23286
+ pageIndex
23287
+ });
23288
+ },
23289
+ style: [styles.selectionAction, styles.defineSelectionAction],
23290
+ children: /* @__PURE__ */ jsx(Text, { style: styles.defineSelectionText, children: "Definir" })
23291
+ }
23292
+ ) : null,
22673
23293
  /* @__PURE__ */ jsx(
22674
23294
  Pressable,
22675
23295
  {
@@ -22969,6 +23589,15 @@ var styles = StyleSheet.create({
22969
23589
  justifyContent: "center",
22970
23590
  marginRight: 10
22971
23591
  },
23592
+ defineSelectionAction: {
23593
+ width: 58,
23594
+ paddingHorizontal: 6
23595
+ },
23596
+ defineSelectionText: {
23597
+ color: "#FFFFFF",
23598
+ fontSize: 11,
23599
+ fontWeight: "700"
23600
+ },
22972
23601
  selectionActionLast: {
22973
23602
  marginRight: 0
22974
23603
  },
@@ -23000,37 +23629,108 @@ var styles = StyleSheet.create({
23000
23629
  borderRadius: 3
23001
23630
  }
23002
23631
  });
23003
- var arePageRendererPropsEqual = (previous, next) => previous.engine === next.engine && previous.pageIndex === next.pageIndex && previous.scale === next.scale && previous.PageViewComponent === next.PageViewComponent && previous.availableWidth === next.availableWidth && 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;
23632
+ var arePageRendererPropsEqual = (previous, next) => previous.engine === next.engine && previous.pageIndex === next.pageIndex && previous.scale === next.scale && previous.PageViewComponent === next.PageViewComponent && previous.availableWidth === next.availableWidth && previous.pageViewportWidth === next.pageViewportWidth && previous.horizontalPadding === next.horizontalPadding && previous.spacing === next.spacing && previous.onSelectionDragActiveChange === next.onSelectionDragActiveChange && previous.onTextSelected === next.onTextSelected && previous.onDefineSelection === next.onDefineSelection && previous.gestureScrollLockActive === next.gestureScrollLockActive && previous.lastPinchEndedAt === next.lastPinchEndedAt && previous.requestSelectionVerticalAutoscroll === next.requestSelectionVerticalAutoscroll;
23004
23633
  var PageRenderer_default = memo(PageRenderer, arePageRendererPropsEqual);
23005
23634
 
23006
23635
  // components/WebViewViewer.tsx
23007
- import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
23636
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
23008
23637
  import { Image, StyleSheet as StyleSheet2, View as View2 } from "react-native";
23009
23638
  import WebView from "react-native-webview";
23010
23639
  import { useViewerStore as useViewerStore2 } from "@papyrus-sdk/core";
23640
+
23641
+ // components/webViewState.ts
23642
+ var parseWebViewState = (raw) => {
23643
+ try {
23644
+ const message = JSON.parse(raw);
23645
+ if (!message || typeof message !== "object") return null;
23646
+ const typedMessage = message;
23647
+ if (typedMessage.type !== "state") return null;
23648
+ if (!typedMessage.payload || typeof typedMessage.payload !== "object") {
23649
+ return null;
23650
+ }
23651
+ const payload = typedMessage.payload;
23652
+ const update = {};
23653
+ if (typeof payload.currentPage === "number" && Number.isInteger(payload.currentPage) && payload.currentPage >= 1) {
23654
+ update.currentPage = payload.currentPage;
23655
+ }
23656
+ if (typeof payload.pageCount === "number" && Number.isInteger(payload.pageCount) && payload.pageCount >= 0) {
23657
+ update.pageCount = payload.pageCount;
23658
+ }
23659
+ return Object.keys(update).length > 0 ? update : null;
23660
+ } catch {
23661
+ return null;
23662
+ }
23663
+ };
23664
+
23665
+ // components/WebViewViewer.tsx
23011
23666
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
23012
23667
  var runtimeAsset = require_runtime();
23013
- var runtimeSource = (() => {
23014
- if (typeof runtimeAsset === "string") {
23015
- return { html: runtimeAsset };
23668
+ var resolveRuntimeSource = (asset) => {
23669
+ if (typeof asset === "string") {
23670
+ return { html: asset };
23016
23671
  }
23017
- if (typeof runtimeAsset === "number") {
23018
- const resolved = Image.resolveAssetSource(runtimeAsset);
23672
+ if (typeof asset === "number") {
23673
+ const resolved = Image.resolveAssetSource(asset);
23019
23674
  if (resolved?.uri) {
23020
23675
  return { uri: resolved.uri };
23021
23676
  }
23022
23677
  }
23023
- if (runtimeAsset && typeof runtimeAsset === "object" && "uri" in runtimeAsset) {
23024
- const uri = runtimeAsset.uri;
23678
+ if (asset && typeof asset === "object" && "uri" in asset) {
23679
+ const uri = asset.uri;
23025
23680
  if (uri) return { uri };
23026
23681
  }
23027
23682
  return { html: "" };
23028
- })();
23683
+ };
23029
23684
  var WebViewViewer = ({ engine }) => {
23030
23685
  const webViewRef = useRef2(null);
23031
23686
  const { pageTheme } = useViewerStore2();
23687
+ const bridgeEngine = engine;
23688
+ const runtimeSource = useMemo2(
23689
+ () => resolveRuntimeSource(
23690
+ bridgeEngine.getWebViewRuntimeSource?.() ?? runtimeAsset
23691
+ ),
23692
+ [bridgeEngine]
23693
+ );
23694
+ const [runtimeHtml, setRuntimeHtml] = useState2(null);
23695
+ useEffect2(() => {
23696
+ let active = true;
23697
+ if ("html" in runtimeSource) {
23698
+ setRuntimeHtml(runtimeSource.html);
23699
+ return () => {
23700
+ active = false;
23701
+ };
23702
+ }
23703
+ setRuntimeHtml(null);
23704
+ void fetch(runtimeSource.uri).then((response) => {
23705
+ if (!response.ok) throw new Error(`status ${response.status}`);
23706
+ return response.text();
23707
+ }).then((html) => {
23708
+ if (active) setRuntimeHtml(html);
23709
+ }).catch(() => {
23710
+ if (active) setRuntimeHtml(null);
23711
+ });
23712
+ return () => {
23713
+ active = false;
23714
+ };
23715
+ }, [runtimeSource]);
23716
+ const webViewSource = useMemo2(() => {
23717
+ if (runtimeHtml !== null) {
23718
+ return {
23719
+ html: runtimeHtml,
23720
+ baseUrl: "http://localhost:3005/"
23721
+ };
23722
+ }
23723
+ if ("uri" in runtimeSource) return { html: "" };
23724
+ return runtimeSource;
23725
+ }, [runtimeHtml, runtimeSource]);
23726
+ const runtimeConfig = bridgeEngine.getWebViewRuntimeConfig?.();
23727
+ const runtimeConfigScript = useMemo2(() => {
23728
+ if (!runtimeConfig) return void 0;
23729
+ return `window.__PAPYRUS_RUNTIME_CONFIG__=${JSON.stringify(
23730
+ runtimeConfig
23731
+ )};true;`;
23732
+ }, [runtimeConfig]);
23032
23733
  useEffect2(() => {
23033
- const bridgeEngine = engine;
23034
23734
  bridgeEngine.attachWebView?.({
23035
23735
  postMessage: (message) => {
23036
23736
  if (__DEV__) {
@@ -23040,9 +23740,22 @@ var WebViewViewer = ({ engine }) => {
23040
23740
  webViewRef.current?.postMessage(message);
23041
23741
  }
23042
23742
  });
23043
- }, [engine]);
23743
+ }, [bridgeEngine]);
23044
23744
  const handleMessage = (event) => {
23045
- const bridgeEngine = engine;
23745
+ const state = parseWebViewState(event.nativeEvent.data);
23746
+ if (state) {
23747
+ const viewerState = useViewerStore2.getState();
23748
+ const nextState = {};
23749
+ if (state.currentPage !== void 0 && state.currentPage !== viewerState.currentPage) {
23750
+ nextState.currentPage = state.currentPage;
23751
+ }
23752
+ if (state.pageCount !== void 0 && state.pageCount !== viewerState.pageCount) {
23753
+ nextState.pageCount = state.pageCount;
23754
+ }
23755
+ if (Object.keys(nextState).length > 0) {
23756
+ viewerState.setDocumentState(nextState);
23757
+ }
23758
+ }
23046
23759
  if (__DEV__) {
23047
23760
  console.log("[Papyrus WebView] message", event.nativeEvent.data);
23048
23761
  }
@@ -23082,11 +23795,12 @@ var WebViewViewer = ({ engine }) => {
23082
23795
  WebView,
23083
23796
  {
23084
23797
  ref: webViewRef,
23085
- source: runtimeSource,
23798
+ source: webViewSource,
23086
23799
  originWhitelist: ["*"],
23087
23800
  onMessage: handleMessage,
23088
23801
  onLoadEnd: handleLoadEnd,
23089
23802
  onError: handleError,
23803
+ injectedJavaScriptBeforeContentLoaded: runtimeConfigScript,
23090
23804
  javaScriptEnabled: true,
23091
23805
  domStorageEnabled: true,
23092
23806
  scalesPageToFit: true,
@@ -23136,7 +23850,7 @@ var styles2 = StyleSheet2.create({
23136
23850
  var WebViewViewer_default = WebViewViewer;
23137
23851
 
23138
23852
  // components/DedicatedAndroidPdfViewer.tsx
23139
- import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
23853
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
23140
23854
  import { Pressable as Pressable2, StyleSheet as StyleSheet3, View as View3 } from "react-native";
23141
23855
  import { useViewerStore as useViewerStore3 } from "@papyrus-sdk/core";
23142
23856
  import { PapyrusPdfDocumentView } from "@papyrus-sdk/engine-native";
@@ -23782,7 +24496,7 @@ function DedicatedAndroidPdfViewer({
23782
24496
  const addAnnotation = useViewerStore3((state) => state.addAnnotation);
23783
24497
  const setSelectedAnnotation = useViewerStore3((state) => state.setSelectedAnnotation);
23784
24498
  const engineId = getDedicatedAndroidPdfEngineId(engine);
23785
- const [selection, setSelection] = useState2(null);
24499
+ const [selection, setSelection] = useState3(null);
23786
24500
  const selectionRef = useRef3(null);
23787
24501
  const lastVisiblePagesKeyRef = useRef3("");
23788
24502
  const lastScrollOffsetYRef = useRef3(0);
@@ -24078,7 +24792,9 @@ var Viewer = ({
24078
24792
  maxToRenderPerBatch,
24079
24793
  removeClippedSubviews,
24080
24794
  useDedicatedAndroidPdfViewer,
24081
- viewerMode
24795
+ viewerMode,
24796
+ onTextSelected,
24797
+ onDefineSelection
24082
24798
  }) => {
24083
24799
  const pageCount = useViewerStore4((state) => state.pageCount);
24084
24800
  const currentPage = useViewerStore4((state) => state.currentPage);
@@ -24135,13 +24851,13 @@ var Viewer = ({
24135
24851
  const lastScrollOffsetYRef = useRef4(0);
24136
24852
  const scrollDownAccumRef = useRef4(0);
24137
24853
  const scrollUpAccumRef = useRef4(0);
24138
- const [layoutRevision, setLayoutRevision] = useState3(0);
24139
- const [selectionDragActive, setSelectionDragActive] = useState3(false);
24854
+ const [layoutRevision, setLayoutRevision] = useState4(0);
24855
+ const [selectionDragActive, setSelectionDragActive] = useState4(false);
24140
24856
  const selectionDragActiveRef = useRef4(false);
24141
- const [gestureScrollLockActive, setGestureScrollLockActive] = useState3(false);
24857
+ const [gestureScrollLockActive, setGestureScrollLockActive] = useState4(false);
24142
24858
  const gestureScrollLockActiveRef = useRef4(false);
24143
- const [pinchPreviewScale, setPinchPreviewScale] = useState3(1);
24144
- const [lastPinchEndedAt, setLastPinchEndedAt] = useState3(null);
24859
+ const [pinchPreviewScale, setPinchPreviewScale] = useState4(1);
24860
+ const [lastPinchEndedAt, setLastPinchEndedAt] = useState4(null);
24145
24861
  const pinchGestureActiveRef = useRef4(false);
24146
24862
  const pinchStartZoomRef = useRef4(1);
24147
24863
  const pinchPreviewZoomRef = useRef4(1);
@@ -25214,6 +25930,8 @@ var Viewer = ({
25214
25930
  pageViewportWidth: columnWidth,
25215
25931
  spacing: DOUBLE_PAGE_SPACING,
25216
25932
  onSelectionDragActiveChange: setSelectionDragActive,
25933
+ onTextSelected,
25934
+ onDefineSelection,
25217
25935
  gestureScrollLockActive,
25218
25936
  lastPinchEndedAt,
25219
25937
  requestSelectionVerticalAutoscroll: handleSelectionVerticalAutoscroll
@@ -25230,6 +25948,8 @@ var Viewer = ({
25230
25948
  pageViewportWidth: columnWidth,
25231
25949
  spacing: DOUBLE_PAGE_SPACING,
25232
25950
  onSelectionDragActiveChange: setSelectionDragActive,
25951
+ onTextSelected,
25952
+ onDefineSelection,
25233
25953
  gestureScrollLockActive,
25234
25954
  lastPinchEndedAt,
25235
25955
  requestSelectionVerticalAutoscroll: handleSelectionVerticalAutoscroll
@@ -25249,6 +25969,8 @@ var Viewer = ({
25249
25969
  pageViewportWidth: documentSurfaceWidth,
25250
25970
  spacing: CONTINUOUS_PAGE_SPACING,
25251
25971
  onSelectionDragActiveChange: setSelectionDragActive,
25972
+ onTextSelected,
25973
+ onDefineSelection,
25252
25974
  gestureScrollLockActive,
25253
25975
  lastPinchEndedAt,
25254
25976
  requestSelectionVerticalAutoscroll: handleSelectionVerticalAutoscroll
@@ -25338,6 +26060,8 @@ var Viewer = ({
25338
26060
  pageViewportWidth: documentSurfaceWidth,
25339
26061
  spacing: 32,
25340
26062
  onSelectionDragActiveChange: setSelectionDragActive,
26063
+ onTextSelected,
26064
+ onDefineSelection,
25341
26065
  gestureScrollLockActive,
25342
26066
  lastPinchEndedAt,
25343
26067
  requestSelectionVerticalAutoscroll: handleSelectionVerticalAutoscroll
@@ -25474,8 +26198,8 @@ var styles4 = StyleSheet4.create({
25474
26198
  var Viewer_default = Viewer;
25475
26199
 
25476
26200
  // components/Topbar.tsx
25477
- import { useState as useState5 } from "react";
25478
- import { View as View6, Text as Text2, Pressable as Pressable4, StyleSheet as StyleSheet6, useWindowDimensions as useWindowDimensions3 } from "react-native";
26201
+ import { useState as useState6 } from "react";
26202
+ import { View as View6, Text as Text3, Pressable as Pressable4, StyleSheet as StyleSheet6, useWindowDimensions as useWindowDimensions3 } from "react-native";
25479
26203
  import { useViewerStore as useViewerStore5 } from "@papyrus-sdk/core";
25480
26204
 
25481
26205
  // components/mobileChromeMetrics.ts
@@ -25497,8 +26221,8 @@ var MOBILE_CHROME_METRICS = {
25497
26221
  };
25498
26222
 
25499
26223
  // components/PageJumpModal.tsx
25500
- import { useEffect as useEffect5, useState as useState4 } from "react";
25501
- import { Modal, Pressable as Pressable3, StyleSheet as StyleSheet5, Text, TextInput, View as View5 } from "react-native";
26224
+ import { useEffect as useEffect5, useState as useState5 } from "react";
26225
+ import { Modal, Pressable as Pressable3, StyleSheet as StyleSheet5, Text as Text2, TextInput, View as View5 } from "react-native";
25502
26226
 
25503
26227
  // components/pageJumpModel.ts
25504
26228
  var resolvePageJumpTarget = (value, pageCount) => {
@@ -25519,7 +26243,7 @@ function PageJumpModal({
25519
26243
  onClose,
25520
26244
  onConfirm
25521
26245
  }) {
25522
- const [value, setValue] = useState4(`${currentPage}`);
26246
+ const [value, setValue] = useState5(`${currentPage}`);
25523
26247
  useEffect5(() => {
25524
26248
  if (visible) setValue(`${currentPage}`);
25525
26249
  }, [currentPage, visible]);
@@ -25541,7 +26265,7 @@ function PageJumpModal({
25541
26265
  style: [styles5.card, isDark && styles5.cardDark],
25542
26266
  onPress: (event) => event.stopPropagation(),
25543
26267
  children: [
25544
- /* @__PURE__ */ jsx6(Text, { style: [styles5.title, isDark && styles5.titleDark], children: "Ir para pagina" }),
26268
+ /* @__PURE__ */ jsx6(Text2, { style: [styles5.title, isDark && styles5.titleDark], children: "Ir para pagina" }),
25545
26269
  /* @__PURE__ */ jsx6(
25546
26270
  TextInput,
25547
26271
  {
@@ -25568,7 +26292,7 @@ function PageJumpModal({
25568
26292
  styles5.actionCancel,
25569
26293
  isDark && styles5.actionCancelDark
25570
26294
  ],
25571
- children: /* @__PURE__ */ jsx6(Text, { style: [styles5.actionText, isDark && styles5.actionTextDark], children: "Cancelar" })
26295
+ children: /* @__PURE__ */ jsx6(Text2, { style: [styles5.actionText, isDark && styles5.actionTextDark], children: "Cancelar" })
25572
26296
  }
25573
26297
  ),
25574
26298
  /* @__PURE__ */ jsx6(
@@ -25576,7 +26300,7 @@ function PageJumpModal({
25576
26300
  {
25577
26301
  onPress: confirmJump,
25578
26302
  style: [styles5.actionButton, { backgroundColor: accentColor }],
25579
- children: /* @__PURE__ */ jsx6(Text, { style: [styles5.actionText, styles5.actionTextPrimary], children: "Ir" })
26303
+ children: /* @__PURE__ */ jsx6(Text2, { style: [styles5.actionText, styles5.actionTextPrimary], children: "Ir" })
25580
26304
  }
25581
26305
  )
25582
26306
  ] })
@@ -25680,7 +26404,7 @@ var Topbar = ({
25680
26404
  accentColor,
25681
26405
  mobileChromeVisible
25682
26406
  } = useViewerStore5();
25683
- const [jumpModalOpen, setJumpModalOpen] = useState5(false);
26407
+ const [jumpModalOpen, setJumpModalOpen] = useState6(false);
25684
26408
  const isDark = uiTheme === "dark";
25685
26409
  const navIconColor = isDark ? "#e5e7eb" : "#111827";
25686
26410
  const { width, height } = useWindowDimensions3();
@@ -25701,7 +26425,7 @@ var Topbar = ({
25701
26425
  }
25702
26426
  setJumpModalOpen(true);
25703
26427
  };
25704
- const defaultLogo = /* @__PURE__ */ jsx7(View6, { style: [styles6.logoBadge, { backgroundColor: accentColor }], children: /* @__PURE__ */ jsx7(Text2, { style: styles6.logoText, children: "P" }) });
26428
+ const defaultLogo = /* @__PURE__ */ jsx7(View6, { style: [styles6.logoBadge, { backgroundColor: accentColor }], children: /* @__PURE__ */ jsx7(Text3, { style: styles6.logoText, children: "P" }) });
25705
26429
  const logoElement = logo ?? defaultLogo;
25706
26430
  if (!mobileChromeVisible) return null;
25707
26431
  return /* @__PURE__ */ jsxs7(Fragment2, { children: [
@@ -25731,7 +26455,7 @@ var Topbar = ({
25731
26455
  style: styles6.titleHit,
25732
26456
  accessibilityLabel: "Open page jump",
25733
26457
  children: /* @__PURE__ */ jsx7(
25734
- Text2,
26458
+ Text3,
25735
26459
  {
25736
26460
  numberOfLines: 1,
25737
26461
  style: [styles6.brandText, isDark && styles6.brandTextDark],
@@ -25763,7 +26487,7 @@ var Topbar = ({
25763
26487
  style: styles6.pageIndicatorHit,
25764
26488
  accessibilityLabel: "Page jump",
25765
26489
  children: /* @__PURE__ */ jsxs7(
25766
- Text2,
26490
+ Text3,
25767
26491
  {
25768
26492
  style: [
25769
26493
  styles6.pageIndicator,
@@ -25983,10 +26707,10 @@ var styles6 = StyleSheet6.create({
25983
26707
  var Topbar_default = Topbar;
25984
26708
 
25985
26709
  // components/ToolDock.tsx
25986
- import React7, { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
26710
+ import React7, { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef5, useState as useState7 } from "react";
25987
26711
  import {
25988
26712
  View as View7,
25989
- Text as Text3,
26713
+ Text as Text4,
25990
26714
  Pressable as Pressable5,
25991
26715
  StyleSheet as StyleSheet7,
25992
26716
  ScrollView as ScrollView2,
@@ -26221,8 +26945,8 @@ var OpacitySlider = React7.memo(function OpacitySlider2({
26221
26945
  isDark,
26222
26946
  onCommit
26223
26947
  }) {
26224
- const [trackWidth, setTrackWidth] = useState6(0);
26225
- const [draftValue, setDraftValue] = useState6(value);
26948
+ const [trackWidth, setTrackWidth] = useState7(0);
26949
+ const [draftValue, setDraftValue] = useState7(value);
26226
26950
  const dragValueRef = useRef5(draftValue);
26227
26951
  const isDraggingRef = useRef5(false);
26228
26952
  const frameRef = useRef5(null);
@@ -26331,8 +27055,8 @@ var ToolDock = () => {
26331
27055
  undoAnnotations,
26332
27056
  redoAnnotations
26333
27057
  } = useViewerStore6();
26334
- const [paletteExpanded, setPaletteExpanded] = useState6(false);
26335
- const [extrasExpanded, setExtrasExpanded] = useState6(false);
27058
+ const [paletteExpanded, setPaletteExpanded] = useState7(false);
27059
+ const [extrasExpanded, setExtrasExpanded] = useState7(false);
26336
27060
  const isDark = uiTheme === "dark";
26337
27061
  const { width: windowWidth } = useWindowDimensions4();
26338
27062
  const t = getStrings(locale);
@@ -26640,7 +27364,7 @@ var ToolDock = () => {
26640
27364
  }
26641
27365
  ],
26642
27366
  children: /* @__PURE__ */ jsx8(
26643
- Text3,
27367
+ Text4,
26644
27368
  {
26645
27369
  style: [
26646
27370
  styles7.utilityLabel,
@@ -26756,7 +27480,7 @@ var ToolDock = () => {
26756
27480
  }
26757
27481
  ],
26758
27482
  children: /* @__PURE__ */ jsx8(
26759
- Text3,
27483
+ Text4,
26760
27484
  {
26761
27485
  style: [
26762
27486
  styles7.utilityLabel,
@@ -27055,13 +27779,14 @@ var withInkAlpha = (hex, alpha) => {
27055
27779
  var ToolDock_default = ToolDock;
27056
27780
 
27057
27781
  // components/RightSheet.tsx
27058
- import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
27782
+ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as useState8 } from "react";
27059
27783
  import {
27060
27784
  Dimensions,
27785
+ Image as Image2,
27061
27786
  Platform as Platform3,
27062
27787
  Pressable as Pressable7,
27063
27788
  StyleSheet as StyleSheet9,
27064
- Text as Text5,
27789
+ Text as Text6,
27065
27790
  UIManager,
27066
27791
  View as View9,
27067
27792
  findNodeHandle as findNodeHandle2
@@ -27082,7 +27807,7 @@ import {
27082
27807
  Pressable as Pressable6,
27083
27808
  ScrollView as ScrollView3,
27084
27809
  StyleSheet as StyleSheet8,
27085
- Text as Text4,
27810
+ Text as Text5,
27086
27811
  View as View8
27087
27812
  } from "react-native";
27088
27813
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -27138,7 +27863,7 @@ function NativeSheet({
27138
27863
  }
27139
27864
  ),
27140
27865
  /* @__PURE__ */ jsx9(
27141
- Text4,
27866
+ Text5,
27142
27867
  {
27143
27868
  style: [styles8.title, isDark && styles8.titleDark],
27144
27869
  numberOfLines: 1,
@@ -27248,11 +27973,13 @@ var PageThumbnail = ({
27248
27973
  frameHeight,
27249
27974
  accentColor,
27250
27975
  useNativePreview,
27976
+ useImagePreview,
27251
27977
  shouldRenderPreview,
27252
27978
  onPress
27253
27979
  }) => {
27254
27980
  const viewRef = useRef6(null);
27255
- const [layoutReady, setLayoutReady] = useState7(false);
27981
+ const [layoutReady, setLayoutReady] = useState8(false);
27982
+ const [previewUri, setPreviewUri] = useState8(null);
27256
27983
  useEffect7(() => {
27257
27984
  if (!layoutReady || !useNativePreview || !shouldRenderPreview) return;
27258
27985
  const viewTag = findNodeHandle2(viewRef.current);
@@ -27268,6 +27995,24 @@ var PageThumbnail = ({
27268
27995
  useNativePreview,
27269
27996
  zoom
27270
27997
  ]);
27998
+ useEffect7(() => {
27999
+ if (!useImagePreview || !shouldRenderPreview) {
28000
+ setPreviewUri(null);
28001
+ return;
28002
+ }
28003
+ let active = true;
28004
+ const previewEngine = engine;
28005
+ const request = previewEngine.getPagePreview?.(pageIndex);
28006
+ if (!request) return;
28007
+ void request.then((uri) => {
28008
+ if (active) setPreviewUri(uri);
28009
+ }).catch(() => {
28010
+ if (active) setPreviewUri(null);
28011
+ });
28012
+ return () => {
28013
+ active = false;
28014
+ };
28015
+ }, [engine, pageIndex, shouldRenderPreview, useImagePreview]);
27271
28016
  const handleLayout = (event) => {
27272
28017
  if (event.nativeEvent.layout.width && event.nativeEvent.layout.height) {
27273
28018
  setLayoutReady(true);
@@ -27290,8 +28035,15 @@ var PageThumbnail = ({
27290
28035
  {
27291
28036
  onLayout: handleLayout,
27292
28037
  style: [styles9.thumbFrame, { width: frameWidth, height: frameHeight }],
27293
- children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ jsx10(PapyrusPageView2, { ref: viewRef, style: styles9.thumbView }) : /* @__PURE__ */ jsx10(View9, { style: [styles9.thumbFallback, isDark && styles9.thumbFallbackDark], children: /* @__PURE__ */ jsx10(
27294
- Text5,
28038
+ children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ jsx10(PapyrusPageView2, { ref: viewRef, style: styles9.thumbView }) : useImagePreview && shouldRenderPreview && previewUri ? /* @__PURE__ */ jsx10(
28039
+ Image2,
28040
+ {
28041
+ source: { uri: previewUri },
28042
+ resizeMode: "contain",
28043
+ style: styles9.thumbImage
28044
+ }
28045
+ ) : /* @__PURE__ */ jsx10(View9, { style: [styles9.thumbFallback, isDark && styles9.thumbFallbackDark], children: /* @__PURE__ */ jsx10(
28046
+ Text6,
27295
28047
  {
27296
28048
  style: [
27297
28049
  styles9.thumbFallbackText,
@@ -27302,7 +28054,7 @@ var PageThumbnail = ({
27302
28054
  ) })
27303
28055
  }
27304
28056
  ),
27305
- /* @__PURE__ */ jsx10(Text5, { style: [styles9.thumbLabel, isDark && styles9.thumbLabelDark], children: pageIndex + 1 })
28057
+ /* @__PURE__ */ jsx10(Text6, { style: [styles9.thumbLabel, isDark && styles9.thumbLabelDark], children: pageIndex + 1 })
27306
28058
  ]
27307
28059
  }
27308
28060
  );
@@ -27319,7 +28071,7 @@ var OutlineNode = ({ item, depth = 0, isDark, onSelect, untitledLabel }) => {
27319
28071
  },
27320
28072
  style: [styles9.outlineRow, { paddingLeft: 12 + depth * 12 }],
27321
28073
  children: /* @__PURE__ */ jsx10(
27322
- Text5,
28074
+ Text6,
27323
28075
  {
27324
28076
  style: [
27325
28077
  styles9.outlineText,
@@ -27367,9 +28119,14 @@ var RightSheet = ({
27367
28119
  locale,
27368
28120
  accentColor
27369
28121
  } = useViewerStore7();
27370
- const [pagesMode, setPagesMode] = useState7(
27371
- documentType === "pdf" ? "thumbnails" : "summary"
28122
+ const [pagesMode, setPagesMode] = useState8(
28123
+ documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
27372
28124
  );
28125
+ useEffect7(() => {
28126
+ setPagesMode(
28127
+ documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
28128
+ );
28129
+ }, [documentType]);
27373
28130
  const isDark = uiTheme === "dark";
27374
28131
  const t = getStrings(locale);
27375
28132
  const showingNotes = sidebarRightTab === "annotations";
@@ -27387,11 +28144,12 @@ var RightSheet = ({
27387
28144
  UIManager.getViewManagerConfig?.("PapyrusPageView")
27388
28145
  );
27389
28146
  const useNativePreview = renderTarget !== "webview" && hasNativePageView;
28147
+ const useImagePreview = documentType === "comic" && renderTarget === "webview" && typeof engine.getPagePreview === "function";
27390
28148
  const thumbnailDimensionsCacheRef = useRef6(/* @__PURE__ */ new Map());
27391
28149
  const thumbnailDimensionsPendingRef = useRef6(/* @__PURE__ */ new Set());
27392
28150
  const thumbnailRefreshTimeoutRef = useRef6(null);
27393
- const [thumbnailLayoutRevision, setThumbnailLayoutRevision] = useState7(0);
27394
- const [visibleThumbnailPages, setVisibleThumbnailPages] = useState7(
28151
+ const [thumbnailLayoutRevision, setThumbnailLayoutRevision] = useState8(0);
28152
+ const [visibleThumbnailPages, setVisibleThumbnailPages] = useState8(
27395
28153
  () => /* @__PURE__ */ new Set()
27396
28154
  );
27397
28155
  const resolvedThumbsInitialCount = useMemo5(
@@ -27543,7 +28301,7 @@ var RightSheet = ({
27543
28301
  );
27544
28302
  const renderThumbnailItem = useCallback4(
27545
28303
  ({ item }) => {
27546
- const shouldRenderPreview = useNativePreview && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
28304
+ const shouldRenderPreview = (useNativePreview || useImagePreview) && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
27547
28305
  return /* @__PURE__ */ jsx10(
27548
28306
  PageThumbnail,
27549
28307
  {
@@ -27557,6 +28315,7 @@ var RightSheet = ({
27557
28315
  frameHeight: getThumbnailFrameHeight(item),
27558
28316
  accentColor,
27559
28317
  useNativePreview,
28318
+ useImagePreview,
27560
28319
  shouldRenderPreview,
27561
28320
  onPress: () => jumpToPage(item)
27562
28321
  }
@@ -27573,6 +28332,7 @@ var RightSheet = ({
27573
28332
  jumpToPage,
27574
28333
  resolvedThumbsPrewarmCount,
27575
28334
  useNativePreview,
28335
+ useImagePreview,
27576
28336
  visibleThumbnailPages,
27577
28337
  zoom
27578
28338
  ]
@@ -27587,7 +28347,7 @@ var RightSheet = ({
27587
28347
  maxHeight: sheetHeight,
27588
28348
  children: /* @__PURE__ */ jsxs10(View9, { style: styles9.sheet, children: [
27589
28349
  /* @__PURE__ */ jsxs10(View9, { style: styles9.header, children: [
27590
- /* @__PURE__ */ jsx10(Text5, { style: [styles9.sheetTitle, isDark && styles9.sheetTitleDark], children: showingNotes ? t.notes : navigationTitle }),
28350
+ /* @__PURE__ */ jsx10(Text6, { style: [styles9.sheetTitle, isDark && styles9.sheetTitleDark], children: showingNotes ? t.notes : navigationTitle }),
27591
28351
  !showingNotes ? /* @__PURE__ */ jsx10(
27592
28352
  Pressable7,
27593
28353
  {
@@ -27595,7 +28355,7 @@ var RightSheet = ({
27595
28355
  disabled: showingProgress || pageCount <= 0,
27596
28356
  style: styles9.pageStatusHit,
27597
28357
  accessibilityLabel: "Open page jump",
27598
- children: /* @__PURE__ */ jsx10(Text5, { style: [styles9.pageStatus, isDark && styles9.pageStatusDark], children: showingProgress ? `${Math.round(currentPage / Math.max(pageCount, 1) * 100)}%` : `${currentPage}/${pageCount}` })
28358
+ children: /* @__PURE__ */ jsx10(Text6, { style: [styles9.pageStatus, isDark && styles9.pageStatusDark], children: showingProgress ? `${Math.round(currentPage / Math.max(pageCount, 1) * 100)}%` : `${currentPage}/${pageCount}` })
27599
28359
  }
27600
28360
  ) : null
27601
28361
  ] }),
@@ -27613,7 +28373,7 @@ var RightSheet = ({
27613
28373
  }
27614
28374
  ],
27615
28375
  children: /* @__PURE__ */ jsx10(
27616
- Text5,
28376
+ Text6,
27617
28377
  {
27618
28378
  style: [
27619
28379
  styles9.segmentText,
@@ -27637,7 +28397,7 @@ var RightSheet = ({
27637
28397
  }
27638
28398
  ],
27639
28399
  children: /* @__PURE__ */ jsx10(
27640
- Text5,
28400
+ Text6,
27641
28401
  {
27642
28402
  style: [
27643
28403
  styles9.segmentText,
@@ -27686,7 +28446,7 @@ var RightSheet = ({
27686
28446
  ],
27687
28447
  children: [
27688
28448
  /* @__PURE__ */ jsxs10(
27689
- Text5,
28449
+ Text6,
27690
28450
  {
27691
28451
  style: [
27692
28452
  styles9.progressLabel,
@@ -27699,7 +28459,7 @@ var RightSheet = ({
27699
28459
  }
27700
28460
  ),
27701
28461
  /* @__PURE__ */ jsxs10(
27702
- Text5,
28462
+ Text6,
27703
28463
  {
27704
28464
  style: [
27705
28465
  styles9.progressMeta,
@@ -27723,7 +28483,7 @@ var RightSheet = ({
27723
28483
  {
27724
28484
  contentContainerStyle: styles9.summaryContent,
27725
28485
  showsVerticalScrollIndicator: false,
27726
- children: outline.length === 0 ? /* @__PURE__ */ jsx10(Text5, { style: [styles9.emptyText, isDark && styles9.emptyTextDark], children: t.noSummary }) : outline.map((item, index) => /* @__PURE__ */ jsx10(
28486
+ children: outline.length === 0 ? /* @__PURE__ */ jsx10(Text6, { style: [styles9.emptyText, isDark && styles9.emptyTextDark], children: t.noSummary }) : outline.map((item, index) => /* @__PURE__ */ jsx10(
27727
28487
  OutlineNode,
27728
28488
  {
27729
28489
  item,
@@ -27740,7 +28500,7 @@ var RightSheet = ({
27740
28500
  {
27741
28501
  contentContainerStyle: styles9.content,
27742
28502
  showsVerticalScrollIndicator: false,
27743
- children: annotations.length === 0 ? /* @__PURE__ */ jsx10(Text5, { style: [styles9.emptyText, isDark && styles9.emptyTextDark], children: t.noAnnotations }) : /* @__PURE__ */ jsx10(View9, { children: annotations.map((ann) => /* @__PURE__ */ jsxs10(
28503
+ children: annotations.length === 0 ? /* @__PURE__ */ jsx10(Text6, { style: [styles9.emptyText, isDark && styles9.emptyTextDark], children: t.noAnnotations }) : /* @__PURE__ */ jsx10(View9, { children: annotations.map((ann) => /* @__PURE__ */ jsxs10(
27744
28504
  Pressable7,
27745
28505
  {
27746
28506
  onPress: () => {
@@ -27758,7 +28518,7 @@ var RightSheet = ({
27758
28518
  }
27759
28519
  ),
27760
28520
  /* @__PURE__ */ jsxs10(
27761
- Text5,
28521
+ Text6,
27762
28522
  {
27763
28523
  style: [styles9.noteTitle, isDark && styles9.noteTitleDark],
27764
28524
  children: [
@@ -27770,7 +28530,7 @@ var RightSheet = ({
27770
28530
  )
27771
28531
  ] }),
27772
28532
  /* @__PURE__ */ jsx10(
27773
- Text5,
28533
+ Text6,
27774
28534
  {
27775
28535
  style: [
27776
28536
  styles9.noteType,
@@ -27781,7 +28541,7 @@ var RightSheet = ({
27781
28541
  }
27782
28542
  ),
27783
28543
  ann.content ? /* @__PURE__ */ jsx10(
27784
- Text5,
28544
+ Text6,
27785
28545
  {
27786
28546
  style: [
27787
28547
  styles9.noteContent,
@@ -27904,6 +28664,10 @@ var styles9 = StyleSheet9.create({
27904
28664
  width: "100%",
27905
28665
  height: "100%"
27906
28666
  },
28667
+ thumbImage: {
28668
+ width: "100%",
28669
+ height: "100%"
28670
+ },
27907
28671
  thumbFallback: {
27908
28672
  flex: 1,
27909
28673
  alignItems: "center",
@@ -28044,11 +28808,11 @@ var styles9 = StyleSheet9.create({
28044
28808
  var RightSheet_default = RightSheet;
28045
28809
 
28046
28810
  // components/AnnotationEditor.tsx
28047
- import { useEffect as useEffect8, useState as useState8 } from "react";
28811
+ import { useEffect as useEffect8, useState as useState9 } from "react";
28048
28812
  import {
28049
28813
  Modal as Modal3,
28050
28814
  View as View10,
28051
- Text as Text6,
28815
+ Text as Text7,
28052
28816
  TextInput as TextInput2,
28053
28817
  Pressable as Pressable8,
28054
28818
  StyleSheet as StyleSheet10
@@ -28067,7 +28831,7 @@ var AnnotationEditor = () => {
28067
28831
  } = useViewerStore8();
28068
28832
  const annotation = annotations.find((ann) => ann.id === selectedAnnotationId);
28069
28833
  const isEditable = annotation && (annotation.type === "text" || annotation.type === "comment");
28070
- const [draft, setDraft] = useState8("");
28834
+ const [draft, setDraft] = useState9("");
28071
28835
  const isDark = uiTheme === "dark";
28072
28836
  const t = getStrings(locale);
28073
28837
  useEffect8(() => {
@@ -28089,7 +28853,7 @@ var AnnotationEditor = () => {
28089
28853
  animationType: "fade",
28090
28854
  onRequestClose: handleClose,
28091
28855
  children: /* @__PURE__ */ jsx11(View10, { style: styles10.overlay, children: /* @__PURE__ */ jsxs11(View10, { style: [styles10.card, isDark && styles10.cardDark], children: [
28092
- /* @__PURE__ */ jsx11(Text6, { style: [styles10.title, isDark && styles10.titleDark], children: t.editNote }),
28856
+ /* @__PURE__ */ jsx11(Text7, { style: [styles10.title, isDark && styles10.titleDark], children: t.editNote }),
28093
28857
  /* @__PURE__ */ jsx11(
28094
28858
  TextInput2,
28095
28859
  {
@@ -28107,7 +28871,7 @@ var AnnotationEditor = () => {
28107
28871
  {
28108
28872
  onPress: handleClose,
28109
28873
  style: [styles10.actionButton, styles10.actionCancel],
28110
- children: /* @__PURE__ */ jsx11(Text6, { style: styles10.actionText, children: t.cancel })
28874
+ children: /* @__PURE__ */ jsx11(Text7, { style: styles10.actionText, children: t.cancel })
28111
28875
  }
28112
28876
  ),
28113
28877
  /* @__PURE__ */ jsx11(
@@ -28119,7 +28883,7 @@ var AnnotationEditor = () => {
28119
28883
  styles10.actionSave,
28120
28884
  { backgroundColor: accentColor }
28121
28885
  ],
28122
- children: /* @__PURE__ */ jsx11(Text6, { style: [styles10.actionText, styles10.actionTextLight], children: t.save })
28886
+ children: /* @__PURE__ */ jsx11(Text7, { style: [styles10.actionText, styles10.actionTextLight], children: t.save })
28123
28887
  }
28124
28888
  )
28125
28889
  ] })
@@ -28485,7 +29249,7 @@ var styles11 = StyleSheet11.create({
28485
29249
  var BottomBar_default = BottomBar;
28486
29250
 
28487
29251
  // components/SettingsSheet.tsx
28488
- import { Dimensions as Dimensions2, View as View12, Text as Text7, Pressable as Pressable10, StyleSheet as StyleSheet12 } from "react-native";
29252
+ import { Dimensions as Dimensions2, View as View12, Text as Text8, Pressable as Pressable10, StyleSheet as StyleSheet12 } from "react-native";
28489
29253
  import { useViewerStore as useViewerStore10 } from "@papyrus-sdk/core";
28490
29254
  import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
28491
29255
  var PAGE_THEME_OPTIONS = [
@@ -28576,7 +29340,7 @@ var SettingsSheet = ({
28576
29340
  children: [
28577
29341
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28578
29342
  /* @__PURE__ */ jsx13(
28579
- Text7,
29343
+ Text8,
28580
29344
  {
28581
29345
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28582
29346
  children: t.appearance
@@ -28594,7 +29358,7 @@ var SettingsSheet = ({
28594
29358
  uiTheme === "light" && { backgroundColor: accentColor }
28595
29359
  ],
28596
29360
  children: /* @__PURE__ */ jsx13(
28597
- Text7,
29361
+ Text8,
28598
29362
  {
28599
29363
  style: [
28600
29364
  styles12.optionText,
@@ -28617,7 +29381,7 @@ var SettingsSheet = ({
28617
29381
  uiTheme === "dark" && { backgroundColor: accentColor }
28618
29382
  ],
28619
29383
  children: /* @__PURE__ */ jsx13(
28620
- Text7,
29384
+ Text8,
28621
29385
  {
28622
29386
  style: [
28623
29387
  styles12.optionText,
@@ -28633,7 +29397,7 @@ var SettingsSheet = ({
28633
29397
  ] }),
28634
29398
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28635
29399
  /* @__PURE__ */ jsx13(
28636
- Text7,
29400
+ Text8,
28637
29401
  {
28638
29402
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28639
29403
  children: t.pageTheme
@@ -28654,7 +29418,7 @@ var SettingsSheet = ({
28654
29418
  children: [
28655
29419
  /* @__PURE__ */ jsx13(ThemeSwatch, { value: option.value }),
28656
29420
  /* @__PURE__ */ jsx13(
28657
- Text7,
29421
+ Text8,
28658
29422
  {
28659
29423
  style: [
28660
29424
  styles12.themeOptionLabel,
@@ -28672,7 +29436,7 @@ var SettingsSheet = ({
28672
29436
  ] }),
28673
29437
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28674
29438
  /* @__PURE__ */ jsx13(
28675
- Text7,
29439
+ Text8,
28676
29440
  {
28677
29441
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28678
29442
  children: t.pageTransition
@@ -28690,7 +29454,7 @@ var SettingsSheet = ({
28690
29454
  (!isPaged || isDouble) && { backgroundColor: accentColor }
28691
29455
  ],
28692
29456
  children: /* @__PURE__ */ jsx13(
28693
- Text7,
29457
+ Text8,
28694
29458
  {
28695
29459
  style: [
28696
29460
  styles12.optionText,
@@ -28713,7 +29477,7 @@ var SettingsSheet = ({
28713
29477
  isPaged && { backgroundColor: accentColor }
28714
29478
  ],
28715
29479
  children: /* @__PURE__ */ jsx13(
28716
- Text7,
29480
+ Text8,
28717
29481
  {
28718
29482
  style: [
28719
29483
  styles12.optionText,
@@ -28729,7 +29493,7 @@ var SettingsSheet = ({
28729
29493
  ] }),
28730
29494
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28731
29495
  /* @__PURE__ */ jsx13(
28732
- Text7,
29496
+ Text8,
28733
29497
  {
28734
29498
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28735
29499
  children: t.layout
@@ -28747,7 +29511,7 @@ var SettingsSheet = ({
28747
29511
  !isDouble && { backgroundColor: accentColor }
28748
29512
  ],
28749
29513
  children: /* @__PURE__ */ jsx13(
28750
- Text7,
29514
+ Text8,
28751
29515
  {
28752
29516
  style: [
28753
29517
  styles12.optionText,
@@ -28770,7 +29534,7 @@ var SettingsSheet = ({
28770
29534
  isDouble && { backgroundColor: accentColor }
28771
29535
  ],
28772
29536
  children: /* @__PURE__ */ jsx13(
28773
- Text7,
29537
+ Text8,
28774
29538
  {
28775
29539
  style: [
28776
29540
  styles12.optionText,
@@ -28786,7 +29550,7 @@ var SettingsSheet = ({
28786
29550
  ] }),
28787
29551
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28788
29552
  /* @__PURE__ */ jsx13(
28789
- Text7,
29553
+ Text8,
28790
29554
  {
28791
29555
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28792
29556
  children: t.rotate
@@ -28799,7 +29563,7 @@ var SettingsSheet = ({
28799
29563
  onPress: () => handleRotate("counterclockwise"),
28800
29564
  style: [styles12.optionButton, isDark && styles12.optionButtonDark],
28801
29565
  children: /* @__PURE__ */ jsx13(
28802
- Text7,
29566
+ Text8,
28803
29567
  {
28804
29568
  style: [styles12.optionText, isDark && styles12.optionTextDark],
28805
29569
  children: t.counterclockwise
@@ -28813,7 +29577,7 @@ var SettingsSheet = ({
28813
29577
  onPress: () => handleRotate("clockwise"),
28814
29578
  style: [styles12.optionButton, isDark && styles12.optionButtonDark],
28815
29579
  children: /* @__PURE__ */ jsx13(
28816
- Text7,
29580
+ Text8,
28817
29581
  {
28818
29582
  style: [styles12.optionText, isDark && styles12.optionTextDark],
28819
29583
  children: t.clockwise
@@ -28825,7 +29589,7 @@ var SettingsSheet = ({
28825
29589
  ] }),
28826
29590
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28827
29591
  /* @__PURE__ */ jsx13(
28828
- Text7,
29592
+ Text8,
28829
29593
  {
28830
29594
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28831
29595
  children: t.zoom
@@ -28840,7 +29604,7 @@ var SettingsSheet = ({
28840
29604
  children: /* @__PURE__ */ jsx13(IconZoomOut, { size: 16, color: isDark ? "#e5e7eb" : "#111827" })
28841
29605
  }
28842
29606
  ),
28843
- /* @__PURE__ */ jsx13(View12, { style: styles12.zoomValue, children: /* @__PURE__ */ jsxs13(Text7, { style: [styles12.zoomText, isDark && styles12.zoomTextDark], children: [
29607
+ /* @__PURE__ */ jsx13(View12, { style: styles12.zoomValue, children: /* @__PURE__ */ jsxs13(Text8, { style: [styles12.zoomText, isDark && styles12.zoomTextDark], children: [
28844
29608
  Math.round(zoom * 100),
28845
29609
  "%"
28846
29610
  ] }) }),
@@ -28856,7 +29620,7 @@ var SettingsSheet = ({
28856
29620
  ] }),
28857
29621
  /* @__PURE__ */ jsxs13(View12, { style: styles12.section, children: [
28858
29622
  /* @__PURE__ */ jsx13(
28859
- Text7,
29623
+ Text8,
28860
29624
  {
28861
29625
  style: [styles12.sectionTitle, isDark && styles12.sectionTitleDark],
28862
29626
  children: t.language
@@ -28874,7 +29638,7 @@ var SettingsSheet = ({
28874
29638
  locale === "en" && { backgroundColor: accentColor }
28875
29639
  ],
28876
29640
  children: /* @__PURE__ */ jsx13(
28877
- Text7,
29641
+ Text8,
28878
29642
  {
28879
29643
  style: [
28880
29644
  styles12.optionText,
@@ -28897,7 +29661,7 @@ var SettingsSheet = ({
28897
29661
  locale === "pt-BR" && { backgroundColor: accentColor }
28898
29662
  ],
28899
29663
  children: /* @__PURE__ */ jsx13(
28900
- Text7,
29664
+ Text8,
28901
29665
  {
28902
29666
  style: [
28903
29667
  styles12.optionText,
@@ -29050,11 +29814,11 @@ var styles12 = StyleSheet12.create({
29050
29814
  var SettingsSheet_default = SettingsSheet;
29051
29815
 
29052
29816
  // components/CoverPreview.tsx
29053
- import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as useState9 } from "react";
29817
+ import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as useState10 } from "react";
29054
29818
  import {
29055
29819
  ActivityIndicator,
29056
29820
  StyleSheet as StyleSheet13,
29057
- Text as Text8,
29821
+ Text as Text9,
29058
29822
  UIManager as UIManager2,
29059
29823
  View as View13,
29060
29824
  findNodeHandle as findNodeHandle3
@@ -29108,11 +29872,11 @@ var CoverPreview = ({
29108
29872
  onLoadEnd,
29109
29873
  onError
29110
29874
  }) => {
29111
- const [engine, setEngine] = useState9(null);
29112
- const [layoutReady, setLayoutReady] = useState9(false);
29113
- const [loaded, setLoaded] = useState9(false);
29114
- const [loading, setLoading] = useState9(false);
29115
- const [error, setError] = useState9(null);
29875
+ const [engine, setEngine] = useState10(null);
29876
+ const [layoutReady, setLayoutReady] = useState10(false);
29877
+ const [loaded, setLoaded] = useState10(false);
29878
+ const [loading, setLoading] = useState10(false);
29879
+ const [error, setError] = useState10(null);
29116
29880
  const viewRef = useRef7(null);
29117
29881
  const resolvedType = useMemo6(() => inferDocumentType(source, type), [source, type]);
29118
29882
  const isPdf = resolvedType === "pdf";
@@ -29177,12 +29941,12 @@ var CoverPreview = ({
29177
29941
  }
29178
29942
  };
29179
29943
  if (!shouldRender || !canRender) {
29180
- return /* @__PURE__ */ jsx14(View13, { style: [styles13.container, style], children: placeholder ?? /* @__PURE__ */ jsx14(View13, { style: styles13.placeholder, children: /* @__PURE__ */ jsx14(Text8, { style: styles13.placeholderText, children: "Preview" }) }) });
29944
+ return /* @__PURE__ */ jsx14(View13, { style: [styles13.container, style], children: placeholder ?? /* @__PURE__ */ jsx14(View13, { style: styles13.placeholder, children: /* @__PURE__ */ jsx14(Text9, { style: styles13.placeholderText, children: "Preview" }) }) });
29181
29945
  }
29182
29946
  return /* @__PURE__ */ jsxs14(View13, { style: [styles13.container, style], onLayout: handleLayout, children: [
29183
29947
  /* @__PURE__ */ jsx14(View13, { style: styles13.previewFrame, pointerEvents: allowInteraction ? "auto" : "none", children: isPdf ? /* @__PURE__ */ jsx14(PapyrusPageView3, { ref: viewRef, style: styles13.pageView }) : engine && /* @__PURE__ */ jsx14(WebViewViewer_default, { engine }) }),
29184
29948
  showLoading && loading && /* @__PURE__ */ jsx14(View13, { pointerEvents: "none", style: styles13.loadingOverlay, children: loadingIndicator ?? /* @__PURE__ */ jsx14(ActivityIndicator, { size: "small", color: "#2563eb" }) }),
29185
- error && /* @__PURE__ */ jsx14(View13, { pointerEvents: "none", style: styles13.errorOverlay, children: /* @__PURE__ */ jsx14(Text8, { style: styles13.errorText, children: "Preview unavailable" }) })
29949
+ error && /* @__PURE__ */ jsx14(View13, { pointerEvents: "none", style: styles13.errorOverlay, children: /* @__PURE__ */ jsx14(Text9, { style: styles13.errorText, children: "Preview unavailable" }) })
29186
29950
  ] });
29187
29951
  };
29188
29952
  var styles13 = StyleSheet13.create({
@@ -29232,7 +29996,7 @@ var styles13 = StyleSheet13.create({
29232
29996
  var CoverPreview_default = CoverPreview;
29233
29997
 
29234
29998
  // components/ReadingShell.tsx
29235
- import React13, { useState as useState11 } from "react";
29999
+ import React13, { useState as useState12 } from "react";
29236
30000
  import { Keyboard as Keyboard2, View as View20, StyleSheet as StyleSheet20 } from "react-native";
29237
30001
  import { useViewerStore as useViewerStore17 } from "@papyrus-sdk/core";
29238
30002
 
@@ -29241,7 +30005,7 @@ import {
29241
30005
  Modal as Modal4,
29242
30006
  Pressable as Pressable11,
29243
30007
  StyleSheet as StyleSheet14,
29244
- Text as Text9,
30008
+ Text as Text10,
29245
30009
  View as View14
29246
30010
  } from "react-native";
29247
30011
  import { useViewerStore as useViewerStore11 } from "@papyrus-sdk/core";
@@ -29258,12 +30022,12 @@ function DocumentActionsSheet({
29258
30022
  onPress: (event) => event.stopPropagation(),
29259
30023
  style: [styles14.card, isDark && styles14.cardDark],
29260
30024
  children: [
29261
- /* @__PURE__ */ jsx15(Text9, { style: [styles14.eyebrow, isDark && styles14.eyebrowDark], children: "Document actions" }),
30025
+ /* @__PURE__ */ jsx15(Text10, { style: [styles14.eyebrow, isDark && styles14.eyebrowDark], children: "Document actions" }),
29262
30026
  /* @__PURE__ */ jsxs15(View14, { style: styles14.actionList, children: [
29263
- /* @__PURE__ */ jsx15(View14, { style: [styles14.actionItem, isDark && styles14.actionItemDark], children: /* @__PURE__ */ jsx15(Text9, { style: [styles14.actionText, isDark && styles14.actionTextDark], children: "Share" }) }),
29264
- /* @__PURE__ */ jsx15(View14, { style: [styles14.actionItem, isDark && styles14.actionItemDark], children: /* @__PURE__ */ jsx15(Text9, { style: [styles14.actionText, isDark && styles14.actionTextDark], children: "Export" }) })
30027
+ /* @__PURE__ */ jsx15(View14, { style: [styles14.actionItem, isDark && styles14.actionItemDark], children: /* @__PURE__ */ jsx15(Text10, { style: [styles14.actionText, isDark && styles14.actionTextDark], children: "Share" }) }),
30028
+ /* @__PURE__ */ jsx15(View14, { style: [styles14.actionItem, isDark && styles14.actionItemDark], children: /* @__PURE__ */ jsx15(Text10, { style: [styles14.actionText, isDark && styles14.actionTextDark], children: "Export" }) })
29265
30029
  ] }),
29266
- /* @__PURE__ */ jsx15(Pressable11, { onPress: onClose, style: styles14.closeButton, accessibilityLabel: "Close actions sheet", children: /* @__PURE__ */ jsx15(Text9, { style: styles14.closeText, children: "Close" }) })
30030
+ /* @__PURE__ */ jsx15(Pressable11, { onPress: onClose, style: styles14.closeButton, accessibilityLabel: "Close actions sheet", children: /* @__PURE__ */ jsx15(Text10, { style: styles14.closeText, children: "Close" }) })
29267
30031
  ]
29268
30032
  }
29269
30033
  ) }) });
@@ -29334,7 +30098,7 @@ import {
29334
30098
  Pressable as Pressable12,
29335
30099
  ScrollView as ScrollView4,
29336
30100
  StyleSheet as StyleSheet15,
29337
- Text as Text10,
30101
+ Text as Text11,
29338
30102
  View as View15
29339
30103
  } from "react-native";
29340
30104
  import { useViewerStore as useViewerStore12 } from "@papyrus-sdk/core";
@@ -29493,7 +30257,7 @@ function InfoSheet({
29493
30257
  }
29494
30258
  ),
29495
30259
  /* @__PURE__ */ jsx16(
29496
- Text10,
30260
+ Text11,
29497
30261
  {
29498
30262
  style: [styles15.headerTitle, isDark && styles15.headerTitleDark],
29499
30263
  numberOfLines: 1,
@@ -29510,7 +30274,7 @@ function InfoSheet({
29510
30274
  showsVerticalScrollIndicator: false,
29511
30275
  children: [
29512
30276
  /* @__PURE__ */ jsx16(
29513
- Text10,
30277
+ Text11,
29514
30278
  {
29515
30279
  style: [styles15.sectionTitle, isDark && styles15.sectionTitleDark],
29516
30280
  children: locale === "pt-BR" ? "Informa\xE7\xF5es" : "Information"
@@ -29521,9 +30285,9 @@ function InfoSheet({
29521
30285
  {
29522
30286
  style: [styles15.row, isDark && styles15.rowDark],
29523
30287
  children: [
29524
- /* @__PURE__ */ jsx16(Text10, { style: [styles15.label, isDark && styles15.labelDark], children: row.label }),
30288
+ /* @__PURE__ */ jsx16(Text11, { style: [styles15.label, isDark && styles15.labelDark], children: row.label }),
29525
30289
  /* @__PURE__ */ jsx16(
29526
- Text10,
30290
+ Text11,
29527
30291
  {
29528
30292
  style: [styles15.value, isDark && styles15.valueDark],
29529
30293
  selectable: true,
@@ -29646,7 +30410,7 @@ var styles15 = StyleSheet15.create({
29646
30410
  });
29647
30411
 
29648
30412
  // components/OverflowSheet.tsx
29649
- import { Modal as Modal6, Pressable as Pressable13, StyleSheet as StyleSheet16, Text as Text11, View as View16 } from "react-native";
30413
+ import { Modal as Modal6, Pressable as Pressable13, StyleSheet as StyleSheet16, Text as Text12, View as View16 } from "react-native";
29650
30414
  import { useViewerStore as useViewerStore13 } from "@papyrus-sdk/core";
29651
30415
  import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
29652
30416
  var actionLabels = [{ key: "actions", label: "Document actions" }];
@@ -29681,7 +30445,7 @@ function OverflowSheet({
29681
30445
  style: [styles16.actionButton, { borderColor: accentColor }],
29682
30446
  accessibilityLabel: action.label,
29683
30447
  children: /* @__PURE__ */ jsx17(
29684
- Text11,
30448
+ Text12,
29685
30449
  {
29686
30450
  style: [styles16.actionText, isDark && styles16.actionTextDark],
29687
30451
  children: action.label
@@ -29738,7 +30502,7 @@ var styles16 = StyleSheet16.create({
29738
30502
 
29739
30503
  // components/ProgressPill.tsx
29740
30504
  import { useMemo as useMemo7 } from "react";
29741
- import { Pressable as Pressable14, StyleSheet as StyleSheet17, Text as Text12, View as View17 } from "react-native";
30505
+ import { Pressable as Pressable14, StyleSheet as StyleSheet17, Text as Text13, View as View17 } from "react-native";
29742
30506
  import { useViewerStore as useViewerStore14 } from "@papyrus-sdk/core";
29743
30507
  import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
29744
30508
  var clampPercent = (value) => Math.max(0, Math.min(100, Math.round(value)));
@@ -29802,7 +30566,7 @@ function ProgressPill({
29802
30566
  onLongPress: onOpenPageJump,
29803
30567
  style: styles17.labelHit,
29804
30568
  accessibilityLabel: "Open page jump",
29805
- children: /* @__PURE__ */ jsx18(Text12, { style: [styles17.label, isDark && styles17.labelDark], children: label })
30569
+ children: /* @__PURE__ */ jsx18(Text13, { style: [styles17.label, isDark && styles17.labelDark], children: label })
29806
30570
  }
29807
30571
  )
29808
30572
  ]
@@ -29857,12 +30621,12 @@ var styles17 = StyleSheet17.create({
29857
30621
  });
29858
30622
 
29859
30623
  // components/SearchOverlay.tsx
29860
- import { useEffect as useEffect10, useMemo as useMemo8, useState as useState10 } from "react";
30624
+ import { useEffect as useEffect10, useMemo as useMemo8, useState as useState11 } from "react";
29861
30625
  import {
29862
30626
  Keyboard,
29863
30627
  Pressable as Pressable15,
29864
30628
  StyleSheet as StyleSheet18,
29865
- Text as Text13,
30629
+ Text as Text14,
29866
30630
  TextInput as TextInput3,
29867
30631
  View as View18
29868
30632
  } from "react-native";
@@ -29886,8 +30650,8 @@ function SearchOverlay({
29886
30650
  uiTheme,
29887
30651
  accentColor
29888
30652
  } = useViewerStore15();
29889
- const [draft, setDraft] = useState10(searchQuery);
29890
- const [isSearching, setIsSearching] = useState10(false);
30653
+ const [draft, setDraft] = useState11(searchQuery);
30654
+ const [isSearching, setIsSearching] = useState11(false);
29891
30655
  const isDark = uiTheme === "dark";
29892
30656
  const t = getStrings(locale);
29893
30657
  const searchService = useMemo8(() => new SearchService(engine), [engine]);
@@ -29947,7 +30711,7 @@ function SearchOverlay({
29947
30711
  onPress: handleClose,
29948
30712
  style: [styles18.closeButton, isDark && styles18.closeButtonDark],
29949
30713
  accessibilityLabel: "Close search",
29950
- children: /* @__PURE__ */ jsx19(Text13, { style: [styles18.closeText, isDark && styles18.closeTextDark], children: t.cancel })
30714
+ children: /* @__PURE__ */ jsx19(Text14, { style: [styles18.closeText, isDark && styles18.closeTextDark], children: t.cancel })
29951
30715
  }
29952
30716
  )
29953
30717
  ] }),
@@ -29966,7 +30730,7 @@ function SearchOverlay({
29966
30730
  children: /* @__PURE__ */ jsx19(IconChevronLeft, { size: 16, color: isDark ? "#e5e7eb" : "#111827" })
29967
30731
  }
29968
30732
  ),
29969
- /* @__PURE__ */ jsx19(Text13, { style: [styles18.metaText, isDark && styles18.metaTextDark], children: isSearching ? t.searching : `${currentCount}/${searchResults.length} ${targetLabel}` }),
30733
+ /* @__PURE__ */ jsx19(Text14, { style: [styles18.metaText, isDark && styles18.metaTextDark], children: isSearching ? t.searching : `${currentCount}/${searchResults.length} ${targetLabel}` }),
29970
30734
  /* @__PURE__ */ jsx19(
29971
30735
  Pressable15,
29972
30736
  {
@@ -29989,7 +30753,7 @@ function SearchOverlay({
29989
30753
  onPress: handleSubmit,
29990
30754
  style: [styles18.primaryAction, { backgroundColor: accentColor }],
29991
30755
  accessibilityLabel: "Run search",
29992
- children: /* @__PURE__ */ jsx19(Text13, { style: styles18.primaryActionText, children: t.searchGo })
30756
+ children: /* @__PURE__ */ jsx19(Text14, { style: styles18.primaryActionText, children: t.searchGo })
29993
30757
  }
29994
30758
  ),
29995
30759
  /* @__PURE__ */ jsx19(
@@ -30004,7 +30768,7 @@ function SearchOverlay({
30004
30768
  ],
30005
30769
  accessibilityLabel: "Open all results",
30006
30770
  children: /* @__PURE__ */ jsx19(
30007
- Text13,
30771
+ Text14,
30008
30772
  {
30009
30773
  style: [
30010
30774
  styles18.secondaryActionText,
@@ -30166,7 +30930,7 @@ import {
30166
30930
  Pressable as Pressable16,
30167
30931
  ScrollView as ScrollView5,
30168
30932
  StyleSheet as StyleSheet19,
30169
- Text as Text14,
30933
+ Text as Text15,
30170
30934
  View as View19
30171
30935
  } from "react-native";
30172
30936
  import { useViewerStore as useViewerStore16 } from "@papyrus-sdk/core";
@@ -30206,14 +30970,14 @@ function SearchResultsSheet({
30206
30970
  /* @__PURE__ */ jsxs20(View19, { style: [styles19.sheet, isDark && styles19.sheetDark], children: [
30207
30971
  /* @__PURE__ */ jsx20(View19, { style: [styles19.handle, isDark && styles19.handleDark] }),
30208
30972
  /* @__PURE__ */ jsxs20(View19, { style: styles19.header, children: [
30209
- /* @__PURE__ */ jsx20(Text14, { style: [styles19.title, isDark && styles19.titleDark], children: t.allResults }),
30210
- /* @__PURE__ */ jsxs20(Text14, { style: [styles19.meta, isDark && styles19.metaDark], children: [
30973
+ /* @__PURE__ */ jsx20(Text15, { style: [styles19.title, isDark && styles19.titleDark], children: t.allResults }),
30974
+ /* @__PURE__ */ jsxs20(Text15, { style: [styles19.meta, isDark && styles19.metaDark], children: [
30211
30975
  searchResults.length,
30212
30976
  " ",
30213
30977
  t.results
30214
30978
  ] })
30215
30979
  ] }),
30216
- /* @__PURE__ */ jsx20(ScrollView5, { contentContainerStyle: styles19.content, children: searchResults.length === 0 ? /* @__PURE__ */ jsx20(Text14, { style: [styles19.emptyText, isDark && styles19.emptyTextDark], children: t.noResults }) : searchResults.map((result, index) => {
30980
+ /* @__PURE__ */ jsx20(ScrollView5, { contentContainerStyle: styles19.content, children: searchResults.length === 0 ? /* @__PURE__ */ jsx20(Text15, { style: [styles19.emptyText, isDark && styles19.emptyTextDark], children: t.noResults }) : searchResults.map((result, index) => {
30217
30981
  const isActive = index === activeSearchIndex;
30218
30982
  return /* @__PURE__ */ jsxs20(
30219
30983
  Pressable16,
@@ -30235,9 +30999,9 @@ function SearchResultsSheet({
30235
30999
  }
30236
31000
  ],
30237
31001
  children: [
30238
- /* @__PURE__ */ jsx20(Text14, { style: [styles19.resultLabel, { color: accentColor }], children: getResultLabel(result.pageIndex) }),
31002
+ /* @__PURE__ */ jsx20(Text15, { style: [styles19.resultLabel, { color: accentColor }], children: getResultLabel(result.pageIndex) }),
30239
31003
  /* @__PURE__ */ jsx20(
30240
- Text14,
31004
+ Text15,
30241
31005
  {
30242
31006
  style: [
30243
31007
  styles19.resultText,
@@ -30378,12 +31142,12 @@ function ReadingShell({
30378
31142
  uiTheme,
30379
31143
  accentColor
30380
31144
  } = useViewerStore17();
30381
- const [overflowOpen, setOverflowOpen] = useState11(false);
30382
- const [infoOpen, setInfoOpen] = useState11(false);
30383
- const [actionsOpen, setActionsOpen] = useState11(false);
30384
- const [settingsOpen, setSettingsOpen] = useState11(false);
30385
- const [searchResultsOpen, setSearchResultsOpen] = useState11(false);
30386
- const [pageJumpOpen, setPageJumpOpen] = useState11(false);
31145
+ const [overflowOpen, setOverflowOpen] = useState12(false);
31146
+ const [infoOpen, setInfoOpen] = useState12(false);
31147
+ const [actionsOpen, setActionsOpen] = useState12(false);
31148
+ const [settingsOpen, setSettingsOpen] = useState12(false);
31149
+ const [searchResultsOpen, setSearchResultsOpen] = useState12(false);
31150
+ const [pageJumpOpen, setPageJumpOpen] = useState12(false);
30387
31151
  const isDark = uiTheme === "dark";
30388
31152
  React13.useEffect(() => {
30389
31153
  const showSubscription = Keyboard2.addListener("keyboardDidShow", () => {