@intlayer/design-system 9.0.0-canary.0 → 9.0.0-canary.2

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 (249) hide show
  1. package/dist/esm/api/hooks/ai.mjs +20 -1
  2. package/dist/esm/api/hooks/ai.mjs.map +1 -1
  3. package/dist/esm/api/hooks/asset.mjs +78 -0
  4. package/dist/esm/api/hooks/asset.mjs.map +1 -0
  5. package/dist/esm/api/index.mjs +6 -5
  6. package/dist/esm/api/useIntlayerAPI.mjs +6 -1
  7. package/dist/esm/api/useIntlayerAPI.mjs.map +1 -1
  8. package/dist/esm/components/Command/index.mjs +8 -8
  9. package/dist/esm/components/Command/index.mjs.map +1 -1
  10. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  11. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs +132 -35
  12. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +1 -1
  13. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +33 -11
  14. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  15. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +146 -35
  16. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs.map +1 -1
  17. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.mjs +308 -0
  18. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.mjs.map +1 -1
  19. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs +12 -2
  20. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.mjs.map +1 -1
  21. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +460 -20
  22. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
  23. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.mjs +506 -0
  24. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.mjs.map +1 -1
  25. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +10 -6
  26. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
  27. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
  28. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +9 -9
  29. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
  30. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
  31. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs.map +1 -1
  32. package/dist/esm/components/DictionaryFieldEditor/contentEditor.content.mjs +79 -0
  33. package/dist/esm/components/DictionaryFieldEditor/contentEditor.content.mjs.map +1 -0
  34. package/dist/esm/components/Form/Form.mjs +3 -43
  35. package/dist/esm/components/Form/Form.mjs.map +1 -1
  36. package/dist/esm/components/Form/elements/FormElement.mjs +2 -3
  37. package/dist/esm/components/Form/elements/FormElement.mjs.map +1 -1
  38. package/dist/esm/components/Form/elements/MultiselectElement.mjs +2 -3
  39. package/dist/esm/components/Form/elements/MultiselectElement.mjs.map +1 -1
  40. package/dist/esm/components/Form/elements/OTPElement.mjs +3 -4
  41. package/dist/esm/components/Form/elements/OTPElement.mjs.map +1 -1
  42. package/dist/esm/components/Form/elements/SelectElement.mjs +2 -3
  43. package/dist/esm/components/Form/elements/SelectElement.mjs.map +1 -1
  44. package/dist/esm/components/Form/elements/SwitchSelectorElement.mjs +2 -3
  45. package/dist/esm/components/Form/elements/SwitchSelectorElement.mjs.map +1 -1
  46. package/dist/esm/components/Form/index.mjs +21 -2
  47. package/dist/esm/components/Form/layout/FormItemLayout.mjs +8 -5
  48. package/dist/esm/components/Form/layout/FormItemLayout.mjs.map +1 -1
  49. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs +3 -8
  50. package/dist/esm/components/LanguageBackground/LanguageBackground.mjs.map +1 -1
  51. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +1 -1
  52. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +1 -1
  53. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  54. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs +113 -0
  55. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -0
  56. package/dist/esm/components/MarkdownEditor/extensions.mjs +103 -0
  57. package/dist/esm/components/MarkdownEditor/extensions.mjs.map +1 -0
  58. package/dist/esm/components/MarkdownEditor/generative/AICompletionCommands.mjs +51 -0
  59. package/dist/esm/components/MarkdownEditor/generative/AICompletionCommands.mjs.map +1 -0
  60. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +100 -0
  61. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs.map +1 -0
  62. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +81 -0
  63. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs.map +1 -0
  64. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +47 -0
  65. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs.map +1 -0
  66. package/dist/esm/components/MarkdownEditor/generative/useAICompletion.mjs +83 -0
  67. package/dist/esm/components/MarkdownEditor/generative/useAICompletion.mjs.map +1 -0
  68. package/dist/esm/components/MarkdownEditor/imageUpload.mjs +47 -0
  69. package/dist/esm/components/MarkdownEditor/imageUpload.mjs.map +1 -0
  70. package/dist/esm/components/MarkdownEditor/index.mjs +4 -0
  71. package/dist/esm/components/MarkdownEditor/markdownEditor.content.mjs +1140 -0
  72. package/dist/esm/components/MarkdownEditor/markdownEditor.content.mjs.map +1 -0
  73. package/dist/esm/components/MarkdownEditor/novel/components/editor-bubble-item.mjs +20 -0
  74. package/dist/esm/components/MarkdownEditor/novel/components/editor-bubble-item.mjs.map +1 -0
  75. package/dist/esm/components/MarkdownEditor/novel/components/editor-bubble.mjs +39 -0
  76. package/dist/esm/components/MarkdownEditor/novel/components/editor-bubble.mjs.map +1 -0
  77. package/dist/esm/components/MarkdownEditor/novel/components/editor-command-item.mjs +25 -0
  78. package/dist/esm/components/MarkdownEditor/novel/components/editor-command-item.mjs.map +1 -0
  79. package/dist/esm/components/MarkdownEditor/novel/components/editor-command.mjs +61 -0
  80. package/dist/esm/components/MarkdownEditor/novel/components/editor-command.mjs.map +1 -0
  81. package/dist/esm/components/MarkdownEditor/novel/components/editor.mjs +27 -0
  82. package/dist/esm/components/MarkdownEditor/novel/components/editor.mjs.map +1 -0
  83. package/dist/esm/components/MarkdownEditor/novel/components/index.mjs +8 -0
  84. package/dist/esm/components/MarkdownEditor/novel/extensions/ai-highlight.mjs +74 -0
  85. package/dist/esm/components/MarkdownEditor/novel/extensions/ai-highlight.mjs.map +1 -0
  86. package/dist/esm/components/MarkdownEditor/novel/extensions/custom-keymap.mjs +37 -0
  87. package/dist/esm/components/MarkdownEditor/novel/extensions/custom-keymap.mjs.map +1 -0
  88. package/dist/esm/components/MarkdownEditor/novel/extensions/image-resizer.mjs +49 -0
  89. package/dist/esm/components/MarkdownEditor/novel/extensions/image-resizer.mjs.map +1 -0
  90. package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs +51 -0
  91. package/dist/esm/components/MarkdownEditor/novel/extensions/index.mjs.map +1 -0
  92. package/dist/esm/components/MarkdownEditor/novel/extensions/slash-command.mjs +79 -0
  93. package/dist/esm/components/MarkdownEditor/novel/extensions/slash-command.mjs.map +1 -0
  94. package/dist/esm/components/MarkdownEditor/novel/extensions/updated-image.mjs +17 -0
  95. package/dist/esm/components/MarkdownEditor/novel/extensions/updated-image.mjs.map +1 -0
  96. package/dist/esm/components/MarkdownEditor/novel/index.mjs +17 -0
  97. package/dist/esm/components/MarkdownEditor/novel/plugins/index.mjs +3 -0
  98. package/dist/esm/components/MarkdownEditor/novel/plugins/upload-images.mjs +92 -0
  99. package/dist/esm/components/MarkdownEditor/novel/plugins/upload-images.mjs.map +1 -0
  100. package/dist/esm/components/MarkdownEditor/novel/utils/atoms.mjs +39 -0
  101. package/dist/esm/components/MarkdownEditor/novel/utils/atoms.mjs.map +1 -0
  102. package/dist/esm/components/MarkdownEditor/novel/utils/index.mjs +38 -0
  103. package/dist/esm/components/MarkdownEditor/novel/utils/index.mjs.map +1 -0
  104. package/dist/esm/components/MarkdownEditor/selectors/ColorSelector.mjs +176 -0
  105. package/dist/esm/components/MarkdownEditor/selectors/ColorSelector.mjs.map +1 -0
  106. package/dist/esm/components/MarkdownEditor/selectors/LinkSelector.mjs +95 -0
  107. package/dist/esm/components/MarkdownEditor/selectors/LinkSelector.mjs.map +1 -0
  108. package/dist/esm/components/MarkdownEditor/selectors/NodeSelector.mjs +139 -0
  109. package/dist/esm/components/MarkdownEditor/selectors/NodeSelector.mjs.map +1 -0
  110. package/dist/esm/components/MarkdownEditor/selectors/TextButtons.mjs +81 -0
  111. package/dist/esm/components/MarkdownEditor/selectors/TextButtons.mjs.map +1 -0
  112. package/dist/esm/components/MarkdownEditor/slashCommand.mjs +161 -0
  113. package/dist/esm/components/MarkdownEditor/slashCommand.mjs.map +1 -0
  114. package/dist/esm/components/Modal/Modal.mjs +2 -2
  115. package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
  116. package/dist/esm/components/Pagination/Pagination.mjs +1 -1
  117. package/dist/esm/components/Pattern/GridDistortionPattern.mjs +275 -0
  118. package/dist/esm/components/Pattern/GridDistortionPattern.mjs.map +1 -0
  119. package/dist/esm/components/Pattern/index.mjs +2 -1
  120. package/dist/esm/components/RightDrawer/RightDrawer.mjs +3 -3
  121. package/dist/esm/components/Tab/Tab.mjs +1 -1
  122. package/dist/esm/components/Table/Table.mjs +1 -1
  123. package/dist/esm/components/Table/Table.mjs.map +1 -1
  124. package/dist/esm/components/Tag/index.mjs +0 -9
  125. package/dist/esm/components/Tag/index.mjs.map +1 -1
  126. package/dist/esm/components/Toaster/Toast.mjs +1 -1
  127. package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
  128. package/dist/esm/components/index.mjs +27 -6
  129. package/dist/esm/hooks/index.mjs +8 -8
  130. package/dist/esm/hooks/useIsMounted.mjs +4 -2
  131. package/dist/esm/hooks/useIsMounted.mjs.map +1 -1
  132. package/dist/esm/routes.mjs +2 -1
  133. package/dist/esm/routes.mjs.map +1 -1
  134. package/dist/types/api/hooks/ai.d.ts +5 -1
  135. package/dist/types/api/hooks/ai.d.ts.map +1 -1
  136. package/dist/types/api/hooks/asset.d.ts +27 -0
  137. package/dist/types/api/hooks/asset.d.ts.map +1 -0
  138. package/dist/types/api/hooks/audit.d.ts +1 -1
  139. package/dist/types/api/index.d.ts +4 -3
  140. package/dist/types/api/useIntlayerAPI.d.ts +15 -3
  141. package/dist/types/api/useIntlayerAPI.d.ts.map +1 -1
  142. package/dist/types/components/Badge/index.d.ts +1 -1
  143. package/dist/types/components/Breadcrumb/index.d.ts +1 -1
  144. package/dist/types/components/Button/Button.d.ts +3 -3
  145. package/dist/types/components/CollapsibleTable/CollapsibleTable.d.ts +2 -2
  146. package/dist/types/components/Command/index.d.ts.map +1 -1
  147. package/dist/types/components/Container/index.d.ts +1 -1
  148. package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts.map +1 -1
  149. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  150. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.d.ts.map +1 -1
  151. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts +308 -0
  152. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/dictionaryCreationForm.content.d.ts.map +1 -1
  153. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts +12 -1
  154. package/dist/types/components/DictionaryFieldEditor/DictionaryCreationForm/useDictionaryFormSchema.d.ts.map +1 -1
  155. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.d.ts +2 -0
  156. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.d.ts.map +1 -1
  157. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts +506 -0
  158. package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/dictionaryDetails.content.d.ts.map +1 -1
  159. package/dist/types/components/DictionaryFieldEditor/DictionaryFieldEditor.d.ts +2 -1
  160. package/dist/types/components/DictionaryFieldEditor/DictionaryFieldEditor.d.ts.map +1 -1
  161. package/dist/types/components/DictionaryFieldEditor/contentEditor.content.d.ts +80 -0
  162. package/dist/types/components/DictionaryFieldEditor/contentEditor.content.d.ts.map +1 -0
  163. package/dist/types/components/Form/Form.d.ts +27 -47
  164. package/dist/types/components/Form/Form.d.ts.map +1 -1
  165. package/dist/types/components/Form/elements/FormElement.d.ts.map +1 -1
  166. package/dist/types/components/Form/elements/MultiselectElement.d.ts.map +1 -1
  167. package/dist/types/components/Form/elements/OTPElement.d.ts.map +1 -1
  168. package/dist/types/components/Form/elements/SelectElement.d.ts.map +1 -1
  169. package/dist/types/components/Form/elements/SwitchSelectorElement.d.ts.map +1 -1
  170. package/dist/types/components/Form/index.d.ts +22 -3
  171. package/dist/types/components/Form/layout/FormItemLayout.d.ts.map +1 -1
  172. package/dist/types/components/Input/Checkbox.d.ts +3 -3
  173. package/dist/types/components/Input/Input.d.ts +1 -1
  174. package/dist/types/components/Input/Radio.d.ts +3 -3
  175. package/dist/types/components/LanguageBackground/LanguageBackground.d.ts.map +1 -1
  176. package/dist/types/components/Link/Link.d.ts +1 -1
  177. package/dist/types/components/MarkdownEditor/MarkdownEditor.d.ts +31 -0
  178. package/dist/types/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
  179. package/dist/types/components/MarkdownEditor/extensions.d.ts +11 -0
  180. package/dist/types/components/MarkdownEditor/extensions.d.ts.map +1 -0
  181. package/dist/types/components/MarkdownEditor/generative/AICompletionCommands.d.ts +18 -0
  182. package/dist/types/components/MarkdownEditor/generative/AICompletionCommands.d.ts.map +1 -0
  183. package/dist/types/components/MarkdownEditor/generative/AISelector.d.ts +22 -0
  184. package/dist/types/components/MarkdownEditor/generative/AISelector.d.ts.map +1 -0
  185. package/dist/types/components/MarkdownEditor/generative/AISelectorCommands.d.ts +17 -0
  186. package/dist/types/components/MarkdownEditor/generative/AISelectorCommands.d.ts.map +1 -0
  187. package/dist/types/components/MarkdownEditor/generative/GenerativeMenuSwitch.d.ts +26 -0
  188. package/dist/types/components/MarkdownEditor/generative/GenerativeMenuSwitch.d.ts.map +1 -0
  189. package/dist/types/components/MarkdownEditor/generative/useAICompletion.d.ts +30 -0
  190. package/dist/types/components/MarkdownEditor/generative/useAICompletion.d.ts.map +1 -0
  191. package/dist/types/components/MarkdownEditor/imageUpload.d.ts +29 -0
  192. package/dist/types/components/MarkdownEditor/imageUpload.d.ts.map +1 -0
  193. package/dist/types/components/MarkdownEditor/index.d.ts +3 -0
  194. package/dist/types/components/MarkdownEditor/markdownEditor.content.d.ts +1135 -0
  195. package/dist/types/components/MarkdownEditor/markdownEditor.content.d.ts.map +1 -0
  196. package/dist/types/components/MarkdownEditor/novel/components/editor-bubble-item.d.ts +14 -0
  197. package/dist/types/components/MarkdownEditor/novel/components/editor-bubble-item.d.ts.map +1 -0
  198. package/dist/types/components/MarkdownEditor/novel/components/editor-bubble.d.ts +18 -0
  199. package/dist/types/components/MarkdownEditor/novel/components/editor-bubble.d.ts.map +1 -0
  200. package/dist/types/components/MarkdownEditor/novel/components/editor-command-item.d.ts +28 -0
  201. package/dist/types/components/MarkdownEditor/novel/components/editor-command-item.d.ts.map +1 -0
  202. package/dist/types/components/MarkdownEditor/novel/components/editor-command.d.ts +34 -0
  203. package/dist/types/components/MarkdownEditor/novel/components/editor-command.d.ts.map +1 -0
  204. package/dist/types/components/MarkdownEditor/novel/components/editor.d.ts +23 -0
  205. package/dist/types/components/MarkdownEditor/novel/components/editor.d.ts.map +1 -0
  206. package/dist/types/components/MarkdownEditor/novel/components/index.d.ts +8 -0
  207. package/dist/types/components/MarkdownEditor/novel/extensions/ai-highlight.d.ts +36 -0
  208. package/dist/types/components/MarkdownEditor/novel/extensions/ai-highlight.d.ts.map +1 -0
  209. package/dist/types/components/MarkdownEditor/novel/extensions/custom-keymap.d.ts +17 -0
  210. package/dist/types/components/MarkdownEditor/novel/extensions/custom-keymap.d.ts.map +1 -0
  211. package/dist/types/components/MarkdownEditor/novel/extensions/image-resizer.d.ts +7 -0
  212. package/dist/types/components/MarkdownEditor/novel/extensions/image-resizer.d.ts.map +1 -0
  213. package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts +30 -0
  214. package/dist/types/components/MarkdownEditor/novel/extensions/index.d.ts.map +1 -0
  215. package/dist/types/components/MarkdownEditor/novel/extensions/slash-command.d.ts +38 -0
  216. package/dist/types/components/MarkdownEditor/novel/extensions/slash-command.d.ts.map +1 -0
  217. package/dist/types/components/MarkdownEditor/novel/extensions/updated-image.d.ts +8 -0
  218. package/dist/types/components/MarkdownEditor/novel/extensions/updated-image.d.ts.map +1 -0
  219. package/dist/types/components/MarkdownEditor/novel/index.d.ts +16 -0
  220. package/dist/types/components/MarkdownEditor/novel/plugins/index.d.ts +2 -0
  221. package/dist/types/components/MarkdownEditor/novel/plugins/upload-images.d.ts +22 -0
  222. package/dist/types/components/MarkdownEditor/novel/plugins/upload-images.d.ts.map +1 -0
  223. package/dist/types/components/MarkdownEditor/novel/utils/atoms.d.ts +21 -0
  224. package/dist/types/components/MarkdownEditor/novel/utils/atoms.d.ts.map +1 -0
  225. package/dist/types/components/MarkdownEditor/novel/utils/index.d.ts +10 -0
  226. package/dist/types/components/MarkdownEditor/novel/utils/index.d.ts.map +1 -0
  227. package/dist/types/components/MarkdownEditor/selectors/ColorSelector.d.ts +17 -0
  228. package/dist/types/components/MarkdownEditor/selectors/ColorSelector.d.ts.map +1 -0
  229. package/dist/types/components/MarkdownEditor/selectors/LinkSelector.d.ts +15 -0
  230. package/dist/types/components/MarkdownEditor/selectors/LinkSelector.d.ts.map +1 -0
  231. package/dist/types/components/MarkdownEditor/selectors/NodeSelector.d.ts +22 -0
  232. package/dist/types/components/MarkdownEditor/selectors/NodeSelector.d.ts.map +1 -0
  233. package/dist/types/components/MarkdownEditor/selectors/TextButtons.d.ts +11 -0
  234. package/dist/types/components/MarkdownEditor/selectors/TextButtons.d.ts.map +1 -0
  235. package/dist/types/components/MarkdownEditor/slashCommand.d.ts +29 -0
  236. package/dist/types/components/MarkdownEditor/slashCommand.d.ts.map +1 -0
  237. package/dist/types/components/Pattern/GridDistortionPattern.d.ts +15 -0
  238. package/dist/types/components/Pattern/GridDistortionPattern.d.ts.map +1 -0
  239. package/dist/types/components/Pattern/index.d.ts +2 -1
  240. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +1 -1
  241. package/dist/types/components/SwitchSelector/VerticalSwitchSelector.d.ts +1 -1
  242. package/dist/types/components/Tag/index.d.ts +2 -2
  243. package/dist/types/components/Tag/index.d.ts.map +1 -1
  244. package/dist/types/components/Toaster/Toast.d.ts +1 -1
  245. package/dist/types/components/index.d.ts +24 -3
  246. package/dist/types/routes.d.ts +2 -1
  247. package/dist/types/routes.d.ts.map +1 -1
  248. package/package.json +63 -32
  249. package/src/components/MarkdownEditor/markdown-editor.css +297 -0
