@prosekit/extensions 0.0.0-next-20240427200701 → 0.0.0-next-20240427204211
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.
@@ -415,10 +415,21 @@ expandLink: [];
|
|
415
415
|
};
|
416
416
|
}>;
|
417
417
|
|
418
|
+
/**
|
419
|
+
* Apply link marks after typing Enter.
|
420
|
+
*/
|
418
421
|
export declare function defineLinkEnterRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
|
419
422
|
|
423
|
+
/**
|
424
|
+
* Apply link marks after pressing Space.
|
425
|
+
*/
|
420
426
|
export declare function defineLinkInputRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
|
421
427
|
|
428
|
+
/**
|
429
|
+
* Apply and remove link marks to the text during typing.
|
430
|
+
*/
|
431
|
+
export declare function defineLinkMarkRule(): Extension<ExtensionTyping<string, string, CommandArgs>>;
|
432
|
+
|
422
433
|
export declare function defineLinkSpec(): Extension< {
|
423
434
|
MARKS: "link";
|
424
435
|
}>;
|
@@ -810,9 +821,11 @@ export declare function insertTable({ row, col, header, }: {
|
|
810
821
|
header: boolean;
|
811
822
|
}): Command;
|
812
823
|
|
813
|
-
export declare const
|
824
|
+
export declare const LINK_ENTER_RE: RegExp;
|
825
|
+
|
826
|
+
export declare const LINK_INPUT_RE: RegExp;
|
814
827
|
|
815
|
-
export declare const
|
828
|
+
export declare const LINK_MARK_RE: RegExp;
|
816
829
|
|
817
830
|
/**
|
818
831
|
* @public
|
@@ -0,0 +1,185 @@
|
|
1
|
+
// src/mark-rule/index.ts
|
2
|
+
import { Facet, pluginFacet } from "@prosekit/core";
|
3
|
+
import { ProseMirrorPlugin } from "@prosekit/pm/state";
|
4
|
+
|
5
|
+
// src/mark-rule/apply.ts
|
6
|
+
import { OBJECT_REPLACEMENT_CHARACTER, getMarkType } from "@prosekit/core";
|
7
|
+
import "@prosekit/pm/model";
|
8
|
+
import "@prosekit/pm/state";
|
9
|
+
|
10
|
+
// src/mark-rule/range.ts
|
11
|
+
import "@prosekit/pm/state";
|
12
|
+
function getSpanTextRanges($from, $to) {
|
13
|
+
const nodeRange = $from.blockRange($to);
|
14
|
+
if (!nodeRange) {
|
15
|
+
return [];
|
16
|
+
}
|
17
|
+
const stack = [];
|
18
|
+
let start = nodeRange.start;
|
19
|
+
for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
|
20
|
+
const child = nodeRange.parent.child(i);
|
21
|
+
stack.push([start, child]);
|
22
|
+
start += child.nodeSize;
|
23
|
+
}
|
24
|
+
const ranges = [];
|
25
|
+
while (stack.length > 0) {
|
26
|
+
const [start2, node] = stack.pop();
|
27
|
+
if (node.type.spec.code) {
|
28
|
+
continue;
|
29
|
+
}
|
30
|
+
if (node.type.isTextblock) {
|
31
|
+
ranges.push([start2 + 1, start2 + 1 + node.content.size]);
|
32
|
+
continue;
|
33
|
+
}
|
34
|
+
node.forEach((child, offset) => {
|
35
|
+
stack.push([start2 + offset + 1, child]);
|
36
|
+
});
|
37
|
+
}
|
38
|
+
return ranges;
|
39
|
+
}
|
40
|
+
function getInlineTextRange($from, $to) {
|
41
|
+
return [$from.start(), $to.end()];
|
42
|
+
}
|
43
|
+
function getTextRanges(doc, from, to) {
|
44
|
+
const $from = doc.resolve(from);
|
45
|
+
const $to = doc.resolve(to);
|
46
|
+
if ($from.sameParent($to) && $from.parent.isTextblock) {
|
47
|
+
return [getInlineTextRange($from, $to)];
|
48
|
+
} else {
|
49
|
+
const nodeRange = $from.blockRange($to);
|
50
|
+
if (!nodeRange) {
|
51
|
+
return [];
|
52
|
+
}
|
53
|
+
return getSpanTextRanges($from, $to);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
function getMapRange(transactions, oldState, newState) {
|
57
|
+
let lo = oldState.selection.from;
|
58
|
+
let hi = oldState.selection.to;
|
59
|
+
for (const tr of transactions) {
|
60
|
+
for (const map of tr.mapping.maps) {
|
61
|
+
lo = map.map(lo);
|
62
|
+
hi = map.map(hi);
|
63
|
+
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
64
|
+
lo = Math.min(lo, hi, newStart);
|
65
|
+
hi = Math.max(hi, hi, newEnd);
|
66
|
+
});
|
67
|
+
}
|
68
|
+
}
|
69
|
+
lo = Math.min(lo, hi, newState.selection.from);
|
70
|
+
hi = Math.min(lo, hi, newState.selection.to);
|
71
|
+
return [lo, hi];
|
72
|
+
}
|
73
|
+
function getCheckRanges(transactions, oldState, newState) {
|
74
|
+
const [from, to] = getMapRange(transactions, oldState, newState);
|
75
|
+
return getTextRanges(newState.doc, from, to);
|
76
|
+
}
|
77
|
+
|
78
|
+
// src/mark-rule/apply.ts
|
79
|
+
function getExpectedMarkings(rules, doc, from, to) {
|
80
|
+
const text = doc.textBetween(from, to, OBJECT_REPLACEMENT_CHARACTER);
|
81
|
+
const result = [];
|
82
|
+
for (const rule of rules) {
|
83
|
+
rule.regex.lastIndex = 0;
|
84
|
+
const matches = text.matchAll(rule.regex);
|
85
|
+
const markType = getMarkType(doc.type.schema, rule.type);
|
86
|
+
for (const match of matches) {
|
87
|
+
const index = match.index;
|
88
|
+
if (index == null)
|
89
|
+
continue;
|
90
|
+
const attrs = rule.getAttrs(match);
|
91
|
+
const mark = markType.create(attrs);
|
92
|
+
result.push([mark, from + index, from + index + match[0].length]);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return result;
|
96
|
+
}
|
97
|
+
function getReceivedMarkings(rules, doc, from, to) {
|
98
|
+
const result = [];
|
99
|
+
const schema = doc.type.schema;
|
100
|
+
const markTypes = rules.map((rule) => getMarkType(schema, rule.type));
|
101
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
102
|
+
if (!node.isInline) {
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
for (const markType of markTypes) {
|
106
|
+
const mark = node.marks.find((mark2) => mark2.type === markType);
|
107
|
+
if (mark) {
|
108
|
+
result.push([mark, pos, pos + node.nodeSize]);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
});
|
112
|
+
return result;
|
113
|
+
}
|
114
|
+
function markingEquals(a, b) {
|
115
|
+
return a[1] === b[1] && a[2] === b[2] && a[0].eq(b[0]);
|
116
|
+
}
|
117
|
+
function markingDiffs(a, b) {
|
118
|
+
return a.filter((x) => !b.some((y) => markingEquals(x, y)));
|
119
|
+
}
|
120
|
+
function applyMarkRules(rules, transactions, oldState, newState) {
|
121
|
+
if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged)) {
|
122
|
+
return null;
|
123
|
+
}
|
124
|
+
const ranges = getCheckRanges(transactions, oldState, newState);
|
125
|
+
const toRemove = [];
|
126
|
+
const toCreate = [];
|
127
|
+
for (const [from, to] of ranges) {
|
128
|
+
const expected = getExpectedMarkings(rules, newState.doc, from, to);
|
129
|
+
const received = getReceivedMarkings(rules, newState.doc, from, to);
|
130
|
+
toRemove.push(...markingDiffs(received, expected));
|
131
|
+
toCreate.push(...markingDiffs(expected, received));
|
132
|
+
}
|
133
|
+
if (toCreate.length === 0 && toRemove.length === 0) {
|
134
|
+
return null;
|
135
|
+
}
|
136
|
+
const tr = newState.tr;
|
137
|
+
for (const [mark, from, to] of toRemove) {
|
138
|
+
tr.removeMark(from, to, mark);
|
139
|
+
}
|
140
|
+
for (const [mark, from, to] of toCreate) {
|
141
|
+
tr.addMark(from, to, mark);
|
142
|
+
}
|
143
|
+
return tr;
|
144
|
+
}
|
145
|
+
|
146
|
+
// src/mark-rule/rule.ts
|
147
|
+
import "@prosekit/pm/model";
|
148
|
+
var MarkRule = class {
|
149
|
+
constructor({ regex, type, attrs = null }) {
|
150
|
+
this.regex = regex;
|
151
|
+
this.type = type;
|
152
|
+
this.getAttrs = typeof attrs === "function" ? attrs : () => attrs;
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
// src/mark-rule/index.ts
|
157
|
+
function defineMarkRule(options) {
|
158
|
+
return markRuleFacet.extension([new MarkRule(options)]);
|
159
|
+
}
|
160
|
+
var markRuleFacet = Facet.define({
|
161
|
+
converter: () => {
|
162
|
+
let rules = [];
|
163
|
+
const plugin = new ProseMirrorPlugin({
|
164
|
+
appendTransaction: (transactions, oldState, newState) => {
|
165
|
+
return applyMarkRules(rules, transactions, oldState, newState);
|
166
|
+
}
|
167
|
+
});
|
168
|
+
const pluginFunc = () => [plugin];
|
169
|
+
return {
|
170
|
+
create: (inputs) => {
|
171
|
+
rules = inputs;
|
172
|
+
return pluginFunc;
|
173
|
+
},
|
174
|
+
update: (inputs) => {
|
175
|
+
rules = inputs;
|
176
|
+
return null;
|
177
|
+
}
|
178
|
+
};
|
179
|
+
},
|
180
|
+
next: pluginFacet
|
181
|
+
});
|
182
|
+
|
183
|
+
export {
|
184
|
+
defineMarkRule
|
185
|
+
};
|
@@ -2,5 +2,6 @@ export { defineLinkSpec } from './_tsup-dts-rollup';
|
|
2
2
|
export { defineLinkCommands } from './_tsup-dts-rollup';
|
3
3
|
export { defineLinkInputRule } from './_tsup-dts-rollup';
|
4
4
|
export { defineLinkEnterRule } from './_tsup-dts-rollup';
|
5
|
+
export { defineLinkMarkRule } from './_tsup-dts-rollup';
|
5
6
|
export { defineLink } from './_tsup-dts-rollup';
|
6
7
|
export { LinkAttrs } from './_tsup-dts-rollup';
|
@@ -1,3 +1,6 @@
|
|
1
|
+
import {
|
2
|
+
defineMarkRule
|
3
|
+
} from "./chunk-TXF4SPMB.js";
|
1
4
|
import {
|
2
5
|
defineEnterRule
|
3
6
|
} from "./chunk-ASTUC4KT.js";
|
@@ -24,10 +27,12 @@ var LINK_RE_BASE_PATTERN = (
|
|
24
27
|
"((?:(?:(?:https?:)?\\/\\/)?(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:" + TLD_RE_PATTERN + "))(?::\\d{2,5})?(?:[/?#]\\S*)?)"
|
25
28
|
);
|
26
29
|
var LINK_STOP_PATTERN = "(?:\\.|\\,|\\;\\!)?";
|
27
|
-
var
|
28
|
-
var
|
29
|
-
var
|
30
|
-
var
|
30
|
+
var LINK_ENTER_PATTERN = LINK_RE_BASE_PATTERN + LINK_STOP_PATTERN + "$";
|
31
|
+
var LINK_INPUT_PATTERN = LINK_RE_BASE_PATTERN + LINK_STOP_PATTERN + "\\s$";
|
32
|
+
var LINK_MARK_PATTERN = LINK_RE_BASE_PATTERN + LINK_STOP_PATTERN;
|
33
|
+
var LINK_ENTER_RE = new RegExp(LINK_ENTER_PATTERN, "gi");
|
34
|
+
var LINK_INPUT_RE = new RegExp(LINK_INPUT_PATTERN, "gi");
|
35
|
+
var LINK_MARK_RE = new RegExp(LINK_MARK_PATTERN, "gi");
|
31
36
|
|
32
37
|
// src/link/index.ts
|
33
38
|
function defineLinkSpec() {
|
@@ -63,7 +68,7 @@ function defineLinkCommands() {
|
|
63
68
|
}
|
64
69
|
function defineLinkInputRule() {
|
65
70
|
return defineInputRule(
|
66
|
-
new InputRule(
|
71
|
+
new InputRule(LINK_INPUT_RE, (state, match, from) => {
|
67
72
|
const href = match[1];
|
68
73
|
if (!href)
|
69
74
|
return null;
|
@@ -74,7 +79,7 @@ function defineLinkInputRule() {
|
|
74
79
|
}
|
75
80
|
function defineLinkEnterRule() {
|
76
81
|
return defineEnterRule({
|
77
|
-
regex:
|
82
|
+
regex: LINK_ENTER_RE,
|
78
83
|
handler: ({ state, from, match }) => {
|
79
84
|
const href = match[1];
|
80
85
|
if (!href)
|
@@ -85,6 +90,13 @@ function defineLinkEnterRule() {
|
|
85
90
|
}
|
86
91
|
});
|
87
92
|
}
|
93
|
+
function defineLinkMarkRule() {
|
94
|
+
return defineMarkRule({
|
95
|
+
regex: LINK_MARK_RE,
|
96
|
+
type: "link",
|
97
|
+
attrs: (match) => ({ href: match[1] })
|
98
|
+
});
|
99
|
+
}
|
88
100
|
function defineLink() {
|
89
101
|
return union([
|
90
102
|
defineLinkSpec(),
|
@@ -98,5 +110,6 @@ export {
|
|
98
110
|
defineLinkCommands,
|
99
111
|
defineLinkEnterRule,
|
100
112
|
defineLinkInputRule,
|
113
|
+
defineLinkMarkRule,
|
101
114
|
defineLinkSpec
|
102
115
|
};
|
@@ -1,184 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
// src/mark-rule/apply.ts
|
6
|
-
import { OBJECT_REPLACEMENT_CHARACTER, getMarkType } from "@prosekit/core";
|
7
|
-
import "@prosekit/pm/model";
|
8
|
-
import "@prosekit/pm/state";
|
9
|
-
|
10
|
-
// src/mark-rule/range.ts
|
11
|
-
import "@prosekit/pm/state";
|
12
|
-
function getSpanTextRanges($from, $to) {
|
13
|
-
const nodeRange = $from.blockRange($to);
|
14
|
-
if (!nodeRange) {
|
15
|
-
return [];
|
16
|
-
}
|
17
|
-
const stack = [];
|
18
|
-
let start = nodeRange.start;
|
19
|
-
for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
|
20
|
-
const child = nodeRange.parent.child(i);
|
21
|
-
stack.push([start, child]);
|
22
|
-
start += child.nodeSize;
|
23
|
-
}
|
24
|
-
const ranges = [];
|
25
|
-
while (stack.length > 0) {
|
26
|
-
const [start2, node] = stack.pop();
|
27
|
-
if (node.type.spec.code) {
|
28
|
-
continue;
|
29
|
-
}
|
30
|
-
if (node.type.isTextblock) {
|
31
|
-
ranges.push([start2 + 1, start2 + 1 + node.content.size]);
|
32
|
-
continue;
|
33
|
-
}
|
34
|
-
node.forEach((child, offset) => {
|
35
|
-
stack.push([start2 + offset + 1, child]);
|
36
|
-
});
|
37
|
-
}
|
38
|
-
return ranges;
|
39
|
-
}
|
40
|
-
function getInlineTextRange($from, $to) {
|
41
|
-
return [$from.start(), $to.end()];
|
42
|
-
}
|
43
|
-
function getTextRanges(doc, from, to) {
|
44
|
-
const $from = doc.resolve(from);
|
45
|
-
const $to = doc.resolve(to);
|
46
|
-
if ($from.sameParent($to) && $from.parent.isTextblock) {
|
47
|
-
return [getInlineTextRange($from, $to)];
|
48
|
-
} else {
|
49
|
-
const nodeRange = $from.blockRange($to);
|
50
|
-
if (!nodeRange) {
|
51
|
-
return [];
|
52
|
-
}
|
53
|
-
return getSpanTextRanges($from, $to);
|
54
|
-
}
|
55
|
-
}
|
56
|
-
function getMapRange(transactions, oldState, newState) {
|
57
|
-
let lo = oldState.selection.from;
|
58
|
-
let hi = oldState.selection.to;
|
59
|
-
for (const tr of transactions) {
|
60
|
-
for (const map of tr.mapping.maps) {
|
61
|
-
lo = map.map(lo);
|
62
|
-
hi = map.map(hi);
|
63
|
-
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
64
|
-
lo = Math.min(lo, hi, newStart);
|
65
|
-
hi = Math.max(hi, hi, newEnd);
|
66
|
-
});
|
67
|
-
}
|
68
|
-
}
|
69
|
-
lo = Math.min(lo, hi, newState.selection.from);
|
70
|
-
hi = Math.min(lo, hi, newState.selection.to);
|
71
|
-
return [lo, hi];
|
72
|
-
}
|
73
|
-
function getCheckRanges(transactions, oldState, newState) {
|
74
|
-
const [from, to] = getMapRange(transactions, oldState, newState);
|
75
|
-
return getTextRanges(newState.doc, from, to);
|
76
|
-
}
|
77
|
-
|
78
|
-
// src/mark-rule/apply.ts
|
79
|
-
function getExpectedMarkings(rules, doc, from, to) {
|
80
|
-
const text = doc.textBetween(from, to, OBJECT_REPLACEMENT_CHARACTER);
|
81
|
-
const result = [];
|
82
|
-
for (const rule of rules) {
|
83
|
-
rule.regex.lastIndex = 0;
|
84
|
-
const matches = text.matchAll(rule.regex);
|
85
|
-
const markType = getMarkType(doc.type.schema, rule.type);
|
86
|
-
for (const match of matches) {
|
87
|
-
const index = match.index;
|
88
|
-
if (index == null)
|
89
|
-
continue;
|
90
|
-
const attrs = rule.getAttrs(match);
|
91
|
-
const mark = markType.create(attrs);
|
92
|
-
result.push([mark, from + index, from + index + match[0].length]);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
return result;
|
96
|
-
}
|
97
|
-
function getReceivedMarkings(rules, doc, from, to) {
|
98
|
-
const result = [];
|
99
|
-
const schema = doc.type.schema;
|
100
|
-
const markTypes = rules.map((rule) => getMarkType(schema, rule.type));
|
101
|
-
doc.nodesBetween(from, to, (node, pos) => {
|
102
|
-
if (!node.isInline) {
|
103
|
-
return;
|
104
|
-
}
|
105
|
-
for (const markType of markTypes) {
|
106
|
-
const mark = node.marks.find((mark2) => mark2.type === markType);
|
107
|
-
if (mark) {
|
108
|
-
result.push([mark, pos, pos + node.nodeSize]);
|
109
|
-
}
|
110
|
-
}
|
111
|
-
});
|
112
|
-
return result;
|
113
|
-
}
|
114
|
-
function markingEquals(a, b) {
|
115
|
-
return a[1] === b[1] && a[2] === b[2] && a[0].eq(b[0]);
|
116
|
-
}
|
117
|
-
function markingDiffs(a, b) {
|
118
|
-
return a.filter((x) => !b.some((y) => markingEquals(x, y)));
|
119
|
-
}
|
120
|
-
function applyMarkRules(rules, transactions, oldState, newState) {
|
121
|
-
if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged)) {
|
122
|
-
return null;
|
123
|
-
}
|
124
|
-
const ranges = getCheckRanges(transactions, oldState, newState);
|
125
|
-
const toRemove = [];
|
126
|
-
const toCreate = [];
|
127
|
-
for (const [from, to] of ranges) {
|
128
|
-
const expected = getExpectedMarkings(rules, newState.doc, from, to);
|
129
|
-
const received = getReceivedMarkings(rules, newState.doc, from, to);
|
130
|
-
toRemove.push(...markingDiffs(received, expected));
|
131
|
-
toCreate.push(...markingDiffs(expected, received));
|
132
|
-
}
|
133
|
-
if (toCreate.length === 0 && toRemove.length === 0) {
|
134
|
-
return null;
|
135
|
-
}
|
136
|
-
const tr = newState.tr;
|
137
|
-
for (const [mark, from, to] of toRemove) {
|
138
|
-
tr.removeMark(from, to, mark);
|
139
|
-
}
|
140
|
-
for (const [mark, from, to] of toCreate) {
|
141
|
-
tr.addMark(from, to, mark);
|
142
|
-
}
|
143
|
-
return tr;
|
144
|
-
}
|
145
|
-
|
146
|
-
// src/mark-rule/rule.ts
|
147
|
-
import "@prosekit/pm/model";
|
148
|
-
var MarkRule = class {
|
149
|
-
constructor({ regex, type, attrs = null }) {
|
150
|
-
this.regex = regex;
|
151
|
-
this.type = type;
|
152
|
-
this.getAttrs = typeof attrs === "function" ? attrs : () => attrs;
|
153
|
-
}
|
154
|
-
};
|
155
|
-
|
156
|
-
// src/mark-rule/index.ts
|
157
|
-
function defineMarkRule(options) {
|
158
|
-
return markRuleFacet.extension([new MarkRule(options)]);
|
159
|
-
}
|
160
|
-
var markRuleFacet = Facet.define({
|
161
|
-
converter: () => {
|
162
|
-
let rules = [];
|
163
|
-
const plugin = new ProseMirrorPlugin({
|
164
|
-
appendTransaction: (transactions, oldState, newState) => {
|
165
|
-
return applyMarkRules(rules, transactions, oldState, newState);
|
166
|
-
}
|
167
|
-
});
|
168
|
-
const pluginFunc = () => [plugin];
|
169
|
-
return {
|
170
|
-
create: (inputs) => {
|
171
|
-
rules = inputs;
|
172
|
-
return pluginFunc;
|
173
|
-
},
|
174
|
-
update: (inputs) => {
|
175
|
-
rules = inputs;
|
176
|
-
return null;
|
177
|
-
}
|
178
|
-
};
|
179
|
-
},
|
180
|
-
next: pluginFacet
|
181
|
-
});
|
1
|
+
import {
|
2
|
+
defineMarkRule
|
3
|
+
} from "./chunk-TXF4SPMB.js";
|
182
4
|
export {
|
183
5
|
defineMarkRule
|
184
6
|
};
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@prosekit/extensions",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.0.0-next-
|
4
|
+
"version": "0.0.0-next-20240427204211",
|
5
5
|
"private": false,
|
6
6
|
"author": {
|
7
7
|
"name": "ocavue",
|
@@ -152,8 +152,8 @@
|
|
152
152
|
"dist"
|
153
153
|
],
|
154
154
|
"dependencies": {
|
155
|
-
"@prosekit/core": "0.0.0-next-
|
156
|
-
"@prosekit/pm": "0.0.0-next-
|
155
|
+
"@prosekit/core": "0.0.0-next-20240427204211",
|
156
|
+
"@prosekit/pm": "0.0.0-next-20240427204211",
|
157
157
|
"prosemirror-dropcursor": "^1.8.1",
|
158
158
|
"prosemirror-flat-list": "^0.5.0",
|
159
159
|
"prosemirror-highlight": "^0.5.0",
|