@keboola/design 10.1.0 → 11.0.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 (382) hide show
  1. package/AGENTS.md +2 -0
  2. package/dist/AIKit-Dre37SJr.js +1320 -0
  3. package/dist/AIKit-Dre37SJr.js.map +1 -0
  4. package/dist/Alert-DfZq9kGe.js +133 -0
  5. package/dist/Alert-DfZq9kGe.js.map +1 -0
  6. package/dist/AlertModal-BUC4Es8o.js +222 -0
  7. package/dist/AlertModal-BUC4Es8o.js.map +1 -0
  8. package/dist/AppShell-B5GQN6Zs.js +143 -0
  9. package/dist/AppShell-B5GQN6Zs.js.map +1 -0
  10. package/dist/AutoSaveTextarea-BeuWuszr.js +58 -0
  11. package/dist/AutoSaveTextarea-BeuWuszr.js.map +1 -0
  12. package/dist/Badge-Ch26CXoP.js +195 -0
  13. package/dist/Badge-Ch26CXoP.js.map +1 -0
  14. package/dist/BrandEditor-B8o9zdZO.js +903 -0
  15. package/dist/BrandEditor-B8o9zdZO.js.map +1 -0
  16. package/dist/BrandLogo-CJkRd0F8.js +46 -0
  17. package/dist/BrandLogo-CJkRd0F8.js.map +1 -0
  18. package/dist/Breadcrumb-DJp7WwSv.js +330 -0
  19. package/dist/Breadcrumb-DJp7WwSv.js.map +1 -0
  20. package/dist/Button-nzmDNS2q.js +391 -0
  21. package/dist/Button-nzmDNS2q.js.map +1 -0
  22. package/dist/Calendar-cG5NIJ4O.js +6768 -0
  23. package/dist/Calendar-cG5NIJ4O.js.map +1 -0
  24. package/dist/Card-BAoZ35CV.js +105 -0
  25. package/dist/Card-BAoZ35CV.js.map +1 -0
  26. package/dist/Checkbox-GJ8-srt4.js +97 -0
  27. package/dist/Checkbox-GJ8-srt4.js.map +1 -0
  28. package/dist/Clipboard-CBJZiviP.js +58 -0
  29. package/dist/Clipboard-CBJZiviP.js.map +1 -0
  30. package/dist/ClipboardButton-ez5V3fkk.js +43 -0
  31. package/dist/ClipboardButton-ez5V3fkk.js.map +1 -0
  32. package/dist/CodeEditor-BfQJfbhL.js +952 -0
  33. package/dist/CodeEditor-BfQJfbhL.js.map +1 -0
  34. package/dist/Collapsible-BQXWEOBC.js +34 -0
  35. package/dist/Collapsible-BQXWEOBC.js.map +1 -0
  36. package/dist/ColorInput-Dx8Dsv7Y.js +180 -0
  37. package/dist/ColorInput-Dx8Dsv7Y.js.map +1 -0
  38. package/dist/ColorModeSwitch-CCqj7tjE.js +77 -0
  39. package/dist/ColorModeSwitch-CCqj7tjE.js.map +1 -0
  40. package/dist/Combobox-c_xBjf0P.js +439 -0
  41. package/dist/Combobox-c_xBjf0P.js.map +1 -0
  42. package/dist/Command-CQ_bQsJp.js +102 -0
  43. package/dist/Command-CQ_bQsJp.js.map +1 -0
  44. package/dist/CopyField-BJ96zvXs.js +322 -0
  45. package/dist/CopyField-BJ96zvXs.js.map +1 -0
  46. package/dist/DateInput-CMD3dY-T.js +141 -0
  47. package/dist/DateInput-CMD3dY-T.js.map +1 -0
  48. package/dist/DatePicker-BbFbmsGl.js +24 -0
  49. package/dist/DatePicker-BbFbmsGl.js.map +1 -0
  50. package/dist/DateRangeInput-D3BhiKyk.js +139 -0
  51. package/dist/DateRangeInput-D3BhiKyk.js.map +1 -0
  52. package/dist/DateRangePicker-BiUgtt_M.js +159 -0
  53. package/dist/DateRangePicker-BiUgtt_M.js.map +1 -0
  54. package/dist/DiagramSkeleton-lwBS2YBa.js +11 -0
  55. package/dist/DiagramSkeleton-lwBS2YBa.js.map +1 -0
  56. package/dist/DiffView-ppTIzh2h.js +429 -0
  57. package/dist/DiffView-ppTIzh2h.js.map +1 -0
  58. package/dist/DropdownMenu-B6ZbYvaM.js +327 -0
  59. package/dist/DropdownMenu-B6ZbYvaM.js.map +1 -0
  60. package/dist/FieldBox-D7DYtX_m.js +70 -0
  61. package/dist/FieldBox-D7DYtX_m.js.map +1 -0
  62. package/dist/FileUpload-sXRv7JiY.js +2527 -0
  63. package/dist/FileUpload-sXRv7JiY.js.map +1 -0
  64. package/dist/Form-BDCef6O9.js +371 -0
  65. package/dist/Form-BDCef6O9.js.map +1 -0
  66. package/dist/FormGroup-S9mPNHKH.js +124 -0
  67. package/dist/FormGroup-S9mPNHKH.js.map +1 -0
  68. package/dist/FullScreenModal-CJa2VkJ2.js +96 -0
  69. package/dist/FullScreenModal-CJa2VkJ2.js.map +1 -0
  70. package/dist/HoverCard-WfDkLK1s.js +31 -0
  71. package/dist/HoverCard-WfDkLK1s.js.map +1 -0
  72. package/dist/InputGroup-BJWsI35s.js +292 -0
  73. package/dist/InputGroup-BJWsI35s.js.map +1 -0
  74. package/dist/Kbd-CxHAo1oo.js +111 -0
  75. package/dist/Kbd-CxHAo1oo.js.map +1 -0
  76. package/dist/MarkedText-Ckbft5mw.js +44 -0
  77. package/dist/MarkedText-Ckbft5mw.js.map +1 -0
  78. package/dist/MermaidDiagram-BB06gymm.js +399 -0
  79. package/dist/MermaidDiagram-BB06gymm.js.map +1 -0
  80. package/dist/Modal-BA5Wc3sx.js +185 -0
  81. package/dist/Modal-BA5Wc3sx.js.map +1 -0
  82. package/dist/ModalTitle-C76ke2eo.js +28 -0
  83. package/dist/ModalTitle-C76ke2eo.js.map +1 -0
  84. package/dist/MultiActionButtonGroup-CiBHxsSk.js +33 -0
  85. package/dist/MultiActionButtonGroup-CiBHxsSk.js.map +1 -0
  86. package/dist/Navbar-t16usFZQ.js +322 -0
  87. package/dist/Navbar-t16usFZQ.js.map +1 -0
  88. package/dist/OtpInput-CbbymqYs.js +93 -0
  89. package/dist/OtpInput-CbbymqYs.js.map +1 -0
  90. package/dist/PageHeader-Q3IpCnQd.js +177 -0
  91. package/dist/PageHeader-Q3IpCnQd.js.map +1 -0
  92. package/dist/PageLayout-DSVM-ASz.js +176 -0
  93. package/dist/PageLayout-DSVM-ASz.js.map +1 -0
  94. package/dist/PageToolbar-0_1OBzaZ.js +153 -0
  95. package/dist/PageToolbar-0_1OBzaZ.js.map +1 -0
  96. package/dist/Pagination-CM_Rszz-.js +70 -0
  97. package/dist/Pagination-CM_Rszz-.js.map +1 -0
  98. package/dist/Popconfirm-BGe1jLov.js +170 -0
  99. package/dist/Popconfirm-BGe1jLov.js.map +1 -0
  100. package/dist/Popover-Ca0V6AD1.js +92 -0
  101. package/dist/Popover-Ca0V6AD1.js.map +1 -0
  102. package/dist/RadioGroup-sIveSsNa.js +233 -0
  103. package/dist/RadioGroup-sIveSsNa.js.map +1 -0
  104. package/dist/Resizable-D3vYToZH.js +52 -0
  105. package/dist/Resizable-D3vYToZH.js.map +1 -0
  106. package/dist/Search-3UiqNZRj.js +184 -0
  107. package/dist/Search-3UiqNZRj.js.map +1 -0
  108. package/dist/SearchInput-D2fcOv7E.js +102 -0
  109. package/dist/SearchInput-D2fcOv7E.js.map +1 -0
  110. package/dist/SectionNav--7bw2b6u.js +276 -0
  111. package/dist/SectionNav--7bw2b6u.js.map +1 -0
  112. package/dist/Select-4YX38G5h.js +181 -0
  113. package/dist/Select-4YX38G5h.js.map +1 -0
  114. package/dist/Sheet-BQrX8-h5.js +169 -0
  115. package/dist/Sheet-BQrX8-h5.js.map +1 -0
  116. package/dist/Sidebar-ROaR_AqC.js +799 -0
  117. package/dist/Sidebar-ROaR_AqC.js.map +1 -0
  118. package/dist/Slider-Da_3mS5v.js +127 -0
  119. package/dist/Slider-Da_3mS5v.js.map +1 -0
  120. package/dist/Stepper-C8sm9ZFM.js +325 -0
  121. package/dist/Stepper-C8sm9ZFM.js.map +1 -0
  122. package/dist/Switch-CX2A0v50.js +201 -0
  123. package/dist/Switch-CX2A0v50.js.map +1 -0
  124. package/dist/Table-CvYuTuNU.js +104 -0
  125. package/dist/Table-CvYuTuNU.js.map +1 -0
  126. package/dist/Tabs-EAihyqCt.js +74 -0
  127. package/dist/Tabs-EAihyqCt.js.map +1 -0
  128. package/dist/TextInput-BGed-V13.js +110 -0
  129. package/dist/TextInput-BGed-V13.js.map +1 -0
  130. package/dist/ThemeProvider-QncUrfJn.js +203 -0
  131. package/dist/ThemeProvider-QncUrfJn.js.map +1 -0
  132. package/dist/TimePicker-B_NegL-7.js +98 -0
  133. package/dist/TimePicker-B_NegL-7.js.map +1 -0
  134. package/dist/Timeline-6btyJl4D.js +154 -0
  135. package/dist/Timeline-6btyJl4D.js.map +1 -0
  136. package/dist/Toast-DQSbs3oA.js +49 -0
  137. package/dist/Toast-DQSbs3oA.js.map +1 -0
  138. package/dist/ToggleGroup-Cy_10Mvu.js +47 -0
  139. package/dist/ToggleGroup-Cy_10Mvu.js.map +1 -0
  140. package/dist/Workbench-BzE-Vvd9.js +293 -0
  141. package/dist/Workbench-BzE-Vvd9.js.map +1 -0
  142. package/dist/ZoomableSvg-DG9PnAm8.js +248 -0
  143. package/dist/ZoomableSvg-DG9PnAm8.js.map +1 -0
  144. package/dist/charts.d.ts +124 -111
  145. package/dist/charts.js +303 -249
  146. package/dist/charts.js.map +1 -1
  147. package/dist/classNames-BzrkDnOD.js +9 -0
  148. package/dist/classNames-BzrkDnOD.js.map +1 -0
  149. package/dist/constants-D6JcsD4_.d.ts +41 -0
  150. package/dist/context-KxxeMHbR.js +62 -0
  151. package/dist/context-KxxeMHbR.js.map +1 -0
  152. package/dist/editors.d.ts +180 -173
  153. package/dist/editors.js +747 -420
  154. package/dist/editors.js.map +1 -1
  155. package/dist/escapeStringRegexp-8MxP2knR.js +6 -0
  156. package/dist/escapeStringRegexp-8MxP2knR.js.map +1 -0
  157. package/dist/icons-Cek151jC.js +640 -0
  158. package/dist/icons-Cek151jC.js.map +1 -0
  159. package/dist/icons.d.ts +2 -41
  160. package/dist/icons.js +2 -4
  161. package/dist/index-m-kQEWiY.d.ts +54 -0
  162. package/dist/index.d.ts +6192 -9904
  163. package/dist/index.js +112 -15803
  164. package/dist/internal/blocks/AIKit.js +2 -0
  165. package/dist/internal/blocks/AppShell.js +2 -0
  166. package/dist/internal/blocks/AutoSaveTextarea.js +2 -0
  167. package/dist/internal/blocks/BrandEditor.js +2 -0
  168. package/dist/internal/blocks/ChartPanel.js +46 -0
  169. package/dist/internal/blocks/ChartPanel.js.map +1 -0
  170. package/dist/internal/blocks/ClipboardButton.js +2 -0
  171. package/dist/internal/blocks/ColorInput.js +2 -0
  172. package/dist/internal/blocks/ColorModeSwitch.js +2 -0
  173. package/dist/internal/blocks/Command.js +2 -0
  174. package/dist/internal/blocks/CopyField.js +2 -0
  175. package/dist/internal/blocks/DashboardHero.js +59 -0
  176. package/dist/internal/blocks/DashboardHero.js.map +1 -0
  177. package/dist/internal/blocks/DateInput.js +2 -0
  178. package/dist/internal/blocks/DateRangeInput.js +2 -0
  179. package/dist/internal/blocks/EmptyConfigurationActionCard.js +16 -0
  180. package/dist/internal/blocks/EmptyConfigurationActionCard.js.map +1 -0
  181. package/dist/internal/blocks/EmptyState.js +142 -0
  182. package/dist/internal/blocks/EmptyState.js.map +1 -0
  183. package/dist/internal/blocks/ErrorState.js +179 -0
  184. package/dist/internal/blocks/ErrorState.js.map +1 -0
  185. package/dist/internal/blocks/ExportMenu.js +41 -0
  186. package/dist/internal/blocks/ExportMenu.js.map +1 -0
  187. package/dist/internal/blocks/Form.js +2 -0
  188. package/dist/internal/blocks/FormGroup.js +2 -0
  189. package/dist/internal/blocks/GoogleButton.js +95 -0
  190. package/dist/internal/blocks/GoogleButton.js.map +1 -0
  191. package/dist/internal/blocks/MicrosoftButton.js +64 -0
  192. package/dist/internal/blocks/MicrosoftButton.js.map +1 -0
  193. package/dist/internal/blocks/MultiActionButtonGroup.js +2 -0
  194. package/dist/internal/blocks/MultiCombobox.js +188 -0
  195. package/dist/internal/blocks/MultiCombobox.js.map +1 -0
  196. package/dist/internal/blocks/NumberInput.js +44 -0
  197. package/dist/internal/blocks/NumberInput.js.map +1 -0
  198. package/dist/internal/blocks/OtpInput.js +2 -0
  199. package/dist/internal/blocks/PageHeader.js +2 -0
  200. package/dist/internal/blocks/PageLayout.js +2 -0
  201. package/dist/internal/blocks/PageToolbar.js +2 -0
  202. package/dist/internal/blocks/PasswordInput.js +97 -0
  203. package/dist/internal/blocks/PasswordInput.js.map +1 -0
  204. package/dist/internal/blocks/SearchInput.js +2 -0
  205. package/dist/internal/blocks/SearchInputWithNavigation.js +65 -0
  206. package/dist/internal/blocks/SearchInputWithNavigation.js.map +1 -0
  207. package/dist/internal/blocks/SectionNav.js +2 -0
  208. package/dist/internal/blocks/SplitButton.js +46 -0
  209. package/dist/internal/blocks/SplitButton.js.map +1 -0
  210. package/dist/internal/blocks/StatCard.js +97 -0
  211. package/dist/internal/blocks/StatCard.js.map +1 -0
  212. package/dist/internal/blocks/TextInput.js +2 -0
  213. package/dist/internal/blocks/Textarea.js +32 -0
  214. package/dist/internal/blocks/Textarea.js.map +1 -0
  215. package/dist/internal/blocks/TimeInput.js +77 -0
  216. package/dist/internal/blocks/TimeInput.js.map +1 -0
  217. package/dist/internal/components/Alert.js +2 -0
  218. package/dist/internal/components/AlertModal.js +2 -0
  219. package/dist/internal/components/Avatar.js +53 -0
  220. package/dist/internal/components/Avatar.js.map +1 -0
  221. package/dist/internal/components/Badge.js +2 -0
  222. package/dist/internal/components/BrandLogo.js +2 -0
  223. package/dist/internal/components/Breadcrumb.js +2 -0
  224. package/dist/internal/components/Button.js +2 -0
  225. package/dist/internal/components/ButtonGroup.js +66 -0
  226. package/dist/internal/components/ButtonGroup.js.map +1 -0
  227. package/dist/internal/components/Calendar.js +2 -0
  228. package/dist/internal/components/Card.js +2 -0
  229. package/dist/internal/components/Checkbox.js +2 -0
  230. package/dist/internal/components/Clipboard.js +2 -0
  231. package/dist/internal/components/Collapsible.js +2 -0
  232. package/dist/internal/components/CollapsiblePanel.js +87 -0
  233. package/dist/internal/components/CollapsiblePanel.js.map +1 -0
  234. package/dist/internal/components/Combobox.js +2 -0
  235. package/dist/internal/components/DatePicker.js +2 -0
  236. package/dist/internal/components/DateRangePicker.js +2 -0
  237. package/dist/internal/components/DiffView.js +2 -0
  238. package/dist/internal/components/DotsLoader.js +18 -0
  239. package/dist/internal/components/DotsLoader.js.map +1 -0
  240. package/dist/internal/components/DropdownMenu.js +2 -0
  241. package/dist/internal/components/EmptySearchContent.js +12 -0
  242. package/dist/internal/components/EmptySearchContent.js.map +1 -0
  243. package/dist/internal/components/FileUpload.js +2 -0
  244. package/dist/internal/components/FullScreenModal.js +2 -0
  245. package/dist/internal/components/HelpBlock.js +25 -0
  246. package/dist/internal/components/HelpBlock.js.map +1 -0
  247. package/dist/internal/components/HoverCard.js +2 -0
  248. package/dist/internal/components/InputGroup.js +2 -0
  249. package/dist/internal/components/Kbd.js +2 -0
  250. package/dist/internal/components/Label.js +39 -0
  251. package/dist/internal/components/Label.js.map +1 -0
  252. package/dist/internal/components/Link.js +22 -0
  253. package/dist/internal/components/Link.js.map +1 -0
  254. package/dist/internal/components/MarkedText.js +2 -0
  255. package/dist/internal/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.js +20 -0
  256. package/dist/internal/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.js.map +1 -0
  257. package/dist/internal/components/MergeEditor/PaneHeader.js +13 -0
  258. package/dist/internal/components/MergeEditor/PaneHeader.js.map +1 -0
  259. package/dist/internal/components/MergeEditor/constants.js +18 -0
  260. package/dist/internal/components/MergeEditor/constants.js.map +1 -0
  261. package/dist/internal/components/MergeEditor/useMergeDiff.js +2 -0
  262. package/dist/internal/components/Modal.js +2 -0
  263. package/dist/internal/components/Navbar.js +2 -0
  264. package/dist/internal/components/Pagination.js +2 -0
  265. package/dist/internal/components/PanelWithDetails.js +31 -0
  266. package/dist/internal/components/PanelWithDetails.js.map +1 -0
  267. package/dist/internal/components/Popconfirm.js +2 -0
  268. package/dist/internal/components/Popover.js +2 -0
  269. package/dist/internal/components/Progress.js +47 -0
  270. package/dist/internal/components/Progress.js.map +1 -0
  271. package/dist/internal/components/RadioGroup.js +2 -0
  272. package/dist/internal/components/Resizable.js +2 -0
  273. package/dist/internal/components/ScrollArea.js +42 -0
  274. package/dist/internal/components/ScrollArea.js.map +1 -0
  275. package/dist/internal/components/Search.js +2 -0
  276. package/dist/internal/components/Select.js +2 -0
  277. package/dist/internal/components/Separator.js +84 -0
  278. package/dist/internal/components/Separator.js.map +1 -0
  279. package/dist/internal/components/Sheet.js +2 -0
  280. package/dist/internal/components/Sidebar.js +2 -0
  281. package/dist/internal/components/Skeleton/Skeleton.js +39 -0
  282. package/dist/internal/components/Skeleton/Skeleton.js.map +1 -0
  283. package/dist/internal/components/Skeleton/SkeletonList.js +41 -0
  284. package/dist/internal/components/Skeleton/SkeletonList.js.map +1 -0
  285. package/dist/internal/components/Slider.js +2 -0
  286. package/dist/internal/components/SpotlightBackdrop.js +132 -0
  287. package/dist/internal/components/SpotlightBackdrop.js.map +1 -0
  288. package/dist/internal/components/Stepper.js +2 -0
  289. package/dist/internal/components/Switch.js +2 -0
  290. package/dist/internal/components/Table.js +2 -0
  291. package/dist/internal/components/Tabs.js +2 -0
  292. package/dist/internal/components/ThemeProvider.js +3 -0
  293. package/dist/internal/components/TimePicker.js +2 -0
  294. package/dist/internal/components/Timeline.js +2 -0
  295. package/dist/internal/components/Toast.js +3 -0
  296. package/dist/internal/components/Toggle.js +169 -0
  297. package/dist/internal/components/Toggle.js.map +1 -0
  298. package/dist/internal/components/ToggleGroup.js +2 -0
  299. package/dist/internal/components/Tooltip.js +2 -0
  300. package/dist/internal/components/Truncated.js +45 -0
  301. package/dist/internal/components/Truncated.js.map +1 -0
  302. package/dist/internal/components/Workbench.js +2 -0
  303. package/dist/internal/components/ZoomableSvg.js +2 -0
  304. package/dist/internal/hooks/useAutosizeInput.js +77 -0
  305. package/dist/internal/hooks/useAutosizeInput.js.map +1 -0
  306. package/dist/internal/hooks/useScrollbarPosition.js +45 -0
  307. package/dist/internal/hooks/useScrollbarPosition.js.map +1 -0
  308. package/dist/internal/hooks/useStableCallback.js +29 -0
  309. package/dist/internal/hooks/useStableCallback.js.map +1 -0
  310. package/dist/internal/utils/mergeRefs.js +20 -0
  311. package/dist/internal/utils/mergeRefs.js.map +1 -0
  312. package/dist/internal/utils.js +5 -0
  313. package/dist/markdown-editor.d.ts +72 -72
  314. package/dist/markdown-editor.js +1122 -1193
  315. package/dist/markdown-editor.js.map +1 -1
  316. package/dist/mermaid.d.ts +12 -12
  317. package/dist/mermaid.js +2 -10
  318. package/dist/rjsf.d.ts +33 -34
  319. package/dist/rjsf.js +1543 -1600
  320. package/dist/rjsf.js.map +1 -1
  321. package/dist/rolldown-runtime-Bdc48AWt.js +33 -0
  322. package/dist/styles-DSUP_-9Z.js +158 -0
  323. package/dist/styles-DSUP_-9Z.js.map +1 -0
  324. package/dist/styles.css +1 -1
  325. package/dist/testing.d.ts +8 -7
  326. package/dist/testing.js +36 -16
  327. package/dist/testing.js.map +1 -1
  328. package/dist/themeTokens-BNg98Mu9.js +85 -0
  329. package/dist/themeTokens-BNg98Mu9.js.map +1 -0
  330. package/dist/toast-CjkURw0M.js +184 -0
  331. package/dist/toast-CjkURw0M.js.map +1 -0
  332. package/dist/useColorSchemeState-CYqcUfSH.js +61 -0
  333. package/dist/useColorSchemeState-CYqcUfSH.js.map +1 -0
  334. package/dist/useControllableState-M6494mCT.js +31 -0
  335. package/dist/useControllableState-M6494mCT.js.map +1 -0
  336. package/dist/useElement-DkF6esWL.js +15 -0
  337. package/dist/useElement-DkF6esWL.js.map +1 -0
  338. package/dist/useMergeDiff-CvowEXJ1.js +465 -0
  339. package/dist/useMergeDiff-CvowEXJ1.js.map +1 -0
  340. package/dist/utils-D0c5VAyF.js +308 -0
  341. package/dist/utils-D0c5VAyF.js.map +1 -0
  342. package/package.json +10 -9
  343. package/src/styles.css +35 -7
  344. package/src/theme.css +64 -0
  345. package/dist/MermaidDiagram-EOKJHQQV.js +0 -10
  346. package/dist/MermaidDiagram-EOKJHQQV.js.map +0 -1
  347. package/dist/chunk-63NSN2VL.js +0 -274
  348. package/dist/chunk-63NSN2VL.js.map +0 -1
  349. package/dist/chunk-6FPEI4EM.js +0 -6683
  350. package/dist/chunk-6FPEI4EM.js.map +0 -1
  351. package/dist/chunk-7PUUHEJQ.js +0 -1313
  352. package/dist/chunk-7PUUHEJQ.js.map +0 -1
  353. package/dist/chunk-BOHNXTMM.js +0 -606
  354. package/dist/chunk-BOHNXTMM.js.map +0 -1
  355. package/dist/chunk-C3L3TBBT.js +0 -242
  356. package/dist/chunk-C3L3TBBT.js.map +0 -1
  357. package/dist/chunk-CTCFYWJC.js +0 -407
  358. package/dist/chunk-CTCFYWJC.js.map +0 -1
  359. package/dist/chunk-ESBL3IPF.js +0 -23
  360. package/dist/chunk-ESBL3IPF.js.map +0 -1
  361. package/dist/chunk-IJTHFNNJ.js +0 -261
  362. package/dist/chunk-IJTHFNNJ.js.map +0 -1
  363. package/dist/chunk-JP7PNHLH.js +0 -40
  364. package/dist/chunk-JP7PNHLH.js.map +0 -1
  365. package/dist/chunk-KPNAW5MU.js +0 -229
  366. package/dist/chunk-KPNAW5MU.js.map +0 -1
  367. package/dist/chunk-L37JJ6MR.js +0 -152
  368. package/dist/chunk-L37JJ6MR.js.map +0 -1
  369. package/dist/chunk-Q7NWXLCT.js +0 -90
  370. package/dist/chunk-Q7NWXLCT.js.map +0 -1
  371. package/dist/chunk-SYIEF644.js +0 -1016
  372. package/dist/chunk-SYIEF644.js.map +0 -1
  373. package/dist/chunk-VMFSRV5H.js +0 -16
  374. package/dist/chunk-VMFSRV5H.js.map +0 -1
  375. package/dist/chunk-VPPM7UUQ.js +0 -47
  376. package/dist/chunk-VPPM7UUQ.js.map +0 -1
  377. package/dist/chunk-XV3UUOYF.js +0 -845
  378. package/dist/chunk-XV3UUOYF.js.map +0 -1
  379. package/dist/constants-D3gaMY-W.d.ts +0 -39
  380. package/dist/icons.js.map +0 -1
  381. package/dist/index.js.map +0 -1
  382. package/dist/mermaid.js.map +0 -1
