@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papyrus-sdk/ui-react-native",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"papyrus",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"url": "https://github.com/solrachix/Papyrus/issues"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"build": "tsup"
|
|
42
|
+
"build": "node runtime/syncComicRuntime.mjs && tsup"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"react-native-svg": "^15.5.0"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === "object" && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.PapyrusComicRuntime = Object.assign(
|
|
6
|
+
factory(),
|
|
7
|
+
root.PapyrusComicRuntime || {}
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
})(typeof self === "object" ? self : this, function () {
|
|
11
|
+
var IMAGE_EXTENSIONS = /\.(?:gif|jpe?g|png|svg|webp)$/i;
|
|
12
|
+
|
|
13
|
+
function isComicImageName(name) {
|
|
14
|
+
return typeof name === "string" && IMAGE_EXTENSIONS.test(name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function naturalCompare(left, right) {
|
|
18
|
+
var leftParts = String(left).split(/(\d+)/);
|
|
19
|
+
var rightParts = String(right).split(/(\d+)/);
|
|
20
|
+
var length = Math.max(leftParts.length, rightParts.length);
|
|
21
|
+
|
|
22
|
+
for (var index = 0; index < length; index += 1) {
|
|
23
|
+
var leftPart = leftParts[index] || "";
|
|
24
|
+
var rightPart = rightParts[index] || "";
|
|
25
|
+
if (leftPart === rightPart) continue;
|
|
26
|
+
|
|
27
|
+
var leftNumber = /^\d+$/.test(leftPart) ? Number(leftPart) : null;
|
|
28
|
+
var rightNumber = /^\d+$/.test(rightPart) ? Number(rightPart) : null;
|
|
29
|
+
if (leftNumber !== null && rightNumber !== null) {
|
|
30
|
+
return leftNumber - rightNumber;
|
|
31
|
+
}
|
|
32
|
+
return leftPart.localeCompare(rightPart, undefined, {
|
|
33
|
+
numeric: true,
|
|
34
|
+
sensitivity: "base",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sortComicPageNames(names) {
|
|
42
|
+
return names.slice().sort(naturalCompare);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function patchCbrWorkerSource(workerSource, wasmUrl) {
|
|
46
|
+
return workerSource.replace(
|
|
47
|
+
/new URL\("libarchive\.wasm",import\.meta\.url\)\.href/g,
|
|
48
|
+
JSON.stringify(wasmUrl)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getComicPreviewSize(width, height, maxWidth, maxHeight) {
|
|
53
|
+
var safeWidth = Math.max(1, Number(width) || 1);
|
|
54
|
+
var safeHeight = Math.max(1, Number(height) || 1);
|
|
55
|
+
var widthLimit = maxWidth || 240;
|
|
56
|
+
var heightLimit = maxHeight || 360;
|
|
57
|
+
var scale = Math.min(1, widthLimit / safeWidth, heightLimit / safeHeight);
|
|
58
|
+
return {
|
|
59
|
+
width: Math.max(1, Math.round(safeWidth * scale)),
|
|
60
|
+
height: Math.max(1, Math.round(safeHeight * scale)),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getComicPageAspectRatio(width, height) {
|
|
65
|
+
var safeWidth = Math.max(1, Number(width) || 1);
|
|
66
|
+
var safeHeight = Math.max(1, Number(height) || 1);
|
|
67
|
+
return safeWidth + " / " + safeHeight;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getProtectedComicPageIndexes(
|
|
71
|
+
currentPage,
|
|
72
|
+
visiblePageIndexes,
|
|
73
|
+
loadingPageIndexes
|
|
74
|
+
) {
|
|
75
|
+
var protectedIndexes = new Set([
|
|
76
|
+
currentPage - 1,
|
|
77
|
+
currentPage,
|
|
78
|
+
currentPage - 2,
|
|
79
|
+
]);
|
|
80
|
+
(visiblePageIndexes || []).forEach(function (pageIndex) {
|
|
81
|
+
protectedIndexes.add(pageIndex);
|
|
82
|
+
});
|
|
83
|
+
(loadingPageIndexes || []).forEach(function (pageIndex) {
|
|
84
|
+
protectedIndexes.add(pageIndex);
|
|
85
|
+
});
|
|
86
|
+
return protectedIndexes;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isCurrentComicPageLoad(
|
|
90
|
+
loadGeneration,
|
|
91
|
+
currentGeneration,
|
|
92
|
+
entry,
|
|
93
|
+
currentEntry
|
|
94
|
+
) {
|
|
95
|
+
return loadGeneration === currentGeneration && entry === currentEntry;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
isComicImageName: isComicImageName,
|
|
100
|
+
sortComicPageNames: sortComicPageNames,
|
|
101
|
+
patchCbrWorkerSource: patchCbrWorkerSource,
|
|
102
|
+
getComicPreviewSize: getComicPreviewSize,
|
|
103
|
+
getComicPageAspectRatio: getComicPageAspectRatio,
|
|
104
|
+
getProtectedComicPageIndexes: getProtectedComicPageIndexes,
|
|
105
|
+
isCurrentComicPageLoad: isCurrentComicPageLoad,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
getComicPreviewSize,
|
|
6
|
+
getComicPageAspectRatio,
|
|
7
|
+
getProtectedComicPageIndexes,
|
|
8
|
+
isCurrentComicPageLoad,
|
|
9
|
+
isComicImageName,
|
|
10
|
+
patchCbrWorkerSource,
|
|
11
|
+
sortComicPageNames,
|
|
12
|
+
} from "./comicRuntime";
|
|
13
|
+
|
|
14
|
+
describe("mobile comic runtime helpers", () => {
|
|
15
|
+
it("keeps only supported comic image entries", () => {
|
|
16
|
+
expect(isComicImageName("pages/001.jpg")).toBe(true);
|
|
17
|
+
expect(isComicImageName("pages/002.webp")).toBe(true);
|
|
18
|
+
expect(isComicImageName("pages/readme.txt")).toBe(false);
|
|
19
|
+
expect(isComicImageName("pages/")).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("sorts page names naturally", () => {
|
|
23
|
+
expect(
|
|
24
|
+
sortComicPageNames(["page-10.jpg", "page-2.jpg", "page-1.jpg"])
|
|
25
|
+
).toEqual(["page-1.jpg", "page-2.jpg", "page-10.jpg"]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("patches the shipped CBR worker to use the packaged WASM asset", () => {
|
|
29
|
+
const worker = readFileSync(
|
|
30
|
+
resolve(
|
|
31
|
+
process.cwd(),
|
|
32
|
+
"packages/engine-cbr-mobile/runtime/worker-bundle.js.txt"
|
|
33
|
+
),
|
|
34
|
+
"utf8"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const patched = patchCbrWorkerSource(worker, "/assets/libarchive.wasm");
|
|
38
|
+
|
|
39
|
+
expect(patched).not.toContain(
|
|
40
|
+
'new URL("libarchive.wasm",import.meta.url).href'
|
|
41
|
+
);
|
|
42
|
+
expect(patched).toContain(JSON.stringify("/assets/libarchive.wasm"));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("caps comic previews without upscaling them", () => {
|
|
46
|
+
expect(getComicPreviewSize(1600, 2400)).toEqual({ width: 240, height: 360 });
|
|
47
|
+
expect(getComicPreviewSize(120, 180)).toEqual({ width: 120, height: 180 });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("keeps the page geometry when a cached image URL is evicted", () => {
|
|
51
|
+
expect(getComicPageAspectRatio(1200, 1800)).toBe("1200 / 1800");
|
|
52
|
+
expect(getComicPageAspectRatio(0, 0)).toBe("1 / 1");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("protects visible and pending pages from URL eviction", () => {
|
|
56
|
+
expect(
|
|
57
|
+
Array.from(getProtectedComicPageIndexes(1, [3, 4], [5])).sort(
|
|
58
|
+
(left, right) => left - right
|
|
59
|
+
)
|
|
60
|
+
).toEqual([-1, 0, 1, 3, 4, 5]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("rejects page extraction completions from an older document", () => {
|
|
64
|
+
const entry = { name: "old.jpg" };
|
|
65
|
+
const currentEntry = { name: "new.jpg" };
|
|
66
|
+
|
|
67
|
+
expect(isCurrentComicPageLoad(2, 3, entry, currentEntry)).toBe(false);
|
|
68
|
+
expect(isCurrentComicPageLoad(3, 3, entry, currentEntry)).toBe(false);
|
|
69
|
+
expect(isCurrentComicPageLoad(3, 3, entry, entry)).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("embeds the tested helpers in the shipped runtimes", () => {
|
|
73
|
+
const runtime = readFileSync(
|
|
74
|
+
resolve(process.cwd(), "packages/ui-react-native/runtime/runtime.js"),
|
|
75
|
+
"utf8"
|
|
76
|
+
);
|
|
77
|
+
const html = readFileSync(
|
|
78
|
+
resolve(process.cwd(), "packages/ui-react-native/runtime/index.html"),
|
|
79
|
+
"utf8"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(runtime.match(/function getComicPageAspectRatio/g)).toHaveLength(1);
|
|
83
|
+
expect(html.match(/function getComicPageAspectRatio/g)).toHaveLength(1);
|
|
84
|
+
expect(html).toContain("root.PapyrusComicRuntime");
|
|
85
|
+
expect(runtime).toContain("getProtectedComicPageIndexes");
|
|
86
|
+
expect(html).toContain("getProtectedComicPageIndexes");
|
|
87
|
+
expect(runtime).toContain("file-chunk-request");
|
|
88
|
+
expect(html).toContain("file-chunk-request");
|
|
89
|
+
});
|
|
90
|
+
});
|