@hyebook/vue3-adapter 0.2.5 → 2.2.7

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.
@@ -1,25 +1,65 @@
1
1
  import type { EbookDoc } from "../types/ebook";
2
- import type { PlayerDataProvider, ReaderNote, ReaderProgress } from "../types/player";
2
+ import type { ReaderAnnotationChangeSource, ReaderAnnotationCreateEvent, ReaderAnnotationDeleteEvent, ReaderAnnotations, ReaderAnnotationsChangeEvent, ReaderAnnotationUpdateEvent, ReaderHighlight, PlayerDataProvider, PlayerEngineOptions, PlayerFeatures, ReaderNote, ReaderProgress } from "../types/player";
3
3
  export interface PlayerState {
4
4
  currentPage: number;
5
5
  zoom: number;
6
+ highlights: ReaderHighlight[];
6
7
  notes: ReaderNote[];
7
8
  progress: ReaderProgress | null;
8
9
  }
10
+ type AnnotationCreateListener = (event: ReaderAnnotationCreateEvent) => void;
11
+ type AnnotationUpdateListener = (event: ReaderAnnotationUpdateEvent) => void;
12
+ type AnnotationDeleteListener = (event: ReaderAnnotationDeleteEvent) => void;
13
+ type AnnotationsChangeListener = (event: ReaderAnnotationsChangeEvent) => void;
9
14
  export declare class PlayerEngine {
10
15
  private provider;
16
+ private features;
11
17
  private doc;
12
18
  private state;
13
- constructor(provider: PlayerDataProvider);
19
+ private annotationCreateListeners;
20
+ private annotationUpdateListeners;
21
+ private annotationDeleteListeners;
22
+ private annotationsChangeListeners;
23
+ constructor(provider: PlayerDataProvider, options?: PlayerEngineOptions);
14
24
  load(): Promise<EbookDoc>;
15
25
  getDocument(): EbookDoc;
16
26
  getState(): PlayerState;
27
+ getHighlights(): ReaderHighlight[];
28
+ getNotes(): ReaderNote[];
29
+ getAnnotations(): ReaderAnnotations;
30
+ getFeatures(): Required<PlayerFeatures>;
31
+ isBuiltInNotesModuleEnabled(): boolean;
32
+ reloadAnnotations(): Promise<ReaderAnnotations>;
33
+ goToHighlight(highlightId: string): number | null;
34
+ goToNote(noteId: string): number | null;
17
35
  goToPage(index: number): number;
18
36
  nextPage(): number;
19
37
  prevPage(): number;
20
38
  setZoom(zoom: number): number;
21
39
  saveProgress(): Promise<void>;
40
+ createHighlight(highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?: ReaderAnnotationChangeSource): Promise<ReaderHighlight>;
41
+ updateHighlight(highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?: ReaderAnnotationChangeSource): Promise<ReaderHighlight | null>;
42
+ deleteHighlight(highlightId: string, source?: ReaderAnnotationChangeSource): Promise<boolean>;
22
43
  addNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">): Promise<ReaderNote>;
44
+ createNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?: ReaderAnnotationChangeSource): Promise<ReaderNote>;
45
+ updateNote(noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?: ReaderAnnotationChangeSource): Promise<ReaderNote | null>;
46
+ deleteNote(noteId: string, source?: ReaderAnnotationChangeSource): Promise<boolean>;
47
+ onAnnotationCreate(listener: AnnotationCreateListener): () => void;
48
+ onAnnotationUpdate(listener: AnnotationUpdateListener): () => void;
49
+ onAnnotationDelete(listener: AnnotationDeleteListener): () => void;
50
+ onAnnotationsChange(listener: AnnotationsChangeListener): () => void;
23
51
  private restoreUserState;
52
+ private loadAnnotationsState;
53
+ private findPageIndexByPageId;
54
+ private normalizeRange;
55
+ private normalizeAnnotations;
56
+ private mapPreviewAnnotationsToReader;
57
+ private persistAnnotations;
58
+ private emitAnnotationCreate;
59
+ private emitAnnotationUpdate;
60
+ private emitAnnotationDelete;
61
+ private emitAnnotationsChange;
62
+ private notifyListeners;
24
63
  }
64
+ export {};
25
65
  //# sourceMappingURL=engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EACV,kBAAkB,EAClB,UAAU,EACV,cAAc,EACf,MAAM,iBAAiB,CAAC;AAGzB,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;CACjC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,GAAG,CAAyB;IACpC,OAAO,CAAC,KAAK,CAKX;gBAEU,QAAQ,EAAE,kBAAkB;IAIlC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAa/B,WAAW,IAAI,QAAQ;IAOvB,QAAQ,IAAI,WAAW;IAIvB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAO/B,QAAQ,IAAI,MAAM;IAIlB,QAAQ,IAAI,MAAM;IAIlB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMvB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAc7B,OAAO,CACX,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC,GAClE,OAAO,CAAC,UAAU,CAAC;YAiBR,gBAAgB;CAmB/B"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/engine.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,26 @@
1
1
  import { assertEbookDoc } from "../utils/validate";
