@somecat/epub-reader 0.1.0
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 +74 -0
- package/dist/core.cjs +201 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +6 -0
- package/dist/core.d.ts +6 -0
- package/dist/core.js +199 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +201 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +1144 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +33 -0
- package/dist/react.d.ts +33 -0
- package/dist/react.js +1142 -0
- package/dist/react.js.map +1 -0
- package/dist/types-Dc66KVH2.d.cts +58 -0
- package/dist/types-Dc66KVH2.d.ts +58 -0
- package/dist/vue.cjs +0 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +107 -0
- package/dist/vue.d.ts +107 -0
- package/dist/vue.js +1700 -0
- package/dist/vue.js.map +1 -0
- package/package.json +73 -0
- package/style.css +482 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('foliate-js/view.js');
|
|
4
|
+
|
|
5
|
+
// src/core/reader.ts
|
|
6
|
+
var getContentCSS = (fontSize, isDark, extraCSS) => `
|
|
7
|
+
@namespace epub "http://www.idpf.org/2007/ops";
|
|
8
|
+
html {
|
|
9
|
+
color-scheme: ${isDark ? "dark" : "light"} !important;
|
|
10
|
+
}
|
|
11
|
+
body {
|
|
12
|
+
background-color: transparent !important;
|
|
13
|
+
color: ${isDark ? "#e0e0e0" : "black"} !important;
|
|
14
|
+
font-size: ${fontSize}% !important;
|
|
15
|
+
}
|
|
16
|
+
p {
|
|
17
|
+
line-height: 1.6;
|
|
18
|
+
margin-bottom: 1em;
|
|
19
|
+
}
|
|
20
|
+
a {
|
|
21
|
+
color: ${isDark ? "#64b5f6" : "#2563eb"} !important;
|
|
22
|
+
}
|
|
23
|
+
img {
|
|
24
|
+
max-width: 100%;
|
|
25
|
+
height: auto;
|
|
26
|
+
object-fit: contain;
|
|
27
|
+
${isDark ? "filter: brightness(0.8) contrast(1.2);" : ""}
|
|
28
|
+
}
|
|
29
|
+
${extraCSS ?? ""}
|
|
30
|
+
`;
|
|
31
|
+
function createEBookReader(container, options = {}) {
|
|
32
|
+
if (!container) throw new Error("container is required");
|
|
33
|
+
if (!customElements.get("foliate-view")) throw new Error("foliate-view is not defined");
|
|
34
|
+
const {
|
|
35
|
+
darkMode: initialDarkMode = false,
|
|
36
|
+
fontSize: initialFontSize = 100,
|
|
37
|
+
extraContentCSS,
|
|
38
|
+
onReady,
|
|
39
|
+
onError,
|
|
40
|
+
onProgress,
|
|
41
|
+
onToc,
|
|
42
|
+
onSearchProgress,
|
|
43
|
+
onContentLoad
|
|
44
|
+
} = options;
|
|
45
|
+
let destroyed = false;
|
|
46
|
+
let toc = [];
|
|
47
|
+
let fontSize = initialFontSize;
|
|
48
|
+
let darkMode = initialDarkMode;
|
|
49
|
+
let searchToken = 0;
|
|
50
|
+
container.innerHTML = "";
|
|
51
|
+
const viewer = document.createElement("foliate-view");
|
|
52
|
+
viewer.style.display = "block";
|
|
53
|
+
viewer.style.width = "100%";
|
|
54
|
+
viewer.style.height = "100%";
|
|
55
|
+
viewer.setAttribute("margin", "48");
|
|
56
|
+
viewer.setAttribute("gap", "0.07");
|
|
57
|
+
const applyStyles = () => {
|
|
58
|
+
if (destroyed) return;
|
|
59
|
+
if (!viewer.renderer?.setStyles) return;
|
|
60
|
+
viewer.renderer.setStyles(getContentCSS(fontSize, darkMode, extraContentCSS));
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
if (destroyed) return;
|
|
64
|
+
viewer.renderer?.render?.();
|
|
65
|
+
viewer.renderer?.expand?.();
|
|
66
|
+
}, 50);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const handleLoad = (e) => {
|
|
70
|
+
applyStyles();
|
|
71
|
+
const detail = e.detail;
|
|
72
|
+
if (detail?.doc) onContentLoad?.(detail.doc);
|
|
73
|
+
};
|
|
74
|
+
const handleRelocate = (e) => {
|
|
75
|
+
const detail = e.detail;
|
|
76
|
+
onProgress?.(detail);
|
|
77
|
+
};
|
|
78
|
+
viewer.addEventListener("load", handleLoad);
|
|
79
|
+
viewer.addEventListener("relocate", handleRelocate);
|
|
80
|
+
container.appendChild(viewer);
|
|
81
|
+
const handle = {
|
|
82
|
+
async open(file) {
|
|
83
|
+
if (destroyed) return;
|
|
84
|
+
if (!file) return;
|
|
85
|
+
try {
|
|
86
|
+
viewer.clearSearch?.();
|
|
87
|
+
searchToken++;
|
|
88
|
+
await viewer.open?.(file);
|
|
89
|
+
const nextToc = viewer.book?.toc ?? [];
|
|
90
|
+
toc = nextToc;
|
|
91
|
+
onToc?.(toc);
|
|
92
|
+
await viewer.init?.({ showTextStart: true });
|
|
93
|
+
applyStyles();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
onError?.(error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
destroy() {
|
|
100
|
+
if (destroyed) return;
|
|
101
|
+
destroyed = true;
|
|
102
|
+
searchToken++;
|
|
103
|
+
viewer.removeEventListener("load", handleLoad);
|
|
104
|
+
viewer.removeEventListener("relocate", handleRelocate);
|
|
105
|
+
container.innerHTML = "";
|
|
106
|
+
},
|
|
107
|
+
prevPage() {
|
|
108
|
+
viewer.goLeft?.();
|
|
109
|
+
},
|
|
110
|
+
nextPage() {
|
|
111
|
+
viewer.goRight?.();
|
|
112
|
+
},
|
|
113
|
+
prevSection() {
|
|
114
|
+
viewer.renderer?.prevSection?.();
|
|
115
|
+
},
|
|
116
|
+
nextSection() {
|
|
117
|
+
viewer.renderer?.nextSection?.();
|
|
118
|
+
},
|
|
119
|
+
goTo(target) {
|
|
120
|
+
if (!target) return;
|
|
121
|
+
viewer.goTo?.(target);
|
|
122
|
+
},
|
|
123
|
+
goToFraction(fraction) {
|
|
124
|
+
const safe = Math.min(1, Math.max(0, fraction));
|
|
125
|
+
viewer.goToFraction?.(safe);
|
|
126
|
+
},
|
|
127
|
+
setDarkMode(nextDarkMode) {
|
|
128
|
+
darkMode = nextDarkMode;
|
|
129
|
+
applyStyles();
|
|
130
|
+
},
|
|
131
|
+
setFontSize(nextFontSize) {
|
|
132
|
+
const safe = Math.min(300, Math.max(50, nextFontSize));
|
|
133
|
+
fontSize = safe;
|
|
134
|
+
applyStyles();
|
|
135
|
+
},
|
|
136
|
+
async search(query, opts = {}) {
|
|
137
|
+
const normalized = query.trim();
|
|
138
|
+
if (!normalized) {
|
|
139
|
+
viewer.clearSearch?.();
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const token = ++searchToken;
|
|
143
|
+
const results = [];
|
|
144
|
+
try {
|
|
145
|
+
for await (const item of viewer.search?.({
|
|
146
|
+
query: normalized,
|
|
147
|
+
matchCase: Boolean(opts.matchCase),
|
|
148
|
+
matchWholeWords: Boolean(opts.wholeWords),
|
|
149
|
+
matchDiacritics: Boolean(opts.matchDiacritics)
|
|
150
|
+
}) ?? []) {
|
|
151
|
+
if (destroyed || token !== searchToken) return results;
|
|
152
|
+
if (item === "done") {
|
|
153
|
+
onSearchProgress?.({ done: true, progress: 1 });
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (typeof item === "object" && item && "progress" in item) {
|
|
157
|
+
const progress = item.progress;
|
|
158
|
+
if (typeof progress === "number") onSearchProgress?.({ progress });
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const anyItem = item;
|
|
162
|
+
if (anyItem?.subitems?.length) {
|
|
163
|
+
for (const sub of anyItem.subitems) {
|
|
164
|
+
results.push({
|
|
165
|
+
label: anyItem.label,
|
|
166
|
+
cfi: sub?.cfi,
|
|
167
|
+
excerpt: sub?.excerpt,
|
|
168
|
+
title: sub?.title
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
} else if (anyItem?.cfi) {
|
|
172
|
+
results.push({
|
|
173
|
+
cfi: anyItem.cfi,
|
|
174
|
+
excerpt: anyItem.excerpt,
|
|
175
|
+
title: anyItem.title
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
onError?.(error);
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
return results;
|
|
184
|
+
},
|
|
185
|
+
cancelSearch() {
|
|
186
|
+
searchToken++;
|
|
187
|
+
},
|
|
188
|
+
clearSearch() {
|
|
189
|
+
viewer.clearSearch?.();
|
|
190
|
+
},
|
|
191
|
+
getToc() {
|
|
192
|
+
return toc;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
onReady?.(handle);
|
|
196
|
+
return handle;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
exports.createEBookReader = createEBookReader;
|
|
200
|
+
//# sourceMappingURL=index.cjs.map
|
|
201
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/reader.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import 'foliate-js/view.js'\r\nimport type { EBookReaderHandle, EBookReaderOptions, ProgressInfo, SearchOptions, SearchResult, TocItem } from './types.js'\r\n\r\ntype FoliateViewElement = HTMLElement & {\r\n book?: { toc?: TocItem[] }\r\n renderer?: {\r\n prevSection?: () => void\r\n nextSection?: () => void\r\n setStyles?: (css: string) => void\r\n render?: () => void\r\n expand?: () => void\r\n }\r\n open?: (file: File) => Promise<void>\r\n init?: (options?: unknown) => Promise<void>\r\n goLeft?: () => void\r\n goRight?: () => void\r\n goTo?: (target: string) => Promise<void> | void\r\n goToFraction?: (fraction: number) => Promise<void> | void\r\n search?: (options: unknown) => AsyncIterable<unknown>\r\n clearSearch?: () => void\r\n}\r\n\r\nconst getContentCSS = (fontSize: number, isDark: boolean, extraCSS?: string) => `\r\n@namespace epub \"http://www.idpf.org/2007/ops\";\r\nhtml {\r\n color-scheme: ${isDark ? 'dark' : 'light'} !important;\r\n}\r\nbody {\r\n background-color: transparent !important;\r\n color: ${isDark ? '#e0e0e0' : 'black'} !important;\r\n font-size: ${fontSize}% !important;\r\n}\r\np {\r\n line-height: 1.6;\r\n margin-bottom: 1em;\r\n}\r\na {\r\n color: ${isDark ? '#64b5f6' : '#2563eb'} !important;\r\n}\r\nimg {\r\n max-width: 100%;\r\n height: auto;\r\n object-fit: contain;\r\n ${isDark ? 'filter: brightness(0.8) contrast(1.2);' : ''}\r\n}\r\n${extraCSS ?? ''}\r\n`\r\n\r\nexport function createEBookReader(container: HTMLElement, options: EBookReaderOptions = {}): EBookReaderHandle {\r\n if (!container) throw new Error('container is required')\r\n if (!customElements.get('foliate-view')) throw new Error('foliate-view is not defined')\r\n\r\n const {\r\n darkMode: initialDarkMode = false,\r\n fontSize: initialFontSize = 100,\r\n extraContentCSS,\r\n onReady,\r\n onError,\r\n onProgress,\r\n onToc,\r\n onSearchProgress,\r\n onContentLoad,\r\n } = options\r\n\r\n let destroyed = false\r\n let toc: TocItem[] = []\r\n let fontSize = initialFontSize\r\n let darkMode = initialDarkMode\r\n let searchToken = 0\r\n\r\n container.innerHTML = ''\r\n\r\n const viewer = document.createElement('foliate-view') as FoliateViewElement\r\n viewer.style.display = 'block'\r\n viewer.style.width = '100%'\r\n viewer.style.height = '100%'\r\n viewer.setAttribute('margin', '48')\r\n viewer.setAttribute('gap', '0.07')\r\n\r\n const applyStyles = () => {\r\n if (destroyed) return\r\n if (!viewer.renderer?.setStyles) return\r\n viewer.renderer.setStyles(getContentCSS(fontSize, darkMode, extraContentCSS))\r\n requestAnimationFrame(() => {\r\n setTimeout(() => {\r\n if (destroyed) return\r\n viewer.renderer?.render?.()\r\n viewer.renderer?.expand?.()\r\n }, 50)\r\n })\r\n }\r\n\r\n const handleLoad = (e: Event) => {\r\n applyStyles()\r\n const detail = (e as CustomEvent).detail as { doc?: Document } | undefined\r\n if (detail?.doc) onContentLoad?.(detail.doc)\r\n }\r\n\r\n const handleRelocate = (e: Event) => {\r\n const detail = (e as CustomEvent).detail as ProgressInfo\r\n onProgress?.(detail)\r\n }\r\n\r\n viewer.addEventListener('load', handleLoad as EventListener)\r\n viewer.addEventListener('relocate', handleRelocate as EventListener)\r\n\r\n container.appendChild(viewer)\r\n\r\n const handle: EBookReaderHandle = {\r\n async open(file) {\r\n if (destroyed) return\r\n if (!file) return\r\n\r\n try {\r\n viewer.clearSearch?.()\r\n searchToken++\r\n\r\n await viewer.open?.(file)\r\n\r\n const nextToc = viewer.book?.toc ?? []\r\n toc = nextToc\r\n onToc?.(toc)\r\n\r\n await viewer.init?.({ showTextStart: true })\r\n applyStyles()\r\n } catch (error) {\r\n onError?.(error)\r\n throw error\r\n }\r\n },\r\n destroy() {\r\n if (destroyed) return\r\n destroyed = true\r\n searchToken++\r\n viewer.removeEventListener('load', handleLoad)\r\n viewer.removeEventListener('relocate', handleRelocate as EventListener)\r\n container.innerHTML = ''\r\n },\r\n prevPage() {\r\n viewer.goLeft?.()\r\n },\r\n nextPage() {\r\n viewer.goRight?.()\r\n },\r\n prevSection() {\r\n viewer.renderer?.prevSection?.()\r\n },\r\n nextSection() {\r\n viewer.renderer?.nextSection?.()\r\n },\r\n goTo(target) {\r\n if (!target) return\r\n viewer.goTo?.(target)\r\n },\r\n goToFraction(fraction) {\r\n const safe = Math.min(1, Math.max(0, fraction))\r\n viewer.goToFraction?.(safe)\r\n },\r\n setDarkMode(nextDarkMode) {\r\n darkMode = nextDarkMode\r\n applyStyles()\r\n },\r\n setFontSize(nextFontSize) {\r\n const safe = Math.min(300, Math.max(50, nextFontSize))\r\n fontSize = safe\r\n applyStyles()\r\n },\r\n async search(query, opts: SearchOptions = {}) {\r\n const normalized = query.trim()\r\n if (!normalized) {\r\n viewer.clearSearch?.()\r\n return []\r\n }\r\n\r\n const token = ++searchToken\r\n const results: SearchResult[] = []\r\n\r\n try {\r\n for await (const item of viewer.search?.({\r\n query: normalized,\r\n matchCase: Boolean(opts.matchCase),\r\n matchWholeWords: Boolean(opts.wholeWords),\r\n matchDiacritics: Boolean(opts.matchDiacritics),\r\n }) ?? []) {\r\n if (destroyed || token !== searchToken) return results\r\n\r\n if (item === 'done') {\r\n onSearchProgress?.({ done: true, progress: 1 })\r\n break\r\n }\r\n\r\n if (typeof item === 'object' && item && 'progress' in (item as any)) {\r\n const progress = (item as any).progress\r\n if (typeof progress === 'number') onSearchProgress?.({ progress })\r\n continue\r\n }\r\n\r\n const anyItem = item as any\r\n if (anyItem?.subitems?.length) {\r\n for (const sub of anyItem.subitems) {\r\n results.push({\r\n label: anyItem.label,\r\n cfi: sub?.cfi,\r\n excerpt: sub?.excerpt,\r\n title: sub?.title,\r\n })\r\n }\r\n } else if (anyItem?.cfi) {\r\n results.push({\r\n cfi: anyItem.cfi,\r\n excerpt: anyItem.excerpt,\r\n title: anyItem.title,\r\n })\r\n }\r\n }\r\n } catch (error) {\r\n onError?.(error)\r\n throw error\r\n }\r\n\r\n return results\r\n },\r\n cancelSearch() {\r\n searchToken++\r\n },\r\n clearSearch() {\r\n viewer.clearSearch?.()\r\n },\r\n getToc() {\r\n return toc\r\n },\r\n }\r\n\r\n onReady?.(handle)\r\n return handle\r\n}\r\n"]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import 'foliate-js/view.js';
|
|
2
|
+
|
|
3
|
+
// src/core/reader.ts
|
|
4
|
+
var getContentCSS = (fontSize, isDark, extraCSS) => `
|
|
5
|
+
@namespace epub "http://www.idpf.org/2007/ops";
|
|
6
|
+
html {
|
|
7
|
+
color-scheme: ${isDark ? "dark" : "light"} !important;
|
|
8
|
+
}
|
|
9
|
+
body {
|
|
10
|
+
background-color: transparent !important;
|
|
11
|
+
color: ${isDark ? "#e0e0e0" : "black"} !important;
|
|
12
|
+
font-size: ${fontSize}% !important;
|
|
13
|
+
}
|
|
14
|
+
p {
|
|
15
|
+
line-height: 1.6;
|
|
16
|
+
margin-bottom: 1em;
|
|
17
|
+
}
|
|
18
|
+
a {
|
|
19
|
+
color: ${isDark ? "#64b5f6" : "#2563eb"} !important;
|
|
20
|
+
}
|
|
21
|
+
img {
|
|
22
|
+
max-width: 100%;
|
|
23
|
+
height: auto;
|
|
24
|
+
object-fit: contain;
|
|
25
|
+
${isDark ? "filter: brightness(0.8) contrast(1.2);" : ""}
|
|
26
|
+
}
|
|
27
|
+
${extraCSS ?? ""}
|
|
28
|
+
`;
|
|
29
|
+
function createEBookReader(container, options = {}) {
|
|
30
|
+
if (!container) throw new Error("container is required");
|
|
31
|
+
if (!customElements.get("foliate-view")) throw new Error("foliate-view is not defined");
|
|
32
|
+
const {
|
|
33
|
+
darkMode: initialDarkMode = false,
|
|
34
|
+
fontSize: initialFontSize = 100,
|
|
35
|
+
extraContentCSS,
|
|
36
|
+
onReady,
|
|
37
|
+
onError,
|
|
38
|
+
onProgress,
|
|
39
|
+
onToc,
|
|
40
|
+
onSearchProgress,
|
|
41
|
+
onContentLoad
|
|
42
|
+
} = options;
|
|
43
|
+
let destroyed = false;
|
|
44
|
+
let toc = [];
|
|
45
|
+
let fontSize = initialFontSize;
|
|
46
|
+
let darkMode = initialDarkMode;
|
|
47
|
+
let searchToken = 0;
|
|
48
|
+
container.innerHTML = "";
|
|
49
|
+
const viewer = document.createElement("foliate-view");
|
|
50
|
+
viewer.style.display = "block";
|
|
51
|
+
viewer.style.width = "100%";
|
|
52
|
+
viewer.style.height = "100%";
|
|
53
|
+
viewer.setAttribute("margin", "48");
|
|
54
|
+
viewer.setAttribute("gap", "0.07");
|
|
55
|
+
const applyStyles = () => {
|
|
56
|
+
if (destroyed) return;
|
|
57
|
+
if (!viewer.renderer?.setStyles) return;
|
|
58
|
+
viewer.renderer.setStyles(getContentCSS(fontSize, darkMode, extraContentCSS));
|
|
59
|
+
requestAnimationFrame(() => {
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
if (destroyed) return;
|
|
62
|
+
viewer.renderer?.render?.();
|
|
63
|
+
viewer.renderer?.expand?.();
|
|
64
|
+
}, 50);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const handleLoad = (e) => {
|
|
68
|
+
applyStyles();
|
|
69
|
+
const detail = e.detail;
|
|
70
|
+
if (detail?.doc) onContentLoad?.(detail.doc);
|
|
71
|
+
};
|
|
72
|
+
const handleRelocate = (e) => {
|
|
73
|
+
const detail = e.detail;
|
|
74
|
+
onProgress?.(detail);
|
|
75
|
+
};
|
|
76
|
+
viewer.addEventListener("load", handleLoad);
|
|
77
|
+
viewer.addEventListener("relocate", handleRelocate);
|
|
78
|
+
container.appendChild(viewer);
|
|
79
|
+
const handle = {
|
|
80
|
+
async open(file) {
|
|
81
|
+
if (destroyed) return;
|
|
82
|
+
if (!file) return;
|
|
83
|
+
try {
|
|
84
|
+
viewer.clearSearch?.();
|
|
85
|
+
searchToken++;
|
|
86
|
+
await viewer.open?.(file);
|
|
87
|
+
const nextToc = viewer.book?.toc ?? [];
|
|
88
|
+
toc = nextToc;
|
|
89
|
+
onToc?.(toc);
|
|
90
|
+
await viewer.init?.({ showTextStart: true });
|
|
91
|
+
applyStyles();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
onError?.(error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
destroy() {
|
|
98
|
+
if (destroyed) return;
|
|
99
|
+
destroyed = true;
|
|
100
|
+
searchToken++;
|
|
101
|
+
viewer.removeEventListener("load", handleLoad);
|
|
102
|
+
viewer.removeEventListener("relocate", handleRelocate);
|
|
103
|
+
container.innerHTML = "";
|
|
104
|
+
},
|
|
105
|
+
prevPage() {
|
|
106
|
+
viewer.goLeft?.();
|
|
107
|
+
},
|
|
108
|
+
nextPage() {
|
|
109
|
+
viewer.goRight?.();
|
|
110
|
+
},
|
|
111
|
+
prevSection() {
|
|
112
|
+
viewer.renderer?.prevSection?.();
|
|
113
|
+
},
|
|
114
|
+
nextSection() {
|
|
115
|
+
viewer.renderer?.nextSection?.();
|
|
116
|
+
},
|
|
117
|
+
goTo(target) {
|
|
118
|
+
if (!target) return;
|
|
119
|
+
viewer.goTo?.(target);
|
|
120
|
+
},
|
|
121
|
+
goToFraction(fraction) {
|
|
122
|
+
const safe = Math.min(1, Math.max(0, fraction));
|
|
123
|
+
viewer.goToFraction?.(safe);
|
|
124
|
+
},
|
|
125
|
+
setDarkMode(nextDarkMode) {
|
|
126
|
+
darkMode = nextDarkMode;
|
|
127
|
+
applyStyles();
|
|
128
|
+
},
|
|
129
|
+
setFontSize(nextFontSize) {
|
|
130
|
+
const safe = Math.min(300, Math.max(50, nextFontSize));
|
|
131
|
+
fontSize = safe;
|
|
132
|
+
applyStyles();
|
|
133
|
+
},
|
|
134
|
+
async search(query, opts = {}) {
|
|
135
|
+
const normalized = query.trim();
|
|
136
|
+
if (!normalized) {
|
|
137
|
+
viewer.clearSearch?.();
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const token = ++searchToken;
|
|
141
|
+
const results = [];
|
|
142
|
+
try {
|
|
143
|
+
for await (const item of viewer.search?.({
|
|
144
|
+
query: normalized,
|
|
145
|
+
matchCase: Boolean(opts.matchCase),
|
|
146
|
+
matchWholeWords: Boolean(opts.wholeWords),
|
|
147
|
+
matchDiacritics: Boolean(opts.matchDiacritics)
|
|
148
|
+
}) ?? []) {
|
|
149
|
+
if (destroyed || token !== searchToken) return results;
|
|
150
|
+
if (item === "done") {
|
|
151
|
+
onSearchProgress?.({ done: true, progress: 1 });
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (typeof item === "object" && item && "progress" in item) {
|
|
155
|
+
const progress = item.progress;
|
|
156
|
+
if (typeof progress === "number") onSearchProgress?.({ progress });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const anyItem = item;
|
|
160
|
+
if (anyItem?.subitems?.length) {
|
|
161
|
+
for (const sub of anyItem.subitems) {
|
|
162
|
+
results.push({
|
|
163
|
+
label: anyItem.label,
|
|
164
|
+
cfi: sub?.cfi,
|
|
165
|
+
excerpt: sub?.excerpt,
|
|
166
|
+
title: sub?.title
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} else if (anyItem?.cfi) {
|
|
170
|
+
results.push({
|
|
171
|
+
cfi: anyItem.cfi,
|
|
172
|
+
excerpt: anyItem.excerpt,
|
|
173
|
+
title: anyItem.title
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
onError?.(error);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
return results;
|
|
182
|
+
},
|
|
183
|
+
cancelSearch() {
|
|
184
|
+
searchToken++;
|
|
185
|
+
},
|
|
186
|
+
clearSearch() {
|
|
187
|
+
viewer.clearSearch?.();
|
|
188
|
+
},
|
|
189
|
+
getToc() {
|
|
190
|
+
return toc;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
onReady?.(handle);
|
|
194
|
+
return handle;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { createEBookReader };
|
|
198
|
+
//# sourceMappingURL=index.js.map
|
|
199
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/reader.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import 'foliate-js/view.js'\r\nimport type { EBookReaderHandle, EBookReaderOptions, ProgressInfo, SearchOptions, SearchResult, TocItem } from './types.js'\r\n\r\ntype FoliateViewElement = HTMLElement & {\r\n book?: { toc?: TocItem[] }\r\n renderer?: {\r\n prevSection?: () => void\r\n nextSection?: () => void\r\n setStyles?: (css: string) => void\r\n render?: () => void\r\n expand?: () => void\r\n }\r\n open?: (file: File) => Promise<void>\r\n init?: (options?: unknown) => Promise<void>\r\n goLeft?: () => void\r\n goRight?: () => void\r\n goTo?: (target: string) => Promise<void> | void\r\n goToFraction?: (fraction: number) => Promise<void> | void\r\n search?: (options: unknown) => AsyncIterable<unknown>\r\n clearSearch?: () => void\r\n}\r\n\r\nconst getContentCSS = (fontSize: number, isDark: boolean, extraCSS?: string) => `\r\n@namespace epub \"http://www.idpf.org/2007/ops\";\r\nhtml {\r\n color-scheme: ${isDark ? 'dark' : 'light'} !important;\r\n}\r\nbody {\r\n background-color: transparent !important;\r\n color: ${isDark ? '#e0e0e0' : 'black'} !important;\r\n font-size: ${fontSize}% !important;\r\n}\r\np {\r\n line-height: 1.6;\r\n margin-bottom: 1em;\r\n}\r\na {\r\n color: ${isDark ? '#64b5f6' : '#2563eb'} !important;\r\n}\r\nimg {\r\n max-width: 100%;\r\n height: auto;\r\n object-fit: contain;\r\n ${isDark ? 'filter: brightness(0.8) contrast(1.2);' : ''}\r\n}\r\n${extraCSS ?? ''}\r\n`\r\n\r\nexport function createEBookReader(container: HTMLElement, options: EBookReaderOptions = {}): EBookReaderHandle {\r\n if (!container) throw new Error('container is required')\r\n if (!customElements.get('foliate-view')) throw new Error('foliate-view is not defined')\r\n\r\n const {\r\n darkMode: initialDarkMode = false,\r\n fontSize: initialFontSize = 100,\r\n extraContentCSS,\r\n onReady,\r\n onError,\r\n onProgress,\r\n onToc,\r\n onSearchProgress,\r\n onContentLoad,\r\n } = options\r\n\r\n let destroyed = false\r\n let toc: TocItem[] = []\r\n let fontSize = initialFontSize\r\n let darkMode = initialDarkMode\r\n let searchToken = 0\r\n\r\n container.innerHTML = ''\r\n\r\n const viewer = document.createElement('foliate-view') as FoliateViewElement\r\n viewer.style.display = 'block'\r\n viewer.style.width = '100%'\r\n viewer.style.height = '100%'\r\n viewer.setAttribute('margin', '48')\r\n viewer.setAttribute('gap', '0.07')\r\n\r\n const applyStyles = () => {\r\n if (destroyed) return\r\n if (!viewer.renderer?.setStyles) return\r\n viewer.renderer.setStyles(getContentCSS(fontSize, darkMode, extraContentCSS))\r\n requestAnimationFrame(() => {\r\n setTimeout(() => {\r\n if (destroyed) return\r\n viewer.renderer?.render?.()\r\n viewer.renderer?.expand?.()\r\n }, 50)\r\n })\r\n }\r\n\r\n const handleLoad = (e: Event) => {\r\n applyStyles()\r\n const detail = (e as CustomEvent).detail as { doc?: Document } | undefined\r\n if (detail?.doc) onContentLoad?.(detail.doc)\r\n }\r\n\r\n const handleRelocate = (e: Event) => {\r\n const detail = (e as CustomEvent).detail as ProgressInfo\r\n onProgress?.(detail)\r\n }\r\n\r\n viewer.addEventListener('load', handleLoad as EventListener)\r\n viewer.addEventListener('relocate', handleRelocate as EventListener)\r\n\r\n container.appendChild(viewer)\r\n\r\n const handle: EBookReaderHandle = {\r\n async open(file) {\r\n if (destroyed) return\r\n if (!file) return\r\n\r\n try {\r\n viewer.clearSearch?.()\r\n searchToken++\r\n\r\n await viewer.open?.(file)\r\n\r\n const nextToc = viewer.book?.toc ?? []\r\n toc = nextToc\r\n onToc?.(toc)\r\n\r\n await viewer.init?.({ showTextStart: true })\r\n applyStyles()\r\n } catch (error) {\r\n onError?.(error)\r\n throw error\r\n }\r\n },\r\n destroy() {\r\n if (destroyed) return\r\n destroyed = true\r\n searchToken++\r\n viewer.removeEventListener('load', handleLoad)\r\n viewer.removeEventListener('relocate', handleRelocate as EventListener)\r\n container.innerHTML = ''\r\n },\r\n prevPage() {\r\n viewer.goLeft?.()\r\n },\r\n nextPage() {\r\n viewer.goRight?.()\r\n },\r\n prevSection() {\r\n viewer.renderer?.prevSection?.()\r\n },\r\n nextSection() {\r\n viewer.renderer?.nextSection?.()\r\n },\r\n goTo(target) {\r\n if (!target) return\r\n viewer.goTo?.(target)\r\n },\r\n goToFraction(fraction) {\r\n const safe = Math.min(1, Math.max(0, fraction))\r\n viewer.goToFraction?.(safe)\r\n },\r\n setDarkMode(nextDarkMode) {\r\n darkMode = nextDarkMode\r\n applyStyles()\r\n },\r\n setFontSize(nextFontSize) {\r\n const safe = Math.min(300, Math.max(50, nextFontSize))\r\n fontSize = safe\r\n applyStyles()\r\n },\r\n async search(query, opts: SearchOptions = {}) {\r\n const normalized = query.trim()\r\n if (!normalized) {\r\n viewer.clearSearch?.()\r\n return []\r\n }\r\n\r\n const token = ++searchToken\r\n const results: SearchResult[] = []\r\n\r\n try {\r\n for await (const item of viewer.search?.({\r\n query: normalized,\r\n matchCase: Boolean(opts.matchCase),\r\n matchWholeWords: Boolean(opts.wholeWords),\r\n matchDiacritics: Boolean(opts.matchDiacritics),\r\n }) ?? []) {\r\n if (destroyed || token !== searchToken) return results\r\n\r\n if (item === 'done') {\r\n onSearchProgress?.({ done: true, progress: 1 })\r\n break\r\n }\r\n\r\n if (typeof item === 'object' && item && 'progress' in (item as any)) {\r\n const progress = (item as any).progress\r\n if (typeof progress === 'number') onSearchProgress?.({ progress })\r\n continue\r\n }\r\n\r\n const anyItem = item as any\r\n if (anyItem?.subitems?.length) {\r\n for (const sub of anyItem.subitems) {\r\n results.push({\r\n label: anyItem.label,\r\n cfi: sub?.cfi,\r\n excerpt: sub?.excerpt,\r\n title: sub?.title,\r\n })\r\n }\r\n } else if (anyItem?.cfi) {\r\n results.push({\r\n cfi: anyItem.cfi,\r\n excerpt: anyItem.excerpt,\r\n title: anyItem.title,\r\n })\r\n }\r\n }\r\n } catch (error) {\r\n onError?.(error)\r\n throw error\r\n }\r\n\r\n return results\r\n },\r\n cancelSearch() {\r\n searchToken++\r\n },\r\n clearSearch() {\r\n viewer.clearSearch?.()\r\n },\r\n getToc() {\r\n return toc\r\n },\r\n }\r\n\r\n onReady?.(handle)\r\n return handle\r\n}\r\n"]}
|