@@ -1,1223 +1,1152 @@
1
- import { DropdownMenu, ButtonGroup, toast } from './chunk-XV3UUOYF.js';
2
- import { Skeleton } from './chunk-VPPM7UUQ.js';
3
- import { FormGroup, Label, TextInput } from './chunk-SYIEF644.js';
4
- import './chunk-ESBL3IPF.js';
5
- import { Button } from './chunk-CTCFYWJC.js';
6
- import { AlignLeft, AlignCenter, AlignRight, AlignJustify, SquareCode, Quote, ListOrdered, List, Heading3, Heading2, Heading1, Type, Bold, Italic, Underline, Strikethrough, Code, ChevronDown, Link } from './chunk-BOHNXTMM.js';
7
- import { Tooltip } from './chunk-63NSN2VL.js';
8
- import { cn } from './chunk-JP7PNHLH.js';
9
- import { createContext, lazy, Suspense, use, useState, useRef, useCallback, useEffect } from 'react';
10
- import { CodeNode, $isCodeNode, $createCodeNode } from '@lexical/code';
11
- import { LinkNode, $isLinkNode, TOGGLE_LINK_COMMAND, $createLinkNode } from '@lexical/link';
12
- import { ListNode, ListItemNode, $isListNode, REMOVE_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND } from '@lexical/list';
13
- import { CHECK_LIST, TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString } from '@lexical/markdown';
14
- import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
15
- import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
16
- import { ClickableLinkPlugin } from '@lexical/react/LexicalClickableLinkPlugin';
17
- import { LexicalComposer } from '@lexical/react/LexicalComposer';
18
- import { ContentEditable } from '@lexical/react/LexicalContentEditable';
19
- import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
20
- import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
21
- import { HorizontalRuleNode, $createHorizontalRuleNode, $isHorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
22
- import { HorizontalRulePlugin } from '@lexical/react/LexicalHorizontalRulePlugin';
23
- import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
24
- import { ListPlugin } from '@lexical/react/LexicalListPlugin';
25
- import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';
26
- import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
27
- import { TabIndentationPlugin } from '@lexical/react/LexicalTabIndentationPlugin';
28
- import { HeadingNode, QuoteNode, $isHeadingNode, $isQuoteNode, $createQuoteNode, $createHeadingNode } from '@lexical/rich-text';
29
- import { DecoratorNode, $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND, $createParagraphNode, PASTE_COMMAND, COMMAND_PRIORITY_CRITICAL, DROP_COMMAND, KEY_DOWN_COMMAND, COMMAND_PRIORITY_HIGH, $insertNodes, $getNodeByKey, $setSelection, FORMAT_ELEMENT_COMMAND, $createTextNode } from 'lexical';
30
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
31
- import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
32
- import { createPortal } from 'react-dom';
33
- import { $getNearestBlockElementAncestorOrThrow } from '@lexical/utils';
34
-
35
- var ImageRendererContext = createContext(null);
36
- var ImageRendererProvider = ImageRendererContext;
37
- var useImageRenderer = () => use(ImageRendererContext);
38
- var isPlaceholder = (src) => src.startsWith("upload-placeholder-");
39
- var ImageNodeComponent = ({ src, alt }) => {
40
- const renderer = useImageRenderer();
41
- if (isPlaceholder(src)) {
42
- return /* @__PURE__ */ jsx("span", { className: "tw:inline-flex tw:items-center tw:gap-1 tw:rounded tw:bg-neutral-100 tw:px-2 tw:py-1 tw:text-xs tw:text-neutral-400", children: "Uploading image\u2026" });
43
- }
44
- if (renderer) return /* @__PURE__ */ jsx(Fragment, { children: renderer({ src, alt }) });
45
- return /* @__PURE__ */ jsx("img", { src, alt });
1
+ import { cn } from "./classNames-BzrkDnOD.js";
2
+ import { Tooltip } from "./utils-D0c5VAyF.js";
3
+ import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, ChevronDown, Code, Heading1, Heading2, Heading3, Italic, Link, List, ListOrdered, Quote, SquareCode, Strikethrough, Type, Underline } from "./icons-Cek151jC.js";
4
+ import { Button } from "./Button-nzmDNS2q.js";
5
+ import { ButtonGroup } from "./internal/components/ButtonGroup.js";
6
+ import { DropdownMenu } from "./DropdownMenu-B6ZbYvaM.js";
7
+ import { Label } from "./internal/components/Label.js";
8
+ import { TextInput } from "./TextInput-BGed-V13.js";
9
+ import { FormGroup } from "./FormGroup-S9mPNHKH.js";
10
+ import { Skeleton } from "./internal/components/Skeleton/Skeleton.js";
11
+ import { toast } from "./toast-CjkURw0M.js";
12
+ import { Suspense, createContext, lazy, use, useCallback, useEffect, useRef, useState } from "react";
13
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
+ import { createPortal } from "react-dom";
15
+ import { $createCodeNode, $isCodeNode, CodeNode } from "@lexical/code";
16
+ import { $createLinkNode, $isLinkNode, LinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
17
+ import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListItemNode, ListNode, REMOVE_LIST_COMMAND } from "@lexical/list";
18
+ import { $convertFromMarkdownString, $convertToMarkdownString, CHECK_LIST, TRANSFORMERS } from "@lexical/markdown";
19
+ import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
20
+ import { CheckListPlugin } from "@lexical/react/LexicalCheckListPlugin";
21
+ import { ClickableLinkPlugin } from "@lexical/react/LexicalClickableLinkPlugin";
22
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
23
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
24
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
25
+ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
26
+ import { $createHorizontalRuleNode, $isHorizontalRuleNode, HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode";
27
+ import { HorizontalRulePlugin } from "@lexical/react/LexicalHorizontalRulePlugin";
28
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
29
+ import { ListPlugin } from "@lexical/react/LexicalListPlugin";
30
+ import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
31
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
32
+ import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin";
33
+ import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode } from "@lexical/rich-text";
34
+ import { $createParagraphNode, $createTextNode, $getNodeByKey, $getSelection, $insertNodes, $isRangeSelection, $setSelection, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_HIGH, DROP_COMMAND, DecoratorNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND } from "lexical";
35
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
36
+ import { $getNearestBlockElementAncestorOrThrow } from "@lexical/utils";
37
+ //#region src/blocks/MarkdownEditor/context.ts
38
+ const ImageRendererContext = createContext(null);
39
+ const ImageRendererProvider = ImageRendererContext;
40
+ const useImageRenderer = () => use(ImageRendererContext);
41
+ //#endregion
42
+ //#region src/blocks/MarkdownEditor/nodes/ImageNodeComponent.tsx
43
+ const isPlaceholder = (src) => src.startsWith("upload-placeholder-");
44
+ const ImageNodeComponent = ({ src, alt }) => {
45
+ const renderer = useImageRenderer();
46
+ if (isPlaceholder(src)) return /* @__PURE__ */ jsx("span", {
47
+ className: "tw:inline-flex tw:items-center tw:gap-1 tw:rounded tw:bg-neutral-100 tw:px-2 tw:py-1 tw:text-xs tw:text-neutral-400",
48
+ children: "Uploading image…"
49
+ });
50
+ if (renderer) return /* @__PURE__ */ jsx(Fragment$1, { children: renderer({
51
+ src,
52
+ alt
53
+ }) });
54
+ return /* @__PURE__ */ jsx("img", {
55
+ src,
56
+ alt
57
+ });
46
58
  };
47
- var ImageNode = class _ImageNode extends DecoratorNode {
48
- __src;
49
- __altText;
50
- static getType() {
51
- return "image";
52
- }
53
- static clone(node) {
54
- return new _ImageNode(node.__src, node.__altText, node.__key);
55
- }
56
- constructor(src, altText, key) {
57
- super(key);
58
- this.__src = src;
59
- this.__altText = altText;
60
- }
61
- getSrc() {
62
- return this.__src;
63
- }
64
- getAlt() {
65
- return this.__altText;
66
- }
67
- setSrc(src) {
68
- const writable = this.getWritable();
69
- writable.__src = src;
70
- }
71
- setAlt(altText) {
72
- const writable = this.getWritable();
73
- writable.__altText = altText;
74
- }
75
- createDOM(config) {
76
- const span = document.createElement("span");
77
- const className = config.theme.image;
78
- if (typeof className === "string") {
79
- span.className = className;
80
- }
81
- return span;
82
- }
83
- updateDOM() {
84
- return false;
85
- }
86
- decorate() {
87
- return /* @__PURE__ */ jsx(ImageNodeComponent, { src: this.__src, alt: this.__altText });
88
- }
89
- isInline() {
90
- return true;
91
- }
92
- static importJSON(serializedNode) {
93
- return $createImageNode(serializedNode.src, serializedNode.altText);
94
- }
95
- exportJSON() {
96
- return {
97
- type: "image",
98
- version: 1,
99
- src: this.__src,
100
- altText: this.__altText
101
- };
102
- }
103
- exportDOM() {
104
- const element = document.createElement("img");
105
- element.setAttribute("src", this.__src);
106
- element.setAttribute("alt", this.__altText);
107
- return { element };
108
- }
109
- static importDOM() {
110
- return {
111
- img: () => ({
112
- conversion: (domNode) => {
113
- const src = domNode.getAttribute("src") || "";
114
- const alt = domNode.getAttribute("alt") || "";
115
- return { node: $createImageNode(src, alt) };
116
- },
117
- priority: 0
118
- })
119
- };
120
- }
59
+ //#endregion
60
+ //#region src/blocks/MarkdownEditor/nodes/ImageNode.tsx
61
+ var ImageNode = class ImageNode extends DecoratorNode {
62
+ __src;
63
+ __altText;
64
+ static getType() {
65
+ return "image";
66
+ }
67
+ static clone(node) {
68
+ return new ImageNode(node.__src, node.__altText, node.__key);
69
+ }
70
+ constructor(src, altText, key) {
71
+ super(key);
72
+ this.__src = src;
73
+ this.__altText = altText;
74
+ }
75
+ getSrc() {
76
+ return this.__src;
77
+ }
78
+ getAlt() {
79
+ return this.__altText;
80
+ }
81
+ setSrc(src) {
82
+ const writable = this.getWritable();
83
+ writable.__src = src;
84
+ }
85
+ setAlt(altText) {
86
+ const writable = this.getWritable();
87
+ writable.__altText = altText;
88
+ }
89
+ createDOM(config) {
90
+ const span = document.createElement("span");
91
+ const className = config.theme.image;
92
+ if (typeof className === "string") span.className = className;
93
+ return span;
94
+ }
95
+ updateDOM() {
96
+ return false;
97
+ }
98
+ decorate() {
99
+ return /* @__PURE__ */ jsx(ImageNodeComponent, {
100
+ src: this.__src,
101
+ alt: this.__altText
102
+ });
103
+ }
104
+ isInline() {
105
+ return true;
106
+ }
107
+ static importJSON(serializedNode) {
108
+ return $createImageNode(serializedNode.src, serializedNode.altText);
109
+ }
110
+ exportJSON() {
111
+ return {
112
+ type: "image",
113
+ version: 1,
114
+ src: this.__src,
115
+ altText: this.__altText
116
+ };
117
+ }
118
+ exportDOM() {
119
+ const element = document.createElement("img");
120
+ element.setAttribute("src", this.__src);
121
+ element.setAttribute("alt", this.__altText);
122
+ return { element };
123
+ }
124
+ static importDOM() {
125
+ return { img: () => ({
126
+ conversion: (domNode) => {
127
+ const src = domNode.getAttribute("src") || "";
128
+ const alt = domNode.getAttribute("alt") || "";
129
+ return { node: $createImageNode(src, alt) };
130
+ },
131
+ priority: 0
132
+ }) };
133
+ }
121
134
  };
122
- var $createImageNode = (src, altText) => {
123
- return new ImageNode(src, altText);
135
+ const $createImageNode = (src, altText) => {
136
+ return new ImageNode(src, altText);
124
137
  };
125
- var $isImageNode = (node) => {
126
- return node instanceof ImageNode;
138
+ const $isImageNode = (node) => {
139
+ return node instanceof ImageNode;
127
140
  };
128
- var MermaidDiagram = lazy(
129
- () => import('./MermaidDiagram-EOKJHQQV.js').then(
130
- (m) => ({ default: m.MermaidDiagram }),
131
- (error) => {
132
- console.error("[MarkdownEditor] failed to load MermaidDiagram chunk", error);
133
- return {
134
- default: ({ code }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) })
135
- };
136
- }
137
- )
138
- );
139
- var MermaidNodeComponent = ({ code }) => {
140
- return /* @__PURE__ */ jsx(
141
- Suspense,
142
- {
143
- fallback: /* @__PURE__ */ jsx("div", { className: "tw:my-3 tw:overflow-hidden tw:rounded-card tw:border tw:border-neutral-150 tw:bg-neutral-50 tw:p-6", children: /* @__PURE__ */ jsx(Skeleton, { className: "tw:h-40" }) }),
144
- children: /* @__PURE__ */ jsx(MermaidDiagram, { code })
145
- }
146
- );
141
+ //#endregion
142
+ //#region src/blocks/MarkdownEditor/nodes/MermaidNodeComponent.tsx
143
+ const MermaidDiagram = lazy(() => import("./MermaidDiagram-BB06gymm.js").then((n) => n.MermaidDiagram_exports).then((m) => ({ default: m.MermaidDiagram }), (error) => {
144
+ console.error("[MarkdownEditor] failed to load MermaidDiagram chunk", error);
145
+ return { default: ({ code }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) }) };
146
+ }));
147
+ const MermaidNodeComponent = ({ code }) => {
148
+ return /* @__PURE__ */ jsx(Suspense, {
149
+ fallback: /* @__PURE__ */ jsx("div", {
150
+ className: "tw:my-3 tw:overflow-hidden tw:rounded-card tw:border tw:border-neutral-150 tw:bg-neutral-50 tw:p-6",
151
+ children: /* @__PURE__ */ jsx(Skeleton, { className: "tw:h-40" })
152
+ }),
153
+ children: /* @__PURE__ */ jsx(MermaidDiagram, { code })
154
+ });
147
155
  };
148
- var MermaidNode = class _MermaidNode extends DecoratorNode {
149
- __code;
150
- static getType() {
151
- return "mermaid";
152
- }
153
- static clone(node) {
154
- return new _MermaidNode(node.__code, node.__key);
155
- }
156
- constructor(code, key) {
157
- super(key);
158
- this.__code = code;
159
- }
160
- getCode() {
161
- return this.__code;
162
- }
163
- setCode(code) {
164
- const writable = this.getWritable();
165
- writable.__code = code;
166
- }
167
- createDOM() {
168
- const div = document.createElement("div");
169
- div.setAttribute("data-lexical-mermaid", "true");
170
- return div;
171
- }
172
- updateDOM() {
173
- return false;
174
- }
175
- decorate() {
176
- return /* @__PURE__ */ jsx(MermaidNodeComponent, { code: this.__code });
177
- }
178
- isInline() {
179
- return false;
180
- }
181
- static importJSON(serializedNode) {
182
- return $createMermaidNode(serializedNode.code);
183
- }
184
- exportJSON() {
185
- return {
186
- type: "mermaid",
187
- version: 1,
188
- code: this.__code
189
- };
190
- }
156
+ //#endregion
157
+ //#region src/blocks/MarkdownEditor/nodes/MermaidNode.tsx
158
+ var MermaidNode = class MermaidNode extends DecoratorNode {
159
+ __code;
160
+ static getType() {
161
+ return "mermaid";
162
+ }
163
+ static clone(node) {
164
+ return new MermaidNode(node.__code, node.__key);
165
+ }
166
+ constructor(code, key) {
167
+ super(key);
168
+ this.__code = code;
169
+ }
170
+ getCode() {
171
+ return this.__code;
172
+ }
173
+ setCode(code) {
174
+ const writable = this.getWritable();
175
+ writable.__code = code;
176
+ }
177
+ createDOM() {
178
+ const div = document.createElement("div");
179
+ div.setAttribute("data-lexical-mermaid", "true");
180
+ return div;
181
+ }
182
+ updateDOM() {
183
+ return false;
184
+ }
185
+ decorate() {
186
+ return /* @__PURE__ */ jsx(MermaidNodeComponent, { code: this.__code });
187
+ }
188
+ isInline() {
189
+ return false;
190
+ }
191
+ static importJSON(serializedNode) {
192
+ return $createMermaidNode(serializedNode.code);
193
+ }
194
+ exportJSON() {
195
+ return {
196
+ type: "mermaid",
197
+ version: 1,
198
+ code: this.__code
199
+ };
200
+ }
191
201
  };
192
- var $createMermaidNode = (code) => {
193
- return new MermaidNode(code);
202
+ const $createMermaidNode = (code) => {
203
+ return new MermaidNode(code);
194
204
  };
195
- var $isMermaidNode = (node) => {
196
- return node instanceof MermaidNode;
205
+ const $isMermaidNode = (node) => {
206
+ return node instanceof MermaidNode;
197
207
  };
198
- var ACCEPTED_IMAGE_TYPES = [
199
- "image/png",
200
- "image/jpeg",
201
- "image/gif",
202
- "image/webp",
203
- "image/svg+xml"
208
+ //#endregion
209
+ //#region src/blocks/MarkdownEditor/plugins/ImagePastePlugin.tsx
210
+ const ACCEPTED_IMAGE_TYPES = [
211
+ "image/png",
212
+ "image/jpeg",
213
+ "image/gif",
214
+ "image/webp",
215
+ "image/svg+xml"
204
216
  ];
205
- var MAX_IMAGE_SIZE = 10 * 1024 * 1024;
206
- var PLACEHOLDER_PREFIX = "upload-placeholder-";
207
- var generatePlaceholderId = () => {
208
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
209
- return crypto.randomUUID();
210
- }
211
- return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
212
- };
213
- var isAcceptedImageType = (type) => ACCEPTED_IMAGE_TYPES.includes(type);
214
- var getImageFilesFromDataTransfer = (dataTransfer) => {
215
- if (!dataTransfer) return [];
216
- if (dataTransfer.items.length > 0) {
217
- const files = [];
218
- for (const item of dataTransfer.items) {
219
- if (item.kind === "file" && isAcceptedImageType(item.type)) {
220
- const file = item.getAsFile();
221
- if (file) files.push(file);
222
- }
223
- }
224
- if (files.length > 0) return files;
225
- }
226
- return Array.from(dataTransfer.files).filter((file) => isAcceptedImageType(file.type));
227
- };
228
- var escapeMarkdownAlt = (text) => text.replace(/[[\]()]/g, "");
229
- var ImagePastePlugin = ({ onImageUpload }) => {
230
- const [editor] = useLexicalComposerContext();
231
- const onImageUploadRef = useRef(onImageUpload);
232
- onImageUploadRef.current = onImageUpload;
233
- useEffect(() => {
234
- const uploadFile = (file) => {
235
- const upload = onImageUploadRef.current;
236
- if (!upload) return;
237
- if (file.size > MAX_IMAGE_SIZE) {
238
- toast.error(`Image "${file.name}" is too large. Maximum size is 10 MB.`);
239
- return;
240
- }
241
- let nodeKey = null;
242
- editor.update(() => {
243
- const placeholderNode = $createImageNode(
244
- `${PLACEHOLDER_PREFIX}${generatePlaceholderId()}`,
245
- "Uploading image\u2026"
246
- );
247
- $insertNodes([placeholderNode]);
248
- nodeKey = placeholderNode.getKey();
249
- });
250
- if (!nodeKey) return;
251
- const capturedKey = nodeKey;
252
- upload(file).then(({ url }) => {
253
- editor.update(() => {
254
- const node = $getNodeByKey(capturedKey);
255
- if ($isImageNode(node)) {
256
- const altText = escapeMarkdownAlt(file.name.replace(/\.[^.]+$/, "") || "image");
257
- node.setSrc(url);
258
- node.setAlt(altText);
259
- }
260
- });
261
- }).catch((error) => {
262
- console.error("[MarkdownEditor] image upload failed", error);
263
- toast.error(`Failed to upload "${file.name}". Please try again.`);
264
- editor.update(() => {
265
- const node = $getNodeByKey(capturedKey);
266
- if (node) node.remove();
267
- });
268
- });
269
- };
270
- const removePaste = editor.registerCommand(
271
- PASTE_COMMAND,
272
- (event) => {
273
- if (!onImageUploadRef.current) return false;
274
- if (!(event instanceof ClipboardEvent)) return false;
275
- const files = getImageFilesFromDataTransfer(event.clipboardData);
276
- if (files.length === 0) return false;
277
- event.preventDefault();
278
- Promise.resolve().then(() => files.forEach(uploadFile));
279
- return true;
280
- },
281
- COMMAND_PRIORITY_CRITICAL
282
- );
283
- const removeDrop = editor.registerCommand(
284
- DROP_COMMAND,
285
- (event) => {
286
- if (!onImageUploadRef.current) return false;
287
- const files = getImageFilesFromDataTransfer(event.dataTransfer);
288
- if (files.length === 0) return false;
289
- event.preventDefault();
290
- Promise.resolve().then(() => files.forEach(uploadFile));
291
- return true;
292
- },
293
- COMMAND_PRIORITY_CRITICAL
294
- );
295
- return () => {
296
- removePaste();
297
- removeDrop();
298
- };
299
- }, [editor]);
300
- return null;
217
+ const MAX_IMAGE_SIZE = 10485760;
218
+ const PLACEHOLDER_PREFIX = "upload-placeholder-";
219
+ const generatePlaceholderId = () => {
220
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
221
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
301
222
  };
302
- var KeyboardPlugin = ({
303
- onSave,
304
- onCancel,
305
- singleLine,
306
- blurOnEscape,
307
- skipBlurSaveRef
308
- }) => {
309
- const [editor] = useLexicalComposerContext();
310
- const onSaveRef = useRef(onSave);
311
- onSaveRef.current = onSave;
312
- const onCancelRef = useRef(onCancel);
313
- onCancelRef.current = onCancel;
314
- const singleLineRef = useRef(singleLine);
315
- singleLineRef.current = singleLine;
316
- const blurOnEscapeRef = useRef(blurOnEscape);
317
- blurOnEscapeRef.current = blurOnEscape;
318
- const skipBlurSaveExternalRef = skipBlurSaveRef;
319
- useEffect(() => {
320
- return editor.registerCommand(
321
- KEY_DOWN_COMMAND,
322
- (event) => {
323
- if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
324
- event.preventDefault();
325
- onSaveRef.current?.();
326
- return true;
327
- }
328
- if (event.key === "Enter" && !event.shiftKey && !event.altKey && singleLineRef.current) {
329
- event.preventDefault();
330
- editor.getRootElement()?.blur();
331
- return true;
332
- }
333
- if (event.key === "Escape" && blurOnEscapeRef.current) {
334
- if (skipBlurSaveExternalRef) skipBlurSaveExternalRef.current = true;
335
- onCancelRef.current?.();
336
- editor.getRootElement()?.blur();
337
- return true;
338
- }
339
- return false;
340
- },
341
- COMMAND_PRIORITY_HIGH
342
- );
343
- }, [editor, skipBlurSaveExternalRef]);
344
- return null;
223
+ const isAcceptedImageType = (type) => ACCEPTED_IMAGE_TYPES.includes(type);
224
+ const getImageFilesFromDataTransfer = (dataTransfer) => {
225
+ if (!dataTransfer) return [];
226
+ if (dataTransfer.items.length > 0) {
227
+ const files = [];
228
+ for (const item of dataTransfer.items) if (item.kind === "file" && isAcceptedImageType(item.type)) {
229
+ const file = item.getAsFile();
230
+ if (file) files.push(file);
231
+ }
232
+ if (files.length > 0) return files;
233
+ }
234
+ return Array.from(dataTransfer.files).filter((file) => isAcceptedImageType(file.type));
345
235
  };
346
- var IMAGE = {
347
- dependencies: [ImageNode],
348
- export: (node) => {
349
- if (!$isImageNode(node)) return null;
350
- return `![${node.getAlt()}](${node.getSrc()})`;
351
- },
352
- importRegExp: /!\[([^\]]*)\]\(([^)]+)\)/,
353
- regExp: /!\[([^\]]*)\]\(([^)]+)\)$/,
354
- replace: (textNode, match) => {
355
- const [, alt = "", src = ""] = match;
356
- const imageNode = $createImageNode(src, alt);
357
- textNode.replace(imageNode);
358
- },
359
- trigger: ")",
360
- type: "text-match"
236
+ const escapeMarkdownAlt = (text) => text.replace(/[[\]()]/g, "");
237
+ const ImagePastePlugin = ({ onImageUpload }) => {
238
+ const [editor] = useLexicalComposerContext();
239
+ const onImageUploadRef = useRef(onImageUpload);
240
+ onImageUploadRef.current = onImageUpload;
241
+ useEffect(() => {
242
+ const uploadFile = (file) => {
243
+ const upload = onImageUploadRef.current;
244
+ if (!upload) return;
245
+ if (file.size > MAX_IMAGE_SIZE) {
246
+ toast.error(`Image "${file.name}" is too large. Maximum size is 10 MB.`);
247
+ return;
248
+ }
249
+ let nodeKey = null;
250
+ editor.update(() => {
251
+ const placeholderNode = $createImageNode(`${PLACEHOLDER_PREFIX}${generatePlaceholderId()}`, "Uploading image…");
252
+ $insertNodes([placeholderNode]);
253
+ nodeKey = placeholderNode.getKey();
254
+ });
255
+ if (!nodeKey) return;
256
+ const capturedKey = nodeKey;
257
+ upload(file).then(({ url }) => {
258
+ editor.update(() => {
259
+ const node = $getNodeByKey(capturedKey);
260
+ if ($isImageNode(node)) {
261
+ const altText = escapeMarkdownAlt(file.name.replace(/\.[^.]+$/, "") || "image");
262
+ node.setSrc(url);
263
+ node.setAlt(altText);
264
+ }
265
+ });
266
+ }).catch((error) => {
267
+ console.error("[MarkdownEditor] image upload failed", error);
268
+ toast.error(`Failed to upload "${file.name}". Please try again.`);
269
+ editor.update(() => {
270
+ const node = $getNodeByKey(capturedKey);
271
+ if (node) node.remove();
272
+ });
273
+ });
274
+ };
275
+ const removePaste = editor.registerCommand(PASTE_COMMAND, (event) => {
276
+ if (!onImageUploadRef.current) return false;
277
+ if (!(event instanceof ClipboardEvent)) return false;
278
+ const files = getImageFilesFromDataTransfer(event.clipboardData);
279
+ if (files.length === 0) return false;
280
+ event.preventDefault();
281
+ Promise.resolve().then(() => files.forEach(uploadFile));
282
+ return true;
283
+ }, COMMAND_PRIORITY_CRITICAL);
284
+ const removeDrop = editor.registerCommand(DROP_COMMAND, (event) => {
285
+ if (!onImageUploadRef.current) return false;
286
+ const files = getImageFilesFromDataTransfer(event.dataTransfer);
287
+ if (files.length === 0) return false;
288
+ event.preventDefault();
289
+ Promise.resolve().then(() => files.forEach(uploadFile));
290
+ return true;
291
+ }, COMMAND_PRIORITY_CRITICAL);
292
+ return () => {
293
+ removePaste();
294
+ removeDrop();
295
+ };
296
+ }, [editor]);
297
+ return null;
361
298
  };
362
- var HORIZONTAL_RULE = {
363
- dependencies: [HorizontalRuleNode],
364
- export: (node) => {
365
- return $isHorizontalRuleNode(node) ? "---" : null;
366
- },
367
- regExp: /^(---|\*\*\*|___)$/,
368
- replace: (parentNode) => {
369
- const node = $createHorizontalRuleNode();
370
- parentNode.replace(node);
371
- },
372
- type: "element"
299
+ //#endregion
300
+ //#region src/blocks/MarkdownEditor/plugins/KeyboardPlugin.tsx
301
+ const KeyboardPlugin = ({ onSave, onCancel, singleLine, blurOnEscape, skipBlurSaveRef }) => {
302
+ const [editor] = useLexicalComposerContext();
303
+ const onSaveRef = useRef(onSave);
304
+ onSaveRef.current = onSave;
305
+ const onCancelRef = useRef(onCancel);
306
+ onCancelRef.current = onCancel;
307
+ const singleLineRef = useRef(singleLine);
308
+ singleLineRef.current = singleLine;
309
+ const blurOnEscapeRef = useRef(blurOnEscape);
310
+ blurOnEscapeRef.current = blurOnEscape;
311
+ const skipBlurSaveExternalRef = skipBlurSaveRef;
312
+ useEffect(() => {
313
+ return editor.registerCommand(KEY_DOWN_COMMAND, (event) => {
314
+ if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
315
+ event.preventDefault();
316
+ onSaveRef.current?.();
317
+ return true;
318
+ }
319
+ if (event.key === "Enter" && !event.shiftKey && !event.altKey && singleLineRef.current) {
320
+ event.preventDefault();
321
+ editor.getRootElement()?.blur();
322
+ return true;
323
+ }
324
+ if (event.key === "Escape" && blurOnEscapeRef.current) {
325
+ if (skipBlurSaveExternalRef) skipBlurSaveExternalRef.current = true;
326
+ onCancelRef.current?.();
327
+ editor.getRootElement()?.blur();
328
+ return true;
329
+ }
330
+ return false;
331
+ }, COMMAND_PRIORITY_HIGH);
332
+ }, [editor, skipBlurSaveExternalRef]);
333
+ return null;
373
334
  };
374
- var MERMAID = {
375
- dependencies: [MermaidNode],
376
- export: (node) => {
377
- if (!$isMermaidNode(node)) return null;
378
- return "```mermaid\n" + node.getCode() + "\n```";
379
- },
380
- regExpStart: /^[ \t]*(`{3,})mermaid(?:\s.*)?$/,
381
- regExpEnd: {
382
- optional: true,
383
- regExp: /^[ \t]*`{3,}$/
384
- },
385
- replace: (rootNode, _children, _startMatch, _endMatch, linesInBetween) => {
386
- if (!linesInBetween) return;
387
- const code = linesInBetween.join("\n").trim();
388
- if (code) {
389
- rootNode.append($createMermaidNode(code));
390
- }
391
- },
392
- handleImportAfterStartMatch: ({
393
- lines,
394
- rootNode,
395
- startLineIndex,
396
- startMatch
397
- }) => {
398
- const fence = startMatch[1];
399
- if (!fence) return [false, startLineIndex];
400
- const fenceLength = fence.trim().length;
401
- const endRegex = new RegExp(`^[ \\t]*\`{${fenceLength},}$`);
402
- for (let i = startLineIndex + 1; i < lines.length; i++) {
403
- if (endRegex.test(lines[i] ?? "")) {
404
- const code2 = lines.slice(startLineIndex + 1, i).join("\n").trim();
405
- if (code2) {
406
- rootNode.append($createMermaidNode(code2));
407
- }
408
- return [true, i];
409
- }
410
- }
411
- const code = lines.slice(startLineIndex + 1).join("\n").trim();
412
- if (code) {
413
- rootNode.append($createMermaidNode(code));
414
- }
415
- return [true, lines.length - 1];
416
- },
417
- type: "multiline-element"
418
- };
419
- var CUSTOM_TRANSFORMERS = [
420
- IMAGE,
421
- HORIZONTAL_RULE,
422
- CHECK_LIST,
423
- MERMAID,
424
- ...TRANSFORMERS
335
+ /**
336
+ * Custom transformers = our additions + built-in Lexical transformers.
337
+ * MERMAID is placed before TRANSFORMERS so it intercepts ` ```mermaid ``` ` before the generic CODE transformer.
338
+ * IMAGE is placed before built-in LINK to prevent `![alt](url)` from being parsed as `!` + link.
339
+ */
340
+ const CUSTOM_TRANSFORMERS = [
341
+ {
342
+ dependencies: [ImageNode],
343
+ export: (node) => {
344
+ if (!$isImageNode(node)) return null;
345
+ return `![${node.getAlt()}](${node.getSrc()})`;
346
+ },
347
+ importRegExp: /!\[([^\]]*)\]\(([^)]+)\)/,
348
+ regExp: /!\[([^\]]*)\]\(([^)]+)\)$/,
349
+ replace: (textNode, match) => {
350
+ const [, alt = "", src = ""] = match;
351
+ const imageNode = $createImageNode(src, alt);
352
+ textNode.replace(imageNode);
353
+ },
354
+ trigger: ")",
355
+ type: "text-match"
356
+ },
357
+ {
358
+ dependencies: [HorizontalRuleNode],
359
+ export: (node) => {
360
+ return $isHorizontalRuleNode(node) ? "---" : null;
361
+ },
362
+ regExp: /^(---|\*\*\*|___)$/,
363
+ replace: (parentNode) => {
364
+ const node = $createHorizontalRuleNode();
365
+ parentNode.replace(node);
366
+ },
367
+ type: "element"
368
+ },
369
+ CHECK_LIST,
370
+ {
371
+ dependencies: [MermaidNode],
372
+ export: (node) => {
373
+ if (!$isMermaidNode(node)) return null;
374
+ return "```mermaid\n" + node.getCode() + "\n```";
375
+ },
376
+ regExpStart: /^[ \t]*(`{3,})mermaid(?:\s.*)?$/,
377
+ regExpEnd: {
378
+ optional: true,
379
+ regExp: /^[ \t]*`{3,}$/
380
+ },
381
+ replace: (rootNode, _children, _startMatch, _endMatch, linesInBetween) => {
382
+ if (!linesInBetween) return;
383
+ const code = linesInBetween.join("\n").trim();
384
+ if (code) rootNode.append($createMermaidNode(code));
385
+ },
386
+ handleImportAfterStartMatch: ({ lines, rootNode, startLineIndex, startMatch }) => {
387
+ const fence = startMatch[1];
388
+ if (!fence) return [false, startLineIndex];
389
+ const fenceLength = fence.trim().length;
390
+ const endRegex = new RegExp(`^[ \\t]*\`{${fenceLength},}$`);
391
+ for (let i = startLineIndex + 1; i < lines.length; i++) if (endRegex.test(lines[i] ?? "")) {
392
+ const code = lines.slice(startLineIndex + 1, i).join("\n").trim();
393
+ if (code) rootNode.append($createMermaidNode(code));
394
+ return [true, i];
395
+ }
396
+ const code = lines.slice(startLineIndex + 1).join("\n").trim();
397
+ if (code) rootNode.append($createMermaidNode(code));
398
+ return [true, lines.length - 1];
399
+ },
400
+ type: "multiline-element"
401
+ },
402
+ ...TRANSFORMERS
425
403
  ];
