@caipira/vue-reader 0.0.1

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.
Files changed (64) hide show
  1. package/README.md +153 -0
  2. package/dist/composables/useEpubReaderStrategy.d.ts +19 -0
  3. package/dist/composables/useEpubReaderStrategy.js +1 -0
  4. package/dist/composables/useEpubReaderWasm.d.ts +2 -0
  5. package/dist/composables/useEpubReaderWasm.js +131 -0
  6. package/dist/composables/useEpubReaderZipFetcher.d.ts +2 -0
  7. package/dist/composables/useEpubReaderZipFetcher.js +29 -0
  8. package/dist/services/browser/epub.d.ts +4 -0
  9. package/dist/services/browser/epub.js +22 -0
  10. package/dist/services/browser/manifest.d.ts +7 -0
  11. package/dist/services/browser/manifest.js +224 -0
  12. package/dist/services/browser/url-rewrite.d.ts +5 -0
  13. package/dist/services/browser/url-rewrite.js +183 -0
  14. package/dist/services/browser/url.d.ts +11 -0
  15. package/dist/services/browser/url.js +82 -0
  16. package/dist/services/browser/zip-fetcher.d.ts +12 -0
  17. package/dist/services/browser/zip-fetcher.js +123 -0
  18. package/dist/services/common/progression.d.ts +32 -0
  19. package/dist/services/common/progression.js +169 -0
  20. package/dist/services/common/title.d.ts +11 -0
  21. package/dist/services/common/title.js +40 -0
  22. package/dist/services/common/word-decorations.d.ts +4 -0
  23. package/dist/services/common/word-decorations.js +316 -0
  24. package/dist/services/common/word-lookup.d.ts +4 -0
  25. package/dist/services/common/word-lookup.js +154 -0
  26. package/dist/services/wasm/frame-document-bridge.d.ts +1 -0
  27. package/dist/services/wasm/frame-document-bridge.js +84 -0
  28. package/dist/services/wasm/wasm-streamer.d.ts +35 -0
  29. package/dist/services/wasm/wasm-streamer.js +157 -0
  30. package/dist/src/composables/useEpubReader.d.ts +27788 -0
  31. package/dist/src/composables/useEpubReader.js +8 -0
  32. package/dist/src/composables/useEpubReaderController.d.ts +27787 -0
  33. package/dist/src/composables/useEpubReaderController.js +23 -0
  34. package/dist/src/composables/useEpubReaderNavigation.d.ts +6 -0
  35. package/dist/src/composables/useEpubReaderNavigation.js +26 -0
  36. package/dist/src/composables/useEpubReaderSettings.d.ts +15 -0
  37. package/dist/src/composables/useEpubReaderSettings.js +8 -0
  38. package/dist/src/composables/useEpubReaderState.d.ts +22 -0
  39. package/dist/src/composables/useEpubReaderState.js +29 -0
  40. package/dist/src/composables/useReaderDictionary.d.ts +8 -0
  41. package/dist/src/composables/useReaderDictionary.js +13 -0
  42. package/dist/src/composables/useReaderSettings.d.ts +25 -0
  43. package/dist/src/composables/useReaderSettings.js +42 -0
  44. package/dist/src/composables/utils.d.ts +2 -0
  45. package/dist/src/composables/utils.js +1 -0
  46. package/dist/src/core/controller.d.ts +27789 -0
  47. package/dist/src/core/controller.js +262 -0
  48. package/dist/src/core/fonts.d.ts +1 -0
  49. package/dist/src/core/fonts.js +55 -0
  50. package/dist/src/core/settings-store.d.ts +16 -0
  51. package/dist/src/core/settings-store.js +58 -0
  52. package/dist/src/core/storage.d.ts +2 -0
  53. package/dist/src/core/storage.js +38 -0
  54. package/dist/src/index.d.ts +13 -0
  55. package/dist/src/index.js +11 -0
  56. package/dist/src/settings/options.d.ts +20 -0
  57. package/dist/src/settings/options.js +27 -0
  58. package/dist/src/types.d.ts +40 -0
  59. package/dist/src/types.js +1 -0
  60. package/dist/types/browser.d.ts +1 -0
  61. package/dist/types/browser.js +1 -0
  62. package/dist/types/common.d.ts +55 -0
  63. package/dist/types/common.js +1 -0
  64. package/package.json +46 -0
