vditor 3.11.1 → 3.11.3
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 +13 -2
- 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 +133 -4
- package/dist/index.js +715 -57
- package/dist/index.min.js +1 -1
- package/dist/js/i18n/es_ES.js +86 -0
- package/dist/js/i18n/ja_JP.js +2 -2
- package/dist/js/i18n/vi_VN.js +86 -0
- package/dist/js/i18n/zh_TW.js +8 -8
- package/dist/js/icons/ant.js +1 -1
- package/dist/js/icons/material.js +1 -1
- package/dist/js/lute/lute.min.js +87 -71
- 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 +319 -8
- 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/util/fixBrowserBehavior.d.ts +1 -0
- package/dist/types/index.d.ts +47 -3
- package/package.json +7 -4
- package/src/assets/less/_reset.less +103 -2
- package/src/assets/less/index.less +20 -0
- package/src/index.ts +4 -1
- package/src/method.ts +3 -0
- package/src/ts/constants.ts +5 -1
- package/src/ts/export/index.ts +2 -0
- 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/imageCaptionRender.ts +175 -0
- package/src/ts/markdown/mermaidRender.ts +1 -1
- package/src/ts/markdown/previewRender.ts +8 -2
- 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/util/Options.ts +1 -1
- package/src/ts/util/fixBrowserBehavior.ts +165 -21
- package/src/ts/util/processCode.ts +3 -0
- package/src/ts/util/selection.ts +7 -2
- package/src/ts/wysiwyg/afterRenderEvent.ts +2 -0
- package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +23 -4
- package/src/ts/wysiwyg/input.ts +5 -0
- package/src/ts/wysiwyg/setHeading.ts +110 -6
|
@@ -70,6 +70,35 @@
|
|
|
70
70
|
vertical-align: sub;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
figure.vditor-image {
|
|
74
|
+
margin: 0 0 16px;
|
|
75
|
+
text-align: center;
|
|
76
|
+
|
|
77
|
+
figcaption {
|
|
78
|
+
font-size: 0.875em;
|
|
79
|
+
margin-top: 4px;
|
|
80
|
+
opacity: 0.65;
|
|
81
|
+
white-space: pre-wrap;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
span.vditor-image[data-image-caption] {
|
|
86
|
+
display: inline-block;
|
|
87
|
+
max-width: 100%;
|
|
88
|
+
text-align: center;
|
|
89
|
+
vertical-align: top;
|
|
90
|
+
|
|
91
|
+
&::after {
|
|
92
|
+
content: attr(data-image-caption);
|
|
93
|
+
display: block;
|
|
94
|
+
font-size: 0.875em;
|
|
95
|
+
line-height: 1.5;
|
|
96
|
+
margin-top: 4px;
|
|
97
|
+
opacity: 0.65;
|
|
98
|
+
white-space: pre-wrap;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
73
102
|
h1,
|
|
74
103
|
h2,
|
|
75
104
|
h3,
|
|
@@ -235,7 +264,8 @@
|
|
|
235
264
|
.language-markmap,
|
|
236
265
|
.language-abc,
|
|
237
266
|
.language-flowchart,
|
|
238
|
-
.language-graphviz
|
|
267
|
+
.language-graphviz,
|
|
268
|
+
.language-wavedrom {
|
|
239
269
|
margin-bottom: 16px;
|
|
240
270
|
}
|
|
241
271
|
|
|
@@ -261,7 +291,8 @@
|
|
|
261
291
|
.language-mermaid,
|
|
262
292
|
.language-markmap,
|
|
263
293
|
.language-flowchart,
|
|
264
|
-
.language-graphviz
|
|
294
|
+
.language-graphviz,
|
|
295
|
+
.language-wavedrom {
|
|
265
296
|
text-align: center;
|
|
266
297
|
}
|
|
267
298
|
|
|
@@ -617,3 +648,73 @@
|
|
|
617
648
|
}
|
|
618
649
|
}
|
|
619
650
|
}
|
|
651
|
+
|
|
652
|
+
.vditor-reset .callout,
|
|
653
|
+
.vditor-wysiwyg blockquote.callout,
|
|
654
|
+
.vditor-ir blockquote.callout {
|
|
655
|
+
--callout-color: var(--callout-note-color, #0969da);
|
|
656
|
+
--callout-background-color: var(--callout-note-background-color, rgba(9, 105, 218, 0.08));
|
|
657
|
+
position: relative;
|
|
658
|
+
margin: 0 0 16px;
|
|
659
|
+
padding: 0.75em 1em;
|
|
660
|
+
color: inherit;
|
|
661
|
+
background-color: var(--callout-background-color);
|
|
662
|
+
border: 0;
|
|
663
|
+
border-left: 0.25em solid var(--callout-color);
|
|
664
|
+
|
|
665
|
+
&[data-subtype="TIP"] {
|
|
666
|
+
--callout-color: var(--callout-tip-color, #1a7f37);
|
|
667
|
+
--callout-background-color: var(--callout-tip-background-color, rgba(26, 127, 55, 0.08));
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
&[data-subtype="IMPORTANT"] {
|
|
671
|
+
--callout-color: var(--callout-important-color, #8250df);
|
|
672
|
+
--callout-background-color: var(--callout-important-background-color, rgba(130, 80, 223, 0.08));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
&[data-subtype="WARNING"] {
|
|
676
|
+
--callout-color: var(--callout-warning-color, #9a6700);
|
|
677
|
+
--callout-background-color: var(--callout-warning-background-color, rgba(154, 103, 0, 0.08));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
&[data-subtype="CAUTION"] {
|
|
681
|
+
--callout-color: var(--callout-caution-color, #cf222e);
|
|
682
|
+
--callout-background-color: var(--callout-caution-background-color, rgba(207, 34, 46, 0.08));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
> .callout-info {
|
|
686
|
+
display: flex;
|
|
687
|
+
align-items: center;
|
|
688
|
+
min-height: 1.5em;
|
|
689
|
+
margin: 0 0 0.5em;
|
|
690
|
+
color: var(--callout-color);
|
|
691
|
+
font-weight: 600;
|
|
692
|
+
|
|
693
|
+
.callout-icon {
|
|
694
|
+
display: inline-flex;
|
|
695
|
+
align-items: center;
|
|
696
|
+
margin-right: 0.35em;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.callout-title {
|
|
700
|
+
min-width: 1px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.callout-img,
|
|
704
|
+
> .callout-img {
|
|
705
|
+
width: 1.2em;
|
|
706
|
+
height: 1.2em;
|
|
707
|
+
margin-right: 0.35em;
|
|
708
|
+
object-fit: contain;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
> .callout-content > :last-child,
|
|
713
|
+
> :last-child {
|
|
714
|
+
margin-bottom: 0;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.vditor-wysiwyg__callout-marker {
|
|
719
|
+
display: none;
|
|
720
|
+
}
|
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
--count-background-color: rgba(27, 31, 35, 0.05);
|
|
38
38
|
--heading-border-color: #eaecef;
|
|
39
39
|
--blockquote-color: #6a737d;
|
|
40
|
+
--callout-note-color: #0969da;
|
|
41
|
+
--callout-note-background-color: rgba(9, 105, 218, 0.08);
|
|
42
|
+
--callout-tip-color: #1a7f37;
|
|
43
|
+
--callout-tip-background-color: rgba(26, 127, 55, 0.08);
|
|
44
|
+
--callout-important-color: #8250df;
|
|
45
|
+
--callout-important-background-color: rgba(130, 80, 223, 0.08);
|
|
46
|
+
--callout-warning-color: #9a6700;
|
|
47
|
+
--callout-warning-background-color: rgba(154, 103, 0, 0.08);
|
|
48
|
+
--callout-caution-color: #cf222e;
|
|
49
|
+
--callout-caution-background-color: rgba(207, 34, 46, 0.08);
|
|
40
50
|
|
|
41
51
|
--ir-heading-color: #660e7a;
|
|
42
52
|
--ir-title-color: #808080;
|
|
@@ -67,6 +77,16 @@
|
|
|
67
77
|
--count-background-color: rgba(@blurColor, 0.36);
|
|
68
78
|
--heading-border-color: var(--textarea-text-color);
|
|
69
79
|
--blockquote-color: var(--toolbar-icon-color);
|
|
80
|
+
--callout-note-color: #58a6ff;
|
|
81
|
+
--callout-note-background-color: rgba(56, 139, 253, 0.15);
|
|
82
|
+
--callout-tip-color: #3fb950;
|
|
83
|
+
--callout-tip-background-color: rgba(46, 160, 67, 0.15);
|
|
84
|
+
--callout-important-color: #a371f7;
|
|
85
|
+
--callout-important-background-color: rgba(163, 113, 247, 0.15);
|
|
86
|
+
--callout-warning-color: #d29922;
|
|
87
|
+
--callout-warning-background-color: rgba(187, 128, 9, 0.15);
|
|
88
|
+
--callout-caution-color: #f85149;
|
|
89
|
+
--callout-caution-background-color: rgba(248, 81, 73, 0.15);
|
|
70
90
|
|
|
71
91
|
--ir-heading-color: #9876aa;
|
|
72
92
|
--ir-title-color: #808080;
|
package/src/index.ts
CHANGED
|
@@ -75,7 +75,7 @@ class Vditor extends VditorMethod {
|
|
|
75
75
|
|
|
76
76
|
// 支持自定义国际化
|
|
77
77
|
if (!mergedOptions.i18n) {
|
|
78
|
-
if (!["de_DE", "en_US", "
|
|
78
|
+
if (!["de_DE", "en_US", "es_ES", "fr_FR", "ja_JP", "ko_KR", "pt_BR", "ru_RU", "sv_SE", "vi_VN", "zh_CN", "zh_TW"].includes(mergedOptions.lang)) {
|
|
79
79
|
throw new Error(
|
|
80
80
|
"options.lang error, see https://ld246.com/article/1549638745630#options",
|
|
81
81
|
);
|
|
@@ -525,6 +525,7 @@ class Vditor extends VditorMethod {
|
|
|
525
525
|
).then(() => {
|
|
526
526
|
this.vditor.lute = setLute({
|
|
527
527
|
autoSpace: this.vditor.options.preview.markdown.autoSpace,
|
|
528
|
+
callout: this.vditor.options.preview.markdown.callout,
|
|
528
529
|
gfmAutoLink: this.vditor.options.preview.markdown.gfmAutoLink,
|
|
529
530
|
codeBlockPreview: this.vditor.options.preview.markdown
|
|
530
531
|
.codeBlockPreview,
|
|
@@ -543,6 +544,8 @@ class Vditor extends VditorMethod {
|
|
|
543
544
|
paragraphBeginningSpace: this.vditor.options.preview.markdown
|
|
544
545
|
.paragraphBeginningSpace,
|
|
545
546
|
sanitize: this.vditor.options.preview.markdown.sanitize,
|
|
547
|
+
sub: this.vditor.options.preview.markdown.sub,
|
|
548
|
+
sup: this.vditor.options.preview.markdown.sup,
|
|
546
549
|
toc: this.vditor.options.preview.markdown.toc,
|
|
547
550
|
});
|
|
548
551
|
|
package/src/method.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {markmapRender} from "./ts/markdown/markmapRender";
|
|
|
14
14
|
import {mindmapRender} from "./ts/markdown/mindmapRender";
|
|
15
15
|
import {outlineRender} from "./ts/markdown/outlineRender";
|
|
16
16
|
import {plantumlRender} from "./ts/markdown/plantumlRender";
|
|
17
|
+
import {wavedromRender} from "./ts/markdown/wavedromRender";
|
|
17
18
|
import {md2html, previewRender} from "./ts/markdown/previewRender";
|
|
18
19
|
import {speechRender} from "./ts/markdown/speechRender";
|
|
19
20
|
import {previewImage} from "./ts/preview/image";
|
|
@@ -30,6 +31,8 @@ class Vditor {
|
|
|
30
31
|
public static codeRender = codeRender;
|
|
31
32
|
/** 对 graphviz 进行渲染 */
|
|
32
33
|
public static graphvizRender = graphvizRender;
|
|
34
|
+
/** 对数字波形图进行渲染 */
|
|
35
|
+
public static wavedromRender = wavedromRender;
|
|
33
36
|
/** 为 element 中的代码块进行高亮渲染 */
|
|
34
37
|
public static highlightRender = highlightRender;
|
|
35
38
|
/** 对数学公式进行渲染 */
|
package/src/ts/constants.ts
CHANGED
|
@@ -44,17 +44,19 @@ export abstract class Constants {
|
|
|
44
44
|
"stackoverflow-light", "tokyo-night-light", "vs", "xcode", "default"];
|
|
45
45
|
public static readonly ALIAS_CODE_LANGUAGES: string[] = [
|
|
46
46
|
// 自定义
|
|
47
|
-
"abc", "plantuml", "mermaid", "flowchart", "echarts", "mindmap", "graphviz", "math", "markmap", "smiles",
|
|
47
|
+
"abc", "plantuml", "mermaid", "flowchart", "echarts", "mindmap", "graphviz", "wavedrom", "math", "markmap", "smiles",
|
|
48
48
|
// 别名
|
|
49
49
|
"js", "ts", "html", "toml", "c#", "bat"
|
|
50
50
|
];
|
|
51
51
|
public static readonly CDN = `https://unpkg.com/vditor@${VDITOR_VERSION}`;
|
|
52
52
|
public static readonly MARKDOWN_OPTIONS = {
|
|
53
53
|
autoSpace: false,
|
|
54
|
+
callout: true,
|
|
54
55
|
gfmAutoLink: true,
|
|
55
56
|
codeBlockPreview: true,
|
|
56
57
|
fixTermTypo: false,
|
|
57
58
|
footnotes: true,
|
|
59
|
+
imageCaption: false,
|
|
58
60
|
linkBase: "",
|
|
59
61
|
linkPrefix: "",
|
|
60
62
|
listStyle: false,
|
|
@@ -62,6 +64,8 @@ export abstract class Constants {
|
|
|
62
64
|
mathBlockPreview: true,
|
|
63
65
|
paragraphBeginningSpace: false,
|
|
64
66
|
sanitize: true,
|
|
67
|
+
sub: false,
|
|
68
|
+
sup: false,
|
|
65
69
|
toc: false,
|
|
66
70
|
};
|
|
67
71
|
public static readonly HLJS_OPTIONS = {
|
package/src/ts/export/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ window.addEventListener("message", (e) => {
|
|
|
36
36
|
Vditor.preview(document.getElementById('preview'), e.data, {
|
|
37
37
|
cdn: "${vditor.options.cdn}",
|
|
38
38
|
markdown: {
|
|
39
|
+
callout: ${JSON.stringify(vditor.options.preview.markdown.callout)},
|
|
39
40
|
theme: ${JSON.stringify(vditor.options.preview.theme)}
|
|
40
41
|
},
|
|
41
42
|
hljs: {
|
|
@@ -73,6 +74,7 @@ export const exportHTML = (vditor: IVditor) => {
|
|
|
73
74
|
Vditor.markmapRender(previewElement, '${vditor.options.cdn}');
|
|
74
75
|
Vditor.flowchartRender(previewElement, '${vditor.options.cdn}');
|
|
75
76
|
Vditor.graphvizRender(previewElement, '${vditor.options.cdn}');
|
|
77
|
+
Vditor.wavedromRender(previewElement, '${vditor.options.cdn}');
|
|
76
78
|
Vditor.chartRender(previewElement, '${vditor.options.cdn}', '${vditor.options.theme}');
|
|
77
79
|
Vditor.mindmapRender(previewElement, '${vditor.options.cdn}', '${vditor.options.theme}');
|
|
78
80
|
Vditor.abcRender(previewElement, '${vditor.options.cdn}');
|
package/src/ts/ir/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../util/editorCommonEvent";
|
|
12
12
|
import {paste} from "../util/fixBrowserBehavior";
|
|
13
13
|
import {hasClosestByAttribute, hasClosestByClassName} from "../util/hasClosest";
|
|
14
|
+
import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
|
|
14
15
|
import {
|
|
15
16
|
getEditorRange, setRangeByWbr,
|
|
16
17
|
setSelectionFocus,
|
|
@@ -22,6 +23,28 @@ import {input} from "./input";
|
|
|
22
23
|
import {processAfterRender, processHint} from "./process";
|
|
23
24
|
import {hidePanel} from "../toolbar/setToolbar";
|
|
24
25
|
|
|
26
|
+
const getHeadingMarker = (headingElement: Element) => {
|
|
27
|
+
return Array.from(headingElement.children).find((item) => item.getAttribute("data-type") === "heading-marker");
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const restoreHeadingMarker = (sourceHeading: HTMLElement | false, copiedHeading?: HTMLElement) => {
|
|
31
|
+
if (!sourceHeading || !copiedHeading || sourceHeading.tagName !== copiedHeading.tagName ||
|
|
32
|
+
sourceHeading.getAttribute("data-marker") !== copiedHeading.getAttribute("data-marker") ||
|
|
33
|
+
copiedHeading.textContent.trim() === "" || getHeadingMarker(copiedHeading)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const markerElement = getHeadingMarker(sourceHeading);
|
|
38
|
+
if (!markerElement) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (sourceHeading.firstElementChild === markerElement) {
|
|
42
|
+
copiedHeading.insertBefore(markerElement.cloneNode(true), copiedHeading.firstChild);
|
|
43
|
+
} else if (sourceHeading.lastElementChild === markerElement) {
|
|
44
|
+
copiedHeading.appendChild(markerElement.cloneNode(true));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
25
48
|
class IR {
|
|
26
49
|
public range: Range;
|
|
27
50
|
public element: HTMLPreElement;
|
|
@@ -61,6 +84,9 @@ class IR {
|
|
|
61
84
|
|
|
62
85
|
const tempElement = document.createElement("div");
|
|
63
86
|
tempElement.appendChild(range.cloneContents());
|
|
87
|
+
const copiedHeadings = tempElement.querySelectorAll<HTMLElement>("h1, h2, h3, h4, h5, h6");
|
|
88
|
+
restoreHeadingMarker(hasClosestByHeadings(range.startContainer), copiedHeadings.item(0));
|
|
89
|
+
restoreHeadingMarker(hasClosestByHeadings(range.endContainer), copiedHeadings.item(copiedHeadings.length - 1));
|
|
64
90
|
|
|
65
91
|
event.clipboardData.setData("text/plain", vditor.lute.VditorIRDOM2Md(tempElement.innerHTML).trim());
|
|
66
92
|
event.clipboardData.setData("text/html", "");
|
package/src/ts/ir/process.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Constants} from "../constants";
|
|
2
2
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
3
|
+
import {renderImageCaptions} from "../markdown/imageCaptionRender";
|
|
3
4
|
import {removeCurrentToolbar} from "../toolbar/setToolbar";
|
|
4
5
|
import {accessLocalStorage} from "../util/compatibility";
|
|
5
6
|
import {listToggle} from "../util/fixBrowserBehavior";
|
|
@@ -41,6 +42,7 @@ export const processAfterRender = (vditor: IVditor, options = {
|
|
|
41
42
|
enableHint: false,
|
|
42
43
|
enableInput: true,
|
|
43
44
|
}) => {
|
|
45
|
+
renderImageCaptions(vditor.ir.element, "ir", vditor.options.preview.markdown.imageCaption);
|
|
44
46
|
if (options.enableHint) {
|
|
45
47
|
processHint(vditor);
|
|
46
48
|
}
|
|
@@ -96,12 +98,25 @@ export const processHeading = (vditor: IVditor, value: string) => {
|
|
|
96
98
|
const removeInline = (range: Range, vditor: IVditor, type: string) => {
|
|
97
99
|
const inlineElement = hasClosestByAttribute(range.startContainer, "data-type", type) as HTMLElement;
|
|
98
100
|
if (inlineElement) {
|
|
101
|
+
const keepSelection = !range.collapsed;
|
|
99
102
|
inlineElement.firstElementChild.remove();
|
|
100
103
|
inlineElement.lastElementChild.remove();
|
|
104
|
+
if (keepSelection) {
|
|
105
|
+
const endRange = range.cloneRange();
|
|
106
|
+
endRange.collapse(false);
|
|
107
|
+
endRange.insertNode(document.createElement("wbr"));
|
|
108
|
+
}
|
|
101
109
|
range.insertNode(document.createElement("wbr"));
|
|
102
110
|
const tempElement = document.createElement("div");
|
|
103
111
|
tempElement.innerHTML = vditor.lute.SpinVditorIRDOM(inlineElement.outerHTML);
|
|
104
112
|
inlineElement.outerHTML = tempElement.firstElementChild.innerHTML.trim();
|
|
113
|
+
if (keepSelection) {
|
|
114
|
+
setRangeByWbr(vditor.ir.element, range);
|
|
115
|
+
const endWbrElement = vditor.ir.element.querySelector("wbr");
|
|
116
|
+
range.setEndBefore(endWbrElement);
|
|
117
|
+
endWbrElement.remove();
|
|
118
|
+
setSelectionFocus(range);
|
|
119
|
+
}
|
|
105
120
|
}
|
|
106
121
|
};
|
|
107
122
|
|
|
@@ -182,6 +197,7 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
|
|
|
182
197
|
} else if (commandName === "italic" || commandName === "bold" || commandName === "strike"
|
|
183
198
|
|| commandName === "inline-code" || commandName === "code" || commandName === "table") {
|
|
184
199
|
let html;
|
|
200
|
+
let keepSelection = false;
|
|
185
201
|
if (range.toString() === "") {
|
|
186
202
|
html = `${prefix}<wbr>${suffix}`;
|
|
187
203
|
} else {
|
|
@@ -190,7 +206,8 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
|
|
|
190
206
|
} else if (commandName === "table") {
|
|
191
207
|
html = `${prefix}${range.toString()}<wbr>${suffix}`;
|
|
192
208
|
} else {
|
|
193
|
-
html = `${prefix}
|
|
209
|
+
html = `${prefix}<wbr>${range.toString()}<wbr>${suffix}`;
|
|
210
|
+
keepSelection = true;
|
|
194
211
|
}
|
|
195
212
|
range.deleteContents();
|
|
196
213
|
}
|
|
@@ -202,6 +219,12 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
|
|
|
202
219
|
spanElement.innerHTML = html;
|
|
203
220
|
range.insertNode(spanElement);
|
|
204
221
|
input(vditor, range);
|
|
222
|
+
if (keepSelection) {
|
|
223
|
+
const endWbrElement = vditor.ir.element.querySelector("wbr");
|
|
224
|
+
range.setEndBefore(endWbrElement);
|
|
225
|
+
endWbrElement.remove();
|
|
226
|
+
setSelectionFocus(range);
|
|
227
|
+
}
|
|
205
228
|
|
|
206
229
|
if (commandName === "table") {
|
|
207
230
|
range.selectNodeContents(getSelection().getRangeAt(0).startContainer.parentElement);
|
|
@@ -31,6 +31,10 @@ export const graphvizRenderAdapter = {
|
|
|
31
31
|
getCode: (el: Element) => el.textContent,
|
|
32
32
|
getElements: (el: HTMLElement | Document) => el.querySelectorAll(".language-graphviz"),
|
|
33
33
|
};
|
|
34
|
+
export const wavedromRenderAdapter = {
|
|
35
|
+
getCode: (el: Element) => el.textContent,
|
|
36
|
+
getElements: (el: HTMLElement | Document) => el.querySelectorAll(".language-wavedrom"),
|
|
37
|
+
};
|
|
34
38
|
export const flowchartRenderAdapter = {
|
|
35
39
|
getCode: (el: Element) => el.textContent,
|
|
36
40
|
getElements: (el: HTMLElement | Document) => el.querySelectorAll(".language-flowchart"),
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import {getMarkdown} from "./getMarkdown";
|
|
2
|
+
import {renderImageCaptionHTML} from "./imageCaptionRender";
|
|
2
3
|
|
|
3
4
|
export const getHTML = (vditor: IVditor) => {
|
|
5
|
+
let html = "";
|
|
4
6
|
if (vditor.currentMode === "sv") {
|
|
5
|
-
|
|
7
|
+
html = vditor.lute.Md2HTML(getMarkdown(vditor));
|
|
6
8
|
} else if (vditor.currentMode === "wysiwyg") {
|
|
7
|
-
|
|
9
|
+
html = vditor.lute.VditorDOM2HTML(vditor.wysiwyg.element.innerHTML);
|
|
8
10
|
} else if (vditor.currentMode === "ir") {
|
|
9
|
-
|
|
11
|
+
html = vditor.lute.VditorIRDOM2HTML(vditor.ir.element.innerHTML);
|
|
10
12
|
}
|
|
13
|
+
return renderImageCaptionHTML(html, vditor.options.preview.markdown.imageCaption);
|
|
11
14
|
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const getContentNodes = (element: Element, ignoreIRMarkers = false) => {
|
|
2
|
+
return Array.from(element.childNodes).filter((node) => {
|
|
3
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
7
|
+
return node.textContent.replace(/\u200b/g, "").trim() !== "";
|
|
8
|
+
}
|
|
9
|
+
if (!(node instanceof HTMLElement)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (node.tagName === "WBR") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return !ignoreIRMarkers || !node.classList.contains("vditor-ir__marker");
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getWYSIWYGImage = (paragraph: HTMLParagraphElement) => {
|
|
20
|
+
const nodes = getContentNodes(paragraph);
|
|
21
|
+
if (nodes.length !== 1 || !(nodes[0] instanceof HTMLElement)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const container = nodes[0];
|
|
26
|
+
if (container instanceof HTMLImageElement) {
|
|
27
|
+
return {
|
|
28
|
+
container,
|
|
29
|
+
image: container,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (container instanceof HTMLAnchorElement) {
|
|
33
|
+
const linkNodes = getContentNodes(container);
|
|
34
|
+
if (linkNodes.length === 1 && linkNodes[0] instanceof HTMLImageElement) {
|
|
35
|
+
return {
|
|
36
|
+
container,
|
|
37
|
+
image: linkNodes[0],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const getIRImage = (paragraph: HTMLParagraphElement) => {
|
|
45
|
+
const nodes = getContentNodes(paragraph);
|
|
46
|
+
if (nodes.length !== 1 || !(nodes[0] instanceof HTMLElement)) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let imageNode = nodes[0];
|
|
51
|
+
if (imageNode.getAttribute("data-type") === "a") {
|
|
52
|
+
const linkNodes = getContentNodes(imageNode, true);
|
|
53
|
+
if (linkNodes.length !== 1 || !(linkNodes[0] instanceof HTMLElement)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
imageNode = linkNodes[0];
|
|
57
|
+
}
|
|
58
|
+
if (imageNode.getAttribute("data-type") !== "img") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const image = imageNode.querySelector(":scope > img");
|
|
63
|
+
const titleMarker = imageNode.querySelector<HTMLElement>(":scope > .vditor-ir__marker--title");
|
|
64
|
+
if (!(image instanceof HTMLImageElement) || !titleMarker) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const markerText = titleMarker.textContent.trim();
|
|
69
|
+
const closeMarker = markerText[0] === "(" ? ")" : markerText[0];
|
|
70
|
+
if (markerText.length < 2 || !["\"", "'", "("].includes(markerText[0]) ||
|
|
71
|
+
markerText[markerText.length - 1] !== closeMarker) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
caption: markerText.slice(1, -1).trim(),
|
|
76
|
+
container: imageNode,
|
|
77
|
+
image,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const unwrapWYSIWYGCaptions = (element: HTMLElement) => {
|
|
82
|
+
element.querySelectorAll("span.vditor-image[data-image-caption]").forEach((wrapper) => {
|
|
83
|
+
while (wrapper.firstChild) {
|
|
84
|
+
wrapper.parentNode.insertBefore(wrapper.firstChild, wrapper);
|
|
85
|
+
}
|
|
86
|
+
wrapper.remove();
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const resetIRCaptions = (element: HTMLElement) => {
|
|
91
|
+
element.querySelectorAll(".vditor-image[data-image-caption]").forEach((imageNode) => {
|
|
92
|
+
imageNode.classList.remove("vditor-image");
|
|
93
|
+
imageNode.removeAttribute("data-image-caption");
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const renderPreviewCaptions = (element: HTMLElement) => {
|
|
98
|
+
element.querySelectorAll("p").forEach((paragraph: HTMLParagraphElement) => {
|
|
99
|
+
const imageData = getWYSIWYGImage(paragraph);
|
|
100
|
+
const caption = imageData?.image.getAttribute("title")?.trim();
|
|
101
|
+
if (!imageData || !caption) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const figure = document.createElement("figure");
|
|
106
|
+
Array.from(paragraph.attributes).forEach((attribute) => {
|
|
107
|
+
figure.setAttribute(attribute.name, attribute.value);
|
|
108
|
+
});
|
|
109
|
+
figure.classList.add("vditor-image");
|
|
110
|
+
const figcaption = document.createElement("figcaption");
|
|
111
|
+
figcaption.textContent = caption;
|
|
112
|
+
figure.append(imageData.container, figcaption);
|
|
113
|
+
paragraph.replaceWith(figure);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const renderWYSIWYGCaptions = (element: HTMLElement) => {
|
|
118
|
+
element.querySelectorAll("p").forEach((paragraph: HTMLParagraphElement) => {
|
|
119
|
+
const imageData = getWYSIWYGImage(paragraph);
|
|
120
|
+
const caption = imageData?.image.getAttribute("title")?.trim();
|
|
121
|
+
if (!imageData || !caption) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const wrapper = document.createElement("span");
|
|
126
|
+
wrapper.className = "vditor-image";
|
|
127
|
+
wrapper.setAttribute("data-image-caption", caption);
|
|
128
|
+
imageData.container.replaceWith(wrapper);
|
|
129
|
+
wrapper.append(imageData.container);
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const renderIRCaptions = (element: HTMLElement) => {
|
|
134
|
+
element.querySelectorAll("p").forEach((paragraph: HTMLParagraphElement) => {
|
|
135
|
+
const imageData = getIRImage(paragraph);
|
|
136
|
+
if (!imageData?.caption) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
imageData.container.classList.add("vditor-image");
|
|
140
|
+
imageData.container.setAttribute("data-image-caption", imageData.caption);
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const renderImageCaptions = (
|
|
145
|
+
element: HTMLElement,
|
|
146
|
+
mode: "preview" | "wysiwyg" | "ir",
|
|
147
|
+
enable: boolean,
|
|
148
|
+
) => {
|
|
149
|
+
if (mode === "wysiwyg") {
|
|
150
|
+
unwrapWYSIWYGCaptions(element);
|
|
151
|
+
} else if (mode === "ir") {
|
|
152
|
+
resetIRCaptions(element);
|
|
153
|
+
}
|
|
154
|
+
if (!enable) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (mode === "preview") {
|
|
159
|
+
renderPreviewCaptions(element);
|
|
160
|
+
} else if (mode === "wysiwyg") {
|
|
161
|
+
renderWYSIWYGCaptions(element);
|
|
162
|
+
} else {
|
|
163
|
+
renderIRCaptions(element);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const renderImageCaptionHTML = (html: string, enable: boolean) => {
|
|
168
|
+
if (!enable) {
|
|
169
|
+
return html;
|
|
170
|
+
}
|
|
171
|
+
const container = document.createElement("div");
|
|
172
|
+
container.innerHTML = html;
|
|
173
|
+
renderImageCaptions(container, "preview", true);
|
|
174
|
+
return container.innerHTML;
|
|
175
|
+
};
|
|
@@ -13,7 +13,7 @@ export const mermaidRender = (element: (HTMLElement | Document) = document, cdn
|
|
|
13
13
|
if (mermaidElements.length === 0) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
addScript(`${cdn}/dist/js/mermaid/mermaid.min.js?v=11.
|
|
16
|
+
addScript(`${cdn}/dist/js/mermaid/mermaid.min.js?v=11.16.1`, "vditorMermaidScript").then(() => {
|
|
17
17
|
const config: any = {
|
|
18
18
|
securityLevel: "loose", // 升级后无 https://github.com/siyuan-note/siyuan/issues/3587,可使用该选项
|
|
19
19
|
altFontFamily: "sans-serif",
|
|
@@ -10,6 +10,7 @@ import {codeRender} from "./codeRender";
|
|
|
10
10
|
import {flowchartRender} from "./flowchartRender";
|
|
11
11
|
import {graphvizRender} from "./graphvizRender";
|
|
12
12
|
import {highlightRender} from "./highlightRender";
|
|
13
|
+
import {renderImageCaptionHTML} from "./imageCaptionRender";
|
|
13
14
|
import {lazyLoadImageRender} from "./lazyLoadImageRender";
|
|
14
15
|
import {mathRender} from "./mathRender";
|
|
15
16
|
import {mediaRender} from "./mediaRender";
|
|
@@ -20,6 +21,7 @@ import {mindmapRender} from "./mindmapRender";
|
|
|
20
21
|
import {plantumlRender} from "./plantumlRender";
|
|
21
22
|
import {setLute} from "./setLute";
|
|
22
23
|
import {speechRender} from "./speechRender";
|
|
24
|
+
import {wavedromRender} from "./wavedromRender";
|
|
23
25
|
|
|
24
26
|
const mergeOptions = (options?: IPreviewOptions) => {
|
|
25
27
|
const defaultOption: IPreviewOptions = {
|
|
@@ -59,6 +61,7 @@ export const md2html = (mdText: string, options?: IPreviewOptions) => {
|
|
|
59
61
|
return addScript(`${mergedOptions.cdn}/dist/js/lute/lute.min.js`, "vditorLuteScript").then(() => {
|
|
60
62
|
const lute = setLute({
|
|
61
63
|
autoSpace: mergedOptions.markdown.autoSpace,
|
|
64
|
+
callout: mergedOptions.markdown.callout,
|
|
62
65
|
gfmAutoLink: mergedOptions.markdown.gfmAutoLink,
|
|
63
66
|
codeBlockPreview: mergedOptions.markdown.codeBlockPreview,
|
|
64
67
|
emojiSite: mergedOptions.emojiPath,
|
|
@@ -75,6 +78,8 @@ export const md2html = (mdText: string, options?: IPreviewOptions) => {
|
|
|
75
78
|
mathBlockPreview: mergedOptions.markdown.mathBlockPreview,
|
|
76
79
|
paragraphBeginningSpace: mergedOptions.markdown.paragraphBeginningSpace,
|
|
77
80
|
sanitize: mergedOptions.markdown.sanitize,
|
|
81
|
+
sub: mergedOptions.markdown.sub,
|
|
82
|
+
sup: mergedOptions.markdown.sup,
|
|
78
83
|
toc: mergedOptions.markdown.toc,
|
|
79
84
|
});
|
|
80
85
|
if (options?.renderers) {
|
|
@@ -85,7 +90,7 @@ export const md2html = (mdText: string, options?: IPreviewOptions) => {
|
|
|
85
90
|
});
|
|
86
91
|
}
|
|
87
92
|
lute.SetHeadingID(true);
|
|
88
|
-
return lute.Md2HTML(mdText);
|
|
93
|
+
return renderImageCaptionHTML(lute.Md2HTML(mdText), mergedOptions.markdown.imageCaption);
|
|
89
94
|
});
|
|
90
95
|
};
|
|
91
96
|
|
|
@@ -99,7 +104,7 @@ export const previewRender = async (previewElement: HTMLDivElement, markdown: st
|
|
|
99
104
|
previewElement.classList.add("vditor-reset");
|
|
100
105
|
|
|
101
106
|
if (!mergedOptions.i18n) {
|
|
102
|
-
if (!["de_DE", "en_US", "
|
|
107
|
+
if (!["de_DE", "en_US", "es_ES", "fr_FR", "ja_JP", "ko_KR", "pt_BR", "ru_RU", "sv_SE", "vi_VN", "zh_CN", "zh_TW"].includes(mergedOptions.lang)) {
|
|
103
108
|
throw new Error(
|
|
104
109
|
"options.lang error, see https://ld246.com/article/1549638745630#options",
|
|
105
110
|
);
|
|
@@ -136,6 +141,7 @@ export const previewRender = async (previewElement: HTMLDivElement, markdown: st
|
|
|
136
141
|
markmapRender(previewElement, mergedOptions.cdn);
|
|
137
142
|
flowchartRender(previewElement, mergedOptions.cdn);
|
|
138
143
|
graphvizRender(previewElement, mergedOptions.cdn);
|
|
144
|
+
wavedromRender(previewElement, mergedOptions.cdn);
|
|
139
145
|
chartRender(previewElement, mergedOptions.cdn, mergedOptions.mode);
|
|
140
146
|
mindmapRender(previewElement, mergedOptions.cdn, mergedOptions.mode);
|
|
141
147
|
plantumlRender(previewElement, mergedOptions.cdn);
|
|
@@ -5,6 +5,7 @@ export const setLute = (options: ILuteOptions) => {
|
|
|
5
5
|
lute.SetHeadingAnchor(options.headingAnchor);
|
|
6
6
|
lute.SetInlineMathAllowDigitAfterOpenMarker(options.inlineMathDigit);
|
|
7
7
|
lute.SetAutoSpace(options.autoSpace);
|
|
8
|
+
lute.SetCallout(options.callout);
|
|
8
9
|
lute.SetToC(options.toc);
|
|
9
10
|
lute.SetFootnotes(options.footnotes);
|
|
10
11
|
lute.SetFixTermTypo(options.fixTermTypo);
|
|
@@ -20,5 +21,7 @@ export const setLute = (options: ILuteOptions) => {
|
|
|
20
21
|
if (options.lazyLoadImage) {
|
|
21
22
|
lute.SetImageLazyLoading(options.lazyLoadImage);
|
|
22
23
|
}
|
|
24
|
+
lute.SetSup(options.sup);
|
|
25
|
+
lute.SetSub(options.sub);
|
|
23
26
|
return lute;
|
|
24
27
|
};
|