@8btc/mditor 0.0.7 → 0.0.9
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/index.css +56 -2
- package/dist/index.d.ts +9 -0
- package/dist/index.js +3289 -2841
- package/dist/index.min.js +1 -1
- package/dist/method.d.ts +14 -10
- package/dist/method.js +527 -3
- package/dist/method.min.js +1 -1
- package/dist/ts/util/editorCommonEvent.d.ts +3 -2
- package/dist/ts/wysiwyg/index.d.ts +29 -0
- package/dist/types/index.d.ts +18 -2
- package/package.json +1 -1
- package/src/assets/less/_line-number.less +6 -1
- package/src/assets/less/_selection-popover.less +148 -0
- package/src/assets/less/_selection-tag.less +38 -0
- package/src/assets/less/index.less +2 -0
- package/src/index.ts +58 -3
- package/src/method.ts +1 -1
- package/src/ts/ir/index.ts +1 -1
- package/src/ts/ir/process.ts +1 -1
- package/src/ts/markdown/selectionRender.ts +28 -0
- package/src/ts/preview/index.ts +6 -5
- package/src/ts/sv/index.ts +1 -1
- package/src/ts/sv/process.ts +1 -1
- package/src/ts/ui/initUI.ts +4 -2
- package/src/ts/util/Options.ts +8 -1
- package/src/ts/util/editorCommonEvent.ts +296 -184
- package/src/ts/util/processCode.ts +43 -24
- package/src/ts/wysiwyg/afterRenderEvent.ts +1 -1
- package/src/ts/wysiwyg/index.ts +509 -119
- package/src/ts/wysiwyg/renderDomByMd.ts +1 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
@selection-popover-radius: 8px;
|
|
2
|
+
@selection-popover-padding: 4px;
|
|
3
|
+
@selection-popover-gap: 0px;
|
|
4
|
+
@selection-popover-shadow: var(--panel-shadow);
|
|
5
|
+
@selection-popover-bg: var(--panel-background-color);
|
|
6
|
+
@selection-popover-color: var(--textarea-text-color);
|
|
7
|
+
@selection-popover-hover-color: var(--toolbar-icon-hover-color);
|
|
8
|
+
|
|
9
|
+
.vditor-selection-popover {
|
|
10
|
+
position: absolute;
|
|
11
|
+
z-index: 4;
|
|
12
|
+
display: none;
|
|
13
|
+
padding: 0;
|
|
14
|
+
border-radius: @selection-popover-radius;
|
|
15
|
+
background-color: @selection-popover-bg;
|
|
16
|
+
box-shadow: @selection-popover-shadow;
|
|
17
|
+
border: 1px solid var(--border-color);
|
|
18
|
+
user-select: none;
|
|
19
|
+
white-space: nowrap;
|
|
20
|
+
transform-origin: top left;
|
|
21
|
+
opacity: 0;
|
|
22
|
+
transition: opacity 150ms cubic-bezier(0.2, 0, 0.13, 1.5),
|
|
23
|
+
transform 150ms cubic-bezier(0.2, 0, 0.13, 1.5);
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
flex-wrap: nowrap;
|
|
26
|
+
|
|
27
|
+
&__actions {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: row;
|
|
30
|
+
align-items: center;
|
|
31
|
+
width: 100%;
|
|
32
|
+
border-bottom: 1px solid var(--border-color);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__input {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
width: 327px;
|
|
39
|
+
padding: 12px;
|
|
40
|
+
gap: 8px;
|
|
41
|
+
align-items: flex-end;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
|
|
44
|
+
textarea {
|
|
45
|
+
flex: 1;
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 60px;
|
|
48
|
+
background: transparent;
|
|
49
|
+
border: none;
|
|
50
|
+
resize: none;
|
|
51
|
+
outline: none;
|
|
52
|
+
color: @selection-popover-color;
|
|
53
|
+
font-size: 14px;
|
|
54
|
+
line-height: 20px;
|
|
55
|
+
padding: 0;
|
|
56
|
+
margin: 0;
|
|
57
|
+
|
|
58
|
+
&::placeholder {
|
|
59
|
+
color: #585a5a;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.vditor-selection-popover__send {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
width: 32px;
|
|
68
|
+
height: 32px;
|
|
69
|
+
border: none;
|
|
70
|
+
background: var(--toolbar-icon-hover-color);
|
|
71
|
+
border-radius: 8px;
|
|
72
|
+
color: #fff;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
padding: 0;
|
|
75
|
+
transition: opacity 0.2s;
|
|
76
|
+
|
|
77
|
+
&:hover {
|
|
78
|
+
opacity: 0.8;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&[disabled] {
|
|
82
|
+
opacity: 0.5;
|
|
83
|
+
cursor: not-allowed;
|
|
84
|
+
pointer-events: none;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&--loading {
|
|
88
|
+
pointer-events: none;
|
|
89
|
+
opacity: 0.8;
|
|
90
|
+
|
|
91
|
+
svg {
|
|
92
|
+
animation: vditor-rotate 1s linear infinite;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
svg {
|
|
97
|
+
width: 16px;
|
|
98
|
+
height: 16px;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@keyframes vditor-rotate {
|
|
104
|
+
from {
|
|
105
|
+
transform: rotate(0deg);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
to {
|
|
109
|
+
transform: rotate(360deg);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__btn {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
height: 32px;
|
|
118
|
+
width: 109px;
|
|
119
|
+
flex: 0 0 109px;
|
|
120
|
+
box-sizing: border-box;
|
|
121
|
+
white-space: nowrap;
|
|
122
|
+
margin: 0 @selection-popover-gap;
|
|
123
|
+
border: none;
|
|
124
|
+
background: transparent;
|
|
125
|
+
color: @selection-popover-color;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
outline: none;
|
|
128
|
+
font-size: 14px;
|
|
129
|
+
font-weight: 500;
|
|
130
|
+
transition: @transition;
|
|
131
|
+
|
|
132
|
+
&:hover,
|
|
133
|
+
&:focus {
|
|
134
|
+
color: @selection-popover-hover-color;
|
|
135
|
+
background-color: transparent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
&[disabled] {
|
|
139
|
+
opacity: 0.9;
|
|
140
|
+
cursor: not-allowed;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&__btn+&__btn {
|
|
145
|
+
border-left: 1px solid var(--border-color);
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Selection tag styles - 参考代码块样式
|
|
2
|
+
.vditor-reset .vditor-selection-tag {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 0.31rem;
|
|
6
|
+
padding: 0.25rem 0.5rem;
|
|
7
|
+
background-color: #f6f8fa;
|
|
8
|
+
border: 1px solid #d1d5da;
|
|
9
|
+
border-radius: 0.25rem;
|
|
10
|
+
font-size: 0.875rem;
|
|
11
|
+
line-height: 1.25rem;
|
|
12
|
+
color: @primaryColor;
|
|
13
|
+
font-family: @font-family-code;
|
|
14
|
+
margin: 0.25rem 0.25rem 0.25rem 0;
|
|
15
|
+
|
|
16
|
+
&__file {
|
|
17
|
+
color: @primaryColor;
|
|
18
|
+
font-weight: 500;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&__lines {
|
|
22
|
+
color: #586069;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Dark mode
|
|
27
|
+
.vditor--dark .vditor-reset .vditor-selection-tag {
|
|
28
|
+
background-color: #2f363d;
|
|
29
|
+
border-color: #141414;
|
|
30
|
+
|
|
31
|
+
.vditor-selection-tag__file {
|
|
32
|
+
color: #fafafa;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vditor-selection-tag__lines {
|
|
36
|
+
color: #b9b9b9;
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -52,6 +52,9 @@ import {
|
|
|
52
52
|
attachLineNumbersToBlocksThrottled,
|
|
53
53
|
} from "./ts/util/attachLineNumbers";
|
|
54
54
|
|
|
55
|
+
const LINE_HIGHLIGHT_CLASS = "vditor-line-highlight";
|
|
56
|
+
const lineHighlightTimers = new WeakMap<HTMLElement, number>();
|
|
57
|
+
|
|
55
58
|
/**
|
|
56
59
|
* 设置所有 details 默认展开,并确保不影响内容展示与可访问性
|
|
57
60
|
*/
|
|
@@ -200,8 +203,9 @@ class Vditor extends VditorMethod {
|
|
|
200
203
|
* - 依赖配置 `lineNumber: true`,关闭时直接跳过以提升性能
|
|
201
204
|
* @param immediate 为 true 时立即更新;默认为节流更新以避免频繁调用
|
|
202
205
|
*/
|
|
206
|
+
/** 手动更新行号 */
|
|
203
207
|
public updateLineNumbers(immediate = false) {
|
|
204
|
-
if (!this.vditor.options.lineNumber) {
|
|
208
|
+
if (!this.vditor.options.lineNumber?.enable) {
|
|
205
209
|
return;
|
|
206
210
|
}
|
|
207
211
|
const text = getMarkdown(this.vditor);
|
|
@@ -213,6 +217,57 @@ class Vditor extends VditorMethod {
|
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
|
|
220
|
+
/**
|
|
221
|
+
* 高亮指定行号对应的块元素
|
|
222
|
+
* - 仅匹配拥有 `data-linenumber`/`data-lineNumber` 且值等于指定行号的元素
|
|
223
|
+
* - 应用高亮样式并在约 500ms 后自动恢复
|
|
224
|
+
* @param numbers 要高亮的行号数组
|
|
225
|
+
*/
|
|
226
|
+
/** 高亮指定行号,时长受 `options.lineNumber.highlightTimer` 控制 */
|
|
227
|
+
public setLinehightNumbers(numbers: number[]) {
|
|
228
|
+
if (!Array.isArray(numbers)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const valid = numbers
|
|
232
|
+
.filter((n) => typeof n === "number" && isFinite(n))
|
|
233
|
+
.map((n) => String(Math.floor(n)));
|
|
234
|
+
if (valid.length === 0) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const root = this.vditor[this.vditor.currentMode].element;
|
|
238
|
+
const sel = valid
|
|
239
|
+
.map((v) => `[data-linenumber="${v}"],[data-lineNumber="${v}"]`)
|
|
240
|
+
.join(",");
|
|
241
|
+
const nodeList = root.querySelectorAll<HTMLElement>(sel);
|
|
242
|
+
if (nodeList.length === 0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const duration = Math.max(
|
|
246
|
+
0,
|
|
247
|
+
Number(this.vditor.options.lineNumber?.highlightTimer) || 1500
|
|
248
|
+
);
|
|
249
|
+
const apply = () => {
|
|
250
|
+
nodeList.forEach((el) => {
|
|
251
|
+
const old = lineHighlightTimers.get(el);
|
|
252
|
+
if (old) {
|
|
253
|
+
clearTimeout(old);
|
|
254
|
+
lineHighlightTimers.delete(el);
|
|
255
|
+
}
|
|
256
|
+
el.classList.remove(LINE_HIGHLIGHT_CLASS);
|
|
257
|
+
});
|
|
258
|
+
requestAnimationFrame(() => {
|
|
259
|
+
nodeList.forEach((el) => {
|
|
260
|
+
el.classList.add(LINE_HIGHLIGHT_CLASS);
|
|
261
|
+
const handle = window.setTimeout(() => {
|
|
262
|
+
el.classList.remove(LINE_HIGHLIGHT_CLASS);
|
|
263
|
+
lineHighlightTimers.delete(el);
|
|
264
|
+
}, duration);
|
|
265
|
+
lineHighlightTimers.set(el, handle);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
apply();
|
|
270
|
+
}
|
|
216
271
|
/** 获取编辑器当前编辑模式 */
|
|
217
272
|
public getCurrentMode() {
|
|
218
273
|
return this.vditor.currentMode;
|
|
@@ -408,7 +463,7 @@ class Vditor extends VditorMethod {
|
|
|
408
463
|
public setValue(markdown: string, clearStack = false) {
|
|
409
464
|
if (this.vditor.currentMode === "sv") {
|
|
410
465
|
this.vditor.sv.element.innerHTML = `<div data-block='0'>${this.vditor.lute.SpinVditorSVDOM(markdown)}</div>`;
|
|
411
|
-
if (this.vditor.options.lineNumber) {
|
|
466
|
+
if (this.vditor.options.lineNumber?.enable) {
|
|
412
467
|
try {
|
|
413
468
|
attachLineNumbersToBlocks(
|
|
414
469
|
this.vditor.sv.element,
|
|
@@ -435,7 +490,7 @@ class Vditor extends VditorMethod {
|
|
|
435
490
|
.forEach((item: HTMLElement) => {
|
|
436
491
|
processCodeRender(item, this.vditor);
|
|
437
492
|
});
|
|
438
|
-
if (this.vditor.options.lineNumber) {
|
|
493
|
+
if (this.vditor.options.lineNumber?.enable) {
|
|
439
494
|
try {
|
|
440
495
|
attachLineNumbersToBlocks(
|
|
441
496
|
this.vditor.ir.element,
|
package/src/method.ts
CHANGED
|
@@ -77,7 +77,7 @@ class Vditor {
|
|
|
77
77
|
public static updateLineNumbers(instance: any, immediate = false) {
|
|
78
78
|
try {
|
|
79
79
|
const v = instance?.vditor ?? instance;
|
|
80
|
-
if (!v?.options?.lineNumber) return;
|
|
80
|
+
if (!v?.options?.lineNumber?.enable) return;
|
|
81
81
|
const text = getMarkdown(v);
|
|
82
82
|
const root = v[v.currentMode].element as HTMLElement;
|
|
83
83
|
if (immediate) {
|
package/src/ts/ir/index.ts
CHANGED
|
@@ -33,7 +33,7 @@ class IR {
|
|
|
33
33
|
constructor(vditor: IVditor) {
|
|
34
34
|
const divElement = document.createElement("div");
|
|
35
35
|
divElement.className = "vditor-ir";
|
|
36
|
-
if (vditor.options.lineNumber) {
|
|
36
|
+
if (vditor.options.lineNumber?.enable) {
|
|
37
37
|
divElement.classList.add("vditor--linenumber");
|
|
38
38
|
}
|
|
39
39
|
|
package/src/ts/ir/process.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 渲染 selection 代码块为文件选择标签
|
|
3
|
+
*/
|
|
4
|
+
export const selectionRender = (element: HTMLElement) => {
|
|
5
|
+
element.querySelectorAll("pre > code.language-selection").forEach((codeElement: HTMLElement) => {
|
|
6
|
+
const preElement = codeElement.parentElement as HTMLElement;
|
|
7
|
+
const content = codeElement.textContent.trim();
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
|
|
10
|
+
const tags = lines.map(line => {
|
|
11
|
+
const match = line.trim().match(/^(.+?)\s+(\d+-\d+)$/);
|
|
12
|
+
if (match) {
|
|
13
|
+
const [, filename, lineRange] = match;
|
|
14
|
+
return `<div class="vditor-selection-tag">
|
|
15
|
+
<span class="vditor-selection-tag__file">${filename}</span>
|
|
16
|
+
<span class="vditor-selection-tag__lines">${lineRange}</span>
|
|
17
|
+
</div>`;
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
}).filter(tag => tag).join('');
|
|
21
|
+
|
|
22
|
+
if (tags) {
|
|
23
|
+
const container = document.createElement('div');
|
|
24
|
+
container.innerHTML = tags;
|
|
25
|
+
preElement.parentNode.replaceChild(container, preElement);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
package/src/ts/preview/index.ts
CHANGED
|
@@ -12,11 +12,9 @@ import { SMILESRender } from "../markdown/SMILESRender";
|
|
|
12
12
|
import { markmapRender } from "../markdown/markmapRender";
|
|
13
13
|
import { mindmapRender } from "../markdown/mindmapRender";
|
|
14
14
|
import { plantumlRender } from "../markdown/plantumlRender";
|
|
15
|
-
import {
|
|
16
|
-
hasClosestByClassName,
|
|
17
|
-
hasClosestByMatchTag,
|
|
18
|
-
} from "../util/hasClosest";
|
|
15
|
+
import { hasClosestByClassName, hasClosestByMatchTag } from "../util/hasClosest";
|
|
19
16
|
import { setSelectionFocus } from "../util/selection";
|
|
17
|
+
import { selectionRender } from "../markdown/selectionRender";
|
|
20
18
|
import { previewImage } from "./image";
|
|
21
19
|
|
|
22
20
|
export class Preview {
|
|
@@ -247,8 +245,11 @@ export class Preview {
|
|
|
247
245
|
if (vditor.options.preview.render.media.enable) {
|
|
248
246
|
mediaRender(vditor.preview.previewElement);
|
|
249
247
|
}
|
|
248
|
+
selectionRender(vditor.preview.previewElement);
|
|
250
249
|
vditor.options.customRenders.forEach((item) => {
|
|
251
|
-
|
|
250
|
+
{
|
|
251
|
+
item.render(vditor.preview.previewElement, vditor);
|
|
252
|
+
}
|
|
252
253
|
});
|
|
253
254
|
// toc render
|
|
254
255
|
const editorElement = vditor.preview.element;
|
package/src/ts/sv/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ class Editor {
|
|
|
24
24
|
constructor(vditor: IVditor) {
|
|
25
25
|
this.element = document.createElement("pre");
|
|
26
26
|
this.element.className = "vditor-sv vditor-reset";
|
|
27
|
-
if (vditor.options.lineNumber) {
|
|
27
|
+
if (vditor.options.lineNumber?.enable) {
|
|
28
28
|
this.element.classList.add("vditor--linenumber");
|
|
29
29
|
}
|
|
30
30
|
this.element.setAttribute("placeholder", vditor.options.placeholder);
|
package/src/ts/sv/process.ts
CHANGED
|
@@ -178,7 +178,7 @@ export const processAfterRender = (
|
|
|
178
178
|
vditor.undo.addToUndoStack(vditor);
|
|
179
179
|
}
|
|
180
180
|
try {
|
|
181
|
-
if (vditor.options.lineNumber) {
|
|
181
|
+
if (vditor.options.lineNumber?.enable) {
|
|
182
182
|
attachLineNumbersToBlocksThrottled(vditor.sv.element, text);
|
|
183
183
|
}
|
|
184
184
|
} catch {
|
package/src/ts/ui/initUI.ts
CHANGED
|
@@ -121,7 +121,8 @@ export const setPadding = (vditor: IVditor) => {
|
|
|
121
121
|
vditor.options.preview.maxWidth) /
|
|
122
122
|
2;
|
|
123
123
|
const basePad = Math.max(minPadding, padding);
|
|
124
|
-
const leftExtra =
|
|
124
|
+
const leftExtra =
|
|
125
|
+
vditor.options.lineNumber?.enable && basePad < 40 ? 20 : 0;
|
|
125
126
|
vditor.wysiwyg.element.style.padding = `10px ${basePad}px 10px ${basePad + leftExtra}px`;
|
|
126
127
|
}
|
|
127
128
|
|
|
@@ -131,7 +132,8 @@ export const setPadding = (vditor: IVditor) => {
|
|
|
131
132
|
vditor.options.preview.maxWidth) /
|
|
132
133
|
2;
|
|
133
134
|
const basePad = Math.max(minPadding, padding);
|
|
134
|
-
const leftExtra =
|
|
135
|
+
const leftExtra =
|
|
136
|
+
vditor.options.lineNumber?.enable && basePad < 40 ? 20 : 0;
|
|
135
137
|
vditor.ir.element.style.padding = `10px ${basePad}px 10px ${basePad + leftExtra}px`;
|
|
136
138
|
}
|
|
137
139
|
|
package/src/ts/util/Options.ts
CHANGED
|
@@ -9,6 +9,10 @@ export class Options {
|
|
|
9
9
|
inlinePopover: {
|
|
10
10
|
enable: false,
|
|
11
11
|
},
|
|
12
|
+
selectionPopover: {
|
|
13
|
+
enable: false,
|
|
14
|
+
debounceDelay: 150,
|
|
15
|
+
},
|
|
12
16
|
cache: {
|
|
13
17
|
enable: true,
|
|
14
18
|
},
|
|
@@ -118,7 +122,10 @@ export class Options {
|
|
|
118
122
|
pin: false,
|
|
119
123
|
},
|
|
120
124
|
typewriterMode: false,
|
|
121
|
-
lineNumber:
|
|
125
|
+
lineNumber: {
|
|
126
|
+
enable: false,
|
|
127
|
+
highlightTimer: 1500,
|
|
128
|
+
},
|
|
122
129
|
undoDelay: 800,
|
|
123
130
|
upload: {
|
|
124
131
|
extraData: {},
|