@antscorp/antsomi-ui 2.0.82 → 2.0.83-text-editor-beta.1

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.
Files changed (213) hide show
  1. package/es/assets/css/main.scss +4 -2
  2. package/es/components/index.scss +1 -0
  3. package/es/components/molecules/AccountSelection/AccountListing.js +14 -3
  4. package/es/components/molecules/EmojiPopover/EmojiPopover.js +5 -1
  5. package/es/components/molecules/FontSizeInput/FontSizeInput.d.ts +3 -0
  6. package/es/components/molecules/FontSizeInput/FontSizeInput.js +77 -0
  7. package/es/components/molecules/FontSizeInput/constants.d.ts +2 -0
  8. package/es/components/molecules/FontSizeInput/constants.js +5 -0
  9. package/es/components/molecules/FontSizeInput/index.d.ts +2 -0
  10. package/es/components/molecules/FontSizeInput/index.js +1 -0
  11. package/es/components/molecules/FontSizeInput/types.d.ts +21 -0
  12. package/es/components/molecules/FontSizeInput/types.js +1 -0
  13. package/es/components/molecules/VirtualizedMenu/VirtualizedMenu.d.ts +1 -2
  14. package/es/components/molecules/VirtualizedMenu/__mocks__/index.js +2550 -938
  15. package/es/components/molecules/VirtualizedMenu/components/Item/Item.d.ts +4 -3
  16. package/es/components/molecules/VirtualizedMenu/components/Item/Item.js +24 -22
  17. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.d.ts +1 -2
  18. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.js +59 -31
  19. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.optimized.d.ts +19 -0
  20. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.optimized.js +233 -0
  21. package/es/components/molecules/VirtualizedMenu/components/MenuInline/index.d.ts +1 -1
  22. package/es/components/molecules/VirtualizedMenu/components/MenuInline/index.js +2 -1
  23. package/es/components/molecules/VirtualizedMenu/styled.d.ts +1 -1
  24. package/es/components/molecules/VirtualizedMenu/styled.js +11 -3
  25. package/es/components/molecules/VirtualizedMenu/types.d.ts +1 -8
  26. package/es/components/molecules/VirtualizedMenu/utils.d.ts +7 -5
  27. package/es/components/molecules/VirtualizedMenu/utils.js +12 -18
  28. package/es/components/molecules/index.d.ts +1 -0
  29. package/es/components/molecules/index.js +1 -0
  30. package/es/components/organism/ActivityTimeline/__mocks__/event_tracking.json +1290 -0
  31. package/es/components/organism/ActivityTimeline/__mocks__/timeline.json +3059 -0
  32. package/es/components/organism/TextEditor/TextEditor.d.ts +3 -0
  33. package/es/components/organism/TextEditor/TextEditor.js +239 -0
  34. package/es/components/organism/TextEditor/__mocks__/text-block.settings.json +320 -0
  35. package/es/components/organism/TextEditor/__mocks__/text-contennt.d.ts +1 -0
  36. package/es/components/organism/TextEditor/__mocks__/text-contennt.js +38 -0
  37. package/es/components/organism/TextEditor/constants.d.ts +135 -0
  38. package/es/components/organism/TextEditor/constants.js +280 -0
  39. package/es/components/organism/TextEditor/extensions/BackgroundColor.d.ts +25 -0
  40. package/es/components/organism/TextEditor/extensions/BackgroundColor.js +41 -0
  41. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.d.ts +130 -0
  42. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.js +247 -0
  43. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.d.ts +15 -0
  44. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.js +31 -0
  45. package/es/components/organism/TextEditor/extensions/BubbleMenu/index.d.ts +2 -0
  46. package/es/components/organism/TextEditor/extensions/BubbleMenu/index.js +2 -0
  47. package/es/components/organism/TextEditor/extensions/Color.d.ts +6 -0
  48. package/es/components/organism/TextEditor/extensions/Color.js +19 -0
  49. package/es/components/organism/TextEditor/extensions/Emoji.d.ts +57 -0
  50. package/es/components/organism/TextEditor/extensions/Emoji.js +184 -0
  51. package/es/components/organism/TextEditor/extensions/FontFamily.d.ts +6 -0
  52. package/es/components/organism/TextEditor/extensions/FontFamily.js +43 -0
  53. package/es/components/organism/TextEditor/extensions/FontSize.d.ts +32 -0
  54. package/es/components/organism/TextEditor/extensions/FontSize.js +47 -0
  55. package/es/components/organism/TextEditor/extensions/FontWeight.d.ts +23 -0
  56. package/es/components/organism/TextEditor/extensions/FontWeight.js +41 -0
  57. package/es/components/organism/TextEditor/extensions/Highlight.d.ts +1 -0
  58. package/es/components/organism/TextEditor/extensions/Highlight.js +14 -0
  59. package/es/components/organism/TextEditor/extensions/Indent.d.ts +28 -0
  60. package/es/components/organism/TextEditor/extensions/Indent.js +66 -0
  61. package/es/components/organism/TextEditor/extensions/LineHeight.d.ts +20 -0
  62. package/es/components/organism/TextEditor/extensions/LineHeight.js +36 -0
  63. package/es/components/organism/TextEditor/extensions/Link.d.ts +3 -0
  64. package/es/components/organism/TextEditor/extensions/Link.js +47 -0
  65. package/es/components/organism/TextEditor/extensions/ListItemMarker.d.ts +13 -0
  66. package/es/components/organism/TextEditor/extensions/ListItemMarker.js +174 -0
  67. package/es/components/organism/TextEditor/extensions/Selection.d.ts +6 -0
  68. package/es/components/organism/TextEditor/extensions/Selection.js +40 -0
  69. package/es/components/organism/TextEditor/extensions/SmartTag.d.ts +33 -0
  70. package/es/components/organism/TextEditor/extensions/SmartTag.js +162 -0
  71. package/es/components/organism/TextEditor/extensions/StyleMemory.d.ts +36 -0
  72. package/es/components/organism/TextEditor/extensions/StyleMemory.js +163 -0
  73. package/es/components/organism/TextEditor/extensions/TextTransform.d.ts +31 -0
  74. package/es/components/organism/TextEditor/extensions/TextTransform.js +35 -0
  75. package/es/components/organism/TextEditor/hooks/index.d.ts +6 -0
  76. package/es/components/organism/TextEditor/hooks/index.js +6 -0
  77. package/es/components/organism/TextEditor/hooks/useDocumentState.d.ts +18 -0
  78. package/es/components/organism/TextEditor/hooks/useDocumentState.js +42 -0
  79. package/es/components/organism/TextEditor/hooks/useLinkHandler.d.ts +27 -0
  80. package/es/components/organism/TextEditor/hooks/useLinkHandler.js +279 -0
  81. package/es/components/organism/TextEditor/hooks/useMarkTracking.d.ts +26 -0
  82. package/es/components/organism/TextEditor/hooks/useMarkTracking.js +68 -0
  83. package/es/components/organism/TextEditor/hooks/usePersistence.d.ts +31 -0
  84. package/es/components/organism/TextEditor/hooks/usePersistence.js +169 -0
  85. package/es/components/organism/TextEditor/hooks/useStyleMemory.d.ts +6 -0
  86. package/es/components/organism/TextEditor/hooks/useStyleMemory.js +42 -0
  87. package/es/components/organism/TextEditor/hooks/useStylePresets.d.ts +34 -0
  88. package/es/components/organism/TextEditor/hooks/useStylePresets.js +83 -0
  89. package/es/components/organism/TextEditor/index.d.ts +5 -0
  90. package/es/components/organism/TextEditor/index.js +2 -0
  91. package/es/components/organism/TextEditor/index.scss +26 -0
  92. package/es/components/organism/TextEditor/provider.d.ts +10 -0
  93. package/es/components/organism/TextEditor/provider.js +20 -0
  94. package/es/components/organism/TextEditor/store.d.ts +11 -0
  95. package/es/components/organism/TextEditor/store.js +12 -0
  96. package/es/components/organism/TextEditor/styled.d.ts +5 -0
  97. package/es/components/organism/TextEditor/styled.js +69 -0
  98. package/es/components/organism/TextEditor/types.d.ts +95 -0
  99. package/es/components/organism/TextEditor/types.js +1 -0
  100. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.d.ts +6 -0
  101. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.js +78 -0
  102. package/es/components/organism/TextEditor/ui/BubbleMenu/index.d.ts +1 -0
  103. package/es/components/organism/TextEditor/ui/BubbleMenu/index.js +1 -0
  104. package/es/components/organism/TextEditor/ui/ColorPicker/ColorPicker.d.ts +43 -0
  105. package/es/components/organism/TextEditor/ui/ColorPicker/ColorPicker.js +120 -0
  106. package/es/components/organism/TextEditor/ui/ColorPicker/index.d.ts +1 -0
  107. package/es/components/organism/TextEditor/ui/ColorPicker/index.js +1 -0
  108. package/es/components/organism/TextEditor/ui/Emoji/EmojiList.d.ts +11 -0
  109. package/es/components/organism/TextEditor/ui/Emoji/EmojiList.js +66 -0
  110. package/es/components/organism/TextEditor/ui/Emoji/index.d.ts +2 -0
  111. package/es/components/organism/TextEditor/ui/Emoji/index.js +2 -0
  112. package/es/components/organism/TextEditor/ui/Emoji/suggestion.d.ts +4 -0
  113. package/es/components/organism/TextEditor/ui/Emoji/suggestion.js +71 -0
  114. package/es/components/organism/TextEditor/ui/FontPopover/FontPopover.d.ts +11 -0
  115. package/es/components/organism/TextEditor/ui/FontPopover/FontPopover.js +72 -0
  116. package/es/components/organism/TextEditor/ui/FontPopover/styled.d.ts +1 -0
  117. package/es/components/organism/TextEditor/ui/FontPopover/styled.js +51 -0
  118. package/es/components/organism/TextEditor/ui/Popover/Popover.d.ts +6 -0
  119. package/es/components/organism/TextEditor/ui/Popover/Popover.js +7 -0
  120. package/es/components/organism/TextEditor/ui/Popover/index.d.ts +1 -0
  121. package/es/components/organism/TextEditor/ui/Popover/index.js +1 -0
  122. package/es/components/organism/TextEditor/ui/Select/Select.d.ts +4 -0
  123. package/es/components/organism/TextEditor/ui/Select/Select.js +7 -0
  124. package/es/components/organism/TextEditor/ui/Select/index.d.ts +1 -0
  125. package/es/components/organism/TextEditor/ui/Select/index.js +1 -0
  126. package/es/components/organism/TextEditor/ui/TextAlignSelect/TextAlignSelect.d.ts +30 -0
  127. package/es/components/organism/TextEditor/ui/TextAlignSelect/TextAlignSelect.js +49 -0
  128. package/es/components/organism/TextEditor/ui/TextAlignSelect/index.d.ts +1 -0
  129. package/es/components/organism/TextEditor/ui/TextAlignSelect/index.js +1 -0
  130. package/es/components/organism/TextEditor/ui/Toolbar/Toolbar.d.ts +14 -0
  131. package/es/components/organism/TextEditor/ui/Toolbar/Toolbar.js +42 -0
  132. package/es/components/organism/TextEditor/ui/Toolbar/actions/BoldAction.d.ts +5 -0
  133. package/es/components/organism/TextEditor/ui/Toolbar/actions/BoldAction.js +7 -0
  134. package/es/components/organism/TextEditor/ui/Toolbar/actions/BulletListAction.d.ts +5 -0
  135. package/es/components/organism/TextEditor/ui/Toolbar/actions/BulletListAction.js +7 -0
  136. package/es/components/organism/TextEditor/ui/Toolbar/actions/ClearFormattingAction.d.ts +5 -0
  137. package/es/components/organism/TextEditor/ui/Toolbar/actions/ClearFormattingAction.js +18 -0
  138. package/es/components/organism/TextEditor/ui/Toolbar/actions/EmojiAction.d.ts +4 -0
  139. package/es/components/organism/TextEditor/ui/Toolbar/actions/EmojiAction.js +13 -0
  140. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.d.ts +7 -0
  141. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.js +18 -0
  142. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontSizeAction.d.ts +6 -0
  143. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontSizeAction.js +42 -0
  144. package/es/components/organism/TextEditor/ui/Toolbar/actions/HighlightAction.d.ts +5 -0
  145. package/es/components/organism/TextEditor/ui/Toolbar/actions/HighlightAction.js +7 -0
  146. package/es/components/organism/TextEditor/ui/Toolbar/actions/IndentAction.d.ts +5 -0
  147. package/es/components/organism/TextEditor/ui/Toolbar/actions/IndentAction.js +7 -0
  148. package/es/components/organism/TextEditor/ui/Toolbar/actions/ItalicAction.d.ts +5 -0
  149. package/es/components/organism/TextEditor/ui/Toolbar/actions/ItalicAction.js +7 -0
  150. package/es/components/organism/TextEditor/ui/Toolbar/actions/LinkAction.d.ts +6 -0
  151. package/es/components/organism/TextEditor/ui/Toolbar/actions/LinkAction.js +4 -0
  152. package/es/components/organism/TextEditor/ui/Toolbar/actions/OrderedListAction.d.ts +5 -0
  153. package/es/components/organism/TextEditor/ui/Toolbar/actions/OrderedListAction.js +7 -0
  154. package/es/components/organism/TextEditor/ui/Toolbar/actions/OutdentAction.d.ts +5 -0
  155. package/es/components/organism/TextEditor/ui/Toolbar/actions/OutdentAction.js +7 -0
  156. package/es/components/organism/TextEditor/ui/Toolbar/actions/SmartTagAction.d.ts +7 -0
  157. package/es/components/organism/TextEditor/ui/Toolbar/actions/SmartTagAction.js +9 -0
  158. package/es/components/organism/TextEditor/ui/Toolbar/actions/SpacingAction.d.ts +9 -0
  159. package/es/components/organism/TextEditor/ui/Toolbar/actions/SpacingAction.js +22 -0
  160. package/es/components/organism/TextEditor/ui/Toolbar/actions/StrikeAction.d.ts +5 -0
  161. package/es/components/organism/TextEditor/ui/Toolbar/actions/StrikeAction.js +7 -0
  162. package/es/components/organism/TextEditor/ui/Toolbar/actions/SubscriptAction.d.ts +5 -0
  163. package/es/components/organism/TextEditor/ui/Toolbar/actions/SubscriptAction.js +13 -0
  164. package/es/components/organism/TextEditor/ui/Toolbar/actions/SuperscriptAction.d.ts +5 -0
  165. package/es/components/organism/TextEditor/ui/Toolbar/actions/SuperscriptAction.js +13 -0
  166. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextAlignAction.d.ts +5 -0
  167. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextAlignAction.js +3 -0
  168. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextBackgroundColorAction.d.ts +7 -0
  169. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextBackgroundColorAction.js +19 -0
  170. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextColorAction.d.ts +15 -0
  171. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextColorAction.js +14 -0
  172. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextTransformAction.d.ts +5 -0
  173. package/es/components/organism/TextEditor/ui/Toolbar/actions/TextTransformAction.js +28 -0
  174. package/es/components/organism/TextEditor/ui/Toolbar/actions/UnderlineAction.d.ts +5 -0
  175. package/es/components/organism/TextEditor/ui/Toolbar/actions/UnderlineAction.js +5 -0
  176. package/es/components/organism/TextEditor/ui/Toolbar/actions/UnsetLink.d.ts +6 -0
  177. package/es/components/organism/TextEditor/ui/Toolbar/actions/UnsetLink.js +10 -0
  178. package/es/components/organism/TextEditor/ui/Toolbar/actions/index.d.ts +20 -0
  179. package/es/components/organism/TextEditor/ui/Toolbar/actions/index.js +20 -0
  180. package/es/components/organism/TextEditor/ui/Toolbar/index.d.ts +1 -0
  181. package/es/components/organism/TextEditor/ui/Toolbar/index.js +1 -0
  182. package/es/components/organism/TextEditor/utils/documentState.d.ts +57 -0
  183. package/es/components/organism/TextEditor/utils/documentState.js +100 -0
  184. package/es/components/organism/TextEditor/utils/font.d.ts +84 -0
  185. package/es/components/organism/TextEditor/utils/font.js +175 -0
  186. package/es/components/organism/TextEditor/utils/htmlProcessing.d.ts +62 -0
  187. package/es/components/organism/TextEditor/utils/htmlProcessing.js +304 -0
  188. package/es/components/organism/TextEditor/utils/index.d.ts +8 -0
  189. package/es/components/organism/TextEditor/utils/index.js +16 -0
  190. package/es/components/organism/TextEditor/utils/link.d.ts +100 -0
  191. package/es/components/organism/TextEditor/utils/link.js +149 -0
  192. package/es/components/organism/TextEditor/utils/menu.d.ts +134 -0
  193. package/es/components/organism/TextEditor/utils/menu.js +317 -0
  194. package/es/components/organism/TextEditor/utils/selection.d.ts +25 -0
  195. package/es/components/organism/TextEditor/utils/selection.js +57 -0
  196. package/es/components/organism/TextEditor/utils/smartTag.d.ts +49 -0
  197. package/es/components/organism/TextEditor/utils/smartTag.js +89 -0
  198. package/es/components/organism/TextEditor/utils/style.d.ts +78 -0
  199. package/es/components/organism/TextEditor/utils/style.js +193 -0
  200. package/es/components/organism/index.d.ts +1 -0
  201. package/es/components/organism/index.js +1 -0
  202. package/es/components/organism/index.scss +1 -0
  203. package/es/hooks/useBroadcastedLocalStorage.d.ts +5 -0
  204. package/es/hooks/useBroadcastedLocalStorage.js +71 -0
  205. package/es/utils/common.d.ts +6 -9
  206. package/es/utils/common.js +44 -23
  207. package/es/utils/index.d.ts +1 -0
  208. package/es/utils/index.js +1 -0
  209. package/es/utils/tree.d.ts +225 -0
  210. package/es/utils/tree.js +469 -0
  211. package/es/utils/web.d.ts +4 -0
  212. package/es/utils/web.js +25 -0
  213. package/package.json +29 -3
