@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/dist/vue.js ADDED
@@ -0,0 +1,1700 @@
1
+ import { defineComponent, getCurrentInstance, ref, onMounted, nextTick, onBeforeUnmount, watch, h, createElementBlock, openBlock, createElementVNode, createVNode, toDisplayString, normalizeClass, createBlock, withKeys, createTextVNode, createCommentVNode, normalizeStyle, computed, resolveComponent, Fragment, renderList } from 'vue';
2
+ import 'foliate-js/view.js';
3
+
4
+ // src/vue/EBookReaderVue.ts
5
+ var getContentCSS = (fontSize, isDark, extraCSS) => `
6
+ @namespace epub "http://www.idpf.org/2007/ops";
7
+ html {
8
+ color-scheme: ${isDark ? "dark" : "light"} !important;
9
+ }
10
+ body {
11
+ background-color: transparent !important;
12
+ color: ${isDark ? "#e0e0e0" : "black"} !important;
13
+ font-size: ${fontSize}% !important;
14
+ }
15
+ p {
16
+ line-height: 1.6;
17
+ margin-bottom: 1em;
18
+ }
19
+ a {
20
+ color: ${isDark ? "#64b5f6" : "#2563eb"} !important;
21
+ }
22
+ img {
23
+ max-width: 100%;
24
+ height: auto;
25
+ object-fit: contain;
26
+ ${isDark ? "filter: brightness(0.8) contrast(1.2);" : ""}
27
+ }
28
+ ${extraCSS ?? ""}
29
+ `;
30
+ function createEBookReader(container, options = {}) {
31
+ if (!container) throw new Error("container is required");
32
+ if (!customElements.get("foliate-view")) throw new Error("foliate-view is not defined");
33
+ const {
34
+ darkMode: initialDarkMode = false,
35
+ fontSize: initialFontSize = 100,
36
+ extraContentCSS,
37
+ onReady,
38
+ onError,
39
+ onProgress,
40
+ onToc,
41
+ onSearchProgress,
42
+ onContentLoad
43
+ } = options;
44
+ let destroyed = false;
45
+ let toc = [];
46
+ let fontSize = initialFontSize;
47
+ let darkMode = initialDarkMode;
48
+ let searchToken = 0;
49
+ container.innerHTML = "";
50
+ const viewer = document.createElement("foliate-view");
51
+ viewer.style.display = "block";
52
+ viewer.style.width = "100%";
53
+ viewer.style.height = "100%";
54
+ viewer.setAttribute("margin", "48");
55
+ viewer.setAttribute("gap", "0.07");
56
+ const applyStyles = () => {
57
+ if (destroyed) return;
58
+ if (!viewer.renderer?.setStyles) return;
59
+ viewer.renderer.setStyles(getContentCSS(fontSize, darkMode, extraContentCSS));
60
+ requestAnimationFrame(() => {
61
+ setTimeout(() => {
62
+ if (destroyed) return;
63
+ viewer.renderer?.render?.();
64
+ viewer.renderer?.expand?.();
65
+ }, 50);
66
+ });
67
+ };
68
+ const handleLoad = (e) => {
69
+ applyStyles();
70
+ const detail = e.detail;
71
+ if (detail?.doc) onContentLoad?.(detail.doc);
72
+ };
73
+ const handleRelocate = (e) => {
74
+ const detail = e.detail;
75
+ onProgress?.(detail);
76
+ };
77
+ viewer.addEventListener("load", handleLoad);
78
+ viewer.addEventListener("relocate", handleRelocate);
79
+ container.appendChild(viewer);
80
+ const handle = {
81
+ async open(file) {
82
+ if (destroyed) return;
83
+ if (!file) return;
84
+ try {
85
+ viewer.clearSearch?.();
86
+ searchToken++;
87
+ await viewer.open?.(file);
88
+ const nextToc = viewer.book?.toc ?? [];
89
+ toc = nextToc;
90
+ onToc?.(toc);
91
+ await viewer.init?.({ showTextStart: true });
92
+ applyStyles();
93
+ } catch (error) {
94
+ onError?.(error);
95
+ throw error;
96
+ }
97
+ },
98
+ destroy() {
99
+ if (destroyed) return;
100
+ destroyed = true;
101
+ searchToken++;
102
+ viewer.removeEventListener("load", handleLoad);
103
+ viewer.removeEventListener("relocate", handleRelocate);
104
+ container.innerHTML = "";
105
+ },
106
+ prevPage() {
107
+ viewer.goLeft?.();
108
+ },
109
+ nextPage() {
110
+ viewer.goRight?.();
111
+ },
112
+ prevSection() {
113
+ viewer.renderer?.prevSection?.();
114
+ },
115
+ nextSection() {
116
+ viewer.renderer?.nextSection?.();
117
+ },
118
+ goTo(target) {
119
+ if (!target) return;
120
+ viewer.goTo?.(target);
121
+ },
122
+ goToFraction(fraction) {
123
+ const safe = Math.min(1, Math.max(0, fraction));
124
+ viewer.goToFraction?.(safe);
125
+ },
126
+ setDarkMode(nextDarkMode) {
127
+ darkMode = nextDarkMode;
128
+ applyStyles();
129
+ },
130
+ setFontSize(nextFontSize) {
131
+ const safe = Math.min(300, Math.max(50, nextFontSize));
132
+ fontSize = safe;
133
+ applyStyles();
134
+ },
135
+ async search(query, opts = {}) {
136
+ const normalized = query.trim();
137
+ if (!normalized) {
138
+ viewer.clearSearch?.();
139
+ return [];
140
+ }
141
+ const token = ++searchToken;
142
+ const results = [];
143
+ try {
144
+ for await (const item of viewer.search?.({
145
+ query: normalized,
146
+ matchCase: Boolean(opts.matchCase),
147
+ matchWholeWords: Boolean(opts.wholeWords),
148
+ matchDiacritics: Boolean(opts.matchDiacritics)
149
+ }) ?? []) {
150
+ if (destroyed || token !== searchToken) return results;
151
+ if (item === "done") {
152
+ onSearchProgress?.({ done: true, progress: 1 });
153
+ break;
154
+ }
155
+ if (typeof item === "object" && item && "progress" in item) {
156
+ const progress = item.progress;
157
+ if (typeof progress === "number") onSearchProgress?.({ progress });
158
+ continue;
159
+ }
160
+ const anyItem = item;
161
+ if (anyItem?.subitems?.length) {
162
+ for (const sub of anyItem.subitems) {
163
+ results.push({
164
+ label: anyItem.label,
165
+ cfi: sub?.cfi,
166
+ excerpt: sub?.excerpt,
167
+ title: sub?.title
168
+ });
169
+ }
170
+ } else if (anyItem?.cfi) {
171
+ results.push({
172
+ cfi: anyItem.cfi,
173
+ excerpt: anyItem.excerpt,
174
+ title: anyItem.title
175
+ });
176
+ }
177
+ }
178
+ } catch (error) {
179
+ onError?.(error);
180
+ throw error;
181
+ }
182
+ return results;
183
+ },
184
+ cancelSearch() {
185
+ searchToken++;
186
+ },
187
+ clearSearch() {
188
+ viewer.clearSearch?.();
189
+ },
190
+ getToc() {
191
+ return toc;
192
+ }
193
+ };
194
+ onReady?.(handle);
195
+ return handle;
196
+ }
197
+
198
+ // src/vue/types.ts
199
+ var EBookReaderVuePropsDef = {
200
+ file: { type: Object, required: false },
201
+ defaultFontSize: { type: Number, required: false, default: 100 },
202
+ fontSize: { type: Number, required: false },
203
+ defaultDarkMode: { type: Boolean, required: false, default: false },
204
+ darkMode: { type: Boolean, required: false },
205
+ enableKeyboardNav: { type: Boolean, required: false, default: true },
206
+ defaultSearchOptions: {
207
+ type: Object,
208
+ required: false,
209
+ default: () => ({ matchCase: false, wholeWords: false, matchDiacritics: false })
210
+ }
211
+ };
212
+
213
+ // src/core/icons.ts
214
+ var icons = {
215
+ list: '<path d="M3 12h18M3 6h18M3 18h18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
216
+ search: '<path d="M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.35-4.35" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
217
+ "chevron-left": '<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
218
+ "chevron-right": '<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
219
+ "chevrons-left": '<path d="M11 17l-5-5 5-5M18 17l-5-5 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
220
+ "chevrons-right": '<path d="M13 17l5-5-5-5M6 17l5-5-5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
221
+ sun: '<circle cx="12" cy="12" r="5" stroke="currentColor" stroke-width="2"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
222
+ moon: '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
223
+ plus: '<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
224
+ minus: '<path d="M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
225
+ x: '<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
226
+ type: '<path d="M4 7V4h16v3M9 20h6M12 4v16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
227
+ sliders: '<path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
228
+ };
229
+
230
+ // unplugin-vue:/plugin-vue/export-helper
231
+ var export_helper_default = (sfc, props) => {
232
+ const target = sfc.__vccOpts || sfc;
233
+ for (const [key, val] of props) {
234
+ target[key] = val;
235
+ }
236
+ return target;
237
+ };
238
+
239
+ // src/vue/components/SvgIcon.vue
240
+ var _sfc_main = /* @__PURE__ */ defineComponent({
241
+ __name: "SvgIcon",
242
+ props: {
243
+ name: {
244
+ type: String,
245
+ required: true
246
+ },
247
+ size: {
248
+ type: [Number, String],
249
+ required: false,
250
+ default: 24
251
+ },
252
+ color: {
253
+ type: String,
254
+ required: false,
255
+ default: "currentColor"
256
+ }
257
+ },
258
+ setup(__props, { expose: __expose }) {
259
+ __expose();
260
+ const props = __props;
261
+ const iconPath = computed(() => icons[props.name] || "");
262
+ const iconStyle = computed(() => {
263
+ const sizeVal = typeof props.size === "number" ? `${props.size}px` : props.size;
264
+ return {
265
+ width: sizeVal,
266
+ height: sizeVal,
267
+ color: props.color,
268
+ minWidth: sizeVal
269
+ };
270
+ });
271
+ const __returned__ = {
272
+ props,
273
+ iconPath,
274
+ iconStyle
275
+ };
276
+ Object.defineProperty(__returned__, "__isScriptSetup", {
277
+ enumerable: false,
278
+ value: true
279
+ });
280
+ return __returned__;
281
+ }
282
+ });
283
+ var _hoisted_1 = ["innerHTML"];
284
+ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
285
+ return $setup.iconPath ? (openBlock(), createElementBlock("svg", {
286
+ key: 0,
287
+ xmlns: "http://www.w3.org/2000/svg",
288
+ viewBox: "0 0 24 24",
289
+ fill: "none",
290
+ style: normalizeStyle($setup.iconStyle),
291
+ class: "ebook-reader-icon",
292
+ innerHTML: $setup.iconPath
293
+ }, null, 12, _hoisted_1)) : createCommentVNode("v-if", true);
294
+ }
295
+ var SvgIcon_default = /* @__PURE__ */ export_helper_default(_sfc_main, [["render", _sfc_render], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\SvgIcon.vue"]]);
296
+ var _sfc_main2 = /* @__PURE__ */ defineComponent({
297
+ __name: "DesktopToolbar",
298
+ props: {
299
+ darkMode: {
300
+ type: Boolean,
301
+ required: true
302
+ },
303
+ fontSize: {
304
+ type: Number,
305
+ required: true
306
+ }
307
+ },
308
+ emits: [
309
+ "toggleToc",
310
+ "toggleSearch",
311
+ "prevSection",
312
+ "prevPage",
313
+ "nextPage",
314
+ "nextSection",
315
+ "toggleDarkMode",
316
+ "changeFontSize"
317
+ ],
318
+ setup(__props, { expose: __expose, emit: __emit }) {
319
+ __expose();
320
+ const emit = __emit;
321
+ const __returned__ = {
322
+ emit,
323
+ SvgIcon: SvgIcon_default
324
+ };
325
+ Object.defineProperty(__returned__, "__isScriptSetup", {
326
+ enumerable: false,
327
+ value: true
328
+ });
329
+ return __returned__;
330
+ }
331
+ });
332
+ var _hoisted_12 = { class: "ebook-reader__toolbar" };
333
+ var _hoisted_2 = { class: "ebook-reader__panel" };
334
+ var _hoisted_3 = { class: "ebook-reader__panel" };
335
+ var _hoisted_4 = { class: "ebook-reader__font" };
336
+ function _sfc_render2(_ctx, _cache, $props, $setup, $data, $options) {
337
+ return openBlock(), createElementBlock("div", _hoisted_12, [createElementVNode("div", _hoisted_2, [
338
+ createElementVNode("button", {
339
+ type: "button",
340
+ class: "ebook-reader__btn",
341
+ title: "\u76EE\u5F55",
342
+ onClick: _cache[0] || (_cache[0] = ($event) => $setup.emit("toggleToc"))
343
+ }, [createVNode($setup["SvgIcon"], { name: "list" })]),
344
+ createElementVNode("button", {
345
+ type: "button",
346
+ class: "ebook-reader__btn",
347
+ title: "\u641C\u7D22",
348
+ onClick: _cache[1] || (_cache[1] = ($event) => $setup.emit("toggleSearch"))
349
+ }, [createVNode($setup["SvgIcon"], { name: "search" })]),
350
+ _cache[9] || (_cache[9] = createElementVNode(
351
+ "div",
352
+ { class: "ebook-reader__divider" },
353
+ null,
354
+ -1
355
+ /* CACHED */
356
+ )),
357
+ createElementVNode("button", {
358
+ type: "button",
359
+ class: "ebook-reader__btn",
360
+ title: "\u4E0A\u4E00\u7AE0",
361
+ onClick: _cache[2] || (_cache[2] = ($event) => $setup.emit("prevSection"))
362
+ }, [createVNode($setup["SvgIcon"], { name: "chevrons-left" })]),
363
+ createElementVNode("button", {
364
+ type: "button",
365
+ class: "ebook-reader__btn",
366
+ title: "\u4E0A\u4E00\u9875",
367
+ onClick: _cache[3] || (_cache[3] = ($event) => $setup.emit("prevPage"))
368
+ }, [createVNode($setup["SvgIcon"], { name: "chevron-left" })]),
369
+ createElementVNode("button", {
370
+ type: "button",
371
+ class: "ebook-reader__btn",
372
+ title: "\u4E0B\u4E00\u9875",
373
+ onClick: _cache[4] || (_cache[4] = ($event) => $setup.emit("nextPage"))
374
+ }, [createVNode($setup["SvgIcon"], { name: "chevron-right" })]),
375
+ createElementVNode("button", {
376
+ type: "button",
377
+ class: "ebook-reader__btn",
378
+ title: "\u4E0B\u4E00\u7AE0",
379
+ onClick: _cache[5] || (_cache[5] = ($event) => $setup.emit("nextSection"))
380
+ }, [createVNode($setup["SvgIcon"], { name: "chevrons-right" })])
381
+ ]), createElementVNode("div", _hoisted_3, [
382
+ createElementVNode("button", {
383
+ type: "button",
384
+ class: "ebook-reader__btn",
385
+ title: "\u4E3B\u9898",
386
+ onClick: _cache[6] || (_cache[6] = ($event) => $setup.emit("toggleDarkMode"))
387
+ }, [createVNode($setup["SvgIcon"], { name: $props.darkMode ? "sun" : "moon" }, null, 8, ["name"])]),
388
+ _cache[10] || (_cache[10] = createElementVNode(
389
+ "div",
390
+ { class: "ebook-reader__divider" },
391
+ null,
392
+ -1
393
+ /* CACHED */
394
+ )),
395
+ createElementVNode("button", {
396
+ type: "button",
397
+ class: "ebook-reader__btn",
398
+ title: "\u589E\u5927\u5B57\u53F7",
399
+ onClick: _cache[7] || (_cache[7] = ($event) => $setup.emit("changeFontSize", $props.fontSize + 10))
400
+ }, [createVNode($setup["SvgIcon"], { name: "plus" })]),
401
+ createElementVNode(
402
+ "div",
403
+ _hoisted_4,
404
+ toDisplayString($props.fontSize) + "%",
405
+ 1
406
+ /* TEXT */
407
+ ),
408
+ createElementVNode("button", {
409
+ type: "button",
410
+ class: "ebook-reader__btn",
411
+ title: "\u51CF\u5C0F\u5B57\u53F7",
412
+ onClick: _cache[8] || (_cache[8] = ($event) => $setup.emit("changeFontSize", $props.fontSize - 10))
413
+ }, [createVNode($setup["SvgIcon"], { name: "minus" })])
414
+ ])]);
415
+ }
416
+ var DesktopToolbar_default = /* @__PURE__ */ export_helper_default(_sfc_main2, [["render", _sfc_render2], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\DesktopToolbar.vue"]]);
417
+ var _sfc_main3 = /* @__PURE__ */ defineComponent({
418
+ __name: "DesktopBottomBar",
419
+ props: {
420
+ status: {
421
+ type: String,
422
+ required: true
423
+ },
424
+ errorText: {
425
+ type: String,
426
+ required: true
427
+ },
428
+ sectionLabel: {
429
+ type: String,
430
+ required: true
431
+ },
432
+ displayedPercent: {
433
+ type: Number,
434
+ required: true
435
+ }
436
+ },
437
+ emits: [
438
+ "seekStart",
439
+ "seekChange",
440
+ "seekEnd",
441
+ "seekCommit"
442
+ ],
443
+ setup(__props, { expose: __expose, emit: __emit }) {
444
+ __expose();
445
+ const emit = __emit;
446
+ const __returned__ = { emit };
447
+ Object.defineProperty(__returned__, "__isScriptSetup", {
448
+ enumerable: false,
449
+ value: true
450
+ });
451
+ return __returned__;
452
+ }
453
+ });
454
+ var _hoisted_13 = { class: "ebook-reader__bottom" };
455
+ var _hoisted_22 = { class: "ebook-reader__bottom-left" };
456
+ var _hoisted_32 = { class: "ebook-reader__status" };
457
+ var _hoisted_42 = {
458
+ key: 0,
459
+ class: "ebook-reader__section"
460
+ };
461
+ var _hoisted_5 = { class: "ebook-reader__bottom-right" };
462
+ var _hoisted_6 = ["value"];
463
+ var _hoisted_7 = { class: "ebook-reader__percent" };
464
+ function _sfc_render3(_ctx, _cache, $props, $setup, $data, $options) {
465
+ return openBlock(), createElementBlock("div", _hoisted_13, [createElementVNode("div", _hoisted_22, [createElementVNode(
466
+ "span",
467
+ _hoisted_32,
468
+ toDisplayString($props.status === "error" ? $props.errorText || "\u9519\u8BEF" : $props.status === "opening" ? "\u6B63\u5728\u6253\u5F00\u2026" : "\u5C31\u7EEA"),
469
+ 1
470
+ /* TEXT */
471
+ ), $props.sectionLabel ? (openBlock(), createElementBlock(
472
+ "span",
473
+ _hoisted_42,
474
+ toDisplayString($props.sectionLabel),
475
+ 1
476
+ /* TEXT */
477
+ )) : createCommentVNode("v-if", true)]), createElementVNode("div", _hoisted_5, [createElementVNode("input", {
478
+ class: "ebook-reader__range",
479
+ type: "range",
480
+ min: 0,
481
+ max: 100,
482
+ step: 1,
483
+ value: $props.displayedPercent,
484
+ onInput: _cache[0] || (_cache[0] = (e) => {
485
+ $setup.emit("seekStart");
486
+ $setup.emit("seekChange", Number(e.target.value));
487
+ }),
488
+ onPointerup: _cache[1] || (_cache[1] = (e) => $setup.emit("seekEnd", Number(e.target.value))),
489
+ onKeyup: _cache[2] || (_cache[2] = withKeys((e) => $setup.emit("seekCommit", Number(e.target.value)), ["enter"]))
490
+ }, null, 40, _hoisted_6), createElementVNode(
491
+ "span",
492
+ _hoisted_7,
493
+ toDisplayString($props.displayedPercent) + "%",
494
+ 1
495
+ /* TEXT */
496
+ )])]);
497
+ }
498
+ var DesktopBottomBar_default = /* @__PURE__ */ export_helper_default(_sfc_main3, [["render", _sfc_render3], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\DesktopBottomBar.vue"]]);
499
+ var _sfc_main4 = /* @__PURE__ */ defineComponent({
500
+ __name: "TocTree",
501
+ props: { items: {
502
+ type: Array,
503
+ required: true
504
+ } },
505
+ emits: ["select"],
506
+ setup(__props, { expose: __expose, emit: __emit }) {
507
+ __expose();
508
+ const emit = __emit;
509
+ const __returned__ = { emit };
510
+ Object.defineProperty(__returned__, "__isScriptSetup", {
511
+ enumerable: false,
512
+ value: true
513
+ });
514
+ return __returned__;
515
+ }
516
+ });
517
+ var _hoisted_14 = { class: "ebook-reader__toc-list" };
518
+ var _hoisted_23 = ["onClick"];
519
+ var _hoisted_33 = {
520
+ key: 1,
521
+ class: "ebook-reader__toc-details"
522
+ };
523
+ var _hoisted_43 = { class: "ebook-reader__toc-summary" };
524
+ function _sfc_render4(_ctx, _cache, $props, $setup, $data, $options) {
525
+ const _component_TocTree = resolveComponent("TocTree", true);
526
+ return openBlock(), createElementBlock("ul", _hoisted_14, [(openBlock(true), createElementBlock(
527
+ Fragment,
528
+ null,
529
+ renderList($props.items, (item, idx) => {
530
+ return openBlock(), createElementBlock("li", {
531
+ key: item.href || `${item.label ?? "item"}-${idx}`,
532
+ class: "ebook-reader__toc-item"
533
+ }, [!item.subitems?.length ? (openBlock(), createElementBlock("button", {
534
+ key: 0,
535
+ type: "button",
536
+ class: "ebook-reader__toc-btn",
537
+ onClick: ($event) => $setup.emit("select", item.href)
538
+ }, toDisplayString(item.label || item.href || "\u672A\u547D\u540D"), 9, _hoisted_23)) : (openBlock(), createElementBlock("details", _hoisted_33, [createElementVNode(
539
+ "summary",
540
+ _hoisted_43,
541
+ toDisplayString(item.label || item.href || "\u672A\u547D\u540D"),
542
+ 1
543
+ /* TEXT */
544
+ ), createVNode(_component_TocTree, {
545
+ items: item.subitems,
546
+ onSelect: _cache[0] || (_cache[0] = ($event) => $setup.emit("select", $event))
547
+ }, null, 8, ["items"])]))]);
548
+ }),
549
+ 128
550
+ /* KEYED_FRAGMENT */
551
+ ))]);
552
+ }
553
+ var TocTree_default = /* @__PURE__ */ export_helper_default(_sfc_main4, [["render", _sfc_render4], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\TocTree.vue"]]);
554
+ var _sfc_main5 = /* @__PURE__ */ defineComponent({
555
+ __name: "TocDrawer",
556
+ props: {
557
+ isOpen: {
558
+ type: Boolean,
559
+ required: true
560
+ },
561
+ toc: {
562
+ type: Array,
563
+ required: true
564
+ }
565
+ },
566
+ emits: ["close", "select"],
567
+ setup(__props, { expose: __expose, emit: __emit }) {
568
+ __expose();
569
+ const emit = __emit;
570
+ const __returned__ = {
571
+ emit,
572
+ TocTree: TocTree_default,
573
+ SvgIcon: SvgIcon_default
574
+ };
575
+ Object.defineProperty(__returned__, "__isScriptSetup", {
576
+ enumerable: false,
577
+ value: true
578
+ });
579
+ return __returned__;
580
+ }
581
+ });
582
+ var _hoisted_15 = ["aria-hidden"];
583
+ var _hoisted_24 = { class: "ebook-reader__drawer-header" };
584
+ var _hoisted_34 = { class: "ebook-reader__drawer-body" };
585
+ var _hoisted_44 = {
586
+ key: 1,
587
+ class: "ebook-reader__empty"
588
+ };
589
+ function _sfc_render5(_ctx, _cache, $props, $setup, $data, $options) {
590
+ return openBlock(), createElementBlock("aside", {
591
+ class: normalizeClass(["ebook-reader__drawer", { "is-open": $props.isOpen }]),
592
+ "aria-hidden": !$props.isOpen
593
+ }, [createElementVNode("div", _hoisted_24, [_cache[2] || (_cache[2] = createElementVNode(
594
+ "div",
595
+ { class: "ebook-reader__drawer-title" },
596
+ "\u76EE\u5F55",
597
+ -1
598
+ /* CACHED */
599
+ )), createElementVNode("button", {
600
+ type: "button",
601
+ class: "ebook-reader__btn",
602
+ onClick: _cache[0] || (_cache[0] = ($event) => $setup.emit("close"))
603
+ }, [createVNode($setup["SvgIcon"], { name: "x" })])]), createElementVNode("div", _hoisted_34, [$props.toc.length ? (openBlock(), createBlock($setup["TocTree"], {
604
+ key: 0,
605
+ items: $props.toc,
606
+ onSelect: _cache[1] || (_cache[1] = (href) => {
607
+ $setup.emit("select", href);
608
+ $setup.emit("close");
609
+ })
610
+ }, null, 8, ["items"])) : (openBlock(), createElementBlock("div", _hoisted_44, "\u672A\u627E\u5230\u76EE\u5F55"))])], 10, _hoisted_15);
611
+ }
612
+ var TocDrawer_default = /* @__PURE__ */ export_helper_default(_sfc_main5, [["render", _sfc_render5], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\TocDrawer.vue"]]);
613
+ var _sfc_main6 = /* @__PURE__ */ defineComponent({
614
+ __name: "SearchResultList",
615
+ props: { results: {
616
+ type: Array,
617
+ required: true
618
+ } },
619
+ emits: ["select"],
620
+ setup(__props, { expose: __expose, emit: __emit }) {
621
+ __expose();
622
+ const emit = __emit;
623
+ const __returned__ = { emit };
624
+ Object.defineProperty(__returned__, "__isScriptSetup", {
625
+ enumerable: false,
626
+ value: true
627
+ });
628
+ return __returned__;
629
+ }
630
+ });
631
+ var _hoisted_16 = { class: "ebook-reader__search-list" };
632
+ var _hoisted_25 = ["onClick"];
633
+ var _hoisted_35 = {
634
+ key: 0,
635
+ class: "ebook-reader__search-label"
636
+ };
637
+ var _hoisted_45 = { class: "ebook-reader__search-excerpt" };
638
+ function _sfc_render6(_ctx, _cache, $props, $setup, $data, $options) {
639
+ return openBlock(), createElementBlock("ul", _hoisted_16, [(openBlock(true), createElementBlock(
640
+ Fragment,
641
+ null,
642
+ renderList($props.results, (r, idx) => {
643
+ return openBlock(), createElementBlock("li", {
644
+ key: `${r.cfi ?? "no-cfi"}-${idx}`,
645
+ class: "ebook-reader__search-item"
646
+ }, [createElementVNode("button", {
647
+ type: "button",
648
+ class: "ebook-reader__search-btn",
649
+ onClick: ($event) => r.cfi && $setup.emit("select", r.cfi)
650
+ }, [r.label ? (openBlock(), createElementBlock(
651
+ "div",
652
+ _hoisted_35,
653
+ toDisplayString(r.label),
654
+ 1
655
+ /* TEXT */
656
+ )) : createCommentVNode("v-if", true), createElementVNode(
657
+ "div",
658
+ _hoisted_45,
659
+ toDisplayString(typeof r.excerpt === "string" ? r.excerpt : `${r.excerpt?.pre ?? ""}${r.excerpt?.match ?? ""}${r.excerpt?.post ?? ""}`),
660
+ 1
661
+ /* TEXT */
662
+ )], 8, _hoisted_25)]);
663
+ }),
664
+ 128
665
+ /* KEYED_FRAGMENT */
666
+ ))]);
667
+ }
668
+ var SearchResultList_default = /* @__PURE__ */ export_helper_default(_sfc_main6, [["render", _sfc_render6], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\SearchResultList.vue"]]);
669
+ var _sfc_main7 = /* @__PURE__ */ defineComponent({
670
+ __name: "SearchDrawer",
671
+ props: {
672
+ isOpen: {
673
+ type: Boolean,
674
+ required: true
675
+ },
676
+ status: {
677
+ type: String,
678
+ required: true
679
+ },
680
+ query: {
681
+ type: String,
682
+ required: true
683
+ },
684
+ options: {
685
+ type: Object,
686
+ required: true
687
+ },
688
+ progressPercent: {
689
+ type: Number,
690
+ required: true
691
+ },
692
+ searching: {
693
+ type: Boolean,
694
+ required: true
695
+ },
696
+ results: {
697
+ type: Array,
698
+ required: true
699
+ }
700
+ },
701
+ emits: [
702
+ "close",
703
+ "search",
704
+ "update:query",
705
+ "update:options",
706
+ "cancelSearch",
707
+ "selectResult"
708
+ ],
709
+ setup(__props, { expose: __expose, emit: __emit }) {
710
+ __expose();
711
+ const emit = __emit;
712
+ const updateOption = (key, value) => {
713
+ emit("update:options", { [key]: value });
714
+ };
715
+ const __returned__ = {
716
+ emit,
717
+ updateOption,
718
+ SearchResultList: SearchResultList_default,
719
+ SvgIcon: SvgIcon_default
720
+ };
721
+ Object.defineProperty(__returned__, "__isScriptSetup", {
722
+ enumerable: false,
723
+ value: true
724
+ });
725
+ return __returned__;
726
+ }
727
+ });
728
+ var _hoisted_17 = ["aria-hidden"];
729
+ var _hoisted_26 = { class: "ebook-reader__drawer-header" };
730
+ var _hoisted_36 = { class: "ebook-reader__drawer-body" };
731
+ var _hoisted_46 = { class: "ebook-reader__field" };
732
+ var _hoisted_52 = ["value", "disabled"];
733
+ var _hoisted_62 = ["disabled"];
734
+ var _hoisted_72 = { class: "ebook-reader__checks" };
735
+ var _hoisted_8 = { class: "ebook-reader__check" };
736
+ var _hoisted_9 = ["checked"];
737
+ var _hoisted_10 = { class: "ebook-reader__check" };
738
+ var _hoisted_11 = ["checked"];
739
+ var _hoisted_122 = { class: "ebook-reader__check" };
740
+ var _hoisted_132 = ["checked"];
741
+ var _hoisted_142 = { class: "ebook-reader__meta" };
742
+ var _hoisted_152 = { key: 0 };
743
+ var _hoisted_162 = {
744
+ key: 1,
745
+ class: "ebook-reader__empty"
746
+ };
747
+ function _sfc_render7(_ctx, _cache, $props, $setup, $data, $options) {
748
+ return openBlock(), createElementBlock("aside", {
749
+ class: normalizeClass([
750
+ "ebook-reader__drawer",
751
+ "right",
752
+ { "is-open": $props.isOpen }
753
+ ]),
754
+ "aria-hidden": !$props.isOpen
755
+ }, [createElementVNode("div", _hoisted_26, [_cache[9] || (_cache[9] = createElementVNode(
756
+ "div",
757
+ { class: "ebook-reader__drawer-title" },
758
+ "\u641C\u7D22",
759
+ -1
760
+ /* CACHED */
761
+ )), createElementVNode("button", {
762
+ type: "button",
763
+ class: "ebook-reader__btn",
764
+ onClick: _cache[0] || (_cache[0] = ($event) => $setup.emit("close"))
765
+ }, [createVNode($setup["SvgIcon"], { name: "x" })])]), createElementVNode("div", _hoisted_36, [
766
+ createElementVNode("div", _hoisted_46, [createElementVNode("input", {
767
+ class: "ebook-reader__input",
768
+ placeholder: "\u8F93\u5165\u5173\u952E\u8BCD",
769
+ value: $props.query,
770
+ disabled: $props.status !== "ready",
771
+ onInput: _cache[1] || (_cache[1] = (e) => {
772
+ const v = e.target.value;
773
+ $setup.emit("update:query", v);
774
+ if (!v.trim()) $setup.emit("search", "");
775
+ }),
776
+ onKeydown: _cache[2] || (_cache[2] = withKeys(($event) => $setup.emit("search", $props.query), ["enter"]))
777
+ }, null, 40, _hoisted_52), createElementVNode("button", {
778
+ type: "button",
779
+ class: "ebook-reader__btn",
780
+ disabled: $props.status !== "ready",
781
+ onClick: _cache[3] || (_cache[3] = ($event) => $setup.emit("search", $props.query))
782
+ }, [createVNode($setup["SvgIcon"], { name: "search" })], 8, _hoisted_62)]),
783
+ createElementVNode("div", _hoisted_72, [
784
+ createElementVNode("label", _hoisted_8, [createElementVNode("input", {
785
+ type: "checkbox",
786
+ checked: Boolean($props.options.matchCase),
787
+ onChange: _cache[4] || (_cache[4] = (e) => $setup.updateOption("matchCase", e.target.checked))
788
+ }, null, 40, _hoisted_9), _cache[10] || (_cache[10] = createTextVNode(
789
+ " \u533A\u5206\u5927\u5C0F\u5199 ",
790
+ -1
791
+ /* CACHED */
792
+ ))]),
793
+ createElementVNode("label", _hoisted_10, [createElementVNode("input", {
794
+ type: "checkbox",
795
+ checked: Boolean($props.options.wholeWords),
796
+ onChange: _cache[5] || (_cache[5] = (e) => $setup.updateOption("wholeWords", e.target.checked))
797
+ }, null, 40, _hoisted_11), _cache[11] || (_cache[11] = createTextVNode(
798
+ " \u5168\u8BCD\u5339\u914D ",
799
+ -1
800
+ /* CACHED */
801
+ ))]),
802
+ createElementVNode("label", _hoisted_122, [createElementVNode("input", {
803
+ type: "checkbox",
804
+ checked: Boolean($props.options.matchDiacritics),
805
+ onChange: _cache[6] || (_cache[6] = (e) => $setup.updateOption("matchDiacritics", e.target.checked))
806
+ }, null, 40, _hoisted_132), _cache[12] || (_cache[12] = createTextVNode(
807
+ " \u533A\u5206\u53D8\u97F3 ",
808
+ -1
809
+ /* CACHED */
810
+ ))])
811
+ ]),
812
+ createElementVNode("div", _hoisted_142, [
813
+ createElementVNode(
814
+ "span",
815
+ null,
816
+ "\u8FDB\u5EA6 " + toDisplayString($props.progressPercent) + "%",
817
+ 1
818
+ /* TEXT */
819
+ ),
820
+ $props.searching ? (openBlock(), createElementBlock("span", _hoisted_152, "\u641C\u7D22\u4E2D\u2026")) : createCommentVNode("v-if", true),
821
+ $props.searching ? (openBlock(), createElementBlock("button", {
822
+ key: 1,
823
+ type: "button",
824
+ class: "ebook-reader__link",
825
+ onClick: _cache[7] || (_cache[7] = ($event) => $setup.emit("cancelSearch"))
826
+ }, " \u53D6\u6D88 ")) : createCommentVNode("v-if", true)
827
+ ]),
828
+ $props.results.length ? (openBlock(), createBlock($setup["SearchResultList"], {
829
+ key: 0,
830
+ results: $props.results,
831
+ onSelect: _cache[8] || (_cache[8] = (cfi) => $setup.emit("selectResult", cfi))
832
+ }, null, 8, ["results"])) : (openBlock(), createElementBlock(
833
+ "div",
834
+ _hoisted_162,
835
+ toDisplayString($props.query.trim() ? "\u65E0\u5339\u914D\u7ED3\u679C" : "\u8BF7\u8F93\u5165\u5173\u952E\u8BCD"),
836
+ 1
837
+ /* TEXT */
838
+ ))
839
+ ])], 10, _hoisted_17);
840
+ }
841
+ var SearchDrawer_default = /* @__PURE__ */ export_helper_default(_sfc_main7, [["render", _sfc_render7], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\SearchDrawer.vue"]]);
842
+ var _sfc_main8 = /* @__PURE__ */ defineComponent({
843
+ __name: "MobileUI",
844
+ props: {
845
+ barVisible: {
846
+ type: Boolean,
847
+ required: true
848
+ },
849
+ activePanel: {
850
+ type: [String, null],
851
+ required: true
852
+ },
853
+ toc: {
854
+ type: Array,
855
+ required: true
856
+ },
857
+ status: {
858
+ type: String,
859
+ required: true
860
+ },
861
+ errorText: {
862
+ type: String,
863
+ required: true
864
+ },
865
+ sectionLabel: {
866
+ type: String,
867
+ required: true
868
+ },
869
+ displayedPercent: {
870
+ type: Number,
871
+ required: true
872
+ },
873
+ darkMode: {
874
+ type: Boolean,
875
+ required: true
876
+ },
877
+ fontSize: {
878
+ type: Number,
879
+ required: true
880
+ },
881
+ searchQuery: {
882
+ type: String,
883
+ required: true
884
+ },
885
+ searchOptions: {
886
+ type: Object,
887
+ required: true
888
+ },
889
+ searchProgressPercent: {
890
+ type: Number,
891
+ required: true
892
+ },
893
+ searching: {
894
+ type: Boolean,
895
+ required: true
896
+ },
897
+ searchResults: {
898
+ type: Array,
899
+ required: true
900
+ }
901
+ },
902
+ emits: [
903
+ "togglePanel",
904
+ "closePanel",
905
+ "tocSelect",
906
+ "search",
907
+ "update:searchQuery",
908
+ "update:searchOptions",
909
+ "cancelSearch",
910
+ "searchResultSelect",
911
+ "seekStart",
912
+ "seekChange",
913
+ "seekEnd",
914
+ "seekCommit",
915
+ "toggleDarkMode",
916
+ "changeFontSize"
917
+ ],
918
+ setup(__props, { expose: __expose, emit: __emit }) {
919
+ __expose();
920
+ const props = __props;
921
+ const emit = __emit;
922
+ const mobileTitle = computed(() => {
923
+ switch (props.activePanel) {
924
+ case "menu":
925
+ return "\u76EE\u5F55";
926
+ case "search":
927
+ return "\u641C\u7D22";
928
+ case "progress":
929
+ return "\u8FDB\u5EA6";
930
+ case "theme":
931
+ return "\u660E\u6697";
932
+ case "font":
933
+ return "\u5B57\u53F7";
934
+ default:
935
+ return "";
936
+ }
937
+ });
938
+ const updateSearchOption = (key, value) => {
939
+ emit("update:searchOptions", { [key]: value });
940
+ };
941
+ const tooltip = ref(null);
942
+ let timer = null;
943
+ const handleTouchStart = (e, text) => {
944
+ const target = e.currentTarget;
945
+ const rect = target.getBoundingClientRect();
946
+ timer = window.setTimeout(() => {
947
+ tooltip.value = {
948
+ text,
949
+ left: rect.left + rect.width / 2
950
+ };
951
+ }, 500);
952
+ };
953
+ const handleTouchEnd = () => {
954
+ if (timer) {
955
+ clearTimeout(timer);
956
+ timer = null;
957
+ }
958
+ tooltip.value = null;
959
+ };
960
+ const __returned__ = {
961
+ props,
962
+ emit,
963
+ mobileTitle,
964
+ updateSearchOption,
965
+ tooltip,
966
+ get timer() {
967
+ return timer;
968
+ },
969
+ set timer(v) {
970
+ timer = v;
971
+ },
972
+ handleTouchStart,
973
+ handleTouchEnd,
974
+ TocTree: TocTree_default,
975
+ SearchResultList: SearchResultList_default,
976
+ SvgIcon: SvgIcon_default
977
+ };
978
+ Object.defineProperty(__returned__, "__isScriptSetup", {
979
+ enumerable: false,
980
+ value: true
981
+ });
982
+ return __returned__;
983
+ }
984
+ });
985
+ var _hoisted_18 = ["aria-pressed"];
986
+ var _hoisted_27 = ["aria-pressed"];
987
+ var _hoisted_37 = ["aria-pressed"];
988
+ var _hoisted_47 = ["aria-pressed"];
989
+ var _hoisted_53 = ["aria-pressed"];
990
+ var _hoisted_63 = ["aria-hidden"];
991
+ var _hoisted_73 = { class: "ebook-reader__msheet-header" };
992
+ var _hoisted_82 = { class: "ebook-reader__msheet-title" };
993
+ var _hoisted_92 = { class: "ebook-reader__msheet-body" };
994
+ var _hoisted_102 = {
995
+ key: 1,
996
+ class: "ebook-reader__empty"
997
+ };
998
+ var _hoisted_112 = { class: "ebook-reader__field" };
999
+ var _hoisted_123 = ["value", "disabled"];
1000
+ var _hoisted_133 = ["disabled"];
1001
+ var _hoisted_143 = { class: "ebook-reader__checks" };
1002
+ var _hoisted_153 = { class: "ebook-reader__check" };
1003
+ var _hoisted_163 = ["checked"];
1004
+ var _hoisted_172 = { class: "ebook-reader__check" };
1005
+ var _hoisted_182 = ["checked"];
1006
+ var _hoisted_19 = { class: "ebook-reader__check" };
1007
+ var _hoisted_20 = ["checked"];
1008
+ var _hoisted_21 = { class: "ebook-reader__meta" };
1009
+ var _hoisted_222 = { key: 0 };
1010
+ var _hoisted_232 = {
1011
+ key: 1,
1012
+ class: "ebook-reader__empty"
1013
+ };
1014
+ var _hoisted_242 = { class: "ebook-reader__meta" };
1015
+ var _hoisted_252 = { class: "ebook-reader__status" };
1016
+ var _hoisted_262 = { key: 0 };
1017
+ var _hoisted_272 = { class: "ebook-reader__mprogress" };
1018
+ var _hoisted_28 = ["value"];
1019
+ var _hoisted_29 = { class: "ebook-reader__mprogress-percent" };
1020
+ var _hoisted_30 = {
1021
+ key: 4,
1022
+ class: "ebook-reader__mfont"
1023
+ };
1024
+ var _hoisted_31 = { class: "ebook-reader__font" };
1025
+ function _sfc_render8(_ctx, _cache, $props, $setup, $data, $options) {
1026
+ return openBlock(), createElementBlock("div", null, [
1027
+ createElementVNode(
1028
+ "div",
1029
+ { class: normalizeClass(["ebook-reader__mbar", { "is-visible": $props.barVisible }]) },
1030
+ [
1031
+ $setup.tooltip ? (openBlock(), createElementBlock(
1032
+ "div",
1033
+ {
1034
+ key: 0,
1035
+ class: "ebook-reader__tooltip",
1036
+ style: normalizeStyle({
1037
+ position: "fixed",
1038
+ bottom: "60px",
1039
+ left: `${$setup.tooltip.left}px`,
1040
+ transform: "translateX(-50%)",
1041
+ background: "rgba(0,0,0,0.8)",
1042
+ color: "#fff",
1043
+ padding: "4px 8px",
1044
+ borderRadius: "4px",
1045
+ fontSize: "12px",
1046
+ pointerEvents: "none",
1047
+ zIndex: 1e3,
1048
+ whiteSpace: "nowrap"
1049
+ })
1050
+ },
1051
+ toDisplayString($setup.tooltip.text),
1052
+ 5
1053
+ /* TEXT, STYLE */
1054
+ )) : createCommentVNode("v-if", true),
1055
+ createElementVNode("button", {
1056
+ type: "button",
1057
+ class: "ebook-reader__btn",
1058
+ "aria-pressed": $props.activePanel === "menu",
1059
+ onClick: _cache[0] || (_cache[0] = ($event) => $setup.emit("togglePanel", "menu")),
1060
+ onTouchstart: _cache[1] || (_cache[1] = (e) => $setup.handleTouchStart(e, "\u76EE\u5F55")),
1061
+ onTouchend: $setup.handleTouchEnd,
1062
+ onTouchcancel: $setup.handleTouchEnd,
1063
+ title: "\u76EE\u5F55"
1064
+ }, [createVNode($setup["SvgIcon"], { name: "list" })], 40, _hoisted_18),
1065
+ createElementVNode("button", {
1066
+ type: "button",
1067
+ class: "ebook-reader__btn",
1068
+ "aria-pressed": $props.activePanel === "search",
1069
+ onClick: _cache[2] || (_cache[2] = ($event) => $setup.emit("togglePanel", "search")),
1070
+ onTouchstart: _cache[3] || (_cache[3] = (e) => $setup.handleTouchStart(e, "\u641C\u7D22")),
1071
+ onTouchend: $setup.handleTouchEnd,
1072
+ onTouchcancel: $setup.handleTouchEnd,
1073
+ title: "\u641C\u7D22"
1074
+ }, [createVNode($setup["SvgIcon"], { name: "search" })], 40, _hoisted_27),
1075
+ createElementVNode("button", {
1076
+ type: "button",
1077
+ class: "ebook-reader__btn",
1078
+ "aria-pressed": $props.activePanel === "progress",
1079
+ onClick: _cache[4] || (_cache[4] = ($event) => $setup.emit("togglePanel", "progress")),
1080
+ onTouchstart: _cache[5] || (_cache[5] = (e) => $setup.handleTouchStart(e, "\u8FDB\u5EA6")),
1081
+ onTouchend: $setup.handleTouchEnd,
1082
+ onTouchcancel: $setup.handleTouchEnd,
1083
+ title: "\u8FDB\u5EA6"
1084
+ }, [createVNode($setup["SvgIcon"], { name: "sliders" })], 40, _hoisted_37),
1085
+ createElementVNode("button", {
1086
+ type: "button",
1087
+ class: "ebook-reader__btn",
1088
+ "aria-pressed": $props.activePanel === "theme",
1089
+ onClick: _cache[6] || (_cache[6] = ($event) => $setup.emit("togglePanel", "theme")),
1090
+ onTouchstart: _cache[7] || (_cache[7] = (e) => $setup.handleTouchStart(e, "\u660E\u6697")),
1091
+ onTouchend: $setup.handleTouchEnd,
1092
+ onTouchcancel: $setup.handleTouchEnd,
1093
+ title: "\u660E\u6697"
1094
+ }, [createVNode($setup["SvgIcon"], { name: "sun" })], 40, _hoisted_47),
1095
+ createElementVNode("button", {
1096
+ type: "button",
1097
+ class: "ebook-reader__btn",
1098
+ "aria-pressed": $props.activePanel === "font",
1099
+ onClick: _cache[8] || (_cache[8] = ($event) => $setup.emit("togglePanel", "font")),
1100
+ onTouchstart: _cache[9] || (_cache[9] = (e) => $setup.handleTouchStart(e, "\u5B57\u53F7")),
1101
+ onTouchend: $setup.handleTouchEnd,
1102
+ onTouchcancel: $setup.handleTouchEnd,
1103
+ title: "\u5B57\u53F7"
1104
+ }, [createVNode($setup["SvgIcon"], { name: "type" })], 40, _hoisted_53)
1105
+ ],
1106
+ 2
1107
+ /* CLASS */
1108
+ ),
1109
+ $props.activePanel ? (openBlock(), createElementBlock("div", {
1110
+ key: 0,
1111
+ class: "ebook-reader__moverlay",
1112
+ onClick: _cache[10] || (_cache[10] = ($event) => $setup.emit("closePanel"))
1113
+ })) : createCommentVNode("v-if", true),
1114
+ createElementVNode("div", {
1115
+ class: normalizeClass(["ebook-reader__msheet", { "is-open": $props.activePanel }]),
1116
+ "aria-hidden": !$props.activePanel
1117
+ }, [createElementVNode("div", _hoisted_73, [createElementVNode(
1118
+ "div",
1119
+ _hoisted_82,
1120
+ toDisplayString($setup.mobileTitle),
1121
+ 1
1122
+ /* TEXT */
1123
+ ), createElementVNode("button", {
1124
+ type: "button",
1125
+ class: "ebook-reader__btn",
1126
+ onClick: _cache[11] || (_cache[11] = ($event) => $setup.emit("closePanel"))
1127
+ }, [createVNode($setup["SvgIcon"], { name: "x" })])]), createElementVNode("div", _hoisted_92, [
1128
+ $props.activePanel === "menu" ? (openBlock(), createElementBlock(
1129
+ Fragment,
1130
+ { key: 0 },
1131
+ [$props.toc.length ? (openBlock(), createBlock($setup["TocTree"], {
1132
+ key: 0,
1133
+ items: $props.toc,
1134
+ onSelect: _cache[12] || (_cache[12] = (href) => {
1135
+ $setup.emit("tocSelect", href);
1136
+ $setup.emit("closePanel");
1137
+ })
1138
+ }, null, 8, ["items"])) : (openBlock(), createElementBlock("div", _hoisted_102, "\u672A\u627E\u5230\u76EE\u5F55"))],
1139
+ 64
1140
+ /* STABLE_FRAGMENT */
1141
+ )) : createCommentVNode("v-if", true),
1142
+ $props.activePanel === "search" ? (openBlock(), createElementBlock(
1143
+ Fragment,
1144
+ { key: 1 },
1145
+ [
1146
+ createElementVNode("div", _hoisted_112, [createElementVNode("input", {
1147
+ class: "ebook-reader__input",
1148
+ placeholder: "\u8F93\u5165\u5173\u952E\u8BCD",
1149
+ value: $props.searchQuery,
1150
+ disabled: $props.status !== "ready",
1151
+ onInput: _cache[13] || (_cache[13] = (e) => {
1152
+ const v = e.target.value;
1153
+ $setup.emit("update:searchQuery", v);
1154
+ if (!v.trim()) $setup.emit("search", "");
1155
+ }),
1156
+ onKeydown: _cache[14] || (_cache[14] = withKeys(($event) => $setup.emit("search", $props.searchQuery), ["enter"]))
1157
+ }, null, 40, _hoisted_123), createElementVNode("button", {
1158
+ type: "button",
1159
+ class: "ebook-reader__btn",
1160
+ disabled: $props.status !== "ready",
1161
+ onClick: _cache[15] || (_cache[15] = ($event) => $setup.emit("search", $props.searchQuery))
1162
+ }, " \u641C\u7D22 ", 8, _hoisted_133)]),
1163
+ createElementVNode("div", _hoisted_143, [
1164
+ createElementVNode("label", _hoisted_153, [createElementVNode("input", {
1165
+ type: "checkbox",
1166
+ checked: Boolean($props.searchOptions.matchCase),
1167
+ onChange: _cache[16] || (_cache[16] = (e) => $setup.updateSearchOption("matchCase", e.target.checked))
1168
+ }, null, 40, _hoisted_163), _cache[27] || (_cache[27] = createTextVNode(
1169
+ " \u533A\u5206\u5927\u5C0F\u5199 ",
1170
+ -1
1171
+ /* CACHED */
1172
+ ))]),
1173
+ createElementVNode("label", _hoisted_172, [createElementVNode("input", {
1174
+ type: "checkbox",
1175
+ checked: Boolean($props.searchOptions.wholeWords),
1176
+ onChange: _cache[17] || (_cache[17] = (e) => $setup.updateSearchOption("wholeWords", e.target.checked))
1177
+ }, null, 40, _hoisted_182), _cache[28] || (_cache[28] = createTextVNode(
1178
+ " \u5168\u8BCD\u5339\u914D ",
1179
+ -1
1180
+ /* CACHED */
1181
+ ))]),
1182
+ createElementVNode("label", _hoisted_19, [createElementVNode("input", {
1183
+ type: "checkbox",
1184
+ checked: Boolean($props.searchOptions.matchDiacritics),
1185
+ onChange: _cache[18] || (_cache[18] = (e) => $setup.updateSearchOption("matchDiacritics", e.target.checked))
1186
+ }, null, 40, _hoisted_20), _cache[29] || (_cache[29] = createTextVNode(
1187
+ " \u533A\u5206\u53D8\u97F3 ",
1188
+ -1
1189
+ /* CACHED */
1190
+ ))])
1191
+ ]),
1192
+ createElementVNode("div", _hoisted_21, [
1193
+ createElementVNode(
1194
+ "span",
1195
+ null,
1196
+ "\u8FDB\u5EA6 " + toDisplayString($props.searchProgressPercent) + "%",
1197
+ 1
1198
+ /* TEXT */
1199
+ ),
1200
+ $props.searching ? (openBlock(), createElementBlock("span", _hoisted_222, "\u641C\u7D22\u4E2D\u2026")) : createCommentVNode("v-if", true),
1201
+ $props.searching ? (openBlock(), createElementBlock("button", {
1202
+ key: 1,
1203
+ type: "button",
1204
+ class: "ebook-reader__link",
1205
+ onClick: _cache[19] || (_cache[19] = ($event) => $setup.emit("cancelSearch"))
1206
+ }, " \u53D6\u6D88 ")) : createCommentVNode("v-if", true)
1207
+ ]),
1208
+ $props.searchResults.length ? (openBlock(), createBlock($setup["SearchResultList"], {
1209
+ key: 0,
1210
+ results: $props.searchResults,
1211
+ onSelect: _cache[20] || (_cache[20] = (cfi) => $setup.emit("searchResultSelect", cfi))
1212
+ }, null, 8, ["results"])) : (openBlock(), createElementBlock(
1213
+ "div",
1214
+ _hoisted_232,
1215
+ toDisplayString($props.searchQuery.trim() ? "\u65E0\u5339\u914D\u7ED3\u679C" : "\u8BF7\u8F93\u5165\u5173\u952E\u8BCD"),
1216
+ 1
1217
+ /* TEXT */
1218
+ ))
1219
+ ],
1220
+ 64
1221
+ /* STABLE_FRAGMENT */
1222
+ )) : createCommentVNode("v-if", true),
1223
+ $props.activePanel === "progress" ? (openBlock(), createElementBlock(
1224
+ Fragment,
1225
+ { key: 2 },
1226
+ [createElementVNode("div", _hoisted_242, [createElementVNode(
1227
+ "span",
1228
+ _hoisted_252,
1229
+ toDisplayString($props.status === "error" ? $props.errorText || "\u9519\u8BEF" : $props.status === "opening" ? "\u6B63\u5728\u6253\u5F00\u2026" : "\u5C31\u7EEA"),
1230
+ 1
1231
+ /* TEXT */
1232
+ ), $props.sectionLabel ? (openBlock(), createElementBlock(
1233
+ "span",
1234
+ _hoisted_262,
1235
+ toDisplayString($props.sectionLabel),
1236
+ 1
1237
+ /* TEXT */
1238
+ )) : createCommentVNode("v-if", true)]), createElementVNode("div", _hoisted_272, [createElementVNode("input", {
1239
+ class: "ebook-reader__range",
1240
+ type: "range",
1241
+ min: 0,
1242
+ max: 100,
1243
+ step: 1,
1244
+ value: $props.displayedPercent,
1245
+ onInput: _cache[21] || (_cache[21] = (e) => {
1246
+ $setup.emit("seekStart");
1247
+ $setup.emit("seekChange", Number(e.target.value));
1248
+ }),
1249
+ onPointerup: _cache[22] || (_cache[22] = (e) => $setup.emit("seekEnd", Number(e.target.value))),
1250
+ onKeyup: _cache[23] || (_cache[23] = withKeys((e) => $setup.emit("seekCommit", Number(e.target.value)), ["enter"]))
1251
+ }, null, 40, _hoisted_28), createElementVNode(
1252
+ "div",
1253
+ _hoisted_29,
1254
+ toDisplayString($props.displayedPercent) + "%",
1255
+ 1
1256
+ /* TEXT */
1257
+ )])],
1258
+ 64
1259
+ /* STABLE_FRAGMENT */
1260
+ )) : createCommentVNode("v-if", true),
1261
+ $props.activePanel === "theme" ? (openBlock(), createElementBlock(
1262
+ "button",
1263
+ {
1264
+ key: 3,
1265
+ type: "button",
1266
+ class: "ebook-reader__btn",
1267
+ onClick: _cache[24] || (_cache[24] = ($event) => $setup.emit("toggleDarkMode", !$props.darkMode))
1268
+ },
1269
+ toDisplayString($props.darkMode ? "\u5207\u6362\u5230\u4EAE\u8272" : "\u5207\u6362\u5230\u6697\u9ED1"),
1270
+ 1
1271
+ /* TEXT */
1272
+ )) : createCommentVNode("v-if", true),
1273
+ $props.activePanel === "font" ? (openBlock(), createElementBlock("div", _hoisted_30, [
1274
+ createElementVNode("button", {
1275
+ type: "button",
1276
+ class: "ebook-reader__btn",
1277
+ onClick: _cache[25] || (_cache[25] = ($event) => $setup.emit("changeFontSize", $props.fontSize - 10))
1278
+ }, " A- "),
1279
+ createElementVNode(
1280
+ "div",
1281
+ _hoisted_31,
1282
+ toDisplayString($props.fontSize) + "%",
1283
+ 1
1284
+ /* TEXT */
1285
+ ),
1286
+ createElementVNode("button", {
1287
+ type: "button",
1288
+ class: "ebook-reader__btn",
1289
+ onClick: _cache[26] || (_cache[26] = ($event) => $setup.emit("changeFontSize", $props.fontSize + 10))
1290
+ }, " A+ ")
1291
+ ])) : createCommentVNode("v-if", true)
1292
+ ])], 10, _hoisted_63)
1293
+ ]);
1294
+ }
1295
+ var MobileUI_default = /* @__PURE__ */ export_helper_default(_sfc_main8, [["render", _sfc_render8], ["__file", "D:\\Project\\Demo\\EBook\\plugin\\ebook-reader\\src\\vue\\components\\MobileUI.vue"]]);
1296
+
1297
+ // src/vue/EBookReaderVue.ts
1298
+ var MOBILE_MAX_WIDTH = 768;
1299
+ var WIDE_MIN_WIDTH = 1024;
1300
+ var clamp = (n, min, max) => Math.min(max, Math.max(min, n));
1301
+ var EBookReaderVue = defineComponent({
1302
+ name: "EBookReaderVue",
1303
+ props: EBookReaderVuePropsDef,
1304
+ emits: ["ready", "error", "progress", "fontSizeChange", "darkModeChange", "update:fontSize", "update:darkMode"],
1305
+ setup(props, { emit, expose }) {
1306
+ const instance = getCurrentInstance();
1307
+ const isPropProvided = (key) => {
1308
+ const vnodeProps = instance?.vnode.props;
1309
+ if (!vnodeProps) return false;
1310
+ return Object.prototype.hasOwnProperty.call(vnodeProps, key);
1311
+ };
1312
+ const rootEl = ref(null);
1313
+ const viewerHost = ref(null);
1314
+ const reader = ref(null);
1315
+ const status = ref("idle");
1316
+ const errorText = ref("");
1317
+ const toc = ref([]);
1318
+ const tocOpen = ref(false);
1319
+ const searchOpen = ref(false);
1320
+ const progressInfo = ref(null);
1321
+ const isSeeking = ref(false);
1322
+ const seekPercent = ref(0);
1323
+ const layout = ref("default");
1324
+ const mobileBarVisible = ref(false);
1325
+ const mobilePanel = ref(null);
1326
+ const uncontrolledFontSize = ref(props.defaultFontSize ?? 100);
1327
+ const uncontrolledDarkMode = ref(Boolean(props.defaultDarkMode));
1328
+ const fontSize = () => props.fontSize ?? uncontrolledFontSize.value;
1329
+ const darkMode = () => isPropProvided("darkMode") ? Boolean(props.darkMode) : uncontrolledDarkMode.value;
1330
+ const searchQuery = ref("");
1331
+ const searchOptions = ref(props.defaultSearchOptions ?? { matchCase: false, wholeWords: false, matchDiacritics: false });
1332
+ const searchProgressPercent = ref(0);
1333
+ const searching = ref(false);
1334
+ const searchResults = ref([]);
1335
+ const closeDrawers = () => {
1336
+ tocOpen.value = false;
1337
+ searchOpen.value = false;
1338
+ };
1339
+ const closeMobileSheet = () => {
1340
+ mobilePanel.value = null;
1341
+ };
1342
+ const toggleMobilePanel = (panel) => {
1343
+ mobileBarVisible.value = true;
1344
+ mobilePanel.value = mobilePanel.value === panel ? null : panel;
1345
+ };
1346
+ const setDarkModeInternal = (next) => {
1347
+ if (!isPropProvided("darkMode")) uncontrolledDarkMode.value = next;
1348
+ emit("update:darkMode", next);
1349
+ emit("darkModeChange", next);
1350
+ reader.value?.setDarkMode(next);
1351
+ };
1352
+ const setFontSizeInternal = (next) => {
1353
+ const safe = clamp(next, 50, 300);
1354
+ if (props.fontSize == null) uncontrolledFontSize.value = safe;
1355
+ emit("update:fontSize", safe);
1356
+ emit("fontSizeChange", safe);
1357
+ reader.value?.setFontSize(safe);
1358
+ };
1359
+ const openFile = async (nextFile) => {
1360
+ if (!reader.value) return;
1361
+ status.value = "opening";
1362
+ errorText.value = "";
1363
+ toc.value = [];
1364
+ progressInfo.value = null;
1365
+ isSeeking.value = false;
1366
+ seekPercent.value = 0;
1367
+ searchResults.value = [];
1368
+ searchProgressPercent.value = 0;
1369
+ searching.value = false;
1370
+ try {
1371
+ await reader.value.open(nextFile);
1372
+ status.value = "ready";
1373
+ } catch (e) {
1374
+ status.value = "error";
1375
+ errorText.value = e?.message ? String(e.message) : "\u6253\u5F00\u5931\u8D25";
1376
+ emit("error", e);
1377
+ }
1378
+ };
1379
+ const runSearch = async (query) => {
1380
+ const r = reader.value;
1381
+ const normalized = query.trim();
1382
+ searchQuery.value = query;
1383
+ searchProgressPercent.value = 0;
1384
+ searching.value = Boolean(normalized);
1385
+ if (!r || !normalized) {
1386
+ r?.clearSearch();
1387
+ searchResults.value = [];
1388
+ searching.value = false;
1389
+ return;
1390
+ }
1391
+ r.cancelSearch();
1392
+ try {
1393
+ const results = await r.search(normalized, searchOptions.value);
1394
+ searchResults.value = results;
1395
+ searchProgressPercent.value = 100;
1396
+ } catch (e) {
1397
+ emit("error", e);
1398
+ } finally {
1399
+ searching.value = false;
1400
+ }
1401
+ };
1402
+ const keydownHandler = (e) => {
1403
+ if (!props.enableKeyboardNav) return;
1404
+ if (e.key === "ArrowLeft") reader.value?.prevPage();
1405
+ if (e.key === "ArrowRight") reader.value?.nextPage();
1406
+ if (e.key === "Escape") closeDrawers();
1407
+ };
1408
+ let gestureStartX = 0;
1409
+ let gestureStartY = 0;
1410
+ let gestureTracking = false;
1411
+ let gestureMoved = false;
1412
+ let gestureActionTaken = false;
1413
+ let gestureStartAt = 0;
1414
+ const boundDocs = /* @__PURE__ */ new WeakSet();
1415
+ const pointerDownHandler = (e) => {
1416
+ if (layout.value !== "mobile") return;
1417
+ const t = e.target;
1418
+ if (!t) return;
1419
+ if (t.closest(".ebook-reader__mbar") || t.closest(".ebook-reader__msheet")) return;
1420
+ if (t.closest('a,button,input,textarea,select,label,[role="button"],[contenteditable="true"]')) return;
1421
+ gestureTracking = true;
1422
+ gestureMoved = false;
1423
+ gestureActionTaken = false;
1424
+ gestureStartAt = e.timeStamp;
1425
+ gestureStartX = e.screenX;
1426
+ gestureStartY = e.screenY;
1427
+ };
1428
+ const pointerMoveHandler = (e) => {
1429
+ if (!gestureTracking) return;
1430
+ const dx = e.screenX - gestureStartX;
1431
+ const dy = e.screenY - gestureStartY;
1432
+ if (Math.abs(dx) > 8 || Math.abs(dy) > 8) gestureMoved = true;
1433
+ if (Math.abs(dy) < 8) return;
1434
+ if (Math.abs(dy) < Math.abs(dx)) {
1435
+ if (Math.abs(dx) >= 8) gestureTracking = false;
1436
+ return;
1437
+ }
1438
+ if (dy <= -24) {
1439
+ gestureActionTaken = true;
1440
+ gestureTracking = false;
1441
+ mobileBarVisible.value = true;
1442
+ return;
1443
+ }
1444
+ if (dy >= 24) {
1445
+ gestureActionTaken = true;
1446
+ gestureTracking = false;
1447
+ mobileBarVisible.value = false;
1448
+ mobilePanel.value = null;
1449
+ }
1450
+ };
1451
+ const pointerEndHandler = (e) => {
1452
+ if (layout.value !== "mobile") {
1453
+ gestureTracking = false;
1454
+ return;
1455
+ }
1456
+ if (gestureActionTaken) {
1457
+ gestureTracking = false;
1458
+ gestureMoved = false;
1459
+ gestureActionTaken = false;
1460
+ return;
1461
+ }
1462
+ if (!gestureTracking) {
1463
+ gestureMoved = false;
1464
+ return;
1465
+ }
1466
+ const dx = e.screenX - gestureStartX;
1467
+ const dy = e.screenY - gestureStartY;
1468
+ const dt = e.timeStamp - gestureStartAt;
1469
+ const isTap = !gestureMoved && Math.hypot(dx, dy) <= 10 && dt <= 300;
1470
+ if (isTap) {
1471
+ const next = !mobileBarVisible.value;
1472
+ mobileBarVisible.value = next;
1473
+ if (!next) mobilePanel.value = null;
1474
+ }
1475
+ gestureTracking = false;
1476
+ gestureMoved = false;
1477
+ };
1478
+ const bindContentPointerListeners = (doc) => {
1479
+ if (boundDocs.has(doc)) return;
1480
+ boundDocs.add(doc);
1481
+ doc.addEventListener("pointerdown", pointerDownHandler);
1482
+ doc.addEventListener("pointermove", pointerMoveHandler);
1483
+ doc.addEventListener("pointerup", pointerEndHandler);
1484
+ doc.addEventListener("pointercancel", pointerEndHandler);
1485
+ };
1486
+ let ro = null;
1487
+ onMounted(async () => {
1488
+ await nextTick();
1489
+ const host = viewerHost.value;
1490
+ if (!host) return;
1491
+ try {
1492
+ reader.value = createEBookReader(host, {
1493
+ darkMode: darkMode(),
1494
+ fontSize: fontSize(),
1495
+ onReady: (h2) => emit("ready", h2),
1496
+ onError: (e) => emit("error", e),
1497
+ onProgress: (info) => {
1498
+ progressInfo.value = info;
1499
+ emit("progress", info);
1500
+ },
1501
+ onToc: (items) => toc.value = items,
1502
+ onSearchProgress: (info) => {
1503
+ if (typeof info.progress === "number") searchProgressPercent.value = Math.round(info.progress * 100);
1504
+ },
1505
+ onContentLoad: (doc) => bindContentPointerListeners(doc)
1506
+ });
1507
+ status.value = "ready";
1508
+ } catch (e) {
1509
+ status.value = "error";
1510
+ errorText.value = e?.message ? String(e.message) : "\u521D\u59CB\u5316\u5931\u8D25";
1511
+ emit("error", e);
1512
+ return;
1513
+ }
1514
+ const root = rootEl.value;
1515
+ if (root) {
1516
+ root.addEventListener("keydown", keydownHandler);
1517
+ root.addEventListener("pointerdown", pointerDownHandler);
1518
+ root.addEventListener("pointermove", pointerMoveHandler);
1519
+ root.addEventListener("pointerup", pointerEndHandler);
1520
+ root.addEventListener("pointercancel", pointerEndHandler);
1521
+ }
1522
+ if (root) {
1523
+ ro = new ResizeObserver((entries) => {
1524
+ const w = entries[0]?.contentRect?.width ?? root.getBoundingClientRect().width;
1525
+ layout.value = w <= MOBILE_MAX_WIDTH ? "mobile" : w >= WIDE_MIN_WIDTH ? "wide" : "default";
1526
+ });
1527
+ ro.observe(root);
1528
+ }
1529
+ });
1530
+ onBeforeUnmount(() => {
1531
+ const root = rootEl.value;
1532
+ if (root) {
1533
+ root.removeEventListener("keydown", keydownHandler);
1534
+ root.removeEventListener("pointerdown", pointerDownHandler);
1535
+ root.removeEventListener("pointermove", pointerMoveHandler);
1536
+ root.removeEventListener("pointerup", pointerEndHandler);
1537
+ root.removeEventListener("pointercancel", pointerEndHandler);
1538
+ }
1539
+ ro?.disconnect();
1540
+ reader.value?.destroy();
1541
+ reader.value = null;
1542
+ });
1543
+ watch(
1544
+ () => props.file,
1545
+ (next) => {
1546
+ if (next) void openFile(next);
1547
+ }
1548
+ );
1549
+ watch(
1550
+ () => darkMode(),
1551
+ (v) => reader.value?.setDarkMode(Boolean(v))
1552
+ );
1553
+ watch(
1554
+ () => fontSize(),
1555
+ (v) => reader.value?.setFontSize(Number(v))
1556
+ );
1557
+ watch(
1558
+ () => layout.value,
1559
+ (v) => {
1560
+ closeDrawers();
1561
+ if (v !== "mobile") {
1562
+ mobilePanel.value = null;
1563
+ mobileBarVisible.value = false;
1564
+ }
1565
+ }
1566
+ );
1567
+ expose({
1568
+ prevPage: () => reader.value?.prevPage(),
1569
+ nextPage: () => reader.value?.nextPage(),
1570
+ prevSection: () => reader.value?.prevSection(),
1571
+ nextSection: () => reader.value?.nextSection(),
1572
+ goTo: (t) => reader.value?.goTo(t),
1573
+ goToFraction: (f) => reader.value?.goToFraction(f),
1574
+ search: (q, o) => reader.value ? reader.value.search(q, o) : Promise.resolve([]),
1575
+ cancelSearch: () => reader.value?.cancelSearch(),
1576
+ clearSearch: () => reader.value?.clearSearch()
1577
+ });
1578
+ return () => {
1579
+ const pct = Math.round((progressInfo.value?.fraction ?? 0) * 100);
1580
+ const displayed = isSeeking.value ? seekPercent.value : pct;
1581
+ const sectionLabel = progressInfo.value?.tocItem?.label ?? "";
1582
+ const isMobile = layout.value === "mobile";
1583
+ const viewer = h("div", { class: "ebook-reader__viewer", ref: viewerHost });
1584
+ const children = isMobile ? [
1585
+ viewer,
1586
+ h(MobileUI_default, {
1587
+ barVisible: mobileBarVisible.value,
1588
+ activePanel: mobilePanel.value,
1589
+ toc: toc.value,
1590
+ status: status.value,
1591
+ errorText: errorText.value,
1592
+ sectionLabel,
1593
+ displayedPercent: displayed,
1594
+ darkMode: darkMode(),
1595
+ fontSize: fontSize(),
1596
+ searchQuery: searchQuery.value,
1597
+ searchOptions: searchOptions.value,
1598
+ searchProgressPercent: searchProgressPercent.value,
1599
+ searching: searching.value,
1600
+ searchResults: searchResults.value,
1601
+ onTogglePanel: toggleMobilePanel,
1602
+ onClosePanel: closeMobileSheet,
1603
+ onTocSelect: (href) => {
1604
+ if (href) reader.value?.goTo(href);
1605
+ },
1606
+ onSearch: (q) => void runSearch(q),
1607
+ "onUpdate:searchQuery": (v) => searchQuery.value = v,
1608
+ "onUpdate:searchOptions": (v) => searchOptions.value = { ...searchOptions.value, ...v },
1609
+ onCancelSearch: () => reader.value?.cancelSearch(),
1610
+ onSearchResultSelect: (cfi) => {
1611
+ if (cfi) reader.value?.goTo(cfi);
1612
+ },
1613
+ onSeekStart: () => isSeeking.value = true,
1614
+ onSeekChange: (v) => seekPercent.value = v,
1615
+ onSeekEnd: (v) => {
1616
+ isSeeking.value = false;
1617
+ reader.value?.goToFraction(v / 100);
1618
+ },
1619
+ onSeekCommit: (v) => {
1620
+ isSeeking.value = false;
1621
+ reader.value?.goToFraction(v / 100);
1622
+ },
1623
+ onToggleDarkMode: setDarkModeInternal,
1624
+ onChangeFontSize: setFontSizeInternal
1625
+ })
1626
+ ] : [
1627
+ viewer,
1628
+ h(DesktopToolbar_default, {
1629
+ darkMode: darkMode(),
1630
+ fontSize: fontSize(),
1631
+ onToggleToc: () => tocOpen.value = true,
1632
+ onToggleSearch: () => searchOpen.value = true,
1633
+ onPrevSection: () => reader.value?.prevSection(),
1634
+ onPrevPage: () => reader.value?.prevPage(),
1635
+ onNextPage: () => reader.value?.nextPage(),
1636
+ onNextSection: () => reader.value?.nextSection(),
1637
+ onToggleDarkMode: () => setDarkModeInternal(!darkMode()),
1638
+ onChangeFontSize: setFontSizeInternal
1639
+ }),
1640
+ tocOpen.value || searchOpen.value ? h("div", { class: "ebook-reader__overlay", onClick: closeDrawers }) : null,
1641
+ h(TocDrawer_default, {
1642
+ isOpen: tocOpen.value,
1643
+ toc: toc.value,
1644
+ onClose: () => tocOpen.value = false,
1645
+ onSelect: (href) => {
1646
+ if (href) reader.value?.goTo(href);
1647
+ }
1648
+ }),
1649
+ h(SearchDrawer_default, {
1650
+ isOpen: searchOpen.value,
1651
+ status: status.value,
1652
+ query: searchQuery.value,
1653
+ options: searchOptions.value,
1654
+ progressPercent: searchProgressPercent.value,
1655
+ searching: searching.value,
1656
+ results: searchResults.value,
1657
+ onClose: () => searchOpen.value = false,
1658
+ onSearch: (q) => void runSearch(q),
1659
+ "onUpdate:query": (v) => searchQuery.value = v,
1660
+ "onUpdate:options": (v) => searchOptions.value = { ...searchOptions.value, ...v },
1661
+ onCancelSearch: () => reader.value?.cancelSearch(),
1662
+ onSelectResult: (cfi) => {
1663
+ if (cfi) reader.value?.goTo(cfi);
1664
+ }
1665
+ }),
1666
+ h(DesktopBottomBar_default, {
1667
+ status: status.value,
1668
+ errorText: errorText.value,
1669
+ sectionLabel,
1670
+ displayedPercent: displayed,
1671
+ onSeekStart: () => isSeeking.value = true,
1672
+ onSeekChange: (v) => seekPercent.value = v,
1673
+ onSeekEnd: (v) => {
1674
+ isSeeking.value = false;
1675
+ reader.value?.goToFraction(v / 100);
1676
+ },
1677
+ onSeekCommit: (v) => {
1678
+ isSeeking.value = false;
1679
+ reader.value?.goToFraction(v / 100);
1680
+ }
1681
+ })
1682
+ ];
1683
+ return h(
1684
+ "div",
1685
+ {
1686
+ ref: rootEl,
1687
+ class: "ebook-reader",
1688
+ "data-theme": darkMode() ? "dark" : "light",
1689
+ "data-layout": layout.value,
1690
+ tabindex: 0
1691
+ },
1692
+ children
1693
+ );
1694
+ };
1695
+ }
1696
+ });
1697
+
1698
+ export { EBookReaderVue };
1699
+ //# sourceMappingURL=vue.js.map
1700
+ //# sourceMappingURL=vue.js.map