@kerebron/editor-kits 0.4.7 → 0.4.9
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/esm/AdvancedEditorKit.d.ts.map +1 -0
- package/esm/{editor-kits/src/AdvancedEditorKit.js → AdvancedEditorKit.js} +3 -13
- package/esm/CodeEditorKit.d.ts.map +1 -0
- package/esm/{editor-kits/src/CodeEditorKit.js → CodeEditorKit.js} +3 -13
- package/esm/DevAdvancedEditorKit.d.ts.map +1 -0
- package/esm/{editor-kits/src/DevAdvancedEditorKit.js → DevAdvancedEditorKit.js} +3 -13
- package/esm/LspEditorKit.d.ts.map +1 -0
- package/esm/{editor-kits/src/LspEditorKit.js → LspEditorKit.js} +3 -18
- package/esm/YjsEditorKit.d.ts.map +1 -0
- package/esm/{editor-kits/src/YjsEditorKit.js → YjsEditorKit.js} +4 -18
- package/esm/mod.d.ts +1 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +1 -0
- package/package.json +20 -24
- package/esm/editor/src/commands/mod.d.ts +0 -7
- package/esm/editor/src/commands/mod.d.ts.map +0 -1
- package/esm/editor/src/commands/mod.js +0 -76
- package/esm/editor/src/commands/types.d.ts +0 -18
- package/esm/editor/src/commands/types.d.ts.map +0 -1
- package/esm/editor/src/commands/types.js +0 -1
- package/esm/editor/src/plugins/keymap/keymap.d.ts +0 -11
- package/esm/editor/src/plugins/keymap/keymap.d.ts.map +0 -1
- package/esm/editor/src/plugins/keymap/keymap.js +0 -125
- package/esm/editor/src/plugins/keymap/mod.d.ts +0 -2
- package/esm/editor/src/plugins/keymap/mod.d.ts.map +0 -1
- package/esm/editor/src/plugins/keymap/mod.js +0 -1
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts +0 -4
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts.map +0 -1
- package/esm/editor/src/plugins/keymap/w3c-keyname.js +0 -124
- package/esm/editor-kits/src/AdvancedEditorKit.d.ts.map +0 -1
- package/esm/editor-kits/src/CodeEditorKit.d.ts.map +0 -1
- package/esm/editor-kits/src/DevAdvancedEditorKit.d.ts.map +0 -1
- package/esm/editor-kits/src/LspEditorKit.d.ts.map +0 -1
- package/esm/editor-kits/src/YjsEditorKit.d.ts.map +0 -1
- package/esm/editor-kits/src/mod.d.ts +0 -5
- package/esm/editor-kits/src/mod.d.ts.map +0 -1
- package/esm/editor-kits/src/mod.js +0 -4
- package/esm/extension-basic-editor/src/ExtensionBaseKeymap.d.ts +0 -7
- package/esm/extension-basic-editor/src/ExtensionBaseKeymap.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionBaseKeymap.js +0 -63
- package/esm/extension-basic-editor/src/ExtensionBasicCodeEditor.d.ts +0 -11
- package/esm/extension-basic-editor/src/ExtensionBasicCodeEditor.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionBasicCodeEditor.js +0 -71
- package/esm/extension-basic-editor/src/ExtensionDropcursor.d.ts +0 -19
- package/esm/extension-basic-editor/src/ExtensionDropcursor.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionDropcursor.js +0 -238
- package/esm/extension-basic-editor/src/ExtensionGapcursor.d.ts +0 -32
- package/esm/extension-basic-editor/src/ExtensionGapcursor.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionGapcursor.js +0 -266
- package/esm/extension-basic-editor/src/ExtensionHtml.d.ts +0 -15
- package/esm/extension-basic-editor/src/ExtensionHtml.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionHtml.js +0 -108
- package/esm/extension-basic-editor/src/ExtensionSelection.d.ts +0 -11
- package/esm/extension-basic-editor/src/ExtensionSelection.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/ExtensionSelection.js +0 -237
- package/esm/extension-basic-editor/src/NodeDocumentCode.d.ts +0 -7
- package/esm/extension-basic-editor/src/NodeDocumentCode.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/NodeDocumentCode.js +0 -37
- package/esm/extension-basic-editor/src/NodeText.d.ts +0 -7
- package/esm/extension-basic-editor/src/NodeText.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/NodeText.js +0 -17
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts +0 -24
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.js +0 -35
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts +0 -25
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts.map +0 -1
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.js +0 -96
- /package/esm/{editor-kits/src/AdvancedEditorKit.d.ts → AdvancedEditorKit.d.ts} +0 -0
- /package/esm/{editor-kits/src/CodeEditorKit.d.ts → CodeEditorKit.d.ts} +0 -0
- /package/esm/{editor-kits/src/DevAdvancedEditorKit.d.ts → DevAdvancedEditorKit.d.ts} +0 -0
- /package/esm/{editor-kits/src/LspEditorKit.d.ts → LspEditorKit.d.ts} +0 -0
- /package/esm/{editor-kits/src/YjsEditorKit.d.ts → YjsEditorKit.d.ts} +0 -0
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { NodeSelection, Plugin, Selection, TextSelection, } from 'prosemirror-state';
|
|
2
|
-
import { Fragment, Slice } from 'prosemirror-model';
|
|
3
|
-
import { Decoration, DecorationSet } from 'prosemirror-view';
|
|
4
|
-
import { Extension } from '@kerebron/editor';
|
|
5
|
-
import { keydownHandler } from '../../editor/src/plugins/keymap/mod.js';
|
|
6
|
-
/// Gap cursor selections are represented using this class. Its
|
|
7
|
-
/// `$anchor` and `$head` properties both point at the cursor position.
|
|
8
|
-
export class GapCursor extends Selection {
|
|
9
|
-
/// Create a gap cursor.
|
|
10
|
-
constructor($pos) {
|
|
11
|
-
super($pos, $pos);
|
|
12
|
-
this.visible = false;
|
|
13
|
-
}
|
|
14
|
-
map(doc, mapping) {
|
|
15
|
-
let $pos = doc.resolve(mapping.map(this.head));
|
|
16
|
-
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
|
17
|
-
}
|
|
18
|
-
content() {
|
|
19
|
-
return Slice.empty;
|
|
20
|
-
}
|
|
21
|
-
eq(other) {
|
|
22
|
-
return other instanceof GapCursor && other.head == this.head;
|
|
23
|
-
}
|
|
24
|
-
toJSON() {
|
|
25
|
-
return { type: 'gapcursor', pos: this.head };
|
|
26
|
-
}
|
|
27
|
-
/// @internal
|
|
28
|
-
static fromJSONToGapCursor(doc, json) {
|
|
29
|
-
if (typeof json.pos != 'number') {
|
|
30
|
-
throw new RangeError('Invalid input for GapCursor.fromJSON');
|
|
31
|
-
}
|
|
32
|
-
return new GapCursor(doc.resolve(json.pos));
|
|
33
|
-
}
|
|
34
|
-
/// @internal
|
|
35
|
-
getBookmark() {
|
|
36
|
-
return new GapBookmark(this.anchor);
|
|
37
|
-
}
|
|
38
|
-
/// @internal
|
|
39
|
-
static valid($pos) {
|
|
40
|
-
let parent = $pos.parent;
|
|
41
|
-
if (parent.isTextblock || !closedBefore($pos) || !closedAfter($pos)) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
let override = parent.type.spec.allowGapCursor;
|
|
45
|
-
if (override != null)
|
|
46
|
-
return override;
|
|
47
|
-
let deflt = parent.contentMatchAt($pos.index()).defaultType;
|
|
48
|
-
return deflt && deflt.isTextblock;
|
|
49
|
-
}
|
|
50
|
-
/// @internal
|
|
51
|
-
static findGapCursorFrom($pos, dir, mustMove = false) {
|
|
52
|
-
search: for (;;) {
|
|
53
|
-
if (!mustMove && GapCursor.valid($pos))
|
|
54
|
-
return $pos;
|
|
55
|
-
let pos = $pos.pos, next = null;
|
|
56
|
-
// Scan up from this position
|
|
57
|
-
for (let d = $pos.depth;; d--) {
|
|
58
|
-
let parent = $pos.node(d);
|
|
59
|
-
if (dir > 0 ? $pos.indexAfter(d) < parent.childCount : $pos.index(d) > 0) {
|
|
60
|
-
next = parent.child(dir > 0 ? $pos.indexAfter(d) : $pos.index(d) - 1);
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
else if (d == 0) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
pos += dir;
|
|
67
|
-
let $cur = $pos.doc.resolve(pos);
|
|
68
|
-
if (GapCursor.valid($cur))
|
|
69
|
-
return $cur;
|
|
70
|
-
}
|
|
71
|
-
// And then down into the next node
|
|
72
|
-
for (;;) {
|
|
73
|
-
let inside = dir > 0 ? next.firstChild : next.lastChild;
|
|
74
|
-
if (!inside) {
|
|
75
|
-
if (next.isAtom && !next.isText && !NodeSelection.isSelectable(next)) {
|
|
76
|
-
$pos = $pos.doc.resolve(pos + next.nodeSize * dir);
|
|
77
|
-
mustMove = false;
|
|
78
|
-
continue search;
|
|
79
|
-
}
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
next = inside;
|
|
83
|
-
pos += dir;
|
|
84
|
-
let $cur = $pos.doc.resolve(pos);
|
|
85
|
-
if (GapCursor.valid($cur))
|
|
86
|
-
return $cur;
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
Selection.jsonID('gapcursor', GapCursor);
|
|
93
|
-
class GapBookmark {
|
|
94
|
-
constructor(pos) {
|
|
95
|
-
Object.defineProperty(this, "pos", {
|
|
96
|
-
enumerable: true,
|
|
97
|
-
configurable: true,
|
|
98
|
-
writable: true,
|
|
99
|
-
value: pos
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
map(mapping) {
|
|
103
|
-
return new GapBookmark(mapping.map(this.pos));
|
|
104
|
-
}
|
|
105
|
-
resolve(doc) {
|
|
106
|
-
let $pos = doc.resolve(this.pos);
|
|
107
|
-
return GapCursor.valid($pos) ? new GapCursor($pos) : Selection.near($pos);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function closedBefore($pos) {
|
|
111
|
-
for (let d = $pos.depth; d >= 0; d--) {
|
|
112
|
-
let index = $pos.index(d), parent = $pos.node(d);
|
|
113
|
-
// At the start of this parent, look at next one
|
|
114
|
-
if (index == 0) {
|
|
115
|
-
if (parent.type.spec.isolating)
|
|
116
|
-
return true;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
// See if the node before (or its first ancestor) is closed
|
|
120
|
-
for (let before = parent.child(index - 1);; before = before.lastChild) {
|
|
121
|
-
if ((before.childCount == 0 && !before.inlineContent) || before.isAtom ||
|
|
122
|
-
before.type.spec.isolating)
|
|
123
|
-
return true;
|
|
124
|
-
if (before.inlineContent)
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
// Hit start of document
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
function closedAfter($pos) {
|
|
132
|
-
for (let d = $pos.depth; d >= 0; d--) {
|
|
133
|
-
let index = $pos.indexAfter(d), parent = $pos.node(d);
|
|
134
|
-
if (index == parent.childCount) {
|
|
135
|
-
if (parent.type.spec.isolating)
|
|
136
|
-
return true;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
for (let after = parent.child(index);; after = after.firstChild) {
|
|
140
|
-
if ((after.childCount == 0 && !after.inlineContent) || after.isAtom ||
|
|
141
|
-
after.type.spec.isolating)
|
|
142
|
-
return true;
|
|
143
|
-
if (after.inlineContent)
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
/// Create a gap cursor plugin. When enabled, this will capture clicks
|
|
150
|
-
/// near and arrow-key-motion past places that don't have a normally
|
|
151
|
-
/// selectable position nearby, and create a gap cursor selection for
|
|
152
|
-
/// them. The cursor is drawn as an element with class
|
|
153
|
-
/// `kb-gapcursor`. You can either include
|
|
154
|
-
/// `style/gapcursor.css` from the package's directory or add your own
|
|
155
|
-
/// styles to make it visible.
|
|
156
|
-
function gapCursor() {
|
|
157
|
-
return new Plugin({
|
|
158
|
-
props: {
|
|
159
|
-
decorations: drawGapCursor,
|
|
160
|
-
createSelectionBetween(_view, $anchor, $head) {
|
|
161
|
-
return $anchor.pos == $head.pos && GapCursor.valid($head)
|
|
162
|
-
? new GapCursor($head)
|
|
163
|
-
: null;
|
|
164
|
-
},
|
|
165
|
-
handleClick,
|
|
166
|
-
handleKeyDown,
|
|
167
|
-
handleDOMEvents: { beforeinput: beforeinput },
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
const handleKeyDown = keydownHandler({
|
|
172
|
-
'ArrowLeft': arrow('horiz', -1),
|
|
173
|
-
'ArrowRight': arrow('horiz', 1),
|
|
174
|
-
'ArrowUp': arrow('vert', -1),
|
|
175
|
-
'ArrowDown': arrow('vert', 1),
|
|
176
|
-
});
|
|
177
|
-
function arrow(axis, dir) {
|
|
178
|
-
const dirStr = axis == 'vert'
|
|
179
|
-
? (dir > 0 ? 'down' : 'up')
|
|
180
|
-
: (dir > 0 ? 'right' : 'left');
|
|
181
|
-
return function (state, dispatch, view) {
|
|
182
|
-
let sel = state.selection;
|
|
183
|
-
let $start = dir > 0 ? sel.$to : sel.$from, mustMove = sel.empty;
|
|
184
|
-
if (sel instanceof TextSelection) {
|
|
185
|
-
if (!view.endOfTextblock(dirStr) || $start.depth == 0)
|
|
186
|
-
return false;
|
|
187
|
-
mustMove = false;
|
|
188
|
-
$start = state.doc.resolve(dir > 0 ? $start.after() : $start.before());
|
|
189
|
-
}
|
|
190
|
-
let $found = GapCursor.findGapCursorFrom($start, dir, mustMove);
|
|
191
|
-
if (!$found)
|
|
192
|
-
return false;
|
|
193
|
-
if (dispatch)
|
|
194
|
-
dispatch(state.tr.setSelection(new GapCursor($found)));
|
|
195
|
-
return true;
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function handleClick(view, pos, event) {
|
|
199
|
-
if (!view || !view.editable)
|
|
200
|
-
return false;
|
|
201
|
-
let $pos = view.state.doc.resolve(pos);
|
|
202
|
-
if (!GapCursor.valid($pos))
|
|
203
|
-
return false;
|
|
204
|
-
let clickPos = view.posAtCoords({ left: event.clientX, top: event.clientY });
|
|
205
|
-
if (clickPos && clickPos.inside > -1 &&
|
|
206
|
-
NodeSelection.isSelectable(view.state.doc.nodeAt(clickPos.inside)))
|
|
207
|
-
return false;
|
|
208
|
-
view.dispatch(view.state.tr.setSelection(new GapCursor($pos)));
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
// This is a hack that, when a composition starts while a gap cursor
|
|
212
|
-
// is active, quickly creates an inline context for the composition to
|
|
213
|
-
// happen in, to avoid it being aborted by the DOM selection being
|
|
214
|
-
// moved into a valid position.
|
|
215
|
-
function beforeinput(view, event) {
|
|
216
|
-
if (event.inputType != 'insertCompositionText' ||
|
|
217
|
-
!(view.state.selection instanceof GapCursor))
|
|
218
|
-
return false;
|
|
219
|
-
let { $from } = view.state.selection;
|
|
220
|
-
let insert = $from.parent.contentMatchAt($from.index()).findWrapping(view.state.schema.nodes.text);
|
|
221
|
-
if (!insert)
|
|
222
|
-
return false;
|
|
223
|
-
let frag = Fragment.empty;
|
|
224
|
-
for (let i = insert.length - 1; i >= 0; i--) {
|
|
225
|
-
frag = Fragment.from(insert[i].createAndFill(null, frag));
|
|
226
|
-
}
|
|
227
|
-
let tr = view.state.tr.replace($from.pos, $from.pos, new Slice(frag, 0, 0));
|
|
228
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
229
|
-
view.dispatch(tr);
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
function drawGapCursor(state) {
|
|
233
|
-
if (!(state.selection instanceof GapCursor))
|
|
234
|
-
return null;
|
|
235
|
-
let node = document.createElement('div');
|
|
236
|
-
node.className = 'kb-gapcursor';
|
|
237
|
-
return DecorationSet.create(state.doc, [
|
|
238
|
-
Decoration.widget(state.selection.head, node, { key: 'gapcursor' }),
|
|
239
|
-
]);
|
|
240
|
-
}
|
|
241
|
-
export class ExtensionGapcursor extends Extension {
|
|
242
|
-
constructor() {
|
|
243
|
-
super(...arguments);
|
|
244
|
-
Object.defineProperty(this, "name", {
|
|
245
|
-
enumerable: true,
|
|
246
|
-
configurable: true,
|
|
247
|
-
writable: true,
|
|
248
|
-
value: 'gapcursor'
|
|
249
|
-
});
|
|
250
|
-
Object.defineProperty(this, "options", {
|
|
251
|
-
enumerable: true,
|
|
252
|
-
configurable: true,
|
|
253
|
-
writable: true,
|
|
254
|
-
value: {
|
|
255
|
-
color: 'currentColor',
|
|
256
|
-
width: 1,
|
|
257
|
-
class: undefined,
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
getProseMirrorPlugins() {
|
|
262
|
-
return [
|
|
263
|
-
gapCursor(),
|
|
264
|
-
];
|
|
265
|
-
}
|
|
266
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Fragment, Node, type ParseOptions, Schema } from 'prosemirror-model';
|
|
2
|
-
import { type Converter, type CoreEditor, Extension } from '@kerebron/editor';
|
|
3
|
-
export type CreateNodeFromContentOptions = {
|
|
4
|
-
parseOptions?: ParseOptions;
|
|
5
|
-
errorOnInvalidContent?: boolean;
|
|
6
|
-
};
|
|
7
|
-
export declare function getHTMLFromFragment(fragment: Fragment, schema: Schema): string;
|
|
8
|
-
export declare function elementFromString(value: string): HTMLElement;
|
|
9
|
-
export declare function createNodeFromHTML(content: string, schema: Schema, options?: CreateNodeFromContentOptions): Node;
|
|
10
|
-
export declare function createFragmentFromHTML(content: string, schema: Schema, options?: CreateNodeFromContentOptions): Fragment;
|
|
11
|
-
export declare class ExtensionHtml extends Extension {
|
|
12
|
-
name: string;
|
|
13
|
-
getConverters(editor: CoreEditor, schema: Schema): Record<string, Converter>;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=ExtensionHtml.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionHtml.d.ts","sourceRoot":"","sources":["../../../src/extension-basic-editor/src/ExtensionHtml.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,QAAQ,EACR,IAAI,EACJ,KAAK,YAAY,EACjB,MAAM,EACP,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE9E,MAAM,MAAM,4BAA4B,GAAG;IACzC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,MAAM,CAaR;AAqBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAQ5D;AA+BD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,4BAA4B,GACrC,IAAI,CAgBN;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,4BAA4B,GACrC,QAAQ,CAiBV;AAED,qBAAa,aAAc,SAAQ,SAAS;IAC1C,IAAI,SAAU;IAEL,aAAa,CACpB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;CAe7B"}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { DOMParser, DOMSerializer, Schema, } from 'prosemirror-model';
|
|
2
|
-
import { Extension } from '@kerebron/editor';
|
|
3
|
-
export function getHTMLFromFragment(fragment, schema) {
|
|
4
|
-
const document = globalThis.document;
|
|
5
|
-
const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(fragment, { document });
|
|
6
|
-
const temporaryDocument = document.implementation.createHTMLDocument();
|
|
7
|
-
const container = temporaryDocument.createElement('div');
|
|
8
|
-
container.appendChild(documentFragment);
|
|
9
|
-
return container.innerHTML;
|
|
10
|
-
}
|
|
11
|
-
const removeWhitespaces = (node) => {
|
|
12
|
-
const children = node.childNodes;
|
|
13
|
-
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
14
|
-
const child = children[i];
|
|
15
|
-
if (child.nodeType === 3 && child.nodeValue &&
|
|
16
|
-
/^(\n\s\s|\n)$/.test(child.nodeValue)) {
|
|
17
|
-
node.removeChild(child);
|
|
18
|
-
}
|
|
19
|
-
else if (child.nodeType === 1) {
|
|
20
|
-
removeWhitespaces(child);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return node;
|
|
24
|
-
};
|
|
25
|
-
export function elementFromString(value) {
|
|
26
|
-
// add a wrapper to preserve leading and trailing whitespace
|
|
27
|
-
const wrappedValue = `<body>${value}</body>`;
|
|
28
|
-
const html = new globalThis.DOMParser().parseFromString(wrappedValue, 'text/html').body;
|
|
29
|
-
return removeWhitespaces(html);
|
|
30
|
-
}
|
|
31
|
-
function prepareContentCheckSchema(schema) {
|
|
32
|
-
const contentCheckSchema = new Schema({
|
|
33
|
-
topNode: schema.spec.topNode,
|
|
34
|
-
marks: schema.spec.marks,
|
|
35
|
-
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
|
36
|
-
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
|
|
37
|
-
nodes: schema.spec.nodes.append({
|
|
38
|
-
__unknown__catch__all__node: {
|
|
39
|
-
content: 'inline*',
|
|
40
|
-
group: 'block',
|
|
41
|
-
parseDOM: [
|
|
42
|
-
{
|
|
43
|
-
tag: '*',
|
|
44
|
-
getAttrs: (e) => {
|
|
45
|
-
// Try to stringify the element for a more helpful error message
|
|
46
|
-
const invalidContent = typeof e === 'string' ? e : e.outerHTML;
|
|
47
|
-
throw new Error('Invalid HTML content', {
|
|
48
|
-
cause: new Error(`Invalid element found: ${invalidContent}`),
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
}),
|
|
55
|
-
});
|
|
56
|
-
return contentCheckSchema;
|
|
57
|
-
}
|
|
58
|
-
export function createNodeFromHTML(content, schema, options) {
|
|
59
|
-
options = {
|
|
60
|
-
parseOptions: {},
|
|
61
|
-
...options,
|
|
62
|
-
};
|
|
63
|
-
if (options.errorOnInvalidContent) {
|
|
64
|
-
const contentCheckSchema = prepareContentCheckSchema(schema);
|
|
65
|
-
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
|
|
66
|
-
}
|
|
67
|
-
const parser = DOMParser.fromSchema(schema);
|
|
68
|
-
return parser.parse(elementFromString(content), options.parseOptions);
|
|
69
|
-
}
|
|
70
|
-
export function createFragmentFromHTML(content, schema, options) {
|
|
71
|
-
options = {
|
|
72
|
-
parseOptions: {},
|
|
73
|
-
...options,
|
|
74
|
-
};
|
|
75
|
-
if (options.errorOnInvalidContent) {
|
|
76
|
-
const contentCheckSchema = prepareContentCheckSchema(schema);
|
|
77
|
-
DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
|
|
78
|
-
}
|
|
79
|
-
const parser = DOMParser.fromSchema(schema);
|
|
80
|
-
return parser.parseSlice(elementFromString(content), options.parseOptions)
|
|
81
|
-
.content;
|
|
82
|
-
}
|
|
83
|
-
export class ExtensionHtml extends Extension {
|
|
84
|
-
constructor() {
|
|
85
|
-
super(...arguments);
|
|
86
|
-
Object.defineProperty(this, "name", {
|
|
87
|
-
enumerable: true,
|
|
88
|
-
configurable: true,
|
|
89
|
-
writable: true,
|
|
90
|
-
value: 'html'
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
getConverters(editor, schema) {
|
|
94
|
-
const config = this.config;
|
|
95
|
-
return {
|
|
96
|
-
'text/html': {
|
|
97
|
-
fromDoc: async (document) => {
|
|
98
|
-
const html = getHTMLFromFragment(document.content, editor.schema);
|
|
99
|
-
return new TextEncoder().encode(html);
|
|
100
|
-
},
|
|
101
|
-
toDoc: async (buffer) => {
|
|
102
|
-
const html = new TextDecoder().decode(buffer);
|
|
103
|
-
return createNodeFromHTML(html, editor.schema);
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Node } from 'prosemirror-model';
|
|
2
|
-
import { type CoreEditor, Extension } from '@kerebron/editor';
|
|
3
|
-
import type { CommandFactories } from '../../editor/src/commands/mod.js';
|
|
4
|
-
export declare class ExtensionSelection extends Extension {
|
|
5
|
-
name: string;
|
|
6
|
-
extractSelection(): Node;
|
|
7
|
-
replaceSelection(otherDoc: Node): void;
|
|
8
|
-
appendSelection(otherDoc: Node): void;
|
|
9
|
-
getCommandFactories(editor: CoreEditor): Partial<CommandFactories>;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=ExtensionSelection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionSelection.d.ts","sourceRoot":"","sources":["../../../src/extension-basic-editor/src/ExtensionSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,EAIL,MAAM,mBAAmB,CAAC;AAS3B,OAAO,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,kCAAkC,CAAC;AAuS1C,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,IAAI,SAAe;IAEnB,gBAAgB,IAAI,IAAI;IAaxB,gBAAgB,CAAC,QAAQ,EAAE,IAAI;IAuB/B,eAAe,CAAC,QAAQ,EAAE,IAAI;IAmBrB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAO5E"}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { Fragment, Slice, } from 'prosemirror-model';
|
|
2
|
-
import { AllSelection, TextSelection, } from 'prosemirror-state';
|
|
3
|
-
import { Extension } from '@kerebron/editor';
|
|
4
|
-
import { createNodeFromObject } from '@kerebron/editor/utilities';
|
|
5
|
-
function normalizeSiblings(fragment, $context) {
|
|
6
|
-
if (fragment.childCount < 2)
|
|
7
|
-
return fragment;
|
|
8
|
-
for (let d = $context.depth; d >= 0; d--) {
|
|
9
|
-
let parent = $context.node(d);
|
|
10
|
-
let match = parent.contentMatchAt($context.index(d));
|
|
11
|
-
let lastWrap;
|
|
12
|
-
let result = [];
|
|
13
|
-
fragment.forEach((node) => {
|
|
14
|
-
if (!result)
|
|
15
|
-
return;
|
|
16
|
-
let wrap = match.findWrapping(node.type);
|
|
17
|
-
let inLast;
|
|
18
|
-
if (!wrap)
|
|
19
|
-
return result = null;
|
|
20
|
-
if (inLast = result.length && lastWrap.length &&
|
|
21
|
-
addToSibling(wrap, lastWrap, node, result[result.length - 1], 0)) {
|
|
22
|
-
result[result.length - 1] = inLast;
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
if (result.length) {
|
|
26
|
-
result[result.length - 1] = closeRight(result[result.length - 1], lastWrap.length);
|
|
27
|
-
}
|
|
28
|
-
let wrapped = withWrappers(node, wrap);
|
|
29
|
-
result.push(wrapped);
|
|
30
|
-
match = match.matchType(wrapped.type);
|
|
31
|
-
lastWrap = wrap;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
if (result)
|
|
35
|
-
return Fragment.from(result);
|
|
36
|
-
}
|
|
37
|
-
return fragment;
|
|
38
|
-
}
|
|
39
|
-
function withWrappers(node, wrap, from = 0) {
|
|
40
|
-
for (let i = wrap.length - 1; i >= from; i--) {
|
|
41
|
-
node = wrap[i].create(null, Fragment.from(node));
|
|
42
|
-
}
|
|
43
|
-
return node;
|
|
44
|
-
}
|
|
45
|
-
function addToSibling(wrap, lastWrap, node, sibling, depth) {
|
|
46
|
-
if (depth < wrap.length && depth < lastWrap.length &&
|
|
47
|
-
wrap[depth] == lastWrap[depth]) {
|
|
48
|
-
let inner = addToSibling(wrap, lastWrap, node, sibling.lastChild, depth + 1);
|
|
49
|
-
if (inner) {
|
|
50
|
-
return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner));
|
|
51
|
-
}
|
|
52
|
-
let match = sibling.contentMatchAt(sibling.childCount);
|
|
53
|
-
if (match.matchType(depth == wrap.length - 1 ? node.type : wrap[depth + 1])) {
|
|
54
|
-
return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function closeRight(node, depth) {
|
|
59
|
-
if (depth == 0)
|
|
60
|
-
return node;
|
|
61
|
-
let fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.lastChild, depth - 1));
|
|
62
|
-
let fill = node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true);
|
|
63
|
-
return node.copy(fragment.append(fill));
|
|
64
|
-
}
|
|
65
|
-
function closeRange(fragment, side, from, to, depth, openEnd) {
|
|
66
|
-
let node = side < 0 ? fragment.firstChild : fragment.lastChild, inner = node.content;
|
|
67
|
-
if (fragment.childCount > 1)
|
|
68
|
-
openEnd = 0;
|
|
69
|
-
if (depth < to - 1) {
|
|
70
|
-
inner = closeRange(inner, side, from, to, depth + 1, openEnd);
|
|
71
|
-
}
|
|
72
|
-
if (depth >= from) {
|
|
73
|
-
inner = side < 0
|
|
74
|
-
? node.contentMatchAt(0).fillBefore(inner, openEnd <= depth).append(inner)
|
|
75
|
-
: inner.append(node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true));
|
|
76
|
-
}
|
|
77
|
-
return fragment.replaceChild(side < 0 ? 0 : fragment.childCount - 1, node.copy(inner));
|
|
78
|
-
}
|
|
79
|
-
function closeSlice(slice, openStart, openEnd) {
|
|
80
|
-
if (openStart < slice.openStart) {
|
|
81
|
-
slice = new Slice(closeRange(slice.content, -1, openStart, slice.openStart, 0, slice.openEnd), openStart, slice.openEnd);
|
|
82
|
-
}
|
|
83
|
-
if (openEnd < slice.openEnd) {
|
|
84
|
-
slice = new Slice(closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0), slice.openStart, openEnd);
|
|
85
|
-
}
|
|
86
|
-
return slice;
|
|
87
|
-
}
|
|
88
|
-
function sliceSingleNode(slice) {
|
|
89
|
-
return slice.openStart == 0 && slice.openEnd == 0 &&
|
|
90
|
-
slice.content.childCount == 1
|
|
91
|
-
? slice.content.firstChild
|
|
92
|
-
: null;
|
|
93
|
-
}
|
|
94
|
-
function fixSlice(slice, $context) {
|
|
95
|
-
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
|
|
96
|
-
if (slice.openStart || slice.openEnd) {
|
|
97
|
-
let openStart = 0, openEnd = 0;
|
|
98
|
-
for (let node = slice.content.firstChild; openStart < slice.openStart && !node.type.spec.isolating; openStart++, node = node.firstChild
|
|
99
|
-
// deno-lint-ignore no-empty
|
|
100
|
-
) { }
|
|
101
|
-
for (let node = slice.content.lastChild; openEnd < slice.openEnd && !node.type.spec.isolating; openEnd++, node = node.lastChild
|
|
102
|
-
// deno-lint-ignore no-empty
|
|
103
|
-
) { }
|
|
104
|
-
slice = closeSlice(slice, openStart, openEnd);
|
|
105
|
-
}
|
|
106
|
-
return slice;
|
|
107
|
-
}
|
|
108
|
-
function sliceHasOnlyText(slice) {
|
|
109
|
-
return slice.content.content.every((node) => node.isInline);
|
|
110
|
-
}
|
|
111
|
-
const selectAll = () => {
|
|
112
|
-
return function (state, dispatch, view) {
|
|
113
|
-
const tr = state.tr.setSelection(new AllSelection(state.doc));
|
|
114
|
-
if (view) {
|
|
115
|
-
view.dispatch(tr);
|
|
116
|
-
}
|
|
117
|
-
return true;
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
function textPositionsToResolvedPos(textPosVec, doc, paraNum) {
|
|
121
|
-
const retVal = textPosVec.map((x) => -1);
|
|
122
|
-
let currentTextPos = 0;
|
|
123
|
-
let inParaRange = false;
|
|
124
|
-
function callback(currentPos, level, idx, textLen) {
|
|
125
|
-
if (!inParaRange) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
for (let i = 0; i < textPosVec.length; i++) {
|
|
129
|
-
const val = textPosVec[i];
|
|
130
|
-
if (val >= currentTextPos && val < currentTextPos + textLen) {
|
|
131
|
-
retVal[i] = currentPos + (val - currentTextPos);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
currentTextPos += textLen;
|
|
135
|
-
}
|
|
136
|
-
function treeTraverse(node, level = 0, idx = 0, currentPos = 0) {
|
|
137
|
-
if (level === 1 && idx === paraNum) {
|
|
138
|
-
inParaRange = true;
|
|
139
|
-
}
|
|
140
|
-
let textLen = 0;
|
|
141
|
-
if (node.isText && node.text) {
|
|
142
|
-
textLen = node.text?.length;
|
|
143
|
-
}
|
|
144
|
-
else if (node.isLeaf) {
|
|
145
|
-
textLen = 1;
|
|
146
|
-
}
|
|
147
|
-
if (textLen > 0) {
|
|
148
|
-
callback(currentPos, level, idx, textLen);
|
|
149
|
-
}
|
|
150
|
-
node.forEach((child, offset, childIndex) => {
|
|
151
|
-
treeTraverse(child, level + 1, childIndex, currentPos + offset + 1);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
treeTraverse(doc);
|
|
155
|
-
if (inParaRange) {
|
|
156
|
-
for (let i = 0; i < textPosVec.length; i++) {
|
|
157
|
-
const val = textPosVec[i];
|
|
158
|
-
if (retVal[i] === -1) {
|
|
159
|
-
if (val < currentTextPos) {
|
|
160
|
-
retVal[i] = 1;
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
retVal[i] = doc.nodeSize - 1;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return retVal.map((x) => doc.resolve(x - 1));
|
|
169
|
-
}
|
|
170
|
-
const selectText = (textStart, length, paraNum = 0) => {
|
|
171
|
-
return function (state, dispatch, view) {
|
|
172
|
-
const [$head, $anchor] = textPositionsToResolvedPos([textStart + length, textStart], state.doc, paraNum);
|
|
173
|
-
const tr = state.tr.setSelection(new TextSelection($anchor, $head));
|
|
174
|
-
if (view) {
|
|
175
|
-
view.dispatch(tr);
|
|
176
|
-
}
|
|
177
|
-
return true;
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
|
-
export class ExtensionSelection extends Extension {
|
|
181
|
-
constructor() {
|
|
182
|
-
super(...arguments);
|
|
183
|
-
Object.defineProperty(this, "name", {
|
|
184
|
-
enumerable: true,
|
|
185
|
-
configurable: true,
|
|
186
|
-
writable: true,
|
|
187
|
-
value: 'selection'
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
extractSelection() {
|
|
191
|
-
const state = this.editor.state;
|
|
192
|
-
const { from, to } = state.selection;
|
|
193
|
-
const slice = state.doc.slice(from, to);
|
|
194
|
-
if (sliceHasOnlyText(slice)) {
|
|
195
|
-
const para = state.schema.nodes.paragraph.create(null, slice.content);
|
|
196
|
-
return state.schema.topNodeType.createAndFill(null, [para]);
|
|
197
|
-
}
|
|
198
|
-
return state.schema.topNodeType.createAndFill(null, slice.content);
|
|
199
|
-
}
|
|
200
|
-
replaceSelection(otherDoc) {
|
|
201
|
-
const preferPlain = false;
|
|
202
|
-
const view = this.editor.view;
|
|
203
|
-
const state = this.editor.state;
|
|
204
|
-
let slice;
|
|
205
|
-
if (otherDoc.type?.name === 'doc') {
|
|
206
|
-
otherDoc = createNodeFromObject(otherDoc.toJSON(), this.editor.schema);
|
|
207
|
-
}
|
|
208
|
-
slice = new Slice(otherDoc.content, 1, 1);
|
|
209
|
-
const $context = state.selection.$from;
|
|
210
|
-
slice = fixSlice(slice, $context);
|
|
211
|
-
let singleNode = sliceSingleNode(slice);
|
|
212
|
-
let tr = singleNode
|
|
213
|
-
? state.tr.replaceSelectionWith(singleNode, preferPlain)
|
|
214
|
-
: state.tr.replaceSelection(slice);
|
|
215
|
-
view.dispatch(tr.scrollIntoView());
|
|
216
|
-
}
|
|
217
|
-
appendSelection(otherDoc) {
|
|
218
|
-
const view = this.editor.view;
|
|
219
|
-
const { state } = view;
|
|
220
|
-
let slice;
|
|
221
|
-
if (otherDoc.type?.name === 'doc') {
|
|
222
|
-
otherDoc = createNodeFromObject(otherDoc.toJSON(), this.editor.schema);
|
|
223
|
-
}
|
|
224
|
-
slice = new Slice(otherDoc.content, 1, 1);
|
|
225
|
-
const $context = view.state.selection.$from;
|
|
226
|
-
slice = fixSlice(slice, $context);
|
|
227
|
-
const tr = state.tr.insert(view.state.selection.to, slice.content);
|
|
228
|
-
view.dispatch(tr.scrollIntoView());
|
|
229
|
-
}
|
|
230
|
-
getCommandFactories(editor) {
|
|
231
|
-
this.editor = editor;
|
|
232
|
-
return {
|
|
233
|
-
'selectAll': () => selectAll(),
|
|
234
|
-
'selectText': (...args) => selectText(...args),
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NodeDocumentCode.d.ts","sourceRoot":"","sources":["../../../src/extension-basic-editor/src/NodeDocumentCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,qBAAa,gBAAiB,SAAQ,IAAI;IAC/B,IAAI,SAAc;IAElB,WAAW,IAAI,QAAQ;CAyBjC"}
|