dn-react-text-editor 0.2.4 → 0.3.1
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/attach_file.js +6 -1
- package/dist/attach_file.mjs +6 -1
- package/dist/create_text_editor.d.mts +10 -19
- package/dist/create_text_editor.d.ts +10 -19
- package/dist/create_text_editor.js +478 -451
- package/dist/create_text_editor.mjs +479 -452
- package/dist/html.js +6 -5
- package/dist/html.mjs +6 -5
- package/dist/index.d.mts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.js +486 -456
- package/dist/index.mjs +486 -457
- package/dist/input.d.mts +6 -9
- package/dist/input.d.ts +6 -9
- package/dist/input.js +12 -13
- package/dist/input.mjs +12 -13
- package/dist/plugins/keymap.js +45 -17
- package/dist/plugins/keymap.mjs +45 -17
- package/dist/text_editor_controller.d.mts +20 -10
- package/dist/text_editor_controller.d.ts +20 -10
- package/dist/text_editor_controller.js +658 -125
- package/dist/text_editor_controller.mjs +656 -123
- package/package.json +56 -55
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,265 @@
|
|
|
1
1
|
// src/create_text_editor.tsx
|
|
2
2
|
import React2, {
|
|
3
|
-
|
|
3
|
+
useMemo
|
|
4
4
|
} from "react";
|
|
5
|
-
import "prosemirror-state";
|
|
6
|
-
import "prosemirror-view";
|
|
7
5
|
import { useEffect as useEffect2, useRef } from "react";
|
|
8
6
|
|
|
7
|
+
// src/input.tsx
|
|
8
|
+
import React, {
|
|
9
|
+
useEffect
|
|
10
|
+
} from "react";
|
|
11
|
+
import { debounceTime, filter } from "rxjs";
|
|
12
|
+
function TextEditorInput({ controller, onChange, ...props }) {
|
|
13
|
+
const inputRef = React.useRef(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const sub = controller.subject.pipe(
|
|
16
|
+
filter((tr) => tr.docChanged),
|
|
17
|
+
debounceTime(controller.props.updateDelay || 0)
|
|
18
|
+
).subscribe(() => {
|
|
19
|
+
if (inputRef.current) {
|
|
20
|
+
inputRef.current.value = controller.value;
|
|
21
|
+
const event = new Event("input", { bubbles: true });
|
|
22
|
+
inputRef.current.dispatchEvent(event);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return () => {
|
|
26
|
+
sub.unsubscribe();
|
|
27
|
+
};
|
|
28
|
+
}, []);
|
|
29
|
+
return /* @__PURE__ */ React.createElement(
|
|
30
|
+
"input",
|
|
31
|
+
{
|
|
32
|
+
...props,
|
|
33
|
+
ref: inputRef,
|
|
34
|
+
type: "hidden",
|
|
35
|
+
onInput: onChange,
|
|
36
|
+
defaultValue: controller.props.defaultValue
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/text_editor_controller.tsx
|
|
42
|
+
import {
|
|
43
|
+
EditorState as EditorState2
|
|
44
|
+
} from "prosemirror-state";
|
|
45
|
+
import { EditorView as EditorView3 } from "prosemirror-view";
|
|
46
|
+
import * as commands2 from "prosemirror-commands";
|
|
47
|
+
import { keymap } from "prosemirror-keymap";
|
|
48
|
+
|
|
49
|
+
// src/plugins/drag_and_drop.tsx
|
|
50
|
+
import { Plugin } from "prosemirror-state";
|
|
51
|
+
function dragAndDropPlugin({ attachFile }) {
|
|
52
|
+
return new Plugin({
|
|
53
|
+
props: {
|
|
54
|
+
handleDOMEvents: {
|
|
55
|
+
drop(view, event) {
|
|
56
|
+
if (!attachFile) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const files = event.dataTransfer?.files;
|
|
60
|
+
if (!files || files.length === 0) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
const pos = view.state.selection.$from.pos || view.posAtCoords({
|
|
65
|
+
left: event.clientX,
|
|
66
|
+
top: event.clientY
|
|
67
|
+
})?.pos || null;
|
|
68
|
+
if (pos === null) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const medias = Array.from(files).filter(
|
|
72
|
+
(file) => file.type.startsWith("image/") || file.type.startsWith("video/")
|
|
73
|
+
);
|
|
74
|
+
attachFile(view, medias);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/plugins/upload_placeholder.tsx
|
|
83
|
+
import { Plugin as Plugin2 } from "prosemirror-state";
|
|
84
|
+
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
85
|
+
var uploadPlaceholderPlugin = new Plugin2({
|
|
86
|
+
state: {
|
|
87
|
+
init() {
|
|
88
|
+
return DecorationSet.empty;
|
|
89
|
+
},
|
|
90
|
+
apply(tr, set) {
|
|
91
|
+
set = set.map(tr.mapping, tr.doc);
|
|
92
|
+
const action = tr.getMeta(this);
|
|
93
|
+
if (action && action.add) {
|
|
94
|
+
const { type, width, height } = action.add;
|
|
95
|
+
const widget = document.createElement("div");
|
|
96
|
+
widget.className = "upload-placeholder";
|
|
97
|
+
widget.style.width = `100%`;
|
|
98
|
+
if (type.startsWith("image/") || type.startsWith("video/")) {
|
|
99
|
+
widget.style.aspectRatio = `${width} / ${height}`;
|
|
100
|
+
widget.style.maxWidth = `${width}px`;
|
|
101
|
+
} else {
|
|
102
|
+
widget.style.height = "80px";
|
|
103
|
+
}
|
|
104
|
+
const progress = document.createElement("div");
|
|
105
|
+
progress.className = "upload-progress";
|
|
106
|
+
widget.appendChild(progress);
|
|
107
|
+
const deco = Decoration.widget(action.add.pos, widget, {
|
|
108
|
+
id: action.add.id
|
|
109
|
+
});
|
|
110
|
+
set = set.add(tr.doc, [deco]);
|
|
111
|
+
} else if (action && action.progress) {
|
|
112
|
+
const found = set.find(
|
|
113
|
+
void 0,
|
|
114
|
+
void 0,
|
|
115
|
+
(spec) => spec.id === action.progress.id
|
|
116
|
+
);
|
|
117
|
+
if (found.length) {
|
|
118
|
+
const widget = found[0].type.toDOM;
|
|
119
|
+
const progress = widget.querySelector(".upload-progress");
|
|
120
|
+
if (progress) {
|
|
121
|
+
progress.innerHTML = `${Math.round(action.progress.progress)}%`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else if (action && action.remove) {
|
|
125
|
+
set = set.remove(
|
|
126
|
+
set.find(void 0, void 0, (spec) => spec.id === action.remove.id)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return set;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
props: {
|
|
133
|
+
decorations(state) {
|
|
134
|
+
return this.getState(state);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
var findPlaceholder = (state, id) => {
|
|
139
|
+
const decos = uploadPlaceholderPlugin.getState(state);
|
|
140
|
+
if (!decos) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const found = decos.find(void 0, void 0, (spec) => spec.id === id);
|
|
144
|
+
return found.length ? found[0].from : null;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/plugins/placehoder.tsx
|
|
148
|
+
import "prosemirror-model";
|
|
149
|
+
import { Plugin as Plugin3 } from "prosemirror-state";
|
|
150
|
+
import "prosemirror-view";
|
|
151
|
+
var getFirstChildDescendants = (view) => {
|
|
152
|
+
const nodes = [];
|
|
153
|
+
view.state.doc?.descendants((n) => {
|
|
154
|
+
nodes.push(n);
|
|
155
|
+
});
|
|
156
|
+
return nodes;
|
|
157
|
+
};
|
|
158
|
+
function placeholderPlugin(text) {
|
|
159
|
+
const update = (view) => {
|
|
160
|
+
const decos = uploadPlaceholderPlugin.getState(view.state);
|
|
161
|
+
if (decos && decos.find().length > 0 || view.state.doc.content.content.some((e) => e.type.name !== "paragraph") || view.state.doc.childCount > 1 || getFirstChildDescendants(view).length > 1 || view.state.doc.textContent) {
|
|
162
|
+
view.dom.removeAttribute("data-placeholder");
|
|
163
|
+
} else {
|
|
164
|
+
view.dom.setAttribute("data-placeholder", text);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return new Plugin3({
|
|
168
|
+
view(view) {
|
|
169
|
+
update(view);
|
|
170
|
+
return { update };
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/text_editor_controller.tsx
|
|
176
|
+
import { history } from "prosemirror-history";
|
|
177
|
+
|
|
178
|
+
// src/plugins/keymap.tsx
|
|
179
|
+
import { TextSelection } from "prosemirror-state";
|
|
180
|
+
import { undo, redo } from "prosemirror-history";
|
|
181
|
+
import { chainCommands, splitBlockAs } from "prosemirror-commands";
|
|
182
|
+
import { splitListItem } from "prosemirror-schema-list";
|
|
183
|
+
function buildKeymap(schema) {
|
|
184
|
+
const keys = {};
|
|
185
|
+
function bind(key, cmd) {
|
|
186
|
+
keys[key] = cmd;
|
|
187
|
+
}
|
|
188
|
+
bind("Mod-z", undo);
|
|
189
|
+
bind("Shift-Mod-z", redo);
|
|
190
|
+
bind("Mod-y", redo);
|
|
191
|
+
bind("ArrowDown", (state, dispatch) => {
|
|
192
|
+
const doc = state.doc;
|
|
193
|
+
const lastNode = doc.lastChild;
|
|
194
|
+
if (lastNode && lastNode.type.name !== "paragraph") {
|
|
195
|
+
const paragraphType = state.schema.nodes.paragraph;
|
|
196
|
+
let tr = state.tr;
|
|
197
|
+
const endPos = doc.content.size;
|
|
198
|
+
tr = tr.insert(endPos, paragraphType.create());
|
|
199
|
+
tr = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
|
|
200
|
+
if (dispatch) {
|
|
201
|
+
dispatch(tr);
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
});
|
|
207
|
+
const li = schema.nodes.list_item;
|
|
208
|
+
bind(
|
|
209
|
+
"Enter",
|
|
210
|
+
chainCommands(
|
|
211
|
+
splitListItem(li),
|
|
212
|
+
(state, dispatch) => {
|
|
213
|
+
const { $head } = state.selection;
|
|
214
|
+
if ($head.parent.type === state.schema.nodes.paragraph) {
|
|
215
|
+
splitBlockAs((n) => {
|
|
216
|
+
return {
|
|
217
|
+
type: n.type,
|
|
218
|
+
attrs: n.attrs
|
|
219
|
+
};
|
|
220
|
+
})(state, dispatch);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
},
|
|
225
|
+
(state, dispatch) => {
|
|
226
|
+
const { selection } = state;
|
|
227
|
+
const { $from, $to } = selection;
|
|
228
|
+
const lines = state.doc.textBetween($from.before(), $to.pos).split("\n");
|
|
229
|
+
const currentLine = lines[lines.length - 1];
|
|
230
|
+
const match = currentLine.match(/^(\s+).*$/);
|
|
231
|
+
if (match) {
|
|
232
|
+
if (dispatch) {
|
|
233
|
+
dispatch(state.tr.insertText("\n" + match[1], $from.pos));
|
|
234
|
+
}
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
);
|
|
241
|
+
bind("Tab", (state, dispatch) => {
|
|
242
|
+
const { selection } = state;
|
|
243
|
+
const { $from, $to } = selection;
|
|
244
|
+
if ($from.parent.type === schema.nodes.code_block) {
|
|
245
|
+
if (dispatch) {
|
|
246
|
+
dispatch(state.tr.insertText(" ", $from.pos, $to.pos));
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
});
|
|
252
|
+
return keys;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/cn.ts
|
|
256
|
+
function cn(...classes) {
|
|
257
|
+
return classes.filter(Boolean).join(" ").trim();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/attach_file.tsx
|
|
261
|
+
import "prosemirror-view";
|
|
262
|
+
|
|
9
263
|
// src/schema.tsx
|
|
10
264
|
import { Schema } from "prosemirror-model";
|
|
11
265
|
import { addListNodes } from "prosemirror-schema-list";
|
|
@@ -496,317 +750,12 @@ function createSchema(spec = { nodes: {}, marks: {} }) {
|
|
|
496
750
|
return prosemirrorSchema;
|
|
497
751
|
}
|
|
498
752
|
|
|
499
|
-
// src/
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
var createCommands = (schema, view, options = {}) => {
|
|
506
|
-
{
|
|
507
|
-
const isBlockTypeActive = (node, attrs, excludes = []) => {
|
|
508
|
-
const state = view.state;
|
|
509
|
-
const ranges = state.selection.ranges;
|
|
510
|
-
let active = false;
|
|
511
|
-
for (const range of ranges) {
|
|
512
|
-
const { $from, $to } = range;
|
|
513
|
-
state.doc.nodesBetween($from.pos, $to.pos, (n) => {
|
|
514
|
-
if (active) {
|
|
515
|
-
return true;
|
|
516
|
-
}
|
|
517
|
-
if (n.type !== node || excludes.includes(n.type)) {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
if (!attrs || Object.keys(attrs).every((key) => n.attrs[key] === attrs[key])) {
|
|
521
|
-
active = true;
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
return active;
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
const setBlockType2 = (node, attrs) => {
|
|
528
|
-
view.focus();
|
|
529
|
-
const nodeType = schema.nodes[node];
|
|
530
|
-
const command = commands.setBlockType(nodeType, attrs);
|
|
531
|
-
command(view.state, view.dispatch);
|
|
532
|
-
};
|
|
533
|
-
const toggleBlockType = (node, attrs) => {
|
|
534
|
-
view.focus();
|
|
535
|
-
const nodeType = schema.nodes[node];
|
|
536
|
-
const command = commands.setBlockType(nodeType, attrs);
|
|
537
|
-
if (isBlockTypeActive(nodeType, attrs)) {
|
|
538
|
-
command(view.state, view.dispatch);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
const toggleMark2 = (mark, attrs, options2) => {
|
|
542
|
-
view.focus();
|
|
543
|
-
const markType = schema.marks[mark];
|
|
544
|
-
const command = commands.toggleMark(markType, attrs, options2);
|
|
545
|
-
command(view.state, view.dispatch);
|
|
546
|
-
};
|
|
547
|
-
const wrapInList2 = (listType, attrs) => {
|
|
548
|
-
view.focus();
|
|
549
|
-
const nodeType = schema.nodes[listType];
|
|
550
|
-
const command = schemaList.wrapInList(nodeType, attrs);
|
|
551
|
-
command(view.state, view.dispatch);
|
|
552
|
-
};
|
|
553
|
-
const clear = () => {
|
|
554
|
-
const tr = view.state.tr.replaceWith(
|
|
555
|
-
0,
|
|
556
|
-
view.state.doc.content.size,
|
|
557
|
-
schema.nodes.doc.createAndFill()
|
|
558
|
-
);
|
|
559
|
-
view.dispatch(tr);
|
|
560
|
-
};
|
|
561
|
-
return {
|
|
562
|
-
isBlockTypeActive,
|
|
563
|
-
setBlockType: setBlockType2,
|
|
564
|
-
toggleBlockType,
|
|
565
|
-
toggleMark: toggleMark2,
|
|
566
|
-
wrapInList: wrapInList2,
|
|
567
|
-
clear,
|
|
568
|
-
attachFile: (files) => {
|
|
569
|
-
options.attachFile?.(view, files);
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
// src/input.tsx
|
|
576
|
-
import React, {
|
|
577
|
-
useEffect
|
|
578
|
-
} from "react";
|
|
579
|
-
import { debounceTime, filter } from "rxjs";
|
|
580
|
-
function TextEditorInput({
|
|
581
|
-
ref,
|
|
582
|
-
onChange,
|
|
583
|
-
updateDelay = 0,
|
|
584
|
-
...props
|
|
585
|
-
}) {
|
|
586
|
-
const inputRef = React.useRef(null);
|
|
587
|
-
useEffect(() => {
|
|
588
|
-
const controller = ref.current;
|
|
589
|
-
if (!controller) {
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
const sub = controller.subject.pipe(
|
|
593
|
-
filter((tr) => tr.docChanged),
|
|
594
|
-
debounceTime(updateDelay)
|
|
595
|
-
).subscribe(() => {
|
|
596
|
-
if (inputRef.current) {
|
|
597
|
-
inputRef.current.value = controller.value;
|
|
598
|
-
const event = new Event("input", { bubbles: true });
|
|
599
|
-
inputRef.current.dispatchEvent(event);
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
return () => {
|
|
603
|
-
sub.unsubscribe();
|
|
604
|
-
controller.view.destroy();
|
|
605
|
-
};
|
|
606
|
-
}, []);
|
|
607
|
-
return /* @__PURE__ */ React.createElement("input", { ...props, ref: inputRef, type: "hidden", onInput: onChange });
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// src/text_editor_controller.tsx
|
|
611
|
-
import {
|
|
612
|
-
EditorState as EditorState2
|
|
613
|
-
} from "prosemirror-state";
|
|
614
|
-
import { EditorView as EditorView3 } from "prosemirror-view";
|
|
615
|
-
import * as commands2 from "prosemirror-commands";
|
|
616
|
-
import { keymap } from "prosemirror-keymap";
|
|
617
|
-
|
|
618
|
-
// src/plugins/drag_and_drop.tsx
|
|
619
|
-
import { Plugin } from "prosemirror-state";
|
|
620
|
-
function dragAndDropPlugin({ attachFile }) {
|
|
621
|
-
return new Plugin({
|
|
622
|
-
props: {
|
|
623
|
-
handleDOMEvents: {
|
|
624
|
-
drop(view, event) {
|
|
625
|
-
if (!attachFile) {
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
const files = event.dataTransfer?.files;
|
|
629
|
-
if (!files || files.length === 0) {
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
event.preventDefault();
|
|
633
|
-
const pos = view.state.selection.$from.pos || view.posAtCoords({
|
|
634
|
-
left: event.clientX,
|
|
635
|
-
top: event.clientY
|
|
636
|
-
})?.pos || null;
|
|
637
|
-
if (pos === null) {
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
const medias = Array.from(files).filter(
|
|
641
|
-
(file) => file.type.startsWith("image/") || file.type.startsWith("video/")
|
|
642
|
-
);
|
|
643
|
-
attachFile(view, medias);
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/plugins/upload_placeholder.tsx
|
|
652
|
-
import { Plugin as Plugin2 } from "prosemirror-state";
|
|
653
|
-
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
654
|
-
var uploadPlaceholderPlugin = new Plugin2({
|
|
655
|
-
state: {
|
|
656
|
-
init() {
|
|
657
|
-
return DecorationSet.empty;
|
|
658
|
-
},
|
|
659
|
-
apply(tr, set) {
|
|
660
|
-
set = set.map(tr.mapping, tr.doc);
|
|
661
|
-
const action = tr.getMeta(this);
|
|
662
|
-
if (action && action.add) {
|
|
663
|
-
const { type, width, height } = action.add;
|
|
664
|
-
const widget = document.createElement("div");
|
|
665
|
-
widget.className = "upload-placeholder";
|
|
666
|
-
widget.style.width = `100%`;
|
|
667
|
-
if (type.startsWith("image/") || type.startsWith("video/")) {
|
|
668
|
-
widget.style.aspectRatio = `${width} / ${height}`;
|
|
669
|
-
widget.style.maxWidth = `${width}px`;
|
|
670
|
-
} else {
|
|
671
|
-
widget.style.height = "80px";
|
|
672
|
-
}
|
|
673
|
-
const progress = document.createElement("div");
|
|
674
|
-
progress.className = "upload-progress";
|
|
675
|
-
widget.appendChild(progress);
|
|
676
|
-
const deco = Decoration.widget(action.add.pos, widget, {
|
|
677
|
-
id: action.add.id
|
|
678
|
-
});
|
|
679
|
-
set = set.add(tr.doc, [deco]);
|
|
680
|
-
} else if (action && action.progress) {
|
|
681
|
-
const found = set.find(
|
|
682
|
-
void 0,
|
|
683
|
-
void 0,
|
|
684
|
-
(spec) => spec.id === action.progress.id
|
|
685
|
-
);
|
|
686
|
-
if (found.length) {
|
|
687
|
-
const widget = found[0].type.toDOM;
|
|
688
|
-
const progress = widget.querySelector(".upload-progress");
|
|
689
|
-
if (progress) {
|
|
690
|
-
progress.innerHTML = `${Math.round(action.progress.progress)}%`;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
} else if (action && action.remove) {
|
|
694
|
-
set = set.remove(
|
|
695
|
-
set.find(void 0, void 0, (spec) => spec.id === action.remove.id)
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
return set;
|
|
699
|
-
}
|
|
700
|
-
},
|
|
701
|
-
props: {
|
|
702
|
-
decorations(state) {
|
|
703
|
-
return this.getState(state);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
var findPlaceholder = (state, id) => {
|
|
708
|
-
const decos = uploadPlaceholderPlugin.getState(state);
|
|
709
|
-
if (!decos) {
|
|
710
|
-
return null;
|
|
711
|
-
}
|
|
712
|
-
const found = decos.find(void 0, void 0, (spec) => spec.id === id);
|
|
713
|
-
return found.length ? found[0].from : null;
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
// src/plugins/placehoder.tsx
|
|
717
|
-
import "prosemirror-model";
|
|
718
|
-
import { Plugin as Plugin3 } from "prosemirror-state";
|
|
719
|
-
import "prosemirror-view";
|
|
720
|
-
var getFirstChildDescendants = (view) => {
|
|
721
|
-
const nodes = [];
|
|
722
|
-
view.state.doc?.descendants((n) => {
|
|
723
|
-
nodes.push(n);
|
|
724
|
-
});
|
|
725
|
-
return nodes;
|
|
726
|
-
};
|
|
727
|
-
function placeholderPlugin(text) {
|
|
728
|
-
const update = (view) => {
|
|
729
|
-
const decos = uploadPlaceholderPlugin.getState(view.state);
|
|
730
|
-
if (decos && decos.find().length > 0 || view.state.doc.content.content.some((e) => e.type.name !== "paragraph") || view.state.doc.childCount > 1 || getFirstChildDescendants(view).length > 1 || view.state.doc.textContent) {
|
|
731
|
-
view.dom.removeAttribute("data-placeholder");
|
|
732
|
-
} else {
|
|
733
|
-
view.dom.setAttribute("data-placeholder", text);
|
|
734
|
-
}
|
|
735
|
-
};
|
|
736
|
-
return new Plugin3({
|
|
737
|
-
view(view) {
|
|
738
|
-
update(view);
|
|
739
|
-
return { update };
|
|
740
|
-
}
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// src/text_editor_controller.tsx
|
|
745
|
-
import { history } from "prosemirror-history";
|
|
746
|
-
|
|
747
|
-
// src/plugins/keymap.tsx
|
|
748
|
-
import { TextSelection } from "prosemirror-state";
|
|
749
|
-
import { undo, redo } from "prosemirror-history";
|
|
750
|
-
import { chainCommands, splitBlockAs } from "prosemirror-commands";
|
|
751
|
-
import { splitListItem } from "prosemirror-schema-list";
|
|
752
|
-
function buildKeymap(schema) {
|
|
753
|
-
const keys = {};
|
|
754
|
-
function bind(key, cmd) {
|
|
755
|
-
keys[key] = cmd;
|
|
756
|
-
}
|
|
757
|
-
bind("Mod-z", undo);
|
|
758
|
-
bind("Shift-Mod-z", redo);
|
|
759
|
-
bind("Mod-y", redo);
|
|
760
|
-
const li = schema.nodes.list_item;
|
|
761
|
-
bind(
|
|
762
|
-
"Enter",
|
|
763
|
-
chainCommands(splitListItem(li), (state, dispatch) => {
|
|
764
|
-
const { $head } = state.selection;
|
|
765
|
-
if ($head.parent.type === state.schema.nodes.paragraph) {
|
|
766
|
-
splitBlockAs((n) => {
|
|
767
|
-
return {
|
|
768
|
-
type: n.type,
|
|
769
|
-
attrs: n.attrs
|
|
770
|
-
};
|
|
771
|
-
})(state, dispatch);
|
|
772
|
-
return true;
|
|
773
|
-
}
|
|
774
|
-
return false;
|
|
775
|
-
})
|
|
776
|
-
);
|
|
777
|
-
bind("ArrowDown", (state, dispatch) => {
|
|
778
|
-
const doc = state.doc;
|
|
779
|
-
const lastNode = doc.lastChild;
|
|
780
|
-
if (lastNode && lastNode.type.name !== "paragraph") {
|
|
781
|
-
const paragraphType = state.schema.nodes.paragraph;
|
|
782
|
-
let tr = state.tr;
|
|
783
|
-
const endPos = doc.content.size;
|
|
784
|
-
tr = tr.insert(endPos, paragraphType.create());
|
|
785
|
-
tr = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
|
|
786
|
-
if (dispatch) {
|
|
787
|
-
dispatch(tr);
|
|
788
|
-
}
|
|
789
|
-
return true;
|
|
790
|
-
}
|
|
791
|
-
return false;
|
|
792
|
-
});
|
|
793
|
-
return keys;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// src/cn.ts
|
|
797
|
-
function cn(...classes) {
|
|
798
|
-
return classes.filter(Boolean).join(" ").trim();
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// src/attach_file.tsx
|
|
802
|
-
import "prosemirror-view";
|
|
803
|
-
|
|
804
|
-
// src/base64_file_uploader.ts
|
|
805
|
-
var base64FileUploader = async (file) => {
|
|
806
|
-
const base64 = await new Promise((resolve, reject) => {
|
|
807
|
-
const reader = new FileReader();
|
|
808
|
-
reader.onload = () => {
|
|
809
|
-
resolve(reader.result);
|
|
753
|
+
// src/base64_file_uploader.ts
|
|
754
|
+
var base64FileUploader = async (file) => {
|
|
755
|
+
const base64 = await new Promise((resolve, reject) => {
|
|
756
|
+
const reader = new FileReader();
|
|
757
|
+
reader.onload = () => {
|
|
758
|
+
resolve(reader.result);
|
|
810
759
|
};
|
|
811
760
|
reader.onerror = reject;
|
|
812
761
|
reader.readAsDataURL(file);
|
|
@@ -871,7 +820,12 @@ function createAttachFile({
|
|
|
871
820
|
if (!node) {
|
|
872
821
|
return;
|
|
873
822
|
}
|
|
874
|
-
view.
|
|
823
|
+
const current = view.state.doc.resolve($pos);
|
|
824
|
+
if (current.parentOffset === 0) {
|
|
825
|
+
view.dispatch(tr2.replaceWith($pos - 1, $pos, node));
|
|
826
|
+
} else {
|
|
827
|
+
view.dispatch(tr2.replaceWith($pos, $pos, node));
|
|
828
|
+
}
|
|
875
829
|
} catch (e) {
|
|
876
830
|
view.dispatch(tr.setMeta(uploadPlaceholderPlugin, { remove: { id } }));
|
|
877
831
|
}
|
|
@@ -884,129 +838,212 @@ function createAttachFile({
|
|
|
884
838
|
};
|
|
885
839
|
}
|
|
886
840
|
|
|
841
|
+
// src/commands.tsx
|
|
842
|
+
import * as commands from "prosemirror-commands";
|
|
843
|
+
import * as schemaList from "prosemirror-schema-list";
|
|
844
|
+
var createCommands = (schema, view, options = {}) => {
|
|
845
|
+
{
|
|
846
|
+
const isBlockTypeActive = (node, attrs, excludes = []) => {
|
|
847
|
+
const state = view.state;
|
|
848
|
+
const ranges = state.selection.ranges;
|
|
849
|
+
let active = false;
|
|
850
|
+
for (const range of ranges) {
|
|
851
|
+
const { $from, $to } = range;
|
|
852
|
+
state.doc.nodesBetween($from.pos, $to.pos, (n) => {
|
|
853
|
+
if (active) {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
if (n.type !== node || excludes.includes(n.type)) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (!attrs || Object.keys(attrs).every((key) => n.attrs[key] === attrs[key])) {
|
|
860
|
+
active = true;
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
return active;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const setBlockType2 = (node, attrs) => {
|
|
867
|
+
view.focus();
|
|
868
|
+
const nodeType = schema.nodes[node];
|
|
869
|
+
const command = commands.setBlockType(nodeType, attrs);
|
|
870
|
+
command(view.state, view.dispatch);
|
|
871
|
+
};
|
|
872
|
+
const toggleBlockType = (node, attrs) => {
|
|
873
|
+
view.focus();
|
|
874
|
+
const nodeType = schema.nodes[node];
|
|
875
|
+
const command = commands.setBlockType(nodeType, attrs);
|
|
876
|
+
if (isBlockTypeActive(nodeType, attrs)) {
|
|
877
|
+
command(view.state, view.dispatch);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
const toggleMark2 = (mark, attrs, options2) => {
|
|
881
|
+
view.focus();
|
|
882
|
+
const markType = schema.marks[mark];
|
|
883
|
+
const command = commands.toggleMark(markType, attrs, options2);
|
|
884
|
+
command(view.state, view.dispatch);
|
|
885
|
+
};
|
|
886
|
+
const wrapInList2 = (listType, attrs) => {
|
|
887
|
+
view.focus();
|
|
888
|
+
const nodeType = schema.nodes[listType];
|
|
889
|
+
const command = schemaList.wrapInList(nodeType, attrs);
|
|
890
|
+
command(view.state, view.dispatch);
|
|
891
|
+
};
|
|
892
|
+
const clear = () => {
|
|
893
|
+
const tr = view.state.tr.replaceWith(
|
|
894
|
+
0,
|
|
895
|
+
view.state.doc.content.size,
|
|
896
|
+
schema.nodes.doc.createAndFill()
|
|
897
|
+
);
|
|
898
|
+
view.dispatch(tr);
|
|
899
|
+
};
|
|
900
|
+
return {
|
|
901
|
+
isBlockTypeActive,
|
|
902
|
+
setBlockType: setBlockType2,
|
|
903
|
+
toggleBlockType,
|
|
904
|
+
toggleMark: toggleMark2,
|
|
905
|
+
wrapInList: wrapInList2,
|
|
906
|
+
clear,
|
|
907
|
+
attachFile: (files) => {
|
|
908
|
+
options.attachFile?.(view, files);
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
|
|
887
914
|
// src/text_editor_controller.tsx
|
|
888
915
|
import { DOMParser, DOMSerializer } from "prosemirror-model";
|
|
889
916
|
import { Subject } from "rxjs";
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
917
|
+
import { highlightPlugin } from "prosemirror-highlightjs";
|
|
918
|
+
var TextEditorController = class {
|
|
919
|
+
schema;
|
|
920
|
+
props;
|
|
921
|
+
subject;
|
|
922
|
+
view;
|
|
923
|
+
prosemirrorParser;
|
|
924
|
+
prosemirrorSerializer;
|
|
925
|
+
get value() {
|
|
926
|
+
if (this.props.mode === "text") {
|
|
927
|
+
return this.toTextContent();
|
|
928
|
+
}
|
|
929
|
+
return this.toHTML();
|
|
930
|
+
}
|
|
931
|
+
set value(value) {
|
|
932
|
+
const wrap = document.createElement("div");
|
|
933
|
+
wrap.innerHTML = this.toInnerHTML(value);
|
|
934
|
+
const doc = this.prosemirrorParser.parse(wrap);
|
|
935
|
+
const tr = this.view.state.tr.replaceWith(
|
|
936
|
+
0,
|
|
937
|
+
this.view.state.doc.content.size,
|
|
938
|
+
doc.content
|
|
939
|
+
);
|
|
940
|
+
this.view.dispatch(tr);
|
|
941
|
+
}
|
|
942
|
+
constructor(props = {}) {
|
|
943
|
+
this.schema = createSchema();
|
|
944
|
+
this.props = props;
|
|
945
|
+
this.subject = new Subject();
|
|
946
|
+
this.prosemirrorParser = DOMParser.fromSchema(this.schema);
|
|
947
|
+
this.prosemirrorSerializer = DOMSerializer.fromSchema(this.schema);
|
|
948
|
+
}
|
|
949
|
+
toInnerHTML(value) {
|
|
950
|
+
if (this.props.mode === "text") {
|
|
904
951
|
return value.split("\n").map((line) => `<p>${line}</p>`).join("");
|
|
905
952
|
}
|
|
906
953
|
return value;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
954
|
+
}
|
|
955
|
+
attachFile(files) {
|
|
956
|
+
return createAttachFile({
|
|
957
|
+
schema: this.schema,
|
|
958
|
+
generateMetadata: this.props.attachFile?.generateMetadata,
|
|
959
|
+
uploadFile: this.props.attachFile?.uploadFile
|
|
960
|
+
})(this.view, files);
|
|
961
|
+
}
|
|
962
|
+
bind(container) {
|
|
963
|
+
const wrapper = document.createElement("div");
|
|
964
|
+
wrapper.innerHTML = this.toInnerHTML(
|
|
965
|
+
this.props.defaultValue ? String(this.props.defaultValue) : ""
|
|
966
|
+
);
|
|
967
|
+
this.view = new EditorView3(container, {
|
|
968
|
+
...this.props.editor,
|
|
969
|
+
attributes: (state) => {
|
|
970
|
+
const propsAttributes = (() => {
|
|
971
|
+
if (typeof this.props.editor?.attributes === "function") {
|
|
972
|
+
return this.props.editor.attributes(state);
|
|
973
|
+
}
|
|
974
|
+
return this.props.editor?.attributes;
|
|
975
|
+
})();
|
|
976
|
+
return {
|
|
977
|
+
...propsAttributes,
|
|
978
|
+
class: cn(this.props?.className, propsAttributes?.class),
|
|
979
|
+
spellcheck: propsAttributes?.spellcheck || "false",
|
|
980
|
+
style: this.props.style || "width: 100%; height: inherit; outline: none;"
|
|
981
|
+
};
|
|
982
|
+
},
|
|
983
|
+
state: EditorState2.create({
|
|
984
|
+
...this.props.state,
|
|
985
|
+
schema: this.props.state?.schema || this.schema,
|
|
986
|
+
doc: this.props.state?.doc || this.prosemirrorParser.parse(wrapper),
|
|
987
|
+
plugins: [
|
|
988
|
+
...this.props.state?.plugins || [],
|
|
989
|
+
history({
|
|
990
|
+
newGroupDelay: this.props.updateDelay
|
|
991
|
+
}),
|
|
992
|
+
keymap(buildKeymap(this.schema)),
|
|
993
|
+
keymap(commands2.baseKeymap),
|
|
994
|
+
uploadPlaceholderPlugin,
|
|
995
|
+
dragAndDropPlugin({
|
|
996
|
+
attachFile: (view, files) => this.attachFile(files)
|
|
997
|
+
}),
|
|
998
|
+
this.props.placeholder && placeholderPlugin(this.props.placeholder),
|
|
999
|
+
highlightPlugin(highlighter, ["code_block"], (node) => {
|
|
1000
|
+
const auto = highlighter.highlightAuto(node.textContent);
|
|
1001
|
+
return auto.language || "";
|
|
1002
|
+
})
|
|
1003
|
+
].filter((e) => !!e)
|
|
1004
|
+
}),
|
|
1005
|
+
dispatchTransaction: (tr) => {
|
|
1006
|
+
let result;
|
|
1007
|
+
if (this.props.editor?.dispatchTransaction) {
|
|
1008
|
+
result = this.props.editor.dispatchTransaction(tr);
|
|
1009
|
+
} else {
|
|
1010
|
+
this.view?.updateState(this.view.state.apply(tr));
|
|
920
1011
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
return {
|
|
924
|
-
...propsAttributes,
|
|
925
|
-
class: cn(options?.className, propsAttributes?.class),
|
|
926
|
-
spellcheck: propsAttributes?.spellcheck || "false",
|
|
927
|
-
style: options.style || "width: 100%; height: inherit; outline: none;"
|
|
928
|
-
};
|
|
929
|
-
},
|
|
930
|
-
state: EditorState2.create({
|
|
931
|
-
...state,
|
|
932
|
-
schema: state?.schema || schema,
|
|
933
|
-
doc: state?.doc || prosemirrorParser.parse(wrapper),
|
|
934
|
-
plugins: [
|
|
935
|
-
...state?.plugins || [],
|
|
936
|
-
history({
|
|
937
|
-
newGroupDelay: updateDelay
|
|
938
|
-
}),
|
|
939
|
-
keymap(buildKeymap(schema)),
|
|
940
|
-
keymap(commands2.baseKeymap),
|
|
941
|
-
uploadPlaceholderPlugin,
|
|
942
|
-
dragAndDropPlugin({
|
|
943
|
-
attachFile
|
|
944
|
-
}),
|
|
945
|
-
placeholder && placeholderPlugin(placeholder)
|
|
946
|
-
].filter((e) => !!e)
|
|
947
|
-
}),
|
|
948
|
-
dispatchTransaction(tr) {
|
|
949
|
-
let result;
|
|
950
|
-
if (editor?.dispatchTransaction) {
|
|
951
|
-
result = editor.dispatchTransaction(tr);
|
|
952
|
-
} else {
|
|
953
|
-
view.updateState(view.state.apply(tr));
|
|
1012
|
+
this.subject.next(tr);
|
|
1013
|
+
return result;
|
|
954
1014
|
}
|
|
955
|
-
|
|
956
|
-
|
|
1015
|
+
});
|
|
1016
|
+
if (this.props.autoFocus) {
|
|
1017
|
+
this.view?.focus();
|
|
957
1018
|
}
|
|
958
|
-
});
|
|
959
|
-
function setValue(value) {
|
|
960
|
-
const wrap = document.createElement("div");
|
|
961
|
-
wrap.innerHTML = toInnerHTML(value);
|
|
962
|
-
const doc = prosemirrorParser.parse(wrap);
|
|
963
|
-
const tr = view.state.tr.replaceWith(
|
|
964
|
-
0,
|
|
965
|
-
view.state.doc.content.size,
|
|
966
|
-
doc.content
|
|
967
|
-
);
|
|
968
|
-
view.dispatch(tr);
|
|
969
1019
|
}
|
|
970
|
-
|
|
971
|
-
const fragment = prosemirrorSerializer.serializeFragment(
|
|
972
|
-
view.state.doc.content
|
|
1020
|
+
toHTML() {
|
|
1021
|
+
const fragment = this.prosemirrorSerializer.serializeFragment(
|
|
1022
|
+
this.view.state.doc.content
|
|
973
1023
|
);
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
return
|
|
1024
|
+
const container = document.createElement("div");
|
|
1025
|
+
container.appendChild(fragment);
|
|
1026
|
+
return container.innerHTML;
|
|
977
1027
|
}
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
return
|
|
1028
|
+
toTextContent() {
|
|
1029
|
+
const state = this.view.state;
|
|
1030
|
+
return state.doc.textBetween(0, state.doc.content.size, "\n");
|
|
981
1031
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
},
|
|
992
|
-
get value() {
|
|
993
|
-
switch (mode) {
|
|
994
|
-
case "text":
|
|
995
|
-
return toTextContent();
|
|
996
|
-
default:
|
|
997
|
-
return toHTML();
|
|
998
|
-
}
|
|
999
|
-
},
|
|
1000
|
-
commands: textEditorCommands
|
|
1001
|
-
};
|
|
1002
|
-
return textEditorController;
|
|
1003
|
-
}
|
|
1032
|
+
get commands() {
|
|
1033
|
+
return createCommands(this.schema, this.view, {
|
|
1034
|
+
attachFile: (view, files) => this.attachFile(files)
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
dispose() {
|
|
1038
|
+
this.view?.destroy();
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1004
1041
|
|
|
1005
1042
|
// src/create_text_editor.tsx
|
|
1006
1043
|
function createTextEditor(options = {}) {
|
|
1007
|
-
const schema = createSchema();
|
|
1008
1044
|
function Component({
|
|
1009
|
-
|
|
1045
|
+
controller: externalController,
|
|
1046
|
+
name,
|
|
1010
1047
|
className,
|
|
1011
1048
|
autoFocus,
|
|
1012
1049
|
onChange,
|
|
@@ -1019,47 +1056,37 @@ function createTextEditor(options = {}) {
|
|
|
1019
1056
|
...props
|
|
1020
1057
|
} = {}) {
|
|
1021
1058
|
const containerRef = useRef(null);
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
defaultValue,
|
|
1036
|
-
updateDelay,
|
|
1037
|
-
placeholder
|
|
1038
|
-
}
|
|
1039
|
-
);
|
|
1040
|
-
controllerRef.current = textEditorController;
|
|
1041
|
-
return textEditorController;
|
|
1042
|
-
},
|
|
1059
|
+
const innerController = useMemo(
|
|
1060
|
+
() => new TextEditorController({
|
|
1061
|
+
mode,
|
|
1062
|
+
state,
|
|
1063
|
+
editor,
|
|
1064
|
+
autoFocus,
|
|
1065
|
+
placeholder,
|
|
1066
|
+
updateDelay,
|
|
1067
|
+
defaultValue,
|
|
1068
|
+
className: options.className,
|
|
1069
|
+
style: options.style,
|
|
1070
|
+
attachFile: options.attachFile
|
|
1071
|
+
}),
|
|
1043
1072
|
[]
|
|
1044
1073
|
);
|
|
1074
|
+
const controller = externalController || innerController;
|
|
1045
1075
|
useEffect2(() => {
|
|
1046
|
-
const
|
|
1047
|
-
if (!
|
|
1076
|
+
const container = containerRef.current;
|
|
1077
|
+
if (!container) {
|
|
1048
1078
|
return;
|
|
1049
1079
|
}
|
|
1050
|
-
|
|
1051
|
-
controller.view.focus();
|
|
1052
|
-
}
|
|
1080
|
+
controller.bind(container);
|
|
1053
1081
|
return () => {
|
|
1054
|
-
controller.
|
|
1082
|
+
controller.dispose();
|
|
1055
1083
|
};
|
|
1056
|
-
}, []);
|
|
1084
|
+
}, [controller]);
|
|
1057
1085
|
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { ...props, ref: containerRef, className }), /* @__PURE__ */ React2.createElement(
|
|
1058
1086
|
TextEditorInput,
|
|
1059
1087
|
{
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
defaultValue,
|
|
1088
|
+
name,
|
|
1089
|
+
controller,
|
|
1063
1090
|
onChange
|
|
1064
1091
|
}
|
|
1065
1092
|
));
|
|
@@ -1074,11 +1101,12 @@ function createInnerHTML(raw) {
|
|
|
1074
1101
|
return raw.replace(/<\/p>/g, "<br></p>").replace(/(<p><br><\/p>)+$/g, "").replace(
|
|
1075
1102
|
/<code class="language-(\w+)">([\s\S]*?)<\/code>/g,
|
|
1076
1103
|
(_, lang, code) => {
|
|
1104
|
+
if (lang === "undefined") {
|
|
1105
|
+
return `<code>${decode(code)}</code>`;
|
|
1106
|
+
}
|
|
1077
1107
|
try {
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
}).value;
|
|
1081
|
-
return `<code class="language-${lang}">${decode(highlighted)}</code>`;
|
|
1108
|
+
const { language, value } = highlighter.highlightAuto(code);
|
|
1109
|
+
return `<code class="language-${language}">${decode(value)}</code>`;
|
|
1082
1110
|
} catch (e) {
|
|
1083
1111
|
return `<code class="language-${lang}">${decode(code)}</code>`;
|
|
1084
1112
|
}
|
|
@@ -1086,7 +1114,7 @@ function createInnerHTML(raw) {
|
|
|
1086
1114
|
).replace(
|
|
1087
1115
|
/<a([^>]*target="_blank"[^>]*)>(.*?)<\/a>/g,
|
|
1088
1116
|
(_, attrs, content) => {
|
|
1089
|
-
return `<a${attrs} rel="noopener noreferrer" target="_blank">${content}<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.25 15.5a.75.75 0 0 1-.75-.75V7.56L7.28 17.78a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L16.44 6.5H9.25a.75.75 0 0 1 0-1.5h9a.75.75 0 0 1 .75.75v9a.75.75 0 0 1-.75.75Z"></path></svg></a>`;
|
|
1117
|
+
return `<a${attrs} rel="noopener noreferrer" target="_blank">${content}<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" style="display:inline; height:1em;"><path d="M18.25 15.5a.75.75 0 0 1-.75-.75V7.56L7.28 17.78a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L16.44 6.5H9.25a.75.75 0 0 1 0-1.5h9a.75.75 0 0 1 .75.75v9a.75.75 0 0 1-.75.75Z"></path></svg></a>`;
|
|
1090
1118
|
}
|
|
1091
1119
|
);
|
|
1092
1120
|
}
|
|
@@ -1111,6 +1139,7 @@ function createTextEditorView(options = {}) {
|
|
|
1111
1139
|
};
|
|
1112
1140
|
}
|
|
1113
1141
|
export {
|
|
1142
|
+
TextEditorController,
|
|
1114
1143
|
createAttachFile,
|
|
1115
1144
|
createInnerHTML,
|
|
1116
1145
|
createSchema,
|