@hyebook/vue3-adapter 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/src/player/ebook-player.d.ts +23 -9
- package/dist/core/src/player/ebook-player.d.ts.map +1 -1
- package/dist/core/src/player/ebook-player.js +215 -15
- package/dist/core/src/player/engine.d.ts +13 -8
- package/dist/core/src/player/engine.d.ts.map +1 -1
- package/dist/core/src/player/engine.js +155 -19
- package/dist/core/src/types/player.d.ts +41 -21
- package/dist/core/src/types/player.d.ts.map +1 -1
- package/dist/core/src/workbench/editor-workbench.js +70 -1
- package/package.json +2 -2
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { EbookDoc } from "../types/ebook";
|
|
2
|
-
import type { EBookPlayerHandle, EBookPlayerOptions,
|
|
2
|
+
import type { EBookPlayerHandle, EBookPlayerOptions, ReaderAnnotationCreateEvent, ReaderAnnotationDeleteEvent, ReaderAnnotations, ReaderAnnotationsChangeEvent, ReaderAnnotationUpdateEvent, ReaderBookmark, ReaderHighlight, ReaderNote } from "../types/player";
|
|
3
3
|
export declare class EBookPlayer implements EBookPlayerHandle {
|
|
4
4
|
private container;
|
|
5
5
|
private options;
|
|
6
6
|
private engine;
|
|
7
|
+
private runtimeDocument;
|
|
7
8
|
private root;
|
|
8
9
|
private content;
|
|
9
10
|
private selectionToolbar;
|
|
10
11
|
private highlightBtn;
|
|
12
|
+
private toolbarDivider;
|
|
11
13
|
private noteBtn;
|
|
12
14
|
private selectionOptions;
|
|
15
|
+
private inputLimits;
|
|
13
16
|
private selectionDraft;
|
|
14
17
|
private loaded;
|
|
15
18
|
private destroyed;
|
|
@@ -19,20 +22,24 @@ export declare class EBookPlayer implements EBookPlayerHandle {
|
|
|
19
22
|
private readonly handleHighlightClickBound;
|
|
20
23
|
private readonly handleNoteClickBound;
|
|
21
24
|
constructor(container: HTMLElement, options: EBookPlayerOptions);
|
|
22
|
-
load(): Promise<EbookDoc>;
|
|
25
|
+
load(doc?: EbookDoc): Promise<EbookDoc>;
|
|
23
26
|
destroy(): void;
|
|
24
27
|
getDocument(): EbookDoc;
|
|
25
28
|
getAnnotations(): ReaderAnnotations;
|
|
26
29
|
getHighlights(): ReaderHighlight[];
|
|
27
30
|
getNotes(): ReaderNote[];
|
|
28
|
-
|
|
31
|
+
getBookmarks(): ReaderBookmark[];
|
|
32
|
+
setAnnotations(annotations: ReaderAnnotations, source?: "api" | "user"): Promise<ReaderAnnotations>;
|
|
29
33
|
reloadAnnotations(): Promise<ReaderAnnotations>;
|
|
30
|
-
createHighlight(highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
31
|
-
updateHighlight(highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?:
|
|
32
|
-
deleteHighlight(highlightId: string, source?:
|
|
33
|
-
createNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
34
|
-
updateNote(noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?:
|
|
35
|
-
deleteNote(noteId: string, source?:
|
|
34
|
+
createHighlight(highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user"): Promise<ReaderHighlight>;
|
|
35
|
+
updateHighlight(highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?: "api" | "user"): Promise<ReaderHighlight | null>;
|
|
36
|
+
deleteHighlight(highlightId: string, source?: "api" | "user"): Promise<boolean>;
|
|
37
|
+
createNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user"): Promise<ReaderNote>;
|
|
38
|
+
updateNote(noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?: "api" | "user"): Promise<ReaderNote | null>;
|
|
39
|
+
deleteNote(noteId: string, source?: "api" | "user"): Promise<boolean>;
|
|
40
|
+
setBookmark(pageId: string, blockId: string, source?: "api" | "user"): Promise<ReaderBookmark | null>;
|
|
41
|
+
deleteBookmark(bookmarkId: string, source?: "api" | "user"): Promise<boolean>;
|
|
42
|
+
goToBookmark(bookmarkId: string): number | null;
|
|
36
43
|
onAnnotationCreate(listener: (event: ReaderAnnotationCreateEvent) => void): () => void;
|
|
37
44
|
onAnnotationUpdate(listener: (event: ReaderAnnotationUpdateEvent) => void): () => void;
|
|
38
45
|
onAnnotationDelete(listener: (event: ReaderAnnotationDeleteEvent) => void): () => void;
|
|
@@ -46,6 +53,7 @@ export declare class EBookPlayer implements EBookPlayerHandle {
|
|
|
46
53
|
private toBlockKey;
|
|
47
54
|
private applyHighlightMarks;
|
|
48
55
|
private applyNoteMarks;
|
|
56
|
+
private applyBookmarkMarker;
|
|
49
57
|
private wrapRangeWithMark;
|
|
50
58
|
private createRangeFromOffsets;
|
|
51
59
|
private normalizeOffsetRange;
|
|
@@ -60,6 +68,12 @@ export declare class EBookPlayer implements EBookPlayerHandle {
|
|
|
60
68
|
private handleNoteClick;
|
|
61
69
|
private resolveWidth;
|
|
62
70
|
private resolveSelectionToolbarOptions;
|
|
71
|
+
private resolveInputLimits;
|
|
72
|
+
private normalizeLimitValue;
|
|
73
|
+
private promptTextWithLimit;
|
|
74
|
+
private ensureTextBlockExists;
|
|
75
|
+
private findBookmarkByBlock;
|
|
76
|
+
private setToolbarButtonContent;
|
|
63
77
|
private ensureLoaded;
|
|
64
78
|
}
|
|
65
79
|
//# sourceMappingURL=ebook-player.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ebook-player.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/ebook-player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,QAAQ,EAAuB,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EACV,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ebook-player.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/ebook-player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,QAAQ,EAAuB,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EACV,iBAAiB,EAEjB,kBAAkB,EAGlB,2BAA2B,EAC3B,2BAA2B,EAC3B,iBAAiB,EACjB,4BAA4B,EAC5B,2BAA2B,EAC3B,cAAc,EACd,eAAe,EACf,UAAU,EAGX,MAAM,iBAAiB,CAAC;AA8DzB,qBAAa,WAAY,YAAW,iBAAiB;IACnD,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,gBAAgB,CAAiB;IACzC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAEzC;IAEF,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAE7C;IAEF,OAAO,CAAC,QAAQ,CAAC,wCAAwC,CAIvD;IAEF,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAExC;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAEnC;gBAEU,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB;IAsFzD,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAoB7C,OAAO,IAAI,IAAI;IAgCf,WAAW,IAAI,QAAQ;IAIvB,cAAc,IAAI,iBAAiB;IAInC,aAAa,IAAI,eAAe,EAAE;IAIlC,QAAQ,IAAI,UAAU,EAAE;IAIxB,YAAY,IAAI,cAAc,EAAE;IAI1B,cAAc,CAClB,WAAW,EAAE,iBAAiB,EAC9B,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAOvB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAO/C,eAAe,CACnB,SAAS,EAAE,IAAI,CACb,eAAe,EACf,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAC5C,EACD,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,eAAe,CAAC;IAOrB,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC,CAAC,EACpE,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAW5B,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,OAAO,CAAC;IAOb,UAAU,CACd,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC,EACnE,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,UAAU,CAAC;IAOhB,UAAU,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,QAAQ,GAAG,WAAW,CAAC,CAAC,EAC/D,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAOvB,UAAU,CACd,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,OAAO,CAAC;IAOb,WAAW,CACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAyB3B,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,KAAK,GAAG,MAAc,GAC7B,OAAO,CAAC,OAAO,CAAC;IAOnB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAK/C,kBAAkB,CAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,GACrD,MAAM,IAAI;IAIb,kBAAkB,CAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,GACrD,MAAM,IAAI;IAIb,kBAAkB,CAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,GACrD,MAAM,IAAI;IAIb,mBAAmB,CACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,4BAA4B,KAAK,IAAI,GACtD,MAAM,IAAI;IAIb,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,cAAc;IA2BtB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,sBAAsB;IAkE9B,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,qBAAqB;IAsD7B,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,mBAAmB;IAuB3B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,yBAAyB;IAgBjC,OAAO,CAAC,mCAAmC;YAK7B,oBAAoB;YAqBpB,eAAe;IAiC7B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,8BAA8B;IA8BtC,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,mBAAmB;IA8B3B,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,uBAAuB;IAmB/B,OAAO,CAAC,YAAY;CAKrB"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { PlayerEngine } from "./engine";
|
|
2
2
|
const EBOOK_PLAYER_STYLE_ID = "hy-ebook-lite-player-style";
|
|
3
3
|
const EBOOK_PLAYER_STYLE_VERSION = "0.1.0";
|
|
4
|
+
const HIGHLIGHT_ICON_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 22V12.5H6V8.5H18V12.5H21V22H3Z" stroke="#333333" stroke-width="2" stroke-linejoin="round"/><path d="M8.5 8.5V4L15.5 2V8.5" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
5
|
+
const NOTE_ICON_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15.4998 4.49951L19.4998 8.4995" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.99977 15.9995L17.9997 2L21.9998 5.9995L7.99975 19.9995L2.99976 20.9995L3.99977 15.9995Z" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M4.49976 15.9995L7.99975 19.4995" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.49976 17.4995L17.4998 6.4995" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
6
|
+
const BOOKMARK_ICON_SVG = `<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 3C0 1.34315 1.34315 0 3 0H27C28.6569 0 30 1.34315 30 3V38.1989C30 38.9837 29.1374 39.4627 28.4713 39.0477L15 30.6562L1.52873 39.0477C0.862627 39.4627 0 38.9837 0 38.1989L0 3Z" fill="#4FCEBB"/></svg>`;
|
|
4
7
|
const EBOOK_PLAYER_CSS = `
|
|
5
8
|
.hyepl-root{position:relative;max-width:100%;margin:0 auto;color:#0f172a;font-size:14px;line-height:1.7}
|
|
6
9
|
.hyepl-content{position:relative}
|
|
@@ -8,15 +11,27 @@ const EBOOK_PLAYER_CSS = `
|
|
|
8
11
|
.hyepl-page:last-child{margin-bottom:0}
|
|
9
12
|
.hyepl-block{margin:0 0 10px;white-space:pre-wrap;word-break:break-word}
|
|
10
13
|
.hyepl-block:last-child{margin-bottom:0}
|
|
14
|
+
.hyepl-block-text{position:relative;padding-right:18px}
|
|
11
15
|
.hyepl-block-muted{color:#64748b}
|
|
12
16
|
.hyepl-highlight{background:var(--hyepl-highlight-color,#fff59d);color:inherit;padding:0 .08em;border-radius:.2em}
|
|
13
17
|
.hyepl-note{background:rgba(14,116,144,.14);border-bottom:2px solid #0e7490;color:inherit;padding:0 .04em;border-radius:.2em}
|
|
14
|
-
.hyepl-
|
|
15
|
-
.hyepl-
|
|
16
|
-
.hyepl-toolbar
|
|
18
|
+
.hyepl-bookmark-flag{position:absolute;top:4px;right:0;display:inline-flex;align-items:center;justify-content:center;width:12px;height:16px;pointer-events:none;user-select:none}
|
|
19
|
+
.hyepl-bookmark-flag svg{display:block;width:12px;height:16px}
|
|
20
|
+
.hyepl-selection-toolbar{position:fixed;z-index:10060;display:none;align-items:stretch;gap:0;padding:8px 10px;border-radius:14px;border:1px solid #e5e7eb;background:#ffffff;box-shadow:0 10px 24px rgba(15,23,42,.14);transform:translate(-50%,-100%);overflow:visible}
|
|
21
|
+
.hyepl-selection-toolbar.show{display:flex}
|
|
22
|
+
.hyepl-selection-toolbar:after{content:"";position:absolute;bottom:-10px;left:50%;transform:translateX(-50%);border-width:10px 10px 0 10px;border-style:solid;border-color:#ffffff transparent transparent transparent;filter:drop-shadow(0 1px 0 #e5e7eb);pointer-events:none}
|
|
23
|
+
|
|
24
|
+
.hyepl-toolbar-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;width:57px;height:67px;padding:10px 8px 8px;border:0;border-radius:10px;background:transparent;color:#111827;cursor:pointer;transition:background .2s ease,color .2s ease}
|
|
25
|
+
.hyepl-toolbar-btn:hover{background:#f3f4f6}
|
|
26
|
+
.hyepl-toolbar-btn:focus-visible{outline:2px solid #38bdf8;outline-offset:2px}
|
|
27
|
+
.hyepl-toolbar-divider{align-self:stretch;width:1px;margin:6px 8px;background:#e5e7eb}
|
|
28
|
+
.hyepl-toolbar-icon{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}
|
|
29
|
+
.hyepl-toolbar-icon svg{display:block;width:24px;height:24px}
|
|
30
|
+
.hyepl-toolbar-text{font-size:14px;font-weight:700;color:#111827}
|
|
17
31
|
`;
|
|
18
32
|
export class EBookPlayer {
|
|
19
33
|
constructor(container, options) {
|
|
34
|
+
this.runtimeDocument = null;
|
|
20
35
|
this.selectionDraft = null;
|
|
21
36
|
this.loaded = false;
|
|
22
37
|
this.destroyed = false;
|
|
@@ -37,8 +52,10 @@ export class EBookPlayer {
|
|
|
37
52
|
};
|
|
38
53
|
this.container = container;
|
|
39
54
|
this.options = options;
|
|
40
|
-
|
|
55
|
+
const runtimeProvider = createRuntimeProvider(options.provider, () => this.runtimeDocument);
|
|
56
|
+
this.engine = new PlayerEngine(runtimeProvider, options.engineOptions);
|
|
41
57
|
this.selectionOptions = this.resolveSelectionToolbarOptions(options.selectionToolbar);
|
|
58
|
+
this.inputLimits = this.resolveInputLimits(options.inputLimits);
|
|
42
59
|
ensureEBookPlayerStyles();
|
|
43
60
|
this.root = document.createElement("section");
|
|
44
61
|
this.root.className = "hyepl-root";
|
|
@@ -50,14 +67,17 @@ export class EBookPlayer {
|
|
|
50
67
|
this.highlightBtn = document.createElement("button");
|
|
51
68
|
this.highlightBtn.type = "button";
|
|
52
69
|
this.highlightBtn.className = "hyepl-toolbar-btn";
|
|
53
|
-
this.highlightBtn
|
|
70
|
+
this.setToolbarButtonContent(this.highlightBtn, HIGHLIGHT_ICON_SVG, this.selectionOptions.highlightButtonText);
|
|
54
71
|
this.highlightBtn.setAttribute("aria-label", this.selectionOptions.highlightButtonText);
|
|
72
|
+
this.toolbarDivider = document.createElement("div");
|
|
73
|
+
this.toolbarDivider.className = "hyepl-toolbar-divider";
|
|
74
|
+
this.toolbarDivider.setAttribute("aria-hidden", "true");
|
|
55
75
|
this.noteBtn = document.createElement("button");
|
|
56
76
|
this.noteBtn.type = "button";
|
|
57
77
|
this.noteBtn.className = "hyepl-toolbar-btn";
|
|
58
|
-
this.noteBtn
|
|
78
|
+
this.setToolbarButtonContent(this.noteBtn, NOTE_ICON_SVG, this.selectionOptions.noteButtonText);
|
|
59
79
|
this.noteBtn.setAttribute("aria-label", this.selectionOptions.noteButtonText);
|
|
60
|
-
this.selectionToolbar.append(this.highlightBtn, this.noteBtn);
|
|
80
|
+
this.selectionToolbar.append(this.highlightBtn, this.toolbarDivider, this.noteBtn);
|
|
61
81
|
this.root.append(this.content, this.selectionToolbar);
|
|
62
82
|
this.container.innerHTML = "";
|
|
63
83
|
this.container.append(this.root);
|
|
@@ -68,14 +88,20 @@ export class EBookPlayer {
|
|
|
68
88
|
document.addEventListener("selectionchange", this.handleSelectionChangeBound);
|
|
69
89
|
document.addEventListener("pointerdown", this.handleDocumentPointerDownBound);
|
|
70
90
|
}
|
|
71
|
-
async load() {
|
|
72
|
-
|
|
91
|
+
async load(doc) {
|
|
92
|
+
if (doc) {
|
|
93
|
+
this.runtimeDocument = cloneEbookDoc(doc);
|
|
94
|
+
}
|
|
95
|
+
if (!this.options.provider && !this.runtimeDocument) {
|
|
96
|
+
throw new Error("Provider is not configured. Call load(doc) when using EBookPlayer without provider.");
|
|
97
|
+
}
|
|
98
|
+
const loadedDoc = await this.engine.load();
|
|
73
99
|
this.loaded = true;
|
|
74
100
|
if (this.options.initialAnnotations) {
|
|
75
101
|
await this.engine.setAnnotations(this.options.initialAnnotations, "api");
|
|
76
102
|
}
|
|
77
103
|
this.renderDocument();
|
|
78
|
-
return
|
|
104
|
+
return loadedDoc;
|
|
79
105
|
}
|
|
80
106
|
destroy() {
|
|
81
107
|
if (this.destroyed) {
|
|
@@ -105,6 +131,9 @@ export class EBookPlayer {
|
|
|
105
131
|
getNotes() {
|
|
106
132
|
return this.engine.getNotes();
|
|
107
133
|
}
|
|
134
|
+
getBookmarks() {
|
|
135
|
+
return this.engine.getBookmarks();
|
|
136
|
+
}
|
|
108
137
|
async setAnnotations(annotations, source = "api") {
|
|
109
138
|
this.ensureLoaded();
|
|
110
139
|
const all = await this.engine.setAnnotations(annotations, source);
|
|
@@ -153,6 +182,28 @@ export class EBookPlayer {
|
|
|
153
182
|
this.renderDocument();
|
|
154
183
|
return deleted;
|
|
155
184
|
}
|
|
185
|
+
async setBookmark(pageId, blockId, source = "api") {
|
|
186
|
+
this.ensureLoaded();
|
|
187
|
+
this.ensureTextBlockExists(pageId, blockId);
|
|
188
|
+
const current = this.findBookmarkByBlock(pageId, blockId);
|
|
189
|
+
const content = this.promptTextWithLimit("请输入书签内容", current?.content || "", this.inputLimits.bookmarkMaxLength);
|
|
190
|
+
if (content === null) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const bookmark = await this.engine.setBookmark(pageId, blockId, content, source);
|
|
194
|
+
this.renderDocument();
|
|
195
|
+
return bookmark;
|
|
196
|
+
}
|
|
197
|
+
async deleteBookmark(bookmarkId, source = "api") {
|
|
198
|
+
this.ensureLoaded();
|
|
199
|
+
const deleted = await this.engine.deleteBookmark(bookmarkId, source);
|
|
200
|
+
this.renderDocument();
|
|
201
|
+
return deleted;
|
|
202
|
+
}
|
|
203
|
+
goToBookmark(bookmarkId) {
|
|
204
|
+
this.ensureLoaded();
|
|
205
|
+
return this.engine.goToBookmark(bookmarkId);
|
|
206
|
+
}
|
|
156
207
|
onAnnotationCreate(listener) {
|
|
157
208
|
return this.engine.onAnnotationCreate(listener);
|
|
158
209
|
}
|
|
@@ -201,8 +252,12 @@ export class EBookPlayer {
|
|
|
201
252
|
const blockKey = this.toBlockKey(pageId, block.id);
|
|
202
253
|
const highlights = buckets.highlightsByBlock.get(blockKey) || [];
|
|
203
254
|
const notes = buckets.notesByBlock.get(blockKey) || [];
|
|
255
|
+
const bookmark = buckets.bookmarksByBlock.get(blockKey) || null;
|
|
204
256
|
this.applyHighlightMarks(blockTextNode, highlights);
|
|
205
257
|
this.applyNoteMarks(blockTextNode, notes);
|
|
258
|
+
if (bookmark) {
|
|
259
|
+
this.applyBookmarkMarker(blockTextNode, bookmark);
|
|
260
|
+
}
|
|
206
261
|
return blockTextNode;
|
|
207
262
|
}
|
|
208
263
|
renderNonTextBlock(block) {
|
|
@@ -233,6 +288,7 @@ export class EBookPlayer {
|
|
|
233
288
|
createAnnotationBuckets(annotations) {
|
|
234
289
|
const highlightsByBlock = new Map();
|
|
235
290
|
const notesByBlock = new Map();
|
|
291
|
+
const bookmarksByBlock = new Map();
|
|
236
292
|
annotations.highlights.forEach((item) => {
|
|
237
293
|
const key = this.toBlockKey(item.pageId, item.blockId);
|
|
238
294
|
const current = highlightsByBlock.get(key) || [];
|
|
@@ -245,7 +301,14 @@ export class EBookPlayer {
|
|
|
245
301
|
current.push(item);
|
|
246
302
|
notesByBlock.set(key, current);
|
|
247
303
|
});
|
|
248
|
-
|
|
304
|
+
const bookmarks = Array.isArray(annotations.bookmarks)
|
|
305
|
+
? annotations.bookmarks
|
|
306
|
+
: [];
|
|
307
|
+
bookmarks.forEach((item) => {
|
|
308
|
+
const key = this.toBlockKey(item.pageId, item.blockId);
|
|
309
|
+
bookmarksByBlock.set(key, item);
|
|
310
|
+
});
|
|
311
|
+
return { highlightsByBlock, notesByBlock, bookmarksByBlock };
|
|
249
312
|
}
|
|
250
313
|
toBlockKey(pageId, blockId) {
|
|
251
314
|
return `${pageId}::${blockId}`;
|
|
@@ -289,6 +352,15 @@ export class EBookPlayer {
|
|
|
289
352
|
this.wrapRangeWithMark(range, mark);
|
|
290
353
|
});
|
|
291
354
|
}
|
|
355
|
+
applyBookmarkMarker(container, bookmark) {
|
|
356
|
+
const marker = document.createElement("span");
|
|
357
|
+
marker.className = "hyepl-bookmark-flag";
|
|
358
|
+
marker.dataset.bookmarkId = bookmark.id;
|
|
359
|
+
marker.title = bookmark.content;
|
|
360
|
+
marker.setAttribute("aria-label", `书签:${bookmark.content}`);
|
|
361
|
+
marker.innerHTML = BOOKMARK_ICON_SVG;
|
|
362
|
+
container.append(marker);
|
|
363
|
+
}
|
|
292
364
|
wrapRangeWithMark(range, mark) {
|
|
293
365
|
try {
|
|
294
366
|
range.surroundContents(mark);
|
|
@@ -395,7 +467,7 @@ export class EBookPlayer {
|
|
|
395
467
|
text: selection.toString().trim(),
|
|
396
468
|
};
|
|
397
469
|
this.selectionToolbar.style.left = `${Math.round(rect.left + rect.width / 2)}px`;
|
|
398
|
-
this.selectionToolbar.style.top = `${Math.round(rect.top -
|
|
470
|
+
this.selectionToolbar.style.top = `${Math.round(rect.top - 20)}px`;
|
|
399
471
|
this.selectionToolbar.classList.add("show");
|
|
400
472
|
}
|
|
401
473
|
resolveTextContainer(node) {
|
|
@@ -477,9 +549,8 @@ export class EBookPlayer {
|
|
|
477
549
|
return;
|
|
478
550
|
}
|
|
479
551
|
const defaultText = this.selectionDraft.text || "";
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
if (!content) {
|
|
552
|
+
const content = this.promptTextWithLimit("请输入笔记内容", defaultText, this.inputLimits.noteMaxLength);
|
|
553
|
+
if (content === null) {
|
|
483
554
|
return;
|
|
484
555
|
}
|
|
485
556
|
const payload = {
|
|
@@ -526,6 +597,72 @@ export class EBookPlayer {
|
|
|
526
597
|
: "#0e7490",
|
|
527
598
|
};
|
|
528
599
|
}
|
|
600
|
+
resolveInputLimits(options) {
|
|
601
|
+
const noteMaxLength = this.normalizeLimitValue(options?.noteMaxLength, 100);
|
|
602
|
+
const bookmarkMaxLength = this.normalizeLimitValue(options?.bookmarkMaxLength, 100);
|
|
603
|
+
return {
|
|
604
|
+
noteMaxLength,
|
|
605
|
+
bookmarkMaxLength,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
normalizeLimitValue(value, fallback) {
|
|
609
|
+
if (!Number.isFinite(value)) {
|
|
610
|
+
return fallback;
|
|
611
|
+
}
|
|
612
|
+
return Math.max(1, Math.floor(value));
|
|
613
|
+
}
|
|
614
|
+
promptTextWithLimit(title, defaultText, maxLength) {
|
|
615
|
+
let nextDefault = defaultText;
|
|
616
|
+
while (true) {
|
|
617
|
+
const raw = window.prompt(`${title}(最多${maxLength}字)`, nextDefault);
|
|
618
|
+
if (raw === null) {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
const trimmed = raw.trim();
|
|
622
|
+
if (!trimmed) {
|
|
623
|
+
window.alert("内容不能为空");
|
|
624
|
+
nextDefault = "";
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (trimmed.length > maxLength) {
|
|
628
|
+
window.alert(`最多输入${maxLength}字,当前${trimmed.length}字。`);
|
|
629
|
+
nextDefault = trimmed.slice(0, maxLength);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
return trimmed;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
ensureTextBlockExists(pageId, blockId) {
|
|
636
|
+
const doc = this.engine.getDocument();
|
|
637
|
+
const page = doc.pages.find((item) => item.id === pageId);
|
|
638
|
+
if (!page) {
|
|
639
|
+
throw new Error(`Page not found: ${pageId}`);
|
|
640
|
+
}
|
|
641
|
+
const block = page.blocks.find((item) => item.id === blockId);
|
|
642
|
+
if (!block) {
|
|
643
|
+
throw new Error(`Block not found: ${blockId}`);
|
|
644
|
+
}
|
|
645
|
+
if (block.type !== "text") {
|
|
646
|
+
throw new Error("Bookmark is only supported on text blocks.");
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
findBookmarkByBlock(pageId, blockId) {
|
|
650
|
+
const found = this.engine
|
|
651
|
+
.getBookmarks()
|
|
652
|
+
.find((item) => item.pageId === pageId && item.blockId === blockId);
|
|
653
|
+
return found || null;
|
|
654
|
+
}
|
|
655
|
+
setToolbarButtonContent(button, iconSvg, labelText) {
|
|
656
|
+
button.innerHTML = "";
|
|
657
|
+
const icon = document.createElement("span");
|
|
658
|
+
icon.className = "hyepl-toolbar-icon";
|
|
659
|
+
icon.setAttribute("aria-hidden", "true");
|
|
660
|
+
icon.innerHTML = iconSvg;
|
|
661
|
+
const text = document.createElement("span");
|
|
662
|
+
text.className = "hyepl-toolbar-text";
|
|
663
|
+
text.textContent = labelText;
|
|
664
|
+
button.append(icon, text);
|
|
665
|
+
}
|
|
529
666
|
ensureLoaded() {
|
|
530
667
|
if (!this.loaded) {
|
|
531
668
|
throw new Error("Document is not loaded. Call load() first.");
|
|
@@ -548,3 +685,66 @@ function ensureEBookPlayerStyles() {
|
|
|
548
685
|
style.textContent = EBOOK_PLAYER_CSS;
|
|
549
686
|
document.head.append(style);
|
|
550
687
|
}
|
|
688
|
+
function createRuntimeProvider(provider, getRuntimeDocument) {
|
|
689
|
+
const runtimeProvider = {
|
|
690
|
+
getData: async () => {
|
|
691
|
+
const runtimeDocument = getRuntimeDocument();
|
|
692
|
+
if (runtimeDocument) {
|
|
693
|
+
return cloneEbookDoc(runtimeDocument);
|
|
694
|
+
}
|
|
695
|
+
if (provider) {
|
|
696
|
+
return provider.getData();
|
|
697
|
+
}
|
|
698
|
+
throw new Error("Provider is not configured. Call load(doc) when using EBookPlayer without provider.");
|
|
699
|
+
},
|
|
700
|
+
...(provider?.loadProgress
|
|
701
|
+
? {
|
|
702
|
+
loadProgress: (bookId) => provider.loadProgress(bookId),
|
|
703
|
+
}
|
|
704
|
+
: {}),
|
|
705
|
+
...(provider?.saveProgress
|
|
706
|
+
? {
|
|
707
|
+
saveProgress: (progress) => provider.saveProgress(progress),
|
|
708
|
+
}
|
|
709
|
+
: {}),
|
|
710
|
+
...(provider?.loadAnnotations
|
|
711
|
+
? {
|
|
712
|
+
loadAnnotations: (bookId) => provider.loadAnnotations(bookId),
|
|
713
|
+
}
|
|
714
|
+
: {}),
|
|
715
|
+
...(provider?.saveAnnotations
|
|
716
|
+
? {
|
|
717
|
+
saveAnnotations: (bookId, annotations) => provider.saveAnnotations(bookId, annotations),
|
|
718
|
+
}
|
|
719
|
+
: {}),
|
|
720
|
+
...(provider?.loadNotes
|
|
721
|
+
? {
|
|
722
|
+
loadNotes: (bookId) => provider.loadNotes(bookId),
|
|
723
|
+
}
|
|
724
|
+
: {}),
|
|
725
|
+
...(provider?.saveNotes
|
|
726
|
+
? {
|
|
727
|
+
saveNotes: (bookId, notes) => provider.saveNotes(bookId, notes),
|
|
728
|
+
}
|
|
729
|
+
: {}),
|
|
730
|
+
...(provider?.loadBookmarks
|
|
731
|
+
? {
|
|
732
|
+
loadBookmarks: (bookId) => provider.loadBookmarks(bookId),
|
|
733
|
+
}
|
|
734
|
+
: {}),
|
|
735
|
+
...(provider?.saveBookmarks
|
|
736
|
+
? {
|
|
737
|
+
saveBookmarks: (bookId, bookmarks) => provider.saveBookmarks(bookId, bookmarks),
|
|
738
|
+
}
|
|
739
|
+
: {}),
|
|
740
|
+
...(provider?.onError
|
|
741
|
+
? {
|
|
742
|
+
onError: (error) => provider.onError(error),
|
|
743
|
+
}
|
|
744
|
+
: {}),
|
|
745
|
+
};
|
|
746
|
+
return runtimeProvider;
|
|
747
|
+
}
|
|
748
|
+
function cloneEbookDoc(doc) {
|
|
749
|
+
return JSON.parse(JSON.stringify(doc));
|
|
750
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { EbookDoc } from "../types/ebook";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReaderAnnotationCreateEvent, ReaderAnnotationDeleteEvent, ReaderAnnotations, ReaderAnnotationsChangeEvent, ReaderAnnotationUpdateEvent, ReaderBookmark, ReaderHighlight, PlayerDataProvider, PlayerEngineOptions, PlayerFeatures, ReaderNote, ReaderProgress } from "../types/player";
|
|
3
3
|
export interface PlayerState {
|
|
4
4
|
currentPage: number;
|
|
5
5
|
zoom: number;
|
|
6
6
|
highlights: ReaderHighlight[];
|
|
7
7
|
notes: ReaderNote[];
|
|
8
|
+
bookmarks: ReaderBookmark[];
|
|
8
9
|
progress: ReaderProgress | null;
|
|
9
10
|
}
|
|
10
11
|
type AnnotationCreateListener = (event: ReaderAnnotationCreateEvent) => void;
|
|
@@ -26,25 +27,29 @@ export declare class PlayerEngine {
|
|
|
26
27
|
getState(): PlayerState;
|
|
27
28
|
getHighlights(): ReaderHighlight[];
|
|
28
29
|
getNotes(): ReaderNote[];
|
|
30
|
+
getBookmarks(): ReaderBookmark[];
|
|
29
31
|
getAnnotations(): ReaderAnnotations;
|
|
30
32
|
getFeatures(): Required<PlayerFeatures>;
|
|
31
33
|
isBuiltInNotesModuleEnabled(): boolean;
|
|
32
34
|
reloadAnnotations(): Promise<ReaderAnnotations>;
|
|
33
|
-
setAnnotations(annotations: ReaderAnnotations, source?:
|
|
35
|
+
setAnnotations(annotations: ReaderAnnotations, source?: "api" | "user"): Promise<ReaderAnnotations>;
|
|
34
36
|
goToHighlight(highlightId: string): number | null;
|
|
35
37
|
goToNote(noteId: string): number | null;
|
|
38
|
+
goToBookmark(bookmarkId: string): number | null;
|
|
36
39
|
goToPage(index: number): number;
|
|
37
40
|
nextPage(): number;
|
|
38
41
|
prevPage(): number;
|
|
39
42
|
setZoom(zoom: number): number;
|
|
40
43
|
saveProgress(): Promise<void>;
|
|
41
|
-
createHighlight(highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
42
|
-
updateHighlight(highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?:
|
|
43
|
-
deleteHighlight(highlightId: string, source?:
|
|
44
|
+
createHighlight(highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user"): Promise<ReaderHighlight>;
|
|
45
|
+
updateHighlight(highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?: "api" | "user"): Promise<ReaderHighlight | null>;
|
|
46
|
+
deleteHighlight(highlightId: string, source?: "api" | "user"): Promise<boolean>;
|
|
44
47
|
addNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">): Promise<ReaderNote>;
|
|
45
|
-
createNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
46
|
-
updateNote(noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?:
|
|
47
|
-
deleteNote(noteId: string, source?:
|
|
48
|
+
createNote(note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user"): Promise<ReaderNote>;
|
|
49
|
+
updateNote(noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?: "api" | "user"): Promise<ReaderNote | null>;
|
|
50
|
+
deleteNote(noteId: string, source?: "api" | "user"): Promise<boolean>;
|
|
51
|
+
setBookmark(pageId: string, blockId: string, content: string, source?: "api" | "user"): Promise<ReaderBookmark>;
|
|
52
|
+
deleteBookmark(bookmarkId: string, source?: "api" | "user"): Promise<boolean>;
|
|
48
53
|
onAnnotationCreate(listener: AnnotationCreateListener): () => void;
|
|
49
54
|
onAnnotationUpdate(listener: AnnotationUpdateListener): () => void;
|
|
50
55
|
onAnnotationDelete(listener: AnnotationDeleteListener): () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAsB,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAEV,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../../core/src/player/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ export class PlayerEngine {
|
|
|
10
10
|
zoom: 1,
|
|
11
11
|
highlights: [],
|
|
12
12
|
notes: [],
|
|
13
|
+
bookmarks: [],
|
|
13
14
|
progress: null,
|
|
14
15
|
};
|
|
15
16
|
this.annotationCreateListeners = new Set();
|
|
@@ -50,10 +51,14 @@ export class PlayerEngine {
|
|
|
50
51
|
getNotes() {
|
|
51
52
|
return clone(this.state.notes);
|
|
52
53
|
}
|
|
54
|
+
getBookmarks() {
|
|
55
|
+
return clone(this.state.bookmarks);
|
|
56
|
+
}
|
|
53
57
|
getAnnotations() {
|
|
54
58
|
return {
|
|
55
59
|
highlights: this.getHighlights(),
|
|
56
60
|
notes: this.getNotes(),
|
|
61
|
+
bookmarks: this.getBookmarks(),
|
|
57
62
|
};
|
|
58
63
|
}
|
|
59
64
|
getFeatures() {
|
|
@@ -76,6 +81,7 @@ export class PlayerEngine {
|
|
|
76
81
|
const normalized = this.normalizeAnnotations(annotations, this.doc.id);
|
|
77
82
|
this.state.highlights = normalized.highlights;
|
|
78
83
|
this.state.notes = normalized.notes;
|
|
84
|
+
this.state.bookmarks = normalized.bookmarks || [];
|
|
79
85
|
await this.persistAnnotations();
|
|
80
86
|
const all = this.getAnnotations();
|
|
81
87
|
this.emitAnnotationsChange({
|
|
@@ -90,6 +96,12 @@ export class PlayerEngine {
|
|
|
90
96
|
source,
|
|
91
97
|
all,
|
|
92
98
|
});
|
|
99
|
+
this.emitAnnotationsChange({
|
|
100
|
+
action: "update",
|
|
101
|
+
kind: "bookmark",
|
|
102
|
+
source,
|
|
103
|
+
all,
|
|
104
|
+
});
|
|
93
105
|
return all;
|
|
94
106
|
}
|
|
95
107
|
goToHighlight(highlightId) {
|
|
@@ -108,6 +120,14 @@ export class PlayerEngine {
|
|
|
108
120
|
const pageIndex = this.findPageIndexByPageId(target.pageId);
|
|
109
121
|
return pageIndex >= 0 ? this.goToPage(pageIndex) : null;
|
|
110
122
|
}
|
|
123
|
+
goToBookmark(bookmarkId) {
|
|
124
|
+
const target = this.state.bookmarks.find((item) => item.id === bookmarkId);
|
|
125
|
+
if (!target) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const pageIndex = this.findPageIndexByPageId(target.pageId);
|
|
129
|
+
return pageIndex >= 0 ? this.goToPage(pageIndex) : null;
|
|
130
|
+
}
|
|
111
131
|
goToPage(index) {
|
|
112
132
|
const doc = this.getDocument();
|
|
113
133
|
const safeIndex = Math.max(0, Math.min(index, doc.pages.length - 1));
|
|
@@ -133,14 +153,14 @@ export class PlayerEngine {
|
|
|
133
153
|
bookId: this.doc.id,
|
|
134
154
|
lastPage: this.state.currentPage,
|
|
135
155
|
zoom: this.state.zoom,
|
|
136
|
-
updatedAt:
|
|
156
|
+
updatedAt: Date.now(),
|
|
137
157
|
};
|
|
138
158
|
this.state.progress = progress;
|
|
139
159
|
await this.provider.saveProgress(progress);
|
|
140
160
|
}
|
|
141
161
|
async createHighlight(highlight, source = "api") {
|
|
142
162
|
const doc = this.getDocument();
|
|
143
|
-
const now =
|
|
163
|
+
const now = Date.now();
|
|
144
164
|
const nextHighlight = {
|
|
145
165
|
...highlight,
|
|
146
166
|
range: this.normalizeRange(highlight.range),
|
|
@@ -169,7 +189,7 @@ export class PlayerEngine {
|
|
|
169
189
|
id: current.id,
|
|
170
190
|
bookId: current.bookId,
|
|
171
191
|
createdAt: current.createdAt,
|
|
172
|
-
updatedAt:
|
|
192
|
+
updatedAt: Date.now(),
|
|
173
193
|
range: patch.range ? this.normalizeRange(patch.range) : current.range,
|
|
174
194
|
};
|
|
175
195
|
this.state.highlights[index] = nextHighlight;
|
|
@@ -195,7 +215,7 @@ export class PlayerEngine {
|
|
|
195
215
|
}
|
|
196
216
|
async createNote(note, source = "api") {
|
|
197
217
|
const doc = this.getDocument();
|
|
198
|
-
const now =
|
|
218
|
+
const now = Date.now();
|
|
199
219
|
const nextNote = {
|
|
200
220
|
...note,
|
|
201
221
|
id: `note-${Date.now()}`,
|
|
@@ -223,7 +243,7 @@ export class PlayerEngine {
|
|
|
223
243
|
id: current.id,
|
|
224
244
|
bookId: current.bookId,
|
|
225
245
|
createdAt: current.createdAt,
|
|
226
|
-
updatedAt:
|
|
246
|
+
updatedAt: Date.now(),
|
|
227
247
|
...(patch.range ? { range: this.normalizeRange(patch.range) } : {}),
|
|
228
248
|
};
|
|
229
249
|
this.state.notes[index] = nextNote;
|
|
@@ -244,6 +264,53 @@ export class PlayerEngine {
|
|
|
244
264
|
this.emitAnnotationDelete("note", removedNote, source);
|
|
245
265
|
return true;
|
|
246
266
|
}
|
|
267
|
+
async setBookmark(pageId, blockId, content, source = "api") {
|
|
268
|
+
const doc = this.getDocument();
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
const normalizedContent = content.trim();
|
|
271
|
+
const index = this.state.bookmarks.findIndex((item) => item.pageId === pageId && item.blockId === blockId);
|
|
272
|
+
if (index >= 0) {
|
|
273
|
+
const current = this.state.bookmarks[index];
|
|
274
|
+
if (!current) {
|
|
275
|
+
throw new Error("Bookmark update failed.");
|
|
276
|
+
}
|
|
277
|
+
const updatedBookmark = {
|
|
278
|
+
...current,
|
|
279
|
+
content: normalizedContent,
|
|
280
|
+
updatedAt: now,
|
|
281
|
+
};
|
|
282
|
+
this.state.bookmarks[index] = updatedBookmark;
|
|
283
|
+
await this.persistAnnotations();
|
|
284
|
+
this.emitAnnotationUpdate("bookmark", current, updatedBookmark, source);
|
|
285
|
+
return clone(updatedBookmark);
|
|
286
|
+
}
|
|
287
|
+
const nextBookmark = {
|
|
288
|
+
id: `bookmark-${Date.now()}`,
|
|
289
|
+
bookId: doc.id,
|
|
290
|
+
pageId,
|
|
291
|
+
blockId,
|
|
292
|
+
content: normalizedContent,
|
|
293
|
+
createdAt: now,
|
|
294
|
+
updatedAt: now,
|
|
295
|
+
};
|
|
296
|
+
this.state.bookmarks.push(nextBookmark);
|
|
297
|
+
await this.persistAnnotations();
|
|
298
|
+
this.emitAnnotationCreate("bookmark", nextBookmark, source);
|
|
299
|
+
return clone(nextBookmark);
|
|
300
|
+
}
|
|
301
|
+
async deleteBookmark(bookmarkId, source = "api") {
|
|
302
|
+
const index = this.state.bookmarks.findIndex((item) => item.id === bookmarkId);
|
|
303
|
+
if (index < 0) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
const [removedBookmark] = this.state.bookmarks.splice(index, 1);
|
|
307
|
+
if (!removedBookmark) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
await this.persistAnnotations();
|
|
311
|
+
this.emitAnnotationDelete("bookmark", removedBookmark, source);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
247
314
|
onAnnotationCreate(listener) {
|
|
248
315
|
this.annotationCreateListeners.add(listener);
|
|
249
316
|
return () => {
|
|
@@ -275,9 +342,13 @@ export class PlayerEngine {
|
|
|
275
342
|
if (this.provider.loadProgress) {
|
|
276
343
|
const progress = await this.provider.loadProgress(this.doc.id);
|
|
277
344
|
if (progress) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
345
|
+
const normalizedProgress = {
|
|
346
|
+
...progress,
|
|
347
|
+
updatedAt: toTimestamp(progress.updatedAt, Date.now()),
|
|
348
|
+
};
|
|
349
|
+
this.state.progress = normalizedProgress;
|
|
350
|
+
this.state.currentPage = normalizedProgress.lastPage;
|
|
351
|
+
this.state.zoom = normalizedProgress.zoom;
|
|
281
352
|
}
|
|
282
353
|
}
|
|
283
354
|
await this.loadAnnotationsState();
|
|
@@ -293,6 +364,7 @@ export class PlayerEngine {
|
|
|
293
364
|
const normalized = this.normalizeAnnotations(annotations, this.doc.id);
|
|
294
365
|
this.state.highlights = normalized.highlights;
|
|
295
366
|
this.state.notes = normalized.notes;
|
|
367
|
+
this.state.bookmarks = normalized.bookmarks || [];
|
|
296
368
|
restoredFromAnnotationsProvider = true;
|
|
297
369
|
}
|
|
298
370
|
}
|
|
@@ -300,11 +372,25 @@ export class PlayerEngine {
|
|
|
300
372
|
const fromDocument = this.mapPreviewAnnotationsToReader(this.doc.meta.previewAnnotations, this.doc.id);
|
|
301
373
|
this.state.highlights = fromDocument.highlights;
|
|
302
374
|
this.state.notes = fromDocument.notes;
|
|
375
|
+
this.state.bookmarks = fromDocument.bookmarks || [];
|
|
303
376
|
if (this.provider.loadNotes) {
|
|
304
377
|
const notes = await this.provider.loadNotes(this.doc.id);
|
|
305
|
-
|
|
378
|
+
const normalized = this.normalizeAnnotations({
|
|
379
|
+
highlights: [],
|
|
380
|
+
notes: Array.isArray(notes) ? notes : [],
|
|
381
|
+
}, this.doc.id);
|
|
382
|
+
this.state.notes = normalized.notes;
|
|
306
383
|
}
|
|
307
384
|
}
|
|
385
|
+
if (this.provider.loadBookmarks) {
|
|
386
|
+
const bookmarks = await this.provider.loadBookmarks(this.doc.id);
|
|
387
|
+
const normalized = this.normalizeAnnotations({
|
|
388
|
+
highlights: [],
|
|
389
|
+
notes: [],
|
|
390
|
+
bookmarks: Array.isArray(bookmarks) ? bookmarks : [],
|
|
391
|
+
}, this.doc.id);
|
|
392
|
+
this.state.bookmarks = normalized.bookmarks || [];
|
|
393
|
+
}
|
|
308
394
|
}
|
|
309
395
|
findPageIndexByPageId(pageId) {
|
|
310
396
|
if (!this.doc) {
|
|
@@ -320,14 +406,19 @@ export class PlayerEngine {
|
|
|
320
406
|
: { start: safeEnd, end: safeStart };
|
|
321
407
|
}
|
|
322
408
|
normalizeAnnotations(annotations, bookId) {
|
|
323
|
-
const now =
|
|
409
|
+
const now = Date.now();
|
|
324
410
|
const highlights = Array.isArray(annotations?.highlights)
|
|
325
411
|
? annotations.highlights
|
|
326
412
|
: [];
|
|
327
413
|
const notes = Array.isArray(annotations?.notes) ? annotations.notes : [];
|
|
414
|
+
const bookmarks = Array.isArray(annotations?.bookmarks)
|
|
415
|
+
? annotations.bookmarks
|
|
416
|
+
: [];
|
|
328
417
|
return {
|
|
329
418
|
highlights: highlights.map((item, index) => {
|
|
330
419
|
const fallbackId = `highlight-${Date.now()}-${index}`;
|
|
420
|
+
const createdAt = toTimestamp(item.createdAt, now);
|
|
421
|
+
const updatedAt = toTimestamp(item.updatedAt, createdAt);
|
|
331
422
|
return {
|
|
332
423
|
...clone(item),
|
|
333
424
|
id: item.id || fallbackId,
|
|
@@ -335,12 +426,14 @@ export class PlayerEngine {
|
|
|
335
426
|
pageId: item.pageId || "",
|
|
336
427
|
blockId: item.blockId || "preview-flow-text",
|
|
337
428
|
range: this.normalizeRange(item.range),
|
|
338
|
-
createdAt
|
|
339
|
-
updatedAt
|
|
429
|
+
createdAt,
|
|
430
|
+
updatedAt,
|
|
340
431
|
};
|
|
341
432
|
}),
|
|
342
433
|
notes: notes.map((item, index) => {
|
|
343
434
|
const fallbackId = `note-${Date.now()}-${index}`;
|
|
435
|
+
const createdAt = toTimestamp(item.createdAt, now);
|
|
436
|
+
const updatedAt = toTimestamp(item.updatedAt, createdAt);
|
|
344
437
|
return {
|
|
345
438
|
...clone(item),
|
|
346
439
|
id: item.id || fallbackId,
|
|
@@ -349,17 +442,34 @@ export class PlayerEngine {
|
|
|
349
442
|
blockId: item.blockId || "preview-flow-text",
|
|
350
443
|
...(item.range ? { range: this.normalizeRange(item.range) } : {}),
|
|
351
444
|
content: item.content || "",
|
|
352
|
-
createdAt
|
|
353
|
-
updatedAt
|
|
445
|
+
createdAt,
|
|
446
|
+
updatedAt,
|
|
447
|
+
};
|
|
448
|
+
}),
|
|
449
|
+
bookmarks: bookmarks.map((item, index) => {
|
|
450
|
+
const fallbackId = `bookmark-${Date.now()}-${index}`;
|
|
451
|
+
const createdAt = toTimestamp(item.createdAt, now);
|
|
452
|
+
const updatedAt = toTimestamp(item.updatedAt, createdAt);
|
|
453
|
+
return {
|
|
454
|
+
...clone(item),
|
|
455
|
+
id: item.id || fallbackId,
|
|
456
|
+
bookId: item.bookId || bookId || "",
|
|
457
|
+
pageId: item.pageId || "",
|
|
458
|
+
blockId: item.blockId || "",
|
|
459
|
+
content: item.content || "",
|
|
460
|
+
createdAt,
|
|
461
|
+
updatedAt,
|
|
354
462
|
};
|
|
355
463
|
}),
|
|
356
464
|
};
|
|
357
465
|
}
|
|
358
466
|
mapPreviewAnnotationsToReader(annotations, bookId) {
|
|
359
|
-
const now =
|
|
467
|
+
const now = Date.now();
|
|
360
468
|
const safe = annotations || { highlights: [], notes: [] };
|
|
361
469
|
const highlights = safe.highlights.map((item, index) => {
|
|
362
470
|
const range = this.normalizeRange({ start: item.start, end: item.end });
|
|
471
|
+
const createdAt = toTimestamp(item.createdAt, now);
|
|
472
|
+
const updatedAt = toTimestamp(item.updatedAt, createdAt);
|
|
363
473
|
return {
|
|
364
474
|
id: item.id || `highlight-preview-${index}`,
|
|
365
475
|
bookId,
|
|
@@ -370,12 +480,14 @@ export class PlayerEngine {
|
|
|
370
480
|
? { selectedText: item.selectedText }
|
|
371
481
|
: {}),
|
|
372
482
|
...(item.color !== undefined ? { color: item.color } : {}),
|
|
373
|
-
createdAt
|
|
374
|
-
updatedAt
|
|
483
|
+
createdAt,
|
|
484
|
+
updatedAt,
|
|
375
485
|
};
|
|
376
486
|
});
|
|
377
487
|
const notes = safe.notes.map((item, index) => {
|
|
378
488
|
const range = this.normalizeRange({ start: item.start, end: item.end });
|
|
489
|
+
const createdAt = toTimestamp(item.createdAt, now);
|
|
490
|
+
const updatedAt = toTimestamp(item.updatedAt, createdAt);
|
|
379
491
|
return {
|
|
380
492
|
id: item.id || `note-preview-${index}`,
|
|
381
493
|
bookId,
|
|
@@ -384,13 +496,14 @@ export class PlayerEngine {
|
|
|
384
496
|
range,
|
|
385
497
|
content: item.text,
|
|
386
498
|
...(item.color !== undefined ? { color: item.color } : {}),
|
|
387
|
-
createdAt
|
|
388
|
-
updatedAt
|
|
499
|
+
createdAt,
|
|
500
|
+
updatedAt,
|
|
389
501
|
};
|
|
390
502
|
});
|
|
391
503
|
return {
|
|
392
504
|
highlights,
|
|
393
505
|
notes,
|
|
506
|
+
bookmarks: [],
|
|
394
507
|
};
|
|
395
508
|
}
|
|
396
509
|
async persistAnnotations() {
|
|
@@ -405,6 +518,9 @@ export class PlayerEngine {
|
|
|
405
518
|
if (this.provider.saveNotes) {
|
|
406
519
|
await this.provider.saveNotes(this.doc.id, all.notes);
|
|
407
520
|
}
|
|
521
|
+
if (this.provider.saveBookmarks) {
|
|
522
|
+
await this.provider.saveBookmarks(this.doc.id, all.bookmarks || []);
|
|
523
|
+
}
|
|
408
524
|
}
|
|
409
525
|
emitAnnotationCreate(kind, annotation, source) {
|
|
410
526
|
const all = this.getAnnotations();
|
|
@@ -479,6 +595,26 @@ function toError(error) {
|
|
|
479
595
|
}
|
|
480
596
|
return new Error(String(error));
|
|
481
597
|
}
|
|
598
|
+
function toTimestamp(value, fallback) {
|
|
599
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
600
|
+
return Math.floor(value);
|
|
601
|
+
}
|
|
602
|
+
if (typeof value === "string") {
|
|
603
|
+
const trimmed = value.trim();
|
|
604
|
+
if (!trimmed) {
|
|
605
|
+
return fallback;
|
|
606
|
+
}
|
|
607
|
+
const numeric = Number(trimmed);
|
|
608
|
+
if (Number.isFinite(numeric)) {
|
|
609
|
+
return Math.floor(numeric);
|
|
610
|
+
}
|
|
611
|
+
const parsed = Date.parse(trimmed);
|
|
612
|
+
if (Number.isFinite(parsed)) {
|
|
613
|
+
return parsed;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return fallback;
|
|
617
|
+
}
|
|
482
618
|
function clone(value) {
|
|
483
619
|
return JSON.parse(JSON.stringify(value));
|
|
484
620
|
}
|
|
@@ -7,7 +7,7 @@ export interface ReaderProgress {
|
|
|
7
7
|
bookId: string;
|
|
8
8
|
lastPage: number;
|
|
9
9
|
zoom: number;
|
|
10
|
-
updatedAt:
|
|
10
|
+
updatedAt: number;
|
|
11
11
|
}
|
|
12
12
|
export interface ReaderNote {
|
|
13
13
|
id: string;
|
|
@@ -17,8 +17,8 @@ export interface ReaderNote {
|
|
|
17
17
|
range?: ReaderRange;
|
|
18
18
|
content: string;
|
|
19
19
|
color?: string;
|
|
20
|
-
createdAt:
|
|
21
|
-
updatedAt:
|
|
20
|
+
createdAt: number;
|
|
21
|
+
updatedAt: number;
|
|
22
22
|
}
|
|
23
23
|
export interface ReaderHighlight {
|
|
24
24
|
id: string;
|
|
@@ -28,40 +28,49 @@ export interface ReaderHighlight {
|
|
|
28
28
|
range: ReaderRange;
|
|
29
29
|
selectedText?: string;
|
|
30
30
|
color?: string;
|
|
31
|
-
createdAt:
|
|
32
|
-
updatedAt:
|
|
31
|
+
createdAt: number;
|
|
32
|
+
updatedAt: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ReaderBookmark {
|
|
35
|
+
id: string;
|
|
36
|
+
bookId: string;
|
|
37
|
+
pageId: string;
|
|
38
|
+
blockId: string;
|
|
39
|
+
content: string;
|
|
40
|
+
createdAt: number;
|
|
41
|
+
updatedAt: number;
|
|
33
42
|
}
|
|
34
43
|
export interface ReaderAnnotations {
|
|
35
44
|
highlights: ReaderHighlight[];
|
|
36
45
|
notes: ReaderNote[];
|
|
46
|
+
bookmarks?: ReaderBookmark[];
|
|
37
47
|
}
|
|
38
|
-
export type ReaderAnnotationKind = "highlight" | "note";
|
|
48
|
+
export type ReaderAnnotationKind = "highlight" | "note" | "bookmark";
|
|
39
49
|
export type ReaderAnnotationAction = "create" | "update" | "delete";
|
|
40
|
-
export type
|
|
41
|
-
export type ReaderAnnotation = ReaderHighlight | ReaderNote;
|
|
50
|
+
export type ReaderAnnotation = ReaderHighlight | ReaderNote | ReaderBookmark;
|
|
42
51
|
export interface ReaderAnnotationCreateEvent {
|
|
43
52
|
kind: ReaderAnnotationKind;
|
|
44
|
-
source:
|
|
53
|
+
source: "api" | "user";
|
|
45
54
|
annotation: ReaderAnnotation;
|
|
46
55
|
all: ReaderAnnotations;
|
|
47
56
|
}
|
|
48
57
|
export interface ReaderAnnotationUpdateEvent {
|
|
49
58
|
kind: ReaderAnnotationKind;
|
|
50
|
-
source:
|
|
59
|
+
source: "api" | "user";
|
|
51
60
|
previous: ReaderAnnotation;
|
|
52
61
|
current: ReaderAnnotation;
|
|
53
62
|
all: ReaderAnnotations;
|
|
54
63
|
}
|
|
55
64
|
export interface ReaderAnnotationDeleteEvent {
|
|
56
65
|
kind: ReaderAnnotationKind;
|
|
57
|
-
source:
|
|
66
|
+
source: "api" | "user";
|
|
58
67
|
annotation: ReaderAnnotation;
|
|
59
68
|
all: ReaderAnnotations;
|
|
60
69
|
}
|
|
61
70
|
export interface ReaderAnnotationsChangeEvent {
|
|
62
71
|
action: ReaderAnnotationAction;
|
|
63
72
|
kind: ReaderAnnotationKind;
|
|
64
|
-
source:
|
|
73
|
+
source: "api" | "user";
|
|
65
74
|
all: ReaderAnnotations;
|
|
66
75
|
current?: ReaderAnnotation;
|
|
67
76
|
previous?: ReaderAnnotation;
|
|
@@ -79,6 +88,10 @@ export interface EBookPlayerSelectionToolbarOptions {
|
|
|
79
88
|
highlightColor?: string;
|
|
80
89
|
noteColor?: string;
|
|
81
90
|
}
|
|
91
|
+
export interface EBookPlayerInputLimitsOptions {
|
|
92
|
+
noteMaxLength?: number;
|
|
93
|
+
bookmarkMaxLength?: number;
|
|
94
|
+
}
|
|
82
95
|
export interface PlayerDataProvider {
|
|
83
96
|
getData: () => Promise<EbookDoc>;
|
|
84
97
|
loadProgress?: (bookId: string) => Promise<ReaderProgress | null>;
|
|
@@ -87,30 +100,37 @@ export interface PlayerDataProvider {
|
|
|
87
100
|
saveAnnotations?: (bookId: string, annotations: ReaderAnnotations) => Promise<void>;
|
|
88
101
|
loadNotes?: (bookId: string) => Promise<ReaderNote[]>;
|
|
89
102
|
saveNotes?: (bookId: string, notes: ReaderNote[]) => Promise<void>;
|
|
103
|
+
loadBookmarks?: (bookId: string) => Promise<ReaderBookmark[]>;
|
|
104
|
+
saveBookmarks?: (bookId: string, bookmarks: ReaderBookmark[]) => Promise<void>;
|
|
90
105
|
onError?: (error: Error) => void;
|
|
91
106
|
}
|
|
92
107
|
export interface EBookPlayerOptions {
|
|
93
|
-
provider
|
|
108
|
+
provider?: PlayerDataProvider;
|
|
94
109
|
engineOptions?: PlayerEngineOptions;
|
|
95
110
|
width?: number;
|
|
96
111
|
initialAnnotations?: ReaderAnnotations;
|
|
97
112
|
selectionToolbar?: EBookPlayerSelectionToolbarOptions;
|
|
113
|
+
inputLimits?: EBookPlayerInputLimitsOptions;
|
|
98
114
|
}
|
|
99
115
|
export interface EBookPlayerHandle {
|
|
100
|
-
load: () => Promise<EbookDoc>;
|
|
116
|
+
load: (doc?: EbookDoc) => Promise<EbookDoc>;
|
|
101
117
|
destroy: () => void;
|
|
102
118
|
getDocument: () => EbookDoc;
|
|
103
119
|
getAnnotations: () => ReaderAnnotations;
|
|
104
120
|
getHighlights: () => ReaderHighlight[];
|
|
105
121
|
getNotes: () => ReaderNote[];
|
|
106
|
-
|
|
122
|
+
getBookmarks: () => ReaderBookmark[];
|
|
123
|
+
setAnnotations: (annotations: ReaderAnnotations, source?: "api" | "user") => Promise<ReaderAnnotations>;
|
|
107
124
|
reloadAnnotations: () => Promise<ReaderAnnotations>;
|
|
108
|
-
createHighlight: (highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
109
|
-
updateHighlight: (highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?:
|
|
110
|
-
deleteHighlight: (highlightId: string, source?:
|
|
111
|
-
createNote: (note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?:
|
|
112
|
-
updateNote: (noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?:
|
|
113
|
-
deleteNote: (noteId: string, source?:
|
|
125
|
+
createHighlight: (highlight: Omit<ReaderHighlight, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user") => Promise<ReaderHighlight>;
|
|
126
|
+
updateHighlight: (highlightId: string, patch: Partial<Omit<ReaderHighlight, "id" | "bookId" | "createdAt">>, source?: "api" | "user") => Promise<ReaderHighlight | null>;
|
|
127
|
+
deleteHighlight: (highlightId: string, source?: "api" | "user") => Promise<boolean>;
|
|
128
|
+
createNote: (note: Omit<ReaderNote, "id" | "createdAt" | "updatedAt" | "bookId">, source?: "api" | "user") => Promise<ReaderNote>;
|
|
129
|
+
updateNote: (noteId: string, patch: Partial<Omit<ReaderNote, "id" | "bookId" | "createdAt">>, source?: "api" | "user") => Promise<ReaderNote | null>;
|
|
130
|
+
deleteNote: (noteId: string, source?: "api" | "user") => Promise<boolean>;
|
|
131
|
+
setBookmark: (pageId: string, blockId: string, source?: "api" | "user") => Promise<ReaderBookmark | null>;
|
|
132
|
+
deleteBookmark: (bookmarkId: string, source?: "api" | "user") => Promise<boolean>;
|
|
133
|
+
goToBookmark: (bookmarkId: string) => number | null;
|
|
114
134
|
onAnnotationCreate: (listener: (event: ReaderAnnotationCreateEvent) => void) => () => void;
|
|
115
135
|
onAnnotationUpdate: (listener: (event: ReaderAnnotationUpdateEvent) => void) => () => void;
|
|
116
136
|
onAnnotationDelete: (listener: (event: ReaderAnnotationDeleteEvent) => void) => () => 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,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,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,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,KAAK,EAAE,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../../../../core/src/types/player.ts"],"names":[],"mappings":"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"}
|
|
@@ -5817,6 +5817,9 @@ function buildDocxStyleContext(stylesXml, themeXml) {
|
|
|
5817
5817
|
const pPr = childXmlNodeByLocalName(styleNode, "pPr");
|
|
5818
5818
|
const rPr = childXmlNodeByLocalName(styleNode, "rPr");
|
|
5819
5819
|
const parsedAlign = styleType === "paragraph" ? parseDocxParagraphAlign(pPr) : undefined;
|
|
5820
|
+
const parsedIndent = styleType === "paragraph"
|
|
5821
|
+
? parseDocxParagraphIndent(pPr, defaultTextStyle.fontSize)
|
|
5822
|
+
: undefined;
|
|
5820
5823
|
const parsedParagraphType = styleType === "paragraph"
|
|
5821
5824
|
? resolveParagraphTypeFromDocxStyle(styleId, styleName)
|
|
5822
5825
|
: undefined;
|
|
@@ -5831,15 +5834,18 @@ function buildDocxStyleContext(stylesXml, themeXml) {
|
|
|
5831
5834
|
type: styleType,
|
|
5832
5835
|
...(basedOn ? { basedOn } : {}),
|
|
5833
5836
|
...(parsedAlign ? { align: parsedAlign } : {}),
|
|
5837
|
+
...(parsedIndent !== undefined ? { indent: parsedIndent } : {}),
|
|
5834
5838
|
...(parsedParagraphType ? { paragraphType: parsedParagraphType } : {}),
|
|
5835
5839
|
marks: tableRunMarks,
|
|
5836
5840
|
};
|
|
5837
5841
|
}
|
|
5838
5842
|
const defaultParagraphAlign = parseDocxParagraphAlign(defaultsParagraphProps);
|
|
5843
|
+
const defaultParagraphIndent = parseDocxParagraphIndent(defaultsParagraphProps, defaultTextStyle.fontSize);
|
|
5839
5844
|
return {
|
|
5840
5845
|
defaultTextStyle,
|
|
5841
5846
|
defaultMarks,
|
|
5842
5847
|
...(defaultParagraphAlign ? { defaultParagraphAlign } : {}),
|
|
5848
|
+
...(defaultParagraphIndent !== undefined ? { defaultParagraphIndent } : {}),
|
|
5843
5849
|
themeColorMap,
|
|
5844
5850
|
styles,
|
|
5845
5851
|
};
|
|
@@ -5849,10 +5855,14 @@ function parseDocxParagraphElement(paragraphNode, index, styleContext, extraInhe
|
|
|
5849
5855
|
const paragraphStyleId = xmlAttribute(childXmlNodeByLocalName(pPr, "pStyle"), "val");
|
|
5850
5856
|
const paragraphStyle = resolveDocxStyle(styleContext, paragraphStyleId, "paragraph");
|
|
5851
5857
|
const directAlign = parseDocxParagraphAlign(pPr);
|
|
5858
|
+
const directIndent = parseDocxParagraphIndent(pPr, styleContext.defaultTextStyle.fontSize);
|
|
5852
5859
|
const paragraphAlign = directAlign ||
|
|
5853
5860
|
paragraphStyle?.align ||
|
|
5854
5861
|
styleContext.defaultParagraphAlign ||
|
|
5855
5862
|
"left";
|
|
5863
|
+
const paragraphIndent = directIndent ??
|
|
5864
|
+
paragraphStyle?.indent ??
|
|
5865
|
+
styleContext.defaultParagraphIndent;
|
|
5856
5866
|
const paragraphTypeByStyle = paragraphStyle?.paragraphType || "p";
|
|
5857
5867
|
const paragraphBaseMarks = mergeTextMarks(styleContext.defaultMarks, extraInheritedMarks, paragraphStyle?.marks, parseDocxRunMarks(childXmlNodeByLocalName(pPr, "rPr"), styleContext.themeColorMap));
|
|
5858
5868
|
// Keep imported size fidelity first: if style chain already carries explicit
|
|
@@ -5863,6 +5873,7 @@ function parseDocxParagraphElement(paragraphNode, index, styleContext, extraInhe
|
|
|
5863
5873
|
id: `p-${Date.now()}-${index}`,
|
|
5864
5874
|
type: paragraphType,
|
|
5865
5875
|
align: paragraphAlign,
|
|
5876
|
+
...(paragraphIndent !== undefined ? { indent: paragraphIndent } : {}),
|
|
5866
5877
|
runs: applyImportedDefaultTextStyle(runs, styleContext.defaultTextStyle),
|
|
5867
5878
|
};
|
|
5868
5879
|
}
|
|
@@ -5993,6 +6004,59 @@ function parseDocxParagraphAlign(paragraphProps) {
|
|
|
5993
6004
|
}
|
|
5994
6005
|
return undefined;
|
|
5995
6006
|
}
|
|
6007
|
+
function parseDocxParagraphIndent(paragraphProps, baseFontSizePx) {
|
|
6008
|
+
if (!paragraphProps) {
|
|
6009
|
+
return undefined;
|
|
6010
|
+
}
|
|
6011
|
+
const indentNode = childXmlNodeByLocalName(paragraphProps, "ind");
|
|
6012
|
+
if (!indentNode) {
|
|
6013
|
+
return undefined;
|
|
6014
|
+
}
|
|
6015
|
+
const firstLineChars = parseDocxHundredthCharIndent(xmlAttribute(indentNode, "firstLineChars"));
|
|
6016
|
+
if (firstLineChars !== undefined) {
|
|
6017
|
+
return firstLineChars;
|
|
6018
|
+
}
|
|
6019
|
+
const hangingChars = parseDocxHundredthCharIndent(xmlAttribute(indentNode, "hangingChars"));
|
|
6020
|
+
if (hangingChars !== undefined) {
|
|
6021
|
+
return -hangingChars;
|
|
6022
|
+
}
|
|
6023
|
+
const resolvedBaseFontSizePx = typeof baseFontSizePx === "number" &&
|
|
6024
|
+
Number.isFinite(baseFontSizePx) &&
|
|
6025
|
+
baseFontSizePx > 0
|
|
6026
|
+
? baseFontSizePx
|
|
6027
|
+
: 16;
|
|
6028
|
+
const firstLineTwip = parseDocxTwip(xmlAttribute(indentNode, "firstLine"));
|
|
6029
|
+
if (firstLineTwip !== undefined) {
|
|
6030
|
+
return convertDocxTwipToEm(firstLineTwip, resolvedBaseFontSizePx);
|
|
6031
|
+
}
|
|
6032
|
+
const hangingTwip = parseDocxTwip(xmlAttribute(indentNode, "hanging"));
|
|
6033
|
+
if (hangingTwip !== undefined) {
|
|
6034
|
+
return -convertDocxTwipToEm(hangingTwip, resolvedBaseFontSizePx);
|
|
6035
|
+
}
|
|
6036
|
+
return undefined;
|
|
6037
|
+
}
|
|
6038
|
+
function parseDocxHundredthCharIndent(raw) {
|
|
6039
|
+
if (!raw) {
|
|
6040
|
+
return undefined;
|
|
6041
|
+
}
|
|
6042
|
+
const value = Number.parseFloat(raw);
|
|
6043
|
+
if (!Number.isFinite(value)) {
|
|
6044
|
+
return undefined;
|
|
6045
|
+
}
|
|
6046
|
+
return Number.parseFloat((value / 100).toFixed(2));
|
|
6047
|
+
}
|
|
6048
|
+
function parseDocxTwip(raw) {
|
|
6049
|
+
if (!raw) {
|
|
6050
|
+
return undefined;
|
|
6051
|
+
}
|
|
6052
|
+
const value = Number.parseFloat(raw);
|
|
6053
|
+
return Number.isFinite(value) ? value : undefined;
|
|
6054
|
+
}
|
|
6055
|
+
function convertDocxTwipToEm(twip, baseFontSizePx) {
|
|
6056
|
+
const px = (twip * 96) / (72 * 20);
|
|
6057
|
+
const em = px / baseFontSizePx;
|
|
6058
|
+
return Number.parseFloat(em.toFixed(2));
|
|
6059
|
+
}
|
|
5996
6060
|
function resolveParagraphTypeFromDocxStyle(styleId, styleName) {
|
|
5997
6061
|
const normalized = `${styleId} ${styleName}`.toLowerCase();
|
|
5998
6062
|
if (normalized.includes("heading 1") ||
|
|
@@ -6061,10 +6125,12 @@ function resolveDocxStyle(styleContext, styleId, type, visited = new Set()) {
|
|
|
6061
6125
|
? parent.paragraphType
|
|
6062
6126
|
: entry.paragraphType || parent.paragraphType;
|
|
6063
6127
|
const align = entry.align || parent.align;
|
|
6128
|
+
const indent = entry.indent !== undefined ? entry.indent : parent.indent;
|
|
6064
6129
|
return {
|
|
6065
6130
|
...entry,
|
|
6066
6131
|
marks: mergeTextMarks(parent.marks, entry.marks),
|
|
6067
6132
|
...(align ? { align } : {}),
|
|
6133
|
+
...(indent !== undefined ? { indent } : {}),
|
|
6068
6134
|
...(paragraphType ? { paragraphType } : {}),
|
|
6069
6135
|
};
|
|
6070
6136
|
}
|
|
@@ -6316,6 +6382,9 @@ function resolveDocxTableCellStyle(cellProps, themeColorMap, rowHeight, tableBor
|
|
|
6316
6382
|
resolvedCellBorders[side] = borderStyle;
|
|
6317
6383
|
}
|
|
6318
6384
|
}
|
|
6385
|
+
if (!Object.keys(resolvedCellBorders).length) {
|
|
6386
|
+
styleTokens.push("border:1px solid #000000");
|
|
6387
|
+
}
|
|
6319
6388
|
const shadingNode = childXmlNodeByLocalName(cellProps, "shd");
|
|
6320
6389
|
const fillColor = normalizeImportedCssColor(xmlAttribute(shadingNode, "fill") || "") ||
|
|
6321
6390
|
themeColorMap[xmlAttribute(shadingNode, "themeFill") || ""];
|
|
@@ -6403,7 +6472,7 @@ function resolveDocxBorderStyle(borderNode, themeColorMap) {
|
|
|
6403
6472
|
const themeColor = xmlAttribute(borderNode, "themeColor") || "";
|
|
6404
6473
|
const color = (themeColor && themeColorMap[themeColor]) ||
|
|
6405
6474
|
normalizeImportedCssColor(colorRaw) ||
|
|
6406
|
-
"#
|
|
6475
|
+
"#000000";
|
|
6407
6476
|
return `${widthPx}px ${style} ${color}`;
|
|
6408
6477
|
}
|
|
6409
6478
|
function resolveDocxTableWidthPx(tableProps) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyebook/vue3-adapter",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
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": "^2.3.
|
|
20
|
+
"@hyebook/core": "^2.3.2"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc -p tsconfig.json",
|