@prosekit/core 0.0.5 → 0.0.6

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.
@@ -130,23 +130,32 @@ declare class Editor<E extends Extension = any> {
130
130
  isMarkActive(markType: string | MarkType, attrs?: Attrs): boolean;
131
131
  }
132
132
 
133
+ interface Slot<Input, Output> {
134
+ create: (inputs: Input[]) => Output;
135
+ update: (inputs: Input[]) => Output | null;
136
+ }
137
+
133
138
  /** @public */
134
139
  interface FacetOptions<Input, Output> {
135
- combine: (inputs: Input[]) => Output;
140
+ combine?: (inputs: Input[]) => Output;
141
+ slot?: () => Slot<Input, Output>;
136
142
  next: Facet<Output, any>;
143
+ single?: boolean;
137
144
  }
138
145
  /** @public */
139
146
  declare class Facet<Input, Output> {
140
147
  /** @internal */
141
148
  readonly index: number;
142
149
  /** @internal */
143
- readonly combine: (inputs: Input[]) => Output;
150
+ readonly slot: () => Slot<Input, Output>;
144
151
  /** @internal */
145
152
  readonly next: Facet<Output, any> | null;
153
+ /** @internal */
154
+ readonly single: boolean;
146
155
  private constructor();
147
- static define<Input, Output>({ combine, next }: FacetOptions<Input, Output>): Facet<Input, Output>;
156
+ static define<Input, Output>({ slot, combine, next, single, }: FacetOptions<Input, Output>): Facet<Input, Output>;
148
157
  /** @internal */
149
- static defineSlot<Input>({ combine, }: Omit<FacetOptions<Input, Input>, 'next'>): Facet<Input, Input>;
158
+ static defineSlot<Input>(options: Omit<FacetOptions<Input, Input>, 'next'>): Facet<Input, Input>;
150
159
  extension(inputs: Input[]): FacetExtension<Input, Output>;
151
160
  }
152
161
  /** @public */
@@ -294,4 +303,4 @@ declare function getMarkType(schema: Schema, type: string | MarkType): MarkType;
294
303
  /** @internal */
295
304
  declare function getNodeType(schema: Schema, type: string | NodeType): NodeType;
296
305
 
297
- export { CommandArgs, Editor, EditorOptions, Extension, ExtensionTyping, ExtractCommandCreators, ExtractCommandDispatchers, ExtractMarks, ExtractNodes, Facet, FacetExtension, FacetOptions, Keymap, MarkSpecOptions, NodeSpecOptions, NodeViewOptions, PluginFacetInput, PluginOptions, Priority, ProseKitError, SimplifyUnion, StateConfigCallback, StateConfigContext, ToggleMarkOptions, ToggleNodeOptions, ViewProps, addBaseCommands, addBaseKeymap, addCommands, addDoc, addInputRule, addKeymap, addMarkSpec, addNodeSpec, addNodeView, addParagraph, addPlugin, addText, createEditor, defineExtension, getMarkType, getNodeType, pluginFacet, toggleMark, toggleNode, withPriority };
306
+ export { CommandArgs, Editor, EditorOptions, Extension, ExtensionTyping, ExtractCommandCreators, ExtractCommandDispatchers, ExtractMarks, ExtractNodes, Facet, FacetExtension, FacetOptions, Keymap, MarkSpecOptions, NodeSpecOptions, NodeViewOptions, PluginFacetInput, PluginOptions, Priority, ProseKitError, SimplifyExtension, SimplifyUnion, StateConfigCallback, StateConfigContext, ToggleMarkOptions, ToggleNodeOptions, ViewProps, addBaseCommands, addBaseKeymap, addCommands, addDoc, addInputRule, addKeymap, addMarkSpec, addNodeSpec, addNodeView, addParagraph, addPlugin, addText, createEditor, defineExtension, getMarkType, getNodeType, pluginFacet, toggleMark, toggleNode, withPriority };
@@ -114,10 +114,6 @@ import { Schema as Schema3 } from "@prosekit/pm/model";
114
114
  import { EditorState } from "@prosekit/pm/state";
