@lofcz/platejs-core 52.3.6 → 53.0.0

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.
@@ -112,6 +112,7 @@ function createSlatePlugin(config = {}) {
112
112
  render: {},
113
113
  rules: {},
114
114
  shortcuts: {},
115
+ inputRules: [],
115
116
  transforms: {}
116
117
  }, config);
117
118
  if (plugin.node.isLeaf && !isDefined(plugin.node.isDecoration)) plugin.node.isDecoration = true;
@@ -283,6 +284,11 @@ function getEditorPlugin(editor, p) {
283
284
 
284
285
  //#endregion
285
286
  //#region src/internal/plugin/resolvePlugin.ts
287
+ const normalizeConfiguredInputRules = (config) => {
288
+ if (config === void 0) return [];
289
+ if (Array.isArray(config)) return [...config];
290
+ throw new Error("inputRules config must be an array of explicit rule instances.");
291
+ };
286
292
  /**
287
293
  * Resolves and finalizes a plugin configuration for use in a Plate editor.
288
294
  *
@@ -303,6 +309,11 @@ const resolvePlugin = (editor, _plugin) => {
303
309
  plugin.__resolved = true;
304
310
  if (plugin.__configuration) {
305
311
  const configResult = plugin.__configuration(getEditorPlugin(editor, plugin));
312
+ if (configResult.inputRules !== void 0) {
313
+ const normalizedInputRules = normalizeConfiguredInputRules(configResult.inputRules);
314
+ plugin.__configuredInputRules = [...normalizeConfiguredInputRules(plugin.__configuredInputRules), ...normalizedInputRules];
315
+ configResult.inputRules = void 0;
316
+ }
306
317
  plugin = mergePlugins(plugin, configResult);
307
318
  plugin.__configuration = void 0;
308
319
  }
@@ -361,11 +372,390 @@ const getPluginByType = (editor, type) => {
361
372
  };
362
373
  const getContainerTypes = (editor) => getPluginTypes(editor, editor.meta.pluginCache.node.isContainer);
363
374
 
375
+ //#endregion
376
+ //#region src/lib/plugins/input-rules/defineInputRule.ts
377
+ function defineInputRule(rule) {
378
+ return rule;
379
+ }
380
+
381
+ //#endregion
382
+ //#region src/lib/plugins/input-rules/createInputRules.ts
383
+ const noWhiteSpaceRegex = /\S+/;
384
+ const isPreviousCharacterEmpty = (editor, at) => {
385
+ const range = editor.api.range("before", at);
386
+ if (!range) return true;
387
+ const text = editor.api.string(range);
388
+ return text ? !noWhiteSpaceRegex.exec(text) : true;
389
+ };
390
+ const getMarkMatch = (editor, { end = "", start }) => {
391
+ const { selection } = editor;
392
+ if (!selection) return;
393
+ let beforeEndMatchPoint = selection.anchor;
394
+ if (end) {
395
+ beforeEndMatchPoint = editor.api.before(selection, { matchString: end });
396
+ if (!beforeEndMatchPoint) return;
397
+ }
398
+ const afterStartMatchPoint = editor.api.before(beforeEndMatchPoint, {
399
+ afterMatch: true,
400
+ matchString: start,
401
+ skipInvalid: true
402
+ });
403
+ if (!afterStartMatchPoint) return;
404
+ const beforeStartMatchPoint = editor.api.before(beforeEndMatchPoint, {
405
+ matchString: start,
406
+ skipInvalid: true
407
+ });
408
+ if (!beforeStartMatchPoint) return;
409
+ if (!isPreviousCharacterEmpty(editor, beforeStartMatchPoint)) return;
410
+ return {
411
+ afterStartMatchPoint,
412
+ beforeEndMatchPoint,
413
+ beforeStartMatchPoint
414
+ };
415
+ };
416
+ const createMarkInputRule = (config) => defineInputRule({
417
+ enabled: config.enabled,
418
+ priority: config.priority,
419
+ target: "insertText",
420
+ trigger: config.trigger,
421
+ resolve: ({ editor, text }) => {
422
+ if (text !== config.trigger || !editor.selection || !editor.api.isCollapsed()) return;
423
+ const match = getMarkMatch(editor, {
424
+ end: config.end,
425
+ start: config.start
426
+ });
427
+ if (!match) return;
428
+ const range = {
429
+ anchor: match.afterStartMatchPoint,
430
+ focus: match.beforeEndMatchPoint
431
+ };
432
+ const matchText = editor.api.string(range);
433
+ if (config.trim !== "allow" && matchText.trim() !== matchText) return;
434
+ return {
435
+ ...match,
436
+ end: config.end
437
+ };
438
+ },
439
+ apply: ({ editor, pluginKey }, match) => {
440
+ const marks = config.marks ? [...config.marks] : [config.mark ?? pluginKey];
441
+ if (match.beforeEndMatchPoint !== editor.selection?.anchor) editor.tf.delete({ at: {
442
+ anchor: match.beforeEndMatchPoint,
443
+ focus: editor.selection.anchor
444
+ } });
445
+ editor.tf.select({
446
+ anchor: match.afterStartMatchPoint,
447
+ focus: match.beforeEndMatchPoint
448
+ });
449
+ marks.forEach((mark) => {
450
+ editor.tf.addMark(editor.getType(mark), true);
451
+ });
452
+ editor.tf.collapse({ edge: "end" });
453
+ editor.tf.removeMarks(marks.map((mark) => editor.getType(mark)), { shouldChange: false });
454
+ editor.tf.delete({ at: {
455
+ anchor: match.beforeStartMatchPoint,
456
+ focus: match.afterStartMatchPoint
457
+ } });
458
+ return true;
459
+ }
460
+ });
461
+ const matchBlockStart = (context, config) => {
462
+ if (!context.isCollapsed) return;
463
+ const pattern = typeof config.match === "function" ? config.match(context) : config.match;
464
+ if (!pattern) return;
465
+ const range = context.getBlockStartRange();
466
+ const blockText = context.getBlockStartText();
467
+ if (!range || blockText === void 0) return;
468
+ const baseMatch = {
469
+ range,
470
+ text: blockText
471
+ };
472
+ if (typeof pattern === "string") {
473
+ if (blockText !== pattern) return;
474
+ if (config.resolveMatch) {
475
+ const resolved = config.resolveMatch({
476
+ match: pattern,
477
+ range,
478
+ text: blockText
479
+ });
480
+ if (resolved === void 0) return;
481
+ return {
482
+ ...baseMatch,
483
+ ...resolved
484
+ };
485
+ }
486
+ return baseMatch;
487
+ }
488
+ const regexMatch = blockText.match(pattern);
489
+ if (!regexMatch) return;
490
+ if (config.resolveMatch) {
491
+ const resolved = config.resolveMatch({
492
+ match: regexMatch,
493
+ range,
494
+ text: blockText
495
+ });
496
+ if (resolved === void 0) return;
497
+ return {
498
+ ...baseMatch,
499
+ ...resolved
500
+ };
501
+ }
502
+ return baseMatch;
503
+ };
504
+ const createBlockStartInputRule = (config) => defineInputRule({
505
+ enabled: config.enabled,
506
+ priority: config.priority,
507
+ target: "insertText",
508
+ trigger: config.trigger,
509
+ resolve: (context) => matchBlockStart(context, config),
510
+ apply: (context, match) => {
511
+ if (config.apply) return config.apply(context, match);
512
+ const { editor, pluginKey } = context;
513
+ const defaultMatch = match;
514
+ if (config.removeMatchedText !== false) editor.tf.delete({ at: defaultMatch.range });
515
+ const node = editor.getType(config.node ?? pluginKey);
516
+ if (config.mode === "wrap") {
517
+ editor.tf.toggleBlock(node, { wrap: true });
518
+ return true;
519
+ }
520
+ if (config.mode === "toggle") {
521
+ editor.tf.toggleBlock(node);
522
+ return true;
523
+ }
524
+ editor.tf.setNodes({ type: node }, { match: (entryNode) => editor.api.isBlock(entryNode) });
525
+ return true;
526
+ }
527
+ });
528
+ const matchBlockFence = (context, config) => {
529
+ const { editor } = context;
530
+ const { selection } = editor;
531
+ if (!context.isCollapsed || !selection) return;
532
+ const blockEntry = context.getBlockEntry();
533
+ if (!blockEntry) return;
534
+ const [blockNode, path] = blockEntry;
535
+ const endPoint = editor.api.end(path);
536
+ if (config.block && blockNode.type !== editor.getType(config.block)) return;
537
+ if (!endPoint || !editor.api.isEnd(selection.focus, path)) return;
538
+ const range = context.getBlockStartRange();
539
+ const blockText = context.getBlockStartText();
540
+ if (!range || blockText === void 0 || blockText !== config.fence) return;
541
+ return config.resolveMatch ? config.resolveMatch({
542
+ fence: config.fence,
543
+ path,
544
+ range,
545
+ text: blockText
546
+ }) : {
547
+ path,
548
+ range,
549
+ text: blockText
550
+ };
551
+ };
552
+ function createBlockFenceInputRule(config) {
553
+ if (config.on === "break") return defineInputRule({
554
+ priority: config.priority,
555
+ target: "insertBreak",
556
+ enabled: config.enabled,
557
+ resolve: (context) => matchBlockFence(context, {
558
+ block: config.block,
559
+ fence: config.fence,
560
+ resolveMatch: config.resolveMatch
561
+ }),
562
+ apply: config.apply
563
+ });
564
+ const trigger = config.fence.at(-1);
565
+ if (!trigger) throw new Error("createBlockFenceInputRule requires a non-empty fence.");
566
+ return defineInputRule({
567
+ priority: config.priority,
568
+ target: "insertText",
569
+ enabled: config.enabled,
570
+ trigger,
571
+ resolve: (context) => {
572
+ if (context.text !== trigger) return;
573
+ return matchBlockFence(context, {
574
+ block: config.block,
575
+ fence: config.fence.slice(0, -trigger.length),
576
+ resolveMatch: config.resolveMatch
577
+ });
578
+ },
579
+ apply: config.apply
580
+ });
581
+ }
582
+ const matchDelimitedInline = (context, { boundaryRe, close, followRe, open, requireClosingDelimiter = true, rejectRepeatedOpen = true, trim = "reject" }) => {
583
+ const { editor } = context;
584
+ const { selection } = editor;
585
+ if (!selection || !context.isCollapsed) return;
586
+ const blockRange = context.getBlockStartRange();
587
+ if (!blockRange) return;
588
+ const openingDelimiter = open;
589
+ const closingDelimiter = close ?? open;
590
+ const textBefore = editor.api.string(blockRange);
591
+ const beforeClose = requireClosingDelimiter ? (() => {
592
+ const closeLength = closingDelimiter.length;
593
+ if (textBefore.length < closeLength) return;
594
+ if (!textBefore.endsWith(closingDelimiter)) return;
595
+ return textBefore.slice(0, -closeLength);
596
+ })() : textBefore;
597
+ if (!beforeClose) return;
598
+ const openIndex = beforeClose.lastIndexOf(openingDelimiter);
599
+ if (openIndex < 0) return;
600
+ const prefix = beforeClose.slice(0, openIndex);
601
+ const content = beforeClose.slice(openIndex + openingDelimiter.length);
602
+ if (!content) return;
603
+ if (trim === "reject" && content.trim() !== content) return;
604
+ if (rejectRepeatedOpen && openingDelimiter === closingDelimiter && prefix.endsWith(openingDelimiter)) return;
605
+ const previousChar = prefix.at(-1);
606
+ if (previousChar && boundaryRe && !boundaryRe.test(previousChar)) return;
607
+ const nextPoint = editor.api.after(selection, {
608
+ distance: 1,
609
+ unit: "character"
610
+ });
611
+ if (nextPoint && followRe) {
612
+ const nextChar = editor.api.string({
613
+ anchor: selection.anchor,
614
+ focus: nextPoint
615
+ });
616
+ if (nextChar && !followRe.test(nextChar)) return;
617
+ }
618
+ const startPoint = editor.api.before(selection, {
619
+ distance: content.length + openingDelimiter.length,
620
+ unit: "character"
621
+ });
622
+ if (!startPoint) return;
623
+ return {
624
+ content,
625
+ deleteRange: {
626
+ anchor: startPoint,
627
+ focus: selection.anchor
628
+ }
629
+ };
630
+ };
631
+ const getTextSubstitutionMatchRange = ({ match, trigger }) => {
632
+ const start = match;
633
+ const reversed = start.split("").reverse().join("");
634
+ const triggers = trigger ? Array.isArray(trigger) ? [...trigger] : [trigger] : [reversed.slice(-1)];
635
+ return {
636
+ end: trigger ? reversed : reversed.slice(0, -1),
637
+ start,
638
+ triggers
639
+ };
640
+ };
641
+ const getTextSubstitutionMatchPoints = (editor, { end, start }) => {
642
+ const { selection } = editor;
643
+ if (!selection) return;
644
+ let beforeEndMatchPoint = selection.anchor;
645
+ if (end) {
646
+ beforeEndMatchPoint = editor.api.before(selection, { matchString: end });
647
+ if (!beforeEndMatchPoint) return;
648
+ }
649
+ let afterStartMatchPoint;
650
+ let beforeStartMatchPoint;
651
+ if (start) {
652
+ afterStartMatchPoint = editor.api.before(beforeEndMatchPoint, {
653
+ afterMatch: true,
654
+ matchString: start,
655
+ skipInvalid: true
656
+ });
657
+ if (!afterStartMatchPoint) return;
658
+ beforeStartMatchPoint = editor.api.before(beforeEndMatchPoint, {
659
+ matchString: start,
660
+ skipInvalid: true
661
+ });
662
+ if (!beforeStartMatchPoint) return;
663
+ if (!isPreviousCharacterEmpty(editor, beforeStartMatchPoint)) return;
664
+ }
665
+ return {
666
+ afterStartMatchPoint,
667
+ beforeEndMatchPoint,
668
+ beforeStartMatchPoint
669
+ };
670
+ };
671
+ const getTextSubstitutionTriggers = (patterns) => Array.from(new Set(patterns.flatMap((pattern) => {
672
+ return (Array.isArray(pattern.match) ? [...pattern.match] : [pattern.match]).flatMap((match) => getTextSubstitutionMatchRange({
673
+ match,
674
+ trigger: pattern.trigger
675
+ }).triggers);
676
+ })));
677
+ const resolveTextSubstitution = ({ editor, patterns, text }) => {
678
+ for (const pattern of patterns) {
679
+ const matches = Array.isArray(pattern.match) ? [...pattern.match] : [pattern.match];
680
+ for (const match of matches) {
681
+ const { end, start, triggers } = getTextSubstitutionMatchRange({
682
+ match,
683
+ trigger: pattern.trigger
684
+ });
685
+ if (!triggers.includes(text)) continue;
686
+ const points = getTextSubstitutionMatchPoints(editor, {
687
+ end: Array.isArray(pattern.format) ? "" : end,
688
+ start
689
+ });
690
+ if (!points) continue;
691
+ return {
692
+ end: Array.isArray(pattern.format) ? "" : end,
693
+ pattern,
694
+ points
695
+ };
696
+ }
697
+ }
698
+ };
699
+ const applyTextSubstitution = (editor, match) => {
700
+ const selection = editor.selection;
701
+ if (!selection || !match) return false;
702
+ if (match.end) editor.tf.delete({ at: {
703
+ anchor: match.points.beforeEndMatchPoint,
704
+ focus: selection.anchor
705
+ } });
706
+ const formatEnd = Array.isArray(match.pattern.format) ? match.pattern.format[1] : match.pattern.format;
707
+ editor.tf.insertText(formatEnd);
708
+ if (match.points.beforeStartMatchPoint && match.points.afterStartMatchPoint) {
709
+ const formatStart = Array.isArray(match.pattern.format) ? match.pattern.format[0] : match.pattern.format;
710
+ editor.tf.delete({ at: {
711
+ anchor: match.points.beforeStartMatchPoint,
712
+ focus: match.points.afterStartMatchPoint
713
+ } });
714
+ editor.tf.insertText(formatStart, { at: match.points.beforeStartMatchPoint });
715
+ }
716
+ return true;
717
+ };
718
+ const createTextSubstitutionInputRule = ({ enabled, patterns, priority }) => defineInputRule({
719
+ enabled,
720
+ priority,
721
+ target: "insertText",
722
+ trigger: getTextSubstitutionTriggers(patterns),
723
+ resolve: ({ editor, text }) => {
724
+ if (!editor.selection || !editor.api.isCollapsed()) return;
725
+ return resolveTextSubstitution({
726
+ editor,
727
+ patterns,
728
+ text
729
+ });
730
+ },
731
+ apply: ({ editor }, match) => applyTextSubstitution(editor, match)
732
+ });
733
+
734
+ //#endregion
735
+ //#region src/lib/plugins/input-rules/internal/createInputRuleBuilder.ts
736
+ const createInputRuleBuilder = () => ({
737
+ blockFence: (config) => createBlockFenceInputRule(config),
738
+ blockStart: (config) => createBlockStartInputRule(config),
739
+ insertBreak: (rule) => defineInputRule(rule),
740
+ insertData: (rule) => defineInputRule(rule),
741
+ insertText: (rule) => defineInputRule(rule),
742
+ mark: (config) => createMarkInputRule(config)
743
+ });
744
+
364
745
  //#endregion
365
746
  //#region src/internal/plugin/resolvePlugins.ts
366
747
  const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore) => {
367
748
  editor.plugins = {};
368
749
  editor.meta.pluginList = [];
750
+ editor.meta.inputRules = {
751
+ insertBreak: [],
752
+ insertData: [],
753
+ insertText: {
754
+ all: [],
755
+ byTrigger: {}
756
+ },
757
+ plugins: {}
758
+ };
369
759
  editor.meta.shortcuts = {};
370
760
  editor.meta.components = {};
371
761
  editor.meta.pluginCache = {
@@ -435,6 +825,8 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
435
825
  if (plugin.handlers?.onTextChange) editor.meta.pluginCache.handlers.onTextChange.push(plugin.key);
436
826
  });
437
827
  resolvePluginShortcuts(editor);
828
+ resolvePluginInputRules(editor);
829
+ validateRemovedRuntimePlugins(editor);
438
830
  return editor;
439
831
  };
440
832
  const resolvePluginStores = (editor, createStore) => {
@@ -512,6 +904,68 @@ const resolvePluginShortcuts = (editor) => {
512
904
  });
513
905
  });
514
906
  };
907
+ const resolvePluginInputRules = (editor) => {
908
+ const resolvedMeta = {
909
+ insertBreak: [],
910
+ insertData: [],
911
+ insertText: {
912
+ all: [],
913
+ byTrigger: {}
914
+ },
915
+ plugins: {}
916
+ };
917
+ editor.meta.pluginList.forEach((plugin, pluginIndex) => {
918
+ const pluginKey = plugin.key;
919
+ const inputRulesDefinition = plugin.inputRules;
920
+ const definitionRules = typeof inputRulesDefinition === "function" ? inputRulesDefinition({ rule: createInputRuleBuilder() }) : inputRulesDefinition ?? [];
921
+ const configuredRules = plugin.__configuredInputRules ?? [];
922
+ const ruleDefinitions = [...definitionRules, ...configuredRules];
923
+ resolvedMeta.plugins[pluginKey] = { rules: [] };
924
+ ruleDefinitions.forEach((definition, ruleIndex) => {
925
+ if (!definition) return;
926
+ const mergedRule = mergePlugins({}, definition);
927
+ const resolvedRule = {
928
+ ...mergedRule,
929
+ id: `${pluginKey}.${ruleIndex}`,
930
+ pluginIndex,
931
+ pluginKey,
932
+ priority: mergedRule.priority ?? plugin.priority,
933
+ ruleIndex
934
+ };
935
+ resolvedMeta.plugins[pluginKey].rules.push(resolvedRule);
936
+ if (resolvedRule.target === "insertText") {
937
+ const triggers = Array.isArray(resolvedRule.trigger) ? [...resolvedRule.trigger] : [resolvedRule.trigger];
938
+ resolvedMeta.insertText.all.push(resolvedRule);
939
+ triggers.forEach((trigger) => {
940
+ if (!resolvedMeta.insertText.byTrigger[trigger]) resolvedMeta.insertText.byTrigger[trigger] = [];
941
+ resolvedMeta.insertText.byTrigger[trigger].push(resolvedRule);
942
+ });
943
+ } else if (resolvedRule.target === "insertBreak") resolvedMeta.insertBreak.push(resolvedRule);
944
+ else if (resolvedRule.target === "insertData") resolvedMeta.insertData.push(resolvedRule);
945
+ });
946
+ });
947
+ const sortRules = (a, b) => {
948
+ if (b.priority !== a.priority) return b.priority - a.priority;
949
+ if (a.pluginIndex !== b.pluginIndex) return a.pluginIndex - b.pluginIndex;
950
+ return a.ruleIndex - b.ruleIndex;
951
+ };
952
+ resolvedMeta.insertBreak.sort(sortRules);
953
+ resolvedMeta.insertData.sort(sortRules);
954
+ resolvedMeta.insertText.all.sort(sortRules);
955
+ Object.values(resolvedMeta.insertText.byTrigger).forEach((rules) => {
956
+ rules.sort(sortRules);
957
+ });
958
+ editor.meta.inputRules = resolvedMeta;
959
+ };
960
+ const validateRemovedRuntimePlugins = (editor) => {
961
+ const hasAutoformatPlugin = !!editor.plugins.autoformat;
962
+ const hasResolvedInputRules = editor.meta.inputRules.insertBreak.length > 0 || editor.meta.inputRules.insertData.length > 0 || editor.meta.inputRules.insertText.all.length > 0;
963
+ if (hasAutoformatPlugin && hasResolvedInputRules) throw new Error([
964
+ "AutoformatPlugin cannot be used with plugin-owned input rules.",
965
+ "Remove AutoformatPlugin from your editor plugins.",
966
+ "Enable inputRules on the feature plugins you use instead."
967
+ ].join(" "));
968
+ };
515
969
  const flattenAndResolvePlugins = (editor, plugins) => {
516
970
  const pluginMap = /* @__PURE__ */ new Map();
517
971
  const processPlugin = (plugin) => {
@@ -643,11 +1097,11 @@ const withBreakRules = (ctx) => {
643
1097
  node: blockNode,
644
1098
  path: blockPath,
645
1099
  rule
646
- })) return overridePlugin.rules.break;
1100
+ })) return overridePlugin;
647
1101
  }