426
- var SINGLE_LINE_TRANSFORMERS = [];
427
- var TABLE_FENCE_LANG = "__kbc_table__";
428
- var isTableLine = (line) => {
429
- const trimmed = line.trim();
430
- return trimmed.startsWith("|") && trimmed.endsWith("|");
404
+ /**
405
+ * No transformers = plain text. `singleLine` mode edits entity names / titles, which are
406
+ * plain text, not markdown. Parsing inline formatting here corrupts ordinary names: e.g.
407
+ * `staging_chargebee_invoices` would render the `_chargebee_` span as italics, and Lexical
408
+ * escapes the underscores on export (`\_`) to prevent exactly that — but those backslashes
409
+ * then leak into the persisted name. Dropping every transformer keeps `_`, `*`, `` ` ``, `~`
410
+ * literal on both import and export; pair the empty set with `unescapeMarkdownText` on export
411
+ * to strip Lexical's escaping (see MarkdownSyncPlugin).
412
+ */
413
+ const SINGLE_LINE_TRANSFORMERS = [];
414
+ const TABLE_FENCE_LANG = "__kbc_table__";
415
+ const isTableLine = (line) => {
416
+ const trimmed = line.trim();
417
+ return trimmed.startsWith("|") && trimmed.endsWith("|");
431
418
  };
432
- var CODE_FENCE_REGEX = /^[ \t]*`{3,}/;
433
- var preprocessMarkdown = (markdown) => {
434
- const lines = markdown.split("\n");
435
- const result = [];
436
- let tableLines = [];
437
- let insideCodeFence = false;
438
- const flushTable = () => {
439
- if (tableLines.length >= 2) {
440
- result.push(`\`\`\`${TABLE_FENCE_LANG}`);
441
- result.push(...tableLines);
442
- result.push("```");
443
- } else {
444
- result.push(...tableLines);
445
- }
446
- tableLines = [];
447
- };
448
- for (const line of lines) {
449
- if (CODE_FENCE_REGEX.test(line)) {
450
- if (tableLines.length > 0) flushTable();
451
- insideCodeFence = !insideCodeFence;
452
- result.push(line);
453
- continue;
454
- }
455
- if (insideCodeFence) {
456
- result.push(line);
457
- continue;
458
- }
459
- if (isTableLine(line)) {
460
- tableLines.push(line);
461
- } else {
462
- if (tableLines.length > 0) flushTable();
463
- result.push(line);
464
- }
465
- }
466
- if (tableLines.length > 0) flushTable();
467
- return result.join("\n");
419
+ const CODE_FENCE_REGEX = /^[ \t]*`{3,}/;
420
+ /** Wrap GFM tables in code fences so they survive Lexical's markdown round-trip. */
421
+ const preprocessMarkdown = (markdown) => {
422
+ const lines = markdown.split("\n");
423
+ const result = [];
424
+ let tableLines = [];
425
+ let insideCodeFence = false;
426
+ const flushTable = () => {
427
+ if (tableLines.length >= 2) {
428
+ result.push(`\`\`\`${TABLE_FENCE_LANG}`);
429
+ result.push(...tableLines);
430
+ result.push("```");
431
+ } else result.push(...tableLines);
432
+ tableLines = [];
433
+ };
434
+ for (const line of lines) {
435
+ if (CODE_FENCE_REGEX.test(line)) {
436
+ if (tableLines.length > 0) flushTable();
437
+ insideCodeFence = !insideCodeFence;
438
+ result.push(line);
439
+ continue;
440
+ }
441
+ if (insideCodeFence) {
442
+ result.push(line);
443
+ continue;
444
+ }
445
+ if (isTableLine(line)) tableLines.push(line);
446
+ else {
447
+ if (tableLines.length > 0) flushTable();
448
+ result.push(line);
449
+ }
450
+ }
451
+ if (tableLines.length > 0) flushTable();
452
+ return result.join("\n");
468
453
  };
