@laminapdf/viewer 0.2.0-alpha.3 → 0.2.0-alpha.5
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 +102 -8
- package/dist/client.d.ts +75 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +192 -32
- package/dist/client.js.map +1 -1
- package/dist/find/controller.d.ts +73 -0
- package/dist/find/controller.d.ts.map +1 -0
- package/dist/find/controller.js +227 -0
- package/dist/find/controller.js.map +1 -0
- package/dist/find/fold.d.ts +23 -0
- package/dist/find/fold.d.ts.map +1 -0
- package/dist/find/fold.js +99 -0
- package/dist/find/fold.js.map +1 -0
- package/dist/find/page.d.ts +44 -0
- package/dist/find/page.d.ts.map +1 -0
- package/dist/find/page.js +173 -0
- package/dist/find/page.js.map +1 -0
- package/dist/first-draw.d.ts +10 -0
- package/dist/first-draw.d.ts.map +1 -0
- package/dist/first-draw.js +33 -0
- package/dist/first-draw.js.map +1 -0
- package/dist/history/history.d.ts +65 -0
- package/dist/history/history.d.ts.map +1 -0
- package/dist/history/history.js +135 -0
- package/dist/history/history.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/layers/box.d.ts +10 -0
- package/dist/layers/box.d.ts.map +1 -0
- package/dist/layers/box.js +19 -0
- package/dist/layers/box.js.map +1 -0
- package/dist/layers/copy.d.ts +26 -0
- package/dist/layers/copy.d.ts.map +1 -0
- package/dist/layers/copy.js +150 -0
- package/dist/layers/copy.js.map +1 -0
- package/dist/layers/highlight-layer.d.ts +31 -0
- package/dist/layers/highlight-layer.d.ts.map +1 -0
- package/dist/layers/highlight-layer.js +95 -0
- package/dist/layers/highlight-layer.js.map +1 -0
- package/dist/layers/link-layer.d.ts +68 -0
- package/dist/layers/link-layer.d.ts.map +1 -0
- package/dist/layers/link-layer.js +279 -0
- package/dist/layers/link-layer.js.map +1 -0
- package/dist/layers/link-url.d.ts +19 -0
- package/dist/layers/link-url.d.ts.map +1 -0
- package/dist/layers/link-url.js +47 -0
- package/dist/layers/link-url.js.map +1 -0
- package/dist/layers/spans.d.ts +36 -0
- package/dist/layers/spans.d.ts.map +1 -0
- package/dist/layers/spans.js +103 -0
- package/dist/layers/spans.js.map +1 -0
- package/dist/layers/structure-layer.d.ts +15 -0
- package/dist/layers/structure-layer.d.ts.map +1 -0
- package/dist/layers/structure-layer.js +155 -0
- package/dist/layers/structure-layer.js.map +1 -0
- package/dist/layers/text-layer.d.ts +70 -0
- package/dist/layers/text-layer.d.ts.map +1 -0
- package/dist/layers/text-layer.js +576 -0
- package/dist/layers/text-layer.js.map +1 -0
- package/dist/layout/range.d.ts +3 -0
- package/dist/layout/range.d.ts.map +1 -0
- package/dist/layout/range.js +5 -0
- package/dist/layout/range.js.map +1 -0
- package/dist/network/watch.d.ts +30 -0
- package/dist/network/watch.d.ts.map +1 -0
- package/dist/network/watch.js +181 -0
- package/dist/network/watch.js.map +1 -0
- package/dist/protocol.d.ts +193 -6
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +16 -4
- package/dist/protocol.js.map +1 -1
- package/dist/render/tiles.d.ts +43 -3
- package/dist/render/tiles.d.ts.map +1 -1
- package/dist/render/tiles.js +62 -10
- package/dist/render/tiles.js.map +1 -1
- package/dist/source/http-range.d.ts +59 -0
- package/dist/source/http-range.d.ts.map +1 -0
- package/dist/source/http-range.js +284 -0
- package/dist/source/http-range.js.map +1 -0
- package/dist/source/opfs.d.ts +35 -0
- package/dist/source/opfs.d.ts.map +1 -0
- package/dist/source/opfs.js +178 -0
- package/dist/source/opfs.js.map +1 -0
- package/dist/thumbnails/strip.d.ts +37 -0
- package/dist/thumbnails/strip.d.ts.map +1 -0
- package/dist/thumbnails/strip.js +324 -0
- package/dist/thumbnails/strip.js.map +1 -0
- package/dist/view/budgets.d.ts +29 -0
- package/dist/view/budgets.d.ts.map +1 -0
- package/dist/view/budgets.js +63 -0
- package/dist/view/budgets.js.map +1 -0
- package/dist/view/touch.d.ts +71 -0
- package/dist/view/touch.d.ts.map +1 -0
- package/dist/view/touch.js +220 -0
- package/dist/view/touch.js.map +1 -0
- package/dist/view/user-select.d.ts +3 -0
- package/dist/view/user-select.d.ts.map +1 -0
- package/dist/view/user-select.js +10 -0
- package/dist/view/user-select.js.map +1 -0
- package/dist/view/viewer.d.ts +102 -1
- package/dist/view/viewer.d.ts.map +1 -1
- package/dist/view/viewer.js +660 -95
- package/dist/view/viewer.js.map +1 -1
- package/dist/worker/document.d.ts +53 -1
- package/dist/worker/document.d.ts.map +1 -1
- package/dist/worker/document.js +222 -24
- package/dist/worker/document.js.map +1 -1
- package/dist/worker/entry.d.ts +1 -1
- package/dist/worker/entry.d.ts.map +1 -1
- package/dist/worker/entry.js +2 -0
- package/dist/worker/entry.js.map +1 -1
- package/dist/worker/find.d.ts +7 -0
- package/dist/worker/find.d.ts.map +1 -0
- package/dist/worker/find.js +46 -0
- package/dist/worker/find.js.map +1 -0
- package/dist/worker/methods.d.ts.map +1 -1
- package/dist/worker/methods.js +49 -4
- package/dist/worker/methods.js.map +1 -1
- package/dist/worker/navigation.d.ts +10 -0
- package/dist/worker/navigation.d.ts.map +1 -0
- package/dist/worker/navigation.js +34 -0
- package/dist/worker/navigation.js.map +1 -0
- package/dist/worker/network.d.ts +4 -0
- package/dist/worker/network.d.ts.map +1 -0
- package/dist/worker/network.js +24 -0
- package/dist/worker/network.js.map +1 -0
- package/dist/worker/print.d.ts +14 -0
- package/dist/worker/print.d.ts.map +1 -0
- package/dist/worker/print.js +49 -0
- package/dist/worker/print.js.map +1 -0
- package/dist/worker/recent-pages.d.ts +14 -0
- package/dist/worker/recent-pages.d.ts.map +1 -0
- package/dist/worker/recent-pages.js +53 -0
- package/dist/worker/recent-pages.js.map +1 -0
- package/dist/worker/render.d.ts +5 -2
- package/dist/worker/render.d.ts.map +1 -1
- package/dist/worker/render.js +16 -6
- package/dist/worker/render.js.map +1 -1
- package/dist/worker/text.d.ts +25 -0
- package/dist/worker/text.d.ts.map +1 -0
- package/dist/worker/text.js +82 -0
- package/dist/worker/text.js.map +1 -0
- package/dist/zoom/destination.d.ts +18 -0
- package/dist/zoom/destination.d.ts.map +1 -0
- package/dist/zoom/destination.js +56 -0
- package/dist/zoom/destination.js.map +1 -0
- package/package.json +6 -5
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// A large local file read from the origin-private file system: copied there once, in the document
|
|
2
|
+
// worker, then read through a synchronous access handle, so neither Lamina PDF nor the render
|
|
3
|
+
// backend (PDFium reads synchronously) needs the whole file in memory. Copies live in one folder,
|
|
4
|
+
// named by the file's size, date and name, so opening the same file again reads the copy already
|
|
5
|
+
// there; when the folder would pass its budget, the copies used longest ago go first. OPFS is a
|
|
6
|
+
// cache, never the only copy: a browser without it, a copy that doesn't fit or fails partway, or a
|
|
7
|
+
// file another tab holds opens from memory, as before; only a cancel stops the open.
|
|
8
|
+
/** The OPFS folder of document copies, which the app's "Clear recent files and cached documents" empties too. */
|
|
9
|
+
export const CACHE_FOLDER = "documents";
|
|
10
|
+
/** Files above this many bytes are read from a copy: 200 MiB. */
|
|
11
|
+
export const COPY_ABOVE = 200 * 1024 * 1024;
|
|
12
|
+
/** How much the copies may take, at most: 2 GiB, and never more than half the free quota. */
|
|
13
|
+
const MAX_CACHE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
14
|
+
/** Copied in steps of 8 MiB, each a chance to stop and to report progress. */
|
|
15
|
+
const CHUNK = 8 * 1024 * 1024;
|
|
16
|
+
/** Beside each copy, a file holding when it was last opened (the copy's own date is when it was made). */
|
|
17
|
+
const USED = ".used";
|
|
18
|
+
/** The copy's name: its size, date and a hash of its name, so the same file finds its copy again. */
|
|
19
|
+
export function copyName(file) {
|
|
20
|
+
let hash = 2166136261;
|
|
21
|
+
for (let i = 0; i < file.name.length; i++)
|
|
22
|
+
hash = Math.imul(hash ^ file.name.charCodeAt(i), 16777619);
|
|
23
|
+
return `${file.size}-${file.lastModified}-${(hash >>> 0).toString(16)}.pdf`;
|
|
24
|
+
}
|
|
25
|
+
/** A copied file, read synchronously; both a RangeSource (Lamina PDF) and a RenderSource. */
|
|
26
|
+
export class OpfsByteSource {
|
|
27
|
+
size;
|
|
28
|
+
#handle;
|
|
29
|
+
#folder;
|
|
30
|
+
#name;
|
|
31
|
+
#keep;
|
|
32
|
+
constructor(handle, folder, name, keep) {
|
|
33
|
+
this.#handle = handle;
|
|
34
|
+
this.#folder = folder;
|
|
35
|
+
this.#name = name;
|
|
36
|
+
this.#keep = keep;
|
|
37
|
+
this.size = handle.getSize();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `file` read from its OPFS copy, made now if it isn't there; `undefined` where the browser has
|
|
41
|
+
* no OPFS or the copy can't be made (no room, locked by another tab, a write that fails), so the
|
|
42
|
+
* caller reads it from memory. Rejects only when `options.signal` aborts.
|
|
43
|
+
*/
|
|
44
|
+
static async open(file, options) {
|
|
45
|
+
const folder = await cacheFolder();
|
|
46
|
+
if (folder === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
const name = copyName(file);
|
|
49
|
+
let handle;
|
|
50
|
+
try {
|
|
51
|
+
const fileHandle = (await folder.getFileHandle(name, { create: true }));
|
|
52
|
+
handle = await fileHandle.createSyncAccessHandle();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Locked by another tab's worker, or the browser refuses: read it from memory instead.
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
if (handle.getSize() !== file.size) {
|
|
60
|
+
if (!(await makeRoom(folder, name, file.size)))
|
|
61
|
+
throw new Error("no room for the copy");
|
|
62
|
+
await copyInto(handle, file, options);
|
|
63
|
+
}
|
|
64
|
+
// The use record is best effort: another tab may hold it, and the copy is good without it.
|
|
65
|
+
await touch(folder, name).catch(() => undefined);
|
|
66
|
+
return new OpfsByteSource(handle, folder, name, options.keep);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// A copy cut short (a cancel, the disk full) is never left to look complete.
|
|
70
|
+
handle.close();
|
|
71
|
+
await removeCopy(folder, name);
|
|
72
|
+
if (options.signal.aborted)
|
|
73
|
+
throw error;
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
readSync(offset, length) {
|
|
78
|
+
const end = Math.min(this.size, offset + length);
|
|
79
|
+
if (offset < 0 || end < offset)
|
|
80
|
+
return undefined;
|
|
81
|
+
const out = new Uint8Array(end - offset);
|
|
82
|
+
this.#handle.read(out, { at: offset });
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
read(offset, length, options) {
|
|
86
|
+
options?.signal?.throwIfAborted();
|
|
87
|
+
return Promise.resolve(this.readSync(offset, length) ?? new Uint8Array());
|
|
88
|
+
}
|
|
89
|
+
/** Lets the copy go: kept in the folder for next time, or removed when it isn't to be kept. */
|
|
90
|
+
close() {
|
|
91
|
+
this.#handle.close();
|
|
92
|
+
if (!this.#keep)
|
|
93
|
+
void removeCopy(this.#folder, this.#name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function cacheFolder() {
|
|
97
|
+
try {
|
|
98
|
+
const root = await navigator.storage.getDirectory();
|
|
99
|
+
return await root.getDirectoryHandle(CACHE_FOLDER, { create: true });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Removes a copy and its use record; a copy another tab is reading stays (false). */
|
|
106
|
+
async function removeCopy(folder, name) {
|
|
107
|
+
try {
|
|
108
|
+
await folder.removeEntry(name);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
await folder.removeEntry(name + USED).catch(() => undefined);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
/** Records that copy `name` was opened now. */
|
|
117
|
+
async function touch(folder, name) {
|
|
118
|
+
const used = (await folder.getFileHandle(name + USED, { create: true }));
|
|
119
|
+
const handle = await used.createSyncAccessHandle();
|
|
120
|
+
try {
|
|
121
|
+
const stamp = new Uint8Array(8);
|
|
122
|
+
new DataView(stamp.buffer).setFloat64(0, Date.now());
|
|
123
|
+
handle.truncate(0);
|
|
124
|
+
handle.write(stamp, { at: 0 });
|
|
125
|
+
handle.flush();
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
handle.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** When copy `name` was last opened, else when it was made. */
|
|
132
|
+
async function lastUsed(folder, name, made) {
|
|
133
|
+
try {
|
|
134
|
+
const record = await (await folder.getFileHandle(name + USED)).getFile();
|
|
135
|
+
return new DataView(await record.arrayBuffer()).getFloat64(0);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return made;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Whether a copy of `size` bytes fits, making room by removing other copies, those used longest
|
|
143
|
+
* ago first. The copies together may take half of what is free for them (the quota's free space
|
|
144
|
+
* plus what the copies already hold), and never more than MAX_CACHE_BYTES.
|
|
145
|
+
*/
|
|
146
|
+
async function makeRoom(folder, keep, size) {
|
|
147
|
+
const copies = [];
|
|
148
|
+
for await (const [name, entry] of folder.entries()) {
|
|
149
|
+
if (name === keep || name.endsWith(USED) || entry.kind !== "file")
|
|
150
|
+
continue;
|
|
151
|
+
const copy = await entry.getFile().catch(() => undefined);
|
|
152
|
+
if (copy !== undefined)
|
|
153
|
+
copies.push({ name, size: copy.size, used: await lastUsed(folder, name, copy.lastModified) });
|
|
154
|
+
}
|
|
155
|
+
let held = copies.reduce((sum, c) => sum + c.size, 0);
|
|
156
|
+
const { quota = 0, usage = 0 } = await navigator.storage.estimate();
|
|
157
|
+
const budget = Math.min(MAX_CACHE_BYTES, (quota - usage + held) / 2);
|
|
158
|
+
if (size > budget)
|
|
159
|
+
return false;
|
|
160
|
+
for (const copy of copies.sort((a, b) => a.used - b.used)) {
|
|
161
|
+
if (held + size <= budget)
|
|
162
|
+
break;
|
|
163
|
+
if (await removeCopy(folder, copy.name))
|
|
164
|
+
held -= copy.size;
|
|
165
|
+
}
|
|
166
|
+
return held + size <= budget;
|
|
167
|
+
}
|
|
168
|
+
async function copyInto(handle, file, options) {
|
|
169
|
+
handle.truncate(0);
|
|
170
|
+
for (let at = 0; at < file.size; at += CHUNK) {
|
|
171
|
+
options.signal.throwIfAborted();
|
|
172
|
+
const bytes = new Uint8Array(await file.slice(at, Math.min(file.size, at + CHUNK)).arrayBuffer());
|
|
173
|
+
handle.write(bytes, { at });
|
|
174
|
+
options.onProgress?.(Math.min(file.size, at + CHUNK), file.size);
|
|
175
|
+
}
|
|
176
|
+
handle.flush();
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=opfs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opfs.js","sourceRoot":"","sources":["../../src/source/opfs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PageInfo } from "@laminapdf/sdk";
|
|
2
|
+
import type { ViewerClient } from "../client.js";
|
|
3
|
+
import { type OpenedDocument } from "../protocol.js";
|
|
4
|
+
import type { Viewer } from "../view/viewer.js";
|
|
5
|
+
/** How a strip looks and how much it keeps. */
|
|
6
|
+
export interface ThumbnailStripOptions {
|
|
7
|
+
/** The widest page's thumbnail width in CSS px (default 120). */
|
|
8
|
+
readonly width?: number;
|
|
9
|
+
/** Thumbnails mounted past each end of the view (default 4). */
|
|
10
|
+
readonly margin?: number;
|
|
11
|
+
/** The bitmap cache's budget in bytes (default 32 MB). */
|
|
12
|
+
readonly cacheBytes?: number;
|
|
13
|
+
/**
|
|
14
|
+
* A thumbnail's accessible name and visible caption: by default its page label, and the
|
|
15
|
+
* name adds "Page … of …".
|
|
16
|
+
*/
|
|
17
|
+
readonly label?: (page: PageInfo, count: number) => {
|
|
18
|
+
readonly caption: string;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
};
|
|
21
|
+
/** The list's own accessible name (default "Pages"). */
|
|
22
|
+
readonly name?: string;
|
|
23
|
+
}
|
|
24
|
+
/** The pages as thumbnails. Make one with `new ThumbnailStrip(client, viewer)`, then `mount` and `open`. */
|
|
25
|
+
export declare class ThumbnailStrip {
|
|
26
|
+
#private;
|
|
27
|
+
/** The scrolling list (`role=listbox`); size it with CSS. */
|
|
28
|
+
readonly element: HTMLElement;
|
|
29
|
+
constructor(client: ViewerClient, viewer: Viewer, options?: ThumbnailStripOptions);
|
|
30
|
+
/** Puts the strip in `container`. */
|
|
31
|
+
mount(container: HTMLElement): void;
|
|
32
|
+
/** Shows `document`'s pages (the one the viewer opened). */
|
|
33
|
+
open(document: OpenedDocument): void;
|
|
34
|
+
/** Takes the strip down: renders aborted, canvases and bitmaps freed, listeners removed. */
|
|
35
|
+
destroy(): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../../src/thumbnails/strip.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,+CAA+C;AAC/C,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxG,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAyBD,4GAA4G;AAC5G,qBAAa,cAAc;;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAqB9B,YAAY,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,EA4BpF;IAED,qCAAqC;IACrC,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAGlC;IAED,4DAA4D;IAC5D,IAAI,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CASnC;IAED,4FAA4F;IAC5F,OAAO,IAAI,IAAI,CAMd;CA2NF"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// The thumbnails strip: one small picture per page in a scrolling list, the
|
|
2
|
+
// current page marked, a click or Enter going there. Like the viewer it mounts only the
|
|
3
|
+
// thumbnails in view and a few either side (the same `PageLayout` at thumbnail scale), each with
|
|
4
|
+
// a canvas from its own small pool, rendered at `background` priority so the main view's pages
|
|
5
|
+
// always come first; a thumbnail scrolled away aborts its render. Bitmaps stay in a cache of their
|
|
6
|
+
// own (32 MB by default), so scrolling back costs nothing. Framework-free; styles are set through
|
|
7
|
+
// the CSSOM, which a `style-src 'self'` policy allows.
|
|
8
|
+
import { BitmapCache } from "../canvas/cache.js";
|
|
9
|
+
import { CanvasPool } from "../canvas/pool.js";
|
|
10
|
+
import { PageLayout, rotated } from "../layout/page-layout.js";
|
|
11
|
+
import { appearanceField, appearanceKey } from "../protocol.js";
|
|
12
|
+
/** Space for the caption under each thumbnail, and between thumbnails, in CSS px. */
|
|
13
|
+
const CAPTION = 20;
|
|
14
|
+
const GAP = 12;
|
|
15
|
+
/** The default caption (the page label, else the number) and name. */
|
|
16
|
+
function defaultLabel(page, count) {
|
|
17
|
+
const number = String(page.index + 1);
|
|
18
|
+
const caption = page.label ?? number;
|
|
19
|
+
const name = caption === number ? `Page ${number} of ${count}` : `Page ${caption} (${number} of ${count})`;
|
|
20
|
+
return { caption, name };
|
|
21
|
+
}
|
|
22
|
+
/** The pages as thumbnails. Make one with `new ThumbnailStrip(client, viewer)`, then `mount` and `open`. */
|
|
23
|
+
export class ThumbnailStrip {
|
|
24
|
+
/** The scrolling list (`role=listbox`); size it with CSS. */
|
|
25
|
+
element;
|
|
26
|
+
#client;
|
|
27
|
+
#viewer;
|
|
28
|
+
#width;
|
|
29
|
+
#margin;
|
|
30
|
+
#label;
|
|
31
|
+
#spacer;
|
|
32
|
+
#pool = new CanvasPool({ max: 32, maxPixels: 8 * 1024 * 1024 });
|
|
33
|
+
#cache;
|
|
34
|
+
#items = new Map();
|
|
35
|
+
#cleanup = [];
|
|
36
|
+
#document;
|
|
37
|
+
#layout;
|
|
38
|
+
#rotation = 0;
|
|
39
|
+
#appearance = "original";
|
|
40
|
+
/** The page the viewer shows, marked selected. */
|
|
41
|
+
#current = 0;
|
|
42
|
+
/** The page that takes Tab (roving focus). */
|
|
43
|
+
#focus = 0;
|
|
44
|
+
#frame;
|
|
45
|
+
constructor(client, viewer, options = {}) {
|
|
46
|
+
this.#client = client;
|
|
47
|
+
this.#viewer = viewer;
|
|
48
|
+
this.#width = options.width ?? 120;
|
|
49
|
+
this.#margin = options.margin ?? 4;
|
|
50
|
+
this.#label = options.label ?? defaultLabel;
|
|
51
|
+
this.#cache = new BitmapCache(options.cacheBytes ?? 32 * 1024 * 1024);
|
|
52
|
+
const root = document.createElement("div");
|
|
53
|
+
root.className = "lamina-thumbnails";
|
|
54
|
+
root.setAttribute("role", "listbox");
|
|
55
|
+
root.setAttribute("aria-label", options.name ?? "Pages");
|
|
56
|
+
Object.assign(root.style, { position: "relative", overflowY: "auto", overflowX: "hidden" });
|
|
57
|
+
this.#spacer = document.createElement("div");
|
|
58
|
+
this.#spacer.style.position = "relative";
|
|
59
|
+
root.append(this.#spacer);
|
|
60
|
+
this.element = root;
|
|
61
|
+
const on = (type, f) => {
|
|
62
|
+
root.addEventListener(type, f);
|
|
63
|
+
this.#cleanup.push(() => root.removeEventListener(type, f));
|
|
64
|
+
};
|
|
65
|
+
on("scroll", () => this.#schedule());
|
|
66
|
+
on("click", (e) => this.#clicked(e));
|
|
67
|
+
on("keydown", (e) => this.#key(e));
|
|
68
|
+
this.#cleanup.push(viewer.subscribe(() => this.#viewChanged()));
|
|
69
|
+
// A strip shown later (its tab opened, its sheet raised) mounts what it now shows.
|
|
70
|
+
const resized = new ResizeObserver(() => this.#schedule());
|
|
71
|
+
resized.observe(root);
|
|
72
|
+
this.#cleanup.push(() => resized.disconnect());
|
|
73
|
+
}
|
|
74
|
+
/** Puts the strip in `container`. */
|
|
75
|
+
mount(container) {
|
|
76
|
+
container.append(this.element);
|
|
77
|
+
this.#schedule();
|
|
78
|
+
}
|
|
79
|
+
/** Shows `document`'s pages (the one the viewer opened). */
|
|
80
|
+
open(document) {
|
|
81
|
+
this.#unmountAll();
|
|
82
|
+
this.#cache.clear();
|
|
83
|
+
this.#document = document;
|
|
84
|
+
this.#current = 0;
|
|
85
|
+
this.#focus = 0;
|
|
86
|
+
this.#rotation = this.#viewer.getState().rotation;
|
|
87
|
+
this.element.scrollTop = 0;
|
|
88
|
+
this.#relayout();
|
|
89
|
+
}
|
|
90
|
+
/** Takes the strip down: renders aborted, canvases and bitmaps freed, listeners removed. */
|
|
91
|
+
destroy() {
|
|
92
|
+
if (this.#frame !== undefined)
|
|
93
|
+
cancelAnimationFrame(this.#frame);
|
|
94
|
+
this.#unmountAll();
|
|
95
|
+
this.#cache.clear();
|
|
96
|
+
for (const f of this.#cleanup.splice(0))
|
|
97
|
+
f();
|
|
98
|
+
this.element.remove();
|
|
99
|
+
}
|
|
100
|
+
// --- internals ---
|
|
101
|
+
#relayout() {
|
|
102
|
+
const pages = this.#document?.pages ?? [];
|
|
103
|
+
const widest = Math.max(1, ...pages.map((p) => rotated(p, this.#rotation).width));
|
|
104
|
+
this.#layout = new PageLayout(pages, { scale: this.#width / widest, rotation: this.#rotation, gap: GAP + CAPTION });
|
|
105
|
+
this.#spacer.style.height = `${this.#layout.height}px`;
|
|
106
|
+
this.#unmountAll();
|
|
107
|
+
this.#schedule();
|
|
108
|
+
}
|
|
109
|
+
#viewChanged() {
|
|
110
|
+
const state = this.#viewer.getState();
|
|
111
|
+
if (state.rotation !== this.#rotation) {
|
|
112
|
+
this.#rotation = state.rotation;
|
|
113
|
+
this.#relayout();
|
|
114
|
+
}
|
|
115
|
+
// The thumbnails follow the page appearance: drawn again in it.
|
|
116
|
+
if (appearanceKey(state.appearance) !== appearanceKey(this.#appearance)) {
|
|
117
|
+
this.#appearance = state.appearance;
|
|
118
|
+
this.#unmountAll();
|
|
119
|
+
this.#schedule();
|
|
120
|
+
}
|
|
121
|
+
if (state.currentPage === this.#current || this.#document === undefined)
|
|
122
|
+
return;
|
|
123
|
+
this.#current = state.currentPage;
|
|
124
|
+
// Tab lands on the current page, unless focus is already in the list.
|
|
125
|
+
if (!this.element.contains(document.activeElement))
|
|
126
|
+
this.#focus = this.#current;
|
|
127
|
+
this.#reveal(this.#current);
|
|
128
|
+
this.#schedule();
|
|
129
|
+
}
|
|
130
|
+
/** Scrolls the strip just enough to show page `index`'s thumbnail. */
|
|
131
|
+
#reveal(index) {
|
|
132
|
+
const layout = this.#layout;
|
|
133
|
+
if (layout === undefined || index >= layout.pageCount)
|
|
134
|
+
return;
|
|
135
|
+
const top = layout.offsetOf(index) - GAP;
|
|
136
|
+
const bottom = top + layout.heights[index] + CAPTION + 2 * GAP;
|
|
137
|
+
const view = this.element;
|
|
138
|
+
if (top < view.scrollTop)
|
|
139
|
+
view.scrollTop = top;
|
|
140
|
+
else if (bottom > view.scrollTop + view.clientHeight)
|
|
141
|
+
view.scrollTop = bottom - view.clientHeight;
|
|
142
|
+
}
|
|
143
|
+
#schedule() {
|
|
144
|
+
if (this.#frame !== undefined)
|
|
145
|
+
return;
|
|
146
|
+
this.#frame = requestAnimationFrame(() => {
|
|
147
|
+
this.#frame = undefined;
|
|
148
|
+
this.#update();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
#update() {
|
|
152
|
+
const layout = this.#layout;
|
|
153
|
+
if (layout === undefined || layout.pageCount === 0)
|
|
154
|
+
return;
|
|
155
|
+
// Nothing renders while the strip is out of the page or hidden (its tab not shown).
|
|
156
|
+
if (!this.element.isConnected || this.element.clientHeight === 0)
|
|
157
|
+
return;
|
|
158
|
+
const top = this.element.scrollTop;
|
|
159
|
+
const view = layout.rangeIn(top, top + Math.max(1, this.element.clientHeight));
|
|
160
|
+
const first = Math.max(0, view.first - this.#margin);
|
|
161
|
+
const last = Math.min(layout.pageCount - 1, view.last + this.#margin);
|
|
162
|
+
for (const [index, item] of this.#items)
|
|
163
|
+
if (index < first || index > last)
|
|
164
|
+
this.#unmount(item);
|
|
165
|
+
for (let index = first; index <= last; index++) {
|
|
166
|
+
const item = this.#items.get(index) ?? this.#mount(index, layout);
|
|
167
|
+
this.#mark(item);
|
|
168
|
+
if (!item.done && item.abort === undefined)
|
|
169
|
+
void this.#draw(item, layout);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
#mount(index, layout) {
|
|
173
|
+
const page = this.#document?.pages[index];
|
|
174
|
+
const { caption, name } = this.#label(page, layout.pageCount);
|
|
175
|
+
const root = document.createElement("div");
|
|
176
|
+
root.className = "lamina-thumbnail";
|
|
177
|
+
root.setAttribute("role", "option");
|
|
178
|
+
root.setAttribute("aria-label", name);
|
|
179
|
+
root.setAttribute("aria-posinset", String(index + 1));
|
|
180
|
+
root.setAttribute("aria-setsize", String(layout.pageCount));
|
|
181
|
+
root.dataset.pageIndex = String(index);
|
|
182
|
+
const width = layout.widths[index];
|
|
183
|
+
const height = layout.heights[index];
|
|
184
|
+
Object.assign(root.style, {
|
|
185
|
+
position: "absolute",
|
|
186
|
+
top: `${layout.offsetOf(index) - GAP / 2}px`,
|
|
187
|
+
left: "0",
|
|
188
|
+
right: "0",
|
|
189
|
+
height: `${height + CAPTION + GAP}px`,
|
|
190
|
+
display: "flex",
|
|
191
|
+
flexDirection: "column",
|
|
192
|
+
alignItems: "center",
|
|
193
|
+
paddingTop: `${GAP / 2}px`,
|
|
194
|
+
});
|
|
195
|
+
const frame = document.createElement("div");
|
|
196
|
+
frame.className = "lamina-thumbnail-page";
|
|
197
|
+
Object.assign(frame.style, { width: `${width}px`, height: `${height}px`, flex: "none" });
|
|
198
|
+
const label = document.createElement("div");
|
|
199
|
+
label.className = "lamina-thumbnail-label";
|
|
200
|
+
label.textContent = caption;
|
|
201
|
+
label.setAttribute("aria-hidden", "true");
|
|
202
|
+
root.append(frame, label);
|
|
203
|
+
this.#spacer.append(root);
|
|
204
|
+
const item = { index, root, frame, canvas: undefined, abort: undefined, done: false };
|
|
205
|
+
this.#items.set(index, item);
|
|
206
|
+
return item;
|
|
207
|
+
}
|
|
208
|
+
/** Sets `item`'s selected state and whether Tab reaches it. */
|
|
209
|
+
#mark(item) {
|
|
210
|
+
item.root.setAttribute("aria-selected", String(item.index === this.#current));
|
|
211
|
+
item.root.tabIndex = item.index === this.#focus ? 0 : -1;
|
|
212
|
+
}
|
|
213
|
+
async #draw(item, layout) {
|
|
214
|
+
const ratio = globalThis.devicePixelRatio || 1;
|
|
215
|
+
const scale = layout.scale * ratio;
|
|
216
|
+
const appearance = this.#appearance;
|
|
217
|
+
const id = this.#client.document?.id ?? 0;
|
|
218
|
+
const key = `${id}:${item.index}:${layout.rotation}:${scale.toFixed(4)}:${appearanceKey(appearance)}`;
|
|
219
|
+
let bitmap = this.#cache.get(key);
|
|
220
|
+
let owned = false;
|
|
221
|
+
if (bitmap === undefined) {
|
|
222
|
+
const abort = new AbortController();
|
|
223
|
+
item.abort = abort;
|
|
224
|
+
try {
|
|
225
|
+
const rendered = await this.#client.render({ page: item.index, scale, rotation: layout.rotation, ...appearanceField(appearance) }, { priority: "background", signal: abort.signal });
|
|
226
|
+
bitmap = rendered.bitmap;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
// Scrolled away or a new document: drawn again if it comes back into view.
|
|
230
|
+
if (abort.signal.aborted)
|
|
231
|
+
return;
|
|
232
|
+
// A failed render keeps its placeholder, and isn't asked for again while mounted.
|
|
233
|
+
item.done = true;
|
|
234
|
+
reportError(error);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
if (item.abort === abort)
|
|
239
|
+
item.abort = undefined;
|
|
240
|
+
}
|
|
241
|
+
if (abort.signal.aborted || this.#items.get(item.index) !== item || this.#layout !== layout) {
|
|
242
|
+
bitmap.close();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
owned = !this.#cache.set(key, item.index, bitmap, this.#current);
|
|
246
|
+
}
|
|
247
|
+
this.#paint(item, bitmap, layout);
|
|
248
|
+
if (owned)
|
|
249
|
+
bitmap.close();
|
|
250
|
+
}
|
|
251
|
+
#paint(item, bitmap, layout) {
|
|
252
|
+
const canvas = item.canvas ?? this.#pool.acquire();
|
|
253
|
+
if (canvas === undefined)
|
|
254
|
+
return;
|
|
255
|
+
item.canvas = canvas;
|
|
256
|
+
const size = this.#pool.fit(canvas, bitmap.width, bitmap.height);
|
|
257
|
+
canvas.width = size.width;
|
|
258
|
+
canvas.height = size.height;
|
|
259
|
+
Object.assign(canvas.style, {
|
|
260
|
+
display: "block",
|
|
261
|
+
width: `${layout.widths[item.index]}px`,
|
|
262
|
+
height: `${layout.heights[item.index]}px`,
|
|
263
|
+
});
|
|
264
|
+
canvas.getContext("2d")?.drawImage(bitmap, 0, 0, size.width, size.height);
|
|
265
|
+
if (canvas.parentElement !== item.frame)
|
|
266
|
+
item.frame.append(canvas);
|
|
267
|
+
item.done = true;
|
|
268
|
+
}
|
|
269
|
+
#unmount(item) {
|
|
270
|
+
item.abort?.abort();
|
|
271
|
+
if (item.canvas !== undefined)
|
|
272
|
+
this.#pool.release(item.canvas);
|
|
273
|
+
item.root.remove();
|
|
274
|
+
this.#items.delete(item.index);
|
|
275
|
+
}
|
|
276
|
+
#unmountAll() {
|
|
277
|
+
for (const item of [...this.#items.values()])
|
|
278
|
+
this.#unmount(item);
|
|
279
|
+
}
|
|
280
|
+
#clicked(e) {
|
|
281
|
+
const target = e.target?.closest(".lamina-thumbnail");
|
|
282
|
+
const index = Number(target?.dataset.pageIndex);
|
|
283
|
+
if (target === null || target === undefined || !Number.isInteger(index))
|
|
284
|
+
return;
|
|
285
|
+
this.#go(index);
|
|
286
|
+
}
|
|
287
|
+
/** Arrow keys, Home and End move focus; Enter and Space go to the focused page. */
|
|
288
|
+
#key(e) {
|
|
289
|
+
const count = this.#layout?.pageCount ?? 0;
|
|
290
|
+
if (count === 0)
|
|
291
|
+
return;
|
|
292
|
+
const moves = {
|
|
293
|
+
ArrowDown: this.#focus + 1,
|
|
294
|
+
ArrowUp: this.#focus - 1,
|
|
295
|
+
Home: 0,
|
|
296
|
+
End: count - 1,
|
|
297
|
+
};
|
|
298
|
+
const next = moves[e.key];
|
|
299
|
+
if (next !== undefined) {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
this.#moveFocus(Math.min(count - 1, Math.max(0, next)));
|
|
302
|
+
}
|
|
303
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
304
|
+
e.preventDefault();
|
|
305
|
+
this.#go(this.#focus);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
#moveFocus(index) {
|
|
309
|
+
this.#focus = index;
|
|
310
|
+
this.#reveal(index);
|
|
311
|
+
// The item may not be mounted yet: mount the range now, then focus it.
|
|
312
|
+
this.#update();
|
|
313
|
+
const item = this.#items.get(index);
|
|
314
|
+
for (const other of this.#items.values())
|
|
315
|
+
this.#mark(other);
|
|
316
|
+
item?.root.focus();
|
|
317
|
+
}
|
|
318
|
+
#go(index) {
|
|
319
|
+
this.#focus = index;
|
|
320
|
+
// A jump: Back returns to where the view was (R06).
|
|
321
|
+
this.#viewer.jumpToPage(index);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip.js","sourceRoot":"","sources":["../../src/thumbnails/strip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CanvasPoolOptions } from "../canvas/pool.js";
|
|
2
|
+
/** What the budgets depend on: whether the main pointer is coarse, and the device's memory in GB, if known. */
|
|
3
|
+
export interface DeviceHints {
|
|
4
|
+
readonly coarse: boolean;
|
|
5
|
+
readonly memoryGb?: number | undefined;
|
|
6
|
+
}
|
|
7
|
+
/** The viewer's pixel and byte budgets. */
|
|
8
|
+
export interface ViewerBudgets {
|
|
9
|
+
/** The most device pixels one page canvas holds. */
|
|
10
|
+
readonly backdropMaxPixels: number;
|
|
11
|
+
/** The most device pixels any one canvas holds, a detail canvas too. */
|
|
12
|
+
readonly canvasMaxPixels: number;
|
|
13
|
+
readonly pool: CanvasPoolOptions;
|
|
14
|
+
readonly detailPool: CanvasPoolOptions;
|
|
15
|
+
readonly cacheBytes: number;
|
|
16
|
+
}
|
|
17
|
+
/** The desktop budgets: the viewer's defaults before touch devices had their own. */
|
|
18
|
+
export declare const DESKTOP_BUDGETS: ViewerBudgets;
|
|
19
|
+
/** The decoded bytes `budgets` allow at most: 4 a pixel across both pools, and the bitmap cache. */
|
|
20
|
+
export declare function budgetBytes(budgets: ViewerBudgets): number;
|
|
21
|
+
/**
|
|
22
|
+
* The budgets for a device. On a coarse pointer the canvases and cache total at most 4 bytes per
|
|
23
|
+
* pixel × (pool + detail pool) + cache: 184 MB when the memory is unknown (iOS Safari reports none),
|
|
24
|
+
* 116 MB at 2 GB or less, 304 MB at 4 GB or more.
|
|
25
|
+
*/
|
|
26
|
+
export declare function viewerBudgets(hints: DeviceHints): ViewerBudgets;
|
|
27
|
+
/** This device's hints: a coarse main pointer, and `navigator.deviceMemory` (Chromium only). */
|
|
28
|
+
export declare function deviceHints(): DeviceHints;
|
|
29
|
+
//# sourceMappingURL=budgets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budgets.d.ts","sourceRoot":"","sources":["../../src/view/budgets.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,+GAA+G;AAC/G,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAOD,qFAAqF;AACrF,eAAO,MAAM,eAAe,EAAE,aAM7B,CAAC;AAIF,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CAyB/D;AAED,gGAAgG;AAChG,wBAAgB,WAAW,IAAI,WAAW,CAIzC"}
|