@hyebook/vue3-adapter 2.3.9 → 2.3.11
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 +33 -0
- package/dist/core/src/player/ebook-player.d.ts.map +1 -1
- package/dist/core/src/player/ebook-player.js +402 -50
- package/dist/core/src/player/engine.d.ts +2 -1
- package/dist/core/src/player/engine.d.ts.map +1 -1
- package/dist/core/src/player/engine.js +29 -0
- package/dist/core/src/types/player.d.ts +5 -0
- package/dist/core/src/types/player.d.ts.map +1 -1
- package/dist/core/src/workbench/editor-workbench.d.ts +4 -0
- package/dist/core/src/workbench/editor-workbench.d.ts.map +1 -1
- package/dist/core/src/workbench/editor-workbench.js +376 -86
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { addPageCommand } from "../editor/commands";
|
|
2
2
|
import { EditorEngine } from "../editor/engine";
|
|
3
3
|
const WORKBENCH_STYLE_ID = "hy-ebook-workbench-style";
|
|
4
|
-
const WORKBENCH_STYLE_VERSION = "0.3.
|
|
4
|
+
const WORKBENCH_STYLE_VERSION = "0.3.1";
|
|
5
5
|
const WORKBENCH_CSS = `
|
|
6
6
|
.hyewb-root{font-family:"Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;color:#0f172a;background:#f8fbff;border:1px solid #d8e0ea;border-radius:12px;padding:12px;display:grid;gap:10px;position:relative;--hyewb-top-shield-height:0px}
|
|
7
7
|
.hyewb-root::before{content:"";position:absolute;left:0;right:0;top:0;height:var(--hyewb-top-shield-height);background:#f8fbff;z-index:10030;pointer-events:none}
|
|
@@ -138,6 +138,15 @@ const WORKBENCH_CSS = `
|
|
|
138
138
|
.hyewb-media-handle[data-handle="w"]{left:-6px;top:calc(50% - 5px);cursor:ew-resize}
|
|
139
139
|
.hyewb-media-list{margin:0;padding-left:18px;color:#334155;font-size:13px;display:grid;gap:4px}
|
|
140
140
|
.hyewb-status{font-size:12px;color:#475569}
|
|
141
|
+
.hyewb-load-warning{position:absolute;top:12px;right:12px;z-index:10090;max-width:min(420px,calc(100% - 24px));display:none;border:1px solid #f5c2c7;background:#fff5f5;color:#7f1d1d;border-radius:10px;box-shadow:0 10px 24px rgba(15,23,42,.12);padding:8px 10px}
|
|
142
|
+
.hyewb-load-warning.show{display:block}
|
|
143
|
+
.hyewb-load-warning-header{display:flex;align-items:center;gap:8px}
|
|
144
|
+
.hyewb-load-warning-title{font-size:12px;font-weight:600;line-height:1.4}
|
|
145
|
+
.hyewb-load-warning-actions{margin-left:auto;display:inline-flex;align-items:center;gap:6px}
|
|
146
|
+
.hyewb-load-warning-btn{border:1px solid #f1b3b3;background:#fff;color:#7f1d1d;border-radius:6px;height:24px;padding:0 8px;font-size:12px;line-height:1;cursor:pointer}
|
|
147
|
+
.hyewb-load-warning-close{width:24px;padding:0}
|
|
148
|
+
.hyewb-load-warning-message{margin:8px 0 0;font-size:12px;line-height:1.5;color:#7f1d1d;white-space:pre-wrap;word-break:break-word;display:none;max-height:180px;overflow:auto}
|
|
149
|
+
.hyewb-load-warning.expanded .hyewb-load-warning-message{display:block}
|
|
141
150
|
.hyewb-upload-backdrop{position:fixed;inset:0;display:none;align-items:center;justify-content:center;background:rgba(15,23,42,.45);z-index:10100;padding:16px}
|
|
142
151
|
.hyewb-upload-backdrop.show{display:flex}
|
|
143
152
|
.hyewb-upload-dialog{position:relative;width:min(520px,100%);background:#fff;border:1px solid #cbd5e1;border-radius:12px;padding:14px;display:grid;gap:10px;box-shadow:0 16px 34px rgba(15,23,42,.28)}
|
|
@@ -332,6 +341,15 @@ function resolvePreviewAnnotationOptions(options) {
|
|
|
332
341
|
},
|
|
333
342
|
};
|
|
334
343
|
}
|
|
344
|
+
function resolveExtensionActionPlacement(placement) {
|
|
345
|
+
return placement === "toolbar" ? "toolbar" : "header";
|
|
346
|
+
}
|
|
347
|
+
function resolveExtensionActionVisibility(visibility) {
|
|
348
|
+
if (visibility === "always" || visibility === "fullscreen") {
|
|
349
|
+
return visibility;
|
|
350
|
+
}
|
|
351
|
+
return "editor";
|
|
352
|
+
}
|
|
335
353
|
export function createDefaultWorkbenchDocument() {
|
|
336
354
|
const now = new Date().toISOString();
|
|
337
355
|
return {
|
|
@@ -369,54 +387,19 @@ export function createDefaultWorkbenchDocument() {
|
|
|
369
387
|
{
|
|
370
388
|
id: "text-1",
|
|
371
389
|
type: "text",
|
|
372
|
-
rect: { x: 80, y: 80, width: 620, height:
|
|
390
|
+
rect: { x: 80, y: 80, width: 620, height: 120 },
|
|
373
391
|
zIndex: 1,
|
|
374
392
|
content: {
|
|
375
393
|
paragraphs: [
|
|
376
394
|
{
|
|
377
395
|
id: "p-1",
|
|
378
|
-
type: "h1",
|
|
379
|
-
align: "left",
|
|
380
|
-
spacingBefore: 12,
|
|
381
|
-
spacingAfter: 12,
|
|
382
|
-
runs: [
|
|
383
|
-
{
|
|
384
|
-
text: "欢迎使用业务系统编辑器",
|
|
385
|
-
marks: { bold: true, fontSize: 30 },
|
|
386
|
-
},
|
|
387
|
-
],
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
id: "p-2",
|
|
391
396
|
type: "p",
|
|
392
397
|
align: "left",
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
spacingAfter: 12,
|
|
396
|
-
indent: 2,
|
|
397
|
-
inlineElements: [
|
|
398
|
-
{
|
|
399
|
-
id: "inline-image-1",
|
|
400
|
-
type: "image",
|
|
401
|
-
src: "https://picsum.photos/id/1025/1200/800",
|
|
402
|
-
alt: "Classroom",
|
|
403
|
-
width: 320,
|
|
404
|
-
},
|
|
405
|
-
],
|
|
398
|
+
spacingBefore: 0,
|
|
399
|
+
spacingAfter: 0,
|
|
406
400
|
runs: [
|
|
407
|
-
{
|
|
408
|
-
text: "该页面默认包含样式和基础编辑能力。你可以在初始化阶段通过 features 开关关闭不需要的模块。\n",
|
|
409
|
-
},
|
|
410
401
|
{
|
|
411
402
|
text: "",
|
|
412
|
-
inlineRef: "inline-image-1",
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
text: "\n图片后依然可以继续输入文字。",
|
|
416
|
-
marks: {
|
|
417
|
-
color: "#0f766e",
|
|
418
|
-
backgroundColor: "#ecfeff",
|
|
419
|
-
},
|
|
420
403
|
},
|
|
421
404
|
],
|
|
422
405
|
},
|
|
@@ -430,8 +413,16 @@ export function createDefaultWorkbenchDocument() {
|
|
|
430
413
|
}
|
|
431
414
|
export function mountEditorWorkbench(container, options = {}) {
|
|
432
415
|
ensureWorkbenchStyles();
|
|
433
|
-
|
|
434
|
-
|
|
416
|
+
let initialLoadErrorMessage = null;
|
|
417
|
+
let editor;
|
|
418
|
+
try {
|
|
419
|
+
const initialDoc = clone(options.initialDoc ?? createDefaultWorkbenchDocument());
|
|
420
|
+
editor = new EditorEngine(initialDoc);
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
initialLoadErrorMessage = resolveErrorMessage(error, "initialDoc 校验失败");
|
|
424
|
+
editor = new EditorEngine(clone(createDefaultWorkbenchDocument()));
|
|
425
|
+
}
|
|
435
426
|
const state = {
|
|
436
427
|
mode: "editor",
|
|
437
428
|
pageIndex: 0,
|
|
@@ -443,13 +434,19 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
443
434
|
const root = document.createElement("section");
|
|
444
435
|
root.className = "hyewb-root";
|
|
445
436
|
const extensionActionConfigs = Array.isArray(options.extension?.actions)
|
|
446
|
-
? options.extension.actions
|
|
437
|
+
? options.extension.actions
|
|
438
|
+
.filter((action) => {
|
|
447
439
|
return (!!action &&
|
|
448
440
|
typeof action.id === "string" &&
|
|
449
441
|
action.id.trim().length > 0 &&
|
|
450
442
|
typeof action.text === "string" &&
|
|
451
443
|
action.text.trim().length > 0);
|
|
452
444
|
})
|
|
445
|
+
.map((action) => ({
|
|
446
|
+
...action,
|
|
447
|
+
placement: resolveExtensionActionPlacement(action.placement),
|
|
448
|
+
visibility: resolveExtensionActionVisibility(action.visibility),
|
|
449
|
+
}))
|
|
453
450
|
: [];
|
|
454
451
|
const header = document.createElement("header");
|
|
455
452
|
header.className = "hyewb-header hyewb-top-control";
|
|
@@ -459,32 +456,6 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
459
456
|
const headerActions = document.createElement("div");
|
|
460
457
|
headerActions.className = "hyewb-header-actions";
|
|
461
458
|
const fullscreenBtn = createButton("fullscreen");
|
|
462
|
-
const extensionActionButtons = extensionActionConfigs.map((action) => {
|
|
463
|
-
const button = document.createElement("button");
|
|
464
|
-
button.type = "button";
|
|
465
|
-
button.className = "hyewb-btn hyewb-btn-text hyewb-header-action-btn";
|
|
466
|
-
const text = action.text.trim();
|
|
467
|
-
button.textContent = text;
|
|
468
|
-
button.title = text;
|
|
469
|
-
button.setAttribute("aria-label", text);
|
|
470
|
-
button.style.display = "none";
|
|
471
|
-
const color = typeof action.color === "string" ? action.color.trim() : "";
|
|
472
|
-
if (color) {
|
|
473
|
-
button.style.backgroundColor = color;
|
|
474
|
-
button.style.borderColor = color;
|
|
475
|
-
}
|
|
476
|
-
const textColor = typeof action.textColor === "string" ? action.textColor.trim() : "";
|
|
477
|
-
if (textColor) {
|
|
478
|
-
button.style.color = textColor;
|
|
479
|
-
}
|
|
480
|
-
const borderColor = typeof action.borderColor === "string" ? action.borderColor.trim() : "";
|
|
481
|
-
if (borderColor) {
|
|
482
|
-
button.style.borderColor = borderColor;
|
|
483
|
-
}
|
|
484
|
-
headerActions.append(button);
|
|
485
|
-
return { action, button };
|
|
486
|
-
});
|
|
487
|
-
headerActions.append(fullscreenBtn);
|
|
488
459
|
header.append(title, headerActions);
|
|
489
460
|
const toolbar = document.createElement("div");
|
|
490
461
|
toolbar.className = "hyewb-row hyewb-top-control";
|
|
@@ -607,6 +578,40 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
607
578
|
const tableAddBtn = createButton("insertTable");
|
|
608
579
|
const importDocxBtn = createButton("importDocx");
|
|
609
580
|
toolbar.append(boldBtn, italicBtn, underlineBtn, strikeBtn, superscriptBtn, subscriptBtn, headingSelect, fontSizeSelect, fontFamilySelect, textColorInput, bgColorInput, alignGroup, lineHeightField, spacingField, indentField, tableAddBtn, importDocxBtn, videoAddBtn, imageUploadBtn);
|
|
581
|
+
const extensionActionButtons = extensionActionConfigs.map((action) => {
|
|
582
|
+
const button = document.createElement("button");
|
|
583
|
+
button.type = "button";
|
|
584
|
+
button.className = "hyewb-btn hyewb-btn-text";
|
|
585
|
+
if (action.placement === "header") {
|
|
586
|
+
button.classList.add("hyewb-header-action-btn");
|
|
587
|
+
}
|
|
588
|
+
const text = action.text.trim();
|
|
589
|
+
button.textContent = text;
|
|
590
|
+
button.title = text;
|
|
591
|
+
button.setAttribute("aria-label", text);
|
|
592
|
+
button.style.display = "none";
|
|
593
|
+
const color = typeof action.color === "string" ? action.color.trim() : "";
|
|
594
|
+
if (color) {
|
|
595
|
+
button.style.backgroundColor = color;
|
|
596
|
+
button.style.borderColor = color;
|
|
597
|
+
}
|
|
598
|
+
const textColor = typeof action.textColor === "string" ? action.textColor.trim() : "";
|
|
599
|
+
if (textColor) {
|
|
600
|
+
button.style.color = textColor;
|
|
601
|
+
}
|
|
602
|
+
const borderColor = typeof action.borderColor === "string" ? action.borderColor.trim() : "";
|
|
603
|
+
if (borderColor) {
|
|
604
|
+
button.style.borderColor = borderColor;
|
|
605
|
+
}
|
|
606
|
+
if (action.placement === "toolbar") {
|
|
607
|
+
toolbar.append(button);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
headerActions.append(button);
|
|
611
|
+
}
|
|
612
|
+
return { action, button };
|
|
613
|
+
});
|
|
614
|
+
headerActions.append(fullscreenBtn);
|
|
610
615
|
const tablePicker = document.createElement("div");
|
|
611
616
|
tablePicker.className = "hyewb-table-picker";
|
|
612
617
|
const tablePickerHint = document.createElement("div");
|
|
@@ -883,6 +888,51 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
883
888
|
const status = document.createElement("p");
|
|
884
889
|
status.className = "hyewb-status";
|
|
885
890
|
status.textContent = "已加载默认编辑器能力";
|
|
891
|
+
const loadWarning = document.createElement("div");
|
|
892
|
+
loadWarning.className = "hyewb-load-warning";
|
|
893
|
+
const loadWarningHeader = document.createElement("div");
|
|
894
|
+
loadWarningHeader.className = "hyewb-load-warning-header";
|
|
895
|
+
const loadWarningTitle = document.createElement("span");
|
|
896
|
+
loadWarningTitle.className = "hyewb-load-warning-title";
|
|
897
|
+
loadWarningTitle.textContent = "加载数据异常";
|
|
898
|
+
const loadWarningActions = document.createElement("div");
|
|
899
|
+
loadWarningActions.className = "hyewb-load-warning-actions";
|
|
900
|
+
const loadWarningToggleBtn = document.createElement("button");
|
|
901
|
+
loadWarningToggleBtn.type = "button";
|
|
902
|
+
loadWarningToggleBtn.className = "hyewb-load-warning-btn";
|
|
903
|
+
const loadWarningCloseBtn = document.createElement("button");
|
|
904
|
+
loadWarningCloseBtn.type = "button";
|
|
905
|
+
loadWarningCloseBtn.className = "hyewb-load-warning-btn hyewb-load-warning-close";
|
|
906
|
+
loadWarningCloseBtn.textContent = "×";
|
|
907
|
+
loadWarningCloseBtn.title = "关闭";
|
|
908
|
+
loadWarningCloseBtn.setAttribute("aria-label", "关闭加载异常提示");
|
|
909
|
+
const loadWarningMessage = document.createElement("pre");
|
|
910
|
+
loadWarningMessage.className = "hyewb-load-warning-message";
|
|
911
|
+
const setLoadWarningExpanded = (expanded) => {
|
|
912
|
+
loadWarning.classList.toggle("expanded", expanded);
|
|
913
|
+
loadWarningToggleBtn.textContent = expanded ? "收起" : "详情";
|
|
914
|
+
};
|
|
915
|
+
const hideLoadWarning = () => {
|
|
916
|
+
loadWarning.classList.remove("show");
|
|
917
|
+
setLoadWarningExpanded(false);
|
|
918
|
+
};
|
|
919
|
+
const showLoadWarning = (message) => {
|
|
920
|
+
loadWarningMessage.textContent = message;
|
|
921
|
+
loadWarning.classList.add("show");
|
|
922
|
+
setLoadWarningExpanded(false);
|
|
923
|
+
};
|
|
924
|
+
const handleLoadWarningToggle = () => {
|
|
925
|
+
setLoadWarningExpanded(!loadWarning.classList.contains("expanded"));
|
|
926
|
+
};
|
|
927
|
+
const handleLoadWarningClose = () => {
|
|
928
|
+
hideLoadWarning();
|
|
929
|
+
};
|
|
930
|
+
setLoadWarningExpanded(false);
|
|
931
|
+
loadWarningToggleBtn.addEventListener("click", handleLoadWarningToggle);
|
|
932
|
+
loadWarningCloseBtn.addEventListener("click", handleLoadWarningClose);
|
|
933
|
+
loadWarningActions.append(loadWarningToggleBtn, loadWarningCloseBtn);
|
|
934
|
+
loadWarningHeader.append(loadWarningTitle, loadWarningActions);
|
|
935
|
+
loadWarning.append(loadWarningHeader, loadWarningMessage);
|
|
886
936
|
const debugConfig = typeof options.debug === "object" && options.debug !== null
|
|
887
937
|
? options.debug
|
|
888
938
|
: undefined;
|
|
@@ -1002,10 +1052,14 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
1002
1052
|
const colorGrid = document.createElement("div");
|
|
1003
1053
|
colorGrid.className = "hyewb-color-grid";
|
|
1004
1054
|
colorPalette.append(colorGrid);
|
|
1005
|
-
root.append(header, toolbar, pageRow, previewLayout, colorPalette, tablePicker, tableTools, tableRowDeleteBtn, tableMoveHandle, tableScaleHandle, tableColEdgeLayer, tableRowEdgeLayer, tableRowGapInsertBtn, tableColGapInsertBtn, tableDropIndicator, tableContextMenu, uploadBackdrop, previewNoteBackdrop, docxInput, previewSelectionToolbar);
|
|
1055
|
+
root.append(header, toolbar, pageRow, previewLayout, colorPalette, tablePicker, tableTools, tableRowDeleteBtn, tableMoveHandle, tableScaleHandle, tableColEdgeLayer, tableRowEdgeLayer, tableRowGapInsertBtn, tableColGapInsertBtn, tableDropIndicator, tableContextMenu, uploadBackdrop, previewNoteBackdrop, docxInput, previewSelectionToolbar, loadWarning);
|
|
1006
1056
|
container.innerHTML = "";
|
|
1007
1057
|
root.append(floatingToolbar);
|
|
1008
1058
|
container.append(root);
|
|
1059
|
+
if (initialLoadErrorMessage) {
|
|
1060
|
+
status.textContent = `加载数据异常:${initialLoadErrorMessage}`;
|
|
1061
|
+
showLoadWarning(initialLoadErrorMessage);
|
|
1062
|
+
}
|
|
1009
1063
|
renderWorkbenchIcons();
|
|
1010
1064
|
let editorRenderKey = "";
|
|
1011
1065
|
let lastSelection = null;
|
|
@@ -1082,8 +1136,24 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
1082
1136
|
fullscreenBtn.title = ariaLabel;
|
|
1083
1137
|
fullscreenBtn.setAttribute("aria-label", ariaLabel);
|
|
1084
1138
|
fullscreenBtn.style.display = isFullscreen ? "inline-flex" : "none";
|
|
1085
|
-
|
|
1086
|
-
|
|
1139
|
+
};
|
|
1140
|
+
const shouldShowExtensionAction = (action) => {
|
|
1141
|
+
const isFullscreen = isWorkbenchFullscreen();
|
|
1142
|
+
const isEditorMode = state.mode === "editor";
|
|
1143
|
+
const visibleByRule = action.visibility === "always"
|
|
1144
|
+
? true
|
|
1145
|
+
: action.visibility === "fullscreen"
|
|
1146
|
+
? isFullscreen
|
|
1147
|
+
: isEditorMode;
|
|
1148
|
+
const visibleByPlacement = action.placement === "toolbar"
|
|
1149
|
+
? isEditorMode && state.features.textToolbar
|
|
1150
|
+
: true;
|
|
1151
|
+
return visibleByRule && visibleByPlacement;
|
|
1152
|
+
};
|
|
1153
|
+
const updateExtensionActionButtonsVisibility = () => {
|
|
1154
|
+
extensionActionButtons.forEach(({ action, button }) => {
|
|
1155
|
+
button.style.display =
|
|
1156
|
+
shouldShowExtensionAction(action) ? "inline-flex" : "none";
|
|
1087
1157
|
});
|
|
1088
1158
|
};
|
|
1089
1159
|
const updateShellViewportHeight = () => {
|
|
@@ -1123,6 +1193,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
1123
1193
|
};
|
|
1124
1194
|
const syncWorkbenchLayoutAfterFullscreenChange = () => {
|
|
1125
1195
|
updateFullscreenButtonState();
|
|
1196
|
+
updateExtensionActionButtonsVisibility();
|
|
1126
1197
|
updateShellViewportHeight();
|
|
1127
1198
|
updateTopControlShieldHeight();
|
|
1128
1199
|
updateInlineResizeOverlay();
|
|
@@ -2851,6 +2922,26 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
2851
2922
|
return item !== editorArea && isParagraphElement(item);
|
|
2852
2923
|
});
|
|
2853
2924
|
};
|
|
2925
|
+
const ensureEditorHasFocusableParagraph = () => {
|
|
2926
|
+
const paragraphs = getEditorParagraphCandidates();
|
|
2927
|
+
if (paragraphs.length) {
|
|
2928
|
+
return paragraphs[0];
|
|
2929
|
+
}
|
|
2930
|
+
const paragraph = document.createElement("p");
|
|
2931
|
+
paragraph.innerHTML = "<br>";
|
|
2932
|
+
editorArea.append(paragraph);
|
|
2933
|
+
return paragraph;
|
|
2934
|
+
};
|
|
2935
|
+
const isEditorSurfaceEffectivelyEmpty = () => {
|
|
2936
|
+
if (getEditorParagraphCandidates().length > 0) {
|
|
2937
|
+
return false;
|
|
2938
|
+
}
|
|
2939
|
+
const text = (editorArea.textContent || "").replace(/\u00a0/g, " ").trim();
|
|
2940
|
+
if (text.length > 0) {
|
|
2941
|
+
return false;
|
|
2942
|
+
}
|
|
2943
|
+
return !editorArea.querySelector("table,img,video,.inline-media");
|
|
2944
|
+
};
|
|
2854
2945
|
const rememberActiveParagraphIndex = () => {
|
|
2855
2946
|
const paragraph = resolveActiveParagraph(editorArea, rememberedRange);
|
|
2856
2947
|
if (!paragraph) {
|
|
@@ -3437,6 +3528,14 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3437
3528
|
});
|
|
3438
3529
|
return container.innerHTML;
|
|
3439
3530
|
};
|
|
3531
|
+
const normalizePastedRichHtml = (html) => {
|
|
3532
|
+
const stripped = stripMediaTagsFromHtml(html);
|
|
3533
|
+
if (!stripped.trim()) {
|
|
3534
|
+
return "";
|
|
3535
|
+
}
|
|
3536
|
+
const flowBlocks = parseRichHTMLToFlowBlocks(stripped);
|
|
3537
|
+
return flowBlocksToHTML(flowBlocks);
|
|
3538
|
+
};
|
|
3440
3539
|
const createPasteAnchor = () => {
|
|
3441
3540
|
editorArea.focus();
|
|
3442
3541
|
if (!restoreSelectionRange()) {
|
|
@@ -3796,6 +3895,9 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3796
3895
|
editorRenderKey = key;
|
|
3797
3896
|
selectInlineImage(null);
|
|
3798
3897
|
rememberedParagraphIndex = null;
|
|
3898
|
+
if (isEditorSurfaceEffectivelyEmpty()) {
|
|
3899
|
+
ensureEditorHasFocusableParagraph();
|
|
3900
|
+
}
|
|
3799
3901
|
}
|
|
3800
3902
|
if (state.mode === "preview") {
|
|
3801
3903
|
canvas.style.width = "100%";
|
|
@@ -3809,8 +3911,15 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3809
3911
|
previewArea.classList.toggle("show", state.mode === "preview");
|
|
3810
3912
|
editorArea.style.display = state.mode === "editor" ? "block" : "none";
|
|
3811
3913
|
const showEditorControls = state.mode === "editor";
|
|
3914
|
+
const hasVisibleHeaderExtensionAction = extensionActionButtons.some(({ action }) => {
|
|
3915
|
+
return action.placement === "header" && shouldShowExtensionAction(action);
|
|
3916
|
+
});
|
|
3812
3917
|
header.style.display =
|
|
3813
|
-
showEditorControls ||
|
|
3918
|
+
showEditorControls ||
|
|
3919
|
+
isWorkbenchFullscreen() ||
|
|
3920
|
+
hasVisibleHeaderExtensionAction
|
|
3921
|
+
? "flex"
|
|
3922
|
+
: "none";
|
|
3814
3923
|
toolbar.style.display =
|
|
3815
3924
|
showEditorControls && state.features.textToolbar ? "flex" : "none";
|
|
3816
3925
|
pageRow.style.display = "none";
|
|
@@ -3829,6 +3938,7 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
3829
3938
|
hideTablePicker();
|
|
3830
3939
|
hideTableTools();
|
|
3831
3940
|
}
|
|
3941
|
+
updateExtensionActionButtonsVisibility();
|
|
3832
3942
|
updateTopControlShieldHeight();
|
|
3833
3943
|
prevPageBtn.disabled = state.pageIndex <= 0;
|
|
3834
3944
|
nextPageBtn.disabled = state.pageIndex >= state.doc.pages.length - 1;
|
|
@@ -4054,6 +4164,9 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
4054
4164
|
tableWidthInput,
|
|
4055
4165
|
tableHeightInput,
|
|
4056
4166
|
tableRowHeightInput,
|
|
4167
|
+
...extensionActionButtons
|
|
4168
|
+
.filter(({ action }) => action.placement === "toolbar")
|
|
4169
|
+
.map(({ button }) => button),
|
|
4057
4170
|
].forEach((control) => {
|
|
4058
4171
|
control.addEventListener("mousedown", preserveSelectionForInputControl);
|
|
4059
4172
|
control.addEventListener("focus", preserveSelectionForInputControl);
|
|
@@ -4179,6 +4292,20 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
4179
4292
|
const image = target.closest("img.inline-image");
|
|
4180
4293
|
if (!image || !editorArea.contains(image)) {
|
|
4181
4294
|
selectInlineImage(null);
|
|
4295
|
+
if (target === editorArea && isEditorSurfaceEffectivelyEmpty()) {
|
|
4296
|
+
const paragraph = ensureEditorHasFocusableParagraph();
|
|
4297
|
+
editorArea.focus();
|
|
4298
|
+
const range = document.createRange();
|
|
4299
|
+
range.selectNodeContents(paragraph);
|
|
4300
|
+
range.collapse(false);
|
|
4301
|
+
const selection = window.getSelection();
|
|
4302
|
+
if (selection) {
|
|
4303
|
+
selection.removeAllRanges();
|
|
4304
|
+
selection.addRange(range);
|
|
4305
|
+
rememberedRange = range.cloneRange();
|
|
4306
|
+
rememberActiveParagraphIndex();
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4182
4309
|
return;
|
|
4183
4310
|
}
|
|
4184
4311
|
selectInlineImage(image);
|
|
@@ -4215,6 +4342,25 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
4215
4342
|
if (!clipboard) {
|
|
4216
4343
|
return;
|
|
4217
4344
|
}
|
|
4345
|
+
const htmlContent = clipboard.getData("text/html");
|
|
4346
|
+
const plainContent = clipboard.getData("text/plain");
|
|
4347
|
+
const normalizedRichHtml = normalizePastedRichHtml(htmlContent);
|
|
4348
|
+
if (normalizedRichHtml.trim()) {
|
|
4349
|
+
event.preventDefault();
|
|
4350
|
+
cacheSelectionRange();
|
|
4351
|
+
const pasteAnchorId = createPasteAnchor();
|
|
4352
|
+
if (!pasteAnchorId) {
|
|
4353
|
+
status.textContent = "粘贴失败:无法定位插入位置";
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
4356
|
+
insertHtmlBeforePasteAnchor(pasteAnchorId, normalizedRichHtml);
|
|
4357
|
+
removePasteAnchor(pasteAnchorId);
|
|
4358
|
+
syncEditorToDoc();
|
|
4359
|
+
status.textContent = "已粘贴富文本内容";
|
|
4360
|
+
updateInlineResizeOverlay();
|
|
4361
|
+
syncToolbarState();
|
|
4362
|
+
return;
|
|
4363
|
+
}
|
|
4218
4364
|
const mediaFiles = Array.from(clipboard.items)
|
|
4219
4365
|
.map((item) => item.getAsFile())
|
|
4220
4366
|
.filter((file) => {
|
|
@@ -4226,8 +4372,6 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
4226
4372
|
}
|
|
4227
4373
|
event.preventDefault();
|
|
4228
4374
|
cacheSelectionRange();
|
|
4229
|
-
const htmlContent = clipboard.getData("text/html");
|
|
4230
|
-
const plainContent = clipboard.getData("text/plain");
|
|
4231
4375
|
const pasteAnchorId = createPasteAnchor();
|
|
4232
4376
|
if (!pasteAnchorId) {
|
|
4233
4377
|
status.textContent = "粘贴失败:无法定位插入位置";
|
|
@@ -5030,6 +5174,8 @@ export function mountEditorWorkbench(container, options = {}) {
|
|
|
5030
5174
|
closeWorkbenchFullscreen();
|
|
5031
5175
|
hideTablePicker();
|
|
5032
5176
|
hideTableTools();
|
|
5177
|
+
loadWarningToggleBtn.removeEventListener("click", handleLoadWarningToggle);
|
|
5178
|
+
loadWarningCloseBtn.removeEventListener("click", handleLoadWarningClose);
|
|
5033
5179
|
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
5034
5180
|
document.removeEventListener("pointermove", onMediaPointerMove);
|
|
5035
5181
|
document.removeEventListener("pointerup", onMediaPointerUp);
|
|
@@ -5202,35 +5348,108 @@ function parseRichHTMLToFlowBlocks(html, defaultTextStyle) {
|
|
|
5202
5348
|
return [createDefaultTextFlowBlock()];
|
|
5203
5349
|
}
|
|
5204
5350
|
const nodes = Array.from(root.childNodes).filter((node) => {
|
|
5205
|
-
if (node.nodeType
|
|
5206
|
-
return
|
|
5351
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
5352
|
+
return (node.nodeValue || "").trim().length > 0;
|
|
5353
|
+
}
|
|
5354
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
5355
|
+
return false;
|
|
5207
5356
|
}
|
|
5208
|
-
return (node.
|
|
5357
|
+
return !isIgnorableImportedElementTag(node.tagName);
|
|
5209
5358
|
});
|
|
5210
5359
|
if (!nodes.length) {
|
|
5211
5360
|
return [createDefaultTextFlowBlock()];
|
|
5212
5361
|
}
|
|
5213
5362
|
const result = [];
|
|
5214
|
-
|
|
5363
|
+
let flowIndex = 0;
|
|
5364
|
+
const pushParagraphIfMeaningful = (paragraph) => {
|
|
5365
|
+
if (!hasMeaningfulParagraphContent(paragraph)) {
|
|
5366
|
+
return;
|
|
5367
|
+
}
|
|
5368
|
+
result.push(createTextFlowBlock(paragraph, flowIndex));
|
|
5369
|
+
flowIndex += 1;
|
|
5370
|
+
};
|
|
5371
|
+
const pushNode = (node) => {
|
|
5215
5372
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
5373
|
+
const text = node.nodeValue || "";
|
|
5374
|
+
if (!text.trim()) {
|
|
5375
|
+
return;
|
|
5376
|
+
}
|
|
5216
5377
|
const paragraph = {
|
|
5217
|
-
id: `p-${Date.now()}-${
|
|
5378
|
+
id: `p-${Date.now()}-${flowIndex}`,
|
|
5218
5379
|
type: "p",
|
|
5219
5380
|
align: "left",
|
|
5220
|
-
runs: applyImportedDefaultTextStyle([{ text
|
|
5381
|
+
runs: applyImportedDefaultTextStyle([{ text }], defaultTextStyle),
|
|
5221
5382
|
};
|
|
5222
|
-
|
|
5383
|
+
pushParagraphIfMeaningful(paragraph);
|
|
5384
|
+
return;
|
|
5385
|
+
}
|
|
5386
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
5223
5387
|
return;
|
|
5224
5388
|
}
|
|
5225
5389
|
const element = node;
|
|
5226
5390
|
if (element.tagName === "TABLE") {
|
|
5227
|
-
result.push(parseTableElement(element,
|
|
5391
|
+
result.push(parseTableElement(element, flowIndex));
|
|
5392
|
+
flowIndex += 1;
|
|
5228
5393
|
return;
|
|
5229
5394
|
}
|
|
5230
|
-
|
|
5395
|
+
const hasNestedTable = Boolean(element.querySelector("table"));
|
|
5396
|
+
if (!hasNestedTable) {
|
|
5397
|
+
pushParagraphIfMeaningful(parseElementToParagraph(element, flowIndex, defaultTextStyle));
|
|
5398
|
+
return;
|
|
5399
|
+
}
|
|
5400
|
+
let textContainer = element.cloneNode(false);
|
|
5401
|
+
const flushTextContainer = () => {
|
|
5402
|
+
if (!hasMeaningfulElementContent(textContainer)) {
|
|
5403
|
+
textContainer = element.cloneNode(false);
|
|
5404
|
+
return;
|
|
5405
|
+
}
|
|
5406
|
+
const paragraph = parseElementToParagraph(textContainer, flowIndex, defaultTextStyle);
|
|
5407
|
+
pushParagraphIfMeaningful(paragraph);
|
|
5408
|
+
textContainer = element.cloneNode(false);
|
|
5409
|
+
};
|
|
5410
|
+
Array.from(element.childNodes).forEach((child) => {
|
|
5411
|
+
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
5412
|
+
const childElement = child;
|
|
5413
|
+
if (childElement.tagName === "TABLE") {
|
|
5414
|
+
flushTextContainer();
|
|
5415
|
+
result.push(parseTableElement(childElement, flowIndex));
|
|
5416
|
+
flowIndex += 1;
|
|
5417
|
+
return;
|
|
5418
|
+
}
|
|
5419
|
+
if (childElement.querySelector("table")) {
|
|
5420
|
+
flushTextContainer();
|
|
5421
|
+
pushNode(childElement);
|
|
5422
|
+
return;
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
textContainer.append(child.cloneNode(true));
|
|
5426
|
+
});
|
|
5427
|
+
flushTextContainer();
|
|
5428
|
+
};
|
|
5429
|
+
nodes.forEach((node) => {
|
|
5430
|
+
pushNode(node);
|
|
5231
5431
|
});
|
|
5232
5432
|
return result.length ? result : [createDefaultTextFlowBlock()];
|
|
5233
5433
|
}
|
|
5434
|
+
function hasMeaningfulElementContent(element) {
|
|
5435
|
+
const text = (element.textContent || "").replace(/\u00a0/g, " ").trim();
|
|
5436
|
+
if (text.length > 0) {
|
|
5437
|
+
return true;
|
|
5438
|
+
}
|
|
5439
|
+
return Boolean(element.querySelector("img,video,br"));
|
|
5440
|
+
}
|
|
5441
|
+
function hasMeaningfulParagraphContent(paragraph) {
|
|
5442
|
+
if (Array.isArray(paragraph.inlineElements) && paragraph.inlineElements.length) {
|
|
5443
|
+
return true;
|
|
5444
|
+
}
|
|
5445
|
+
const runs = Array.isArray(paragraph.runs) ? paragraph.runs : [];
|
|
5446
|
+
return runs.some((run) => {
|
|
5447
|
+
if (run.inlineRef) {
|
|
5448
|
+
return true;
|
|
5449
|
+
}
|
|
5450
|
+
return (run.text || "").replace(/\u00a0/g, " ").trim().length > 0;
|
|
5451
|
+
});
|
|
5452
|
+
}
|
|
5234
5453
|
function parseElementToParagraph(element, index, defaultTextStyle) {
|
|
5235
5454
|
const parsedLineHeight = parseLineHeight(element.style.lineHeight);
|
|
5236
5455
|
const parsedBefore = parsePx(element.style.marginTop);
|
|
@@ -5463,6 +5682,9 @@ function extractRuns(node, inheritedMarks, inlineElements) {
|
|
|
5463
5682
|
return [];
|
|
5464
5683
|
}
|
|
5465
5684
|
const element = node;
|
|
5685
|
+
if (isIgnorableImportedElementTag(element.tagName)) {
|
|
5686
|
+
return [];
|
|
5687
|
+
}
|
|
5466
5688
|
if (element.tagName === "BR") {
|
|
5467
5689
|
const marks = cleanupMarks(inheritedMarks);
|
|
5468
5690
|
return [marks ? { text: "\n", marks } : { text: "\n" }];
|
|
@@ -5853,8 +6075,34 @@ function parseLineHeight(value) {
|
|
|
5853
6075
|
if (!value) {
|
|
5854
6076
|
return undefined;
|
|
5855
6077
|
}
|
|
5856
|
-
const
|
|
5857
|
-
|
|
6078
|
+
const normalized = value.trim().toLowerCase();
|
|
6079
|
+
if (!normalized || normalized === "normal") {
|
|
6080
|
+
return undefined;
|
|
6081
|
+
}
|
|
6082
|
+
const percentMatch = normalized.match(/^(-?\d+(?:\.\d+)?)%$/);
|
|
6083
|
+
if (percentMatch && percentMatch[1] !== undefined) {
|
|
6084
|
+
const percent = Number.parseFloat(percentMatch[1]);
|
|
6085
|
+
if (!Number.isFinite(percent) || percent <= 0) {
|
|
6086
|
+
return undefined;
|
|
6087
|
+
}
|
|
6088
|
+
return Number.parseFloat((percent / 100).toFixed(4));
|
|
6089
|
+
}
|
|
6090
|
+
const unitlessMatch = normalized.match(/^-?\d+(?:\.\d+)?$/);
|
|
6091
|
+
if (unitlessMatch) {
|
|
6092
|
+
const raw = Number.parseFloat(normalized);
|
|
6093
|
+
if (!Number.isFinite(raw) || raw <= 0) {
|
|
6094
|
+
return undefined;
|
|
6095
|
+
}
|
|
6096
|
+
if (raw >= 100) {
|
|
6097
|
+
return Number.parseFloat((raw / 100).toFixed(4));
|
|
6098
|
+
}
|
|
6099
|
+
return raw;
|
|
6100
|
+
}
|
|
6101
|
+
const number = Number.parseFloat(normalized);
|
|
6102
|
+
if (!Number.isFinite(number) || number <= 0) {
|
|
6103
|
+
return undefined;
|
|
6104
|
+
}
|
|
6105
|
+
return number;
|
|
5858
6106
|
}
|
|
5859
6107
|
function parseIndentEm(value) {
|
|
5860
6108
|
if (!value) {
|
|
@@ -7055,6 +7303,26 @@ function normalizeStyleAttribute(styleValue) {
|
|
|
7055
7303
|
.filter((segment) => segment.length > 0)
|
|
7056
7304
|
.join(";");
|
|
7057
7305
|
}
|
|
7306
|
+
function isIgnorableImportedElementTag(tagName) {
|
|
7307
|
+
const normalized = String(tagName || "")
|
|
7308
|
+
.trim()
|
|
7309
|
+
.toUpperCase();
|
|
7310
|
+
if (!normalized) {
|
|
7311
|
+
return false;
|
|
7312
|
+
}
|
|
7313
|
+
if (normalized === "SCRIPT" ||
|
|
7314
|
+
normalized === "STYLE" ||
|
|
7315
|
+
normalized === "META" ||
|
|
7316
|
+
normalized === "LINK" ||
|
|
7317
|
+
normalized === "TITLE" ||
|
|
7318
|
+
normalized === "HEAD" ||
|
|
7319
|
+
normalized === "BASE" ||
|
|
7320
|
+
normalized === "XML" ||
|
|
7321
|
+
normalized === "O:P") {
|
|
7322
|
+
return true;
|
|
7323
|
+
}
|
|
7324
|
+
return normalized.startsWith("W:");
|
|
7325
|
+
}
|
|
7058
7326
|
function normalizeStoredTableCellHTML(html) {
|
|
7059
7327
|
const source = String(html || "").trim();
|
|
7060
7328
|
if (!source) {
|
|
@@ -7066,7 +7334,20 @@ function normalizeStoredTableCellHTML(html) {
|
|
|
7066
7334
|
if (!root) {
|
|
7067
7335
|
return "";
|
|
7068
7336
|
}
|
|
7069
|
-
root.querySelectorAll("
|
|
7337
|
+
const allElements = Array.from(root.querySelectorAll("*"));
|
|
7338
|
+
allElements.forEach((node) => {
|
|
7339
|
+
if (isIgnorableImportedElementTag(node.tagName)) {
|
|
7340
|
+
node.remove();
|
|
7341
|
+
}
|
|
7342
|
+
});
|
|
7343
|
+
const commentWalker = doc.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
7344
|
+
const comments = [];
|
|
7345
|
+
while (commentWalker.nextNode()) {
|
|
7346
|
+
comments.push(commentWalker.currentNode);
|
|
7347
|
+
}
|
|
7348
|
+
comments.forEach((commentNode) => {
|
|
7349
|
+
commentNode.remove();
|
|
7350
|
+
});
|
|
7070
7351
|
root.querySelectorAll("*").forEach((node) => {
|
|
7071
7352
|
Array.from(node.attributes).forEach((attr) => {
|
|
7072
7353
|
if (attr.name.toLowerCase().startsWith("on")) {
|
|
@@ -7658,3 +7939,12 @@ function isRecord(value) {
|
|
|
7658
7939
|
function clone(value) {
|
|
7659
7940
|
return JSON.parse(JSON.stringify(value));
|
|
7660
7941
|
}
|
|
7942
|
+
function resolveErrorMessage(error, fallback = "未知错误") {
|
|
7943
|
+
if (error instanceof Error && error.message) {
|
|
7944
|
+
return error.message;
|
|
7945
|
+
}
|
|
7946
|
+
if (typeof error === "string" && error.trim()) {
|
|
7947
|
+
return error.trim();
|
|
7948
|
+
}
|
|
7949
|
+
return fallback;
|
|
7950
|
+
}
|