vditor 3.11.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/css/content-theme/ant-design.css +10 -0
- package/dist/css/content-theme/dark.css +10 -0
- package/dist/css/content-theme/light.css +13 -0
- package/dist/css/content-theme/wechat.css +10 -0
- package/dist/index.css +163 -81
- package/dist/index.js +3035 -2715
- package/dist/index.min.js +1 -1
- package/dist/js/i18n/ja_JP.js +2 -2
- package/dist/js/i18n/zh_TW.js +8 -8
- package/dist/js/lute/lute.min.js +75 -60
- package/dist/js/mermaid/mermaid.min.js +2027 -1047
- package/dist/js/wavedrom/LICENSE.WaveDrom +21 -0
- package/dist/js/wavedrom/wavedrom.min.js +9 -0
- package/dist/method.d.ts +2 -0
- package/dist/method.js +323 -9
- package/dist/method.min.js +1 -1
- package/dist/ts/constants.d.ts +4 -0
- package/dist/ts/markdown/adapterRender.d.ts +4 -0
- package/dist/ts/markdown/imageCaptionRender.d.ts +2 -0
- package/dist/ts/markdown/wavedromRender.d.ts +1 -0
- package/dist/ts/sv/index.d.ts +1 -5
- package/dist/ts/sv/process.d.ts +1 -3
- package/dist/ts/upload/getElement.d.ts +1 -1
- package/dist/ts/upload/index.d.ts +2 -0
- package/dist/ts/util/editorCommonEvent.d.ts +1 -1
- package/dist/ts/util/fixBrowserBehavior.d.ts +1 -0
- package/dist/types/index.d.ts +22 -5
- package/package.json +7 -4
- package/src/assets/less/_reset.less +103 -2
- package/src/assets/less/_sv.less +2 -98
- package/src/assets/less/_wysiwyg.less +30 -0
- package/src/assets/less/index.less +20 -0
- package/src/index.ts +48 -14
- package/src/method.ts +3 -0
- package/src/ts/constants.ts +5 -1
- package/src/ts/export/index.ts +2 -0
- package/src/ts/hint/index.ts +9 -4
- package/src/ts/ir/highlightToolbarIR.ts +4 -4
- package/src/ts/ir/index.ts +26 -0
- package/src/ts/ir/process.ts +24 -1
- package/src/ts/markdown/adapterRender.ts +4 -0
- package/src/ts/markdown/getHTML.ts +6 -3
- package/src/ts/markdown/getMarkdown.ts +3 -3
- package/src/ts/markdown/imageCaptionRender.ts +175 -0
- package/src/ts/markdown/mermaidRender.ts +1 -1
- package/src/ts/markdown/previewRender.ts +7 -1
- package/src/ts/markdown/setLute.ts +3 -0
- package/src/ts/markdown/wavedromRender.ts +54 -0
- package/src/ts/preview/index.ts +5 -0
- package/src/ts/sv/index.ts +37 -76
- package/src/ts/sv/process.ts +48 -164
- package/src/ts/sv/processKeydown.ts +19 -186
- package/src/ts/toolbar/EditMode.ts +3 -10
- package/src/ts/toolbar/Emoji.ts +8 -1
- package/src/ts/toolbar/Record.ts +10 -2
- package/src/ts/toolbar/index.ts +3 -0
- package/src/ts/ui/initUI.ts +7 -3
- package/src/ts/undo/index.ts +32 -17
- package/src/ts/upload/index.ts +42 -10
- package/src/ts/util/Options.ts +1 -1
- package/src/ts/util/editorCommonEvent.ts +18 -9
- package/src/ts/util/fixBrowserBehavior.ts +197 -51
- package/src/ts/util/processCode.ts +3 -0
- package/src/ts/util/selection.ts +11 -3
- package/src/ts/wysiwyg/afterRenderEvent.ts +2 -0
- package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +58 -2
- package/src/ts/wysiwyg/input.ts +5 -0
- package/src/ts/wysiwyg/processKeydown.ts +47 -0
- package/src/ts/wysiwyg/setHeading.ts +110 -6
- package/dist/ts/sv/combineFootnote.d.ts +0 -6
- package/dist/ts/sv/inputEvent.d.ts +0 -1
- package/src/ts/sv/combineFootnote.ts +0 -27
- package/src/ts/sv/inputEvent.ts +0 -188
package/src/ts/sv/process.ts
CHANGED
|
@@ -1,104 +1,14 @@
|
|
|
1
1
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
2
2
|
import {accessLocalStorage} from "../util/compatibility";
|
|
3
|
-
import {scrollCenter} from "../util/editorCommonEvent";
|
|
4
|
-
import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
|
|
5
|
-
import {hasClosestByTag} from "../util/hasClosestByHeadings";
|
|
6
|
-
import {log} from "../util/log";
|
|
7
|
-
import {getEditorRange, setRangeByWbr} from "../util/selection";
|
|
8
|
-
import {inputEvent} from "./inputEvent";
|
|
9
|
-
import {combineFootnote} from "./combineFootnote";
|
|
10
3
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
range.insertNode(document.createTextNode(Lute.Caret));
|
|
16
|
-
range.insertNode(document.createTextNode(text));
|
|
17
|
-
let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
|
|
18
|
-
if (!blockElement) {
|
|
19
|
-
blockElement = vditor.sv.element;
|
|
20
|
-
}
|
|
21
|
-
let spinHTML = vditor.lute.SpinVditorSVDOM(blockElement.textContent)
|
|
22
|
-
spinHTML = "<div data-block='0'>" +
|
|
23
|
-
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
|
|
24
|
-
"</div>";
|
|
25
|
-
if (blockElement.isEqualNode(vditor.sv.element)) {
|
|
26
|
-
blockElement.innerHTML = spinHTML;
|
|
27
|
-
} else {
|
|
28
|
-
blockElement.outerHTML = spinHTML;
|
|
29
|
-
}
|
|
30
|
-
combineFootnote(vditor.sv.element)
|
|
31
|
-
setRangeByWbr(vditor.sv.element, range);
|
|
32
|
-
|
|
33
|
-
scrollCenter(vditor);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const getSideByType = (spanNode: Node, type: string, isPrevious = true) => {
|
|
37
|
-
let sideElement = spanNode as Element;
|
|
38
|
-
if (sideElement.nodeType === 3) {
|
|
39
|
-
sideElement = sideElement.parentElement;
|
|
40
|
-
}
|
|
41
|
-
while (sideElement) {
|
|
42
|
-
if (sideElement.getAttribute("data-type") === type) {
|
|
43
|
-
return sideElement;
|
|
44
|
-
}
|
|
45
|
-
if (isPrevious) {
|
|
46
|
-
sideElement = sideElement.previousElementSibling;
|
|
47
|
-
} else {
|
|
48
|
-
sideElement = sideElement.nextElementSibling;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
4
|
+
export const replaceSelection = (vditor: IVditor, text: string, selectionMode: SelectionMode = "end") => {
|
|
5
|
+
const element = vditor.sv.element;
|
|
6
|
+
element.setRangeText(text, element.selectionStart, element.selectionEnd, selectionMode);
|
|
7
|
+
element.focus();
|
|
52
8
|
};
|
|
53
9
|
|
|
54
|
-
export const
|
|
55
|
-
|
|
56
|
-
const spinHTML = vditor.lute.SpinVditorSVDOM(html)
|
|
57
|
-
html = "<div data-block='0'>" +
|
|
58
|
-
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
|
|
59
|
-
"</div>";
|
|
60
|
-
log("SpinVditorSVDOM", html, "result", vditor.options.debugger);
|
|
61
|
-
return html;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const processPreviousMarkers = (spanElement: HTMLElement) => {
|
|
65
|
-
const spanType = spanElement.getAttribute("data-type");
|
|
66
|
-
let previousElement = spanElement.previousElementSibling;
|
|
67
|
-
// 有内容的子列表/标题,在其 marker 后换行
|
|
68
|
-
let markerText = (spanType && spanType !== "text" && spanType !== "table" && spanType !== "heading-marker" &&
|
|
69
|
-
spanType !== "newline" && spanType !== "yaml-front-matter-open-marker" && spanType !== "yaml-front-matter-close-marker"
|
|
70
|
-
&& spanType !== "code-block-info" && spanType !== "code-block-close-marker" && spanType !== "code-block-open-marker") ?
|
|
71
|
-
spanElement.textContent : "";
|
|
72
|
-
let hasNL = false;
|
|
73
|
-
if (spanType === "newline") {
|
|
74
|
-
hasNL = true;
|
|
75
|
-
}
|
|
76
|
-
while (previousElement && !hasNL) {
|
|
77
|
-
const previousType = previousElement.getAttribute("data-type");
|
|
78
|
-
if (previousType === "li-marker" || previousType === "blockquote-marker" || previousType === "task-marker" ||
|
|
79
|
-
previousType === "padding") {
|
|
80
|
-
const previousText = previousElement.textContent;
|
|
81
|
-
if (previousType === "li-marker" &&
|
|
82
|
-
(spanType === "code-block-open-marker" || spanType === "code-block-info")) {
|
|
83
|
-
// https://github.com/Vanessa219/vditor/issues/586
|
|
84
|
-
markerText = previousText.replace(/\S/g, " ") + markerText;
|
|
85
|
-
} else if (spanType === "code-block-close-marker" &&
|
|
86
|
-
previousElement.nextElementSibling.isSameNode(spanElement)) {
|
|
87
|
-
// https://github.com/Vanessa219/vditor/issues/594
|
|
88
|
-
const openMarker = getSideByType(spanElement, "code-block-open-marker");
|
|
89
|
-
if (openMarker && openMarker.previousElementSibling) {
|
|
90
|
-
previousElement = openMarker.previousElementSibling;
|
|
91
|
-
markerText = previousText + markerText;
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
markerText = previousText + markerText;
|
|
95
|
-
}
|
|
96
|
-
} else if (previousType === "newline") {
|
|
97
|
-
hasNL = true;
|
|
98
|
-
}
|
|
99
|
-
previousElement = previousElement.previousElementSibling;
|
|
100
|
-
}
|
|
101
|
-
return markerText;
|
|
10
|
+
export const processPaste = (vditor: IVditor, text: string) => {
|
|
11
|
+
replaceSelection(vditor, text);
|
|
102
12
|
};
|
|
103
13
|
|
|
104
14
|
export const processAfterRender = (vditor: IVditor, options = {
|
|
@@ -106,10 +16,6 @@ export const processAfterRender = (vditor: IVditor, options = {
|
|
|
106
16
|
enableHint: false,
|
|
107
17
|
enableInput: true,
|
|
108
18
|
}) => {
|
|
109
|
-
if (options.enableHint) {
|
|
110
|
-
vditor.hint.render(vditor);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
19
|
vditor.preview.render(vditor);
|
|
114
20
|
|
|
115
21
|
const text = getMarkdown(vditor);
|
|
@@ -141,75 +47,53 @@ export const processAfterRender = (vditor: IVditor, options = {
|
|
|
141
47
|
};
|
|
142
48
|
|
|
143
49
|
export const processHeading = (vditor: IVditor, value: string) => {
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
50
|
+
const element = vditor.sv.element;
|
|
51
|
+
const lineStart = element.value.lastIndexOf("\n", element.selectionStart - 1) + 1;
|
|
52
|
+
const lineEndIndex = element.value.indexOf("\n", element.selectionEnd);
|
|
53
|
+
const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
|
|
54
|
+
const selectedLines = element.value.substring(lineStart, lineEnd);
|
|
55
|
+
const heading = value ? value + selectedLines.replace(/^#{1,6}\s+/, "") : selectedLines.replace(/^#{1,6}\s+/, "");
|
|
56
|
+
element.setRangeText(heading, lineStart, lineEnd, "select");
|
|
57
|
+
element.focus();
|
|
58
|
+
processAfterRender(vditor);
|
|
151
59
|
};
|
|
152
60
|
|
|
153
61
|
export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: string, suffix: string) => {
|
|
154
|
-
const
|
|
62
|
+
const element = vditor.sv.element;
|
|
155
63
|
const commandName = actionBtn.getAttribute("data-type");
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (!blockElement) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (commandName === "link") {
|
|
168
|
-
let html;
|
|
169
|
-
if (range.toString() === "") {
|
|
170
|
-
html = `${prefix}${Lute.Caret}${suffix}`;
|
|
171
|
-
} else {
|
|
172
|
-
html = `${prefix}${range.toString()}${suffix.replace(")", Lute.Caret + ")")}`;
|
|
173
|
-
}
|
|
174
|
-
document.execCommand("insertHTML", false, html);
|
|
175
|
-
return;
|
|
176
|
-
} else if (commandName === "italic" || commandName === "bold" || commandName === "strike" ||
|
|
177
|
-
commandName === "inline-code" || commandName === "code" || commandName === "table" || commandName === "line") {
|
|
178
|
-
let html;
|
|
179
|
-
// https://github.com/Vanessa219/vditor/issues/563 代码块不需要后面的 ```
|
|
180
|
-
if (range.toString() === "") {
|
|
181
|
-
html = `${prefix}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
|
|
182
|
-
} else {
|
|
183
|
-
html = `${prefix}${range.toString()}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
|
|
184
|
-
}
|
|
185
|
-
if (commandName === "table" || (commandName === "code" && spanElement && spanElement.textContent !== "")) {
|
|
186
|
-
html = "\n\n" + html;
|
|
187
|
-
} else if (commandName === "line") {
|
|
188
|
-
html = `\n\n${prefix}\n${Lute.Caret}`;
|
|
189
|
-
}
|
|
190
|
-
document.execCommand("insertHTML", false, html);
|
|
191
|
-
return;
|
|
192
|
-
} else if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
|
|
64
|
+
const start = element.selectionStart;
|
|
65
|
+
const end = element.selectionEnd;
|
|
66
|
+
const selectedText = element.value.substring(start, end);
|
|
67
|
+
let text = "";
|
|
68
|
+
let selectionStart = start + prefix.length;
|
|
69
|
+
let selectionEnd = selectionStart + selectedText.length;
|
|
70
|
+
|
|
71
|
+
if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
|
|
193
72
|
commandName === "quote") {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
73
|
+
const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
|
|
74
|
+
const lineEndIndex = element.value.indexOf("\n", end);
|
|
75
|
+
const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
|
|
76
|
+
const lines = element.value.substring(lineStart, lineEnd);
|
|
77
|
+
text = lines.split("\n").map((line) => prefix + line).join("\n");
|
|
78
|
+
element.setRangeText(text, lineStart, lineEnd, "select");
|
|
79
|
+
} else if (commandName === "line") {
|
|
80
|
+
text = `${start === 0 ? "" : "\n\n"}${prefix}\n`;
|
|
81
|
+
element.setRangeText(text, start, end, "end");
|
|
82
|
+
} else if (commandName === "code") {
|
|
83
|
+
text = `${prefix}\n${selectedText}${suffix}`;
|
|
84
|
+
element.setRangeText(text, start, end, "end");
|
|
85
|
+
selectionStart = start + prefix.length + 1;
|
|
86
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
87
|
+
element.setSelectionRange(selectionStart, selectionEnd);
|
|
88
|
+
} else if (commandName === "table") {
|
|
89
|
+
text = `${start === 0 ? "" : "\n\n"}${prefix}${suffix}`;
|
|
90
|
+
element.setRangeText(text, start, end, "end");
|
|
91
|
+
} else {
|
|
92
|
+
text = `${prefix}${selectedText}${suffix}`;
|
|
93
|
+
element.setRangeText(text, start, end, "end");
|
|
94
|
+
element.setSelectionRange(selectionStart, selectionEnd);
|
|
212
95
|
}
|
|
213
|
-
|
|
96
|
+
|
|
97
|
+
element.focus();
|
|
214
98
|
processAfterRender(vditor);
|
|
215
99
|
};
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import {isCtrl} from "../util/compatibility";
|
|
2
|
-
import {
|
|
3
|
-
import {hasClosestByAttribute} from "../util/hasClosest";
|
|
4
|
-
import {hasClosestByTag} from "../util/hasClosestByHeadings";
|
|
5
|
-
import {getEditorRange, getSelectPosition} from "../util/selection";
|
|
6
|
-
import {inputEvent} from "./inputEvent";
|
|
7
|
-
import {processAfterRender, processPreviousMarkers} from "./process";
|
|
2
|
+
import {processAfterRender} from "./process";
|
|
8
3
|
|
|
9
4
|
export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
|
|
10
5
|
vditor.sv.composingLock = event.isComposing;
|
|
@@ -16,187 +11,25 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
|
|
|
16
11
|
event.key !== "Shift" && event.key !== "CapsLock" && event.key !== "Escape" && !/^F\d{1,2}$/.test(event.key)) {
|
|
17
12
|
vditor.undo.recordFirstPosition(vditor, event);
|
|
18
13
|
}
|
|
19
|
-
// 仅处理以下快捷键操作
|
|
20
|
-
if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
|
|
21
|
-
&& !isCtrl(event) && event.key !== "Escape") {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
const range = getEditorRange(vditor);
|
|
25
|
-
let startContainer = range.startContainer;
|
|
26
|
-
if (range.startContainer.nodeType !== 3 && (range.startContainer as HTMLElement).tagName === "DIV") {
|
|
27
|
-
startContainer = range.startContainer.childNodes[range.startOffset - 1];
|
|
28
|
-
}
|
|
29
|
-
const textElement = hasClosestByAttribute(startContainer, "data-type", "text");
|
|
30
|
-
|
|
31
|
-
// blockquote
|
|
32
|
-
let blockquoteMarkerElement = hasClosestByAttribute(startContainer, "data-type", "blockquote-marker");
|
|
33
|
-
if (!blockquoteMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
|
|
34
|
-
textElement.previousElementSibling.getAttribute("data-type") === "blockquote-marker") {
|
|
35
|
-
blockquoteMarkerElement = textElement.previousElementSibling as HTMLElement;
|
|
36
|
-
}
|
|
37
|
-
// 回车逐个删除 blockquote marker 标记
|
|
38
|
-
if (blockquoteMarkerElement) {
|
|
39
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey &&
|
|
40
|
-
blockquoteMarkerElement.nextElementSibling.textContent.trim() === "" &&
|
|
41
|
-
getSelectPosition(blockquoteMarkerElement, vditor.sv.element, range).start ===
|
|
42
|
-
blockquoteMarkerElement.textContent.length) {
|
|
43
|
-
if (blockquoteMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
|
|
44
|
-
// 列表中存在多行 BQ 时,标记回车需跳出列表
|
|
45
|
-
blockquoteMarkerElement.previousElementSibling.setAttribute("data-action", "enter-remove");
|
|
46
|
-
}
|
|
47
|
-
blockquoteMarkerElement.remove();
|
|
48
|
-
processAfterRender(vditor);
|
|
49
|
-
event.preventDefault();
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
14
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const taskMarkerElement = hasClosestByAttribute(startContainer, "data-type", "task-marker") as HTMLElement;
|
|
57
|
-
let listLastMarkerElement = listMarkerElement;
|
|
58
|
-
if (!listLastMarkerElement) {
|
|
59
|
-
if (taskMarkerElement && taskMarkerElement.nextElementSibling.getAttribute("data-type") !== "task-marker") {
|
|
60
|
-
listLastMarkerElement = taskMarkerElement;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!listLastMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
|
|
64
|
-
(textElement.previousElementSibling.getAttribute("data-type") === "li-marker" ||
|
|
65
|
-
textElement.previousElementSibling.getAttribute("data-type") === "task-marker")) {
|
|
66
|
-
listLastMarkerElement = textElement.previousElementSibling as HTMLElement;
|
|
67
|
-
}
|
|
68
|
-
if (listLastMarkerElement) {
|
|
69
|
-
const startIndex = getSelectPosition(listLastMarkerElement, vditor.sv.element, range).start;
|
|
70
|
-
const isTask = listLastMarkerElement.getAttribute("data-type") === "task-marker";
|
|
71
|
-
let listFirstMarkerElement = listLastMarkerElement;
|
|
72
|
-
if (isTask) {
|
|
73
|
-
listFirstMarkerElement = listLastMarkerElement.previousElementSibling.previousElementSibling
|
|
74
|
-
.previousElementSibling as HTMLElement;
|
|
75
|
-
}
|
|
76
|
-
if (startIndex === listLastMarkerElement.textContent.length) {
|
|
77
|
-
// 回车清空列表标记符
|
|
78
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey &&
|
|
79
|
-
listLastMarkerElement.nextElementSibling.textContent.trim() === "") {
|
|
80
|
-
if (listFirstMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
|
|
81
|
-
listFirstMarkerElement.previousElementSibling.remove();
|
|
82
|
-
inputEvent(vditor);
|
|
83
|
-
} else {
|
|
84
|
-
if (isTask) {
|
|
85
|
-
listFirstMarkerElement.remove();
|
|
86
|
-
listLastMarkerElement.previousElementSibling.previousElementSibling.remove();
|
|
87
|
-
listLastMarkerElement.previousElementSibling.remove();
|
|
88
|
-
}
|
|
89
|
-
listLastMarkerElement.nextElementSibling.remove();
|
|
90
|
-
listLastMarkerElement.remove();
|
|
91
|
-
processAfterRender(vditor);
|
|
92
|
-
}
|
|
93
|
-
event.preventDefault();
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
// 第一个 marker 后 tab 进行缩进
|
|
97
|
-
if (event.key === "Tab") {
|
|
98
|
-
if (event.shiftKey) {
|
|
99
|
-
if (listFirstMarkerElement.previousElementSibling.getAttribute("data-type") === "padding") {
|
|
100
|
-
listFirstMarkerElement.previousElementSibling.remove();
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
listFirstMarkerElement.insertAdjacentHTML("beforebegin",
|
|
104
|
-
`<span data-type="padding">${listFirstMarkerElement.textContent.replace(/\S/g, " ")}</span>`);
|
|
105
|
-
}
|
|
106
|
-
if (/^\d/.test(listFirstMarkerElement.textContent)) {
|
|
107
|
-
listFirstMarkerElement.textContent = listFirstMarkerElement.textContent.replace(/^\d{1,}/, "1");
|
|
108
|
-
range.selectNodeContents(listLastMarkerElement.firstChild);
|
|
109
|
-
range.collapse(false);
|
|
110
|
-
}
|
|
111
|
-
inputEvent(vditor);
|
|
112
|
-
event.preventDefault();
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// tab
|
|
119
|
-
if (fixTab(vditor, range, event)) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
|
|
124
|
-
const spanElement = hasClosestByTag(startContainer, "SPAN");
|
|
125
|
-
// 回车
|
|
126
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey && blockElement) {
|
|
127
|
-
let isFirst = false;
|
|
128
|
-
const newLineMatch = blockElement.textContent.match(/^\n+/);
|
|
129
|
-
if (getSelectPosition(blockElement, vditor.sv.element).start <= (newLineMatch ? newLineMatch[0].length : 0)) {
|
|
130
|
-
// 允许段落开始换行
|
|
131
|
-
isFirst = true;
|
|
132
|
-
}
|
|
133
|
-
let newLineText = "\n";
|
|
134
|
-
if (spanElement) {
|
|
135
|
-
if (spanElement.previousElementSibling?.getAttribute("data-action") === "enter-remove") {
|
|
136
|
-
// https://github.com/Vanessa219/vditor/issues/596
|
|
137
|
-
spanElement.previousElementSibling.remove();
|
|
138
|
-
processAfterRender(vditor);
|
|
139
|
-
event.preventDefault();
|
|
140
|
-
return true;
|
|
141
|
-
} else {
|
|
142
|
-
newLineText += processPreviousMarkers(spanElement);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
range.insertNode(document.createTextNode(newLineText));
|
|
146
|
-
range.collapse(false);
|
|
147
|
-
if (blockElement && blockElement.textContent.trim() !== "" && !isFirst) {
|
|
148
|
-
inputEvent(vditor);
|
|
149
|
-
} else {
|
|
150
|
-
processAfterRender(vditor);
|
|
151
|
-
}
|
|
152
|
-
event.preventDefault();
|
|
153
|
-
return true;
|
|
15
|
+
if (event.key !== "Tab" || isCtrl(event) || event.altKey) {
|
|
16
|
+
return false;
|
|
154
17
|
}
|
|
155
18
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
// 每一段第一个字符前
|
|
174
|
-
if (blockElement && getSelectPosition(blockElement, vditor.sv.element, range).start === 0 &&
|
|
175
|
-
blockElement.previousElementSibling) {
|
|
176
|
-
range.extractContents();
|
|
177
|
-
let previousLastElement = blockElement.previousElementSibling.lastElementChild;
|
|
178
|
-
if (previousLastElement.getAttribute("data-type") === "newline") {
|
|
179
|
-
previousLastElement.remove();
|
|
180
|
-
previousLastElement = blockElement.previousElementSibling.lastElementChild;
|
|
181
|
-
}
|
|
182
|
-
// 场景:末尾无法删除 [```\ntext\n```\n\n]
|
|
183
|
-
if (previousLastElement.getAttribute("data-type") !== "newline") {
|
|
184
|
-
previousLastElement.insertAdjacentHTML("afterend", blockElement.innerHTML);
|
|
185
|
-
blockElement.remove();
|
|
186
|
-
}
|
|
187
|
-
if (blockElement.textContent.trim() !== "" && !blockElement.previousElementSibling?.querySelector('[data-type="code-block-open-marker"]')) {
|
|
188
|
-
inputEvent(vditor);
|
|
189
|
-
} else {
|
|
190
|
-
if (previousLastElement.getAttribute("data-type") !== "newline") {
|
|
191
|
-
// https://github.com/Vanessa219/vditor/issues/597
|
|
192
|
-
range.selectNodeContents(previousLastElement.lastChild);
|
|
193
|
-
range.collapse(false);
|
|
194
|
-
}
|
|
195
|
-
processAfterRender(vditor);
|
|
196
|
-
}
|
|
197
|
-
event.preventDefault();
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return false;
|
|
19
|
+
const element = vditor.sv.element;
|
|
20
|
+
const start = element.selectionStart;
|
|
21
|
+
const end = element.selectionEnd;
|
|
22
|
+
if (event.shiftKey) {
|
|
23
|
+
const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
|
|
24
|
+
const indentation = element.value.substring(lineStart, start).match(/^(\t| {1,4})/);
|
|
25
|
+
if (indentation) {
|
|
26
|
+
element.setRangeText("", lineStart, lineStart + indentation[0].length, "end");
|
|
27
|
+
element.setSelectionRange(start - indentation[0].length, end - indentation[0].length);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
element.setRangeText(vditor.options.tab, start, end, "end");
|
|
31
|
+
}
|
|
32
|
+
processAfterRender(vditor);
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
return true;
|
|
202
35
|
};
|
|
@@ -2,7 +2,7 @@ import {Constants} from "../constants";
|
|
|
2
2
|
import {processAfterRender} from "../ir/process";
|
|
3
3
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
4
4
|
import {mathRender} from "../markdown/mathRender";
|
|
5
|
-
import {processAfterRender as processSVAfterRender
|
|
5
|
+
import {processAfterRender as processSVAfterRender} from "../sv/process";
|
|
6
6
|
import {setPadding, setTypewriterPosition} from "../ui/initUI";
|
|
7
7
|
import {getEventName, updateHotkeyTip} from "../util/compatibility";
|
|
8
8
|
import {highlightToolbar} from "../util/highlightToolbar";
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
removeCurrentToolbar,
|
|
19
19
|
showToolbar, toggleSubMenu,
|
|
20
20
|
} from "./setToolbar";
|
|
21
|
-
import {combineFootnote} from "../sv/combineFootnote";
|
|
22
21
|
|
|
23
22
|
export const setEditMode = (vditor: IVditor, type: string, event: Event | string) => {
|
|
24
23
|
let markdownText;
|
|
@@ -114,16 +113,10 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
|
|
|
114
113
|
|
|
115
114
|
vditor.lute.SetVditorIR(false);
|
|
116
115
|
vditor.lute.SetVditorWYSIWYG(false);
|
|
117
|
-
vditor.lute.SetVditorSV(
|
|
116
|
+
vditor.lute.SetVditorSV(false);
|
|
118
117
|
|
|
119
118
|
vditor.currentMode = "sv";
|
|
120
|
-
|
|
121
|
-
if (svHTML === "<div data-block='0'></div>") {
|
|
122
|
-
// https://github.com/Vanessa219/vditor/issues/654 SV 模式 Placeholder 显示问题
|
|
123
|
-
svHTML = "";
|
|
124
|
-
}
|
|
125
|
-
vditor.sv.element.innerHTML = svHTML;
|
|
126
|
-
combineFootnote(vditor.sv.element)
|
|
119
|
+
vditor.sv.element.value = markdownText;
|
|
127
120
|
processSVAfterRender(vditor, {
|
|
128
121
|
enableAddUndoStack: true,
|
|
129
122
|
enableHint: false,
|
package/src/ts/toolbar/Emoji.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {MenuItem} from "./MenuItem";
|
|
|
6
6
|
import {toggleSubMenu} from "./setToolbar";
|
|
7
7
|
import {hasClosestBlock} from "../util/hasClosest";
|
|
8
8
|
import {modifyPre} from "../wysiwyg/inlineTag";
|
|
9
|
+
import {processPaste} from "../sv/process";
|
|
9
10
|
|
|
10
11
|
export class Emoji extends MenuItem {
|
|
11
12
|
public element: HTMLElement;
|
|
@@ -44,6 +45,12 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
|
|
|
44
45
|
if (btnElement) {
|
|
45
46
|
event.preventDefault();
|
|
46
47
|
const value = btnElement.getAttribute("data-value");
|
|
48
|
+
if (vditor.currentMode === "sv") {
|
|
49
|
+
processPaste(vditor, value);
|
|
50
|
+
(this.element.lastElementChild as HTMLElement).style.display = "none";
|
|
51
|
+
execAfterRender(vditor);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
47
54
|
const range = getEditorRange(vditor);
|
|
48
55
|
let html = value;
|
|
49
56
|
if (vditor.currentMode === "wysiwyg") {
|
|
@@ -51,7 +58,7 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
|
|
|
51
58
|
} else if (vditor.currentMode === "ir") {
|
|
52
59
|
html = vditor.lute.SpinVditorIRDOM(value);
|
|
53
60
|
}
|
|
54
|
-
if (value.indexOf(":") > -1
|
|
61
|
+
if (value.indexOf(":") > -1) {
|
|
55
62
|
const tempElement = document.createElement("div");
|
|
56
63
|
tempElement.innerHTML = html;
|
|
57
64
|
html = tempElement.firstElementChild.firstElementChild.outerHTML + " ";
|
package/src/ts/toolbar/Record.ts
CHANGED
|
@@ -34,7 +34,11 @@ export class Record extends MenuItem {
|
|
|
34
34
|
};
|
|
35
35
|
mediaRecorder.startRecordingNewWavFile();
|
|
36
36
|
vditor.tip.show(window.VditorI18n.recording);
|
|
37
|
-
|
|
37
|
+
if (vditor.currentMode === "sv") {
|
|
38
|
+
vditor.sv.element.disabled = true;
|
|
39
|
+
} else {
|
|
40
|
+
editorElement.setAttribute("contenteditable", "false");
|
|
41
|
+
}
|
|
38
42
|
this.element.children[0].classList.add("vditor-menu--current");
|
|
39
43
|
}).catch(() => {
|
|
40
44
|
vditor.tip.show(window.VditorI18n["record-tip"]);
|
|
@@ -51,7 +55,11 @@ export class Record extends MenuItem {
|
|
|
51
55
|
this.element.children[0].classList.remove("vditor-menu--current");
|
|
52
56
|
} else {
|
|
53
57
|
vditor.tip.show(window.VditorI18n.recording);
|
|
54
|
-
|
|
58
|
+
if (vditor.currentMode === "sv") {
|
|
59
|
+
vditor.sv.element.disabled = true;
|
|
60
|
+
} else {
|
|
61
|
+
editorElement.setAttribute("contenteditable", "false");
|
|
62
|
+
}
|
|
55
63
|
mediaRecorder.startRecordingNewWavFile();
|
|
56
64
|
this.element.children[0].classList.add("vditor-menu--current");
|
|
57
65
|
}
|
package/src/ts/toolbar/index.ts
CHANGED
|
@@ -181,6 +181,9 @@ export class Toolbar {
|
|
|
181
181
|
if (!menuItemObj) {
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
184
|
+
menuItemObj.element.querySelectorAll("button").forEach((button) => {
|
|
185
|
+
button.setAttribute("type", "button");
|
|
186
|
+
});
|
|
184
187
|
let key = menuItem.name;
|
|
185
188
|
if (key === "br" || key === "|") {
|
|
186
189
|
key = key + index;
|
package/src/ts/ui/initUI.ts
CHANGED
|
@@ -146,9 +146,13 @@ export const setTypewriterPosition = (vditor: IVditor) => {
|
|
|
146
146
|
if (vditor.element.classList.contains("vditor--fullscreen")) {
|
|
147
147
|
height = window.innerHeight;
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
vditor
|
|
151
|
-
|
|
149
|
+
const editorBottom = ((height - vditor.toolbar.element.offsetHeight) / 2) + "px";
|
|
150
|
+
if (vditor.currentMode === "sv") {
|
|
151
|
+
vditor.sv.element.style.paddingBottom = editorBottom;
|
|
152
|
+
} else {
|
|
153
|
+
// 由于 Firefox padding-bottom bug,只能使用 :after
|
|
154
|
+
vditor[vditor.currentMode].element.style.setProperty("--editor-bottom", editorBottom);
|
|
155
|
+
}
|
|
152
156
|
};
|
|
153
157
|
|
|
154
158
|
let resizeCb: () => void;
|