@lofcz/platejs-core 53.1.1 → 53.1.8

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,2 +1,2 @@
1
- import { $a as SlateHTMLProps, Ba as getSelectedDomNode, Co as SlateRenderLeaf, Do as SlateRenderElement, Eo as pipeRenderElementStatic, Ga as getPluginDataAttributes, Ha as getSelectedDomBlocks, Ia as stripSlateDataAttributes, Ja as ViewPlugin, Ka as createStaticString, La as stripHtmlClassNames, Oo as pluginRenderElementStatic, Qa as SlateElementProps, Ra as pipeDecorate, So as pluginRenderTextStatic, To as pluginRenderLeafStatic, Ua as getRenderNodeStaticProps, Va as getSelectedDomFragment, Wa as getNodeDataAttributes, Xa as getEditorDOMFromHtmlString, Ya as createStaticEditor, Za as SlateElement, _o as LeafStatic, ao as StyledSlateElementProps, bo as SlateRenderText, co as useNodeAttributes, do as SlateRenderLeafProps, eo as SlateLeaf, fo as SlateRenderNodeProps, go as ElementStatic, ho as serializeHtml, io as SlateTextProps, lo as BoxStaticProps, mo as SerializeHtmlOptions, no as SlateNodeProps, oo as StyledSlateLeafProps, po as SlateRenderTextProps, qa as getStaticPlugins, ro as SlateText, so as StyledSlateTextProps, to as SlateLeafProps, uo as SlateRenderElementProps, vo as PlateStatic, wo as pipeRenderLeafStatic, xo as pipeRenderTextStatic, yo as PlateStaticProps, za as isSelectOutside } from "../index-DRweaH9Q";
1
+ import { $a as SlateElement, Ao as pluginRenderElementStatic, Ba as pipeDecorate, Co as pipeRenderTextStatic, Do as pluginRenderLeafStatic, Eo as pipeRenderLeafStatic, Ga as getRenderNodeStaticProps, Ha as getSelectedDomNode, Ja as createStaticString, Ka as getNodeDataAttributes, Oo as pipeRenderElementStatic, Qa as getEditorDOMFromHtmlString, Ra as stripSlateDataAttributes, So as SlateRenderText, To as SlateRenderLeaf, Ua as getSelectedDomFragment, Va as isSelectOutside, Wa as getSelectedDomBlocks, Xa as ViewPlugin, Ya as getStaticPlugins, Za as createStaticEditor, _o as serializeHtml, ao as SlateText, bo as PlateStatic, co as StyledSlateLeafProps, do as BoxStaticProps, eo as SlateElementProps, fo as SlateRenderElementProps, go as SerializeHtmlOptions, ho as SlateRenderTextProps, io as SlateNodeProps, ko as SlateRenderElement, lo as StyledSlateTextProps, mo as SlateRenderNodeProps, no as SlateLeaf, oo as SlateTextProps, po as SlateRenderLeafProps, qa as getPluginDataAttributes, ro as SlateLeafProps, so as StyledSlateElementProps, to as SlateHTMLProps, uo as useNodeAttributes, vo as ElementStatic, wo as pluginRenderTextStatic, xo as PlateStaticProps, yo as LeafStatic, za as stripHtmlClassNames } from "../index-BDSzA1zP";
2
2
  export { BoxStaticProps, ElementStatic, LeafStatic, PlateStatic, PlateStaticProps, SerializeHtmlOptions, SlateElement, SlateElementProps, SlateHTMLProps, SlateLeaf, SlateLeafProps, SlateNodeProps, SlateRenderElement, SlateRenderElementProps, SlateRenderLeaf, SlateRenderLeafProps, SlateRenderNodeProps, SlateRenderText, SlateRenderTextProps, SlateText, SlateTextProps, StyledSlateElementProps, StyledSlateLeafProps, StyledSlateTextProps, ViewPlugin, createStaticEditor, createStaticString, getEditorDOMFromHtmlString, getNodeDataAttributes, getPluginDataAttributes, getRenderNodeStaticProps, getSelectedDomBlocks, getSelectedDomFragment, getSelectedDomNode, getStaticPlugins, isSelectOutside, pipeDecorate, pipeRenderElementStatic, pipeRenderLeafStatic, pipeRenderTextStatic, pluginRenderElementStatic, pluginRenderLeafStatic, pluginRenderTextStatic, serializeHtml, stripHtmlClassNames, stripSlateDataAttributes, useNodeAttributes };