469
- var postprocessMarkdown = (markdown) => {
470
- const regex = new RegExp(`\`\`\`${TABLE_FENCE_LANG}\\n([\\s\\S]*?)\\n\`\`\``, "g");
471
- return markdown.replace(regex, "$1");
454
+ /** Unwrap code-fenced tables back to raw GFM table markdown. */
455
+ const postprocessMarkdown = (markdown) => {
456
+ const regex = new RegExp(`\`\`\`${TABLE_FENCE_LANG}\\n([\\s\\S]*?)\\n\`\`\``, "g");
457
+ return markdown.replace(regex, "$1");
472
458
  };
473
- var unescapeMarkdownText = (markdown) => markdown.replace(/\\([*_`~\\])/g, "$1");
474
-
475
- // src/blocks/MarkdownEditor/plugins/MarkdownSyncPlugin.tsx
476
- var IMPORT_TAG = "markdown-import";
477
- var MarkdownSyncPlugin = ({
478
- value,
479
- editable,
480
- onChange,
481
- transformers = CUSTOM_TRANSFORMERS,
482
- singleLine = false
483
- }) => {
484
- const [editor] = useLexicalComposerContext();
485
- const lastExportedRef = useRef(value);
486
- const onChangeRef = useRef(onChange);
487
- onChangeRef.current = onChange;
488
- const transformersRef = useRef(transformers);
489
- transformersRef.current = transformers;
490
- const singleLineRef = useRef(singleLine);
491
- singleLineRef.current = singleLine;
492
- useEffect(() => {
493
- editor.setEditable(editable);
494
- }, [editor, editable]);
495
- useEffect(() => {
496
- if (value === lastExportedRef.current) return;
497
- lastExportedRef.current = value;
498
- editor.update(
499
- () => {
500
- $convertFromMarkdownString(preprocessMarkdown(value), transformersRef.current);
501
- },
502
- { tag: IMPORT_TAG }
503
- );
504
- }, [editor, value]);
505
- useEffect(() => {
506
- return editor.registerUpdateListener(({ tags }) => {
507
- if (tags.has(IMPORT_TAG)) return;
508
- editor.getEditorState().read(() => {
509
- const raw = $convertToMarkdownString(transformersRef.current);
510
- const markdown = singleLineRef.current ? unescapeMarkdownText(raw) : postprocessMarkdown(raw);
511
- if (markdown !== lastExportedRef.current) {
512
- lastExportedRef.current = markdown;
513
- onChangeRef.current?.(markdown);
514
- }
515
- });
516
- });
517
- }, [editor]);
518
- return null;
459
+ /**
460
+ * Reverses the backslash-escaping Lexical applies to markdown punctuation (`* _ ` ~ \`) when
461
+ * exporting text nodes. Used for `singleLine` (plain-text) mode, where the export value is a
462
+ * bare entity name — the escaping would otherwise persist as literal backslashes in the name.
463
+ */
464
+ const unescapeMarkdownText = (markdown) => markdown.replace(/\\([*_`~\\])/g, "$1");
465
+ //#endregion
466
+ //#region src/blocks/MarkdownEditor/plugins/MarkdownSyncPlugin.tsx
467
+ const IMPORT_TAG = "markdown-import";
468
+ const MarkdownSyncPlugin = ({ value, editable, onChange, transformers = CUSTOM_TRANSFORMERS, singleLine = false }) => {
469
+ const [editor] = useLexicalComposerContext();
470
+ const lastExportedRef = useRef(value);
471
+ const onChangeRef = useRef(onChange);
472
+ onChangeRef.current = onChange;
473
+ const transformersRef = useRef(transformers);
474
+ transformersRef.current = transformers;
475
+ const singleLineRef = useRef(singleLine);
476
+ singleLineRef.current = singleLine;
477
+ useEffect(() => {
478
+ editor.setEditable(editable);
479
+ }, [editor, editable]);
480
+ useEffect(() => {
481
+ if (value === lastExportedRef.current) return;
482
+ lastExportedRef.current = value;
483
+ editor.update(() => {
484
+ $convertFromMarkdownString(preprocessMarkdown(value), transformersRef.current);
485
+ }, { tag: IMPORT_TAG });
486
+ }, [editor, value]);
487
+ useEffect(() => {
488
+ return editor.registerUpdateListener(({ tags }) => {
489
+ if (tags.has(IMPORT_TAG)) return;
490
+ editor.getEditorState().read(() => {
491
+ const raw = $convertToMarkdownString(transformersRef.current);
492
+ const markdown = singleLineRef.current ? unescapeMarkdownText(raw) : postprocessMarkdown(raw);
493
+ if (markdown !== lastExportedRef.current) {
494
+ lastExportedRef.current = markdown;
495
+ onChangeRef.current?.(markdown);
496
+ }
497
+ });
498
+ });
499
+ }, [editor]);
500
+ return null;
519
501
  };
520
- var ALIGNMENT_OPTIONS = [
521
- { value: "left", label: "Left", icon: AlignLeft },
522
- { value: "center", label: "Center", icon: AlignCenter },
523
- { value: "right", label: "Right", icon: AlignRight },
524
- { value: "justify", label: "Justify", icon: AlignJustify }
502
+ //#endregion
503
+ //#region src/blocks/MarkdownEditor/plugins/toolbar/AlignmentDropdown.tsx
504
+ const ALIGNMENT_OPTIONS = [
505
+ {
506
+ value: "left",
507
+ label: "Left",
508
+ icon: AlignLeft
509
+ },
510
+ {
511
+ value: "center",
512
+ label: "Center",
513
+ icon: AlignCenter
514
+ },
515
+ {
516
+ value: "right",
517
+ label: "Right",
518
+ icon: AlignRight
519
+ },
520
+ {
521
+ value: "justify",
522
+ label: "Justify",
523
+ icon: AlignJustify
524
+ }
525
525
  ];
526
- var ALIGNMENT_ICONS = Object.fromEntries(
527
- ALIGNMENT_OPTIONS.map(({ value, icon }) => [value, icon])
528
- );
529
- var AlignmentDropdown = ({
530
- editor,
531
- alignment
532
- }) => {
533
- const savedSelectionRef = useRef(null);
534
- const handleOpenChange = (open) => {
535
- if (open) {
536
- editor.getEditorState().read(() => {
537
- const selection = $getSelection();
538
- savedSelectionRef.current = selection ? selection.clone() : null;
539
- });
540
- }
541
- };
542
- const handleSelect = (align) => {
543
- editor.update(() => {
544
- if (savedSelectionRef.current) {
545
- $setSelection(savedSelectionRef.current);
546
- }
547
- editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, align);
548
- });
549
- };
550
- const CurrentAlignmentIcon = ALIGNMENT_ICONS[alignment] ?? AlignLeft;
551
- return /* @__PURE__ */ jsxs(DropdownMenu, { onOpenChange: handleOpenChange, children: [
552
- /* @__PURE__ */ jsx(Tooltip, { tooltip: "Text alignment", placement: "top", triggerAsChild: true, children: /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
553
- "button",
554
- {
555
- type: "button",
556
- className: cn(
557
- "tw:flex tw:h-7 tw:cursor-pointer tw:items-center tw:gap-1 tw:rounded tw:border-0 tw:bg-transparent tw:px-1.5 tw:text-xs tw:text-muted-foreground",
558
- "tw:hover:bg-accent"
559
- ),
560
- children: [
561
- /* @__PURE__ */ jsx(CurrentAlignmentIcon, { className: "tw:size-4" }),
562
- /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-3" })
563
- ]
564
- }
565
- ) }) }),
566
- /* @__PURE__ */ jsx(DropdownMenu.Content, { align: "start", className: "tw:min-w-32", children: ALIGNMENT_OPTIONS.map(({ value, label, icon: IconComp }) => /* @__PURE__ */ jsxs(
567
- DropdownMenu.Item,
568
- {
569
- onSelect: () => handleSelect(value),
570
- className: cn(
571
- "tw:flex tw:items-center tw:gap-2",
572
- alignment === value && "tw:bg-secondary-50 tw:text-secondary-500"
573
- ),
574
- children: [
575
- /* @__PURE__ */ jsx(IconComp, { className: "tw:size-4" }),
576
- label
577
- ]
578
- },
579
- value
580
- )) })
581
- ] });
526
+ const ALIGNMENT_ICONS = Object.fromEntries(ALIGNMENT_OPTIONS.map(({ value, icon }) => [value, icon]));
527
+ const AlignmentDropdown = ({ editor, alignment }) => {
528
+ const savedSelectionRef = useRef(null);
529
+ const handleOpenChange = (open) => {
530
+ if (open) editor.getEditorState().read(() => {
531
+ const selection = $getSelection();
532
+ savedSelectionRef.current = selection ? selection.clone() : null;
533
+ });
534
+ };
535
+ const handleSelect = (align) => {
536
+ editor.update(() => {
537
+ if (savedSelectionRef.current) $setSelection(savedSelectionRef.current);
538
+ editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, align);
539
+ });
540
+ };
541
+ const CurrentAlignmentIcon = ALIGNMENT_ICONS[alignment] ?? AlignLeft;
542
+ return /* @__PURE__ */ jsxs(DropdownMenu, {
543
+ onOpenChange: handleOpenChange,
544
+ children: [/* @__PURE__ */ jsx(Tooltip, {
545
+ tooltip: "Text alignment",
546
+ placement: "top",
547
+ triggerAsChild: true,
548
+ children: /* @__PURE__ */ jsx(DropdownMenu.Trigger, {
549
+ asChild: true,
550
+ children: /* @__PURE__ */ jsxs("button", {
551
+ type: "button",
552
+ className: cn("tw:flex tw:h-7 tw:cursor-pointer tw:items-center tw:gap-1 tw:rounded tw:border-0 tw:bg-transparent tw:px-1.5 tw:text-xs tw:text-muted-foreground", "tw:hover:bg-accent"),
553
+ children: [/* @__PURE__ */ jsx(CurrentAlignmentIcon, { className: "tw:size-4" }), /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-3" })]
554
+ })
555
+ })
556
+ }), /* @__PURE__ */ jsx(DropdownMenu.Content, {
557
+ align: "start",
558
+ className: "tw:min-w-32",
559
+ children: ALIGNMENT_OPTIONS.map(({ value, label, icon: IconComp }) => /* @__PURE__ */ jsxs(DropdownMenu.Item, {
560
+ onSelect: () => handleSelect(value),
561
+ className: cn("tw:flex tw:items-center tw:gap-2", alignment === value && "tw:bg-secondary-50 tw:text-secondary-500"),
562
+ children: [/* @__PURE__ */ jsx(IconComp, { className: "tw:size-4" }), label]
563
+ }, value))
564
+ })]
565
+ });
582
566
  };
