@hyebook/vue3-adapter 0.2.1 → 0.2.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.
|
@@ -9,12 +9,22 @@ export interface EditorWorkbenchFeatures {
|
|
|
9
9
|
export interface EditorWorkbenchOptions {
|
|
10
10
|
initialDoc?: EbookDoc;
|
|
11
11
|
features?: Partial<EditorWorkbenchFeatures>;
|
|
12
|
+
previewAnnotations?: EditorWorkbenchPreviewAnnotationOptions;
|
|
12
13
|
title?: string;
|
|
13
14
|
pageWidth?: number;
|
|
14
15
|
upload?: EditorWorkbenchUploadOptions;
|
|
15
16
|
importDocx?: EditorWorkbenchDocxImportOptions;
|
|
16
17
|
onDocumentChange?: (doc: EbookDoc) => void;
|
|
17
18
|
}
|
|
19
|
+
export interface EditorWorkbenchPreviewAnnotationActions {
|
|
20
|
+
canDelete?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface EditorWorkbenchPreviewAnnotationOptions {
|
|
23
|
+
showAnnotationList?: boolean;
|
|
24
|
+
enablePreviewSelectionToolbar?: boolean;
|
|
25
|
+
annotationListDefaultTab?: "highlights" | "notes";
|
|
26
|
+
annotationActions?: EditorWorkbenchPreviewAnnotationActions;
|
|
27
|
+
}
|
|
18
28
|
export interface EditorWorkbenchDocxImportOptions {
|
|
19
29
|
maxSizeBytes?: number;
|
|
20
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-workbench.d.ts","sourceRoot":"","sources":["../../../../../core/src/workbench/editor-workbench.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,EAKR,kBAAkB,EAWnB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"editor-workbench.d.ts","sourceRoot":"","sources":["../../../../../core/src/workbench/editor-workbench.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,9 @@ const WORKBENCH_CSS = `
|
|
|
23
23
|
.hyewb-field-icon svg{width:14px;height:14px;stroke-width:2}
|
|
24
24
|
.hyewb-field-with-icon .hyewb-select,.hyewb-field-with-icon .hyewb-number{padding-left:30px}
|
|
25
25
|
.hyewb-shell{border:1px dashed #b8c3d1;border-radius:10px;background:#eef4ff;padding:10px;overflow-x:auto;overflow-y:auto;overscroll-behavior:auto;-webkit-overflow-scrolling:touch}
|
|
26
|
-
.hyewb-
|
|
26
|
+
.hyewb-preview-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start}
|
|
27
|
+
.hyewb-preview-layout.with-annotation-panel{grid-template-columns:minmax(0,1fr) 300px}
|
|
28
|
+
.hyewb-canvas{position:relative;background:#fff;border:1px solid #d9e3ee;border-radius:8px;min-height:420px;padding:20px 24px;box-shadow:0 6px 16px rgba(15,23,42,.08);margin:0 auto;max-width:100%;box-sizing:border-box}
|
|
27
29
|
.hyewb-editor{min-height:360px;outline:none;line-height:1.7;font-size:16px;color:#1e293b}
|
|
28
30
|
.hyewb-float{position:fixed;z-index:9999;display:none;align-items:center;gap:6px;padding:6px 8px;border-radius:10px;border:1px solid #0f172a;background:rgba(15,23,42,.96);box-shadow:0 10px 24px rgba(15,23,42,.22)}
|
|
29
31
|
.hyewb-float.show{display:inline-flex}
|
|
@@ -139,6 +141,29 @@ const WORKBENCH_CSS = `
|
|
|
139
141
|
.hyewb-upload-progress{height:8px;background:#e2e8f0;border-radius:999px;overflow:hidden}
|
|
140
142
|
.hyewb-upload-progress > span{display:block;height:100%;width:0;background:#0b7285;transition:width .18s ease}
|
|
141
143
|
.hyewb-upload-error{min-height:18px;font-size:12px;color:#b91c1c;margin:0}
|
|
144
|
+
.hyewb-preview-highlight-mark{background:var(--hyewb-highlight-color,#fff59d);color:inherit;padding:0 .08em;border-radius:.2em}
|
|
145
|
+
.hyewb-preview-note-mark{background:rgba(14,116,144,.14);border-bottom:2px solid #0e7490;color:inherit;padding:0 .04em;border-radius:.2em}
|
|
146
|
+
.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}
|
|
147
|
+
.hyewb-preview-annotation-title{margin:0;font-size:14px}
|
|
148
|
+
.hyewb-preview-annotation-tabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
|
|
149
|
+
.hyewb-preview-annotation-tab{width:auto;min-width:0;height:32px;padding:0 12px;font-size:13px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;color:#0f172a;cursor:pointer}
|
|
150
|
+
.hyewb-preview-annotation-tab.active{color:#ffffff;background:#0b7285;border-color:#0b7285}
|
|
151
|
+
.hyewb-preview-annotation-list{margin:0;padding:0;list-style:none;display:grid;gap:8px}
|
|
152
|
+
.hyewb-preview-annotation-item{width:100%;text-align:left;display:grid;gap:4px;padding:8px 36px 8px 8px;border-radius:8px;border:1px solid #dce5ef;background:#f8fafc;position:relative;cursor:pointer}
|
|
153
|
+
.hyewb-preview-annotation-item:hover{background:#eef6ff;border-color:#b9d7f4}
|
|
154
|
+
.hyewb-preview-annotation-item-title{font-size:12px;color:#0f766e}
|
|
155
|
+
.hyewb-preview-annotation-item-body{font-size:13px;color:#1e293b;line-height:1.5}
|
|
156
|
+
.hyewb-preview-annotation-delete{position:absolute;right:8px;top:8px;border-radius:8px;border:1px solid #f1b3b3;background:#fff5f5;color:#b42318;width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .15s ease;cursor:pointer;padding:0}
|
|
157
|
+
.hyewb-preview-annotation-item:hover .hyewb-preview-annotation-delete{opacity:1;pointer-events:auto}
|
|
158
|
+
.hyewb-preview-annotation-delete svg{width:14px;height:14px}
|
|
159
|
+
.hyewb-preview-annotation-empty{margin:0;color:#64748b;font-size:13px}
|
|
160
|
+
.hyewb-preview-selection-toolbar{position:fixed;z-index:10060;display:none;align-items:center;gap:8px;padding:6px 8px;border-radius:10px;border:1px solid #0f172a;background:rgba(15,23,42,.96);box-shadow:0 10px 24px rgba(15,23,42,.22);transform:translate(-50%,-100%)}
|
|
161
|
+
.hyewb-preview-selection-toolbar.show{display:inline-flex}
|
|
162
|
+
.hyewb-preview-selection-btn{width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;border:1px solid #334155;background:#1e293b;color:#f8fafc;border-radius:8px;cursor:pointer}
|
|
163
|
+
.hyewb-preview-selection-btn:hover{background:#334155}
|
|
164
|
+
.hyewb-preview-selection-btn svg{width:16px;height:16px}
|
|
165
|
+
.hyewb-preview-annotation-flash{animation:hyewb-preview-annotation-pulse .9s ease}
|
|
166
|
+
@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)}}
|
|
142
167
|
.hyewb-root:fullscreen .hyewb-editor,.hyewb-root:fullscreen .hyewb-preview{min-height:1000px}
|
|
143
168
|
.hyewb-root:-webkit-full-screen .hyewb-editor,.hyewb-root:-webkit-full-screen .hyewb-preview{min-height:1000px}
|
|
144
169
|
`;
|
|
@@ -161,11 +186,13 @@ const WORKBENCH_ICON_PATHS = {
|
|
|
161
186
|
"chevron-right": '<polyline points="9 18 15 12 9 6"/>',
|
|
162
187
|
"fullscreen-enter": '<polyline points="9 3 3 3 3 9"/><line x1="3" y1="3" x2="10" y2="10"/><polyline points="15 3 21 3 21 9"/><line x1="14" y1="10" x2="21" y2="3"/><polyline points="3 15 3 21 9 21"/><line x1="3" y1="21" x2="10" y2="14"/><polyline points="15 21 21 21 21 15"/><line x1="14" y1="14" x2="21" y2="21"/>',
|
|
163
188
|
"fullscreen-exit": '<polyline points="10 14 10 21 3 21"/><line x1="10" y1="14" x2="3" y2="21"/><polyline points="14 10 21 10 21 3"/><line x1="14" y1="10" x2="21" y2="3"/><polyline points="3 9 3 3 9 3"/><line x1="3" y1="3" x2="10" y2="10"/><polyline points="21 15 21 21 15 21"/><line x1="21" y1="21" x2="14" y2="14"/>',
|
|
189
|
+
highlighter: '<path d="M15 2l7 7-8.5 8.5-7-7z"/><path d="M12.5 11.5L2 22l6-1.5 6-6"/><line x1="13" y1="4" x2="20" y2="11"/>',
|
|
164
190
|
"file-up": '<path d="M14 2H8a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9.5 14.5 12 12 14.5 14.5"/>',
|
|
165
191
|
image: '<rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="1.5"/><path d="M6 17l4-4 3 3 3-2 2 3"/>',
|
|
166
192
|
italic: '<line x1="10" y1="4" x2="16" y2="4"/><line x1="8" y1="20" x2="14" y2="20"/><line x1="14" y1="4" x2="10" y2="20"/>',
|
|
167
193
|
"indent-increase": '<line x1="4" y1="6" x2="14" y2="6"/><line x1="4" y1="10" x2="10" y2="10"/><line x1="4" y1="14" x2="14" y2="14"/><line x1="4" y1="18" x2="10" y2="18"/><polyline points="15 9 19 12 15 15"/>',
|
|
168
194
|
move: '<polyline points="8 4 12 1 16 4"/><line x1="12" y1="1" x2="12" y2="23"/><polyline points="8 20 12 23 16 20"/><polyline points="4 8 1 12 4 16"/><line x1="1" y1="12" x2="23" y2="12"/><polyline points="20 8 23 12 20 16"/>',
|
|
195
|
+
"notebook-pen": '<path d="M4 5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"/><line x1="8" y1="7" x2="14" y2="7"/><line x1="8" y1="11" x2="14" y2="11"/><path d="M14.5 16.5l4-4 2 2-4 4-2.5.5z"/>',
|
|
169
196
|
"paint-bucket": '<path d="M7 10l6-6 7 7-6 6z"/><path d="M12 5l7 7"/><path d="M5 19a2 2 0 1 0 4 0c0-1-1-2-2-3-1 1-2 2-2 3z"/>',
|
|
170
197
|
palette: '<path d="M12 3a9 9 0 1 0 0 18h1.5a2.5 2.5 0 0 0 0-5h-1a2 2 0 0 1 0-4H16a5 5 0 0 0-4-9z"/><circle cx="7.5" cy="10" r="1"/><circle cx="10" cy="7.5" r="1"/><circle cx="14" cy="7.5" r="1"/>',
|
|
171
198
|
pilcrow: '<path d="M9 4h7v16"/><path d="M12 4v16"/><path d="M9 4a4 4 0 0 0 0 8h3"/>',
|
|
@@ -175,6 +202,7 @@ const WORKBENCH_ICON_PATHS = {
|
|
|
175
202
|
subscript: '<line x1="4" y1="8" x2="10" y2="16"/><line x1="10" y1="8" x2="4" y2="16"/><path d="M14 15c0-1.1.9-2 2-2s2 .9 2 2c0 .7-.3 1.2-.9 1.7L14 20h4"/>',
|
|
176
203
|
superscript: '<line x1="4" y1="8" x2="10" y2="16"/><line x1="10" y1="8" x2="4" y2="16"/><path d="M14 6c0-1.1.9-2 2-2s2 .9 2 2c0 .7-.3 1.2-.9 1.7L14 11h4"/>',
|
|
177
204
|
table: '<rect x="3" y="5" width="18" height="14" rx="1"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="9" y1="5" x2="9" y2="19"/><line x1="15" y1="5" x2="15" y2="19"/>',
|
|
205
|
+
"trash-2": '<polyline points="3 6 5 6 21 6"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/>',
|
|
178
206
|
underline: '<path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/>',
|
|
179
207
|
video: '<rect x="3" y="6" width="14" height="12" rx="2"/><polygon points="10 10 10 14 13 12"/><path d="M17 10l4-2v8l-4-2z"/>',
|
|
180
208
|
x: '<line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/>',
|
|
@@ -274,6 +302,16 @@ const RESIZE_HANDLES = [
|
|
|
274
302
|
"w",
|
|
275
303
|
];
|
|
276
304
|
const DEFAULT_FLOW_BLOCK_WIDTH = 620;
|
|
305
|
+
function resolvePreviewAnnotationOptions(options) {
|
|
306
|
+
return {
|
|
307
|
+
showAnnotationList: options?.showAnnotationList ?? true,
|
|
308
|
+
enablePreviewSelectionToolbar: options?.enablePreviewSelectionToolbar ?? true,
|
|
309
|
+
annotationListDefaultTab: options?.annotationListDefaultTab === "notes" ? "notes" : "highlights",
|
|
310
|
+
annotationActions: {
|
|
311
|
+
canDelete: options?.annotationActions?.canDelete ?? true,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
277
315
|
export function createDefaultWorkbenchDocument() {
|
|
278
316
|
const now = new Date().toISOString();
|
|
279
317
|
return {
|
|
@@ -394,6 +432,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
394
432
|
doc: editor.getDocument(),
|
|
395
433
|
features: resolveFeatures(options.features),
|
|
396
434
|
};
|
|
435
|
+
const previewAnnotationOptions = resolvePreviewAnnotationOptions(options.previewAnnotations);
|
|
397
436
|
const paperWidth = resolvePaperWidth(options.pageWidth, state.doc.settings.page.width);
|
|
398
437
|
const root = document.createElement("section");
|
|
399
438
|
root.className = "hyewb-root";
|
|
@@ -674,6 +713,8 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
674
713
|
pageInfo.className = "hyewb-status";
|
|
675
714
|
fullscreenBtn.style.marginLeft = "auto";
|
|
676
715
|
pageRow.append(prevPageBtn, nextPageBtn, addPageBtn, pageInfo, fullscreenBtn);
|
|
716
|
+
const previewLayout = document.createElement("div");
|
|
717
|
+
previewLayout.className = "hyewb-preview-layout";
|
|
677
718
|
const shell = document.createElement("div");
|
|
678
719
|
shell.className = "hyewb-shell";
|
|
679
720
|
const canvas = document.createElement("div");
|
|
@@ -698,6 +739,43 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
698
739
|
mediaList.className = "hyewb-media-list";
|
|
699
740
|
canvas.append(editorArea, previewArea, mediaStage, inlineResizeOverlay, mediaList);
|
|
700
741
|
shell.append(canvas);
|
|
742
|
+
const annotationPanel = document.createElement("aside");
|
|
743
|
+
annotationPanel.className = "hyewb-preview-annotation-panel";
|
|
744
|
+
const annotationTitle = document.createElement("h3");
|
|
745
|
+
annotationTitle.className = "hyewb-preview-annotation-title";
|
|
746
|
+
annotationTitle.textContent = "预览标注";
|
|
747
|
+
const annotationTabs = document.createElement("div");
|
|
748
|
+
annotationTabs.className = "hyewb-preview-annotation-tabs";
|
|
749
|
+
const annotationHighlightTab = document.createElement("button");
|
|
750
|
+
annotationHighlightTab.type = "button";
|
|
751
|
+
annotationHighlightTab.className = "hyewb-preview-annotation-tab";
|
|
752
|
+
annotationHighlightTab.textContent = "高亮";
|
|
753
|
+
const annotationNoteTab = document.createElement("button");
|
|
754
|
+
annotationNoteTab.type = "button";
|
|
755
|
+
annotationNoteTab.className = "hyewb-preview-annotation-tab";
|
|
756
|
+
annotationNoteTab.textContent = "笔记";
|
|
757
|
+
annotationTabs.append(annotationHighlightTab, annotationNoteTab);
|
|
758
|
+
const annotationList = document.createElement("ul");
|
|
759
|
+
annotationList.className = "hyewb-preview-annotation-list";
|
|
760
|
+
const annotationEmpty = document.createElement("p");
|
|
761
|
+
annotationEmpty.className = "hyewb-preview-annotation-empty";
|
|
762
|
+
annotationPanel.append(annotationTitle, annotationTabs, annotationList, annotationEmpty);
|
|
763
|
+
previewLayout.append(shell, annotationPanel);
|
|
764
|
+
const previewSelectionToolbar = document.createElement("div");
|
|
765
|
+
previewSelectionToolbar.className = "hyewb-preview-selection-toolbar";
|
|
766
|
+
const previewHighlightBtn = document.createElement("button");
|
|
767
|
+
previewHighlightBtn.type = "button";
|
|
768
|
+
previewHighlightBtn.className = "hyewb-preview-selection-btn";
|
|
769
|
+
previewHighlightBtn.title = "创建高亮";
|
|
770
|
+
previewHighlightBtn.setAttribute("aria-label", "创建高亮");
|
|
771
|
+
previewHighlightBtn.append(createIconPlaceholder("highlighter"));
|
|
772
|
+
const previewNoteBtn = document.createElement("button");
|
|
773
|
+
previewNoteBtn.type = "button";
|
|
774
|
+
previewNoteBtn.className = "hyewb-preview-selection-btn";
|
|
775
|
+
previewNoteBtn.title = "创建笔记";
|
|
776
|
+
previewNoteBtn.setAttribute("aria-label", "创建笔记");
|
|
777
|
+
previewNoteBtn.append(createIconPlaceholder("notebook-pen"));
|
|
778
|
+
previewSelectionToolbar.append(previewHighlightBtn, previewNoteBtn);
|
|
701
779
|
const floatingToolbar = document.createElement("div");
|
|
702
780
|
floatingToolbar.className = "hyewb-float";
|
|
703
781
|
const floatBoldBtn = createButton("bold");
|
|
@@ -772,7 +850,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
772
850
|
const colorGrid = document.createElement("div");
|
|
773
851
|
colorGrid.className = "hyewb-color-grid";
|
|
774
852
|
colorPalette.append(colorGrid);
|
|
775
|
-
root.append(header, toolbar, pageRow,
|
|
853
|
+
root.append(header, toolbar, pageRow, previewLayout, status, colorPalette, tablePicker, tableTools, tableRowDeleteBtn, tableMoveHandle, tableScaleHandle, tableColEdgeLayer, tableRowEdgeLayer, tableRowGapInsertBtn, tableColGapInsertBtn, tableDropIndicator, tableContextMenu, uploadBackdrop, docxInput, previewSelectionToolbar);
|
|
776
854
|
container.innerHTML = "";
|
|
777
855
|
container.append(root, floatingToolbar);
|
|
778
856
|
renderWorkbenchIcons();
|
|
@@ -798,6 +876,8 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
798
876
|
let tableContextTarget = null;
|
|
799
877
|
let selectedTableRowIndex = null;
|
|
800
878
|
let selectedTableColIndex = null;
|
|
879
|
+
let annotationActiveTab = previewAnnotationOptions.annotationListDefaultTab;
|
|
880
|
+
let previewSelectionDraft = null;
|
|
801
881
|
const isTableGapInsertTarget = (node) => {
|
|
802
882
|
if (!node) {
|
|
803
883
|
return false;
|
|
@@ -1132,6 +1212,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
1132
1212
|
syncEditorToDoc();
|
|
1133
1213
|
state.mode = nextMode;
|
|
1134
1214
|
hideFloatingToolbar();
|
|
1215
|
+
hidePreviewSelectionToolbar();
|
|
1135
1216
|
render();
|
|
1136
1217
|
};
|
|
1137
1218
|
const loadPreviewAnnotations = () => {
|
|
@@ -1144,6 +1225,423 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
1144
1225
|
updateStateDoc(nextDoc);
|
|
1145
1226
|
return clone(normalized);
|
|
1146
1227
|
};
|
|
1228
|
+
const getCurrentPageId = () => {
|
|
1229
|
+
return state.doc.pages[state.pageIndex]?.id || `page-${state.pageIndex}`;
|
|
1230
|
+
};
|
|
1231
|
+
const normalizeOffsetRange = (start, end) => {
|
|
1232
|
+
const safeStart = Math.max(0, Math.floor(start || 0));
|
|
1233
|
+
const safeEnd = Math.max(0, Math.floor(end || 0));
|
|
1234
|
+
if (safeStart === safeEnd) {
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
return safeStart < safeEnd
|
|
1238
|
+
? { start: safeStart, end: safeEnd }
|
|
1239
|
+
: { start: safeEnd, end: safeStart };
|
|
1240
|
+
};
|
|
1241
|
+
const getNodeTextLength = (rootNode) => {
|
|
1242
|
+
if (!rootNode) {
|
|
1243
|
+
return 0;
|
|
1244
|
+
}
|
|
1245
|
+
const walker = document.createTreeWalker(rootNode, NodeFilter.SHOW_TEXT);
|
|
1246
|
+
let size = 0;
|
|
1247
|
+
while (walker.nextNode()) {
|
|
1248
|
+
size += walker.currentNode.textContent?.length || 0;
|
|
1249
|
+
}
|
|
1250
|
+
return size;
|
|
1251
|
+
};
|
|
1252
|
+
const getRangeTextLength = (range) => {
|
|
1253
|
+
if (!range) {
|
|
1254
|
+
return 0;
|
|
1255
|
+
}
|
|
1256
|
+
return getNodeTextLength(range.cloneContents());
|
|
1257
|
+
};
|
|
1258
|
+
const getSelectionOffsets = (containerNode, selection) => {
|
|
1259
|
+
if (!selection.rangeCount) {
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
const range = selection.getRangeAt(0);
|
|
1263
|
+
if (!containerNode.contains(range.startContainer) ||
|
|
1264
|
+
!containerNode.contains(range.endContainer)) {
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
const beforeStart = range.cloneRange();
|
|
1268
|
+
beforeStart.selectNodeContents(containerNode);
|
|
1269
|
+
beforeStart.setEnd(range.startContainer, range.startOffset);
|
|
1270
|
+
const beforeEnd = range.cloneRange();
|
|
1271
|
+
beforeEnd.selectNodeContents(containerNode);
|
|
1272
|
+
beforeEnd.setEnd(range.endContainer, range.endOffset);
|
|
1273
|
+
return normalizeOffsetRange(getRangeTextLength(beforeStart), getRangeTextLength(beforeEnd));
|
|
1274
|
+
};
|
|
1275
|
+
const createRangeFromOffsets = (containerNode, start, end) => {
|
|
1276
|
+
const normalized = normalizeOffsetRange(start, end);
|
|
1277
|
+
if (!normalized) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
const range = document.createRange();
|
|
1281
|
+
const walker = document.createTreeWalker(containerNode, NodeFilter.SHOW_TEXT);
|
|
1282
|
+
let cursor = 0;
|
|
1283
|
+
let startNode = null;
|
|
1284
|
+
let endNode = null;
|
|
1285
|
+
let startOffset = 0;
|
|
1286
|
+
let endOffset = 0;
|
|
1287
|
+
let lastTextNode = null;
|
|
1288
|
+
while (walker.nextNode()) {
|
|
1289
|
+
const node = walker.currentNode;
|
|
1290
|
+
const length = node.textContent?.length || 0;
|
|
1291
|
+
lastTextNode = node;
|
|
1292
|
+
const nextCursor = cursor + length;
|
|
1293
|
+
if (!startNode &&
|
|
1294
|
+
normalized.start >= cursor &&
|
|
1295
|
+
normalized.start < nextCursor) {
|
|
1296
|
+
startNode = node;
|
|
1297
|
+
startOffset = normalized.start - cursor;
|
|
1298
|
+
}
|
|
1299
|
+
if (!endNode && normalized.end > cursor && normalized.end <= nextCursor) {
|
|
1300
|
+
endNode = node;
|
|
1301
|
+
endOffset = normalized.end - cursor;
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
cursor = nextCursor;
|
|
1305
|
+
}
|
|
1306
|
+
if (!startNode && lastTextNode && normalized.start === cursor) {
|
|
1307
|
+
startNode = lastTextNode;
|
|
1308
|
+
startOffset = lastTextNode.textContent?.length || 0;
|
|
1309
|
+
}
|
|
1310
|
+
if (!endNode && lastTextNode && normalized.end === cursor) {
|
|
1311
|
+
endNode = lastTextNode;
|
|
1312
|
+
endOffset = lastTextNode.textContent?.length || 0;
|
|
1313
|
+
}
|
|
1314
|
+
if (!startNode || !endNode) {
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
range.setStart(startNode, startOffset);
|
|
1318
|
+
range.setEnd(endNode, endOffset);
|
|
1319
|
+
return range;
|
|
1320
|
+
};
|
|
1321
|
+
const unwrapPreviewMarks = (containerNode, kinds) => {
|
|
1322
|
+
const selector = kinds
|
|
1323
|
+
.map((kind) => `mark[data-preview-annotation='${kind}']`)
|
|
1324
|
+
.join(",");
|
|
1325
|
+
if (!selector) {
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
const marks = Array.from(containerNode.querySelectorAll(selector));
|
|
1329
|
+
marks.forEach((mark) => {
|
|
1330
|
+
const parent = mark.parentNode;
|
|
1331
|
+
if (!parent) {
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
while (mark.firstChild) {
|
|
1335
|
+
parent.insertBefore(mark.firstChild, mark);
|
|
1336
|
+
}
|
|
1337
|
+
parent.removeChild(mark);
|
|
1338
|
+
});
|
|
1339
|
+
};
|
|
1340
|
+
const applyHighlightMarks = (containerNode, highlights) => {
|
|
1341
|
+
unwrapPreviewMarks(containerNode, ["highlight"]);
|
|
1342
|
+
[...highlights]
|
|
1343
|
+
.filter((item) => Number.isFinite(item.start) && Number.isFinite(item.end))
|
|
1344
|
+
.sort((a, b) => b.start - a.start)
|
|
1345
|
+
.forEach((item) => {
|
|
1346
|
+
const range = createRangeFromOffsets(containerNode, item.start, item.end);
|
|
1347
|
+
if (!range || range.collapsed) {
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const mark = document.createElement("mark");
|
|
1351
|
+
mark.dataset.previewAnnotation = "highlight";
|
|
1352
|
+
mark.className = "hyewb-preview-highlight-mark";
|
|
1353
|
+
mark.style.setProperty("--hyewb-highlight-color", item.color || "#fff59d");
|
|
1354
|
+
try {
|
|
1355
|
+
range.surroundContents(mark);
|
|
1356
|
+
}
|
|
1357
|
+
catch {
|
|
1358
|
+
const fragment = range.extractContents();
|
|
1359
|
+
mark.append(fragment);
|
|
1360
|
+
range.insertNode(mark);
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
};
|
|
1364
|
+
const applyNoteMarks = (containerNode, notes) => {
|
|
1365
|
+
unwrapPreviewMarks(containerNode, ["note"]);
|
|
1366
|
+
[...notes]
|
|
1367
|
+
.filter((item) => Number.isFinite(item.start) && Number.isFinite(item.end))
|
|
1368
|
+
.sort((a, b) => b.start - a.start)
|
|
1369
|
+
.forEach((item) => {
|
|
1370
|
+
const range = createRangeFromOffsets(containerNode, item.start, item.end);
|
|
1371
|
+
if (!range || range.collapsed) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const mark = document.createElement("mark");
|
|
1375
|
+
mark.dataset.previewAnnotation = "note";
|
|
1376
|
+
mark.dataset.previewNoteId = item.id || "";
|
|
1377
|
+
mark.className = "hyewb-preview-note-mark";
|
|
1378
|
+
try {
|
|
1379
|
+
range.surroundContents(mark);
|
|
1380
|
+
}
|
|
1381
|
+
catch {
|
|
1382
|
+
const fragment = range.extractContents();
|
|
1383
|
+
mark.append(fragment);
|
|
1384
|
+
range.insertNode(mark);
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
};
|
|
1388
|
+
const mergeHighlightRecords = (records, draft) => {
|
|
1389
|
+
const normalized = normalizeOffsetRange(draft.start, draft.end);
|
|
1390
|
+
if (!normalized || !draft.pageId || !draft.blockId) {
|
|
1391
|
+
return records;
|
|
1392
|
+
}
|
|
1393
|
+
const targetColor = draft.color || "#fff59d";
|
|
1394
|
+
const sameBucket = [];
|
|
1395
|
+
const rest = [];
|
|
1396
|
+
records.forEach((item) => {
|
|
1397
|
+
if (item.pageId === draft.pageId &&
|
|
1398
|
+
item.blockId === draft.blockId &&
|
|
1399
|
+
(item.color || "#fff59d") === targetColor) {
|
|
1400
|
+
sameBucket.push(item);
|
|
1401
|
+
}
|
|
1402
|
+
else {
|
|
1403
|
+
rest.push(item);
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
let mergedStart = normalized.start;
|
|
1407
|
+
let mergedEnd = normalized.end;
|
|
1408
|
+
const kept = [];
|
|
1409
|
+
sameBucket.forEach((item) => {
|
|
1410
|
+
const overlap = !(item.end < mergedStart || item.start > mergedEnd);
|
|
1411
|
+
if (overlap) {
|
|
1412
|
+
mergedStart = Math.min(mergedStart, item.start);
|
|
1413
|
+
mergedEnd = Math.max(mergedEnd, item.end);
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
kept.push(item);
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
return [
|
|
1420
|
+
...rest,
|
|
1421
|
+
...kept,
|
|
1422
|
+
{
|
|
1423
|
+
...draft,
|
|
1424
|
+
start: mergedStart,
|
|
1425
|
+
end: mergedEnd,
|
|
1426
|
+
},
|
|
1427
|
+
];
|
|
1428
|
+
};
|
|
1429
|
+
const mergeNoteRecords = (records, draft) => {
|
|
1430
|
+
const index = records.findIndex((item) => {
|
|
1431
|
+
return (item.pageId === draft.pageId &&
|
|
1432
|
+
item.start === draft.start &&
|
|
1433
|
+
item.end === draft.end);
|
|
1434
|
+
});
|
|
1435
|
+
if (index === -1) {
|
|
1436
|
+
return [...records, draft];
|
|
1437
|
+
}
|
|
1438
|
+
const next = [...records];
|
|
1439
|
+
const existing = next[index];
|
|
1440
|
+
if (!existing) {
|
|
1441
|
+
return [...records, draft];
|
|
1442
|
+
}
|
|
1443
|
+
next[index] = {
|
|
1444
|
+
...existing,
|
|
1445
|
+
...draft,
|
|
1446
|
+
id: existing.id || draft.id,
|
|
1447
|
+
updatedAt: new Date().toISOString(),
|
|
1448
|
+
};
|
|
1449
|
+
return next;
|
|
1450
|
+
};
|
|
1451
|
+
const summarizePreviewText = (text, fallback, maxLength = 52) => {
|
|
1452
|
+
const raw = String(text || "")
|
|
1453
|
+
.replace(/\s+/g, " ")
|
|
1454
|
+
.trim();
|
|
1455
|
+
if (!raw) {
|
|
1456
|
+
return fallback;
|
|
1457
|
+
}
|
|
1458
|
+
return raw.length > maxLength ? `${raw.slice(0, maxLength)}...` : raw;
|
|
1459
|
+
};
|
|
1460
|
+
const flashPreviewAnnotationTarget = (node) => {
|
|
1461
|
+
if (!node || !node.classList) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
node.classList.remove("hyewb-preview-annotation-flash");
|
|
1465
|
+
void node.offsetWidth;
|
|
1466
|
+
node.classList.add("hyewb-preview-annotation-flash");
|
|
1467
|
+
window.setTimeout(() => {
|
|
1468
|
+
node.classList.remove("hyewb-preview-annotation-flash");
|
|
1469
|
+
}, 900);
|
|
1470
|
+
};
|
|
1471
|
+
const focusPreviewAnnotationRecord = (record, kind) => {
|
|
1472
|
+
let target = null;
|
|
1473
|
+
if (kind === "note" && "id" in record && record.id) {
|
|
1474
|
+
const noteId = String(record.id);
|
|
1475
|
+
target = previewArea.querySelector(`mark[data-preview-annotation='note'][data-preview-note-id='${noteId.replace(/["\\]/g, "\\$&")}']`);
|
|
1476
|
+
}
|
|
1477
|
+
if (!target) {
|
|
1478
|
+
const range = createRangeFromOffsets(previewArea, record.start, record.end);
|
|
1479
|
+
if (range && !range.collapsed) {
|
|
1480
|
+
const node = range.startContainer;
|
|
1481
|
+
target =
|
|
1482
|
+
node.nodeType === Node.ELEMENT_NODE
|
|
1483
|
+
? node
|
|
1484
|
+
: node.parentElement;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (!target ||
|
|
1488
|
+
typeof target.scrollIntoView !== "function") {
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
target.scrollIntoView({
|
|
1492
|
+
behavior: "smooth",
|
|
1493
|
+
block: "center",
|
|
1494
|
+
inline: "nearest",
|
|
1495
|
+
});
|
|
1496
|
+
flashPreviewAnnotationTarget(target);
|
|
1497
|
+
};
|
|
1498
|
+
const getPreviewAnnotationsByPage = () => {
|
|
1499
|
+
const pageId = getCurrentPageId();
|
|
1500
|
+
const annotations = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
|
|
1501
|
+
return {
|
|
1502
|
+
annotations,
|
|
1503
|
+
pageId,
|
|
1504
|
+
highlights: annotations.highlights.filter((item) => item.pageId === pageId),
|
|
1505
|
+
notes: annotations.notes.filter((item) => item.pageId === pageId),
|
|
1506
|
+
};
|
|
1507
|
+
};
|
|
1508
|
+
const renderAnnotationPanel = () => {
|
|
1509
|
+
const shouldShowPanel = state.mode === "preview" &&
|
|
1510
|
+
state.features.preview &&
|
|
1511
|
+
previewAnnotationOptions.showAnnotationList;
|
|
1512
|
+
annotationPanel.style.display = shouldShowPanel ? "grid" : "none";
|
|
1513
|
+
previewLayout.classList.toggle("with-annotation-panel", shouldShowPanel);
|
|
1514
|
+
if (!shouldShowPanel) {
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
const { highlights, notes } = getPreviewAnnotationsByPage();
|
|
1518
|
+
annotationHighlightTab.classList.toggle("active", annotationActiveTab === "highlights");
|
|
1519
|
+
annotationNoteTab.classList.toggle("active", annotationActiveTab === "notes");
|
|
1520
|
+
const activeList = annotationActiveTab === "highlights" ? highlights : notes;
|
|
1521
|
+
annotationList.innerHTML = "";
|
|
1522
|
+
if (!activeList.length) {
|
|
1523
|
+
annotationEmpty.style.display = "block";
|
|
1524
|
+
annotationEmpty.textContent =
|
|
1525
|
+
annotationActiveTab === "highlights"
|
|
1526
|
+
? "当前页暂无高亮"
|
|
1527
|
+
: "当前页暂无笔记";
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
annotationEmpty.style.display = "none";
|
|
1531
|
+
activeList.forEach((record, index) => {
|
|
1532
|
+
const item = document.createElement("li");
|
|
1533
|
+
const button = document.createElement("button");
|
|
1534
|
+
button.type = "button";
|
|
1535
|
+
button.className = "hyewb-preview-annotation-item";
|
|
1536
|
+
const heading = document.createElement("span");
|
|
1537
|
+
heading.className = "hyewb-preview-annotation-item-title";
|
|
1538
|
+
heading.textContent =
|
|
1539
|
+
annotationActiveTab === "highlights"
|
|
1540
|
+
? `高亮 ${index + 1}`
|
|
1541
|
+
: `笔记 ${index + 1}`;
|
|
1542
|
+
const body = document.createElement("span");
|
|
1543
|
+
body.className = "hyewb-preview-annotation-item-body";
|
|
1544
|
+
const fallbackRange = `[${record.start}, ${record.end}]`;
|
|
1545
|
+
body.textContent =
|
|
1546
|
+
annotationActiveTab === "highlights"
|
|
1547
|
+
? summarizePreviewText(record.selectedText, fallbackRange)
|
|
1548
|
+
: summarizePreviewText(record.text, "未命名笔记");
|
|
1549
|
+
button.append(heading, body);
|
|
1550
|
+
button.addEventListener("click", () => {
|
|
1551
|
+
focusPreviewAnnotationRecord(record, annotationActiveTab === "highlights" ? "highlight" : "note");
|
|
1552
|
+
});
|
|
1553
|
+
if (previewAnnotationOptions.annotationActions.canDelete) {
|
|
1554
|
+
const deleteButton = document.createElement("button");
|
|
1555
|
+
deleteButton.type = "button";
|
|
1556
|
+
deleteButton.className = "hyewb-preview-annotation-delete";
|
|
1557
|
+
deleteButton.title = "删除标注";
|
|
1558
|
+
deleteButton.setAttribute("aria-label", "删除标注");
|
|
1559
|
+
deleteButton.append(createIconPlaceholder("trash-2"));
|
|
1560
|
+
deleteButton.addEventListener("click", (event) => {
|
|
1561
|
+
event.preventDefault();
|
|
1562
|
+
event.stopPropagation();
|
|
1563
|
+
if (!window.confirm("确定删除这条标注吗?")) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
const current = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
|
|
1567
|
+
if (annotationActiveTab === "highlights") {
|
|
1568
|
+
current.highlights = current.highlights.filter((item) => {
|
|
1569
|
+
if ("id" in record && record.id && item.id) {
|
|
1570
|
+
return String(item.id) !== String(record.id);
|
|
1571
|
+
}
|
|
1572
|
+
return !(item.pageId === record.pageId &&
|
|
1573
|
+
item.start === record.start &&
|
|
1574
|
+
item.end === record.end);
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
else {
|
|
1578
|
+
current.notes = current.notes.filter((item) => {
|
|
1579
|
+
if ("id" in record && record.id && item.id) {
|
|
1580
|
+
return String(item.id) !== String(record.id);
|
|
1581
|
+
}
|
|
1582
|
+
return !(item.pageId === record.pageId &&
|
|
1583
|
+
item.start === record.start &&
|
|
1584
|
+
item.end === record.end);
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
savePreviewAnnotations(current);
|
|
1588
|
+
});
|
|
1589
|
+
button.append(deleteButton);
|
|
1590
|
+
}
|
|
1591
|
+
item.append(button);
|
|
1592
|
+
annotationList.append(item);
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
1595
|
+
const applyPreviewAnnotationsToView = () => {
|
|
1596
|
+
if (state.mode !== "preview") {
|
|
1597
|
+
previewSelectionToolbar.classList.remove("show");
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
const { highlights, notes } = getPreviewAnnotationsByPage();
|
|
1601
|
+
applyHighlightMarks(previewArea, highlights);
|
|
1602
|
+
applyNoteMarks(previewArea, notes);
|
|
1603
|
+
};
|
|
1604
|
+
const hidePreviewSelectionToolbar = () => {
|
|
1605
|
+
previewSelectionToolbar.classList.remove("show");
|
|
1606
|
+
previewSelectionDraft = null;
|
|
1607
|
+
};
|
|
1608
|
+
const syncPreviewSelectionToolbar = () => {
|
|
1609
|
+
if (!previewAnnotationOptions.enablePreviewSelectionToolbar ||
|
|
1610
|
+
state.mode !== "preview") {
|
|
1611
|
+
hidePreviewSelectionToolbar();
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
const selection = window.getSelection();
|
|
1615
|
+
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
|
|
1616
|
+
hidePreviewSelectionToolbar();
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
const range = selection.getRangeAt(0);
|
|
1620
|
+
if (!previewArea.contains(range.startContainer) ||
|
|
1621
|
+
!previewArea.contains(range.endContainer)) {
|
|
1622
|
+
hidePreviewSelectionToolbar();
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const offsets = getSelectionOffsets(previewArea, selection);
|
|
1626
|
+
if (!offsets) {
|
|
1627
|
+
hidePreviewSelectionToolbar();
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
const rect = range.getBoundingClientRect();
|
|
1631
|
+
if (!rect.width && !rect.height) {
|
|
1632
|
+
hidePreviewSelectionToolbar();
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
previewSelectionDraft = {
|
|
1636
|
+
start: offsets.start,
|
|
1637
|
+
end: offsets.end,
|
|
1638
|
+
pageId: getCurrentPageId(),
|
|
1639
|
+
text: selection.toString().trim(),
|
|
1640
|
+
};
|
|
1641
|
+
previewSelectionToolbar.style.left = `${Math.round(rect.left + rect.width / 2)}px`;
|
|
1642
|
+
previewSelectionToolbar.style.top = `${Math.round(rect.top - 8)}px`;
|
|
1643
|
+
previewSelectionToolbar.classList.add("show");
|
|
1644
|
+
};
|
|
1147
1645
|
const hideFloatingToolbar = () => {
|
|
1148
1646
|
floatingToolbar.classList.remove("show");
|
|
1149
1647
|
lastSelection = null;
|
|
@@ -2844,7 +3342,14 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
2844
3342
|
selectInlineImage(null);
|
|
2845
3343
|
rememberedParagraphIndex = null;
|
|
2846
3344
|
}
|
|
2847
|
-
|
|
3345
|
+
if (state.mode === "preview") {
|
|
3346
|
+
canvas.style.width = "100%";
|
|
3347
|
+
canvas.style.maxWidth = `${paperWidth}px`;
|
|
3348
|
+
}
|
|
3349
|
+
else {
|
|
3350
|
+
canvas.style.width = `${paperWidth}px`;
|
|
3351
|
+
canvas.style.maxWidth = "100%";
|
|
3352
|
+
}
|
|
2848
3353
|
previewArea.innerHTML = html;
|
|
2849
3354
|
previewArea.classList.toggle("show", state.mode === "preview");
|
|
2850
3355
|
editorArea.style.display = state.mode === "editor" ? "block" : "none";
|
|
@@ -2937,6 +3442,9 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
2937
3442
|
});
|
|
2938
3443
|
updateInlineResizeOverlay();
|
|
2939
3444
|
syncToolbarState();
|
|
3445
|
+
applyPreviewAnnotationsToView();
|
|
3446
|
+
renderAnnotationPanel();
|
|
3447
|
+
syncPreviewSelectionToolbar();
|
|
2940
3448
|
};
|
|
2941
3449
|
boldBtn.addEventListener("click", () => {
|
|
2942
3450
|
withSelectionCommand(() => {
|
|
@@ -3127,6 +3635,59 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3127
3635
|
floatBgColorInput.addEventListener("click", () => {
|
|
3128
3636
|
openColorPalette(floatBgColorInput, "background");
|
|
3129
3637
|
});
|
|
3638
|
+
annotationHighlightTab.addEventListener("click", () => {
|
|
3639
|
+
annotationActiveTab = "highlights";
|
|
3640
|
+
renderAnnotationPanel();
|
|
3641
|
+
});
|
|
3642
|
+
annotationNoteTab.addEventListener("click", () => {
|
|
3643
|
+
annotationActiveTab = "notes";
|
|
3644
|
+
renderAnnotationPanel();
|
|
3645
|
+
});
|
|
3646
|
+
previewHighlightBtn.addEventListener("click", () => {
|
|
3647
|
+
if (!previewSelectionDraft) {
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
const current = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
|
|
3651
|
+
current.highlights = mergeHighlightRecords(current.highlights, {
|
|
3652
|
+
id: `hl-${Date.now()}-${Math.round(Math.random() * 10000)}`,
|
|
3653
|
+
pageId: previewSelectionDraft.pageId,
|
|
3654
|
+
blockId: "preview-flow-text",
|
|
3655
|
+
start: previewSelectionDraft.start,
|
|
3656
|
+
end: previewSelectionDraft.end,
|
|
3657
|
+
color: "#fff59d",
|
|
3658
|
+
selectedText: previewSelectionDraft.text,
|
|
3659
|
+
createdAt: new Date().toISOString(),
|
|
3660
|
+
});
|
|
3661
|
+
annotationActiveTab = "highlights";
|
|
3662
|
+
savePreviewAnnotations(current);
|
|
3663
|
+
window.getSelection()?.removeAllRanges();
|
|
3664
|
+
hidePreviewSelectionToolbar();
|
|
3665
|
+
});
|
|
3666
|
+
previewNoteBtn.addEventListener("click", () => {
|
|
3667
|
+
if (!previewSelectionDraft) {
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
const text = window.prompt("请输入笔记内容", previewSelectionDraft.text || "") || "";
|
|
3671
|
+
const noteText = text.trim();
|
|
3672
|
+
if (!noteText) {
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
const current = normalizePreviewAnnotations(state.doc.meta.previewAnnotations);
|
|
3676
|
+
current.notes = mergeNoteRecords(current.notes, {
|
|
3677
|
+
id: `note-${Date.now()}-${Math.round(Math.random() * 10000)}`,
|
|
3678
|
+
pageId: previewSelectionDraft.pageId,
|
|
3679
|
+
start: previewSelectionDraft.start,
|
|
3680
|
+
end: previewSelectionDraft.end,
|
|
3681
|
+
text: noteText,
|
|
3682
|
+
selectedText: previewSelectionDraft.text,
|
|
3683
|
+
color: "#0e7490",
|
|
3684
|
+
createdAt: new Date().toISOString(),
|
|
3685
|
+
});
|
|
3686
|
+
annotationActiveTab = "notes";
|
|
3687
|
+
savePreviewAnnotations(current);
|
|
3688
|
+
window.getSelection()?.removeAllRanges();
|
|
3689
|
+
hidePreviewSelectionToolbar();
|
|
3690
|
+
});
|
|
3130
3691
|
floatLeftBtn.addEventListener("click", () => updateAlign("left"));
|
|
3131
3692
|
floatCenterBtn.addEventListener("click", () => updateAlign("center"));
|
|
3132
3693
|
floatRightBtn.addEventListener("click", () => updateAlign("right"));
|
|
@@ -3781,12 +4342,14 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3781
4342
|
updateInlineResizeOverlay();
|
|
3782
4343
|
syncToolbarState();
|
|
3783
4344
|
syncFocusedTableControls();
|
|
4345
|
+
syncPreviewSelectionToolbar();
|
|
3784
4346
|
};
|
|
3785
4347
|
const handleWindowResize = () => {
|
|
3786
4348
|
updateShellViewportHeight();
|
|
3787
4349
|
updateInlineResizeOverlay();
|
|
3788
4350
|
syncTableToolsPosition();
|
|
3789
4351
|
syncFocusedTableControls();
|
|
4352
|
+
syncPreviewSelectionToolbar();
|
|
3790
4353
|
if (tablePicker.classList.contains("show")) {
|
|
3791
4354
|
showTablePicker();
|
|
3792
4355
|
}
|
|
@@ -3797,6 +4360,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3797
4360
|
updateFloatingToolbarBySelection();
|
|
3798
4361
|
syncTableToolsPosition();
|
|
3799
4362
|
syncFocusedTableControls();
|
|
4363
|
+
syncPreviewSelectionToolbar();
|
|
3800
4364
|
if (tablePicker.classList.contains("show")) {
|
|
3801
4365
|
showTablePicker();
|
|
3802
4366
|
}
|
|
@@ -3819,6 +4383,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3819
4383
|
updateInlineResizeOverlay();
|
|
3820
4384
|
syncTableToolsPosition();
|
|
3821
4385
|
syncFocusedTableControls();
|
|
4386
|
+
syncPreviewSelectionToolbar();
|
|
3822
4387
|
});
|
|
3823
4388
|
};
|
|
3824
4389
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyebook/vue3-adapter",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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": "^0.2.
|
|
20
|
+
"@hyebook/core": "^0.2.2"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc -p tsconfig.json",
|