@@ -0,0 +1,279 @@
1
+ import { useCallback } from 'react';
2
+ import { TextSelection } from '@tiptap/pm/state';
3
+ import { getActiveLinkIdFromRange, getLinkMark, getLinkMarkRanges, getLinkRange, isLinkColor, } from '../utils';
4
+ import { TEXT_EDITOR_CONSTANTS } from '../constants';
5
+ const { LINK_TEXT_COLOR } = TEXT_EDITOR_CONSTANTS;
6
+ export function useLinkHandler(params) {
7
+ const { editor } = params;
8
+ const setLink = useCallback((params) => {
9
+ const { link, id, title = '', text = '', dynamic } = params;
10
+ if (!editor)
11
+ return;
12
+ const { selection } = editor?.state;
13
+ const { from, to } = selection;
14
+ let linkContent = editor.state.doc.textBetween(from, to);
15
+ if (params.text && linkContent !== text) {
16
+ linkContent = params.text;
17
+ }
18
+ editor
19
+ .chain()
20
+ .focus()
21
+ .deleteSelection()
22
+ .insertContent(linkContent)
23
+ .command(({ tr }) => {
24
+ const endPos = tr.selection.from;
25
+ const startPos = endPos - linkContent.length;
26
+ tr.setSelection(TextSelection.create(tr.doc, startPos, endPos));
27
+ return true;
28
+ })
29
+ .setUnderline()
30
+ .setColor(LINK_TEXT_COLOR)
31
+ .setLink({ href: link })
32
+ .updateAttributes('link', {
33
+ id,
34
+ title,
35
+ dynamic,
36
+ })
37
+ .command(({ tr }) => {
38
+ const endPos = tr.selection.to;
39
+ tr.setSelection(TextSelection.create(tr.doc, endPos, endPos));
40
+ return true;
41
+ })
42
+ .run();
43
+ }, [editor]);
44
+ const updateLink = useCallback(({ id, link, title, text, dynamic }) => {
45
+ if (!editor)
46
+ return;
47
+ const { state } = editor;
48
+ const { selection } = state;
49
+ const { $from, $to } = selection;
50
+ if (editor.schema.spec.marks.get('link') === undefined)
51
+ return;
52
+ const { activeId: linkActiveId } = getActiveLinkIdFromRange({
53
+ state,
54
+ from: $from.pos,
55
+ to: $to.pos,
56
+ }) || {};
57
+ // If we have text to update and we found a link mark
58
+ if (text && linkActiveId) {
59
+ const currentText = state.doc.textBetween($from.pos, $to.pos);
60
+ // Only update if the text is different
61
+ if (currentText !== text) {
62
+ editor
63
+ .chain()
64
+ .focus()
65
+ .deleteSelection()
66
+ .insertContent(text)
67
+ .command(({ tr }) => {
68
+ // Get the end position after insertion
69
+ const endPos = tr.selection.from;
70
+ // Calculate start position by subtracting text length
71
+ const startPos = endPos - text.length;
72
+ tr.setSelection(TextSelection.create(tr.doc, startPos, endPos));
73
+ return true;
74
+ })
75
+ .setUnderline()
76
+ .setColor(LINK_TEXT_COLOR)
77
+ .setLink({ href: link })
78
+ .updateAttributes('link', {
79
+ id,
80
+ title,
81
+ dynamic,
82
+ })
83
+ .command(({ tr }) => {
84
+ const endPos = tr.selection.to;
85
+ tr.setSelection(TextSelection.create(tr.doc, endPos, endPos));
86
+ return true;
87
+ })
88
+ .run();
89
+ }
90
+ }
91
+ }, [editor]);
92
+ const deleteLink = useCallback((id) => {
93
+ if (!editor)
94
+ return;
95
+ const { state } = editor;
96
+ const { doc } = state;
97
+ let linkMark;
98
+ doc.descendants((node, pos) => {
99
+ if (linkMark)
100
+ return;
101
+ linkMark = getLinkMarkRanges({
102
+ state,
103
+ from: pos,
104
+ to: pos + node.nodeSize,
105
+ }).find(range => range.mark.attrs.id === id);
106
+ });
107
+ if (linkMark) {
108
+ editor
109
+ .chain()
110
+ .deleteRange({
111
+ from: linkMark.from,
112
+ to: linkMark.to,
113
+ })
114
+ .run();
115
+ }
116
+ }, [editor]);
117
+ const updateLinkAttrsGlobally = useCallback((updateFn) => {
118
+ if (!editor)
119
+ return;
120
+ const { state, view } = editor;
121
+ const { doc, schema, tr } = state;
122
+ const linkType = schema.marks.link;
123
+ let somethingChanged = false;
124
+ doc.descendants((node, pos) => {
125
+ // Get all link mark ranges within this node
126
+ const linkMarkRanges = getLinkMarkRanges({
127
+ state,
128
+ from: pos,
129
+ to: pos + node.nodeSize,
130
+ });
131
+ linkMarkRanges.forEach(markRange => {
132
+ const updatedAttrs = updateFn(markRange.mark.attrs);
133
+ if (updatedAttrs) {
134
+ // Remove the old mark
135
+ tr.removeMark(markRange.from, markRange.to, markRange.mark);
136
+ // Create a new mark with updated attributes and add it
137
+ const newMark = linkType.create({
138
+ ...markRange.mark.attrs,
139
+ ...updatedAttrs,
140
+ });
141
+ tr.addMark(markRange.from, markRange.to, newMark);
142
+ somethingChanged = true;
143
+ }
144
+ });
145
+ });
146
+ if (somethingChanged) {
147
+ view.dispatch(tr);
148
+ }
149
+ }, [editor]);
150
+ const updateLinkTextGlobally = useCallback((updateFn) => {
151
+ if (!editor)
152
+ return;
153
+ const { state, view } = editor;
154
+ const { doc, schema, tr } = state;
155
+ const linkType = schema.marks.link;
156
+ const underlineType = schema.marks.underline; // Assuming you have an underline mark
157
+ const textStyleType = schema.marks.textStyle; // Assuming textStyle mark handles color
158
+ const boldType = schema.marks.bold; // Assuming bold mark
159
+ const italicType = schema.marks.italic; // Assuming italic mark
160
+ const rangesToProcess = [];
161
+ // Collect all unique extended link ranges
162
+ doc.descendants((node, pos) => {
163
+ if (!node.isText)
164
+ return;
165
+ const linkMark = linkType.isInSet(node.marks);
166
+ if (linkMark) {
167
+ const linkRange = getLinkRange({ state, pos });
168
+ if (linkRange) {
169
+ // Check if this extended range has already been added
170
+ const isProcessed = rangesToProcess.some(pr => pr.from === linkRange.from && pr.to === linkRange.to);
171
+ if (!isProcessed) {
172
+ // Find the first link mark in the range to get original attributes
173
+ const originalLinkMark = getLinkMark({
174
+ state,
175
+ from: linkRange.from,
176
+ to: linkRange.to,
177
+ });
178
+ if (originalLinkMark) {
179
+ rangesToProcess.push({
180
+ from: linkRange.from,
181
+ to: linkRange.to,
182
+ originalAttrs: originalLinkMark.attrs,
183
+ });
184
+ }
185
+ }
186
+ }
187
+ }
188
+ });
189
+ // Sort ranges in descending order to avoid issues with position shifts during deletion
190
+ rangesToProcess.sort((a, b) => b.from - a.from);
191
+ let hasChanged = false;
192
+ const applyLinkMarks = (from, to, originalAttrs, originalMarks) => {
193
+ // Apply the link mark with original attributes
194
+ const newLinkMark = linkType.create({
195
+ ...originalAttrs,
196
+ href: originalAttrs.href, // Ensure href is included
197
+ });
198
+ tr.addMark(from, to, newLinkMark);
199
+ // Apply underline mark
200
+ if (underlineType) {
201
+ const newUnderlineMark = underlineType.create();
202
+ tr.addMark(from, to, newUnderlineMark);
203
+ }
204
+ // Apply blue color mark (#0066CC) by finding the existing textStyle mark or creating a new one
205
+ if (textStyleType) {
206
+ const originalTextStyle = originalMarks.find(m => m.type === textStyleType);
207
+ const newColorMark = textStyleType.create({
208
+ ...originalTextStyle?.attrs,
209
+ color: LINK_TEXT_COLOR,
210
+ });
211
+ tr.addMark(from, to, newColorMark);
212
+ }
213
+ // Apply bold mark if it existed
214
+ if (boldType) {
215
+ const originalBoldMark = originalMarks.find(m => m.type === boldType);
216
+ if (originalBoldMark) {
217
+ tr.addMark(from, to, originalBoldMark);
218
+ }
219
+ }
220
+ // Apply italic mark if it existed
221
+ if (italicType) {
222
+ const originalItalicMark = originalMarks.find(m => m.type === italicType);
223
+ if (originalItalicMark) {
224
+ tr.addMark(from, to, originalItalicMark);
225
+ }
226
+ }
227
+ };
228
+ rangesToProcess.forEach(({ from, to, originalAttrs }) => {
229
+ const currentTextContent = state.doc.textBetween(from, to);
230
+ const updatedText = updateFn({
231
+ currentText: currentTextContent,
232
+ attrs: originalAttrs,
233
+ });
234
+ if (updatedText !== undefined) {
235
+ hasChanged = true;
236
+ // Get the marks from the node at the start of the link range
237
+ const originalMarks = state.doc.resolve(from).marks();
238
+ // Delete the old content
239
+ tr.delete(from, to);
240
+ // Insert the new text node
241
+ const newTextNode = schema.text(updatedText);
242
+ tr.insert(from, newTextNode);
243
+ // Apply the desired marks to the new text range
244
+ applyLinkMarks(from, from + updatedText.length, originalAttrs, originalMarks);
245
+ }
246
+ });
247
+ if (hasChanged) {
248
+ view.dispatch(tr);
249
+ }
250
+ }, [editor]);
251
+ const unsetLink = useCallback(() => {
252
+ if (!editor)
253
+ return;
254
+ const { state } = editor;
255
+ const linkRange = getLinkRange({ state, pos: state.selection.from });
256
+ if (!linkRange)
257
+ return;
258
+ const command = editor
259
+ .chain()
260
+ .setTextSelection({
261
+ from: linkRange.from,
262
+ to: linkRange.to,
263
+ })
264
+ .unsetUnderline()
265
+ .unsetLink();
266
+ if (isLinkColor(editor.getAttributes('textStyle').color)) {
267
+ command.unsetColor();
268
+ }
269
+ command.run();
270
+ }, [editor]);
271
+ return {
272
+ setLink,
273
+ updateLink,
274
+ deleteLink,
275
+ updateLinkAttrsGlobally,
276
+ updateLinkTextGlobally,
277
+ unsetLink,
278
+ };
279
+ }
@@ -0,0 +1,26 @@
1
+ import { Editor } from '@tiptap/react';
2
+ import { Mark } from '@tiptap/pm/model';
3
+ export interface MarkSnapshot {
4
+ marks: Mark[];
5
+ position: number;
6
+ timestamp: number;
7
+ textContent: string;
8
+ }
9
+ export interface UseMarkTrackingOptions {
10
+ /** Maximum number of snapshots to keep */
11
+ maxSnapshots?: number;
12
+ /** Marks to exclude from tracking */
13
+ excludeMarks?: string[];
14
+ /** Debounce delay for tracking (ms) */
15
+ debounceDelay?: number;
16
+ /** Auto-capture snapshots */
17
+ autoCapture?: boolean;
18
+ }
19
+ export declare function useMarkTracking(editor: Editor | null, options?: UseMarkTrackingOptions): {
20
+ snapshots: MarkSnapshot[];
21
+ currentMarks: Mark[];
22
+ captureMarkSnapshot: () => void;
23
+ applySnapshot: (index: number) => void;
24
+ getSimilarSnapshots: (targetMarks?: Mark[]) => MarkSnapshot[];
25
+ clearSnapshots: () => void;
26
+ };
@@ -0,0 +1,68 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ export function useMarkTracking(editor, options = {}) {
3
+ const { maxSnapshots = 10, excludeMarks = ['link', 'smartTag'], debounceDelay = 500, autoCapture = true, } = options;
4
+ const [snapshots, setSnapshots] = useState([]);
5
+ const [currentMarks, setCurrentMarks] = useState([]);
6
+ // Capture current mark state
7
+ const captureMarkSnapshot = useCallback(() => {
8
+ if (!editor)
9
+ return;
10
+ const { selection, storedMarks } = editor.state;
11
+ const { $from } = selection;
12
+ const marks = (storedMarks || $from.marks()).filter(mark => !excludeMarks.includes(mark.type.name));
13
+ const snapshot = {
14
+ marks,
15
+ position: $from.pos,
16
+ timestamp: Date.now(),
17
+ textContent: editor.state.doc.textBetween(Math.max(0, $from.pos - 50), Math.min(editor.state.doc.content.size, $from.pos + 50)),
18
+ };
19
+ setSnapshots(prev => {
20
+ const updated = [snapshot, ...prev];
21
+ return updated.slice(0, maxSnapshots);
22
+ });
23
+ setCurrentMarks(marks);
24
+ }, [editor, excludeMarks, maxSnapshots]);
25
+ // Apply marks from snapshot
26
+ const applySnapshot = useCallback((index) => {
27
+ if (!editor || !snapshots[index])
28
+ return;
29
+ const snapshot = snapshots[index];
30
+ // Clear current marks
31
+ editor.chain().unsetAllMarks().run();
32
+ // Apply snapshot marks
33
+ snapshot.marks.forEach(mark => {
34
+ editor.chain().setMark(mark.type.name, mark.attrs).run();
35
+ });
36
+ }, [editor, snapshots]);
37
+ // Get similar snapshots based on marks
38
+ const getSimilarSnapshots = useCallback((targetMarks = currentMarks) => snapshots.filter(snapshot => {
39
+ const markNames = snapshot.marks.map(m => m.type.name).sort();
40
+ const targetNames = targetMarks.map(m => m.type.name).sort();
41
+ return JSON.stringify(markNames) === JSON.stringify(targetNames);
42
+ }), [snapshots, currentMarks]);
43
+ // Auto-capture setup
44
+ useEffect(() => {
45
+ if (!editor || !autoCapture)
46
+ return;
47
+ let timeoutId;
48
+ const handleUpdate = () => {
49
+ clearTimeout(timeoutId);
50
+ timeoutId = setTimeout(captureMarkSnapshot, debounceDelay);
51
+ };
52
+ editor.on('selectionUpdate', handleUpdate);
53
+ editor.on('update', handleUpdate);
54
+ return () => {
55
+ clearTimeout(timeoutId);
56
+ editor.off('selectionUpdate', handleUpdate);
57
+ editor.off('update', handleUpdate);
58
+ };
59
+ }, [editor, autoCapture, debounceDelay, captureMarkSnapshot]);
60
+ return {
61
+ snapshots,
62
+ currentMarks,
63
+ captureMarkSnapshot,
64
+ applySnapshot,
65
+ getSimilarSnapshots,
66
+ clearSnapshots: () => setSnapshots([]),
67
+ };
68
+ }
@@ -0,0 +1,31 @@
1
+ import { Editor } from '@tiptap/react';
2
+ export interface PersistenceConfig {
3
+ /** Storage key */
4
+ key: string;
5
+ /** Storage type */
6
+ storage?: 'localStorage' | 'sessionStorage' | 'indexedDB';
7
+ /** Auto-save delay */
8
+ autoSaveDelay?: number;
9
+ /** Version for data migration */
10
+ version?: number;
11
+ /** Compression */
12
+ compress?: boolean;
13
+ /** Encryption key */
14
+ encryptionKey?: string;
15
+ }
16
+ export interface PersistentData {
17
+ content: string;
18
+ marks: any[];
19
+ metadata: {
20
+ timestamp: number;
21
+ version: number;
22
+ wordCount: number;
23
+ characterCount: number;
24
+ };
25
+ }
26
+ export declare function usePersistence(editor: Editor | null, config: PersistenceConfig): {
27
+ save: () => Promise<void>;
28
+ load: () => Promise<boolean>;
29
+ saveToStorage: (data: PersistentData) => Promise<void>;
30
+ loadFromStorage: () => Promise<PersistentData | null>;
31
+ };
@@ -0,0 +1,169 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { useDebouncedCallback } from 'use-debounce';
3
+ // Helper functions (simplified implementations)
4
+ async function compressString(str) {
5
+ // Implement compression logic
6
+ return str;
7
+ }
8
+ async function decompressString(str) {
9
+ // Implement decompression logic
10
+ return str;
11
+ }
12
+ async function encryptString(str, _key) {
13
+ // Implement encryption logic
14
+ return str;
15
+ }
16
+ async function decryptString(str, _key) {
17
+ // Implement decryption logic
18
+ return str;
19
+ }
20
+ async function saveToIndexedDB(_key, _data) {
21
+ // Implement IndexedDB save
22
+ }
23
+ async function loadFromIndexedDB(_key) {
24
+ // Implement IndexedDB load
25
+ return null;
26
+ }
27
+ function migrateData(data, _targetVersion) {
28
+ // Implement data migration logic
29
+ return data;
30
+ }
31
+ export function usePersistence(editor, config) {
32
+ const { key, storage = 'localStorage', autoSaveDelay = 2000, version = 1, compress = false, encryptionKey, } = config;
33
+ const lastSavedContent = useRef('');
34
+ // Storage operations
35
+ const saveToStorage = useCallback(async (data) => {
36
+ try {
37
+ let serializedData = JSON.stringify(data);
38
+ // Apply compression if enabled
39
+ if (compress && 'CompressionStream' in window) {
40
+ // Use browser compression API if available
41
+ serializedData = await compressString(serializedData);
42
+ }
43
+ // Apply encryption if key provided
44
+ if (encryptionKey) {
45
+ serializedData = await encryptString(serializedData, encryptionKey);
46
+ }
47
+ switch (storage) {
48
+ case 'localStorage':
49
+ localStorage.setItem(key, serializedData);
50
+ break;
51
+ case 'sessionStorage':
52
+ sessionStorage.setItem(key, serializedData);
53
+ break;
54
+ case 'indexedDB':
55
+ await saveToIndexedDB(key, serializedData);
56
+ break;
57
+ default:
58
+ break;
59
+ }
60
+ }
61
+ catch (error) {
62
+ // eslint-disable-next-line no-console
63
+ console.error('Failed to save to storage:', error);
64
+ }
65
+ }, [key, storage, compress, encryptionKey]);
66
+ const loadFromStorage = useCallback(async () => {
67
+ try {
68
+ let serializedData = null;
69
+ switch (storage) {
70
+ case 'localStorage':
71
+ serializedData = localStorage.getItem(key);
72
+ break;
73
+ case 'sessionStorage':
74
+ serializedData = sessionStorage.getItem(key);
75
+ break;
76
+ case 'indexedDB':
77
+ serializedData = await loadFromIndexedDB(key);
78
+ break;
79
+ default:
80
+ break;
81
+ }
82
+ if (!serializedData)
83
+ return null;
84
+ // Apply decryption if key provided
85
+ if (encryptionKey) {
86
+ serializedData = await decryptString(serializedData, encryptionKey);
87
+ }
88
+ // Apply decompression if enabled
89
+ if (compress && 'DecompressionStream' in window) {
90
+ serializedData = await decompressString(serializedData);
91
+ }
92
+ const data = JSON.parse(serializedData);
93
+ // Handle version migration
94
+ if (data.metadata.version !== version) {
95
+ return migrateData(data, version);
96
+ }
97
+ return data;
98
+ }
99
+ catch (error) {
100
+ // eslint-disable-next-line no-console
101
+ console.error('Failed to load from storage:', error);
102
+ return null;
103
+ }
104
+ }, [key, storage, compress, encryptionKey, version]);
105
+ // Auto-save debounced function
106
+ const debouncedSave = useDebouncedCallback(async () => {
107
+ if (!editor)
108
+ return;
109
+ const currentContent = editor.getHTML();
110
+ // Only save if content changed
111
+ if (currentContent === lastSavedContent.current)
112
+ return;
113
+ const { selection, storedMarks } = editor.state;
114
+ const marks = storedMarks || selection.$from.marks();
115
+ const data = {
116
+ content: currentContent,
117
+ marks: marks.map(mark => ({
118
+ type: mark.type.name,
119
+ attrs: mark.attrs,
120
+ })),
121
+ metadata: {
122
+ timestamp: Date.now(),
123
+ version,
124
+ wordCount: editor.state.doc.textContent.split(/\s+/).length,
125
+ characterCount: editor.state.doc.textContent.length,
126
+ },
127
+ };
128
+ await saveToStorage(data);
129
+ lastSavedContent.current = currentContent;
130
+ }, autoSaveDelay);
131
+ // Manual save
132
+ const save = useCallback(async () => {
133
+ debouncedSave.flush();
134
+ }, [debouncedSave]);
135
+ // Load data
136
+ const load = useCallback(async () => {
137
+ if (!editor)
138
+ return false;
139
+ const data = await loadFromStorage();
140
+ if (!data)
141
+ return false;
142
+ // Set content
143
+ editor.commands.setContent(data.content);
144
+ // Restore marks if available
145
+ if (data.marks.length > 0) {
146
+ data.marks.forEach(({ type, attrs }) => {
147
+ editor.commands.setMark(type, attrs);
148
+ });
149
+ }
150
+ lastSavedContent.current = data.content;
151
+ return true;
152
+ }, [editor, loadFromStorage]);
153
+ // Auto-save setup
154
+ useEffect(() => {
155
+ if (!editor)
156
+ return;
157
+ editor.on('update', debouncedSave);
158
+ return () => {
159
+ editor.off('update', debouncedSave);
160
+ debouncedSave.flush(); // Save any pending changes
161
+ };
162
+ }, [editor, debouncedSave]);
163
+ return {
164
+ save,
165
+ load,
166
+ saveToStorage,
167
+ loadFromStorage,
168
+ };
169
+ }
@@ -0,0 +1,6 @@
1
+ import { Editor } from '@tiptap/react';
2
+ export declare function useStyleMemory(editor: Editor | null): {
3
+ storeCurrentMarks: () => void;
4
+ clearStoredMarks: () => void;
5
+ applyStoredMarks: () => void;
6
+ };
@@ -0,0 +1,42 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ export function useStyleMemory(editor) {
3
+ // Store current marks manually
4
+ const storeCurrentMarks = useCallback(() => {
5
+ editor?.commands.storeCurrentMarks();
6
+ }, [editor]);
7
+ // Clear stored marks
8
+ const clearStoredMarks = useCallback(() => {
9
+ editor?.commands.clearStoredMarks();
10
+ }, [editor]);
11
+ // Apply stored marks
12
+ const applyStoredMarks = useCallback(() => {
13
+ editor?.commands.applyStoredMarks();
14
+ }, [editor]);
15
+ // Auto-store marks when selection changes
16
+ useEffect(() => {
17
+ if (!editor)
18
+ return;
19
+ const handleSelectionUpdate = () => {
20
+ const { selection, doc } = editor.state;
21
+ // Only store if editor has content and selection has marks
22
+ if (doc.content.size > 4 && !selection.empty) {
23
+ const { $from } = selection;
24
+ const marks = editor.state.storedMarks || $from.marks();
25
+ if (marks.length > 0) {
26
+ storeCurrentMarks();
27
+ }
28
+ }
29
+ };
30
+ editor.on('selectionUpdate', handleSelectionUpdate);
31
+ editor.on('update', handleSelectionUpdate);
32
+ return () => {
33
+ editor.off('selectionUpdate', handleSelectionUpdate);
34
+ editor.off('update', handleSelectionUpdate);
35
+ };
36
+ }, [editor, storeCurrentMarks]);
37
+ return {
38
+ storeCurrentMarks,
39
+ clearStoredMarks,
40
+ applyStoredMarks,
41
+ };
42
+ }
@@ -0,0 +1,34 @@
1
+ import { Editor } from '@tiptap/react';
2
+ export interface StylePreset {
3
+ id: string;
4
+ name: string;
5
+ description?: string;
6
+ marks: Array<{
7
+ type: string;
8
+ attrs: Record<string, any>;
9
+ }>;
10
+ textStyle: {
11
+ fontSize?: string;
12
+ fontFamily?: string;
13
+ color?: string;
14
+ backgroundColor?: string;
15
+ fontWeight?: string | number;
16
+ };
17
+ createdAt: number;
18
+ usageCount: number;
19
+ }
20
+ export interface UseStylePresetsOptions {
21
+ /** Storage key for presets */
22
+ storageKey?: string;
23
+ /** Maximum number of presets */
24
+ maxPresets?: number;
25
+ /** Default presets */
26
+ defaultPresets?: Omit<StylePreset, 'id' | 'createdAt' | 'usageCount'>[];
27
+ }
28
+ export declare function useStylePresets(editor: Editor | null, options?: UseStylePresetsOptions): {
29
+ presets: StylePreset[];
30
+ createPreset: (name: string, description?: string) => StylePreset | undefined;
31
+ applyPreset: (presetId: string) => void;
32
+ deletePreset: (presetId: string) => void;
33
+ getPopularPresets: (limit?: number) => StylePreset[];
34
+ };