583
- var BLOCK_TYPE_META = {
584
- paragraph: { label: "Normal", icon: Type, selectable: true },
585
- h1: { label: "Heading 1", icon: Heading1, selectable: true },
586
- h2: { label: "Heading 2", icon: Heading2, selectable: true },
587
- h3: { label: "Heading 3", icon: Heading3, selectable: true },
588
- bullet: { label: "Bullet List", icon: List, selectable: false },
589
- number: { label: "Numbered List", icon: ListOrdered, selectable: false },
590
- quote: { label: "Quote", icon: Quote, selectable: false },
591
- code: { label: "Code Block", icon: SquareCode, selectable: false }
567
+ //#endregion
568
+ //#region src/blocks/MarkdownEditor/plugins/toolbar/BlockTypeDropdown.tsx
569
+ const BLOCK_TYPE_META = {
570
+ paragraph: {
571
+ label: "Normal",
572
+ icon: Type,
573
+ selectable: true
574
+ },
575
+ h1: {
576
+ label: "Heading 1",
577
+ icon: Heading1,
578
+ selectable: true
579
+ },
580
+ h2: {
581
+ label: "Heading 2",
582
+ icon: Heading2,
583
+ selectable: true
584
+ },
585
+ h3: {
586
+ label: "Heading 3",
587
+ icon: Heading3,
588
+ selectable: true
589
+ },
590
+ bullet: {
591
+ label: "Bullet List",
592
+ icon: List,
593
+ selectable: false
594
+ },
595
+ number: {
596
+ label: "Numbered List",
597
+ icon: ListOrdered,
598
+ selectable: false
599
+ },
600
+ quote: {
601
+ label: "Quote",
602
+ icon: Quote,
603
+ selectable: false
604
+ },
605
+ code: {
606
+ label: "Code Block",
607
+ icon: SquareCode,
608
+ selectable: false
609
+ }
592
610
  };
593
- var BLOCK_TYPE_OPTIONS = Object.entries(BLOCK_TYPE_META).filter(([, meta]) => meta.selectable).map(([value, { label, icon }]) => ({ value, label, icon }));
594
- var isBlockType = (value) => Object.prototype.hasOwnProperty.call(BLOCK_TYPE_META, value);
595
- var BlockTypeDropdown = ({
596
- editor,
597
- blockType
598
- }) => {
599
- const savedSelectionRef = useRef(null);
600
- const currentMeta = isBlockType(blockType) ? BLOCK_TYPE_META[blockType] : null;
601
- const CurrentBlockIcon = currentMeta?.icon ?? Type;
602
- const handleOpenChange = (open) => {
603
- if (open) {
604
- editor.getEditorState().read(() => {
605
- const selection = $getSelection();
606
- savedSelectionRef.current = selection ? selection.clone() : null;
607
- });
608
- }
609
- };
610
- const handleSelect = (type) => {
611
- editor.update(() => {
612
- if (savedSelectionRef.current) {
613
- $setSelection(savedSelectionRef.current);
614
- }
615
- const selection = $getSelection();
616
- if (!$isRangeSelection(selection)) return;
617
- if (type === "bullet") {
618
- editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0);
619
- return;
620
- }
621
- if (type === "number") {
622
- editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0);
623
- return;
624
- }
625
- const anchorNode = selection.anchor.getNode();
626
- const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
627
- const newNode = type === "h1" || type === "h2" || type === "h3" ? $createHeadingNode(type) : type === "quote" ? $createQuoteNode() : type === "code" ? $createCodeNode() : $createParagraphNode();
628
- element.getChildren().forEach((child) => newNode.append(child));
629
- element.replace(newNode);
630
- });
631
- };
632
- return /* @__PURE__ */ jsxs(DropdownMenu, { onOpenChange: handleOpenChange, children: [
633
- /* @__PURE__ */ jsx(Tooltip, { tooltip: "Change block type", placement: "top", triggerAsChild: true, children: /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
634
- "button",
635
- {
636
- type: "button",
637
- className: cn(
638
- "tw:flex tw:h-7 tw:cursor-pointer tw:items-center tw:gap-1.5 tw:rounded tw:border-0 tw:bg-transparent tw:px-1.5 tw:text-xs tw:text-muted-foreground",
639
- "tw:hover:bg-accent"
640
- ),
641
- children: [
642
- /* @__PURE__ */ jsx(CurrentBlockIcon, { className: "tw:size-4" }),
643
- currentMeta?.label ?? "Normal",
644
- /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-3" })
645
- ]
646
- }
647
- ) }) }),
648
- /* @__PURE__ */ jsx(DropdownMenu.Content, { align: "start", className: "tw:min-w-40", children: BLOCK_TYPE_OPTIONS.map(({ value, label, icon: IconComp }) => /* @__PURE__ */ jsxs(
649
- DropdownMenu.Item,
650
- {
651
- onSelect: () => handleSelect(value),
652
- className: cn(
653
- "tw:flex tw:items-center tw:gap-2",
654
- blockType === value && "tw:bg-secondary-50 tw:text-secondary-500"
655
- ),
656
- children: [
657
- /* @__PURE__ */ jsx(IconComp, { className: "tw:size-4" }),
658
- label
659
- ]
660
- },
661
- value
662
- )) })
663
- ] });
611
+ const BLOCK_TYPE_OPTIONS = Object.entries(BLOCK_TYPE_META).filter(([, meta]) => meta.selectable).map(([value, { label, icon }]) => ({
612
+ value,
613
+ label,
614
+ icon
615
+ }));
616
+ const isBlockType = (value) => Object.prototype.hasOwnProperty.call(BLOCK_TYPE_META, value);
617
+ const BlockTypeDropdown = ({ editor, blockType }) => {
618
+ const savedSelectionRef = useRef(null);
619
+ const currentMeta = isBlockType(blockType) ? BLOCK_TYPE_META[blockType] : null;
620
+ const CurrentBlockIcon = currentMeta?.icon ?? Type;
621
+ const handleOpenChange = (open) => {
622
+ if (open) editor.getEditorState().read(() => {
623
+ const selection = $getSelection();
624
+ savedSelectionRef.current = selection ? selection.clone() : null;
625
+ });
626
+ };
627
+ const handleSelect = (type) => {
628
+ editor.update(() => {
629
+ if (savedSelectionRef.current) $setSelection(savedSelectionRef.current);
630
+ const selection = $getSelection();
631
+ if (!$isRangeSelection(selection)) return;
632
+ if (type === "bullet") {
633
+ editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0);
634
+ return;
635
+ }
636
+ if (type === "number") {
637
+ editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0);
638
+ return;
639
+ }
640
+ const anchorNode = selection.anchor.getNode();
641
+ const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
642
+ const newNode = type === "h1" || type === "h2" || type === "h3" ? $createHeadingNode(type) : type === "quote" ? $createQuoteNode() : type === "code" ? $createCodeNode() : $createParagraphNode();
643
+ element.getChildren().forEach((child) => newNode.append(child));
644
+ element.replace(newNode);
645
+ });
646
+ };
647
+ return /* @__PURE__ */ jsxs(DropdownMenu, {
648
+ onOpenChange: handleOpenChange,
649
+ children: [/* @__PURE__ */ jsx(Tooltip, {
650
+ tooltip: "Change block type",
651
+ placement: "top",
652
+ triggerAsChild: true,
653
+ children: /* @__PURE__ */ jsx(DropdownMenu.Trigger, {
654
+ asChild: true,
655
+ children: /* @__PURE__ */ jsxs("button", {
656
+ type: "button",
657
+ className: cn("tw:flex tw:h-7 tw:cursor-pointer tw:items-center tw:gap-1.5 tw:rounded tw:border-0 tw:bg-transparent tw:px-1.5 tw:text-xs tw:text-muted-foreground", "tw:hover:bg-accent"),
658
+ children: [
659
+ /* @__PURE__ */ jsx(CurrentBlockIcon, { className: "tw:size-4" }),
660
+ currentMeta?.label ?? "Normal",
661
+ /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-3" })
662
+ ]
663
+ })
664
+ })
665
+ }), /* @__PURE__ */ jsx(DropdownMenu.Content, {
666
+ align: "start",
667
+ className: "tw:min-w-40",
668
+ children: BLOCK_TYPE_OPTIONS.map(({ value, label, icon: IconComp }) => /* @__PURE__ */ jsxs(DropdownMenu.Item, {
669
+ onSelect: () => handleSelect(value),
670
+ className: cn("tw:flex tw:items-center tw:gap-2", blockType === value && "tw:bg-secondary-50 tw:text-secondary-500"),
671
+ children: [/* @__PURE__ */ jsx(IconComp, { className: "tw:size-4" }), label]
672
+ }, value))
673
+ })]
674
+ });
664
675
  };
