@mhamz.01/easyflow-texteditor 0.1.151 → 0.1.152

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.
package/dist/index.mjs CHANGED
@@ -2876,37 +2876,98 @@ function useTiptapEditor(providedEditor) {
2876
2876
 
2877
2877
  // src/hooks/mark-preservers/mark-preserver.ts
2878
2878
  var MarkPreserver = class {
2879
- static preservedMarks = /* @__PURE__ */ new Map();
2879
+ static preservedStyles = /* @__PURE__ */ new Map();
2880
2880
  /**
2881
- * Preserve marks before an action that will cause blur
2882
- * Call this in onMouseDown or onPointerDown events
2881
+ * Preserve current font and color before blur or node change
2883
2882
  */
2884
2883
  static preserve(editor) {
2885
2884
  if (!editor) return;
2886
- const { state } = editor;
2887
- const { $from } = state.selection;
2888
- const currentMarks = state.storedMarks || $from.marks();
2889
- if (currentMarks.length > 0) {
2890
- this.preservedMarks.set(editor, [...currentMarks]);
2885
+ try {
2886
+ const { state } = editor;
2887
+ const { $from } = state.selection;
2888
+ const currentNode = $from.parent;
2889
+ let font = currentNode.attrs.fontFamily;
2890
+ let color = currentNode.attrs.color;
2891
+ const marks = state.storedMarks || $from.marks();
2892
+ const textStyleMark = marks.find((m) => m.type.name === "textStyle");
2893
+ if (!font && textStyleMark?.attrs.fontFamily) {
2894
+ font = textStyleMark.attrs.fontFamily;
2895
+ }
2896
+ if (!color && textStyleMark?.attrs.color) {
2897
+ color = textStyleMark.attrs.color;
2898
+ }
2899
+ if (font || color) {
2900
+ this.preservedStyles.set(editor, { font, color });
2901
+ }
2902
+ } catch (error) {
2903
+ console.debug("MarkPreserver.preserve error:", error);
2891
2904
  }
2892
2905
  }
2893
2906
  /**
2894
- * Restore marks after an action completes
2895
- * Call this after the editor action in onClick or in a setTimeout
2907
+ * Restore preserved styles to current node
2896
2908
  */
2897
2909
  static restore(editor, delay = 0) {
2898
2910
  if (!editor) return;
2899
- const marks = this.preservedMarks.get(editor);
2900
- if (!marks) return;
2911
+ const styles = this.preservedStyles.get(editor);
2912
+ if (!styles) return;
2901
2913
  const restoreFn = () => {
2902
2914
  if (editor.isDestroyed) {
2903
- this.preservedMarks.delete(editor);
2915
+ this.preservedStyles.delete(editor);
2904
2916
  return;
2905
2917
  }
2906
- const { state } = editor;
2907
- editor.view.dispatch(state.tr.setStoredMarks(marks));
2908
- this.preservedMarks.delete(editor);
2909
- editor.commands.focus();
2918
+ try {
2919
+ const { state } = editor;
2920
+ const { $from } = state.selection;
2921
+ const depth = $from.depth;
2922
+ const pos = $from.before(depth);
2923
+ const node = $from.node(depth);
2924
+ const supportsAttributes = [
2925
+ "paragraph",
2926
+ "heading",
2927
+ "listItem",
2928
+ "taskItem",
2929
+ "blockquote"
2930
+ ].includes(node.type.name);
2931
+ if (!supportsAttributes) {
2932
+ this.preservedStyles.delete(editor);
2933
+ editor.commands.focus();
2934
+ return;
2935
+ }
2936
+ const updates = {};
2937
+ if (styles.font && !node.attrs.fontFamily) {
2938
+ updates.fontFamily = styles.font;
2939
+ }
2940
+ if (styles.color && !node.attrs.color) {
2941
+ updates.color = styles.color;
2942
+ }
2943
+ if (Object.keys(updates).length > 0) {
2944
+ const newAttrs = { ...node.attrs, ...updates };
2945
+ editor.view.dispatch(
2946
+ state.tr.setNodeMarkup(pos, void 0, newAttrs)
2947
+ );
2948
+ }
2949
+ const marks = state.storedMarks || $from.marks();
2950
+ const filteredMarks = marks.filter((mark) => mark.type.name !== "textStyle");
2951
+ const textStyleAttrs = {};
2952
+ if (styles.font) textStyleAttrs.fontFamily = styles.font;
2953
+ if (styles.color) textStyleAttrs.color = styles.color;
2954
+ const newMarks = [
2955
+ ...filteredMarks,
2956
+ state.schema.marks.textStyle.create(textStyleAttrs)
2957
+ ];
2958
+ setTimeout(() => {
2959
+ if (!editor.isDestroyed) {
2960
+ editor.view.dispatch(
2961
+ editor.state.tr.setStoredMarks(newMarks)
2962
+ );
2963
+ editor.commands.focus();
2964
+ }
2965
+ }, 0);
2966
+ } catch (error) {
2967
+ console.debug("MarkPreserver.restore error:", error);
2968
+ } finally {
2969
+ this.preservedStyles.delete(editor);
2970
+ }
2910
2971
  };
2911
2972
  if (delay > 0) {
2912
2973
  setTimeout(restoreFn, delay);
@@ -2915,10 +2976,10 @@ var MarkPreserver = class {
2915
2976
  }
2916
2977
  }
2917
2978
  /**
2918
- * Clear preserved marks for an editor
2979
+ * Clear preserved styles
2919
2980
  */
2920
2981
  static clear(editor) {
2921
- this.preservedMarks.delete(editor);
2982
+ this.preservedStyles.delete(editor);
2922
2983
  }
2923
2984
  };
2924
2985
 
@@ -7634,6 +7695,7 @@ function useCursorVisibility({
7634
7695
 
7635
7696
  // src/components/extensions/font-family-block.ts
7636
7697
  import { Extension as Extension2 } from "@tiptap/core";
7698
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
7637
7699
  var FontFamilyBlock = Extension2.create({
7638
7700
  name: "fontFamilyBlock",
7639
7701
  addGlobalAttributes() {
@@ -7690,150 +7752,174 @@ var ColorBlock = Extension2.create({
7690
7752
  ];
7691
7753
  }
7692
7754
  });
7755
+ var stylePersistenceKey = new PluginKey("stylePersistence");
7693
7756
  var StylePersistence = Extension2.create({
7694
7757
  name: "stylePersistence",
7695
7758
  addOptions() {
7696
7759
  return {
7697
- // Marks that should NOT inherit font family or color
7698
7760
  excludedMarks: ["code", "codeBlock"]
7699
7761
  };
7700
7762
  },
7701
- addStorage() {
7702
- return {
7703
- // Store marks temporarily when editor loses focus
7704
- preservedMarks: null,
7705
- // Store marks from the previous node (for Enter key handling)
7706
- previousNodeMarks: null
7707
- };
7708
- },
7709
- addKeyboardShortcuts() {
7710
- return {
7711
- // Handle Enter key to preserve marks when creating new paragraphs
7712
- "Enter": ({ editor }) => {
7713
- const { state } = editor;
7714
- const { $from } = state.selection;
7715
- const currentNode = $from.parent;
7716
- const blockFont = currentNode.attrs.fontFamily;
7717
- const blockColor = currentNode.attrs.color;
7718
- if (blockFont || blockColor) {
7719
- this.storage.previousNodeMarks = { blockFont, blockColor };
7720
- }
7721
- return false;
7722
- },
7723
- // Handle Shift+Enter (soft break) - same logic
7724
- "Shift-Enter": ({ editor }) => {
7725
- const { state } = editor;
7726
- const { $from } = state.selection;
7727
- const currentNode = $from.parent;
7728
- const blockFont = currentNode.attrs.fontFamily;
7729
- const blockColor = currentNode.attrs.color;
7730
- if (blockFont || blockColor) {
7731
- this.storage.previousNodeMarks = { blockFont, blockColor };
7732
- }
7733
- return false;
7734
- }
7735
- };
7736
- },
7737
- onSelectionUpdate({ editor }) {
7738
- const excludedMarks = this.options.excludedMarks;
7739
- syncStoredMarks(editor, excludedMarks, this.storage);
7740
- },
7741
- onFocus({ editor }) {
7742
- const excludedMarks = this.options.excludedMarks;
7743
- if (this.storage.preservedMarks) {
7744
- editor.view.dispatch(
7745
- editor.state.tr.setStoredMarks(this.storage.preservedMarks)
7746
- );
7747
- this.storage.preservedMarks = null;
7748
- }
7749
- syncStoredMarks(editor, excludedMarks, this.storage);
7750
- },
7751
- onBlur({ editor }) {
7752
- const { state } = editor;
7753
- const { $from } = state.selection;
7754
- const currentMarks = state.storedMarks || $from.marks();
7755
- const hasTextStyle = currentMarks.some((mark) => mark.type.name === "textStyle");
7756
- if (hasTextStyle) {
7757
- this.storage.preservedMarks = currentMarks;
7758
- }
7759
- },
7760
- onCreate({ editor }) {
7761
- const excludedMarks = this.options.excludedMarks;
7762
- syncStoredMarks(editor, excludedMarks, this.storage);
7763
- },
7764
- onTransaction({ editor, transaction }) {
7765
- if (!transaction.selectionSet && !transaction.docChanged) return;
7763
+ addProseMirrorPlugins() {
7766
7764
  const excludedMarks = this.options.excludedMarks;
7767
- requestAnimationFrame(() => {
7768
- if (!editor.isDestroyed) {
7769
- syncStoredMarks(editor, excludedMarks, this.storage);
7770
- }
7771
- });
7765
+ return [
7766
+ new Plugin({
7767
+ key: stylePersistenceKey,
7768
+ state: {
7769
+ init() {
7770
+ return {
7771
+ lastFont: null,
7772
+ lastColor: null
7773
+ };
7774
+ },
7775
+ apply(tr, value, oldState, newState) {
7776
+ const { $from } = newState.selection;
7777
+ const marks = $from.marks();
7778
+ const hasExcludedMarks = marks.some(
7779
+ (mark) => excludedMarks.includes(mark.type.name)
7780
+ );
7781
+ let isInCodeBlock = false;
7782
+ for (let depth = $from.depth; depth > 0; depth--) {
7783
+ const node = $from.node(depth);
7784
+ if (node.type.name === "codeBlock") {
7785
+ isInCodeBlock = true;
7786
+ break;
7787
+ }
7788
+ }
7789
+ if (hasExcludedMarks || isInCodeBlock) {
7790
+ return value;
7791
+ }
7792
+ const parentNode = $from.parent;
7793
+ let font = parentNode.attrs.fontFamily;
7794
+ let color = parentNode.attrs.color;
7795
+ const textStyleMark = marks.find((m) => m.type.name === "textStyle");
7796
+ if (!font && textStyleMark?.attrs.fontFamily) {
7797
+ font = textStyleMark.attrs.fontFamily;
7798
+ }
7799
+ if (!color && textStyleMark?.attrs.color) {
7800
+ color = textStyleMark.attrs.color;
7801
+ }
7802
+ return {
7803
+ lastFont: font || value.lastFont,
7804
+ lastColor: color || value.lastColor
7805
+ };
7806
+ }
7807
+ },
7808
+ appendTransaction(transactions, oldState, newState) {
7809
+ if (!transactions.some((tr2) => tr2.docChanged)) {
7810
+ return null;
7811
+ }
7812
+ const pluginState = stylePersistenceKey.getState(newState);
7813
+ if (!pluginState.lastFont && !pluginState.lastColor) {
7814
+ return null;
7815
+ }
7816
+ const { $from } = newState.selection;
7817
+ const parentNode = $from.parent;
7818
+ const marks = $from.marks();
7819
+ const hasExcludedMarks = marks.some(
7820
+ (mark) => excludedMarks.includes(mark.type.name)
7821
+ );
7822
+ let isInCodeBlock = false;
7823
+ for (let depth = $from.depth; depth > 0; depth--) {
7824
+ const node = $from.node(depth);
7825
+ if (node.type.name === "codeBlock") {
7826
+ isInCodeBlock = true;
7827
+ break;
7828
+ }
7829
+ }
7830
+ if (hasExcludedMarks || isInCodeBlock) {
7831
+ return null;
7832
+ }
7833
+ const supportsAttributes = [
7834
+ "paragraph",
7835
+ "heading",
7836
+ "listItem",
7837
+ "taskItem",
7838
+ "blockquote"
7839
+ ].includes(parentNode.type.name);
7840
+ if (!supportsAttributes) {
7841
+ return null;
7842
+ }
7843
+ const currentFont = parentNode.attrs.fontFamily;
7844
+ const currentColor = parentNode.attrs.color;
7845
+ const needsFont = !currentFont && pluginState.lastFont;
7846
+ const needsColor = !currentColor && pluginState.lastColor;
7847
+ if (!needsFont && !needsColor) {
7848
+ return null;
7849
+ }
7850
+ const tr = newState.tr;
7851
+ try {
7852
+ const depth = $from.depth;
7853
+ const pos = $from.before(depth);
7854
+ const node = $from.node(depth);
7855
+ const newAttrs = { ...node.attrs };
7856
+ if (needsFont) newAttrs.fontFamily = pluginState.lastFont;
7857
+ if (needsColor) newAttrs.color = pluginState.lastColor;
7858
+ tr.setNodeMarkup(pos, void 0, newAttrs);
7859
+ const storedMarks = newState.storedMarks || $from.marks();
7860
+ const filteredMarks = storedMarks.filter(
7861
+ (mark) => mark.type.name !== "textStyle" && !excludedMarks.includes(mark.type.name)
7862
+ );
7863
+ const textStyleAttrs = {};
7864
+ if (pluginState.lastFont) textStyleAttrs.fontFamily = pluginState.lastFont;
7865
+ if (pluginState.lastColor) textStyleAttrs.color = pluginState.lastColor;
7866
+ const newMarks = [
7867
+ ...filteredMarks,
7868
+ newState.schema.marks.textStyle.create(textStyleAttrs)
7869
+ ];
7870
+ tr.setStoredMarks(newMarks);
7871
+ return tr;
7872
+ } catch (error) {
7873
+ console.error("StylePersistence error:", error);
7874
+ return null;
7875
+ }
7876
+ },
7877
+ props: {
7878
+ handleKeyDown(view, event) {
7879
+ if (event.key === "Enter") {
7880
+ const { state } = view;
7881
+ const { $from } = state.selection;
7882
+ const marks = $from.marks();
7883
+ const hasExcludedMarks = marks.some(
7884
+ (mark) => excludedMarks.includes(mark.type.name)
7885
+ );
7886
+ if (hasExcludedMarks) {
7887
+ return false;
7888
+ }
7889
+ let isInCodeBlock = false;
7890
+ for (let depth = $from.depth; depth > 0; depth--) {
7891
+ const node = $from.node(depth);
7892
+ if (node.type.name === "codeBlock") {
7893
+ isInCodeBlock = true;
7894
+ break;
7895
+ }
7896
+ }
7897
+ if (isInCodeBlock) {
7898
+ return false;
7899
+ }
7900
+ const parentNode = $from.parent;
7901
+ let font = parentNode.attrs.fontFamily;
7902
+ let color = parentNode.attrs.color;
7903
+ const textStyleMark = marks.find((m) => m.type.name === "textStyle");
7904
+ if (!font && textStyleMark?.attrs.fontFamily) {
7905
+ font = textStyleMark.attrs.fontFamily;
7906
+ }
7907
+ if (!color && textStyleMark?.attrs.color) {
7908
+ color = textStyleMark.attrs.color;
7909
+ }
7910
+ if (font || color) {
7911
+ const pluginState = stylePersistenceKey.getState(state);
7912
+ pluginState.lastFont = font || pluginState.lastFont;
7913
+ pluginState.lastColor = color || pluginState.lastColor;
7914
+ }
7915
+ }
7916
+ return false;
7917
+ }
7918
+ }
7919
+ })
7920
+ ];
7772
7921
  }
7773
7922
  });
7774
- function syncStoredMarks(editor, excludedMarks, storage) {
7775
- const { state } = editor;
7776
- const { $from } = state.selection;
7777
- const marks = $from.marks();
7778
- const hasExcludedMarks = marks.some(
7779
- (mark) => excludedMarks.includes(mark.type.name)
7780
- );
7781
- let isInCodeBlock = false;
7782
- for (let depth = $from.depth; depth > 0; depth--) {
7783
- const node = $from.node(depth);
7784
- if (node.type.name === "codeBlock") {
7785
- isInCodeBlock = true;
7786
- break;
7787
- }
7788
- }
7789
- if (hasExcludedMarks || isInCodeBlock) {
7790
- return;
7791
- }
7792
- const parentNode = $from.parent;
7793
- let blockFont = parentNode.attrs.fontFamily;
7794
- let blockColor = parentNode.attrs.color;
7795
- if ((!blockFont || !blockColor) && storage?.previousNodeMarks) {
7796
- if (!blockFont && storage.previousNodeMarks.blockFont) {
7797
- blockFont = storage.previousNodeMarks.blockFont;
7798
- }
7799
- if (!blockColor && storage.previousNodeMarks.blockColor) {
7800
- blockColor = storage.previousNodeMarks.blockColor;
7801
- }
7802
- if (blockFont || blockColor) {
7803
- const depth = $from.depth;
7804
- const nodePos = $from.before(depth);
7805
- const node = $from.node(depth);
7806
- const newAttrs = { ...node.attrs };
7807
- if (blockFont) newAttrs.fontFamily = blockFont;
7808
- if (blockColor) newAttrs.color = blockColor;
7809
- const tr = state.tr.setNodeMarkup(nodePos, void 0, newAttrs);
7810
- editor.view.dispatch(tr);
7811
- }
7812
- storage.previousNodeMarks = null;
7813
- }
7814
- if (!blockFont && !blockColor) {
7815
- return;
7816
- }
7817
- const storedMarks = state.storedMarks || $from.marks();
7818
- const existingTextStyle = storedMarks.find((mark) => mark.type.name === "textStyle");
7819
- const needsUpdate = !existingTextStyle || blockFont && existingTextStyle.attrs.fontFamily !== blockFont || blockColor && existingTextStyle.attrs.color !== blockColor;
7820
- if (!needsUpdate) {
7821
- return;
7822
- }
7823
- const filteredMarks = storedMarks.filter(
7824
- (mark) => mark.type.name !== "textStyle" && !excludedMarks.includes(mark.type.name)
7825
- );
7826
- const textStyleAttrs = {};
7827
- if (blockFont) textStyleAttrs.fontFamily = blockFont;
7828
- if (blockColor) textStyleAttrs.color = blockColor;
7829
- const newMarks = [
7830
- ...filteredMarks,
7831
- state.schema.marks.textStyle.create(textStyleAttrs)
7832
- ];
7833
- editor.view.dispatch(
7834
- state.tr.setStoredMarks(newMarks)
7835
- );
7836
- }
7837
7923
 
7838
7924
  // src/components/tiptap-templates/simple/simple-editor.tsx
7839
7925
  import { Fragment as Fragment12, jsx as jsx81, jsxs as jsxs48 } from "react/jsx-runtime";