115
115
  import { EditorView } from "@prosekit/pm/view";
116
116
 
117
- // src/types/void-function.ts
118
- function voidFunction() {
119
- }
120
-
121
117
  // src/utils/is-mark-active.ts
122
118
  function isMarkActive(state, type, attrs) {
123
119
  const markType = getMarkType(state.schema, type);
@@ -136,23 +132,57 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
136
132
  return Priority2;
137
133
  })(Priority || {});
138
134
 
135
+ // src/utils/uniq-array.ts
136
+ function uniqPush(prev, next) {
137
+ const result = [...prev];
138
+ for (const item of next) {
139
+ if (!result.includes(item)) {
140
+ result.push(item);
141
+ }
142
+ }
143
+ return result;
144
+ }
145
+ function uniqRemove(prev, next) {
146
+ const result = [...prev];
147
+ for (const item of next) {
148
+ const index = result.indexOf(item);
149
+ if (index !== -1) {
150
+ result.splice(index, 1);
151
+ }
152
+ }
153
+ return result;
154
+ }
155
+
139
156
  // src/editor/facet.ts
140
157
  var nextIndex = 0;
141
158
  var Facet = class _Facet {
142
- constructor(combine, next) {
159
+ constructor(slot, next, single) {
143
160
  /** @internal */
144
161
  this.index = nextIndex++;
145
- this.combine = combine;
162
+ this.slot = slot;
146
163
  this.next = next;
164
+ this.single = single;
147
165
  }
148
- static define({ combine, next }) {
149
- return new _Facet(combine, next);
166
+ static define({
167
+ slot,
168
+ combine,
169
+ next,
170
+ single
171
+ }) {
172
+ const slotFn = slot ? slot : combine ? () => ({
173
+ create: combine,
174
+ update: combine
175
+ }) : null;
176
+ if (!slotFn) {
177
+ throw new ProseKitError(
178
+ "Facet must have either 'slot' or 'combine' option"
179
+ );
180
+ }
181
+ return new _Facet(slotFn, next, single != null ? single : false);
150
182
  }
151
183
  /** @internal */
152
- static defineSlot({
153
- combine
154
- }) {
155
- return new _Facet(combine, null);
184
+ static defineSlot(options) {
185
+ return _Facet.define(options);
156
186
  }
157
187
  extension(inputs) {
158
188
  return new FacetExtension(this, inputs);
@@ -215,7 +245,7 @@ function sortFacets(unsorted) {
215
245
  return sortedFacets;
216
246
  }
217
247
 
218
- // src/editor/slot.ts
248
+ // src/editor/slots.ts
219
249
  import OrderedMap from "orderedmap";
220
250
  var schemaSlot = Facet.defineSlot({
221
251
  combine: (specs) => {
@@ -234,7 +264,7 @@ var schemaSlot = Facet.defineSlot({
234
264
  var stateSlot = Facet.defineSlot({
235
265
  combine: (callbacks) => {
236
266
  return (ctx) => {
237
- var _a, _b, _c, _d, _e;
267
+ var _a, _b, _c, _d, _e, _f;
238
268
  const configs = callbacks.map((cb) => cb(ctx));
239
269
  const config = {
240
270
  schema: ctx.schema,
@@ -246,7 +276,7 @@ var stateSlot = Facet.defineSlot({
246
276
  config.doc = (_b = config.doc) != null ? _b : c.doc;
247
277
  config.selection = (_c = config.selection) != null ? _c : c.selection;
248
278
  config.storedMarks = [...config.storedMarks, ...(_d = c.storedMarks) != null ? _d : []];
249
- config.plugins = [...config.plugins, ...(_e = c.plugins) != null ? _e : []];
279
+ config.plugins = uniqPush((_e = config.plugins) != null ? _e : [], (_f = c.plugins) != null ? _f : []);
250
280
  }
251
281
  if (!config.doc && !config.schema) {
252
282
  throw new Error("Can't create state without a schema nor a document");
@@ -270,7 +300,46 @@ var commandSlot = Facet.defineSlot({
270
300
  });
271
301
 
272
302
  // src/editor/flatten.ts
273
- function flatten(root) {
303
+ function flattenInputTuple(inputTuple) {
304
+ return [
305
+ ...inputTuple[0],
306
+ ...inputTuple[1],
307
+ ...inputTuple[2],
308
+ ...inputTuple[3],
309
+ ...inputTuple[4]
310
+ ];
311
+ }
312
+ function mergeInputTuple(tupleA, tupleB) {
313
+ if (!tupleA)
314
+ return tupleB;
315
+ if (!tupleB)
316
+ return tupleA;
317
+ const [a0, a1, a2, a3, a4] = tupleA;
318
+ const [b0, b1, b2, b3, b4] = tupleB;
319
+ return [
320
+ uniqPush(a0, b0),
321
+ uniqPush(a1, b1),
322
+ uniqPush(a2, b2),
323
+ uniqPush(a3, b3),
324
+ uniqPush(a4, b4)
325
+ ];
326
+ }
327
+ function removeInputTuple(tupleA, tupleB) {
328
+ if (!tupleA)
329
+ return [[], [], [], [], []];
330
+ if (!tupleB)
331
+ return tupleA;
332
+ const [a0, a1, a2, a3, a4] = tupleA;
333
+ const [b0, b1, b2, b3, b4] = tupleB;
334
+ return [
335
+ uniqRemove(a0, b0),
336
+ uniqRemove(a1, b1),
337
+ uniqRemove(a2, b2),
338
+ uniqRemove(a3, b3),
339
+ uniqRemove(a4, b4)
340
+ ];
341
+ }
342
+ function extractFacets(root) {
274
343
  var _a;
275
344
  const extensions = [root];
276
345
  const priorities = [2 /* default */];
@@ -302,43 +371,97 @@ function flatten(root) {
302
371
  throw new Error("Invalid extension");
303
372
  }
304
373
  }
374
+ return [facets, inputs];
375
+ }
376
+ function updateExtension(prevInputs, prevSlots, extension, mode) {
377
+ const modifyInputTuple = mode === "add" ? mergeInputTuple : removeInputTuple;
378
+ const [facets, inputs] = extractFacets(extension);
305
379
  let schemaInput = null;
306
380
  let stateInput = null;
307
381
  let viewInput = null;
308
382
  let commandInput = null;
309
- const sortedFacets = sortFacets(facets);
310
- for (const facet of sortedFacets) {
311
- const nextFacet = facet.next;
312
- if (nextFacet) {
383
+ for (const facet of sortFacets(facets)) {
384
+ if (!inputs[facet.index]) {
385
+ continue;
386
+ }
387
+ const inputTuple = modifyInputTuple(
388
+ prevInputs[facet.index],
389
+ inputs[facet.index]
390
+ );
391
+ prevInputs[facet.index] = inputTuple;
392
+ if (facet.next && !facet.single) {
393
+ let hasOutput = false;
394
+ const outputTuple = [[], [], [], [], []];
313
395
  for (let pri = 0; pri < 5; pri++) {
314
- const input = inputs[facet.index][pri];
315
- if (input.length > 0) {
316
- const output = facet.combine(input);
317
- if (!inputs[nextFacet.index]) {
318
- inputs[nextFacet.index] = [[], [], [], [], []];
319
- }
320
- inputs[nextFacet.index][pri].push(output);
396
+ const inputArray = inputTuple[pri];
397
+ if (inputArray.length === 0) {
398
+ continue;
321
399
  }
400
+ const slotTuple = prevSlots[facet.index] || (prevSlots[facet.index] = [
401
+ void 0,
402
+ void 0,
403
+ void 0,
404
+ void 0,
405
+ void 0
406
+ ]);
407
+ const prevSlot = slotTuple[pri];
408
+ const slot = prevSlot || facet.slot();
409
+ prevSlots[facet.index][pri] = slot;
410
+ const output = prevSlot ? slot.update(inputArray) : slot.create(inputArray);
411
+ if (!output) {
412
+ continue;
413
+ }
414
+ hasOutput = true;
415
+ outputTuple[pri].push(output);
322
416
  }
323
- } else if (inputs[facet.index]) {
324
- const [i1, i2, i3, i4, i5] = inputs[facet.index];
325
- const jointInputs = [...i1, ...i2, ...i3, ...i4, ...i5];
326
- const output = facet.combine(jointInputs);
327
- switch (facet) {
328
- case schemaSlot:
329
- schemaInput = output;
330
- break;
331
- case stateSlot:
332
- stateInput = output;
333
- break;
334
- case viewSlot:
335
- viewInput = output;
336
- break;
337
- case commandSlot:
338
- commandInput = output;
339
- break;
340
- default:
341
- throw new Error("Invalid facet");
417
+ if (!hasOutput) {
418
+ continue;
419
+ }
420
+ inputs[facet.next.index] = modifyInputTuple(
421
+ inputs[facet.next.index],
422
+ outputTuple
423
+ );
424
+ continue;
425
+ } else {
426
+ const inputArray = flattenInputTuple(inputTuple);
427
+ const slotTuple = prevSlots[facet.index] || (prevSlots[facet.index] = [
428
+ void 0,
429
+ void 0,
430
+ void 0,
431
+ void 0,
432
+ void 0
433
+ ]);
434
+ const prevSlot = slotTuple[2 /* default */];
435
+ const slot = prevSlot || facet.slot();
436
+ prevSlots[facet.index][2 /* default */] = slot;
437
+ const output = prevSlot ? slot.update(inputArray) : slot.create(inputArray);
438
+ if (!output) {
439
+ continue;
440
+ }
441
+ const outputTuple = [[], [], [output], [], []];
442
+ if (facet.next) {
443
+ inputs[facet.next.index] = modifyInputTuple(
444
+ inputs[facet.next.index],
445
+ outputTuple
446
+ );
447
+ continue;
448
+ } else {
449
+ switch (facet) {
450
+ case schemaSlot:
451
+ schemaInput = output;
452
+ break;
453
+ case stateSlot:
454
+ stateInput = output;
455
+ break;
456
+ case viewSlot:
457
+ viewInput = output;
458
+ break;
459
+ case commandSlot:
460
+ commandInput = output;
461
+ break;
462
+ default:
463
+ throw new Error("Invalid facet");
464
+ }
342
465
  }
343
466
  }
344
467
  }
@@ -349,30 +472,58 @@ function flatten(root) {
349
472
  function createEditor({
350
473
  extension
351
474
  }) {
352
- const { schemaInput, stateInput, viewInput, commandInput } = flatten(extension);
353
- if (!schemaInput) {
354
- throw new Error("Schema must be defined");
355
- }
356
- const schema = new Schema3(schemaInput);
357
- const stateConfig = stateInput ? stateInput({ schema }) : { schema };
358
- const state = EditorState.create(stateConfig);
359
- const directEditorProps = { state, ...viewInput };
360
- const instance = new EditorInstance(directEditorProps);
361
- if (commandInput) {
362
- for (const [name, commandCreator] of Object.entries(commandInput)) {
363
- instance.addCommand(name, commandCreator);
364
- }
365
- }
366
- return Editor.create(instance);
475
+ const instance = new EditorInstance(extension);
476
+ const editor = Editor.create(instance);
477
+ return editor;
367
478
  }
368
479
  var EditorInstance = class {
369
- constructor(directEditorProps) {
370
- this.directEditorProps = directEditorProps;
480
+ constructor(extension) {
371
481
  this.view = null;
372
482
  this.commandDispatchers = {};
483
+ this.inputs = [];
484
+ this.slots = [];
373
485
  this.mount = this.mount.bind(this);
374
486
  this.unmount = this.unmount.bind(this);
375
- this.schema = directEditorProps.state.schema;
487
+ const { schemaInput, stateInput, viewInput, commandInput } = updateExtension(this.inputs, this.slots, extension, "add");
488
+ if (!schemaInput) {
489
+ throw new Error("Schema must be defined");
490
+ }
491
+ const schema = new Schema3(schemaInput);
492
+ const stateConfig = stateInput ? stateInput({ schema }) : { schema };
493
+ const state = EditorState.create(stateConfig);
494
+ if (commandInput) {
495
+ for (const [name, commandCreator] of Object.entries(commandInput)) {
496
+ this.addCommand(name, commandCreator);
497
+ }
498
+ }
499
+ this.directEditorProps = { state, ...viewInput };
500
+ this.schema = this.directEditorProps.state.schema;
501
+ }
502
+ updateExtension(extension, mode) {
503
+ var _a;
504
+ const { schemaInput, stateInput, viewInput, commandInput } = updateExtension(this.inputs, this.slots, extension, mode);
505
+ if (schemaInput) {
506
+ throw new ProseKitError("Schema cannot be changed");
507
+ }
508
+ if (viewInput) {
509
+ throw new ProseKitError("View cannot be changed");
510
+ }
511
+ const plugins = (_a = stateInput == null ? void 0 : stateInput({ schema: this.schema })) == null ? void 0 : _a.plugins;
512
+ if (plugins && plugins.length > 0) {
513
+ if (!this.view) {
514
+ throw new ProseKitError(
515
+ "Unexpected inner state: EditorInstance.view is not defined"
516
+ );
517
+ }
518
+ const state = this.view.state.reconfigure({ plugins });
519
+ this.view.updateState(state);
520
+ }
521
+ if (commandInput) {
522
+ const names = Object.keys(commandInput);
523
+ for (const name of names) {
524
+ this.addCommand(name, commandInput[name]);
525
+ }
526
+ }
376
527
  }
377
528
  mount(place) {
378
529
  if (this.view) {
@@ -434,7 +585,7 @@ var Editor = class _Editor {
434
585
  /** @internal */
435
586
  static create(instance) {
436
587
  if (!(instance instanceof EditorInstance)) {
437
- throw new TypeError("Editor's instance is not EditorInstance");
588
+ throw new TypeError("Invalid EditorInstance");
438
589
  }
439
590
  return new _Editor(instance);
440
591
  }
@@ -473,33 +624,8 @@ var Editor = class _Editor {
473
624
  lazyRemove == null ? void 0 : lazyRemove();
474
625
  };
475
626
  }
476
- const { schemaInput, stateInput, viewInput, commandInput } = flatten(extension);
477
- if (schemaInput) {
478
- throw new ProseKitError("Schema cannot be changed");
479
- }
480
- if (viewInput) {
481
- throw new ProseKitError("View cannot be changed");
482
- }
483
- if (stateInput) {
484
- const stateConfig = stateInput({ schema: this.schema });
485
- const plugins = stateConfig.plugins;
486
- if (plugins && plugins.length > 0) {
487
- this.instance.addPlugins(plugins);
488
- return () => this.instance.removePlugins(plugins);
489
- }
490
- }
491
- if (commandInput) {
492
- const names = Object.keys(commandInput);
493
- for (const name of names) {
494
- this.instance.addCommand(name, commandInput[name]);
495
- }
496
- return () => {
497
- for (const name of names) {
498
- this.instance.removeCommand(name);
499
- }
500
- };
501
- }
502
- return voidFunction;
627
+ this.instance.updateExtension(extension, "add");
628
+ return () => this.instance.updateExtension(extension, "remove");
503
629
  }
504
630
  isNodeActive(nodeType, attrs) {
505
631
  return isNodeActive(this.view.state, nodeType, attrs);
@@ -524,7 +650,7 @@ function withPriority(extension, priority) {
524
650
 
525
651
  // src/extensions/command.ts
526
652
  import "@prosekit/pm/model";
527
- import { AllSelection, Selection } from "@prosekit/pm/state";
653
+ import { AllSelection } from "@prosekit/pm/state";
528
654
  import { findWrapping, insertPoint } from "@prosekit/pm/transform";
529
655
  function addCommands(commands) {
530
656
  return commandSlot.extension([commands]);
@@ -554,8 +680,6 @@ function addBaseCommands() {
554
680
  return false;
555
681
  if (dispatch) {
556
682
  const tr = state.tr.insert(insertPos, node);
557
- const $pos = tr.doc.resolve(insertPos);
558
- tr.setSelection(Selection.near($pos));
559
683
  dispatch(tr);
560
684
  }
561
685
  return true;
@@ -674,8 +798,8 @@ var inputRuleFacet = Facet.define({
674
798
 
675
799
  // src/extensions/keymap.ts
676
800
  import { baseKeymap, chainCommands } from "@prosekit/pm/commands";
677
- import { keymap as createKeymapPlugin } from "@prosekit/pm/keymap";
678
- import "@prosekit/pm/state";
801
+ import { keydownHandler } from "@prosekit/pm/keymap";
802
+ import { Plugin as Plugin3, PluginKey } from "@prosekit/pm/state";
679
803
  function addKeymap(keymap) {
680
804
  return keymapFacet.extension([keymap]);
681
805
  }
@@ -683,20 +807,38 @@ function addBaseKeymap() {
683
807
  return addKeymap(baseKeymap);
684
808
  }
685
809
  var keymapFacet = Facet.define({
686
- combine: (keymaps) => {
687
- const keymap = mergeKeymaps(keymaps);
688
- const plugin = createKeymapPlugin(keymap);
689
- return () => [plugin];
810
+ slot: () => {
811
+ let handler = null;
812
+ const handlerWrapper = (view, event) => {
813
+ if (handler)
814
+ return handler(view, event);
815
+ return false;
816
+ };
817
+ const plugin = new Plugin3({
818
+ key: keymapPluginKey,
819
+ props: { handleKeyDown: handlerWrapper }
820
+ });
821
+ const pluginFunc = () => [plugin];
822
+ return {
823
+ create: (keymaps) => {
824
+ handler = keydownHandler(mergeKeymaps(keymaps));
825
+ return pluginFunc;
826
+ },
827
+ update: (keymaps) => {
828
+ handler = keydownHandler(mergeKeymaps(keymaps));
829
+ return null;
830
+ }
831
+ };
690
832
  },
691
- next: pluginFacet
833
+ next: pluginFacet,
834
+ single: true
692
835
  });
693
836
  function mergeKeymaps(keymaps) {
694
837
  const bindings = {};
695
838
  for (const keymap of keymaps) {
696
839
  for (const [key, command] of Object.entries(keymap)) {
697
- if (!bindings[key])
698
- bindings[key] = [];
699
- bindings[key].push(command);
840
+ const commands = bindings[key] || (bindings[key] = []);
841
+ commands.push(command);
700
842
  }
701
843
  }
702
844
  return Object.fromEntries(
@@ -706,6 +848,7 @@ function mergeKeymaps(keymaps) {
706
848
  ])
707
849
  );
708
850
  }
851
+ var keymapPluginKey = new PluginKey("prosekit-keymap");
709
852
 
710
853
  // src/extensions/mark-spec.ts
711
854
  function addMarkSpec(options) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prosekit/core",
3
3
  "type": "module",
4
- "version": "0.0.5",
4
+ "version": "0.0.6",
5
5
  "private": false,
6
6
  "author": {
7
7
  "name": "ocavue",
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ export {
28
28
  type ExtractCommandDispatchers,
29
29
  type ExtractMarks,
30
30
  type ExtractNodes,
31
+ type SimplifyExtension,
31
32
  } from './types/extension'
32
33
  export { type ExtensionTyping } from './types/extension-typing'
33
34
  export { Priority } from './types/priority'