@@ -1,4 +1,4 @@
1
- import "../withSlate-DsAgt7dN.js";
2
- import { C as createStaticEditor, D as getSelectedDomNode, E as isSelectOutside, O as getSelectedDomFragment, S as useNodeAttributes, T as ViewPlugin, _ as getPluginDataAttributes, a as ElementStatic, b as SlateLeaf, c as pipeRenderTextStatic, d as pluginRenderElementStatic, f as stripSlateDataAttributes, g as getNodeDataAttributes, h as getRenderNodeStaticProps, i as pluginRenderLeafStatic, k as pipeDecorate, l as pluginRenderTextStatic, m as getSelectedDomBlocks, n as serializeHtml, o as LeafStatic, p as stripHtmlClassNames, r as pipeRenderLeafStatic, s as PlateStatic, t as getEditorDOMFromHtmlString, u as pipeRenderElementStatic, v as createStaticString, w as getStaticPlugins, x as SlateText, y as SlateElement } from "../static-HrbPXDQ1.js";
1
+ import "../withSlate-NVP0S9vL.js";
2
+ import { C as createStaticEditor, D as getSelectedDomNode, E as isSelectOutside, O as getSelectedDomFragment, S as useNodeAttributes, T as ViewPlugin, _ as getPluginDataAttributes, a as ElementStatic, b as SlateLeaf, c as pipeRenderTextStatic, d as pluginRenderElementStatic, f as stripSlateDataAttributes, g as getNodeDataAttributes, h as getRenderNodeStaticProps, i as pluginRenderLeafStatic, k as pipeDecorate, l as pluginRenderTextStatic, m as getSelectedDomBlocks, n as serializeHtml, o as LeafStatic, p as stripHtmlClassNames, r as pipeRenderLeafStatic, s as PlateStatic, t as getEditorDOMFromHtmlString, u as pipeRenderElementStatic, v as createStaticString, w as getStaticPlugins, x as SlateText, y as SlateElement } from "../static-KMalmfNw.js";
3
3
 
4
4
  export { ElementStatic, LeafStatic, PlateStatic, SlateElement, SlateLeaf, SlateText, ViewPlugin, createStaticEditor, createStaticString, getEditorDOMFromHtmlString, getNodeDataAttributes, getPluginDataAttributes, getRenderNodeStaticProps, getSelectedDomBlocks, getSelectedDomFragment, getSelectedDomNode, getStaticPlugins, isSelectOutside, pipeDecorate, pipeRenderElementStatic, pipeRenderLeafStatic, pipeRenderTextStatic, pluginRenderElementStatic, pluginRenderLeafStatic, pluginRenderTextStatic, serializeHtml, stripHtmlClassNames, stripSlateDataAttributes, useNodeAttributes };
@@ -1,4 +1,4 @@
1
- import { Ct as getInjectMatch, at as DOMPlugin, n as withSlate, p as isEditOnly, sn as getEditorPlugin, tn as getPluginByType, vt as getSlateClass, xt as keyToDataAttribute, yt as getPluginNodeProps } from "./withSlate-DsAgt7dN.js";
1
+ import { St as keyToDataAttribute, bt as getPluginNodeProps, n as withSlate, ot as DOMPlugin, p as isEditOnly, sn as getEditorPlugin, tn as getPluginByType, wt as getInjectMatch, yt as getSlateClass } from "./withSlate-NVP0S9vL.js";
2
2
  import { ElementApi, NodeApi, RangeApi, TextApi, createEditor, isElementDecorationsEqual, isTextDecorationsEqual } from "@platejs/slate";
3
3
  import { isDefined } from "@udecode/utils";
4
4
  import React from "react";
@@ -14,13 +14,13 @@ import { decode } from "html-entities";
14
14
  * `[styleKey]: value`.
15
15
  */