648
1102
  return null;
649
1103
  };
650
- const executeBreakAction = (action, blockPath) => {
1104
+ const executeBreakAction = (action, blockPath, type) => {
651
1105
  if (action === "reset") {
652
1106
  editor.tf.resetBlock({ at: blockPath });
653
1107
  return true;
@@ -665,30 +1119,40 @@ const withBreakRules = (ctx) => {
665
1119
  editor.tf.insertSoftBreak();
666
1120
  return true;
667
1121
  }
1122
+ if (action === "lift" && type) return !!editor.tf.liftBlock({
1123
+ at: blockPath,
1124
+ match: { type }
1125
+ });
668
1126
  return false;
669
1127
  };
670
1128
  return { transforms: { insertBreak() {
671
- if (editor.selection && editor.api.isCollapsed()) {
1129
+ if (editor.selection) {
672
1130
  const block = editor.api.block();
673
1131
  if (block) {
674
1132
  const [blockNode, blockPath] = block;
675
1133
  const breakRules = getPluginByType(editor, blockNode.type)?.rules.break;
676
- if (editor.api.isEmpty(editor.selection, { block: true })) {
677
- const emptyAction = (checkMatchRulesOverride("break.empty", blockNode, blockPath) || breakRules)?.empty;
678
- if (executeBreakAction(emptyAction, blockPath)) return;
1134
+ if (editor.api.isCollapsed() && editor.api.isEmpty(editor.selection, { block: true })) {
1135
+ const overridePlugin = checkMatchRulesOverride("break.empty", blockNode, blockPath);
1136
+ const emptyAction = (overridePlugin?.rules.break ?? breakRules)?.empty;
1137
+ const actionType = overridePlugin?.node.type;
1138
+ if (executeBreakAction(emptyAction, blockPath, actionType)) return;
679
1139
  }
680
- if (!editor.api.isEmpty(editor.selection, { block: true }) && editor.api.isAt({ end: true })) {
1140
+ if (editor.api.isCollapsed() && !editor.api.isEmpty(editor.selection, { block: true }) && editor.api.isAt({ end: true })) {
681
1141
  const range = editor.api.range("before", editor.selection);
682
1142
  if (range) {
683
1143
  if (editor.api.string(range) === "\n") {
684
- const emptyLineEndAction = (checkMatchRulesOverride("break.emptyLineEnd", blockNode, blockPath) || breakRules)?.emptyLineEnd;
685
- if (executeBreakAction(emptyLineEndAction, blockPath)) return;
1144
+ const overridePlugin = checkMatchRulesOverride("break.emptyLineEnd", blockNode, blockPath);
1145
+ const emptyLineEndAction = (overridePlugin?.rules.break ?? breakRules)?.emptyLineEnd;
1146
+ const actionType = overridePlugin?.node.type;
1147
+ if (executeBreakAction(emptyLineEndAction, blockPath, actionType)) return;
686
1148
  }
687
1149
  }
688
1150
  }
689
- const defaultAction = (checkMatchRulesOverride("break.default", blockNode, blockPath) || breakRules)?.default;
690
- if (executeBreakAction(defaultAction, blockPath)) return;
691
- if (checkMatchRulesOverride("break.splitReset", blockNode, blockPath)?.splitReset ?? breakRules?.splitReset) {
1151
+ const overrideDefaultPlugin = checkMatchRulesOverride("break.default", blockNode, blockPath);
1152
+ const defaultAction = (overrideDefaultPlugin?.rules.break ?? breakRules)?.default;
1153
+ const defaultActionType = overrideDefaultPlugin?.node.type;
1154
+ if (executeBreakAction(defaultAction, blockPath, defaultActionType)) return;
1155
+ if ((checkMatchRulesOverride("break.splitReset", blockNode, blockPath)?.rules.break?.splitReset ?? breakRules?.splitReset) && !editor.api.isAt({ blocks: true })) {
692
1156
  const isAtStart = editor.api.isAt({ start: true });
693
1157
  insertBreak();
694
1158
  editor.tf.resetBlock({ at: isAtStart ? blockPath : PathApi.next(blockPath) });
@@ -716,15 +1180,19 @@ const withDeleteRules = (ctx) => {
716
1180
  node: blockNode,
717
1181
  path: blockPath,
718
1182
  rule
719
- })) return overridePlugin.rules.delete;
1183
+ })) return overridePlugin;
720
1184
  }
721
1185
  return null;
722
1186
  };
723
- const executeDeleteAction = (action, blockPath) => {
1187
+ const executeDeleteAction = (action, blockPath, type) => {
724
1188
  if (action === "reset") {
725
1189
  editor.tf.resetBlock({ at: blockPath });
726
1190
  return true;
727
1191
  }
1192
+ if (action === "lift" && type) return !!editor.tf.liftBlock({
1193
+ at: blockPath,
1194
+ match: { type }
1195
+ });
728
1196
  return false;
729
1197
  };
730
1198
  return { transforms: {
@@ -735,12 +1203,16 @@ const withDeleteRules = (ctx) => {
735
1203
  const [blockNode, blockPath] = block;
736
1204
  const deleteRules = getPluginByType(editor, blockNode.type)?.rules.delete;
737
1205
  if (editor.api.isAt({ start: true })) {
738
- const startAction = (checkMatchRulesOverride("delete.start", blockNode, blockPath) || deleteRules)?.start;
739
- if (executeDeleteAction(startAction, blockPath)) return;
1206
+ const overridePlugin = checkMatchRulesOverride("delete.start", blockNode, blockPath);
1207
+ const startAction = (overridePlugin?.rules.delete ?? deleteRules)?.start;
1208
+ const actionType = overridePlugin?.node.type;
1209
+ if (executeDeleteAction(startAction, blockPath, actionType)) return;
740
1210
  }
741
1211
  if (editor.api.isEmpty(editor.selection, { block: true })) {
742
- const emptyAction = (checkMatchRulesOverride("delete.empty", blockNode, blockPath) || deleteRules)?.empty;
743
- if (executeDeleteAction(emptyAction, blockPath)) return;
1212
+ const overridePlugin = checkMatchRulesOverride("delete.empty", blockNode, blockPath);
1213
+ const emptyAction = (overridePlugin?.rules.delete ?? deleteRules)?.empty;
1214
+ const actionType = overridePlugin?.node.type;
1215
+ if (executeDeleteAction(emptyAction, blockPath, actionType)) return;
744
1216
  }
745
1217
  }
746
1218
  if (PointApi.equals(editor.selection.anchor, editor.api.start([]))) {
@@ -994,6 +1466,7 @@ const getInjectMatch = (editor, plugin) => {
994
1466
  if (targetPlugins && !targetPlugins.includes(getPluginKey(editor, element.type))) return false;
995
1467
  }
996
1468
  if (excludeBelowPlugins || maxLevel) {
1469
+ if (!path) return false;
997
1470
  if (maxLevel && path.length > maxLevel) return false;
998
1471
  if (excludeBelowPlugins) {
999
1472
  const excludeTypes = getPluginKeys(editor, excludeBelowPlugins);
@@ -2067,6 +2540,182 @@ const LengthPlugin = createTSlatePlugin({ key: "length" }).overrideEditor(({ edi
2067
2540
  });
2068
2541
  } } }));
2069
2542
 
2543
+ //#endregion
2544
+ //#region src/lib/plugins/navigation-feedback/types.ts
2545
+ const NAVIGATION_FEEDBACK_KEY = "navigationFeedback";
2546
+ const NavigationFeedbackPluginKey = { key: NAVIGATION_FEEDBACK_KEY };
2547
+
2548
+ //#endregion
2549
+ //#region src/lib/plugins/navigation-feedback/transforms/flashTarget.ts
2550
+ const NAVIGATION_FEEDBACK_TIMEOUT = /* @__PURE__ */ new WeakMap();
2551
+ const NAVIGATION_FEEDBACK_PULSE = /* @__PURE__ */ new WeakMap();
2552
+ const NAVIGATION_FEEDBACK_ATTRIBUTES = [
2553
+ "data-nav-cycle",
2554
+ "data-nav-highlight",
2555
+ "data-nav-pulse",
2556
+ "data-nav-target"
2557
+ ];
2558
+ const clearNavigationPathRef = (target) => {
2559
+ target?.pathRef.unref();
2560
+ };
2561
+ const resolveNavigationFeedbackTarget = (target) => {
2562
+ const path = target?.pathRef.current;
2563
+ if (!target || !path) return null;
2564
+ const { pathRef: _pathRef, ...rest } = target;
2565
+ return {
2566
+ ...rest,
2567
+ path
2568
+ };
2569
+ };
2570
+ const getNavigationElement = (editor, target) => {
2571
+ const node = NodeApi.get(editor, target.path);
2572
+ if (!node) return;
2573
+ try {
2574
+ return editor.api.toDOMNode(node);
2575
+ } catch {
2576
+ return;
2577
+ }
2578
+ };
2579
+ const clearNavigationElement = (editor, target) => {
2580
+ if (!target) return;
2581
+ const element = getNavigationElement(editor, target);
2582
+ if (!element) return;
2583
+ for (const attribute of NAVIGATION_FEEDBACK_ATTRIBUTES) element.removeAttribute(attribute);
2584
+ element.style.removeProperty("--plate-nav-feedback-duration");
2585
+ };
2586
+ const setNavigationElement = (editor, target) => {
2587
+ const element = getNavigationElement(editor, target);
2588
+ if (!element) return;
2589
+ element.setAttribute("data-nav-cycle", String(target.cycle));
2590
+ element.setAttribute("data-nav-highlight", target.variant);
2591
+ element.setAttribute("data-nav-pulse", String(target.pulse));
2592
+ element.setAttribute("data-nav-target", "true");
2593
+ element.style.setProperty("--plate-nav-feedback-duration", `${target.duration}ms`);
2594
+ };
2595
+ const clearNavigationTimeout = (editor) => {
2596
+ const timeoutId = NAVIGATION_FEEDBACK_TIMEOUT.get(editor);
2597
+ if (timeoutId) {
2598
+ clearTimeout(timeoutId);
2599
+ NAVIGATION_FEEDBACK_TIMEOUT.delete(editor);
2600
+ }
2601
+ };
2602
+ const nextPulse = (editor) => {
2603
+ const pulse = (NAVIGATION_FEEDBACK_PULSE.get(editor) ?? 0) + 1;
2604
+ NAVIGATION_FEEDBACK_PULSE.set(editor, pulse);
2605
+ return pulse;
2606
+ };
2607
+ const clearNavigationFeedbackTarget = (editor, pulse) => {
2608
+ const storedTarget = editor.getOption(NavigationFeedbackPluginKey, "activeTarget");
2609
+ const activeTarget = resolveNavigationFeedbackTarget(storedTarget);
2610
+ if (!storedTarget) return false;
2611
+ if (pulse !== void 0 && storedTarget.pulse !== pulse) return false;
2612
+ clearNavigationTimeout(editor);
2613
+ clearNavigationElement(editor, activeTarget);
2614
+ clearNavigationPathRef(storedTarget);
2615
+ editor.setOption(NavigationFeedbackPluginKey, "activeTarget", null);
2616
+ return true;
2617
+ };
2618
+ const flashTarget = (editor, { duration, target, variant = "navigated" }) => {
2619
+ if (!editor.api.node(target.path)) return false;
2620
+ const pulse = nextPulse(editor);
2621
+ const timeoutMs = duration ?? editor.getOption(NavigationFeedbackPluginKey, "duration") ?? 800;
2622
+ const previousTarget = editor.getOption(NavigationFeedbackPluginKey, "activeTarget");
2623
+ clearNavigationTimeout(editor);
2624
+ clearNavigationElement(editor, resolveNavigationFeedbackTarget(previousTarget));
2625
+ clearNavigationPathRef(previousTarget);
2626
+ const pathRef = editor.api.pathRef(target.path);
2627
+ const activeTarget = {
2628
+ cycle: pulse % 2,
2629
+ duration: timeoutMs,
2630
+ pathRef,
2631
+ pulse,
2632
+ type: target.type,
2633
+ variant
2634
+ };
2635
+ editor.setOption(NavigationFeedbackPluginKey, "activeTarget", activeTarget);
2636
+ setNavigationElement(editor, resolveNavigationFeedbackTarget(activeTarget) ?? {
2637
+ cycle: activeTarget.cycle,
2638
+ duration: activeTarget.duration,
2639
+ path: target.path,
2640
+ pulse: activeTarget.pulse,
2641
+ type: activeTarget.type,
2642
+ variant: activeTarget.variant
2643
+ });
2644
+ const timeoutId = setTimeout(() => {
2645
+ clearNavigationFeedbackTarget(editor, pulse);
2646
+ }, timeoutMs);
2647
+ NAVIGATION_FEEDBACK_TIMEOUT.set(editor, timeoutId);
2648
+ return true;
2649
+ };
2650
+
2651
+ //#endregion
2652
+ //#region src/lib/plugins/navigation-feedback/transforms/navigate.ts
2653
+ const getScrollTarget = (editor, { scrollTarget, select, target }) => {
2654
+ if (scrollTarget) return scrollTarget;
2655
+ if (select && "focus" in select && select.focus) return select.focus;
2656
+ if (select && "anchor" in select && select.anchor) return select.anchor;
2657
+ if (select && "path" in select) return select;
2658
+ return editor.api.start(target.path);
2659
+ };
2660
+ const navigate = (editor, { flash, focus = true, scroll = true, scrollTarget, select, target }) => {
2661
+ if (!editor.api.node(target.path)) return false;
2662
+ if (select) if ("focus" in select) editor.tf.select(select);
2663
+ else editor.tf.select({
2664
+ anchor: select,
2665
+ focus: select
2666
+ });
2667
+ if (focus) editor.tf.focus();
2668
+ if (scroll) {
2669
+ const point = getScrollTarget(editor, {
2670
+ flash,
2671
+ focus,
2672
+ scroll,
2673
+ scrollTarget,
2674
+ select,
2675
+ target
2676
+ });
2677
+ if (point) editor.api.scrollIntoView(point);
2678
+ }
2679
+ if (flash !== false) flashTarget(editor, {
2680
+ duration: flash?.duration,
2681
+ target,
2682
+ variant: flash?.variant
2683
+ });
2684
+ return true;
2685
+ };
2686
+
2687
+ //#endregion
2688
+ //#region src/lib/plugins/navigation-feedback/NavigationFeedbackPlugin.ts
2689
+ const NavigationFeedbackPlugin = createTSlatePlugin({
2690
+ key: NAVIGATION_FEEDBACK_KEY,
2691
+ options: {
2692
+ activeTarget: null,
2693
+ duration: 1600
2694
+ }
2695
+ }).extendEditorApi(({ editor }) => {
2696
+ const getActiveTarget = () => {
2697
+ const storedTarget = editor.getOption(NavigationFeedbackPluginKey, "activeTarget");
2698
+ const activeTarget = resolveNavigationFeedbackTarget(storedTarget);
2699
+ if (!activeTarget && storedTarget) {
2700
+ clearNavigationFeedbackTarget(editor);
2701
+ return null;
2702
+ }
2703
+ return activeTarget;
2704
+ };
2705
+ return { navigation: {
2706
+ activeTarget: getActiveTarget,
2707
+ clear: () => clearNavigationFeedbackTarget(editor),
2708
+ isTarget: (path) => {
2709
+ const activeTarget = getActiveTarget();
2710
+ return !!activeTarget && PathApi.equals(activeTarget.path, path);
2711
+ }
2712
+ } };
2713
+ }).extendEditorTransforms(({ editor }) => ({ navigation: {
2714
+ clear: () => clearNavigationFeedbackTarget(editor),
2715
+ flashTarget: (options) => flashTarget(editor, options),
2716
+ navigate: (options) => navigate(editor, options)
2717
+ } }));
2718
+
2070
2719
  //#endregion
2071
2720
  //#region src/lib/plugins/node-id/withNodeId.ts
2072
2721
  /** Enables support for inserting nodes with an id key. */
@@ -2402,6 +3051,31 @@ const insertExitBreak = (editor, { match, reverse } = {}) => {
2402
3051
  return true;
2403
3052
  };
2404
3053
 
3054
+ //#endregion
3055
+ //#region src/lib/plugins/slate-extension/transforms/liftBlock.ts
3056
+ /**
3057
+ * Lift the current block out of the nearest matching ancestor container.
3058
+ *
3059
+ * This unwraps only the current block and splits the ancestor around it when
3060
+ * needed, so one keypress changes one structural level instead of exploding the
3061
+ * whole container.
3062
+ */
3063
+ const liftBlock = (editor, { at, match } = {}) => {
3064
+ const block = editor.api.block({ at });
3065
+ if (!block || !match) return;
3066
+ const [, blockPath] = block;
3067
+ if (!editor.api.above({
3068
+ at: blockPath,
3069
+ match: combineMatchOptions(editor, (_node, path) => path.length < blockPath.length, { match })
3070
+ })) return;
3071
+ editor.tf.unwrapNodes({
3072
+ at: blockPath,
3073
+ match,
3074
+ split: true
3075
+ });
3076
+ return true;
3077
+ };
3078
+
2405
3079
  //#endregion
2406
3080
  //#region src/lib/plugins/slate-extension/transforms/resetBlock.ts
2407
3081
  /**
@@ -2439,25 +3113,27 @@ const setValue = (editor, value) => {
2439
3113
 
2440
3114
  //#endregion
2441
3115
  //#region src/lib/plugins/slate-extension/SlateExtensionPlugin.ts
3116
+ const NOOP_ON_NODE_CHANGE = () => {};
3117
+ const NOOP_ON_TEXT_CHANGE = () => {};
2442
3118
  /** Opinionated extension of slate default behavior. */
2443
3119
  const SlateExtensionPlugin = createTSlatePlugin({
2444
3120
  api: { redecorate: () => {} },
2445
3121
  key: "slateExtension",
2446
3122
  options: {
2447
- onNodeChange: () => {},
2448
- onTextChange: () => {}
3123
+ onNodeChange: NOOP_ON_NODE_CHANGE,
3124
+ onTextChange: NOOP_ON_TEXT_CHANGE
2449
3125
  }
2450
3126
  }).extendEditorTransforms(({ editor, getOption, tf }) => {
2451
3127
  const apply = tf?.apply ?? editor.tf.apply;
2452
3128
  return {
2453
3129
  init: bindFirst(init, editor),
2454
3130
  insertExitBreak: bindFirst(insertExitBreak, editor),
3131
+ liftBlock: bindFirst(liftBlock, editor),
2455
3132
  resetBlock: bindFirst(resetBlock, editor),
2456
3133
  setValue: bindFirst(setValue, editor),
2457
3134
  apply(operation) {
2458
- const noop = () => {};
2459
- const hasNodeHandlers = editor.meta.pluginCache.handlers.onNodeChange.length > 0 || getOption("onNodeChange") !== noop;
2460
- const hasTextHandlers = editor.meta.pluginCache.handlers.onTextChange.length > 0 || getOption("onTextChange") !== noop;
3135
+ const hasNodeHandlers = editor.meta.pluginCache.handlers.onNodeChange.length > 0 || getOption("onNodeChange") !== NOOP_ON_NODE_CHANGE;
3136
+ const hasTextHandlers = editor.meta.pluginCache.handlers.onTextChange.length > 0 || getOption("onTextChange") !== NOOP_ON_TEXT_CHANGE;
2461
3137
  if (!hasNodeHandlers && !hasTextHandlers) {
2462
3138
  apply(operation);
2463
3139
  return;
@@ -2655,16 +3331,159 @@ const ParserPlugin = createSlatePlugin({ key: "parser" }).overrideEditor(({ edit
2655
3331
  insertData(dataTransfer);
2656
3332
  } } }));
2657
3333
 
3334
+ //#endregion
3335
+ //#region src/lib/plugins/input-rules/internal/InputRulesPlugin.ts
3336
+ const createCachedGetter = (compute) => {
3337
+ let hasValue = false;
3338
+ let value;
3339
+ return () => {
3340
+ if (!hasValue) {
3341
+ value = compute();
3342
+ hasValue = true;
3343
+ }
3344
+ return value;
3345
+ };
3346
+ };
3347
+ const createSelectionContext = ({ editor }) => {
3348
+ const { selection } = editor;
3349
+ const isCollapsed = !!selection && editor.api.isCollapsed();
3350
+ const getBlockStartRange = createCachedGetter(() => {
3351
+ if (!selection) return;
3352
+ return editor.api.range("start", selection);
3353
+ });
3354
+ const getBlockStartText = createCachedGetter(() => {
3355
+ const range = getBlockStartRange();
3356
+ return range ? editor.api.string(range) : void 0;
3357
+ });
3358
+ return {
3359
+ editor,
3360
+ getBlockEntry: createCachedGetter(() => {
3361
+ if (!selection) return;
3362
+ return editor.api.block({ at: selection });
3363
+ }),
3364
+ getBlockStartRange,
3365
+ getBlockStartText,
3366
+ getBlockTextBeforeSelection: createCachedGetter(() => getBlockStartText() ?? ""),
3367
+ getCharAfter: createCachedGetter(() => {
3368
+ if (!selection || !isCollapsed) return;
3369
+ const afterPoint = editor.api.after(selection, {
3370
+ distance: 1,
3371
+ unit: "character"
3372
+ });
3373
+ if (!afterPoint) return;
3374
+ return editor.api.string({
3375
+ anchor: selection.anchor,
3376
+ focus: afterPoint
3377
+ }) || void 0;
3378
+ }),
3379
+ getCharBefore: createCachedGetter(() => {
3380
+ if (!selection || !isCollapsed) return;
3381
+ const beforePoint = editor.api.before(selection, {
3382
+ distance: 1,
3383
+ unit: "character"
3384
+ });
3385
+ if (!beforePoint) return;
3386
+ return editor.api.string({
3387
+ anchor: beforePoint,
3388
+ focus: selection.anchor
3389
+ }) || void 0;
3390
+ }),
3391
+ isCollapsed
3392
+ };
3393
+ };
3394
+ const isTriggerMatch = (trigger, text) => Array.isArray(trigger) ? trigger.includes(text) : trigger === text;
3395
+ const InputRulesPlugin = createTSlatePlugin({
3396
+ editOnly: true,
3397
+ key: "inputRules"
3398
+ }).overrideEditor(({ editor, tf: { insertBreak, insertData, insertText } }) => ({ transforms: {
3399
+ insertBreak() {
3400
+ const selectionContext = createSelectionContext({ editor });
3401
+ let handled = false;
3402
+ for (const rule of editor.meta.inputRules.insertBreak) {
3403
+ const context = {
3404
+ cause: "insertBreak",
3405
+ insertBreak,
3406
+ pluginKey: rule.pluginKey,
3407
+ ...selectionContext
3408
+ };
3409
+ if (rule.enabled?.(context) === false) continue;
3410
+ const match = rule.resolve ? rule.resolve(context) : true;
3411
+ if (match === void 0) continue;
3412
+ if (rule.apply(context, match) !== false) {
3413
+ handled = true;
3414
+ break;
3415
+ }
3416
+ }
3417
+ if (handled) return;
3418
+ insertBreak();
3419
+ },
3420
+ insertData(data) {
3421
+ const text = data.getData("text/plain") || null;
3422
+ const selectionContext = createSelectionContext({ editor });
3423
+ let handled = false;
3424
+ for (const rule of editor.meta.inputRules.insertData) {
3425
+ const context = {
3426
+ cause: "insertData",
3427
+ data,
3428
+ insertData,
3429
+ pluginKey: rule.pluginKey,
3430
+ text,
3431
+ ...selectionContext
3432
+ };
3433
+ if (rule.enabled?.(context) === false) continue;
3434
+ if (rule.mimeTypes && rule.mimeTypes.length > 0 && !rule.mimeTypes.some((type) => !!context.data.getData(type))) continue;
3435
+ const match = rule.resolve ? rule.resolve(context) : true;
3436
+ if (match === void 0) continue;
3437
+ if (rule.apply(context, match) !== false) {
3438
+ handled = true;
3439
+ break;
3440
+ }
3441
+ }
3442
+ if (handled) return;
3443
+ insertData(data);
3444
+ },
3445
+ insertText(text, options) {
3446
+ const rules = editor.meta.inputRules.insertText.byTrigger[text] ?? [];
3447
+ const selectionContext = createSelectionContext({ editor });
3448
+ let handled = false;
3449
+ for (const rule of rules) {
3450
+ const context = {
3451
+ cause: "insertText",
3452
+ insertText,
3453
+ options,
3454
+ pluginKey: rule.pluginKey,
3455
+ text,
3456
+ ...selectionContext
3457
+ };
3458
+ if (!isTriggerMatch(rule.trigger, context.text)) continue;
3459
+ if (rule.enabled?.(context) === false) continue;
3460
+ const match = rule.resolve ? rule.resolve(context) : true;
3461
+ if (match === void 0) continue;
3462
+ if (rule.apply(context, match) !== false) {
3463
+ handled = true;
3464
+ break;
3465
+ }
3466
+ }
3467
+ if (handled) return;
3468
+ insertText(text, options);
3469
+ }
3470
+ } }));
3471
+
2658
3472
  //#endregion
2659
3473
  //#region src/lib/plugins/getCorePlugins.ts
2660
- const getCorePlugins = ({ affinity, chunking, maxLength, nodeId, plugins = [] }) => {
3474
+ const getCorePlugins = ({ affinity, chunking, maxLength, navigationFeedback, nodeId, plugins = [] }) => {
2661
3475
  let resolvedNodeId = nodeId;
2662
3476
  if (process.env.NODE_ENV === "test" && nodeId === void 0) resolvedNodeId = false;
2663
3477
  let corePlugins = [
2664
3478
  DebugPlugin,
2665
3479
  SlateExtensionPlugin,
2666
3480
  DOMPlugin,
3481
+ NavigationFeedbackPlugin.configure({
3482
+ enabled: navigationFeedback !== false,
3483
+ options: typeof navigationFeedback === "boolean" ? void 0 : navigationFeedback
3484
+ }),
2667
3485
  HistoryPlugin,
3486
+ InputRulesPlugin,
2668
3487
  OverridePlugin,
2669
3488
  ParserPlugin,
2670
3489
  maxLength ? LengthPlugin.configure({ options: { maxLength } }) : LengthPlugin,
@@ -2707,7 +3526,7 @@ const getCorePlugins = ({ affinity, chunking, maxLength, nodeId, plugins = [] })
2707
3526
  * @see {@link usePlateEditor} for a memoized React version.
2708
3527
  * @see {@link withPlate} for the React-specific enhancement function.
2709
3528
  */
2710
- const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLength, nodeId, optionsStoreFactory, plugins = [], readOnly = false, rootPlugin, selection, shouldNormalizeEditor, skipInitialization, userId, value, onReady, ...pluginConfig } = {}) => {
3529
+ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLength, navigationFeedback, nodeId, optionsStoreFactory, plugins = [], readOnly = false, rootPlugin, selection, shouldNormalizeEditor, skipInitialization, userId, value, onReady, ...pluginConfig } = {}) => {
2711
3530
  const editor = e;
2712
3531
  editor.id = id ?? editor.id ?? nanoid();
2713
3532
  editor.meta.key = editor.meta.key ?? nanoid();
@@ -2765,6 +3584,7 @@ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLen
2765
3584
  affinity,
2766
3585
  chunking,
2767
3586
  maxLength,
3587
+ navigationFeedback,
2768
3588
  nodeId,
2769
3589
  plugins
2770
3590
  });
@@ -2837,4 +3657,4 @@ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLen
2837
3657
  const createSlateEditor = ({ editor = createEditor(), ...options } = {}) => withSlate(editor, options);
2838
3658
 
2839
3659
  //#endregion
2840
- export { DebugPlugin as $, pluginDeserializeHtml as A, withNormalizeRules as At, collapseWhiteSpaceText as B, getPluginKey as Bt, deserializeHtmlElement as C, isSlatePluginNode as Ct, pipeDeserializeHtmlLeaf as D, applyDeepToNodes as Dt, htmlElementToLeaf as E, isSlateVoid as Et, collapseWhiteSpace as F, HistoryPlugin as Ft, isHtmlBlockElement as G, getEditorPlugin as Gt, upsertInlineFormattingContext as H, getPluginType as Ht, collapseWhiteSpaceElement as I, withPlateHistory as It, isHtmlText as J, isHtmlInlineElement as K, createSlatePlugin as Kt, inferWhiteSpaceRule as L, AstPlugin as Lt, htmlBrToNewLine as M, withDeleteRules as Mt, htmlBodyToFragment as N, withBreakRules as Nt, htmlElementToElement as O, OverridePlugin as Ot, deserializeHtmlNodeChildren as P, BaseParagraphPlugin as Pt, withScrolling as Q, collapseWhiteSpaceChildren as R, getContainerTypes as Rt, htmlStringToDOMNode as S, isSlatePluginElement as St, htmlTextNodeToString as T, isSlateText as Tt, isLastNonEmptyTextOfInlineFormattingContext as U, getPluginTypes as Ut, endInlineFormattingContext as V, getPluginKeys as Vt, collapseString as W, getSlatePlugin as Wt, AUTO_SCROLL as X, isHtmlElement as Y, DOMPlugin as Z, withNodeId as _, getSlateElements as _t, pipeInsertDataQuery as a, isNodeAffinity as at, parseHtmlDocument as b, isSlateLeaf as bt, setValue as c, getEdgeNodes as ct, init as d, getPluginNodeProps as dt, PlateError as et, isEditOnly as f, getNodeDataAttributeKeys as ft, normalizeNodeId as g, defaultsDeepToNodes as gt, NodeIdPlugin as h, getInjectMatch as ht, ParserPlugin as i, setAffinitySelection as it, getDataNodeProps as j, withMergeRules as jt, pipeDeserializeHtmlElement as k, withOverrides as kt, resetBlock as l, mergeDeepToNodes as lt, pipeOnNodeChange as m, getInjectedPlugins as mt, withSlate as n, withChunking as nt, normalizeDescendantsToDocumentFragment as o, isNodesAffinity as ot, pipeOnTextChange as p, keyToDataAttribute as pt, inlineTagNames as q, createTSlatePlugin as qt, getCorePlugins as r, AffinityPlugin as rt, SlateExtensionPlugin as s, getMarkBoundaryAffinity as st, createSlateEditor as t, ChunkingPlugin as tt, insertExitBreak as u, getSlateClass as ut, LengthPlugin as v, isSlateEditor as vt, deserializeHtmlNode as w, isSlateString as wt, deserializeHtml as x, isSlateNode as xt, HtmlPlugin as y, isSlateElement as yt, collapseWhiteSpaceNode as z, getPluginByType as zt };
3660
+ export { isHtmlBlockElement as $, defineInputRule as $t, htmlStringToDOMNode as A, isSlatePluginElement as At, htmlBrToNewLine as B, withDeleteRules as Bt, resolveNavigationFeedbackTarget as C, getInjectMatch as Ct, HtmlPlugin as D, isSlateElement as Dt, LengthPlugin as E, isSlateEditor as Et, pipeDeserializeHtmlLeaf as F, applyDeepToNodes as Ft, inferWhiteSpaceRule as G, AstPlugin as Gt, deserializeHtmlNodeChildren as H, BaseParagraphPlugin as Ht, htmlElementToElement as I, OverridePlugin as It, collapseWhiteSpaceText as J, createMarkInputRule as Jt, collapseWhiteSpaceChildren as K, createBlockFenceInputRule as Kt, pipeDeserializeHtmlElement as L, withOverrides as Lt, deserializeHtmlNode as M, isSlateString as Mt, htmlTextNodeToString as N, isSlateText as Nt, parseHtmlDocument as O, isSlateLeaf as Ot, htmlElementToLeaf as P, isSlateVoid as Pt, collapseString as Q, matchDelimitedInline as Qt, pluginDeserializeHtml as R, withNormalizeRules as Rt, flashTarget as S, getInjectedPlugins as St, NavigationFeedbackPluginKey as T, getSlateElements as Tt, collapseWhiteSpace as U, HistoryPlugin as Ut, htmlBodyToFragment as V, withBreakRules as Vt, collapseWhiteSpaceElement as W, withPlateHistory as Wt, upsertInlineFormattingContext as X, matchBlockFence as Xt, endInlineFormattingContext as Y, createTextSubstitutionInputRule as Yt, isLastNonEmptyTextOfInlineFormattingContext as Z, matchBlockStart as Zt, normalizeNodeId as _, mergeDeepToNodes as _t, pipeInsertDataQuery as a, getPluginTypes as an, DOMPlugin as at, navigate as b, getNodeDataAttributeKeys as bt, setValue as c, createSlatePlugin as cn, PlateError as ct, insertExitBreak as d, AffinityPlugin as dt, getContainerTypes as en, isHtmlInlineElement as et, init as f, setAffinitySelection as ft, NodeIdPlugin as g, getEdgeNodes as gt, pipeOnNodeChange as h, getMarkBoundaryAffinity as ht, ParserPlugin as i, getPluginType as in, AUTO_SCROLL as it, deserializeHtmlElement as j, isSlatePluginNode as jt, deserializeHtml as k, isSlateNode as kt, resetBlock as l, createTSlatePlugin as ln, ChunkingPlugin as lt, pipeOnTextChange as m, isNodesAffinity as mt, withSlate as n, getPluginKey as nn, isHtmlText as nt, normalizeDescendantsToDocumentFragment as o, getSlatePlugin as on, withScrolling as ot, isEditOnly as p, isNodeAffinity as pt, collapseWhiteSpaceNode as q, createBlockStartInputRule as qt, getCorePlugins as r, getPluginKeys as rn, isHtmlElement as rt, SlateExtensionPlugin as s, getEditorPlugin as sn, DebugPlugin as st, createSlateEditor as t, getPluginByType as tn, inlineTagNames as tt, liftBlock as u, withChunking as ut, withNodeId as v, getSlateClass as vt, NAVIGATION_FEEDBACK_KEY as w, defaultsDeepToNodes as wt, clearNavigationFeedbackTarget as x, keyToDataAttribute as xt, NavigationFeedbackPlugin as y, getPluginNodeProps as yt, getDataNodeProps as z, withMergeRules as zt };