2
+ const DEFAULT_PLAYER_FEATURES = {
3
+ useBuiltInNotesModule: true,
4
+ };
2
5
  export class PlayerEngine {
3
- constructor(provider) {
6
+ constructor(provider, options = {}) {
4
7
  this.doc = null;
5
8
  this.state = {
6
9
  currentPage: 0,
7
10
  zoom: 1,
11
+ highlights: [],
8
12
  notes: [],
9
13
  progress: null,
10
14
  };
15
+ this.annotationCreateListeners = new Set();
16
+ this.annotationUpdateListeners = new Set();
17
+ this.annotationDeleteListeners = new Set();
18
+ this.annotationsChangeListeners = new Set();
11
19
  this.provider = provider;
20
+ this.features = {
21
+ useBuiltInNotesModule: options.features?.useBuiltInNotesModule ??
22
+ DEFAULT_PLAYER_FEATURES.useBuiltInNotesModule,
23
+ };
12
24
  }
13
25
  async load() {
14
26
  try {
@@ -30,7 +42,48 @@ export class PlayerEngine {
30
42
  return JSON.parse(JSON.stringify(this.doc));
31
43
  }
32
44
  getState() {
33
- return JSON.parse(JSON.stringify(this.state));
45
+ return clone(this.state);
46
+ }
47
+ getHighlights() {
48
+ return clone(this.state.highlights);
49
+ }
50
+ getNotes() {
51
+ return clone(this.state.notes);
52
+ }
53
+ getAnnotations() {
54
+ return {
55
+ highlights: this.getHighlights(),
56
+ notes: this.getNotes(),
57
+ };
58
+ }
59
+ getFeatures() {
60
+ return { ...this.features };
61
+ }
62
+ isBuiltInNotesModuleEnabled() {
63
+ return this.features.useBuiltInNotesModule;
64
+ }
65
+ async reloadAnnotations() {
66
+ if (!this.doc) {
67
+ throw new Error("Document is not loaded. Call load() first.");
68
+ }
69
+ await this.loadAnnotationsState();
70
+ return this.getAnnotations();
71
+ }
72
+ goToHighlight(highlightId) {
73
+ const target = this.state.highlights.find((item) => item.id === highlightId);
74
+ if (!target) {
75
+ return null;
76
+ }
77
+ const pageIndex = this.findPageIndexByPageId(target.pageId);
78
+ return pageIndex >= 0 ? this.goToPage(pageIndex) : null;
79
+ }
80
+ goToNote(noteId) {
81
+ const target = this.state.notes.find((item) => item.id === noteId);
82
+ if (!target) {
83
+ return null;
84
+ }
85
+ const pageIndex = this.findPageIndexByPageId(target.pageId);
86
+ return pageIndex >= 0 ? this.goToPage(pageIndex) : null;
34
87
  }
35
88
  goToPage(index) {
36
89
  const doc = this.getDocument();
@@ -62,7 +115,62 @@ export class PlayerEngine {
62
115
  this.state.progress = progress;
63
116
  await this.provider.saveProgress(progress);
64
117
  }
118
+ async createHighlight(highlight, source = "api") {
119
+ const doc = this.getDocument();
120
+ const now = new Date().toISOString();
121
+ const nextHighlight = {
122
+ ...highlight,
123
+ range: this.normalizeRange(highlight.range),
124
+ id: `highlight-${Date.now()}`,
125
+ bookId: doc.id,
126
+ createdAt: now,
127
+ updatedAt: now,
128
+ };
129
+ this.state.highlights.push(nextHighlight);
130
+ await this.persistAnnotations();
131
+ this.emitAnnotationCreate("highlight", nextHighlight, source);
132
+ return clone(nextHighlight);
133
+ }
134
+ async updateHighlight(highlightId, patch, source = "api") {
135
+ const index = this.state.highlights.findIndex((item) => item.id === highlightId);
136
+ if (index < 0) {
137
+ return null;
138
+ }
139
+ const current = this.state.highlights[index];
140
+ if (!current) {
141
+ return null;
142
+ }
143
+ const nextHighlight = {
144
+ ...current,
145
+ ...patch,
146
+ id: current.id,
147
+ bookId: current.bookId,
148
+ createdAt: current.createdAt,
149
+ updatedAt: new Date().toISOString(),
150
+ range: patch.range ? this.normalizeRange(patch.range) : current.range,
151
+ };
152
+ this.state.highlights[index] = nextHighlight;
153
+ await this.persistAnnotations();
154
+ this.emitAnnotationUpdate("highlight", current, nextHighlight, source);
155
+ return clone(nextHighlight);
156
+ }
157
+ async deleteHighlight(highlightId, source = "api") {
158
+ const index = this.state.highlights.findIndex((item) => item.id === highlightId);
159
+ if (index < 0) {
160
+ return false;
161
+ }
162
+ const [removedHighlight] = this.state.highlights.splice(index, 1);
163
+ if (!removedHighlight) {
164
+ return false;
165
+ }
166
+ await this.persistAnnotations();
167
+ this.emitAnnotationDelete("highlight", removedHighlight, source);
168
+ return true;
169
+ }
65
170
  async addNote(note) {
171
+ return this.createNote(note, "api");
172
+ }
173
+ async createNote(note, source = "api") {
66
174
  const doc = this.getDocument();
67
175
  const now = new Date().toISOString();
68
176
  const nextNote = {
@@ -73,10 +181,69 @@ export class PlayerEngine {
73
181
  updatedAt: now,
74
182
  };
75
183
  this.state.notes.push(nextNote);
76
- if (this.provider.saveNotes) {
77
- await this.provider.saveNotes(doc.id, this.state.notes);
184
+ await this.persistAnnotations();
185
+ this.emitAnnotationCreate("note", nextNote, source);
186
+ return clone(nextNote);
187
+ }
188
+ async updateNote(noteId, patch, source = "api") {
189
+ const index = this.state.notes.findIndex((item) => item.id === noteId);
190
+ if (index < 0) {
191
+ return null;
192
+ }
193
+ const current = this.state.notes[index];
194
+ if (!current) {
195
+ return null;
196
+ }
197
+ const nextNote = {
198
+ ...current,
199
+ ...patch,
200
+ id: current.id,
201
+ bookId: current.bookId,
202
+ createdAt: current.createdAt,
203
+ updatedAt: new Date().toISOString(),
204
+ ...(patch.range ? { range: this.normalizeRange(patch.range) } : {}),
205
+ };
206
+ this.state.notes[index] = nextNote;
207
+ await this.persistAnnotations();
208
+ this.emitAnnotationUpdate("note", current, nextNote, source);
209
+ return clone(nextNote);
210
+ }
211
+ async deleteNote(noteId, source = "api") {
212
+ const index = this.state.notes.findIndex((item) => item.id === noteId);
213
+ if (index < 0) {
214
+ return false;
78
215
  }
79
- return nextNote;
216
+ const [removedNote] = this.state.notes.splice(index, 1);
217
+ if (!removedNote) {
218
+ return false;
219
+ }
220
+ await this.persistAnnotations();
221
+ this.emitAnnotationDelete("note", removedNote, source);
222
+ return true;
223
+ }
224
+ onAnnotationCreate(listener) {
225
+ this.annotationCreateListeners.add(listener);
226
+ return () => {
227
+ this.annotationCreateListeners.delete(listener);
228
+ };
229
+ }
230
+ onAnnotationUpdate(listener) {
231
+ this.annotationUpdateListeners.add(listener);
232
+ return () => {
233
+ this.annotationUpdateListeners.delete(listener);
234
+ };
235
+ }
236
+ onAnnotationDelete(listener) {
237
+ this.annotationDeleteListeners.add(listener);
238
+ return () => {
239
+ this.annotationDeleteListeners.delete(listener);
240
+ };
241
+ }
242
+ onAnnotationsChange(listener) {
243
+ this.annotationsChangeListeners.add(listener);
244
+ return () => {
245
+ this.annotationsChangeListeners.delete(listener);
246
+ };
80
247
  }
81
248
  async restoreUserState() {
82
249
  if (!this.doc) {
@@ -90,10 +257,170 @@ export class PlayerEngine {
90
257
  this.state.zoom = progress.zoom;
91
258
  }
92
259
  }
93
- if (this.provider.loadNotes) {
94
- const notes = await this.provider.loadNotes(this.doc.id);
95
- this.state.notes = notes;
260
+ await this.loadAnnotationsState();
261
+ }
262
+ async loadAnnotationsState() {
263
+ if (!this.doc) {
264
+ return;
96
265
  }
266
+ let restoredFromAnnotationsProvider = false;
267
+ if (this.provider.loadAnnotations) {
268
+ const annotations = await this.provider.loadAnnotations(this.doc.id);
269
+ if (annotations) {
270
+ const normalized = this.normalizeAnnotations(annotations);
271
+ this.state.highlights = normalized.highlights;
272
+ this.state.notes = normalized.notes;
273
+ restoredFromAnnotationsProvider = true;
274
+ }
275
+ }
276
+ if (!restoredFromAnnotationsProvider) {
277
+ const fromDocument = this.mapPreviewAnnotationsToReader(this.doc.meta.previewAnnotations, this.doc.id);
278
+ this.state.highlights = fromDocument.highlights;
279
+ this.state.notes = fromDocument.notes;
280
+ if (this.provider.loadNotes) {
281
+ const notes = await this.provider.loadNotes(this.doc.id);
282
+ this.state.notes = Array.isArray(notes) ? clone(notes) : [];
283
+ }
284
+ }
285
+ }
286
+ findPageIndexByPageId(pageId) {
287
+ if (!this.doc) {
288
+ return -1;
289
+ }
290
+ return this.doc.pages.findIndex((page) => page.id === pageId);
291
+ }
292
+ normalizeRange(range) {
293
+ const safeStart = Math.max(0, Math.floor(range.start || 0));
294
+ const safeEnd = Math.max(0, Math.floor(range.end || 0));
295
+ return safeStart <= safeEnd
296
+ ? { start: safeStart, end: safeEnd }
297
+ : { start: safeEnd, end: safeStart };
298
+ }
299
+ normalizeAnnotations(annotations) {
300
+ const highlights = Array.isArray(annotations?.highlights)
301
+ ? clone(annotations.highlights)
302
+ : [];
303
+ const notes = Array.isArray(annotations?.notes)
304
+ ? clone(annotations.notes)
305
+ : [];
306
+ return { highlights, notes };
307
+ }
308
+ mapPreviewAnnotationsToReader(annotations, bookId) {
309
+ const now = new Date().toISOString();
310
+ const safe = annotations || { highlights: [], notes: [] };
311
+ const highlights = safe.highlights.map((item, index) => {
312
+ const range = this.normalizeRange({ start: item.start, end: item.end });
313
+ return {
314
+ id: item.id || `highlight-preview-${index}`,
315
+ bookId,
316
+ pageId: item.pageId,
317
+ blockId: item.blockId || "preview-flow-text",
318
+ range,
319
+ ...(item.selectedText !== undefined
320
+ ? { selectedText: item.selectedText }
321
+ : {}),
322
+ ...(item.color !== undefined ? { color: item.color } : {}),
323
+ createdAt: item.createdAt || now,
324
+ updatedAt: item.updatedAt || item.createdAt || now,
325
+ };
326
+ });
327
+ const notes = safe.notes.map((item, index) => {
328
+ const range = this.normalizeRange({ start: item.start, end: item.end });
329
+ return {
330
+ id: item.id || `note-preview-${index}`,
331
+ bookId,
332
+ pageId: item.pageId,
333
+ blockId: "preview-flow-text",
334
+ range,
335
+ content: item.text,
336
+ ...(item.color !== undefined ? { color: item.color } : {}),
337
+ createdAt: item.createdAt || now,
338
+ updatedAt: item.updatedAt || item.createdAt || now,
339
+ };
340
+ });
341
+ return {
342
+ highlights,
343
+ notes,
344
+ };
345
+ }
346
+ async persistAnnotations() {
347
+ if (!this.doc) {
348
+ return;
349
+ }
350
+ const all = this.getAnnotations();
351
+ if (this.provider.saveAnnotations) {
352
+ await this.provider.saveAnnotations(this.doc.id, all);
353
+ return;
354
+ }
355
+ if (this.provider.saveNotes) {
356
+ await this.provider.saveNotes(this.doc.id, all.notes);
357
+ }
358
+ }
359
+ emitAnnotationCreate(kind, annotation, source) {
360
+ const all = this.getAnnotations();
361
+ const event = {
362
+ kind,
363
+ source,
364
+ annotation: clone(annotation),
365
+ all,
366
+ };
367
+ this.notifyListeners(this.annotationCreateListeners, event);
368
+ this.emitAnnotationsChange({
369
+ action: "create",
370
+ kind,
371
+ source,
372
+ all,
373
+ current: event.annotation,
374
+ });
375
+ }
376
+ emitAnnotationUpdate(kind, previous, current, source) {
377
+ const all = this.getAnnotations();
378
+ const event = {
379
+ kind,
380
+ source,
381
+ previous: clone(previous),
382
+ current: clone(current),
383
+ all,
384
+ };
385
+ this.notifyListeners(this.annotationUpdateListeners, event);
386
+ this.emitAnnotationsChange({
387
+ action: "update",
388
+ kind,
389
+ source,
390
+ all,
391
+ current: event.current,
392
+ previous: event.previous,
393
+ });
394
+ }
395
+ emitAnnotationDelete(kind, annotation, source) {
396
+ const all = this.getAnnotations();
397
+ const event = {
398
+ kind,
399
+ source,
400
+ annotation: clone(annotation),
401
+ all,
402
+ };
403
+ this.notifyListeners(this.annotationDeleteListeners, event);
404
+ this.emitAnnotationsChange({
405
+ action: "delete",
406
+ kind,
407
+ source,
408
+ all,
409
+ previous: event.annotation,
410
+ });
411
+ }
412
+ emitAnnotationsChange(event) {
413
+ this.notifyListeners(this.annotationsChangeListeners, event);
414
+ }
415
+ notifyListeners(listeners, event) {
416
+ listeners.forEach((listener) => {
417
+ try {
418
+ listener(event);
419
+ }
420
+ catch (error) {
421
+ this.provider.onError?.(toError(error));
422
+ }
423
+ });
97
424
  }
98
425
  }
99
426
  function toError(error) {
@@ -102,3 +429,6 @@ function toError(error) {
102
429
  }
103
430
  return new Error(String(error));
104
431
  }
432
+ function clone(value) {
433
+ return JSON.parse(JSON.stringify(value));
434
+ }
@@ -1,4 +1,8 @@
1
1
  import type { EbookDoc } from "./ebook";
2
+ export interface ReaderRange {
3
+ start: number;
4
+ end: number;
5
+ }
2
6
  export interface ReaderProgress {
3
7
  bookId: string;
4
8
  lastPage: number;
@@ -10,19 +14,70 @@ export interface ReaderNote {
10
14
  bookId: string;
11
15
  pageId: string;
12
16
  blockId: string;
13
- range?: {
14
- start: number;
15
- end: number;
16
- };
17
+ range?: ReaderRange;
17
18
  content: string;
18
19
  color?: string;
19
20
  createdAt: string;
20
21
  updatedAt: string;
21
22
  }
23
+ export interface ReaderHighlight {
24
+ id: string;
25
+ bookId: string;
26
+ pageId: string;
27
+ blockId: string;
28
+ range: ReaderRange;
29
+ selectedText?: string;
30
+ color?: string;
31
+ createdAt: string;
32
+ updatedAt: string;
33
+ }
34
+ export interface ReaderAnnotations {
35
+ highlights: ReaderHighlight[];
36
+ notes: ReaderNote[];
37
+ }
38
+ export type ReaderAnnotationKind = "highlight" | "note";
39
+ export type ReaderAnnotationAction = "create" | "update" | "delete";
40
+ export type ReaderAnnotationChangeSource = "api" | "user";
41
+ export type ReaderAnnotation = ReaderHighlight | ReaderNote;
42
+ export interface ReaderAnnotationCreateEvent {
43
+ kind: ReaderAnnotationKind;
44
+ source: ReaderAnnotationChangeSource;
45
+ annotation: ReaderAnnotation;
46
+ all: ReaderAnnotations;
47
+ }
48
+ export interface ReaderAnnotationUpdateEvent {
49
+ kind: ReaderAnnotationKind;
50
+ source: ReaderAnnotationChangeSource;
51
+ previous: ReaderAnnotation;
52
+ current: ReaderAnnotation;
53
+ all: ReaderAnnotations;
54
+ }
55
+ export interface ReaderAnnotationDeleteEvent {
56
+ kind: ReaderAnnotationKind;
57
+ source: ReaderAnnotationChangeSource;
58
+ annotation: ReaderAnnotation;
59
+ all: ReaderAnnotations;
60
+ }
61
+ export interface ReaderAnnotationsChangeEvent {
62
+ action: ReaderAnnotationAction;
63
+ kind: ReaderAnnotationKind;
64
+ source: ReaderAnnotationChangeSource;
65
+ all: ReaderAnnotations;
66
+ current?: ReaderAnnotation;
67
+ previous?: ReaderAnnotation;
68
+ }
69
+ export interface PlayerFeatures {
70
+ useBuiltInNotesModule?: boolean;
71
+ }
72
+ export interface PlayerEngineOptions {
73
+ features?: PlayerFeatures;
74
+ }
22
75
  export interface PlayerDataProvider {
23
76
  getData: () => Promise<EbookDoc>;
24
77
  loadProgress?: (bookId: string) => Promise<ReaderProgress | null>;
25
78
  saveProgress?: (progress: ReaderProgress) => Promise<void>;
79
+ loadAnnotations?: (bookId: string) => Promise<ReaderAnnotations | null>;
80
+ saveAnnotations?: (bookId: string, annotations: ReaderAnnotations) => Promise<void>;
26
81
  loadNotes?: (bookId: string) => Promise<ReaderNote[]>;
27
82
  saveNotes?: (bookId: string, notes: ReaderNote[]) => Promise<void>;
28
83
  onError?: (error: Error) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../../../../core/src/types/player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAClE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACtD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC"}
1
+ {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../../../../core/src/types/player.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,8 @@ export interface EditorWorkbenchOptions {
14
14
  pageWidth?: number;
15
15
  upload?: EditorWorkbenchUploadOptions;
16
16
  importDocx?: EditorWorkbenchDocxImportOptions;
17
- onDocumentChange?: (doc: EbookDoc) => void;
17
+ onDocumentChange?: (doc: EbookDoc) => void | Promise<void>;
18
+ onFullscreenSave?: (doc: EbookDoc) => void | Promise<void>;
18
19
  }
19
20
  export interface EditorWorkbenchPreviewAnnotationActions {
20
21
  canDelete?: boolean;
@@ -52,9 +53,12 @@ export interface EditorWorkbenchUploadOptions {
52
53
  export interface EditorWorkbenchHandle {
53
54
  destroy: () => void;
54
55
  getDocument: () => EbookDoc;
56
+ isFullscreen: () => boolean;
55
57
  getMode: () => Mode;
56
58
  getPageIndex: () => number;
57
59
  importDocx: (file: File) => Promise<void>;
60
+ openFullscreen: () => void;
61
+ closeFullscreenAndSave: () => Promise<void>;
58
62
  setMode: (mode: Mode) => void;
59
63
  loadPreviewAnnotations: () => PreviewAnnotations;
60
64
  savePreviewAnnotations: (annotations: PreviewAnnotations | null | undefined) => PreviewAnnotations;
@@ -1 +1 @@
1
- {"version":3,"file":"editor-workbench.d.ts","sourceRoot":"","sources":["../../../../../core/src/workbench/editor-workbench.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor-workbench.d.ts","sourceRoot":"","sources":["../../../../../core/src/workbench/editor-workbench.ts"],"names":[],"mappings":"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"}
@@ -144,6 +144,14 @@ const WORKBENCH_CSS = `
144
144
  .hyewb-upload-progress{height:8px;background:#e2e8f0;border-radius:999px;overflow:hidden}
145
145
  .hyewb-upload-progress > span{display:block;height:100%;width:0;background:#0b7285;transition:width .18s ease}
146
146
  .hyewb-upload-error{min-height:18px;font-size:12px;color:#b91c1c;margin:0}
147
+ .hyewb-preview-note-backdrop{position:fixed;inset:0;display:none;align-items:center;justify-content:center;background:rgba(15,23,42,.45);z-index:10080;padding:16px}
148
+ .hyewb-preview-note-backdrop.show{display:flex}
149
+ .hyewb-preview-note-dialog{width:min(520px,100%);background:#fff;border:1px solid #cbd5e1;border-radius:12px;padding:14px;display:grid;gap:10px;box-shadow:0 16px 34px rgba(15,23,42,.28)}
150
+ .hyewb-preview-note-title{margin:0;font-size:14px;font-weight:600;color:#0f172a}
151
+ .hyewb-preview-note-input{width:100%;min-height:120px;resize:vertical;border:1px solid #94a3b8;border-radius:8px;padding:8px 10px;background:#fff;color:#0f172a;box-sizing:border-box;font-family:inherit;font-size:14px;line-height:1.5}
152
+ .hyewb-preview-note-actions{display:flex;justify-content:flex-end;gap:8px}
153
+ .hyewb-preview-note-btn{height:34px;padding:0 12px;border-radius:8px;border:1px solid #94a3b8;background:#fff;color:#0f172a;cursor:pointer}
154
+ .hyewb-preview-note-btn.primary{border-color:#0b7285;background:#0b7285;color:#fff}
147
155
  .hyewb-preview-highlight-mark{background:var(--hyewb-highlight-color,#fff59d);color:inherit;padding:0 .08em;border-radius:.2em}
148
156
  .hyewb-preview-note-mark{background:rgba(14,116,144,.14);border-bottom:2px solid #0e7490;color:inherit;padding:0 .04em;border-radius:.2em}
149
157
  .hyewb-preview-annotation-panel{display:none;align-content:start;gap:10px;border:1px solid #d8e0ea;border-radius:10px;background:#ffffff;padding:12px;max-height:100%;overflow:auto}
@@ -167,8 +175,10 @@ const WORKBENCH_CSS = `
167
175
  .hyewb-preview-selection-btn svg{width:16px;height:16px}
168
176
  .hyewb-preview-annotation-flash{animation:hyewb-preview-annotation-pulse .9s ease}
169
177
  @keyframes hyewb-preview-annotation-pulse{0%{box-shadow:0 0 0 0 rgba(14,116,144,.4)}100%{box-shadow:0 0 0 12px rgba(14,116,144,0)}}
170
- .hyewb-root:fullscreen .hyewb-editor,.hyewb-root:fullscreen .hyewb-preview{min-height:1000px}
171
- .hyewb-root:-webkit-full-screen .hyewb-editor,.hyewb-root:-webkit-full-screen .hyewb-preview{min-height:1000px}
178
+ .hyewb-overlay{position:fixed;inset:0;z-index:2147483000;background:rgba(15,23,42,.35);padding:0;box-sizing:border-box;overflow:auto}
179
+ .hyewb-overlay-layer{min-height:100%;display:flex;align-items:stretch;justify-content:stretch}
180
+ .hyewb-root.hyewb-overlay-active{width:100%;max-width:none;min-height:100%;margin:0;border-radius:0;box-shadow:none}
181
+ .hyewb-root.hyewb-overlay-active .hyewb-editor,.hyewb-root.hyewb-overlay-active .hyewb-preview{min-height:1000px}
172
182
  `;
173
183
  export const DEFAULT_EDITOR_WORKBENCH_FEATURES = {
174
184
  textToolbar: true,
@@ -445,7 +455,8 @@ export function mountEditorWorkbench(container, options = {}) {
445
455
  title.className = "hyewb-title";
446
456
  title.textContent = options.title ?? "hy-ebook Editor Workbench";
447
457
  const fullscreenBtn = createButton("fullscreen");
448
- header.append(title);
458
+ fullscreenBtn.style.marginLeft = "auto";
459
+ header.append(title, fullscreenBtn);
449
460
  const toolbar = document.createElement("div");
450
461
  toolbar.className = "hyewb-row hyewb-top-control";
451
462
  const boldBtn = createButton("bold");
@@ -714,8 +725,7 @@ export function mountEditorWorkbench(container, options = {}) {
714
725
  const addPageBtn = createButton("addPage");
715
726
  const pageInfo = document.createElement("span");
716
727
  pageInfo.className = "hyewb-status";
717
- fullscreenBtn.style.marginLeft = "auto";
718
- pageRow.append(prevPageBtn, nextPageBtn, addPageBtn, pageInfo, fullscreenBtn);
728
+ pageRow.append(prevPageBtn, nextPageBtn, addPageBtn, pageInfo);
719
729
  const previewLayout = document.createElement("div");
720
730
  previewLayout.className = "hyewb-preview-layout";
721
731
  const shell = document.createElement("div");
@@ -779,6 +789,35 @@ export function mountEditorWorkbench(container, options = {}) {
779
789
  previewNoteBtn.setAttribute("aria-label", "创建笔记");
780
790
  previewNoteBtn.append(createIconPlaceholder("notebook-pen"));
781
791
  previewSelectionToolbar.append(previewHighlightBtn, previewNoteBtn);
792
+ const preservePreviewSelectionOnToolbarPointerDown = (event) => {
793
+ // Keep native text selection active while clicking toolbar actions.
794
+ event.preventDefault();
795
+ event.stopPropagation();
796
+ };
797
+ previewSelectionToolbar.addEventListener("mousedown", preservePreviewSelectionOnToolbarPointerDown);
798
+ const previewNoteBackdrop = document.createElement("div");
799
+ previewNoteBackdrop.className = "hyewb-preview-note-backdrop";
800
+ const previewNoteDialog = document.createElement("div");
801
+ previewNoteDialog.className = "hyewb-preview-note-dialog";
802
+ const previewNoteTitle = document.createElement("h4");
803
+ previewNoteTitle.className = "hyewb-preview-note-title";
804
+ previewNoteTitle.textContent = "新增笔记";
805
+ const previewNoteInput = document.createElement("textarea");
806
+ previewNoteInput.className = "hyewb-preview-note-input";
807
+ previewNoteInput.placeholder = "请输入笔记内容";
808
+ const previewNoteActions = document.createElement("div");
809
+ previewNoteActions.className = "hyewb-preview-note-actions";
810
+ const previewNoteCancelBtn = document.createElement("button");
811
+ previewNoteCancelBtn.type = "button";
812
+ previewNoteCancelBtn.className = "hyewb-preview-note-btn";
813
+ previewNoteCancelBtn.textContent = "取消";
814
+ const previewNoteConfirmBtn = document.createElement("button");
815
+ previewNoteConfirmBtn.type = "button";
816
+ previewNoteConfirmBtn.className = "hyewb-preview-note-btn primary";
817
+ previewNoteConfirmBtn.textContent = "保存";
818
+ previewNoteActions.append(previewNoteCancelBtn, previewNoteConfirmBtn);
819
+ previewNoteDialog.append(previewNoteTitle, previewNoteInput, previewNoteActions);
820
+ previewNoteBackdrop.append(previewNoteDialog);
782
821
  const floatingToolbar = document.createElement("div");
783
822
  floatingToolbar.className = "hyewb-float";
784
823
  const floatBoldBtn = createButton("bold");
@@ -853,7 +892,7 @@ export function mountEditorWorkbench(container, options = {}) {
853
892
  const colorGrid = document.createElement("div");
854
893
  colorGrid.className = "hyewb-color-grid";
855
894
  colorPalette.append(colorGrid);
856
- root.append(header, toolbar, pageRow, previewLayout, status, colorPalette, tablePicker, tableTools, tableRowDeleteBtn, tableMoveHandle, tableScaleHandle, tableColEdgeLayer, tableRowEdgeLayer, tableRowGapInsertBtn, tableColGapInsertBtn, tableDropIndicator, tableContextMenu, uploadBackdrop, docxInput, previewSelectionToolbar);
895
+ root.append(header, toolbar, pageRow, previewLayout, status, colorPalette, tablePicker, tableTools, tableRowDeleteBtn, tableMoveHandle, tableScaleHandle, tableColEdgeLayer, tableRowEdgeLayer, tableRowGapInsertBtn, tableColGapInsertBtn, tableDropIndicator, tableContextMenu, uploadBackdrop, previewNoteBackdrop, docxInput, previewSelectionToolbar);
857
896
  container.innerHTML = "";
858
897
  root.append(floatingToolbar);
859
898
  container.append(root);
@@ -882,6 +921,7 @@ export function mountEditorWorkbench(container, options = {}) {
882
921
  let selectedTableColIndex = null;
883
922
  let annotationActiveTab = previewAnnotationOptions.annotationListDefaultTab;
884
923
  let previewSelectionDraft = null;
924
+ let previewNoteDialogDraft = null;
885
925
  const isTableGapInsertTarget = (node) => {
886
926
  if (!node) {
887
927
  return false;
@@ -897,27 +937,25 @@ export function mountEditorWorkbench(container, options = {}) {
897
937
  speedMbps: undefined,
898
938
  error: undefined,
899
939
  };
900
- let shouldRestoreFullscreenAfterFilePicker = false;
940
+ let fullscreenOverlay = null;
941
+ let fullscreenAnchor = null;
942
+ let bodyOverflowBeforeOverlay = null;
943
+ let isOverlayFullscreen = false;
901
944
  const setButtonIcon = (button, iconName) => {
902
945
  button.innerHTML = "";
903
946
  button.append(createIconPlaceholder(iconName));
904
947
  };
905
- const getFullscreenElement = () => {
906
- const webkitDocument = document;
907
- return (document.fullscreenElement ||
908
- webkitDocument.webkitFullscreenElement ||
909
- null);
910
- };
911
948
  const isWorkbenchFullscreen = () => {
912
- return getFullscreenElement() === root;
949
+ return isOverlayFullscreen;
913
950
  };
914
951
  const updateFullscreenButtonState = () => {
915
952
  const isFullscreen = isWorkbenchFullscreen();
916
953
  setButtonActive(fullscreenBtn, isFullscreen);
917
954
  setButtonIcon(fullscreenBtn, isFullscreen ? "fullscreen-exit" : "fullscreen-enter");
918
- const label = isFullscreen ? "退出全屏" : "全屏";
955
+ const label = isFullscreen ? "保存并退出" : "全屏";
919
956
  fullscreenBtn.title = label;
920
957
  fullscreenBtn.setAttribute("aria-label", label);
958
+ fullscreenBtn.style.display = isFullscreen ? "inline-flex" : "none";
921
959
  };
922
960
  const updateShellViewportHeight = () => {
923
961
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
@@ -948,67 +986,82 @@ export function mountEditorWorkbench(container, options = {}) {
948
986
  const rounded = Math.round(top);
949
987
  return Math.min(rounded, maxTop);
950
988
  };
951
- const requestWorkbenchFullscreen = async () => {
952
- const target = root;
953
- if (typeof target.requestFullscreen === "function") {
954
- await target.requestFullscreen();
989
+ const syncWorkbenchLayoutAfterFullscreenChange = () => {
990
+ updateFullscreenButtonState();
991
+ updateShellViewportHeight();
992
+ updateTopControlShieldHeight();
993
+ updateInlineResizeOverlay();
994
+ syncTableToolsPosition();
995
+ syncFocusedTableControls();
996
+ syncPreviewSelectionToolbar();
997
+ };
998
+ const openWorkbenchFullscreen = () => {
999
+ if (isWorkbenchFullscreen()) {
955
1000
  return;
956
1001
  }
957
- if (typeof target.webkitRequestFullscreen === "function") {
958
- target.webkitRequestFullscreen();
1002
+ const parentNode = root.parentNode;
1003
+ if (!parentNode) {
959
1004
  return;
960
1005
  }
961
- throw new Error("当前环境不支持全屏");
1006
+ fullscreenAnchor = document.createComment("hyewb-fullscreen-anchor");
1007
+ parentNode.insertBefore(fullscreenAnchor, root);
1008
+ fullscreenOverlay = document.createElement("div");
1009
+ fullscreenOverlay.className = "hyewb-overlay";
1010
+ const fullscreenOverlayLayer = document.createElement("div");
1011
+ fullscreenOverlayLayer.className = "hyewb-overlay-layer";
1012
+ fullscreenOverlay.append(fullscreenOverlayLayer);
1013
+ fullscreenOverlayLayer.append(root);
1014
+ document.body.append(fullscreenOverlay);
1015
+ bodyOverflowBeforeOverlay = document.body.style.overflow;
1016
+ document.body.style.overflow = "hidden";
1017
+ root.classList.add("hyewb-overlay-active");
1018
+ isOverlayFullscreen = true;
1019
+ status.textContent = "已进入页面全屏编辑";
1020
+ syncWorkbenchLayoutAfterFullscreenChange();
962
1021
  };
963
- const exitWorkbenchFullscreen = async () => {
964
- const webkitDocument = document;
965
- if (typeof document.exitFullscreen === "function") {
966
- await document.exitFullscreen();
1022
+ const closeWorkbenchFullscreen = () => {
1023
+ if (!isWorkbenchFullscreen()) {
967
1024
  return;
968
1025
  }
969
- if (typeof webkitDocument.webkitExitFullscreen === "function") {
970
- webkitDocument.webkitExitFullscreen();
971
- return;
1026
+ if (fullscreenAnchor && fullscreenAnchor.parentNode) {
1027
+ fullscreenAnchor.parentNode.insertBefore(root, fullscreenAnchor);
1028
+ fullscreenAnchor.parentNode.removeChild(fullscreenAnchor);
972
1029
  }
973
- if (typeof webkitDocument.webkitCancelFullScreen === "function") {
974
- webkitDocument.webkitCancelFullScreen();
1030
+ else {
1031
+ container.append(root);
975
1032
  }
976
- };
977
- const toggleWorkbenchFullscreen = async () => {
978
- try {
979
- if (isWorkbenchFullscreen()) {
980
- await exitWorkbenchFullscreen();
981
- }
982
- else {
983
- await requestWorkbenchFullscreen();
984
- }
1033
+ fullscreenAnchor = null;
1034
+ if (fullscreenOverlay && fullscreenOverlay.parentNode) {
1035
+ fullscreenOverlay.parentNode.removeChild(fullscreenOverlay);
985
1036
  }
986
- catch {
987
- status.textContent = "当前浏览器不支持全屏";
988
- }
989
- finally {
990
- updateFullscreenButtonState();
991
- updateShellViewportHeight();
1037
+ fullscreenOverlay = null;
1038
+ root.classList.remove("hyewb-overlay-active");
1039
+ isOverlayFullscreen = false;
1040
+ if (bodyOverflowBeforeOverlay !== null) {
1041
+ document.body.style.overflow = bodyOverflowBeforeOverlay;
1042
+ bodyOverflowBeforeOverlay = null;
992
1043
  }
1044
+ syncWorkbenchLayoutAfterFullscreenChange();
993
1045
  };
994
- const restoreWorkbenchFullscreenIfNeeded = async () => {
995
- if (!shouldRestoreFullscreenAfterFilePicker) {
996
- return;
997
- }
998
- shouldRestoreFullscreenAfterFilePicker = false;
999
- if (isWorkbenchFullscreen()) {
1046
+ const closeWorkbenchFullscreenAndSave = async () => {
1047
+ if (!isWorkbenchFullscreen()) {
1000
1048
  return;
1001
1049
  }
1002
1050
  try {
1003
- await requestWorkbenchFullscreen();
1004
- }
1005
- catch {
1006
- // Ignore restore failure when browser blocks fullscreen without activation.
1051
+ syncEditorToDoc({ emitChange: false });
1052
+ const snapshot = clone(state.doc);
1053
+ if (options.onFullscreenSave) {
1054
+ await options.onFullscreenSave(snapshot);
1055
+ }
1056
+ else {
1057
+ await Promise.resolve(options.onDocumentChange?.(snapshot));
1058
+ }
1059
+ closeWorkbenchFullscreen();
1060
+ status.textContent = "已保存并退出页面全屏";
1007
1061
  }
1008
- finally {
1009
- updateFullscreenButtonState();
1010
- updateShellViewportHeight();
1011
- updateTopControlShieldHeight();
1062
+ catch (error) {
1063
+ const errorMessage = error instanceof Error && error.message ? error.message : "请稍后重试";
1064
+ status.textContent = `保存失败:${errorMessage}`;
1012
1065
  }
1013
1066
  };
1014
1067
  const formatUploadProgress = (percent) => {
@@ -1029,6 +1082,48 @@ export function mountEditorWorkbench(container, options = {}) {
1029
1082
  uploadProgressBar.style.width = `${formatUploadProgress(uploadState.progress)}%`;
1030
1083
  uploadError.textContent = uploadState.error || "";
1031
1084
  };
1085
+ const closePreviewNoteDialog = () => {
1086
+ previewNoteDialogDraft = null;
1087
+ previewNoteInput.value = "";
1088
+ previewNoteBackdrop.classList.remove("show");
1089
+ previewNoteInput.blur();
1090
+ };
1091
+ const openPreviewNoteDialog = (draft) => {
1092
+ previewNoteDialogDraft = { ...draft };
1093
+ previewNoteInput.value = draft.text || "";
1094
+ previewNoteBackdrop.classList.add("show");
1095
+ window.setTimeout(() => {
1096
+ previewNoteInput.focus();
1097
+ previewNoteInput.select();
1098
+ }, 0);
1099
+ };
1100
+ const savePreviewNoteFromDialog = () => {
1101
+ if (!previewNoteDialogDraft) {
1102
+ return;
1103
+ }
1104
+ const noteText = previewNoteInput.value.trim();
1105
+ if (!noteText) {
1106
+ status.textContent = "请输入笔记内容";
1107
+ previewNoteInput.focus();
1108
+ return;
1109
+ }
1110
+ const current = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
1111
+ current.notes = mergeNoteRecords(current.notes, {
1112
+ id: `note-${Date.now()}-${Math.round(Math.random() * 10000)}`,
1113
+ pageId: previewNoteDialogDraft.pageId,
1114
+ start: previewNoteDialogDraft.start,
1115
+ end: previewNoteDialogDraft.end,
1116
+ text: noteText,
1117
+ selectedText: previewNoteDialogDraft.text,
1118
+ color: "#0e7490",
1119
+ createdAt: new Date().toISOString(),
1120
+ });
1121
+ annotationActiveTab = "notes";
1122
+ savePreviewAnnotations(current);
1123
+ window.getSelection()?.removeAllRanges();
1124
+ closePreviewNoteDialog();
1125
+ hidePreviewSelectionToolbar();
1126
+ };
1032
1127
  const closeUploadDialog = () => {
1033
1128
  if (uploadState.uploading) {
1034
1129
  return;
@@ -2883,7 +2978,7 @@ export function mountEditorWorkbench(container, options = {}) {
2883
2978
  });
2884
2979
  colorGrid.append(swatch);
2885
2980
  });
2886
- const syncEditorToDoc = () => {
2981
+ const syncEditorToDoc = (options) => {
2887
2982
  if (state.mode !== "editor") {
2888
2983
  return;
2889
2984
  }
@@ -2895,7 +2990,9 @@ export function mountEditorWorkbench(container, options = {}) {
2895
2990
  page.blocks = replaceFlowBlocks(page.blocks, flowBlocks);
2896
2991
  state.doc.meta.updatedAt = new Date().toISOString();
2897
2992
  editor.setDocument(state.doc);
2898
- emitDocumentChange();
2993
+ if (options?.emitChange !== false) {
2994
+ emitDocumentChange();
2995
+ }
2899
2996
  };
2900
2997
  const updateAlign = (align) => {
2901
2998
  if (selectedInlineImage &&
@@ -3419,12 +3516,12 @@ export function mountEditorWorkbench(container, options = {}) {
3419
3516
  previewArea.classList.toggle("show", state.mode === "preview");
3420
3517
  editorArea.style.display = state.mode === "editor" ? "block" : "none";
3421
3518
  const showEditorControls = state.mode === "editor";
3422
- header.style.display = showEditorControls ? "flex" : "none";
3519
+ header.style.display =
3520
+ showEditorControls || isWorkbenchFullscreen() ? "flex" : "none";
3423
3521
  toolbar.style.display =
3424
3522
  showEditorControls && state.features.textToolbar ? "flex" : "none";
3425
- pageRow.style.display = state.features.pageNav ? "flex" : "none";
3426
- addPageBtn.style.display =
3427
- showEditorControls && state.features.pageNav ? "inline-flex" : "none";
3523
+ pageRow.style.display = "none";
3524
+ addPageBtn.style.display = "none";
3428
3525
  videoAddBtn.style.display = state.features.video ? "inline-block" : "none";
3429
3526
  imageUploadBtn.style.display = "inline-block";
3430
3527
  status.style.display = showEditorControls ? "block" : "none";
@@ -3735,26 +3832,25 @@ export function mountEditorWorkbench(container, options = {}) {
3735
3832
  if (!previewSelectionDraft) {
3736
3833
  return;
3737
3834
  }
3738
- const text = window.prompt("请输入笔记内容", previewSelectionDraft.text || "") || "";
3739
- const noteText = text.trim();
3740
- if (!noteText) {
3741
- return;
3835
+ openPreviewNoteDialog(previewSelectionDraft);
3836
+ });
3837
+ previewNoteConfirmBtn.addEventListener("click", () => {
3838
+ savePreviewNoteFromDialog();
3839
+ });
3840
+ previewNoteCancelBtn.addEventListener("click", () => {
3841
+ closePreviewNoteDialog();
3842
+ });
3843
+ previewNoteBackdrop.addEventListener("click", () => {
3844
+ closePreviewNoteDialog();
3845
+ });
3846
+ previewNoteDialog.addEventListener("click", (event) => {
3847
+ event.stopPropagation();
3848
+ });
3849
+ previewNoteInput.addEventListener("keydown", (event) => {
3850
+ if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
3851
+ event.preventDefault();
3852
+ savePreviewNoteFromDialog();
3742
3853
  }
3743
- const current = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
3744
- current.notes = mergeNoteRecords(current.notes, {
3745
- id: `note-${Date.now()}-${Math.round(Math.random() * 10000)}`,
3746
- pageId: previewSelectionDraft.pageId,
3747
- start: previewSelectionDraft.start,
3748
- end: previewSelectionDraft.end,
3749
- text: noteText,
3750
- selectedText: previewSelectionDraft.text,
3751
- color: "#0e7490",
3752
- createdAt: new Date().toISOString(),
3753
- });
3754
- annotationActiveTab = "notes";
3755
- savePreviewAnnotations(current);
3756
- window.getSelection()?.removeAllRanges();
3757
- hidePreviewSelectionToolbar();
3758
3854
  });
3759
3855
  floatLeftBtn.addEventListener("click", () => updateAlign("left"));
3760
3856
  floatCenterBtn.addEventListener("click", () => updateAlign("center"));
@@ -4446,19 +4542,6 @@ export function mountEditorWorkbench(container, options = {}) {
4446
4542
  };
4447
4543
  window.addEventListener("resize", handleWindowResize);
4448
4544
  window.addEventListener("scroll", handleWindowScroll, true);
4449
- const handleFullscreenChange = () => {
4450
- window.requestAnimationFrame(() => {
4451
- updateFullscreenButtonState();
4452
- updateShellViewportHeight();
4453
- updateTopControlShieldHeight();
4454
- updateInlineResizeOverlay();
4455
- syncTableToolsPosition();
4456
- syncFocusedTableControls();
4457
- syncPreviewSelectionToolbar();
4458
- });
4459
- };
4460
- document.addEventListener("fullscreenchange", handleFullscreenChange);
4461
- document.addEventListener("webkitfullscreenchange", handleFullscreenChange);
4462
4545
  document.addEventListener("selectionchange", handleSelectionChange);
4463
4546
  const handleDocumentPointerDownForPalette = (event) => {
4464
4547
  const clickTarget = event.target;
@@ -4526,12 +4609,15 @@ export function mountEditorWorkbench(container, options = {}) {
4526
4609
  const handleDocumentKeyDown = (event) => {
4527
4610
  if (event.key === "Escape") {
4528
4611
  hideTableContextMenu();
4612
+ if (previewNoteBackdrop.classList.contains("show")) {
4613
+ closePreviewNoteDialog();
4614
+ }
4529
4615
  }
4530
4616
  };
4531
4617
  document.addEventListener("pointerdown", handleDocumentPointerDownForPalette);
4532
4618
  document.addEventListener("keydown", handleDocumentKeyDown);
4533
4619
  fullscreenBtn.addEventListener("click", () => {
4534
- void toggleWorkbenchFullscreen();
4620
+ void closeWorkbenchFullscreenAndSave();
4535
4621
  });
4536
4622
  tableAddBtn.addEventListener("mousedown", preserveSelectionForInputControl);
4537
4623
  videoAddBtn.addEventListener("mousedown", preserveSelectionForInputControl);
@@ -4562,12 +4648,10 @@ export function mountEditorWorkbench(container, options = {}) {
4562
4648
  status.textContent = "请先切换到编辑模式后再导入 Word";
4563
4649
  return;
4564
4650
  }
4565
- shouldRestoreFullscreenAfterFilePicker = isWorkbenchFullscreen();
4566
4651
  docxInput.value = "";
4567
4652
  docxInput.click();
4568
4653
  });
4569
4654
  docxInput.addEventListener("change", () => {
4570
- void restoreWorkbenchFullscreenIfNeeded();
4571
4655
  const picked = docxInput.files?.[0] || null;
4572
4656
  docxInput.value = "";
4573
4657
  if (!picked) {
@@ -4575,11 +4659,7 @@ export function mountEditorWorkbench(container, options = {}) {
4575
4659
  }
4576
4660
  void importDocxFile(picked);
4577
4661
  });
4578
- uploadInput.addEventListener("click", () => {
4579
- shouldRestoreFullscreenAfterFilePicker = isWorkbenchFullscreen();
4580
- });
4581
4662
  uploadInput.addEventListener("change", () => {
4582
- void restoreWorkbenchFullscreenIfNeeded();
4583
4663
  const picked = uploadInput.files?.[0] || null;
4584
4664
  uploadState.progress = 0;
4585
4665
  uploadState.speedMbps = undefined;
@@ -4614,10 +4694,6 @@ export function mountEditorWorkbench(container, options = {}) {
4614
4694
  uploadDialog.addEventListener("click", (event) => {
4615
4695
  event.stopPropagation();
4616
4696
  });
4617
- const handleWindowFocusForFullscreenRestore = () => {
4618
- void restoreWorkbenchFullscreenIfNeeded();
4619
- };
4620
- window.addEventListener("focus", handleWindowFocusForFullscreenRestore);
4621
4697
  prevPageBtn.addEventListener("click", () => jumpPage(-1));
4622
4698
  nextPageBtn.addEventListener("click", () => jumpPage(1));
4623
4699
  addPageBtn.addEventListener("click", addPage);
@@ -4628,6 +4704,7 @@ export function mountEditorWorkbench(container, options = {}) {
4628
4704
  emitDocumentChange();
4629
4705
  return {
4630
4706
  destroy: () => {
4707
+ closeWorkbenchFullscreen();
4631
4708
  hideTablePicker();
4632
4709
  hideTableTools();
4633
4710
  document.removeEventListener("selectionchange", handleSelectionChange);
@@ -4635,17 +4712,17 @@ export function mountEditorWorkbench(container, options = {}) {
4635
4712
  document.removeEventListener("pointerup", onMediaPointerUp);
4636
4713
  window.removeEventListener("resize", handleWindowResize);
4637
4714
  window.removeEventListener("scroll", handleWindowScroll, true);
4638
- window.removeEventListener("focus", handleWindowFocusForFullscreenRestore);
4639
4715
  document.removeEventListener("pointerdown", handleDocumentPointerDownForPalette);
4640
4716
  document.removeEventListener("keydown", handleDocumentKeyDown);
4641
- document.removeEventListener("fullscreenchange", handleFullscreenChange);
4642
- document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
4643
4717
  container.innerHTML = "";
4644
4718
  },
4645
4719
  getDocument: () => clone(state.doc),
4720
+ isFullscreen: isWorkbenchFullscreen,
4646
4721
  getMode: () => state.mode,
4647
4722
  getPageIndex: () => state.pageIndex,
4648
4723
  importDocx: importDocxFile,
4724
+ openFullscreen: openWorkbenchFullscreen,
4725
+ closeFullscreenAndSave: closeWorkbenchFullscreenAndSave,
4649
4726
  setMode,
4650
4727
  loadPreviewAnnotations,
4651
4728
  savePreviewAnnotations,
@@ -1,10 +1,11 @@
1
- import { EditorEngine, PlayerEngine, type EditorWorkbenchHandle, type EditorWorkbenchOptions, type EbookDoc, type PlayerDataProvider } from "@hyebook/core";
1
+ import { EditorEngine, PlayerEngine, type EditorWorkbenchHandle, type EditorWorkbenchOptions, type EbookDoc, type PlayerDataProvider, type PlayerEngineOptions } from "@hyebook/core";
2
2
  export interface Vue3EditorAdapterOptions {
3
3
  initialDoc: EbookDoc;
4
4
  onDocumentChange?: (doc: EbookDoc) => void;
5
5
  }
6
6
  export interface Vue3PlayerAdapterOptions {
7
7
  provider: PlayerDataProvider;
8
+ engineOptions?: PlayerEngineOptions;
8
9
  }
9
10
  export interface Vue3EditorWorkbenchOptions extends EditorWorkbenchOptions {
10
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EAEZ,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,QAAQ,CAAC;IACrB,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;CAAG;AAE7E,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;CAAG;AAE3E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,YAAY,CAId;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,YAAY,CAEd;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,WAAW,EACtB,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CAE3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EAEZ,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,QAAQ,CAAC;IACrB,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;CAAG;AAE7E,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;CAAG;AAE3E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,YAAY,CAId;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,YAAY,CAEd;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,WAAW,EACtB,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CAE3B"}
@@ -5,7 +5,7 @@ export function createVue3EditorAdapter(options) {
5
5
  return engine;
6
6
  }
7
7
  export function createVue3PlayerAdapter(options) {
8
- return new PlayerEngine(options.provider);
8
+ return new PlayerEngine(options.provider, options.engineOptions);
9
9
  }
10
10
  export function mountVue3EditorWorkbench(container, options = {}) {
11
11
  return mountEditorWorkbench(container, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyebook/vue3-adapter",
3
- "version": "0.2.5",
3
+ "version": "2.2.7",
4
4
  "description": "Vue3 adapter for hy-ebook core",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@hyebook/core": "^0.2.5"
20
+ "@hyebook/core": "^2.2.7"
21
21
  },
22
22
  "scripts": {
23
23
  "build": "tsc -p tsconfig.json",