@domternal/core 0.2.1 → 0.3.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/README.md +13 -10
- package/dist/helpers/inputRulesPlugin.d.ts.map +1 -0
- package/dist/index.cjs +352 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -1
- package/dist/index.d.ts +177 -1
- package/dist/index.js +349 -64
- package/dist/index.js.map +1 -1
- package/dist/nodes/HorizontalRule.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@domternal/core)
|
|
4
4
|
[](https://github.com/domternal/domternal/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
A lightweight, extensible rich text editor toolkit built on [ProseMirror](https://prosemirror.net/)
|
|
6
|
+
A lightweight, extensible rich text editor toolkit built on <u>[ProseMirror](https://prosemirror.net/)</u>. Framework-agnostic headless core with first-class Angular support.
|
|
7
|
+
Use it headless with vanilla JS/TS, add the built-in toolbar and theme, or drop in ready-made Angular components. Fully tree-shakeable, import only what you use, unused extensions are stripped from your bundle.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
## Links
|
|
10
|
+
|
|
11
|
+
<u>[Website](https://domternal.dev)</u> • <u>[Documentation](https://domternal.dev/v1/introduction)</u> • <u>[StackBlitz (Vanilla TS)](https://stackblitz.com/edit/domternal-vanilla-full-example)</u> • <u>[StackBlitz (Angular)](https://stackblitz.com/edit/domternal-angular-full-example)</u>
|
|
9
12
|
|
|
10
13
|
## Features
|
|
11
14
|
|
|
12
|
-
See [Packages & Bundle Size](https://domternal.dev/v1/packages) for a full breakdown of all packages and what each one includes.
|
|
15
|
+
See <u>[Packages & Bundle Size](https://domternal.dev/v1/packages)</u> for a full breakdown of all packages and what each one includes.
|
|
13
16
|
|
|
14
17
|
- **Headless core** - use with any framework or vanilla JS/TS
|
|
15
18
|
- **Angular components** - editor, toolbar, bubble menu, floating menu, emoji picker (signals, OnPush, zoneless-ready)
|
|
@@ -17,20 +20,20 @@ See [Packages & Bundle Size](https://domternal.dev/v1/packages) for a full break
|
|
|
17
20
|
- **140+ chainable commands** - `editor.chain().focus().toggleBold().run()`
|
|
18
21
|
- **Full table support** - cell merging, column resize, row/column controls, cell toolbar, all free and MIT licensed
|
|
19
22
|
- **Tree-shakeable** - import only what you use, your bundler strips the rest
|
|
20
|
-
- **~38 KB gzipped** (own code), [~108 KB total](https://domternal.dev/v1/packages) with ProseMirror
|
|
23
|
+
- **~38 KB gzipped** (own code), <u>[~108 KB total](https://domternal.dev/v1/packages)</u> with ProseMirror
|
|
21
24
|
- **TypeScript first** - 100% typed, zero `any`
|
|
22
|
-
- **4,
|
|
25
|
+
- **4,400+ tests** - 2,687 unit tests and 1,796 E2E tests across 37 Playwright specs
|
|
23
26
|
- **Light and dark theme** - 70+ CSS custom properties for full visual control
|
|
24
27
|
- **Inline styles export** - `getHTML({ styled: true })` produces inline CSS ready for email clients, CMS, and Google Docs
|
|
25
28
|
- **SSR helpers** - `generateHTML`, `generateJSON`, `generateText` for server-side rendering
|
|
26
29
|
|
|
27
30
|
## Documentation
|
|
28
31
|
|
|
29
|
-
- [Getting Started](https://domternal.dev/v1/getting-started) - install and create your first editor
|
|
30
|
-
- [Introduction](https://domternal.dev/v1/introduction) - core concepts, architecture, and design decisions
|
|
31
|
-
- [Packages & Bundle Size](https://domternal.dev/v1/packages) - what each package includes and bundle size breakdown
|
|
32
|
-
- [Blog](https://domternal.dev/blog)
|
|
32
|
+
- <u>[Getting Started](https://domternal.dev/v1/getting-started)</u> - install and create your first editor
|
|
33
|
+
- <u>[Introduction](https://domternal.dev/v1/introduction)</u> - core concepts, architecture, and design decisions
|
|
34
|
+
- <u>[Packages & Bundle Size](https://domternal.dev/v1/packages)</u> - what each package includes and bundle size breakdown
|
|
35
|
+
- <u>[Blog](https://domternal.dev/blog)</u>
|
|
33
36
|
|
|
34
37
|
## License
|
|
35
38
|
|
|
36
|
-
[MIT](https://github.com/domternal/domternal/blob/main/LICENSE)
|
|
39
|
+
<u>[MIT](https://github.com/domternal/domternal/blob/main/LICENSE)</u>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputRulesPlugin.d.ts","sourceRoot":"","sources":["../../src/helpers/inputRulesPlugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AA6G1D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GAAG,MAAM,CA4C1E"}
|
package/dist/index.cjs
CHANGED
|
@@ -4,8 +4,8 @@ var state = require('@domternal/pm/state');
|
|
|
4
4
|
var view = require('@domternal/pm/view');
|
|
5
5
|
var model = require('@domternal/pm/model');
|
|
6
6
|
var keymap = require('@domternal/pm/keymap');
|
|
7
|
-
var inputrules = require('@domternal/pm/inputrules');
|
|
8
7
|
var commands = require('@domternal/pm/commands');
|
|
8
|
+
var inputrules = require('@domternal/pm/inputrules');
|
|
9
9
|
var transform = require('@domternal/pm/transform');
|
|
10
10
|
var schemaList = require('@domternal/pm/schema-list');
|
|
11
11
|
var dom = require('@floating-ui/dom');
|
|
@@ -109,6 +109,112 @@ var EventEmitter = class {
|
|
|
109
109
|
return Array.from(this.callbacks.keys());
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
|
+
var MAX_MATCH = 500;
|
|
113
|
+
function run(view, from, to, text, rules, plugin) {
|
|
114
|
+
if (view.composing) return false;
|
|
115
|
+
const state = view.state;
|
|
116
|
+
const $from = state.doc.resolve(from);
|
|
117
|
+
const textBefore = $from.parent.textBetween(
|
|
118
|
+
Math.max(0, $from.parentOffset - MAX_MATCH),
|
|
119
|
+
$from.parentOffset,
|
|
120
|
+
null,
|
|
121
|
+
"\uFFFC"
|
|
122
|
+
) + text;
|
|
123
|
+
for (const rawRule of rules) {
|
|
124
|
+
const rule = rawRule;
|
|
125
|
+
if (!rule.inCodeMark && $from.marks().some((m) => m.type.spec.code)) continue;
|
|
126
|
+
if ($from.parent.type.spec.code) {
|
|
127
|
+
if (!rule.inCode) continue;
|
|
128
|
+
} else if (rule.inCode === "only") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const match = rule.match.exec(textBefore);
|
|
132
|
+
if (!match || match[0].length < text.length) continue;
|
|
133
|
+
const startPos = from - (match[0].length - text.length);
|
|
134
|
+
if (!rule.inCodeMark) {
|
|
135
|
+
const codeMarks = [];
|
|
136
|
+
state.doc.nodesBetween(startPos, $from.pos, (node) => {
|
|
137
|
+
if (node.isInline && node.marks.some((m) => m.type.spec.code)) codeMarks.push(true);
|
|
138
|
+
});
|
|
139
|
+
if (codeMarks.length > 0) continue;
|
|
140
|
+
}
|
|
141
|
+
const tr = rule.handler(state, match, startPos, to);
|
|
142
|
+
if (!tr) continue;
|
|
143
|
+
if (rule.undoable) {
|
|
144
|
+
tr.setMeta(plugin, { transform: tr, from, to, text });
|
|
145
|
+
}
|
|
146
|
+
view.dispatch(tr);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
function undoInputRule(plugin, state$1, dispatch) {
|
|
152
|
+
const undoable = plugin.getState(state$1);
|
|
153
|
+
if (!undoable) return false;
|
|
154
|
+
if (dispatch) {
|
|
155
|
+
const tr = state$1.tr;
|
|
156
|
+
const toUndo = undoable.transform;
|
|
157
|
+
for (let j = toUndo.steps.length - 1; j >= 0; j--) {
|
|
158
|
+
const step = toUndo.steps[j];
|
|
159
|
+
const doc = toUndo.docs[j];
|
|
160
|
+
if (step && doc) tr.step(step.invert(doc));
|
|
161
|
+
}
|
|
162
|
+
if (undoable.text) {
|
|
163
|
+
const marks = tr.doc.resolve(undoable.from).marks();
|
|
164
|
+
tr.replaceWith(undoable.from, undoable.to, state$1.schema.text(undoable.text, marks));
|
|
165
|
+
const endPos = undoable.from + undoable.text.length;
|
|
166
|
+
if (endPos <= tr.doc.content.size) {
|
|
167
|
+
tr.setSelection(state.TextSelection.create(tr.doc, endPos));
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
tr.delete(undoable.from, undoable.to);
|
|
171
|
+
}
|
|
172
|
+
dispatch(tr);
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
function inputRulesPlugin({ rules }) {
|
|
177
|
+
const plugin = new state.Plugin({
|
|
178
|
+
state: {
|
|
179
|
+
init() {
|
|
180
|
+
return null;
|
|
181
|
+
},
|
|
182
|
+
apply(tr, prev) {
|
|
183
|
+
const stored = tr.getMeta(plugin);
|
|
184
|
+
if (stored) return stored;
|
|
185
|
+
if (tr.getMeta("appendedTransaction")) return prev;
|
|
186
|
+
return tr.selectionSet || tr.docChanged ? null : prev;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
props: {
|
|
190
|
+
handleTextInput(view, from, to, text) {
|
|
191
|
+
return run(view, from, to, text, rules, plugin);
|
|
192
|
+
},
|
|
193
|
+
handleKeyDown(view, event) {
|
|
194
|
+
if (event.key === "Backspace" && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
195
|
+
return undoInputRule(plugin, view.state, (tr) => {
|
|
196
|
+
view.dispatch(tr);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
},
|
|
201
|
+
handleDOMEvents: {
|
|
202
|
+
compositionend: (view) => {
|
|
203
|
+
setTimeout(() => {
|
|
204
|
+
const { $cursor } = view.state.selection;
|
|
205
|
+
if ($cursor) {
|
|
206
|
+
run(view, $cursor.pos, $cursor.pos, "", rules, plugin);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
// Tag so external undoInputRule can also find this plugin
|
|
213
|
+
isInputRules: true
|
|
214
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
215
|
+
});
|
|
216
|
+
return plugin;
|
|
217
|
+
}
|
|
112
218
|
|
|
113
219
|
// src/helpers/callOrReturn.ts
|
|
114
220
|
function callOrReturn(value, context, ...args) {
|
|
@@ -486,14 +592,14 @@ var ExtensionManager = class {
|
|
|
486
592
|
*/
|
|
487
593
|
buildPlugins() {
|
|
488
594
|
const plugins = [];
|
|
595
|
+
const rules = this.collectInputRules();
|
|
596
|
+
if (rules.length > 0) {
|
|
597
|
+
plugins.push(inputRulesPlugin({ rules }));
|
|
598
|
+
}
|
|
489
599
|
const shortcuts = this.collectKeyboardShortcuts();
|
|
490
600
|
if (Object.keys(shortcuts).length > 0) {
|
|
491
601
|
plugins.push(keymap.keymap(shortcuts));
|
|
492
602
|
}
|
|
493
|
-
const rules = this.collectInputRules();
|
|
494
|
-
if (rules.length > 0) {
|
|
495
|
-
plugins.push(inputrules.inputRules({ rules }));
|
|
496
|
-
}
|
|
497
603
|
for (const ext of this._extensions) {
|
|
498
604
|
const addPlugins = ext.config.addProseMirrorPlugins;
|
|
499
605
|
if (addPlugins) {
|
|
@@ -936,7 +1042,7 @@ function isDocumentEmpty(doc) {
|
|
|
936
1042
|
});
|
|
937
1043
|
}
|
|
938
1044
|
function markInputRule(options) {
|
|
939
|
-
const { find: find2, type, getAttributes } = options;
|
|
1045
|
+
const { find: find2, type, getAttributes, undoable } = options;
|
|
940
1046
|
return new inputrules.InputRule(
|
|
941
1047
|
find2,
|
|
942
1048
|
(state, match, start, end) => {
|
|
@@ -953,7 +1059,8 @@ function markInputRule(options) {
|
|
|
953
1059
|
tr.addMark(start, start + textContent.length, type.create(attributes ?? void 0));
|
|
954
1060
|
tr.removeStoredMark(type);
|
|
955
1061
|
return tr;
|
|
956
|
-
}
|
|
1062
|
+
},
|
|
1063
|
+
undoable !== void 0 ? { undoable } : {}
|
|
957
1064
|
);
|
|
958
1065
|
}
|
|
959
1066
|
var markInputRulePatterns = {
|
|
@@ -978,6 +1085,69 @@ var markInputRulePatterns = {
|
|
|
978
1085
|
*/
|
|
979
1086
|
highlight: /(?:==)([^=]+)(?:==)$/
|
|
980
1087
|
};
|
|
1088
|
+
function wrappingInputRule(options) {
|
|
1089
|
+
const { find: find2, type, getAttributes = null, joinPredicate, undoable, guard } = options;
|
|
1090
|
+
return new inputrules.InputRule(
|
|
1091
|
+
find2,
|
|
1092
|
+
(state, match, start, end) => {
|
|
1093
|
+
if (guard && !guard(state)) return null;
|
|
1094
|
+
const attrs = getAttributes instanceof Function ? getAttributes(match) : getAttributes;
|
|
1095
|
+
const tr = state.tr.delete(start, end);
|
|
1096
|
+
const $start = tr.doc.resolve(start);
|
|
1097
|
+
const range = $start.blockRange();
|
|
1098
|
+
const wrapping = range && transform.findWrapping(range, type, attrs);
|
|
1099
|
+
if (!wrapping) return null;
|
|
1100
|
+
tr.wrap(range, wrapping);
|
|
1101
|
+
const before = tr.doc.resolve(start - 1).nodeBefore;
|
|
1102
|
+
if (before?.type === type && transform.canJoin(tr.doc, start - 1) && (!joinPredicate || joinPredicate(match, before))) {
|
|
1103
|
+
tr.join(start - 1);
|
|
1104
|
+
}
|
|
1105
|
+
return tr;
|
|
1106
|
+
},
|
|
1107
|
+
undoable !== void 0 ? { undoable } : {}
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
function notInsideList(state) {
|
|
1111
|
+
const { $from } = state.selection;
|
|
1112
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
1113
|
+
const name = $from.node(d).type.name;
|
|
1114
|
+
if (name === "listItem" || name === "taskItem") return false;
|
|
1115
|
+
}
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
function textblockTypeInputRule(options) {
|
|
1119
|
+
const { find: find2, type, getAttributes = null, undoable } = options;
|
|
1120
|
+
return new inputrules.InputRule(
|
|
1121
|
+
find2,
|
|
1122
|
+
(state, match, start, end) => {
|
|
1123
|
+
const $start = state.doc.resolve(start);
|
|
1124
|
+
const attrs = getAttributes instanceof Function ? getAttributes(match) : getAttributes;
|
|
1125
|
+
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), type)) return null;
|
|
1126
|
+
return state.tr.delete(start, end).setBlockType(start, start, type, attrs);
|
|
1127
|
+
},
|
|
1128
|
+
undoable !== void 0 ? { undoable } : {}
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
function textInputRule(options) {
|
|
1132
|
+
const { find: find2, replace, undoable } = options;
|
|
1133
|
+
return new inputrules.InputRule(
|
|
1134
|
+
find2,
|
|
1135
|
+
(state, _match, start, end) => state.tr.replaceWith(start, end, state.schema.text(replace)),
|
|
1136
|
+
undoable !== void 0 ? { undoable } : {}
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
function nodeInputRule(options) {
|
|
1140
|
+
const { find: find2, type, getAttributes = null, undoable } = options;
|
|
1141
|
+
return new inputrules.InputRule(
|
|
1142
|
+
find2,
|
|
1143
|
+
(state, match, start, end) => {
|
|
1144
|
+
const attrs = getAttributes instanceof Function ? getAttributes(match, state) : getAttributes;
|
|
1145
|
+
if (getAttributes instanceof Function && attrs === null) return null;
|
|
1146
|
+
return state.tr.replaceWith(start, end, type.create(attrs));
|
|
1147
|
+
},
|
|
1148
|
+
undoable !== void 0 ? { undoable } : {}
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
981
1151
|
|
|
982
1152
|
// src/helpers/isValidUrl.ts
|
|
983
1153
|
function isValidUrl(url, options = {}) {
|
|
@@ -1821,8 +1991,8 @@ var wrapIn = (nodeName, attributes) => ({ state, tr, dispatch }) => {
|
|
|
1821
1991
|
return true;
|
|
1822
1992
|
};
|
|
1823
1993
|
var toggleWrap = (nodeName, attributes) => (props) => {
|
|
1824
|
-
const { state
|
|
1825
|
-
const nodeType = state
|
|
1994
|
+
const { state, tr, dispatch } = props;
|
|
1995
|
+
const nodeType = state.schema.nodes[nodeName];
|
|
1826
1996
|
if (!nodeType) {
|
|
1827
1997
|
return false;
|
|
1828
1998
|
}
|
|
@@ -1866,8 +2036,18 @@ var toggleWrap = (nodeName, attributes) => (props) => {
|
|
|
1866
2036
|
const allWrapped = contentBlocks.length > 0 ? contentBlocks.every(({ pos }) => isInsideWrap(pos)) : isInsideWrap(from);
|
|
1867
2037
|
if (allWrapped) {
|
|
1868
2038
|
const first = contentBlocks[0];
|
|
1869
|
-
|
|
1870
|
-
|
|
2039
|
+
const last = contentBlocks[contentBlocks.length - 1];
|
|
2040
|
+
if (first && last) {
|
|
2041
|
+
const $liftFrom = tr.doc.resolve(first.pos + 1);
|
|
2042
|
+
const $liftTo = tr.doc.resolve(last.pos + 1);
|
|
2043
|
+
const range = $liftFrom.blockRange($liftTo);
|
|
2044
|
+
if (!range) return false;
|
|
2045
|
+
const target = transform.liftTarget(range);
|
|
2046
|
+
if (target === null) return false;
|
|
2047
|
+
if (!dispatch) return true;
|
|
2048
|
+
tr.lift(range, target).scrollIntoView();
|
|
2049
|
+
dispatch(tr);
|
|
2050
|
+
return true;
|
|
1871
2051
|
}
|
|
1872
2052
|
return lift()(props);
|
|
1873
2053
|
}
|
|
@@ -1901,6 +2081,36 @@ var lift = () => ({ tr, dispatch }) => {
|
|
|
1901
2081
|
dispatch(tr);
|
|
1902
2082
|
return true;
|
|
1903
2083
|
};
|
|
2084
|
+
function joinListBackwards(tr, listType) {
|
|
2085
|
+
const { $from } = tr.selection;
|
|
2086
|
+
for (let d = $from.depth; d >= 0; d--) {
|
|
2087
|
+
if ($from.node(d).type === listType) {
|
|
2088
|
+
const listPos = $from.before(d);
|
|
2089
|
+
if (listPos > 0 && transform.canJoin(tr.doc, listPos)) {
|
|
2090
|
+
const nodeBefore = tr.doc.resolve(listPos - 1).parent;
|
|
2091
|
+
if (nodeBefore.type === listType) {
|
|
2092
|
+
tr.join(listPos);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
function joinListForwards(tr, listType) {
|
|
2100
|
+
const { $from } = tr.selection;
|
|
2101
|
+
for (let d = $from.depth; d >= 0; d--) {
|
|
2102
|
+
if ($from.node(d).type === listType) {
|
|
2103
|
+
const after = $from.after(d);
|
|
2104
|
+
if (after < tr.doc.content.size && transform.canJoin(tr.doc, after)) {
|
|
2105
|
+
const nodeAfter = tr.doc.nodeAt(after);
|
|
2106
|
+
if (nodeAfter?.type === listType) {
|
|
2107
|
+
tr.join(after);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
1904
2114
|
var toggleList = (listNodeName, listItemNodeName, attributes) => ({ state: state$1, tr, dispatch }) => {
|
|
1905
2115
|
const listType = state$1.schema.nodes[listNodeName];
|
|
1906
2116
|
const listItemType = state$1.schema.nodes[listItemNodeName];
|
|
@@ -2056,11 +2266,56 @@ var toggleList = (listNodeName, listItemNodeName, attributes) => ({ state: state
|
|
|
2056
2266
|
dispatch(tr);
|
|
2057
2267
|
return true;
|
|
2058
2268
|
}
|
|
2059
|
-
const
|
|
2269
|
+
const blocksInList = contentBlocks.filter((b) => b.inSomeList);
|
|
2270
|
+
if (blocksInList.length === 0) {
|
|
2271
|
+
const { $from: $wf, $to: $wt } = tr.selection;
|
|
2272
|
+
const wr = $wf.blockRange($wt);
|
|
2273
|
+
if (!wr) return false;
|
|
2274
|
+
if (!schemaList.wrapRangeInList(dispatch ? tr : null, wr, listType, attributes)) return false;
|
|
2275
|
+
if (dispatch) {
|
|
2276
|
+
joinListBackwards(tr, listType);
|
|
2277
|
+
joinListForwards(tr, listType);
|
|
2278
|
+
dispatch(tr.scrollIntoView());
|
|
2279
|
+
}
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2282
|
+
if (!dispatch) return true;
|
|
2283
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2284
|
+
const listPositions = [];
|
|
2285
|
+
for (const block of blocksInList) {
|
|
2286
|
+
const $pos = tr.doc.resolve(block.pos);
|
|
2287
|
+
for (let d = $pos.depth; d >= 0; d--) {
|
|
2288
|
+
const groups = ($pos.node(d).type.spec.group ?? "").split(/\s+/);
|
|
2289
|
+
if (groups.includes("list")) {
|
|
2290
|
+
const lpos = $pos.before(d);
|
|
2291
|
+
if (!seen.has(lpos)) {
|
|
2292
|
+
seen.add(lpos);
|
|
2293
|
+
listPositions.push(lpos);
|
|
2294
|
+
}
|
|
2295
|
+
break;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
listPositions.sort((a, b) => b - a);
|
|
2300
|
+
for (const pos of listPositions) {
|
|
2301
|
+
const listNode = tr.doc.nodeAt(pos);
|
|
2302
|
+
if (!listNode) continue;
|
|
2303
|
+
const children = [];
|
|
2304
|
+
listNode.forEach((item) => {
|
|
2305
|
+
item.forEach((child) => children.push(child));
|
|
2306
|
+
});
|
|
2307
|
+
tr.replaceWith(pos, pos + listNode.nodeSize, children);
|
|
2308
|
+
}
|
|
2309
|
+
const mappedFrom = tr.mapping.map(from, -1);
|
|
2310
|
+
const mappedTo = tr.mapping.map(to, 1);
|
|
2311
|
+
const $wrapFrom = tr.doc.resolve(mappedFrom);
|
|
2312
|
+
const $wrapTo = tr.doc.resolve(mappedTo);
|
|
2060
2313
|
const wrapRange = $wrapFrom.blockRange($wrapTo);
|
|
2061
2314
|
if (!wrapRange) return false;
|
|
2062
|
-
|
|
2063
|
-
|
|
2315
|
+
schemaList.wrapRangeInList(tr, wrapRange, listType, attributes);
|
|
2316
|
+
joinListBackwards(tr, listType);
|
|
2317
|
+
joinListForwards(tr, listType);
|
|
2318
|
+
dispatch(tr.scrollIntoView());
|
|
2064
2319
|
return true;
|
|
2065
2320
|
};
|
|
2066
2321
|
|
|
@@ -4409,7 +4664,32 @@ var Heading = Node.create({
|
|
|
4409
4664
|
];
|
|
4410
4665
|
},
|
|
4411
4666
|
addProseMirrorPlugins() {
|
|
4667
|
+
const { options, editor } = this;
|
|
4668
|
+
const codeToLevel = {};
|
|
4669
|
+
for (const level of options.levels) {
|
|
4670
|
+
codeToLevel[`Digit${String(level)}`] = level;
|
|
4671
|
+
}
|
|
4672
|
+
codeToLevel["Digit0"] = 0;
|
|
4412
4673
|
return [
|
|
4674
|
+
new state.Plugin({
|
|
4675
|
+
key: new state.PluginKey("headingKeydownFix"),
|
|
4676
|
+
props: {
|
|
4677
|
+
handleDOMEvents: {
|
|
4678
|
+
keydown(_view, event) {
|
|
4679
|
+
if (!event.altKey || !(event.metaKey || event.ctrlKey)) return false;
|
|
4680
|
+
const level = codeToLevel[event.code];
|
|
4681
|
+
if (level === void 0) return false;
|
|
4682
|
+
event.preventDefault();
|
|
4683
|
+
if (level === 0) {
|
|
4684
|
+
editor?.commands["setParagraph"]?.();
|
|
4685
|
+
} else {
|
|
4686
|
+
editor?.commands["toggleHeading"]?.({ level });
|
|
4687
|
+
}
|
|
4688
|
+
return true;
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}),
|
|
4413
4693
|
keymap.keymap({
|
|
4414
4694
|
Backspace: ((state, dispatch) => {
|
|
4415
4695
|
const { selection } = state;
|
|
@@ -4436,10 +4716,10 @@ var Heading = Node.create({
|
|
|
4436
4716
|
}
|
|
4437
4717
|
const maxLevel = Math.max(...options.levels);
|
|
4438
4718
|
return [
|
|
4439
|
-
|
|
4440
|
-
new RegExp(`^(#{1,${String(maxLevel)}})\\s$`),
|
|
4441
|
-
nodeType,
|
|
4442
|
-
(match) => {
|
|
4719
|
+
textblockTypeInputRule({
|
|
4720
|
+
find: new RegExp(`^(#{1,${String(maxLevel)}})\\s$`),
|
|
4721
|
+
type: nodeType,
|
|
4722
|
+
getAttributes: (match) => {
|
|
4443
4723
|
const hashes = match[1];
|
|
4444
4724
|
if (!hashes) {
|
|
4445
4725
|
return null;
|
|
@@ -4450,10 +4730,12 @@ var Heading = Node.create({
|
|
|
4450
4730
|
}
|
|
4451
4731
|
return { level };
|
|
4452
4732
|
}
|
|
4453
|
-
)
|
|
4733
|
+
})
|
|
4454
4734
|
];
|
|
4455
4735
|
}
|
|
4456
4736
|
});
|
|
4737
|
+
|
|
4738
|
+
// src/nodes/Blockquote.ts
|
|
4457
4739
|
var Blockquote = Node.create({
|
|
4458
4740
|
name: "blockquote",
|
|
4459
4741
|
group: "block",
|
|
@@ -4513,7 +4795,7 @@ var Blockquote = Node.create({
|
|
|
4513
4795
|
return [];
|
|
4514
4796
|
}
|
|
4515
4797
|
return [
|
|
4516
|
-
|
|
4798
|
+
wrappingInputRule({ find: /^\s*>\s$/, type: nodeType })
|
|
4517
4799
|
];
|
|
4518
4800
|
}
|
|
4519
4801
|
});
|
|
@@ -4643,14 +4925,14 @@ var CodeBlock = Node.create({
|
|
|
4643
4925
|
return [];
|
|
4644
4926
|
}
|
|
4645
4927
|
return [
|
|
4646
|
-
|
|
4647
|
-
/^```([a-z]*)?[\s\n]$/,
|
|
4648
|
-
nodeType,
|
|
4649
|
-
(match) => {
|
|
4928
|
+
textblockTypeInputRule({
|
|
4929
|
+
find: /^```([a-z]*)?[\s\n]$/,
|
|
4930
|
+
type: nodeType,
|
|
4931
|
+
getAttributes: (match) => {
|
|
4650
4932
|
const language = match[1] ?? null;
|
|
4651
4933
|
return { language };
|
|
4652
4934
|
}
|
|
4653
|
-
)
|
|
4935
|
+
})
|
|
4654
4936
|
];
|
|
4655
4937
|
}
|
|
4656
4938
|
});
|
|
@@ -4834,14 +5116,16 @@ var BulletList = Node.create({
|
|
|
4834
5116
|
}
|
|
4835
5117
|
return [
|
|
4836
5118
|
// - item
|
|
4837
|
-
|
|
5119
|
+
wrappingInputRule({ find: /^\s*[-]\s$/, type: nodeType, guard: notInsideList }),
|
|
4838
5120
|
// * item
|
|
4839
|
-
|
|
5121
|
+
wrappingInputRule({ find: /^\s*[*]\s$/, type: nodeType, guard: notInsideList }),
|
|
4840
5122
|
// + item
|
|
4841
|
-
|
|
5123
|
+
wrappingInputRule({ find: /^\s*[+]\s$/, type: nodeType, guard: notInsideList })
|
|
4842
5124
|
];
|
|
4843
5125
|
}
|
|
4844
5126
|
});
|
|
5127
|
+
|
|
5128
|
+
// src/nodes/OrderedList.ts
|
|
4845
5129
|
var OrderedList = Node.create({
|
|
4846
5130
|
name: "orderedList",
|
|
4847
5131
|
group: "block list",
|
|
@@ -4917,14 +5201,15 @@ var OrderedList = Node.create({
|
|
|
4917
5201
|
}
|
|
4918
5202
|
return [
|
|
4919
5203
|
// 1. item (any number followed by . )
|
|
4920
|
-
|
|
4921
|
-
/^(\d+)\.\s$/,
|
|
4922
|
-
nodeType,
|
|
4923
|
-
|
|
5204
|
+
wrappingInputRule({
|
|
5205
|
+
find: /^(\d+)\.\s$/,
|
|
5206
|
+
type: nodeType,
|
|
5207
|
+
guard: notInsideList,
|
|
5208
|
+
getAttributes: (match) => {
|
|
4924
5209
|
const num = match[1];
|
|
4925
5210
|
return { start: num ? parseInt(num, 10) : 1 };
|
|
4926
5211
|
}
|
|
4927
|
-
)
|
|
5212
|
+
})
|
|
4928
5213
|
];
|
|
4929
5214
|
}
|
|
4930
5215
|
});
|
|
@@ -4999,13 +5284,12 @@ var HorizontalRule = Node.create({
|
|
|
4999
5284
|
const $start = state$1.doc.resolve(start);
|
|
5000
5285
|
const from = $start.before();
|
|
5001
5286
|
const to = $start.after();
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
}
|
|
5287
|
+
const paragraph = state$1.schema.nodes["paragraph"]?.create();
|
|
5288
|
+
const nodes = paragraph ? [nodeType.create(), paragraph] : [nodeType.create()];
|
|
5289
|
+
tr.replaceWith(from, to, nodes);
|
|
5290
|
+
const sel = state.TextSelection.findFrom(tr.doc.resolve(from + 1), 1);
|
|
5291
|
+
if (sel) {
|
|
5292
|
+
tr.setSelection(sel);
|
|
5009
5293
|
}
|
|
5010
5294
|
}
|
|
5011
5295
|
return tr;
|
|
@@ -5296,9 +5580,9 @@ var TaskList = Node.create({
|
|
|
5296
5580
|
}
|
|
5297
5581
|
return [
|
|
5298
5582
|
// [ ] at start of line creates unchecked task
|
|
5299
|
-
|
|
5583
|
+
wrappingInputRule({ find: /^\s*\[\s?\]\s$/, type: nodeType, guard: notInsideList }),
|
|
5300
5584
|
// [x] or [X] at start of line creates checked task
|
|
5301
|
-
|
|
5585
|
+
wrappingInputRule({ find: /^\s*\[[xX]\]\s$/, type: nodeType, guard: notInsideList })
|
|
5302
5586
|
];
|
|
5303
5587
|
}
|
|
5304
5588
|
});
|
|
@@ -6596,40 +6880,39 @@ var Typography = Extension.create({
|
|
|
6596
6880
|
},
|
|
6597
6881
|
addInputRules() {
|
|
6598
6882
|
const rules = [];
|
|
6599
|
-
const textReplace = (find2, replacement) => new inputrules.InputRule(find2, (state, _match, start, end) => state.tr.replaceWith(start, end, state.schema.text(replacement)));
|
|
6600
6883
|
if (this.options.emDash) {
|
|
6601
|
-
rules.push(
|
|
6884
|
+
rules.push(textInputRule({ find: /--$/, replace: "\u2014" }));
|
|
6602
6885
|
}
|
|
6603
6886
|
if (this.options.ellipsis) {
|
|
6604
|
-
rules.push(
|
|
6887
|
+
rules.push(textInputRule({ find: /\.\.\.$/, replace: "\u2026" }));
|
|
6605
6888
|
}
|
|
6606
6889
|
if (this.options.arrows) {
|
|
6607
|
-
rules.push(
|
|
6608
|
-
rules.push(
|
|
6609
|
-
rules.push(
|
|
6890
|
+
rules.push(textInputRule({ find: /<-$/, replace: "\u2190" }));
|
|
6891
|
+
rules.push(textInputRule({ find: /->$/, replace: "\u2192" }));
|
|
6892
|
+
rules.push(textInputRule({ find: /=>$/, replace: "\u21D2" }));
|
|
6610
6893
|
}
|
|
6611
6894
|
if (this.options.fractions) {
|
|
6612
|
-
rules.push(
|
|
6613
|
-
rules.push(
|
|
6614
|
-
rules.push(
|
|
6615
|
-
rules.push(
|
|
6616
|
-
rules.push(
|
|
6895
|
+
rules.push(textInputRule({ find: /1\/2$/, replace: "\xBD" }));
|
|
6896
|
+
rules.push(textInputRule({ find: /1\/4$/, replace: "\xBC" }));
|
|
6897
|
+
rules.push(textInputRule({ find: /3\/4$/, replace: "\xBE" }));
|
|
6898
|
+
rules.push(textInputRule({ find: /1\/3$/, replace: "\u2153" }));
|
|
6899
|
+
rules.push(textInputRule({ find: /2\/3$/, replace: "\u2154" }));
|
|
6617
6900
|
}
|
|
6618
6901
|
if (this.options.symbols) {
|
|
6619
|
-
rules.push(
|
|
6620
|
-
rules.push(
|
|
6621
|
-
rules.push(
|
|
6622
|
-
rules.push(
|
|
6902
|
+
rules.push(textInputRule({ find: /\(c\)$/i, replace: "\xA9" }));
|
|
6903
|
+
rules.push(textInputRule({ find: /\(r\)$/i, replace: "\xAE" }));
|
|
6904
|
+
rules.push(textInputRule({ find: /\(tm\)$/i, replace: "\u2122" }));
|
|
6905
|
+
rules.push(textInputRule({ find: /\(sm\)$/i, replace: "\u2120" }));
|
|
6623
6906
|
}
|
|
6624
6907
|
if (this.options.math) {
|
|
6625
|
-
rules.push(
|
|
6626
|
-
rules.push(
|
|
6627
|
-
rules.push(
|
|
6628
|
-
rules.push(
|
|
6908
|
+
rules.push(textInputRule({ find: /\+\/-$/, replace: "\xB1" }));
|
|
6909
|
+
rules.push(textInputRule({ find: /!=$/, replace: "\u2260" }));
|
|
6910
|
+
rules.push(textInputRule({ find: /<=$/, replace: "\u2264" }));
|
|
6911
|
+
rules.push(textInputRule({ find: />=$/, replace: "\u2265" }));
|
|
6629
6912
|
}
|
|
6630
6913
|
if (this.options.guillemets) {
|
|
6631
|
-
rules.push(
|
|
6632
|
-
rules.push(
|
|
6914
|
+
rules.push(textInputRule({ find: /<<$/, replace: "\xAB" }));
|
|
6915
|
+
rules.push(textInputRule({ find: />>$/, replace: "\xBB" }));
|
|
6633
6916
|
}
|
|
6634
6917
|
if (this.options.smartQuotes) {
|
|
6635
6918
|
const { openDoubleQuote, closeDoubleQuote, openSingleQuote, closeSingleQuote } = this.options;
|
|
@@ -7866,11 +8149,13 @@ function linkPopoverPlugin({ editor, markType, protocols }) {
|
|
|
7866
8149
|
applyBtn.type = "button";
|
|
7867
8150
|
applyBtn.className = "dm-link-popover-btn dm-link-popover-apply";
|
|
7868
8151
|
applyBtn.title = "Apply link";
|
|
8152
|
+
applyBtn.setAttribute("aria-label", "Apply link");
|
|
7869
8153
|
applyBtn.innerHTML = defaultIcons["check"] ?? "";
|
|
7870
8154
|
const removeBtn = document.createElement("button");
|
|
7871
8155
|
removeBtn.type = "button";
|
|
7872
8156
|
removeBtn.className = "dm-link-popover-btn dm-link-popover-remove";
|
|
7873
8157
|
removeBtn.title = "Remove link";
|
|
8158
|
+
removeBtn.setAttribute("aria-label", "Remove link");
|
|
7874
8159
|
removeBtn.innerHTML = defaultIcons["linkBreak"] ?? "";
|
|
7875
8160
|
el.appendChild(input);
|
|
7876
8161
|
el.appendChild(applyBtn);
|
|
@@ -8636,6 +8921,7 @@ exports.linkPastePlugin = linkPastePlugin;
|
|
|
8636
8921
|
exports.linkPastePluginKey = linkPastePluginKey;
|
|
8637
8922
|
exports.markInputRule = markInputRule;
|
|
8638
8923
|
exports.markInputRulePatterns = markInputRulePatterns;
|
|
8924
|
+
exports.nodeInputRule = nodeInputRule;
|
|
8639
8925
|
exports.placeholderPluginKey = placeholderPluginKey;
|
|
8640
8926
|
exports.positionFloating = positionFloating;
|
|
8641
8927
|
exports.positionFloatingOnce = positionFloatingOnce;
|
|
@@ -8646,6 +8932,8 @@ exports.selectionDecorationPluginKey = selectionDecorationPluginKey;
|
|
|
8646
8932
|
exports.setBlockType = setBlockType;
|
|
8647
8933
|
exports.setContent = setContent;
|
|
8648
8934
|
exports.setMark = setMark;
|
|
8935
|
+
exports.textInputRule = textInputRule;
|
|
8936
|
+
exports.textblockTypeInputRule = textblockTypeInputRule;
|
|
8649
8937
|
exports.toggleBlockType = toggleBlockType;
|
|
8650
8938
|
exports.toggleList = toggleList;
|
|
8651
8939
|
exports.toggleMark = toggleMark;
|
|
@@ -8655,5 +8943,6 @@ exports.unsetAllMarks = unsetAllMarks;
|
|
|
8655
8943
|
exports.unsetMark = unsetMark;
|
|
8656
8944
|
exports.updateAttributes = updateAttributes;
|
|
8657
8945
|
exports.wrapIn = wrapIn;
|
|
8946
|
+
exports.wrappingInputRule = wrappingInputRule;
|
|
8658
8947
|
//# sourceMappingURL=index.cjs.map
|
|
8659
8948
|
//# sourceMappingURL=index.cjs.map
|