16
16
  const pluginInjectNodeProps = (editor, plugin, nodeProps, getElementPath) => {
17
- const { key, inject: { nodeProps: injectNodeProps } } = plugin;
17
+ const { key, inject: { excludeBelowPlugins, maxLevel, nodeProps: injectNodeProps } } = plugin;
18
18
  const { element, text } = nodeProps;
19
19
  const node = element ?? text;
20
20
  if (!node) return;
21
21
  if (!injectNodeProps) return;
22
22
  const { classNames, defaultNodeValue, nodeKey = editor.getType(key), query, styleKey = nodeKey, transformClassName, transformNodeValue, transformProps, transformStyle, validNodeValues } = injectNodeProps;
23
- if (!getInjectMatch(editor, plugin)(node, !!(plugin.inject.excludeBelowPlugins || plugin.inject.maxLevel) ? getElementPath(node) : void 0)) return;
23
+ if (!getInjectMatch(editor, plugin)(node, !!(excludeBelowPlugins || maxLevel) ? getElementPath(node) : void 0)) return;
24
24
  const queryResult = query?.({
25
25
  ...injectNodeProps,
26
26
  ...getEditorPlugin(editor, plugin),
@@ -4,7 +4,6 @@ import { bindFirst, isDefined } from "@udecode/utils";
4
4
  import merge from "lodash/merge.js";
5
5
  import { createVanillaStore } from "zustand-x/vanilla";
6
6
  import mergeWith from "lodash/mergeWith.js";
7
- import defaults from "lodash/defaults.js";
8
7
  import kebabCase from "lodash/kebabCase.js";
9
8
  import pick from "lodash/pick.js";
10
9
  import castArray from "lodash/castArray.js";
@@ -308,11 +307,10 @@ const resolvePlugin = (editor, _plugin) => {
308
307
  let plugin = mergePlugins({}, _plugin);
309
308
  plugin.__resolved = true;
310
309
  if (plugin.__configuration) {
311
- const configResult = plugin.__configuration(getEditorPlugin(editor, plugin));
312
- if (configResult.inputRules !== void 0) {
313
- const normalizedInputRules = normalizeConfiguredInputRules(configResult.inputRules);
310
+ const { inputRules: configInputRules, ...configResult } = plugin.__configuration(getEditorPlugin(editor, plugin));
311
+ if (configInputRules !== void 0) {
312
+ const normalizedInputRules = normalizeConfiguredInputRules(configInputRules);
314
313
  plugin.__configuredInputRules = [...normalizeConfiguredInputRules(plugin.__configuredInputRules), ...normalizedInputRules];
315
- configResult.inputRules = void 0;
316
314
  }
317
315
  plugin = mergePlugins(plugin, configResult);
318
316
  plugin.__configuration = void 0;
@@ -629,12 +627,10 @@ const matchDelimitedInline = (context, { boundaryRe, close, followRe, open, requ
629
627
  };
630
628
  };
631
629
  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)];
630
+ const triggers = trigger ? Array.isArray(trigger) ? [...trigger] : [trigger] : [match.slice(-1)];
635
631
  return {
636
- end: trigger ? reversed : reversed.slice(0, -1),
637
- start,
632
+ end: trigger ? match : match.slice(0, -1),
633
+ start: match,
638
634
  triggers
639
635
  };
640
636
  };
@@ -668,33 +664,46 @@ const getTextSubstitutionMatchPoints = (editor, { end, start }) => {
668
664
  beforeStartMatchPoint
669
665
  };
670
666
  };
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 }) => {
667
+ const compilePatternsByTrigger = (patterns) => {
668
+ const byTrigger = /* @__PURE__ */ new Map();
678
669
  for (const pattern of patterns) {
679
- const matches = Array.isArray(pattern.match) ? [...pattern.match] : [pattern.match];
670
+ const matches = Array.isArray(pattern.match) ? pattern.match : [pattern.match];
671
+ const isPaired = Array.isArray(pattern.format);
680
672
  for (const match of matches) {
681
673
  const { end, start, triggers } = getTextSubstitutionMatchRange({
682
674
  match,
683
675
  trigger: pattern.trigger
684
676
  });
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,
677
+ const compiled = {
678
+ end: isPaired ? "" : end,
693
679
  pattern,
694
- points
680
+ start: isPaired ? start : ""
695
681
  };
682
+ for (const trigger of triggers) {
683
+ let list = byTrigger.get(trigger);
684
+ if (!list) {
685
+ list = [];
686
+ byTrigger.set(trigger, list);
687
+ }
688
+ list.push(compiled);
689
+ }
696
690
  }
697
691
  }
692
+ return byTrigger;
693
+ };
694
+ const resolveTextSubstitution = ({ candidates, editor }) => {
695
+ for (const { end, pattern, start } of candidates) {
696
+ const points = getTextSubstitutionMatchPoints(editor, {
697
+ end,
698
+ start
699
+ });
700
+ if (!points) continue;
701
+ return {
702
+ end,
703
+ pattern,
704
+ points
705
+ };
706
+ }
698
707
  };
699
708
  const applyTextSubstitution = (editor, match) => {
700
709
  const selection = editor.selection;
@@ -715,21 +724,25 @@ const applyTextSubstitution = (editor, match) => {
715
724
  }
716
725
  return true;
717
726
  };
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
- });
727
+ const createTextSubstitutionInputRule = ({ enabled, patterns, priority }) => {
728
+ const patternsByTrigger = compilePatternsByTrigger(patterns);
729
+ return defineInputRule({
730
+ enabled,
731
+ priority,
732
+ target: "insertText",
733
+ trigger: Array.from(patternsByTrigger.keys()),
734
+ resolve: ({ editor, text }) => {
735
+ if (!editor.selection || !editor.api.isCollapsed()) return;
736
+ const candidates = patternsByTrigger.get(text);
737
+ if (!candidates) return;
738
+ return resolveTextSubstitution({
739
+ candidates,
740
+ editor
741
+ });
742
+ },
743
+ apply: ({ editor }, match) => applyTextSubstitution(editor, match)
744
+ });
745
+ };
733
746
 
734
747
  //#endregion
735
748
  //#region src/lib/plugins/input-rules/internal/createInputRuleBuilder.ts
@@ -774,7 +787,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
774
787
  textProps: [],
775
788
  types: {}
776
789
  },
