@prosekit/core 0.8.0 → 0.8.1

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.
@@ -1,1188 +0,0 @@
1
- // src/error.ts
2
- var ProseKitError = class extends Error {
3
- };
4
- var EditorNotFoundError = class extends ProseKitError {
5
- constructor() {
6
- super(
7
- "Unable to find editor. Pass it as an argument or call this function inside a ProseKit component."
8
- );
9
- }
10
- };
11
- var DOMDocumentNotFoundError = class extends ProseKitError {
12
- constructor() {
13
- super(
14
- "Unable to find browser Document. When not in the browser environment, you need to pass a DOM Document."
15
- );
16
- }
17
- };
18
-
19
- // src/utils/get-mark-type.ts
20
- function getMarkType(schema, type) {
21
- if (typeof type === "string") {
22
- const markType = schema.marks[type];
23
- if (!markType) {
24
- throw new ProseKitError(`Cannot find mark type "${type}"`);
25
- }
26
- return markType;
27
- }
28
- return type;
29
- }
30
-
31
- // src/utils/assert.ts
32
- function assert(condition, message = "Assertion failed") {
33
- if (!condition) {
34
- throw new ProseKitError(message);
35
- }
36
- }
37
-
38
- // src/utils/get-node-type.ts
39
- function getNodeType(schema, type) {
40
- if (typeof type === "string") {
41
- const nodeType = schema.nodes[type];
42
- if (!nodeType) {
43
- throw new ProseKitError(`Cannot find ProseMirror node type "${type}"`);
44
- }
45
- return nodeType;
46
- }
47
- return type;
48
- }
49
-
50
- // src/types/priority.ts
51
- var Priority = /* @__PURE__ */ ((Priority2) => {
52
- Priority2[Priority2["lowest"] = 0] = "lowest";
53
- Priority2[Priority2["low"] = 1] = "low";
54
- Priority2[Priority2["default"] = 2] = "default";
55
- Priority2[Priority2["high"] = 3] = "high";
56
- Priority2[Priority2["highest"] = 4] = "highest";
57
- return Priority2;
58
- })(Priority || {});
59
-
60
- // src/facets/facet.ts
61
- var facetCount = 0;
62
- var Facet = class {
63
- /**
64
- * @internal
65
- */
66
- constructor(parent, singleton, _reducer, _reduce) {
67
- this._reducer = _reducer;
68
- this._reduce = _reduce;
69
- /**
70
- * @internal
71
- */
72
- this.index = facetCount++;
73
- assert((_reduce || _reducer) && !(_reduce && _reducer));
74
- this.parent = parent;
75
- this.singleton = singleton;
76
- this.path = parent ? [...parent.path, this.index] : [];
77
- }
78
- get reducer() {
79
- return this._reducer ?? this._reduce?.();
80
- }
81
- };
82
- function defineFacet(options) {
83
- return new Facet(
84
- options.parent,
85
- options.singleton ?? false,
86
- options.reducer,
87
- options.reduce
88
- );
89
- }
90
-
91
- // src/utils/type-assertion.ts
92
- import {
93
- Fragment,
94
- Mark,
95
- ProseMirrorNode,
96
- Slice
97
- } from "@prosekit/pm/model";
98
- import {
99
- AllSelection,
100
- NodeSelection,
101
- Selection,
102
- TextSelection
103
- } from "@prosekit/pm/state";
104
- function isProseMirrorNode(node) {
105
- return node instanceof ProseMirrorNode;
106
- }
107
- function isMark(mark) {
108
- return mark instanceof Mark;
109
- }
110
- function isFragment(fragment) {
111
- return fragment instanceof Fragment;
112
- }
113
- function isSlice(slice) {
114
- return slice instanceof Slice;
115
- }
116
- function isSelection(sel) {
117
- return sel instanceof Selection;
118
- }
119
- function isTextSelection(sel) {
120
- return sel instanceof TextSelection;
121
- }
122
- function isNodeSelection(sel) {
123
- return sel instanceof NodeSelection;
124
- }
125
- function isAllSelection(sel) {
126
- return sel instanceof AllSelection;
127
- }
128
- function isNotNullish(value) {
129
- return value != null;
130
- }
131
-
132
- // src/facets/schema.ts
133
- import {
134
- Schema
135
- } from "@prosekit/pm/model";
136
-
137
- // src/facets/root.ts
138
- function rootReducer(inputs) {
139
- let schema;
140
- let commands;
141
- let stateFunc;
142
- let view;
143
- for (const input of inputs) {
144
- schema = input.schema || schema;
145
- commands = input.commands || commands;
146
- stateFunc = input.state || stateFunc;
147
- view = input.view || view;
148
- }
149
- const state = schema && (stateFunc?.({ schema }) ?? { schema });
150
- return { schema, state, commands, view };
151
- }
152
- var rootFacet = new Facet(null, true, rootReducer);
153
-
154
- // src/facets/schema.ts
155
- var schemaFacet = defineFacet({
156
- reducer: (specs) => {
157
- assert(specs.length <= 1);
158
- const spec = specs[0];
159
- const schema = spec ? new Schema(spec) : null;
160
- return { schema };
161
- },
162
- parent: rootFacet,
163
- singleton: true
164
- });
165
-
166
- // src/facets/base-extension.ts
167
- var BaseExtension = class {
168
- constructor() {
169
- this.trees = [null, null, null, null, null];
170
- }
171
- /**
172
- * @internal
173
- */
174
- getTree(priority) {
175
- var _a;
176
- const pri = priority ?? this.priority ?? 2 /* default */;
177
- return (_a = this.trees)[pri] || (_a[pri] = this.createTree(pri));
178
- }
179
- /**
180
- * @internal
181
- */
182
- findFacetOutput(facet) {
183
- let node = this.getTree();
184
- for (const index of facet.path) {
185
- node = node?.children.get(index);
186
- }
187
- return node?.getOutput() ?? null;
188
- }
189
- get schema() {
190
- const output = this.findFacetOutput(schemaFacet);
191
- return output?.find(Boolean)?.schema ?? null;
192
- }
193
- };
194
-
195
- // src/utils/array.ts
196
- function uniqPush(prev, next) {
197
- const result = [...prev];
198
- for (const item of next) {
199
- if (!result.includes(item)) {
200
- result.push(item);
201
- }
202
- }
203
- return result;
204
- }
205
- function arraySubstract(a, b) {
206
- return a.filter((x) => !b.includes(x));
207
- }
208
- function toReversed(arr) {
209
- return arr.toReversed?.() ?? [...arr].reverse();
210
- }
211
-
212
- // src/facets/facet-node.ts
213
- function zip5(a, b, mapper) {
214
- return [
215
- mapper(a[0], b[0]),
216
- mapper(a[1], b[1]),
217
- mapper(a[2], b[2]),
218
- mapper(a[3], b[3]),
219
- mapper(a[4], b[4])
220
- ];
221
- }
222
- function unionInput(a, b) {
223
- if (!a && !b) return null;
224
- return uniqPush(a ?? [], b ?? []);
225
- }
226
- function subtractInput(a, b) {
227
- if (!a) return null;
228
- if (!b) return [...a];
229
- return arraySubstract(a, b);
230
- }
231
- function unionChildren(a, b) {
232
- const merged = new Map(a);
233
- for (const [key, valueB] of b.entries()) {
234
- const valueA = a.get(key);
235
- merged.set(key, valueA ? unionFacetNode(valueA, valueB) : valueB);
236
- }
237
- return merged;
238
- }
239
- function subtractChildren(a, b) {
240
- const merged = new Map(a);
241
- for (const [key, valueB] of b.entries()) {
242
- const valueA = a.get(key);
243
- if (valueA) {
244
- merged.set(key, subtractFacetNode(valueA, valueB));
245
- }
246
- }
247
- return merged;
248
- }
249
- function unionFacetNode(a, b) {
250
- assert(a.facet === b.facet);
251
- return new FacetNode(
252
- a.facet,
253
- zip5(a.inputs, b.inputs, unionInput),
254
- unionChildren(a.children, b.children),
255
- a.reducers
256
- );
257
- }
258
- function subtractFacetNode(a, b) {
259
- assert(a.facet === b.facet);
260
- return new FacetNode(
261
- a.facet,
262
- zip5(a.inputs, b.inputs, subtractInput),
263
- subtractChildren(a.children, b.children),
264
- a.reducers
265
- );
266
- }
267
- var FacetNode = class {
268
- constructor(facet, inputs = [null, null, null, null, null], children = /* @__PURE__ */ new Map(), reducers = [
269
- null,
270
- null,
271
- null,
272
- null,
273
- null
274
- ]) {
275
- this.facet = facet;
276
- this.inputs = inputs;
277
- this.children = children;
278
- this.reducers = reducers;
279
- this.output = null;
280
- }
281
- calcOutput() {
282
- var _a, _b, _c;
283
- const inputs = [null, null, null, null, null];
284
- const output = [null, null, null, null, null];
285
- for (let pri = 0; pri < 5; pri++) {
286
- const input = this.inputs[pri];
287
- if (input) {
288
- inputs[pri] = [...input];
289
- }
290
- }
291
- for (const child of this.children.values()) {
292
- const childOutput = child.getOutput();
293
- for (let pri = 0; pri < 5; pri++) {
294
- if (childOutput[pri]) {
295
- const input = inputs[pri] || (inputs[pri] = []);
296
- input.push(childOutput[pri]);
297
- }
298
- }
299
- }
300
- if (this.facet.singleton) {
301
- const reducer = (_a = this.reducers)[_b = 2 /* default */] || (_a[_b] = this.facet.reducer);
302
- const input = inputs.filter(isNotNullish).flat();
303
- output[2 /* default */] = reducer(input);
304
- } else {
305
- for (let pri = 0; pri < 5; pri++) {
306
- const input = inputs[pri];
307
- if (input) {
308
- const reducer = (_c = this.reducers)[pri] || (_c[pri] = this.facet.reducer);
309
- output[pri] = reducer(input);
310
- }
311
- }
312
- }
313
- return output;
314
- }
315
- getOutput() {
316
- if (!this.output) {
317
- this.output = this.calcOutput();
318
- }
319
- return this.output;
320
- }
321
- getSingletonOutput() {
322
- assert(this.facet.singleton);
323
- return this.getOutput()[2 /* default */];
324
- }
325
- getRootOutput() {
326
- assert(this.isRoot());
327
- const output = this.getSingletonOutput();
328
- assert(output);
329
- return output;
330
- }
331
- isRoot() {
332
- return !this.facet.parent;
333
- }
334
- };
335
-
336
- // src/facets/facet-extension.ts
337
- var FacetExtensionImpl = class extends BaseExtension {
338
- /**
339
- * @internal
340
- */
341
- constructor(facet, payloads) {
342
- super();
343
- this.facet = facet;
344
- this.payloads = payloads;
345
- }
346
- /**
347
- * @internal
348
- */
349
- createTree(priority) {
350
- const pri = this.priority ?? priority;
351
- const inputs = [null, null, null, null, null];
352
- inputs[pri] = [...this.payloads];
353
- let node = new FacetNode(this.facet, inputs);
354
- while (node.facet.parent) {
355
- const children = /* @__PURE__ */ new Map([[node.facet.index, node]]);
356
- node = new FacetNode(node.facet.parent, void 0, children);
357
- }
358
- return node;
359
- }
360
- };
361
- function defineFacetPayload(facet, payloads) {
362
- return new FacetExtensionImpl(facet, payloads);
363
- }
364
-
365
- // src/utils/is-object.ts
366
- function isObject(v) {
367
- return typeof v === "object" && v != null;
368
- }
369
-
370
- // src/utils/is-element.ts
371
- var ELEMENT_NODE = 1;
372
- function isElement(el) {
373
- return isObject(el) && el.nodeType === ELEMENT_NODE && typeof el.nodeName === "string";
374
- }
375
-
376
- // src/utils/parse.ts
377
- import {
378
- DOMParser,
379
- DOMSerializer
380
- } from "@prosekit/pm/model";
381
- import { EditorState } from "@prosekit/pm/state";
382
-
383
- // src/utils/get-dom-api.ts
384
- function findGlobalBrowserDocument() {
385
- if (typeof document !== "undefined") {
386
- return document;
387
- }
388
- if (typeof globalThis !== "undefined" && globalThis.document) {
389
- return globalThis.document;
390
- }
391
- }
392
- function findGlobalBrowserWindow() {
393
- if (typeof window !== "undefined") {
394
- return window;
395
- }
396
- if (typeof globalThis !== "undefined" && globalThis.window) {
397
- return globalThis.window;
398
- }
399
- }
400
- function findBrowserDocument(options) {
401
- return options?.document ?? findGlobalBrowserDocument() ?? findGlobalBrowserWindow()?.document;
402
- }
403
- function findBrowserWindow(options) {
404
- return options?.document?.defaultView ?? findGlobalBrowserWindow() ?? findBrowserDocument(options)?.defaultView ?? void 0;
405
- }
406
- function getBrowserDocument(options) {
407
- const doc = findBrowserDocument(options);
408
- if (doc) return doc;
409
- throw new DOMDocumentNotFoundError();
410
- }
411
- function getBrowserWindow(options) {
412
- const win = findBrowserWindow(options);
413
- if (win) return win;
414
- throw new DOMDocumentNotFoundError();
415
- }
416
-
417
- // src/utils/parse.ts
418
- function jsonFromState(state) {
419
- return state.toJSON();
420
- }
421
- function stateFromJSON(json, options) {
422
- return EditorState.fromJSON({ schema: options.schema }, json);
423
- }
424
- function jsonFromNode(node) {
425
- return node.toJSON();
426
- }
427
- function nodeFromJSON(json, options) {
428
- return options.schema.nodeFromJSON(json);
429
- }
430
- function nodeFromElement(element, options) {
431
- const { DOMParser: CustomDOMParser, schema, ...parseOptions } = options;
432
- return (CustomDOMParser || DOMParser).fromSchema(schema).parse(element, parseOptions);
433
- }
434
- function elementFromNode(node, options) {
435
- const Serializer = options?.DOMSerializer || DOMSerializer;
436
- const document2 = getBrowserDocument(options);
437
- const schema = node.type.schema;
438
- const serializer = Serializer.fromSchema(schema);
439
- if (schema.topNodeType !== node.type) {
440
- return serializer.serializeNode(node, { document: document2 });
441
- } else {
442
- return serializer.serializeFragment(
443
- node.content,
444
- { document: document2 },
445
- document2.createElement("div")
446
- );
447
- }
448
- }
449
- function elementFromHTML(html, options) {
450
- const win = getBrowserWindow(options);
451
- const parser = new win.DOMParser();
452
- return parser.parseFromString(`<body><div>${html}</div></body>`, "text/html").body.firstElementChild;
453
- }
454
- function htmlFromElement(element) {
455
- return element.outerHTML;
456
- }
457
- function nodeFromHTML(html, options) {
458
- return nodeFromElement(elementFromHTML(html, options), options);
459
- }
460
- function htmlFromNode(node, options) {
461
- return elementFromNode(node, options).outerHTML;
462
- }
463
- function jsonFromElement(element, options) {
464
- return jsonFromNode(nodeFromElement(element, options));
465
- }
466
- function elementFromJSON(json, options) {
467
- return elementFromNode(nodeFromJSON(json, options), options);
468
- }
469
- function jsonFromHTML(html, options) {
470
- return jsonFromElement(elementFromHTML(html, options), options);
471
- }
472
- function htmlFromJSON(json, options) {
473
- return htmlFromElement(elementFromJSON(json, options));
474
- }
475
-
476
- // src/extensions/default-state.ts
477
- import {
478
- Selection as Selection3
479
- } from "@prosekit/pm/state";
480
-
481
- // src/facets/state.ts
482
- var stateFacet = defineFacet({
483
- reduce: () => {
484
- let callbacks = [];
485
- const state = (ctx) => {
486
- const configs = callbacks.map((cb) => cb(ctx));
487
- const config = {
488
- schema: ctx.schema,
489
- storedMarks: [],
490
- plugins: []
491
- };
492
- for (const c of configs) {
493
- config.schema = config.schema ?? c.schema;
494
- config.doc = config.doc ?? c.doc;
495
- config.selection = config.selection ?? c.selection;
496
- config.storedMarks = [...config.storedMarks, ...c.storedMarks ?? []];
497
- config.plugins = uniqPush(config.plugins ?? [], c.plugins ?? []);
498
- }
499
- assert(
500
- config.doc || config.schema,
501
- "Can't create state without a schema nor a document"
502
- );
503
- if (config.doc) {
504
- config.schema = void 0;
505
- }
506
- return config;
507
- };
508
- return function reducer(inputs) {
509
- callbacks = inputs;
510
- return { state };
511
- };
512
- },
513
- singleton: true,
514
- parent: rootFacet
515
- });
516
-
517
- // src/utils/editor-content.ts
518
- import { Selection as Selection2 } from "@prosekit/pm/state";
519
- function getEditorContentJSON(schema, content) {
520
- if (typeof content === "string") {
521
- return jsonFromHTML(content, { schema });
522
- } else if (isElement(content)) {
523
- return jsonFromElement(content, { schema });
524
- } else {
525
- return content;
526
- }
527
- }
528
- function getEditorContentNode(schema, content) {
529
- if (isProseMirrorNode(content)) {
530
- return content;
531
- }
532
- return schema.nodeFromJSON(getEditorContentJSON(schema, content));
533
- }
534
- function getEditorContentDoc(schema, content) {
535
- const doc = getEditorContentNode(schema, content);
536
- assert(
537
- doc.type.schema === schema,
538
- "Document schema does not match editor schema"
539
- );
540
- assert(
541
- doc.type === schema.topNodeType,
542
- `Document type does not match editor top node type. Expected ${schema.topNodeType.name}, got ${doc.type.name}`
543
- );
544
- return doc;
545
- }
546
- function getEditorSelection(doc, selection) {
547
- if (isSelection(selection)) {
548
- assert(selection.$head.doc === doc, "Selection and doc do not match");
549
- return selection;
550
- }
551
- if (selection === "start") {
552
- return Selection2.atStart(doc);
553
- }
554
- if (selection === "end") {
555
- return Selection2.atEnd(doc);
556
- }
557
- return Selection2.fromJSON(doc, selection);
558
- }
559
-
560
- // src/extensions/default-state.ts
561
- function defineDefaultState({
562
- defaultSelection,
563
- defaultContent,
564
- defaultDoc,
565
- defaultHTML
566
- }) {
567
- const defaultDocContent = defaultContent || defaultDoc || defaultHTML;
568
- return defineFacetPayload(stateFacet, [
569
- ({ schema }) => {
570
- const config = {};
571
- if (defaultDocContent) {
572
- const json = getEditorContentJSON(schema, defaultDocContent);
573
- config.doc = schema.nodeFromJSON(json);
574
- if (defaultSelection) {
575
- config.selection = Selection3.fromJSON(config.doc, defaultSelection);
576
- }
577
- }
578
- return config;
579
- }
580
- ]);
581
- }
582
-
583
- // src/utils/is-subset.ts
584
- function isSubset(subset, superset) {
585
- return Object.keys(subset).every((key) => subset[key] === superset[key]);
586
- }
587
-
588
- // src/utils/includes-mark.ts
589
- function includesMark(marks, markType, attrs) {
590
- attrs = attrs || {};
591
- return marks.some((mark) => {
592
- return mark.type === markType && isSubset(attrs, mark.attrs);
593
- });
594
- }
595
-
596
- // src/utils/is-mark-absent.ts
597
- function isMarkAbsent(node, from, to, markType, attrs) {
598
- let missing = false;
599
- let available = false;
600
- node.nodesBetween(from, to, (node2, pos, parent) => {
601
- if (missing) {
602
- return false;
603
- }
604
- const allowed = parent?.type.allowsMarkType(markType) && !node2.marks.some((m) => m.type !== markType && m.type.excludes(markType));
605
- if (allowed) {
606
- available = true;
607
- if (!includesMark(node2.marks, markType, attrs)) {
608
- missing = true;
609
- }
610
- }
611
- });
612
- return available ? missing : true;
613
- }
614
-
615
- // src/utils/is-mark-active.ts
616
- function isMarkActive(state, type, attrs) {
617
- const { from, $from, to, empty } = state.selection;
618
- const markType = getMarkType(state.schema, type);
619
- if (empty) {
620
- const marks = state.storedMarks || $from.marks();
621
- return includesMark(marks, markType, attrs);
622
- } else {
623
- return !isMarkAbsent(state.doc, from, to, markType, attrs);
624
- }
625
- }
626
-
627
- // src/facets/union-extension.ts
628
- var UnionExtensionImpl = class extends BaseExtension {
629
- /**
630
- * @internal
631
- */
632
- constructor(extension = []) {
633
- super();
634
- this.extension = extension;
635
- }
636
- /**
637
- * @internal
638
- */
639
- createTree(priority) {
640
- const pri = this.priority ?? priority;
641
- const extensions = [...this.extension];
642
- extensions.sort((a, b) => (a.priority ?? pri) - (b.priority ?? pri));
643
- const children = extensions.map((ext) => ext.getTree(pri));
644
- assert(children.length > 0);
645
- let node = children[0];
646
- for (let i = 1; i < children.length; i++) {
647
- node = unionFacetNode(node, children[i]);
648
- }
649
- return node;
650
- }
651
- };
652
-
653
- // src/editor/union.ts
654
- function union(...exts) {
655
- const extensions = exts.flat();
656
- assert(extensions.length > 0, "At least one extension is required");
657
- return new UnionExtensionImpl(extensions);
658
- }
659
-
660
- // src/editor/editor.ts
661
- import {
662
- EditorState as EditorState2
663
- } from "@prosekit/pm/state";
664
- import {
665
- EditorView
666
- } from "@prosekit/pm/view";
667
-
668
- // src/utils/deep-equals.ts
669
- import OrderedMap from "orderedmap";
670
- function deepEquals(a, b) {
671
- if (a === b) {
672
- return true;
673
- }
674
- if (!a || !b) {
675
- return false;
676
- }
677
- if (Array.isArray(a) && Array.isArray(b)) {
678
- return a.length === b.length && a.every((x, i) => deepEquals(x, b[i]));
679
- }
680
- if (a instanceof OrderedMap && b instanceof OrderedMap) {
681
- return a.size === b.size && deepEquals(a.toObject(), b.toObject());
682
- }
683
- if (typeof a === "object" && typeof b === "object") {
684
- const aKeys = Object.keys(a);
685
- const bKeys = Object.keys(b);
686
- return aKeys.length === bKeys.length && aKeys.every((key) => deepEquals(a[key], b[key]));
687
- }
688
- return false;
689
- }
690
-
691
- // src/editor/action.ts
692
- import mapValues from "just-map-values";
693
-
694
- // src/utils/attrs-match.ts
695
- function attrsMatch(nodeOrMark, attrs) {
696
- const currentAttrs = nodeOrMark.attrs;
697
- for (const [key, value] of Object.entries(attrs)) {
698
- if (currentAttrs[key] !== value) {
699
- return false;
700
- }
701
- }
702
- return true;
703
- }
704
-
705
- // src/utils/is-node-active.ts
706
- function isNodeActive(state, type, attrs) {
707
- const $pos = state.selection.$from;
708
- const nodeType = getNodeType(state.schema, type);
709
- for (let depth = $pos.depth; depth >= 0; depth--) {
710
- const node = $pos.node(depth);
711
- if (node.type === nodeType && (!attrs || attrsMatch(node, attrs))) {
712
- return true;
713
- }
714
- }
715
- return false;
716
- }
717
-
718
- // src/editor/action.ts
719
- function createNodeActions(schema, getState, createNode = defaultCreateNode) {
720
- return mapValues(schema.nodes, (type) => createNodeAction(type, getState, createNode));
721
- }
722
- function createNodeAction(type, getState, createNode) {
723
- const action = (...args) => buildNode(type, args, createNode);
724
- action.isActive = (attrs) => {
725
- const state = getState();
726
- return state ? isNodeActive(state, type, attrs) : false;
727
- };
728
- return action;
729
- }
730
- function createMarkActions(schema, getState, applyMark = defaultApplyMark) {
731
- return mapValues(schema.marks, (type) => createMarkAction(type, getState, applyMark));
732
- }
733
- function createMarkAction(type, getState, applyMark) {
734
- const action = (...args) => buildMark(type, args, applyMark);
735
- action.isActive = (attrs) => {
736
- const state = getState();
737
- return state ? isMarkActive(state, type, attrs) : false;
738
- };
739
- return action;
740
- }
741
- function buildMark(type, args, applyMark) {
742
- const [attrs, children] = normalizeArgs(args);
743
- const mark = type.create(attrs);
744
- return applyMark(mark, flattenChildren(type.schema, children));
745
- }
746
- var defaultApplyMark = (mark, children) => {
747
- return children.map((node) => node.mark(mark.addToSet(node.marks)));
748
- };
749
- function buildNode(type, args, createNode) {
750
- const [attrs, children] = normalizeArgs(args);
751
- return createNode(type, attrs, flattenChildren(type.schema, children));
752
- }
753
- var defaultCreateNode = (type, attrs, children) => {
754
- const node = type.createAndFill(attrs, children);
755
- assert(node, `Failed to create node ${type.name}`);
756
- return node;
757
- };
758
- function flattenChildren(schema, children) {
759
- const nodes = [];
760
- for (const child of children) {
761
- if (typeof child === "string") {
762
- if (child) {
763
- nodes.push(schema.text(child, null));
764
- }
765
- } else if (Array.isArray(child)) {
766
- nodes.push(...flattenChildren(schema, child));
767
- } else if (isProseMirrorNode(child)) {
768
- nodes.push(child);
769
- } else {
770
- throw new ProseKitError(`Invalid node child: ${typeof child}`);
771
- }
772
- }
773
- return nodes;
774
- }
775
- function normalizeArgs(args) {
776
- const [attrs, ...children] = args;
777
- if (isNodeChild(attrs)) {
778
- children.unshift(attrs);
779
- return [null, children];
780
- } else if (typeof attrs === "object") {
781
- return [attrs, children];
782
- } else {
783
- return [null, children];
784
- }
785
- }
786
- function isNodeChild(value) {
787
- if (!value) {
788
- return false;
789
- }
790
- return typeof value === "string" || Array.isArray(value) || isProseMirrorNode(value);
791
- }
792
-
793
- // src/editor/editor.ts
794
- function setupEditorExtension(options) {
795
- if (options.defaultContent || options.defaultDoc || options.defaultHTML) {
796
- return union(
797
- options.extension,
798
- defineDefaultState(options)
799
- );
800
- }
801
- return options.extension;
802
- }
803
- function createEditor(options) {
804
- const extension = setupEditorExtension(options);
805
- const instance = new EditorInstance(extension);
806
- return new Editor(instance);
807
- }
808
- var EditorInstance = class {
809
- constructor(extension) {
810
- this.view = null;
811
- this.commands = {};
812
- this.afterMounted = [];
813
- this.getState = () => {
814
- return this.view?.state || this.directEditorProps.state;
815
- };
816
- this.dispatch = (tr) => {
817
- if (this.view) {
818
- this.view.dispatch(tr);
819
- } else {
820
- this.directEditorProps.state = this.directEditorProps.state.apply(tr);
821
- }
822
- };
823
- /**
824
- * Return a JSON object representing the editor's current document.
825
- */
826
- this.getDocJSON = () => {
827
- const state = this.getState();
828
- return jsonFromNode(state.doc);
829
- };
830
- /**
831
- * Return a HTML string representing the editor's current document.
832
- */
833
- this.getDocHTML = (options) => {
834
- const serializer = this.getProp("clipboardSerializer");
835
- const DOMSerializer2 = serializer ? { fromSchema: () => serializer } : void 0;
836
- const doc = this.getDoc();
837
- return htmlFromNode(doc, { ...options, DOMSerializer: DOMSerializer2 });
838
- };
839
- this.tree = extension.getTree();
840
- const payload = this.tree.getRootOutput();
841
- const schema = payload.schema;
842
- const stateConfig = payload.state;
843
- assert(schema && stateConfig, "Schema must be defined");
844
- const state = EditorState2.create(stateConfig);
845
- if (payload.commands) {
846
- for (const [name, commandCreator] of Object.entries(payload.commands)) {
847
- this.defineCommand(name, commandCreator);
848
- }
849
- }
850
- this.nodes = createNodeActions(state.schema, this.getState);
851
- this.marks = createMarkActions(state.schema, this.getState);
852
- this.schema = state.schema;
853
- this.directEditorProps = { state, ...payload.view };
854
- }
855
- getDoc() {
856
- return this.getState().doc;
857
- }
858
- getProp(propName) {
859
- return this.view?.someProp(propName) ?? this.directEditorProps[propName];
860
- }
861
- updateState(state) {
862
- if (this.view) {
863
- this.view.updateState(state);
864
- } else {
865
- this.directEditorProps.state = state;
866
- }
867
- }
868
- setContent(content, selection) {
869
- const doc = getEditorContentDoc(this.schema, content);
870
- doc.check();
871
- const sel = getEditorSelection(doc, selection || "start");
872
- const oldState = this.getState();
873
- if (doc.eq(oldState.doc) && (!selection || sel.eq(oldState.selection))) {
874
- return;
875
- }
876
- const newState = EditorState2.create({
877
- doc,
878
- selection: sel,
879
- plugins: oldState.plugins
880
- });
881
- this.updateState(newState);
882
- }
883
- updateExtension(extension, add) {
884
- const view = this.view;
885
- if (!view || view.isDestroyed) {
886
- return;
887
- }
888
- const tree = extension.getTree();
889
- const payload = tree.getRootOutput();
890
- if (payload?.schema) {
891
- throw new ProseKitError("Schema cannot be changed");
892
- }
893
- if (payload?.view) {
894
- throw new ProseKitError("View cannot be changed");
895
- }
896
- const oldPayload = this.tree.getRootOutput();
897
- const oldPlugins = [...view.state?.plugins ?? []];
898
- this.tree = add ? unionFacetNode(this.tree, tree) : subtractFacetNode(this.tree, tree);
899
- const newPayload = this.tree.getRootOutput();
900
- const newPlugins = [...newPayload?.state?.plugins ?? []];
901
- if (!deepEquals(oldPlugins, newPlugins)) {
902
- const state = view.state.reconfigure({ plugins: newPlugins });
903
- view.updateState(state);
904
- }
905
- if (newPayload?.commands && !deepEquals(oldPayload?.commands, newPayload?.commands)) {
906
- const commands = newPayload.commands;
907
- const names = Object.keys(commands);
908
- for (const name of names) {
909
- this.defineCommand(name, commands[name]);
910
- }
911
- }
912
- }
913
- use(extension) {
914
- if (!this.mounted) {
915
- let canceled = false;
916
- let lazyRemove = null;
917
- const lazyCreate = () => {
918
- if (!canceled) {
919
- lazyRemove = this.use(extension);
920
- }
921
- };
922
- this.afterMounted.push(lazyCreate);
923
- return () => {
924
- canceled = true;
925
- lazyRemove?.();
926
- };
927
- }
928
- this.updateExtension(extension, true);
929
- return () => this.updateExtension(extension, false);
930
- }
931
- mount(place) {
932
- if (this.view) {
933
- throw new ProseKitError("Editor is already mounted");
934
- }
935
- this.view = new EditorView({ mount: place }, this.directEditorProps);
936
- this.afterMounted.forEach((callback) => callback());
937
- }
938
- unmount() {
939
- if (!this.view) return;
940
- this.directEditorProps.state = this.view.state;
941
- this.view.destroy();
942
- this.view = null;
943
- }
944
- get mounted() {
945
- return !!this.view && !this.view.isDestroyed;
946
- }
947
- get assertView() {
948
- if (!this.view) {
949
- throw new ProseKitError("Editor is not mounted");
950
- }
951
- return this.view;
952
- }
953
- definePlugins(plugins) {
954
- const view = this.assertView;
955
- const state = view.state;
956
- const newPlugins = [...plugins, ...state.plugins];
957
- const newState = state.reconfigure({ plugins: newPlugins });
958
- view.setProps({ state: newState });
959
- }
960
- removePlugins(plugins) {
961
- const view = this.view;
962
- if (!view) return;
963
- const state = view.state;
964
- const newPlugins = state.plugins.filter((p) => !plugins.includes(p));
965
- const newState = state.reconfigure({ plugins: newPlugins });
966
- view.setProps({ state: newState });
967
- }
968
- exec(command) {
969
- const state = this.getState();
970
- return command(state, this.dispatch, this.view ?? void 0);
971
- }
972
- canExec(command) {
973
- const state = this.getState();
974
- return command(state, void 0, this.view ?? void 0);
975
- }
976
- defineCommand(name, commandCreator) {
977
- const action = (...args) => {
978
- const command = commandCreator(...args);
979
- return this.exec(command);
980
- };
981
- const canExec = (...args) => {
982
- const command = commandCreator(...args);
983
- return this.canExec(command);
984
- };
985
- action.canApply = canExec;
986
- action.canExec = canExec;
987
- this.commands[name] = action;
988
- }
989
- removeCommand(name) {
990
- delete this.commands[name];
991
- }
992
- };
993
- var Editor = class {
994
- /**
995
- * @internal
996
- */
997
- constructor(instance) {
998
- /**
999
- * Mount the editor to the given HTML element.
1000
- * Pass `null` or `undefined` to unmount the editor.
1001
- */
1002
- this.mount = (place) => {
1003
- if (place) {
1004
- this.instance.mount(place);
1005
- } else {
1006
- this.instance.unmount();
1007
- }
1008
- };
1009
- /**
1010
- * Unmount the editor. This is equivalent to `mount(null)`.
1011
- */
1012
- this.unmount = () => {
1013
- this.instance.unmount();
1014
- };
1015
- /**
1016
- * Focus the editor.
1017
- */
1018
- this.focus = () => {
1019
- this.instance.view?.focus();
1020
- };
1021
- /**
1022
- * Blur the editor.
1023
- */
1024
- this.blur = () => {
1025
- this.instance.view?.dom.blur();
1026
- };
1027
- /**
1028
- * Register an extension to the editor. Return a function to unregister the
1029
- * extension.
1030
- */
1031
- this.use = (extension) => {
1032
- return this.instance.use(extension);
1033
- };
1034
- /**
1035
- * Update the editor's state.
1036
- *
1037
- * @remarks
1038
- *
1039
- * This is an advanced method. Use it only if you have a specific reason to
1040
- * directly manipulate the editor's state.
1041
- */
1042
- this.updateState = (state) => {
1043
- this.instance.updateState(state);
1044
- };
1045
- /**
1046
- * Update the editor's document and selection.
1047
- *
1048
- * @param content - The new document to set. It can be one of the following:
1049
- * - A ProseMirror node instance
1050
- * - A ProseMirror node JSON object
1051
- * - An HTML string
1052
- * - An HTML element instance
1053
- * @param selection - Optional. Specifies the new selection. It can be one of the following:
1054
- * - A ProseMirror selection instance
1055
- * - A ProseMirror selection JSON object
1056
- * - The string "start" (to set selection at the beginning, default value)
1057
- * - The string "end" (to set selection at the end)
1058
- */
1059
- this.setContent = (content, selection) => {
1060
- return this.instance.setContent(content, selection);
1061
- };
1062
- /**
1063
- * Return a JSON object representing the editor's current document.
1064
- */
1065
- this.getDocJSON = () => {
1066
- return this.instance.getDocJSON();
1067
- };
1068
- /**
1069
- * Return a HTML string representing the editor's current document.
1070
- */
1071
- this.getDocHTML = (options) => {
1072
- return this.instance.getDocHTML(options);
1073
- };
1074
- /**
1075
- * Execute the given command. Return `true` if the command was successfully
1076
- * executed, otherwise `false`.
1077
- */
1078
- this.exec = (command) => {
1079
- return this.instance.exec(command);
1080
- };
1081
- /**
1082
- * Check if the given command can be executed. Return `true` if the command
1083
- * can be executed, otherwise `false`.
1084
- */
1085
- this.canExec = (command) => {
1086
- return this.instance.canExec(command);
1087
- };
1088
- if (!(instance instanceof EditorInstance)) {
1089
- throw new TypeError("Invalid EditorInstance");
1090
- }
1091
- this.instance = instance;
1092
- }
1093
- /**
1094
- * Whether the editor is mounted.
1095
- */
1096
- get mounted() {
1097
- return this.instance.mounted;
1098
- }
1099
- /**
1100
- * The editor view.
1101
- */
1102
- get view() {
1103
- return this.instance.assertView;
1104
- }
1105
- /**
1106
- * The editor schema.
1107
- */
1108
- get schema() {
1109
- return this.instance.schema;
1110
- }
1111
- /**
1112
- * The editor's current state.
1113
- */
1114
- get state() {
1115
- return this.instance.getState();
1116
- }
1117
- /**
1118
- * Whether the editor is focused.
1119
- */
1120
- get focused() {
1121
- return this.instance.view?.hasFocus() ?? false;
1122
- }
1123
- /**
1124
- * All {@link CommandAction}s defined by the editor.
1125
- */
1126
- get commands() {
1127
- return this.instance.commands;
1128
- }
1129
- /**
1130
- * All {@link NodeAction}s defined by the editor.
1131
- */
1132
- get nodes() {
1133
- return this.instance.nodes;
1134
- }
1135
- /**
1136
- * All {@link MarkAction}s defined by the editor.
1137
- */
1138
- get marks() {
1139
- return this.instance.marks;
1140
- }
1141
- };
1142
-
1143
- export {
1144
- ProseKitError,
1145
- EditorNotFoundError,
1146
- getMarkType,
1147
- assert,
1148
- getNodeType,
1149
- isNodeActive,
1150
- Priority,
1151
- defineFacet,
1152
- rootFacet,
1153
- schemaFacet,
1154
- toReversed,
1155
- isProseMirrorNode,
1156
- isMark,
1157
- isFragment,
1158
- isSlice,
1159
- isSelection,
1160
- isTextSelection,
1161
- isNodeSelection,
1162
- isAllSelection,
1163
- isNotNullish,
1164
- defineFacetPayload,
1165
- stateFacet,
1166
- isElement,
1167
- jsonFromState,
1168
- stateFromJSON,
1169
- jsonFromNode,
1170
- nodeFromJSON,
1171
- nodeFromElement,
1172
- elementFromNode,
1173
- nodeFromHTML,
1174
- htmlFromNode,
1175
- elementFromJSON,
1176
- jsonFromHTML,
1177
- htmlFromJSON,
1178
- defineDefaultState,
1179
- isMarkAbsent,
1180
- isMarkActive,
1181
- createNodeActions,
1182
- createMarkActions,
1183
- union,
1184
- setupEditorExtension,
1185
- createEditor,
1186
- EditorInstance,
1187
- Editor
1188
- };