markupeditor 0.9.31 → 0.9.33

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.
@@ -235,9 +235,14 @@ class Registry {
235
235
 
236
236
  /**
237
237
  * Add `plugin` to the registry, keyed by `name`.
238
+ * A `plugin` object has `name` and `type` string properties at minimum.
239
+ * The invoker can add additional properties to the `plugin`.
240
+ * For example, a `run` function could be part of the `plugin`, which could,
241
+ * by convention of the application environment the markupeditor is used in,
242
+ * be used to run the `plugin` to perform some action or return something.
238
243
  *
239
- * @param {object} plugin Plugin object with string values for { name, type, filename }.
240
- * @param {string} name The key used to retrieve and invoke the plugin.
244
+ * @param {object} plugin Plugin object with string properties for {name, type} at minimum.
245
+ * @param {string} name The key used to retrieve and invoke the plugin. Taken from `plugin.name` by default.
241
246
  */
242
247
  registerPlugin(plugin, name) {
243
248
  this._plugins.set(name ?? plugin.name, plugin);
@@ -256,17 +261,17 @@ class Registry {
256
261
  * Return the plugin object with `name`, or undefined if not found.
257
262
  *
258
263
  * @param {string} name The key used to identify the plugin.
259
- * @returns {object|undefined}
264
+ * @returns {object | undefined}
260
265
  */
261
266
  getPlugin(name) {
262
267
  return this._plugins.get(name)
263
268
  }
264
269
 
265
270
  /**
266
- * Return an array of registered plugins that match type, all if not specified.
267
- * Each entry contains {name, type, filename}
271
+ * Return an array of registered plugins that match `type`, all if not specified.
268
272
  *
269
- * @returns {Array<{name: string, type: string, filename: string}>}
273
+ * @param {string} type The plugin type to filter by; all plugins are returned if omitted.
274
+ * @returns {Array<object>} Array of plugin objects with string properties for {name, type} at minimum.
270
275
  */
271
276
  getPlugins(type) {
272
277
  if (type) {
@@ -512,10 +517,15 @@ const setSelectedID = _registry.setSelectedID.bind(_registry);
512
517
 
513
518
  /**
514
519
  * Add `plugin` to the registry, keyed by `name`.
520
+ * A `plugin` object has `name` and `type` string properties at minimum.
521
+ * The invoker can add additional properties to the `plugin`.
522
+ * For example, a `run` function could be part of the `plugin`, which could,
523
+ * by convention of the application environment the markupeditor is used in,
524
+ * be used to run the `plugin` to perform some action or return something.
515
525
  *
516
526
  * @function
517
- * @param {object} plugin Plugin object with string values for { name, type, filename }.
518
- * @param {string} name The key used to retrieve and invoke the plugin.
527
+ * @param {object} plugin Plugin object with string properties for {name, type} at minimum.
528
+ * @param {string} name The key used to retrieve and invoke the plugin. Taken from `plugin.name` by default.
519
529
  */
520
530
  const registerPlugin = _registry.registerPlugin.bind(_registry);
521
531
 
@@ -538,14 +548,36 @@ const getPlugin = _registry.getPlugin.bind(_registry);
538
548
 
539
549
  /**
540
550
  * Return an array of registered plugins that match `type`, all if not specified.
541
- * Each entry contains {name, type, filename}
542
551
  *
543
552
  * @function
544
- * @param {string} [type] The plugin type to filter by; all plugins are returned if omitted.
545
- * @returns {Array<{name: string, type: string, filename: string}>}
553
+ * @param {string} type The plugin type to filter by; all plugins are returned if omitted.
554
+ * @returns {Array<object>} Array of plugin objects with string properties for {name, type} at minimum.
546
555
  */
547
556
  const getPlugins = _registry.getPlugins.bind(_registry);
548
557
 
558
+ /**
559
+ * Look up the plugin with `name` and invoke its `run` function, returning/awaiting whatever
560
+ * it resolves to.
561
+ *
562
+ * Content-agnostic and plugin-type-agnostic: `runPlugin` never fetches or supplies content
563
+ * on the plugin's behalf. The plugin's `run` function is responsible for getting
564
+ * whatever content it needs, via whichever accessor (e.g. `getHTML`) fits its purpose.
565
+ *
566
+ * Returns `null`, never rejects, when there's nothing to run: no plugin registered with
567
+ * `name`, or a plugin registered without a `run` function. If `run` itself rejects, that
568
+ * rejection propagates unchanged — it is not swallowed into `null`, so a caller can't
569
+ * confuse "nothing to run" with "the plugin failed."
570
+ *
571
+ * @function
572
+ * @param {string} name The key used to identify the plugin.
573
+ * @returns {Promise<*>} Whatever `plugin.run()` resolves to, or `null`.
574
+ */
575
+ async function runPlugin(name) {
576
+ const plugin = getPlugin(name);
577
+ if (!plugin?.run) return null
578
+ return await plugin.run()
579
+ }
580
+
549
581
  /**
550
582
  * MUError captures internal errors and makes it easy to communicate them externally.
551
583
  *
@@ -32935,6 +32967,40 @@ function handleDelete() {
32935
32967
  return false
32936
32968
  }
32937
32969
 
32970
+ /**
32971
+ * Delete a `\n` immediately before (dir -1) or after (dir +1) the cursor inside a code_block's
32972
+ * text, via a direct transaction rather than relying on native contenteditable editing.
32973
+ *
32974
+ * Safari/WebKit never issues a native deleteContentBackward/deleteContentForward `beforeinput`
32975
+ * at all when the adjacent `\n` is the last character in the block (cursor at the start of an
32976
+ * empty trailing line) under `white-space: pre-wrap` — confirmed by instrumenting keydown/
32977
+ * beforeinput/input: keydown fires and is not prevented, but no beforeinput or input event
32978
+ * follows, and neither the ProseMirror doc nor the DOM change. The unhandled key produces the
32979
+ * system beep. This only reproduces with the real `white-space: pre-wrap` styling the editor
32980
+ * ships with; it does not reproduce against an unstyled contenteditable. Newline deletion inside
32981
+ * a code_block is therefore always handled explicitly here so it never depends on native
32982
+ * contenteditable editing for this specific character.
32983
+ *
32984
+ * @ignore
32985
+ */
32986
+ function deleteAdjacentNewline(dir) {
32987
+ return (state, dispatch) => {
32988
+ const { $from, empty } = state.selection;
32989
+ if (!empty || $from.parent.type !== state.schema.nodes.code_block) return false
32990
+ const text = $from.parent.textContent;
32991
+ const charOffset = dir < 0 ? $from.parentOffset - 1 : $from.parentOffset;
32992
+ if (charOffset < 0 || charOffset >= text.length || text[charOffset] !== '\n') return false
32993
+ if (dispatch) {
32994
+ const from = $from.pos + (dir < 0 ? -1 : 0);
32995
+ dispatch(state.tr.delete(from, from + 1));
32996
+ }
32997
+ return true
32998
+ }
32999
+ }
33000
+
33001
+ const deleteNewlineBackward = deleteAdjacentNewline(-1);
33002
+ const deleteNewlineForward = deleteAdjacentNewline(1);
33003
+
32938
33004
  /**
32939
33005
  * Called to set attributes to the editor div, typically to ,
32940
33006
  * set spellcheck and autocorrect. Note that contenteditable
@@ -40591,7 +40657,7 @@ function buildKeymap(config, schema) {
40591
40657
  // The MarkupEditor handles Shift-Enter as searchBackward when search is active.
40592
40658
  bind("Shift-Enter", handleShiftEnter);
40593
40659
  // The MarkupEditor needs to be notified of state changes on Delete, like Backspace
40594
- bind("Delete", handleDelete);
40660
+ bind("Delete", chainCommands(handleDelete, deleteNewlineForward));
40595
40661
  // Table navigation by Tab/Shift-Tab
40596
40662
  bind('Tab', goToNextCell(1));
40597
40663
  bind('Shift-Tab', goToNextCell(-1));
@@ -40607,7 +40673,7 @@ function buildKeymap(config, schema) {
40607
40673
  // Correction (needs to be chained with stateChanged also)
40608
40674
  bind(keymap.undo, undoCommand());
40609
40675
  bind(keymap.redo, redoCommand());
40610
- bind("Backspace", chainCommands(handleDelete, undoInputRule));
40676
+ bind("Backspace", chainCommands(handleDelete, deleteNewlineBackward, undoInputRule));
40611
40677
  // List types
40612
40678
  bind(keymap.bullet, wrapInListCommand(schema, schema.nodes.bullet_list));
40613
40679
  bind(keymap.number, wrapInListCommand(schema, schema.nodes.ordered_list));
@@ -42529,6 +42595,7 @@ const MU = {
42529
42595
  unregisterPlugin,
42530
42596
  getPlugin,
42531
42597
  getPlugins,
42598
+ runPlugin,
42532
42599
  };
42533
42600
 
42534
42601
  sheet.media.mediaText = '(prefers-color-scheme: dark)';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markupeditor",
3
- "version": "0.9.31",
3
+ "version": "0.9.33",
4
4
  "description": "A web component and API for WYSIWYG HTML editing.",
5
5
  "keywords": [
6
6
  "wysiwyg",