777
- normalizeInitialValue: [],
790
+ transformInitialValue: [],
778
791
  render: {
779
792
  aboveEditable: [],
780
793
  aboveNodes: [],
@@ -815,7 +828,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
815
828
  if (plugin.render.afterContainer) editor.meta.pluginCache.render.afterContainer.push(plugin.key);
816
829
  if (plugin.render.beforeContainer) editor.meta.pluginCache.render.beforeContainer.push(plugin.key);
817
830
  if (plugin.render.belowRootNodes) editor.meta.pluginCache.render.belowRootNodes.push(plugin.key);
818
- if (plugin.normalizeInitialValue) editor.meta.pluginCache.normalizeInitialValue.push(plugin.key);
831
+ if (plugin.transformInitialValue || plugin.normalizeInitialValue) editor.meta.pluginCache.transformInitialValue.push(plugin.key);
819
832
  if (plugin.decorate) editor.meta.pluginCache.decorate.push(plugin.key);
820
833
  if (plugin.render.aboveNodes) editor.meta.pluginCache.render.aboveNodes.push(plugin.key);
821
834
  if (plugin.render.belowNodes) editor.meta.pluginCache.render.belowNodes.push(plugin.key);
@@ -1442,16 +1455,6 @@ const isSlatePluginElement = (element, pluginKey) => element.dataset.slateNode =
1442
1455
  const isSlatePluginNode = (element, pluginKey) => element.classList.contains(`slate-${pluginKey}`);
1443
1456
  const getSlateElements = (element) => Array.from(element.querySelectorAll("[data-slate-node=\"element\"]"));
1444
1457
 
1445
- //#endregion
1446
- //#region src/lib/utils/defaultsDeepToNodes.ts
1447
- /** Recursively merge a source object to children nodes with a query. */
1448
- const defaultsDeepToNodes = (options) => {
1449
- applyDeepToNodes({
1450
- ...options,
1451
- apply: defaults
1452
- });
1453
- };
1454
-
1455
1458
  //#endregion
1456
1459
  //#region src/lib/utils/getInjectMatch.ts
1457
1460
  const getInjectMatch = (editor, plugin) => {
@@ -2718,6 +2721,7 @@ const NavigationFeedbackPlugin = createTSlatePlugin({
2718
2721
 
2719
2722
  //#endregion
2720
2723
  //#region src/lib/plugins/node-id/withNodeId.ts
2724
+ const now = () => globalThis.performance?.now() ?? Date.now();
2721
2725
  /** Enables support for inserting nodes with an id key. */
2722
2726
  const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes } }) => {
2723
2727
  const idPropsCreator = () => ({ [getOptions().idKey ?? ""]: getOptions().idCreator() });
@@ -2725,23 +2729,42 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2725
2729
  const { filter, filterText } = getOptions();
2726
2730
  return filter(nodeEntry) && (!filterText || nodeEntry[0]?.type !== void 0);
2727
2731
  };
2728
- const removeIdFromNodeIfDuplicate = (node) => {
2729
- const { idKey = "", reuseId } = getOptions();
2730
- if (!reuseId && editor.api.some({
2731
- at: [],
2732
- match: { [idKey]: node[idKey] }
2733
- })) delete node[idKey];
2732
+ const collectDuplicateCandidateIds = ({ disableInsertOverrides, idKey, nodeEntry, query }) => {
2733
+ const duplicateCandidateIds = /* @__PURE__ */ new Set();
2734
+ const collectNodeIds = (entry) => {
2735
+ const [entryNode, path] = entry;
2736
+ if (queryNode(entry, query)) {
2737
+ if (entryNode[idKey] !== void 0) duplicateCandidateIds.add(entryNode[idKey]);
2738
+ if (!disableInsertOverrides && isDefined(entryNode._id)) duplicateCandidateIds.add(entryNode._id);
2739
+ }
2740
+ const children = entryNode.children;
2741
+ if (!children) return;
2742
+ children.forEach((child, index) => {
2743
+ collectNodeIds([child, [...path, index]]);
2744
+ });
2745
+ };
2746
+ collectNodeIds(nodeEntry);
2747
+ return duplicateCandidateIds;
2734
2748
  };
2735
- const overrideIdIfSet = (node) => {
2736
- const { idKey = "" } = getOptions();
2737
- if (isDefined(node._id)) {
2738
- const id = node._id;
2739
- node._id = void 0;
2740
- if (!editor.api.some({
2741
- at: [],
2742
- match: { [idKey]: id }
2743
- })) node[idKey] = id;
2749
+ const collectExistingEditorIds = ({ duplicateCandidateIds, idKey }) => {
2750
+ if (duplicateCandidateIds.size === 0) return /* @__PURE__ */ new Set();
2751
+ const existingIds = /* @__PURE__ */ new Set();
2752
+ const start = now();
2753
+ let visitedCount = 0;
2754
+ for (const [entryNode] of NodeApi.nodes(editor)) {
2755
+ visitedCount += 1;
2756
+ const id = entryNode?.[idKey];
2757
+ if (id === void 0 || !duplicateCandidateIds.has(id)) continue;
2758
+ existingIds.add(id);
2759
+ if (existingIds.size === duplicateCandidateIds.size) break;
2744
2760
  }
2761
+ getOptions().onDuplicateIdScan?.({
2762
+ candidateCount: duplicateCandidateIds.size,
2763
+ duration: now() - start,
2764
+ existingCount: existingIds.size,
2765
+ visitedCount
2766
+ });
2767
+ return existingIds;
2745
2768
  };
2746
2769
  return { transforms: {
2747
2770
  apply(operation) {
@@ -2753,24 +2776,34 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2753
2776
  };
2754
2777
  if (operation.type === "insert_node") {
2755
2778
  const node = cloneDeep(operation.node);
2756
- applyDeepToNodes({
2757
- apply: removeIdFromNodeIfDuplicate,
2758
- node,
2759
- query,
2760
- source: {}
2761
- });
2762
- defaultsDeepToNodes({
2763
- node,
2764
- path: operation.path,
2765
- query,
2766
- source: idPropsCreator
2767
- });
2768
- if (!disableInsertOverrides) applyDeepToNodes({
2769
- apply: overrideIdIfSet,
2770
- node,
2771
- query,
2772
- source: {}
2779
+ const existingIds = collectExistingEditorIds({
2780
+ duplicateCandidateIds: collectDuplicateCandidateIds({
2781
+ disableInsertOverrides,
2782
+ idKey,
2783
+ nodeEntry: [node, operation.path],
2784
+ query
2785
+ }),
2786
+ idKey
2773
2787
  });
2788
+ const hasIdInEditor = (id) => existingIds.has(id);
2789
+ const normalizeInsertedNode = (nodeEntry) => {
2790
+ const [entryNode, path] = nodeEntry;
2791
+ if (queryNode(nodeEntry, query)) {
2792
+ if (entryNode[idKey] !== void 0 && hasIdInEditor(entryNode[idKey])) delete entryNode[idKey];
2793
+ if (entryNode[idKey] === void 0) Object.assign(entryNode, idPropsCreator());
2794
+ if (!disableInsertOverrides && isDefined(entryNode._id)) {
2795
+ const id = entryNode._id;
2796
+ entryNode._id = void 0;
2797
+ if (!hasIdInEditor(id)) entryNode[idKey] = id;
2798
+ }
2799
+ }
2800
+ const children = entryNode.children;
2801
+ if (!children) return;
2802
+ children.forEach((child, index) => {
2803
+ normalizeInsertedNode([child, [...path, index]]);
2804
+ });
2805
+ };
2806
+ normalizeInsertedNode([node, operation.path]);
2774
2807
  return apply({
2775
2808
  ...operation,
2776
2809
  node
@@ -2827,29 +2860,103 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2827
2860
 
2828
2861
  //#endregion
2829
2862
  //#region src/lib/plugins/node-id/NodeIdPlugin.ts
2863
+ const defaultNodeIdFilter = () => true;
2864
+ const isDefaultNodeIdFastPath = ({ allow, exclude, filter, filterInline = true, filterText = true }) => allow === void 0 && exclude === void 0 && (filter === void 0 || filter === defaultNodeIdFilter) && filterInline && filterText;
2865
+ const isBlockCandidate = (node, isBlock) => ElementApi.isElement(node) && (isBlock ? isBlock(node) : node.inline !== true);
2866
+ const shouldAssignNodeId = (entry, options = {}) => {
2867
+ const { allow, exclude, filter = () => true, filterInline = true, filterText = true, isBlock, idKey = "id" } = options;
2868
+ const [node, path] = entry;
2869
+ return !node[idKey] && queryNode([node, path], {
2870
+ allow,
2871
+ exclude,
2872
+ filter: (nextEntry) => {
2873
+ const [entryNode] = nextEntry;
2874
+ if (filterText && !ElementApi.isElement(entryNode)) return false;
2875
+ if (filterInline && ElementApi.isElement(entryNode) && !(isBlock ? isBlock(entryNode) : entryNode.inline !== true)) return false;
2876
+ return filter(nextEntry);
2877
+ }
2878
+ });
2879
+ };
2880
+ const resolveInitialValueIds = (options) => {
2881
+ if (options.initialValueIds !== void 0) return options.initialValueIds;
2882
+ if (options.normalizeInitialValue === null) return false;
2883
+ if (options.normalizeInitialValue === true) return "always";
2884
+ return "if-needed";
2885
+ };
2830
2886
  /**
2831
2887
  * Normalize node IDs in a value without using editor operations. This is a pure
2832
- * function that returns a new normalized value.
2888
+ * function that returns the normalized value and preserves references for
2889
+ * unchanged branches.
2833
2890
  */
2834
- const normalizeNodeId = (value, options = {}) => {
2835
- const { allow, exclude, filter = () => true, filterInline = true, filterText = true, idCreator = () => nanoid(10), idKey = "id" } = options;
2891
+ const normalizeNodeIdRuntime = (value, options = {}) => {
2892
+ const { idCreator = () => nanoid(10), idKey = "id" } = options;
2893
+ if (isDefaultNodeIdFastPath(options)) {
2894
+ const normalizeNodeFast = (node) => {
2895
+ if (!ElementApi.isElement(node)) return node;
2896
+ if (!isBlockCandidate(node, options.isBlock)) return node;
2897
+ let nextChildren;
2898
+ node.children.forEach((child, index) => {
2899
+ const nextChild = normalizeNodeFast(child);
2900
+ if (nextChild !== child) {
2901
+ if (!nextChildren) nextChildren = [...node.children];
2902
+ nextChildren[index] = nextChild;
2903
+ }
2904
+ });
2905
+ if (!node[idKey]) return {
2906
+ ...node,
2907
+ ...nextChildren ? { children: nextChildren } : {},
2908
+ [idKey]: idCreator()
2909
+ };
2910
+ if (nextChildren) return {
2911
+ ...node,
2912
+ children: nextChildren
2913
+ };
2914
+ return node;
2915
+ };
2916
+ let valueChanged$1 = false;
2917
+ const nextValue$1 = value.map((node) => {
2918
+ const nextNode = normalizeNodeFast(node);
2919
+ if (nextNode !== node) valueChanged$1 = true;
2920
+ return nextNode;
2921
+ });
2922
+ return valueChanged$1 ? nextValue$1 : value;
2923
+ }
2836
2924
  const normalizeNode = (node, path) => {
2837
- const clonedNode = { ...node };
2838
- if (!clonedNode[idKey] && queryNode([clonedNode, path], {
2839
- allow,
2840
- exclude,
2841
- filter: (entry) => {
2842
- const [node$1] = entry;
2843
- if (filterText && !ElementApi.isElement(node$1)) return false;
2844
- if (filterInline && ElementApi.isElement(node$1) && node$1.inline === true) return false;
2845
- return filter(entry);
2846
- }
2847
- })) clonedNode[idKey] = idCreator();
2848
- if (ElementApi.isElement(clonedNode)) clonedNode.children = clonedNode.children.map((child, index) => normalizeNode(child, [...path, index]));
2849
- return clonedNode;
2925
+ let nextNode = node;
2926
+ let childrenChanged = false;
2927
+ if (shouldAssignNodeId([node, path], options)) nextNode = {
2928
+ ...node,
2929
+ [idKey]: idCreator()
2930
+ };
2931
+ if (ElementApi.isElement(node)) {
2932
+ const nextChildren = node.children.map((child, index) => {
2933
+ const nextChild = normalizeNode(child, [...path, index]);
2934
+ if (nextChild !== child) childrenChanged = true;
2935
+ return nextChild;
2936
+ });
2937
+ if (childrenChanged) nextNode = nextNode === node ? {
2938
+ ...node,
2939
+ children: nextChildren
2940
+ } : {
2941
+ ...nextNode,
2942
+ children: nextChildren
2943
+ };
2944
+ }
2945
+ return nextNode;
2850
2946
  };
2851
- return value.map((node, index) => normalizeNode(node, [index]));
2947
+ let valueChanged = false;
2948
+ const nextValue = value.map((node, index) => {
2949
+ const nextNode = normalizeNode(node, [index]);
2950
+ if (nextNode !== node) valueChanged = true;
2951
+ return nextNode;
2952
+ });
2953
+ return valueChanged ? nextValue : value;
2852
2954
  };
2955
+ const normalizeNodeId = (value, options = {}) => normalizeNodeIdRuntime(value, options);
2956
+ const normalizeNodeIdWithEditor = (editor, value, options = {}) => normalizeNodeIdRuntime(value, {
2957
+ ...options,
2958
+ isBlock: editor.api.isBlock
2959
+ });
2853
2960
  /** @see {@link withNodeId} */
2854
2961
  const NodeIdPlugin = createTSlatePlugin({
2855
2962
  key: "nodeId",
@@ -2857,29 +2964,51 @@ const NodeIdPlugin = createTSlatePlugin({
2857
2964
  filterInline: true,
2858
2965
  filterText: true,
2859
2966
  idKey: "id",
2860
- normalizeInitialValue: false,
2861
- filter: () => true,
2967
+ filter: defaultNodeIdFilter,
2862
2968
  idCreator: () => nanoid(10)
2863
2969
  }
2864
2970
  }).extendTransforms(({ editor, getOptions }) => ({ normalize() {
2865
- const { allow, exclude, filter, filterInline, filterText, idKey } = getOptions();
2971
+ const options = getOptions();
2972
+ const { idCreator, idKey } = options;
2973
+ const updates = [];
2974
+ if (isDefaultNodeIdFastPath({
2975
+ ...options,
2976
+ isBlock: editor.api.isBlock
2977
+ })) {
2978
+ const path = [];
2979
+ const visitFast = (node) => {
2980
+ if (!ElementApi.isElement(node)) return;
2981
+ if (!isBlockCandidate(node, editor.api.isBlock)) return;
2982
+ if (!node[idKey]) updates.push({
2983
+ at: [...path],
2984
+ props: { [idKey]: idCreator() }
2985
+ });
2986
+ node.children.forEach((child, index) => {
2987
+ path.push(index);
2988
+ visitFast(child);
2989
+ path.pop();
2990
+ });
2991
+ };
2992
+ editor.children.forEach((node, index) => {
2993
+ path.push(index);
2994
+ visitFast(node);
2995
+ path.pop();
2996
+ });
2997
+ if (updates.length === 0) return;
2998
+ editor.tf.withoutSaving(() => {
2999
+ editor.tf.setNodesBatch(updates);
3000
+ });
3001
+ return;
3002
+ }
2866
3003
  const addNodeId = (entry) => {
2867
3004
  const [node, path] = entry;
2868
- if (!node[idKey] && queryNode([node, path], {
2869
- allow,
2870
- exclude,
2871
- filter: (entry$1) => {
2872
- const [node$1] = entry$1;
2873
- if (filterText && !ElementApi.isElement(node$1)) return false;
2874
- if (filterInline && ElementApi.isElement(node$1) && !editor.api.isBlock(node$1)) return false;
2875
- return filter(entry$1);
2876
- }
2877
- })) {
2878
- if (!editor.api.node(path)) return;
2879
- editor.tf.withoutSaving(() => {
2880
- editor.tf.setNodes({ [idKey]: getOptions().idCreator() }, { at: path });
2881
- });
2882
- }
3005
+ if (shouldAssignNodeId(entry, {
3006
+ ...options,
3007
+ isBlock: editor.api.isBlock
3008
+ })) updates.push({
3009
+ at: path,
3010
+ props: { [idKey]: idCreator() }
3011
+ });
2883
3012
  if (ElementApi.isElement(node)) node.children.forEach((child, index) => {
2884
3013
  addNodeId([child, [...path, index]]);
2885
3014
  });
@@ -2887,14 +3016,21 @@ const NodeIdPlugin = createTSlatePlugin({
2887
3016
  editor.children.forEach((node, index) => {
2888
3017
  addNodeId([node, [index]]);
2889
3018
  });
2890
- } })).extend({ normalizeInitialValue: ({ editor, getOptions, tf }) => {
2891
- const { normalizeInitialValue } = getOptions();
2892
- if (!normalizeInitialValue) {
2893
- const firstNode = editor.children[0];
2894
- const lastNode = editor.children.at(-1);
2895
- if (firstNode?.id && lastNode?.id) return;
3019
+ if (updates.length === 0) return;
3020
+ editor.tf.withoutSaving(() => {
3021
+ editor.tf.setNodesBatch(updates);
3022
+ });
3023
+ } })).extend({ transformInitialValue: ({ editor, getOptions, value }) => {
3024
+ const options = getOptions();
3025
+ const { idKey = "id" } = options;
3026
+ const initialValueIds = resolveInitialValueIds(options);
3027
+ if (initialValueIds === false) return value;
3028
+ if (initialValueIds === "if-needed") {
3029
+ const firstNode = value[0];
3030
+ const lastNode = value.at(-1);
3031
+ if (firstNode?.[idKey] && lastNode?.[idKey]) return value;
2896
3032
  }
2897
- tf.nodeId.normalize();
3033
+ return normalizeNodeIdWithEditor(editor, value, options);
2898
3034
  } }).overrideEditor(withNodeId);
2899
3035
 
2900
3036
  //#endregion
@@ -2943,7 +3079,7 @@ const pipeOnTextChange = (editor, node, text, prevText, operation) => {
2943
3079
  const DEFAULT = {
2944
3080
  handlers: true,
2945
3081
  inject: true,
2946
- normalizeInitialValue: false,
3082
+ transformInitialValue: false,
2947
3083
  render: true
2948
3084
  };
2949
3085
  /**
@@ -2953,29 +3089,43 @@ const DEFAULT = {
2953
3089
  * @param plugin The plugin to check
2954
3090
  * @param isReadOnly Whether the editor is in read-only mode
2955
3091
  * @param feature The feature to check ('render' | 'handlers' | 'inject' |
2956
- * 'normalizeInitialValue')
3092
+ * 'transformInitialValue')
2957
3093
  * @returns True if the feature should be disabled
2958
3094
  */
2959
3095
  const isEditOnly = (readOnly, plugin, feature) => {
2960
3096
  if (!readOnly) return false;
2961
- if (plugin.editOnly === true) return DEFAULT[feature];
2962
- if (typeof plugin.editOnly === "object") return plugin.editOnly[feature] ?? DEFAULT[feature];
3097
+ const resolvedFeature = feature === "normalizeInitialValue" ? "transformInitialValue" : feature;
3098
+ if (plugin.editOnly === true) return DEFAULT[resolvedFeature];
3099
+ if (typeof plugin.editOnly === "object") {
3100
+ if (plugin.editOnly[resolvedFeature] !== void 0) return plugin.editOnly[resolvedFeature];
3101
+ if (resolvedFeature === "transformInitialValue") return plugin.editOnly.normalizeInitialValue ?? DEFAULT.transformInitialValue;
3102
+ return DEFAULT[resolvedFeature];
3103
+ }
2963
3104
  return false;
2964
3105
  };
2965
3106
 
2966
3107
  //#endregion
2967
- //#region src/internal/plugin/pipeNormalizeInitialValue.ts
2968
- /** Normalize initial value from editor plugins. Set into plate store if diff. */
2969
- const pipeNormalizeInitialValue = (editor) => {
3108
+ //#region src/internal/plugin/pipeTransformInitialValue.ts
3109
+ /** Transform initial value from editor plugins before the editor is ready. */
3110
+ const pipeTransformInitialValue = (editor) => {
2970
3111
  const value = editor.meta.isNormalizing;
2971
3112
  editor.meta.isNormalizing = true;
2972
- editor.meta.pluginCache.normalizeInitialValue.forEach((key) => {
3113
+ editor.meta.pluginCache.transformInitialValue.forEach((key) => {
2973
3114
  const p = editor.getPlugin({ key });
2974
- if (isEditOnly(editor.dom.readOnly, p, "normalizeInitialValue")) return;
2975
- p.normalizeInitialValue?.({
3115
+ if (isEditOnly(editor.dom.readOnly, p, "transformInitialValue")) return;
3116
+ if (!p.transformInitialValue && !p.normalizeInitialValue) return;
3117
+ const ctx = {
2976
3118
  ...getEditorPlugin(editor, p),
2977
3119
  value: editor.children
2978
- });
3120
+ };
3121
+ if (p.transformInitialValue) {
3122
+ const nextValue$1 = p.transformInitialValue(ctx);
3123
+ if (nextValue$1 === void 0) throw new Error(`Plugin "${key}" transformInitialValue must return the next value.`);
3124
+ editor.children = nextValue$1;
3125
+ return;
3126
+ }
3127
+ const nextValue = p.normalizeInitialValue?.(ctx);
3128
+ if (nextValue !== void 0) editor.children = nextValue;
2979
3129
  });
2980
3130
  editor.meta.isNormalizing = value;
2981
3131
  };
@@ -2990,7 +3140,7 @@ const init = (editor, { autoSelect, selection, shouldNormalizeEditor, value, onR
2990
3140
  const target = (autoSelect === "start" ? "start" : "end") === "start" ? editor.api.start([]) : editor.api.end([]);
2991
3141
  editor.tf.select(target);
2992
3142
  }
2993
- if (editor.children.length > 0) pipeNormalizeInitialValue(editor);
3143
+ if (editor.children.length > 0) pipeTransformInitialValue(editor);
2994
3144
  if (shouldNormalizeEditor) editor.tf.normalize({ force: true });
2995
3145
  if (onReady) onReady({
2996
3146
  editor,
@@ -3657,4 +3807,4 @@ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLen
3657
3807
  const createSlateEditor = ({ editor = createEditor(), ...options } = {}) => withSlate(editor, options);
3658
3808
 
3659
3809
  //#endregion
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 };
3810
+ export { collapseString as $, defineInputRule as $t, deserializeHtml as A, isSlatePluginElement as At, getDataNodeProps as B, withDeleteRules as Bt, flashTarget as C, getInjectedPlugins as Ct, LengthPlugin as D, isSlateElement as Dt, NavigationFeedbackPluginKey as E, isSlateEditor as Et, htmlElementToLeaf as F, applyDeepToNodes as Ft, collapseWhiteSpaceElement as G, AstPlugin as Gt, htmlBodyToFragment as H, BaseParagraphPlugin as Ht, pipeDeserializeHtmlLeaf as I, OverridePlugin as It, collapseWhiteSpaceNode as J, createMarkInputRule as Jt, inferWhiteSpaceRule as K, createBlockFenceInputRule as Kt, htmlElementToElement as L, withOverrides as Lt, deserializeHtmlElement as M, isSlateString as Mt, deserializeHtmlNode as N, isSlateText as Nt, HtmlPlugin as O, isSlateLeaf as Ot, htmlTextNodeToString as P, isSlateVoid as Pt, isLastNonEmptyTextOfInlineFormattingContext as Q, matchDelimitedInline as Qt, pipeDeserializeHtmlElement as R, withNormalizeRules as Rt, clearNavigationFeedbackTarget as S, keyToDataAttribute as St, NAVIGATION_FEEDBACK_KEY as T, getSlateElements as Tt, deserializeHtmlNodeChildren as U, HistoryPlugin as Ut, htmlBrToNewLine as V, withBreakRules as Vt, collapseWhiteSpace as W, withPlateHistory as Wt, endInlineFormattingContext as X, matchBlockFence as Xt, collapseWhiteSpaceText as Y, createTextSubstitutionInputRule as Yt, upsertInlineFormattingContext as Z, matchBlockStart as Zt, normalizeNodeId as _, getEdgeNodes as _t, pipeInsertDataQuery as a, getPluginTypes as an, AUTO_SCROLL as at, NavigationFeedbackPlugin as b, getPluginNodeProps as bt, setValue as c, createSlatePlugin as cn, DebugPlugin as ct, insertExitBreak as d, withChunking as dt, getContainerTypes as en, isHtmlBlockElement as et, init as f, AffinityPlugin as ft, NodeIdPlugin as g, getMarkBoundaryAffinity as gt, pipeOnNodeChange as h, isNodesAffinity as ht, ParserPlugin as i, getPluginType as in, isHtmlElement as it, htmlStringToDOMNode as j, isSlatePluginNode as jt, parseHtmlDocument as k, isSlateNode as kt, resetBlock as l, createTSlatePlugin as ln, PlateError as lt, pipeOnTextChange as m, isNodeAffinity as mt, withSlate as n, getPluginKey as nn, inlineTagNames as nt, normalizeDescendantsToDocumentFragment as o, getSlatePlugin as on, DOMPlugin as ot, isEditOnly as p, setAffinitySelection as pt, collapseWhiteSpaceChildren as q, createBlockStartInputRule as qt, getCorePlugins as r, getPluginKeys as rn, isHtmlText as rt, SlateExtensionPlugin as s, getEditorPlugin as sn, withScrolling as st, createSlateEditor as t, getPluginByType as tn, isHtmlInlineElement as tt, liftBlock as u, ChunkingPlugin as ut, normalizeNodeIdWithEditor as v, mergeDeepToNodes as vt, resolveNavigationFeedbackTarget as w, getInjectMatch as wt, navigate as x, getNodeDataAttributeKeys as xt, withNodeId as y, getSlateClass as yt, pluginDeserializeHtml as z, withMergeRules as zt };