@@ -0,0 +1,262 @@
1
+ import { ref, computed, toRaw } from 'vue';
2
+ import { Publication as RPublication } from '@readium/shared';
3
+ import { EpubDefaults, EpubNavigator, EpubPreferences } from '@readium/navigator';
4
+ import { getCurrentTitle } from '../../services/common/title';
5
+ import { useEpubReaderWasm } from '../../composables/useEpubReaderWasm';
6
+ import { generatePositions } from '../../services/common/progression';
7
+ import { hasReaderDictionary } from '../../src/composables/useReaderDictionary';
8
+ import { onReaderColorsChange } from '../../src/composables/useReaderSettings';
9
+ import { setWasmStreamerConfig } from '../../services/wasm/wasm-streamer';
10
+ import { useEpubReaderZipFetcher } from '../../composables/useEpubReaderZipFetcher';
11
+ import { createReaderSettingsStore } from '../../src/core/settings-store';
12
+ import { createSessionStorageAdapter } from '../../src/core/storage';
13
+ import { injectCurrentFontIntoIframe } from '../../src/core/fonts';
14
+ import { createReadiumWordLookupController } from '../../services/common/word-lookup';
15
+ import { createReadiumWordDecorationsController } from '../../services/common/word-decorations';
16
+ const ENABLE_EDGE_TAP_PAGE_TURN = false;
17
+ export const createEpubReaderController = (src, containerRef, options) => {
18
+ const error = ref(null);
19
+ const loading = ref(false);
20
+ const tocLinks = ref([]);
21
+ const navigator = ref(null);
22
+ const publication = ref(null);
23
+ const currentPosition = ref(0);
24
+ const totalPositions = ref(0);
25
+ const currentChapterTitle = ref('');
26
+ const progressPercent = computed(() => Math.min(100, Math.max(0, currentPosition.value)));
27
+ const storage = options?.storage ?? createSessionStorageAdapter();
28
+ const settingsStore = createReaderSettingsStore(options?.settings, options?.preferences);
29
+ const settings = settingsStore.settings;
30
+ const fontBaseUrl = options?.assets?.fontBaseUrl ?? '/fonts';
31
+ let stopSettingsWatcher = null;
32
+ let activeChapterHref = '';
33
+ let chapterProgressions = new Map();
34
+ let activeSource = null;
35
+ let strategyFrameLoaded = null;
36
+ let stopReaderColorsWatcher = onReaderColorsChange((colors) => {
37
+ settings.backgroundColor = colors.background;
38
+ settings.textColor = colors.text;
39
+ });
40
+ let getProgress = (_idx, rawProg) => ({
41
+ progress: rawProg ?? 0,
42
+ resourceProgression: rawProg ?? 0,
43
+ });
44
+ const wordDecorations = hasReaderDictionary()
45
+ ? createReadiumWordDecorationsController(navigator, () => publication.value?.metadata?.languages?.[0])
46
+ : null;
47
+ const wordLookup = hasReaderDictionary()
48
+ ? createReadiumWordLookupController(() => publication.value?.metadata?.languages?.[0])
49
+ : null;
50
+ const applyPreferences = async () => {
51
+ if (!navigator.value)
52
+ return;
53
+ await toRaw(navigator.value).submitPreferences(new EpubPreferences({
54
+ backgroundColor: settings.backgroundColor,
55
+ columnCount: settings.columnCount,
56
+ fontFamily: settings.fontFamily,
57
+ fontSize: settings.fontSize,
58
+ lineHeight: settings.lineHeight,
59
+ scroll: settings.scroll,
60
+ textAlign: settings.textAlign,
61
+ textColor: settings.textColor,
62
+ }));
63
+ await injectCurrentFontIntoIframe(containerRef.value, settings.fontFamily, fontBaseUrl);
64
+ };
65
+ const destroyNavigator = () => {
66
+ toRaw(navigator.value)?.destroy();
67
+ navigator.value = null;
68
+ };
69
+ const destroy = () => {
70
+ destroyNavigator();
71
+ publication.value = null;
72
+ tocLinks.value = [];
73
+ currentPosition.value = 0;
74
+ totalPositions.value = 0;
75
+ currentChapterTitle.value = '';
76
+ strategyFrameLoaded = null;
77
+ chapterProgressions = new Map();
78
+ activeChapterHref = '';
79
+ activeSource?.destroy();
80
+ activeSource = null;
81
+ wordDecorations?.destroy();
82
+ wordLookup?.destroy();
83
+ stopSettingsWatcher?.();
84
+ stopSettingsWatcher = null;
85
+ stopReaderColorsWatcher?.();
86
+ stopReaderColorsWatcher = null;
87
+ };
88
+ const selectStrategy = () => {
89
+ const strategy = options?.strategy ?? 'zip-fetcher';
90
+ if (strategy === 'wasm') {
91
+ setWasmStreamerConfig({
92
+ swUrl: options?.assets?.swUrl,
93
+ swScope: options?.assets?.swScope,
94
+ wasmUrl: options?.assets?.wasmUrl,
95
+ });
96
+ return useEpubReaderWasm();
97
+ }
98
+ return useEpubReaderZipFetcher();
99
+ };
100
+ const init = async () => {
101
+ if (!containerRef.value)
102
+ return;
103
+ await settingsStore.hydrate();
104
+ loading.value = true;
105
+ error.value = null;
106
+ try {
107
+ const url = src.value;
108
+ const strategySource = selectStrategy();
109
+ const source = await strategySource.load(url);
110
+ activeSource = strategySource;
111
+ let pub;
112
+ let positions = [];
113
+ if (source.publication) {
114
+ pub = source.publication;
115
+ }
116
+ else {
117
+ pub = new RPublication({
118
+ manifest: source.manifest,
119
+ fetcher: source.fetcher,
120
+ });
121
+ }
122
+ getProgress = source.createProgressCalculator(pub);
123
+ chapterProgressions = source.chapterProgressions;
124
+ strategyFrameLoaded = source.onFrameLoaded ?? null;
125
+ positions = source.createPositions
126
+ ? (await source.createPositions(pub))
127
+ : generatePositions(pub);
128
+ publication.value = pub;
129
+ if (pub.toc) {
130
+ tocLinks.value = pub.toc.items.map((link) => ({
131
+ title: link.title || link.href,
132
+ link,
133
+ }));
134
+ }
135
+ if (positions.length === 0) {
136
+ positions = generatePositions(pub);
137
+ }
138
+ totalPositions.value = positions.length;
139
+ const initialLocator = storage.restorePosition(url, pub) ?? positions[0];
140
+ const listeners = {
141
+ positionChanged: (locator) => {
142
+ if (!locator) {
143
+ return;
144
+ }
145
+ activeChapterHref = locator.href.split('#')[0] ?? locator.href;
146
+ wordDecorations?.onPositionChanged(activeChapterHref);
147
+ const idx = publication.value
148
+ ? publication.value.readingOrder.findIndexWithHref(locator.href)
149
+ : -1;
150
+ if (idx < 0) {
151
+ return;
152
+ }
153
+ const { progress, resourceProgression } = getProgress(idx, locator.locations?.progression);
154
+ currentPosition.value = Math.round(Math.min(1, Math.max(0, progress)) * 100);
155
+ currentChapterTitle.value = getCurrentTitle(locator, chapterProgressions, tocLinks.value);
156
+ const persisted = locator.copyWithLocations({
157
+ position: idx + 1,
158
+ progression: resourceProgression,
159
+ });
160
+ storage.savePosition(url, persisted);
161
+ },
162
+ frameLoaded: async (wnd) => {
163
+ const nav = toRaw(navigator.value);
164
+ const currentHref = nav?.currentLocator?.href ?? activeChapterHref;
165
+ const chapterHref = (currentHref ?? '').split('#')[0] ?? '';
166
+ if (chapterHref) {
167
+ wordDecorations?.onFrameLoaded(wnd, chapterHref);
168
+ wordLookup?.onFrameLoaded(wnd);
169
+ }
170
+ if (chapterHref && strategyFrameLoaded) {
171
+ strategyFrameLoaded(wnd, chapterHref);
172
+ }
173
+ await injectCurrentFontIntoIframe(containerRef.value, settings.fontFamily, fontBaseUrl);
174
+ },
175
+ tap: () => !ENABLE_EDGE_TAP_PAGE_TURN,
176
+ click: () => !ENABLE_EDGE_TAP_PAGE_TURN,
177
+ zoom: () => { },
178
+ miscPointer: () => { },
179
+ scroll: () => { },
180
+ customEvent: () => { },
181
+ handleLocator: () => true,
182
+ textSelected: () => { },
183
+ contentProtection: () => { },
184
+ contextMenu: () => { },
185
+ peripheral: () => { },
186
+ };
187
+ navigator.value = new EpubNavigator(containerRef.value, pub, listeners, positions, initialLocator, {
188
+ preferences: new EpubPreferences(settings),
189
+ defaults: new EpubDefaults({}),
190
+ injectables: {
191
+ rules: [],
192
+ allowedDomains: [window.location.origin],
193
+ },
194
+ });
195
+ await toRaw(navigator.value).load();
196
+ await toRaw(navigator.value).submitPreferences(new EpubPreferences(settings));
197
+ stopSettingsWatcher = settingsStore.onSettingsChange(applyPreferences);
198
+ loading.value = false;
199
+ }
200
+ catch (e) {
201
+ error.value = e instanceof Error ? e.message : 'Unknown error loading EPUB';
202
+ loading.value = false;
203
+ }
204
+ };
205
+ const reload = async () => {
206
+ destroy();
207
+ await new Promise((resolve) => setTimeout(resolve, 0));
208
+ await init();
209
+ };
210
+ const goForward = () => {
211
+ toRaw(navigator.value)?.goForward(false, () => { });
212
+ };
213
+ const goBackward = () => {
214
+ toRaw(navigator.value)?.goBackward(false, () => { });
215
+ };
216
+ const goToLink = (link) => {
217
+ toRaw(navigator.value)?.goLink(link, false, () => { });
218
+ };
219
+ const toggleFullscreen = () => {
220
+ const el = containerRef.value?.parentElement;
221
+ if (!el) {
222
+ return;
223
+ }
224
+ if (!document.fullscreenElement) {
225
+ el.requestFullscreen()
226
+ .then(() => {
227
+ settings.isFullscreen = true;
228
+ })
229
+ .catch(() => { });
230
+ return;
231
+ }
232
+ document
233
+ .exitFullscreen()
234
+ .then(() => {
235
+ settings.isFullscreen = false;
236
+ })
237
+ .catch(() => { });
238
+ };
239
+ return {
240
+ state: {
241
+ error,
242
+ loading,
243
+ tocLinks,
244
+ navigator,
245
+ publication,
246
+ currentPosition,
247
+ totalPositions,
248
+ progressPercent,
249
+ currentChapterTitle,
250
+ settings,
251
+ },
252
+ actions: {
253
+ init,
254
+ destroy,
255
+ reload,
256
+ goForward,
257
+ goBackward,
258
+ goToLink,
259
+ toggleFullscreen,
260
+ },
261
+ };
262
+ };
@@ -0,0 +1 @@
1
+ export declare const injectCurrentFontIntoIframe: (container: HTMLDivElement | null, fontFamily: string | null, fontBaseUrl: string) => Promise<void>;
@@ -0,0 +1,55 @@
1
+ const FONT_CSS_TEMPLATE = (fontFamily, fontBlobUrl) => `
2
+ @font-face {
3
+ font-family: '${fontFamily}';
4
+ src: url('${fontBlobUrl}') format('truetype');
5
+ font-weight: normal;
6
+ font-style: normal;
7
+ }
8
+ `;
9
+ const fontBlobUrls = new Map();
10
+ const loadFontAsBlobUrl = async (fontFamily, fontBaseUrl) => {
11
+ if (fontBlobUrls.has(fontFamily)) {
12
+ return fontBlobUrls.get(fontFamily) ?? null;
13
+ }
14
+ try {
15
+ const response = await fetch(`${fontBaseUrl.replace(/\/$/, '')}/${fontFamily}.ttf`);
16
+ if (!response.ok) {
17
+ return null;
18
+ }
19
+ const blob = await response.blob();
20
+ const blobUrl = URL.createObjectURL(blob);
21
+ fontBlobUrls.set(fontFamily, blobUrl);
22
+ return blobUrl;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ };
28
+ const injectFontIntoIframe = async (iframe, fontFamily, fontBaseUrl) => {
29
+ const doc = iframe.contentDocument;
30
+ if (!doc) {
31
+ return;
32
+ }
33
+ const existingStyle = doc.querySelector(`style[data-readium-font=\"${fontFamily}\"]`);
34
+ if (existingStyle) {
35
+ return;
36
+ }
37
+ const blobUrl = await loadFontAsBlobUrl(fontFamily, fontBaseUrl);
38
+ if (!blobUrl) {
39
+ return;
40
+ }
41
+ const css = FONT_CSS_TEMPLATE(fontFamily, blobUrl);
42
+ const style = doc.createElement('style');
43
+ style.setAttribute('data-readium-font', fontFamily);
44
+ style.textContent = css;
45
+ doc.head.appendChild(style);
46
+ };
47
+ export const injectCurrentFontIntoIframe = async (container, fontFamily, fontBaseUrl) => {
48
+ if (!fontFamily || !container) {
49
+ return;
50
+ }
51
+ const iframes = container.querySelectorAll('iframe');
52
+ for (const iframe of iframes) {
53
+ await injectFontIntoIframe(iframe, fontFamily, fontBaseUrl);
54
+ }
55
+ };
@@ -0,0 +1,16 @@
1
+ import type { ReaderPreferencesAdapter, ReaderSettings } from '../../src/types';
2
+ export declare const createReaderSettingsStore: (initialSettings?: Partial<ReaderSettings>, preferences?: ReaderPreferencesAdapter) => {
3
+ settings: {
4
+ backgroundColor: string | null;
5
+ columnCount: number | null;
6
+ fontFamily: string | null;
7
+ fontSize: number | null;
8
+ isFullscreen: boolean;
9
+ lineHeight: number | null;
10
+ scroll: boolean | null;
11
+ textAlign: import("@readium/navigator").TextAlignment | null;
12
+ textColor: string | null;
13
+ };
14
+ hydrate: () => Promise<void>;
15
+ onSettingsChange: (handler: (settings: Readonly<ReaderSettings>) => void) => () => boolean;
16
+ };
@@ -0,0 +1,58 @@
1
+ import { reactive, watch } from 'vue';
2
+ import { getReaderColors } from '../../src/composables/useReaderSettings';
3
+ const defaultSettings = () => ({
4
+ ...(() => {
5
+ const colors = getReaderColors();
6
+ return {
7
+ backgroundColor: colors.background,
8
+ textColor: colors.text,
9
+ };
10
+ })(),
11
+ fontFamily: 'DEFAULT',
12
+ fontSize: 1,
13
+ lineHeight: null,
14
+ textAlign: null,
15
+ columnCount: null,
16
+ scroll: null,
17
+ isFullscreen: false,
18
+ });
19
+ export const createReaderSettingsStore = (initialSettings, preferences) => {
20
+ const settings = reactive({
21
+ ...defaultSettings(),
22
+ ...(initialSettings ?? {}),
23
+ });
24
+ const listeners = new Set();
25
+ let hydrated = false;
26
+ const hydrate = async () => {
27
+ if (hydrated || !preferences?.load) {
28
+ hydrated = true;
29
+ return;
30
+ }
31
+ const loaded = await preferences.load();
32
+ Object.assign(settings, loaded ?? {});
33
+ hydrated = true;
34
+ };
35
+ const onSettingsChange = (handler) => {
36
+ listeners.add(handler);
37
+ return () => listeners.delete(handler);
38
+ };
39
+ watch(() => [
40
+ settings.fontFamily,
41
+ settings.fontSize,
42
+ settings.lineHeight,
43
+ settings.textAlign,
44
+ settings.columnCount,
45
+ settings.scroll,
46
+ settings.backgroundColor,
47
+ settings.textColor,
48
+ settings.isFullscreen,
49
+ ], () => {
50
+ listeners.forEach((handler) => handler(settings));
51
+ void preferences?.save?.(settings);
52
+ });
53
+ return {
54
+ settings,
55
+ hydrate,
56
+ onSettingsChange,
57
+ };
58
+ };
@@ -0,0 +1,2 @@
1
+ import type { ReaderStorageAdapter } from '../../src/types';
2
+ export declare const createSessionStorageAdapter: () => ReaderStorageAdapter;
@@ -0,0 +1,38 @@
1
+ import { Locator } from '@readium/shared';
2
+ export const createSessionStorageAdapter = () => {
3
+ return {
4
+ savePosition: (key, locator) => {
5
+ sessionStorage.setItem(`epub-locator:${key}`, JSON.stringify(locator.serialize()));
6
+ },
7
+ restorePosition: (key, publication) => {
8
+ const saved = sessionStorage.getItem(`epub-locator:${key}`);
9
+ if (!saved) {
10
+ return undefined;
11
+ }
12
+ try {
13
+ const parsed = Locator.deserialize(JSON.parse(saved));
14
+ if (!parsed) {
15
+ return undefined;
16
+ }
17
+ const idx = publication.readingOrder.findIndexWithHref(parsed.href) ??
18
+ -1;
19
+ if (idx < 0) {
20
+ return undefined;
21
+ }
22
+ const progression = parsed.locations?.progression;
23
+ const safeProgression = typeof progression === 'number' &&
24
+ progression >= 0 &&
25
+ progression <= 1
26
+ ? progression
27
+ : 0;
28
+ return parsed.copyWithLocations({
29
+ position: idx + 1,
30
+ progression: safeProgression,
31
+ });
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ },
37
+ };
38
+ };
@@ -0,0 +1,13 @@
1
+ export type { TocEntry, ReaderSettings, ReaderAssetConfig, EpubReaderStrategy, ReaderStorageAdapter, ReaderPreferencesAdapter, EpubReaderControllerOptions, } from '../src/types';
2
+ export { createEpubReaderController } from '../src/core/controller';
3
+ export { createSessionStorageAdapter } from '../src/core/storage';
4
+ export { useEpubReader } from '../src/composables/useEpubReader';
5
+ export { useEpubReaderSettings } from '../src/composables/useEpubReaderSettings';
6
+ export { useEpubReaderController } from '../src/composables/useEpubReaderController';
7
+ export { useEpubReaderNavigation } from '../src/composables/useEpubReaderNavigation';
8
+ export { useEpubReaderState, setEpubReaderController, useEpubReaderControllerRef, } from '../src/composables/useEpubReaderState';
9
+ export { setReaderColors, getReaderColors, useReaderSettings, } from '../src/composables/useReaderSettings';
10
+ export { setReaderDictionary, hasReaderDictionary, useReaderDictionary, } from '../src/composables/useReaderDictionary';
11
+ export type { ReaderDictionaryApi, ReaderDictionaryWordStatus, } from '../src/composables/useReaderDictionary';
12
+ export { ALIGN_OPTIONS, COLUMN_OPTIONS, PX_FONT_SIZE_OPTIONS, EPUB_FONT_SIZE_OPTIONS, } from '../src/settings/options';
13
+ export { setWasmStreamerConfig, ensureSW as ensureEpubServiceWorker, } from '../services/wasm/wasm-streamer';
@@ -0,0 +1,11 @@
1
+ export { createEpubReaderController } from '../src/core/controller';
2
+ export { createSessionStorageAdapter } from '../src/core/storage';
3
+ export { useEpubReader } from '../src/composables/useEpubReader';
4
+ export { useEpubReaderSettings } from '../src/composables/useEpubReaderSettings';
5
+ export { useEpubReaderController } from '../src/composables/useEpubReaderController';
6
+ export { useEpubReaderNavigation } from '../src/composables/useEpubReaderNavigation';
7
+ export { useEpubReaderState, setEpubReaderController, useEpubReaderControllerRef, } from '../src/composables/useEpubReaderState';
8
+ export { setReaderColors, getReaderColors, useReaderSettings, } from '../src/composables/useReaderSettings';
9
+ export { setReaderDictionary, hasReaderDictionary, useReaderDictionary, } from '../src/composables/useReaderDictionary';
10
+ export { ALIGN_OPTIONS, COLUMN_OPTIONS, PX_FONT_SIZE_OPTIONS, EPUB_FONT_SIZE_OPTIONS, } from '../src/settings/options';
11
+ export { setWasmStreamerConfig, ensureSW as ensureEpubServiceWorker, } from '../services/wasm/wasm-streamer';
@@ -0,0 +1,20 @@
1
+ import { TextAlignment } from '@readium/navigator';
2
+ export declare const EPUB_FONT_SIZE_OPTIONS: {
3
+ value: number;
4
+ label: string;
5
+ }[];
6
+ export declare const PX_FONT_SIZE_OPTIONS: {
7
+ value: number;
8
+ label: string;
9
+ }[];
10
+ export declare const COLUMN_OPTIONS: {
11
+ value: number;
12
+ label: string;
13
+ }[];
14
+ export declare const ALIGN_OPTIONS: ({
15
+ key: null;
16
+ label: string;
17
+ } | {
18
+ key: TextAlignment;
19
+ label: string;
20
+ })[];
@@ -0,0 +1,27 @@
1
+ import { TextAlignment } from '@readium/navigator';
2
+ export const EPUB_FONT_SIZE_OPTIONS = [
3
+ { value: 0.8, label: '80%' },
4
+ { value: 1, label: '100% (Default)' },
5
+ { value: 1.2, label: '120%' },
6
+ { value: 1.4, label: '140%' },
7
+ { value: 1.6, label: '160%' },
8
+ ];
9
+ export const PX_FONT_SIZE_OPTIONS = [
10
+ { value: 16, label: '16 (Default)' },
11
+ { value: 20, label: '20' },
12
+ { value: 24, label: '24' },
13
+ { value: 28, label: '28' },
14
+ { value: 32, label: '32' },
15
+ ];
16
+ export const COLUMN_OPTIONS = [
17
+ { value: 0, label: 'Auto' },
18
+ { value: 1, label: '1' },
19
+ { value: 2, label: '2' },
20
+ ];
21
+ export const ALIGN_OPTIONS = [
22
+ { key: null, label: 'Auto' },
23
+ { key: TextAlignment.start, label: 'Start' },
24
+ { key: TextAlignment.left, label: 'Left' },
25
+ { key: TextAlignment.right, label: 'Right' },
26
+ { key: TextAlignment.justify, label: 'Justify' },
27
+ ];
@@ -0,0 +1,40 @@
1
+ import type { Link, Locator, Publication } from '@readium/shared';
2
+ import type { TextAlignment } from '@readium/navigator';
3
+ export type EpubReaderStrategy = 'wasm' | 'zip-fetcher';
4
+ export type TocEntry = {
5
+ title: string;
6
+ link: Link;
7
+ };
8
+ export type ReaderSettings = {
9
+ backgroundColor: string | null;
10
+ columnCount: number | null;
11
+ fontFamily: string | null;
12
+ fontSize: number | null;
13
+ isFullscreen: boolean;
14
+ lineHeight: number | null;
15
+ scroll: boolean | null;
16
+ textAlign: TextAlignment | null;
17
+ textColor: string | null;
18
+ };
19
+ export type ReaderStorageAdapter = {
20
+ savePosition: (key: string, locator: Locator) => void;
21
+ restorePosition: (key: string, publication: Publication) => Locator | undefined;
22
+ };
23
+ export type ReaderPreferencesAdapter = {
24
+ load: () => Partial<ReaderSettings> | Promise<Partial<ReaderSettings>>;
25
+ save?: (settings: Readonly<ReaderSettings>) => void | Promise<void>;
26
+ };
27
+ export type ReaderAssetConfig = {
28
+ swUrl?: string;
29
+ swScope?: string;
30
+ wasmUrl?: string;
31
+ fontBaseUrl?: string;
32
+ };
33
+ export type EpubReaderControllerOptions = {
34
+ strategy?: EpubReaderStrategy;
35
+ settings?: Partial<ReaderSettings>;
36
+ storage?: ReaderStorageAdapter;
37
+ preferences?: ReaderPreferencesAdapter;
38
+ assets?: ReaderAssetConfig;
39
+ logNamespace?: string;
40
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type ZipData = Record<string, Uint8Array>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ import type { Link } from '@readium/shared';
2
+ export type ChapterBoundary = {
3
+ title: string;
4
+ /** Normalised progression (0–1) where this chapter starts in the file. */
5
+ startProg: number;
6
+ };
7
+ export type WeightedResource = {
8
+ href: string;
9
+ /** Uncompressed byte size (proxy for page count) */
10
+ size: number;
11
+ };
12
+ export type TocEntry = {
13
+ title: string;
14
+ link: Link;
15
+ };
16
+ type WordStatus = 'known' | 'unknown';
17
+ export type DecorationCommand = {
18
+ group: string;
19
+ action: 'add' | 'remove' | 'clear' | 'update';
20
+ decoration?: {
21
+ id: string;
22
+ locator?: unknown;
23
+ style?: {
24
+ tint?: string;
25
+ layout?: 'bounds' | 'boxes';
26
+ width?: 'bounds';
27
+ };
28
+ };
29
+ };
30
+ export type Candidate = {
31
+ id: string;
32
+ lemma: string;
33
+ group?: string;
34
+ status?: WordStatus;
35
+ tint?: string;
36
+ locator: {
37
+ href: string;
38
+ type: string;
39
+ locations: {
40
+ cssSelector?: string;
41
+ };
42
+ text: {
43
+ before?: string;
44
+ highlight: string;
45
+ after?: string;
46
+ };
47
+ };
48
+ };
49
+ export type ReadiumWordDecorationsController = {
50
+ onFrameLoaded: (wnd: Window, chapterHref: string) => void;
51
+ onPositionChanged: (chapterHref: string) => void;
52
+ refresh: () => Promise<void>;
53
+ destroy: () => void;
54
+ };
55
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@caipira/vue-reader",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "description": "Headless e-book reader toolkit for vue",
6
+ "author": {
7
+ "name": "Theone Lucas",
8
+ "email": "theone@caipira.io"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/caipira-io/vue-reader.git"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public",
16
+ "registry": "https://registry.npmjs.org/"
17
+ },
18
+ "type": "module",
19
+ "main": "dist/src/index.js",
20
+ "types": "dist/src/index.d.ts",
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "clean": "rm -rf dist",
26
+ "build": "npm run clean && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "peerDependencies": {
31
+ "vue": "^3.5.0"
32
+ },
33
+ "dependencies": {
34
+ "@readium/navigator": "2.5.4",
35
+ "@readium/shared": "2.2.0",
36
+ "jszip": "^3.10.1",
37
+ "lodash": "^4.18.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.16.5",
41
+ "tsc-alias": "^1.8.17",
42
+ "typescript": "^6.0.3",
43
+ "vue": "^3.5.13",
44
+ "@types/lodash": "^4.17.24"
45
+ }
46
+ }