665
- var ToolbarButton = ({
666
- active,
667
- disabled,
668
- onClick,
669
- tooltip,
670
- children,
671
- className: extraClassName
672
- }) => /* @__PURE__ */ jsx(Tooltip, { tooltip, placement: "top", triggerAsChild: true, children: /* @__PURE__ */ jsx(
673
- "button",
674
- {
675
- type: "button",
676
- disabled,
677
- onClick,
678
- className: cn(
679
- "tw:flex tw:h-7 tw:min-w-7 tw:cursor-pointer tw:items-center tw:justify-center tw:rounded tw:border-0 tw:bg-transparent tw:px-1 tw:text-xs tw:text-muted-foreground",
680
- "tw:hover:bg-accent tw:disabled:cursor-not-allowed tw:disabled:opacity-30",
681
- active && "tw:bg-secondary-100 tw:text-secondary-500",
682
- extraClassName
683
- ),
684
- children
685
- }
686
- ) });
687
- var insertLink = (editor, url, label, savedSelection) => {
688
- editor.update(() => {
689
- $setSelection(savedSelection);
690
- const selection = $getSelection();
691
- if ($isRangeSelection(selection)) {
692
- const selectedText = selection.getTextContent();
693
- if (selectedText && label === selectedText) {
694
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
695
- return;
696
- }
697
- }
698
- const linkNode = $createLinkNode(url);
699
- const textNode = $createTextNode(label || url);
700
- linkNode.append(textNode);
701
- if ($isRangeSelection(selection)) {
702
- selection.insertNodes([linkNode]);
703
- }
704
- });
676
+ //#endregion
677
+ //#region src/blocks/MarkdownEditor/plugins/toolbar/ToolbarButton.tsx
678
+ const ToolbarButton = ({ active, disabled, onClick, tooltip, children, className: extraClassName }) => /* @__PURE__ */ jsx(Tooltip, {
679
+ tooltip,
680
+ placement: "top",
681
+ triggerAsChild: true,
682
+ children: /* @__PURE__ */ jsx("button", {
683
+ type: "button",
684
+ disabled,
685
+ onClick,
686
+ className: cn("tw:flex tw:h-7 tw:min-w-7 tw:cursor-pointer tw:items-center tw:justify-center tw:rounded tw:border-0 tw:bg-transparent tw:px-1 tw:text-xs tw:text-muted-foreground", "tw:hover:bg-accent tw:disabled:cursor-not-allowed tw:disabled:opacity-30", active && "tw:bg-secondary-100 tw:text-secondary-500", extraClassName),
687
+ children
688
+ })
689
+ });
690
+ //#endregion
691
+ //#region src/blocks/MarkdownEditor/plugins/toolbar/LinkPanel.tsx
692
+ const insertLink = (editor, url, label, savedSelection) => {
693
+ editor.update(() => {
694
+ $setSelection(savedSelection);
695
+ const selection = $getSelection();
696
+ if ($isRangeSelection(selection)) {
697
+ const selectedText = selection.getTextContent();
698
+ if (selectedText && label === selectedText) {
699
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
700
+ return;
701
+ }
702
+ }
703
+ const linkNode = $createLinkNode(url);
704
+ const textNode = $createTextNode(label || url);
705
+ linkNode.append(textNode);
706
+ if ($isRangeSelection(selection)) selection.insertNodes([linkNode]);
707
+ });
705
708
  };
706
- var LinkPanel = ({ editor, isLink }) => {
707
- const [open, setOpen] = useState(false);
708
- const [url, setUrl] = useState("");
709
- const [label, setLabel] = useState("");
710
- const savedSelectionRef = useRef(null);
711
- const panelRef = useRef(null);
712
- const handleOpen = () => {
713
- editor.getEditorState().read(() => {
714
- const selection = $getSelection();
715
- savedSelectionRef.current = selection ? selection.clone() : null;
716
- if ($isRangeSelection(selection)) {
717
- const text = selection.getTextContent();
718
- if (text) setLabel(text);
719
- }
720
- });
721
- setOpen(true);
722
- };
723
- const handleClose = () => {
724
- setOpen(false);
725
- setUrl("");
726
- setLabel("");
727
- savedSelectionRef.current = null;
728
- editor.focus();
729
- };
730
- const handleSubmit = () => {
731
- const trimmedUrl = url.trim();
732
- if (!trimmedUrl) return;
733
- insertLink(editor, trimmedUrl, label.trim(), savedSelectionRef.current);
734
- handleClose();
735
- };
736
- const handleKeyDown = (e) => {
737
- if (e.key === "Enter") {
738
- e.preventDefault();
739
- handleSubmit();
740
- }
741
- if (e.key === "Escape") {
742
- e.preventDefault();
743
- handleClose();
744
- }
745
- };
746
- if (isLink) {
747
- return /* @__PURE__ */ jsx(
748
- ToolbarButton,
749
- {
750
- active: true,
751
- onClick: () => editor.dispatchCommand(TOGGLE_LINK_COMMAND, null),
752
- tooltip: "Remove Link",
753
- children: /* @__PURE__ */ jsx(Link, { className: "tw:size-4" })
754
- }
755
- );
756
- }
757
- return /* @__PURE__ */ jsxs("div", { className: "tw:relative", children: [
758
- /* @__PURE__ */ jsx(ToolbarButton, { active: open, onClick: handleOpen, tooltip: "Insert Link", children: /* @__PURE__ */ jsx(Link, { className: "tw:size-4" }) }),
759
- open && /* @__PURE__ */ jsx(
760
- "div",
761
- {
762
- ref: panelRef,
763
- className: "tw:absolute tw:left-0 tw:top-full tw:z-50 tw:mt-1 tw:w-80 tw:rounded-md tw:border tw:border-border tw:bg-popover tw:p-3 tw:text-popover-foreground tw:shadow-lg",
764
- children: /* @__PURE__ */ jsxs("div", { className: "tw:flex tw:flex-col tw:gap-2", children: [
765
- /* @__PURE__ */ jsxs(FormGroup, { children: [
766
- /* @__PURE__ */ jsx(Label, { htmlFor: "link-label", className: "tw:mb-1 tw:text-xs", children: "Label" }),
767
- /* @__PURE__ */ jsx(
768
- TextInput,
769
- {
770
- id: "link-label",
771
- autoFocus: true,
772
- placeholder: "Link text",
773
- value: label,
774
- onValueChange: setLabel,
775
- onKeyDown: handleKeyDown,
776
- variant: "secondary"
777
- }
778
- )
779
- ] }),
780
- /* @__PURE__ */ jsxs(FormGroup, { children: [
781
- /* @__PURE__ */ jsx(Label, { htmlFor: "link-url", className: "tw:mb-1 tw:text-xs", children: "URL" }),
782
- /* @__PURE__ */ jsx(
783
- TextInput,
784
- {
785
- id: "link-url",
786
- placeholder: "https://\u2026",
787
- value: url,
788
- onValueChange: setUrl,
789
- onKeyDown: handleKeyDown,
790
- variant: "secondary"
791
- }
792
- )
793
- ] }),
794
- /* @__PURE__ */ jsxs(ButtonGroup, { children: [
795
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "outline", onClick: handleClose, children: "Cancel" }),
796
- /* @__PURE__ */ jsx(Button, { size: "small", onClick: handleSubmit, disabled: !url.trim(), children: "Add link" })
797
- ] })
798
- ] })
799
- }
800
- )
801
- ] });
709
+ const LinkPanel = ({ editor, isLink }) => {
710
+ const [open, setOpen] = useState(false);
711
+ const [url, setUrl] = useState("");
712
+ const [label, setLabel] = useState("");
713
+ const savedSelectionRef = useRef(null);
714
+ const panelRef = useRef(null);
715
+ const handleOpen = () => {
716
+ editor.getEditorState().read(() => {
717
+ const selection = $getSelection();
718
+ savedSelectionRef.current = selection ? selection.clone() : null;
719
+ if ($isRangeSelection(selection)) {
720
+ const text = selection.getTextContent();
721
+ if (text) setLabel(text);
722
+ }
723
+ });
724
+ setOpen(true);
725
+ };
726
+ const handleClose = () => {
727
+ setOpen(false);
728
+ setUrl("");
729
+ setLabel("");
730
+ savedSelectionRef.current = null;
731
+ editor.focus();
732
+ };
733
+ const handleSubmit = () => {
734
+ const trimmedUrl = url.trim();
735
+ if (!trimmedUrl) return;
736
+ insertLink(editor, trimmedUrl, label.trim(), savedSelectionRef.current);
737
+ handleClose();
738
+ };
739
+ const handleKeyDown = (e) => {
740
+ if (e.key === "Enter") {
741
+ e.preventDefault();
742
+ handleSubmit();
743
+ }
744
+ if (e.key === "Escape") {
745
+ e.preventDefault();
746
+ handleClose();
747
+ }
748
+ };
749
+ if (isLink) return /* @__PURE__ */ jsx(ToolbarButton, {
750
+ active: true,
751
+ onClick: () => editor.dispatchCommand(TOGGLE_LINK_COMMAND, null),
752
+ tooltip: "Remove Link",
753
+ children: /* @__PURE__ */ jsx(Link, { className: "tw:size-4" })
754
+ });
755
+ return /* @__PURE__ */ jsxs("div", {
756
+ className: "tw:relative",
757
+ children: [/* @__PURE__ */ jsx(ToolbarButton, {
758
+ active: open,
759
+ onClick: handleOpen,
760
+ tooltip: "Insert Link",
761
+ children: /* @__PURE__ */ jsx(Link, { className: "tw:size-4" })
762
+ }), open && /* @__PURE__ */ jsx("div", {
763
+ ref: panelRef,
764
+ className: "tw:absolute tw:left-0 tw:top-full tw:z-50 tw:mt-1 tw:w-80 tw:rounded-md tw:border tw:border-border tw:bg-popover tw:p-3 tw:text-popover-foreground tw:shadow-lg",
765
+ children: /* @__PURE__ */ jsxs("div", {
766
+ className: "tw:flex tw:flex-col tw:gap-2",
767
+ children: [
768
+ /* @__PURE__ */ jsxs(FormGroup, { children: [/* @__PURE__ */ jsx(Label, {
769
+ htmlFor: "link-label",
770
+ className: "tw:mb-1 tw:text-xs",
771
+ children: "Label"
772
+ }), /* @__PURE__ */ jsx(TextInput, {
773
+ id: "link-label",
774
+ autoFocus: true,
775
+ placeholder: "Link text",
776
+ value: label,
777
+ onValueChange: setLabel,
778
+ onKeyDown: handleKeyDown,
779
+ variant: "secondary"
780
+ })] }),
781
+ /* @__PURE__ */ jsxs(FormGroup, { children: [/* @__PURE__ */ jsx(Label, {
782
+ htmlFor: "link-url",
783
+ className: "tw:mb-1 tw:text-xs",
784
+ children: "URL"
785
+ }), /* @__PURE__ */ jsx(TextInput, {
786
+ id: "link-url",
787
+ placeholder: "https://…",
788
+ value: url,
789
+ onValueChange: setUrl,
790
+ onKeyDown: handleKeyDown,
791
+ variant: "secondary"
792
+ })] }),
793
+ /* @__PURE__ */ jsxs(ButtonGroup, { children: [/* @__PURE__ */ jsx(Button, {
794
+ size: "small",
795
+ variant: "outline",
796
+ onClick: handleClose,
797
+ children: "Cancel"
798
+ }), /* @__PURE__ */ jsx(Button, {
799
+ size: "small",
800
+ onClick: handleSubmit,
801
+ disabled: !url.trim(),
802
+ children: "Add link"
803
+ })] })
804
+ ]
805
+ })
806
+ })]
807
+ });
802
808
  };
