@jimka/typescript-ui 0.1.1 → 0.2.0

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 (279) hide show
  1. package/dist/lib/{AbstractBooleanInput-C3rj7sje.js → AbstractBooleanInput-BGesvIZy.js} +2 -2
  2. package/dist/lib/{AbstractBooleanInput-C3rj7sje.js.map → AbstractBooleanInput-BGesvIZy.js.map} +1 -1
  3. package/dist/lib/{AbstractInput-CfjW_HM2.js → AbstractInput-BxOtFrER.js} +2 -2
  4. package/dist/lib/{AbstractInput-CfjW_HM2.js.map → AbstractInput-BxOtFrER.js.map} +1 -1
  5. package/dist/lib/AnimatedDropdown-CZQEqQi-.js +2 -0
  6. package/dist/lib/AnimatedDropdown-CZQEqQi-.js.map +1 -0
  7. package/dist/lib/Border-BgaZkc1R.js +2 -0
  8. package/dist/lib/Border-BgaZkc1R.js.map +1 -0
  9. package/dist/lib/{BoxLayout-8ryMD4j5.js → BoxLayout-DrKGfsup.js} +2 -2
  10. package/dist/lib/{BoxLayout-8ryMD4j5.js.map → BoxLayout-DrKGfsup.js.map} +1 -1
  11. package/dist/lib/Button-U1rsP4v8.js +2 -0
  12. package/dist/lib/Button-U1rsP4v8.js.map +1 -0
  13. package/dist/lib/{Card-AKIUAWYS.js → Card-BVwOS8AG.js} +2 -2
  14. package/dist/lib/{Card-AKIUAWYS.js.map → Card-BVwOS8AG.js.map} +1 -1
  15. package/dist/lib/ComboBox-B9EDPu6T.js +2 -0
  16. package/dist/lib/ComboBox-B9EDPu6T.js.map +1 -0
  17. package/dist/lib/Component-DXf5a3g0.js +2 -0
  18. package/dist/lib/Component-DXf5a3g0.js.map +1 -0
  19. package/dist/lib/Container-D5H8VxJK.js +2 -0
  20. package/dist/lib/{Container-3KsvsJ6S.js.map → Container-D5H8VxJK.js.map} +1 -1
  21. package/dist/lib/DOM-CGn7xyTw.js +2 -0
  22. package/dist/lib/DOM-CGn7xyTw.js.map +1 -0
  23. package/dist/lib/{DragManager-c6M_mE3e.js → DragManager-CI4mZQFy.js} +2 -2
  24. package/dist/lib/{DragManager-tPMnbjeO.js.map → DragManager-CI4mZQFy.js.map} +1 -1
  25. package/dist/lib/FieldDecorator-CJs9gwqH.js +2 -0
  26. package/dist/lib/{FieldDecorator-Bl0h9UEF.js.map → FieldDecorator-CJs9gwqH.js.map} +1 -1
  27. package/dist/lib/{Fit-CFIr4P9R.js → Fit-DjRsiwlg.js} +2 -2
  28. package/dist/lib/{Fit-CUS1JXy3.js.map → Fit-DjRsiwlg.js.map} +1 -1
  29. package/dist/lib/Glyph-KEf4nIv2.js +2 -0
  30. package/dist/lib/Glyph-KEf4nIv2.js.map +1 -0
  31. package/dist/lib/{Grid-Czux--Mr.js → Grid-DdL9TVeJ.js} +2 -2
  32. package/dist/lib/{Grid-D8luwRPI.js.map → Grid-DdL9TVeJ.js.map} +1 -1
  33. package/dist/lib/GridConstraints-DkLueyrf.js +2 -0
  34. package/dist/lib/GridConstraints-DkLueyrf.js.map +1 -0
  35. package/dist/lib/{HBox-BWMf9h6r.js → HBox-Ce0GTCJP.js} +2 -2
  36. package/dist/lib/{HBox-BWMf9h6r.js.map → HBox-Ce0GTCJP.js.map} +1 -1
  37. package/dist/lib/{Header-CQjomjo0.js → Header-BQIVpBte.js} +2 -2
  38. package/dist/lib/{Header-CQjomjo0.js.map → Header-BQIVpBte.js.map} +1 -1
  39. package/dist/lib/{IconText-BXJK0MOq.js → IconText-Bo4Qcnot.js} +2 -2
  40. package/dist/lib/{IconText-BXJK0MOq.js.map → IconText-Bo4Qcnot.js.map} +1 -1
  41. package/dist/lib/LayoutConstraints-DkctJZXB.js +2 -0
  42. package/dist/lib/LayoutConstraints-DkctJZXB.js.map +1 -0
  43. package/dist/lib/LayoutSerialization-BT66TY83.js +2 -0
  44. package/dist/lib/LayoutSerialization-BT66TY83.js.map +1 -0
  45. package/dist/lib/LayoutSizes-DU3qSq0w.js +2 -0
  46. package/dist/lib/LayoutSizes-DU3qSq0w.js.map +1 -0
  47. package/dist/lib/List-DtTVTyWW.js +2 -0
  48. package/dist/lib/List-DtTVTyWW.js.map +1 -0
  49. package/dist/lib/Menu-Dz4L7EKJ.js +2 -0
  50. package/dist/lib/Menu-Dz4L7EKJ.js.map +1 -0
  51. package/dist/lib/{MenuButton-D15cSbMf.js → MenuButton-VJ5uC3c1.js} +2 -2
  52. package/dist/lib/{MenuButton-D15cSbMf.js.map → MenuButton-VJ5uC3c1.js.map} +1 -1
  53. package/dist/lib/Panel-DzWdYwq1.js +2 -0
  54. package/dist/lib/Panel-DzWdYwq1.js.map +1 -0
  55. package/dist/lib/{Position-DJKB0N3l.js → Position-A39T7-_1.js} +2 -2
  56. package/dist/lib/{Position-DJKB0N3l.js.map → Position-A39T7-_1.js.map} +1 -1
  57. package/dist/lib/ProgressSpinner-zJKtGyNJ.js +2 -0
  58. package/dist/lib/ProgressSpinner-zJKtGyNJ.js.map +1 -0
  59. package/dist/lib/RadioButton-CYSSv4nV.js +2 -0
  60. package/dist/lib/RadioButton-CYSSv4nV.js.map +1 -0
  61. package/dist/lib/{RovingTabIndex-CFIzIjjU.js → RovingTabIndex-BSZ31Vp5.js} +2 -2
  62. package/dist/lib/{RovingTabIndex-D-zOuptw.js.map → RovingTabIndex-BSZ31Vp5.js.map} +1 -1
  63. package/dist/lib/Scrollbar-BhtOjqkG.js +2 -0
  64. package/dist/lib/Scrollbar-BhtOjqkG.js.map +1 -0
  65. package/dist/lib/Slider-CdXm7E64.js +2 -0
  66. package/dist/lib/Slider-CdXm7E64.js.map +1 -0
  67. package/dist/lib/{Spacer-BVQncB5B.js → Spacer-D9NGW4QG.js} +2 -2
  68. package/dist/lib/{Spacer-BVQncB5B.js.map → Spacer-D9NGW4QG.js.map} +1 -1
  69. package/dist/lib/{TabButton-DqYDfN7P.js → TabButton-CThevdY_.js} +2 -2
  70. package/dist/lib/{TabButton-DqYDfN7P.js.map → TabButton-CThevdY_.js.map} +1 -1
  71. package/dist/lib/Text-gz6qjFfP.js +2 -0
  72. package/dist/lib/Text-gz6qjFfP.js.map +1 -0
  73. package/dist/lib/Tooltip-tkXDk5Bo.js +3 -0
  74. package/dist/lib/Tooltip-tkXDk5Bo.js.map +1 -0
  75. package/dist/lib/{VBox-CdV9VGxR.js → VBox-EIqw85Rh.js} +2 -2
  76. package/dist/lib/{VBox-CdV9VGxR.js.map → VBox-EIqw85Rh.js.map} +1 -1
  77. package/dist/lib/{VirtualRowView-C18j90n5.js → VirtualRowView-BxpHQ_u8.js} +2 -2
  78. package/dist/lib/{VirtualRowView-C18j90n5.js.map → VirtualRowView-BxpHQ_u8.js.map} +1 -1
  79. package/dist/lib/{VirtualScroller-BKMvTisA.js → VirtualScroller-DwEEJE1_.js} +2 -2
  80. package/dist/lib/{VirtualScroller-BKMvTisA.js.map → VirtualScroller-DwEEJE1_.js.map} +1 -1
  81. package/dist/lib/component/button.es.js +1 -1
  82. package/dist/lib/component/button.es.js.map +1 -1
  83. package/dist/lib/component/chart.es.js +1 -1
  84. package/dist/lib/component/chart.es.js.map +1 -1
  85. package/dist/lib/component/container.es.js +1 -1
  86. package/dist/lib/component/container.es.js.map +1 -1
  87. package/dist/lib/component/diagram.es.js +1 -1
  88. package/dist/lib/component/diagram.es.js.map +1 -1
  89. package/dist/lib/component/display.es.js +1 -1
  90. package/dist/lib/component/display.es.js.map +1 -1
  91. package/dist/lib/component/editor.es.js +2 -2
  92. package/dist/lib/component/editor.es.js.map +1 -1
  93. package/dist/lib/component/input.es.js +1 -1
  94. package/dist/lib/component/input.es.js.map +1 -1
  95. package/dist/lib/component/list.es.js +1 -1
  96. package/dist/lib/component/menubar.es.js +1 -1
  97. package/dist/lib/component/menubar.es.js.map +1 -1
  98. package/dist/lib/component/table.es.js +2 -2
  99. package/dist/lib/component/table.es.js.map +1 -1
  100. package/dist/lib/component/tree.es.js +1 -1
  101. package/dist/lib/component/tree.es.js.map +1 -1
  102. package/dist/lib/core.es.js +1 -1
  103. package/dist/lib/core.es.js.map +1 -1
  104. package/dist/lib/layout.es.js +1 -1
  105. package/dist/lib/overlay.es.js +1 -1
  106. package/dist/lib/overlay.es.js.map +1 -1
  107. package/dist/lib/primitive.es.js +1 -1
  108. package/dist/lib/router.es.js +1 -1
  109. package/dist/lib/types/component/button/Button.d.ts +4 -3
  110. package/dist/lib/types/component/chart/AbstractChart.d.ts +1 -1
  111. package/dist/lib/types/component/chart/ChartLegend.d.ts +1 -1
  112. package/dist/lib/types/component/container/DialogBackdrop.d.ts +2 -1
  113. package/dist/lib/types/component/container/MenuItem.d.ts +1 -1
  114. package/dist/lib/types/component/container/ScrollStrip.d.ts +1 -0
  115. package/dist/lib/types/component/container/SplitGutter.d.ts +3 -2
  116. package/dist/lib/types/component/container/TabBar.d.ts +1 -2
  117. package/dist/lib/types/component/container/TabPanel.d.ts +6 -3
  118. package/dist/lib/types/component/container/WindowBorder.d.ts +2 -1
  119. package/dist/lib/types/component/container/WindowHeader.d.ts +2 -1
  120. package/dist/lib/types/component/display/Glyph.d.ts +2 -1
  121. package/dist/lib/types/component/display/Markdown.d.ts +1 -1
  122. package/dist/lib/types/component/display/PaginationBar.d.ts +1 -1
  123. package/dist/lib/types/component/display/SpinnerWrap.d.ts +2 -0
  124. package/dist/lib/types/component/display/Video.d.ts +1 -1
  125. package/dist/lib/types/component/display/VideoPlayer.d.ts +1 -1
  126. package/dist/lib/types/component/editor/CodeEditor.d.ts +2 -1
  127. package/dist/lib/types/component/editor/MarkdownEditor.d.ts +1 -1
  128. package/dist/lib/types/component/input/Checkbox.d.ts +2 -1
  129. package/dist/lib/types/component/input/Link.d.ts +1 -1
  130. package/dist/lib/types/component/input/RadioButton.d.ts +2 -1
  131. package/dist/lib/types/component/input/Slider.d.ts +2 -1
  132. package/dist/lib/types/component/input/Text.d.ts +1 -3
  133. package/dist/lib/types/component/input/TextInput.d.ts +3 -2
  134. package/dist/lib/types/component/list/AbstractSelectableList.d.ts +2 -1
  135. package/dist/lib/types/component/menubar/MenuBarButton.d.ts +1 -1
  136. package/dist/lib/types/core/AnimatedDropdown.d.ts +6 -2
  137. package/dist/lib/types/core/Animation.d.ts +9 -6
  138. package/dist/lib/types/core/Component.d.ts +16 -11
  139. package/dist/lib/types/core/DOM.d.ts +8 -0
  140. package/dist/lib/types/core/Event.d.ts +12 -6
  141. package/dist/lib/types/core/index.d.ts +2 -2
  142. package/dist/lib/types/layout/Accordion.d.ts +2 -0
  143. package/dist/lib/types/layout/Border.d.ts +1 -0
  144. package/dist/lib/types/layout/CollapseSupport.d.ts +5 -1
  145. package/dist/lib/types/layout/LayoutConstraints.d.ts +1 -0
  146. package/dist/lib/types/layout/LayoutManager.d.ts +2 -0
  147. package/dist/lib/types/layout/Split.d.ts +1 -0
  148. package/dist/lib/types/layout/Tab.d.ts +10 -3
  149. package/dist/lib/types/overlay/AbstractWindow.d.ts +9 -2
  150. package/dist/lib/types/overlay/Dialog.d.ts +6 -0
  151. package/dist/lib/types/overlay/Dock.d.ts +12 -2
  152. package/dist/lib/types/overlay/Drawer.d.ts +5 -0
  153. package/dist/lib/types/overlay/Menu.d.ts +3 -1
  154. package/dist/lib/types/overlay/Notification.d.ts +3 -0
  155. package/dist/lib/types/overlay/Popover.d.ts +3 -1
  156. package/dist/lib/types/overlay/Rail.d.ts +7 -0
  157. package/dist/lib/types/overlay/Tooltip.d.ts +3 -0
  158. package/dist/lib/types/overlay/index.d.ts +1 -1
  159. package/dist/lib/validation.es.js +1 -1
  160. package/llms.txt +1 -1
  161. package/package.json +1 -1
  162. package/dist/lib/AbstractBooleanInput-C6CidMMX.js +0 -2
  163. package/dist/lib/AbstractBooleanInput-C6CidMMX.js.map +0 -1
  164. package/dist/lib/AbstractInput-DD-NpicS.js +0 -2
  165. package/dist/lib/AbstractInput-DD-NpicS.js.map +0 -1
  166. package/dist/lib/AnimatedDropdown-BFz-DO7R.js +0 -2
  167. package/dist/lib/AnimatedDropdown-BFz-DO7R.js.map +0 -1
  168. package/dist/lib/AnimatedDropdown-BbGCs70E.js +0 -2
  169. package/dist/lib/AnimatedDropdown-BbGCs70E.js.map +0 -1
  170. package/dist/lib/Border-5Ki2DCWz.js +0 -2
  171. package/dist/lib/Border-5Ki2DCWz.js.map +0 -1
  172. package/dist/lib/Border-oZC_SDy8.js +0 -2
  173. package/dist/lib/Border-oZC_SDy8.js.map +0 -1
  174. package/dist/lib/BoxLayout-DgZukB5_.js +0 -2
  175. package/dist/lib/BoxLayout-DgZukB5_.js.map +0 -1
  176. package/dist/lib/Button-Cdq2ySPL.js +0 -2
  177. package/dist/lib/Button-Cdq2ySPL.js.map +0 -1
  178. package/dist/lib/Button-DEUtBqK4.js +0 -2
  179. package/dist/lib/Button-DEUtBqK4.js.map +0 -1
  180. package/dist/lib/Card-UF0fq8cQ.js +0 -2
  181. package/dist/lib/Card-UF0fq8cQ.js.map +0 -1
  182. package/dist/lib/ComboBox-GOBOAJ1N.js +0 -2
  183. package/dist/lib/ComboBox-GOBOAJ1N.js.map +0 -1
  184. package/dist/lib/ComboBox-e_YVsWBr.js +0 -2
  185. package/dist/lib/ComboBox-e_YVsWBr.js.map +0 -1
  186. package/dist/lib/Component-BR2f0Itb.js +0 -2
  187. package/dist/lib/Component-BR2f0Itb.js.map +0 -1
  188. package/dist/lib/Component-Bs6TvaGZ.js +0 -2
  189. package/dist/lib/Component-Bs6TvaGZ.js.map +0 -1
  190. package/dist/lib/Container-3KsvsJ6S.js +0 -2
  191. package/dist/lib/Container-CzyZeMyZ.js +0 -2
  192. package/dist/lib/Container-CzyZeMyZ.js.map +0 -1
  193. package/dist/lib/DOM-DeF_dWe2.js +0 -2
  194. package/dist/lib/DOM-DeF_dWe2.js.map +0 -1
  195. package/dist/lib/DragManager-c6M_mE3e.js.map +0 -1
  196. package/dist/lib/DragManager-tPMnbjeO.js +0 -2
  197. package/dist/lib/FieldDecorator-Bl0h9UEF.js +0 -2
  198. package/dist/lib/FieldDecorator-E7WzutjO.js +0 -2
  199. package/dist/lib/FieldDecorator-E7WzutjO.js.map +0 -1
  200. package/dist/lib/Fit-CFIr4P9R.js.map +0 -1
  201. package/dist/lib/Fit-CUS1JXy3.js +0 -2
  202. package/dist/lib/Glyph-6Ki3A1_a.js +0 -2
  203. package/dist/lib/Glyph-6Ki3A1_a.js.map +0 -1
  204. package/dist/lib/Glyph-WUqTF_Z9.js +0 -2
  205. package/dist/lib/Glyph-WUqTF_Z9.js.map +0 -1
  206. package/dist/lib/Grid-Czux--Mr.js.map +0 -1
  207. package/dist/lib/Grid-D8luwRPI.js +0 -2
  208. package/dist/lib/GridConstraints-CzC0w4i-.js +0 -2
  209. package/dist/lib/GridConstraints-CzC0w4i-.js.map +0 -1
  210. package/dist/lib/GridConstraints-Q3wvuovN.js +0 -2
  211. package/dist/lib/GridConstraints-Q3wvuovN.js.map +0 -1
  212. package/dist/lib/HBox-Q8y1PHlk.js +0 -2
  213. package/dist/lib/HBox-Q8y1PHlk.js.map +0 -1
  214. package/dist/lib/Header-zPdvC2AX.js +0 -2
  215. package/dist/lib/Header-zPdvC2AX.js.map +0 -1
  216. package/dist/lib/IconText-BvGfu7eq.js +0 -2
  217. package/dist/lib/IconText-BvGfu7eq.js.map +0 -1
  218. package/dist/lib/LayoutConstraints-CHI2lhPd.js +0 -2
  219. package/dist/lib/LayoutConstraints-CHI2lhPd.js.map +0 -1
  220. package/dist/lib/LayoutSerialization-CWU0iEuV.js +0 -2
  221. package/dist/lib/LayoutSerialization-CWU0iEuV.js.map +0 -1
  222. package/dist/lib/LayoutSerialization-C_zcwaN3.js +0 -2
  223. package/dist/lib/LayoutSerialization-C_zcwaN3.js.map +0 -1
  224. package/dist/lib/LayoutSizes-BW4wyGDI.js +0 -2
  225. package/dist/lib/LayoutSizes-BW4wyGDI.js.map +0 -1
  226. package/dist/lib/LayoutSizes-XdhpAOmc.js +0 -2
  227. package/dist/lib/LayoutSizes-XdhpAOmc.js.map +0 -1
  228. package/dist/lib/List-BgxnLWnl.js +0 -2
  229. package/dist/lib/List-BgxnLWnl.js.map +0 -1
  230. package/dist/lib/List-D-GhoVgB.js +0 -2
  231. package/dist/lib/List-D-GhoVgB.js.map +0 -1
  232. package/dist/lib/Menu-BdnwRY0M.js +0 -2
  233. package/dist/lib/Menu-BdnwRY0M.js.map +0 -1
  234. package/dist/lib/Menu-Wz4MK6sA.js +0 -2
  235. package/dist/lib/Menu-Wz4MK6sA.js.map +0 -1
  236. package/dist/lib/MenuButton-JQQaKThJ.js +0 -2
  237. package/dist/lib/MenuButton-JQQaKThJ.js.map +0 -1
  238. package/dist/lib/Panel-DApa7vA6.js +0 -2
  239. package/dist/lib/Panel-DApa7vA6.js.map +0 -1
  240. package/dist/lib/Panel-DIsdaqc2.js +0 -2
  241. package/dist/lib/Panel-DIsdaqc2.js.map +0 -1
  242. package/dist/lib/Position-B-WfZYAJ.js +0 -2
  243. package/dist/lib/Position-B-WfZYAJ.js.map +0 -1
  244. package/dist/lib/ProgressSpinner-CNYcsBhw.js +0 -2
  245. package/dist/lib/ProgressSpinner-CNYcsBhw.js.map +0 -1
  246. package/dist/lib/ProgressSpinner-Dzaka7_c.js +0 -2
  247. package/dist/lib/ProgressSpinner-Dzaka7_c.js.map +0 -1
  248. package/dist/lib/RadioButton-CepkneSA.js +0 -2
  249. package/dist/lib/RadioButton-CepkneSA.js.map +0 -1
  250. package/dist/lib/RadioButton-eI3-vXn3.js +0 -2
  251. package/dist/lib/RadioButton-eI3-vXn3.js.map +0 -1
  252. package/dist/lib/RovingTabIndex-CFIzIjjU.js.map +0 -1
  253. package/dist/lib/RovingTabIndex-D-zOuptw.js +0 -2
  254. package/dist/lib/Scrollbar-DQdCdj94.js +0 -2
  255. package/dist/lib/Scrollbar-DQdCdj94.js.map +0 -1
  256. package/dist/lib/Scrollbar-DchfiFKC.js +0 -2
  257. package/dist/lib/Scrollbar-DchfiFKC.js.map +0 -1
  258. package/dist/lib/Slider-DvDdVvRm.js +0 -2
  259. package/dist/lib/Slider-DvDdVvRm.js.map +0 -1
  260. package/dist/lib/Slider-xRxNSjFN.js +0 -2
  261. package/dist/lib/Slider-xRxNSjFN.js.map +0 -1
  262. package/dist/lib/Spacer-BqiPiCqz.js +0 -2
  263. package/dist/lib/Spacer-BqiPiCqz.js.map +0 -1
  264. package/dist/lib/TabButton-pZKFYg7b.js +0 -2
  265. package/dist/lib/TabButton-pZKFYg7b.js.map +0 -1
  266. package/dist/lib/Text-D1RrBY52.js +0 -2
  267. package/dist/lib/Text-D1RrBY52.js.map +0 -1
  268. package/dist/lib/Text-DJoUHukb.js +0 -2
  269. package/dist/lib/Text-DJoUHukb.js.map +0 -1
  270. package/dist/lib/Tooltip-4lU_tf6r.js +0 -3
  271. package/dist/lib/Tooltip-4lU_tf6r.js.map +0 -1
  272. package/dist/lib/Tooltip-5BR7yYEX.js +0 -3
  273. package/dist/lib/Tooltip-5BR7yYEX.js.map +0 -1
  274. package/dist/lib/VBox-D5B9ecw0.js +0 -2
  275. package/dist/lib/VBox-D5B9ecw0.js.map +0 -1
  276. package/dist/lib/VirtualRowView-Con9SWiU.js +0 -2
  277. package/dist/lib/VirtualRowView-Con9SWiU.js.map +0 -1
  278. package/dist/lib/VirtualScroller-BPDmO1ir.js +0 -2
  279. package/dist/lib/VirtualScroller-BPDmO1ir.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"editor.es.js","names":[],"sources":["../../../src/typescript/lib/component/editor/LanguageRegistry.ts","../../../src/typescript/lib/component/editor/formatters/prettier.ts","../../../src/typescript/lib/component/editor/formatters/sql.ts","../../../src/typescript/lib/component/editor/languages.ts","../../../src/typescript/lib/component/editor/theme.ts","../../../src/typescript/lib/component/editor/CodeEditor.ts","../../../src/typescript/lib/component/editor/markdownTableTransformer.ts","../../../src/typescript/lib/component/editor/markdownTransformers.ts","../../../src/typescript/lib/component/editor/editorNodes.ts","../../../src/typescript/lib/component/editor/editorTheme.ts","../../../src/typescript/lib/component/editor/MarkdownEditor.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Extension } from \"@codemirror/state\";\n\n/**\n * Formats `source`, returning the formatted text and a cursor offset mapped\n * (or clamped) into the new document. May return synchronously or via a\n * `Promise` — `CodeEditor.format()` awaits either.\n *\n * @param source - The document text to format.\n * @param cursorOffset - The cursor's current offset into `source`.\n * @returns The formatted text and the mapped/clamped cursor offset.\n */\nexport type Formatter = (\n source: string,\n cursorOffset: number,\n) =>\n | Promise<{ formatted: string; cursorOffset: number }>\n | { formatted: string; cursorOffset: number };\n\n/**\n * Describes one language {@link CodeEditor} can be configured with: its\n * grammar (a lazily-loaded CodeMirror `Extension`) and, optionally, its\n * formatter.\n *\n * @category Components\n */\nexport interface LanguageDefinition {\n /** The registry key passed to `CodeEditor.setLanguage` / the `language` option. */\n id: string;\n /** Human-readable name, for a consumer building a language picker. */\n label?: string;\n /** Dynamically imports and builds the CodeMirror grammar extension. */\n loadExtension: () => Promise<Extension>;\n /** Dynamically imports and builds this language's formatter, when one exists. */\n loadFormatter?: () => Promise<Formatter>;\n}\n\n/** Module-level registry, keyed by {@link LanguageDefinition.id}. */\nconst _registry = new Map<string, LanguageDefinition>();\n\n/**\n * Registers a language definition, replacing any prior registration under the\n * same {@link LanguageDefinition.id}.\n *\n * @param def - The language definition to register.\n */\nexport function registerLanguage(def: LanguageDefinition): void {\n _registry.set(def.id, def);\n}\n\n/**\n * Looks up a registered language definition by id.\n *\n * @param id - The language id.\n * @returns The definition, or `undefined` when no language is registered\n * under that id.\n */\nexport function getLanguage(id: string): LanguageDefinition | undefined {\n return _registry.get(id);\n}\n\n/**\n * Lists every currently registered language definition.\n *\n * @returns A read-only snapshot of the registered definitions.\n */\nexport function listLanguages(): readonly LanguageDefinition[] {\n return Array.from(_registry.values());\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Plugin } from \"prettier\";\nimport type { Formatter } from \"~/component/editor/LanguageRegistry.js\";\n\n/**\n * Builds a Prettier-backed {@link Formatter} for a given parser id.\n *\n * The returned closure dynamically imports `prettier/standalone` and the\n * caller-supplied plugin set on first invocation, so the (large) Prettier\n * standalone bundle only ever loads behind a `format()` call, never as part\n * of the base editor chunk. This adapter is shared by every Prettier-backed\n * language entry in `languages.ts` — each supplies its own parser id and\n * plugin loader (`babel-ts` + the babel/estree plugins for JS/TS, `json` for\n * JSON, `html` for HTML, `markdown` for Markdown).\n *\n * @param parser - The Prettier parser id (e.g. `\"babel-ts\"`, `\"json\"`).\n * @param loadPlugins - Dynamically imports the Prettier plugin modules the\n * parser needs.\n * @returns A {@link Formatter} that formats through Prettier's\n * `formatWithCursor`, which maps the cursor offset through the reformat.\n */\nexport function formatWithPrettier(\n parser: string,\n loadPlugins: () => Promise<Plugin[]>,\n): Formatter {\n return async (source: string, cursorOffset: number) => {\n const [{ formatWithCursor }, plugins] = await Promise.all([\n import(\"prettier/standalone\"),\n loadPlugins(),\n ]);\n\n return formatWithCursor(source, { parser, plugins, cursorOffset });\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Formatter } from \"~/component/editor/LanguageRegistry.js\";\n\n/**\n * SQL formatter adapter backed by `sql-formatter`, dynamically imported on\n * first call so it never lands in the base editor chunk.\n *\n * Unlike Prettier's `formatWithCursor`, `sql-formatter` has no cursor-mapping\n * API, so the old cursor offset is clamped to the formatted document's length\n * rather than mapped to its logically-equivalent position.\n */\nexport const formatWithSql: Formatter = async (source: string, cursorOffset: number) => {\n const { format } = await import(\"sql-formatter\");\n const formatted = format(source);\n\n return { formatted, cursorOffset: Math.min(cursorOffset, formatted.length) };\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n// Registers the five built-in language definitions as a side effect of\n// importing this module (from the barrel `index.ts`). Every grammar and\n// formatter loader is a dynamic `import()`, so — because the CodeMirror\n// language packages and Prettier/sql-formatter are `external` in the lib\n// build — none of them reach the base editor chunk; each loads only when a\n// consumer actually selects that language or calls `format()`.\n\nimport { registerLanguage } from \"~/component/editor/LanguageRegistry.js\";\nimport { formatWithPrettier } from \"~/component/editor/formatters/prettier.js\";\nimport { formatWithSql } from \"~/component/editor/formatters/sql.js\";\n\n/** Loads the babel + estree plugins Prettier's `babel-ts` and `json` parsers need. */\nasync function loadBabelPlugins() {\n const [babel, estree] = await Promise.all([\n import(\"prettier/plugins/babel\"),\n import(\"prettier/plugins/estree\"),\n ]);\n\n return [babel, estree];\n}\n\nregisterLanguage({\n id: \"javascript\",\n label: \"JavaScript / TypeScript\",\n loadExtension: async () => {\n const { javascript } = await import(\"@codemirror/lang-javascript\");\n\n return javascript({ typescript: true });\n },\n loadFormatter: async () => formatWithPrettier(\"babel-ts\", loadBabelPlugins),\n});\n\nregisterLanguage({\n id: \"json\",\n label: \"JSON\",\n loadExtension: async () => {\n const { json } = await import(\"@codemirror/lang-json\");\n\n return json();\n },\n loadFormatter: async () => formatWithPrettier(\"json\", loadBabelPlugins),\n});\n\nregisterLanguage({\n id: \"html\",\n label: \"HTML\",\n loadExtension: async () => {\n const { html } = await import(\"@codemirror/lang-html\");\n\n return html();\n },\n loadFormatter: async () => formatWithPrettier(\"html\", async () => [await import(\"prettier/plugins/html\")]),\n});\n\nregisterLanguage({\n id: \"sql\",\n label: \"SQL\",\n loadExtension: async () => {\n const { sql } = await import(\"@codemirror/lang-sql\");\n\n return sql();\n },\n loadFormatter: async () => formatWithSql,\n});\n\nregisterLanguage({\n id: \"markdown\",\n label: \"Markdown\",\n loadExtension: async () => {\n const { markdown } = await import(\"@codemirror/lang-markdown\");\n\n return markdown();\n },\n loadFormatter: async () => formatWithPrettier(\"markdown\", async () => [await import(\"prettier/plugins/markdown\")]),\n});\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { EditorView } from \"@codemirror/view\";\nimport { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { tags } from \"@lezer/highlight\";\nimport type { Extension } from \"@codemirror/state\";\n\n// The syntax palette has no dedicated project theme tokens (the framework\n// defines chrome/surface/accent tokens, not a per-token-kind syntax scheme),\n// so it is a fixed, IDE-conventional colour set chosen for light/dark\n// legibility rather than derived from CSS variables — mirroring how\n// `Markdown`'s code-block wash is a neutral constant rather than a token.\nconst SYNTAX_KEYWORD = \"#2563eb\"; // matches the framework's single accent hue\nconst SYNTAX_STRING = \"#16a34a\"; // green — conventional \"string\" colour\nconst SYNTAX_COMMENT = \"#6b7280\"; // muted grey — de-emphasises comments\nconst SYNTAX_LITERAL = \"#d97706\"; // amber — numbers/booleans/null\nconst SYNTAX_FUNCTION = \"#7c3aed\"; // violet — function/variable definitions\nconst SYNTAX_TYPE = \"#0891b2\"; // teal — type names\nconst SYNTAX_PROPERTY = \"#be185d\"; // pink — object/attribute property names\n\n/**\n * Builds the editor's theme extension: the chrome (background, gutters,\n * cursor, selection) via `EditorView.theme`, plus syntax-highlighting colours\n * via a `HighlightStyle`.\n *\n * The chrome reads the project's CSS custom-property tokens directly\n * (`--ts-ui-input-bg`, `--ts-ui-text-color`, `--ts-ui-font-mono`,\n * `--ts-ui-border-color`, `--ts-ui-indicator-focus`,\n * `--ts-ui-indicator-selection`), each with a light/dark-safe fallback, so a\n * `ThemeManager.setTheme` toggle recolours the editor with no rebuild — only\n * the underlying CSS variables change.\n *\n * @param dark - Whether the dark theme flag (`--ts-ui-color-scheme: dark`) is\n * currently active, forwarded to `EditorView.theme`'s `dark` option so\n * CodeMirror's own dark-mode heuristics (default scrollbar styling, base\n * theme selection) agree with the project's active theme.\n * @returns The combined chrome + syntax-highlighting extension.\n */\nexport function codeEditorTheme(dark: boolean): Extension {\n const chrome = EditorView.theme({\n \"&\": {\n height: \"100%\",\n color: \"var(--ts-ui-text-color, #1a1a1a)\",\n backgroundColor: \"var(--ts-ui-input-bg, #ffffff)\",\n },\n \".cm-content\": {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n caretColor: \"var(--ts-ui-indicator-focus, #2563eb)\",\n cursor: \"text\",\n },\n \".cm-gutters\": {\n color: \"var(--ts-ui-text-color, #1a1a1a)\",\n backgroundColor: \"var(--ts-ui-input-bg, #ffffff)\",\n borderRight: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n },\n \"&.cm-focused .cm-cursor\": {\n borderLeftColor: \"var(--ts-ui-indicator-focus, #2563eb)\",\n },\n \"&.cm-focused .cm-selectionBackground, .cm-selectionBackground\": {\n backgroundColor: \"var(--ts-ui-indicator-selection, rgba(37, 99, 235, 0.25))\",\n },\n \".cm-activeLine, .cm-activeLineGutter\": {\n backgroundColor: \"rgba(127, 127, 127, 0.08)\",\n },\n }, { dark });\n\n const highlight = syntaxHighlighting(HighlightStyle.define([\n { tag: tags.keyword, color: SYNTAX_KEYWORD },\n { tag: [tags.string, tags.special(tags.string)], color: SYNTAX_STRING },\n { tag: tags.comment, color: SYNTAX_COMMENT, fontStyle: \"italic\" },\n { tag: [tags.number, tags.bool, tags.null], color: SYNTAX_LITERAL },\n { tag: [tags.definition(tags.variableName), tags.function(tags.variableName)], color: SYNTAX_FUNCTION },\n { tag: tags.typeName, color: SYNTAX_TYPE },\n { tag: [tags.propertyName, tags.attributeName], color: SYNTAX_PROPERTY },\n { tag: tags.tagName, color: SYNTAX_KEYWORD },\n // Markdown constructs (the `markdown` language, e.g. the MarkdownEditor\n // source surface). The parser tags these; without a style here they\n // would render as plain text. Headings/links reuse the accent hue; bold\n // and italic carry weight/slant rather than colour so prose stays\n // readable; the markup marks (`#`, `**`, `` ` ``, `>`, `-`) and quotes\n // are muted so the content reads over the syntax.\n { tag: [tags.heading, tags.heading1, tags.heading2, tags.heading3,\n tags.heading4, tags.heading5, tags.heading6], color: SYNTAX_KEYWORD, fontWeight: \"bold\" },\n { tag: tags.strong, fontWeight: \"bold\" },\n { tag: tags.emphasis, fontStyle: \"italic\" },\n { tag: tags.monospace, color: SYNTAX_STRING },\n { tag: [tags.link, tags.url], color: SYNTAX_KEYWORD, textDecoration: \"underline\" },\n { tag: [tags.quote, tags.contentSeparator], color: SYNTAX_COMMENT },\n { tag: tags.processingInstruction, color: SYNTAX_COMMENT },\n ]));\n\n return [chrome, highlight];\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Animation } from \"~/core/Animation.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { consumeWheel } from \"~/core/SmoothScroller.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { EditorView, keymap, drawSelection, lineNumbers, highlightActiveLine, highlightActiveLineGutter } from \"@codemirror/view\";\nimport { EditorState, Compartment } from \"@codemirror/state\";\nimport type { Extension } from \"@codemirror/state\";\nimport { history, defaultKeymap, historyKeymap, indentWithTab } from \"@codemirror/commands\";\nimport { indentOnInput, bracketMatching, indentRange } from \"@codemirror/language\";\nimport { getLanguage } from \"~/component/editor/LanguageRegistry.js\";\nimport { codeEditorTheme } from \"~/component/editor/theme.js\";\n\n/**\n * Payload of {@link CodeEditor}'s `\"change\"` event: the document text after\n * the edit that triggered it.\n *\n * @category Components\n */\nexport interface CodeEditorChange {\n value: string;\n}\n\n/**\n * The events {@link CodeEditor} exposes through its custom `on` / `off` surface:\n *\n * - `\"change\"` — the document text changed (payload {@link CodeEditorChange}).\n * - `\"readonlyedit\"` — a user edit was rejected because the editor is read-only\n * (no payload); see {@link CodeEditor.on}.\n */\ntype CodeEditorEvent = \"change\" | \"readonlyedit\";\n\n/**\n * Construction-time options for {@link CodeEditor}.\n *\n * @category Components\n */\nexport interface CodeEditorOptions extends ComponentOptions {\n /** Initial document text (also accepted as the positional first argument). */\n value?: string;\n /** Registered language id (e.g. `\"javascript\"`, `\"json\"`, `\"sql\"`). Unset renders plain text. */\n language?: string;\n /** Whether the editor rejects edits. Default `false`. */\n readOnly?: boolean;\n /** Construction-time listener bag; the events are `\"change\"` and `\"readonlyedit\"`. */\n listeners?: { change?: (payload: CodeEditorChange) => void; readonlyedit?: () => void };\n}\n\n/** Duration of the read-only rejection flash, in milliseconds. */\nconst READONLY_FLASH_MS = 300;\n\n/** Peak opacity of the read-only rejection wash — a subtle tint that keeps the text readable. */\nconst READONLY_FLASH_PEAK_OPACITY = 0.16;\n\n/**\n * Fill of the read-only rejection wash: the project's validation-error token,\n * with a light/dark-safe fallback, so the tint follows a `ThemeManager.setTheme`\n * toggle with no rebuild (the CSS variable resolves live).\n */\nconst READONLY_FLASH_COLOR = \"var(--ts-ui-validation-error-border, #dc2626)\";\n\n/**\n * Builds the readOnly-state extension: `EditorState.readOnly` blocks every\n * edit (including programmatic ones) while leaving the content DOM editable, so\n * the caret, cursor navigation, and text selection / copy all keep working —\n * the standard read-only experience. (`EditorView.editable.of(false)` would\n * additionally strip selection by making CodeMirror stop managing a caret in\n * the content, so it is deliberately *not* used here.)\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns The extension for the readOnly compartment.\n */\nfunction buildReadOnlyExtension(readOnly: boolean): Extension {\n return EditorState.readOnly.of(readOnly);\n}\n\n/**\n * A syntax-highlighting, formatting code editor wrapping CodeMirror 6.\n *\n * @remarks\n * `CodeEditor` deliberately omits IntelliSense (no autocomplete, no lint, no\n * language service) — its scope is highlighting plus one-command formatting.\n * CodeMirror's `EditorView` is a *foreign live widget*: it takes a real parent\n * element and mutates a whole DOM region it owns directly, exactly like the\n * `CanvasRenderingContext2D` a [`Canvas`](/api/component/display/classes/Canvas)\n * obtains from the seam. So `CodeEditor` is **live-only** — under the modelled\n * test sink it mounts nothing, and every editor operation guards on a `null`\n * view, matching `Canvas` / `WebGLCanvas`.\n *\n * The editor mounts once, in the component's first connected + sized layout\n * (`onFirstLayout`) — CodeMirror needs a real, sized parent to measure its\n * internal scroller correctly. It fills the assigned box and scrolls\n * internally; give it a sized host (a `Fit` panel or an explicit\n * `preferredSize`), the same as `Canvas`.\n *\n * Highlighting grammars and formatters load lazily, per language, through the\n * registry in `LanguageRegistry.ts` (`registerLanguage` / `getLanguage` /\n * `listLanguages`) — see that module and `languages.ts` for the five built-in\n * languages (JavaScript/TypeScript, JSON, HTML, SQL, Markdown).\n *\n * @example\n * ```typescript\n * import { CodeEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new CodeEditor('const x = 1;', { language: 'javascript' });\n * panel.addComponent(editor);\n * await editor.format();\n * ```\n *\n * @category Components\n */\nclass CodeEditor extends Component<CodeEditorOptions> {\n\n /** The live CodeMirror view; `null` until mounted (or forever, offline). */\n private _view: EditorView | null = null;\n\n /** Overlay flashed on a rejected read-only edit; `null` until mounted (or forever, offline). */\n private _flashOverlay: Handle | null = null;\n\n /** Reconfigured by {@link CodeEditor.setLanguage} to swap the active grammar. */\n private readonly _langCompartment: Compartment = new Compartment();\n\n /** Reconfigured by {@link CodeEditor.setReadOnly}. */\n private readonly _readOnlyCompartment: Compartment = new Compartment();\n\n /** Reconfigured on a theme change to recolour the editor. */\n private readonly _themeCompartment: Compartment = new Compartment();\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<CodeEditorEvent> = new ListenerBag<CodeEditorEvent>();\n\n /** Handle to detach the {@link ThemeManager.onThemeChange} listener on {@link CodeEditor.dispose}. */\n private readonly _unsubscribeTheme: () => void;\n\n /**\n * Stable reference for the subtree wheel-claim listener's add (in\n * {@link CodeEditor.mount}) and remove (in {@link CodeEditor.dispose}); see\n * {@link CodeEditor.claimScrollableWheel}.\n */\n private readonly _onWheelClaim: (e: WheelEvent) => void = (e) => this.claimScrollableWheel(e);\n\n /**\n * Constructs a code editor.\n *\n * @param value - Initial document text (optional; defaults to \"\").\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: CodeEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n this._unsubscribeTheme = ThemeManager.onThemeChange(() => this.onThemeChange());\n\n this.applyListeners(options?.listeners);\n\n this.onFirstLayout(() => this.mount());\n }\n\n /**\n * Caches the `value` / `language` / `readOnly` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. No view\n * exists yet at this point (the editor mounts later, in `onFirstLayout`), so\n * these are pure caches, applied to the freshly-created `EditorState` and\n * compartments in `mount()`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: CodeEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.language !== undefined) this._options.language = options.language;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n\n return this;\n }\n\n /**\n * Returns the current document text: read live from the view when mounted,\n * else the cached pre-mount / offline value.\n *\n * @returns The document text, or `\"\"` when unset.\n */\n getValue(): string {\n if (this._view) {\n return this._view.state.doc.toString();\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document. Caches the value; when a view is mounted,\n * also dispatches a full-document replace transaction.\n *\n * @param value - The new document text.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n this._options.value = value;\n\n if (this._view) {\n this._view.dispatch({\n changes: { from: 0, to: this._view.state.doc.length, insert: value },\n });\n }\n\n return this;\n }\n\n /**\n * Returns the active language id.\n *\n * @returns The registered language id, or `null` when unset.\n */\n getLanguage(): string | null {\n return this._options.language ?? null;\n }\n\n /**\n * Sets (or clears) the active language. Caches the id; when a view is\n * mounted and the id names a registered language, kicks off the grammar's\n * lazy `loadExtension()` and reconfigures the grammar compartment once it\n * resolves — guarded against a stale resolution by re-checking the active\n * language id, so a rapid double-swap applies only the latest.\n *\n * @param id - A registered language id, or `null` to clear highlighting.\n * @returns This component, for method chaining.\n */\n setLanguage(id: string | null): this {\n this._options.language = id ?? undefined;\n\n if (!this._view) {\n return this;\n }\n\n if (!id) {\n this._view.dispatch({ effects: this._langCompartment.reconfigure([]) });\n\n return this;\n }\n\n const def = getLanguage(id);\n\n if (!def) {\n return this;\n }\n\n void def.loadExtension().then((extension) => {\n if (this._view && this.getLanguage() === id) {\n this._view.dispatch({ effects: this._langCompartment.reconfigure(extension) });\n }\n });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The readOnly state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits. Caches the state; when a view is\n * mounted, also reconfigures the readOnly compartment.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n\n if (this._view) {\n this._view.dispatch({ effects: this._readOnlyCompartment.reconfigure(buildReadOnlyExtension(readOnly)) });\n }\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the editor.\n *\n * @remarks Overrides {@link core!Component.focus} because a `CodeEditor`'s own\n * element is a plain, non-focusable host `<div>` — CodeMirror owns the real\n * focusable target (its `contentDOM`), so the base `element.focus()` would be\n * a no-op. When mounted this hands focus to the live view; offline / before\n * mount it falls back to the base behaviour (also a no-op, matching every\n * other editor operation's null-view guard). The current selection / caret is\n * left untouched — pair with {@link CodeEditor.moveCursorToEnd} to also move it.\n *\n * @param preventScroll - Forwarded to the base fallback; CodeMirror's own\n * `focus()` takes no scroll option, so it is honoured only offline.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this._view) {\n this._view.focus();\n\n return this;\n }\n\n return super.focus(preventScroll);\n }\n\n /**\n * Places the caret at the end of the document (after the last character),\n * collapsing any selection, and scrolls it into view.\n *\n * @remarks A selection-only transaction — it changes no text, so it emits no\n * `\"change\"`. No-op before the view is mounted (offline / pre-mount), like\n * every other view operation. Pair with {@link CodeEditor.focus} to land the\n * caret ready for typing (e.g. on a freshly opened, pre-seeded editor).\n *\n * @returns This component, for method chaining.\n */\n moveCursorToEnd(): this {\n if (this._view) {\n const end = this._view.state.doc.length;\n\n this._view.dispatch({ selection: { anchor: end }, scrollIntoView: true });\n }\n\n return this;\n }\n\n /**\n * Formats the document via the active language's formatter, or re-indents\n * it (CodeMirror's own indentation service) when the language has none.\n *\n * @remarks If the formatter throws (invalid syntax), the rejection\n * propagates and the document is left **untouched** — the full-document\n * replace only runs after the formatter resolves successfully, so a\n * throwing formatter never reaches it. The cursor is preserved through\n * Prettier's `formatWithCursor` mapping, or clamped to the new document\n * length for a formatter with no cursor map (sql-formatter).\n *\n * @returns A promise that resolves once formatting completes, or rejects\n * with the formatter's error.\n */\n async format(): Promise<void> {\n const id = this.getLanguage();\n const def = id ? getLanguage(id) : undefined;\n\n if (!def?.loadFormatter) {\n this.reindentFallback();\n\n return;\n }\n\n const formatter = await def.loadFormatter();\n const source = this.getValue();\n const cursorOffset = this._view ? this._view.state.selection.main.head : 0;\n\n const result = await formatter(source, cursorOffset);\n\n this._options.value = result.formatted;\n\n if (this._view) {\n this._view.dispatch({\n changes: { from: 0, to: this._view.state.doc.length, insert: result.formatted },\n selection: { anchor: Math.min(result.cursorOffset, result.formatted.length) },\n });\n }\n }\n\n /**\n * The `format()` fallback for a language with no formatter: re-indents the\n * whole document via CodeMirror's own indentation service. Factored out\n * from `format()` so the dispatch decision (formatter vs. this fallback) is\n * unit-testable by spying on this method — the actual re-indent needs a\n * live view (guarded below) and is otherwise manual-verify only.\n */\n private reindentFallback(): void {\n if (this._view) {\n this._view.dispatch({ changes: indentRange(this._view.state, 0, this._view.state.doc.length) });\n }\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the\n * document changes (including via {@link CodeEditor.format} / {@link CodeEditor.setValue}).\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new document text.\n * @returns This component, for method chaining.\n */\n on(event: \"change\", listener: (payload: CodeEditorChange) => void): this;\n /**\n * Registers a listener for the `\"readonlyedit\"` event, fired when a user\n * edit (typing, paste, drop) is rejected because the editor is read-only.\n *\n * @param event - Must be `\"readonlyedit\"`.\n * @param listener - Invoked with no arguments on each rejected edit.\n * @returns This component, for method chaining.\n */\n on(event: \"readonlyedit\", listener: () => void): this;\n on(event: CodeEditorEvent, listener: (payload: CodeEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: \"change\", listener: (payload: CodeEditorChange) => void): this;\n /**\n * Removes a previously registered `\"readonlyedit\"` listener.\n *\n * @param event - Must be `\"readonlyedit\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: \"readonlyedit\", listener: () => void): this;\n off(event: CodeEditorEvent, listener: (payload: CodeEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans an event out to its registered listeners.\n *\n * @param event - The event name.\n * @param payload - The event payload (`\"change\"` only; `\"readonlyedit\"` has none).\n */\n protected emit(event: \"change\", payload: CodeEditorChange): void;\n protected emit(event: \"readonlyedit\"): void;\n protected emit(event: CodeEditorEvent, payload?: CodeEditorChange): void {\n this._listeners.fire(event, ...(payload ? [payload] : []));\n }\n\n /**\n * Detaches the theme-change listener and destroys the live CodeMirror view.\n * Call before discarding a dynamically-built `CodeEditor`, mirroring\n * `Markdown.dispose`.\n */\n dispose(): void {\n this._unsubscribeTheme();\n\n if (this._view) {\n Event.removeSubtreeListener(this, \"wheel\", this._onWheelClaim);\n }\n\n this._view?.destroy();\n }\n\n /**\n * Marks a wheel event consumed (via {@link consumeWheel}) when CodeMirror's\n * own scroller can move along the gesture's axis — WITHOUT\n * `preventDefault()`, so CodeMirror still scrolls `.cm-scroller` natively.\n *\n * @param e - The wheel event reaching this editor's subtree.\n *\n * @remarks A floating overlay installs a wheel trap (see `WheelTrap`) that\n * `preventDefault()`s any wheel no inner scroller claimed, so an unconsumed\n * wheel cannot fall through to content behind the overlay. CodeMirror\n * scrolls natively without going through the framework's eased scroller, so\n * it never claims the wheel — and the trap, firing last as the outermost\n * ancestor, would cancel the editor's native scroll. Claiming here (the\n * subtree walk reaches this inner editor before the outer overlay) lets the\n * native scroll proceed. Only an axis with somewhere to go is claimed; at a\n * fits-in-box axis the wheel stays unclaimed so the trap still swallows it.\n */\n private claimScrollableWheel(e: WheelEvent): void {\n const scroller = this._view?.scrollDOM;\n\n if (!scroller) {\n return;\n }\n\n const extentX = scroller.scrollWidth > scroller.clientWidth;\n const extentY = scroller.scrollHeight > scroller.clientHeight;\n\n // shift+wheel with a bare vertical delta scrolls horizontally (native\n // behaviour CodeMirror preserves), so it targets the horizontal axis.\n const targetsHorizontal = e.deltaX !== 0 || (e.shiftKey && e.deltaY !== 0);\n const targetsVertical = e.deltaY !== 0 && !e.shiftKey;\n\n if ((targetsVertical && extentY) || (targetsHorizontal && extentX)) {\n consumeWheel(e);\n }\n }\n\n /**\n * Builds the `EditorState` and mounts the CodeMirror view through the DOM\n * seam's `mountView` escape. Idempotent (guards on an existing view).\n * Offline / before the element exists, `DOM.sink.mountView` returns `null`,\n * so `_view` stays `null` and every editor operation continues to no-op.\n */\n private mount(): void {\n if (this._view) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n const dark = ThemeManager.getTheme().colorScheme === \"dark\";\n\n const extensions: Extension[] = [\n history(),\n // `indentWithTab` is deliberately absent from `defaultKeymap`,\n // because binding Tab traps it inside the editor and a keyboard\n // user can no longer Tab out. A code editor that does not indent\n // on Tab is the worse trade, and `defaultKeymap` already carries\n // the escape hatch: Ctrl-m (Alt-Shift-m on macOS) toggles\n // CodeMirror's tab-focus mode, after which Tab moves focus again.\n // Listed last so its Tab / Shift-Tab bindings take precedence.\n keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),\n drawSelection(),\n lineNumbers(),\n highlightActiveLine(),\n highlightActiveLineGutter(),\n indentOnInput(),\n bracketMatching(),\n this._readOnlyCompartment.of(buildReadOnlyExtension(this.getReadOnly())),\n this._themeCompartment.of(codeEditorTheme(dark)),\n this._langCompartment.of([]),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n this._options.value = update.state.doc.toString();\n this.emit(\"change\", { value: this._options.value });\n }\n }),\n EditorView.domEventHandlers({\n // A user edit while read-only is blocked by `EditorState.readOnly`\n // at the input layer — it never becomes a transaction, so it emits\n // no \"change\". These hooks surface the rejection as feedback:\n // typing / IME / delete fire `beforeinput`, while paste and drop are\n // prevented before `beforeinput` runs, so each needs its own hook.\n beforeinput: () => this.onEditIntent(),\n paste: () => this.onEditIntent(),\n drop: () => this.onEditIntent(),\n }),\n ];\n\n const state = EditorState.create({ doc: this._options.value ?? \"\", extensions });\n\n this._view = DOM.sink.mountView(element, (parent) => new EditorView({ parent, state }));\n\n if (this._view) {\n this.mountFlashOverlay(element);\n // Claim wheels CodeMirror's own scroller will act on, so an\n // enclosing overlay's wheel trap leaves this editor's native scroll\n // alone (see claimScrollableWheel). Descendant-first subtree\n // dispatch reaches this editor before the overlay, so the claim wins.\n Event.addSubtreeListener(this, \"wheel\", this._onWheelClaim, { passive: false });\n }\n\n const language = this.getLanguage();\n\n if (language) {\n this.setLanguage(language);\n }\n }\n\n /**\n * Creates the read-only rejection wash: a pointer-transparent overlay that\n * fills the editor box and sits above CodeMirror's content, appended after\n * the view so it paints on top. It rests at `opacity: 0`; {@link CodeEditor.flashReadOnly}\n * pulses it. Tracked as an owned handle, so the component's teardown releases it.\n *\n * @param host - The mounted editor element the overlay fills.\n */\n private mountFlashOverlay(host: Handle): void {\n const overlay = this.trackHandle(DOM.sink.createElement(\"div\"));\n\n DOM.sink.apply(overlay, {\n style: {\n position: \"absolute\",\n inset: \"0\",\n pointerEvents: \"none\",\n // Above CodeMirror's sticky line-number gutter (z-index 200) so the\n // wash covers the gutter too; `.cm-editor` forms no stacking context,\n // so the gutter would otherwise paint over a lower overlay.\n zIndex: \"300\",\n backgroundColor: READONLY_FLASH_COLOR,\n opacity: \"0\",\n },\n });\n DOM.sink.appendChild(host, overlay);\n\n this._flashOverlay = overlay;\n }\n\n /**\n * Reconfigures the theme compartment when the project theme changes, so\n * the editor's chrome + syntax colours follow a `ThemeManager.setTheme`\n * toggle with no rebuild. No-ops before the view exists.\n */\n private onThemeChange(): void {\n if (!this._view) {\n return;\n }\n\n const dark = ThemeManager.getTheme().colorScheme === \"dark\";\n\n this._view.dispatch({ effects: this._themeCompartment.reconfigure(codeEditorTheme(dark)) });\n }\n\n /**\n * DOM-event hook for the read-only rejection feedback, wired to\n * `beforeinput` / `paste` / `drop` in {@link CodeEditor.mount}. When the\n * editor is read-only it surfaces the attempt via\n * {@link CodeEditor.signalReadOnlyEdit}; it never consumes the event, so\n * CodeMirror's own `EditorState.readOnly` handling still blocks the edit.\n *\n * @returns Always `false` (event left unhandled).\n */\n private onEditIntent(): boolean {\n if (this.getReadOnly()) {\n this.signalReadOnlyEdit();\n }\n\n return false;\n }\n\n /**\n * Emits the `\"readonlyedit\"` event and plays the rejection flash — a\n * programmatic hook plus a built-in visual cue that an edit was refused\n * because the editor is read-only.\n */\n private signalReadOnlyEdit(): void {\n this.emit(\"readonlyedit\");\n this.flashReadOnly();\n }\n\n /**\n * Flashes a brief error-coloured wash over the editor as feedback for a\n * rejected edit: fades the overlay from a subtle tint back to transparent.\n * No-op before the overlay is mounted (offline / pre-mount), and — via\n * {@link Animation.play} — honours `prefers-reduced-motion`: under reduced\n * motion the `\"readonlyedit\"` event still fires but nothing animates.\n *\n * A full-box wash is used rather than a border/ring cue because the editor\n * is often large and mostly empty, so an edge flash sits far from where the\n * eye is (the caret) and reads as nothing; the wash covers the content the\n * user is looking at while staying faint enough to keep the text legible.\n */\n private flashReadOnly(): void {\n if (!this._flashOverlay) {\n return;\n }\n\n Animation.play(this._flashOverlay, {\n from: { opacity: String(READONLY_FLASH_PEAK_OPACITY) },\n to: { opacity: \"0\" },\n durationMs: READONLY_FLASH_MS,\n properties: [\"opacity\"],\n });\n }\n}\n\nconst CodeEditorCallable = callable(CodeEditor);\ntype CodeEditorCallable = CodeEditor;\nexport {\n CodeEditor as _CodeEditor,\n CodeEditorCallable as CodeEditor,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ElementFormatType } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString } from \"@lexical/markdown\";\nimport type { MultilineElementTransformer, Transformer } from \"@lexical/markdown\";\nimport {\n $createTableNode, $createTableRowNode, $createTableCellNode,\n $isTableNode, $isTableRowNode, $isTableCellNode,\n TableNode, TableRowNode, TableCellNode, TableCellHeaderStates,\n} from \"@lexical/table\";\nimport type { TableRowNode as TableRowNodeType } from \"@lexical/table\";\n\n/**\n * A candidate table row: any line containing a pipe. Deliberately loose — a\n * line only becomes a table once the next line is a matching delimiter row,\n * which {@link createTableTransformer}'s `handleImportAfterStartMatch` checks.\n * Leading and trailing pipes are optional in GFM, so they cannot be part of\n * this test.\n */\nconst TABLE_ROW_REG_EXP = /\\|/;\n\n/**\n * Splits one row into its cell texts. Drops one optional leading and one\n * optional trailing pipe, splits on unescaped `|` only, turns each `\\|` back\n * into a literal `|`, and trims each cell. A manual scan rather than a regexp\n * with a lookbehind — the same choice `@lexical/markdown`'s own\n * `isTableRowDivider` makes, since a nested-quantifier lookbehind pattern can\n * run in super-linear time on a backtracking regexp engine.\n *\n * @param line - One raw table-row line (with or without surrounding pipes).\n * @returns The row's cell texts, trimmed and unescaped.\n *\n * @example\n * ```\n * splitTableRow(\"| a | b \\\\| c |\") // -> [\"a\", \"b | c\"]\n * splitTableRow(\"a | b\") // -> [\"a\", \"b\"]\n * ```\n */\nfunction splitTableRow(line: string): string[] {\n let trimmed = line.trim();\n\n if (trimmed.startsWith(\"|\")) {\n trimmed = trimmed.slice(1);\n }\n\n if (trimmed.endsWith(\"|\") && !trimmed.endsWith(\"\\\\|\")) {\n trimmed = trimmed.slice(0, -1);\n }\n\n const cells: string[] = [];\n let current = \"\";\n\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n\n if (char === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n } else if (char === \"|\") {\n cells.push(current.trim());\n current = \"\";\n } else {\n current += char;\n }\n }\n\n cells.push(current.trim());\n\n return cells;\n}\n\n/**\n * Reads a GFM delimiter row's per-column alignment, or `null` when the line\n * is not a delimiter row (any segment isn't `-`s optionally bounded by `:`).\n *\n * @param line - The candidate delimiter-row line.\n * @returns One {@link ElementFormatType} per column, or `null` when `line` is\n * not a valid delimiter row.\n *\n * @example\n * ```\n * parseDelimiterRow(\"| :--- | :---: | ---: | --- |\") // -> [\"left\", \"center\", \"right\", \"\"]\n * parseDelimiterRow(\"| a | b |\") // -> null\n * ```\n */\nfunction parseDelimiterRow(line: string): ElementFormatType[] | null {\n const segments = splitTableRow(line);\n const alignments: ElementFormatType[] = [];\n\n for (const segment of segments) {\n if (!/^:?-+:?$/.test(segment)) {\n return null;\n }\n\n const left = segment.startsWith(\":\");\n const right = segment.endsWith(\":\");\n\n if (left && right) {\n alignments.push(\"center\");\n } else if (left) {\n alignments.push(\"left\");\n } else if (right) {\n alignments.push(\"right\");\n } else {\n alignments.push(\"\");\n }\n }\n\n return alignments;\n}\n\n/**\n * Renders a delimiter row from the header cells' formats.\n *\n * @param alignments - One {@link ElementFormatType} per column.\n * @returns The rendered delimiter-row line.\n *\n * @example\n * ```\n * formatDelimiterRow([\"left\", \"center\", \"right\", \"\"]) // -> \"| :--- | :---: | ---: | --- |\"\n * ```\n */\nfunction formatDelimiterRow(alignments: ElementFormatType[]): string {\n const cells = alignments.map((alignment) => {\n switch (alignment) {\n case \"left\": return \":---\";\n case \"center\": return \":---:\";\n case \"right\": return \"---:\";\n default: return \"---\";\n }\n });\n\n return \"| \" + cells.join(\" | \") + \" |\";\n}\n\n/**\n * Prepares a cell's Markdown for embedding in a pipe row: trims it, escapes\n * every `|` to `\\|`, and replaces every newline with a literal `\\n`.\n *\n * @param markdown - The cell's exported Markdown.\n * @returns The pipe-row-safe cell text.\n */\nfunction escapeCellText(markdown: string): string {\n return markdown.trim().replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n}\n\n/**\n * Builds the GFM pipe-table transformer: a `MultilineElementTransformer` that\n * consumes a whole table block (header, delimiter, and body rows) in one pass\n * — the same `handleImportAfterStartMatch` hook the curated `CODE` transformer\n * uses for fenced blocks — so it can require a delimiter row before\n * committing, rather than the row-at-a-time shape Lexical's own playground\n * `TABLE` transformer uses.\n *\n * @param getTransformers - Returns the curated transformer array. Called at\n * import/export time, not at construction time, so the array may reference\n * the transformer this call returns (avoiding a module import cycle with\n * the file that builds that array).\n * @returns The table transformer, ready to include in a transformer array.\n */\nexport function createTableTransformer(getTransformers: () => Transformer[]): MultilineElementTransformer {\n return {\n dependencies: [TableNode, TableRowNode, TableCellNode],\n regExpStart: TABLE_ROW_REG_EXP,\n type: \"multiline-element\",\n\n handleImportAfterStartMatch: ({ lines, rootNode, startLineIndex }) => {\n const delimiterLine = lines[startLineIndex + 1];\n\n if (delimiterLine === undefined) {\n return null;\n }\n\n const alignments = parseDelimiterRow(delimiterLine);\n\n if (alignments === null) {\n return null;\n }\n\n const headerCells = splitTableRow(lines[startLineIndex]);\n const columnCount = headerCells.length;\n\n if (alignments.length !== columnCount) {\n return null;\n }\n\n const rows: string[][] = [headerCells];\n let lastConsumedLineIndex = startLineIndex + 1;\n\n // marked ends a table only at a blank line (or end of input), so a\n // following prose line with no pipe in it is absorbed as a\n // one-cell row rather than ending the table here.\n for (let lineIndex = startLineIndex + 2; lineIndex < lines.length; lineIndex += 1) {\n const line = lines[lineIndex];\n\n if (line.trim() === \"\") {\n break;\n }\n\n rows.push(splitTableRow(line));\n lastConsumedLineIndex = lineIndex;\n }\n\n const table = $createTableNode();\n\n rows.forEach((cells, rowIndex) => {\n const row = $createTableRowNode();\n\n for (let column = 0; column < columnCount; column += 1) {\n const cell = $createTableCellNode(\n rowIndex === 0 ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS);\n\n $convertFromMarkdownString(\n (cells[column] ?? \"\").replace(/\\\\n/g, \"\\n\"), getTransformers(), cell);\n\n // AFTER the conversion: it clears the cell's children, and\n // setting the format first risks the clear taking the\n // format with it.\n cell.setFormat(alignments[column] ?? \"\");\n row.append(cell);\n }\n\n table.append(row);\n });\n\n rootNode.append(table);\n\n return [true, lastConsumedLineIndex];\n },\n\n // Never reached: handleImportAfterStartMatch either imports the block or\n // declines it, so the default multiline import path never runs.\n replace: () => false,\n\n export: (node) => {\n if (!$isTableNode(node)) {\n return null;\n }\n\n const rows = node.getChildren().filter($isTableRowNode);\n\n if (rows.length === 0) {\n return null;\n }\n\n const renderRow = (row: TableRowNodeType): string => {\n const cells = row.getChildren().filter($isTableCellNode);\n const rendered = cells.map(\n (cell) => escapeCellText($convertToMarkdownString(getTransformers(), cell)));\n\n return \"| \" + rendered.join(\" | \") + \" |\";\n };\n\n const headerRow = rows[0];\n const headerCells = headerRow.getChildren().filter($isTableCellNode);\n const alignments = headerCells.map((cell) => cell.getFormatType());\n\n const lines = [renderRow(headerRow), formatDelimiterRow(alignments)];\n\n for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {\n lines.push(renderRow(rows[rowIndex]));\n }\n\n return lines.join(\"\\n\");\n },\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport {\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n} from \"@lexical/markdown\";\nimport type { Transformer } from \"@lexical/markdown\";\nimport { createTableTransformer } from \"~/component/editor/markdownTableTransformer.js\";\n\n// Lazy: called at import/export time rather than passed by value, so this\n// module can build TABLE from the very array it is about to join, with no\n// import cycle back into this file.\nconst TABLE = createTableTransformer(() => TRANSFORMERS);\n\n/**\n * The curated Markdown transformer array that defines `MarkdownEditor`'s\n * dialect — the exact subset of Markdown the read-only `Markdown` viewer\n * renders.\n *\n * @remarks\n * This is deliberately **not** Lexical's full `TRANSFORMERS` preset. The preset\n * also carries `STRIKETHROUGH`, `HIGHLIGHT`, `CHECK_LIST`, and an image\n * transformer — constructs the viewer drops to a plain-text fallback. Curating\n * the list down to these ten is the single source of truth that guarantees the\n * editor can never emit Markdown the viewer would fail to render: the same array\n * is passed to the import converter, the export converter, and the\n * markdown-shortcut typing registration, so what the user types, what the editor\n * stores, and what the viewer reads all agree.\n *\n * The mapping to the viewer's supported tokens is:\n *\n * - `HEADING` → `#`…`######` (heading)\n * - `QUOTE` → `> ` (blockquote)\n * - `CODE` → fenced ` ``` ` block (code)\n * - `UNORDERED_LIST` → `- ` (unordered list)\n * - `ORDERED_LIST` → `1. ` (ordered list)\n * - `BOLD_STAR` → `**b**` (strong)\n * - `ITALIC_STAR` → `*i*` (em)\n * - `INLINE_CODE` → `` `c` `` (codespan)\n * - `LINK` → `[t](url)` (link)\n * - `TABLE` → `| a | b |` (table)\n *\n * Star (not underscore) emphasis variants are chosen so bold/italic export is\n * deterministic and matches the viewer's demo output.\n */\nexport const TRANSFORMERS: Transformer[] = [\n TABLE,\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { HeadingNode, QuoteNode } from \"@lexical/rich-text\";\nimport { ListNode, ListItemNode } from \"@lexical/list\";\nimport { LinkNode } from \"@lexical/link\";\nimport { CodeNode, CodeHighlightNode } from \"@lexical/code\";\nimport { TableNode, TableRowNode, TableCellNode } from \"@lexical/table\";\nimport type { Klass, LexicalNode } from \"lexical\";\n\n/**\n * The Lexical node classes registered on every `MarkdownEditor`'s editor.\n *\n * @remarks\n * A Lexical editor can only build or parse a node type whose class it was\n * created with, so this set must cover every construct the curated transformer\n * list produces: headings (`HeadingNode`), blockquotes (`QuoteNode`),\n * ordered/unordered lists (`ListNode` + `ListItemNode`), links (`LinkNode`),\n * fenced code (`CodeNode` + its `CodeHighlightNode` children), and GFM tables\n * (`TableNode` + `TableRowNode` + `TableCellNode`). The remaining dialect\n * constructs — bold, italic, inline code, paragraphs — are plain text formats\n * on the always-present built-in text/paragraph nodes and need no registration.\n */\nexport const EDITOR_NODES: ReadonlyArray<Klass<LexicalNode>> = [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n LinkNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport type { EditorThemeClasses } from \"lexical\";\n\n// Class names Lexical stamps onto the rendered nodes. Prefixed `ts-ui-mde-`\n// (markdown editor) so they never collide with the read-only `Markdown`\n// viewer's `ts-ui-md-` rules; the two components replicate rather than share\n// their class rules — the shared contract is the theme-token names below, not\n// the selectors.\nconst HEADING_CLASS = \"ts-ui-mde-heading\";\nconst QUOTE_CLASS = \"ts-ui-mde-quote\";\nconst CODE_BLOCK_CLASS = \"ts-ui-mde-code\";\nconst INLINE_CODE_CLASS = \"ts-ui-mde-inline-code\";\nconst LIST_CLASS = \"ts-ui-mde-list\";\nconst LINK_CLASS = \"ts-ui-mde-link\";\nconst BOLD_CLASS = \"ts-ui-mde-bold\";\nconst ITALIC_CLASS = \"ts-ui-mde-italic\";\nconst TABLE_CLASS = \"ts-ui-mde-table\";\nconst TABLE_ROW_CLASS = \"ts-ui-mde-table-row\";\nconst TABLE_CELL_CLASS = \"ts-ui-mde-table-cell\";\nconst TABLE_CELL_HEADER_CLASS = \"ts-ui-mde-table-cell-header\";\nconst TABLE_CELL_SELECTED_CLASS = \"ts-ui-mde-table-cell-selected\";\n\n/** Guards the module-singleton class-rule registration in {@link ensureMarkdownEditorClassRules}. */\nlet _classRulesEnsured = false;\n\n/**\n * Registers the shared class rules that style the WYSIWYG editing surface,\n * once per document.\n *\n * @remarks\n * The rules mirror the read-only `Markdown` viewer's constructs and reference\n * the **same** CSS custom-property tokens — `--ts-ui-font-mono` for code,\n * `--ts-ui-border-color` for the quote bar, `--ts-ui-indicator-focus` for links\n * — so the edited rich text looks identical to what the viewer renders (the\n * point of WYSIWYG). Because the rules resolve live CSS variables, a theme\n * toggle recolours the surface with no rebuild and the component needs no\n * theme-change subscription. Each em-relative padding/indent is genuine\n * structural spacing (a code wash, a list-marker gutter, a quote bar), scaling\n * with the surrounding font — not a cosmetic nudge.\n */\nexport function ensureMarkdownEditorClassRules(): void {\n if (_classRulesEnsured) {\n return;\n }\n\n _classRulesEnsured = true;\n\n new StyleRule({\n scope: \"class\",\n name: HEADING_CLASS,\n // Semibold so headings read as headings independent of any UA reset.\n styles: { fontWeight: \"600\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: QUOTE_CLASS,\n styles: {\n // 3px quote bar — the framework's thin-border weight — plus an\n // em-relative gutter indenting the quoted prose off the bar.\n borderLeft: \"3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n paddingLeft: \"1em\",\n marginLeft: \"0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: CODE_BLOCK_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n // Translucent grey wash reads as \"code\" on light and dark alike.\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Structural inset giving the fenced code frame room.\n padding: \"0.6em 0.8em\",\n whiteSpace: \"pre-wrap\",\n // Lexical renders a fenced block as <code>, which is inline by\n // default and so sits on a text baseline — it rides up into a\n // preceding block instead of stacking below it. The viewer's\n // fenced code is a <pre>, a block with the browser's 1em block\n // margin; both are restated here so the two surfaces stack alike.\n display: \"block\",\n margin: \"1em 0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: INLINE_CODE_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Snug padding so the wash hugs the inline-code glyphs.\n padding: \"0.1em 0.3em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LIST_CLASS,\n // Room for the list marker; structural, em-relative indentation.\n styles: { paddingLeft: \"1.5em\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LINK_CLASS,\n // The framework's single accent hue (shared with focus/selection).\n styles: { color: \"var(--ts-ui-indicator-focus, #2563eb)\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: BOLD_CLASS,\n styles: { fontWeight: \"700\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: ITALIC_CLASS,\n styles: { fontStyle: \"italic\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CLASS,\n styles: { borderCollapse: \"collapse\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n // Structural cell padding, matching the read-only viewer's cells.\n padding: \"0.3em 0.6em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_HEADER_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n padding: \"0.3em 0.6em\",\n fontWeight: \"600\",\n },\n });\n\n new StyleRule({\n scope: \"selector\",\n name: `.${TABLE_CELL_CLASS} > p`,\n // Lexical wraps each cell's content in a paragraph, which would carry\n // the browser's default 1em block margin and make every editor cell\n // two line-heights taller than the same cell in the read-only viewer,\n // whose cells hold inline content directly. Only cell paragraphs are\n // reset: prose paragraphs keep their margin, which is what matches the\n // viewer outside a table.\n styles: { margin: \"0\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_SELECTED_CLASS,\n // @lexical/table stamps this class onto cells caught in a drag-selected\n // range; reuses the data-grid Table component's own row-selected token\n // so a selected cell reads the same way here as it does there.\n styles: { backgroundColor: \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\" },\n });\n}\n\n/**\n * The Lexical class-name theme map linking each node type to its shared class\n * rule from {@link ensureMarkdownEditorClassRules}.\n *\n * @remarks\n * Every heading level maps to the one heading rule (the tag itself carries the\n * size); ordered and unordered lists share the indentation rule. Bold, italic,\n * and inline code are text-format entries, and links carry the accent colour.\n */\nexport const EDITOR_THEME: EditorThemeClasses = {\n heading: {\n h1: HEADING_CLASS,\n h2: HEADING_CLASS,\n h3: HEADING_CLASS,\n h4: HEADING_CLASS,\n h5: HEADING_CLASS,\n h6: HEADING_CLASS,\n },\n quote: QUOTE_CLASS,\n code: CODE_BLOCK_CLASS,\n list: {\n ul: LIST_CLASS,\n ol: LIST_CLASS,\n },\n text: {\n bold: BOLD_CLASS,\n italic: ITALIC_CLASS,\n code: INLINE_CODE_CLASS,\n },\n link: LINK_CLASS,\n table: TABLE_CLASS,\n tableRow: TABLE_ROW_CLASS,\n tableCell: TABLE_CELL_CLASS,\n tableCellHeader: TABLE_CELL_HEADER_CLASS,\n tableCellSelected: TABLE_CELL_SELECTED_CLASS,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CodeEditor } from \"~/component/editor/CodeEditor.js\";\nimport type { CodeEditorChange } from \"~/component/editor/CodeEditor.js\";\nimport { createEditor, FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection, $getRoot, $createParagraphNode } from \"lexical\";\nimport type { LexicalEditor, ElementNode } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString, registerMarkdownShortcuts } from \"@lexical/markdown\";\nimport { registerRichText, $createHeadingNode, $createQuoteNode } from \"@lexical/rich-text\";\nimport type { HeadingTagType } from \"@lexical/rich-text\";\nimport { registerList, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND } from \"@lexical/list\";\nimport { $toggleLink } from \"@lexical/link\";\nimport { $createCodeNode } from \"@lexical/code\";\nimport { registerHistory, createEmptyHistoryState } from \"@lexical/history\";\nimport {\n registerTablePlugin, registerTableCellUnmergeTransform, registerTableSelectionObserver,\n $getTableCellNodeFromLexicalNode,\n $insertTableRowAtSelection, $deleteTableRowAtSelection,\n $insertTableColumnAtSelection, $deleteTableColumnAtSelection,\n INSERT_TABLE_COMMAND,\n} from \"@lexical/table\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { $setBlocksType } from \"@lexical/selection\";\nimport { TRANSFORMERS } from \"~/component/editor/markdownTransformers.js\";\nimport { EDITOR_NODES } from \"~/component/editor/editorNodes.js\";\nimport { EDITOR_THEME, ensureMarkdownEditorClassRules } from \"~/component/editor/editorTheme.js\";\n\n/**\n * The coalescing window (ms) for the undo/redo history: edits within this gap\n * merge into a single undo step, matching Lexical's own recommended default.\n */\nconst HISTORY_DELAY_MS = 300;\n\n/**\n * Payload of {@link MarkdownEditor}'s `\"change\"` event: the Markdown string\n * after the edit that triggered it.\n *\n * @category Components\n */\nexport interface MarkdownEditorChange {\n value: string;\n}\n\n/** The event {@link MarkdownEditor} exposes through its custom `on` / `off` surface. */\ntype MarkdownEditorEvent = \"change\";\n\n/**\n * The editing surface a {@link MarkdownEditor} currently shows: the WYSIWYG\n * rich-text surface, or the raw-Markdown source [`CodeEditor`](/api/component/editor/classes/CodeEditor).\n *\n * @category Components\n */\nexport type MarkdownEditorMode = \"wysiwyg\" | \"source\";\n\n/**\n * A block type accepted by {@link MarkdownEditor.setBlockType}: a paragraph, one\n * of the six heading levels, a blockquote, or a fenced code block.\n *\n * @category Components\n */\nexport type MarkdownBlockType = \"paragraph\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"quote\" | \"code\";\n\n/**\n * Construction-time options for {@link MarkdownEditor}.\n *\n * @category Components\n */\nexport interface MarkdownEditorOptions extends ComponentOptions {\n /** Initial Markdown source (also accepted as the positional first argument). */\n value?: string;\n /** Whether the editor is read-only. Default `false`. */\n readOnly?: boolean;\n /** Which surface is shown. Default `\"wysiwyg\"`. */\n mode?: MarkdownEditorMode;\n /** Construction-time listener bag; the only event is `\"change\"`. */\n listeners?: { change?: (payload: MarkdownEditorChange) => void };\n}\n\n/**\n * The `onError` handler for the Lexical editor: rethrows so a node/transform\n * fault surfaces loudly rather than being swallowed.\n *\n * @param error - The error Lexical reports.\n */\nfunction reportEditorError(error: Error): void {\n throw error;\n}\n\n/**\n * Builds the Lexical block node a {@link MarkdownEditor.setBlockType} call\n * converts the selected blocks into.\n *\n * @param type - The requested block type.\n * @returns A freshly-created element node of that type.\n */\nfunction createBlockNode(type: MarkdownBlockType): ElementNode {\n switch (type) {\n case \"quote\": return $createQuoteNode();\n case \"code\": return $createCodeNode();\n case \"paragraph\": return $createParagraphNode();\n default: return $createHeadingNode(type satisfies HeadingTagType);\n }\n}\n\n/**\n * Whether the caret currently sits inside a table cell — the precondition for\n * the row/column helpers, which throw rather than no-op when it does not.\n *\n * @returns Whether the current selection is a range selection anchored inside\n * a table cell.\n */\nfunction $selectionIsInTableCell(): boolean {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n return $getTableCellNodeFromLexicalNode(selection.anchor.getNode()) !== null;\n}\n\n/**\n * The private WYSIWYG editing surface: the element Lexical's `contenteditable`\n * view mounts into.\n *\n * @remarks\n * Split out from {@link MarkdownEditor} so the coordinator's own element can host\n * *both* this surface and the source {@link CodeEditor} as two [`Card`](/api/layout/classes/Card)\n * children — one DOM element per class, since Lexical's `contenteditable` and\n * CodeMirror's view are two foreign live widgets that each need their own host\n * element. `MarkdownEditor` keeps ownership of the Lexical editor object; only\n * the mount element (and its `contenteditable` / caret plumbing) lives here.\n */\nclass WysiwygSurface extends Component {\n\n /** Whether this surface's element hosts an editable region (`contenteditable`). */\n private _contentEditable: boolean = false;\n\n /** Invoked on the surface's first connected + sized layout — the moment to mount the live view. */\n private readonly _onReady: () => void;\n\n /**\n * Constructs the WYSIWYG surface.\n *\n * @param onReady - Called on the surface's first connected + sized layout.\n */\n constructor(onReady: () => void) {\n super();\n\n this._onReady = onReady;\n\n this.setContentEditable(true);\n // Fills its host box and scrolls internally when the document overflows.\n this.setOverflow(\"auto\");\n // A few pixels of inset off the padding box: without it the caret sits\n // flush against the surface's left edge (at column 0 of any line) and\n // gets clipped to invisibility. The padding is on the scroll container,\n // so it clears the caret at every edge while overflow scrolling still\n // reveals the full document. The values match source mode's CodeMirror\n // text inset so toggling modes doesn't shift the prose: 4px vertical\n // (CodeMirror's `.cm-content` padding is `4px 0`) and 6px horizontal\n // (its `.cm-line` padding-left is 6px).\n this.setPadding(new Insets(4, 6, 4, 6));\n // Text caret over the whole surface: signals editability, and — paired\n // with the `user-select: text` Lexical stamps on the root — keeps the\n // surface select-and-copy-able even in read-only mode. `setCursor` caches\n // in `_options.cursor`, which `applyStyle` replays, so it survives both\n // detached construction and Lexical's mount.\n this.setCursor(\"text\");\n\n this.onFirstLayout(() => this._onReady());\n }\n\n /**\n * Replays the cached `contenteditable` state onto the freshly-created\n * element. `setContentEditable` (called during detached construction, before\n * an element exists) caches into `_contentEditable` but its underlying\n * `setElementAttribute` is write-through only, so the constructor-time write\n * is dropped; without this replay the mounted element never becomes editable\n * and the WYSIWYG view silently behaves read-only.\n *\n * The write targets the `element` handle directly — as the base class does\n * for its own cached attributes — because at init time the element is not yet\n * in the document, so a `getElement()`-based setter would miss it.\n *\n * @param element - Optional element to initialise; falls back to `getElement()`.\n * @returns This surface, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element ?? this.getElement();\n\n if (el) {\n DOM.sink.apply(el, { setAttr: { contenteditable: this._contentEditable ? \"true\" : \"false\" } });\n }\n\n return this;\n }\n\n /**\n * Sets whether this surface's element hosts an editable region. Caches the\n * state in `_contentEditable` and writes the `contenteditable` attribute\n * through to the element. The underlying `setElementAttribute` is\n * write-through only, so a call made during detached construction (before the\n * element exists) is a no-op on the DOM; {@link WysiwygSurface.init} replays\n * the cached state onto the element once it is created.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This surface, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._contentEditable = contentEditable;\n this.setElementAttribute(\"contenteditable\", contentEditable ? \"true\" : \"false\");\n\n return this;\n }\n\n /**\n * Returns whether this surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._contentEditable;\n }\n\n /**\n * Mounts the given Lexical editor's editable view into this surface's element\n * through the DOM seam's `mountView` escape. No-ops when the view is already\n * mounted, the element is not yet available, or offline (the seam returns\n * `null`, leaving the editor headless).\n *\n * @param editor - The Lexical editor whose view to mount here.\n */\n mount(editor: LexicalEditor): void {\n if (editor.getRootElement()) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n ensureMarkdownEditorClassRules();\n\n // The factory parameter is left unannotated: its `HTMLElement` type is\n // inferred from the seam signature, so no DOM type is named here and the\n // `no-raw-dom` *hold* clause stays green. `setRootElement` accepts the\n // element structurally.\n DOM.sink.mountView(element, (root) => { editor.setRootElement(root); return root; });\n }\n}\n\n/**\n * A WYSIWYG rich-text editor whose public value is a Markdown string, with an\n * optional raw-Markdown source mode.\n *\n * @remarks\n * `MarkdownEditor` edits a document as rendered rich text — no visible markup —\n * and reads/writes Markdown through Lexical's bidirectional converters, so it is\n * the editing counterpart to the read-only\n * [`Markdown`](/api/component/display/classes/Markdown) viewer. Its dialect is\n * deliberately the **exact subset** the viewer renders (headings, paragraphs,\n * bold, italic, inline code, ordered/unordered lists, blockquotes, fenced code,\n * links, and GFM pipe tables with per-column alignment); a curated transformer\n * list — not Lexical's full preset — guarantees the editor can never emit\n * Markdown the viewer would drop to plain text, so an edited document renders\n * identically in the viewer.\n *\n * Formatting is driven three ways, all without a built-in toolbar: Markdown\n * shortcut typing (`# ` → heading, `**b**` → bold, `- ` → list, `> ` → quote,\n * ` ``` ` → code), the default keyboard shortcuts (Ctrl/Cmd+B / +I, undo/redo),\n * and a thin imperative command API (`toggleBold`, `setBlockType`,\n * `toggleUnorderedList`, `toggleLink`, `insertTable`,\n * `insertTableRow`/`deleteTableRow`, `insertTableColumn`/`deleteTableColumn`,\n * …) a consumer can wire to their own `Button`s.\n *\n * A {@link MarkdownEditor.setMode | mode} (`\"wysiwyg\"` | `\"source\"`) swaps the\n * editing surface between that rich-text view and a raw-Markdown\n * [`CodeEditor`](/api/component/editor/classes/CodeEditor); both are bound to the\n * same Markdown value, so `getValue`/`setValue` and the `\"change\"` event behave\n * identically in either mode and switching modes preserves the document. Like the\n * command API, the mode toggle is **consumer-wired** — there is no built-in\n * chrome — so a consumer drives `setMode` from their own control.\n *\n * Internally the component's own element hosts two swappable child surfaces\n * through a [`Card`](/api/layout/classes/Card) layout: a private WYSIWYG surface owning Lexical's\n * `contenteditable` element (a *foreign live widget*, like the `EditorView`\n * behind `CodeEditor`) and the source `CodeEditor`. Lexical separates its editor\n * **state** (a pure, DOM-free immutable tree) from that view, so the Markdown\n * value get/set/round-trip runs headless: offline neither view attaches, yet\n * `getValue` / `setValue`, the command API, and mode switching still operate on\n * the state. The editor fills its host box and scrolls internally; give it a\n * sized host (a `Fit` panel or an explicit `preferredSize`), the same as\n * `CodeEditor`.\n *\n * @example\n * ```typescript\n * import { MarkdownEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new MarkdownEditor('# Title\\n\\nSome **bold** text.');\n * panel.addComponent(editor);\n * editor.on('change', ({ value }) => console.log(value));\n * editor.setMode('source'); // switch to raw-Markdown editing\n * ```\n *\n * @category Components\n */\nclass MarkdownEditor extends Component<MarkdownEditorOptions> {\n\n /**\n * The Lexical editor, built lazily (headless) on first use or first layout;\n * `null` until then. The editor is DOM-free until its root element mounts,\n * so this is non-null and fully usable offline once built.\n */\n private _editor: LexicalEditor | null = null;\n\n /** The `mergeRegister` teardown for the rich-text / list / history / shortcut / update registrations. */\n private _unregister: (() => void) | null = null;\n\n /**\n * The teardown for {@link registerTableSelectionObserver}, registered only\n * once the WYSIWYG view actually mounts (its mutation listener needs a real\n * table element in the document) and cleared on {@link dispose}.\n */\n private _unregisterTableView: (() => void) | null = null;\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<MarkdownEditorEvent> = new ListenerBag<MarkdownEditorEvent>();\n\n /** The Card layout swapping the visible editing surface between WYSIWYG and source. */\n private readonly _card: Card;\n\n /** The WYSIWYG surface hosting Lexical's `contenteditable` view. */\n private readonly _wysiwyg: WysiwygSurface;\n\n /** The raw-Markdown source surface, shown in `\"source\"` mode. */\n private readonly _codeEditor: CodeEditor;\n\n /**\n * Constructs a Markdown editor.\n *\n * @param value - Initial Markdown source (optional; defaults to `\"\"`).\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: MarkdownEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n // The component's own element hosts two swappable surfaces via a Card\n // (one DOM element per class): the WYSIWYG contenteditable and the source\n // CodeEditor. The Card shows exactly one at a time.\n this._card = new Card();\n this.setLayoutManager(this._card);\n\n this._wysiwyg = new WysiwygSurface(() => this.mountWysiwyg());\n this._codeEditor = new CodeEditor(this._options.value ?? \"\", {\n language: \"markdown\",\n readOnly: this._options.readOnly ?? false,\n listeners: { change: (payload) => this.handleCodeChange(payload) },\n });\n\n this.addComponent(this._wysiwyg);\n this.addComponent(this._codeEditor);\n\n // Pick the initial visible child from the mode.\n this._card.setVisibleComponentId(\n this.getMode() === \"source\" ? this._codeEditor.getId() : this._wysiwyg.getId());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Caches the `value` / `readOnly` / `mode` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. The editor\n * is built later (lazily), so these are pure caches applied to the freshly-\n * created editor state / surfaces in the constructor and `ensureEditor`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MarkdownEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n if (options.mode !== undefined) this._options.mode = options.mode;\n\n return this;\n }\n\n /**\n * Returns the active editing surface mode.\n *\n * @returns `\"source\"` when the raw-Markdown surface is shown, else `\"wysiwyg\"`.\n */\n getMode(): MarkdownEditorMode {\n return this._options.mode ?? \"wysiwyg\";\n }\n\n /**\n * Switches the active editing surface. Converts the current document across\n * the surfaces so no edits are lost — the outgoing surface's Markdown is read\n * (via {@link MarkdownEditor.getValue}) before the mode flips, then loaded\n * into the incoming surface — and swaps the visible [`Card`](/api/layout/classes/Card) child.\n * No-op (no conversion, no `\"change\"`) when already in `mode`.\n *\n * @param mode - The surface to show.\n * @returns This component, for method chaining.\n */\n setMode(mode: MarkdownEditorMode): this {\n if (this.getMode() === mode) {\n return this;\n }\n\n // Read the canonical Markdown from the OUTGOING surface before the flip.\n const markdown = this.getValue();\n this._options.mode = mode;\n\n if (mode === \"source\") {\n this._codeEditor.setValue(markdown);\n this._card.setVisibleComponentId(this._codeEditor.getId());\n } else {\n this.ensureEditor().update(\n () => $convertFromMarkdownString(markdown, TRANSFORMERS), { discrete: true });\n this._card.setVisibleComponentId(this._wysiwyg.getId());\n }\n\n return this;\n }\n\n /**\n * Returns the current document as a Markdown string, from whichever surface\n * is active: the raw source text in `\"source\"` mode, else converted live from\n * the Lexical editor state (or the cached pre-mount / offline value).\n *\n * @returns The Markdown source, or `\"\"` when unset.\n */\n getValue(): string {\n if (this.getMode() === \"source\") {\n return this._codeEditor.getValue();\n }\n\n const editor = this._editor;\n\n if (editor) {\n return editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document from a Markdown string, targeting the active\n * surface: the source `CodeEditor` in `\"source\"` mode, else the Lexical\n * editor (building it if needed). A resulting content change fires `\"change\"`.\n *\n * @param value - The new Markdown source.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.setValue(value);\n\n return this;\n }\n\n // Discrete forces a synchronous commit, so the resulting `\"change\"`\n // fires before this returns rather than on a later flush.\n this.ensureEditor().update(() => $convertFromMarkdownString(value, TRANSFORMERS), { discrete: true });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The read-only state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits, on **both** surfaces. Caches the\n * state; when the Lexical editor exists, toggles its editable flag (which\n * also drops the caret / editing affordances on the mounted view); and\n * forwards to the source `CodeEditor`.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n this._editor?.setEditable(!readOnly);\n this._codeEditor.setReadOnly(readOnly);\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the active editing surface, placing a caret\n * ready for typing: the Lexical contenteditable in WYSIWYG mode (building\n * and mounting the editor if needed), or the source `CodeEditor` in\n * `\"source\"` mode.\n *\n * Overrides {@link core!Component.focus}, whose default focuses this component's\n * own host element — the `Card` wrapper — which would leave the caret out of\n * the nested contenteditable the typing must land in. In WYSIWYG mode it\n * defers to Lexical's own `focus`, which both focuses the root element and\n * sets a selection so a caret appears without a click.\n *\n * @param preventScroll - Forwarded to the source `CodeEditor` in `\"source\"`\n * mode; ignored in WYSIWYG mode, where Lexical manages its own focus scroll.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.focus(preventScroll);\n } else {\n this.ensureEditor().focus();\n }\n\n return this;\n }\n\n /**\n * Toggles bold on the current selection. No-op (without throwing) when there\n * is no range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleBold(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"bold\");\n\n return this;\n }\n\n /**\n * Toggles italic on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleItalic(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"italic\");\n\n return this;\n }\n\n /**\n * Toggles inline code on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleInlineCode(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"code\");\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an unordered (bulleted) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleUnorderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an ordered (numbered) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleOrderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Wraps the current selection in a link to `url`, or unwraps it when `url`\n * is `null`. No-op without a range selection.\n *\n * @param url - The link target, or `null` to remove the link.\n * @returns This component, for method chaining.\n */\n toggleLink(url: string | null): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $toggleLink(url);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Converts the selected blocks to `type` (paragraph, `h1`–`h6`, blockquote,\n * or fenced code). No-op without a range selection.\n *\n * @param type - The target block type.\n * @returns This component, for method chaining.\n */\n setBlockType(type: MarkdownBlockType): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => createBlockNode(type));\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a table of `rows` rows by `columns` columns at the caret. The\n * first row is the header row, so `insertTable(2, 3)` gives a header row\n * plus one body row. The caret lands in the first header cell.\n *\n * @param rows - The number of rows, including the header row.\n * @param columns - The number of columns.\n * @returns This component, for method chaining.\n */\n insertTable(rows: number, columns: number): this {\n const editor = this.ensureEditor();\n\n // INSERT_TABLE_COMMAND inserts at the selection; a freshly built editor\n // may have none, so seed one first.\n editor.update(() => {\n if (!$isRangeSelection($getSelection())) {\n $getRoot().selectEnd();\n }\n }, { discrete: true });\n\n editor.dispatchCommand(INSERT_TABLE_COMMAND, {\n columns: String(columns),\n rows: String(rows),\n includeHeaders: { rows: true, columns: false },\n });\n\n return this;\n }\n\n /**\n * Inserts a row after (default) or before the row holding the caret.\n * No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current row.\n * @returns This component, for method chaining.\n */\n insertTableRow(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableRowAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the row holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableRow(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableRowAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a column after (default) or before the column holding the\n * caret. No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current column.\n * @returns This component, for method chaining.\n */\n insertTableColumn(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableColumnAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the column holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableColumn(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableColumnAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the document\n * content changes (typing, a command, or {@link MarkdownEditor.setValue}) in\n * whichever surface is active.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new Markdown value.\n * @returns This component, for method chaining.\n */\n on(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans the `\"change\"` event out to its registered listeners.\n *\n * @param event - Must be `\"change\"`.\n * @param payload - The event payload.\n */\n protected emit(event: MarkdownEditorEvent, payload: MarkdownEditorChange): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Sets whether the WYSIWYG surface's element hosts an editable region.\n * Forwards to the private surface, preserving the public setter.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This component, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._wysiwyg.setContentEditable(contentEditable);\n\n return this;\n }\n\n /**\n * Returns whether the WYSIWYG surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._wysiwyg.getContentEditable();\n }\n\n /**\n * Detaches the Lexical registrations and both surfaces' live views. Call\n * before discarding a dynamically-built `MarkdownEditor`, mirroring\n * `CodeEditor.dispose`.\n */\n dispose(): void {\n this._unregisterTableView?.();\n this._unregister?.();\n this._editor?.setRootElement(null);\n this._codeEditor.dispose();\n }\n\n /**\n * Builds the headless Lexical editor on first use. Idempotent: the editor is\n * built once. Offline the editor stays headless — the state, and every value\n * / command operation, still work.\n *\n * @returns The live (headless-capable) Lexical editor.\n */\n private ensureEditor(): LexicalEditor {\n if (!this._editor) {\n const editor = createEditor({ nodes: EDITOR_NODES, theme: EDITOR_THEME, onError: reportEditorError });\n\n // Populate the initial state before registering the update listener,\n // so loading the cached value does not fire a spurious `\"change\"`.\n editor.update(() => $convertFromMarkdownString(this._options.value ?? \"\", TRANSFORMERS), { discrete: true });\n editor.setEditable(!(this._options.readOnly ?? false));\n\n this._unregister = mergeRegister(\n registerRichText(editor),\n registerList(editor),\n registerTablePlugin(editor),\n registerTableCellUnmergeTransform(editor),\n registerHistory(editor, createEmptyHistoryState(), HISTORY_DELAY_MS),\n registerMarkdownShortcuts(editor, TRANSFORMERS),\n editor.registerUpdateListener(() => this.handleChange()),\n );\n\n this._editor = editor;\n }\n\n return this._editor;\n }\n\n /**\n * Builds the headless editor (if needed) and mounts its live view into the\n * WYSIWYG surface. Driven by the surface's first connected layout, i.e. the\n * first time it is the visible `Card` child and gets sized.\n */\n private mountWysiwyg(): void {\n const editor = this.ensureEditor();\n\n this._wysiwyg.mount(editor);\n\n // Offline the seam's mountView returns null and the root stays unset;\n // the observer's mutation listener needs a real table element in the\n // document and throws without one, so it is view-time only.\n if (editor.getRootElement() && !this._unregisterTableView) {\n this._unregisterTableView = registerTableSelectionObserver(editor, true);\n }\n }\n\n /**\n * Recomputes the Markdown value from the committed editor state after a\n * Lexical update and, when it differs from the cached value (i.e. the content\n * — not merely the selection — changed), caches it and emits `\"change\"`.\n */\n private handleChange(): void {\n const editor = this._editor;\n\n if (!editor) {\n return;\n }\n\n const value = editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n\n if (value === this._options.value) {\n return;\n }\n\n this._options.value = value;\n this.emit(\"change\", { value });\n }\n\n /**\n * Recomputes the cached value from a source-surface (`CodeEditor`) edit and,\n * when it differs from the cached value, caches it and emits `\"change\"`. The\n * equality guard makes the programmatic `setValue` on a mode switch not\n * double-emit (it loads the value already equal to `_options.value`).\n *\n * @param payload - The `CodeEditor` change payload (the new source text).\n */\n private handleCodeChange(payload: CodeEditorChange): void {\n if (payload.value === this._options.value) {\n return;\n }\n\n this._options.value = payload.value;\n this.emit(\"change\", { value: payload.value });\n }\n}\n\nconst MarkdownEditorCallable = callable(MarkdownEditor);\ntype MarkdownEditorCallable = MarkdownEditor;\nexport {\n MarkdownEditor as _MarkdownEditor,\n MarkdownEditorCallable as MarkdownEditor,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"editor.es.js","names":[],"sources":["../../../src/typescript/lib/component/editor/LanguageRegistry.ts","../../../src/typescript/lib/component/editor/formatters/prettier.ts","../../../src/typescript/lib/component/editor/formatters/sql.ts","../../../src/typescript/lib/component/editor/languages.ts","../../../src/typescript/lib/component/editor/theme.ts","../../../src/typescript/lib/component/editor/CodeEditor.ts","../../../src/typescript/lib/component/editor/markdownTableTransformer.ts","../../../src/typescript/lib/component/editor/markdownTransformers.ts","../../../src/typescript/lib/component/editor/editorNodes.ts","../../../src/typescript/lib/component/editor/editorTheme.ts","../../../src/typescript/lib/component/editor/MarkdownEditor.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Extension } from \"@codemirror/state\";\n\n/**\n * Formats `source`, returning the formatted text and a cursor offset mapped\n * (or clamped) into the new document. May return synchronously or via a\n * `Promise` — `CodeEditor.format()` awaits either.\n *\n * @param source - The document text to format.\n * @param cursorOffset - The cursor's current offset into `source`.\n * @returns The formatted text and the mapped/clamped cursor offset.\n */\nexport type Formatter = (\n source: string,\n cursorOffset: number,\n) =>\n | Promise<{ formatted: string; cursorOffset: number }>\n | { formatted: string; cursorOffset: number };\n\n/**\n * Describes one language {@link CodeEditor} can be configured with: its\n * grammar (a lazily-loaded CodeMirror `Extension`) and, optionally, its\n * formatter.\n *\n * @category Components\n */\nexport interface LanguageDefinition {\n /** The registry key passed to `CodeEditor.setLanguage` / the `language` option. */\n id: string;\n /** Human-readable name, for a consumer building a language picker. */\n label?: string;\n /** Dynamically imports and builds the CodeMirror grammar extension. */\n loadExtension: () => Promise<Extension>;\n /** Dynamically imports and builds this language's formatter, when one exists. */\n loadFormatter?: () => Promise<Formatter>;\n}\n\n/** Module-level registry, keyed by {@link LanguageDefinition.id}. */\nconst _registry = new Map<string, LanguageDefinition>();\n\n/**\n * Registers a language definition, replacing any prior registration under the\n * same {@link LanguageDefinition.id}.\n *\n * @param def - The language definition to register.\n */\nexport function registerLanguage(def: LanguageDefinition): void {\n _registry.set(def.id, def);\n}\n\n/**\n * Looks up a registered language definition by id.\n *\n * @param id - The language id.\n * @returns The definition, or `undefined` when no language is registered\n * under that id.\n */\nexport function getLanguage(id: string): LanguageDefinition | undefined {\n return _registry.get(id);\n}\n\n/**\n * Lists every currently registered language definition.\n *\n * @returns A read-only snapshot of the registered definitions.\n */\nexport function listLanguages(): readonly LanguageDefinition[] {\n return Array.from(_registry.values());\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Plugin } from \"prettier\";\nimport type { Formatter } from \"~/component/editor/LanguageRegistry.js\";\n\n/**\n * Builds a Prettier-backed {@link Formatter} for a given parser id.\n *\n * The returned closure dynamically imports `prettier/standalone` and the\n * caller-supplied plugin set on first invocation, so the (large) Prettier\n * standalone bundle only ever loads behind a `format()` call, never as part\n * of the base editor chunk. This adapter is shared by every Prettier-backed\n * language entry in `languages.ts` — each supplies its own parser id and\n * plugin loader (`babel-ts` + the babel/estree plugins for JS/TS, `json` for\n * JSON, `html` for HTML, `markdown` for Markdown).\n *\n * @param parser - The Prettier parser id (e.g. `\"babel-ts\"`, `\"json\"`).\n * @param loadPlugins - Dynamically imports the Prettier plugin modules the\n * parser needs.\n * @returns A {@link Formatter} that formats through Prettier's\n * `formatWithCursor`, which maps the cursor offset through the reformat.\n */\nexport function formatWithPrettier(\n parser: string,\n loadPlugins: () => Promise<Plugin[]>,\n): Formatter {\n return async (source: string, cursorOffset: number) => {\n const [{ formatWithCursor }, plugins] = await Promise.all([\n import(\"prettier/standalone\"),\n loadPlugins(),\n ]);\n\n return formatWithCursor(source, { parser, plugins, cursorOffset });\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { Formatter } from \"~/component/editor/LanguageRegistry.js\";\n\n/**\n * SQL formatter adapter backed by `sql-formatter`, dynamically imported on\n * first call so it never lands in the base editor chunk.\n *\n * Unlike Prettier's `formatWithCursor`, `sql-formatter` has no cursor-mapping\n * API, so the old cursor offset is clamped to the formatted document's length\n * rather than mapped to its logically-equivalent position.\n */\nexport const formatWithSql: Formatter = async (source: string, cursorOffset: number) => {\n const { format } = await import(\"sql-formatter\");\n const formatted = format(source);\n\n return { formatted, cursorOffset: Math.min(cursorOffset, formatted.length) };\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n// Registers the five built-in language definitions as a side effect of\n// importing this module (from the barrel `index.ts`). Every grammar and\n// formatter loader is a dynamic `import()`, so — because the CodeMirror\n// language packages and Prettier/sql-formatter are `external` in the lib\n// build — none of them reach the base editor chunk; each loads only when a\n// consumer actually selects that language or calls `format()`.\n\nimport { registerLanguage } from \"~/component/editor/LanguageRegistry.js\";\nimport { formatWithPrettier } from \"~/component/editor/formatters/prettier.js\";\nimport { formatWithSql } from \"~/component/editor/formatters/sql.js\";\n\n/** Loads the babel + estree plugins Prettier's `babel-ts` and `json` parsers need. */\nasync function loadBabelPlugins() {\n const [babel, estree] = await Promise.all([\n import(\"prettier/plugins/babel\"),\n import(\"prettier/plugins/estree\"),\n ]);\n\n return [babel, estree];\n}\n\nregisterLanguage({\n id: \"javascript\",\n label: \"JavaScript / TypeScript\",\n loadExtension: async () => {\n const { javascript } = await import(\"@codemirror/lang-javascript\");\n\n return javascript({ typescript: true });\n },\n loadFormatter: async () => formatWithPrettier(\"babel-ts\", loadBabelPlugins),\n});\n\nregisterLanguage({\n id: \"json\",\n label: \"JSON\",\n loadExtension: async () => {\n const { json } = await import(\"@codemirror/lang-json\");\n\n return json();\n },\n loadFormatter: async () => formatWithPrettier(\"json\", loadBabelPlugins),\n});\n\nregisterLanguage({\n id: \"html\",\n label: \"HTML\",\n loadExtension: async () => {\n const { html } = await import(\"@codemirror/lang-html\");\n\n return html();\n },\n loadFormatter: async () => formatWithPrettier(\"html\", async () => [await import(\"prettier/plugins/html\")]),\n});\n\nregisterLanguage({\n id: \"sql\",\n label: \"SQL\",\n loadExtension: async () => {\n const { sql } = await import(\"@codemirror/lang-sql\");\n\n return sql();\n },\n loadFormatter: async () => formatWithSql,\n});\n\nregisterLanguage({\n id: \"markdown\",\n label: \"Markdown\",\n loadExtension: async () => {\n const { markdown } = await import(\"@codemirror/lang-markdown\");\n\n return markdown();\n },\n loadFormatter: async () => formatWithPrettier(\"markdown\", async () => [await import(\"prettier/plugins/markdown\")]),\n});\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { EditorView } from \"@codemirror/view\";\nimport { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { tags } from \"@lezer/highlight\";\nimport type { Extension } from \"@codemirror/state\";\n\n// The syntax palette has no dedicated project theme tokens (the framework\n// defines chrome/surface/accent tokens, not a per-token-kind syntax scheme),\n// so it is a fixed, IDE-conventional colour set chosen for light/dark\n// legibility rather than derived from CSS variables — mirroring how\n// `Markdown`'s code-block wash is a neutral constant rather than a token.\nconst SYNTAX_KEYWORD = \"#2563eb\"; // matches the framework's single accent hue\nconst SYNTAX_STRING = \"#16a34a\"; // green — conventional \"string\" colour\nconst SYNTAX_COMMENT = \"#6b7280\"; // muted grey — de-emphasises comments\nconst SYNTAX_LITERAL = \"#d97706\"; // amber — numbers/booleans/null\nconst SYNTAX_FUNCTION = \"#7c3aed\"; // violet — function/variable definitions\nconst SYNTAX_TYPE = \"#0891b2\"; // teal — type names\nconst SYNTAX_PROPERTY = \"#be185d\"; // pink — object/attribute property names\n\n/**\n * Builds the editor's theme extension: the chrome (background, gutters,\n * cursor, selection) via `EditorView.theme`, plus syntax-highlighting colours\n * via a `HighlightStyle`.\n *\n * The chrome reads the project's CSS custom-property tokens directly\n * (`--ts-ui-input-bg`, `--ts-ui-text-color`, `--ts-ui-font-mono`,\n * `--ts-ui-border-color`, `--ts-ui-indicator-focus`,\n * `--ts-ui-indicator-selection`), each with a light/dark-safe fallback, so a\n * `ThemeManager.setTheme` toggle recolours the editor with no rebuild — only\n * the underlying CSS variables change.\n *\n * @param dark - Whether the dark theme flag (`--ts-ui-color-scheme: dark`) is\n * currently active, forwarded to `EditorView.theme`'s `dark` option so\n * CodeMirror's own dark-mode heuristics (default scrollbar styling, base\n * theme selection) agree with the project's active theme.\n * @returns The combined chrome + syntax-highlighting extension.\n */\nexport function codeEditorTheme(dark: boolean): Extension {\n const chrome = EditorView.theme({\n \"&\": {\n height: \"100%\",\n color: \"var(--ts-ui-text-color, #1a1a1a)\",\n backgroundColor: \"var(--ts-ui-input-bg, #ffffff)\",\n },\n \".cm-content\": {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n caretColor: \"var(--ts-ui-indicator-focus, #2563eb)\",\n cursor: \"text\",\n },\n \".cm-gutters\": {\n color: \"var(--ts-ui-text-color, #1a1a1a)\",\n backgroundColor: \"var(--ts-ui-input-bg, #ffffff)\",\n borderRight: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n },\n \"&.cm-focused .cm-cursor\": {\n borderLeftColor: \"var(--ts-ui-indicator-focus, #2563eb)\",\n },\n \"&.cm-focused .cm-selectionBackground, .cm-selectionBackground\": {\n backgroundColor: \"var(--ts-ui-indicator-selection, rgba(37, 99, 235, 0.25))\",\n },\n \".cm-activeLine, .cm-activeLineGutter\": {\n backgroundColor: \"rgba(127, 127, 127, 0.08)\",\n },\n }, { dark });\n\n const highlight = syntaxHighlighting(HighlightStyle.define([\n { tag: tags.keyword, color: SYNTAX_KEYWORD },\n { tag: [tags.string, tags.special(tags.string)], color: SYNTAX_STRING },\n { tag: tags.comment, color: SYNTAX_COMMENT, fontStyle: \"italic\" },\n { tag: [tags.number, tags.bool, tags.null], color: SYNTAX_LITERAL },\n { tag: [tags.definition(tags.variableName), tags.function(tags.variableName)], color: SYNTAX_FUNCTION },\n { tag: tags.typeName, color: SYNTAX_TYPE },\n { tag: [tags.propertyName, tags.attributeName], color: SYNTAX_PROPERTY },\n { tag: tags.tagName, color: SYNTAX_KEYWORD },\n // Markdown constructs (the `markdown` language, e.g. the MarkdownEditor\n // source surface). The parser tags these; without a style here they\n // would render as plain text. Headings/links reuse the accent hue; bold\n // and italic carry weight/slant rather than colour so prose stays\n // readable; the markup marks (`#`, `**`, `` ` ``, `>`, `-`) and quotes\n // are muted so the content reads over the syntax.\n { tag: [tags.heading, tags.heading1, tags.heading2, tags.heading3,\n tags.heading4, tags.heading5, tags.heading6], color: SYNTAX_KEYWORD, fontWeight: \"bold\" },\n { tag: tags.strong, fontWeight: \"bold\" },\n { tag: tags.emphasis, fontStyle: \"italic\" },\n { tag: tags.monospace, color: SYNTAX_STRING },\n { tag: [tags.link, tags.url], color: SYNTAX_KEYWORD, textDecoration: \"underline\" },\n { tag: [tags.quote, tags.contentSeparator], color: SYNTAX_COMMENT },\n { tag: tags.processingInstruction, color: SYNTAX_COMMENT },\n ]));\n\n return [chrome, highlight];\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Animation } from \"~/core/Animation.js\";\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Event } from \"~/core/Event.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { consumeWheel } from \"~/core/SmoothScroller.js\";\nimport { ThemeManager } from \"~/core/Theme.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { EditorView, keymap, drawSelection, lineNumbers, highlightActiveLine, highlightActiveLineGutter } from \"@codemirror/view\";\nimport { EditorState, Compartment } from \"@codemirror/state\";\nimport type { Extension } from \"@codemirror/state\";\nimport { history, defaultKeymap, historyKeymap, indentWithTab } from \"@codemirror/commands\";\nimport { indentOnInput, bracketMatching, indentRange } from \"@codemirror/language\";\nimport { getLanguage } from \"~/component/editor/LanguageRegistry.js\";\nimport { codeEditorTheme } from \"~/component/editor/theme.js\";\n\n/**\n * Payload of {@link CodeEditor}'s `\"change\"` event: the document text after\n * the edit that triggered it.\n *\n * @category Components\n */\nexport interface CodeEditorChange {\n value: string;\n}\n\n/**\n * The events {@link CodeEditor} exposes through its custom `on` / `off` surface:\n *\n * - `\"change\"` — the document text changed (payload {@link CodeEditorChange}).\n * - `\"readonlyedit\"` — a user edit was rejected because the editor is read-only\n * (no payload); see {@link CodeEditor.on}.\n */\ntype CodeEditorEvent = \"change\" | \"readonlyedit\";\n\n/**\n * Construction-time options for {@link CodeEditor}.\n *\n * @category Components\n */\nexport interface CodeEditorOptions extends ComponentOptions {\n /** Initial document text (also accepted as the positional first argument). */\n value?: string;\n /** Registered language id (e.g. `\"javascript\"`, `\"json\"`, `\"sql\"`). Unset renders plain text. */\n language?: string;\n /** Whether the editor rejects edits. Default `false`. */\n readOnly?: boolean;\n /** Construction-time listener bag; the events are `\"change\"` and `\"readonlyedit\"`. */\n listeners?: { change?: (payload: CodeEditorChange) => void; readonlyedit?: () => void };\n}\n\n/** Duration of the read-only rejection flash, in milliseconds. */\nconst READONLY_FLASH_MS = 300;\n\n/** Peak opacity of the read-only rejection wash — a subtle tint that keeps the text readable. */\nconst READONLY_FLASH_PEAK_OPACITY = 0.16;\n\n/**\n * Fill of the read-only rejection wash: the project's validation-error token,\n * with a light/dark-safe fallback, so the tint follows a `ThemeManager.setTheme`\n * toggle with no rebuild (the CSS variable resolves live).\n */\nconst READONLY_FLASH_COLOR = \"var(--ts-ui-validation-error-border, #dc2626)\";\n\n/**\n * Builds the readOnly-state extension: `EditorState.readOnly` blocks every\n * edit (including programmatic ones) while leaving the content DOM editable, so\n * the caret, cursor navigation, and text selection / copy all keep working —\n * the standard read-only experience. (`EditorView.editable.of(false)` would\n * additionally strip selection by making CodeMirror stop managing a caret in\n * the content, so it is deliberately *not* used here.)\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns The extension for the readOnly compartment.\n */\nfunction buildReadOnlyExtension(readOnly: boolean): Extension {\n return EditorState.readOnly.of(readOnly);\n}\n\n/**\n * A syntax-highlighting, formatting code editor wrapping CodeMirror 6.\n *\n * @remarks\n * `CodeEditor` deliberately omits IntelliSense (no autocomplete, no lint, no\n * language service) — its scope is highlighting plus one-command formatting.\n * CodeMirror's `EditorView` is a *foreign live widget*: it takes a real parent\n * element and mutates a whole DOM region it owns directly, exactly like the\n * `CanvasRenderingContext2D` a [`Canvas`](/api/component/display/classes/Canvas)\n * obtains from the seam. So `CodeEditor` is **live-only** — under the modelled\n * test sink it mounts nothing, and every editor operation guards on a `null`\n * view, matching `Canvas` / `WebGLCanvas`.\n *\n * The editor mounts once, in the component's first connected + sized layout\n * (`onFirstLayout`) — CodeMirror needs a real, sized parent to measure its\n * internal scroller correctly. It fills the assigned box and scrolls\n * internally; give it a sized host (a `Fit` panel or an explicit\n * `preferredSize`), the same as `Canvas`.\n *\n * Highlighting grammars and formatters load lazily, per language, through the\n * registry in `LanguageRegistry.ts` (`registerLanguage` / `getLanguage` /\n * `listLanguages`) — see that module and `languages.ts` for the five built-in\n * languages (JavaScript/TypeScript, JSON, HTML, SQL, Markdown).\n *\n * @example\n * ```typescript\n * import { CodeEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new CodeEditor('const x = 1;', { language: 'javascript' });\n * panel.addComponent(editor);\n * await editor.format();\n * ```\n *\n * @category Components\n */\nclass CodeEditor extends Component<CodeEditorOptions> {\n\n /** The live CodeMirror view; `null` until mounted (or forever, offline). */\n private _view: EditorView | null = null;\n\n /** Overlay flashed on a rejected read-only edit; `null` until mounted (or forever, offline). */\n private _flashOverlay: Handle | null = null;\n\n // In-flight read-only flash, cancelled on teardown so its fallback timer\n // cannot fire against the overlay's released element handle.\n private _flashAnimation: Animation.CancelHandle | null = null;\n\n /** Reconfigured by {@link CodeEditor.setLanguage} to swap the active grammar. */\n private readonly _langCompartment: Compartment = new Compartment();\n\n /** Reconfigured by {@link CodeEditor.setReadOnly}. */\n private readonly _readOnlyCompartment: Compartment = new Compartment();\n\n /** Reconfigured on a theme change to recolour the editor. */\n private readonly _themeCompartment: Compartment = new Compartment();\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<CodeEditorEvent> = new ListenerBag<CodeEditorEvent>();\n\n /** Handle to detach the {@link ThemeManager.onThemeChange} listener on {@link CodeEditor.dispose}. */\n private readonly _unsubscribeTheme: () => void;\n\n /**\n * Stable reference for the subtree wheel-claim listener's add (in\n * {@link CodeEditor.mount}) and remove (in {@link CodeEditor.dispose}); see\n * {@link CodeEditor.claimScrollableWheel}.\n */\n private readonly _onWheelClaim: (e: WheelEvent) => void = (e) => this.claimScrollableWheel(e);\n\n /**\n * Constructs a code editor.\n *\n * @param value - Initial document text (optional; defaults to \"\").\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: CodeEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n this._unsubscribeTheme = ThemeManager.onThemeChange(() => this.onThemeChange());\n\n this.applyListeners(options?.listeners);\n\n this.onFirstLayout(() => this.mount());\n }\n\n /**\n * Caches the `value` / `language` / `readOnly` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. No view\n * exists yet at this point (the editor mounts later, in `onFirstLayout`), so\n * these are pure caches, applied to the freshly-created `EditorState` and\n * compartments in `mount()`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: CodeEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.language !== undefined) this._options.language = options.language;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n\n return this;\n }\n\n /**\n * Returns the current document text: read live from the view when mounted,\n * else the cached pre-mount / offline value.\n *\n * @returns The document text, or `\"\"` when unset.\n */\n getValue(): string {\n if (this._view) {\n return this._view.state.doc.toString();\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document. Caches the value; when a view is mounted,\n * also dispatches a full-document replace transaction.\n *\n * @param value - The new document text.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n this._options.value = value;\n\n if (this._view) {\n this._view.dispatch({\n changes: { from: 0, to: this._view.state.doc.length, insert: value },\n });\n }\n\n return this;\n }\n\n /**\n * Returns the active language id.\n *\n * @returns The registered language id, or `null` when unset.\n */\n getLanguage(): string | null {\n return this._options.language ?? null;\n }\n\n /**\n * Sets (or clears) the active language. Caches the id; when a view is\n * mounted and the id names a registered language, kicks off the grammar's\n * lazy `loadExtension()` and reconfigures the grammar compartment once it\n * resolves — guarded against a stale resolution by re-checking the active\n * language id, so a rapid double-swap applies only the latest.\n *\n * @param id - A registered language id, or `null` to clear highlighting.\n * @returns This component, for method chaining.\n */\n setLanguage(id: string | null): this {\n this._options.language = id ?? undefined;\n\n if (!this._view) {\n return this;\n }\n\n if (!id) {\n this._view.dispatch({ effects: this._langCompartment.reconfigure([]) });\n\n return this;\n }\n\n const def = getLanguage(id);\n\n if (!def) {\n return this;\n }\n\n void def.loadExtension().then((extension) => {\n if (this._view && this.getLanguage() === id) {\n this._view.dispatch({ effects: this._langCompartment.reconfigure(extension) });\n }\n });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The readOnly state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits. Caches the state; when a view is\n * mounted, also reconfigures the readOnly compartment.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n\n if (this._view) {\n this._view.dispatch({ effects: this._readOnlyCompartment.reconfigure(buildReadOnlyExtension(readOnly)) });\n }\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the editor.\n *\n * @remarks Overrides {@link core!Component.focus} because a `CodeEditor`'s own\n * element is a plain, non-focusable host `<div>` — CodeMirror owns the real\n * focusable target (its `contentDOM`), so the base `element.focus()` would be\n * a no-op. When mounted this hands focus to the live view; offline / before\n * mount it falls back to the base behaviour (also a no-op, matching every\n * other editor operation's null-view guard). The current selection / caret is\n * left untouched — pair with {@link CodeEditor.moveCursorToEnd} to also move it.\n *\n * @param preventScroll - Forwarded to the base fallback; CodeMirror's own\n * `focus()` takes no scroll option, so it is honoured only offline.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this._view) {\n this._view.focus();\n\n return this;\n }\n\n return super.focus(preventScroll);\n }\n\n /**\n * Places the caret at the end of the document (after the last character),\n * collapsing any selection, and scrolls it into view.\n *\n * @remarks A selection-only transaction — it changes no text, so it emits no\n * `\"change\"`. No-op before the view is mounted (offline / pre-mount), like\n * every other view operation. Pair with {@link CodeEditor.focus} to land the\n * caret ready for typing (e.g. on a freshly opened, pre-seeded editor).\n *\n * @returns This component, for method chaining.\n */\n moveCursorToEnd(): this {\n if (this._view) {\n const end = this._view.state.doc.length;\n\n this._view.dispatch({ selection: { anchor: end }, scrollIntoView: true });\n }\n\n return this;\n }\n\n /**\n * Formats the document via the active language's formatter, or re-indents\n * it (CodeMirror's own indentation service) when the language has none.\n *\n * @remarks If the formatter throws (invalid syntax), the rejection\n * propagates and the document is left **untouched** — the full-document\n * replace only runs after the formatter resolves successfully, so a\n * throwing formatter never reaches it. The cursor is preserved through\n * Prettier's `formatWithCursor` mapping, or clamped to the new document\n * length for a formatter with no cursor map (sql-formatter).\n *\n * @returns A promise that resolves once formatting completes, or rejects\n * with the formatter's error.\n */\n async format(): Promise<void> {\n const id = this.getLanguage();\n const def = id ? getLanguage(id) : undefined;\n\n if (!def?.loadFormatter) {\n this.reindentFallback();\n\n return;\n }\n\n const formatter = await def.loadFormatter();\n const source = this.getValue();\n const cursorOffset = this._view ? this._view.state.selection.main.head : 0;\n\n const result = await formatter(source, cursorOffset);\n\n this._options.value = result.formatted;\n\n if (this._view) {\n this._view.dispatch({\n changes: { from: 0, to: this._view.state.doc.length, insert: result.formatted },\n selection: { anchor: Math.min(result.cursorOffset, result.formatted.length) },\n });\n }\n }\n\n /**\n * The `format()` fallback for a language with no formatter: re-indents the\n * whole document via CodeMirror's own indentation service. Factored out\n * from `format()` so the dispatch decision (formatter vs. this fallback) is\n * unit-testable by spying on this method — the actual re-indent needs a\n * live view (guarded below) and is otherwise manual-verify only.\n */\n private reindentFallback(): void {\n if (this._view) {\n this._view.dispatch({ changes: indentRange(this._view.state, 0, this._view.state.doc.length) });\n }\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the\n * document changes (including via {@link CodeEditor.format} / {@link CodeEditor.setValue}).\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new document text.\n * @returns This component, for method chaining.\n */\n on(event: \"change\", listener: (payload: CodeEditorChange) => void): this;\n /**\n * Registers a listener for the `\"readonlyedit\"` event, fired when a user\n * edit (typing, paste, drop) is rejected because the editor is read-only.\n *\n * @param event - Must be `\"readonlyedit\"`.\n * @param listener - Invoked with no arguments on each rejected edit.\n * @returns This component, for method chaining.\n */\n on(event: \"readonlyedit\", listener: () => void): this;\n on(event: CodeEditorEvent, listener: (payload: CodeEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: \"change\", listener: (payload: CodeEditorChange) => void): this;\n /**\n * Removes a previously registered `\"readonlyedit\"` listener.\n *\n * @param event - Must be `\"readonlyedit\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: \"readonlyedit\", listener: () => void): this;\n off(event: CodeEditorEvent, listener: (payload: CodeEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans an event out to its registered listeners.\n *\n * @param event - The event name.\n * @param payload - The event payload (`\"change\"` only; `\"readonlyedit\"` has none).\n */\n protected emit(event: \"change\", payload: CodeEditorChange): void;\n protected emit(event: \"readonlyedit\"): void;\n protected emit(event: CodeEditorEvent, payload?: CodeEditorChange): void {\n this._listeners.fire(event, ...(payload ? [payload] : []));\n }\n\n /**\n * Detaches the theme-change listener and destroys the live CodeMirror\n * view, then defers to the base class for the rest of teardown. Call\n * before discarding a dynamically-built `CodeEditor`, mirroring\n * `Markdown.destructor`.\n */\n protected destructor(): void {\n // Before `super.destructor()` releases the overlay's element handle,\n // which the flash's fallback timer would otherwise write to.\n this._flashAnimation?.cancel();\n this._flashAnimation = null;\n\n this._unsubscribeTheme();\n\n if (this._view) {\n Event.removeSubtreeListener(this, \"wheel\", this._onWheelClaim);\n }\n\n // Nulled after destroy so a second destructor() pass on this same\n // instance (dispose() is documented idempotent — a harmless no-op —\n // but CodeMirror's own EditorView.destroy() is not guarded against a\n // repeat call) finds `_view` already null and no-ops via the `?.`\n // above. Cheap insurance on a path the offline test harness cannot\n // reach: it never constructs a live `EditorView`.\n this._view?.destroy();\n this._view = null;\n\n super.destructor();\n }\n\n /**\n * Marks a wheel event consumed (via {@link consumeWheel}) when CodeMirror's\n * own scroller can move along the gesture's axis — WITHOUT\n * `preventDefault()`, so CodeMirror still scrolls `.cm-scroller` natively.\n *\n * @param e - The wheel event reaching this editor's subtree.\n *\n * @remarks A floating overlay installs a wheel trap (see `WheelTrap`) that\n * `preventDefault()`s any wheel no inner scroller claimed, so an unconsumed\n * wheel cannot fall through to content behind the overlay. CodeMirror\n * scrolls natively without going through the framework's eased scroller, so\n * it never claims the wheel — and the trap, firing last as the outermost\n * ancestor, would cancel the editor's native scroll. Claiming here (the\n * subtree walk reaches this inner editor before the outer overlay) lets the\n * native scroll proceed. Only an axis with somewhere to go is claimed; at a\n * fits-in-box axis the wheel stays unclaimed so the trap still swallows it.\n */\n private claimScrollableWheel(e: WheelEvent): void {\n const scroller = this._view?.scrollDOM;\n\n if (!scroller) {\n return;\n }\n\n const extentX = scroller.scrollWidth > scroller.clientWidth;\n const extentY = scroller.scrollHeight > scroller.clientHeight;\n\n // shift+wheel with a bare vertical delta scrolls horizontally (native\n // behaviour CodeMirror preserves), so it targets the horizontal axis.\n const targetsHorizontal = e.deltaX !== 0 || (e.shiftKey && e.deltaY !== 0);\n const targetsVertical = e.deltaY !== 0 && !e.shiftKey;\n\n if ((targetsVertical && extentY) || (targetsHorizontal && extentX)) {\n consumeWheel(e);\n }\n }\n\n /**\n * Builds the `EditorState` and mounts the CodeMirror view through the DOM\n * seam's `mountView` escape. Idempotent (guards on an existing view).\n * Offline / before the element exists, `DOM.sink.mountView` returns `null`,\n * so `_view` stays `null` and every editor operation continues to no-op.\n */\n private mount(): void {\n if (this._view) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n const dark = ThemeManager.getTheme().colorScheme === \"dark\";\n\n const extensions: Extension[] = [\n history(),\n // `indentWithTab` is deliberately absent from `defaultKeymap`,\n // because binding Tab traps it inside the editor and a keyboard\n // user can no longer Tab out. A code editor that does not indent\n // on Tab is the worse trade, and `defaultKeymap` already carries\n // the escape hatch: Ctrl-m (Alt-Shift-m on macOS) toggles\n // CodeMirror's tab-focus mode, after which Tab moves focus again.\n // Listed last so its Tab / Shift-Tab bindings take precedence.\n keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),\n drawSelection(),\n lineNumbers(),\n highlightActiveLine(),\n highlightActiveLineGutter(),\n indentOnInput(),\n bracketMatching(),\n this._readOnlyCompartment.of(buildReadOnlyExtension(this.getReadOnly())),\n this._themeCompartment.of(codeEditorTheme(dark)),\n this._langCompartment.of([]),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n this._options.value = update.state.doc.toString();\n this.emit(\"change\", { value: this._options.value });\n }\n }),\n EditorView.domEventHandlers({\n // A user edit while read-only is blocked by `EditorState.readOnly`\n // at the input layer — it never becomes a transaction, so it emits\n // no \"change\". These hooks surface the rejection as feedback:\n // typing / IME / delete fire `beforeinput`, while paste and drop are\n // prevented before `beforeinput` runs, so each needs its own hook.\n beforeinput: () => this.onEditIntent(),\n paste: () => this.onEditIntent(),\n drop: () => this.onEditIntent(),\n }),\n ];\n\n const state = EditorState.create({ doc: this._options.value ?? \"\", extensions });\n\n this._view = DOM.sink.mountView(element, (parent) => new EditorView({ parent, state }));\n\n if (this._view) {\n this.mountFlashOverlay(element);\n // Claim wheels CodeMirror's own scroller will act on, so an\n // enclosing overlay's wheel trap leaves this editor's native scroll\n // alone (see claimScrollableWheel). Descendant-first subtree\n // dispatch reaches this editor before the overlay, so the claim wins.\n Event.addSubtreeListener(this, \"wheel\", this._onWheelClaim, { passive: false });\n }\n\n const language = this.getLanguage();\n\n if (language) {\n this.setLanguage(language);\n }\n }\n\n /**\n * Creates the read-only rejection wash: a pointer-transparent overlay that\n * fills the editor box and sits above CodeMirror's content, appended after\n * the view so it paints on top. It rests at `opacity: 0`; {@link CodeEditor.flashReadOnly}\n * pulses it. Tracked as an owned handle, so the component's teardown releases it.\n *\n * @param host - The mounted editor element the overlay fills.\n */\n private mountFlashOverlay(host: Handle): void {\n const overlay = this.trackHandle(DOM.sink.createElement(\"div\"));\n\n DOM.sink.apply(overlay, {\n style: {\n position: \"absolute\",\n inset: \"0\",\n pointerEvents: \"none\",\n // Above CodeMirror's sticky line-number gutter (z-index 200) so the\n // wash covers the gutter too; `.cm-editor` forms no stacking context,\n // so the gutter would otherwise paint over a lower overlay.\n zIndex: \"300\",\n backgroundColor: READONLY_FLASH_COLOR,\n opacity: \"0\",\n },\n });\n DOM.sink.appendChild(host, overlay);\n\n this._flashOverlay = overlay;\n }\n\n /**\n * Reconfigures the theme compartment when the project theme changes, so\n * the editor's chrome + syntax colours follow a `ThemeManager.setTheme`\n * toggle with no rebuild. No-ops before the view exists.\n */\n private onThemeChange(): void {\n if (!this._view) {\n return;\n }\n\n const dark = ThemeManager.getTheme().colorScheme === \"dark\";\n\n this._view.dispatch({ effects: this._themeCompartment.reconfigure(codeEditorTheme(dark)) });\n }\n\n /**\n * DOM-event hook for the read-only rejection feedback, wired to\n * `beforeinput` / `paste` / `drop` in {@link CodeEditor.mount}. When the\n * editor is read-only it surfaces the attempt via\n * {@link CodeEditor.signalReadOnlyEdit}; it never consumes the event, so\n * CodeMirror's own `EditorState.readOnly` handling still blocks the edit.\n *\n * @returns Always `false` (event left unhandled).\n */\n private onEditIntent(): boolean {\n if (this.getReadOnly()) {\n this.signalReadOnlyEdit();\n }\n\n return false;\n }\n\n /**\n * Emits the `\"readonlyedit\"` event and plays the rejection flash — a\n * programmatic hook plus a built-in visual cue that an edit was refused\n * because the editor is read-only.\n */\n private signalReadOnlyEdit(): void {\n this.emit(\"readonlyedit\");\n this.flashReadOnly();\n }\n\n /**\n * Flashes a brief error-coloured wash over the editor as feedback for a\n * rejected edit: fades the overlay from a subtle tint back to transparent.\n * No-op before the overlay is mounted (offline / pre-mount), and — via\n * {@link Animation.play} — honours `prefers-reduced-motion`: under reduced\n * motion the `\"readonlyedit\"` event still fires but nothing animates.\n *\n * A full-box wash is used rather than a border/ring cue because the editor\n * is often large and mostly empty, so an edge flash sits far from where the\n * eye is (the caret) and reads as nothing; the wash covers the content the\n * user is looking at while staying faint enough to keep the text legible.\n */\n private flashReadOnly(): void {\n if (!this._flashOverlay) {\n return;\n }\n\n this._flashAnimation?.cancel();\n this._flashAnimation = Animation.play(this._flashOverlay, {\n from: { opacity: String(READONLY_FLASH_PEAK_OPACITY) },\n to: { opacity: \"0\" },\n durationMs: READONLY_FLASH_MS,\n properties: [\"opacity\"],\n });\n }\n}\n\nconst CodeEditorCallable = callable(CodeEditor);\ntype CodeEditorCallable = CodeEditor;\nexport {\n CodeEditor as _CodeEditor,\n CodeEditorCallable as CodeEditor,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport type { ElementFormatType } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString } from \"@lexical/markdown\";\nimport type { MultilineElementTransformer, Transformer } from \"@lexical/markdown\";\nimport {\n $createTableNode, $createTableRowNode, $createTableCellNode,\n $isTableNode, $isTableRowNode, $isTableCellNode,\n TableNode, TableRowNode, TableCellNode, TableCellHeaderStates,\n} from \"@lexical/table\";\nimport type { TableRowNode as TableRowNodeType } from \"@lexical/table\";\n\n/**\n * A candidate table row: any line containing a pipe. Deliberately loose — a\n * line only becomes a table once the next line is a matching delimiter row,\n * which {@link createTableTransformer}'s `handleImportAfterStartMatch` checks.\n * Leading and trailing pipes are optional in GFM, so they cannot be part of\n * this test.\n */\nconst TABLE_ROW_REG_EXP = /\\|/;\n\n/**\n * Splits one row into its cell texts. Drops one optional leading and one\n * optional trailing pipe, splits on unescaped `|` only, turns each `\\|` back\n * into a literal `|`, and trims each cell. A manual scan rather than a regexp\n * with a lookbehind — the same choice `@lexical/markdown`'s own\n * `isTableRowDivider` makes, since a nested-quantifier lookbehind pattern can\n * run in super-linear time on a backtracking regexp engine.\n *\n * @param line - One raw table-row line (with or without surrounding pipes).\n * @returns The row's cell texts, trimmed and unescaped.\n *\n * @example\n * ```\n * splitTableRow(\"| a | b \\\\| c |\") // -> [\"a\", \"b | c\"]\n * splitTableRow(\"a | b\") // -> [\"a\", \"b\"]\n * ```\n */\nfunction splitTableRow(line: string): string[] {\n let trimmed = line.trim();\n\n if (trimmed.startsWith(\"|\")) {\n trimmed = trimmed.slice(1);\n }\n\n if (trimmed.endsWith(\"|\") && !trimmed.endsWith(\"\\\\|\")) {\n trimmed = trimmed.slice(0, -1);\n }\n\n const cells: string[] = [];\n let current = \"\";\n\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n\n if (char === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n } else if (char === \"|\") {\n cells.push(current.trim());\n current = \"\";\n } else {\n current += char;\n }\n }\n\n cells.push(current.trim());\n\n return cells;\n}\n\n/**\n * Reads a GFM delimiter row's per-column alignment, or `null` when the line\n * is not a delimiter row (any segment isn't `-`s optionally bounded by `:`).\n *\n * @param line - The candidate delimiter-row line.\n * @returns One {@link ElementFormatType} per column, or `null` when `line` is\n * not a valid delimiter row.\n *\n * @example\n * ```\n * parseDelimiterRow(\"| :--- | :---: | ---: | --- |\") // -> [\"left\", \"center\", \"right\", \"\"]\n * parseDelimiterRow(\"| a | b |\") // -> null\n * ```\n */\nfunction parseDelimiterRow(line: string): ElementFormatType[] | null {\n const segments = splitTableRow(line);\n const alignments: ElementFormatType[] = [];\n\n for (const segment of segments) {\n if (!/^:?-+:?$/.test(segment)) {\n return null;\n }\n\n const left = segment.startsWith(\":\");\n const right = segment.endsWith(\":\");\n\n if (left && right) {\n alignments.push(\"center\");\n } else if (left) {\n alignments.push(\"left\");\n } else if (right) {\n alignments.push(\"right\");\n } else {\n alignments.push(\"\");\n }\n }\n\n return alignments;\n}\n\n/**\n * Renders a delimiter row from the header cells' formats.\n *\n * @param alignments - One {@link ElementFormatType} per column.\n * @returns The rendered delimiter-row line.\n *\n * @example\n * ```\n * formatDelimiterRow([\"left\", \"center\", \"right\", \"\"]) // -> \"| :--- | :---: | ---: | --- |\"\n * ```\n */\nfunction formatDelimiterRow(alignments: ElementFormatType[]): string {\n const cells = alignments.map((alignment) => {\n switch (alignment) {\n case \"left\": return \":---\";\n case \"center\": return \":---:\";\n case \"right\": return \"---:\";\n default: return \"---\";\n }\n });\n\n return \"| \" + cells.join(\" | \") + \" |\";\n}\n\n/**\n * Prepares a cell's Markdown for embedding in a pipe row: trims it, escapes\n * every `|` to `\\|`, and replaces every newline with a literal `\\n`.\n *\n * @param markdown - The cell's exported Markdown.\n * @returns The pipe-row-safe cell text.\n */\nfunction escapeCellText(markdown: string): string {\n return markdown.trim().replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n}\n\n/**\n * Builds the GFM pipe-table transformer: a `MultilineElementTransformer` that\n * consumes a whole table block (header, delimiter, and body rows) in one pass\n * — the same `handleImportAfterStartMatch` hook the curated `CODE` transformer\n * uses for fenced blocks — so it can require a delimiter row before\n * committing, rather than the row-at-a-time shape Lexical's own playground\n * `TABLE` transformer uses.\n *\n * @param getTransformers - Returns the curated transformer array. Called at\n * import/export time, not at construction time, so the array may reference\n * the transformer this call returns (avoiding a module import cycle with\n * the file that builds that array).\n * @returns The table transformer, ready to include in a transformer array.\n */\nexport function createTableTransformer(getTransformers: () => Transformer[]): MultilineElementTransformer {\n return {\n dependencies: [TableNode, TableRowNode, TableCellNode],\n regExpStart: TABLE_ROW_REG_EXP,\n type: \"multiline-element\",\n\n handleImportAfterStartMatch: ({ lines, rootNode, startLineIndex }) => {\n const delimiterLine = lines[startLineIndex + 1];\n\n if (delimiterLine === undefined) {\n return null;\n }\n\n const alignments = parseDelimiterRow(delimiterLine);\n\n if (alignments === null) {\n return null;\n }\n\n const headerCells = splitTableRow(lines[startLineIndex]);\n const columnCount = headerCells.length;\n\n if (alignments.length !== columnCount) {\n return null;\n }\n\n const rows: string[][] = [headerCells];\n let lastConsumedLineIndex = startLineIndex + 1;\n\n // marked ends a table only at a blank line (or end of input), so a\n // following prose line with no pipe in it is absorbed as a\n // one-cell row rather than ending the table here.\n for (let lineIndex = startLineIndex + 2; lineIndex < lines.length; lineIndex += 1) {\n const line = lines[lineIndex];\n\n if (line.trim() === \"\") {\n break;\n }\n\n rows.push(splitTableRow(line));\n lastConsumedLineIndex = lineIndex;\n }\n\n const table = $createTableNode();\n\n rows.forEach((cells, rowIndex) => {\n const row = $createTableRowNode();\n\n for (let column = 0; column < columnCount; column += 1) {\n const cell = $createTableCellNode(\n rowIndex === 0 ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS);\n\n $convertFromMarkdownString(\n (cells[column] ?? \"\").replace(/\\\\n/g, \"\\n\"), getTransformers(), cell);\n\n // AFTER the conversion: it clears the cell's children, and\n // setting the format first risks the clear taking the\n // format with it.\n cell.setFormat(alignments[column] ?? \"\");\n row.append(cell);\n }\n\n table.append(row);\n });\n\n rootNode.append(table);\n\n return [true, lastConsumedLineIndex];\n },\n\n // Never reached: handleImportAfterStartMatch either imports the block or\n // declines it, so the default multiline import path never runs.\n replace: () => false,\n\n export: (node) => {\n if (!$isTableNode(node)) {\n return null;\n }\n\n const rows = node.getChildren().filter($isTableRowNode);\n\n if (rows.length === 0) {\n return null;\n }\n\n const renderRow = (row: TableRowNodeType): string => {\n const cells = row.getChildren().filter($isTableCellNode);\n const rendered = cells.map(\n (cell) => escapeCellText($convertToMarkdownString(getTransformers(), cell)));\n\n return \"| \" + rendered.join(\" | \") + \" |\";\n };\n\n const headerRow = rows[0];\n const headerCells = headerRow.getChildren().filter($isTableCellNode);\n const alignments = headerCells.map((cell) => cell.getFormatType());\n\n const lines = [renderRow(headerRow), formatDelimiterRow(alignments)];\n\n for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {\n lines.push(renderRow(rows[rowIndex]));\n }\n\n return lines.join(\"\\n\");\n },\n };\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport {\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n} from \"@lexical/markdown\";\nimport type { Transformer } from \"@lexical/markdown\";\nimport { createTableTransformer } from \"~/component/editor/markdownTableTransformer.js\";\n\n// Lazy: called at import/export time rather than passed by value, so this\n// module can build TABLE from the very array it is about to join, with no\n// import cycle back into this file.\nconst TABLE = createTableTransformer(() => TRANSFORMERS);\n\n/**\n * The curated Markdown transformer array that defines `MarkdownEditor`'s\n * dialect — the exact subset of Markdown the read-only `Markdown` viewer\n * renders.\n *\n * @remarks\n * This is deliberately **not** Lexical's full `TRANSFORMERS` preset. The preset\n * also carries `STRIKETHROUGH`, `HIGHLIGHT`, `CHECK_LIST`, and an image\n * transformer — constructs the viewer drops to a plain-text fallback. Curating\n * the list down to these ten is the single source of truth that guarantees the\n * editor can never emit Markdown the viewer would fail to render: the same array\n * is passed to the import converter, the export converter, and the\n * markdown-shortcut typing registration, so what the user types, what the editor\n * stores, and what the viewer reads all agree.\n *\n * The mapping to the viewer's supported tokens is:\n *\n * - `HEADING` → `#`…`######` (heading)\n * - `QUOTE` → `> ` (blockquote)\n * - `CODE` → fenced ` ``` ` block (code)\n * - `UNORDERED_LIST` → `- ` (unordered list)\n * - `ORDERED_LIST` → `1. ` (ordered list)\n * - `BOLD_STAR` → `**b**` (strong)\n * - `ITALIC_STAR` → `*i*` (em)\n * - `INLINE_CODE` → `` `c` `` (codespan)\n * - `LINK` → `[t](url)` (link)\n * - `TABLE` → `| a | b |` (table)\n *\n * Star (not underscore) emphasis variants are chosen so bold/italic export is\n * deterministic and matches the viewer's demo output.\n */\nexport const TRANSFORMERS: Transformer[] = [\n TABLE,\n HEADING,\n QUOTE,\n CODE,\n UNORDERED_LIST,\n ORDERED_LIST,\n BOLD_STAR,\n ITALIC_STAR,\n INLINE_CODE,\n LINK,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { HeadingNode, QuoteNode } from \"@lexical/rich-text\";\nimport { ListNode, ListItemNode } from \"@lexical/list\";\nimport { LinkNode } from \"@lexical/link\";\nimport { CodeNode, CodeHighlightNode } from \"@lexical/code\";\nimport { TableNode, TableRowNode, TableCellNode } from \"@lexical/table\";\nimport type { Klass, LexicalNode } from \"lexical\";\n\n/**\n * The Lexical node classes registered on every `MarkdownEditor`'s editor.\n *\n * @remarks\n * A Lexical editor can only build or parse a node type whose class it was\n * created with, so this set must cover every construct the curated transformer\n * list produces: headings (`HeadingNode`), blockquotes (`QuoteNode`),\n * ordered/unordered lists (`ListNode` + `ListItemNode`), links (`LinkNode`),\n * fenced code (`CodeNode` + its `CodeHighlightNode` children), and GFM tables\n * (`TableNode` + `TableRowNode` + `TableCellNode`). The remaining dialect\n * constructs — bold, italic, inline code, paragraphs — are plain text formats\n * on the always-present built-in text/paragraph nodes and need no registration.\n */\nexport const EDITOR_NODES: ReadonlyArray<Klass<LexicalNode>> = [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n LinkNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n];\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { StyleRule } from \"~/core/StyleTarget.js\";\nimport type { EditorThemeClasses } from \"lexical\";\n\n// Class names Lexical stamps onto the rendered nodes. Prefixed `ts-ui-mde-`\n// (markdown editor) so they never collide with the read-only `Markdown`\n// viewer's `ts-ui-md-` rules; the two components replicate rather than share\n// their class rules — the shared contract is the theme-token names below, not\n// the selectors.\nconst HEADING_CLASS = \"ts-ui-mde-heading\";\nconst QUOTE_CLASS = \"ts-ui-mde-quote\";\nconst CODE_BLOCK_CLASS = \"ts-ui-mde-code\";\nconst INLINE_CODE_CLASS = \"ts-ui-mde-inline-code\";\nconst LIST_CLASS = \"ts-ui-mde-list\";\nconst LINK_CLASS = \"ts-ui-mde-link\";\nconst BOLD_CLASS = \"ts-ui-mde-bold\";\nconst ITALIC_CLASS = \"ts-ui-mde-italic\";\nconst TABLE_CLASS = \"ts-ui-mde-table\";\nconst TABLE_ROW_CLASS = \"ts-ui-mde-table-row\";\nconst TABLE_CELL_CLASS = \"ts-ui-mde-table-cell\";\nconst TABLE_CELL_HEADER_CLASS = \"ts-ui-mde-table-cell-header\";\nconst TABLE_CELL_SELECTED_CLASS = \"ts-ui-mde-table-cell-selected\";\n\n/** Guards the module-singleton class-rule registration in {@link ensureMarkdownEditorClassRules}. */\nlet _classRulesEnsured = false;\n\n/**\n * Registers the shared class rules that style the WYSIWYG editing surface,\n * once per document.\n *\n * @remarks\n * The rules mirror the read-only `Markdown` viewer's constructs and reference\n * the **same** CSS custom-property tokens — `--ts-ui-font-mono` for code,\n * `--ts-ui-border-color` for the quote bar, `--ts-ui-indicator-focus` for links\n * — so the edited rich text looks identical to what the viewer renders (the\n * point of WYSIWYG). Because the rules resolve live CSS variables, a theme\n * toggle recolours the surface with no rebuild and the component needs no\n * theme-change subscription. Each em-relative padding/indent is genuine\n * structural spacing (a code wash, a list-marker gutter, a quote bar), scaling\n * with the surrounding font — not a cosmetic nudge.\n */\nexport function ensureMarkdownEditorClassRules(): void {\n if (_classRulesEnsured) {\n return;\n }\n\n _classRulesEnsured = true;\n\n new StyleRule({\n scope: \"class\",\n name: HEADING_CLASS,\n // Semibold so headings read as headings independent of any UA reset.\n styles: { fontWeight: \"600\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: QUOTE_CLASS,\n styles: {\n // 3px quote bar — the framework's thin-border weight — plus an\n // em-relative gutter indenting the quoted prose off the bar.\n borderLeft: \"3px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n paddingLeft: \"1em\",\n marginLeft: \"0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: CODE_BLOCK_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n // Translucent grey wash reads as \"code\" on light and dark alike.\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Structural inset giving the fenced code frame room.\n padding: \"0.6em 0.8em\",\n whiteSpace: \"pre-wrap\",\n // Lexical renders a fenced block as <code>, which is inline by\n // default and so sits on a text baseline — it rides up into a\n // preceding block instead of stacking below it. The viewer's\n // fenced code is a <pre>, a block with the browser's 1em block\n // margin; both are restated here so the two surfaces stack alike.\n display: \"block\",\n margin: \"1em 0\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: INLINE_CODE_CLASS,\n styles: {\n fontFamily: \"var(--ts-ui-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)\",\n background: \"rgba(127, 127, 127, 0.16)\",\n borderRadius: \"var(--ts-ui-border-radius, 3px)\",\n // Snug padding so the wash hugs the inline-code glyphs.\n padding: \"0.1em 0.3em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LIST_CLASS,\n // Room for the list marker; structural, em-relative indentation.\n styles: { paddingLeft: \"1.5em\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: LINK_CLASS,\n // The framework's single accent hue (shared with focus/selection).\n styles: { color: \"var(--ts-ui-indicator-focus, #2563eb)\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: BOLD_CLASS,\n styles: { fontWeight: \"700\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: ITALIC_CLASS,\n styles: { fontStyle: \"italic\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CLASS,\n styles: { borderCollapse: \"collapse\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n // Structural cell padding, matching the read-only viewer's cells.\n padding: \"0.3em 0.6em\",\n },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_HEADER_CLASS,\n styles: {\n border: \"1px solid var(--ts-ui-border-color, rgba(127, 127, 127, 0.4))\",\n padding: \"0.3em 0.6em\",\n fontWeight: \"600\",\n },\n });\n\n new StyleRule({\n scope: \"selector\",\n name: `.${TABLE_CELL_CLASS} > p`,\n // Lexical wraps each cell's content in a paragraph, which would carry\n // the browser's default 1em block margin and make every editor cell\n // two line-heights taller than the same cell in the read-only viewer,\n // whose cells hold inline content directly. Only cell paragraphs are\n // reset: prose paragraphs keep their margin, which is what matches the\n // viewer outside a table.\n styles: { margin: \"0\" },\n });\n\n new StyleRule({\n scope: \"class\",\n name: TABLE_CELL_SELECTED_CLASS,\n // @lexical/table stamps this class onto cells caught in a drag-selected\n // range; reuses the data-grid Table component's own row-selected token\n // so a selected cell reads the same way here as it does there.\n styles: { backgroundColor: \"var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15))\" },\n });\n}\n\n/**\n * The Lexical class-name theme map linking each node type to its shared class\n * rule from {@link ensureMarkdownEditorClassRules}.\n *\n * @remarks\n * Every heading level maps to the one heading rule (the tag itself carries the\n * size); ordered and unordered lists share the indentation rule. Bold, italic,\n * and inline code are text-format entries, and links carry the accent colour.\n */\nexport const EDITOR_THEME: EditorThemeClasses = {\n heading: {\n h1: HEADING_CLASS,\n h2: HEADING_CLASS,\n h3: HEADING_CLASS,\n h4: HEADING_CLASS,\n h5: HEADING_CLASS,\n h6: HEADING_CLASS,\n },\n quote: QUOTE_CLASS,\n code: CODE_BLOCK_CLASS,\n list: {\n ul: LIST_CLASS,\n ol: LIST_CLASS,\n },\n text: {\n bold: BOLD_CLASS,\n italic: ITALIC_CLASS,\n code: INLINE_CODE_CLASS,\n },\n link: LINK_CLASS,\n table: TABLE_CLASS,\n tableRow: TABLE_ROW_CLASS,\n tableCell: TABLE_CELL_CLASS,\n tableCellHeader: TABLE_CELL_HEADER_CLASS,\n tableCellSelected: TABLE_CELL_SELECTED_CLASS,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport type { Handle } from \"~/core/DOM.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { ListenerBag } from \"~/core/ListenerBag.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Card } from \"~/layout/Card.js\";\nimport { CodeEditor } from \"~/component/editor/CodeEditor.js\";\nimport type { CodeEditorChange } from \"~/component/editor/CodeEditor.js\";\nimport { createEditor, FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection, $getRoot, $createParagraphNode } from \"lexical\";\nimport type { LexicalEditor, ElementNode } from \"lexical\";\nimport { $convertFromMarkdownString, $convertToMarkdownString, registerMarkdownShortcuts } from \"@lexical/markdown\";\nimport { registerRichText, $createHeadingNode, $createQuoteNode } from \"@lexical/rich-text\";\nimport type { HeadingTagType } from \"@lexical/rich-text\";\nimport { registerList, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND } from \"@lexical/list\";\nimport { $toggleLink } from \"@lexical/link\";\nimport { $createCodeNode } from \"@lexical/code\";\nimport { registerHistory, createEmptyHistoryState } from \"@lexical/history\";\nimport {\n registerTablePlugin, registerTableCellUnmergeTransform, registerTableSelectionObserver,\n $getTableCellNodeFromLexicalNode,\n $insertTableRowAtSelection, $deleteTableRowAtSelection,\n $insertTableColumnAtSelection, $deleteTableColumnAtSelection,\n INSERT_TABLE_COMMAND,\n} from \"@lexical/table\";\nimport { mergeRegister } from \"@lexical/utils\";\nimport { $setBlocksType } from \"@lexical/selection\";\nimport { TRANSFORMERS } from \"~/component/editor/markdownTransformers.js\";\nimport { EDITOR_NODES } from \"~/component/editor/editorNodes.js\";\nimport { EDITOR_THEME, ensureMarkdownEditorClassRules } from \"~/component/editor/editorTheme.js\";\n\n/**\n * The coalescing window (ms) for the undo/redo history: edits within this gap\n * merge into a single undo step, matching Lexical's own recommended default.\n */\nconst HISTORY_DELAY_MS = 300;\n\n/**\n * Payload of {@link MarkdownEditor}'s `\"change\"` event: the Markdown string\n * after the edit that triggered it.\n *\n * @category Components\n */\nexport interface MarkdownEditorChange {\n value: string;\n}\n\n/** The event {@link MarkdownEditor} exposes through its custom `on` / `off` surface. */\ntype MarkdownEditorEvent = \"change\";\n\n/**\n * The editing surface a {@link MarkdownEditor} currently shows: the WYSIWYG\n * rich-text surface, or the raw-Markdown source [`CodeEditor`](/api/component/editor/classes/CodeEditor).\n *\n * @category Components\n */\nexport type MarkdownEditorMode = \"wysiwyg\" | \"source\";\n\n/**\n * A block type accepted by {@link MarkdownEditor.setBlockType}: a paragraph, one\n * of the six heading levels, a blockquote, or a fenced code block.\n *\n * @category Components\n */\nexport type MarkdownBlockType = \"paragraph\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"quote\" | \"code\";\n\n/**\n * Construction-time options for {@link MarkdownEditor}.\n *\n * @category Components\n */\nexport interface MarkdownEditorOptions extends ComponentOptions {\n /** Initial Markdown source (also accepted as the positional first argument). */\n value?: string;\n /** Whether the editor is read-only. Default `false`. */\n readOnly?: boolean;\n /** Which surface is shown. Default `\"wysiwyg\"`. */\n mode?: MarkdownEditorMode;\n /** Construction-time listener bag; the only event is `\"change\"`. */\n listeners?: { change?: (payload: MarkdownEditorChange) => void };\n}\n\n/**\n * The `onError` handler for the Lexical editor: rethrows so a node/transform\n * fault surfaces loudly rather than being swallowed.\n *\n * @param error - The error Lexical reports.\n */\nfunction reportEditorError(error: Error): void {\n throw error;\n}\n\n/**\n * Builds the Lexical block node a {@link MarkdownEditor.setBlockType} call\n * converts the selected blocks into.\n *\n * @param type - The requested block type.\n * @returns A freshly-created element node of that type.\n */\nfunction createBlockNode(type: MarkdownBlockType): ElementNode {\n switch (type) {\n case \"quote\": return $createQuoteNode();\n case \"code\": return $createCodeNode();\n case \"paragraph\": return $createParagraphNode();\n default: return $createHeadingNode(type satisfies HeadingTagType);\n }\n}\n\n/**\n * Whether the caret currently sits inside a table cell — the precondition for\n * the row/column helpers, which throw rather than no-op when it does not.\n *\n * @returns Whether the current selection is a range selection anchored inside\n * a table cell.\n */\nfunction $selectionIsInTableCell(): boolean {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) {\n return false;\n }\n\n return $getTableCellNodeFromLexicalNode(selection.anchor.getNode()) !== null;\n}\n\n/**\n * The private WYSIWYG editing surface: the element Lexical's `contenteditable`\n * view mounts into.\n *\n * @remarks\n * Split out from {@link MarkdownEditor} so the coordinator's own element can host\n * *both* this surface and the source {@link CodeEditor} as two [`Card`](/api/layout/classes/Card)\n * children — one DOM element per class, since Lexical's `contenteditable` and\n * CodeMirror's view are two foreign live widgets that each need their own host\n * element. `MarkdownEditor` keeps ownership of the Lexical editor object; only\n * the mount element (and its `contenteditable` / caret plumbing) lives here.\n */\nclass WysiwygSurface extends Component {\n\n /** Whether this surface's element hosts an editable region (`contenteditable`). */\n private _contentEditable: boolean = false;\n\n /** Invoked on the surface's first connected + sized layout — the moment to mount the live view. */\n private readonly _onReady: () => void;\n\n /**\n * Constructs the WYSIWYG surface.\n *\n * @param onReady - Called on the surface's first connected + sized layout.\n */\n constructor(onReady: () => void) {\n super();\n\n this._onReady = onReady;\n\n this.setContentEditable(true);\n // Fills its host box and scrolls internally when the document overflows.\n this.setOverflow(\"auto\");\n // A few pixels of inset off the padding box: without it the caret sits\n // flush against the surface's left edge (at column 0 of any line) and\n // gets clipped to invisibility. The padding is on the scroll container,\n // so it clears the caret at every edge while overflow scrolling still\n // reveals the full document. The values match source mode's CodeMirror\n // text inset so toggling modes doesn't shift the prose: 4px vertical\n // (CodeMirror's `.cm-content` padding is `4px 0`) and 6px horizontal\n // (its `.cm-line` padding-left is 6px).\n this.setPadding(new Insets(4, 6, 4, 6));\n // Text caret over the whole surface: signals editability, and — paired\n // with the `user-select: text` Lexical stamps on the root — keeps the\n // surface select-and-copy-able even in read-only mode. `setCursor` caches\n // in `_options.cursor`, which `applyStyle` replays, so it survives both\n // detached construction and Lexical's mount.\n this.setCursor(\"text\");\n\n this.onFirstLayout(() => this._onReady());\n }\n\n /**\n * Replays the cached `contenteditable` state onto the freshly-created\n * element. `setContentEditable` (called during detached construction, before\n * an element exists) caches into `_contentEditable`, and its underlying\n * `setElementAttribute` now also caches into the base class's\n * `_elementAttributes` map and replays it from `Component.init()`, so this\n * replay is redundant — kept anyway for the reason below.\n *\n * The write targets the `element` handle directly — as the base class does\n * for its own cached attributes — because at init time the element is not yet\n * in the document, so a `getElement()`-based setter would miss it.\n *\n * @param element - Optional element to initialise; falls back to `getElement()`.\n * @returns This surface, for method chaining.\n */\n protected init(element?: Handle): this {\n super.init(element);\n\n const el = element ?? this.getElement();\n\n if (el) {\n DOM.sink.apply(el, { setAttr: { contenteditable: this._contentEditable ? \"true\" : \"false\" } });\n }\n\n return this;\n }\n\n /**\n * Sets whether this surface's element hosts an editable region. Caches the\n * state in `_contentEditable` and writes the `contenteditable` attribute\n * through to the element. The underlying `setElementAttribute` caches and\n * replays the value itself now; {@link WysiwygSurface.init} also replays\n * the cached `_contentEditable` state directly onto the element once it is\n * created, redundantly but for the `getElement()`-timing reason documented\n * there.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This surface, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._contentEditable = contentEditable;\n this.setElementAttribute(\"contenteditable\", contentEditable ? \"true\" : \"false\");\n\n return this;\n }\n\n /**\n * Returns whether this surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._contentEditable;\n }\n\n /**\n * Mounts the given Lexical editor's editable view into this surface's element\n * through the DOM seam's `mountView` escape. No-ops when the view is already\n * mounted, the element is not yet available, or offline (the seam returns\n * `null`, leaving the editor headless).\n *\n * @param editor - The Lexical editor whose view to mount here.\n */\n mount(editor: LexicalEditor): void {\n if (editor.getRootElement()) {\n return;\n }\n\n const element = this.getElement();\n\n if (!element) {\n return;\n }\n\n ensureMarkdownEditorClassRules();\n\n // The factory parameter is left unannotated: its `HTMLElement` type is\n // inferred from the seam signature, so no DOM type is named here and the\n // `no-raw-dom` *hold* clause stays green. `setRootElement` accepts the\n // element structurally.\n DOM.sink.mountView(element, (root) => { editor.setRootElement(root); return root; });\n }\n}\n\n/**\n * A WYSIWYG rich-text editor whose public value is a Markdown string, with an\n * optional raw-Markdown source mode.\n *\n * @remarks\n * `MarkdownEditor` edits a document as rendered rich text — no visible markup —\n * and reads/writes Markdown through Lexical's bidirectional converters, so it is\n * the editing counterpart to the read-only\n * [`Markdown`](/api/component/display/classes/Markdown) viewer. Its dialect is\n * deliberately the **exact subset** the viewer renders (headings, paragraphs,\n * bold, italic, inline code, ordered/unordered lists, blockquotes, fenced code,\n * links, and GFM pipe tables with per-column alignment); a curated transformer\n * list — not Lexical's full preset — guarantees the editor can never emit\n * Markdown the viewer would drop to plain text, so an edited document renders\n * identically in the viewer.\n *\n * Formatting is driven three ways, all without a built-in toolbar: Markdown\n * shortcut typing (`# ` → heading, `**b**` → bold, `- ` → list, `> ` → quote,\n * ` ``` ` → code), the default keyboard shortcuts (Ctrl/Cmd+B / +I, undo/redo),\n * and a thin imperative command API (`toggleBold`, `setBlockType`,\n * `toggleUnorderedList`, `toggleLink`, `insertTable`,\n * `insertTableRow`/`deleteTableRow`, `insertTableColumn`/`deleteTableColumn`,\n * …) a consumer can wire to their own `Button`s.\n *\n * A {@link MarkdownEditor.setMode | mode} (`\"wysiwyg\"` | `\"source\"`) swaps the\n * editing surface between that rich-text view and a raw-Markdown\n * [`CodeEditor`](/api/component/editor/classes/CodeEditor); both are bound to the\n * same Markdown value, so `getValue`/`setValue` and the `\"change\"` event behave\n * identically in either mode and switching modes preserves the document. Like the\n * command API, the mode toggle is **consumer-wired** — there is no built-in\n * chrome — so a consumer drives `setMode` from their own control.\n *\n * Internally the component's own element hosts two swappable child surfaces\n * through a [`Card`](/api/layout/classes/Card) layout: a private WYSIWYG surface owning Lexical's\n * `contenteditable` element (a *foreign live widget*, like the `EditorView`\n * behind `CodeEditor`) and the source `CodeEditor`. Lexical separates its editor\n * **state** (a pure, DOM-free immutable tree) from that view, so the Markdown\n * value get/set/round-trip runs headless: offline neither view attaches, yet\n * `getValue` / `setValue`, the command API, and mode switching still operate on\n * the state. The editor fills its host box and scrolls internally; give it a\n * sized host (a `Fit` panel or an explicit `preferredSize`), the same as\n * `CodeEditor`.\n *\n * @example\n * ```typescript\n * import { MarkdownEditor } from '@jimka/typescript-ui/component/editor';\n *\n * const editor = new MarkdownEditor('# Title\\n\\nSome **bold** text.');\n * panel.addComponent(editor);\n * editor.on('change', ({ value }) => console.log(value));\n * editor.setMode('source'); // switch to raw-Markdown editing\n * ```\n *\n * @category Components\n */\nclass MarkdownEditor extends Component<MarkdownEditorOptions> {\n\n /**\n * The Lexical editor, built lazily (headless) on first use or first layout;\n * `null` until then. The editor is DOM-free until its root element mounts,\n * so this is non-null and fully usable offline once built.\n */\n private _editor: LexicalEditor | null = null;\n\n /** The `mergeRegister` teardown for the rich-text / list / history / shortcut / update registrations. */\n private _unregister: (() => void) | null = null;\n\n /**\n * The teardown for {@link registerTableSelectionObserver}, registered only\n * once the WYSIWYG view actually mounts (its mutation listener needs a real\n * table element in the document) and cleared on {@link dispose}.\n */\n private _unregisterTableView: (() => void) | null = null;\n\n /** Custom-event fan-out for `\"change\"`. */\n private readonly _listeners: ListenerBag<MarkdownEditorEvent> = new ListenerBag<MarkdownEditorEvent>();\n\n /** The Card layout swapping the visible editing surface between WYSIWYG and source. */\n private readonly _card: Card;\n\n /** The WYSIWYG surface hosting Lexical's `contenteditable` view. */\n private readonly _wysiwyg: WysiwygSurface;\n\n /** The raw-Markdown source surface, shown in `\"source\"` mode. */\n private readonly _codeEditor: CodeEditor;\n\n /**\n * Constructs a Markdown editor.\n *\n * @param value - Initial Markdown source (optional; defaults to `\"\"`).\n * @param options - Optional construction options.\n */\n constructor(value?: string, options?: MarkdownEditorOptions) {\n super(options);\n\n // Positional argument: cache it only when the caller didn't also pass\n // `options.value` (which the super-time cascade already stored).\n if (value !== undefined && this._options.value === undefined) {\n this._options.value = value;\n }\n\n // The component's own element hosts two swappable surfaces via a Card\n // (one DOM element per class): the WYSIWYG contenteditable and the source\n // CodeEditor. The Card shows exactly one at a time.\n this._card = new Card();\n this.setLayoutManager(this._card);\n\n this._wysiwyg = new WysiwygSurface(() => this.mountWysiwyg());\n this._codeEditor = new CodeEditor(this._options.value ?? \"\", {\n language: \"markdown\",\n readOnly: this._options.readOnly ?? false,\n listeners: { change: (payload) => this.handleCodeChange(payload) },\n });\n\n this.addComponent(this._wysiwyg);\n this.addComponent(this._codeEditor);\n\n // Pick the initial visible child from the mode.\n this._card.setVisibleComponentId(\n this.getMode() === \"source\" ? this._codeEditor.getId() : this._wysiwyg.getId());\n\n this.applyListeners(options?.listeners);\n }\n\n /**\n * Caches the `value` / `readOnly` / `mode` fields onto `_options` after\n * inherited Component fields cascade through `super.applyOptions`. The editor\n * is built later (lazily), so these are pure caches applied to the freshly-\n * created editor state / surfaces in the constructor and `ensureEditor`.\n *\n * @param options - The options bag carrying the values to apply.\n * @returns This component, for method chaining.\n */\n protected applyOptions(options: MarkdownEditorOptions): this {\n super.applyOptions(options);\n\n if (options.value !== undefined) this._options.value = options.value;\n if (options.readOnly !== undefined) this._options.readOnly = options.readOnly;\n if (options.mode !== undefined) this._options.mode = options.mode;\n\n return this;\n }\n\n /**\n * Returns the active editing surface mode.\n *\n * @returns `\"source\"` when the raw-Markdown surface is shown, else `\"wysiwyg\"`.\n */\n getMode(): MarkdownEditorMode {\n return this._options.mode ?? \"wysiwyg\";\n }\n\n /**\n * Switches the active editing surface. Converts the current document across\n * the surfaces so no edits are lost — the outgoing surface's Markdown is read\n * (via {@link MarkdownEditor.getValue}) before the mode flips, then loaded\n * into the incoming surface — and swaps the visible [`Card`](/api/layout/classes/Card) child.\n * No-op (no conversion, no `\"change\"`) when already in `mode`.\n *\n * @param mode - The surface to show.\n * @returns This component, for method chaining.\n */\n setMode(mode: MarkdownEditorMode): this {\n if (this.getMode() === mode) {\n return this;\n }\n\n // Read the canonical Markdown from the OUTGOING surface before the flip.\n const markdown = this.getValue();\n this._options.mode = mode;\n\n if (mode === \"source\") {\n this._codeEditor.setValue(markdown);\n this._card.setVisibleComponentId(this._codeEditor.getId());\n } else {\n this.ensureEditor().update(\n () => $convertFromMarkdownString(markdown, TRANSFORMERS), { discrete: true });\n this._card.setVisibleComponentId(this._wysiwyg.getId());\n }\n\n return this;\n }\n\n /**\n * Returns the current document as a Markdown string, from whichever surface\n * is active: the raw source text in `\"source\"` mode, else converted live from\n * the Lexical editor state (or the cached pre-mount / offline value).\n *\n * @returns The Markdown source, or `\"\"` when unset.\n */\n getValue(): string {\n if (this.getMode() === \"source\") {\n return this._codeEditor.getValue();\n }\n\n const editor = this._editor;\n\n if (editor) {\n return editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n }\n\n return this._options.value ?? \"\";\n }\n\n /**\n * Replaces the whole document from a Markdown string, targeting the active\n * surface: the source `CodeEditor` in `\"source\"` mode, else the Lexical\n * editor (building it if needed). A resulting content change fires `\"change\"`.\n *\n * @param value - The new Markdown source.\n * @returns This component, for method chaining.\n */\n setValue(value: string): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.setValue(value);\n\n return this;\n }\n\n // Discrete forces a synchronous commit, so the resulting `\"change\"`\n // fires before this returns rather than on a later flush.\n this.ensureEditor().update(() => $convertFromMarkdownString(value, TRANSFORMERS), { discrete: true });\n\n return this;\n }\n\n /**\n * Returns whether the editor currently rejects edits.\n *\n * @returns The read-only state.\n */\n getReadOnly(): boolean {\n return this._options.readOnly ?? false;\n }\n\n /**\n * Sets whether the editor rejects edits, on **both** surfaces. Caches the\n * state; when the Lexical editor exists, toggles its editable flag (which\n * also drops the caret / editing affordances on the mounted view); and\n * forwards to the source `CodeEditor`.\n *\n * @param readOnly - Whether the editor should reject edits.\n * @returns This component, for method chaining.\n */\n setReadOnly(readOnly: boolean): this {\n this._options.readOnly = readOnly;\n this._editor?.setEditable(!readOnly);\n this._codeEditor.setReadOnly(readOnly);\n\n return this;\n }\n\n /**\n * Moves keyboard focus into the active editing surface, placing a caret\n * ready for typing: the Lexical contenteditable in WYSIWYG mode (building\n * and mounting the editor if needed), or the source `CodeEditor` in\n * `\"source\"` mode.\n *\n * Overrides {@link core!Component.focus}, whose default focuses this component's\n * own host element — the `Card` wrapper — which would leave the caret out of\n * the nested contenteditable the typing must land in. In WYSIWYG mode it\n * defers to Lexical's own `focus`, which both focuses the root element and\n * sets a selection so a caret appears without a click.\n *\n * @param preventScroll - Forwarded to the source `CodeEditor` in `\"source\"`\n * mode; ignored in WYSIWYG mode, where Lexical manages its own focus scroll.\n * @returns This component, for method chaining.\n */\n focus(preventScroll: boolean = false): this {\n if (this.getMode() === \"source\") {\n this._codeEditor.focus(preventScroll);\n } else {\n this.ensureEditor().focus();\n }\n\n return this;\n }\n\n /**\n * Toggles bold on the current selection. No-op (without throwing) when there\n * is no range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleBold(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"bold\");\n\n return this;\n }\n\n /**\n * Toggles italic on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleItalic(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"italic\");\n\n return this;\n }\n\n /**\n * Toggles inline code on the current selection. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleInlineCode(): this {\n this.ensureEditor().dispatchCommand(FORMAT_TEXT_COMMAND, \"code\");\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an unordered (bulleted) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleUnorderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Converts the selected blocks into an ordered (numbered) list, or out of\n * one when already a list. No-op without a range selection.\n *\n * @returns This component, for method chaining.\n */\n toggleOrderedList(): this {\n this.ensureEditor().dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);\n\n return this;\n }\n\n /**\n * Wraps the current selection in a link to `url`, or unwraps it when `url`\n * is `null`. No-op without a range selection.\n *\n * @param url - The link target, or `null` to remove the link.\n * @returns This component, for method chaining.\n */\n toggleLink(url: string | null): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $toggleLink(url);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Converts the selected blocks to `type` (paragraph, `h1`–`h6`, blockquote,\n * or fenced code). No-op without a range selection.\n *\n * @param type - The target block type.\n * @returns This component, for method chaining.\n */\n setBlockType(type: MarkdownBlockType): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => createBlockNode(type));\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a table of `rows` rows by `columns` columns at the caret. The\n * first row is the header row, so `insertTable(2, 3)` gives a header row\n * plus one body row. The caret lands in the first header cell.\n *\n * @param rows - The number of rows, including the header row.\n * @param columns - The number of columns.\n * @returns This component, for method chaining.\n */\n insertTable(rows: number, columns: number): this {\n const editor = this.ensureEditor();\n\n // INSERT_TABLE_COMMAND inserts at the selection; a freshly built editor\n // may have none, so seed one first.\n editor.update(() => {\n if (!$isRangeSelection($getSelection())) {\n $getRoot().selectEnd();\n }\n }, { discrete: true });\n\n editor.dispatchCommand(INSERT_TABLE_COMMAND, {\n columns: String(columns),\n rows: String(rows),\n includeHeaders: { rows: true, columns: false },\n });\n\n return this;\n }\n\n /**\n * Inserts a row after (default) or before the row holding the caret.\n * No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current row.\n * @returns This component, for method chaining.\n */\n insertTableRow(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableRowAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the row holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableRow(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableRowAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Inserts a column after (default) or before the column holding the\n * caret. No-op without throwing when the caret is not inside a table cell.\n *\n * @param after - Whether to insert after (`true`, default) or before\n * (`false`) the current column.\n * @returns This component, for method chaining.\n */\n insertTableColumn(after: boolean = true): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $insertTableColumnAtSelection(after);\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Deletes the column holding the caret. No-op without throwing when the\n * caret is not inside a table cell.\n *\n * @returns This component, for method chaining.\n */\n deleteTableColumn(): this {\n const editor = this.ensureEditor();\n\n editor.update(() => {\n if ($selectionIsInTableCell()) {\n $deleteTableColumnAtSelection();\n }\n }, { discrete: true });\n\n return this;\n }\n\n /**\n * Registers a listener for the `\"change\"` event, fired whenever the document\n * content changes (typing, a command, or {@link MarkdownEditor.setValue}) in\n * whichever surface is active.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - Invoked with the new Markdown value.\n * @returns This component, for method chaining.\n */\n on(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered `\"change\"` listener.\n *\n * @param event - Must be `\"change\"`.\n * @param listener - The exact callback reference to remove.\n * @returns This component, for method chaining.\n */\n off(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Fans the `\"change\"` event out to its registered listeners.\n *\n * @param event - Must be `\"change\"`.\n * @param payload - The event payload.\n */\n protected emit(event: MarkdownEditorEvent, payload: MarkdownEditorChange): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Sets whether the WYSIWYG surface's element hosts an editable region.\n * Forwards to the private surface, preserving the public setter.\n *\n * @param contentEditable - Whether the element is contenteditable.\n * @returns This component, for method chaining.\n */\n setContentEditable(contentEditable: boolean): this {\n this._wysiwyg.setContentEditable(contentEditable);\n\n return this;\n }\n\n /**\n * Returns whether the WYSIWYG surface's element hosts an editable region.\n *\n * @returns The contenteditable state.\n */\n getContentEditable(): boolean {\n return this._wysiwyg.getContentEditable();\n }\n\n /**\n * Detaches the Lexical registrations and the WYSIWYG surface's live view,\n * then defers to the base class for the rest of teardown — including\n * disposing the registered `_codeEditor` child, which mirrors\n * `CodeEditor.destructor`. Call before discarding a dynamically-built\n * `MarkdownEditor`.\n */\n protected destructor(): void {\n this._unregisterTableView?.();\n this._unregister?.();\n this._editor?.setRootElement(null);\n\n // `_codeEditor` is registered via `addComponent`, so\n // `super.destructor()`'s child recursion below already disposes it —\n // an explicit call here would run `CodeEditor.destructor()` a second\n // time.\n super.destructor();\n }\n\n /**\n * Builds the headless Lexical editor on first use. Idempotent: the editor is\n * built once. Offline the editor stays headless — the state, and every value\n * / command operation, still work.\n *\n * @returns The live (headless-capable) Lexical editor.\n */\n private ensureEditor(): LexicalEditor {\n if (!this._editor) {\n const editor = createEditor({ nodes: EDITOR_NODES, theme: EDITOR_THEME, onError: reportEditorError });\n\n // Populate the initial state before registering the update listener,\n // so loading the cached value does not fire a spurious `\"change\"`.\n editor.update(() => $convertFromMarkdownString(this._options.value ?? \"\", TRANSFORMERS), { discrete: true });\n editor.setEditable(!(this._options.readOnly ?? false));\n\n this._unregister = mergeRegister(\n registerRichText(editor),\n registerList(editor),\n registerTablePlugin(editor),\n registerTableCellUnmergeTransform(editor),\n registerHistory(editor, createEmptyHistoryState(), HISTORY_DELAY_MS),\n registerMarkdownShortcuts(editor, TRANSFORMERS),\n editor.registerUpdateListener(() => this.handleChange()),\n );\n\n this._editor = editor;\n }\n\n return this._editor;\n }\n\n /**\n * Builds the headless editor (if needed) and mounts its live view into the\n * WYSIWYG surface. Driven by the surface's first connected layout, i.e. the\n * first time it is the visible `Card` child and gets sized.\n */\n private mountWysiwyg(): void {\n const editor = this.ensureEditor();\n\n this._wysiwyg.mount(editor);\n\n // Offline the seam's mountView returns null and the root stays unset;\n // the observer's mutation listener needs a real table element in the\n // document and throws without one, so it is view-time only.\n if (editor.getRootElement() && !this._unregisterTableView) {\n this._unregisterTableView = registerTableSelectionObserver(editor, true);\n }\n }\n\n /**\n * Recomputes the Markdown value from the committed editor state after a\n * Lexical update and, when it differs from the cached value (i.e. the content\n * — not merely the selection — changed), caches it and emits `\"change\"`.\n */\n private handleChange(): void {\n const editor = this._editor;\n\n if (!editor) {\n return;\n }\n\n const value = editor.read(() => $convertToMarkdownString(TRANSFORMERS));\n\n if (value === this._options.value) {\n return;\n }\n\n this._options.value = value;\n this.emit(\"change\", { value });\n }\n\n /**\n * Recomputes the cached value from a source-surface (`CodeEditor`) edit and,\n * when it differs from the cached value, caches it and emits `\"change\"`. The\n * equality guard makes the programmatic `setValue` on a mode switch not\n * double-emit (it loads the value already equal to `_options.value`).\n *\n * @param payload - The `CodeEditor` change payload (the new source text).\n */\n private handleCodeChange(payload: CodeEditorChange): void {\n if (payload.value === this._options.value) {\n return;\n }\n\n this._options.value = payload.value;\n this.emit(\"change\", { value: payload.value });\n }\n}\n\nconst MarkdownEditorCallable = callable(MarkdownEditor);\ntype MarkdownEditorCallable = MarkdownEditor;\nexport {\n MarkdownEditor as _MarkdownEditor,\n MarkdownEditorCallable as MarkdownEditor,\n};\n"],"mappings":"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"}