@prosekit/extensions 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +87 -6
- package/dist/{chunk-DZAKXWWF.js → chunk-2JYT2MT7.js} +21 -41
- package/dist/{chunk-LVMTQOWG.js → chunk-PYT3MOTF.js} +14 -31
- package/dist/chunk-ZPEMHYTU.js +140 -0
- package/dist/list/style.css +4 -8
- package/dist/placeholder/style.css +1 -1
- package/dist/prosekit-extensions-autocomplete.js +33 -62
- package/dist/prosekit-extensions-blockquote.js +2 -2
- package/dist/prosekit-extensions-bold.js +3 -7
- package/dist/prosekit-extensions-code-block.js +61 -81
- package/dist/prosekit-extensions-code.js +1 -1
- package/dist/prosekit-extensions-enter-rule.js +1 -1
- package/dist/prosekit-extensions-heading.js +6 -13
- package/dist/prosekit-extensions-image.js +5 -14
- package/dist/prosekit-extensions-input-rule.js +1 -1
- package/dist/prosekit-extensions-italic.js +1 -1
- package/dist/prosekit-extensions-link.js +16 -31
- package/dist/prosekit-extensions-list.js +2 -4
- package/dist/prosekit-extensions-mark-rule.js +1 -1
- package/dist/prosekit-extensions-mention.js +4 -6
- package/dist/prosekit-extensions-placeholder.js +9 -20
- package/dist/prosekit-extensions-readonly.js +1 -1
- package/dist/prosekit-extensions-search.d.ts +3 -0
- package/dist/prosekit-extensions-search.js +49 -0
- package/dist/prosekit-extensions-strike.js +1 -1
- package/dist/prosekit-extensions-table.js +30 -68
- package/dist/prosekit-extensions-text-align.js +2 -4
- package/dist/prosekit-extensions-virtual-selection.js +6 -12
- package/dist/search/style.css +13 -0
- package/dist/shiki-import-UFUFVKJ2.js +5 -0
- package/package.json +19 -7
- package/dist/chunk-ZOBSD7ZH.js +0 -189
- package/dist/shiki-import-25BJYIO2.js +0 -5
@@ -1,6 +1,6 @@
|
|
1
1
|
import {
|
2
2
|
defineInputRule
|
3
|
-
} from "./chunk-
|
3
|
+
} from "./chunk-PYT3MOTF.js";
|
4
4
|
|
5
5
|
// src/list/index.ts
|
6
6
|
import {
|
@@ -50,9 +50,7 @@ function defineListCommands() {
|
|
50
50
|
toggleList: createToggleListCommand,
|
51
51
|
unwrapList: createUnwrapListCommand,
|
52
52
|
wrapInList: createWrapInListCommand,
|
53
|
-
insertList: (attrs) => {
|
54
|
-
return insertNode({ type: "list", attrs });
|
55
|
-
}
|
53
|
+
insertList: (attrs) => insertNode({ type: "list", attrs })
|
56
54
|
});
|
57
55
|
}
|
58
56
|
function defineList() {
|
@@ -8,18 +8,18 @@ import {
|
|
8
8
|
function defineMentionSpec() {
|
9
9
|
return defineNodeSpec({
|
10
10
|
name: "mention",
|
11
|
-
atom:
|
11
|
+
atom: !0,
|
12
12
|
group: "inline",
|
13
13
|
attrs: {
|
14
14
|
id: {},
|
15
15
|
value: {},
|
16
16
|
kind: { default: "" }
|
17
17
|
},
|
18
|
-
inline:
|
18
|
+
inline: !0,
|
19
19
|
leafText: (node) => node.attrs.value.toString(),
|
20
20
|
parseDOM: [
|
21
21
|
{
|
22
|
-
tag:
|
22
|
+
tag: "span[data-mention]",
|
23
23
|
getAttrs: (dom) => ({
|
24
24
|
id: dom.getAttribute("data-id") || "",
|
25
25
|
kind: dom.getAttribute("data-mention") || "",
|
@@ -41,9 +41,7 @@ function defineMentionSpec() {
|
|
41
41
|
}
|
42
42
|
function defineMentionCommands() {
|
43
43
|
return defineCommands({
|
44
|
-
insertMention: (attrs) => {
|
45
|
-
return insertNode({ type: "mention", attrs });
|
46
|
-
}
|
44
|
+
insertMention: (attrs) => insertNode({ type: "mention", attrs })
|
47
45
|
});
|
48
46
|
}
|
49
47
|
function defineMention() {
|
@@ -11,35 +11,24 @@ function createPlaceholderPlugin(options) {
|
|
11
11
|
key: new PluginKey("prosekit-placeholder"),
|
12
12
|
props: {
|
13
13
|
decorations: (state) => {
|
14
|
-
if (options.strategy === "doc" && !isDocEmpty(state.doc))
|
14
|
+
if (options.strategy === "doc" && !isDocEmpty(state.doc) || isInCodeBlock(state.selection))
|
15
15
|
return null;
|
16
|
-
|
17
|
-
|
18
|
-
return null;
|
19
|
-
}
|
20
|
-
const placeholderText = options.placeholder;
|
21
|
-
const deco = createPlaceholderDecoration(state, placeholderText);
|
22
|
-
if (!deco) {
|
23
|
-
return null;
|
24
|
-
}
|
25
|
-
return DecorationSet.create(state.doc, [deco]);
|
16
|
+
let placeholderText = options.placeholder, deco = createPlaceholderDecoration(state, placeholderText);
|
17
|
+
return deco ? DecorationSet.create(state.doc, [deco]) : null;
|
26
18
|
}
|
27
19
|
}
|
28
20
|
});
|
29
21
|
}
|
30
22
|
function isDocEmpty(doc) {
|
31
23
|
var _a;
|
32
|
-
return doc.childCount <= 1 && !((_a = doc.firstChild)
|
24
|
+
return doc.childCount <= 1 && !((_a = doc.firstChild) != null && _a.content.size);
|
33
25
|
}
|
34
26
|
function createPlaceholderDecoration(state, placeholderText) {
|
35
|
-
|
36
|
-
if (!selection.empty)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
if (node.content.size > 0)
|
41
|
-
return null;
|
42
|
-
const before = $pos.before();
|
27
|
+
let { selection } = state;
|
28
|
+
if (!selection.empty) return null;
|
29
|
+
let $pos = selection.$anchor, node = $pos.parent;
|
30
|
+
if (node.content.size > 0) return null;
|
31
|
+
let before = $pos.before();
|
43
32
|
return Decoration.node(before, before + node.nodeSize, {
|
44
33
|
class: "prosekit-placeholder",
|
45
34
|
"data-placeholder": placeholderText
|
@@ -0,0 +1,49 @@
|
|
1
|
+
// src/search/index.ts
|
2
|
+
import { defineCommands, definePlugin } from "@prosekit/core";
|
3
|
+
import {
|
4
|
+
SearchQuery,
|
5
|
+
findNext,
|
6
|
+
findNextNoWrap,
|
7
|
+
findPrev,
|
8
|
+
findPrevNoWrap,
|
9
|
+
replaceAll,
|
10
|
+
replaceCurrent,
|
11
|
+
replaceNext,
|
12
|
+
replaceNextNoWrap,
|
13
|
+
search
|
14
|
+
} from "prosemirror-search";
|
15
|
+
function defineSearchQuery(options) {
|
16
|
+
let query = new SearchQuery(options);
|
17
|
+
return definePlugin(search({ initialQuery: query }));
|
18
|
+
}
|
19
|
+
function scrollActiveIntoView(view) {
|
20
|
+
if (view.isDestroyed) return;
|
21
|
+
let active = view.dom.querySelector(".ProseMirror-active-search-match");
|
22
|
+
active == null || active.scrollIntoView({
|
23
|
+
block: "nearest",
|
24
|
+
inline: "nearest",
|
25
|
+
behavior: "smooth"
|
26
|
+
});
|
27
|
+
}
|
28
|
+
function withScrollActiveIntoView(command) {
|
29
|
+
return (state, dispatch, view) => {
|
30
|
+
let result = command(state, dispatch, view);
|
31
|
+
return result && dispatch && view && setTimeout(() => scrollActiveIntoView(view), 50), result;
|
32
|
+
};
|
33
|
+
}
|
34
|
+
function defineSearchCommands() {
|
35
|
+
return defineCommands({
|
36
|
+
findNext: () => withScrollActiveIntoView(findNext),
|
37
|
+
findPrev: () => withScrollActiveIntoView(findPrev),
|
38
|
+
findNextNoWrap: () => withScrollActiveIntoView(findNextNoWrap),
|
39
|
+
findPrevNoWrap: () => withScrollActiveIntoView(findPrevNoWrap),
|
40
|
+
replaceNext: () => withScrollActiveIntoView(replaceNext),
|
41
|
+
replaceNextNoWrap: () => withScrollActiveIntoView(replaceNextNoWrap),
|
42
|
+
replaceCurrent: () => withScrollActiveIntoView(replaceCurrent),
|
43
|
+
replaceAll: () => withScrollActiveIntoView(replaceAll)
|
44
|
+
});
|
45
|
+
}
|
46
|
+
export {
|
47
|
+
defineSearchCommands,
|
48
|
+
defineSearchQuery
|
49
|
+
};
|
@@ -10,23 +10,13 @@ import {
|
|
10
10
|
} from "@prosekit/core";
|
11
11
|
import { TextSelection } from "@prosekit/pm/state";
|
12
12
|
function createEmptyTable(schema, row, col, header) {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
Array.from({ length: col }, () => tableHeaderCell.createAndFill())
|
21
|
-
);
|
22
|
-
};
|
23
|
-
const createBodyRow = () => {
|
24
|
-
return tableRow.createAndFill(
|
25
|
-
null,
|
26
|
-
Array.from({ length: col }, () => tableCell.createAndFill())
|
27
|
-
);
|
28
|
-
};
|
29
|
-
const rows = [
|
13
|
+
let table = getNodeType(schema, "table"), tableRow = getNodeType(schema, "tableRow"), tableCell = getNodeType(schema, "tableCell"), tableHeaderCell = getNodeType(schema, "tableHeaderCell"), createHeaderRow = () => tableRow.createAndFill(
|
14
|
+
null,
|
15
|
+
Array.from({ length: col }, () => tableHeaderCell.createAndFill())
|
16
|
+
), createBodyRow = () => tableRow.createAndFill(
|
17
|
+
null,
|
18
|
+
Array.from({ length: col }, () => tableCell.createAndFill())
|
19
|
+
), rows = [
|
30
20
|
...Array.from({ length: header ? 1 : 0 }, createHeaderRow),
|
31
21
|
...Array.from({ length: header ? row - 1 : row }, createBodyRow)
|
32
22
|
];
|
@@ -38,41 +28,27 @@ function insertTable({
|
|
38
28
|
header
|
39
29
|
}) {
|
40
30
|
return (state, dispatch, view) => {
|
41
|
-
|
31
|
+
let table = createEmptyTable(state.schema, row, col, header);
|
42
32
|
return insertNode({ node: table })(state, dispatch, view);
|
43
33
|
};
|
44
34
|
}
|
45
35
|
var exitTable = (state, dispatch) => {
|
46
|
-
|
47
|
-
if (!$head.sameParent($anchor))
|
48
|
-
return
|
49
|
-
|
50
|
-
let
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}
|
58
|
-
}
|
59
|
-
if (tableStart < 0 || tableDepth <= 0) {
|
60
|
-
return false;
|
61
|
-
}
|
62
|
-
const above = $head.node(tableDepth - 1);
|
63
|
-
const after = $head.indexAfter(tableDepth - 1);
|
64
|
-
const type = defaultBlockAt(above.contentMatchAt(after));
|
65
|
-
const node = type == null ? void 0 : type.createAndFill();
|
66
|
-
if (!type || !node || !above.canReplaceWith(after, after, type)) {
|
67
|
-
return false;
|
68
|
-
}
|
36
|
+
let { $head, $anchor } = state.selection;
|
37
|
+
if (!$head.sameParent($anchor))
|
38
|
+
return !1;
|
39
|
+
let tableStart = -1, tableDepth = -1;
|
40
|
+
for (let depth = $head.depth; depth >= 0; depth--)
|
41
|
+
$head.node(depth).type.spec.tableRole === "table" && (tableStart = $head.before(depth), tableDepth = depth);
|
42
|
+
if (tableStart < 0 || tableDepth <= 0)
|
43
|
+
return !1;
|
44
|
+
let above = $head.node(tableDepth - 1), after = $head.indexAfter(tableDepth - 1), type = defaultBlockAt(above.contentMatchAt(after)), node = type == null ? void 0 : type.createAndFill();
|
45
|
+
if (!type || !node || !above.canReplaceWith(after, after, type))
|
46
|
+
return !1;
|
69
47
|
if (dispatch) {
|
70
|
-
|
71
|
-
|
72
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1));
|
73
|
-
dispatch(tr.scrollIntoView());
|
48
|
+
let pos = $head.after(tableDepth), tr = state.tr.replaceWith(pos, pos, node);
|
49
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1)), dispatch(tr.scrollIntoView());
|
74
50
|
}
|
75
|
-
return
|
51
|
+
return !0;
|
76
52
|
};
|
77
53
|
function defineTableCommands() {
|
78
54
|
return defineCommands({
|
@@ -95,15 +71,11 @@ var cellAttrs = {
|
|
95
71
|
colspan: { default: 1 },
|
96
72
|
rowspan: { default: 1 },
|
97
73
|
colwidth: { default: null }
|
98
|
-
};
|
99
|
-
var cellContent = "block+";
|
74
|
+
}, cellContent = "block+";
|
100
75
|
function getCellAttrs(dom) {
|
101
|
-
if (typeof dom
|
76
|
+
if (typeof dom == "string")
|
102
77
|
return {};
|
103
|
-
|
104
|
-
const widthAttr = dom.getAttribute("data-colwidth");
|
105
|
-
const widths = widthAttr && /^\d+(,\d+)*$/.test(widthAttr) ? widthAttr.split(",").map((s) => Number(s)) : null;
|
106
|
-
const colspan = Number(dom.getAttribute("colspan") || 1);
|
78
|
+
let widthAttr = dom.getAttribute("data-colwidth"), widths = widthAttr && /^\d+(,\d+)*$/.test(widthAttr) ? widthAttr.split(",").map((s) => Number(s)) : null, colspan = Number(dom.getAttribute("colspan") || 1);
|
107
79
|
return {
|
108
80
|
colspan,
|
109
81
|
rowspan: Number(dom.getAttribute("rowspan") || 1),
|
@@ -111,25 +83,15 @@ function getCellAttrs(dom) {
|
|
111
83
|
};
|
112
84
|
}
|
113
85
|
function setCellAttrs(node) {
|
114
|
-
|
115
|
-
|
116
|
-
if (pmAttrs.colspan !== 1) {
|
117
|
-
domAttrs.colspan = pmAttrs.colspan;
|
118
|
-
}
|
119
|
-
if (pmAttrs.rowspan !== 1) {
|
120
|
-
domAttrs.rowspan = pmAttrs.rowspan;
|
121
|
-
}
|
122
|
-
if (pmAttrs.colwidth) {
|
123
|
-
domAttrs["data-colwidth"] = pmAttrs.colwidth.join(",");
|
124
|
-
}
|
125
|
-
return domAttrs;
|
86
|
+
let pmAttrs = node.attrs, domAttrs = {};
|
87
|
+
return pmAttrs.colspan !== 1 && (domAttrs.colspan = pmAttrs.colspan), pmAttrs.rowspan !== 1 && (domAttrs.rowspan = pmAttrs.rowspan), pmAttrs.colwidth && (domAttrs["data-colwidth"] = pmAttrs.colwidth.join(",")), domAttrs;
|
126
88
|
}
|
127
89
|
function defineTableSpec() {
|
128
90
|
return defineNodeSpec({
|
129
91
|
name: "table",
|
130
92
|
tableRole: "table",
|
131
93
|
content: "tableRow+",
|
132
|
-
isolating:
|
94
|
+
isolating: !0,
|
133
95
|
group: "block",
|
134
96
|
parseDOM: [{ tag: "table" }],
|
135
97
|
toDOM() {
|
@@ -154,7 +116,7 @@ function defineTableCellSpec() {
|
|
154
116
|
tableRole: "cell",
|
155
117
|
content: cellContent,
|
156
118
|
attrs: cellAttrs,
|
157
|
-
isolating:
|
119
|
+
isolating: !0,
|
158
120
|
parseDOM: [{ tag: "td", getAttrs: (dom) => getCellAttrs(dom) }],
|
159
121
|
toDOM(node) {
|
160
122
|
return ["td", setCellAttrs(node), 0];
|
@@ -167,7 +129,7 @@ function defineTableHeaderCellSpec() {
|
|
167
129
|
tableRole: "header_cell",
|
168
130
|
content: cellContent,
|
169
131
|
attrs: cellAttrs,
|
170
|
-
isolating:
|
132
|
+
isolating: !0,
|
171
133
|
parseDOM: [{ tag: "th", getAttrs: (dom) => getCellAttrs(dom) }],
|
172
134
|
toDOM(node) {
|
173
135
|
return ["th", setCellAttrs(node), 0];
|
@@ -11,11 +11,9 @@ function defineTextAlignAttr(type, defaultValue) {
|
|
11
11
|
type,
|
12
12
|
attr: "textAlign",
|
13
13
|
default: defaultValue,
|
14
|
-
splittable:
|
14
|
+
splittable: !0,
|
15
15
|
toDOM: (value) => value ? ["style", `text-align:${value};`] : null,
|
16
|
-
parseDOM: (node) =>
|
17
|
-
return node.style.getPropertyValue("text-align") || null;
|
18
|
-
}
|
16
|
+
parseDOM: (node) => node.style.getPropertyValue("text-align") || null
|
19
17
|
});
|
20
18
|
}
|
21
19
|
function defineTextAlignAttrs(types, defaultValue) {
|
@@ -21,7 +21,7 @@ function getFocusState(state) {
|
|
21
21
|
var virtualSelectionPlugin = new ProseMirrorPlugin({
|
22
22
|
key,
|
23
23
|
state: {
|
24
|
-
init: () =>
|
24
|
+
init: () => !1,
|
25
25
|
apply: (tr, value) => {
|
26
26
|
var _a;
|
27
27
|
return (_a = getFocusMeta(tr)) != null ? _a : value;
|
@@ -30,22 +30,16 @@ var virtualSelectionPlugin = new ProseMirrorPlugin({
|
|
30
30
|
props: {
|
31
31
|
handleDOMEvents: {
|
32
32
|
focus: (view) => {
|
33
|
-
view.dispatch(setFocusMeta(view.state.tr,
|
33
|
+
view.dispatch(setFocusMeta(view.state.tr, !1));
|
34
34
|
},
|
35
35
|
blur: (view) => {
|
36
|
-
|
37
|
-
|
38
|
-
if (activeElement === dom)
|
39
|
-
return;
|
40
|
-
view.dispatch(setFocusMeta(view.state.tr, true));
|
36
|
+
let { dom, root } = view;
|
37
|
+
root.activeElement !== dom && view.dispatch(setFocusMeta(view.state.tr, !0));
|
41
38
|
}
|
42
39
|
},
|
43
40
|
decorations: (state) => {
|
44
|
-
|
45
|
-
|
46
|
-
return null;
|
47
|
-
}
|
48
|
-
return DecorationSet.create(doc, [
|
41
|
+
let { selection, doc } = state;
|
42
|
+
return selection.empty || !getFocusState(state) ? null : DecorationSet.create(doc, [
|
49
43
|
Decoration.inline(selection.from, selection.to, {
|
50
44
|
class: "prosekit-virtual-selection"
|
51
45
|
})
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/* src/search/style.css */
|
2
|
+
.ProseMirror-search-match {
|
3
|
+
background-color: #ffff0054;
|
4
|
+
box-shadow: 0 0 0 2px #ffff0054;
|
5
|
+
border-radius: 2px;
|
6
|
+
}
|
7
|
+
.ProseMirror.ProseMirror-focused .ProseMirror-active-search-match::selection,
|
8
|
+
.ProseMirror:not(.ProseMirror-focused) .ProseMirror-active-search-match {
|
9
|
+
background-color: #ff6a0054;
|
10
|
+
box-shadow: 0 0 0 2px #ff6a0054;
|
11
|
+
border-radius: 2px;
|
12
|
+
scroll-margin: 8px;
|
13
|
+
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@prosekit/extensions",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.
|
4
|
+
"version": "0.6.0",
|
5
5
|
"private": false,
|
6
6
|
"author": {
|
7
7
|
"name": "ocavue",
|
@@ -126,6 +126,14 @@
|
|
126
126
|
"import": "./dist/prosekit-extensions-readonly.js",
|
127
127
|
"default": "./dist/prosekit-extensions-readonly.js"
|
128
128
|
},
|
129
|
+
"./search": {
|
130
|
+
"types": "./dist/prosekit-extensions-search.d.ts",
|
131
|
+
"import": "./dist/prosekit-extensions-search.js",
|
132
|
+
"default": "./dist/prosekit-extensions-search.js"
|
133
|
+
},
|
134
|
+
"./search/style.css": {
|
135
|
+
"default": "./dist/search/style.css"
|
136
|
+
},
|
129
137
|
"./strike": {
|
130
138
|
"types": "./dist/prosekit-extensions-strike.d.ts",
|
131
139
|
"import": "./dist/prosekit-extensions-strike.js",
|
@@ -162,17 +170,18 @@
|
|
162
170
|
"dist"
|
163
171
|
],
|
164
172
|
"dependencies": {
|
165
|
-
"@prosekit/core": "^0.5.4",
|
166
|
-
"@prosekit/pm": "^0.1.4",
|
167
173
|
"prosemirror-dropcursor": "^1.8.1",
|
168
174
|
"prosemirror-flat-list": "^0.5.0",
|
169
|
-
"prosemirror-highlight": "^0.
|
175
|
+
"prosemirror-highlight": "^0.8.0",
|
176
|
+
"prosemirror-search": "^0.1.0",
|
170
177
|
"prosemirror-tables": "^1.3.7",
|
171
|
-
"shiki": "^1.
|
178
|
+
"shiki": "^1.9.0",
|
179
|
+
"@prosekit/core": "^0.6.0",
|
180
|
+
"@prosekit/pm": "^0.1.5"
|
172
181
|
},
|
173
182
|
"devDependencies": {
|
174
|
-
"tsup": "^8.0
|
175
|
-
"typescript": "^5.
|
183
|
+
"tsup": "^8.1.0",
|
184
|
+
"typescript": "^5.5.2",
|
176
185
|
"vitest": "^1.6.0",
|
177
186
|
"@prosekit/dev": "0.0.0"
|
178
187
|
},
|
@@ -240,6 +249,9 @@
|
|
240
249
|
"readonly": [
|
241
250
|
"./dist/prosekit-extensions-readonly.d.ts"
|
242
251
|
],
|
252
|
+
"search": [
|
253
|
+
"./dist/prosekit-extensions-search.d.ts"
|
254
|
+
],
|
243
255
|
"strike": [
|
244
256
|
"./dist/prosekit-extensions-strike.d.ts"
|
245
257
|
],
|
package/dist/chunk-ZOBSD7ZH.js
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
// src/mark-rule/extension.ts
|
2
|
-
import {
|
3
|
-
defineFacet,
|
4
|
-
defineFacetPayload,
|
5
|
-
pluginFacet
|
6
|
-
} from "@prosekit/core";
|
7
|
-
import {
|
8
|
-
PluginKey,
|
9
|
-
ProseMirrorPlugin
|
10
|
-
} from "@prosekit/pm/state";
|
11
|
-
|
12
|
-
// src/mark-rule/apply.ts
|
13
|
-
import {
|
14
|
-
OBJECT_REPLACEMENT_CHARACTER,
|
15
|
-
getMarkType,
|
16
|
-
maybeRun
|
17
|
-
} from "@prosekit/core";
|
18
|
-
import "@prosekit/pm/model";
|
19
|
-
import "@prosekit/pm/state";
|
20
|
-
|
21
|
-
// src/mark-rule/range.ts
|
22
|
-
import "@prosekit/pm/state";
|
23
|
-
function getSpanTextRanges($from, $to) {
|
24
|
-
const nodeRange = $from.blockRange($to);
|
25
|
-
if (!nodeRange) {
|
26
|
-
return [];
|
27
|
-
}
|
28
|
-
const stack = [];
|
29
|
-
let start = nodeRange.start;
|
30
|
-
for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
|
31
|
-
const child = nodeRange.parent.child(i);
|
32
|
-
stack.push([start, child]);
|
33
|
-
start += child.nodeSize;
|
34
|
-
}
|
35
|
-
const ranges = [];
|
36
|
-
while (stack.length > 0) {
|
37
|
-
const [start2, node] = stack.pop();
|
38
|
-
if (node.type.spec.code) {
|
39
|
-
continue;
|
40
|
-
}
|
41
|
-
if (node.type.isTextblock) {
|
42
|
-
ranges.push([start2 + 1, start2 + 1 + node.content.size]);
|
43
|
-
continue;
|
44
|
-
}
|
45
|
-
node.forEach((child, offset) => {
|
46
|
-
stack.push([start2 + offset + 1, child]);
|
47
|
-
});
|
48
|
-
}
|
49
|
-
return ranges;
|
50
|
-
}
|
51
|
-
function getInlineTextRange($from, $to) {
|
52
|
-
return [$from.start(), $to.end()];
|
53
|
-
}
|
54
|
-
function getTextRanges(doc, from, to) {
|
55
|
-
const $from = doc.resolve(from);
|
56
|
-
const $to = doc.resolve(to);
|
57
|
-
if ($from.sameParent($to) && $from.parent.isTextblock) {
|
58
|
-
return [getInlineTextRange($from, $to)];
|
59
|
-
} else {
|
60
|
-
const nodeRange = $from.blockRange($to);
|
61
|
-
if (!nodeRange) {
|
62
|
-
return [];
|
63
|
-
}
|
64
|
-
return getSpanTextRanges($from, $to);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
function getMapRange(transactions, oldState, newState) {
|
68
|
-
let lo = oldState.selection.from;
|
69
|
-
let hi = oldState.selection.to;
|
70
|
-
for (const tr of transactions) {
|
71
|
-
for (const map of tr.mapping.maps) {
|
72
|
-
lo = map.map(lo);
|
73
|
-
hi = map.map(hi);
|
74
|
-
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
75
|
-
lo = Math.min(lo, hi, newStart);
|
76
|
-
hi = Math.max(lo, hi, newEnd);
|
77
|
-
});
|
78
|
-
}
|
79
|
-
}
|
80
|
-
lo = Math.min(lo, hi, newState.selection.from);
|
81
|
-
hi = Math.min(lo, hi, newState.selection.to);
|
82
|
-
return [lo, hi];
|
83
|
-
}
|
84
|
-
function getCheckRanges(transactions, oldState, newState) {
|
85
|
-
const [from, to] = getMapRange(transactions, oldState, newState);
|
86
|
-
return getTextRanges(newState.doc, from, to);
|
87
|
-
}
|
88
|
-
|
89
|
-
// src/mark-rule/apply.ts
|
90
|
-
function getExpectedMarkings(rules, doc, from, to) {
|
91
|
-
const text = doc.textBetween(from, to, null, OBJECT_REPLACEMENT_CHARACTER);
|
92
|
-
const ranges = [];
|
93
|
-
for (const rule of rules) {
|
94
|
-
rule.regex.lastIndex = 0;
|
95
|
-
const matches = text.matchAll(rule.regex);
|
96
|
-
const markType = getMarkType(doc.type.schema, rule.type);
|
97
|
-
for (const match of matches) {
|
98
|
-
const index = match.index;
|
99
|
-
if (index == null)
|
100
|
-
continue;
|
101
|
-
const attrs = maybeRun(rule.attrs, match);
|
102
|
-
const mark = markType.create(attrs);
|
103
|
-
ranges.push([from + index, from + index + match[0].length, mark]);
|
104
|
-
}
|
105
|
-
}
|
106
|
-
ranges.sort((a, b) => a[0] - b[0] || b[1] - a[1]);
|
107
|
-
const result = [];
|
108
|
-
let freeIndex = 0;
|
109
|
-
for (const range of ranges) {
|
110
|
-
if (range[0] >= freeIndex) {
|
111
|
-
result.push(range);
|
112
|
-
freeIndex = range[1];
|
113
|
-
}
|
114
|
-
}
|
115
|
-
return result;
|
116
|
-
}
|
117
|
-
function getReceivedMarkings(rules, doc, from, to) {
|
118
|
-
const result = [];
|
119
|
-
const schema = doc.type.schema;
|
120
|
-
const markTypes = rules.map((rule) => getMarkType(schema, rule.type));
|
121
|
-
doc.nodesBetween(from, to, (node, pos) => {
|
122
|
-
if (!node.isInline) {
|
123
|
-
return;
|
124
|
-
}
|
125
|
-
for (const markType of markTypes) {
|
126
|
-
const mark = node.marks.find((mark2) => mark2.type === markType);
|
127
|
-
if (mark) {
|
128
|
-
result.push([pos, pos + node.nodeSize, mark]);
|
129
|
-
}
|
130
|
-
}
|
131
|
-
});
|
132
|
-
return result;
|
133
|
-
}
|
134
|
-
function markRangeEquals(a, b) {
|
135
|
-
return a[0] === b[0] && a[1] === b[1] && a[2].eq(b[2]);
|
136
|
-
}
|
137
|
-
function markRangeDiffs(a, b) {
|
138
|
-
return a.filter((x) => !b.some((y) => markRangeEquals(x, y)));
|
139
|
-
}
|
140
|
-
function applyMarkRules(rules, transactions, oldState, newState) {
|
141
|
-
if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged)) {
|
142
|
-
return null;
|
143
|
-
}
|
144
|
-
const ranges = getCheckRanges(transactions, oldState, newState);
|
145
|
-
const toRemove = [];
|
146
|
-
const toCreate = [];
|
147
|
-
for (const [from, to] of ranges) {
|
148
|
-
const expected = getExpectedMarkings(rules, newState.doc, from, to);
|
149
|
-
const received = getReceivedMarkings(rules, newState.doc, from, to);
|
150
|
-
toRemove.push(...markRangeDiffs(received, expected));
|
151
|
-
toCreate.push(...markRangeDiffs(expected, received));
|
152
|
-
}
|
153
|
-
if (toCreate.length === 0 && toRemove.length === 0) {
|
154
|
-
return null;
|
155
|
-
}
|
156
|
-
const tr = newState.tr;
|
157
|
-
for (const [from, to, mark] of toRemove) {
|
158
|
-
tr.removeMark(from, to, mark);
|
159
|
-
}
|
160
|
-
for (const [from, to, mark] of toCreate) {
|
161
|
-
tr.addMark(from, to, mark);
|
162
|
-
}
|
163
|
-
return tr;
|
164
|
-
}
|
165
|
-
|
166
|
-
// src/mark-rule/extension.ts
|
167
|
-
function defineMarkRule(options) {
|
168
|
-
return defineFacetPayload(markRuleFacet, [options]);
|
169
|
-
}
|
170
|
-
var markRuleFacet = defineFacet({
|
171
|
-
reduce: () => {
|
172
|
-
let rules = [];
|
173
|
-
const plugin = new ProseMirrorPlugin({
|
174
|
-
key: new PluginKey("prosekit-mark-rule"),
|
175
|
-
appendTransaction: (transactions, oldState, newState) => {
|
176
|
-
return applyMarkRules(rules, transactions, oldState, newState);
|
177
|
-
}
|
178
|
-
});
|
179
|
-
return function reducer(input) {
|
180
|
-
rules = input;
|
181
|
-
return plugin;
|
182
|
-
};
|
183
|
-
},
|
184
|
-
parent: pluginFacet
|
185
|
-
});
|
186
|
-
|
187
|
-
export {
|
188
|
-
defineMarkRule
|
189
|
-
};
|