@papyrus-sdk/ui-react-native 0.2.19 → 0.2.21
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/README.md +12 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +990 -267
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1032 -308
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/runtime/comicRuntime.js +107 -0
- package/runtime/comicRuntime.test.ts +90 -0
- package/runtime/index.html +829 -248
- package/runtime/runtime.js +744 -3
- package/runtime/syncComicRuntime.mjs +41 -0
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(
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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(
|
|
20659
|
+
window.addEventListener('error', (event) => {
|
|
20490
20660
|
const error = event.error || event.message;
|
|
20491
20661
|
if (shouldIgnoreError(error)) return;
|
|
20492
|
-
reportError(error,
|
|
20662
|
+
reportError(error, 'window.error');
|
|
20493
20663
|
});
|
|
20494
20664
|
|
|
20495
|
-
window.addEventListener(
|
|
20665
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
20496
20666
|
if (shouldIgnoreError(event.reason)) return;
|
|
20497
|
-
reportError(event.reason,
|
|
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(
|
|
20716
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
20520
20717
|
};
|
|
20521
20718
|
|
|
20522
|
-
const
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
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
|
+
}
|
|
20762
|
+
};
|
|
20763
|
+
|
|
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
|
-
|
|
20531
|
-
|
|
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]);
|
|
20532
20942
|
}
|
|
20533
|
-
|
|
20534
|
-
|
|
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.');
|
|
20955
|
+
}
|
|
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.
|
|
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(
|
|
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 ===
|
|
21170
|
+
if (typeof rendition.themes.fontSize === 'function') {
|
|
20579
21171
|
rendition.themes.fontSize(fontSize);
|
|
20580
|
-
} else if (typeof rendition.themes.override ===
|
|
20581
|
-
rendition.themes.override(
|
|
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(
|
|
20595
|
-
textContainer.style.padding =
|
|
20596
|
-
textContainer.style.lineHeight =
|
|
20597
|
-
textContainer.style.whiteSpace =
|
|
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 ===
|
|
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 ===
|
|
21210
|
+
} else if (source.kind === 'base64') {
|
|
20620
21211
|
text = decodeBase64ToText(source.data);
|
|
20621
|
-
} else if (source.kind ===
|
|
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
|
-
|
|
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 ===
|
|
21229
|
+
if (book && typeof book.destroy === 'function') {
|
|
20640
21230
|
book.destroy();
|
|
20641
21231
|
}
|
|
20642
21232
|
|
|
20643
|
-
|
|
20644
|
-
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20649
|
-
);
|
|
20650
|
-
|
|
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" });
|
|
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;
|
|
20667
21241
|
}
|
|
20668
|
-
|
|
20669
|
-
|
|
20670
|
-
|
|
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:
|
|
20681
|
-
height:
|
|
20682
|
-
flow:
|
|
20683
|
-
spread:
|
|
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 ===
|
|
20702
|
-
rendition.on(
|
|
20703
|
-
const selection =
|
|
20704
|
-
|
|
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(
|
|
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
|
-
|
|
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 ===
|
|
20753
|
-
const text = textPages[pageIndex] ||
|
|
20754
|
-
return [
|
|
20755
|
-
|
|
20756
|
-
|
|
20757
|
-
|
|
20758
|
-
|
|
20759
|
-
|
|
20760
|
-
|
|
20761
|
-
|
|
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 ===
|
|
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
|
-
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
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 ===
|
|
20834
|
-
if (typeof dest !==
|
|
20835
|
-
if (dest.kind ===
|
|
20836
|
-
if (dest.kind ===
|
|
20837
|
-
if (dest.kind ===
|
|
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 ===
|
|
21386
|
+
if (currentType === 'text') {
|
|
20852
21387
|
applyTextZoom();
|
|
20853
|
-
} else if (currentType ===
|
|
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 ===
|
|
21399
|
+
if (kind === 'load') {
|
|
20864
21400
|
currentType = payload.type;
|
|
20865
21401
|
zoom = 1.0;
|
|
20866
21402
|
|
|
20867
|
-
if (currentType ===
|
|
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 ===
|
|
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
|
-
|
|
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 ===
|
|
21427
|
+
if (kind === 'go-to-page') {
|
|
20885
21428
|
const page = Math.max(1, payload.page || 1);
|
|
20886
|
-
if (currentType ===
|
|
21429
|
+
if (currentType === 'text') {
|
|
20887
21430
|
currentPage = page;
|
|
20888
21431
|
renderTextPage(page - 1);
|
|
20889
21432
|
sendState();
|
|
20890
|
-
} else if (currentType ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
20917
|
-
|
|
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
|
+
}
|
|
20918
21480
|
return;
|
|
20919
21481
|
}
|
|
20920
21482
|
|
|
20921
|
-
if (kind ===
|
|
20922
|
-
|
|
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
|
+
);
|
|
21493
|
+
return;
|
|
21494
|
+
}
|
|
21495
|
+
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
21520
|
+
if (kind === 'destroy') {
|
|
20946
21521
|
clearViewer();
|
|
20947
21522
|
sendResponse(id, true, {});
|
|
20948
21523
|
return;
|
|
20949
21524
|
}
|
|
20950
21525
|
|
|
20951
|
-
sendResponse(id, false, null,
|
|
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 ===
|
|
21534
|
+
const raw = event && typeof event === 'object' ? event.data : null;
|
|
20965
21535
|
|
|
20966
|
-
if (raw && typeof raw ===
|
|
21536
|
+
if (raw && typeof raw === 'object' && (raw.kind || raw.type) && raw.id) {
|
|
20967
21537
|
message = raw;
|
|
20968
|
-
} else if (typeof raw ===
|
|
20969
|
-
if (raw.startsWith(
|
|
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
|
-
|
|
20995
|
-
|
|
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(
|
|
20999
|
-
document.addEventListener(
|
|
21579
|
+
window.addEventListener('message', onMessage);
|
|
21580
|
+
document.addEventListener('message', onMessage);
|
|
21000
21581
|
|
|
21001
|
-
sendMessage({ type:
|
|
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
|
|
21597
|
+
useState as useState4
|
|
21017
21598
|
} from "react";
|
|
21018
21599
|
import {
|
|
21019
21600
|
FlatList,
|
|
@@ -21310,6 +21891,12 @@ var buildCommentTapGestureDeps = ({
|
|
|
21310
21891
|
addAnnotationAt
|
|
21311
21892
|
];
|
|
21312
21893
|
|
|
21894
|
+
// components/pdfPageMetrics.ts
|
|
21895
|
+
var resolvePdfBasePageWidth = ({
|
|
21896
|
+
viewportWidth,
|
|
21897
|
+
horizontalPadding
|
|
21898
|
+
}) => Math.max(0, viewportWidth - horizontalPadding * 2);
|
|
21899
|
+
|
|
21313
21900
|
// components/PageRenderer.tsx
|
|
21314
21901
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21315
21902
|
var TEXT_MARKUP_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -22271,7 +22858,10 @@ var PageRenderer = ({
|
|
|
22271
22858
|
}, [pageTheme]);
|
|
22272
22859
|
const aspectRatio = typeof pageAspectRatio === "number" && Number.isFinite(pageAspectRatio) && pageAspectRatio > 0 ? pageAspectRatio : pageSize && pageSize.width > 0 && pageSize.height > 0 ? pageSize.width / pageSize.height : 0.77;
|
|
22273
22860
|
const containerWidth = availableWidth ?? windowWidth;
|
|
22274
|
-
const baseWidth =
|
|
22861
|
+
const baseWidth = resolvePdfBasePageWidth({
|
|
22862
|
+
viewportWidth: containerWidth,
|
|
22863
|
+
horizontalPadding
|
|
22864
|
+
});
|
|
22275
22865
|
const pageWidth = isNative ? baseWidth * zoom : baseWidth;
|
|
22276
22866
|
const pageHeight = pageWidth / aspectRatio;
|
|
22277
22867
|
const pageFrameWidth = Math.max(
|
|
@@ -22995,33 +23585,104 @@ var arePageRendererPropsEqual = (previous, next) => previous.engine === next.eng
|
|
|
22995
23585
|
var PageRenderer_default = memo(PageRenderer, arePageRendererPropsEqual);
|
|
22996
23586
|
|
|
22997
23587
|
// components/WebViewViewer.tsx
|
|
22998
|
-
import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
23588
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
22999
23589
|
import { Image, StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
|
23000
23590
|
import WebView from "react-native-webview";
|
|
23001
23591
|
import { useViewerStore as useViewerStore2 } from "@papyrus-sdk/core";
|
|
23592
|
+
|
|
23593
|
+
// components/webViewState.ts
|
|
23594
|
+
var parseWebViewState = (raw) => {
|
|
23595
|
+
try {
|
|
23596
|
+
const message = JSON.parse(raw);
|
|
23597
|
+
if (!message || typeof message !== "object") return null;
|
|
23598
|
+
const typedMessage = message;
|
|
23599
|
+
if (typedMessage.type !== "state") return null;
|
|
23600
|
+
if (!typedMessage.payload || typeof typedMessage.payload !== "object") {
|
|
23601
|
+
return null;
|
|
23602
|
+
}
|
|
23603
|
+
const payload = typedMessage.payload;
|
|
23604
|
+
const update = {};
|
|
23605
|
+
if (typeof payload.currentPage === "number" && Number.isInteger(payload.currentPage) && payload.currentPage >= 1) {
|
|
23606
|
+
update.currentPage = payload.currentPage;
|
|
23607
|
+
}
|
|
23608
|
+
if (typeof payload.pageCount === "number" && Number.isInteger(payload.pageCount) && payload.pageCount >= 0) {
|
|
23609
|
+
update.pageCount = payload.pageCount;
|
|
23610
|
+
}
|
|
23611
|
+
return Object.keys(update).length > 0 ? update : null;
|
|
23612
|
+
} catch {
|
|
23613
|
+
return null;
|
|
23614
|
+
}
|
|
23615
|
+
};
|
|
23616
|
+
|
|
23617
|
+
// components/WebViewViewer.tsx
|
|
23002
23618
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
23003
23619
|
var runtimeAsset = require_runtime();
|
|
23004
|
-
var
|
|
23005
|
-
if (typeof
|
|
23006
|
-
return { html:
|
|
23620
|
+
var resolveRuntimeSource = (asset) => {
|
|
23621
|
+
if (typeof asset === "string") {
|
|
23622
|
+
return { html: asset };
|
|
23007
23623
|
}
|
|
23008
|
-
if (typeof
|
|
23009
|
-
const resolved = Image.resolveAssetSource(
|
|
23624
|
+
if (typeof asset === "number") {
|
|
23625
|
+
const resolved = Image.resolveAssetSource(asset);
|
|
23010
23626
|
if (resolved?.uri) {
|
|
23011
23627
|
return { uri: resolved.uri };
|
|
23012
23628
|
}
|
|
23013
23629
|
}
|
|
23014
|
-
if (
|
|
23015
|
-
const uri =
|
|
23630
|
+
if (asset && typeof asset === "object" && "uri" in asset) {
|
|
23631
|
+
const uri = asset.uri;
|
|
23016
23632
|
if (uri) return { uri };
|
|
23017
23633
|
}
|
|
23018
23634
|
return { html: "" };
|
|
23019
|
-
}
|
|
23635
|
+
};
|
|
23020
23636
|
var WebViewViewer = ({ engine }) => {
|
|
23021
23637
|
const webViewRef = useRef2(null);
|
|
23022
23638
|
const { pageTheme } = useViewerStore2();
|
|
23639
|
+
const bridgeEngine = engine;
|
|
23640
|
+
const runtimeSource = useMemo2(
|
|
23641
|
+
() => resolveRuntimeSource(
|
|
23642
|
+
bridgeEngine.getWebViewRuntimeSource?.() ?? runtimeAsset
|
|
23643
|
+
),
|
|
23644
|
+
[bridgeEngine]
|
|
23645
|
+
);
|
|
23646
|
+
const [runtimeHtml, setRuntimeHtml] = useState2(null);
|
|
23647
|
+
useEffect2(() => {
|
|
23648
|
+
let active = true;
|
|
23649
|
+
if ("html" in runtimeSource) {
|
|
23650
|
+
setRuntimeHtml(runtimeSource.html);
|
|
23651
|
+
return () => {
|
|
23652
|
+
active = false;
|
|
23653
|
+
};
|
|
23654
|
+
}
|
|
23655
|
+
setRuntimeHtml(null);
|
|
23656
|
+
void fetch(runtimeSource.uri).then((response) => {
|
|
23657
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
23658
|
+
return response.text();
|
|
23659
|
+
}).then((html) => {
|
|
23660
|
+
if (active) setRuntimeHtml(html);
|
|
23661
|
+
}).catch(() => {
|
|
23662
|
+
if (active) setRuntimeHtml(null);
|
|
23663
|
+
});
|
|
23664
|
+
return () => {
|
|
23665
|
+
active = false;
|
|
23666
|
+
};
|
|
23667
|
+
}, [runtimeSource]);
|
|
23668
|
+
const webViewSource = useMemo2(() => {
|
|
23669
|
+
if (runtimeHtml !== null) {
|
|
23670
|
+
return {
|
|
23671
|
+
html: runtimeHtml,
|
|
23672
|
+
baseUrl: "http://localhost:3005/"
|
|
23673
|
+
};
|
|
23674
|
+
}
|
|
23675
|
+
if ("uri" in runtimeSource) return { html: "" };
|
|
23676
|
+
return runtimeSource;
|
|
23677
|
+
}, [runtimeHtml, runtimeSource]);
|
|
23678
|
+
const runtimeConfig = bridgeEngine.getWebViewRuntimeConfig?.();
|
|
23679
|
+
const runtimeConfigScript = useMemo2(() => {
|
|
23680
|
+
if (!runtimeConfig) return void 0;
|
|
23681
|
+
return `window.__PAPYRUS_RUNTIME_CONFIG__=${JSON.stringify(
|
|
23682
|
+
runtimeConfig
|
|
23683
|
+
)};true;`;
|
|
23684
|
+
}, [runtimeConfig]);
|
|
23023
23685
|
useEffect2(() => {
|
|
23024
|
-
const bridgeEngine = engine;
|
|
23025
23686
|
bridgeEngine.attachWebView?.({
|
|
23026
23687
|
postMessage: (message) => {
|
|
23027
23688
|
if (__DEV__) {
|
|
@@ -23031,9 +23692,22 @@ var WebViewViewer = ({ engine }) => {
|
|
|
23031
23692
|
webViewRef.current?.postMessage(message);
|
|
23032
23693
|
}
|
|
23033
23694
|
});
|
|
23034
|
-
}, [
|
|
23695
|
+
}, [bridgeEngine]);
|
|
23035
23696
|
const handleMessage = (event) => {
|
|
23036
|
-
const
|
|
23697
|
+
const state = parseWebViewState(event.nativeEvent.data);
|
|
23698
|
+
if (state) {
|
|
23699
|
+
const viewerState = useViewerStore2.getState();
|
|
23700
|
+
const nextState = {};
|
|
23701
|
+
if (state.currentPage !== void 0 && state.currentPage !== viewerState.currentPage) {
|
|
23702
|
+
nextState.currentPage = state.currentPage;
|
|
23703
|
+
}
|
|
23704
|
+
if (state.pageCount !== void 0 && state.pageCount !== viewerState.pageCount) {
|
|
23705
|
+
nextState.pageCount = state.pageCount;
|
|
23706
|
+
}
|
|
23707
|
+
if (Object.keys(nextState).length > 0) {
|
|
23708
|
+
viewerState.setDocumentState(nextState);
|
|
23709
|
+
}
|
|
23710
|
+
}
|
|
23037
23711
|
if (__DEV__) {
|
|
23038
23712
|
console.log("[Papyrus WebView] message", event.nativeEvent.data);
|
|
23039
23713
|
}
|
|
@@ -23073,11 +23747,12 @@ var WebViewViewer = ({ engine }) => {
|
|
|
23073
23747
|
WebView,
|
|
23074
23748
|
{
|
|
23075
23749
|
ref: webViewRef,
|
|
23076
|
-
source:
|
|
23750
|
+
source: webViewSource,
|
|
23077
23751
|
originWhitelist: ["*"],
|
|
23078
23752
|
onMessage: handleMessage,
|
|
23079
23753
|
onLoadEnd: handleLoadEnd,
|
|
23080
23754
|
onError: handleError,
|
|
23755
|
+
injectedJavaScriptBeforeContentLoaded: runtimeConfigScript,
|
|
23081
23756
|
javaScriptEnabled: true,
|
|
23082
23757
|
domStorageEnabled: true,
|
|
23083
23758
|
scalesPageToFit: true,
|
|
@@ -23127,7 +23802,7 @@ var styles2 = StyleSheet2.create({
|
|
|
23127
23802
|
var WebViewViewer_default = WebViewViewer;
|
|
23128
23803
|
|
|
23129
23804
|
// components/DedicatedAndroidPdfViewer.tsx
|
|
23130
|
-
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as
|
|
23805
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
23131
23806
|
import { Pressable as Pressable2, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
23132
23807
|
import { useViewerStore as useViewerStore3 } from "@papyrus-sdk/core";
|
|
23133
23808
|
import { PapyrusPdfDocumentView } from "@papyrus-sdk/engine-native";
|
|
@@ -23773,7 +24448,7 @@ function DedicatedAndroidPdfViewer({
|
|
|
23773
24448
|
const addAnnotation = useViewerStore3((state) => state.addAnnotation);
|
|
23774
24449
|
const setSelectedAnnotation = useViewerStore3((state) => state.setSelectedAnnotation);
|
|
23775
24450
|
const engineId = getDedicatedAndroidPdfEngineId(engine);
|
|
23776
|
-
const [selection, setSelection] =
|
|
24451
|
+
const [selection, setSelection] = useState3(null);
|
|
23777
24452
|
const selectionRef = useRef3(null);
|
|
23778
24453
|
const lastVisiblePagesKeyRef = useRef3("");
|
|
23779
24454
|
const lastScrollOffsetYRef = useRef3(0);
|
|
@@ -24126,13 +24801,13 @@ var Viewer = ({
|
|
|
24126
24801
|
const lastScrollOffsetYRef = useRef4(0);
|
|
24127
24802
|
const scrollDownAccumRef = useRef4(0);
|
|
24128
24803
|
const scrollUpAccumRef = useRef4(0);
|
|
24129
|
-
const [layoutRevision, setLayoutRevision] =
|
|
24130
|
-
const [selectionDragActive, setSelectionDragActive] =
|
|
24804
|
+
const [layoutRevision, setLayoutRevision] = useState4(0);
|
|
24805
|
+
const [selectionDragActive, setSelectionDragActive] = useState4(false);
|
|
24131
24806
|
const selectionDragActiveRef = useRef4(false);
|
|
24132
|
-
const [gestureScrollLockActive, setGestureScrollLockActive] =
|
|
24807
|
+
const [gestureScrollLockActive, setGestureScrollLockActive] = useState4(false);
|
|
24133
24808
|
const gestureScrollLockActiveRef = useRef4(false);
|
|
24134
|
-
const [pinchPreviewScale, setPinchPreviewScale] =
|
|
24135
|
-
const [lastPinchEndedAt, setLastPinchEndedAt] =
|
|
24809
|
+
const [pinchPreviewScale, setPinchPreviewScale] = useState4(1);
|
|
24810
|
+
const [lastPinchEndedAt, setLastPinchEndedAt] = useState4(null);
|
|
24136
24811
|
const pinchGestureActiveRef = useRef4(false);
|
|
24137
24812
|
const pinchStartZoomRef = useRef4(1);
|
|
24138
24813
|
const pinchPreviewZoomRef = useRef4(1);
|
|
@@ -24418,7 +25093,10 @@ var Viewer = ({
|
|
|
24418
25093
|
const getPageWidthForZoom = useCallback3(
|
|
24419
25094
|
(pageIndex, zoomValue) => {
|
|
24420
25095
|
const safeZoom = Math.max(zoomValue, 0.25);
|
|
24421
|
-
const baseWidth =
|
|
25096
|
+
const baseWidth = resolvePdfBasePageWidth({
|
|
25097
|
+
viewportWidth: isDouble ? columnWidth : windowWidth,
|
|
25098
|
+
horizontalPadding: isDouble ? 8 : 16
|
|
25099
|
+
});
|
|
24422
25100
|
return baseWidth * safeZoom;
|
|
24423
25101
|
},
|
|
24424
25102
|
[columnWidth, isDouble, windowWidth]
|
|
@@ -24951,7 +25629,10 @@ var Viewer = ({
|
|
|
24951
25629
|
let offset = LIST_TOP_PADDING;
|
|
24952
25630
|
if (isDouble) {
|
|
24953
25631
|
const safeZoom2 = Math.max(zoom, 0.25);
|
|
24954
|
-
const pageWidth2 =
|
|
25632
|
+
const pageWidth2 = resolvePdfBasePageWidth({
|
|
25633
|
+
viewportWidth: columnWidth,
|
|
25634
|
+
horizontalPadding: 8
|
|
25635
|
+
}) * safeZoom2;
|
|
24955
25636
|
const estimatedLength2 = pageWidth2 / DEFAULT_PAGE_ASPECT_RATIO + DOUBLE_PAGE_SPACING;
|
|
24956
25637
|
for (let i = 0; i < rows.length; i += 1) {
|
|
24957
25638
|
const row = rows[i];
|
|
@@ -24967,7 +25648,10 @@ var Viewer = ({
|
|
|
24967
25648
|
return { offsets, lengths, estimatedLength: estimatedLength2 };
|
|
24968
25649
|
}
|
|
24969
25650
|
const safeZoom = Math.max(zoom, 0.25);
|
|
24970
|
-
const pageWidth =
|
|
25651
|
+
const pageWidth = resolvePdfBasePageWidth({
|
|
25652
|
+
viewportWidth: windowWidth,
|
|
25653
|
+
horizontalPadding: 16
|
|
25654
|
+
}) * safeZoom;
|
|
24971
25655
|
const estimatedLength = pageWidth / DEFAULT_PAGE_ASPECT_RATIO + CONTINUOUS_PAGE_SPACING;
|
|
24972
25656
|
for (let i = 0; i < pageCount; i += 1) {
|
|
24973
25657
|
const ratio = getPageAspectRatio(i);
|
|
@@ -25456,7 +26140,7 @@ var styles4 = StyleSheet4.create({
|
|
|
25456
26140
|
var Viewer_default = Viewer;
|
|
25457
26141
|
|
|
25458
26142
|
// components/Topbar.tsx
|
|
25459
|
-
import { useState as
|
|
26143
|
+
import { useState as useState6 } from "react";
|
|
25460
26144
|
import { View as View6, Text as Text2, Pressable as Pressable4, StyleSheet as StyleSheet6, useWindowDimensions as useWindowDimensions3 } from "react-native";
|
|
25461
26145
|
import { useViewerStore as useViewerStore5 } from "@papyrus-sdk/core";
|
|
25462
26146
|
|
|
@@ -25479,7 +26163,7 @@ var MOBILE_CHROME_METRICS = {
|
|
|
25479
26163
|
};
|
|
25480
26164
|
|
|
25481
26165
|
// components/PageJumpModal.tsx
|
|
25482
|
-
import { useEffect as useEffect5, useState as
|
|
26166
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
25483
26167
|
import { Modal, Pressable as Pressable3, StyleSheet as StyleSheet5, Text, TextInput, View as View5 } from "react-native";
|
|
25484
26168
|
|
|
25485
26169
|
// components/pageJumpModel.ts
|
|
@@ -25501,7 +26185,7 @@ function PageJumpModal({
|
|
|
25501
26185
|
onClose,
|
|
25502
26186
|
onConfirm
|
|
25503
26187
|
}) {
|
|
25504
|
-
const [value, setValue] =
|
|
26188
|
+
const [value, setValue] = useState5(`${currentPage}`);
|
|
25505
26189
|
useEffect5(() => {
|
|
25506
26190
|
if (visible) setValue(`${currentPage}`);
|
|
25507
26191
|
}, [currentPage, visible]);
|
|
@@ -25662,7 +26346,7 @@ var Topbar = ({
|
|
|
25662
26346
|
accentColor,
|
|
25663
26347
|
mobileChromeVisible
|
|
25664
26348
|
} = useViewerStore5();
|
|
25665
|
-
const [jumpModalOpen, setJumpModalOpen] =
|
|
26349
|
+
const [jumpModalOpen, setJumpModalOpen] = useState6(false);
|
|
25666
26350
|
const isDark = uiTheme === "dark";
|
|
25667
26351
|
const navIconColor = isDark ? "#e5e7eb" : "#111827";
|
|
25668
26352
|
const { width, height } = useWindowDimensions3();
|
|
@@ -25965,7 +26649,7 @@ var styles6 = StyleSheet6.create({
|
|
|
25965
26649
|
var Topbar_default = Topbar;
|
|
25966
26650
|
|
|
25967
26651
|
// components/ToolDock.tsx
|
|
25968
|
-
import React7, { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef5, useState as
|
|
26652
|
+
import React7, { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef5, useState as useState7 } from "react";
|
|
25969
26653
|
import {
|
|
25970
26654
|
View as View7,
|
|
25971
26655
|
Text as Text3,
|
|
@@ -26203,8 +26887,8 @@ var OpacitySlider = React7.memo(function OpacitySlider2({
|
|
|
26203
26887
|
isDark,
|
|
26204
26888
|
onCommit
|
|
26205
26889
|
}) {
|
|
26206
|
-
const [trackWidth, setTrackWidth] =
|
|
26207
|
-
const [draftValue, setDraftValue] =
|
|
26890
|
+
const [trackWidth, setTrackWidth] = useState7(0);
|
|
26891
|
+
const [draftValue, setDraftValue] = useState7(value);
|
|
26208
26892
|
const dragValueRef = useRef5(draftValue);
|
|
26209
26893
|
const isDraggingRef = useRef5(false);
|
|
26210
26894
|
const frameRef = useRef5(null);
|
|
@@ -26313,8 +26997,8 @@ var ToolDock = () => {
|
|
|
26313
26997
|
undoAnnotations,
|
|
26314
26998
|
redoAnnotations
|
|
26315
26999
|
} = useViewerStore6();
|
|
26316
|
-
const [paletteExpanded, setPaletteExpanded] =
|
|
26317
|
-
const [extrasExpanded, setExtrasExpanded] =
|
|
27000
|
+
const [paletteExpanded, setPaletteExpanded] = useState7(false);
|
|
27001
|
+
const [extrasExpanded, setExtrasExpanded] = useState7(false);
|
|
26318
27002
|
const isDark = uiTheme === "dark";
|
|
26319
27003
|
const { width: windowWidth } = useWindowDimensions4();
|
|
26320
27004
|
const t = getStrings(locale);
|
|
@@ -27037,9 +27721,10 @@ var withInkAlpha = (hex, alpha) => {
|
|
|
27037
27721
|
var ToolDock_default = ToolDock;
|
|
27038
27722
|
|
|
27039
27723
|
// components/RightSheet.tsx
|
|
27040
|
-
import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as
|
|
27724
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as useState8 } from "react";
|
|
27041
27725
|
import {
|
|
27042
27726
|
Dimensions,
|
|
27727
|
+
Image as Image2,
|
|
27043
27728
|
Platform as Platform3,
|
|
27044
27729
|
Pressable as Pressable7,
|
|
27045
27730
|
StyleSheet as StyleSheet9,
|
|
@@ -27230,11 +27915,13 @@ var PageThumbnail = ({
|
|
|
27230
27915
|
frameHeight,
|
|
27231
27916
|
accentColor,
|
|
27232
27917
|
useNativePreview,
|
|
27918
|
+
useImagePreview,
|
|
27233
27919
|
shouldRenderPreview,
|
|
27234
27920
|
onPress
|
|
27235
27921
|
}) => {
|
|
27236
27922
|
const viewRef = useRef6(null);
|
|
27237
|
-
const [layoutReady, setLayoutReady] =
|
|
27923
|
+
const [layoutReady, setLayoutReady] = useState8(false);
|
|
27924
|
+
const [previewUri, setPreviewUri] = useState8(null);
|
|
27238
27925
|
useEffect7(() => {
|
|
27239
27926
|
if (!layoutReady || !useNativePreview || !shouldRenderPreview) return;
|
|
27240
27927
|
const viewTag = findNodeHandle2(viewRef.current);
|
|
@@ -27250,6 +27937,24 @@ var PageThumbnail = ({
|
|
|
27250
27937
|
useNativePreview,
|
|
27251
27938
|
zoom
|
|
27252
27939
|
]);
|
|
27940
|
+
useEffect7(() => {
|
|
27941
|
+
if (!useImagePreview || !shouldRenderPreview) {
|
|
27942
|
+
setPreviewUri(null);
|
|
27943
|
+
return;
|
|
27944
|
+
}
|
|
27945
|
+
let active = true;
|
|
27946
|
+
const previewEngine = engine;
|
|
27947
|
+
const request = previewEngine.getPagePreview?.(pageIndex);
|
|
27948
|
+
if (!request) return;
|
|
27949
|
+
void request.then((uri) => {
|
|
27950
|
+
if (active) setPreviewUri(uri);
|
|
27951
|
+
}).catch(() => {
|
|
27952
|
+
if (active) setPreviewUri(null);
|
|
27953
|
+
});
|
|
27954
|
+
return () => {
|
|
27955
|
+
active = false;
|
|
27956
|
+
};
|
|
27957
|
+
}, [engine, pageIndex, shouldRenderPreview, useImagePreview]);
|
|
27253
27958
|
const handleLayout = (event) => {
|
|
27254
27959
|
if (event.nativeEvent.layout.width && event.nativeEvent.layout.height) {
|
|
27255
27960
|
setLayoutReady(true);
|
|
@@ -27272,7 +27977,14 @@ var PageThumbnail = ({
|
|
|
27272
27977
|
{
|
|
27273
27978
|
onLayout: handleLayout,
|
|
27274
27979
|
style: [styles9.thumbFrame, { width: frameWidth, height: frameHeight }],
|
|
27275
|
-
children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ jsx10(PapyrusPageView2, { ref: viewRef, style: styles9.thumbView }) :
|
|
27980
|
+
children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ jsx10(PapyrusPageView2, { ref: viewRef, style: styles9.thumbView }) : useImagePreview && shouldRenderPreview && previewUri ? /* @__PURE__ */ jsx10(
|
|
27981
|
+
Image2,
|
|
27982
|
+
{
|
|
27983
|
+
source: { uri: previewUri },
|
|
27984
|
+
resizeMode: "contain",
|
|
27985
|
+
style: styles9.thumbImage
|
|
27986
|
+
}
|
|
27987
|
+
) : /* @__PURE__ */ jsx10(View9, { style: [styles9.thumbFallback, isDark && styles9.thumbFallbackDark], children: /* @__PURE__ */ jsx10(
|
|
27276
27988
|
Text5,
|
|
27277
27989
|
{
|
|
27278
27990
|
style: [
|
|
@@ -27349,9 +28061,14 @@ var RightSheet = ({
|
|
|
27349
28061
|
locale,
|
|
27350
28062
|
accentColor
|
|
27351
28063
|
} = useViewerStore7();
|
|
27352
|
-
const [pagesMode, setPagesMode] =
|
|
27353
|
-
documentType === "pdf" ? "thumbnails" : "summary"
|
|
28064
|
+
const [pagesMode, setPagesMode] = useState8(
|
|
28065
|
+
documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
|
|
27354
28066
|
);
|
|
28067
|
+
useEffect7(() => {
|
|
28068
|
+
setPagesMode(
|
|
28069
|
+
documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
|
|
28070
|
+
);
|
|
28071
|
+
}, [documentType]);
|
|
27355
28072
|
const isDark = uiTheme === "dark";
|
|
27356
28073
|
const t = getStrings(locale);
|
|
27357
28074
|
const showingNotes = sidebarRightTab === "annotations";
|
|
@@ -27369,11 +28086,12 @@ var RightSheet = ({
|
|
|
27369
28086
|
UIManager.getViewManagerConfig?.("PapyrusPageView")
|
|
27370
28087
|
);
|
|
27371
28088
|
const useNativePreview = renderTarget !== "webview" && hasNativePageView;
|
|
28089
|
+
const useImagePreview = documentType === "comic" && renderTarget === "webview" && typeof engine.getPagePreview === "function";
|
|
27372
28090
|
const thumbnailDimensionsCacheRef = useRef6(/* @__PURE__ */ new Map());
|
|
27373
28091
|
const thumbnailDimensionsPendingRef = useRef6(/* @__PURE__ */ new Set());
|
|
27374
28092
|
const thumbnailRefreshTimeoutRef = useRef6(null);
|
|
27375
|
-
const [thumbnailLayoutRevision, setThumbnailLayoutRevision] =
|
|
27376
|
-
const [visibleThumbnailPages, setVisibleThumbnailPages] =
|
|
28093
|
+
const [thumbnailLayoutRevision, setThumbnailLayoutRevision] = useState8(0);
|
|
28094
|
+
const [visibleThumbnailPages, setVisibleThumbnailPages] = useState8(
|
|
27377
28095
|
() => /* @__PURE__ */ new Set()
|
|
27378
28096
|
);
|
|
27379
28097
|
const resolvedThumbsInitialCount = useMemo5(
|
|
@@ -27525,7 +28243,7 @@ var RightSheet = ({
|
|
|
27525
28243
|
);
|
|
27526
28244
|
const renderThumbnailItem = useCallback4(
|
|
27527
28245
|
({ item }) => {
|
|
27528
|
-
const shouldRenderPreview = useNativePreview && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
|
|
28246
|
+
const shouldRenderPreview = (useNativePreview || useImagePreview) && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
|
|
27529
28247
|
return /* @__PURE__ */ jsx10(
|
|
27530
28248
|
PageThumbnail,
|
|
27531
28249
|
{
|
|
@@ -27539,6 +28257,7 @@ var RightSheet = ({
|
|
|
27539
28257
|
frameHeight: getThumbnailFrameHeight(item),
|
|
27540
28258
|
accentColor,
|
|
27541
28259
|
useNativePreview,
|
|
28260
|
+
useImagePreview,
|
|
27542
28261
|
shouldRenderPreview,
|
|
27543
28262
|
onPress: () => jumpToPage(item)
|
|
27544
28263
|
}
|
|
@@ -27555,6 +28274,7 @@ var RightSheet = ({
|
|
|
27555
28274
|
jumpToPage,
|
|
27556
28275
|
resolvedThumbsPrewarmCount,
|
|
27557
28276
|
useNativePreview,
|
|
28277
|
+
useImagePreview,
|
|
27558
28278
|
visibleThumbnailPages,
|
|
27559
28279
|
zoom
|
|
27560
28280
|
]
|
|
@@ -27886,6 +28606,10 @@ var styles9 = StyleSheet9.create({
|
|
|
27886
28606
|
width: "100%",
|
|
27887
28607
|
height: "100%"
|
|
27888
28608
|
},
|
|
28609
|
+
thumbImage: {
|
|
28610
|
+
width: "100%",
|
|
28611
|
+
height: "100%"
|
|
28612
|
+
},
|
|
27889
28613
|
thumbFallback: {
|
|
27890
28614
|
flex: 1,
|
|
27891
28615
|
alignItems: "center",
|
|
@@ -28026,7 +28750,7 @@ var styles9 = StyleSheet9.create({
|
|
|
28026
28750
|
var RightSheet_default = RightSheet;
|
|
28027
28751
|
|
|
28028
28752
|
// components/AnnotationEditor.tsx
|
|
28029
|
-
import { useEffect as useEffect8, useState as
|
|
28753
|
+
import { useEffect as useEffect8, useState as useState9 } from "react";
|
|
28030
28754
|
import {
|
|
28031
28755
|
Modal as Modal3,
|
|
28032
28756
|
View as View10,
|
|
@@ -28049,7 +28773,7 @@ var AnnotationEditor = () => {
|
|
|
28049
28773
|
} = useViewerStore8();
|
|
28050
28774
|
const annotation = annotations.find((ann) => ann.id === selectedAnnotationId);
|
|
28051
28775
|
const isEditable = annotation && (annotation.type === "text" || annotation.type === "comment");
|
|
28052
|
-
const [draft, setDraft] =
|
|
28776
|
+
const [draft, setDraft] = useState9("");
|
|
28053
28777
|
const isDark = uiTheme === "dark";
|
|
28054
28778
|
const t = getStrings(locale);
|
|
28055
28779
|
useEffect8(() => {
|
|
@@ -29032,7 +29756,7 @@ var styles12 = StyleSheet12.create({
|
|
|
29032
29756
|
var SettingsSheet_default = SettingsSheet;
|
|
29033
29757
|
|
|
29034
29758
|
// components/CoverPreview.tsx
|
|
29035
|
-
import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as
|
|
29759
|
+
import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as useState10 } from "react";
|
|
29036
29760
|
import {
|
|
29037
29761
|
ActivityIndicator,
|
|
29038
29762
|
StyleSheet as StyleSheet13,
|
|
@@ -29090,11 +29814,11 @@ var CoverPreview = ({
|
|
|
29090
29814
|
onLoadEnd,
|
|
29091
29815
|
onError
|
|
29092
29816
|
}) => {
|
|
29093
|
-
const [engine, setEngine] =
|
|
29094
|
-
const [layoutReady, setLayoutReady] =
|
|
29095
|
-
const [loaded, setLoaded] =
|
|
29096
|
-
const [loading, setLoading] =
|
|
29097
|
-
const [error, setError] =
|
|
29817
|
+
const [engine, setEngine] = useState10(null);
|
|
29818
|
+
const [layoutReady, setLayoutReady] = useState10(false);
|
|
29819
|
+
const [loaded, setLoaded] = useState10(false);
|
|
29820
|
+
const [loading, setLoading] = useState10(false);
|
|
29821
|
+
const [error, setError] = useState10(null);
|
|
29098
29822
|
const viewRef = useRef7(null);
|
|
29099
29823
|
const resolvedType = useMemo6(() => inferDocumentType(source, type), [source, type]);
|
|
29100
29824
|
const isPdf = resolvedType === "pdf";
|
|
@@ -29214,7 +29938,7 @@ var styles13 = StyleSheet13.create({
|
|
|
29214
29938
|
var CoverPreview_default = CoverPreview;
|
|
29215
29939
|
|
|
29216
29940
|
// components/ReadingShell.tsx
|
|
29217
|
-
import React13, { useState as
|
|
29941
|
+
import React13, { useState as useState12 } from "react";
|
|
29218
29942
|
import { Keyboard as Keyboard2, View as View20, StyleSheet as StyleSheet20 } from "react-native";
|
|
29219
29943
|
import { useViewerStore as useViewerStore17 } from "@papyrus-sdk/core";
|
|
29220
29944
|
|
|
@@ -29839,7 +30563,7 @@ var styles17 = StyleSheet17.create({
|
|
|
29839
30563
|
});
|
|
29840
30564
|
|
|
29841
30565
|
// components/SearchOverlay.tsx
|
|
29842
|
-
import { useEffect as useEffect10, useMemo as useMemo8, useState as
|
|
30566
|
+
import { useEffect as useEffect10, useMemo as useMemo8, useState as useState11 } from "react";
|
|
29843
30567
|
import {
|
|
29844
30568
|
Keyboard,
|
|
29845
30569
|
Pressable as Pressable15,
|
|
@@ -29868,8 +30592,8 @@ function SearchOverlay({
|
|
|
29868
30592
|
uiTheme,
|
|
29869
30593
|
accentColor
|
|
29870
30594
|
} = useViewerStore15();
|
|
29871
|
-
const [draft, setDraft] =
|
|
29872
|
-
const [isSearching, setIsSearching] =
|
|
30595
|
+
const [draft, setDraft] = useState11(searchQuery);
|
|
30596
|
+
const [isSearching, setIsSearching] = useState11(false);
|
|
29873
30597
|
const isDark = uiTheme === "dark";
|
|
29874
30598
|
const t = getStrings(locale);
|
|
29875
30599
|
const searchService = useMemo8(() => new SearchService(engine), [engine]);
|
|
@@ -30360,12 +31084,12 @@ function ReadingShell({
|
|
|
30360
31084
|
uiTheme,
|
|
30361
31085
|
accentColor
|
|
30362
31086
|
} = useViewerStore17();
|
|
30363
|
-
const [overflowOpen, setOverflowOpen] =
|
|
30364
|
-
const [infoOpen, setInfoOpen] =
|
|
30365
|
-
const [actionsOpen, setActionsOpen] =
|
|
30366
|
-
const [settingsOpen, setSettingsOpen] =
|
|
30367
|
-
const [searchResultsOpen, setSearchResultsOpen] =
|
|
30368
|
-
const [pageJumpOpen, setPageJumpOpen] =
|
|
31087
|
+
const [overflowOpen, setOverflowOpen] = useState12(false);
|
|
31088
|
+
const [infoOpen, setInfoOpen] = useState12(false);
|
|
31089
|
+
const [actionsOpen, setActionsOpen] = useState12(false);
|
|
31090
|
+
const [settingsOpen, setSettingsOpen] = useState12(false);
|
|
31091
|
+
const [searchResultsOpen, setSearchResultsOpen] = useState12(false);
|
|
31092
|
+
const [pageJumpOpen, setPageJumpOpen] = useState12(false);
|
|
30369
31093
|
const isDark = uiTheme === "dark";
|
|
30370
31094
|
React13.useEffect(() => {
|
|
30371
31095
|
const showSubscription = Keyboard2.addListener("keyboardDidShow", () => {
|