@@ -0,0 +1,79 @@
1
+ import { EditorCommandOut } from "../components/editor-command.mjs";
2
+ import { ReactRenderer } from "@tiptap/react";
3
+ import { Extension } from "@tiptap/core";
4
+ import Suggestion from "@tiptap/suggestion";
5
+ import tippy from "tippy.js";
6
+
7
+ //#region src/components/MarkdownEditor/novel/extensions/slash-command.tsx
8
+ const Command = Extension.create({
9
+ name: "slash-command",
10
+ addOptions() {
11
+ return { suggestion: {
12
+ char: "/",
13
+ command: ({ editor, range, props }) => {
14
+ props.command({
15
+ editor,
16
+ range
17
+ });
18
+ }
19
+ } };
20
+ },
21
+ addProseMirrorPlugins() {
22
+ return [Suggestion({
23
+ editor: this.editor,
24
+ ...this.options.suggestion
25
+ })];
26
+ }
27
+ });
28
+ const renderItems = (elementRef) => {
29
+ let component = null;
30
+ let popup = null;
31
+ return {
32
+ onStart: (props) => {
33
+ component = new ReactRenderer(EditorCommandOut, {
34
+ props,
35
+ editor: props.editor
36
+ });
37
+ const { selection } = props.editor.state;
38
+ if (selection.$from.node(selection.$from.depth).type.name === "codeBlock") return false;
39
+ popup = tippy("body", {
40
+ getReferenceClientRect: props.clientRect,
41
+ appendTo: () => elementRef ? elementRef.current : document.body,
42
+ content: component.element,
43
+ showOnCreate: true,
44
+ interactive: true,
45
+ trigger: "manual",
46
+ placement: "bottom-start"
47
+ });
48
+ },
49
+ onUpdate: (props) => {
50
+ component?.updateProps(props);
51
+ popup?.[0]?.setProps({ getReferenceClientRect: props.clientRect });
52
+ },
53
+ onKeyDown: (props) => {
54
+ if (props.event.key === "Escape") {
55
+ popup?.[0]?.hide();
56
+ return true;
57
+ }
58
+ return component?.ref?.onKeyDown(props);
59
+ },
60
+ onExit: () => {
61
+ popup?.[0]?.destroy();
62
+ component?.destroy();
63
+ }
64
+ };
65
+ };
66
+ const createSuggestionItems = (items) => items;
67
+ const handleCommandNavigation = (event) => {
68
+ if ([
69
+ "ArrowUp",
70
+ "ArrowDown",
71
+ "Enter"
72
+ ].includes(event.key)) {
73
+ if (document.querySelector("#slash-command")) return true;
74
+ }
75
+ };
76
+
77
+ //#endregion
78
+ export { Command, createSuggestionItems, handleCommandNavigation, renderItems };
79
+ //# sourceMappingURL=slash-command.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-command.mjs","names":[],"sources":["../../../../../../src/components/MarkdownEditor/novel/extensions/slash-command.tsx"],"sourcesContent":["import type { Editor, Range } from '@tiptap/core';\nimport { Extension } from '@tiptap/core';\nimport { ReactRenderer } from '@tiptap/react';\nimport Suggestion, { type SuggestionOptions } from '@tiptap/suggestion';\nimport type { ReactNode, RefObject } from 'react';\nimport tippy, {\n type GetReferenceClientRect,\n type Instance,\n type Props,\n} from 'tippy.js';\nimport { EditorCommandOut } from '../components/editor-command';\n\nconst Command = Extension.create({\n name: 'slash-command',\n addOptions() {\n return {\n suggestion: {\n char: '/',\n command: ({ editor, range, props }) => {\n props.command({ editor, range });\n },\n } as SuggestionOptions,\n };\n },\n addProseMirrorPlugins() {\n return [\n Suggestion({\n editor: this.editor,\n ...this.options.suggestion,\n }),\n ];\n },\n});\n\nconst renderItems = (elementRef?: RefObject<Element> | null) => {\n let component: ReactRenderer | null = null;\n let popup: Instance<Props>[] | null = null;\n\n return {\n onStart: (props: { editor: Editor; clientRect: DOMRect }) => {\n component = new ReactRenderer(EditorCommandOut, {\n props,\n editor: props.editor,\n });\n\n const { selection } = props.editor.state;\n\n const parentNode = selection.$from.node(selection.$from.depth);\n const blockType = parentNode.type.name;\n\n if (blockType === 'codeBlock') {\n return false;\n }\n\n // @ts-ignore\n popup = tippy('body', {\n getReferenceClientRect: props.clientRect,\n appendTo: () => (elementRef ? elementRef.current : document.body),\n content: component.element,\n showOnCreate: true,\n interactive: true,\n trigger: 'manual',\n placement: 'bottom-start',\n });\n },\n onUpdate: (props: {\n editor: Editor;\n clientRect: GetReferenceClientRect;\n }) => {\n component?.updateProps(props);\n\n popup?.[0]?.setProps({\n getReferenceClientRect: props.clientRect,\n });\n },\n\n onKeyDown: (props: { event: KeyboardEvent }) => {\n if (props.event.key === 'Escape') {\n popup?.[0]?.hide();\n\n return true;\n }\n\n // @ts-ignore\n return component?.ref?.onKeyDown(props);\n },\n onExit: () => {\n popup?.[0]?.destroy();\n component?.destroy();\n },\n };\n};\n\nexport interface SuggestionItem {\n title: string;\n description: string;\n icon: ReactNode;\n searchTerms?: string[];\n command?: (props: { editor: Editor; range: Range }) => void;\n}\n\nexport const createSuggestionItems = (items: SuggestionItem[]) => items;\n\nexport const handleCommandNavigation = (event: KeyboardEvent) => {\n if (['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key)) {\n const slashCommand = document.querySelector('#slash-command');\n if (slashCommand) {\n return true;\n }\n }\n};\n\nexport { Command, renderItems };\n"],"mappings":";;;;;;;AAYA,MAAM,UAAU,UAAU,OAAO;CAC/B,MAAM;CACN,aAAa;AACX,SAAO,EACL,YAAY;GACV,MAAM;GACN,UAAU,EAAE,QAAQ,OAAO,YAAY;AACrC,UAAM,QAAQ;KAAE;KAAQ;KAAO,CAAC;;GAEnC,EACF;;CAEH,wBAAwB;AACtB,SAAO,CACL,WAAW;GACT,QAAQ,KAAK;GACb,GAAG,KAAK,QAAQ;GACjB,CAAC,CACH;;CAEJ,CAAC;AAEF,MAAM,eAAe,eAA2C;CAC9D,IAAI,YAAkC;CACtC,IAAI,QAAkC;AAEtC,QAAO;EACL,UAAU,UAAmD;AAC3D,eAAY,IAAI,cAAc,kBAAkB;IAC9C;IACA,QAAQ,MAAM;IACf,CAAC;GAEF,MAAM,EAAE,cAAc,MAAM,OAAO;AAKnC,OAHmB,UAAU,MAAM,KAAK,UAAU,MAAM,MAC5B,CAAC,KAAK,SAEhB,YAChB,QAAO;AAIT,WAAQ,MAAM,QAAQ;IACpB,wBAAwB,MAAM;IAC9B,gBAAiB,aAAa,WAAW,UAAU,SAAS;IAC5D,SAAS,UAAU;IACnB,cAAc;IACd,aAAa;IACb,SAAS;IACT,WAAW;IACZ,CAAC;;EAEJ,WAAW,UAGL;AACJ,cAAW,YAAY,MAAM;AAE7B,WAAQ,IAAI,SAAS,EACnB,wBAAwB,MAAM,YAC/B,CAAC;;EAGJ,YAAY,UAAoC;AAC9C,OAAI,MAAM,MAAM,QAAQ,UAAU;AAChC,YAAQ,IAAI,MAAM;AAElB,WAAO;;AAIT,UAAO,WAAW,KAAK,UAAU,MAAM;;EAEzC,cAAc;AACZ,WAAQ,IAAI,SAAS;AACrB,cAAW,SAAS;;EAEvB;;AAWH,MAAa,yBAAyB,UAA4B;AAElE,MAAa,2BAA2B,UAAyB;AAC/D,KAAI;EAAC;EAAW;EAAa;EAAQ,CAAC,SAAS,MAAM,IAAI,EAEvD;MADqB,SAAS,cAAc,iBAC5B,CACd,QAAO"}
@@ -0,0 +1,17 @@
1
+ import Image from "@tiptap/extension-image";
2
+
3
+ //#region src/components/MarkdownEditor/novel/extensions/updated-image.ts
4
+ const UpdatedImage = Image.extend({
5
+ name: "image",
6
+ addAttributes() {
7
+ return {
8
+ ...this.parent?.(),
9
+ width: { default: null },
10
+ height: { default: null }
11
+ };
12
+ }
13
+ });
14
+
15
+ //#endregion
16
+ export { UpdatedImage as default };
17
+ //# sourceMappingURL=updated-image.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updated-image.mjs","names":[],"sources":["../../../../../../src/components/MarkdownEditor/novel/extensions/updated-image.ts"],"sourcesContent":["import Image from '@tiptap/extension-image';\n\nconst UpdatedImage = Image.extend({\n name: 'image',\n addAttributes() {\n return {\n ...this.parent?.(),\n width: {\n default: null,\n },\n height: {\n default: null,\n },\n };\n },\n});\n\nexport default UpdatedImage;\n"],"mappings":";;;AAEA,MAAM,eAAe,MAAM,OAAO;CAChC,MAAM;CACN,gBAAgB;AACd,SAAO;GACL,GAAG,KAAK,UAAU;GAClB,OAAO,EACL,SAAS,MACV;GACD,QAAQ,EACN,SAAS,MACV;GACF;;CAEJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { queryStore, rangeStore, useQuery, useRange } from "./utils/atoms.mjs";
2
+ import { EditorCommand, EditorCommandList } from "./components/editor-command.mjs";
3
+ import { EditorContent, EditorRoot } from "./components/editor.mjs";
4
+ import { EditorBubble } from "./components/editor-bubble.mjs";
5
+ import { EditorBubbleItem } from "./components/editor-bubble-item.mjs";
6
+ import { EditorCommandEmpty, EditorCommandItem } from "./components/editor-command-item.mjs";
7
+ import { useEditor } from "./components/index.mjs";
8
+ import CustomKeymap from "./extensions/custom-keymap.mjs";
9
+ import { ImageResizer } from "./extensions/image-resizer.mjs";
10
+ import UpdatedImage from "./extensions/updated-image.mjs";
11
+ import { AIHighlight, addAIHighlight, removeAIHighlight } from "./extensions/ai-highlight.mjs";
12
+ import { Command, createSuggestionItems, handleCommandNavigation, renderItems } from "./extensions/slash-command.mjs";
13
+ import { CharacterCount, Color, GlobalDragHandle, HighlightExtension, HorizontalRule as Horizontal, InputRule, MarkdownExtension, Placeholder as PlaceholderExtension, StarterKit, TaskItem, TaskList, TextStyle, TiptapImage, TiptapLink, TiptapUnderline, Youtube } from "./extensions/index.mjs";
14
+ import { UploadImagesPlugin, createImageUpload, handleImageDrop, handleImagePaste } from "./plugins/upload-images.mjs";
15
+ import { getAllContent, getPrevText, getUrlFromString, isValidUrl } from "./utils/index.mjs";
16
+
17
+ export { AIHighlight, CharacterCount, Color, Command, CustomKeymap, EditorBubble, EditorBubbleItem, EditorCommand, EditorCommandEmpty, EditorCommandItem, EditorCommandList, EditorContent, EditorRoot, GlobalDragHandle, HighlightExtension, Horizontal as HorizontalRule, ImageResizer, InputRule, MarkdownExtension, PlaceholderExtension as Placeholder, StarterKit, TaskItem, TaskList, TextStyle, TiptapImage, TiptapLink, TiptapUnderline, UpdatedImage, UploadImagesPlugin, Youtube, addAIHighlight, createImageUpload, createSuggestionItems, getAllContent, getPrevText, getUrlFromString, handleCommandNavigation, handleImageDrop, handleImagePaste, isValidUrl, queryStore, rangeStore, removeAIHighlight, renderItems, useEditor, useQuery, useRange };
@@ -0,0 +1,3 @@
1
+ import { UploadImagesPlugin, createImageUpload, handleImageDrop, handleImagePaste } from "./upload-images.mjs";
2
+
3
+ export { UploadImagesPlugin, createImageUpload, handleImageDrop, handleImagePaste };
@@ -0,0 +1,92 @@
1
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
2
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
3
+
4
+ //#region src/components/MarkdownEditor/novel/plugins/upload-images.tsx
5
+ const uploadKey = new PluginKey("upload-image");
6
+ const UploadImagesPlugin = ({ imageClass }) => new Plugin({
7
+ key: uploadKey,
8
+ state: {
9
+ init() {
10
+ return DecorationSet.empty;
11
+ },
12
+ apply(tr, set) {
13
+ set = set.map(tr.mapping, tr.doc);
14
+ const action = tr.getMeta(this);
15
+ if (action?.add) {
16
+ const { id, pos, src } = action.add;
17
+ const placeholder = document.createElement("div");
18
+ placeholder.setAttribute("class", "img-placeholder");
19
+ const image = document.createElement("img");
20
+ image.setAttribute("class", imageClass);
21
+ image.src = src;
22
+ placeholder.appendChild(image);
23
+ const deco = Decoration.widget(pos + 1, placeholder, { id });
24
+ set = set.add(tr.doc, [deco]);
25
+ } else if (action?.remove) set = set.remove(set.find(void 0, void 0, (spec) => spec.id === action.remove.id));
26
+ return set;
27
+ }
28
+ },
29
+ props: { decorations(state) {
30
+ return this.getState(state);
31
+ } }
32
+ });
33
+ function findPlaceholder(state, id) {
34
+ const found = uploadKey.getState(state).find(void 0, void 0, (spec) => spec.id === id);
35
+ return found.length ? found[0]?.from : null;
36
+ }
37
+ const createImageUpload = ({ validateFn, onUpload }) => (file, view, pos) => {
38
+ if (!validateFn?.(file)) return;
39
+ const id = {};
40
+ const tr = view.state.tr;
41
+ if (!tr.selection.empty) tr.deleteSelection();
42
+ const reader = new FileReader();
43
+ reader.readAsDataURL(file);
44
+ reader.onload = () => {
45
+ tr.setMeta(uploadKey, { add: {
46
+ id,
47
+ pos,
48
+ src: reader.result
49
+ } });
50
+ view.dispatch(tr);
51
+ };
52
+ onUpload(file).then((src) => {
53
+ const { schema } = view.state;
54
+ const pos = findPlaceholder(view.state, id);
55
+ if (pos == null) return;
56
+ const imageSrc = typeof src === "object" ? reader.result : src;
57
+ const node = schema.nodes.image?.create({ src: imageSrc });
58
+ if (!node) return;
59
+ const transaction = view.state.tr.replaceWith(pos, pos, node).setMeta(uploadKey, { remove: { id } });
60
+ view.dispatch(transaction);
61
+ }, () => {
62
+ const transaction = view.state.tr.delete(pos, pos).setMeta(uploadKey, { remove: { id } });
63
+ view.dispatch(transaction);
64
+ });
65
+ };
66
+ const handleImagePaste = (view, event, uploadFn) => {
67
+ if (event.clipboardData?.files.length) {
68
+ event.preventDefault();
69
+ const [file] = Array.from(event.clipboardData.files);
70
+ const pos = view.state.selection.from;
71
+ if (file) uploadFn(file, view, pos);
72
+ return true;
73
+ }
74
+ return false;
75
+ };
76
+ const handleImageDrop = (view, event, moved, uploadFn) => {
77
+ if (!moved && event.dataTransfer?.files.length) {
78
+ event.preventDefault();
79
+ const [file] = Array.from(event.dataTransfer.files);
80
+ const coordinates = view.posAtCoords({
81
+ left: event.clientX,
82
+ top: event.clientY
83
+ });
84
+ if (file) uploadFn(file, view, coordinates?.pos ?? -1);
85
+ return true;
86
+ }
87
+ return false;
88
+ };
89
+
90
+ //#endregion
91
+ export { UploadImagesPlugin, createImageUpload, handleImageDrop, handleImagePaste };
92
+ //# sourceMappingURL=upload-images.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-images.mjs","names":[],"sources":["../../../../../../src/components/MarkdownEditor/novel/plugins/upload-images.tsx"],"sourcesContent":["import { type EditorState, Plugin, PluginKey } from '@tiptap/pm/state';\nimport { Decoration, DecorationSet, type EditorView } from '@tiptap/pm/view';\n\nconst uploadKey = new PluginKey('upload-image');\n\nexport const UploadImagesPlugin = ({ imageClass }: { imageClass: string }) =>\n new Plugin({\n key: uploadKey,\n state: {\n init() {\n return DecorationSet.empty;\n },\n apply(tr, set) {\n set = set.map(tr.mapping, tr.doc);\n // See if the transaction adds or removes any placeholders\n //@ts-expect-error - not yet sure what the type I need here\n const action = tr.getMeta(this);\n if (action?.add) {\n const { id, pos, src } = action.add;\n\n const placeholder = document.createElement('div');\n placeholder.setAttribute('class', 'img-placeholder');\n const image = document.createElement('img');\n image.setAttribute('class', imageClass);\n image.src = src;\n placeholder.appendChild(image);\n const deco = Decoration.widget(pos + 1, placeholder, {\n id,\n });\n set = set.add(tr.doc, [deco]);\n } else if (action?.remove) {\n set = set.remove(\n set.find(\n undefined,\n undefined,\n (spec) => spec.id === action.remove.id\n )\n );\n }\n return set;\n },\n },\n props: {\n decorations(state) {\n return this.getState(state);\n },\n },\n });\n\nfunction findPlaceholder(state: EditorState, id: object) {\n const decos = uploadKey.getState(state) as DecorationSet;\n const found = decos.find(undefined, undefined, (spec) => spec.id === id);\n return found.length ? found[0]?.from : null;\n}\n\nexport interface ImageUploadOptions {\n validateFn?: (file: File) => void;\n onUpload: (file: File) => Promise<unknown>;\n}\n\nexport const createImageUpload =\n ({ validateFn, onUpload }: ImageUploadOptions): UploadFn =>\n (file, view, pos) => {\n // check if the file is an image\n const validated = validateFn?.(file);\n if (!validated) return;\n // A fresh object to act as the ID for this upload\n const id = {};\n\n // Replace the selection with a placeholder\n const tr = view.state.tr;\n if (!tr.selection.empty) tr.deleteSelection();\n\n const reader = new FileReader();\n reader.readAsDataURL(file);\n reader.onload = () => {\n tr.setMeta(uploadKey, {\n add: {\n id,\n pos,\n src: reader.result,\n },\n });\n view.dispatch(tr);\n };\n\n onUpload(file).then(\n (src) => {\n const { schema } = view.state;\n\n const pos = findPlaceholder(view.state, id);\n\n // If the content around the placeholder has been deleted, drop\n // the image\n if (pos == null) return;\n\n // Otherwise, insert it at the placeholder's position, and remove\n // the placeholder\n\n // When BLOB_READ_WRITE_TOKEN is not valid or unavailable, read\n // the image locally\n const imageSrc = typeof src === 'object' ? reader.result : src;\n\n const node = schema.nodes.image?.create({ src: imageSrc });\n if (!node) return;\n\n const transaction = view.state.tr\n .replaceWith(pos, pos, node)\n .setMeta(uploadKey, { remove: { id } });\n view.dispatch(transaction);\n },\n () => {\n // Deletes the image placeholder on error\n const transaction = view.state.tr\n .delete(pos, pos)\n .setMeta(uploadKey, { remove: { id } });\n view.dispatch(transaction);\n }\n );\n };\n\nexport type UploadFn = (file: File, view: EditorView, pos: number) => void;\n\nexport const handleImagePaste = (\n view: EditorView,\n event: ClipboardEvent,\n uploadFn: UploadFn\n) => {\n if (event.clipboardData?.files.length) {\n event.preventDefault();\n const [file] = Array.from(event.clipboardData.files);\n const pos = view.state.selection.from;\n\n if (file) uploadFn(file, view, pos);\n return true;\n }\n return false;\n};\n\nexport const handleImageDrop = (\n view: EditorView,\n event: DragEvent,\n moved: boolean,\n uploadFn: UploadFn\n) => {\n if (!moved && event.dataTransfer?.files.length) {\n event.preventDefault();\n const [file] = Array.from(event.dataTransfer.files);\n const coordinates = view.posAtCoords({\n left: event.clientX,\n top: event.clientY,\n });\n // here we deduct 1 from the pos or else the image will create an extra node\n if (file) uploadFn(file, view, coordinates?.pos ?? 0 - 1);\n return true;\n }\n return false;\n};\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { useSyncExternalStore } from "react";
2
+
3
+ //#region src/components/MarkdownEditor/novel/utils/atoms.ts
4
+ /**
5
+ * Minimal module-level observable store.
6
+ *
7
+ * rendered by Tiptap in a separate React root (outside the editor's provider
8
+ * tree), so the query/range state has to live in a plain module singleton that
9
+ * both roots can read and write — no shared React context needed. This is what
10
+ * `useSyncExternalStore` store removes the dependency with identical behaviour.
11
+ */
12
+ const createExternalStore = (initialValue) => {
13
+ let value = initialValue;
14
+ const listeners = /* @__PURE__ */ new Set();
15
+ return {
16
+ getSnapshot: () => value,
17
+ setValue: (next) => {
18
+ if (Object.is(next, value)) return;
19
+ value = next;
20
+ for (const listener of listeners) listener();
21
+ },
22
+ subscribe: (listener) => {
23
+ listeners.add(listener);
24
+ return () => {
25
+ listeners.delete(listener);
26
+ };
27
+ }
28
+ };
29
+ };
30
+ const queryStore = createExternalStore("");
31
+ const rangeStore = createExternalStore(null);
32
+ /** Subscribe to the current slash-command query string. */
33
+ const useQuery = () => useSyncExternalStore(queryStore.subscribe, queryStore.getSnapshot, queryStore.getSnapshot);
34
+ /** Subscribe to the current slash-command range. */
35
+ const useRange = () => useSyncExternalStore(rangeStore.subscribe, rangeStore.getSnapshot, rangeStore.getSnapshot);
36
+
37
+ //#endregion
38
+ export { queryStore, rangeStore, useQuery, useRange };
39
+ //# sourceMappingURL=atoms.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atoms.mjs","names":[],"sources":["../../../../../../src/components/MarkdownEditor/novel/utils/atoms.ts"],"sourcesContent":["import type { Range } from '@tiptap/core';\nimport { useSyncExternalStore } from 'react';\n\ntype Listener = () => void;\n\n/**\n * Minimal module-level observable store.\n *\n * rendered by Tiptap in a separate React root (outside the editor's provider\n * tree), so the query/range state has to live in a plain module singleton that\n * both roots can read and write — no shared React context needed. This is what\n * `useSyncExternalStore` store removes the dependency with identical behaviour.\n */\nconst createExternalStore = <T>(initialValue: T) => {\n let value = initialValue;\n const listeners = new Set<Listener>();\n\n return {\n getSnapshot: (): T => value,\n setValue: (next: T): void => {\n if (Object.is(next, value)) return;\n value = next;\n for (const listener of listeners) listener();\n },\n subscribe: (listener: Listener): (() => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nexport const queryStore = createExternalStore('');\nexport const rangeStore = createExternalStore<Range | null>(null);\n\n/** Subscribe to the current slash-command query string. */\nexport const useQuery = (): string =>\n useSyncExternalStore(\n queryStore.subscribe,\n queryStore.getSnapshot,\n queryStore.getSnapshot\n );\n\n/** Subscribe to the current slash-command range. */\nexport const useRange = (): Range | null =>\n useSyncExternalStore(\n rangeStore.subscribe,\n rangeStore.getSnapshot,\n rangeStore.getSnapshot\n );\n"],"mappings":";;;;;;;;;;;AAaA,MAAM,uBAA0B,iBAAoB;CAClD,IAAI,QAAQ;CACZ,MAAM,4BAAY,IAAI,KAAe;AAErC,QAAO;EACL,mBAAsB;EACtB,WAAW,SAAkB;AAC3B,OAAI,OAAO,GAAG,MAAM,MAAM,CAAE;AAC5B,WAAQ;AACR,QAAK,MAAM,YAAY,UAAW,WAAU;;EAE9C,YAAY,aAAqC;AAC/C,aAAU,IAAI,SAAS;AACvB,gBAAa;AACX,cAAU,OAAO,SAAS;;;EAG/B;;AAGH,MAAa,aAAa,oBAAoB,GAAG;AACjD,MAAa,aAAa,oBAAkC,KAAK;;AAGjE,MAAa,iBACX,qBACE,WAAW,WACX,WAAW,aACX,WAAW,YACZ;;AAGH,MAAa,iBACX,qBACE,WAAW,WACX,WAAW,aACX,WAAW,YACZ"}
@@ -0,0 +1,38 @@
1
+ import { Fragment } from "@tiptap/pm/model";
2
+
3
+ //#region src/components/MarkdownEditor/novel/utils/index.ts
4
+ const isValidUrl = (url) => {
5
+ try {
6
+ new URL(url);
7
+ return true;
8
+ } catch (_e) {
9
+ return false;
10
+ }
11
+ };
12
+ const getUrlFromString = (str) => {
13
+ if (isValidUrl(str)) return str;
14
+ try {
15
+ if (str.includes(".") && !str.includes(" ")) return new URL(`https://${str}`).toString();
16
+ } catch (_e) {
17
+ return null;
18
+ }
19
+ };
20
+ const getPrevText = (editor, position) => {
21
+ const nodes = [];
22
+ editor.state.doc.forEach((node, pos) => {
23
+ if (pos >= position) return;
24
+ nodes.push(node);
25
+ });
26
+ const fragment = Fragment.fromArray(nodes);
27
+ const doc = editor.state.doc.copy(fragment);
28
+ return editor.storage.markdown.serializer.serialize(doc);
29
+ };
30
+ const getAllContent = (editor) => {
31
+ const fragment = editor.state.doc.content;
32
+ const doc = editor.state.doc.copy(fragment);
33
+ return editor.storage.markdown.serializer.serialize(doc);
34
+ };
35
+
36
+ //#endregion
37
+ export { getAllContent, getPrevText, getUrlFromString, isValidUrl };
38
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../src/components/MarkdownEditor/novel/utils/index.ts"],"sourcesContent":["import { Fragment, type Node } from '@tiptap/pm/model';\nimport type { EditorInstance } from '../components';\n\nexport const isValidUrl = (url: string) => {\n try {\n new URL(url);\n return true;\n } catch (_e) {\n return false;\n }\n};\n\nexport const getUrlFromString = (str: string) => {\n if (isValidUrl(str)) return str;\n try {\n if (str.includes('.') && !str.includes(' ')) {\n return new URL(`https://${str}`).toString();\n }\n } catch (_e) {\n return null;\n }\n};\n\n// Get the text before a given position in markdown format\nexport const getPrevText = (editor: EditorInstance, position: number) => {\n const nodes: Node[] = [];\n editor.state.doc.forEach((node, pos) => {\n if (pos >= position) return;\n nodes.push(node);\n });\n const fragment = Fragment.fromArray(nodes);\n const doc = editor.state.doc.copy(fragment);\n\n return editor.storage.markdown.serializer.serialize(doc) as string;\n};\n\n// Get all content from the editor in markdown format\nexport const getAllContent = (editor: EditorInstance) => {\n const fragment = editor.state.doc.content;\n const doc = editor.state.doc.copy(fragment);\n\n return editor.storage.markdown.serializer.serialize(doc) as string;\n};\n"],"mappings":";;;AAGA,MAAa,cAAc,QAAgB;AACzC,KAAI;AACF,MAAI,IAAI,IAAI;AACZ,SAAO;UACA,IAAI;AACX,SAAO;;;AAIX,MAAa,oBAAoB,QAAgB;AAC/C,KAAI,WAAW,IAAI,CAAE,QAAO;AAC5B,KAAI;AACF,MAAI,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,SAAS,IAAI,CACzC,QAAO,IAAI,IAAI,WAAW,MAAM,CAAC,UAAU;UAEtC,IAAI;AACX,SAAO;;;AAKX,MAAa,eAAe,QAAwB,aAAqB;CACvE,MAAM,QAAgB,EAAE;AACxB,QAAO,MAAM,IAAI,SAAS,MAAM,QAAQ;AACtC,MAAI,OAAO,SAAU;AACrB,QAAM,KAAK,KAAK;GAChB;CACF,MAAM,WAAW,SAAS,UAAU,MAAM;CAC1C,MAAM,MAAM,OAAO,MAAM,IAAI,KAAK,SAAS;AAE3C,QAAO,OAAO,QAAQ,SAAS,WAAW,UAAU,IAAI;;AAI1D,MAAa,iBAAiB,WAA2B;CACvD,MAAM,WAAW,OAAO,MAAM,IAAI;CAClC,MAAM,MAAM,OAAO,MAAM,IAAI,KAAK,SAAS;AAE3C,QAAO,OAAO,QAAQ,SAAS,WAAW,UAAU,IAAI"}
@@ -0,0 +1,176 @@
1
+ 'use client';
2
+
3
+ import { useEditor } from "../novel/components/index.mjs";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { Check, ChevronDown } from "lucide-react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { useIntlayer } from "react-intlayer";
8
+
9
+ //#region src/components/MarkdownEditor/selectors/ColorSelector.tsx
10
+ const TEXT_COLORS = [
11
+ {
12
+ name: "Default",
13
+ color: "var(--color-text)"
14
+ },
15
+ {
16
+ name: "Primary",
17
+ color: "var(--color-primary-500)"
18
+ },
19
+ {
20
+ name: "Secondary",
21
+ color: "var(--color-secondary)"
22
+ },
23
+ {
24
+ name: "Success",
25
+ color: "var(--color-success)"
26
+ },
27
+ {
28
+ name: "Warning",
29
+ color: "var(--color-warning)"
30
+ },
31
+ {
32
+ name: "Error",
33
+ color: "var(--color-error)"
34
+ },
35
+ {
36
+ name: "Neutral",
37
+ color: "var(--color-neutral)"
38
+ }
39
+ ];
40
+ const HIGHLIGHT_COLORS = [
41
+ {
42
+ name: "Default",
43
+ color: "transparent"
44
+ },
45
+ {
46
+ name: "Primary",
47
+ color: "var(--color-primary-100)"
48
+ },
49
+ {
50
+ name: "Secondary",
51
+ color: "var(--color-secondary-100)"
52
+ },
53
+ {
54
+ name: "Success",
55
+ color: "var(--color-success-100)"
56
+ },
57
+ {
58
+ name: "Warning",
59
+ color: "var(--color-warning-50)"
60
+ },
61
+ {
62
+ name: "Error",
63
+ color: "var(--color-error-100)"
64
+ },
65
+ {
66
+ name: "Neutral",
67
+ color: "var(--color-neutral-100)"
68
+ }
69
+ ];
70
+ /**
71
+ * Bubble-menu selector for text color and highlight color.
72
+ *
73
+ * Uses `onMouseDown` + `preventDefault` on every interactive element so the
74
+ * editor's selection is never cleared while the panel is open.
75
+ */
76
+ const ColorSelector = () => {
77
+ const { editor } = useEditor();
78
+ const [isOpen, setIsOpen] = useState(false);
79
+ const containerRef = useRef(null);
80
+ const content = useIntlayer("markdown-editor");
81
+ useEffect(() => {
82
+ if (!isOpen) return;
83
+ const handleOutsideMouseDown = (event) => {
84
+ if (!containerRef.current?.contains(event.target)) setIsOpen(false);
85
+ };
86
+ document.addEventListener("mousedown", handleOutsideMouseDown);
87
+ return () => document.removeEventListener("mousedown", handleOutsideMouseDown);
88
+ }, [isOpen]);
89
+ if (!editor) return null;
90
+ const activeColorItem = TEXT_COLORS.find(({ color }) => editor.isActive("textStyle", { color }));
91
+ const activeHighlightItem = HIGHLIGHT_COLORS.find(({ color }) => editor.isActive("highlight", { color }));
92
+ const getColorName = (name) => {
93
+ switch (name) {
94
+ case "Default": return content.colorDefault.value;
95
+ case "Primary": return content.colorPrimary.value;
96
+ case "Secondary": return content.colorSecondary.value;
97
+ case "Success": return content.colorSuccess.value;
98
+ case "Warning": return content.colorWarning.value;
99
+ case "Error": return content.colorError.value;
100
+ case "Neutral": return content.colorNeutral.value;
101
+ default: return name;
102
+ }
103
+ };
104
+ return /* @__PURE__ */ jsxs("div", {
105
+ ref: containerRef,
106
+ className: "relative",
107
+ children: [/* @__PURE__ */ jsxs("button", {
108
+ type: "button",
109
+ onMouseDown: (e) => {
110
+ e.preventDefault();
111
+ setIsOpen((prev) => !prev);
112
+ },
113
+ className: "flex items-center gap-1 whitespace-nowrap rounded-none px-3 py-1.5 text-sm hover:bg-accent",
114
+ children: [/* @__PURE__ */ jsx("span", {
115
+ className: "rounded-sm px-1 font-medium",
116
+ style: {
117
+ color: activeColorItem?.color,
118
+ backgroundColor: activeHighlightItem?.color
119
+ },
120
+ children: "A"
121
+ }), /* @__PURE__ */ jsx(ChevronDown, { className: "size-3" })]
122
+ }), isOpen && /* @__PURE__ */ jsxs("div", {
123
+ className: "absolute top-[calc(100%+0.5rem)] left-0 z-50 flex w-40 flex-col rounded-md border bg-popover p-1 shadow-md",
124
+ children: [
125
+ /* @__PURE__ */ jsx("div", {
126
+ className: "mb-1 px-2 font-semibold text-muted-foreground text-xs",
127
+ children: content.colorHeading.value
128
+ }),
129
+ TEXT_COLORS.map(({ name, color }) => /* @__PURE__ */ jsxs("button", {
130
+ type: "button",
131
+ onMouseDown: (e) => {
132
+ e.preventDefault();
133
+ editor.commands.unsetColor();
134
+ if (name !== "Default") editor.chain().focus().setColor(color).run();
135
+ setIsOpen(false);
136
+ },
137
+ className: "flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1 text-sm hover:bg-accent",
138
+ children: [/* @__PURE__ */ jsxs("div", {
139
+ className: "flex items-center gap-2",
140
+ children: [/* @__PURE__ */ jsx("span", {
141
+ className: "rounded-sm border px-1.5 font-medium",
142
+ style: { color },
143
+ children: "A"
144
+ }), /* @__PURE__ */ jsx("span", { children: getColorName(name) })]
145
+ }), editor.isActive("textStyle", { color }) && /* @__PURE__ */ jsx(Check, { className: "size-3" })]
146
+ }, name)),
147
+ /* @__PURE__ */ jsx("div", {
148
+ className: "mt-2 mb-1 px-2 font-semibold text-muted-foreground text-xs",
149
+ children: content.highlightHeading.value
150
+ }),
151
+ HIGHLIGHT_COLORS.map(({ name, color }) => /* @__PURE__ */ jsxs("button", {
152
+ type: "button",
153
+ onMouseDown: (e) => {
154
+ e.preventDefault();
155
+ editor.commands.unsetHighlight();
156
+ if (name !== "Default") editor.chain().focus().setHighlight({ color }).run();
157
+ setIsOpen(false);
158
+ },
159
+ className: "flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1 text-sm hover:bg-accent",
160
+ children: [/* @__PURE__ */ jsxs("div", {
161
+ className: "flex items-center gap-2",
162
+ children: [/* @__PURE__ */ jsx("span", {
163
+ className: "rounded-sm border px-1.5 font-medium",
164
+ style: { backgroundColor: color },
165
+ children: "A"
166
+ }), /* @__PURE__ */ jsx("span", { children: getColorName(name) })]
167
+ }), editor.isActive("highlight", { color }) && /* @__PURE__ */ jsx(Check, { className: "size-3" })]
168
+ }, name))
169
+ ]
170
+ })]
171
+ });
172
+ };
173
+
174
+ //#endregion
175
+ export { ColorSelector };
176
+ //# sourceMappingURL=ColorSelector.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorSelector.mjs","names":[],"sources":["../../../../../src/components/MarkdownEditor/selectors/ColorSelector.tsx"],"sourcesContent":["'use client';\n\nimport { Check, ChevronDown } from 'lucide-react';\nimport { type FC, useEffect, useRef, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { useEditor } from '../novel';\n\nexport type ColorMenuItem = {\n name: string;\n color: string;\n};\n\nconst TEXT_COLORS: ColorMenuItem[] = [\n { name: 'Default', color: 'var(--color-text)' },\n { name: 'Primary', color: 'var(--color-primary-500)' },\n { name: 'Secondary', color: 'var(--color-secondary)' },\n { name: 'Success', color: 'var(--color-success)' },\n { name: 'Warning', color: 'var(--color-warning)' },\n { name: 'Error', color: 'var(--color-error)' },\n { name: 'Neutral', color: 'var(--color-neutral)' },\n];\n\nconst HIGHLIGHT_COLORS: ColorMenuItem[] = [\n { name: 'Default', color: 'transparent' },\n { name: 'Primary', color: 'var(--color-primary-100)' },\n { name: 'Secondary', color: 'var(--color-secondary-100)' },\n { name: 'Success', color: 'var(--color-success-100)' },\n { name: 'Warning', color: 'var(--color-warning-50)' },\n { name: 'Error', color: 'var(--color-error-100)' },\n { name: 'Neutral', color: 'var(--color-neutral-100)' },\n];\n\n/**\n * Bubble-menu selector for text color and highlight color.\n *\n * Uses `onMouseDown` + `preventDefault` on every interactive element so the\n * editor's selection is never cleared while the panel is open.\n */\nexport const ColorSelector: FC = () => {\n const { editor } = useEditor();\n const [isOpen, setIsOpen] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const content = useIntlayer('markdown-editor');\n\n useEffect(() => {\n if (!isOpen) return;\n const handleOutsideMouseDown = (event: MouseEvent) => {\n if (!containerRef.current?.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener('mousedown', handleOutsideMouseDown);\n return () =>\n document.removeEventListener('mousedown', handleOutsideMouseDown);\n }, [isOpen]);\n\n if (!editor) return null;\n\n const activeColorItem = TEXT_COLORS.find(({ color }) =>\n editor.isActive('textStyle', { color })\n );\n const activeHighlightItem = HIGHLIGHT_COLORS.find(({ color }) =>\n editor.isActive('highlight', { color })\n );\n\n const getColorName = (name: string) => {\n switch (name) {\n case 'Default':\n return content.colorDefault.value;\n case 'Primary':\n return content.colorPrimary.value;\n case 'Secondary':\n return content.colorSecondary.value;\n case 'Success':\n return content.colorSuccess.value;\n case 'Warning':\n return content.colorWarning.value;\n case 'Error':\n return content.colorError.value;\n case 'Neutral':\n return content.colorNeutral.value;\n default:\n return name;\n }\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n type=\"button\"\n onMouseDown={(e) => {\n e.preventDefault();\n setIsOpen((prev) => !prev);\n }}\n className=\"flex items-center gap-1 whitespace-nowrap rounded-none px-3 py-1.5 text-sm hover:bg-accent\"\n >\n <span\n className=\"rounded-sm px-1 font-medium\"\n style={{\n color: activeColorItem?.color,\n backgroundColor: activeHighlightItem?.color,\n }}\n >\n A\n </span>\n <ChevronDown className=\"size-3\" />\n </button>\n\n {isOpen && (\n <div className=\"absolute top-[calc(100%+0.5rem)] left-0 z-50 flex w-40 flex-col rounded-md border bg-popover p-1 shadow-md\">\n <div className=\"mb-1 px-2 font-semibold text-muted-foreground text-xs\">\n {content.colorHeading.value}\n </div>\n {TEXT_COLORS.map(({ name, color }) => (\n <button\n type=\"button\"\n key={name}\n onMouseDown={(e) => {\n e.preventDefault();\n editor.commands.unsetColor();\n if (name !== 'Default') {\n editor.chain().focus().setColor(color).run();\n }\n setIsOpen(false);\n }}\n className=\"flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1 text-sm hover:bg-accent\"\n >\n <div className=\"flex items-center gap-2\">\n <span\n className=\"rounded-sm border px-1.5 font-medium\"\n style={{ color }}\n >\n A\n </span>\n <span>{getColorName(name)}</span>\n </div>\n {editor.isActive('textStyle', { color }) && (\n <Check className=\"size-3\" />\n )}\n </button>\n ))}\n\n <div className=\"mt-2 mb-1 px-2 font-semibold text-muted-foreground text-xs\">\n {content.highlightHeading.value}\n </div>\n {HIGHLIGHT_COLORS.map(({ name, color }) => (\n <button\n type=\"button\"\n key={name}\n onMouseDown={(e) => {\n e.preventDefault();\n editor.commands.unsetHighlight();\n if (name !== 'Default') {\n editor.chain().focus().setHighlight({ color }).run();\n }\n setIsOpen(false);\n }}\n className=\"flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1 text-sm hover:bg-accent\"\n >\n <div className=\"flex items-center gap-2\">\n <span\n className=\"rounded-sm border px-1.5 font-medium\"\n style={{ backgroundColor: color }}\n >\n A\n </span>\n <span>{getColorName(name)}</span>\n </div>\n {editor.isActive('highlight', { color }) && (\n <Check className=\"size-3\" />\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { cn } from "../../../utils/cn.mjs";
4
+ import { Button } from "../../Button/Button.mjs";
5
+ import { useEditor } from "../novel/components/index.mjs";
6
+ import { getUrlFromString } from "../novel/utils/index.mjs";
7
+ import { useEffect, useRef, useState } from "react";
8
+ import { Check, Link2, Trash } from "lucide-react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { useIntlayer } from "react-intlayer";
11
+
12
+ //#region src/components/MarkdownEditor/selectors/LinkSelector.tsx
13
+ /**
14
+ * Bubble-menu selector for adding / editing / removing a link on the current
15
+ * selection.
16
+ *
17
+ * The trigger uses `onMouseDown` + `preventDefault` to keep the editor
18
+ * selection alive. The input inside the panel is allowed to take real focus
19
+ * (the user needs to type a URL) — the editor re-focuses on submit/remove.
20
+ */
21
+ const LinkSelector = () => {
22
+ const { editor } = useEditor();
23
+ const [isOpen, setIsOpen] = useState(false);
24
+ const containerRef = useRef(null);
25
+ const inputRef = useRef(null);
26
+ const content = useIntlayer("markdown-editor");
27
+ useEffect(() => {
28
+ if (!isOpen) return;
29
+ const handleOutsideMouseDown = (event) => {
30
+ if (!containerRef.current?.contains(event.target)) setIsOpen(false);
31
+ };
32
+ document.addEventListener("mousedown", handleOutsideMouseDown);
33
+ inputRef.current?.focus();
34
+ return () => document.removeEventListener("mousedown", handleOutsideMouseDown);
35
+ }, [isOpen]);
36
+ if (!editor) return null;
37
+ const currentHref = editor.getAttributes("link").href;
38
+ return /* @__PURE__ */ jsxs("div", {
39
+ ref: containerRef,
40
+ className: "relative",
41
+ children: [/* @__PURE__ */ jsxs("button", {
42
+ type: "button",
43
+ onMouseDown: (e) => {
44
+ e.preventDefault();
45
+ setIsOpen((prev) => !prev);
46
+ },
47
+ className: cn("flex items-center gap-1 whitespace-nowrap rounded-none px-3 py-1.5 text-sm hover:bg-accent", editor.isActive("link") && "text-blue-500"),
48
+ children: [/* @__PURE__ */ jsx(Link2, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
49
+ className: "underline decoration-stone-400 underline-offset-4",
50
+ children: content.link.value
51
+ })]
52
+ }), isOpen && /* @__PURE__ */ jsx("div", {
53
+ className: "absolute top-[calc(100%+0.5rem)] left-0 z-50",
54
+ children: /* @__PURE__ */ jsxs("form", {
55
+ className: "flex w-60 gap-1 rounded-md border bg-popover p-1 shadow-md",
56
+ onSubmit: (event) => {
57
+ event.preventDefault();
58
+ const input = event.currentTarget[0];
59
+ const url = getUrlFromString(input.value);
60
+ if (url) editor.chain().focus().setLink({ href: url }).run();
61
+ setIsOpen(false);
62
+ },
63
+ children: [/* @__PURE__ */ jsx("input", {
64
+ ref: inputRef,
65
+ type: "text",
66
+ placeholder: content.linkPlaceholder.value,
67
+ className: "flex-1 bg-transparent p-1 text-sm outline-none",
68
+ defaultValue: currentHref ?? ""
69
+ }), currentHref ? /* @__PURE__ */ jsx(Button, {
70
+ label: content.removeLink.value,
71
+ type: "button",
72
+ size: "icon-sm",
73
+ variant: "outline",
74
+ color: "error",
75
+ Icon: Trash,
76
+ onClick: () => {
77
+ editor.chain().focus().unsetLink().run();
78
+ if (inputRef.current) inputRef.current.value = "";
79
+ setIsOpen(false);
80
+ }
81
+ }) : /* @__PURE__ */ jsx(Button, {
82
+ label: content.applyLink.value,
83
+ type: "submit",
84
+ size: "icon-sm",
85
+ color: "text",
86
+ Icon: Check
87
+ })]
88
+ }, currentHref ?? "no-link")
89
+ })]
90
+ });
91
+ };
92
+
93
+ //#endregion
94
+ export { LinkSelector };
95
+ //# sourceMappingURL=LinkSelector.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkSelector.mjs","names":[],"sources":["../../../../../src/components/MarkdownEditor/selectors/LinkSelector.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { cn } from '@utils/cn';\nimport { Check, Link2, Trash } from 'lucide-react';\nimport { type FC, useEffect, useRef, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { getUrlFromString, useEditor } from '../novel';\n\n/**\n * Bubble-menu selector for adding / editing / removing a link on the current\n * selection.\n *\n * The trigger uses `onMouseDown` + `preventDefault` to keep the editor\n * selection alive. The input inside the panel is allowed to take real focus\n * (the user needs to type a URL) — the editor re-focuses on submit/remove.\n */\nexport const LinkSelector: FC = () => {\n const { editor } = useEditor();\n const [isOpen, setIsOpen] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const content = useIntlayer('markdown-editor');\n\n useEffect(() => {\n if (!isOpen) return;\n const handleOutsideMouseDown = (event: MouseEvent) => {\n if (!containerRef.current?.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener('mousedown', handleOutsideMouseDown);\n\n // Programmatically focus the input\n inputRef.current?.focus();\n\n return () =>\n document.removeEventListener('mousedown', handleOutsideMouseDown);\n }, [isOpen]);\n\n if (!editor) return null;\n\n const currentHref = editor.getAttributes('link').href as string | undefined;\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n type=\"button\"\n onMouseDown={(e) => {\n e.preventDefault();\n setIsOpen((prev) => !prev);\n }}\n className={cn(\n 'flex items-center gap-1 whitespace-nowrap rounded-none px-3 py-1.5 text-sm hover:bg-accent',\n editor.isActive('link') && 'text-blue-500'\n )}\n >\n <Link2 className=\"size-4\" />\n <span className=\"underline decoration-stone-400 underline-offset-4\">\n {content.link.value}\n </span>\n </button>\n\n {isOpen && (\n <div className=\"absolute top-[calc(100%+0.5rem)] left-0 z-50\">\n <form\n key={currentHref ?? 'no-link'}\n className=\"flex w-60 gap-1 rounded-md border bg-popover p-1 shadow-md\"\n onSubmit={(event) => {\n event.preventDefault();\n const input = event.currentTarget[0] as HTMLInputElement;\n const url = getUrlFromString(input.value);\n if (url) {\n editor.chain().focus().setLink({ href: url }).run();\n }\n setIsOpen(false);\n }}\n >\n <input\n ref={inputRef}\n type=\"text\"\n placeholder={content.linkPlaceholder.value}\n className=\"flex-1 bg-transparent p-1 text-sm outline-none\"\n defaultValue={currentHref ?? ''}\n />\n {currentHref ? (\n <Button\n label={content.removeLink.value}\n type=\"button\"\n size=\"icon-sm\"\n variant=\"outline\"\n color=\"error\"\n Icon={Trash}\n onClick={() => {\n editor.chain().focus().unsetLink().run();\n if (inputRef.current) inputRef.current.value = '';\n setIsOpen(false);\n }}\n />\n ) : (\n <Button\n label={content.applyLink.value}\n type=\"submit\"\n size=\"icon-sm\"\n color=\"text\"\n Icon={Check}\n />\n )}\n </form>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAiBA,MAAa,qBAAyB;CACpC,MAAM,EAAE,WAAW,WAAW;CAC9B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,WAAW,OAAyB,KAAK;CAC/C,MAAM,UAAU,YAAY,kBAAkB;AAE9C,iBAAgB;AACd,MAAI,CAAC,OAAQ;EACb,MAAM,0BAA0B,UAAsB;AACpD,OAAI,CAAC,aAAa,SAAS,SAAS,MAAM,OAAe,CACvD,WAAU,MAAM;;AAGpB,WAAS,iBAAiB,aAAa,uBAAuB;AAG9D,WAAS,SAAS,OAAO;AAEzB,eACE,SAAS,oBAAoB,aAAa,uBAAuB;IAClE,CAAC,OAAO,CAAC;AAEZ,KAAI,CAAC,OAAQ,QAAO;CAEpB,MAAM,cAAc,OAAO,cAAc,OAAO,CAAC;AAEjD,QACE,qBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;YAAlC,CACE,qBAAC,UAAD;GACE,MAAK;GACL,cAAc,MAAM;AAClB,MAAE,gBAAgB;AAClB,eAAW,SAAS,CAAC,KAAK;;GAE5B,WAAW,GACT,8FACA,OAAO,SAAS,OAAO,IAAI,gBAC5B;aATH,CAWE,oBAAC,OAAD,EAAO,WAAU,UAAW,GAC5B,oBAAC,QAAD;IAAM,WAAU;cACb,QAAQ,KAAK;IACT,EACA;MAER,UACC,oBAAC,OAAD;GAAK,WAAU;aACb,qBAAC,QAAD;IAEE,WAAU;IACV,WAAW,UAAU;AACnB,WAAM,gBAAgB;KACtB,MAAM,QAAQ,MAAM,cAAc;KAClC,MAAM,MAAM,iBAAiB,MAAM,MAAM;AACzC,SAAI,IACF,QAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,KAAK;AAErD,eAAU,MAAM;;cAVpB,CAaE,oBAAC,SAAD;KACE,KAAK;KACL,MAAK;KACL,aAAa,QAAQ,gBAAgB;KACrC,WAAU;KACV,cAAc,eAAe;KAC7B,GACD,cACC,oBAAC,QAAD;KACE,OAAO,QAAQ,WAAW;KAC1B,MAAK;KACL,MAAK;KACL,SAAQ;KACR,OAAM;KACN,MAAM;KACN,eAAe;AACb,aAAO,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK;AACxC,UAAI,SAAS,QAAS,UAAS,QAAQ,QAAQ;AAC/C,gBAAU,MAAM;;KAElB,IAEF,oBAAC,QAAD;KACE,OAAO,QAAQ,UAAU;KACzB,MAAK;KACL,MAAK;KACL,OAAM;KACN,MAAM;KACN,EAEC;MA1CA,eAAe,UA0Cf;GACH,EAEJ"}