803
- var ToolbarDivider = () => /* @__PURE__ */ jsx("div", { className: "tw:mx-0.5 tw:h-5 tw:w-px tw:bg-border" });
804
- var ELEMENT_FORMAT_TYPES = ["left", "center", "right", "justify", "start", "end", ""];
805
- var isElementFormatType = (value) => ELEMENT_FORMAT_TYPES.includes(value);
806
- var toElementFormatType = (value) => isElementFormatType(value) ? value : "left";
807
- var ToolbarPlugin = () => {
808
- const [editor] = useLexicalComposerContext();
809
- const [isBold, setIsBold] = useState(false);
810
- const [isItalic, setIsItalic] = useState(false);
811
- const [isUnderline, setIsUnderline] = useState(false);
812
- const [isStrikethrough, setIsStrikethrough] = useState(false);
813
- const [isCode, setIsCode] = useState(false);
814
- const [isLink, setIsLink] = useState(false);
815
- const [blockType, setBlockType] = useState("paragraph");
816
- const [alignment, setAlignment] = useState("left");
817
- const [isVisible, setIsVisible] = useState(false);
818
- const [position, setPosition] = useState(null);
819
- const toolbarRef = useRef(null);
820
- const isMouseDownRef = useRef(false);
821
- const hasSelectionRef = useRef(false);
822
- const updatePosition = useCallback(() => {
823
- const nativeSelection = window.getSelection();
824
- if (!nativeSelection || nativeSelection.rangeCount === 0) return;
825
- const range = nativeSelection.getRangeAt(0);
826
- const rect = range.getBoundingClientRect();
827
- if (rect.width > 0) {
828
- const toolbarWidth = toolbarRef.current?.offsetWidth ?? 300;
829
- setPosition({
830
- top: rect.top + window.scrollY - 8,
831
- left: Math.max(8, rect.left + window.scrollX + rect.width / 2 - toolbarWidth / 2)
832
- });
833
- }
834
- }, []);
835
- useEffect(() => {
836
- const handleMouseDown = () => {
837
- isMouseDownRef.current = true;
838
- };
839
- const handleMouseUp = () => {
840
- isMouseDownRef.current = false;
841
- if (hasSelectionRef.current) {
842
- setIsVisible(true);
843
- }
844
- };
845
- document.addEventListener("mousedown", handleMouseDown);
846
- document.addEventListener("mouseup", handleMouseUp);
847
- return () => {
848
- document.removeEventListener("mousedown", handleMouseDown);
849
- document.removeEventListener("mouseup", handleMouseUp);
850
- };
851
- }, []);
852
- const updateToolbar = useCallback(() => {
853
- const selection = $getSelection();
854
- if (!$isRangeSelection(selection)) {
855
- hasSelectionRef.current = false;
856
- setIsVisible(false);
857
- return;
858
- }
859
- const hasSelection = !selection.isCollapsed() && selection.getTextContent().length > 0;
860
- hasSelectionRef.current = hasSelection;
861
- if (!hasSelection) {
862
- setIsVisible(false);
863
- } else if (!isMouseDownRef.current) {
864
- setIsVisible(true);
865
- }
866
- if (hasSelection) {
867
- updatePosition();
868
- }
869
- setIsBold(selection.hasFormat("bold"));
870
- setIsItalic(selection.hasFormat("italic"));
871
- setIsUnderline(selection.hasFormat("underline"));
872
- setIsStrikethrough(selection.hasFormat("strikethrough"));
873
- setIsCode(selection.hasFormat("code"));
874
- const node = selection.anchor.getNode();
875
- const parent = node.getParent();
876
- setIsLink($isLinkNode(parent) || $isLinkNode(node));
877
- const blockElement = $getNearestBlockElementAncestorOrThrow(node);
878
- if ($isListNode(blockElement)) {
879
- const listType = blockElement.getListType();
880
- setBlockType(listType === "number" ? "number" : "bullet");
881
- } else if ($isHeadingNode(blockElement)) {
882
- setBlockType(blockElement.getTag());
883
- } else if ($isCodeNode(blockElement)) {
884
- setBlockType("code");
885
- } else if ($isQuoteNode(blockElement)) {
886
- setBlockType("quote");
887
- } else {
888
- setBlockType("paragraph");
889
- }
890
- setAlignment(toElementFormatType(blockElement.getFormatType()));
891
- }, [updatePosition]);
892
- useEffect(() => {
893
- const unregisterUpdate = editor.registerUpdateListener(({ editorState }) => {
894
- editorState.read(() => {
895
- updateToolbar();
896
- });
897
- });
898
- return () => {
899
- unregisterUpdate();
900
- };
901
- }, [editor, updateToolbar]);
902
- if (!isVisible || !position) return null;
903
- return createPortal(
904
- /* @__PURE__ */ jsxs(
905
- "div",
906
- {
907
- ref: toolbarRef,
908
- role: "toolbar",
909
- "aria-label": "Formatting options",
910
- onMouseDown: (e) => e.preventDefault(),
911
- style: { top: position.top, left: position.left },
912
- className: "tw:absolute tw:z-[1060] tw:flex tw:-translate-y-full tw:items-center tw:gap-0.5 tw:rounded-lg tw:border tw:border-border tw:bg-popover tw:px-1.5 tw:py-1 tw:text-popover-foreground tw:shadow-lg",
913
- children: [
914
- /* @__PURE__ */ jsx(BlockTypeDropdown, { editor, blockType }),
915
- /* @__PURE__ */ jsx(ToolbarDivider, {}),
916
- /* @__PURE__ */ jsx(
917
- ToolbarButton,
918
- {
919
- active: isBold,
920
- onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold"),
921
- tooltip: "Bold (Ctrl+B)",
922
- children: /* @__PURE__ */ jsx(Bold, { className: "tw:size-4" })
923
- }
924
- ),
925
- /* @__PURE__ */ jsx(
926
- ToolbarButton,
927
- {
928
- active: isItalic,
929
- onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic"),
930
- tooltip: "Italic (Ctrl+I)",
931
- children: /* @__PURE__ */ jsx(Italic, { className: "tw:size-4" })
932
- }
933
- ),
934
- /* @__PURE__ */ jsx(
935
- ToolbarButton,
936
- {
937
- active: isUnderline,
938
- onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "underline"),
939
- tooltip: "Underline (Ctrl+U)",
940
- children: /* @__PURE__ */ jsx(Underline, { className: "tw:size-4" })
941
- }
942
- ),
943
- /* @__PURE__ */ jsx(
944
- ToolbarButton,
945
- {
946
- active: isStrikethrough,
947
- onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "strikethrough"),
948
- tooltip: "Strikethrough",
949
- children: /* @__PURE__ */ jsx(Strikethrough, { className: "tw:size-4" })
950
- }
951
- ),
952
- /* @__PURE__ */ jsx(
953
- ToolbarButton,
954
- {
955
- active: isCode,
956
- onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "code"),
957
- tooltip: "Inline Code",
958
- children: /* @__PURE__ */ jsx(Code, { className: "tw:size-4" })
959
- }
960
- ),
961
- /* @__PURE__ */ jsx(ToolbarDivider, {}),
962
- /* @__PURE__ */ jsx(LinkPanel, { editor, isLink }),
963
- /* @__PURE__ */ jsx(ToolbarDivider, {}),
964
- /* @__PURE__ */ jsx(
965
- ToolbarButton,
966
- {
967
- active: blockType === "bullet",
968
- onClick: () => {
969
- if (blockType === "bullet") {
970
- editor.dispatchCommand(REMOVE_LIST_COMMAND, void 0);
971
- } else {
972
- editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0);
973
- }
974
- },
975
- tooltip: "Bullet List",
976
- children: /* @__PURE__ */ jsx(List, { className: "tw:size-4" })
977
- }
978
- ),
979
- /* @__PURE__ */ jsx(
980
- ToolbarButton,
981
- {
982
- active: blockType === "number",
983
- onClick: () => {
984
- if (blockType === "number") {
985
- editor.dispatchCommand(REMOVE_LIST_COMMAND, void 0);
986
- } else {
987
- editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0);
988
- }
989
- },
990
- tooltip: "Numbered List",
991
- children: /* @__PURE__ */ jsx(ListOrdered, { className: "tw:size-4" })
992
- }
993
- ),
994
- /* @__PURE__ */ jsx(ToolbarDivider, {}),
995
- /* @__PURE__ */ jsx(AlignmentDropdown, { editor, alignment }),
996
- /* @__PURE__ */ jsx(ToolbarDivider, {}),
997
- /* @__PURE__ */ jsx(
998
- ToolbarButton,
999
- {
1000
- active: blockType === "quote",
1001
- onClick: () => {
1002
- editor.update(() => {
1003
- const selection = $getSelection();
1004
- if (!$isRangeSelection(selection)) return;
1005
- const anchorNode = selection.anchor.getNode();
1006
- const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
1007
- const newNode = blockType === "quote" ? $createParagraphNode() : $createQuoteNode();
1008
- element.getChildren().forEach((child) => newNode.append(child));
1009
- element.replace(newNode);
1010
- });
1011
- },
1012
- tooltip: "Quote",
1013
- children: /* @__PURE__ */ jsx(Quote, { className: "tw:size-4" })
1014
- }
1015
- ),
1016
- /* @__PURE__ */ jsx(
1017
- ToolbarButton,
1018
- {
1019
- active: blockType === "code",
1020
- onClick: () => {
1021
- editor.update(() => {
1022
- const selection = $getSelection();
1023
- if (!$isRangeSelection(selection)) return;
1024
- const anchorNode = selection.anchor.getNode();
1025
- const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
1026
- const newNode = blockType === "code" ? $createParagraphNode() : $createCodeNode();
1027
- element.getChildren().forEach((child) => newNode.append(child));
1028
- element.replace(newNode);
1029
- });
1030
- },
1031
- tooltip: "Code Block",
1032
- children: /* @__PURE__ */ jsx(SquareCode, { className: "tw:size-4" })
1033
- }
1034
- )
1035
- ]
1036
- }
1037
- ),
1038
- document.body
1039
- );
809
+ //#endregion
810
+ //#region src/blocks/MarkdownEditor/plugins/toolbar/ToolbarDivider.tsx
811
+ const ToolbarDivider = () => /* @__PURE__ */ jsx("div", { className: "tw:mx-0.5 tw:h-5 tw:w-px tw:bg-border" });
812
+ //#endregion
813
+ //#region src/blocks/MarkdownEditor/plugins/ToolbarPlugin.tsx
814
+ const ELEMENT_FORMAT_TYPES = [
815
+ "left",
816
+ "center",
817
+ "right",
818
+ "justify",
819
+ "start",
820
+ "end",
821
+ ""
822
+ ];
823
+ const isElementFormatType = (value) => ELEMENT_FORMAT_TYPES.includes(value);
824
+ const toElementFormatType = (value) => isElementFormatType(value) ? value : "left";
825
+ const ToolbarPlugin = () => {
826
+ const [editor] = useLexicalComposerContext();
827
+ const [isBold, setIsBold] = useState(false);
828
+ const [isItalic, setIsItalic] = useState(false);
829
+ const [isUnderline, setIsUnderline] = useState(false);
830
+ const [isStrikethrough, setIsStrikethrough] = useState(false);
831
+ const [isCode, setIsCode] = useState(false);
832
+ const [isLink, setIsLink] = useState(false);
833
+ const [blockType, setBlockType] = useState("paragraph");
834
+ const [alignment, setAlignment] = useState("left");
835
+ const [isVisible, setIsVisible] = useState(false);
836
+ const [position, setPosition] = useState(null);
837
+ const toolbarRef = useRef(null);
838
+ const isMouseDownRef = useRef(false);
839
+ const hasSelectionRef = useRef(false);
840
+ const updatePosition = useCallback(() => {
841
+ const nativeSelection = window.getSelection();
842
+ if (!nativeSelection || nativeSelection.rangeCount === 0) return;
843
+ const rect = nativeSelection.getRangeAt(0).getBoundingClientRect();
844
+ if (rect.width > 0) {
845
+ const toolbarWidth = toolbarRef.current?.offsetWidth ?? 300;
846
+ setPosition({
847
+ top: rect.top + window.scrollY - 8,
848
+ left: Math.max(8, rect.left + window.scrollX + rect.width / 2 - toolbarWidth / 2)
849
+ });
850
+ }
851
+ }, []);
852
+ useEffect(() => {
853
+ const handleMouseDown = () => {
854
+ isMouseDownRef.current = true;
855
+ };
856
+ const handleMouseUp = () => {
857
+ isMouseDownRef.current = false;
858
+ if (hasSelectionRef.current) setIsVisible(true);
859
+ };
860
+ document.addEventListener("mousedown", handleMouseDown);
861
+ document.addEventListener("mouseup", handleMouseUp);
862
+ return () => {
863
+ document.removeEventListener("mousedown", handleMouseDown);
864
+ document.removeEventListener("mouseup", handleMouseUp);
865
+ };
866
+ }, []);
867
+ const updateToolbar = useCallback(() => {
868
+ const selection = $getSelection();
869
+ if (!$isRangeSelection(selection)) {
870
+ hasSelectionRef.current = false;
871
+ setIsVisible(false);
872
+ return;
873
+ }
874
+ const hasSelection = !selection.isCollapsed() && selection.getTextContent().length > 0;
875
+ hasSelectionRef.current = hasSelection;
876
+ if (!hasSelection) setIsVisible(false);
877
+ else if (!isMouseDownRef.current) setIsVisible(true);
878
+ if (hasSelection) updatePosition();
879
+ setIsBold(selection.hasFormat("bold"));
880
+ setIsItalic(selection.hasFormat("italic"));
881
+ setIsUnderline(selection.hasFormat("underline"));
882
+ setIsStrikethrough(selection.hasFormat("strikethrough"));
883
+ setIsCode(selection.hasFormat("code"));
884
+ const node = selection.anchor.getNode();
885
+ const parent = node.getParent();
886
+ setIsLink($isLinkNode(parent) || $isLinkNode(node));
887
+ const blockElement = $getNearestBlockElementAncestorOrThrow(node);
888
+ if ($isListNode(blockElement)) {
889
+ const listType = blockElement.getListType();
890
+ setBlockType(listType === "number" ? "number" : "bullet");
891
+ } else if ($isHeadingNode(blockElement)) setBlockType(blockElement.getTag());
892
+ else if ($isCodeNode(blockElement)) setBlockType("code");
893
+ else if ($isQuoteNode(blockElement)) setBlockType("quote");
894
+ else setBlockType("paragraph");
895
+ setAlignment(toElementFormatType(blockElement.getFormatType()));
896
+ }, [updatePosition]);
897
+ useEffect(() => {
898
+ const unregisterUpdate = editor.registerUpdateListener(({ editorState }) => {
899
+ editorState.read(() => {
900
+ updateToolbar();
901
+ });
902
+ });
903
+ return () => {
904
+ unregisterUpdate();
905
+ };
906
+ }, [editor, updateToolbar]);
907
+ if (!isVisible || !position) return null;
908
+ return createPortal(/* @__PURE__ */ jsxs("div", {
909
+ ref: toolbarRef,
910
+ role: "toolbar",
911
+ "aria-label": "Formatting options",
912
+ onMouseDown: (e) => e.preventDefault(),
913
+ style: {
914
+ top: position.top,
915
+ left: position.left
916
+ },
917
+ className: "tw:absolute tw:z-[1060] tw:flex tw:-translate-y-full tw:items-center tw:gap-0.5 tw:rounded-lg tw:border tw:border-border tw:bg-popover tw:px-1.5 tw:py-1 tw:text-popover-foreground tw:shadow-lg",
918
+ children: [
919
+ /* @__PURE__ */ jsx(BlockTypeDropdown, {
920
+ editor,
921
+ blockType
922
+ }),
923
+ /* @__PURE__ */ jsx(ToolbarDivider, {}),
924
+ /* @__PURE__ */ jsx(ToolbarButton, {
925
+ active: isBold,
926
+ onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold"),
927
+ tooltip: "Bold (Ctrl+B)",
928
+ children: /* @__PURE__ */ jsx(Bold, { className: "tw:size-4" })
929
+ }),
930
+ /* @__PURE__ */ jsx(ToolbarButton, {
931
+ active: isItalic,
932
+ onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic"),
933
+ tooltip: "Italic (Ctrl+I)",
934
+ children: /* @__PURE__ */ jsx(Italic, { className: "tw:size-4" })
935
+ }),
936
+ /* @__PURE__ */ jsx(ToolbarButton, {
937
+ active: isUnderline,
938
+ onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "underline"),
939
+ tooltip: "Underline (Ctrl+U)",
940
+ children: /* @__PURE__ */ jsx(Underline, { className: "tw:size-4" })
941
+ }),
942
+ /* @__PURE__ */ jsx(ToolbarButton, {
943
+ active: isStrikethrough,
944
+ onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "strikethrough"),
945
+ tooltip: "Strikethrough",
946
+ children: /* @__PURE__ */ jsx(Strikethrough, { className: "tw:size-4" })
947
+ }),
948
+ /* @__PURE__ */ jsx(ToolbarButton, {
949
+ active: isCode,
950
+ onClick: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "code"),
951
+ tooltip: "Inline Code",
952
+ children: /* @__PURE__ */ jsx(Code, { className: "tw:size-4" })
953
+ }),
954
+ /* @__PURE__ */ jsx(ToolbarDivider, {}),
955
+ /* @__PURE__ */ jsx(LinkPanel, {
956
+ editor,
957
+ isLink
958
+ }),
959
+ /* @__PURE__ */ jsx(ToolbarDivider, {}),
960
+ /* @__PURE__ */ jsx(ToolbarButton, {
961
+ active: blockType === "bullet",
962
+ onClick: () => {
963
+ if (blockType === "bullet") editor.dispatchCommand(REMOVE_LIST_COMMAND, void 0);
964
+ else editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0);
965
+ },
966
+ tooltip: "Bullet List",
967
+ children: /* @__PURE__ */ jsx(List, { className: "tw:size-4" })
968
+ }),
969
+ /* @__PURE__ */ jsx(ToolbarButton, {
970
+ active: blockType === "number",
971
+ onClick: () => {
972
+ if (blockType === "number") editor.dispatchCommand(REMOVE_LIST_COMMAND, void 0);
973
+ else editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0);
974
+ },
975
+ tooltip: "Numbered List",
976
+ children: /* @__PURE__ */ jsx(ListOrdered, { className: "tw:size-4" })
977
+ }),
978
+ /* @__PURE__ */ jsx(ToolbarDivider, {}),
979
+ /* @__PURE__ */ jsx(AlignmentDropdown, {
980
+ editor,
981
+ alignment
982
+ }),
983
+ /* @__PURE__ */ jsx(ToolbarDivider, {}),
984
+ /* @__PURE__ */ jsx(ToolbarButton, {
985
+ active: blockType === "quote",
986
+ onClick: () => {
987
+ editor.update(() => {
988
+ const selection = $getSelection();
989
+ if (!$isRangeSelection(selection)) return;
990
+ const anchorNode = selection.anchor.getNode();
991
+ const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
992
+ const newNode = blockType === "quote" ? $createParagraphNode() : $createQuoteNode();
993
+ element.getChildren().forEach((child) => newNode.append(child));
994
+ element.replace(newNode);
995
+ });
996
+ },
997
+ tooltip: "Quote",
998
+ children: /* @__PURE__ */ jsx(Quote, { className: "tw:size-4" })
999
+ }),
1000
+ /* @__PURE__ */ jsx(ToolbarButton, {
1001
+ active: blockType === "code",
1002
+ onClick: () => {
1003
+ editor.update(() => {
1004
+ const selection = $getSelection();
1005
+ if (!$isRangeSelection(selection)) return;
1006
+ const anchorNode = selection.anchor.getNode();
1007
+ const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
1008
+ const newNode = blockType === "code" ? $createParagraphNode() : $createCodeNode();
1009
+ element.getChildren().forEach((child) => newNode.append(child));
1010
+ element.replace(newNode);
1011
+ });
1012
+ },
1013
+ tooltip: "Code Block",
1014
+ children: /* @__PURE__ */ jsx(SquareCode, { className: "tw:size-4" })
1015
+ })
1016
+ ]
1017
+ }), document.body);
1040
1018
  };
