@openleaf-editor/core 0.1.0-beta.2 → 0.1.0-beta.4
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 +127 -0
- package/dist/autolink.d.ts +7 -4
- package/dist/autolink.d.ts.map +1 -1
- package/dist/autolink.js +90 -11
- package/dist/autolink.js.map +1 -1
- package/dist/command-helpers.d.ts +8 -0
- package/dist/command-helpers.d.ts.map +1 -0
- package/dist/command-helpers.js +36 -0
- package/dist/command-helpers.js.map +1 -0
- package/dist/commands.d.ts +21 -63
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +430 -346
- package/dist/commands.js.map +1 -1
- package/dist/css.d.ts +1 -175
- package/dist/css.d.ts.map +1 -1
- package/dist/css.js +1 -516
- package/dist/css.js.map +1 -1
- package/dist/disclosure.d.ts +21 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +144 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/elements.d.ts +2 -0
- package/dist/elements.d.ts.map +1 -0
- package/dist/elements.js +2 -0
- package/dist/elements.js.map +1 -0
- package/dist/embed.d.ts +1 -51
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.js +1 -115
- package/dist/embed.js.map +1 -1
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions.d.ts +46 -5
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +272 -52
- package/dist/extensions.js.map +1 -1
- package/dist/figure-drag.d.ts +26 -0
- package/dist/figure-drag.d.ts.map +1 -0
- package/dist/figure-drag.js +75 -0
- package/dist/figure-drag.js.map +1 -0
- package/dist/formats.d.ts.map +1 -1
- package/dist/formats.js +19 -7
- package/dist/formats.js.map +1 -1
- package/dist/gapcursor.d.ts +14 -0
- package/dist/gapcursor.d.ts.map +1 -0
- package/dist/gapcursor.js +17 -0
- package/dist/gapcursor.js.map +1 -0
- package/dist/html.d.ts +32 -2
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +185 -31
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +23 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -6
- package/dist/index.js.map +1 -1
- package/dist/insert-commands.d.ts +172 -0
- package/dist/insert-commands.d.ts.map +1 -0
- package/dist/insert-commands.js +560 -0
- package/dist/insert-commands.js.map +1 -0
- package/dist/isolating-selection.d.ts +59 -0
- package/dist/isolating-selection.d.ts.map +1 -0
- package/dist/isolating-selection.js +251 -0
- package/dist/isolating-selection.js.map +1 -0
- package/dist/noneditable.d.ts.map +1 -1
- package/dist/noneditable.js +18 -4
- package/dist/noneditable.js.map +1 -1
- package/dist/plugins.d.ts +20 -2
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +27 -2
- package/dist/plugins.js.map +1 -1
- package/dist/preserve.d.ts +48 -17
- package/dist/preserve.d.ts.map +1 -1
- package/dist/preserve.js +214 -43
- package/dist/preserve.js.map +1 -1
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +157 -28
- package/dist/schema.js.map +1 -1
- package/dist/structure.d.ts +32 -0
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +53 -10
- package/dist/structure.js.map +1 -1
- package/dist/tables.d.ts +47 -1
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +417 -40
- package/dist/tables.js.map +1 -1
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +1 -0
- package/dist/tokens.d.ts +8 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +54 -6
- package/dist/tokens.js.map +1 -1
- package/dist/url.d.ts +1 -25
- package/dist/url.d.ts.map +1 -1
- package/dist/url.js +1 -77
- package/dist/url.js.map +1 -1
- package/dist/visual-aids.d.ts +11 -1
- package/dist/visual-aids.d.ts.map +1 -1
- package/dist/visual-aids.js +124 -36
- package/dist/visual-aids.js.map +1 -1
- package/package.json +17 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep a `TextSelection` from straddling an isolating boundary.
|
|
3
|
+
*
|
|
4
|
+
* `isolating: true` already means ProseMirror will not join or lift across that
|
|
5
|
+
* node. `Selection.near` honours it when *finding* a caret; `TextSelection.between`
|
|
6
|
+
* does not when *building* a range, so Firefox and WebKit can report a selection
|
|
7
|
+
* that starts in a `<blockquote>` and ends in a following `<details>`. Typing
|
|
8
|
+
* then runs `replaceSelection`, which tries to nest the leftover `details` in
|
|
9
|
+
* the quote (legal: `blockquote` is `block+`, `details` is `group: 'block'`) and
|
|
10
|
+
* join, and `content: 'summary block+'` refuses with `TransformError`.
|
|
11
|
+
*
|
|
12
|
+
* The throw escapes the input handler. ProseMirror re-derives the document from
|
|
13
|
+
* the DOM, history never sees a step, and Ctrl+Z cannot undo the mangling.
|
|
14
|
+
*
|
|
15
|
+
* Chromium already clamps at the isolating boundary natively. This plugin does
|
|
16
|
+
* the same for every isolating node, not just `details`: after a transaction,
|
|
17
|
+
* a crossing `TextSelection` is narrowed to the side the anchor sits on. Input
|
|
18
|
+
* handlers also refuse to let a throwing replace run, so even a race with the
|
|
19
|
+
* native selection cannot rewrite the document outside history.
|
|
20
|
+
*
|
|
21
|
+
* `handleTextInput` is not enough on its own. When the model selection is still
|
|
22
|
+
* a collapsed caret, ProseMirror never calls it, and Chromium applies the
|
|
23
|
+
* keystroke to the *DOM* range — which can still straddle `details`. That path
|
|
24
|
+
* emptied `<summary>` and lifted the body out of `<details>` on a subset of
|
|
25
|
+
* Shift+ArrowDown runs (#163). `beforeinput` fires for every input type before
|
|
26
|
+
* any mutation, so it can read the DOM range (via `posAtDOM`; `state.selection`
|
|
27
|
+
* is untrustworthy in that tick) and apply the clamped edit itself.
|
|
28
|
+
*
|
|
29
|
+
* `CellSelection` / `AllSelection` / `NodeSelection` are left alone. Select-all
|
|
30
|
+
* is an `AllSelection` in the base keymap; a `TextSelection` that merely
|
|
31
|
+
* contains a whole isolating node (both endpoints outside it) is not crossing.
|
|
32
|
+
*/
|
|
33
|
+
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
34
|
+
const key = new PluginKey('openleaf-isolating-selection');
|
|
35
|
+
/** Depth of the innermost isolating ancestor, or 0 if the pos sits in none. */
|
|
36
|
+
export function innermostIsolatingDepth($pos) {
|
|
37
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
38
|
+
if ($pos.node(depth).type.spec.isolating)
|
|
39
|
+
return depth;
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* True when the endpoints do not share an isolating ancestor: one is inside a
|
|
45
|
+
* given isolating node and the other is not (or is inside a different one).
|
|
46
|
+
*/
|
|
47
|
+
export function textSelectionCrossesIsolating(selection) {
|
|
48
|
+
if (!(selection instanceof TextSelection) || selection.empty)
|
|
49
|
+
return false;
|
|
50
|
+
return !sameIsolatingContext(selection.$from, selection.$to);
|
|
51
|
+
}
|
|
52
|
+
function sameIsolatingContext($a, $b) {
|
|
53
|
+
const depthA = innermostIsolatingDepth($a);
|
|
54
|
+
const depthB = innermostIsolatingDepth($b);
|
|
55
|
+
if (depthA === 0 && depthB === 0)
|
|
56
|
+
return true;
|
|
57
|
+
if (depthA === 0 || depthB === 0)
|
|
58
|
+
return false;
|
|
59
|
+
return $a.before(depthA) === $b.before(depthB);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Narrow a crossing text selection to the isolating context that contains the
|
|
63
|
+
* anchor, or to just outside the isolating node that contains the head when the
|
|
64
|
+
* anchor itself sits in none. Same-context selections are returned unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export function clampIsolatingTextSelection(selection) {
|
|
67
|
+
if (selection.empty || sameIsolatingContext(selection.$anchor, selection.$head)) {
|
|
68
|
+
return selection;
|
|
69
|
+
}
|
|
70
|
+
const { $anchor, $head } = selection;
|
|
71
|
+
const doc = $anchor.doc;
|
|
72
|
+
const anchorDepth = innermostIsolatingDepth($anchor);
|
|
73
|
+
if (anchorDepth > 0) {
|
|
74
|
+
const min = $anchor.start(anchorDepth);
|
|
75
|
+
const max = $anchor.end(anchorDepth);
|
|
76
|
+
const head = Math.max(min, Math.min(max, $head.pos));
|
|
77
|
+
if (head === $head.pos)
|
|
78
|
+
return selection;
|
|
79
|
+
const next = TextSelection.between($anchor, doc.resolve(head));
|
|
80
|
+
return next instanceof TextSelection ? next : selection;
|
|
81
|
+
}
|
|
82
|
+
const headDepth = innermostIsolatingDepth($head);
|
|
83
|
+
if (headDepth > 0) {
|
|
84
|
+
const before = $head.before(headDepth);
|
|
85
|
+
const after = $head.after(headDepth);
|
|
86
|
+
const boundary = $anchor.pos <= before ? before : after;
|
|
87
|
+
const next = TextSelection.between($anchor, doc.resolve(boundary));
|
|
88
|
+
return next instanceof TextSelection ? next : selection;
|
|
89
|
+
}
|
|
90
|
+
return selection;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Given a DOM-derived (anchor, head) pair — not necessarily `state.selection` —
|
|
94
|
+
* return the range a keystroke must replace so the edit stays on one side of an
|
|
95
|
+
* isolating boundary. `null` means the range does not cross and the default
|
|
96
|
+
* editor path should run.
|
|
97
|
+
*/
|
|
98
|
+
export function clampIsolatingReplaceRange(doc, anchor, head) {
|
|
99
|
+
const size = doc.content.size;
|
|
100
|
+
const a = Math.max(0, Math.min(anchor, size));
|
|
101
|
+
const h = Math.max(0, Math.min(head, size));
|
|
102
|
+
if (a === h)
|
|
103
|
+
return null;
|
|
104
|
+
let oriented;
|
|
105
|
+
try {
|
|
106
|
+
oriented = TextSelection.create(doc, a, h);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!textSelectionCrossesIsolating(oriented))
|
|
112
|
+
return null;
|
|
113
|
+
const clamped = clampIsolatingTextSelection(oriented);
|
|
114
|
+
return {
|
|
115
|
+
from: Math.min(clamped.from, clamped.to),
|
|
116
|
+
to: Math.max(clamped.from, clamped.to),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function tryInsertText(view, from, to, text) {
|
|
120
|
+
try {
|
|
121
|
+
view.state.tr.insertText(text, from, to);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function textSelectionFromDOM(view) {
|
|
129
|
+
const sel = view.dom.ownerDocument.getSelection();
|
|
130
|
+
if (!sel || sel.rangeCount === 0 || !sel.anchorNode || !sel.focusNode)
|
|
131
|
+
return null;
|
|
132
|
+
if (!view.dom.contains(sel.anchorNode) || !view.dom.contains(sel.focusNode))
|
|
133
|
+
return null;
|
|
134
|
+
try {
|
|
135
|
+
const anchor = view.posAtDOM(sel.anchorNode, sel.anchorOffset);
|
|
136
|
+
const head = view.posAtDOM(sel.focusNode, sel.focusOffset);
|
|
137
|
+
return TextSelection.create(view.state.doc, anchor, head);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function handleIsolatingBeforeInput(view, event) {
|
|
144
|
+
if (!view.editable || !(event instanceof InputEvent))
|
|
145
|
+
return false;
|
|
146
|
+
// insertCompositionText is not cancelable. preventDefault is a no-op, and
|
|
147
|
+
// event.data is the whole composition on every update — committing that as
|
|
148
|
+
// document text duplicates IME candidates on top of the UA mutation.
|
|
149
|
+
if (!event.cancelable)
|
|
150
|
+
return false;
|
|
151
|
+
const type = event.inputType;
|
|
152
|
+
const inserting = type === 'insertText';
|
|
153
|
+
if (!inserting && type !== 'insertParagraph' && !type.startsWith('delete'))
|
|
154
|
+
return false;
|
|
155
|
+
const domSel = textSelectionFromDOM(view);
|
|
156
|
+
if (!domSel || domSel.empty)
|
|
157
|
+
return false;
|
|
158
|
+
const clamped = clampIsolatingReplaceRange(view.state.doc, domSel.anchor, domSel.head);
|
|
159
|
+
if (!clamped)
|
|
160
|
+
return false;
|
|
161
|
+
// Swallow the default. A crossing DOM range that reaches the browser becomes
|
|
162
|
+
// a parse-from-DOM repair, which is the #163 corruption.
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
const { from, to } = clamped;
|
|
165
|
+
try {
|
|
166
|
+
if (inserting) {
|
|
167
|
+
const text = event.data ?? '';
|
|
168
|
+
if (!tryInsertText(view, from, to, text))
|
|
169
|
+
return true;
|
|
170
|
+
view.dispatch(view.state.tr.insertText(text, from, to).scrollIntoView());
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
// delete* and insertParagraph: drop the clamped span so Chromium cannot
|
|
174
|
+
// parse-from-DOM across the isolating node. Desktop Enter is usually eaten
|
|
175
|
+
// by the keymap before beforeinput; Android virtual Enter is this path.
|
|
176
|
+
view.dispatch(view.state.tr.delete(from, to).scrollIntoView());
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export function isolatingSelectionPlugin() {
|
|
184
|
+
return new Plugin({
|
|
185
|
+
key,
|
|
186
|
+
appendTransaction(transactions, _oldState, newState) {
|
|
187
|
+
if (transactions.some((tr) => tr.getMeta(key)))
|
|
188
|
+
return null;
|
|
189
|
+
const { selection } = newState;
|
|
190
|
+
if (!(selection instanceof TextSelection) || selection.empty)
|
|
191
|
+
return null;
|
|
192
|
+
const clamped = clampIsolatingTextSelection(selection);
|
|
193
|
+
if (clamped.eq(selection))
|
|
194
|
+
return null;
|
|
195
|
+
return newState.tr.setSelection(clamped).setMeta(key, true);
|
|
196
|
+
},
|
|
197
|
+
props: {
|
|
198
|
+
/*
|
|
199
|
+
* `from`/`to` are the view's idea of the range at the moment of the
|
|
200
|
+
* input event. The DOM can still report a crossing range in the same
|
|
201
|
+
* tick as a keystroke, before `appendTransaction` has run, so the
|
|
202
|
+
* default `insertText` would throw. Building the transaction first is
|
|
203
|
+
* what keeps a throw from ever reaching ProseMirror's input handler:
|
|
204
|
+
* that handler does not catch, and the DOM-derived repair that follows
|
|
205
|
+
* is the corruption that cannot be undone.
|
|
206
|
+
*/
|
|
207
|
+
handleTextInput(view, from, to, text) {
|
|
208
|
+
const { doc, selection } = view.state;
|
|
209
|
+
const size = doc.content.size;
|
|
210
|
+
const start = Math.max(0, Math.min(from, size));
|
|
211
|
+
const end = Math.max(start, Math.min(to, size));
|
|
212
|
+
const anchor = selection instanceof TextSelection && selection.anchor === end ? end : start;
|
|
213
|
+
const head = anchor === end ? start : end;
|
|
214
|
+
const clamped = start === end ? null : clampIsolatingReplaceRange(doc, anchor, head);
|
|
215
|
+
const replaceFrom = clamped?.from ?? start;
|
|
216
|
+
const replaceTo = clamped?.to ?? end;
|
|
217
|
+
if (!tryInsertText(view, replaceFrom, replaceTo, text))
|
|
218
|
+
return true;
|
|
219
|
+
if (replaceFrom === start && replaceTo === end)
|
|
220
|
+
return false;
|
|
221
|
+
view.dispatch(view.state.tr.insertText(text, replaceFrom, replaceTo).scrollIntoView());
|
|
222
|
+
return true;
|
|
223
|
+
},
|
|
224
|
+
handleDOMEvents: {
|
|
225
|
+
beforeinput(view, event) {
|
|
226
|
+
return handleIsolatingBeforeInput(view, event);
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
handleKeyDown(view, event) {
|
|
230
|
+
if (event.key !== 'Backspace' && event.key !== 'Delete' && event.key !== 'Enter') {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
const { selection } = view.state;
|
|
234
|
+
if (!(selection instanceof TextSelection) || selection.empty)
|
|
235
|
+
return false;
|
|
236
|
+
if (!textSelectionCrossesIsolating(selection))
|
|
237
|
+
return false;
|
|
238
|
+
const clamped = clampIsolatingTextSelection(selection);
|
|
239
|
+
try {
|
|
240
|
+
view.state.tr.setSelection(clamped).deleteSelection();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
view.dispatch(view.state.tr.setSelection(clamped));
|
|
246
|
+
return false;
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=isolating-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolating-selection.js","sourceRoot":"","sources":["../src/isolating-selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noneditable.d.ts","sourceRoot":"","sources":["../src/noneditable.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,MAAM,EAAa,MAAM,mBAAmB,CAAA;AAMrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvD;AAoBD,wBAAgB,iBAAiB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"noneditable.d.ts","sourceRoot":"","sources":["../src/noneditable.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,MAAM,EAAa,MAAM,mBAAmB,CAAA;AAMrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvD;AAoBD,wBAAgB,iBAAiB,IAAI,MAAM,CAgE1C"}
|
package/dist/noneditable.js
CHANGED
|
@@ -50,7 +50,19 @@ export function nonEditablePlugin() {
|
|
|
50
50
|
* nothing at all: inserting a table column after inserting a row produces
|
|
51
51
|
* exactly that shape, and the error was swallowed by the toolbar's guard.
|
|
52
52
|
*
|
|
53
|
-
*
|
|
53
|
+
* The transaction already holds those documents. `tr.docs[i]` is the
|
|
54
|
+
* document step `i` was applied to, kept by `Transform` so that a step can
|
|
55
|
+
* be inverted, so each range can simply be read against it -- no mapping at
|
|
56
|
+
* all, and the positions are exact rather than an approximation recovered
|
|
57
|
+
* through an inverse.
|
|
58
|
+
*
|
|
59
|
+
* The mapping is what made this expensive. It used to be
|
|
60
|
+
* `tr.mapping.slice(0, i).invert()`, built inside the loop: an i-length
|
|
61
|
+
* mapping allocated and inverted for every step, so the cost was the sum of
|
|
62
|
+
* i rather than the count of them. A single-character transaction has one
|
|
63
|
+
* step and never showed it; `clearFormatting` over a select-all on a
|
|
64
|
+
* 100-page document produces about three thousand, and measured 180 ms in
|
|
65
|
+
* this function alone.
|
|
54
66
|
*/
|
|
55
67
|
filterTransaction(tr, state) {
|
|
56
68
|
if (!tr.docChanged)
|
|
@@ -62,13 +74,15 @@ export function nonEditablePlugin() {
|
|
|
62
74
|
const step = tr.steps[i];
|
|
63
75
|
if (!step)
|
|
64
76
|
continue;
|
|
65
|
-
|
|
77
|
+
// `docs[0]` is `state.doc`; the fallback covers a transaction whose
|
|
78
|
+
// steps were recorded elsewhere, where the first document is the only
|
|
79
|
+
// one we can name.
|
|
80
|
+
const before = tr.docs[i] ?? state.doc;
|
|
66
81
|
step.getMap().forEach((start, end) => {
|
|
67
82
|
if (blocked)
|
|
68
83
|
return;
|
|
69
|
-
if (editsLockedInterior(
|
|
84
|
+
if (editsLockedInterior(before, start, end))
|
|
70
85
|
blocked = true;
|
|
71
|
-
}
|
|
72
86
|
});
|
|
73
87
|
}
|
|
74
88
|
return !blocked;
|
package/dist/noneditable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noneditable.js","sourceRoot":"","sources":["../src/noneditable.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,sBAAsB,CAAC,CAAA;AAEjD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAA8C,CAAA;IACrF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC,iBAAiB,CAAC,CAAA;IAC1C,OAAO,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,CAAA;AAC1D,CAAC;AAED,2FAA2F;AAC3F,SAAS,mBAAmB,CAAC,GAAW,EAAE,KAAa,EAAE,GAAW;IAClE,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAC1D,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACzC,MAAM,KAAK,GAAG,GAAG,CAAA;QACjB,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC/B,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,GAAG,GAAG;YAAE,OAAO,GAAG,IAAI,CAAA;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,MAAM,CAAC;QAChB,GAAG;QACH
|
|
1
|
+
{"version":3,"file":"noneditable.js","sourceRoot":"","sources":["../src/noneditable.ts"],"names":[],"mappings":"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"}
|
package/dist/plugins.d.ts
CHANGED
|
@@ -14,9 +14,27 @@
|
|
|
14
14
|
import type { Plugin } from 'prosemirror-state';
|
|
15
15
|
import type { Schema } from 'prosemirror-model';
|
|
16
16
|
export type EditorPluginFactory = (schema: Schema) => Plugin[];
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Register plugins to be installed in every editor created from now on.
|
|
19
|
+
*
|
|
20
|
+
* Throws `OpenLeafError` with code `invalid-argument` when handed something that
|
|
21
|
+
* is not a function. `registerEditorPlugin(42)` used to be accepted silently and
|
|
22
|
+
* fail on the far side of the registry -- once per editor, through
|
|
23
|
+
* `console.error`, with a stack pointing at `createRegisteredPlugins` rather
|
|
24
|
+
* than at the script tag that got it wrong.
|
|
25
|
+
*/
|
|
18
26
|
export declare function registerEditorPlugin(factory: EditorPluginFactory): () => void;
|
|
19
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Build plugin instances for an editor, reusing cached instances when given.
|
|
29
|
+
*
|
|
30
|
+
* `cache` is a host's own per-editor map, and it is genuinely part of the
|
|
31
|
+
* contract rather than an optimisation that leaked: without it, reconfiguring an
|
|
32
|
+
* editor after a late registration builds *new* instances of the plugins already
|
|
33
|
+
* running, and each one starts from its initial state -- so a table plugin loses
|
|
34
|
+
* its selection and a session plugin its draft, on every later registration.
|
|
35
|
+
* Omit it and every call returns fresh instances, which is correct for a host
|
|
36
|
+
* building one editor once.
|
|
37
|
+
*/
|
|
20
38
|
export declare function createRegisteredPlugins(schema: Schema, cache?: Map<EditorPluginFactory, Plugin[]>): Plugin[];
|
|
21
39
|
/**
|
|
22
40
|
* Notified when a plugin registers.
|
package/dist/plugins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG/C,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;AAK9D;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAmB7E;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC,GACzC,MAAM,EAAE,CA2BV;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKtE"}
|
package/dist/plugins.js
CHANGED
|
@@ -11,10 +11,25 @@
|
|
|
11
11
|
* Calling the factory once per editor is the difference between two working
|
|
12
12
|
* editors and two editors fighting over one plugin's state.
|
|
13
13
|
*/
|
|
14
|
+
import { OpenLeafError } from './errors.js';
|
|
14
15
|
const factories = new Set();
|
|
15
16
|
const listeners = new Set();
|
|
16
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Register plugins to be installed in every editor created from now on.
|
|
19
|
+
*
|
|
20
|
+
* Throws `OpenLeafError` with code `invalid-argument` when handed something that
|
|
21
|
+
* is not a function. `registerEditorPlugin(42)` used to be accepted silently and
|
|
22
|
+
* fail on the far side of the registry -- once per editor, through
|
|
23
|
+
* `console.error`, with a stack pointing at `createRegisteredPlugins` rather
|
|
24
|
+
* than at the script tag that got it wrong.
|
|
25
|
+
*/
|
|
17
26
|
export function registerEditorPlugin(factory) {
|
|
27
|
+
if (typeof factory !== 'function') {
|
|
28
|
+
throw new OpenLeafError('invalid-argument', '@openleaf-editor/core: registerEditorPlugin expects a function taking a schema and ' +
|
|
29
|
+
`returning an array of ProseMirror plugins, received ${typeof factory}. Passing a ` +
|
|
30
|
+
'plugin instance is the usual mistake: instances carry per-editor state and cannot ' +
|
|
31
|
+
'be shared, which is why this takes a factory.');
|
|
32
|
+
}
|
|
18
33
|
factories.add(factory);
|
|
19
34
|
notify();
|
|
20
35
|
return () => {
|
|
@@ -44,7 +59,17 @@ function notify() {
|
|
|
44
59
|
}
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Build plugin instances for an editor, reusing cached instances when given.
|
|
64
|
+
*
|
|
65
|
+
* `cache` is a host's own per-editor map, and it is genuinely part of the
|
|
66
|
+
* contract rather than an optimisation that leaked: without it, reconfiguring an
|
|
67
|
+
* editor after a late registration builds *new* instances of the plugins already
|
|
68
|
+
* running, and each one starts from its initial state -- so a table plugin loses
|
|
69
|
+
* its selection and a session plugin its draft, on every later registration.
|
|
70
|
+
* Omit it and every call returns fresh instances, which is correct for a host
|
|
71
|
+
* building one editor once.
|
|
72
|
+
*/
|
|
48
73
|
export function createRegisteredPlugins(schema, cache) {
|
|
49
74
|
const plugins = [];
|
|
50
75
|
const seen = new Set();
|
package/dist/plugins.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAI3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAA;AAChD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA4B;IAC/D,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,IAAI,aAAa,CACrB,kBAAkB,EAClB,qFAAqF;YACnF,uDAAuD,OAAO,OAAO,cAAc;YACnF,oFAAoF;YACpF,+CAA+C,CAClD,CAAA;IACH,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,MAAM,EAAE,CAAA;IACR,OAAO,GAAG,EAAE;QACV,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,2CAA2C;QAC3C,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,MAAM,EAAE,CAAA;IACzC,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,MAAM;IACb,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,QAAQ,EAAE,CAAA;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kEAAkE,EAAE,KAAK,CAAC,CAAA;QAC1F,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAc,EACd,KAA0C;IAE1C,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC3C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACjB,MAAM,MAAM,GAAG,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;QAClC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAA;YACvB,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;YAC/B,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5B,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,yEAAyE;YACzE,wEAAwE;YACxE,oDAAoD;YACpD,OAAO,CAAC,KAAK,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAA;QACpF,CAAC;IACH,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAoB;IACxD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE;QACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC5B,CAAC,CAAA;AACH,CAAC"}
|
package/dist/preserve.d.ts
CHANGED
|
@@ -27,6 +27,36 @@
|
|
|
27
27
|
* and is preserved intact.
|
|
28
28
|
*/
|
|
29
29
|
import type { NodeSpec } from 'prosemirror-model';
|
|
30
|
+
/**
|
|
31
|
+
* Elements that are never preserved, and whose contents are discarded with them.
|
|
32
|
+
*
|
|
33
|
+
* This is where the project's two strongest instincts collide. The preservation
|
|
34
|
+
* layer exists because silently deleting a customer's markup is the failure
|
|
35
|
+
* OpenLeaf was built to prevent -- but "markup the schema does not recognise"
|
|
36
|
+
* includes `<script>`.
|
|
37
|
+
*
|
|
38
|
+
* Preserving an author's `<div class="callout">` is the product working.
|
|
39
|
+
* Preserving a `<script>` is a vulnerability with extra steps: the editor would
|
|
40
|
+
* hand it back on save, the server would store it, and the next reader would
|
|
41
|
+
* execute it. The content-safety promise is about *authorial content*, and a
|
|
42
|
+
* script tag is not that.
|
|
43
|
+
*
|
|
44
|
+
* `ignore` rather than `skip`: the element AND its contents go. Skipping would
|
|
45
|
+
* unwrap `<script>alert(1)</script>` into the literal text "alert(1)" appearing
|
|
46
|
+
* in the document, which is a different kind of wrong.
|
|
47
|
+
*
|
|
48
|
+
* Spread from `@openleaf-editor/content-policy` rather than written out here.
|
|
49
|
+
* This list and the sanitize policy's `dropWithContent` are the same decision
|
|
50
|
+
* made in two packages, they were maintained by hand, and they drifted: `<svg>`
|
|
51
|
+
* and `<math>` were on the sanitizer's list and missing from this one, so the
|
|
52
|
+
* editor stored exactly the markup the server was configured to delete. Sharing
|
|
53
|
+
* the constant is what makes that drift impossible rather than merely unlikely.
|
|
54
|
+
*
|
|
55
|
+
* Exported so the divergence can be asserted in a test as well as prevented by
|
|
56
|
+
* construction. Not re-exported from the package index: it is an internal
|
|
57
|
+
* invariant, and the shared list itself is public in content-policy.
|
|
58
|
+
*/
|
|
59
|
+
export declare const NEVER_PRESERVE: readonly string[];
|
|
30
60
|
/**
|
|
31
61
|
* `<source>` and `<track>`: media children the schema stores as markup rather
|
|
32
62
|
* than as nodes, so they do not count as content when deciding what to keep.
|
|
@@ -67,23 +97,6 @@ export declare function scrub(el: Element): string;
|
|
|
67
97
|
export declare function isLosslesslyUnwrappable(el: Element): boolean;
|
|
68
98
|
/** Run `fn` with preserved-node serialization targeting this Document. */
|
|
69
99
|
export declare function withSerializationDocument<T>(doc: Document, fn: () => T): T;
|
|
70
|
-
/**
|
|
71
|
-
* True while `serializeHtml` is running.
|
|
72
|
-
*
|
|
73
|
-
* A node whose `toDOM` needs to render differently for the editor than for the
|
|
74
|
-
* saved HTML has no other way to tell which one it is building. The table spec
|
|
75
|
-
* needs exactly that: a preserved `<caption>` must be `contenteditable="false"`
|
|
76
|
-
* on screen, because it sits inside the editable area but outside the node's
|
|
77
|
-
* `contentDOM`, and letting a caret into it means typing that ProseMirror will
|
|
78
|
-
* silently revert. That attribute must NOT reach the saved HTML, where it would
|
|
79
|
-
* be our editor scribbling on the author's markup.
|
|
80
|
-
*
|
|
81
|
-
* Stripping it afterwards was the other option and is worse: it cannot tell the
|
|
82
|
-
* attribute it just added from the same attribute in somebody's document -- the
|
|
83
|
-
* collision the preserved-element WeakSet above exists to avoid. Not emitting it
|
|
84
|
-
* at all has no such failure mode.
|
|
85
|
-
*/
|
|
86
|
-
export declare function isSerializing(): boolean;
|
|
87
100
|
/**
|
|
88
101
|
* The Document that serialization should build into.
|
|
89
102
|
*
|
|
@@ -99,6 +112,24 @@ export declare function isInsidePreserved(node: Element | null): boolean;
|
|
|
99
112
|
* delete it, but never edits its interior, so its markup cannot drift.
|
|
100
113
|
*/
|
|
101
114
|
export declare const unknownBlock: NodeSpec;
|
|
115
|
+
/**
|
|
116
|
+
* Start tags that close an open `<p>` in the HTML "in body" insertion mode.
|
|
117
|
+
*
|
|
118
|
+
* Serializing one of these as an inline atom wraps it in `<p>…</p>`. The next
|
|
119
|
+
* parse cannot keep it there, so it splits into an empty paragraph, the
|
|
120
|
+
* element, and another empty paragraph -- and the following save wraps it
|
|
121
|
+
* again. The same unbounded growth `<plaintext>` caused by having no end tag.
|
|
122
|
+
*
|
|
123
|
+
* Shared with anything else that must not claim a p-closing element as inline
|
|
124
|
+
* (`figcaption` being inline is the other door into this). Custom elements are
|
|
125
|
+
* not on the list: they do not close a `<p>`, so `<drupal-media>` inside a
|
|
126
|
+
* blockquote is correctly an inline atom.
|
|
127
|
+
*
|
|
128
|
+
* Source: WHATWG HTML parsing, "in body" -- every start tag that runs
|
|
129
|
+
* "if the stack of open elements has a p element in button scope, then close
|
|
130
|
+
* a p element" before inserting.
|
|
131
|
+
*/
|
|
132
|
+
export declare const CLOSES_OPEN_P: ReadonlySet<string>;
|
|
102
133
|
/**
|
|
103
134
|
* Inline preserved content, for unrecognised markup appearing inside a
|
|
104
135
|
* paragraph -- the `<o:p>` and `<w:sdt>` debris of a Word paste, custom
|
package/dist/preserve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preserve.d.ts","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"preserve.d.ts","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAUjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAA2B,CAAA;AAuBvE;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAgC,CAAA;AAErF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAWrD;AA+BD;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CA+BzC;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAkC5D;AAqED,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ1E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAE9C;AAwCD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAK/D;AAeD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,QA8B1B,CAAA;AA+CD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CA8C5C,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QA2C3B,CAAA"}
|