@libraz/mejiro-react 0.4.0 → 0.6.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 +34 -60
- package/dist/index.d.ts +1673 -38
- package/dist/index.js +3854 -37
- package/package.json +35 -8
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,496 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties, PointerEvent, TextareaHTMLAttributes, RefObject } from 'react';
|
|
3
|
+
import { EpubBook, AssetResolver, EditableEpub, ManuscriptDialect, EpubChapter, EditableEpubBook, AnnotatedParagraph, AddImageInput, EditableEpubImage, EpubExportOptions, EpubProjectMetadata, EpubProject } from '@libraz/mejiro/epub';
|
|
4
|
+
import { BookOptions, ChapterLayout, SpreadResult, PageResult, ComputePageSizeOptions, InChapterAnchor, ReadingAnchor, ManuscriptChapter, AnchorRect, AnchorRange, MejiroBook } from '@libraz/mejiro/book';
|
|
1
5
|
import { RenderPage } from '@libraz/mejiro/render';
|
|
2
|
-
import {
|
|
3
|
-
|
|
6
|
+
import { FontFamily, InlineAnnotation } from '@libraz/mejiro/browser';
|
|
7
|
+
|
|
8
|
+
/** All UI strings rendered by the bundled React components. */
|
|
9
|
+
interface MejiroMessages {
|
|
10
|
+
/** Logo subtitle. */
|
|
11
|
+
readonly logoSubtitle: string;
|
|
12
|
+
/** Header "Open" button label. */
|
|
13
|
+
readonly openButton: string;
|
|
14
|
+
/** Header "Settings" button label. */
|
|
15
|
+
readonly settingsButton: string;
|
|
16
|
+
/** Header "Image" overlay button label. */
|
|
17
|
+
readonly imageButton: string;
|
|
18
|
+
/** ARIA label for removing an image overlay. */
|
|
19
|
+
readonly imageRemoveButton: string;
|
|
20
|
+
/** Loading-state body text. */
|
|
21
|
+
readonly loading: string;
|
|
22
|
+
/** ARIA label for the previous-spread nav zone. */
|
|
23
|
+
readonly prevSpread: string;
|
|
24
|
+
/** ARIA label for the next-spread nav zone. */
|
|
25
|
+
readonly nextSpread: string;
|
|
26
|
+
/** Settings panel group title — Font. */
|
|
27
|
+
readonly settingsFont: string;
|
|
28
|
+
/** Settings panel font-size label. */
|
|
29
|
+
readonly settingsSize: string;
|
|
30
|
+
/** Settings panel "decrease font size" button. */
|
|
31
|
+
readonly settingsSizeDown: string;
|
|
32
|
+
/** Settings panel "increase font size" button. */
|
|
33
|
+
readonly settingsSizeUp: string;
|
|
34
|
+
/** Settings panel group title — Layout. */
|
|
35
|
+
readonly settingsLayout: string;
|
|
36
|
+
/** Settings panel kinsoku label. */
|
|
37
|
+
readonly settingsKinsoku: string;
|
|
38
|
+
/** Settings panel hanging-punctuation label. */
|
|
39
|
+
readonly settingsHanging: string;
|
|
40
|
+
/** Settings panel line-spacing label. */
|
|
41
|
+
readonly settingsLineSpacing: string;
|
|
42
|
+
/** Settings panel "strict" kinsoku option. */
|
|
43
|
+
readonly settingsStrict: string;
|
|
44
|
+
/** Settings panel "loose" kinsoku option. */
|
|
45
|
+
readonly settingsLoose: string;
|
|
46
|
+
/** Settings panel "on" toggle option. */
|
|
47
|
+
readonly toggleOn: string;
|
|
48
|
+
/** Settings panel "off" toggle option. */
|
|
49
|
+
readonly toggleOff: string;
|
|
50
|
+
/** Drop zone primary text. */
|
|
51
|
+
readonly dropZoneTitle: string;
|
|
52
|
+
/** Drop zone secondary hint. */
|
|
53
|
+
readonly dropZoneHint: string;
|
|
54
|
+
/** TOC heading. */
|
|
55
|
+
readonly tocTitle: string;
|
|
56
|
+
/** TOC search placeholder. */
|
|
57
|
+
readonly tocSearchPlaceholder: string;
|
|
58
|
+
/** TOC empty-results template. Placeholder `{query}` is replaced. */
|
|
59
|
+
readonly tocEmpty: string;
|
|
60
|
+
/** Shelf heading. */
|
|
61
|
+
readonly shelfTitle: string;
|
|
62
|
+
/** Generic chapter-N template. Placeholder `{n}` is replaced. */
|
|
63
|
+
readonly chapterN: string;
|
|
64
|
+
/** Aria-live announcement template. Placeholders: `{spread}`, `{total}`. */
|
|
65
|
+
readonly spreadAnnouncement: string;
|
|
66
|
+
/** Embedded EPUB editor preview subtitle. */
|
|
67
|
+
readonly editorPreviewSubtitle: string;
|
|
68
|
+
/** EPUB editor panel title. */
|
|
69
|
+
readonly editorTitle: string;
|
|
70
|
+
/** EPUB editor empty-state title. */
|
|
71
|
+
readonly editorNoBookLoaded: string;
|
|
72
|
+
/** EPUB editor paragraph list section label. */
|
|
73
|
+
readonly editorParagraphs: string;
|
|
74
|
+
/** EPUB editor proofread section label. */
|
|
75
|
+
readonly editorProofread: string;
|
|
76
|
+
/** EPUB editor apply-text button label. */
|
|
77
|
+
readonly editorApplyText: string;
|
|
78
|
+
/** EPUB editor ruby section label. */
|
|
79
|
+
readonly editorRuby: string;
|
|
80
|
+
/** EPUB editor ruby usage hint. */
|
|
81
|
+
readonly editorRubyHint: string;
|
|
82
|
+
/** EPUB editor ruby selected-range template. Placeholders: `{start}`, `{end}`, `{count}`. */
|
|
83
|
+
readonly editorRubyRange: string;
|
|
84
|
+
/** EPUB editor ruby input placeholder. */
|
|
85
|
+
readonly editorRubyPlaceholder: string;
|
|
86
|
+
/** EPUB editor apply-ruby button label. */
|
|
87
|
+
readonly editorApplyRuby: string;
|
|
88
|
+
/** EPUB editor image section label. */
|
|
89
|
+
readonly editorImages: string;
|
|
90
|
+
/** EPUB editor insert-image button label. */
|
|
91
|
+
readonly editorInsertImageAfterParagraph: string;
|
|
92
|
+
/** EPUB editor export button label. */
|
|
93
|
+
readonly editorExportEpub: string;
|
|
94
|
+
/** Manuscript editor preview subtitle. */
|
|
95
|
+
readonly manuscriptPreviewSubtitle: string;
|
|
96
|
+
/** Manuscript editor panel title. */
|
|
97
|
+
readonly manuscriptTitle: string;
|
|
98
|
+
/** Manuscript editor ruby notation hint. */
|
|
99
|
+
readonly manuscriptRubyHint: string;
|
|
100
|
+
/** Manuscript editor metadata section label. */
|
|
101
|
+
readonly manuscriptMetadata: string;
|
|
102
|
+
/** Manuscript editor chapters section label. */
|
|
103
|
+
readonly manuscriptChapters: string;
|
|
104
|
+
/** Manuscript editor draft section label. */
|
|
105
|
+
readonly manuscriptDraft: string;
|
|
106
|
+
/** Manuscript editor cover chooser label. */
|
|
107
|
+
readonly manuscriptChooseCoverImage: string;
|
|
108
|
+
/** Manuscript editor default book title. */
|
|
109
|
+
readonly manuscriptDefaultTitle: string;
|
|
110
|
+
/** Manuscript editor default chapter title template. Placeholder `{n}` is replaced. */
|
|
111
|
+
readonly manuscriptDefaultChapterTitle: string;
|
|
112
|
+
/** Manuscript editor default draft body. */
|
|
113
|
+
readonly manuscriptDefaultBody: string;
|
|
114
|
+
/** Generic untitled fallback. */
|
|
115
|
+
readonly untitled: string;
|
|
116
|
+
/** Manuscript editor add-chapter button label. */
|
|
117
|
+
readonly manuscriptAddChapter: string;
|
|
118
|
+
/** Manuscript editor remove-chapter button label. */
|
|
119
|
+
readonly manuscriptRemove: string;
|
|
120
|
+
/** Manuscript editor emphasis-dots button label. */
|
|
121
|
+
readonly manuscriptEmphasisDots: string;
|
|
122
|
+
/** Manuscript editor tate-chu-yoko button label. */
|
|
123
|
+
readonly manuscriptTcy: string;
|
|
124
|
+
/** Manuscript editor emphasis button label. */
|
|
125
|
+
readonly manuscriptEm: string;
|
|
126
|
+
/** Manuscript editor strong button label. */
|
|
127
|
+
readonly manuscriptStrong: string;
|
|
128
|
+
/** ARIA label for the chapter drag handle. Placeholders: `{title}`. */
|
|
129
|
+
readonly manuscriptReorderHandle: string;
|
|
130
|
+
}
|
|
131
|
+
/** Built-in English catalog (the default). */
|
|
132
|
+
declare const enMessages: MejiroMessages;
|
|
133
|
+
/** Built-in Japanese catalog. */
|
|
134
|
+
declare const jaMessages: MejiroMessages;
|
|
135
|
+
/** Built-in locale identifiers. */
|
|
136
|
+
type MejiroLocale = 'en' | 'ja';
|
|
137
|
+
/** Provider that scopes a message catalog to its descendants. */
|
|
138
|
+
declare function MejiroI18nProvider({ locale, messages, children, }: {
|
|
139
|
+
locale?: MejiroLocale;
|
|
140
|
+
messages?: Partial<MejiroMessages>;
|
|
141
|
+
children: ReactNode;
|
|
142
|
+
}): ReactNode;
|
|
143
|
+
/** Options for {@link useI18n}. */
|
|
144
|
+
interface UseI18nOptions {
|
|
145
|
+
/** Built-in locale to use as the base catalog. @defaultValue 'en' */
|
|
146
|
+
locale?: MejiroLocale;
|
|
147
|
+
/** Overrides merged on top of the built-in catalog. */
|
|
148
|
+
messages?: Partial<MejiroMessages>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Returns the resolved `MejiroMessages` catalog. With no arguments the
|
|
152
|
+
* catalog provided by the nearest {@link MejiroI18nProvider} is returned;
|
|
153
|
+
* passing `locale` / `messages` resolves a catalog without context.
|
|
154
|
+
*/
|
|
155
|
+
declare function useI18n(options?: UseI18nOptions): MejiroMessages;
|
|
156
|
+
/** Build a catalog without invoking React (useful in plain modules). */
|
|
157
|
+
declare function resolveMessages(locale: MejiroLocale | undefined, overrides: Partial<MejiroMessages> | undefined): MejiroMessages;
|
|
158
|
+
/** Replace `{name}` placeholders in a template. */
|
|
159
|
+
declare function format(template: string, vars: Record<string, string | number>): string;
|
|
160
|
+
|
|
161
|
+
type MejiroChapterNavVariant = 'select' | 'panel';
|
|
162
|
+
/** Props for {@link MejiroChapterNav}. */
|
|
163
|
+
interface MejiroChapterNavProps {
|
|
164
|
+
/** EPUB to list chapters from. */
|
|
165
|
+
epub: EpubBook;
|
|
166
|
+
/** Current chapter index. */
|
|
167
|
+
chapter: number;
|
|
168
|
+
/** Called when a new chapter is picked. */
|
|
169
|
+
onChange: (chapter: number) => void;
|
|
170
|
+
/** Visual treatment for the chapter list. @defaultValue 'select' */
|
|
171
|
+
variant?: MejiroChapterNavVariant;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Chapter selector for an {@link EpubBook}.
|
|
175
|
+
*
|
|
176
|
+
* @deprecated Prefer {@link MejiroToc} for new code. `MejiroToc` adds
|
|
177
|
+
* search and current-anchor highlighting and renders the same data with a
|
|
178
|
+
* more accessible markup. `MejiroChapterNav` remains supported for the
|
|
179
|
+
* `<select>` variant and existing `MejiroReader` integration.
|
|
180
|
+
*/
|
|
181
|
+
declare function MejiroChapterNav({ epub, chapter, onChange, variant, }: MejiroChapterNavProps): ReactNode;
|
|
182
|
+
|
|
183
|
+
/** Props for {@link MejiroDropZone}. */
|
|
184
|
+
interface MejiroDropZoneProps {
|
|
185
|
+
/** File `accept` filter for the picker. @defaultValue '.epub' */
|
|
186
|
+
accept?: string;
|
|
187
|
+
/** Validator. Defaults to `name.endsWith('.epub')`. */
|
|
188
|
+
validateFile?: (file: File) => boolean;
|
|
189
|
+
/** Called with the selected/dropped file. */
|
|
190
|
+
onFile: (file: File) => void;
|
|
191
|
+
/** Custom body content (replaces default icon/text). */
|
|
192
|
+
children?: ReactNode;
|
|
193
|
+
/** Additional class name for the root element. */
|
|
194
|
+
className?: string;
|
|
195
|
+
/** Additional inline styles. */
|
|
196
|
+
style?: CSSProperties;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Drop zone for EPUB files. Combines drag-and-drop with a click-to-open file picker.
|
|
200
|
+
*/
|
|
201
|
+
declare function MejiroDropZone({ accept, validateFile, onFile, children, className, style, }: MejiroDropZoneProps): ReactNode;
|
|
202
|
+
|
|
203
|
+
/** A font choice shown in the settings panel. */
|
|
204
|
+
interface FontChoice {
|
|
205
|
+
/** CSS `font-family` value applied to the book. */
|
|
206
|
+
value: string;
|
|
207
|
+
/** Human-readable label. */
|
|
208
|
+
label: string;
|
|
209
|
+
}
|
|
210
|
+
/** Subset of {@link BookOptions} editable from the panel. */
|
|
211
|
+
type EditableSettings = Pick<BookOptions, 'fontFamily' | 'fontSize' | 'lineSpacing' | 'mode' | 'enableHanging'>;
|
|
212
|
+
/** Props for {@link MejiroSettingsPanel}. */
|
|
213
|
+
interface MejiroSettingsPanelProps {
|
|
214
|
+
/** Whether the panel is expanded. */
|
|
215
|
+
open: boolean;
|
|
216
|
+
/** Current editable settings. */
|
|
217
|
+
settings: EditableSettings;
|
|
218
|
+
/** Called when a setting changes. */
|
|
219
|
+
onChange: (next: EditableSettings) => void;
|
|
220
|
+
/** Font choices. */
|
|
221
|
+
fonts?: FontChoice[];
|
|
222
|
+
/** Min font size. @defaultValue 10 */
|
|
223
|
+
minFontSize?: number;
|
|
224
|
+
/** Max font size. @defaultValue 48 */
|
|
225
|
+
maxFontSize?: number;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Reader settings panel: font, size, kinsoku mode, hanging punctuation, line spacing.
|
|
229
|
+
*/
|
|
230
|
+
declare function MejiroSettingsPanel({ open, settings, onChange, fonts, minFontSize, maxFontSize, }: MejiroSettingsPanelProps): ReactNode;
|
|
231
|
+
|
|
232
|
+
interface MejiroEditorProps {
|
|
233
|
+
/** URL fetched and loaded on mount. */
|
|
234
|
+
epubUrl?: string;
|
|
235
|
+
/** Font choices passed to the preview reader. */
|
|
236
|
+
fonts?: FontChoice[];
|
|
237
|
+
/**
|
|
238
|
+
* Allow editing paragraph text (the "Proofread" section).
|
|
239
|
+
* @defaultValue true
|
|
240
|
+
*/
|
|
241
|
+
enableProofread?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Allow editing ruby annotations (the "Ruby" section).
|
|
244
|
+
* @defaultValue true
|
|
245
|
+
*/
|
|
246
|
+
enableRuby?: boolean;
|
|
247
|
+
/**
|
|
248
|
+
* Allow inserting images into the EPUB (the "Images" section).
|
|
249
|
+
* @defaultValue true
|
|
250
|
+
*/
|
|
251
|
+
enableImages?: boolean;
|
|
252
|
+
/**
|
|
253
|
+
* Allow exporting the edited EPUB. SaaS publishers can disable this to
|
|
254
|
+
* restrict downloads (e.g. server-side export only).
|
|
255
|
+
* @defaultValue true
|
|
256
|
+
*/
|
|
257
|
+
enableExport?: boolean;
|
|
258
|
+
/**
|
|
259
|
+
* Called before the export buffer is offered as a download. Return `false`
|
|
260
|
+
* (or a `Promise<false>`) to suppress the browser download — useful for
|
|
261
|
+
* uploading the buffer to a backend instead.
|
|
262
|
+
*/
|
|
263
|
+
onBeforeExport?: (buffer: ArrayBuffer) => boolean | undefined | Promise<boolean | undefined>;
|
|
264
|
+
/**
|
|
265
|
+
* Declarative export policy. When set, supersedes `onBeforeExport` for
|
|
266
|
+
* download control and threads watermark / encrypt transforms through the
|
|
267
|
+
* export pipeline.
|
|
268
|
+
*/
|
|
269
|
+
exportPolicy?: MejiroExportPolicy;
|
|
270
|
+
/**
|
|
271
|
+
* Resolves URL-only image assets ({@link EditableImageAsset.url} set, `data`
|
|
272
|
+
* unset) into bytes at export time. Forwarded to `editor.export()`. Use this
|
|
273
|
+
* to keep large image bytes off the client during editing and only fetch
|
|
274
|
+
* them once when the EPUB is assembled — e.g. signed S3 URLs that require
|
|
275
|
+
* custom auth headers.
|
|
276
|
+
*/
|
|
277
|
+
assetResolver?: AssetResolver;
|
|
278
|
+
/** Called after an EPUB is loaded into the editor. */
|
|
279
|
+
onLoad?: (editor: EditableEpub) => void;
|
|
280
|
+
/** Called after export completes. */
|
|
281
|
+
onExport?: (buffer: ArrayBuffer) => void;
|
|
282
|
+
/** Called when loading or parsing an EPUB fails. */
|
|
283
|
+
onError?: (error: Error) => void;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Declarative restrictions on the export pipeline. Mejiro applies the
|
|
287
|
+
* transforms in this order: `watermark` (modifies the EPUB in-place) →
|
|
288
|
+
* `encrypt` (replaces the buffer with the result) → `allowDownload` (skips
|
|
289
|
+
* the browser download when `false`).
|
|
290
|
+
*/
|
|
291
|
+
interface MejiroExportPolicy {
|
|
292
|
+
/**
|
|
293
|
+
* If `false`, the EPUB buffer is still produced (and `onExport` still fires)
|
|
294
|
+
* but no browser download is triggered. Use when shipping the buffer
|
|
295
|
+
* elsewhere (e.g. uploading to a backend).
|
|
296
|
+
* @defaultValue true
|
|
297
|
+
*/
|
|
298
|
+
allowDownload?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Transforms the EPUB buffer before it is offered for download. Typically
|
|
301
|
+
* a server round-trip that returns a DRM-wrapped EPUB.
|
|
302
|
+
*/
|
|
303
|
+
encrypt?: (buffer: ArrayBuffer) => ArrayBuffer | Promise<ArrayBuffer>;
|
|
304
|
+
/**
|
|
305
|
+
* Embeds a visible watermark string into the EPUB. Implemented by writing
|
|
306
|
+
* the text as XHTML metadata; renderers may surface it differently.
|
|
307
|
+
*/
|
|
308
|
+
watermark?: {
|
|
309
|
+
text: string;
|
|
310
|
+
opacity?: number;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/** EPUB editor UI for proofreading, ruby edits, image insertion, and export. */
|
|
314
|
+
declare function MejiroEditor({ epubUrl, fonts, enableProofread, enableRuby, enableImages, enableExport, onBeforeExport, exportPolicy, assetResolver, onLoad, onExport, onError, }: MejiroEditorProps): ReactNode;
|
|
315
|
+
|
|
316
|
+
/** Rectangle describing an image overlay position and size. */
|
|
317
|
+
interface ImageRect {
|
|
318
|
+
x: number;
|
|
319
|
+
y: number;
|
|
320
|
+
w: number;
|
|
321
|
+
h: number;
|
|
322
|
+
}
|
|
323
|
+
/** Options for {@link useImageOverlay}. */
|
|
324
|
+
interface UseImageOverlayOptions {
|
|
325
|
+
/** Default image width in pixels. @defaultValue 120 */
|
|
326
|
+
defaultWidth?: number;
|
|
327
|
+
/** Default image height in pixels. @defaultValue 160 */
|
|
328
|
+
defaultHeight?: number;
|
|
329
|
+
/** Default x position. @defaultValue 80 */
|
|
330
|
+
defaultX?: number;
|
|
331
|
+
/** Default y position. @defaultValue 100 */
|
|
332
|
+
defaultY?: number;
|
|
333
|
+
/** Margin around the image in pixels. Defaults to the book's font size. */
|
|
334
|
+
margin?: number;
|
|
335
|
+
}
|
|
336
|
+
/** Return value of {@link useImageOverlay}. */
|
|
337
|
+
interface UseImageOverlayReturn {
|
|
338
|
+
/** Current image rectangle, or `null` if no overlay is active. */
|
|
339
|
+
imageRect: ImageRect | null;
|
|
340
|
+
/** Whether an image overlay is currently active. */
|
|
341
|
+
hasImage: boolean;
|
|
342
|
+
/** Toggle the image overlay on/off. */
|
|
343
|
+
toggleImage: () => void;
|
|
344
|
+
/** Pointer-down handler for the overlay body (initiates drag). */
|
|
345
|
+
onOverlayPointerDown: (e: PointerEvent) => void;
|
|
346
|
+
/** Pointer-down handler for the resize handle. */
|
|
347
|
+
onResizePointerDown: (e: PointerEvent) => void;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* React hook that manages a draggable/resizable image overlay
|
|
351
|
+
* with automatic text reflow via {@link ChapterLayout.syncImages}.
|
|
352
|
+
*
|
|
353
|
+
* @param layout - Current chapter layout (or `null` if not yet loaded).
|
|
354
|
+
* @param spreadIdx - Current spread index.
|
|
355
|
+
* @param onUpdate - Called with the updated {@link SpreadResult} after every reflow.
|
|
356
|
+
* @param options - Default dimensions and position for the overlay.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```tsx
|
|
360
|
+
* const { imageRect, hasImage, toggleImage, onOverlayPointerDown, onResizePointerDown } =
|
|
361
|
+
* useImageOverlay(layout, spreadIdx, setSpread);
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
declare function useImageOverlay(layout: ChapterLayout | null, spreadIdx: number, onUpdate: (spread: SpreadResult) => void, options?: UseImageOverlayOptions): UseImageOverlayReturn;
|
|
365
|
+
|
|
366
|
+
/** Props for {@link MejiroImageOverlay}. */
|
|
367
|
+
interface MejiroImageOverlayProps {
|
|
368
|
+
/** Position and size of the overlay (px). */
|
|
369
|
+
rect: ImageRect;
|
|
370
|
+
/** Label inside the overlay. @defaultValue `messages.imageButton` */
|
|
371
|
+
label?: string;
|
|
372
|
+
/** Pointer-down on the body (drag). */
|
|
373
|
+
onOverlayPointerDown?: (e: PointerEvent) => void;
|
|
374
|
+
/** Pointer-down on the resize handle. */
|
|
375
|
+
onResizePointerDown?: (e: PointerEvent) => void;
|
|
376
|
+
/** Triggered when the close button is pressed. */
|
|
377
|
+
onClose?: () => void;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Decorative image placeholder overlay with drag/resize affordances.
|
|
381
|
+
* Pair the pointer handlers with {@link useImageOverlay} or {@link useMultiImageOverlay}.
|
|
382
|
+
*/
|
|
383
|
+
declare function MejiroImageOverlay({ rect, label, onOverlayPointerDown, onResizePointerDown, onClose, }: MejiroImageOverlayProps): ReactNode;
|
|
384
|
+
|
|
385
|
+
interface ManuscriptEditorChapter {
|
|
386
|
+
id: string;
|
|
387
|
+
title: string;
|
|
388
|
+
body: string;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Subset of {@link MejiroReader} props that the manuscript editor passes
|
|
392
|
+
* through to the live preview. Properties driven by the editor itself
|
|
393
|
+
* (`manuscript`, `fonts`, `chapter`, `onChapterChange`) are ignored if
|
|
394
|
+
* supplied here.
|
|
395
|
+
*/
|
|
396
|
+
interface ManuscriptPreviewProps {
|
|
397
|
+
subtitle?: string;
|
|
398
|
+
title?: string;
|
|
399
|
+
chapterNavMode?: 'select' | 'panel' | 'both' | 'none';
|
|
400
|
+
enableHeader?: boolean;
|
|
401
|
+
enableChapterNav?: boolean;
|
|
402
|
+
enableSettings?: boolean;
|
|
403
|
+
enableStats?: boolean;
|
|
404
|
+
enableKeyboard?: boolean;
|
|
405
|
+
enablePageIndicator?: boolean;
|
|
406
|
+
/**
|
|
407
|
+
* Toggle the surface-tap chrome-hide behavior of the embedded reader.
|
|
408
|
+
* Defaults to `false` here (the editor's preview is part of a side-by-side
|
|
409
|
+
* authoring workflow where tap-to-hide would surprise the author). Set
|
|
410
|
+
* to `true` to demo the fullscreen-reader behavior.
|
|
411
|
+
*/
|
|
412
|
+
enableSurfaceTap?: boolean;
|
|
413
|
+
bare?: boolean;
|
|
414
|
+
}
|
|
415
|
+
interface MejiroManuscriptEditorProps {
|
|
416
|
+
/** Font choices passed to the preview reader. */
|
|
417
|
+
fonts?: FontChoice[];
|
|
418
|
+
/**
|
|
419
|
+
* Title. When `onTitleChange` is supplied, this is the controlled value
|
|
420
|
+
* (changes propagate from parent to the input). Otherwise it is the
|
|
421
|
+
* initial value and the editor manages its own title state.
|
|
422
|
+
*/
|
|
423
|
+
title?: string;
|
|
424
|
+
/** Called whenever the title input changes. Enables controlled mode. */
|
|
425
|
+
onTitleChange?: (title: string) => void;
|
|
426
|
+
/**
|
|
427
|
+
* Author. Controlled when `onAuthorChange` is supplied; otherwise treated
|
|
428
|
+
* as the initial value.
|
|
429
|
+
*/
|
|
430
|
+
author?: string;
|
|
431
|
+
/** Called whenever the author input changes. Enables controlled mode. */
|
|
432
|
+
onAuthorChange?: (author: string) => void;
|
|
433
|
+
/**
|
|
434
|
+
* Cover image. Controlled when `onCoverChange` is supplied; otherwise
|
|
435
|
+
* treated as the initial cover. Setting `null` clears the cover.
|
|
436
|
+
*/
|
|
437
|
+
cover?: File | null;
|
|
438
|
+
/** Called whenever the cover changes. Enables controlled mode. */
|
|
439
|
+
onCoverChange?: (cover: File | null) => void;
|
|
440
|
+
/** Initial chapters. */
|
|
441
|
+
chapters?: ManuscriptEditorChapter[];
|
|
442
|
+
/**
|
|
443
|
+
* Called whenever the chapter draft settles (debounced by
|
|
444
|
+
* {@link MejiroManuscriptEditorProps.autosaveDelay}). Use to persist drafts
|
|
445
|
+
* to localStorage, IndexedDB, or upload to a server.
|
|
446
|
+
*/
|
|
447
|
+
onAutosave?: (chapters: ManuscriptEditorChapter[]) => void | Promise<void>;
|
|
448
|
+
/** Autosave debounce in milliseconds. @defaultValue 800 */
|
|
449
|
+
autosaveDelay?: number;
|
|
450
|
+
/**
|
|
451
|
+
* Props forwarded to the embedded {@link MejiroReader} preview. Lets
|
|
452
|
+
* hosts customize subtitle / chapterNavMode / etc.; `manuscript`, `fonts`,
|
|
453
|
+
* `chapter`, and `onChapterChange` remain driven by the editor.
|
|
454
|
+
*/
|
|
455
|
+
previewProps?: ManuscriptPreviewProps;
|
|
456
|
+
/**
|
|
457
|
+
* Resolves URL-only project assets (e.g. covers/illustrations registered as
|
|
458
|
+
* `{ url, ... }`) into bytes at export time. Forwarded to `project.export()`
|
|
459
|
+
* so authors can register signed-URL references without holding raw bytes
|
|
460
|
+
* client-side until publish.
|
|
461
|
+
*/
|
|
462
|
+
assetResolver?: AssetResolver;
|
|
463
|
+
/** Called after export completes. */
|
|
464
|
+
onExport?: (buffer: ArrayBuffer) => void;
|
|
465
|
+
}
|
|
466
|
+
/** Manuscript-to-EPUB editor for author drafts from posting sites. */
|
|
467
|
+
declare function MejiroManuscriptEditor({ fonts, title: titleProp, onTitleChange, author: authorProp, onAuthorChange, cover: coverProp, onCoverChange, chapters: initialChapters, onAutosave, autosaveDelay, previewProps, assetResolver, onExport, }: MejiroManuscriptEditorProps): ReactNode;
|
|
468
|
+
|
|
469
|
+
/** Props for {@link MejiroNotationHighlighter}. */
|
|
470
|
+
interface MejiroNotationHighlighterProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange'> {
|
|
471
|
+
/** Source manuscript text. */
|
|
472
|
+
value: string;
|
|
473
|
+
/** Called whenever the textarea content changes. */
|
|
474
|
+
onChange: (next: string) => void;
|
|
475
|
+
/** Manuscript notation dialect. @defaultValue `'mejiro'` */
|
|
476
|
+
dialect?: ManuscriptDialect;
|
|
477
|
+
/** Class applied to the wrapper element. */
|
|
478
|
+
wrapperClassName?: string;
|
|
479
|
+
/** Inline style on the wrapper element. */
|
|
480
|
+
wrapperStyle?: CSSProperties;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Textarea with a notation-highlight overlay. The overlay sits *behind* the
|
|
484
|
+
* textarea and tints the background of manuscript notation tokens (ruby,
|
|
485
|
+
* emphasis dots, tate-chu-yoko, em / strong, links, footnotes) so authors can
|
|
486
|
+
* see at a glance which characters carry notation.
|
|
487
|
+
*
|
|
488
|
+
* The textarea is fully interactive; selection, undo / redo, and the
|
|
489
|
+
* browser-native caret are unaffected. Notation kinds are exposed via
|
|
490
|
+
* `data-token` attributes on the overlay spans so hosts can restyle them via
|
|
491
|
+
* CSS (`.mejiro-notation-token[data-token="ruby"]` etc.).
|
|
492
|
+
*/
|
|
493
|
+
declare const MejiroNotationHighlighter: react.ForwardRefExoticComponent<MejiroNotationHighlighterProps & react.RefAttributes<HTMLTextAreaElement>>;
|
|
4
494
|
|
|
5
495
|
/** Props for the MejiroPage component. */
|
|
6
496
|
interface MejiroPageProps {
|
|
@@ -19,12 +509,22 @@ interface MejiroPageProps {
|
|
|
19
509
|
*/
|
|
20
510
|
declare function MejiroPage({ page, className, style }: MejiroPageProps): ReactNode;
|
|
21
511
|
|
|
512
|
+
/** Props for {@link MejiroPageIndicator}. */
|
|
513
|
+
interface MejiroPageIndicatorProps {
|
|
514
|
+
/** Current spread (1-based). */
|
|
515
|
+
current: number;
|
|
516
|
+
/** Total number of spreads. */
|
|
517
|
+
total: number;
|
|
518
|
+
}
|
|
519
|
+
/** Displays "current / total" spread position under the book. */
|
|
520
|
+
declare function MejiroPageIndicator({ current, total }: MejiroPageIndicatorProps): ReactNode;
|
|
521
|
+
|
|
22
522
|
/** Props for the MejiroPageView component. */
|
|
23
523
|
interface MejiroPageViewProps {
|
|
24
524
|
/** Page result from {@link ChapterLayout.getSpread} or {@link ChapterLayout.getPage}. */
|
|
25
525
|
result: PageResult;
|
|
26
526
|
/** CSS font family for slot-based rendering (used when images are present). */
|
|
27
|
-
fontFamily?:
|
|
527
|
+
fontFamily?: FontFamily;
|
|
28
528
|
/** Line spacing multiplier for slot-based rendering (used when images are present). */
|
|
29
529
|
lineSpacing?: number;
|
|
30
530
|
/**
|
|
@@ -53,54 +553,1189 @@ interface MejiroPageViewProps {
|
|
|
53
553
|
*/
|
|
54
554
|
declare function MejiroPageView({ result, fontFamily, lineSpacing, slotMode, className, style, }: MejiroPageViewProps): ReactNode;
|
|
55
555
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
556
|
+
type MejiroChapterNavMode = 'select' | 'panel' | 'both' | 'none';
|
|
557
|
+
/**
|
|
558
|
+
* Reading-flow mode for {@link MejiroReader}.
|
|
559
|
+
* - `paginated` — two-page spread with page-turn animation (default).
|
|
560
|
+
* - `scroll` — every page in the chapter stacked in a vertical scroll view.
|
|
561
|
+
*/
|
|
562
|
+
type MejiroReaderMode = 'paginated' | 'scroll';
|
|
563
|
+
/**
|
|
564
|
+
* Two-page vs single-page rendering of a spread.
|
|
565
|
+
* - `double` — always render two pages (default).
|
|
566
|
+
* - `single` — render only the right page, centered.
|
|
567
|
+
* - `auto` — switch based on the surface aspect ratio (single when portrait).
|
|
568
|
+
*/
|
|
569
|
+
type MejiroSpreadMode = 'double' | 'single' | 'auto';
|
|
570
|
+
/**
|
|
571
|
+
* Which page of a spread prints its page number: `'both'` (right = odd,
|
|
572
|
+
* left = even), only `'right'`, only `'left'`, or `'none'`.
|
|
573
|
+
*/
|
|
574
|
+
type PageNumberDisplay = 'both' | 'right' | 'left' | 'none';
|
|
575
|
+
/**
|
|
576
|
+
* How the reader sizes itself inside its container.
|
|
577
|
+
* - `fill` — fill the container's given height; the spread is fitted inside it,
|
|
578
|
+
* letterboxing if the box aspect doesn't match the spread (the default; the
|
|
579
|
+
* host must give the reader a height).
|
|
580
|
+
* - `width` — self-size from width: the reader derives its own height from its
|
|
581
|
+
* measured width and the page aspect ratio, so the spread fills exactly with
|
|
582
|
+
* no letterbox and the host needs no height/aspect magic numbers. The host
|
|
583
|
+
* only constrains the width.
|
|
584
|
+
*/
|
|
585
|
+
type MejiroReaderFit = 'fill' | 'width';
|
|
586
|
+
/** Built-in reader theme presets. */
|
|
587
|
+
type MejiroThemeName = 'light' | 'dark' | 'sepia' | 'high-contrast' | 'auto';
|
|
588
|
+
/**
|
|
589
|
+
* Theme configuration for the reader. Either a preset name, or an object
|
|
590
|
+
* with a preset and an `override` map of CSS custom properties that take
|
|
591
|
+
* precedence over the preset values.
|
|
592
|
+
*/
|
|
593
|
+
type MejiroTheme = MejiroThemeName | {
|
|
594
|
+
name: MejiroThemeName;
|
|
595
|
+
override?: Record<string, string>;
|
|
596
|
+
};
|
|
597
|
+
/** Reading position exposed by {@link MejiroReaderHandle.getReadingPosition}. */
|
|
598
|
+
interface ReadingPosition {
|
|
599
|
+
chapter: number;
|
|
600
|
+
spreadIdx: number;
|
|
601
|
+
totalPages: number;
|
|
602
|
+
totalSpreads: number;
|
|
62
603
|
}
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
604
|
+
/**
|
|
605
|
+
* Event payloads emitted by {@link MejiroReaderHandle.subscribe}.
|
|
606
|
+
*
|
|
607
|
+
* Each property maps an event name to its listener signature.
|
|
608
|
+
*/
|
|
609
|
+
interface MejiroReaderEventMap {
|
|
610
|
+
/** Fires after the current spread index changes. */
|
|
611
|
+
spreadChanged: (payload: {
|
|
612
|
+
chapter: number;
|
|
613
|
+
spreadIdx: number;
|
|
614
|
+
}) => void;
|
|
615
|
+
/** Fires when a turn animation begins (before the new spread is shown). */
|
|
616
|
+
turnStart: (payload: {
|
|
617
|
+
from: number;
|
|
618
|
+
}) => void;
|
|
619
|
+
/** Fires after a turn animation completes (the new spread is now shown). */
|
|
620
|
+
turnEnd: (payload: {
|
|
621
|
+
to: number;
|
|
622
|
+
}) => void;
|
|
623
|
+
/** Fires when the reader reaches the last spread of the current chapter. */
|
|
624
|
+
chapterFinished: (payload: {
|
|
625
|
+
chapter: number;
|
|
626
|
+
}) => void;
|
|
627
|
+
}
|
|
628
|
+
/** Imperative handle returned by `ref={...}` on {@link MejiroReader}. */
|
|
629
|
+
interface MejiroReaderHandle {
|
|
630
|
+
/** Jump to a specific spread (0-based, clamped to [0, totalSpreads − 1]). */
|
|
631
|
+
goToSpread(index: number): void;
|
|
632
|
+
/** Advance one spread forward. */
|
|
633
|
+
next(): void;
|
|
634
|
+
/** Go back one spread. */
|
|
635
|
+
prev(): void;
|
|
636
|
+
/** Jump to a chapter (resets spread index to 0). */
|
|
637
|
+
goToChapter(index: number): void;
|
|
638
|
+
/** Read the current reading position. */
|
|
639
|
+
getReadingPosition(): ReadingPosition;
|
|
640
|
+
/**
|
|
641
|
+
* Navigate to a {@link ReadingAnchor}. If the chapter differs from the
|
|
642
|
+
* current one, the chapter is switched first; once the new layout is ready
|
|
643
|
+
* the anchor is resolved and the matching spread is opened.
|
|
644
|
+
*
|
|
645
|
+
* Returns a promise that resolves once the spread has been applied. If
|
|
646
|
+
* another `goToAnchor` is invoked before the previous one settles, the
|
|
647
|
+
* earlier promise resolves immediately (superseded). Resolves on unmount.
|
|
648
|
+
*/
|
|
649
|
+
goToAnchor(anchor: ReadingAnchor): Promise<void>;
|
|
650
|
+
/**
|
|
651
|
+
* Returns the {@link ReadingAnchor} at the start of the current spread,
|
|
652
|
+
* or `null` if the layout is not ready.
|
|
653
|
+
*/
|
|
654
|
+
getAnchor(): ReadingAnchor | null;
|
|
655
|
+
/**
|
|
656
|
+
* Returns the half-open range of {@link ReadingAnchor}s visible on the
|
|
657
|
+
* current spread. `end` points at the start of the next spread (or the end
|
|
658
|
+
* of the chapter for the last spread).
|
|
659
|
+
*/
|
|
660
|
+
getVisibleRange(): {
|
|
661
|
+
start: ReadingAnchor;
|
|
662
|
+
end: ReadingAnchor;
|
|
663
|
+
} | null;
|
|
664
|
+
/**
|
|
665
|
+
* Updates book options at runtime. Same shape as {@link MejiroBook.setOptions}
|
|
666
|
+
* — font / size changes re-measure and re-layout asynchronously.
|
|
667
|
+
*/
|
|
668
|
+
setOptions(partial: Partial<BookOptions>): Promise<void>;
|
|
669
|
+
/**
|
|
670
|
+
* Subscribes to a reader lifecycle event. Returns a function that
|
|
671
|
+
* detaches the listener.
|
|
672
|
+
*/
|
|
673
|
+
subscribe<E extends keyof MejiroReaderEventMap>(event: E, listener: MejiroReaderEventMap[E]): () => void;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Context passed to a {@link MejiroReaderCommonProps.renderSettings} render prop.
|
|
677
|
+
*
|
|
678
|
+
* Lets a host replace the built-in settings controls with its own UI while
|
|
679
|
+
* keeping the panel chrome and its open/close accordion. All fields are wired
|
|
680
|
+
* to the live reader.
|
|
681
|
+
*/
|
|
682
|
+
interface MejiroReaderSettingsSlot {
|
|
683
|
+
/** Current effective settings (font, size, line spacing, kinsoku, hanging). */
|
|
684
|
+
settings: EditableSettings;
|
|
685
|
+
/** Applies a partial settings change and re-flows the chapter. */
|
|
686
|
+
update: (partial: Partial<BookOptions>) => void;
|
|
687
|
+
/** Whether the settings panel is currently open. */
|
|
688
|
+
open: boolean;
|
|
689
|
+
/** Toggles the settings panel open/closed (same as the header button). */
|
|
690
|
+
toggle: () => void;
|
|
691
|
+
}
|
|
692
|
+
/** Props shared across every {@link MejiroReader} source mode. */
|
|
693
|
+
interface MejiroReaderCommonProps {
|
|
694
|
+
/**
|
|
695
|
+
* Initial book options. Optional — defaults to {@link DEFAULT_BOOK_OPTIONS}
|
|
696
|
+
* (`serif` 16px, line spacing 1.8, strict kinsoku, hanging punctuation on).
|
|
697
|
+
* Spread the defaults to tweak only a few:
|
|
698
|
+
*
|
|
699
|
+
* ```ts
|
|
700
|
+
* { ...DEFAULT_BOOK_OPTIONS, fontFamily: '"Noto Serif JP"', fontSize: 18 }
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
options?: BookOptions;
|
|
704
|
+
/**
|
|
705
|
+
* Page-geometry overrides forwarded to `MejiroBook.computePageSize`. Use to
|
|
706
|
+
* tune how the spread is sized inside the surface — most usefully to shrink
|
|
707
|
+
* the reserved margins so the pages fill their frame, e.g.
|
|
708
|
+
* `pageGeometry={{ gutterOffset: 0, headerOffset: 0 }}`. Also accepts
|
|
709
|
+
* `aspect`, `minWidth`, `minHeight`, `maxHeight`, and inner `padding`; omitted
|
|
710
|
+
* fields fall back to the built-in defaults.
|
|
711
|
+
*/
|
|
712
|
+
pageGeometry?: ComputePageSizeOptions;
|
|
713
|
+
/** Font choices for the settings panel. */
|
|
714
|
+
fonts?: FontChoice[];
|
|
715
|
+
/**
|
|
716
|
+
* Controlled chapter index. When omitted, the reader manages its own
|
|
717
|
+
* chapter state and resets to 0 on EPUB change.
|
|
718
|
+
*/
|
|
719
|
+
chapter?: number;
|
|
720
|
+
/**
|
|
721
|
+
* Controlled spread index. When supplied, the reader is driven by this
|
|
722
|
+
* value and emits {@link MejiroReaderProps.onSpreadIdxChange} on user
|
|
723
|
+
* navigation. Combine with `useReadingPosition` for save/restore.
|
|
724
|
+
*/
|
|
725
|
+
spreadIdx?: number;
|
|
726
|
+
/**
|
|
727
|
+
* Visual theme preset, or `{ name, override }` to layer custom
|
|
728
|
+
* CSS variables on top of a preset. @defaultValue 'light'
|
|
729
|
+
*
|
|
730
|
+
* The selected name is reflected as `data-mejiro-theme` on the reader
|
|
731
|
+
* root, which the bundled CSS uses to swap palettes.
|
|
732
|
+
*/
|
|
733
|
+
theme?: MejiroTheme;
|
|
734
|
+
/**
|
|
735
|
+
* Reading-flow mode. `paginated` (default) shows one spread at a time;
|
|
736
|
+
* `scroll` stacks every page in the chapter inside a vertical scroller.
|
|
737
|
+
*/
|
|
738
|
+
mode?: MejiroReaderMode;
|
|
739
|
+
/**
|
|
740
|
+
* Spread layout. `double` (default) renders two pages; `single` renders
|
|
741
|
+
* only the right page; `auto` flips to `single` for portrait viewports.
|
|
742
|
+
*/
|
|
743
|
+
spreadMode?: MejiroSpreadMode;
|
|
744
|
+
/**
|
|
745
|
+
* How the reader sizes itself in its container. `fill` (default) fills the
|
|
746
|
+
* container height and letterboxes the spread; `width` makes the reader
|
|
747
|
+
* self-size — it derives its height from its width and the page aspect, so an
|
|
748
|
+
* embedding host only has to constrain the width (no height/aspect magic
|
|
749
|
+
* numbers, no letterbox). In `width` mode the reserved `gutterOffset` /
|
|
750
|
+
* `headerOffset` default to 0 so the spread fills edge-to-edge; override via
|
|
751
|
+
* {@link MejiroReaderProps.pageGeometry} if you still want them. @defaultValue 'fill'
|
|
752
|
+
*/
|
|
753
|
+
fit?: MejiroReaderFit;
|
|
754
|
+
/**
|
|
755
|
+
* Enable surface-tap chrome toggling. Tapping the center of the spread
|
|
756
|
+
* (away from buttons) hides the header and chapter panel; tapping again
|
|
757
|
+
* shows them. @defaultValue true
|
|
758
|
+
*/
|
|
759
|
+
enableSurfaceTap?: boolean;
|
|
760
|
+
/**
|
|
761
|
+
* Static fallback rendered while the layout is still hydrating. Pair with
|
|
762
|
+
* {@link renderEpubStatic} to ship server-rendered vertical text that
|
|
763
|
+
* search engines and slow connections can see before the client reader
|
|
764
|
+
* is ready.
|
|
765
|
+
*/
|
|
766
|
+
fallback?: ReactNode;
|
|
767
|
+
/**
|
|
768
|
+
* Extra options merged into the EPUB `fetch` call (URL mode). Useful for
|
|
769
|
+
* sending bearer tokens or cookies.
|
|
770
|
+
*/
|
|
771
|
+
fetchOptions?: RequestInit;
|
|
772
|
+
/**
|
|
773
|
+
* Custom EPUB fetcher used in place of the global `fetch`. Overrides
|
|
774
|
+
* {@link fetchOptions} when set.
|
|
775
|
+
*/
|
|
776
|
+
fetchEpub?: (url: string) => Promise<ArrayBuffer>;
|
|
777
|
+
/**
|
|
778
|
+
* Built-in locale for UI strings (`'en'` / `'ja'`). Pair with `messages`
|
|
779
|
+
* to override individual strings. @defaultValue 'en'
|
|
780
|
+
*/
|
|
781
|
+
locale?: MejiroLocale;
|
|
782
|
+
/**
|
|
783
|
+
* Partial override of the message catalog. Merged on top of the catalog
|
|
784
|
+
* selected by `locale`. Useful for projects that ship their own UI strings
|
|
785
|
+
* without re-implementing every label.
|
|
786
|
+
*/
|
|
787
|
+
messages?: Partial<MejiroMessages>;
|
|
788
|
+
/** Header title text. @defaultValue 'mejiro' */
|
|
789
|
+
title?: string;
|
|
790
|
+
/** Header subtitle. @defaultValue `messages.logoSubtitle` */
|
|
791
|
+
subtitle?: string;
|
|
792
|
+
/**
|
|
793
|
+
* Replaces the default logo block (title + subtitle). Pass `null` to hide
|
|
794
|
+
* the logo while keeping the rest of the header. To remove the entire
|
|
795
|
+
* header, use `enableHeader={false}`.
|
|
796
|
+
*/
|
|
797
|
+
logo?: ReactNode;
|
|
798
|
+
/**
|
|
799
|
+
* Shorthand for a chrome-less reader. When `true`, the defaults for
|
|
800
|
+
* `enableHeader`, `enableChapterNav`, `enableSettings`, `enableStats`, and
|
|
801
|
+
* `enablePageIndicator` flip from `true` to `false`. Explicitly-passed
|
|
802
|
+
* enable* props still win, so you can opt parts back in.
|
|
803
|
+
* @defaultValue false
|
|
804
|
+
*/
|
|
805
|
+
bare?: boolean;
|
|
806
|
+
/** Show the built-in header. @defaultValue `!bare` */
|
|
807
|
+
enableHeader?: boolean;
|
|
808
|
+
/**
|
|
809
|
+
* Show the drop zone affordance. SaaS-style readers should keep this off
|
|
810
|
+
* (the host controls which EPUB is delivered); set true to accept
|
|
811
|
+
* user-supplied books.
|
|
812
|
+
* @defaultValue false
|
|
813
|
+
*/
|
|
814
|
+
enableDropZone?: boolean;
|
|
815
|
+
/** Show the chapter selector in the header. @defaultValue `!bare` */
|
|
816
|
+
enableChapterNav?: boolean;
|
|
817
|
+
/**
|
|
818
|
+
* Where to render the built-in chapter navigation.
|
|
819
|
+
* @defaultValue 'select'
|
|
820
|
+
*/
|
|
821
|
+
chapterNavMode?: MejiroChapterNavMode;
|
|
822
|
+
/** Show the settings panel toggle. @defaultValue `!bare` */
|
|
823
|
+
enableSettings?: boolean;
|
|
824
|
+
/**
|
|
825
|
+
* Replaces the built-in settings controls with custom UI while keeping the
|
|
826
|
+
* panel chrome and its open/close accordion. Receives a
|
|
827
|
+
* {@link MejiroReaderSettingsSlot} wired to the live reader, so a host can
|
|
828
|
+
* build its own settings form without a parallel `options` shadow. The header
|
|
829
|
+
* "Settings" button still toggles the panel. `enableSettings` remains the
|
|
830
|
+
* on/off switch.
|
|
831
|
+
*/
|
|
832
|
+
renderSettings?: (slot: MejiroReaderSettingsSlot) => ReactNode;
|
|
833
|
+
/** Show the image-overlay editing/demo button. @defaultValue false */
|
|
834
|
+
enableImageOverlay?: boolean;
|
|
835
|
+
/** Show the stats line. @defaultValue `!bare` */
|
|
836
|
+
enableStats?: boolean;
|
|
837
|
+
/** Bind ArrowLeft/ArrowRight to navigation. @defaultValue true */
|
|
838
|
+
enableKeyboard?: boolean;
|
|
839
|
+
/** Show the "n / total" indicator. @defaultValue `!bare` */
|
|
840
|
+
enablePageIndicator?: boolean;
|
|
841
|
+
/**
|
|
842
|
+
* Which page of a spread shows its page number in the running head.
|
|
843
|
+
* `'both'` numbers each page (right = odd, left = even), `'right'` /
|
|
844
|
+
* `'left'` number only that side, `'none'` hides them (the
|
|
845
|
+
* {@link MejiroReaderProps.enablePageIndicator} "n / total" badge is
|
|
846
|
+
* independent). @defaultValue 'both'
|
|
847
|
+
*/
|
|
848
|
+
pageNumbers?: PageNumberDisplay;
|
|
849
|
+
/**
|
|
850
|
+
* Reader-side annotations to render as highlights. Each annotation whose
|
|
851
|
+
* `chapter` matches the current chapter is converted to spread-local
|
|
852
|
+
* rectangles via `ChapterLayout.selectionRects` and drawn on top of the
|
|
853
|
+
* page content. Pair with {@link useAnnotations} for persistence, or pass
|
|
854
|
+
* any shape that satisfies `{ chapter, start, end }`.
|
|
855
|
+
*/
|
|
856
|
+
annotations?: ReadonlyArray<{
|
|
857
|
+
chapter: number;
|
|
858
|
+
start: InChapterAnchor;
|
|
859
|
+
end: InChapterAnchor;
|
|
860
|
+
color?: string;
|
|
861
|
+
}>;
|
|
862
|
+
/** Called after a successful EPUB load. */
|
|
863
|
+
onLoad?: (book: EpubBook) => void;
|
|
864
|
+
/** Called when loading or parsing an EPUB fails. */
|
|
865
|
+
onError?: (error: Error) => void;
|
|
866
|
+
/** Called when the chapter index changes. */
|
|
867
|
+
onChapterChange?: (chapter: number) => void;
|
|
868
|
+
/** Called when the spread index changes (alias: `onSpreadIdxChange`). */
|
|
869
|
+
onSpreadChange?: (spreadIdx: number) => void;
|
|
870
|
+
/** Called when the spread index changes — pair with the `spreadIdx` prop for controlled use. */
|
|
871
|
+
onSpreadIdxChange?: (spreadIdx: number) => void;
|
|
872
|
+
/**
|
|
873
|
+
* Called when the reader leaves a spread. Receives the anchor of the
|
|
874
|
+
* spread that was just left and the dwell time in milliseconds (computed
|
|
875
|
+
* via `performance.now()`). Useful for engagement analytics.
|
|
876
|
+
*/
|
|
877
|
+
onPageRead?: (anchor: ReadingAnchor, dwellMs: number) => void;
|
|
878
|
+
/**
|
|
879
|
+
* Called when the reader reaches the last spread of a chapter. Same
|
|
880
|
+
* trigger as the `chapterFinished` event on {@link MejiroReaderHandle.subscribe}.
|
|
881
|
+
*/
|
|
882
|
+
onChapterCompleted?: (chapter: number) => void;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Controlled-source variant: render a pre-parsed `EpubBook`. Pass `null`
|
|
886
|
+
* to render an empty reader (e.g. while the book is still loading on the host).
|
|
887
|
+
*/
|
|
888
|
+
interface MejiroReaderControlledProps extends MejiroReaderCommonProps {
|
|
889
|
+
/** Pre-parsed EPUB. Cannot be combined with `epubUrl` / `manuscript`. */
|
|
890
|
+
epub: EpubBook | null;
|
|
891
|
+
epubUrl?: never;
|
|
892
|
+
manuscript?: never;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* URL-source variant: the reader fetches and parses the EPUB itself.
|
|
896
|
+
* Use this for "just open this book" scenarios.
|
|
897
|
+
*/
|
|
898
|
+
interface MejiroReaderUrlProps extends MejiroReaderCommonProps {
|
|
899
|
+
/** EPUB URL fetched on mount. Cannot be combined with `epub` / `manuscript`. */
|
|
900
|
+
epubUrl: string;
|
|
901
|
+
epub?: never;
|
|
902
|
+
manuscript?: never;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* File-source variant: the reader exposes its drop zone / file picker.
|
|
906
|
+
* Neither `epub` nor `epubUrl` is supplied — useful for free-form viewers
|
|
907
|
+
* that accept user-supplied books.
|
|
908
|
+
*/
|
|
909
|
+
interface MejiroReaderFileProps extends MejiroReaderCommonProps {
|
|
910
|
+
epub?: never;
|
|
911
|
+
epubUrl?: never;
|
|
912
|
+
manuscript?: never;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Manuscript-source variant: render manuscript chapters directly without an
|
|
916
|
+
* EPUB ZIP round-trip. Designed for live preview in custom manuscript editors;
|
|
917
|
+
* each chapter body is split into paragraphs on blank lines and run through
|
|
918
|
+
* {@link parseManuscript} before layout.
|
|
919
|
+
*/
|
|
920
|
+
interface MejiroReaderManuscriptProps extends MejiroReaderCommonProps {
|
|
921
|
+
/** Manuscript chapters to render. Cannot be combined with `epub` / `epubUrl`. */
|
|
922
|
+
manuscript: readonly ManuscriptChapter[];
|
|
923
|
+
/** Manuscript notation dialect. @defaultValue `'mejiro'` */
|
|
924
|
+
dialect?: ManuscriptDialect;
|
|
925
|
+
epub?: never;
|
|
926
|
+
epubUrl?: never;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Props for {@link MejiroReader}. Discriminated union of the four source
|
|
930
|
+
* modes — TypeScript prevents passing more than one source at once.
|
|
931
|
+
*/
|
|
932
|
+
type MejiroReaderProps = MejiroReaderControlledProps | MejiroReaderUrlProps | MejiroReaderFileProps | MejiroReaderManuscriptProps;
|
|
933
|
+
declare const MejiroReader: react.ForwardRefExoticComponent<MejiroReaderProps & react.RefAttributes<MejiroReaderHandle>>;
|
|
934
|
+
|
|
935
|
+
/** Props for {@link MejiroScrollView}. */
|
|
936
|
+
interface MejiroScrollViewProps {
|
|
937
|
+
/** Layout containing the pages to render. */
|
|
938
|
+
layout: ChapterLayout;
|
|
939
|
+
/** Page width in px. */
|
|
940
|
+
pageWidth: number;
|
|
941
|
+
/** Page height in px. */
|
|
942
|
+
pageHeight: number;
|
|
943
|
+
/** Content area height (px). */
|
|
944
|
+
contentHeight: number;
|
|
945
|
+
/** CSS font family applied to the content. */
|
|
946
|
+
fontFamily?: FontFamily;
|
|
947
|
+
/** Font size override (px). */
|
|
948
|
+
fontSize?: number;
|
|
949
|
+
/** Line spacing multiplier. */
|
|
950
|
+
lineSpacing?: number;
|
|
951
|
+
/** Force slot-based rendering on every page. */
|
|
952
|
+
slotMode?: boolean;
|
|
953
|
+
/**
|
|
954
|
+
* Visible page index reported to the parent as the user scrolls. The page
|
|
955
|
+
* with the largest intersection with the viewport is treated as visible.
|
|
956
|
+
*/
|
|
957
|
+
onVisiblePageChange?: (pageIdx: number) => void;
|
|
958
|
+
/**
|
|
959
|
+
* Target page to scroll into view. When set, the view scrolls so that the
|
|
960
|
+
* matching page sits at the top of the scroll container.
|
|
961
|
+
*/
|
|
962
|
+
scrollToPage?: number;
|
|
963
|
+
/** Vertical gap between pages (px). @defaultValue 24 */
|
|
964
|
+
pageGap?: number;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Continuous-scroll variant of {@link MejiroSpread}. Stacks every page in
|
|
968
|
+
* the chapter inside a vertically scrollable container. The visible page is
|
|
969
|
+
* detected via `IntersectionObserver` and reported through
|
|
970
|
+
* {@link MejiroScrollViewProps.onVisiblePageChange}.
|
|
971
|
+
*/
|
|
972
|
+
declare function MejiroScrollView({ layout, pageWidth, pageHeight, contentHeight, fontFamily, fontSize, lineSpacing, onVisiblePageChange, scrollToPage, pageGap, }: MejiroScrollViewProps): ReactNode;
|
|
973
|
+
|
|
974
|
+
/** Props for {@link MejiroSelectionLayer}. */
|
|
975
|
+
interface MejiroSelectionLayerProps {
|
|
976
|
+
/**
|
|
977
|
+
* Spread-local rectangles to highlight. Compute these via
|
|
978
|
+
* {@link ChapterLayout.selectionRects} and pass the full array; the layer
|
|
979
|
+
* picks the entries matching {@link MejiroSelectionLayerProps.side}.
|
|
980
|
+
*/
|
|
981
|
+
rects: readonly AnchorRect[];
|
|
982
|
+
/** Which page-side this layer is rendered into. */
|
|
983
|
+
side: 'right' | 'left';
|
|
984
|
+
/** Optional class added to the outer wrapper. */
|
|
985
|
+
className?: string;
|
|
986
|
+
/** Optional inline style merged onto the outer wrapper. */
|
|
987
|
+
style?: CSSProperties;
|
|
988
|
+
/** Optional class for each rectangle (defaults to `mejiro-selection-rect`). */
|
|
989
|
+
rectClassName?: string;
|
|
990
|
+
/** Optional inline style for each rectangle. */
|
|
991
|
+
rectStyle?: CSSProperties;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Renders a selection-highlight overlay inside a page-content container.
|
|
995
|
+
*
|
|
996
|
+
* Use as a child of `.mejiro-reader-page-clip` (or any element that shares the
|
|
997
|
+
* page's content coordinate frame). The layer is pointer-transparent so it
|
|
998
|
+
* does not interfere with selection drag on the page below.
|
|
999
|
+
*/
|
|
1000
|
+
declare function MejiroSelectionLayer({ rects, side, className, style, rectClassName, rectStyle, }: MejiroSelectionLayerProps): ReactNode;
|
|
1001
|
+
|
|
1002
|
+
/** A single volume in a multi-book library. */
|
|
1003
|
+
interface VolumeInfo<T = unknown> {
|
|
1004
|
+
/** Stable identifier (e.g. ISBN, slug, internal ID). */
|
|
1005
|
+
id: string;
|
|
1006
|
+
/** Human-readable title shown in pickers. */
|
|
1007
|
+
label: string;
|
|
1008
|
+
/** Optional author / byline. */
|
|
1009
|
+
author?: string;
|
|
1010
|
+
/** Optional cover image URL. */
|
|
1011
|
+
cover?: string;
|
|
1012
|
+
/** Free-form metadata attached by the host. */
|
|
1013
|
+
meta?: T;
|
|
1014
|
+
}
|
|
1015
|
+
/** Options for {@link useLibrary}. */
|
|
1016
|
+
interface UseLibraryOptions<T = unknown> {
|
|
1017
|
+
/** The volumes available to the reader. */
|
|
1018
|
+
volumes: readonly VolumeInfo<T>[];
|
|
1019
|
+
/** ID of the volume to start on. Defaults to the first entry. */
|
|
1020
|
+
initialVolumeId?: string;
|
|
1021
|
+
/** Called whenever the active volume changes. */
|
|
1022
|
+
onChange?: (volume: VolumeInfo<T>) => void;
|
|
1023
|
+
}
|
|
1024
|
+
/** Return value of {@link useLibrary}. */
|
|
1025
|
+
interface UseLibraryReturn<T = unknown> {
|
|
1026
|
+
/** The list of volumes (same reference as the options input). */
|
|
1027
|
+
list: readonly VolumeInfo<T>[];
|
|
1028
|
+
/** Currently active volume, or `null` for an empty library. */
|
|
1029
|
+
current: VolumeInfo<T> | null;
|
|
1030
|
+
/** Index of the current volume, or `-1` for an empty library. */
|
|
1031
|
+
currentIndex: number;
|
|
1032
|
+
/** Advance to the next volume in the list (no-op at the end). */
|
|
1033
|
+
next(): void;
|
|
1034
|
+
/** Go back to the previous volume in the list (no-op at the start). */
|
|
1035
|
+
prev(): void;
|
|
1036
|
+
/** Jump to a volume by ID. No-op when the ID is not in the list. */
|
|
1037
|
+
goTo(id: string): void;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Headless hook for managing a multi-volume reading session. Pair it with
|
|
1041
|
+
* {@link MejiroReader} (driving its `epub` / `epubUrl` prop) or
|
|
1042
|
+
* {@link MejiroShelf} (visual picker).
|
|
1043
|
+
*/
|
|
1044
|
+
declare function useLibrary<T = unknown>(options: UseLibraryOptions<T>): UseLibraryReturn<T>;
|
|
1045
|
+
|
|
1046
|
+
/** Props for {@link MejiroShelf}. */
|
|
1047
|
+
interface MejiroShelfProps<T = unknown> {
|
|
1048
|
+
/** Volumes to display. */
|
|
1049
|
+
volumes: readonly VolumeInfo<T>[];
|
|
1050
|
+
/** ID of the currently active volume (highlighted in the grid). */
|
|
1051
|
+
currentId?: string;
|
|
1052
|
+
/** Called when the user picks a volume. */
|
|
1053
|
+
onSelect?: (volume: VolumeInfo<T>) => void;
|
|
1054
|
+
/** Heading shown above the grid. @defaultValue 'Library' */
|
|
1055
|
+
title?: string;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Visual bookshelf picker. Renders each volume as a card with cover, label,
|
|
1059
|
+
* and author. Pair with {@link useLibrary} to drive selection.
|
|
1060
|
+
*/
|
|
1061
|
+
declare function MejiroShelf<T = unknown>({ volumes, currentId, onSelect, title, }: MejiroShelfProps<T>): ReactNode;
|
|
1062
|
+
|
|
1063
|
+
/** A single image overlay placed on a specific spread. */
|
|
1064
|
+
interface MultiImageItem {
|
|
1065
|
+
/** Stable ID for the overlay (auto-assigned on add). */
|
|
1066
|
+
id: string;
|
|
1067
|
+
/** Position and size in pixels, relative to the right page. */
|
|
1068
|
+
rect: ImageRect;
|
|
1069
|
+
}
|
|
1070
|
+
/** Options for {@link useMultiImageOverlay}. */
|
|
1071
|
+
interface UseMultiImageOverlayOptions {
|
|
1072
|
+
/** Default image width. @defaultValue 120 */
|
|
66
1073
|
defaultWidth?: number;
|
|
67
|
-
/** Default image height
|
|
1074
|
+
/** Default image height. @defaultValue 160 */
|
|
68
1075
|
defaultHeight?: number;
|
|
69
|
-
/** Default x
|
|
1076
|
+
/** Default x. @defaultValue 80 */
|
|
70
1077
|
defaultX?: number;
|
|
71
|
-
/** Default y
|
|
1078
|
+
/** Default y. @defaultValue 100 */
|
|
72
1079
|
defaultY?: number;
|
|
73
|
-
/** Margin around
|
|
1080
|
+
/** Margin around each image, in pixels. Defaults to the book's font size. */
|
|
74
1081
|
margin?: number;
|
|
1082
|
+
/** Called after every reflow caused by adding/removing/moving an image. */
|
|
1083
|
+
onUpdate?: (spread: SpreadResult) => void;
|
|
75
1084
|
}
|
|
76
|
-
/** Return value of {@link
|
|
77
|
-
interface
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
|
|
1085
|
+
/** Return value of {@link useMultiImageOverlay}. */
|
|
1086
|
+
interface UseMultiImageOverlayReturn {
|
|
1087
|
+
/** Map of all images keyed by spread index. */
|
|
1088
|
+
imagesBySpread: Map<number, MultiImageItem[]>;
|
|
1089
|
+
/** Images on the current spread. */
|
|
1090
|
+
currentImages: MultiImageItem[];
|
|
1091
|
+
/** Whether any spread has at least one image. */
|
|
1092
|
+
hasImages: boolean;
|
|
1093
|
+
/** Add a new image on the current spread. */
|
|
1094
|
+
addImage: (overrides?: Partial<ImageRect>) => MultiImageItem;
|
|
1095
|
+
/** Remove the image with the given id. */
|
|
1096
|
+
removeImage: (id: string) => void;
|
|
1097
|
+
/** Replace an image's rectangle. */
|
|
1098
|
+
updateImage: (id: string, rect: Partial<ImageRect>) => void;
|
|
1099
|
+
/** Remove every image (or only those on `spreadIdx` if given). */
|
|
1100
|
+
clearImages: (spreadIdx?: number) => void;
|
|
1101
|
+
/** Pointer-down handler for the overlay body (drag). */
|
|
1102
|
+
onOverlayPointerDown: (id: string, e: PointerEvent) => void;
|
|
86
1103
|
/** Pointer-down handler for the resize handle. */
|
|
87
|
-
onResizePointerDown: (e: PointerEvent) => void;
|
|
1104
|
+
onResizePointerDown: (id: string, e: PointerEvent) => void;
|
|
88
1105
|
}
|
|
89
1106
|
/**
|
|
90
|
-
* React hook that manages
|
|
91
|
-
* with automatic
|
|
1107
|
+
* React hook that manages multiple draggable/resizable image overlays
|
|
1108
|
+
* per spread with automatic reflow via {@link ChapterLayout.setImages}.
|
|
1109
|
+
*/
|
|
1110
|
+
declare function useMultiImageOverlay(layout: ChapterLayout | null, spreadIdx: number, options?: UseMultiImageOverlayOptions): UseMultiImageOverlayReturn;
|
|
1111
|
+
|
|
1112
|
+
/** Per-page header data. */
|
|
1113
|
+
interface PageHeaderData {
|
|
1114
|
+
/** Running title (left-aligned). */
|
|
1115
|
+
title?: string;
|
|
1116
|
+
/** Page number (right-aligned). `null` hides it. */
|
|
1117
|
+
pageNumber?: number | null;
|
|
1118
|
+
}
|
|
1119
|
+
/** Props for {@link MejiroSpread}. */
|
|
1120
|
+
interface MejiroSpreadProps {
|
|
1121
|
+
/** Spread data to render. */
|
|
1122
|
+
spread: SpreadResult;
|
|
1123
|
+
/** Page width (px). */
|
|
1124
|
+
pageWidth: number;
|
|
1125
|
+
/** Page height (px). */
|
|
1126
|
+
pageHeight: number;
|
|
1127
|
+
/** Content area height (px). */
|
|
1128
|
+
contentHeight: number;
|
|
1129
|
+
/** CSS font family applied to the content. */
|
|
1130
|
+
fontFamily?: FontFamily;
|
|
1131
|
+
/** Font size override (px). */
|
|
1132
|
+
fontSize?: number;
|
|
1133
|
+
/** Line spacing multiplier (slot mode). */
|
|
1134
|
+
lineSpacing?: number;
|
|
1135
|
+
/** Animate the page turn. */
|
|
1136
|
+
turning?: boolean;
|
|
1137
|
+
/** Header data for the right page. */
|
|
1138
|
+
rightHeader?: PageHeaderData;
|
|
1139
|
+
/** Header data for the left page. */
|
|
1140
|
+
leftHeader?: PageHeaderData;
|
|
1141
|
+
/** Image overlays on the current spread. */
|
|
1142
|
+
images?: MultiImageItem[];
|
|
1143
|
+
/** Force slot-mode rendering on both pages. */
|
|
1144
|
+
slotMode?: boolean;
|
|
1145
|
+
/** Custom renderer for per-page headers. */
|
|
1146
|
+
renderPageHeader?: (side: 'right' | 'left', header: PageHeaderData) => ReactNode;
|
|
1147
|
+
/** Custom indicator placed inside the spread (e.g. {@link MejiroPageIndicator}). */
|
|
1148
|
+
indicator?: ReactNode;
|
|
1149
|
+
/** Called when the previous-page zone is clicked. */
|
|
1150
|
+
onPrev?: () => void;
|
|
1151
|
+
/** Called when the next-page zone is clicked. */
|
|
1152
|
+
onNext?: () => void;
|
|
1153
|
+
/** Pointer-down on an image overlay. */
|
|
1154
|
+
onImagePointerDown?: (id: string, e: PointerEvent) => void;
|
|
1155
|
+
/** Pointer-down on an image resize handle. */
|
|
1156
|
+
onImageResizePointerDown?: (id: string, e: PointerEvent) => void;
|
|
1157
|
+
/** Image overlay close button. */
|
|
1158
|
+
onImageClose?: (id: string) => void;
|
|
1159
|
+
/**
|
|
1160
|
+
* Resolves a spread-local pixel coordinate to an in-chapter anchor.
|
|
1161
|
+
* Typically `(x, y) => layout.anchorAtCoord(spreadIdx, x, y)`. When provided
|
|
1162
|
+
* together with {@link MejiroSpreadProps.onSelectionChange}, the spread
|
|
1163
|
+
* enables pointer-drag selection on the page content area.
|
|
1164
|
+
*/
|
|
1165
|
+
anchorAtCoord?: (x: number, y: number) => InChapterAnchor | null;
|
|
1166
|
+
/**
|
|
1167
|
+
* Selection rectangles to render as a highlight overlay. Compute via
|
|
1168
|
+
* {@link ChapterLayout.selectionRects}. Entries with `spreadIdx` other than
|
|
1169
|
+
* the current spread are ignored.
|
|
1170
|
+
*/
|
|
1171
|
+
selectionRects?: readonly AnchorRect[];
|
|
1172
|
+
/**
|
|
1173
|
+
* Called when the user drags-selects text on the spread.
|
|
1174
|
+
* Called with `null` on a single-click (caret) where start equals end.
|
|
1175
|
+
*/
|
|
1176
|
+
onSelectionChange?: (range: AnchorRange | null) => void;
|
|
1177
|
+
/**
|
|
1178
|
+
* Hide the left page and render only the right page (centered). Use this
|
|
1179
|
+
* for portrait viewports or when a reader explicitly opts into single-page
|
|
1180
|
+
* mode.
|
|
1181
|
+
*/
|
|
1182
|
+
singlePage?: boolean;
|
|
1183
|
+
/**
|
|
1184
|
+
* Called when the user makes a quick swipe gesture on the spread.
|
|
1185
|
+
* `direction === 'next'` means the user swiped from right to left (the
|
|
1186
|
+
* natural direction in vertical-rl for advancing).
|
|
1187
|
+
*/
|
|
1188
|
+
onSwipe?: (direction: 'next' | 'prev') => void;
|
|
1189
|
+
/** Called when the user taps the center area without swiping. */
|
|
1190
|
+
onSurfaceTap?: () => void;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Renders a two-page spread with the book frame, page chrome, navigation
|
|
1194
|
+
* zones, and image overlays. Used inside `mejiro-reader-surface`.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function MejiroSpread({ spread, pageWidth, pageHeight, contentHeight, fontFamily, fontSize, lineSpacing, turning, rightHeader, leftHeader, images, renderPageHeader, indicator, onPrev, onNext, onImagePointerDown, onImageResizePointerDown, onImageClose, anchorAtCoord, selectionRects, onSelectionChange, singlePage, onSwipe, onSurfaceTap, }: MejiroSpreadProps): ReactNode;
|
|
1197
|
+
|
|
1198
|
+
/** Props for {@link MejiroStats}. */
|
|
1199
|
+
interface MejiroStatsProps {
|
|
1200
|
+
/** Chapter currently displayed. */
|
|
1201
|
+
chapter: EpubChapter | null;
|
|
1202
|
+
/** Total page count for the current layout. */
|
|
1203
|
+
totalPages: number;
|
|
1204
|
+
/** Most recent layout time in ms. */
|
|
1205
|
+
elapsedMs: number;
|
|
1206
|
+
/** Optional label for the current font (e.g. "Noto Serif JP 16px"). */
|
|
1207
|
+
fontLabel?: string;
|
|
1208
|
+
/** Show estimated reading time in the stats line. @defaultValue false */
|
|
1209
|
+
showReadingTime?: boolean;
|
|
1210
|
+
/** Characters-per-minute used for the reading-time estimate. @defaultValue 600 */
|
|
1211
|
+
cpm?: number;
|
|
1212
|
+
/** Locale used to format the reading-time label. @defaultValue 'ja' */
|
|
1213
|
+
readingTimeLocale?: 'ja' | 'en';
|
|
1214
|
+
}
|
|
1215
|
+
/** Compact reading stats line. */
|
|
1216
|
+
declare function MejiroStats({ chapter, totalPages, elapsedMs, fontLabel, showReadingTime, cpm, readingTimeLocale, }: MejiroStatsProps): ReactNode;
|
|
1217
|
+
|
|
1218
|
+
/** Props for {@link MejiroToc}. */
|
|
1219
|
+
interface MejiroTocProps {
|
|
1220
|
+
/** EPUB whose chapter list is rendered. */
|
|
1221
|
+
epub: EpubBook;
|
|
1222
|
+
/** Current reading anchor; the matching chapter is highlighted. */
|
|
1223
|
+
currentAnchor?: ReadingAnchor | null;
|
|
1224
|
+
/** Show a search input that filters chapter titles. @defaultValue false */
|
|
1225
|
+
searchable?: boolean;
|
|
1226
|
+
/** Heading displayed above the list. @defaultValue 'Contents' */
|
|
1227
|
+
title?: string;
|
|
1228
|
+
/** Placeholder shown in the search input. @defaultValue 'Search chapters…' */
|
|
1229
|
+
searchPlaceholder?: string;
|
|
1230
|
+
/** Called when the user picks a chapter. */
|
|
1231
|
+
onSelect?: (chapter: number) => void;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Searchable, current-anchor-aware table of contents. Long-form replacement
|
|
1235
|
+
* for {@link MejiroChapterNav}. Supersedes the older `MejiroChapterNav`
|
|
1236
|
+
* for new code — the panel variant remains for backwards compatibility.
|
|
1237
|
+
*/
|
|
1238
|
+
declare function MejiroToc({ epub, currentAnchor, searchable, title, searchPlaceholder, onSelect, }: MejiroTocProps): ReactNode;
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* A user-authored annotation on a book — a half-open range of in-chapter
|
|
1242
|
+
* anchors plus optional metadata (color, note). Pair with the `annotations`
|
|
1243
|
+
* prop on {@link MejiroReader} to render highlights, or with `goToAnchor` to
|
|
1244
|
+
* implement bookmarks.
|
|
1245
|
+
*/
|
|
1246
|
+
interface Annotation {
|
|
1247
|
+
/** Stable identifier. Auto-generated when omitted from {@link useAnnotations.add}. */
|
|
1248
|
+
id: string;
|
|
1249
|
+
/** Chapter the annotation lives in. */
|
|
1250
|
+
chapter: number;
|
|
1251
|
+
/** Range start (inclusive). */
|
|
1252
|
+
start: InChapterAnchor;
|
|
1253
|
+
/** Range end (exclusive). Equal to `start` for caret-style bookmarks. */
|
|
1254
|
+
end: InChapterAnchor;
|
|
1255
|
+
/** Free-form color identifier (e.g. `'yellow'`, `'#ffd166'`). */
|
|
1256
|
+
color?: string;
|
|
1257
|
+
/** Optional note text attached to the annotation. */
|
|
1258
|
+
note?: string;
|
|
1259
|
+
/** Unix epoch ms. Auto-set on {@link useAnnotations.add} when omitted. */
|
|
1260
|
+
createdAt?: number;
|
|
1261
|
+
}
|
|
1262
|
+
/** Minimal storage interface required by {@link useAnnotations}. */
|
|
1263
|
+
interface AnnotationsStorage {
|
|
1264
|
+
getItem(key: string): string | null;
|
|
1265
|
+
setItem(key: string, value: string): void;
|
|
1266
|
+
removeItem(key: string): void;
|
|
1267
|
+
}
|
|
1268
|
+
/** Options for {@link useAnnotations}. */
|
|
1269
|
+
interface UseAnnotationsOptions {
|
|
1270
|
+
/** Storage key — usually scoped per-book (e.g. `mejiro:annotations:${bookId}`). */
|
|
1271
|
+
key: string;
|
|
1272
|
+
/**
|
|
1273
|
+
* Storage backend. Defaults to `window.localStorage` when available.
|
|
1274
|
+
* SSR consumers can omit this and the hook will fall back to in-memory.
|
|
1275
|
+
*/
|
|
1276
|
+
storage?: AnnotationsStorage;
|
|
1277
|
+
/** Throttle ms between writes. @defaultValue 250 */
|
|
1278
|
+
throttleMs?: number;
|
|
1279
|
+
/**
|
|
1280
|
+
* Called immediately after each mutation (`add` / `remove` / `update` /
|
|
1281
|
+
* `clear`) with the new annotation list. Use to mirror state to a server
|
|
1282
|
+
* — `storage` only covers the local persistence side. Not invoked on
|
|
1283
|
+
* initial hydration or when the `key` changes.
|
|
1284
|
+
*/
|
|
1285
|
+
onChange?: (next: readonly Annotation[]) => void;
|
|
1286
|
+
}
|
|
1287
|
+
/** Return value of {@link useAnnotations}. */
|
|
1288
|
+
interface UseAnnotationsReturn {
|
|
1289
|
+
/** Currently saved annotations, sorted by chapter / paragraph / charIndex. */
|
|
1290
|
+
annotations: readonly Annotation[];
|
|
1291
|
+
/** Add a new annotation. `id` and `createdAt` are auto-filled when omitted. */
|
|
1292
|
+
add(input: Omit<Annotation, 'id' | 'createdAt'> & Partial<Pick<Annotation, 'id' | 'createdAt'>>): Annotation;
|
|
1293
|
+
/** Remove an annotation by id. */
|
|
1294
|
+
remove(id: string): void;
|
|
1295
|
+
/** Patch an annotation by id. Pass `undefined` on a field to leave it untouched. */
|
|
1296
|
+
update(id: string, patch: Partial<Omit<Annotation, 'id'>>): void;
|
|
1297
|
+
/** Remove all annotations. */
|
|
1298
|
+
clear(): void;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Persistence helper for reader annotations. Returns the saved annotation list
|
|
1302
|
+
* plus add / update / remove helpers. Pair with the `annotations` prop on
|
|
1303
|
+
* {@link MejiroReader} to render the corresponding highlight rectangles.
|
|
92
1304
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
1305
|
+
* ```tsx
|
|
1306
|
+
* const { annotations, add } = useAnnotations({ key: `mejiro:ann:${bookId}` });
|
|
1307
|
+
* const handle = useRef<MejiroReaderHandle>(null);
|
|
1308
|
+
*
|
|
1309
|
+
* // Save a highlight at the current visible range:
|
|
1310
|
+
* const range = handle.current?.getVisibleRange();
|
|
1311
|
+
* if (range) add({ chapter: 0, start: range.start, end: range.end, color: 'yellow' });
|
|
1312
|
+
*
|
|
1313
|
+
* <MejiroReader epub={book} annotations={annotations} ref={handle} />
|
|
1314
|
+
* ```
|
|
1315
|
+
*/
|
|
1316
|
+
declare function useAnnotations(options: UseAnnotationsOptions): UseAnnotationsReturn;
|
|
1317
|
+
|
|
1318
|
+
/** Options for {@link useChapterLayout}. */
|
|
1319
|
+
interface UseChapterLayoutOptions {
|
|
1320
|
+
/** Observe the surface element for size changes and re-flow. @defaultValue true */
|
|
1321
|
+
enableResize?: boolean;
|
|
1322
|
+
/** Debounce window (ms) applied to size-triggered re-flows. @defaultValue 120 */
|
|
1323
|
+
resizeDebounce?: number;
|
|
1324
|
+
/**
|
|
1325
|
+
* Resolver for page-geometry overrides forwarded to
|
|
1326
|
+
* {@link MejiroBook.computePageSize} — e.g. to shrink the reserved
|
|
1327
|
+
* `gutterOffset` / `headerOffset` so the pages fill their frame. Called on
|
|
1328
|
+
* every (re)layout so it may return a value that changes over time.
|
|
1329
|
+
*/
|
|
1330
|
+
pageGeometry?: () => ComputePageSizeOptions | undefined;
|
|
1331
|
+
/**
|
|
1332
|
+
* Capture the current reading position from the outgoing layout, just before
|
|
1333
|
+
* a **reflow** (non-blank) re-layout replaces it. The returned anchor is
|
|
1334
|
+
* exposed via {@link UseChapterLayoutReturn.pendingRestore} and should be
|
|
1335
|
+
* applied once the new layout commits. Return `null` to skip preservation.
|
|
1336
|
+
* Never called for blank (content-change) re-layouts.
|
|
1337
|
+
*
|
|
1338
|
+
* A reflow yields a brand-new {@link ChapterLayout} object, which resets any
|
|
1339
|
+
* downstream spread index to 0; capturing here and restoring after keeps the
|
|
1340
|
+
* reader on the same passage across resizes and font / option changes.
|
|
1341
|
+
*/
|
|
1342
|
+
capturePosition?: (layout: ChapterLayout) => InChapterAnchor | null;
|
|
1343
|
+
}
|
|
1344
|
+
/** Options for {@link UseChapterLayoutReturn.recompute}. */
|
|
1345
|
+
interface RecomputeOptions {
|
|
1346
|
+
/**
|
|
1347
|
+
* Blank the current layout while the new one is computed. Use for content
|
|
1348
|
+
* changes (chapter / book swaps); skip for size or option changes so the
|
|
1349
|
+
* previous spread stays visible until the re-flow completes (no flicker).
|
|
1350
|
+
* @defaultValue true
|
|
1351
|
+
*/
|
|
1352
|
+
blank?: boolean;
|
|
1353
|
+
}
|
|
1354
|
+
/** Page dimensions returned by {@link MejiroBook.computePageSize}. */
|
|
1355
|
+
interface PageDimensions {
|
|
1356
|
+
pageWidth: number;
|
|
1357
|
+
pageHeight: number;
|
|
1358
|
+
contentHeight: number;
|
|
1359
|
+
}
|
|
1360
|
+
/** Return value of {@link useChapterLayout}. */
|
|
1361
|
+
interface UseChapterLayoutReturn {
|
|
1362
|
+
/** Current layout, or `null` before the first computation. */
|
|
1363
|
+
layout: ChapterLayout | null;
|
|
1364
|
+
/** Page width in pixels. */
|
|
1365
|
+
pageWidth: number;
|
|
1366
|
+
/** Page height in pixels. */
|
|
1367
|
+
pageHeight: number;
|
|
1368
|
+
/** Content height in pixels. */
|
|
1369
|
+
contentHeight: number;
|
|
1370
|
+
/** Most recent layout time (ms). */
|
|
1371
|
+
elapsedMs: number;
|
|
1372
|
+
/** Force a fresh layout computation. */
|
|
1373
|
+
recompute: (opts?: RecomputeOptions) => Promise<void>;
|
|
1374
|
+
/**
|
|
1375
|
+
* Anchor captured before the most recent reflow re-layout, awaiting
|
|
1376
|
+
* restoration into the new {@link layout}. Consume it in a layout effect keyed
|
|
1377
|
+
* on `layout` (after any index reset) and clear it back to `null`.
|
|
1378
|
+
*/
|
|
1379
|
+
pendingRestore: RefObject<InChapterAnchor | null>;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* React hook that lays out the currently-selected chapter and re-flows when the
|
|
1383
|
+
* surface resizes.
|
|
1384
|
+
*
|
|
1385
|
+
* Re-layouts whenever `book`, `epub`, or `chapterIndex` change, against the
|
|
1386
|
+
* dimensions of `surface`. The `surface` element is observed with a
|
|
1387
|
+
* {@link ResizeObserver}: the first observation runs immediately (so a reader
|
|
1388
|
+
* mounted before its container had a final box is still sized correctly on
|
|
1389
|
+
* first paint), and later size changes trigger a debounced **full re-layout**.
|
|
1390
|
+
*
|
|
1391
|
+
* A full re-layout — rather than a `ChapterLayout.resize()` fast-path — is used
|
|
1392
|
+
* for size changes on purpose: the fast-path only stretches `pageWidth` and does
|
|
1393
|
+
* not re-paginate, leaving sparse, half-empty pages after a non-trivial size
|
|
1394
|
+
* delta. Because a full re-layout yields a new {@link ChapterLayout} object
|
|
1395
|
+
* (resetting any downstream spread index to 0), the reading position is
|
|
1396
|
+
* preserved across reflows via {@link UseChapterLayoutOptions.capturePosition}
|
|
1397
|
+
* and {@link UseChapterLayoutReturn.pendingRestore}.
|
|
1398
|
+
*
|
|
1399
|
+
* @param book - The book instance to lay out with.
|
|
1400
|
+
* @param epub - The current parsed EPUB.
|
|
1401
|
+
* @param chapterIndex - Zero-based chapter index to lay out.
|
|
1402
|
+
* @param surface - DOM ref for the reading surface used for page sizing.
|
|
1403
|
+
* @param options - Behavior overrides.
|
|
1404
|
+
*/
|
|
1405
|
+
declare function useChapterLayout(book: MejiroBook, epub: EpubBook | null, chapterIndex: number, surface: RefObject<HTMLElement | null>, options?: UseChapterLayoutOptions): UseChapterLayoutReturn;
|
|
1406
|
+
|
|
1407
|
+
interface EditableEpubSelection {
|
|
1408
|
+
chapter: number;
|
|
1409
|
+
paragraph: number;
|
|
1410
|
+
}
|
|
1411
|
+
interface UseEditableEpubOptions {
|
|
1412
|
+
/** URL fetched and loaded on mount. */
|
|
1413
|
+
defaultUrl?: string;
|
|
1414
|
+
/** Called after a successful load. */
|
|
1415
|
+
onLoad?: (editor: EditableEpub) => void;
|
|
1416
|
+
/** Called when a load fails. */
|
|
1417
|
+
onError?: (error: Error) => void;
|
|
1418
|
+
/** Called after export completes. */
|
|
1419
|
+
onExport?: (buffer: ArrayBuffer) => void;
|
|
1420
|
+
}
|
|
1421
|
+
interface UseEditableEpubReturn {
|
|
1422
|
+
editor: EditableEpub | null;
|
|
1423
|
+
book: EditableEpubBook | null;
|
|
1424
|
+
previewBook: EditableEpubBook | null;
|
|
1425
|
+
loading: boolean;
|
|
1426
|
+
exporting: boolean;
|
|
1427
|
+
error: Error | null;
|
|
1428
|
+
revision: number;
|
|
1429
|
+
history: {
|
|
1430
|
+
canUndo: boolean;
|
|
1431
|
+
canRedo: boolean;
|
|
1432
|
+
depth: number;
|
|
1433
|
+
redoDepth: number;
|
|
1434
|
+
} | null;
|
|
1435
|
+
selection: EditableEpubSelection;
|
|
1436
|
+
selectedParagraph: AnnotatedParagraph | null;
|
|
1437
|
+
setSelection: (selection: EditableEpubSelection) => void;
|
|
1438
|
+
loadBuffer: (buffer: ArrayBuffer) => Promise<EditableEpub | null>;
|
|
1439
|
+
loadFile: (file: File) => Promise<EditableEpub | null>;
|
|
1440
|
+
loadUrl: (url: string) => Promise<EditableEpub | null>;
|
|
1441
|
+
updateParagraph: (text: string, inlineAnnotations?: readonly InlineAnnotation[]) => void;
|
|
1442
|
+
setInlineAnnotations: (inlineAnnotations: readonly InlineAnnotation[]) => void;
|
|
1443
|
+
addImage: (image: AddImageInput | EditableEpubImage) => void;
|
|
1444
|
+
undo: () => boolean;
|
|
1445
|
+
redo: () => boolean;
|
|
1446
|
+
exportEpub: (options?: EpubExportOptions) => Promise<ArrayBuffer | null>;
|
|
1447
|
+
}
|
|
1448
|
+
/** Headless editable-EPUB state for custom proofreading/editor UIs. */
|
|
1449
|
+
declare function useEditableEpub(options?: UseEditableEpubOptions): UseEditableEpubReturn;
|
|
1450
|
+
|
|
1451
|
+
/** Options for {@link useEpub}. */
|
|
1452
|
+
interface UseEpubOptions {
|
|
1453
|
+
/** URL fetched on mount. A non-OK response is treated as "no default". */
|
|
1454
|
+
defaultUrl?: string;
|
|
1455
|
+
/** Called after a successful load. */
|
|
1456
|
+
onLoad?: (book: EpubBook) => void;
|
|
1457
|
+
/** Called when a load fails. Non-OK URL responses are still treated as "no default". */
|
|
1458
|
+
onError?: (error: Error) => void;
|
|
1459
|
+
/**
|
|
1460
|
+
* Extra options merged into the `fetch` call when loading by URL. Useful
|
|
1461
|
+
* for sending bearer tokens or cookies (`credentials: 'include'`).
|
|
1462
|
+
*/
|
|
1463
|
+
fetchOptions?: RequestInit;
|
|
1464
|
+
/**
|
|
1465
|
+
* Custom EPUB fetcher used in place of the global `fetch`. Returns the
|
|
1466
|
+
* raw `ArrayBuffer`. Overrides {@link fetchOptions} when set — common
|
|
1467
|
+
* for hosts that already own an auth-aware HTTP client.
|
|
1468
|
+
*/
|
|
1469
|
+
fetchEpub?: (url: string) => Promise<ArrayBuffer>;
|
|
1470
|
+
}
|
|
1471
|
+
/** Return value of {@link useEpub}. */
|
|
1472
|
+
interface UseEpubReturn {
|
|
1473
|
+
/** Current parsed EPUB, or `null` before any load. */
|
|
1474
|
+
epub: EpubBook | null;
|
|
1475
|
+
/** Whether a load is in progress. */
|
|
1476
|
+
loading: boolean;
|
|
1477
|
+
/** Last load error, if any. */
|
|
1478
|
+
error: Error | null;
|
|
1479
|
+
/** Parse from an in-memory buffer. */
|
|
1480
|
+
loadBuffer: (buffer: ArrayBuffer) => Promise<EpubBook | null>;
|
|
1481
|
+
/** Parse from a {@link File}. */
|
|
1482
|
+
loadFile: (file: File) => Promise<EpubBook | null>;
|
|
1483
|
+
/** Fetch a URL and parse the response. */
|
|
1484
|
+
loadUrl: (url: string) => Promise<EpubBook | null>;
|
|
1485
|
+
/** Replace the current EPUB without going through the parser. */
|
|
1486
|
+
setEpub: (book: EpubBook | null) => void;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* React hook that parses EPUB files and exposes loading/error state plus
|
|
1490
|
+
* convenience loaders for buffers, files, and URLs.
|
|
1491
|
+
*/
|
|
1492
|
+
declare function useEpub(options?: UseEpubOptions): UseEpubReturn;
|
|
1493
|
+
|
|
1494
|
+
interface EpubProjectChapterDraft {
|
|
1495
|
+
id: string;
|
|
1496
|
+
title: string;
|
|
1497
|
+
body: string;
|
|
1498
|
+
}
|
|
1499
|
+
interface UseEpubProjectOptions {
|
|
1500
|
+
metadata?: Partial<EpubProjectMetadata>;
|
|
1501
|
+
chapters?: EpubProjectChapterDraft[];
|
|
1502
|
+
debounceMs?: number;
|
|
1503
|
+
/**
|
|
1504
|
+
* Resolves URL-only project assets into bytes when the preview or export
|
|
1505
|
+
* pipeline materializes them. Forwarded to `project.export()`. Useful when
|
|
1506
|
+
* cover / illustration assets are registered as remote URLs and you want
|
|
1507
|
+
* the host (not the client) to provide auth headers.
|
|
1508
|
+
*/
|
|
1509
|
+
assetResolver?: AssetResolver;
|
|
1510
|
+
onPreview?: (book: EpubBook) => void;
|
|
1511
|
+
onExport?: (buffer: ArrayBuffer) => void;
|
|
1512
|
+
}
|
|
1513
|
+
interface UseEpubProjectReturn {
|
|
1514
|
+
metadata: EpubProjectMetadata;
|
|
1515
|
+
chapters: EpubProjectChapterDraft[];
|
|
1516
|
+
selectedChapter: number;
|
|
1517
|
+
currentChapter: EpubProjectChapterDraft | null;
|
|
1518
|
+
previewBook: EpubBook | null;
|
|
1519
|
+
previewError: Error | null;
|
|
1520
|
+
previewing: boolean;
|
|
1521
|
+
setMetadata: (patch: Partial<EpubProjectMetadata>) => void;
|
|
1522
|
+
setChapters: (chapters: EpubProjectChapterDraft[]) => void;
|
|
1523
|
+
setSelectedChapter: (index: number) => void;
|
|
1524
|
+
patchChapter: (index: number, patch: Partial<EpubProjectChapterDraft>) => void;
|
|
1525
|
+
addChapter: (chapter?: Partial<EpubProjectChapterDraft>) => void;
|
|
1526
|
+
removeChapter: (index?: number) => void;
|
|
1527
|
+
reorderChapters: (from: number, to: number) => void;
|
|
1528
|
+
buildProject: () => EpubProject;
|
|
1529
|
+
exportEpub: () => Promise<ArrayBuffer>;
|
|
1530
|
+
}
|
|
1531
|
+
/** Headless manuscript-to-EPUB project state for custom authoring UIs. */
|
|
1532
|
+
declare function useEpubProject(options?: UseEpubProjectOptions): UseEpubProjectReturn;
|
|
1533
|
+
|
|
1534
|
+
/** Options for {@link useManuscriptDraft}. */
|
|
1535
|
+
interface UseManuscriptDraftOptions {
|
|
1536
|
+
/** Initial chapters. Defaults to a single empty chapter. */
|
|
1537
|
+
initialChapters?: ManuscriptEditorChapter[];
|
|
1538
|
+
/**
|
|
1539
|
+
* Called when the draft changes (debounced). Use to persist to
|
|
1540
|
+
* localStorage, IndexedDB, or upload to a server.
|
|
1541
|
+
*/
|
|
1542
|
+
onAutosave?: (chapters: ManuscriptEditorChapter[]) => void | Promise<void>;
|
|
1543
|
+
/** Debounce delay in milliseconds. @defaultValue 800 */
|
|
1544
|
+
autosaveDelay?: number;
|
|
1545
|
+
}
|
|
1546
|
+
/** Return value of {@link useManuscriptDraft}. */
|
|
1547
|
+
interface UseManuscriptDraftReturn {
|
|
1548
|
+
chapters: ManuscriptEditorChapter[];
|
|
1549
|
+
/** Index of the chapter currently being edited. */
|
|
1550
|
+
selected: number;
|
|
1551
|
+
setSelected(index: number): void;
|
|
1552
|
+
setChapters(chapters: ManuscriptEditorChapter[]): void;
|
|
1553
|
+
patchChapter(index: number, patch: Partial<ManuscriptEditorChapter>): void;
|
|
1554
|
+
addChapter(chapter?: Partial<ManuscriptEditorChapter>): void;
|
|
1555
|
+
removeChapter(index: number): void;
|
|
1556
|
+
reorderChapters(from: number, to: number): void;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Reactive store for manuscript drafts.
|
|
1560
|
+
*
|
|
1561
|
+
* Wraps the chapter array with helpers for adding, removing, reordering, and
|
|
1562
|
+
* patching individual chapters, plus a debounced autosave hook that fires
|
|
1563
|
+
* `onAutosave` whenever the chapter list settles.
|
|
1564
|
+
*/
|
|
1565
|
+
declare function useManuscriptDraft(options?: UseManuscriptDraftOptions): UseManuscriptDraftReturn;
|
|
1566
|
+
|
|
1567
|
+
/** Options for {@link useManuscriptLayout}. */
|
|
1568
|
+
interface UseManuscriptLayoutOptions {
|
|
1569
|
+
/** Manuscript notation dialect. @defaultValue `'mejiro'` */
|
|
1570
|
+
dialect?: ManuscriptDialect;
|
|
1571
|
+
/** Listen for `window.resize` and re-flow on resize. @defaultValue true */
|
|
1572
|
+
enableResize?: boolean;
|
|
1573
|
+
/** Debounce window (ms) for resize re-flows. @defaultValue 120 */
|
|
1574
|
+
resizeDebounce?: number;
|
|
1575
|
+
}
|
|
1576
|
+
/** Page dimensions returned by {@link MejiroBook.computePageSize}. */
|
|
1577
|
+
interface ManuscriptPageDimensions {
|
|
1578
|
+
pageWidth: number;
|
|
1579
|
+
pageHeight: number;
|
|
1580
|
+
contentHeight: number;
|
|
1581
|
+
}
|
|
1582
|
+
/** Return value of {@link useManuscriptLayout}. */
|
|
1583
|
+
interface UseManuscriptLayoutReturn {
|
|
1584
|
+
/** Current layout, or `null` before the first computation. */
|
|
1585
|
+
layout: ChapterLayout | null;
|
|
1586
|
+
/** Page width in pixels. */
|
|
1587
|
+
pageWidth: number;
|
|
1588
|
+
/** Page height in pixels. */
|
|
1589
|
+
pageHeight: number;
|
|
1590
|
+
/** Content height in pixels. */
|
|
1591
|
+
contentHeight: number;
|
|
1592
|
+
/** Most recent layout time (ms). */
|
|
1593
|
+
elapsedMs: number;
|
|
1594
|
+
/** Force a fresh layout computation. */
|
|
1595
|
+
recompute: () => Promise<void>;
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* React hook that lays out a single manuscript chapter directly, skipping the
|
|
1599
|
+
* EPUB ZIP round-trip used by {@link useChapterLayout}.
|
|
1600
|
+
*
|
|
1601
|
+
* Intended for live preview in custom manuscript editors: pair with
|
|
1602
|
+
* {@link useManuscriptDraft} for the chapter array and feed the resulting
|
|
1603
|
+
* {@link ChapterLayout} into {@link MejiroSpread} or {@link MejiroScrollView}.
|
|
1604
|
+
*
|
|
1605
|
+
* Re-layouts whenever `chapter`, `dialect`, or `book` change. On `window.resize`,
|
|
1606
|
+
* page dimensions are recomputed and {@link ChapterLayout.resize} is called
|
|
1607
|
+
* instead of a full re-layout.
|
|
1608
|
+
*/
|
|
1609
|
+
declare function useManuscriptLayout(book: MejiroBook, chapter: ManuscriptChapter | null, surface: RefObject<HTMLElement | null>, options?: UseManuscriptLayoutOptions): UseManuscriptLayoutReturn;
|
|
1610
|
+
|
|
1611
|
+
/** Return value of {@link useMejiroBook}. */
|
|
1612
|
+
interface UseMejiroBookReturn {
|
|
1613
|
+
/** The managed {@link MejiroBook} instance. Stable across renders. */
|
|
1614
|
+
book: MejiroBook;
|
|
1615
|
+
/** Reactive snapshot of the current options. */
|
|
1616
|
+
options: Readonly<BookOptions>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Update options on the underlying book and the snapshot. The returned
|
|
1619
|
+
* promise resolves once {@link MejiroBook.setOptions} has propagated
|
|
1620
|
+
* font / size changes (re-measurement runs lazily inside that call).
|
|
1621
|
+
*/
|
|
1622
|
+
setOptions: (next: Partial<BookOptions>) => Promise<void>;
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* React hook that owns a {@link MejiroBook} instance and exposes its
|
|
1626
|
+
* options as React state. The book is created once on mount; subsequent
|
|
1627
|
+
* option changes must go through {@link UseMejiroBookReturn.setOptions}
|
|
1628
|
+
* (e.g. via the `MejiroReader` imperative handle).
|
|
1629
|
+
*/
|
|
1630
|
+
declare function useMejiroBook(initial: BookOptions): UseMejiroBookReturn;
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* Persisted reading position. Anchor-shaped — pair with
|
|
1634
|
+
* {@link MejiroReaderHandle.goToAnchor} to restore the user's exact location
|
|
1635
|
+
* even after a reflow that invalidates spread indices.
|
|
1636
|
+
*/
|
|
1637
|
+
type ReadingPositionValue = ReadingAnchor;
|
|
1638
|
+
/** Minimal storage interface required by {@link useReadingPosition}. */
|
|
1639
|
+
interface ReadingPositionStorage {
|
|
1640
|
+
getItem(key: string): string | null;
|
|
1641
|
+
setItem(key: string, value: string): void;
|
|
1642
|
+
removeItem(key: string): void;
|
|
1643
|
+
}
|
|
1644
|
+
/** Options for {@link useReadingPosition}. */
|
|
1645
|
+
interface UseReadingPositionOptions {
|
|
1646
|
+
/** Storage key — usually scoped per-book (e.g. `mejiro:position:${bookId}`). */
|
|
1647
|
+
key: string;
|
|
1648
|
+
/**
|
|
1649
|
+
* Storage backend. Defaults to `window.localStorage` when available.
|
|
1650
|
+
* SSR consumers can omit this and the hook will fall back to in-memory.
|
|
1651
|
+
*/
|
|
1652
|
+
storage?: ReadingPositionStorage;
|
|
1653
|
+
/** Throttle ms between writes. @defaultValue 250 */
|
|
1654
|
+
throttleMs?: number;
|
|
1655
|
+
/**
|
|
1656
|
+
* Called immediately after `save()` (with the new anchor) or `clear()`
|
|
1657
|
+
* (with `null`). Use to mirror the position to a server alongside the
|
|
1658
|
+
* local `storage`. Not invoked on initial hydration or `key` changes.
|
|
1659
|
+
*/
|
|
1660
|
+
onChange?: (next: ReadingPositionValue | null) => void;
|
|
1661
|
+
}
|
|
1662
|
+
/** Return value of {@link useReadingPosition}. */
|
|
1663
|
+
interface UseReadingPositionReturn {
|
|
1664
|
+
/** Most recently persisted position, or `null` if none. */
|
|
1665
|
+
position: ReadingPositionValue | null;
|
|
1666
|
+
/** Persist a new position (throttled). */
|
|
1667
|
+
save(next: ReadingPositionValue): void;
|
|
1668
|
+
/** Remove the persisted position. */
|
|
1669
|
+
clear(): void;
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Persistence helper for reader state. Returns the saved anchor and a
|
|
1673
|
+
* throttled saver. Pair with {@link MejiroReaderHandle.goToAnchor} and
|
|
1674
|
+
* {@link MejiroReaderHandle.subscribe} for exact reflow-safe restore.
|
|
97
1675
|
*
|
|
98
|
-
* @example
|
|
99
1676
|
* ```tsx
|
|
100
|
-
* const {
|
|
101
|
-
*
|
|
1677
|
+
* const { position, save } = useReadingPosition({ key: `mejiro:${bookId}` });
|
|
1678
|
+
* const reader = useRef<MejiroReaderHandle>(null);
|
|
1679
|
+
*
|
|
1680
|
+
* useEffect(() => {
|
|
1681
|
+
* if (position) reader.current?.goToAnchor(position);
|
|
1682
|
+
* }, [position]);
|
|
1683
|
+
*
|
|
1684
|
+
* useEffect(() => {
|
|
1685
|
+
* const off = reader.current?.subscribe('spreadChanged', () => {
|
|
1686
|
+
* const anchor = reader.current?.getAnchor();
|
|
1687
|
+
* if (anchor) save(anchor);
|
|
1688
|
+
* });
|
|
1689
|
+
* return off;
|
|
1690
|
+
* }, [save]);
|
|
102
1691
|
* ```
|
|
1692
|
+
*
|
|
1693
|
+
* Legacy `{ chapter, spreadIdx }` JSON from v0.4 is migrated automatically:
|
|
1694
|
+
* the chapter is preserved, `spreadIdx` is dropped (it does not survive
|
|
1695
|
+
* reflow), and the position is treated as the start of the chapter.
|
|
103
1696
|
*/
|
|
104
|
-
declare function
|
|
1697
|
+
declare function useReadingPosition(options: UseReadingPositionOptions): UseReadingPositionReturn;
|
|
1698
|
+
|
|
1699
|
+
/** Options for {@link useSpread}. */
|
|
1700
|
+
interface UseSpreadOptions {
|
|
1701
|
+
/** Enable ArrowLeft/ArrowRight navigation. @defaultValue true */
|
|
1702
|
+
enableKeyboard?: boolean;
|
|
1703
|
+
/** Page-turn animation duration in ms. @defaultValue 180 */
|
|
1704
|
+
turnDuration?: number;
|
|
1705
|
+
/** Called when the spread index changes (after the turn animation midpoint). */
|
|
1706
|
+
onChange?: (spreadIdx: number) => void;
|
|
1707
|
+
}
|
|
1708
|
+
/** Return value of {@link useSpread}. */
|
|
1709
|
+
interface UseSpreadReturn {
|
|
1710
|
+
/** Current spread index. */
|
|
1711
|
+
spreadIdx: number;
|
|
1712
|
+
/** Current spread result, or `null` until the layout is ready. */
|
|
1713
|
+
spread: SpreadResult | null;
|
|
1714
|
+
/** Total number of pages. */
|
|
1715
|
+
totalPages: number;
|
|
1716
|
+
/** Total number of two-page spreads. */
|
|
1717
|
+
totalSpreads: number;
|
|
1718
|
+
/** Whether the turn animation is currently in flight. */
|
|
1719
|
+
turning: boolean;
|
|
1720
|
+
/** Advance one spread forward. */
|
|
1721
|
+
next: () => void;
|
|
1722
|
+
/** Go back one spread. */
|
|
1723
|
+
prev: () => void;
|
|
1724
|
+
/** Jump to an arbitrary spread index (clamped). */
|
|
1725
|
+
goTo: (index: number) => void;
|
|
1726
|
+
/**
|
|
1727
|
+
* Set the spread index immediately, with no page-turn animation, clamped to
|
|
1728
|
+
* `[0, totalSpreads − 1]`. Use to restore a reading position after a reflow
|
|
1729
|
+
* re-layout (where an animated {@link goTo} would briefly flash spread 0).
|
|
1730
|
+
*/
|
|
1731
|
+
setSpread: (index: number) => void;
|
|
1732
|
+
/** Manually refresh `spread` from `layout` at the current index. */
|
|
1733
|
+
refresh: () => void;
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* React hook that tracks the current spread index for a chapter layout
|
|
1737
|
+
* and exposes navigation helpers with an optional page-turn animation.
|
|
1738
|
+
*/
|
|
1739
|
+
declare function useSpread(layout: ChapterLayout | null, options?: UseSpreadOptions): UseSpreadReturn;
|
|
105
1740
|
|
|
106
|
-
export { type ImageRect, MejiroPage, type MejiroPageProps, MejiroPageView, type MejiroPageViewProps, type UseImageOverlayOptions, type UseImageOverlayReturn, useImageOverlay };
|
|
1741
|
+
export { type Annotation, type AnnotationsStorage, type EditableEpubSelection, type EditableSettings, type EpubProjectChapterDraft, type FontChoice, type ImageRect, type ManuscriptEditorChapter, type ManuscriptPageDimensions, type ManuscriptPreviewProps, MejiroChapterNav, type MejiroChapterNavMode, type MejiroChapterNavProps, type MejiroChapterNavVariant, MejiroDropZone, type MejiroDropZoneProps, MejiroEditor, type MejiroEditorProps, type MejiroExportPolicy, MejiroI18nProvider, MejiroImageOverlay, type MejiroImageOverlayProps, type MejiroLocale, MejiroManuscriptEditor, type MejiroManuscriptEditorProps, type MejiroMessages, MejiroNotationHighlighter, type MejiroNotationHighlighterProps, MejiroPage, MejiroPageIndicator, type MejiroPageIndicatorProps, type MejiroPageProps, MejiroPageView, type MejiroPageViewProps, MejiroReader, type MejiroReaderControlledProps, type MejiroReaderEventMap, type MejiroReaderFileProps, type MejiroReaderFit, type MejiroReaderHandle, type MejiroReaderMode, type MejiroReaderProps, type MejiroReaderSettingsSlot, type MejiroReaderUrlProps, MejiroScrollView, type MejiroScrollViewProps, MejiroSelectionLayer, type MejiroSelectionLayerProps, MejiroSettingsPanel, type MejiroSettingsPanelProps, MejiroShelf, type MejiroShelfProps, MejiroSpread, type MejiroSpreadMode, type MejiroSpreadProps, MejiroStats, type MejiroStatsProps, type MejiroTheme, type MejiroThemeName, MejiroToc, type MejiroTocProps, type MultiImageItem, type PageDimensions, type PageHeaderData, type PageNumberDisplay, type ReadingPosition, type ReadingPositionStorage, type ReadingPositionValue, type UseAnnotationsOptions, type UseAnnotationsReturn, type UseChapterLayoutOptions, type UseChapterLayoutReturn, type UseEditableEpubOptions, type UseEditableEpubReturn, type UseEpubOptions, type UseEpubProjectOptions, type UseEpubProjectReturn, type UseEpubReturn, type UseI18nOptions, type UseImageOverlayOptions, type UseImageOverlayReturn, type UseLibraryOptions, type UseLibraryReturn, type UseManuscriptDraftOptions, type UseManuscriptDraftReturn, type UseManuscriptLayoutOptions, type UseManuscriptLayoutReturn, type UseMejiroBookReturn, type UseMultiImageOverlayOptions, type UseMultiImageOverlayReturn, type UseReadingPositionOptions, type UseReadingPositionReturn, type UseSpreadOptions, type UseSpreadReturn, type VolumeInfo, enMessages, format, jaMessages, resolveMessages, useAnnotations, useChapterLayout, useEditableEpub, useEpub, useEpubProject, useI18n, useImageOverlay, useLibrary, useManuscriptDraft, useManuscriptLayout, useMejiroBook, useMultiImageOverlay, useReadingPosition, useSpread };
|