1041
-
1042
- // src/blocks/MarkdownEditor/theme.ts
1043
- var editorTheme = {
1044
- link: "tw:cursor-pointer tw:!text-primary tw:underline",
1045
- text: {
1046
- bold: "tw:font-bold",
1047
- italic: "tw:italic",
1048
- strikethrough: "tw:line-through",
1049
- underline: "tw:underline",
1050
- code: "tw:rounded tw:bg-muted tw:px-1 tw:py-0.5 tw:font-mono tw:text-[0.85em]"
1051
- },
1052
- list: {
1053
- ol: "tw:!list-decimal",
1054
- ul: "tw:!list-disc",
1055
- checklist: "tw:!list-none tw:!pl-0",
1056
- listitemChecked: [
1057
- "tw:relative tw:!pl-6 tw:list-none tw:outline-hidden tw:line-through tw:text-muted-foreground",
1058
- 'tw:before:content-[""] tw:before:absolute tw:before:left-0 tw:before:top-[3px]',
1059
- "tw:before:size-4 tw:before:rounded tw:before:border tw:before:border-solid",
1060
- "tw:before:bg-primary tw:before:border-primary tw:before:cursor-pointer",
1061
- 'tw:after:content-[""] tw:after:absolute tw:after:left-[5.5px] tw:after:top-[5.5px]',
1062
- "tw:after:w-1 tw:after:h-2 tw:after:border-primary-foreground tw:after:border-solid",
1063
- "tw:after:border-r-[1.5px] tw:after:border-b-[1.5px] tw:after:border-t-0 tw:after:border-l-0",
1064
- "tw:after:rotate-45"
1065
- ].join(" "),
1066
- listitemUnchecked: [
1067
- "tw:relative tw:!pl-6 tw:list-none tw:outline-hidden",
1068
- 'tw:before:content-[""] tw:before:absolute tw:before:left-0 tw:before:top-[3px]',
1069
- "tw:before:size-4 tw:before:rounded tw:before:border tw:before:border-solid",
1070
- "tw:before:border-input tw:before:bg-background tw:before:cursor-pointer"
1071
- ].join(" ")
1072
- }
1019
+ //#endregion
1020
+ //#region src/blocks/MarkdownEditor/theme.ts
1021
+ /**
1022
+ * Lexical editor theme — maps node types to CSS classes.
1023
+ * The editor container uses `tw:prose` so semantic elements (headings, lists, blockquotes, etc.)
1024
+ * are styled by Tailwind Typography. We only need explicit classes for text formatting
1025
+ * (bold, italic, etc.) because Lexical renders those as `<span>` elements.
1026
+ */
1027
+ const editorTheme = {
1028
+ link: "tw:cursor-pointer tw:!text-primary tw:underline",
1029
+ text: {
1030
+ bold: "tw:font-bold",
1031
+ italic: "tw:italic",
1032
+ strikethrough: "tw:line-through",
1033
+ underline: "tw:underline",
1034
+ code: "tw:rounded tw:bg-muted tw:px-1 tw:py-0.5 tw:font-mono tw:text-[0.85em]"
1035
+ },
1036
+ list: {
1037
+ ol: "tw:!list-decimal",
1038
+ ul: "tw:!list-disc",
1039
+ checklist: "tw:!list-none tw:!pl-0",
1040
+ listitemChecked: [
1041
+ "tw:relative tw:!pl-6 tw:list-none tw:outline-hidden tw:line-through tw:text-muted-foreground",
1042
+ "tw:before:content-[\"\"] tw:before:absolute tw:before:left-0 tw:before:top-[3px]",
1043
+ "tw:before:size-4 tw:before:rounded tw:before:border tw:before:border-solid",
1044
+ "tw:before:bg-primary tw:before:border-primary tw:before:cursor-pointer",
1045
+ "tw:after:content-[\"\"] tw:after:absolute tw:after:left-[5.5px] tw:after:top-[5.5px]",
1046
+ "tw:after:w-1 tw:after:h-2 tw:after:border-primary-foreground tw:after:border-solid",
1047
+ "tw:after:border-r-[1.5px] tw:after:border-b-[1.5px] tw:after:border-t-0 tw:after:border-l-0",
1048
+ "tw:after:rotate-45"
1049
+ ].join(" "),
1050
+ listitemUnchecked: [
1051
+ "tw:relative tw:!pl-6 tw:list-none tw:outline-hidden",
1052
+ "tw:before:content-[\"\"] tw:before:absolute tw:before:left-0 tw:before:top-[3px]",
1053
+ "tw:before:size-4 tw:before:rounded tw:before:border tw:before:border-solid",
1054
+ "tw:before:border-input tw:before:bg-background tw:before:cursor-pointer"
1055
+ ].join(" ")
1056
+ }
1073
1057
  };
1074
- var EDITOR_NODES = [
1075
- ImageNode,
1076
- MermaidNode,
1077
- HeadingNode,
1078
- QuoteNode,
1079
- ListNode,
1080
- ListItemNode,
1081
- CodeNode,
1082
- LinkNode,
1083
- HorizontalRuleNode
1058
+ //#endregion
1059
+ //#region src/blocks/MarkdownEditor/MarkdownEditor.tsx
1060
+ const EDITOR_NODES = [
1061
+ ImageNode,
1062
+ MermaidNode,
1063
+ HeadingNode,
1064
+ QuoteNode,
1065
+ ListNode,
1066
+ ListItemNode,
1067
+ CodeNode,
1068
+ LinkNode,
1069
+ HorizontalRuleNode
1084
1070
  ];
1085
- var MarkdownEditor = ({
1086
- value,
1087
- onChange,
1088
- readOnly = false,
1089
- placeholder,
1090
- autoFocus = false,
1091
- disabled = false,
1092
- onSave,
1093
- onCancel,
1094
- onImageUpload,
1095
- className,
1096
- imageRenderer,
1097
- showToolbar = false,
1098
- compact = false,
1099
- saveOnBlur = false,
1100
- singleLine = false,
1101
- clampDisplay = false,
1102
- minRows
1103
- }) => {
1104
- const editable = !readOnly && !disabled;
1105
- const transformers = singleLine ? SINGLE_LINE_TRANSFORMERS : CUSTOM_TRANSFORMERS;
1106
- const useDefaultMinHeight = !compact && minRows === void 0;
1107
- const customMinHeight = !compact && minRows !== void 0 ? { minHeight: `${minRows * 1.75}em` } : void 0;
1108
- const [initialConfig] = useState(() => ({
1109
- namespace: "MarkdownEditor",
1110
- theme: editorTheme,
1111
- nodes: EDITOR_NODES,
1112
- onError: (error) => {
1113
- console.error("[MarkdownEditor] uncaught editor error", error);
1114
- },
1115
- editable,
1116
- editorState: () => {
1117
- $convertFromMarkdownString(preprocessMarkdown(value), transformers);
1118
- }
1119
- }));
1120
- const skipBlurSaveRef = useRef(false);
1121
- const handleBlur = saveOnBlur && onSave ? (event) => {
1122
- if (skipBlurSaveRef.current) {
1123
- skipBlurSaveRef.current = false;
1124
- return;
1125
- }
1126
- if (!event.currentTarget.contains(event.relatedTarget)) {
1127
- onSave();
1128
- }
1129
- } : void 0;
1130
- return /* @__PURE__ */ jsx(LexicalComposer, { initialConfig, children: /* @__PURE__ */ jsx(ImageRendererProvider, { value: imageRenderer ?? null, children: /* @__PURE__ */ jsxs(
1131
- "div",
1132
- {
1133
- className: cn(
1134
- !compact && "tw:prose",
1135
- // Compact mode drops `tw:prose`, so Lexical's <p> wrappers fall back to the browser
1136
- // default `margin: 1em 0` — visually huge at heading sizes. Zero them, and use the
1137
- // adjacent-sibling selector for paragraph spacing so single-paragraph blocks (titles,
1138
- // short descriptions) stay tight while multi-paragraph blocks still breathe.
1139
- compact && "tw:[&_p]:m-0 tw:[&_p+p]:mt-2",
1140
- // Display-state clamp: 2 lines, dropped in focus-within so editing expands to full
1141
- // height. line-clamp is self-gating (no effect below 3 lines), so short/empty content
1142
- // renders clean; a CSS fade can't gate on overflow without JS, so it's omitted.
1143
- clampDisplay && "tw:[&:not(:focus-within)]:line-clamp-2",
1144
- className
1145
- ),
1146
- onBlur: handleBlur,
1147
- children: [
1148
- showToolbar && editable && /* @__PURE__ */ jsx(ToolbarPlugin, {}),
1149
- /* @__PURE__ */ jsx(
1150
- "div",
1151
- {
1152
- className: cn(
1153
- "tw:relative tw:rounded-md tw:transition-colors",
1154
- // In compact mode the padding lets the hover bg / focus ring extend past the text,
1155
- // but a matching negative margin shifts the wrapper outward so the text itself stays
1156
- // aligned with the parent's content edge (i.e. with where static text would render).
1157
- compact ? "tw:px-1.5 tw:py-0.5 tw:-mx-1.5 tw:-my-0.5" : "tw:p-2",
1158
- editable && "tw:cursor-text tw:hover:bg-muted tw:focus-within:ring-2 tw:focus-within:ring-ring/40"
1159
- ),
1160
- children: /* @__PURE__ */ jsx(
1161
- RichTextPlugin,
1162
- {
1163
- contentEditable: /* @__PURE__ */ jsx(
1164
- ContentEditable,
1165
- {
1166
- className: cn("tw:outline-hidden", useDefaultMinHeight && "tw:min-h-[100px]"),
1167
- style: customMinHeight,
1168
- "aria-placeholder": placeholder ?? "",
1169
- placeholder: placeholder ? /* @__PURE__ */ jsx(
1170
- "div",
1171
- {
1172
- className: cn(
1173
- "tw:pointer-events-none tw:absolute tw:select-none tw:text-muted-foreground",
1174
- compact ? "tw:left-1.5 tw:top-0.5" : "tw:left-2 tw:top-[calc(0.5rem+0.35em)]"
1175
- ),
1176
- children: placeholder
1177
- }
1178
- ) : /* @__PURE__ */ jsx("div", {})
1179
- }
1180
- ),
1181
- ErrorBoundary: LexicalErrorBoundary
1182
- }
1183
- )
1184
- }
1185
- ),
1186
- /* @__PURE__ */ jsx(HistoryPlugin, {}),
1187
- /* @__PURE__ */ jsx(LinkPlugin, {}),
1188
- /* @__PURE__ */ jsx(ClickableLinkPlugin, { newTab: true }),
1189
- !singleLine && /* @__PURE__ */ jsx(ListPlugin, {}),
1190
- !singleLine && /* @__PURE__ */ jsx(CheckListPlugin, {}),
1191
- !singleLine && /* @__PURE__ */ jsx(HorizontalRulePlugin, {}),
1192
- /* @__PURE__ */ jsx(TabIndentationPlugin, {}),
1193
- /* @__PURE__ */ jsx(MarkdownShortcutPlugin, { transformers }),
1194
- /* @__PURE__ */ jsx(
1195
- MarkdownSyncPlugin,
1196
- {
1197
- value,
1198
- editable,
1199
- onChange,
1200
- transformers,
1201
- singleLine
1202
- }
1203
- ),
1204
- /* @__PURE__ */ jsx(ImagePastePlugin, { onImageUpload }),
1205
- /* @__PURE__ */ jsx(
1206
- KeyboardPlugin,
1207
- {
1208
- onSave,
1209
- onCancel,
1210
- singleLine,
1211
- blurOnEscape: singleLine || saveOnBlur,
1212
- skipBlurSaveRef
1213
- }
1214
- ),
1215
- autoFocus && /* @__PURE__ */ jsx(AutoFocusPlugin, {})
1216
- ]
1217
- }
1218
- ) }) });
1071
+ const MarkdownEditor = ({ value, onChange, readOnly = false, placeholder, autoFocus = false, disabled = false, onSave, onCancel, onImageUpload, className, imageRenderer, showToolbar = false, compact = false, saveOnBlur = false, singleLine = false, clampDisplay = false, minRows }) => {
1072
+ const editable = !readOnly && !disabled;
1073
+ const transformers = singleLine ? SINGLE_LINE_TRANSFORMERS : CUSTOM_TRANSFORMERS;
1074
+ const useDefaultMinHeight = !compact && minRows === void 0;
1075
+ const customMinHeight = !compact && minRows !== void 0 ? { minHeight: `${minRows * 1.75}em` } : void 0;
1076
+ const [initialConfig] = useState(() => ({
1077
+ namespace: "MarkdownEditor",
1078
+ theme: editorTheme,
1079
+ nodes: EDITOR_NODES,
1080
+ onError: (error) => {
1081
+ console.error("[MarkdownEditor] uncaught editor error", error);
1082
+ },
1083
+ editable,
1084
+ editorState: () => {
1085
+ $convertFromMarkdownString(preprocessMarkdown(value), transformers);
1086
+ }
1087
+ }));
1088
+ const skipBlurSaveRef = useRef(false);
1089
+ const handleBlur = saveOnBlur && onSave ? (event) => {
1090
+ if (skipBlurSaveRef.current) {
1091
+ skipBlurSaveRef.current = false;
1092
+ return;
1093
+ }
1094
+ if (!event.currentTarget.contains(event.relatedTarget)) onSave();
1095
+ } : void 0;
1096
+ return /* @__PURE__ */ jsx(LexicalComposer, {
1097
+ initialConfig,
1098
+ children: /* @__PURE__ */ jsx(ImageRendererProvider, {
1099
+ value: imageRenderer ?? null,
1100
+ children: /* @__PURE__ */ jsxs("div", {
1101
+ className: cn(!compact && "tw:prose", compact && "tw:[&_p]:m-0 tw:[&_p+p]:mt-2", clampDisplay && "tw:[&:not(:focus-within)]:line-clamp-2", className),
1102
+ onBlur: handleBlur,
1103
+ children: [
1104
+ showToolbar && editable && /* @__PURE__ */ jsx(ToolbarPlugin, {}),
1105
+ /* @__PURE__ */ jsx("div", {
1106
+ className: cn("tw:relative tw:rounded-md tw:transition-colors", compact ? "tw:px-1.5 tw:py-0.5 tw:-mx-1.5 tw:-my-0.5" : "tw:p-2", editable && "tw:cursor-text tw:hover:bg-muted tw:focus-within:ring-2 tw:focus-within:ring-ring/40"),
1107
+ children: /* @__PURE__ */ jsx(RichTextPlugin, {
1108
+ contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
1109
+ className: cn("tw:outline-hidden", useDefaultMinHeight && "tw:min-h-[100px]"),
1110
+ style: customMinHeight,
1111
+ "aria-placeholder": placeholder ?? "",
1112
+ placeholder: placeholder ? /* @__PURE__ */ jsx("div", {
1113
+ className: cn("tw:pointer-events-none tw:absolute tw:select-none tw:text-muted-foreground", compact ? "tw:left-1.5 tw:top-0.5" : "tw:left-2 tw:top-[calc(0.5rem+0.35em)]"),
1114
+ children: placeholder
1115
+ }) : /* @__PURE__ */ jsx("div", {})
1116
+ }),
1117
+ ErrorBoundary: LexicalErrorBoundary
1118
+ })
1119
+ }),
1120
+ /* @__PURE__ */ jsx(HistoryPlugin, {}),
1121
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
1122
+ /* @__PURE__ */ jsx(ClickableLinkPlugin, { newTab: true }),
1123
+ !singleLine && /* @__PURE__ */ jsx(ListPlugin, {}),
1124
+ !singleLine && /* @__PURE__ */ jsx(CheckListPlugin, {}),
1125
+ !singleLine && /* @__PURE__ */ jsx(HorizontalRulePlugin, {}),
1126
+ /* @__PURE__ */ jsx(TabIndentationPlugin, {}),
1127
+ /* @__PURE__ */ jsx(MarkdownShortcutPlugin, { transformers }),
1128
+ /* @__PURE__ */ jsx(MarkdownSyncPlugin, {
1129
+ value,
1130
+ editable,
1131
+ onChange,
1132
+ transformers,
1133
+ singleLine
1134
+ }),
1135
+ /* @__PURE__ */ jsx(ImagePastePlugin, { onImageUpload }),
1136
+ /* @__PURE__ */ jsx(KeyboardPlugin, {
1137
+ onSave,
1138
+ onCancel,
1139
+ singleLine,
1140
+ blurOnEscape: singleLine || saveOnBlur,
1141
+ skipBlurSaveRef
1142
+ }),
1143
+ autoFocus && /* @__PURE__ */ jsx(AutoFocusPlugin, {})
1144
+ ]
1145
+ })
1146
+ })
1147
+ });
1219
1148
  };
1220
-
1149
+ //#endregion
1221
1150
  export { MarkdownEditor };
1222
- //# sourceMappingURL=markdown-editor.js.map
1151
+
1223
1152
  //# sourceMappingURL=markdown-editor.js.map