@papyrus-sdk/ui-react-native 0.2.20 → 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 +968 -263
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1010 -304
- 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.js
CHANGED
|
@@ -20403,9 +20403,121 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20403
20403
|
]).default;
|
|
20404
20404
|
});
|
|
20405
20405
|
</script>
|
|
20406
|
+
<!-- @papyrus-comic-runtime:start -->
|
|
20407
|
+
<script>
|
|
20408
|
+
(function (root, factory) {
|
|
20409
|
+
if (typeof module === "object" && module.exports) {
|
|
20410
|
+
module.exports = factory();
|
|
20411
|
+
} else {
|
|
20412
|
+
root.PapyrusComicRuntime = Object.assign(
|
|
20413
|
+
factory(),
|
|
20414
|
+
root.PapyrusComicRuntime || {}
|
|
20415
|
+
);
|
|
20416
|
+
}
|
|
20417
|
+
})(typeof self === "object" ? self : this, function () {
|
|
20418
|
+
var IMAGE_EXTENSIONS = /\\.(?:gif|jpe?g|png|svg|webp)$/i;
|
|
20419
|
+
|
|
20420
|
+
function isComicImageName(name) {
|
|
20421
|
+
return typeof name === "string" && IMAGE_EXTENSIONS.test(name);
|
|
20422
|
+
}
|
|
20423
|
+
|
|
20424
|
+
function naturalCompare(left, right) {
|
|
20425
|
+
var leftParts = String(left).split(/(\\d+)/);
|
|
20426
|
+
var rightParts = String(right).split(/(\\d+)/);
|
|
20427
|
+
var length = Math.max(leftParts.length, rightParts.length);
|
|
20428
|
+
|
|
20429
|
+
for (var index = 0; index < length; index += 1) {
|
|
20430
|
+
var leftPart = leftParts[index] || "";
|
|
20431
|
+
var rightPart = rightParts[index] || "";
|
|
20432
|
+
if (leftPart === rightPart) continue;
|
|
20433
|
+
|
|
20434
|
+
var leftNumber = /^\\d+$/.test(leftPart) ? Number(leftPart) : null;
|
|
20435
|
+
var rightNumber = /^\\d+$/.test(rightPart) ? Number(rightPart) : null;
|
|
20436
|
+
if (leftNumber !== null && rightNumber !== null) {
|
|
20437
|
+
return leftNumber - rightNumber;
|
|
20438
|
+
}
|
|
20439
|
+
return leftPart.localeCompare(rightPart, undefined, {
|
|
20440
|
+
numeric: true,
|
|
20441
|
+
sensitivity: "base",
|
|
20442
|
+
});
|
|
20443
|
+
}
|
|
20444
|
+
|
|
20445
|
+
return 0;
|
|
20446
|
+
}
|
|
20447
|
+
|
|
20448
|
+
function sortComicPageNames(names) {
|
|
20449
|
+
return names.slice().sort(naturalCompare);
|
|
20450
|
+
}
|
|
20451
|
+
|
|
20452
|
+
function patchCbrWorkerSource(workerSource, wasmUrl) {
|
|
20453
|
+
return workerSource.replace(
|
|
20454
|
+
/new URL\\("libarchive\\.wasm",import\\.meta\\.url\\)\\.href/g,
|
|
20455
|
+
JSON.stringify(wasmUrl)
|
|
20456
|
+
);
|
|
20457
|
+
}
|
|
20458
|
+
|
|
20459
|
+
function getComicPreviewSize(width, height, maxWidth, maxHeight) {
|
|
20460
|
+
var safeWidth = Math.max(1, Number(width) || 1);
|
|
20461
|
+
var safeHeight = Math.max(1, Number(height) || 1);
|
|
20462
|
+
var widthLimit = maxWidth || 240;
|
|
20463
|
+
var heightLimit = maxHeight || 360;
|
|
20464
|
+
var scale = Math.min(1, widthLimit / safeWidth, heightLimit / safeHeight);
|
|
20465
|
+
return {
|
|
20466
|
+
width: Math.max(1, Math.round(safeWidth * scale)),
|
|
20467
|
+
height: Math.max(1, Math.round(safeHeight * scale)),
|
|
20468
|
+
};
|
|
20469
|
+
}
|
|
20470
|
+
|
|
20471
|
+
function getComicPageAspectRatio(width, height) {
|
|
20472
|
+
var safeWidth = Math.max(1, Number(width) || 1);
|
|
20473
|
+
var safeHeight = Math.max(1, Number(height) || 1);
|
|
20474
|
+
return safeWidth + " / " + safeHeight;
|
|
20475
|
+
}
|
|
20476
|
+
|
|
20477
|
+
function getProtectedComicPageIndexes(
|
|
20478
|
+
currentPage,
|
|
20479
|
+
visiblePageIndexes,
|
|
20480
|
+
loadingPageIndexes
|
|
20481
|
+
) {
|
|
20482
|
+
var protectedIndexes = new Set([
|
|
20483
|
+
currentPage - 1,
|
|
20484
|
+
currentPage,
|
|
20485
|
+
currentPage - 2,
|
|
20486
|
+
]);
|
|
20487
|
+
(visiblePageIndexes || []).forEach(function (pageIndex) {
|
|
20488
|
+
protectedIndexes.add(pageIndex);
|
|
20489
|
+
});
|
|
20490
|
+
(loadingPageIndexes || []).forEach(function (pageIndex) {
|
|
20491
|
+
protectedIndexes.add(pageIndex);
|
|
20492
|
+
});
|
|
20493
|
+
return protectedIndexes;
|
|
20494
|
+
}
|
|
20495
|
+
|
|
20496
|
+
function isCurrentComicPageLoad(
|
|
20497
|
+
loadGeneration,
|
|
20498
|
+
currentGeneration,
|
|
20499
|
+
entry,
|
|
20500
|
+
currentEntry
|
|
20501
|
+
) {
|
|
20502
|
+
return loadGeneration === currentGeneration && entry === currentEntry;
|
|
20503
|
+
}
|
|
20504
|
+
|
|
20505
|
+
return {
|
|
20506
|
+
isComicImageName: isComicImageName,
|
|
20507
|
+
sortComicPageNames: sortComicPageNames,
|
|
20508
|
+
patchCbrWorkerSource: patchCbrWorkerSource,
|
|
20509
|
+
getComicPreviewSize: getComicPreviewSize,
|
|
20510
|
+
getComicPageAspectRatio: getComicPageAspectRatio,
|
|
20511
|
+
getProtectedComicPageIndexes: getProtectedComicPageIndexes,
|
|
20512
|
+
isCurrentComicPageLoad: isCurrentComicPageLoad,
|
|
20513
|
+
};
|
|
20514
|
+
});
|
|
20515
|
+
|
|
20516
|
+
</script>
|
|
20517
|
+
<!-- @papyrus-comic-runtime:end -->
|
|
20406
20518
|
<script>
|
|
20407
20519
|
(function () {
|
|
20408
|
-
const viewer = document.getElementById(
|
|
20520
|
+
const viewer = document.getElementById('viewer');
|
|
20409
20521
|
const DEFAULT_FONT_SIZE = 16;
|
|
20410
20522
|
const TEXT_PAGE_CHUNK = 1600;
|
|
20411
20523
|
|
|
@@ -20418,27 +20530,116 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20418
20530
|
let currentPage = 1;
|
|
20419
20531
|
let pageCount = 0;
|
|
20420
20532
|
let zoom = 1.0;
|
|
20533
|
+
let rotation = 0;
|
|
20534
|
+
let comicArchive = null;
|
|
20535
|
+
let comicEntries = [];
|
|
20536
|
+
let comicContainer = null;
|
|
20537
|
+
let comicPages = [];
|
|
20538
|
+
let comicObserver = null;
|
|
20539
|
+
let comicScrollFrame = null;
|
|
20540
|
+
const comicObjectUrls = new Map();
|
|
20541
|
+
const comicLoading = new Map();
|
|
20542
|
+
const comicDimensions = new Map();
|
|
20543
|
+
const comicPreviewCache = new Map();
|
|
20544
|
+
const comicPreviewLoading = new Map();
|
|
20545
|
+
const COMIC_CACHE_LIMIT = 16;
|
|
20546
|
+
const COMIC_PREVIEW_CACHE_LIMIT = 8;
|
|
20547
|
+
const comicHelpers = window.PapyrusComicRuntime || {};
|
|
20548
|
+
const runtimeConfig = window.__PAPYRUS_RUNTIME_CONFIG__ || {};
|
|
20549
|
+
let comicDispose = null;
|
|
20550
|
+
let comicGeneration = 0;
|
|
20421
20551
|
|
|
20422
20552
|
const sendMessage = (payload) => {
|
|
20423
|
-
if (
|
|
20424
|
-
window.ReactNativeWebView &&
|
|
20425
|
-
window.ReactNativeWebView.postMessage
|
|
20426
|
-
) {
|
|
20553
|
+
if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
|
|
20427
20554
|
window.ReactNativeWebView.postMessage(JSON.stringify(payload));
|
|
20428
20555
|
return;
|
|
20429
20556
|
}
|
|
20430
20557
|
if (window.parent && window.parent !== window) {
|
|
20431
|
-
window.parent.postMessage(payload,
|
|
20558
|
+
window.parent.postMessage(payload, '*');
|
|
20432
20559
|
}
|
|
20433
20560
|
};
|
|
20434
20561
|
|
|
20435
20562
|
const sendResponse = (id, ok, data, error) => {
|
|
20436
|
-
sendMessage({ type:
|
|
20563
|
+
sendMessage({ type: 'response', id, ok, data, error });
|
|
20564
|
+
};
|
|
20565
|
+
|
|
20566
|
+
const runtimeAssetRequests = new Map();
|
|
20567
|
+
const fileChunkRequests = new Map();
|
|
20568
|
+
|
|
20569
|
+
const requestRuntimeAsset = (url, encoding) =>
|
|
20570
|
+
new Promise((resolve, reject) => {
|
|
20571
|
+
const id = \`runtime-asset-\${Date.now()}-\${Math.random()}\`;
|
|
20572
|
+
const timeout = setTimeout(() => {
|
|
20573
|
+
runtimeAssetRequests.delete(id);
|
|
20574
|
+
reject(new Error(\`Tempo esgotado ao carregar asset CBR: \${url}\`));
|
|
20575
|
+
}, 15000);
|
|
20576
|
+
runtimeAssetRequests.set(id, {resolve, reject, timeout});
|
|
20577
|
+
sendMessage({type: 'asset-request', id, url, encoding});
|
|
20578
|
+
});
|
|
20579
|
+
|
|
20580
|
+
const encodeArrayBufferAsDataUrl = async (buffer) => {
|
|
20581
|
+
const bytes = new Uint8Array(buffer);
|
|
20582
|
+
let binary = '';
|
|
20583
|
+
const chunkSize = 0x8000;
|
|
20584
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
20585
|
+
binary += String.fromCharCode(
|
|
20586
|
+
...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)),
|
|
20587
|
+
);
|
|
20588
|
+
}
|
|
20589
|
+
return \`data:application/wasm;base64,\${btoa(binary)}\`;
|
|
20590
|
+
};
|
|
20591
|
+
|
|
20592
|
+
const fetchRuntimeAsset = async (url, encoding) => {
|
|
20593
|
+
try {
|
|
20594
|
+
const response = await fetch(url);
|
|
20595
|
+
if (!response.ok) throw new Error(\`status \${response.status}\`);
|
|
20596
|
+
if (encoding === 'text') return await response.text();
|
|
20597
|
+
return await encodeArrayBufferAsDataUrl(await response.arrayBuffer());
|
|
20598
|
+
} catch {
|
|
20599
|
+
return await requestRuntimeAsset(url, encoding);
|
|
20600
|
+
}
|
|
20601
|
+
};
|
|
20602
|
+
|
|
20603
|
+
const requestLocalFileChunk = (uri, offset, length) =>
|
|
20604
|
+
new Promise((resolve, reject) => {
|
|
20605
|
+
const id = \`file-chunk-\${Date.now()}-\${Math.random()}\`;
|
|
20606
|
+
const timeout = setTimeout(() => {
|
|
20607
|
+
fileChunkRequests.delete(id);
|
|
20608
|
+
reject(new Error(\`Tempo esgotado ao ler arquivo local: \${uri}\`));
|
|
20609
|
+
}, 30000);
|
|
20610
|
+
fileChunkRequests.set(id, {resolve, reject, timeout});
|
|
20611
|
+
sendMessage({type: 'file-chunk-request', id, uri, offset, length});
|
|
20612
|
+
});
|
|
20613
|
+
|
|
20614
|
+
const readLocalFile = async (uri) => {
|
|
20615
|
+
const chunkSize = 1024 * 1024;
|
|
20616
|
+
const chunks = [];
|
|
20617
|
+
let offset = 0;
|
|
20618
|
+
let totalLength = 0;
|
|
20619
|
+
while (true) {
|
|
20620
|
+
const result = await requestLocalFileChunk(uri, offset, chunkSize);
|
|
20621
|
+
const bytes = decodeBase64(result.data || '');
|
|
20622
|
+
if (bytes.length === 0 && !result.done) {
|
|
20623
|
+
throw new Error('Leitura nativa retornou um chunk vazio.');
|
|
20624
|
+
}
|
|
20625
|
+
chunks.push(bytes);
|
|
20626
|
+
totalLength += bytes.length;
|
|
20627
|
+
offset += bytes.length;
|
|
20628
|
+
if (result.done) break;
|
|
20629
|
+
}
|
|
20630
|
+
|
|
20631
|
+
const output = new Uint8Array(totalLength);
|
|
20632
|
+
let outputOffset = 0;
|
|
20633
|
+
chunks.forEach((chunk) => {
|
|
20634
|
+
output.set(chunk, outputOffset);
|
|
20635
|
+
outputOffset += chunk.length;
|
|
20636
|
+
});
|
|
20637
|
+
return output.buffer;
|
|
20437
20638
|
};
|
|
20438
20639
|
|
|
20439
20640
|
const sendState = (extra) => {
|
|
20440
20641
|
sendMessage({
|
|
20441
|
-
type:
|
|
20642
|
+
type: 'state',
|
|
20442
20643
|
payload: {
|
|
20443
20644
|
pageCount,
|
|
20444
20645
|
currentPage,
|
|
@@ -20449,70 +20650,66 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20449
20650
|
};
|
|
20450
20651
|
|
|
20451
20652
|
const sendEvent = (name, payload) => {
|
|
20452
|
-
sendMessage({ type:
|
|
20453
|
-
};
|
|
20454
|
-
|
|
20455
|
-
const log = (...args) => {
|
|
20456
|
-
sendEvent("RUNTIME_LOG", {
|
|
20457
|
-
message: args.map((value) => String(value)).join(" "),
|
|
20458
|
-
});
|
|
20653
|
+
sendMessage({ type: 'event', name, payload });
|
|
20459
20654
|
};
|
|
20460
20655
|
|
|
20461
20656
|
const reportError = (error, context) => {
|
|
20462
|
-
const message =
|
|
20463
|
-
error && error.message ? error.message : String(error);
|
|
20657
|
+
const message = error && error.message ? error.message : String(error);
|
|
20464
20658
|
const stack = error && error.stack ? error.stack : null;
|
|
20465
|
-
sendEvent(
|
|
20466
|
-
};
|
|
20467
|
-
|
|
20468
|
-
const EPUB_OPEN_TIMEOUT_MS = 180000;
|
|
20469
|
-
const EPUB_READY_TIMEOUT_MS = 180000;
|
|
20470
|
-
const EPUB_DISPLAY_TIMEOUT_MS = 30000;
|
|
20471
|
-
|
|
20472
|
-
const withTimeout = async (promise, ms, label) => {
|
|
20473
|
-
let timer = null;
|
|
20474
|
-
try {
|
|
20475
|
-
return await Promise.race([
|
|
20476
|
-
promise,
|
|
20477
|
-
new Promise((_, reject) => {
|
|
20478
|
-
timer = setTimeout(
|
|
20479
|
-
() => reject(new Error(\`Timeout: \${label}\`)),
|
|
20480
|
-
ms
|
|
20481
|
-
);
|
|
20482
|
-
}),
|
|
20483
|
-
]);
|
|
20484
|
-
} finally {
|
|
20485
|
-
if (timer) {
|
|
20486
|
-
clearTimeout(timer);
|
|
20487
|
-
}
|
|
20488
|
-
}
|
|
20659
|
+
sendEvent('RUNTIME_ERROR', { message, context, stack });
|
|
20489
20660
|
};
|
|
20490
20661
|
|
|
20491
20662
|
const shouldIgnoreError = (error) => {
|
|
20492
|
-
const message =
|
|
20493
|
-
|
|
20494
|
-
return message.includes("ResizeObserver loop");
|
|
20663
|
+
const message = error && error.message ? error.message : String(error || '');
|
|
20664
|
+
return message.includes('ResizeObserver loop');
|
|
20495
20665
|
};
|
|
20496
20666
|
|
|
20497
|
-
window.addEventListener(
|
|
20667
|
+
window.addEventListener('error', (event) => {
|
|
20498
20668
|
const error = event.error || event.message;
|
|
20499
20669
|
if (shouldIgnoreError(error)) return;
|
|
20500
|
-
reportError(error,
|
|
20670
|
+
reportError(error, 'window.error');
|
|
20501
20671
|
});
|
|
20502
20672
|
|
|
20503
|
-
window.addEventListener(
|
|
20673
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
20504
20674
|
if (shouldIgnoreError(event.reason)) return;
|
|
20505
|
-
reportError(event.reason,
|
|
20675
|
+
reportError(event.reason, 'unhandledrejection');
|
|
20506
20676
|
});
|
|
20507
20677
|
|
|
20678
|
+
const clearComicState = () => {
|
|
20679
|
+
comicGeneration += 1;
|
|
20680
|
+
if (comicObserver) {
|
|
20681
|
+
comicObserver.disconnect();
|
|
20682
|
+
comicObserver = null;
|
|
20683
|
+
}
|
|
20684
|
+
if (comicScrollFrame !== null) {
|
|
20685
|
+
cancelAnimationFrame(comicScrollFrame);
|
|
20686
|
+
comicScrollFrame = null;
|
|
20687
|
+
}
|
|
20688
|
+
comicObjectUrls.forEach((url) => URL.revokeObjectURL(url));
|
|
20689
|
+
comicObjectUrls.clear();
|
|
20690
|
+
comicLoading.clear();
|
|
20691
|
+
comicDimensions.clear();
|
|
20692
|
+
comicPreviewCache.clear();
|
|
20693
|
+
comicPreviewLoading.clear();
|
|
20694
|
+
if (comicDispose) {
|
|
20695
|
+
void comicDispose();
|
|
20696
|
+
comicDispose = null;
|
|
20697
|
+
}
|
|
20698
|
+
comicArchive = null;
|
|
20699
|
+
comicEntries = [];
|
|
20700
|
+
comicContainer = null;
|
|
20701
|
+
comicPages = [];
|
|
20702
|
+
};
|
|
20703
|
+
|
|
20508
20704
|
const clearViewer = () => {
|
|
20705
|
+
clearComicState();
|
|
20509
20706
|
while (viewer.firstChild) {
|
|
20510
20707
|
viewer.removeChild(viewer.firstChild);
|
|
20511
20708
|
}
|
|
20512
20709
|
};
|
|
20513
20710
|
|
|
20514
20711
|
const decodeBase64 = (value) => {
|
|
20515
|
-
const clean = value.replace(/\\s/g,
|
|
20712
|
+
const clean = value.replace(/\\s/g, '');
|
|
20516
20713
|
const binary = atob(clean);
|
|
20517
20714
|
const len = binary.length;
|
|
20518
20715
|
const bytes = new Uint8Array(len);
|
|
@@ -20524,41 +20721,438 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20524
20721
|
|
|
20525
20722
|
const decodeBase64ToText = (value) => {
|
|
20526
20723
|
const bytes = decodeBase64(value);
|
|
20527
|
-
return new TextDecoder(
|
|
20724
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
20725
|
+
};
|
|
20726
|
+
|
|
20727
|
+
const sourceToArrayBuffer = async (source) => {
|
|
20728
|
+
if (source.kind === 'uri') {
|
|
20729
|
+
if (/^(?:file|content):\\/\\//i.test(source.uri)) {
|
|
20730
|
+
return readLocalFile(source.uri);
|
|
20731
|
+
}
|
|
20732
|
+
const response = await fetch(source.uri);
|
|
20733
|
+
if (!response.ok && response.status !== 0) {
|
|
20734
|
+
throw new Error(\`Falha ao carregar quadrinho (\${response.status})\`);
|
|
20735
|
+
}
|
|
20736
|
+
return response.arrayBuffer();
|
|
20737
|
+
}
|
|
20738
|
+
if (source.kind === 'base64') {
|
|
20739
|
+
return decodeBase64(source.data).buffer;
|
|
20740
|
+
}
|
|
20741
|
+
throw new Error('Fonte de quadrinho inv\xE1lida.');
|
|
20742
|
+
};
|
|
20743
|
+
|
|
20744
|
+
const comicMimeType = (name) => {
|
|
20745
|
+
const extension = name.split('.').pop()?.toLowerCase();
|
|
20746
|
+
if (extension === 'png') return 'image/png';
|
|
20747
|
+
if (extension === 'gif') return 'image/gif';
|
|
20748
|
+
if (extension === 'svg') return 'image/svg+xml';
|
|
20749
|
+
if (extension === 'webp') return 'image/webp';
|
|
20750
|
+
return 'image/jpeg';
|
|
20528
20751
|
};
|
|
20529
20752
|
|
|
20530
|
-
const
|
|
20531
|
-
|
|
20532
|
-
const
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20753
|
+
const updateComicCurrentPage = () => {
|
|
20754
|
+
if (!comicContainer || comicPages.length === 0) return;
|
|
20755
|
+
const viewportTop = comicContainer.scrollTop;
|
|
20756
|
+
let closestIndex = currentPage - 1;
|
|
20757
|
+
let closestDistance = Number.POSITIVE_INFINITY;
|
|
20758
|
+
comicPages.forEach((page, index) => {
|
|
20759
|
+
const distance = Math.abs(page.offsetTop - viewportTop);
|
|
20760
|
+
if (distance < closestDistance) {
|
|
20761
|
+
closestDistance = distance;
|
|
20762
|
+
closestIndex = index;
|
|
20763
|
+
}
|
|
20764
|
+
});
|
|
20765
|
+
const nextPage = Math.max(1, Math.min(pageCount, closestIndex + 1));
|
|
20766
|
+
if (nextPage !== currentPage) {
|
|
20767
|
+
currentPage = nextPage;
|
|
20768
|
+
sendState();
|
|
20769
|
+
}
|
|
20770
|
+
};
|
|
20771
|
+
|
|
20772
|
+
const scheduleComicCurrentPage = () => {
|
|
20773
|
+
if (comicScrollFrame !== null) return;
|
|
20774
|
+
comicScrollFrame = requestAnimationFrame(() => {
|
|
20775
|
+
comicScrollFrame = null;
|
|
20776
|
+
updateComicCurrentPage();
|
|
20777
|
+
});
|
|
20778
|
+
};
|
|
20779
|
+
|
|
20780
|
+
const evictComicUrls = () => {
|
|
20781
|
+
if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) return;
|
|
20782
|
+
const protectedIndexes = new Set([
|
|
20783
|
+
currentPage - 1,
|
|
20784
|
+
currentPage,
|
|
20785
|
+
currentPage - 2,
|
|
20786
|
+
]);
|
|
20787
|
+
for (const [index, url] of comicObjectUrls) {
|
|
20788
|
+
if (comicObjectUrls.size <= COMIC_CACHE_LIMIT) break;
|
|
20789
|
+
if (protectedIndexes.has(index)) continue;
|
|
20790
|
+
URL.revokeObjectURL(url);
|
|
20791
|
+
comicObjectUrls.delete(index);
|
|
20792
|
+
const image = comicPages[index]?.querySelector('img');
|
|
20793
|
+
const page = comicPages[index];
|
|
20794
|
+
const dimensions = comicDimensions.get(index);
|
|
20795
|
+
const width = dimensions?.width || image?.naturalWidth || 0;
|
|
20796
|
+
const height = dimensions?.height || image?.naturalHeight || 0;
|
|
20797
|
+
if (page && width > 0 && height > 0) {
|
|
20798
|
+
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
20799
|
+
width,
|
|
20800
|
+
height
|
|
20801
|
+
);
|
|
20802
|
+
}
|
|
20803
|
+
if (image) image.removeAttribute('src');
|
|
20804
|
+
}
|
|
20805
|
+
};
|
|
20806
|
+
|
|
20807
|
+
const ensureComicPage = async (pageIndex) => {
|
|
20808
|
+
if (comicObjectUrls.has(pageIndex)) {
|
|
20809
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
20810
|
+
if (image && !image.src) image.src = comicObjectUrls.get(pageIndex);
|
|
20811
|
+
return comicObjectUrls.get(pageIndex);
|
|
20812
|
+
}
|
|
20813
|
+
if (comicLoading.has(pageIndex)) return comicLoading.get(pageIndex);
|
|
20814
|
+
|
|
20815
|
+
const entry = comicEntries[pageIndex];
|
|
20816
|
+
if (!entry) throw new Error(\`P\xE1gina de quadrinho inv\xE1lida: \${pageIndex}\`);
|
|
20817
|
+
const loadGeneration = comicGeneration;
|
|
20818
|
+
const pending = entry.async('blob').then((blob) => {
|
|
20819
|
+
if (
|
|
20820
|
+
!comicHelpers.isCurrentComicPageLoad(
|
|
20821
|
+
loadGeneration,
|
|
20822
|
+
comicGeneration,
|
|
20823
|
+
entry,
|
|
20824
|
+
comicEntries[pageIndex]
|
|
20825
|
+
)
|
|
20826
|
+
) {
|
|
20827
|
+
return null;
|
|
20828
|
+
}
|
|
20829
|
+
const url = URL.createObjectURL(
|
|
20830
|
+
new Blob([blob], { type: comicMimeType(entry.name) })
|
|
20536
20831
|
);
|
|
20832
|
+
comicObjectUrls.set(pageIndex, url);
|
|
20833
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
20834
|
+
if (image) image.src = url;
|
|
20835
|
+
evictComicUrls();
|
|
20836
|
+
return url;
|
|
20837
|
+
});
|
|
20838
|
+
comicLoading.set(pageIndex, pending);
|
|
20839
|
+
try {
|
|
20840
|
+
return await pending;
|
|
20841
|
+
} finally {
|
|
20842
|
+
if (comicLoading.get(pageIndex) === pending) {
|
|
20843
|
+
comicLoading.delete(pageIndex);
|
|
20844
|
+
}
|
|
20537
20845
|
}
|
|
20538
|
-
|
|
20539
|
-
|
|
20846
|
+
};
|
|
20847
|
+
|
|
20848
|
+
const renderComicPage = (pageIndex) => {
|
|
20849
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
20850
|
+
if (!image) return;
|
|
20851
|
+
image.style.width = \`\${Math.max(40, Math.round(100 * zoom))}%\`;
|
|
20852
|
+
image.style.transform = \`rotate(\${rotation}deg)\`;
|
|
20853
|
+
};
|
|
20854
|
+
|
|
20855
|
+
const applyComicLayout = () => {
|
|
20856
|
+
comicPages.forEach((_, pageIndex) => renderComicPage(pageIndex));
|
|
20857
|
+
};
|
|
20858
|
+
|
|
20859
|
+
const loadComic = async (source, format) => {
|
|
20860
|
+
clearViewer();
|
|
20861
|
+
let entries;
|
|
20862
|
+
if (format === 'cbr') {
|
|
20863
|
+
const clientUrl = runtimeConfig.cbrClientUrl;
|
|
20864
|
+
const workerUrl = runtimeConfig.cbrWorkerUrl;
|
|
20865
|
+
const wasmUrl = runtimeConfig.cbrWasmUrl;
|
|
20866
|
+
if (!clientUrl || !workerUrl || !wasmUrl) {
|
|
20867
|
+
throw new Error(
|
|
20868
|
+
'CBR no mobile exige o pacote opcional de runtime libarchive.'
|
|
20869
|
+
);
|
|
20870
|
+
}
|
|
20871
|
+
|
|
20872
|
+
const [clientSource, workerSource, wasmSource] = await Promise.all([
|
|
20873
|
+
fetchRuntimeAsset(clientUrl, 'text'),
|
|
20874
|
+
fetchRuntimeAsset(workerUrl, 'text'),
|
|
20875
|
+
runtimeConfig.cbrWasmDataUrl ||
|
|
20876
|
+
fetchRuntimeAsset(wasmUrl, 'base64'),
|
|
20877
|
+
]);
|
|
20878
|
+
const patchedWorkerSource = comicHelpers.patchCbrWorkerSource(
|
|
20879
|
+
workerSource,
|
|
20880
|
+
wasmSource
|
|
20881
|
+
);
|
|
20882
|
+
const workerObjectUrl = URL.createObjectURL(
|
|
20883
|
+
new Blob([patchedWorkerSource], {type: 'text/javascript'})
|
|
20884
|
+
);
|
|
20885
|
+
const clientObjectUrl = URL.createObjectURL(
|
|
20886
|
+
new Blob([clientSource], { type: 'text/javascript' })
|
|
20887
|
+
);
|
|
20888
|
+
try {
|
|
20889
|
+
const archiveModule = await import(clientObjectUrl);
|
|
20890
|
+
const createCbrWorker = () => {
|
|
20891
|
+
const worker = new Worker(workerObjectUrl, {type: 'module'});
|
|
20892
|
+
worker.addEventListener('error', (event) => {
|
|
20893
|
+
sendEvent('RUNTIME_ERROR', {
|
|
20894
|
+
message: \`CBR worker: \${event?.message || 'falha ao executar o worker'}\`,
|
|
20895
|
+
context: 'comic.worker',
|
|
20896
|
+
});
|
|
20897
|
+
});
|
|
20898
|
+
worker.addEventListener('messageerror', () => {
|
|
20899
|
+
sendEvent('RUNTIME_ERROR', {
|
|
20900
|
+
message: 'CBR worker: falha ao transferir mensagem',
|
|
20901
|
+
context: 'comic.worker',
|
|
20902
|
+
});
|
|
20903
|
+
});
|
|
20904
|
+
return worker;
|
|
20905
|
+
};
|
|
20906
|
+
archiveModule.Archive.init({getWorker: createCbrWorker});
|
|
20907
|
+
const bytes = await sourceToArrayBuffer(source);
|
|
20908
|
+
const file = new Blob([bytes], {
|
|
20909
|
+
type: 'application/vnd.comicbook-rar',
|
|
20910
|
+
});
|
|
20911
|
+
const archive = await archiveModule.Archive.open(file);
|
|
20912
|
+
const files = await archive.getFilesArray();
|
|
20913
|
+
entries = files
|
|
20914
|
+
.filter(
|
|
20915
|
+
(item) => {
|
|
20916
|
+
const name = \`\${item?.path || ''}\${item?.file?.name || ''}\`;
|
|
20917
|
+
return item?.file && comicHelpers.isComicImageName(name);
|
|
20918
|
+
}
|
|
20919
|
+
)
|
|
20920
|
+
.map((item) => {
|
|
20921
|
+
const name = \`\${item.path || ''}\${item.file.name}\`;
|
|
20922
|
+
return {
|
|
20923
|
+
name,
|
|
20924
|
+
async: async () => item.file.extract(),
|
|
20925
|
+
};
|
|
20926
|
+
});
|
|
20927
|
+
comicDispose = async () => {
|
|
20928
|
+
await archive.close();
|
|
20929
|
+
URL.revokeObjectURL(workerObjectUrl);
|
|
20930
|
+
URL.revokeObjectURL(clientObjectUrl);
|
|
20931
|
+
};
|
|
20932
|
+
} catch (error) {
|
|
20933
|
+
URL.revokeObjectURL(workerObjectUrl);
|
|
20934
|
+
URL.revokeObjectURL(clientObjectUrl);
|
|
20935
|
+
throw error;
|
|
20936
|
+
}
|
|
20937
|
+
} else {
|
|
20938
|
+
if (!window.JSZip) {
|
|
20939
|
+
throw new Error('O runtime ZIP do leitor mobile n\xE3o foi carregado.');
|
|
20940
|
+
}
|
|
20941
|
+
|
|
20942
|
+
const bytes = await sourceToArrayBuffer(source);
|
|
20943
|
+
comicArchive = await window.JSZip.loadAsync(bytes);
|
|
20944
|
+
entries = Object.keys(comicArchive.files)
|
|
20945
|
+
.filter((name) => {
|
|
20946
|
+
const entry = comicArchive.files[name];
|
|
20947
|
+
return !entry.dir && comicHelpers.isComicImageName(name);
|
|
20948
|
+
})
|
|
20949
|
+
.map((name) => comicArchive.files[name]);
|
|
20540
20950
|
}
|
|
20541
|
-
|
|
20542
|
-
|
|
20951
|
+
|
|
20952
|
+
const names = comicHelpers.sortComicPageNames(
|
|
20953
|
+
entries.map((entry) => entry.name)
|
|
20954
|
+
);
|
|
20955
|
+
const entriesByName = new Map(entries.map((entry) => [entry.name, entry]));
|
|
20956
|
+
comicEntries = names.map((name) => entriesByName.get(name));
|
|
20957
|
+
if (comicEntries.length === 0) {
|
|
20958
|
+
if (comicDispose) {
|
|
20959
|
+
await comicDispose();
|
|
20960
|
+
comicDispose = null;
|
|
20961
|
+
}
|
|
20962
|
+
throw new Error('O arquivo n\xE3o cont\xE9m p\xE1ginas de imagem.');
|
|
20963
|
+
}
|
|
20964
|
+
|
|
20965
|
+
comicContainer = document.createElement('div');
|
|
20966
|
+
comicContainer.style.width = '100%';
|
|
20967
|
+
comicContainer.style.height = '100%';
|
|
20968
|
+
comicContainer.style.overflow = 'auto';
|
|
20969
|
+
comicContainer.style.padding = '16px 0 120px';
|
|
20970
|
+
comicContainer.style.boxSizing = 'border-box';
|
|
20971
|
+
comicContainer.style.background = '#ffffff';
|
|
20972
|
+
comicContainer.addEventListener('scroll', scheduleComicCurrentPage, {
|
|
20973
|
+
passive: true,
|
|
20974
|
+
});
|
|
20975
|
+
viewer.style.overflow = 'hidden';
|
|
20976
|
+
viewer.appendChild(comicContainer);
|
|
20977
|
+
|
|
20978
|
+
comicPages = comicEntries.map((entry, pageIndex) => {
|
|
20979
|
+
const page = document.createElement('div');
|
|
20980
|
+
page.dataset.pageIndex = String(pageIndex);
|
|
20981
|
+
page.style.width = '100%';
|
|
20982
|
+
page.style.minHeight = '80px';
|
|
20983
|
+
page.style.display = 'flex';
|
|
20984
|
+
page.style.justifyContent = 'center';
|
|
20985
|
+
page.style.alignItems = 'flex-start';
|
|
20986
|
+
page.style.padding = '0 12px 20px';
|
|
20987
|
+
page.style.boxSizing = 'border-box';
|
|
20988
|
+
|
|
20989
|
+
const image = document.createElement('img');
|
|
20990
|
+
image.alt = \`P\xE1gina \${pageIndex + 1}\`;
|
|
20991
|
+
image.draggable = false;
|
|
20992
|
+
image.decoding = 'async';
|
|
20993
|
+
image.loading = 'lazy';
|
|
20994
|
+
image.style.display = 'block';
|
|
20995
|
+
image.style.maxWidth = 'none';
|
|
20996
|
+
image.style.height = 'auto';
|
|
20997
|
+
image.style.userSelect = 'none';
|
|
20998
|
+
image.addEventListener('load', () => {
|
|
20999
|
+
if (image.naturalWidth > 0 && image.naturalHeight > 0) {
|
|
21000
|
+
comicDimensions.set(pageIndex, {
|
|
21001
|
+
width: image.naturalWidth,
|
|
21002
|
+
height: image.naturalHeight,
|
|
21003
|
+
});
|
|
21004
|
+
page.style.aspectRatio = comicHelpers.getComicPageAspectRatio(
|
|
21005
|
+
image.naturalWidth,
|
|
21006
|
+
image.naturalHeight
|
|
21007
|
+
);
|
|
21008
|
+
}
|
|
21009
|
+
});
|
|
21010
|
+
page.appendChild(image);
|
|
21011
|
+
comicContainer.appendChild(page);
|
|
21012
|
+
return page;
|
|
21013
|
+
});
|
|
21014
|
+
|
|
21015
|
+
if (typeof IntersectionObserver === 'function') {
|
|
21016
|
+
comicObserver = new IntersectionObserver(
|
|
21017
|
+
(observations) => {
|
|
21018
|
+
observations.forEach((observation) => {
|
|
21019
|
+
if (!observation.isIntersecting) return;
|
|
21020
|
+
const pageIndex = Number(observation.target.dataset.pageIndex);
|
|
21021
|
+
void ensureComicPage(pageIndex).catch((error) =>
|
|
21022
|
+
sendEvent('RUNTIME_ERROR', {
|
|
21023
|
+
message: error?.message || String(error),
|
|
21024
|
+
context: 'comic.page',
|
|
21025
|
+
})
|
|
21026
|
+
);
|
|
21027
|
+
});
|
|
21028
|
+
scheduleComicCurrentPage();
|
|
21029
|
+
},
|
|
21030
|
+
{ root: comicContainer, rootMargin: '600px 0px' }
|
|
21031
|
+
);
|
|
21032
|
+
comicPages.forEach((page) => comicObserver.observe(page));
|
|
21033
|
+
} else {
|
|
21034
|
+
for (let pageIndex = 0; pageIndex < Math.min(3, comicPages.length); pageIndex += 1) {
|
|
21035
|
+
void ensureComicPage(pageIndex);
|
|
21036
|
+
}
|
|
21037
|
+
}
|
|
21038
|
+
pageCount = comicPages.length;
|
|
21039
|
+
currentPage = 1;
|
|
21040
|
+
await ensureComicPage(0);
|
|
21041
|
+
applyComicLayout();
|
|
21042
|
+
return { pageCount };
|
|
21043
|
+
};
|
|
21044
|
+
|
|
21045
|
+
const readComicBlobAsDataUrl = (blob, mimeType) =>
|
|
21046
|
+
new Promise((resolve, reject) => {
|
|
20543
21047
|
const reader = new FileReader();
|
|
20544
|
-
reader.onerror = () =>
|
|
20545
|
-
reject(new Error("Failed to read epub blob"));
|
|
21048
|
+
reader.onerror = () => reject(new Error('Falha ao gerar preview.'));
|
|
20546
21049
|
reader.onload = () => resolve(reader.result);
|
|
20547
|
-
reader.
|
|
21050
|
+
reader.readAsDataURL(new Blob([blob], { type: mimeType }));
|
|
20548
21051
|
});
|
|
21052
|
+
|
|
21053
|
+
const createComicPagePreview = async (pageIndex) => {
|
|
21054
|
+
const entry = comicEntries[pageIndex];
|
|
21055
|
+
if (!entry) return null;
|
|
21056
|
+
const mimeType = comicMimeType(entry.name);
|
|
21057
|
+
const sourceUrl = await ensureComicPage(pageIndex);
|
|
21058
|
+
if (!sourceUrl) return null;
|
|
21059
|
+
try {
|
|
21060
|
+
const image = new Image();
|
|
21061
|
+
image.decoding = 'async';
|
|
21062
|
+
await new Promise((resolve, reject) => {
|
|
21063
|
+
image.onload = resolve;
|
|
21064
|
+
image.onerror = () => reject(new Error('Falha ao decodificar preview.'));
|
|
21065
|
+
image.src = sourceUrl;
|
|
21066
|
+
});
|
|
21067
|
+
const size = comicHelpers.getComicPreviewSize(
|
|
21068
|
+
image.naturalWidth || image.width,
|
|
21069
|
+
image.naturalHeight || image.height
|
|
21070
|
+
);
|
|
21071
|
+
const canvas = document.createElement('canvas');
|
|
21072
|
+
canvas.width = size.width;
|
|
21073
|
+
canvas.height = size.height;
|
|
21074
|
+
const context = canvas.getContext('2d');
|
|
21075
|
+
if (!context) throw new Error('Canvas indispon\xEDvel para preview.');
|
|
21076
|
+
context.fillStyle = '#ffffff';
|
|
21077
|
+
context.fillRect(0, 0, size.width, size.height);
|
|
21078
|
+
context.drawImage(image, 0, 0, size.width, size.height);
|
|
21079
|
+
return canvas.toDataURL('image/jpeg', 0.78);
|
|
21080
|
+
} catch {
|
|
21081
|
+
try {
|
|
21082
|
+
const response = await fetch(sourceUrl);
|
|
21083
|
+
const blob = await response.blob();
|
|
21084
|
+
return await readComicBlobAsDataUrl(blob, mimeType);
|
|
21085
|
+
} catch {
|
|
21086
|
+
return null;
|
|
21087
|
+
}
|
|
21088
|
+
}
|
|
21089
|
+
};
|
|
21090
|
+
|
|
21091
|
+
const getComicPagePreview = async (pageIndex) => {
|
|
21092
|
+
const entry = comicEntries[pageIndex];
|
|
21093
|
+
if (!entry) return null;
|
|
21094
|
+
if (comicPreviewCache.has(pageIndex)) {
|
|
21095
|
+
return comicPreviewCache.get(pageIndex);
|
|
21096
|
+
}
|
|
21097
|
+
if (comicPreviewLoading.has(pageIndex)) {
|
|
21098
|
+
return comicPreviewLoading.get(pageIndex);
|
|
21099
|
+
}
|
|
21100
|
+
const loadGeneration = comicGeneration;
|
|
21101
|
+
const pending = createComicPagePreview(pageIndex).then((preview) => {
|
|
21102
|
+
if (
|
|
21103
|
+
!comicHelpers.isCurrentComicPageLoad(
|
|
21104
|
+
loadGeneration,
|
|
21105
|
+
comicGeneration,
|
|
21106
|
+
entry,
|
|
21107
|
+
comicEntries[pageIndex]
|
|
21108
|
+
)
|
|
21109
|
+
) {
|
|
21110
|
+
return null;
|
|
21111
|
+
}
|
|
21112
|
+
comicPreviewCache.set(pageIndex, preview);
|
|
21113
|
+
while (comicPreviewCache.size > COMIC_PREVIEW_CACHE_LIMIT) {
|
|
21114
|
+
comicPreviewCache.delete(comicPreviewCache.keys().next().value);
|
|
21115
|
+
}
|
|
21116
|
+
return preview;
|
|
21117
|
+
});
|
|
21118
|
+
comicPreviewLoading.set(pageIndex, pending);
|
|
21119
|
+
try {
|
|
21120
|
+
return await pending;
|
|
21121
|
+
} finally {
|
|
21122
|
+
if (comicPreviewLoading.get(pageIndex) === pending) {
|
|
21123
|
+
comicPreviewLoading.delete(pageIndex);
|
|
21124
|
+
}
|
|
21125
|
+
}
|
|
21126
|
+
};
|
|
21127
|
+
|
|
21128
|
+
const getComicPageDimensions = async (pageIndex) => {
|
|
21129
|
+
const known = comicDimensions.get(pageIndex);
|
|
21130
|
+
if (known) return known;
|
|
21131
|
+
await ensureComicPage(pageIndex);
|
|
21132
|
+
const image = comicPages[pageIndex]?.querySelector('img');
|
|
21133
|
+
if (image && !image.complete) {
|
|
21134
|
+
await new Promise((resolve) => {
|
|
21135
|
+
image.addEventListener('load', resolve, { once: true });
|
|
21136
|
+
image.addEventListener('error', resolve, { once: true });
|
|
21137
|
+
});
|
|
21138
|
+
}
|
|
21139
|
+
return (
|
|
21140
|
+
comicDimensions.get(pageIndex) || {
|
|
21141
|
+
width: image?.naturalWidth || 0,
|
|
21142
|
+
height: image?.naturalHeight || 0,
|
|
21143
|
+
}
|
|
21144
|
+
);
|
|
20549
21145
|
};
|
|
20550
21146
|
|
|
20551
21147
|
const normalizeHref = (href) => {
|
|
20552
|
-
if (!href) return
|
|
20553
|
-
return href.split(
|
|
21148
|
+
if (!href) return '';
|
|
21149
|
+
return href.split('#')[0];
|
|
20554
21150
|
};
|
|
20555
21151
|
|
|
20556
21152
|
const getSpineIndexByHref = (href) => {
|
|
20557
21153
|
const normalized = normalizeHref(href);
|
|
20558
21154
|
if (!normalized) return -1;
|
|
20559
|
-
return spineItems.findIndex(
|
|
20560
|
-
(item) => normalizeHref(item.href) === normalized
|
|
20561
|
-
);
|
|
21155
|
+
return spineItems.findIndex((item) => normalizeHref(item.href) === normalized);
|
|
20562
21156
|
};
|
|
20563
21157
|
|
|
20564
21158
|
const buildOutline = async () => {
|
|
@@ -20567,11 +21161,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20567
21161
|
const toc = nav && nav.toc ? nav.toc : [];
|
|
20568
21162
|
|
|
20569
21163
|
const mapItem = (item) => {
|
|
20570
|
-
const title = item.label || item.title ||
|
|
20571
|
-
const pageIndex = getSpineIndexByHref(item.href ||
|
|
20572
|
-
const children = Array.isArray(item.subitems)
|
|
20573
|
-
? item.subitems.map(mapItem)
|
|
20574
|
-
: [];
|
|
21164
|
+
const title = item.label || item.title || '';
|
|
21165
|
+
const pageIndex = getSpineIndexByHref(item.href || '');
|
|
21166
|
+
const children = Array.isArray(item.subitems) ? item.subitems.map(mapItem) : [];
|
|
20575
21167
|
const outlineItem = { title, pageIndex };
|
|
20576
21168
|
if (children.length > 0) outlineItem.children = children;
|
|
20577
21169
|
return outlineItem;
|
|
@@ -20583,10 +21175,10 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20583
21175
|
const applyEpubZoom = () => {
|
|
20584
21176
|
if (!rendition || !rendition.themes) return;
|
|
20585
21177
|
const fontSize = \`\${Math.round(zoom * 100)}%\`;
|
|
20586
|
-
if (typeof rendition.themes.fontSize ===
|
|
21178
|
+
if (typeof rendition.themes.fontSize === 'function') {
|
|
20587
21179
|
rendition.themes.fontSize(fontSize);
|
|
20588
|
-
} else if (typeof rendition.themes.override ===
|
|
20589
|
-
rendition.themes.override(
|
|
21180
|
+
} else if (typeof rendition.themes.override === 'function') {
|
|
21181
|
+
rendition.themes.override('font-size', fontSize);
|
|
20590
21182
|
}
|
|
20591
21183
|
};
|
|
20592
21184
|
|
|
@@ -20597,14 +21189,13 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20597
21189
|
};
|
|
20598
21190
|
|
|
20599
21191
|
const renderTextPage = (pageIndex) => {
|
|
20600
|
-
const text = textPages[pageIndex] ||
|
|
21192
|
+
const text = textPages[pageIndex] || '';
|
|
20601
21193
|
if (!textContainer) {
|
|
20602
|
-
textContainer = document.createElement(
|
|
20603
|
-
textContainer.style.padding =
|
|
20604
|
-
textContainer.style.lineHeight =
|
|
20605
|
-
textContainer.style.whiteSpace =
|
|
20606
|
-
textContainer.style.fontFamily =
|
|
20607
|
-
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
21194
|
+
textContainer = document.createElement('div');
|
|
21195
|
+
textContainer.style.padding = '24px';
|
|
21196
|
+
textContainer.style.lineHeight = '1.6';
|
|
21197
|
+
textContainer.style.whiteSpace = 'pre-wrap';
|
|
21198
|
+
textContainer.style.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
20608
21199
|
viewer.appendChild(textContainer);
|
|
20609
21200
|
}
|
|
20610
21201
|
textContainer.textContent = text;
|
|
@@ -20616,18 +21207,18 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20616
21207
|
for (let i = 0; i < text.length; i += TEXT_PAGE_CHUNK) {
|
|
20617
21208
|
pages.push(text.slice(i, i + TEXT_PAGE_CHUNK));
|
|
20618
21209
|
}
|
|
20619
|
-
return pages.length > 0 ? pages : [
|
|
21210
|
+
return pages.length > 0 ? pages : [''];
|
|
20620
21211
|
};
|
|
20621
21212
|
|
|
20622
21213
|
const loadText = async (source) => {
|
|
20623
|
-
let text =
|
|
20624
|
-
if (source.kind ===
|
|
21214
|
+
let text = '';
|
|
21215
|
+
if (source.kind === 'uri') {
|
|
20625
21216
|
const res = await fetch(source.uri);
|
|
20626
21217
|
text = await res.text();
|
|
20627
|
-
} else if (source.kind ===
|
|
21218
|
+
} else if (source.kind === 'base64') {
|
|
20628
21219
|
text = decodeBase64ToText(source.data);
|
|
20629
|
-
} else if (source.kind ===
|
|
20630
|
-
text = source.text ||
|
|
21220
|
+
} else if (source.kind === 'text') {
|
|
21221
|
+
text = source.text || '';
|
|
20631
21222
|
}
|
|
20632
21223
|
|
|
20633
21224
|
clearViewer();
|
|
@@ -20640,44 +21231,25 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20640
21231
|
};
|
|
20641
21232
|
|
|
20642
21233
|
const loadEpub = async (source) => {
|
|
20643
|
-
|
|
20644
|
-
if (rendition && typeof rendition.destroy === "function") {
|
|
21234
|
+
if (rendition && typeof rendition.destroy === 'function') {
|
|
20645
21235
|
rendition.destroy();
|
|
20646
21236
|
}
|
|
20647
|
-
if (book && typeof book.destroy ===
|
|
21237
|
+
if (book && typeof book.destroy === 'function') {
|
|
20648
21238
|
book.destroy();
|
|
20649
21239
|
}
|
|
20650
21240
|
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
|
|
20655
|
-
|
|
20656
|
-
|
|
20657
|
-
);
|
|
20658
|
-
|
|
20659
|
-
log("epub.load:fetch-uri");
|
|
20660
|
-
const data = await withTimeout(
|
|
20661
|
-
fetchUriAsArrayBuffer(source.uri),
|
|
20662
|
-
EPUB_OPEN_TIMEOUT_MS,
|
|
20663
|
-
"epub.fetch"
|
|
20664
|
-
);
|
|
20665
|
-
const input = data instanceof Uint8Array ? data.buffer : data;
|
|
20666
|
-
book = ePub(input, { openAs: "epub" });
|
|
20667
|
-
} else {
|
|
20668
|
-
let data = null;
|
|
20669
|
-
if (source.kind === "text") {
|
|
20670
|
-
const encoder = new TextEncoder();
|
|
20671
|
-
data = encoder.encode(source.text || "").buffer;
|
|
20672
|
-
}
|
|
20673
|
-
const input = data instanceof Uint8Array ? data.buffer : data;
|
|
20674
|
-
book = ePub(input, { openAs: "epub" });
|
|
20675
|
-
}
|
|
20676
|
-
if (book && book.opened) {
|
|
20677
|
-
await withTimeout(book.opened, EPUB_OPEN_TIMEOUT_MS, "epub.opened");
|
|
21241
|
+
let data = null;
|
|
21242
|
+
if (source.kind === 'uri') {
|
|
21243
|
+
data = source.uri;
|
|
21244
|
+
} else if (source.kind === 'base64') {
|
|
21245
|
+
data = decodeBase64(source.data);
|
|
21246
|
+
} else if (source.kind === 'text') {
|
|
21247
|
+
const encoder = new TextEncoder();
|
|
21248
|
+
data = encoder.encode(source.text || '').buffer;
|
|
20678
21249
|
}
|
|
20679
|
-
|
|
20680
|
-
|
|
21250
|
+
|
|
21251
|
+
book = ePub(data);
|
|
21252
|
+
await book.ready;
|
|
20681
21253
|
|
|
20682
21254
|
spineItems = book.spine && book.spine.items ? book.spine.items : [];
|
|
20683
21255
|
pageCount = spineItems.length;
|
|
@@ -20685,64 +21257,38 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20685
21257
|
|
|
20686
21258
|
clearViewer();
|
|
20687
21259
|
rendition = book.renderTo(viewer, {
|
|
20688
|
-
width:
|
|
20689
|
-
height:
|
|
20690
|
-
flow:
|
|
20691
|
-
spread:
|
|
21260
|
+
width: '100%',
|
|
21261
|
+
height: '100%',
|
|
21262
|
+
flow: 'paginated',
|
|
21263
|
+
spread: 'none',
|
|
20692
21264
|
});
|
|
20693
21265
|
|
|
20694
21266
|
if (rendition && rendition.hooks && rendition.hooks.content) {
|
|
20695
21267
|
rendition.hooks.content.register((contents) => {
|
|
20696
|
-
const frame =
|
|
20697
|
-
contents && contents.document
|
|
20698
|
-
? contents.document.defaultView.frameElement
|
|
20699
|
-
: null;
|
|
21268
|
+
const frame = contents && contents.document ? contents.document.defaultView.frameElement : null;
|
|
20700
21269
|
if (frame) {
|
|
20701
|
-
frame.setAttribute(
|
|
20702
|
-
"sandbox",
|
|
20703
|
-
"allow-scripts allow-same-origin"
|
|
20704
|
-
);
|
|
21270
|
+
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
|
|
20705
21271
|
}
|
|
20706
21272
|
});
|
|
20707
21273
|
}
|
|
20708
21274
|
|
|
20709
|
-
if (rendition && typeof rendition.on ===
|
|
20710
|
-
rendition.on(
|
|
20711
|
-
const selection =
|
|
20712
|
-
|
|
20713
|
-
? contents.window.getSelection()
|
|
20714
|
-
: null;
|
|
20715
|
-
const text = selection ? selection.toString().trim() : "";
|
|
21275
|
+
if (rendition && typeof rendition.on === 'function') {
|
|
21276
|
+
rendition.on('selected', (cfiRange, contents) => {
|
|
21277
|
+
const selection = contents && contents.window ? contents.window.getSelection() : null;
|
|
21278
|
+
const text = selection ? selection.toString().trim() : '';
|
|
20716
21279
|
if (text) {
|
|
20717
|
-
sendEvent(
|
|
20718
|
-
text,
|
|
20719
|
-
pageIndex: Math.max(0, currentPage - 1),
|
|
20720
|
-
});
|
|
21280
|
+
sendEvent('TEXT_SELECTED', { text, pageIndex: Math.max(0, currentPage - 1) });
|
|
20721
21281
|
}
|
|
20722
|
-
if (
|
|
20723
|
-
rendition
|
|
20724
|
-
rendition.annotations &&
|
|
20725
|
-
typeof rendition.annotations.remove === "function"
|
|
20726
|
-
) {
|
|
20727
|
-
rendition.annotations.remove(cfiRange, "highlight");
|
|
21282
|
+
if (rendition && rendition.annotations && typeof rendition.annotations.remove === 'function') {
|
|
21283
|
+
rendition.annotations.remove(cfiRange, 'highlight');
|
|
20728
21284
|
}
|
|
20729
21285
|
});
|
|
20730
21286
|
}
|
|
20731
21287
|
|
|
20732
|
-
|
|
20733
|
-
await withTimeout(
|
|
20734
|
-
displayEpubPage(0),
|
|
20735
|
-
EPUB_DISPLAY_TIMEOUT_MS,
|
|
20736
|
-
"epub.display"
|
|
20737
|
-
);
|
|
20738
|
-
log("epub.load:displayed");
|
|
20739
|
-
} catch (err) {
|
|
20740
|
-
reportError(err, "epub.display");
|
|
20741
|
-
}
|
|
21288
|
+
await displayEpubPage(0);
|
|
20742
21289
|
applyEpubZoom();
|
|
20743
21290
|
|
|
20744
21291
|
const outline = await buildOutline();
|
|
20745
|
-
log("epub.load:outline", outline.length);
|
|
20746
21292
|
return { pageCount, outline };
|
|
20747
21293
|
};
|
|
20748
21294
|
|
|
@@ -20757,41 +21303,34 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20757
21303
|
};
|
|
20758
21304
|
|
|
20759
21305
|
const getTextContent = async (pageIndex) => {
|
|
20760
|
-
if (currentType ===
|
|
20761
|
-
const text = textPages[pageIndex] ||
|
|
20762
|
-
return [
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
|
|
20770
|
-
},
|
|
20771
|
-
];
|
|
21306
|
+
if (currentType === 'text') {
|
|
21307
|
+
const text = textPages[pageIndex] || '';
|
|
21308
|
+
return [{
|
|
21309
|
+
str: text,
|
|
21310
|
+
dir: 'ltr',
|
|
21311
|
+
width: 0,
|
|
21312
|
+
height: 0,
|
|
21313
|
+
transform: [1, 0, 0, 1, 0, 0],
|
|
21314
|
+
fontName: 'default',
|
|
21315
|
+
}];
|
|
20772
21316
|
}
|
|
20773
21317
|
|
|
20774
|
-
if (currentType ===
|
|
21318
|
+
if (currentType === 'epub') {
|
|
20775
21319
|
if (!book) return [];
|
|
20776
21320
|
const item = spineItems[pageIndex];
|
|
20777
21321
|
if (!item) return [];
|
|
20778
21322
|
try {
|
|
20779
21323
|
const section = book.spine.get(item.idref || item.href);
|
|
20780
|
-
const text =
|
|
20781
|
-
section && typeof section.text === "function"
|
|
20782
|
-
? await section.text()
|
|
20783
|
-
: "";
|
|
21324
|
+
const text = section && typeof section.text === 'function' ? await section.text() : '';
|
|
20784
21325
|
if (!text) return [];
|
|
20785
|
-
return [
|
|
20786
|
-
|
|
20787
|
-
|
|
20788
|
-
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
},
|
|
20794
|
-
];
|
|
21326
|
+
return [{
|
|
21327
|
+
str: text,
|
|
21328
|
+
dir: 'ltr',
|
|
21329
|
+
width: 0,
|
|
21330
|
+
height: 0,
|
|
21331
|
+
transform: [1, 0, 0, 1, 0, 0],
|
|
21332
|
+
fontName: 'default',
|
|
21333
|
+
}];
|
|
20795
21334
|
} catch (err) {
|
|
20796
21335
|
return [];
|
|
20797
21336
|
}
|
|
@@ -20802,7 +21341,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20802
21341
|
|
|
20803
21342
|
const getPageText = async (pageIndex) => {
|
|
20804
21343
|
const items = await getTextContent(pageIndex);
|
|
20805
|
-
return items.map((item) => item.str).join(
|
|
21344
|
+
return items.map((item) => item.str).join(' ');
|
|
20806
21345
|
};
|
|
20807
21346
|
|
|
20808
21347
|
const searchText = async (query) => {
|
|
@@ -20818,11 +21357,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20818
21357
|
while (pos !== -1) {
|
|
20819
21358
|
const start = Math.max(0, pos - 20);
|
|
20820
21359
|
const end = Math.min(text.length, pos + query.length + 20);
|
|
20821
|
-
results.push({
|
|
20822
|
-
pageIndex: i,
|
|
20823
|
-
text: text.substring(start, end),
|
|
20824
|
-
matchIndex,
|
|
20825
|
-
});
|
|
21360
|
+
results.push({ pageIndex: i, text: text.substring(start, end), matchIndex });
|
|
20826
21361
|
matchIndex += 1;
|
|
20827
21362
|
pos = lower.indexOf(normalized, pos + 1);
|
|
20828
21363
|
}
|
|
@@ -20838,11 +21373,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20838
21373
|
|
|
20839
21374
|
const getPageIndex = (dest) => {
|
|
20840
21375
|
if (!dest) return null;
|
|
20841
|
-
if (typeof dest ===
|
|
20842
|
-
if (typeof dest !==
|
|
20843
|
-
if (dest.kind ===
|
|
20844
|
-
if (dest.kind ===
|
|
20845
|
-
if (dest.kind ===
|
|
21376
|
+
if (typeof dest === 'string') return getSpineIndexByHref(dest);
|
|
21377
|
+
if (typeof dest !== 'object') return null;
|
|
21378
|
+
if (dest.kind === 'href') return getSpineIndexByHref(dest.value);
|
|
21379
|
+
if (dest.kind === 'pageIndex') return dest.value;
|
|
21380
|
+
if (dest.kind === 'pageNumber') return Math.max(0, dest.value - 1);
|
|
20846
21381
|
return null;
|
|
20847
21382
|
};
|
|
20848
21383
|
|
|
@@ -20856,53 +21391,70 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20856
21391
|
};
|
|
20857
21392
|
|
|
20858
21393
|
const applyZoom = () => {
|
|
20859
|
-
if (currentType ===
|
|
21394
|
+
if (currentType === 'text') {
|
|
20860
21395
|
applyTextZoom();
|
|
20861
|
-
} else if (currentType ===
|
|
21396
|
+
} else if (currentType === 'epub') {
|
|
20862
21397
|
applyEpubZoom();
|
|
21398
|
+
} else if (currentType === 'comic') {
|
|
21399
|
+
applyComicLayout();
|
|
20863
21400
|
}
|
|
20864
21401
|
};
|
|
20865
21402
|
|
|
20866
21403
|
const handleCommand = async (message) => {
|
|
20867
21404
|
const { id, kind, payload } = message;
|
|
20868
|
-
log("command", kind);
|
|
20869
21405
|
|
|
20870
21406
|
try {
|
|
20871
|
-
if (kind ===
|
|
21407
|
+
if (kind === 'load') {
|
|
20872
21408
|
currentType = payload.type;
|
|
20873
21409
|
zoom = 1.0;
|
|
20874
21410
|
|
|
20875
|
-
if (currentType ===
|
|
21411
|
+
if (currentType === 'text') {
|
|
20876
21412
|
const result = await loadText(payload.source);
|
|
20877
21413
|
sendState();
|
|
20878
21414
|
sendResponse(id, true, result);
|
|
20879
21415
|
return;
|
|
20880
21416
|
}
|
|
20881
21417
|
|
|
20882
|
-
if (currentType ===
|
|
21418
|
+
if (currentType === 'epub') {
|
|
20883
21419
|
const result = await loadEpub(payload.source);
|
|
20884
21420
|
sendState({ outline: result.outline || [] });
|
|
20885
21421
|
sendResponse(id, true, result);
|
|
20886
21422
|
return;
|
|
20887
21423
|
}
|
|
20888
21424
|
|
|
20889
|
-
|
|
21425
|
+
if (currentType === 'comic') {
|
|
21426
|
+
const result = await loadComic(payload.source, payload.format || 'cbz');
|
|
21427
|
+
sendState();
|
|
21428
|
+
sendResponse(id, true, result);
|
|
21429
|
+
return;
|
|
21430
|
+
}
|
|
21431
|
+
|
|
21432
|
+
throw new Error('Unsupported document type');
|
|
20890
21433
|
}
|
|
20891
21434
|
|
|
20892
|
-
if (kind ===
|
|
21435
|
+
if (kind === 'go-to-page') {
|
|
20893
21436
|
const page = Math.max(1, payload.page || 1);
|
|
20894
|
-
if (currentType ===
|
|
21437
|
+
if (currentType === 'text') {
|
|
20895
21438
|
currentPage = page;
|
|
20896
21439
|
renderTextPage(page - 1);
|
|
20897
21440
|
sendState();
|
|
20898
|
-
} else if (currentType ===
|
|
21441
|
+
} else if (currentType === 'epub') {
|
|
20899
21442
|
await displayEpubPage(page - 1);
|
|
21443
|
+
} else if (currentType === 'comic') {
|
|
21444
|
+
currentPage = page;
|
|
21445
|
+
const target = comicPages[page - 1];
|
|
21446
|
+
if (target) {
|
|
21447
|
+
await ensureComicPage(page - 1);
|
|
21448
|
+
target.scrollIntoView({ block: 'start' });
|
|
21449
|
+
renderComicPage(page - 1);
|
|
21450
|
+
sendState();
|
|
21451
|
+
}
|
|
20900
21452
|
}
|
|
20901
21453
|
sendResponse(id, true, { currentPage });
|
|
20902
21454
|
return;
|
|
20903
21455
|
}
|
|
20904
21456
|
|
|
20905
|
-
if (kind ===
|
|
21457
|
+
if (kind === 'set-zoom') {
|
|
20906
21458
|
zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
|
|
20907
21459
|
applyZoom();
|
|
20908
21460
|
sendState();
|
|
@@ -20910,103 +21462,132 @@ https://github.com/nodeca/pako/blob/main/LICENSE
|
|
|
20910
21462
|
return;
|
|
20911
21463
|
}
|
|
20912
21464
|
|
|
20913
|
-
if (kind ===
|
|
21465
|
+
if (kind === 'set-rotation') {
|
|
21466
|
+
rotation = ((payload.rotation || 0) % 360 + 360) % 360;
|
|
21467
|
+
if (currentType === 'comic') applyComicLayout();
|
|
20914
21468
|
sendResponse(id, true, {});
|
|
20915
21469
|
return;
|
|
20916
21470
|
}
|
|
20917
21471
|
|
|
20918
|
-
if (kind ===
|
|
21472
|
+
if (kind === 'get-text-content') {
|
|
20919
21473
|
const items = await getTextContent(payload.pageIndex || 0);
|
|
20920
21474
|
sendResponse(id, true, items);
|
|
20921
21475
|
return;
|
|
20922
21476
|
}
|
|
20923
21477
|
|
|
20924
|
-
if (kind ===
|
|
20925
|
-
|
|
21478
|
+
if (kind === 'get-page-dimensions') {
|
|
21479
|
+
if (currentType === 'comic') {
|
|
21480
|
+
sendResponse(
|
|
21481
|
+
id,
|
|
21482
|
+
true,
|
|
21483
|
+
await getComicPageDimensions(payload.pageIndex || 0)
|
|
21484
|
+
);
|
|
21485
|
+
} else {
|
|
21486
|
+
sendResponse(id, true, getPageDimensions());
|
|
21487
|
+
}
|
|
20926
21488
|
return;
|
|
20927
21489
|
}
|
|
20928
21490
|
|
|
20929
|
-
if (kind ===
|
|
20930
|
-
|
|
21491
|
+
if (kind === 'get-page-preview') {
|
|
21492
|
+
if (currentType !== 'comic') {
|
|
21493
|
+
sendResponse(id, true, null);
|
|
21494
|
+
return;
|
|
21495
|
+
}
|
|
21496
|
+
sendResponse(
|
|
21497
|
+
id,
|
|
21498
|
+
true,
|
|
21499
|
+
await getComicPagePreview(payload.pageIndex || 0)
|
|
21500
|
+
);
|
|
21501
|
+
return;
|
|
21502
|
+
}
|
|
21503
|
+
|
|
21504
|
+
if (kind === 'search-text') {
|
|
21505
|
+
const results = await searchText(payload.query || '');
|
|
20931
21506
|
sendResponse(id, true, results);
|
|
20932
21507
|
return;
|
|
20933
21508
|
}
|
|
20934
21509
|
|
|
20935
|
-
if (kind ===
|
|
21510
|
+
if (kind === 'select-text') {
|
|
20936
21511
|
const selection = await selectText(payload.pageIndex || 0);
|
|
20937
21512
|
sendResponse(id, true, selection);
|
|
20938
21513
|
return;
|
|
20939
21514
|
}
|
|
20940
21515
|
|
|
20941
|
-
if (kind ===
|
|
21516
|
+
if (kind === 'get-outline') {
|
|
20942
21517
|
const outline = await buildOutline();
|
|
20943
21518
|
sendResponse(id, true, outline);
|
|
20944
21519
|
return;
|
|
20945
21520
|
}
|
|
20946
21521
|
|
|
20947
|
-
if (kind ===
|
|
21522
|
+
if (kind === 'get-page-index') {
|
|
20948
21523
|
const index = getPageIndex(payload.dest);
|
|
20949
21524
|
sendResponse(id, true, index);
|
|
20950
21525
|
return;
|
|
20951
21526
|
}
|
|
20952
21527
|
|
|
20953
|
-
if (kind ===
|
|
21528
|
+
if (kind === 'destroy') {
|
|
20954
21529
|
clearViewer();
|
|
20955
21530
|
sendResponse(id, true, {});
|
|
20956
21531
|
return;
|
|
20957
21532
|
}
|
|
20958
21533
|
|
|
20959
|
-
sendResponse(id, false, null,
|
|
21534
|
+
sendResponse(id, false, null, 'Unknown command');
|
|
20960
21535
|
} catch (err) {
|
|
20961
|
-
sendResponse(
|
|
20962
|
-
id,
|
|
20963
|
-
false,
|
|
20964
|
-
null,
|
|
20965
|
-
err && err.message ? err.message : String(err)
|
|
20966
|
-
);
|
|
21536
|
+
sendResponse(id, false, null, err && err.message ? err.message : String(err));
|
|
20967
21537
|
}
|
|
20968
21538
|
};
|
|
20969
21539
|
|
|
20970
21540
|
const onMessage = (event) => {
|
|
20971
21541
|
let message = null;
|
|
20972
|
-
const raw = event && typeof event ===
|
|
21542
|
+
const raw = event && typeof event === 'object' ? event.data : null;
|
|
20973
21543
|
|
|
20974
|
-
if (raw && typeof raw ===
|
|
21544
|
+
if (raw && typeof raw === 'object' && (raw.kind || raw.type) && raw.id) {
|
|
20975
21545
|
message = raw;
|
|
20976
|
-
} else if (typeof raw ===
|
|
20977
|
-
if (raw.startsWith(
|
|
20978
|
-
return;
|
|
20979
|
-
}
|
|
21546
|
+
} else if (typeof raw === 'string') {
|
|
21547
|
+
if (raw.startsWith('setImmediate$')) return;
|
|
20980
21548
|
const trimmed = raw.trim();
|
|
20981
|
-
if (
|
|
20982
|
-
!trimmed ||
|
|
20983
|
-
(!trimmed.startsWith("{") && !trimmed.startsWith("["))
|
|
20984
|
-
) {
|
|
21549
|
+
if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) {
|
|
20985
21550
|
return;
|
|
20986
21551
|
}
|
|
20987
21552
|
try {
|
|
20988
21553
|
message = JSON.parse(trimmed);
|
|
20989
21554
|
} catch (err) {
|
|
20990
|
-
sendEvent("RUNTIME_ERROR", {
|
|
20991
|
-
message: "Failed to parse message",
|
|
20992
|
-
context: "runtime.onMessage",
|
|
20993
|
-
stack: err && err.stack ? err.stack : null,
|
|
20994
|
-
preview: trimmed.slice(0, 200),
|
|
20995
|
-
});
|
|
20996
21555
|
return;
|
|
20997
21556
|
}
|
|
20998
21557
|
} else {
|
|
20999
21558
|
return;
|
|
21000
21559
|
}
|
|
21001
21560
|
|
|
21002
|
-
|
|
21003
|
-
|
|
21561
|
+
if (!message || message.id == null) return;
|
|
21562
|
+
|
|
21563
|
+
if (message.type === 'asset-response') {
|
|
21564
|
+
const pending = runtimeAssetRequests.get(message.id);
|
|
21565
|
+
if (!pending) return;
|
|
21566
|
+
runtimeAssetRequests.delete(message.id);
|
|
21567
|
+
clearTimeout(pending.timeout);
|
|
21568
|
+
if (message.ok) pending.resolve(message.data);
|
|
21569
|
+
else pending.reject(new Error(message.error || 'Falha ao carregar asset CBR.'));
|
|
21570
|
+
return;
|
|
21571
|
+
}
|
|
21572
|
+
|
|
21573
|
+
if (message.type === 'file-chunk-response') {
|
|
21574
|
+
const pending = fileChunkRequests.get(message.id);
|
|
21575
|
+
if (!pending) return;
|
|
21576
|
+
fileChunkRequests.delete(message.id);
|
|
21577
|
+
clearTimeout(pending.timeout);
|
|
21578
|
+
if (message.ok) pending.resolve(message);
|
|
21579
|
+
else pending.reject(new Error(message.error || 'Falha ao ler arquivo local.'));
|
|
21580
|
+
return;
|
|
21581
|
+
}
|
|
21582
|
+
|
|
21583
|
+
if (!message.kind) return;
|
|
21584
|
+
handleCommand(message);
|
|
21004
21585
|
};
|
|
21005
21586
|
|
|
21006
|
-
window.addEventListener(
|
|
21007
|
-
document.addEventListener(
|
|
21587
|
+
window.addEventListener('message', onMessage);
|
|
21588
|
+
document.addEventListener('message', onMessage);
|
|
21008
21589
|
|
|
21009
|
-
sendMessage({ type:
|
|
21590
|
+
sendMessage({ type: 'ready' });
|
|
21010
21591
|
})();
|
|
21011
21592
|
</script>
|
|
21012
21593
|
</body>
|
|
@@ -23199,29 +23780,100 @@ var import_react2 = require("react");
|
|
|
23199
23780
|
var import_react_native2 = require("react-native");
|
|
23200
23781
|
var import_react_native_webview = __toESM(require("react-native-webview"));
|
|
23201
23782
|
var import_core2 = require("@papyrus-sdk/core");
|
|
23783
|
+
|
|
23784
|
+
// components/webViewState.ts
|
|
23785
|
+
var parseWebViewState = (raw) => {
|
|
23786
|
+
try {
|
|
23787
|
+
const message = JSON.parse(raw);
|
|
23788
|
+
if (!message || typeof message !== "object") return null;
|
|
23789
|
+
const typedMessage = message;
|
|
23790
|
+
if (typedMessage.type !== "state") return null;
|
|
23791
|
+
if (!typedMessage.payload || typeof typedMessage.payload !== "object") {
|
|
23792
|
+
return null;
|
|
23793
|
+
}
|
|
23794
|
+
const payload = typedMessage.payload;
|
|
23795
|
+
const update = {};
|
|
23796
|
+
if (typeof payload.currentPage === "number" && Number.isInteger(payload.currentPage) && payload.currentPage >= 1) {
|
|
23797
|
+
update.currentPage = payload.currentPage;
|
|
23798
|
+
}
|
|
23799
|
+
if (typeof payload.pageCount === "number" && Number.isInteger(payload.pageCount) && payload.pageCount >= 0) {
|
|
23800
|
+
update.pageCount = payload.pageCount;
|
|
23801
|
+
}
|
|
23802
|
+
return Object.keys(update).length > 0 ? update : null;
|
|
23803
|
+
} catch {
|
|
23804
|
+
return null;
|
|
23805
|
+
}
|
|
23806
|
+
};
|
|
23807
|
+
|
|
23808
|
+
// components/WebViewViewer.tsx
|
|
23202
23809
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
23203
23810
|
var runtimeAsset = require_runtime();
|
|
23204
|
-
var
|
|
23205
|
-
if (typeof
|
|
23206
|
-
return { html:
|
|
23811
|
+
var resolveRuntimeSource = (asset) => {
|
|
23812
|
+
if (typeof asset === "string") {
|
|
23813
|
+
return { html: asset };
|
|
23207
23814
|
}
|
|
23208
|
-
if (typeof
|
|
23209
|
-
const resolved = import_react_native2.Image.resolveAssetSource(
|
|
23815
|
+
if (typeof asset === "number") {
|
|
23816
|
+
const resolved = import_react_native2.Image.resolveAssetSource(asset);
|
|
23210
23817
|
if (resolved?.uri) {
|
|
23211
23818
|
return { uri: resolved.uri };
|
|
23212
23819
|
}
|
|
23213
23820
|
}
|
|
23214
|
-
if (
|
|
23215
|
-
const uri =
|
|
23821
|
+
if (asset && typeof asset === "object" && "uri" in asset) {
|
|
23822
|
+
const uri = asset.uri;
|
|
23216
23823
|
if (uri) return { uri };
|
|
23217
23824
|
}
|
|
23218
23825
|
return { html: "" };
|
|
23219
|
-
}
|
|
23826
|
+
};
|
|
23220
23827
|
var WebViewViewer = ({ engine }) => {
|
|
23221
23828
|
const webViewRef = (0, import_react2.useRef)(null);
|
|
23222
23829
|
const { pageTheme } = (0, import_core2.useViewerStore)();
|
|
23830
|
+
const bridgeEngine = engine;
|
|
23831
|
+
const runtimeSource = (0, import_react2.useMemo)(
|
|
23832
|
+
() => resolveRuntimeSource(
|
|
23833
|
+
bridgeEngine.getWebViewRuntimeSource?.() ?? runtimeAsset
|
|
23834
|
+
),
|
|
23835
|
+
[bridgeEngine]
|
|
23836
|
+
);
|
|
23837
|
+
const [runtimeHtml, setRuntimeHtml] = (0, import_react2.useState)(null);
|
|
23838
|
+
(0, import_react2.useEffect)(() => {
|
|
23839
|
+
let active = true;
|
|
23840
|
+
if ("html" in runtimeSource) {
|
|
23841
|
+
setRuntimeHtml(runtimeSource.html);
|
|
23842
|
+
return () => {
|
|
23843
|
+
active = false;
|
|
23844
|
+
};
|
|
23845
|
+
}
|
|
23846
|
+
setRuntimeHtml(null);
|
|
23847
|
+
void fetch(runtimeSource.uri).then((response) => {
|
|
23848
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
23849
|
+
return response.text();
|
|
23850
|
+
}).then((html) => {
|
|
23851
|
+
if (active) setRuntimeHtml(html);
|
|
23852
|
+
}).catch(() => {
|
|
23853
|
+
if (active) setRuntimeHtml(null);
|
|
23854
|
+
});
|
|
23855
|
+
return () => {
|
|
23856
|
+
active = false;
|
|
23857
|
+
};
|
|
23858
|
+
}, [runtimeSource]);
|
|
23859
|
+
const webViewSource = (0, import_react2.useMemo)(() => {
|
|
23860
|
+
if (runtimeHtml !== null) {
|
|
23861
|
+
return {
|
|
23862
|
+
html: runtimeHtml,
|
|
23863
|
+
baseUrl: "http://localhost:3005/"
|
|
23864
|
+
};
|
|
23865
|
+
}
|
|
23866
|
+
if ("uri" in runtimeSource) return { html: "" };
|
|
23867
|
+
return runtimeSource;
|
|
23868
|
+
}, [runtimeHtml, runtimeSource]);
|
|
23869
|
+
const runtimeConfig = bridgeEngine.getWebViewRuntimeConfig?.();
|
|
23870
|
+
const runtimeConfigScript = (0, import_react2.useMemo)(() => {
|
|
23871
|
+
if (!runtimeConfig) return void 0;
|
|
23872
|
+
return `window.__PAPYRUS_RUNTIME_CONFIG__=${JSON.stringify(
|
|
23873
|
+
runtimeConfig
|
|
23874
|
+
)};true;`;
|
|
23875
|
+
}, [runtimeConfig]);
|
|
23223
23876
|
(0, import_react2.useEffect)(() => {
|
|
23224
|
-
const bridgeEngine = engine;
|
|
23225
23877
|
bridgeEngine.attachWebView?.({
|
|
23226
23878
|
postMessage: (message) => {
|
|
23227
23879
|
if (__DEV__) {
|
|
@@ -23231,9 +23883,22 @@ var WebViewViewer = ({ engine }) => {
|
|
|
23231
23883
|
webViewRef.current?.postMessage(message);
|
|
23232
23884
|
}
|
|
23233
23885
|
});
|
|
23234
|
-
}, [
|
|
23886
|
+
}, [bridgeEngine]);
|
|
23235
23887
|
const handleMessage = (event) => {
|
|
23236
|
-
const
|
|
23888
|
+
const state = parseWebViewState(event.nativeEvent.data);
|
|
23889
|
+
if (state) {
|
|
23890
|
+
const viewerState = import_core2.useViewerStore.getState();
|
|
23891
|
+
const nextState = {};
|
|
23892
|
+
if (state.currentPage !== void 0 && state.currentPage !== viewerState.currentPage) {
|
|
23893
|
+
nextState.currentPage = state.currentPage;
|
|
23894
|
+
}
|
|
23895
|
+
if (state.pageCount !== void 0 && state.pageCount !== viewerState.pageCount) {
|
|
23896
|
+
nextState.pageCount = state.pageCount;
|
|
23897
|
+
}
|
|
23898
|
+
if (Object.keys(nextState).length > 0) {
|
|
23899
|
+
viewerState.setDocumentState(nextState);
|
|
23900
|
+
}
|
|
23901
|
+
}
|
|
23237
23902
|
if (__DEV__) {
|
|
23238
23903
|
console.log("[Papyrus WebView] message", event.nativeEvent.data);
|
|
23239
23904
|
}
|
|
@@ -23273,11 +23938,12 @@ var WebViewViewer = ({ engine }) => {
|
|
|
23273
23938
|
import_react_native_webview.default,
|
|
23274
23939
|
{
|
|
23275
23940
|
ref: webViewRef,
|
|
23276
|
-
source:
|
|
23941
|
+
source: webViewSource,
|
|
23277
23942
|
originWhitelist: ["*"],
|
|
23278
23943
|
onMessage: handleMessage,
|
|
23279
23944
|
onLoadEnd: handleLoadEnd,
|
|
23280
23945
|
onError: handleError,
|
|
23946
|
+
injectedJavaScriptBeforeContentLoaded: runtimeConfigScript,
|
|
23281
23947
|
javaScriptEnabled: true,
|
|
23282
23948
|
domStorageEnabled: true,
|
|
23283
23949
|
scalesPageToFit: true,
|
|
@@ -27406,11 +28072,13 @@ var PageThumbnail = ({
|
|
|
27406
28072
|
frameHeight,
|
|
27407
28073
|
accentColor,
|
|
27408
28074
|
useNativePreview,
|
|
28075
|
+
useImagePreview,
|
|
27409
28076
|
shouldRenderPreview,
|
|
27410
28077
|
onPress
|
|
27411
28078
|
}) => {
|
|
27412
28079
|
const viewRef = (0, import_react8.useRef)(null);
|
|
27413
28080
|
const [layoutReady, setLayoutReady] = (0, import_react8.useState)(false);
|
|
28081
|
+
const [previewUri, setPreviewUri] = (0, import_react8.useState)(null);
|
|
27414
28082
|
(0, import_react8.useEffect)(() => {
|
|
27415
28083
|
if (!layoutReady || !useNativePreview || !shouldRenderPreview) return;
|
|
27416
28084
|
const viewTag = (0, import_react_native9.findNodeHandle)(viewRef.current);
|
|
@@ -27426,6 +28094,24 @@ var PageThumbnail = ({
|
|
|
27426
28094
|
useNativePreview,
|
|
27427
28095
|
zoom
|
|
27428
28096
|
]);
|
|
28097
|
+
(0, import_react8.useEffect)(() => {
|
|
28098
|
+
if (!useImagePreview || !shouldRenderPreview) {
|
|
28099
|
+
setPreviewUri(null);
|
|
28100
|
+
return;
|
|
28101
|
+
}
|
|
28102
|
+
let active = true;
|
|
28103
|
+
const previewEngine = engine;
|
|
28104
|
+
const request = previewEngine.getPagePreview?.(pageIndex);
|
|
28105
|
+
if (!request) return;
|
|
28106
|
+
void request.then((uri) => {
|
|
28107
|
+
if (active) setPreviewUri(uri);
|
|
28108
|
+
}).catch(() => {
|
|
28109
|
+
if (active) setPreviewUri(null);
|
|
28110
|
+
});
|
|
28111
|
+
return () => {
|
|
28112
|
+
active = false;
|
|
28113
|
+
};
|
|
28114
|
+
}, [engine, pageIndex, shouldRenderPreview, useImagePreview]);
|
|
27429
28115
|
const handleLayout = (event) => {
|
|
27430
28116
|
if (event.nativeEvent.layout.width && event.nativeEvent.layout.height) {
|
|
27431
28117
|
setLayoutReady(true);
|
|
@@ -27448,7 +28134,14 @@ var PageThumbnail = ({
|
|
|
27448
28134
|
{
|
|
27449
28135
|
onLayout: handleLayout,
|
|
27450
28136
|
style: [styles9.thumbFrame, { width: frameWidth, height: frameHeight }],
|
|
27451
|
-
children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_engine_native3.PapyrusPageView, { ref: viewRef, style: styles9.thumbView }) :
|
|
28137
|
+
children: useNativePreview && shouldRenderPreview ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_engine_native3.PapyrusPageView, { ref: viewRef, style: styles9.thumbView }) : useImagePreview && shouldRenderPreview && previewUri ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
28138
|
+
import_react_native9.Image,
|
|
28139
|
+
{
|
|
28140
|
+
source: { uri: previewUri },
|
|
28141
|
+
resizeMode: "contain",
|
|
28142
|
+
style: styles9.thumbImage
|
|
28143
|
+
}
|
|
28144
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native9.View, { style: [styles9.thumbFallback, isDark && styles9.thumbFallbackDark], children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
27452
28145
|
import_react_native9.Text,
|
|
27453
28146
|
{
|
|
27454
28147
|
style: [
|
|
@@ -27526,8 +28219,13 @@ var RightSheet = ({
|
|
|
27526
28219
|
accentColor
|
|
27527
28220
|
} = (0, import_core7.useViewerStore)();
|
|
27528
28221
|
const [pagesMode, setPagesMode] = (0, import_react8.useState)(
|
|
27529
|
-
documentType === "pdf" ? "thumbnails" : "summary"
|
|
28222
|
+
documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
|
|
27530
28223
|
);
|
|
28224
|
+
(0, import_react8.useEffect)(() => {
|
|
28225
|
+
setPagesMode(
|
|
28226
|
+
documentType === "pdf" || documentType === "comic" ? "thumbnails" : "summary"
|
|
28227
|
+
);
|
|
28228
|
+
}, [documentType]);
|
|
27531
28229
|
const isDark = uiTheme === "dark";
|
|
27532
28230
|
const t = getStrings(locale);
|
|
27533
28231
|
const showingNotes = sidebarRightTab === "annotations";
|
|
@@ -27545,6 +28243,7 @@ var RightSheet = ({
|
|
|
27545
28243
|
import_react_native9.UIManager.getViewManagerConfig?.("PapyrusPageView")
|
|
27546
28244
|
);
|
|
27547
28245
|
const useNativePreview = renderTarget !== "webview" && hasNativePageView;
|
|
28246
|
+
const useImagePreview = documentType === "comic" && renderTarget === "webview" && typeof engine.getPagePreview === "function";
|
|
27548
28247
|
const thumbnailDimensionsCacheRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
27549
28248
|
const thumbnailDimensionsPendingRef = (0, import_react8.useRef)(/* @__PURE__ */ new Set());
|
|
27550
28249
|
const thumbnailRefreshTimeoutRef = (0, import_react8.useRef)(null);
|
|
@@ -27701,7 +28400,7 @@ var RightSheet = ({
|
|
|
27701
28400
|
);
|
|
27702
28401
|
const renderThumbnailItem = (0, import_react8.useCallback)(
|
|
27703
28402
|
({ item }) => {
|
|
27704
|
-
const shouldRenderPreview = useNativePreview && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
|
|
28403
|
+
const shouldRenderPreview = (useNativePreview || useImagePreview) && (visibleThumbnailPages.has(item) || item < resolvedThumbsPrewarmCount || Math.abs(item + 1 - currentPage) <= 1);
|
|
27705
28404
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
27706
28405
|
PageThumbnail,
|
|
27707
28406
|
{
|
|
@@ -27715,6 +28414,7 @@ var RightSheet = ({
|
|
|
27715
28414
|
frameHeight: getThumbnailFrameHeight(item),
|
|
27716
28415
|
accentColor,
|
|
27717
28416
|
useNativePreview,
|
|
28417
|
+
useImagePreview,
|
|
27718
28418
|
shouldRenderPreview,
|
|
27719
28419
|
onPress: () => jumpToPage(item)
|
|
27720
28420
|
}
|
|
@@ -27731,6 +28431,7 @@ var RightSheet = ({
|
|
|
27731
28431
|
jumpToPage,
|
|
27732
28432
|
resolvedThumbsPrewarmCount,
|
|
27733
28433
|
useNativePreview,
|
|
28434
|
+
useImagePreview,
|
|
27734
28435
|
visibleThumbnailPages,
|
|
27735
28436
|
zoom
|
|
27736
28437
|
]
|
|
@@ -28062,6 +28763,10 @@ var styles9 = import_react_native9.StyleSheet.create({
|
|
|
28062
28763
|
width: "100%",
|
|
28063
28764
|
height: "100%"
|
|
28064
28765
|
},
|
|
28766
|
+
thumbImage: {
|
|
28767
|
+
width: "100%",
|
|
28768
|
+
height: "100%"
|
|
28769
|
+
},
|
|
28065
28770
|
thumbFallback: {
|
|
28066
28771
|
flex: 1,
|
|
28067
28772
|
alignItems: "center",
|