@lukso/web-components 1.154.0 → 1.155.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/README.md +12 -0
  2. package/dist/attach-square-BDYuLWN1.js +3 -0
  3. package/dist/attach-square-BEkoBfpV.js +3 -0
  4. package/dist/attach-square-BF4bK2lN.cjs +7 -0
  5. package/dist/attach-square-B_oIBENK.js +3 -0
  6. package/dist/attach-square-CMjY_cQG.cjs +7 -0
  7. package/dist/attach-square-CPsLNqIT.cjs +7 -0
  8. package/dist/attach-square-Ch9_iq5a.js +3 -0
  9. package/dist/attach-square-D0YgLg2d.cjs +7 -0
  10. package/dist/attach-square-DGJ30O7H.js +3 -0
  11. package/dist/attach-square-DHjVapDw.cjs +7 -0
  12. package/dist/attach-square-Dtfj1Edy.js +3 -0
  13. package/dist/attach-square-nLmVYCe2.cjs +7 -0
  14. package/dist/components/index.cjs +14 -4
  15. package/dist/components/index.d.ts +2 -0
  16. package/dist/components/index.d.ts.map +1 -1
  17. package/dist/components/index.js +6 -4
  18. package/dist/components/lukso-button/index.cjs +3 -3
  19. package/dist/components/lukso-button/index.js +3 -3
  20. package/dist/components/lukso-card/index.cjs +7 -7
  21. package/dist/components/lukso-card/index.js +7 -7
  22. package/dist/components/lukso-checkbox/index.cjs +3 -3
  23. package/dist/components/lukso-checkbox/index.js +3 -3
  24. package/dist/components/lukso-collapse/index.cjs +5 -11
  25. package/dist/components/lukso-collapse/index.js +4 -10
  26. package/dist/components/lukso-color-picker/index.cjs +4 -4
  27. package/dist/components/lukso-color-picker/index.js +4 -4
  28. package/dist/components/lukso-dropdown/index.cjs +11 -7
  29. package/dist/components/lukso-dropdown/index.d.ts +1 -0
  30. package/dist/components/lukso-dropdown/index.d.ts.map +1 -1
  31. package/dist/components/lukso-dropdown/index.js +11 -7
  32. package/dist/components/lukso-dropdown-option/index.cjs +2 -2
  33. package/dist/components/lukso-dropdown-option/index.js +2 -2
  34. package/dist/components/lukso-footer/index.cjs +2 -2
  35. package/dist/components/lukso-footer/index.js +2 -2
  36. package/dist/components/lukso-icon/index.cjs +136 -18
  37. package/dist/components/lukso-icon/index.d.ts +24 -0
  38. package/dist/components/lukso-icon/index.d.ts.map +1 -1
  39. package/dist/components/lukso-icon/index.js +136 -18
  40. package/dist/components/lukso-icon/lukso-icon.stories.d.ts +5 -0
  41. package/dist/components/lukso-icon/lukso-icon.stories.d.ts.map +1 -1
  42. package/dist/components/lukso-icon/vuesax/bold/attach-square.svg +3 -0
  43. package/dist/components/lukso-icon/vuesax/bold/eye-slash.svg +7 -0
  44. package/dist/components/lukso-icon/vuesax/bold/eye.svg +4 -0
  45. package/dist/components/lukso-icon/vuesax/bold/link.svg +5 -0
  46. package/dist/components/lukso-icon/vuesax/bold/smallcaps.svg +3 -0
  47. package/dist/components/lukso-icon/vuesax/bold/task.svg +8 -0
  48. package/dist/components/lukso-icon/vuesax/bold/text-bold.svg +5 -0
  49. package/dist/components/lukso-icon/vuesax/bold/text-italic.svg +3 -0
  50. package/dist/components/lukso-icon/vuesax/bold/textalign-center.svg +6 -0
  51. package/dist/components/lukso-icon/vuesax/bold/textalign-justifycenter.svg +6 -0
  52. package/dist/components/lukso-icon/vuesax/bold/textalign-left.svg +6 -0
  53. package/dist/components/lukso-icon/vuesax/bold/textalign-right.svg +6 -0
  54. package/dist/components/lukso-icon/vuesax/broken/attach-square.svg +4 -0
  55. package/dist/components/lukso-icon/vuesax/broken/eye-slash.svg +9 -0
  56. package/dist/components/lukso-icon/vuesax/broken/eye.svg +4 -0
  57. package/dist/components/lukso-icon/vuesax/broken/link.svg +6 -0
  58. package/dist/components/lukso-icon/vuesax/broken/smallcaps.svg +9 -0
  59. package/dist/components/lukso-icon/vuesax/broken/task.svg +9 -0
  60. package/dist/components/lukso-icon/vuesax/broken/text-bold.svg +4 -0
  61. package/dist/components/lukso-icon/vuesax/broken/text-italic.svg +6 -0
  62. package/dist/components/lukso-icon/vuesax/broken/textalign-center.svg +7 -0
  63. package/dist/components/lukso-icon/vuesax/broken/textalign-justifycenter.svg +7 -0
  64. package/dist/components/lukso-icon/vuesax/broken/textalign-left.svg +7 -0
  65. package/dist/components/lukso-icon/vuesax/broken/textalign-right.svg +7 -0
  66. package/dist/components/lukso-icon/vuesax/bulk/attach-square.svg +4 -0
  67. package/dist/components/lukso-icon/vuesax/bulk/eye-slash.svg +7 -0
  68. package/dist/components/lukso-icon/vuesax/bulk/frame.svg +4 -0
  69. package/dist/components/lukso-icon/vuesax/bulk/link.svg +5 -0
  70. package/dist/components/lukso-icon/vuesax/bulk/smallcaps.svg +5 -0
  71. package/dist/components/lukso-icon/vuesax/bulk/task.svg +8 -0
  72. package/dist/components/lukso-icon/vuesax/bulk/text-bold.svg +4 -0
  73. package/dist/components/lukso-icon/vuesax/bulk/text-italic.svg +4 -0
  74. package/dist/components/lukso-icon/vuesax/bulk/textalign-center.svg +6 -0
  75. package/dist/components/lukso-icon/vuesax/bulk/textalign-justifycenter.svg +6 -0
  76. package/dist/components/lukso-icon/vuesax/bulk/textalign-left.svg +6 -0
  77. package/dist/components/lukso-icon/vuesax/bulk/textalign-right.svg +6 -0
  78. package/dist/components/lukso-icon/vuesax/linear/attach-square.svg +4 -0
  79. package/dist/components/lukso-icon/vuesax/linear/eye-slash.svg +8 -0
  80. package/dist/components/lukso-icon/vuesax/linear/eye.svg +4 -0
  81. package/dist/components/lukso-icon/vuesax/linear/link.svg +5 -0
  82. package/dist/components/lukso-icon/vuesax/linear/smallcaps.svg +8 -0
  83. package/dist/components/lukso-icon/vuesax/linear/task.svg +8 -0
  84. package/dist/components/lukso-icon/vuesax/linear/text-bold.svg +4 -0
  85. package/dist/components/lukso-icon/vuesax/linear/text-italic.svg +5 -0
  86. package/dist/components/lukso-icon/vuesax/linear/textalign-center.svg +6 -0
  87. package/dist/components/lukso-icon/vuesax/linear/textalign-justifycenter.svg +6 -0
  88. package/dist/components/lukso-icon/vuesax/linear/textalign-left.svg +6 -0
  89. package/dist/components/lukso-icon/vuesax/linear/textalign-right.svg +6 -0
  90. package/dist/components/lukso-icon/vuesax/outline/attach-square.svg +4 -0
  91. package/dist/components/lukso-icon/vuesax/outline/eye-slash.svg +8 -0
  92. package/dist/components/lukso-icon/vuesax/outline/eye.svg +4 -0
  93. package/dist/components/lukso-icon/vuesax/outline/link.svg +5 -0
  94. package/dist/components/lukso-icon/vuesax/outline/smallcaps.svg +8 -0
  95. package/dist/components/lukso-icon/vuesax/outline/task.svg +8 -0
  96. package/dist/components/lukso-icon/vuesax/outline/text-bold.svg +4 -0
  97. package/dist/components/lukso-icon/vuesax/outline/text-italic.svg +5 -0
  98. package/dist/components/lukso-icon/vuesax/outline/textalign-center.svg +6 -0
  99. package/dist/components/lukso-icon/vuesax/outline/textalign-justifycenter.svg +6 -0
  100. package/dist/components/lukso-icon/vuesax/outline/textalign-left.svg +6 -0
  101. package/dist/components/lukso-icon/vuesax/outline/textalign-right.svg +6 -0
  102. package/dist/components/lukso-icon/vuesax/twotone/attach-square.svg +4 -0
  103. package/dist/components/lukso-icon/vuesax/twotone/eye-slash.svg +8 -0
  104. package/dist/components/lukso-icon/vuesax/twotone/eye.svg +4 -0
  105. package/dist/components/lukso-icon/vuesax/twotone/link.svg +5 -0
  106. package/dist/components/lukso-icon/vuesax/twotone/smallcaps.svg +10 -0
  107. package/dist/components/lukso-icon/vuesax/twotone/task.svg +8 -0
  108. package/dist/components/lukso-icon/vuesax/twotone/text-bold.svg +4 -0
  109. package/dist/components/lukso-icon/vuesax/twotone/text-italic.svg +5 -0
  110. package/dist/components/lukso-icon/vuesax/twotone/textalign-center.svg +6 -0
  111. package/dist/components/lukso-icon/vuesax/twotone/textalign-justifycenter.svg +6 -0
  112. package/dist/components/lukso-icon/vuesax/twotone/textalign-left.svg +6 -0
  113. package/dist/components/lukso-icon/vuesax/twotone/textalign-right.svg +6 -0
  114. package/dist/components/lukso-image/index.cjs +4 -4
  115. package/dist/components/lukso-image/index.js +4 -4
  116. package/dist/components/lukso-input/index.cjs +3 -3
  117. package/dist/components/lukso-input/index.js +3 -3
  118. package/dist/components/lukso-markdown/index.cjs +168 -0
  119. package/dist/components/lukso-markdown/index.d.ts +22 -0
  120. package/dist/components/lukso-markdown/index.d.ts.map +1 -0
  121. package/dist/components/lukso-markdown/index.js +166 -0
  122. package/dist/components/lukso-markdown/lukso-markdown.stories.d.ts +13 -0
  123. package/dist/components/lukso-markdown/lukso-markdown.stories.d.ts.map +1 -0
  124. package/dist/components/lukso-markdown-editor/index.cjs +2008 -0
  125. package/dist/components/lukso-markdown-editor/index.d.ts +255 -0
  126. package/dist/components/lukso-markdown-editor/index.d.ts.map +1 -0
  127. package/dist/components/lukso-markdown-editor/index.js +2006 -0
  128. package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts +27 -0
  129. package/dist/components/lukso-markdown-editor/lukso-markdown-editor.stories.d.ts.map +1 -0
  130. package/dist/components/lukso-modal/index.cjs +2 -2
  131. package/dist/components/lukso-modal/index.js +2 -2
  132. package/dist/components/lukso-navbar/index.cjs +3 -3
  133. package/dist/components/lukso-navbar/index.js +3 -3
  134. package/dist/components/lukso-pagination/index.cjs +3 -3
  135. package/dist/components/lukso-pagination/index.js +3 -3
  136. package/dist/components/lukso-profile/index.cjs +3 -3
  137. package/dist/components/lukso-profile/index.js +3 -3
  138. package/dist/components/lukso-progress/index.cjs +3 -3
  139. package/dist/components/lukso-progress/index.js +3 -3
  140. package/dist/components/lukso-radio/index.cjs +3 -3
  141. package/dist/components/lukso-radio/index.js +3 -3
  142. package/dist/components/lukso-radio-group/index.cjs +3 -3
  143. package/dist/components/lukso-radio-group/index.js +3 -3
  144. package/dist/components/lukso-sanitize/index.cjs +4 -10
  145. package/dist/components/lukso-sanitize/index.js +4 -10
  146. package/dist/components/lukso-search/index.cjs +6 -6
  147. package/dist/components/lukso-search/index.d.ts.map +1 -1
  148. package/dist/components/lukso-search/index.js +6 -6
  149. package/dist/components/lukso-select/index.cjs +5 -5
  150. package/dist/components/lukso-select/index.js +5 -5
  151. package/dist/components/lukso-share/index.cjs +2 -2
  152. package/dist/components/lukso-share/index.js +2 -2
  153. package/dist/components/lukso-switch/index.cjs +3 -3
  154. package/dist/components/lukso-switch/index.js +3 -3
  155. package/dist/components/lukso-tag/index.cjs +3 -3
  156. package/dist/components/lukso-tag/index.js +3 -3
  157. package/dist/components/lukso-terms/index.cjs +3 -3
  158. package/dist/components/lukso-terms/index.js +3 -3
  159. package/dist/components/lukso-textarea/index.cjs +3 -3
  160. package/dist/components/lukso-textarea/index.js +3 -3
  161. package/dist/components/lukso-tooltip/index.cjs +4 -4
  162. package/dist/components/lukso-tooltip/index.js +4 -4
  163. package/dist/components/lukso-username/index.cjs +5 -5
  164. package/dist/components/lukso-username/index.js +5 -5
  165. package/dist/components/lukso-wizard/index.cjs +2 -2
  166. package/dist/components/lukso-wizard/index.js +2 -2
  167. package/dist/docs/VuesaxPack.stories.d.ts +6 -0
  168. package/dist/docs/VuesaxPack.stories.d.ts.map +1 -0
  169. package/dist/eye-BGOT4dkg.cjs +7 -0
  170. package/dist/eye-BJFvKUDu.cjs +7 -0
  171. package/dist/eye-BRB1atdh.js +3 -0
  172. package/dist/eye-Bf4VFl7a.js +3 -0
  173. package/dist/eye-CC9LXMQ1.js +3 -0
  174. package/dist/eye-CK1M03ez.cjs +7 -0
  175. package/dist/eye-CXBFLyYh.cjs +7 -0
  176. package/dist/eye-CcM-Nmq0.js +3 -0
  177. package/dist/eye-DP4zhXsm.cjs +7 -0
  178. package/dist/eye-DaGlp3S9.js +3 -0
  179. package/dist/eye-slash-4QawqpXV.js +3 -0
  180. package/dist/eye-slash-5K5tDUZr.cjs +7 -0
  181. package/dist/eye-slash-BnjvF1ME.cjs +7 -0
  182. package/dist/eye-slash-BxKMkFDP.js +3 -0
  183. package/dist/eye-slash-CZVpq_gN.cjs +7 -0
  184. package/dist/eye-slash-Chha_f6F.js +3 -0
  185. package/dist/eye-slash-CzBrFoLA.js +3 -0
  186. package/dist/eye-slash-DD4uAbjM.cjs +7 -0
  187. package/dist/eye-slash-DGM6ST4a.js +3 -0
  188. package/dist/eye-slash-DaVNSGPe.cjs +7 -0
  189. package/dist/eye-slash-iagfBjcI.cjs +7 -0
  190. package/dist/eye-slash-j5X2mtyc.js +3 -0
  191. package/dist/frame-BcRKV975.js +3 -0
  192. package/dist/frame-CHLzNIw4.cjs +7 -0
  193. package/dist/{index-6Bz9XVC3.js → index-1cnTXnRX.js} +4 -4
  194. package/dist/{index-DUwutUFV.js → index-B5_11hN3.js} +5 -5
  195. package/dist/index-BWp0TAbf.js +41 -0
  196. package/dist/{index-Dg9hcDqR.cjs → index-CO57mpzm.cjs} +5 -5
  197. package/dist/{index-gJTlTDGh.js → index-CsepkLbs.js} +1 -1
  198. package/dist/{index-m3XGqZFz.cjs → index-D-a6OWyj.cjs} +1 -1
  199. package/dist/{index-U4Y7KwZv.cjs → index-D4HqZcbk.cjs} +2 -2
  200. package/dist/{index-fSZGRljb.cjs → index-D9mdD_OM.cjs} +4 -4
  201. package/dist/{index-CvWyP3CS.js → index-DFculCCQ.js} +2 -2
  202. package/dist/index-KrWvJ44l.cjs +50 -0
  203. package/dist/index.cjs +14 -4
  204. package/dist/index.js +6 -4
  205. package/dist/{isAddress-B3b91Jxf.cjs → isAddress-7c5mkwjj.cjs} +1 -1
  206. package/dist/{isAddress-Dq9UN07g.js → isAddress-DyEmEF7O.js} +1 -1
  207. package/dist/link-BWjOQDxe.js +3 -0
  208. package/dist/link-C248CAet.cjs +7 -0
  209. package/dist/link-CaiWNqlz.cjs +7 -0
  210. package/dist/link-Ccxf5n-l.cjs +7 -0
  211. package/dist/link-CrnK4TCj.js +3 -0
  212. package/dist/link-CtsNQupk.cjs +7 -0
  213. package/dist/link-CxQZZHzv.js +3 -0
  214. package/dist/link-DCLn1IDs.cjs +7 -0
  215. package/dist/link-DUkoe5Km.js +3 -0
  216. package/dist/link-DkkL7LfC.js +3 -0
  217. package/dist/link-YlDhxD95.js +3 -0
  218. package/dist/link-ck4nib4k.cjs +7 -0
  219. package/dist/{property-BXmHod5d.cjs → property-CLMAG2Rj.cjs} +1 -1
  220. package/dist/{property-B4XYi6Sk.js → property-DkFGx5Fg.js} +1 -1
  221. package/dist/query-CHb9Ft_d.js +9 -0
  222. package/dist/query-EFiHeHdi.cjs +11 -0
  223. package/dist/shared/tailwind-element/index.cjs +1 -1
  224. package/dist/shared/tailwind-element/index.js +1 -1
  225. package/dist/smallcaps-4db6OP5z.cjs +7 -0
  226. package/dist/smallcaps-BRGmz5X1.js +3 -0
  227. package/dist/smallcaps-C3aFFEMg.cjs +7 -0
  228. package/dist/smallcaps-CQH0e-Qr.js +3 -0
  229. package/dist/smallcaps-CrTAH_fl.cjs +7 -0
  230. package/dist/smallcaps-Csx5r5fL.js +3 -0
  231. package/dist/smallcaps-D57em6lZ.js +3 -0
  232. package/dist/smallcaps-DMxoMhLq.cjs +7 -0
  233. package/dist/smallcaps-DTc90-wB.js +3 -0
  234. package/dist/smallcaps-DvQw3mgt.cjs +7 -0
  235. package/dist/smallcaps-EMohyYps.js +3 -0
  236. package/dist/smallcaps-tS0-FTm4.cjs +7 -0
  237. package/dist/{state-CkvZ4IP8.js → state-DPXXwNMf.js} +1 -1
  238. package/dist/{state-CW2YmM3f.cjs → state-DXBdLH-W.cjs} +1 -1
  239. package/dist/{style-map-c2S85yDu.cjs → style-map-BFG88xg5.cjs} +1 -1
  240. package/dist/{style-map-D1R4wi4h.js → style-map-CXXmrGLe.js} +1 -1
  241. package/dist/task-5wAtmLia.cjs +7 -0
  242. package/dist/task-BFFgtsR7.cjs +7 -0
  243. package/dist/task-CPHbx6Xp.js +3 -0
  244. package/dist/task-CPK0Xitw.js +3 -0
  245. package/dist/task-CWMoBPrR.js +3 -0
  246. package/dist/task-CZeuxLHS.cjs +7 -0
  247. package/dist/task-CpUWZ14w.cjs +7 -0
  248. package/dist/task-D2dHPypM.js +3 -0
  249. package/dist/task-DHjlHB5u.js +3 -0
  250. package/dist/task-INl8gJof.cjs +7 -0
  251. package/dist/task-YBjBROjF.cjs +7 -0
  252. package/dist/task-rAZ4_dJi.js +3 -0
  253. package/dist/text-bold-B9Uuus_K.js +3 -0
  254. package/dist/text-bold-Bpds56iN.cjs +7 -0
  255. package/dist/text-bold-BwjfWLnV.js +3 -0
  256. package/dist/text-bold-CGib54xr.js +3 -0
  257. package/dist/text-bold-D9E9gOwG.js +3 -0
  258. package/dist/text-bold-D9y0G3W6.cjs +7 -0
  259. package/dist/text-bold-DA18Cobf.cjs +7 -0
  260. package/dist/text-bold-FVlP-onb.js +3 -0
  261. package/dist/text-bold-dNsqnxrJ.cjs +7 -0
  262. package/dist/text-bold-hD59fjBM.cjs +7 -0
  263. package/dist/text-bold-q4PQl2lp.cjs +7 -0
  264. package/dist/text-bold-yLyZsv3k.js +3 -0
  265. package/dist/text-italic-BAQsDLCD.cjs +7 -0
  266. package/dist/text-italic-BBbdgOV8.js +3 -0
  267. package/dist/text-italic-BLRmyoa_.cjs +7 -0
  268. package/dist/text-italic-BRpMpFEP.cjs +7 -0
  269. package/dist/text-italic-BbSEOHaR.js +3 -0
  270. package/dist/text-italic-BbY_nLh3.cjs +7 -0
  271. package/dist/text-italic-BqvfDpZ_.js +3 -0
  272. package/dist/text-italic-COwZp8nN.js +3 -0
  273. package/dist/text-italic-D-86ejcb.js +3 -0
  274. package/dist/text-italic-D5L8cO7S.cjs +7 -0
  275. package/dist/text-italic-Dqj-AGrY.cjs +7 -0
  276. package/dist/text-italic-JVNLjL6A.js +3 -0
  277. package/dist/textalign-center-BNXKb7o1.js +3 -0
  278. package/dist/textalign-center-BgDrvLgu.js +3 -0
  279. package/dist/textalign-center-BiW7-UK8.cjs +7 -0
  280. package/dist/textalign-center-BjXTeLZ9.js +3 -0
  281. package/dist/textalign-center-CMfGM3nt.cjs +7 -0
  282. package/dist/textalign-center-CZUB_mi5.cjs +7 -0
  283. package/dist/textalign-center-DV1ZVVJ9.js +3 -0
  284. package/dist/textalign-center-DhIAARrX.cjs +7 -0
  285. package/dist/textalign-center-DssUllZx.cjs +7 -0
  286. package/dist/textalign-center-DuFr4Z0N.js +3 -0
  287. package/dist/textalign-center-TtJrRcy1.cjs +7 -0
  288. package/dist/textalign-center-XZiUcsoS.js +3 -0
  289. package/dist/textalign-justifycenter-1H0ry4i1.cjs +7 -0
  290. package/dist/textalign-justifycenter-B4Okp5WQ.js +3 -0
  291. package/dist/textalign-justifycenter-BanoqoY2.cjs +7 -0
  292. package/dist/textalign-justifycenter-BrM7y8Q-.cjs +7 -0
  293. package/dist/textalign-justifycenter-BvuL2Tc9.js +3 -0
  294. package/dist/textalign-justifycenter-ByaUTZOq.cjs +7 -0
  295. package/dist/textalign-justifycenter-C7GGAIEd.cjs +7 -0
  296. package/dist/textalign-justifycenter-C_15oG-O.js +3 -0
  297. package/dist/textalign-justifycenter-Cd3T_URi.js +3 -0
  298. package/dist/textalign-justifycenter-Cuq0nA7n.js +3 -0
  299. package/dist/textalign-justifycenter-DTB7BpEM.cjs +7 -0
  300. package/dist/textalign-justifycenter-GJYh78KA.js +3 -0
  301. package/dist/textalign-left-BBkJw9sM.js +3 -0
  302. package/dist/textalign-left-CQFeWNky.cjs +7 -0
  303. package/dist/textalign-left-CVLtezzT.cjs +7 -0
  304. package/dist/textalign-left-CrkiT20G.js +3 -0
  305. package/dist/textalign-left-Czv9gCWL.js +3 -0
  306. package/dist/textalign-left-DDV2YTsX.js +3 -0
  307. package/dist/textalign-left-DbbIELEG.js +3 -0
  308. package/dist/textalign-left-DmbcMnUt.cjs +7 -0
  309. package/dist/textalign-left-DwMDZkz6.cjs +7 -0
  310. package/dist/textalign-left-FcEEgVUa.js +3 -0
  311. package/dist/textalign-left-HOX6BrC8.cjs +7 -0
  312. package/dist/textalign-left-Tim7PD2T.cjs +7 -0
  313. package/dist/textalign-right-AzUv7Wy_.cjs +7 -0
  314. package/dist/textalign-right-B4RMoyPu.js +3 -0
  315. package/dist/textalign-right-Bh1GtPWc.cjs +7 -0
  316. package/dist/textalign-right-BwpxVvNc.js +3 -0
  317. package/dist/textalign-right-CKoFhqEE.js +3 -0
  318. package/dist/textalign-right-CQKHpAQn.cjs +7 -0
  319. package/dist/textalign-right-CSi5EldQ.js +3 -0
  320. package/dist/textalign-right-CaDWcT07.cjs +7 -0
  321. package/dist/textalign-right-CoIc3IEi.js +3 -0
  322. package/dist/textalign-right-D-peBkxs.cjs +7 -0
  323. package/dist/textalign-right-DxQZxeBV.cjs +7 -0
  324. package/dist/textalign-right-LtrQalb-.js +3 -0
  325. package/dist/unsafe-html-C4RqiLkE.js +10 -0
  326. package/dist/unsafe-html-CxwvRvgp.cjs +12 -0
  327. package/dist/vite.full.config.d.ts.map +1 -1
  328. package/package.json +11 -1
  329. package/tailwind.config.cjs +1 -0
  330. package/tools/copy-assets.cjs +16 -16
  331. package/tools/copy-assets.js +2 -2
  332. package/tools/index.cjs +1 -1
  333. package/tools/index.js +1 -1
  334. package/dist/index-C9vH8YlV.js +0 -41
  335. package/dist/index-DkfODalz.cjs +0 -50
@@ -0,0 +1,2006 @@
1
+ import { T as TailwindStyledElement, E, x } from '../../index-BWp0TAbf.js';
2
+ import { n, t } from '../../property-DkFGx5Fg.js';
3
+ import { r } from '../../state-DPXXwNMf.js';
4
+ import { e } from '../../query-CHb9Ft_d.js';
5
+ import { c as ce } from '../../index-B9iart53.js';
6
+ import '../../tailwind-config.js';
7
+ import { c as cn } from '../../cn-Cu9aP49j.js';
8
+ import '../lukso-textarea/index.js';
9
+ import '../lukso-markdown/index.js';
10
+ import '../lukso-switch/index.js';
11
+ import '../lukso-button/index.js';
12
+ import '../lukso-icon/index.js';
13
+ import '../lukso-sanitize/index.js';
14
+ import '../lukso-dropdown/index.js';
15
+ import '../lukso-dropdown-option/index.js';
16
+ import '../lukso-tooltip/index.js';
17
+
18
+ const style = ":host {\n display: flex\n}";
19
+
20
+ var __defProp = Object.defineProperty;
21
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
22
+ var __decorateClass = (decorators, target, key, kind) => {
23
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
24
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
25
+ if (decorator = decorators[i])
26
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
27
+ if (kind && result) __defProp(target, key, result);
28
+ return result;
29
+ };
30
+ let LuksoMarkdownEditor = class extends TailwindStyledElement(style) {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.value = "";
34
+ this.name = "";
35
+ this.label = "";
36
+ this.description = "";
37
+ this.error = "";
38
+ this.isFullWidth = false;
39
+ this.isReadonly = false;
40
+ this.isDisabled = false;
41
+ this.isNonResizable = false;
42
+ this.autofocus = false;
43
+ this.size = "large";
44
+ this.isPreview = false;
45
+ this.rows = 6;
46
+ this.placeholder = "";
47
+ this.savedSelectionForPreview = null;
48
+ this.isHeadingDropdownOpen = false;
49
+ this.isColorDropdownOpen = false;
50
+ this.isListDropdownOpen = false;
51
+ this.headingTriggerId = "heading-dropdown-trigger";
52
+ this.colorTriggerId = "color-dropdown-trigger";
53
+ this.listTriggerId = "list-dropdown-trigger";
54
+ this.currentSelection = { start: 0, end: 0 };
55
+ this.savedSelection = null;
56
+ this.defaultColor = "#374151";
57
+ this.activeFormats = {
58
+ bold: false,
59
+ italic: false,
60
+ link: false,
61
+ h1: false,
62
+ h2: false,
63
+ h3: false,
64
+ color: false,
65
+ activeColor: this.defaultColor,
66
+ unorderedList: false,
67
+ orderedList: false
68
+ };
69
+ // Undo/Redo state
70
+ this.undoStack = [];
71
+ this.redoStack = [];
72
+ this.isUndoRedoAction = false;
73
+ this.lastSavedValue = "";
74
+ this.undoTimeout = null;
75
+ this.MAX_UNDO_STACK_SIZE = 100;
76
+ this.UNDO_SAVE_DELAY = 500;
77
+ this.handleOutsideClick = (event) => {
78
+ const target = event.target;
79
+ const isInsideThisComponent = this.contains(target) || this.shadowRoot?.contains(target);
80
+ if (!isInsideThisComponent) {
81
+ if (this.isHeadingDropdownOpen) {
82
+ this.isHeadingDropdownOpen = false;
83
+ }
84
+ if (this.isColorDropdownOpen) {
85
+ this.isColorDropdownOpen = false;
86
+ }
87
+ if (this.isListDropdownOpen) {
88
+ this.isListDropdownOpen = false;
89
+ }
90
+ return;
91
+ }
92
+ const isInsideHeadingDropdown = this.shadowRoot?.getElementById("headingDropdown")?.contains(target);
93
+ const isInsideColorDropdown = this.shadowRoot?.getElementById("colorDropdown")?.contains(target);
94
+ const isInsideListDropdown = this.shadowRoot?.getElementById("listDropdown")?.contains(target);
95
+ const isHeadingTrigger = this.shadowRoot?.getElementById(this.headingTriggerId)?.contains(target);
96
+ const isColorTrigger = this.shadowRoot?.getElementById(this.colorTriggerId)?.contains(target);
97
+ const isListTrigger = this.shadowRoot?.getElementById(this.listTriggerId)?.contains(target);
98
+ if (!isInsideHeadingDropdown && !isHeadingTrigger && this.isHeadingDropdownOpen) {
99
+ this.isHeadingDropdownOpen = false;
100
+ }
101
+ if (!isInsideColorDropdown && !isColorTrigger && this.isColorDropdownOpen) {
102
+ this.isColorDropdownOpen = false;
103
+ }
104
+ if (!isInsideListDropdown && !isListTrigger && this.isListDropdownOpen) {
105
+ this.isListDropdownOpen = false;
106
+ }
107
+ };
108
+ this.styles = ce({
109
+ slots: {
110
+ wrapper: "w-[inherit] grid gap-3",
111
+ header: "flex items-center justify-between gap-2 border border-neutral-90 rounded-12 px-3 py-2 bg-neutral-100",
112
+ toolbar: "flex flex-wrap items-center gap-1",
113
+ area: "",
114
+ editor: "",
115
+ preview: "p-3",
116
+ colorMenu: "relative",
117
+ headingMenu: "relative",
118
+ listMenu: "relative",
119
+ label: "heading-inter-14-bold text-neutral-20",
120
+ description: "paragraph-inter-12-regular text-neutral-20",
121
+ divider: "w-[1px] h-4 bg-neutral-90"
122
+ },
123
+ variants: {
124
+ isFullWidth: {
125
+ true: { wrapper: "w-full" },
126
+ false: { wrapper: "w-[350px]" }
127
+ }
128
+ },
129
+ compoundVariants: [{ isFullWidth: false, class: { wrapper: "w-[500px]" } }]
130
+ });
131
+ this.toolbarButton = ce({
132
+ base: "hover:bg-neutral-95 transition border-0 !shadow-none",
133
+ variants: {
134
+ active: { true: "bg-neutral-95" },
135
+ disabled: { true: "opacity-50 cursor-not-allowed" }
136
+ }
137
+ });
138
+ /**
139
+ * Hex color palette for the color picker.
140
+ * A curated collection of colors organized by hue families,
141
+ * ranging from light pastels to bold colors.
142
+ */
143
+ this.colorSamples = [
144
+ "#FFE5E5",
145
+ // very light red
146
+ "#FFB3B3",
147
+ "#FF8080",
148
+ "#FF6666",
149
+ "#FF4D4D",
150
+ "#E63946",
151
+ // strong pastel red
152
+ "#FFEBD5",
153
+ // very light orange
154
+ "#FFD1A3",
155
+ "#FFB870",
156
+ "#FFA54D",
157
+ "#FF944D",
158
+ "#FF7F11",
159
+ // bold pastel orange
160
+ "#FFFBD5",
161
+ // very light yellow
162
+ "#FFF3A3",
163
+ "#FFE870",
164
+ "#FFDD4D",
165
+ "#FFD633",
166
+ "#F4C430",
167
+ // deep pastel yellow
168
+ "#E6F9EC",
169
+ // very light green
170
+ "#B3E6C1",
171
+ "#80D499",
172
+ "#66CC80",
173
+ "#4DB870",
174
+ "#2D9C5B",
175
+ // bold pastel green
176
+ "#E6F3FF",
177
+ // very light blue
178
+ "#B3DAFF",
179
+ "#80C2FF",
180
+ "#66B2FF",
181
+ "#4DA6FF",
182
+ "#3A86FF",
183
+ // bold pastel blue
184
+ "#F3E6FF",
185
+ // very light purple
186
+ "#D9B3FF",
187
+ "#BF80FF",
188
+ "#A64DFF",
189
+ "#9333FF",
190
+ "#7B2CBF",
191
+ // bold pastel purple
192
+ "#FFE6F3",
193
+ // very light pink
194
+ "#FFB3D9",
195
+ "#FF80BF",
196
+ "#FF66B2",
197
+ "#FF4DA6",
198
+ "#E75480",
199
+ // bold pastel pink
200
+ "#F5E6DA",
201
+ // very light brown
202
+ "#E6CBB3",
203
+ "#D9B38C",
204
+ "#CC9966",
205
+ "#B3773A",
206
+ "#8B5E3C",
207
+ // bold pastel brown
208
+ "#FFFFFF",
209
+ // white
210
+ "#F5F5F5",
211
+ // very light gray
212
+ "#E0E0E0",
213
+ // light gray
214
+ "#BDBDBD",
215
+ // medium gray
216
+ "#757575",
217
+ // dark gray
218
+ "#000000",
219
+ // black
220
+ this.defaultColor
221
+ ];
222
+ /**
223
+ * Handle input event from the internal textarea component.
224
+ * @param event
225
+ */
226
+ this.handleTextareaInput = (event) => {
227
+ const newValue = event.detail?.value || "";
228
+ if (!this.isUndoRedoAction) {
229
+ this.scheduleUndoStateSave();
230
+ }
231
+ this.value = newValue;
232
+ this.updateActiveFormats();
233
+ };
234
+ /**
235
+ * Textarea keyup handler.
236
+ * Update active formats when user navigates with keyboard.
237
+ */
238
+ this.handleTextareaKeyUp = () => {
239
+ requestAnimationFrame(() => this.updateActiveFormats());
240
+ };
241
+ /**
242
+ * Textarea click handler.
243
+ * Update active formats when user clicks to move cursor.
244
+ */
245
+ this.handleTextareaClick = () => {
246
+ requestAnimationFrame(() => this.updateActiveFormats());
247
+ };
248
+ /**
249
+ * Handle keydown events for undo/redo shortcuts and list continuation.
250
+ */
251
+ this.handleKeyDown = (event) => {
252
+ const isUndo = (event.metaKey || event.ctrlKey) && event.key === "z" && !event.shiftKey;
253
+ const isRedo = (event.metaKey || event.ctrlKey) && (event.key === "y" || event.key === "z" && event.shiftKey);
254
+ if (isUndo) {
255
+ event.preventDefault();
256
+ this.undo();
257
+ } else if (isRedo) {
258
+ event.preventDefault();
259
+ this.redo();
260
+ } else if (event.key === "Enter" && !event.metaKey && !event.ctrlKey && !event.shiftKey) {
261
+ if (this.handleListContinuation()) {
262
+ event.preventDefault();
263
+ }
264
+ } else if (event.key === "Backspace" && !event.metaKey && !event.ctrlKey && !event.shiftKey) {
265
+ if (this.handleListBackspace()) {
266
+ event.preventDefault();
267
+ }
268
+ } else if (event.key === "Tab" && !event.metaKey && !event.ctrlKey) {
269
+ if (event.shiftKey) {
270
+ if (this.handleListOutdent()) {
271
+ event.preventDefault();
272
+ }
273
+ } else {
274
+ if (this.handleListIndent()) {
275
+ event.preventDefault();
276
+ }
277
+ }
278
+ }
279
+ };
280
+ }
281
+ dispatchChange(event) {
282
+ this.updateComplete.then(() => {
283
+ const changeEvent = new CustomEvent("on-change", {
284
+ detail: { value: this.value, event },
285
+ bubbles: false,
286
+ composed: true
287
+ });
288
+ this.dispatchEvent(changeEvent);
289
+ });
290
+ }
291
+ /**
292
+ * Utility to perform an operation with the current textarea selection.
293
+ *
294
+ * @param callback -
295
+ */
296
+ withSelection(callback) {
297
+ const textarea = this.textareaEl?.shadowRoot?.querySelector(
298
+ "textarea"
299
+ );
300
+ if (!textarea) return;
301
+ const start = textarea.selectionStart ?? 0;
302
+ const end = textarea.selectionEnd ?? 0;
303
+ const value = this.value;
304
+ callback(textarea, start, end, value);
305
+ textarea.focus();
306
+ }
307
+ /**
308
+ * Expand empty selection to current word boundaries
309
+ *
310
+ * @param start - selection start
311
+ * @param end - selection end
312
+ * @param value - full textarea value
313
+ */
314
+ expandSelectionToWord(start, end, value) {
315
+ if (start !== end) return { start, end };
316
+ const charBefore = start > 0 ? value[start - 1] : "";
317
+ const charAfter = start < value.length ? value[start] : "";
318
+ if (charBefore === "(" && charAfter === ")" || charBefore === "[" && charAfter === "]") {
319
+ return { start, end };
320
+ }
321
+ let _start = start;
322
+ let _end = end;
323
+ const isWord = (ch) => /[\p{L}\p{N}_-]/u.test(ch);
324
+ while (_start > 0 && isWord(value[_start - 1])) _start--;
325
+ while (_end < value.length && isWord(value[_end])) _end++;
326
+ return { start: _start, end: _end };
327
+ }
328
+ /**
329
+ * Apply or toggle heading formatting for the current line(s).
330
+ *
331
+ * @param level - 0 to remove heading, 1-3 for heading levels
332
+ */
333
+ applyHeading(level) {
334
+ if (this.isReadonly || this.isDisabled) return;
335
+ this.saveUndoStateBeforeChange();
336
+ const desiredPrefix = level > 0 ? `${"#".repeat(level)} ` : "";
337
+ this.withSelection((textarea, start, end, value) => {
338
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
339
+ let lineEnd = value.indexOf("\n", end);
340
+ if (lineEnd === -1) lineEnd = value.length;
341
+ const before = value.slice(0, lineStart);
342
+ const selected = value.slice(lineStart, lineEnd);
343
+ const after = value.slice(lineEnd);
344
+ const lines = selected.split("\n");
345
+ const headingRegex = /^(#{1,6})\s+/;
346
+ const allAlreadyLevel = lines.every((l) => l.startsWith(desiredPrefix));
347
+ let transformed;
348
+ if (level === 0) {
349
+ transformed = lines.map((l) => l.replace(headingRegex, "")).join("\n");
350
+ } else {
351
+ transformed = lines.map((l) => {
352
+ const withoutAny = l.replace(headingRegex, "");
353
+ if (allAlreadyLevel) {
354
+ return withoutAny;
355
+ }
356
+ return desiredPrefix + withoutAny;
357
+ }).join("\n");
358
+ }
359
+ this.value = before + transformed + after;
360
+ textarea.value = before + transformed + after;
361
+ let cursorPosition = before.length;
362
+ if (level > 0 && !allAlreadyLevel) {
363
+ const firstLine = lines[0] || "";
364
+ const contentAfterHeading = firstLine.replace(headingRegex, "");
365
+ if (contentAfterHeading.trim()) {
366
+ const firstLineEnd = transformed.indexOf("\n");
367
+ cursorPosition = before.length + (firstLineEnd === -1 ? transformed.length : firstLineEnd);
368
+ } else {
369
+ cursorPosition = before.length + desiredPrefix.length;
370
+ }
371
+ } else if (level === 0 || allAlreadyLevel) {
372
+ const firstLineEnd = transformed.indexOf("\n");
373
+ cursorPosition = before.length + (firstLineEnd === -1 ? transformed.length : firstLineEnd);
374
+ }
375
+ requestAnimationFrame(() => {
376
+ textarea.setSelectionRange(cursorPosition, cursorPosition);
377
+ this.updateActiveFormats();
378
+ });
379
+ this.dispatchChange();
380
+ });
381
+ }
382
+ /**
383
+ * Apply or toggle list formatting for the current line(s).
384
+ *
385
+ * @param listType - 'none' to remove list, 'unordered' for bullet list, 'ordered' for numbered list
386
+ */
387
+ applyList(listType) {
388
+ if (this.isReadonly || this.isDisabled) return;
389
+ this.saveUndoStateBeforeChange();
390
+ this.withSelection((textarea, start, end, value) => {
391
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
392
+ let lineEnd = value.indexOf("\n", end);
393
+ if (lineEnd === -1) lineEnd = value.length;
394
+ const before = value.slice(0, lineStart);
395
+ const selected = value.slice(lineStart, lineEnd);
396
+ const after = value.slice(lineEnd);
397
+ const lines = selected.split("\n");
398
+ const unorderedRegex = /^(\s*)[-*+]\s+/;
399
+ const orderedRegex = /^(\s*)\d+\.\s+/;
400
+ let transformed;
401
+ if (listType === "none") {
402
+ transformed = lines.map((l) => {
403
+ if (unorderedRegex.test(l)) {
404
+ return l.replace(unorderedRegex, "$1");
405
+ }
406
+ if (orderedRegex.test(l)) {
407
+ return l.replace(orderedRegex, "$1");
408
+ }
409
+ return l;
410
+ }).join("\n");
411
+ } else if (listType === "unordered") {
412
+ const allAlreadyUnordered = lines.every(
413
+ (l) => l.trim() === "" || unorderedRegex.test(l)
414
+ );
415
+ if (allAlreadyUnordered && lines.some((l) => unorderedRegex.test(l))) {
416
+ transformed = lines.map((l) => {
417
+ if (unorderedRegex.test(l)) {
418
+ return l.replace(unorderedRegex, "$1");
419
+ }
420
+ return l;
421
+ }).join("\n");
422
+ } else {
423
+ transformed = lines.map((l) => {
424
+ if (l.trim() === "") return l;
425
+ let cleaned = l;
426
+ if (unorderedRegex.test(l)) {
427
+ cleaned = l.replace(unorderedRegex, "$1");
428
+ } else if (orderedRegex.test(l)) {
429
+ cleaned = l.replace(orderedRegex, "$1");
430
+ }
431
+ const indentMatch = cleaned.match(/^(\s*)/);
432
+ const indent = indentMatch ? indentMatch[1] : "";
433
+ const content = cleaned.replace(/^\s*/, "");
434
+ return `${indent}- ${content}`;
435
+ }).join("\n");
436
+ }
437
+ } else if (listType === "ordered") {
438
+ const allAlreadyOrdered = lines.every(
439
+ (l) => l.trim() === "" || orderedRegex.test(l)
440
+ );
441
+ if (allAlreadyOrdered && lines.some((l) => orderedRegex.test(l))) {
442
+ transformed = lines.map((l) => {
443
+ if (orderedRegex.test(l)) {
444
+ return l.replace(orderedRegex, "$1");
445
+ }
446
+ return l;
447
+ }).join("\n");
448
+ } else {
449
+ let counter = 1;
450
+ const beforeLines = before.split("\n");
451
+ for (let i = beforeLines.length - 1; i >= 0; i--) {
452
+ const line = beforeLines[i];
453
+ const orderedMatch = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
454
+ if (orderedMatch) {
455
+ counter = parseInt(orderedMatch[2], 10) + 1;
456
+ break;
457
+ } else if (line.trim() !== "" && !line.match(/^\s*[-*+]\s+/)) {
458
+ break;
459
+ }
460
+ }
461
+ transformed = lines.map((l) => {
462
+ if (l.trim() === "") return l;
463
+ let cleaned = l;
464
+ if (unorderedRegex.test(l)) {
465
+ cleaned = l.replace(unorderedRegex, "$1");
466
+ } else if (orderedRegex.test(l)) {
467
+ cleaned = l.replace(orderedRegex, "$1");
468
+ }
469
+ const indentMatch = cleaned.match(/^(\s*)/);
470
+ const indent = indentMatch ? indentMatch[1] : "";
471
+ const content = cleaned.replace(/^\s*/, "");
472
+ return `${indent}${counter++}. ${content}`;
473
+ }).join("\n");
474
+ }
475
+ }
476
+ let finalValue = before + transformed + after;
477
+ if (listType === "ordered") {
478
+ const allAlreadyOrdered = lines.every(
479
+ (l) => l.trim() === "" || orderedRegex.test(l)
480
+ );
481
+ if (!allAlreadyOrdered || !lines.some((l) => orderedRegex.test(l))) {
482
+ const transformedLines = transformed.split("\n");
483
+ const lastTransformedLine = transformedLines[transformedLines.length - 1];
484
+ const indentMatch = lastTransformedLine.match(/^(\s*)/);
485
+ const indent = indentMatch ? indentMatch[1] : "";
486
+ const endPosition = before.length + transformed.length;
487
+ finalValue = this.renumberOrderedListItems(
488
+ finalValue,
489
+ endPosition,
490
+ indent
491
+ );
492
+ }
493
+ }
494
+ if (listType === "none") {
495
+ const removedOrderedItems = lines.some((l) => orderedRegex.test(l));
496
+ if (removedOrderedItems) {
497
+ finalValue = this.renumberSubsequentOrderedLists(
498
+ finalValue,
499
+ before.length + transformed.length
500
+ );
501
+ }
502
+ }
503
+ this.value = finalValue;
504
+ textarea.value = finalValue;
505
+ const firstLineEnd = transformed.indexOf("\n");
506
+ const cursorPosition = before.length + (firstLineEnd === -1 ? transformed.length : firstLineEnd);
507
+ requestAnimationFrame(() => {
508
+ textarea.setSelectionRange(cursorPosition, cursorPosition);
509
+ this.updateActiveFormats();
510
+ });
511
+ this.dispatchChange();
512
+ });
513
+ }
514
+ /**
515
+ * Get the current active list type based on activeFormats.
516
+ */
517
+ getActiveListType() {
518
+ if (this.activeFormats.unorderedList) return "unordered";
519
+ if (this.activeFormats.orderedList) return "ordered";
520
+ return "none";
521
+ }
522
+ /**
523
+ * Toggle inline formatting by wrapping/unwrapping selection or current word.
524
+ *
525
+ * @param wrapper - the markdown wrapper to toggle, e.g. '**' for bold, '*' for italic
526
+ */
527
+ toggleWrap(wrapper) {
528
+ if (this.isReadonly || this.isDisabled) return;
529
+ this.saveUndoStateBeforeChange();
530
+ this.withSelection((textarea, start, end, value) => {
531
+ if (this.isSelectionInLinkUrl(start, end, value)) {
532
+ return;
533
+ }
534
+ const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
535
+ let before = value.slice(0, s);
536
+ let selected = value.slice(s, e);
537
+ let after = value.slice(e);
538
+ const hasOuterWrap = before.endsWith(wrapper) && after.startsWith(wrapper);
539
+ if (hasOuterWrap) {
540
+ before = before.slice(0, before.length - wrapper.length);
541
+ after = after.slice(wrapper.length);
542
+ this.value = before + selected + after;
543
+ textarea.value = before + selected + after;
544
+ const selStart2 = before.length;
545
+ const selEnd2 = selStart2 + selected.length;
546
+ requestAnimationFrame(() => {
547
+ textarea.setSelectionRange(selStart2, selEnd2);
548
+ this.updateActiveFormats();
549
+ });
550
+ this.dispatchChange();
551
+ return;
552
+ }
553
+ const innerWrapped = selected.startsWith(wrapper) && selected.endsWith(wrapper);
554
+ if (innerWrapped) {
555
+ selected = selected.slice(
556
+ wrapper.length,
557
+ selected.length - wrapper.length
558
+ );
559
+ this.value = before + selected + after;
560
+ textarea.value = before + selected + after;
561
+ const selStart2 = before.length;
562
+ const selEnd2 = selStart2 + selected.length;
563
+ requestAnimationFrame(() => {
564
+ textarea.setSelectionRange(selStart2, selEnd2);
565
+ this.updateActiveFormats();
566
+ });
567
+ this.dispatchChange();
568
+ return;
569
+ }
570
+ const wrapped = `${wrapper}${selected || ""}${wrapper}`;
571
+ this.value = before + wrapped + after;
572
+ textarea.value = before + wrapped + after;
573
+ const selStart = before.length + wrapper.length;
574
+ const selEnd = selStart + (selected ? selected.length : 0);
575
+ requestAnimationFrame(() => {
576
+ textarea.setSelectionRange(selStart, selEnd);
577
+ this.updateActiveFormats();
578
+ });
579
+ this.dispatchChange();
580
+ });
581
+ }
582
+ /**
583
+ * Toggle preview mode on or off.
584
+ */
585
+ togglePreview() {
586
+ if (this.isPreview) {
587
+ this.exitPreview();
588
+ } else {
589
+ this.enterPreview();
590
+ }
591
+ this.isPreview = !this.isPreview;
592
+ }
593
+ /**
594
+ * Enter preview mode - save current state and remove keyboard listeners from textarea
595
+ */
596
+ enterPreview() {
597
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
598
+ if (textarea) {
599
+ this.savedSelectionForPreview = {
600
+ start: textarea.selectionStart ?? 0,
601
+ end: textarea.selectionEnd ?? 0
602
+ };
603
+ this.removeKeyboardListeners();
604
+ }
605
+ }
606
+ /**
607
+ * Exit preview mode - restore state and reattach keyboard listeners
608
+ */
609
+ exitPreview() {
610
+ requestAnimationFrame(() => {
611
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
612
+ if (textarea && this.savedSelectionForPreview) {
613
+ textarea.focus();
614
+ textarea.setSelectionRange(
615
+ this.savedSelectionForPreview.start,
616
+ this.savedSelectionForPreview.end
617
+ );
618
+ this.currentSelection = this.savedSelectionForPreview;
619
+ this.savedSelectionForPreview = null;
620
+ this.updateActiveFormats();
621
+ }
622
+ this.addKeyboardListeners();
623
+ });
624
+ }
625
+ /**
626
+ * Insert or edit a markdown link [text](url).
627
+ */
628
+ insertLink() {
629
+ if (this.isReadonly || this.isDisabled) return;
630
+ this.saveUndoStateBeforeChange();
631
+ const placeholderText = "link text";
632
+ const placeholderUrl = "";
633
+ this.withSelection((textarea, start, end, value) => {
634
+ if (this.isSelectionInLinkUrl(start, end, value) || this.isSelectionInLinkText(start, end, value)) {
635
+ const leftBracket2 = value.lastIndexOf("[", start);
636
+ const rightParen2 = value.indexOf(")", Math.max(start, end));
637
+ const rightBracket2 = value.indexOf("]", leftBracket2);
638
+ const openParen2 = value.indexOf("(", rightBracket2);
639
+ if (leftBracket2 !== -1 && rightBracket2 !== -1 && openParen2 !== -1 && rightParen2 !== -1) {
640
+ const candidate = value.slice(leftBracket2, rightParen2 + 1);
641
+ const linkRegex2 = /^\[([^\]]+)\]\(([^)]*)\)$/;
642
+ const match = candidate.match(linkRegex2);
643
+ if (match) {
644
+ const textOnly = match[1];
645
+ this.value = value.slice(0, leftBracket2) + textOnly + value.slice(rightParen2 + 1);
646
+ textarea.value = value.slice(0, leftBracket2) + textOnly + value.slice(rightParen2 + 1);
647
+ const newCursor = leftBracket2 + textOnly.length;
648
+ requestAnimationFrame(() => {
649
+ textarea.setSelectionRange(newCursor, newCursor);
650
+ this.updateActiveFormats();
651
+ });
652
+ this.dispatchChange();
653
+ return;
654
+ }
655
+ }
656
+ return;
657
+ }
658
+ const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
659
+ const before = value.slice(0, s);
660
+ const selected = value.slice(s, e);
661
+ const after = value.slice(e);
662
+ const linkRegex = /^\[([^\]]+)\]\(([^)]+)\)$/;
663
+ if (linkRegex.test(selected)) {
664
+ const match = selected.match(linkRegex);
665
+ const textOnly = match?.[1] ?? selected;
666
+ this.value = before + textOnly + after;
667
+ textarea.value = before + textOnly + after;
668
+ const newStart = before.length;
669
+ const newEnd = newStart + textOnly.length;
670
+ requestAnimationFrame(() => {
671
+ textarea.setSelectionRange(newStart, newEnd);
672
+ this.updateActiveFormats();
673
+ });
674
+ this.dispatchChange();
675
+ return;
676
+ }
677
+ const leftBracket = value.lastIndexOf("[", s);
678
+ const rightParen = value.indexOf(")", e);
679
+ const rightBracket = value.indexOf("]", e);
680
+ const openParen = value.indexOf("(", e);
681
+ if (leftBracket !== -1 && rightBracket !== -1 && openParen !== -1 && rightParen !== -1 && leftBracket < rightBracket && rightBracket < openParen && openParen < rightParen) {
682
+ const candidate = value.slice(leftBracket, rightParen + 1);
683
+ if (linkRegex.test(candidate)) {
684
+ const match = candidate.match(linkRegex);
685
+ const textOnly = match?.[1] ?? candidate;
686
+ this.value = value.slice(0, leftBracket) + textOnly + value.slice(rightParen + 1);
687
+ textarea.value = value.slice(0, leftBracket) + textOnly + value.slice(rightParen + 1);
688
+ const newCursor = leftBracket + textOnly.length;
689
+ requestAnimationFrame(() => {
690
+ textarea.setSelectionRange(newCursor, newCursor);
691
+ this.updateActiveFormats();
692
+ });
693
+ this.dispatchChange();
694
+ return;
695
+ }
696
+ }
697
+ const text = selected || placeholderText;
698
+ const md = `[${text}](${placeholderUrl})`;
699
+ this.value = before + md + after;
700
+ textarea.value = before + md + after;
701
+ const cursorPosition = before.length + 1 + text.length + 2;
702
+ requestAnimationFrame(() => {
703
+ textarea.focus();
704
+ textarea.setSelectionRange(cursorPosition, cursorPosition);
705
+ this.updateActiveFormats();
706
+ });
707
+ this.dispatchChange();
708
+ });
709
+ }
710
+ /**
711
+ * Update active formatting states based on current selection or cursor position.
712
+ */
713
+ updateActiveFormats() {
714
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
715
+ if (!textarea || !this.value) return;
716
+ const start = textarea.selectionStart ?? 0;
717
+ const end = textarea.selectionEnd ?? 0;
718
+ this.currentSelection = { start, end };
719
+ const { start: s, end: e } = this.expandSelectionToWord(
720
+ start,
721
+ end,
722
+ this.value
723
+ );
724
+ const selectedText = this.value.slice(s, e);
725
+ const beforeSelection = this.value.slice(0, s);
726
+ const afterSelection = this.value.slice(e);
727
+ const hasBoldWrap = beforeSelection.endsWith("**") && afterSelection.startsWith("**") || selectedText.startsWith("**") && selectedText.endsWith("**");
728
+ const hasItalicWrap = beforeSelection.endsWith("*") && afterSelection.startsWith("*") && !beforeSelection.endsWith("**") || selectedText.startsWith("*") && selectedText.endsWith("*") && !selectedText.startsWith("**");
729
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/;
730
+ const hasLink = linkRegex.test(selectedText) || this.isWithinLink(start, this.value);
731
+ const lineStart = this.value.lastIndexOf("\n", start - 1) + 1;
732
+ const lineEnd = this.value.indexOf("\n", start);
733
+ const currentLine = this.value.slice(
734
+ lineStart,
735
+ lineEnd === -1 ? this.value.length : lineEnd
736
+ );
737
+ const headingMatch = currentLine.match(/^(#{1,6})\s/);
738
+ const headingLevel = headingMatch ? headingMatch[1].length : 0;
739
+ const unorderedListMatch = currentLine.match(/^\s*[-*+]\s/);
740
+ const orderedListMatch = currentLine.match(/^\s*\d+\.\s/);
741
+ const hasUnorderedList = !!unorderedListMatch;
742
+ const hasOrderedList = !!orderedListMatch;
743
+ const colorRegex = /<span style="color: ([^"]+)">/;
744
+ const hasColorWrap = !!(beforeSelection.match(colorRegex) && afterSelection.includes("</span>") || selectedText.match(/^<span style="color: ([^"]+)">(.*)<\/span>$/s));
745
+ let activeColor = this.defaultColor;
746
+ if (hasColorWrap) {
747
+ const beforeColorMatch = beforeSelection.match(
748
+ /<span style="color: ([^"]+)">([^<]*)$/
749
+ );
750
+ const selectedColorMatch = selectedText.match(
751
+ /^<span style="color: ([^"]+)">/
752
+ );
753
+ activeColor = beforeColorMatch?.[1] || selectedColorMatch?.[1] || "";
754
+ }
755
+ this.activeFormats = {
756
+ bold: hasBoldWrap,
757
+ italic: hasItalicWrap,
758
+ link: hasLink,
759
+ h1: headingLevel === 1,
760
+ h2: headingLevel === 2,
761
+ h3: headingLevel === 3,
762
+ color: hasColorWrap,
763
+ activeColor,
764
+ unorderedList: hasUnorderedList,
765
+ orderedList: hasOrderedList
766
+ };
767
+ }
768
+ /**
769
+ * Check if a given position is within a markdown link [text](url).
770
+ * @param position - cursor position
771
+ * @param value - full textarea value
772
+ */
773
+ isWithinLink(position, value) {
774
+ const leftBracket = value.lastIndexOf("[", position);
775
+ const rightParen = value.indexOf(")", position);
776
+ if (leftBracket === -1 || rightParen === -1) return false;
777
+ const candidate = value.slice(leftBracket, rightParen + 1);
778
+ return /^\[([^\]]+)\]\(([^)]+)\)$/.test(candidate);
779
+ }
780
+ /**
781
+ * Check if selection is within the text part [] of a markdown link
782
+ * @param start - selection start position
783
+ * @param end - selection end position
784
+ * @param value - full textarea value
785
+ */
786
+ isSelectionInLinkText(start, end, value) {
787
+ const leftBracket = value.lastIndexOf("[", start);
788
+ const rightParen = value.indexOf(")", Math.max(start, end));
789
+ if (leftBracket === -1 || rightParen === -1) return false;
790
+ const rightBracket = value.indexOf("]", leftBracket);
791
+ const openParen = value.indexOf("(", rightBracket);
792
+ if (rightBracket === -1 || openParen === -1) return false;
793
+ if (!(leftBracket < rightBracket && rightBracket < openParen && openParen < rightParen))
794
+ return false;
795
+ const candidate = value.slice(leftBracket, rightParen + 1);
796
+ if (!/^\[([^\]]+)\]\(([^)]*)\)$/.test(candidate)) return false;
797
+ return start >= leftBracket + 1 && end <= rightBracket;
798
+ }
799
+ /**
800
+ * Check if selection is within the URL part () of a markdown link
801
+ * @param start - selection start position
802
+ * @param end - selection end position
803
+ * @param value - full textarea value
804
+ */
805
+ isSelectionInLinkUrl(start, end, value) {
806
+ const leftBracket = value.lastIndexOf("[", start);
807
+ const rightParen = value.indexOf(")", Math.max(start, end));
808
+ if (leftBracket === -1 || rightParen === -1) return false;
809
+ const rightBracket = value.indexOf("]", leftBracket);
810
+ const openParen = value.indexOf("(", rightBracket);
811
+ if (rightBracket === -1 || openParen === -1) return false;
812
+ if (!(leftBracket < rightBracket && rightBracket < openParen && openParen < rightParen))
813
+ return false;
814
+ const candidate = value.slice(leftBracket, rightParen + 1);
815
+ if (!/^\[([^\]]+)\]\(([^)]*)\)$/.test(candidate)) return false;
816
+ return start >= openParen + 1 && end <= rightParen;
817
+ }
818
+ /**
819
+ * Add keyboard event listeners to the textarea for undo/redo functionality.
820
+ */
821
+ addKeyboardListeners() {
822
+ const tryAddListener = (attempts = 0) => {
823
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
824
+ if (textarea) {
825
+ textarea.removeEventListener("keydown", this.handleKeyDown);
826
+ textarea.addEventListener("keydown", this.handleKeyDown);
827
+ } else if (attempts < 15) {
828
+ requestAnimationFrame(() => tryAddListener(attempts + 1));
829
+ }
830
+ };
831
+ tryAddListener();
832
+ }
833
+ /**
834
+ * Remove keyboard event listeners from the textarea to prevent memory leaks.
835
+ */
836
+ removeKeyboardListeners() {
837
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
838
+ if (textarea) {
839
+ textarea.removeEventListener("keydown", this.handleKeyDown);
840
+ }
841
+ }
842
+ /**
843
+ * Toggle color formatting by wrapping/unwrapping selection or current word.
844
+ *
845
+ * @param color - the hex color code to apply, e.g. '#FF0000' for red
846
+ */
847
+ toggleColorWrap(color) {
848
+ if (this.isReadonly || this.isDisabled) return;
849
+ this.saveUndoStateBeforeChange();
850
+ this.withSelection((textarea, start, end, value) => {
851
+ if (this.isSelectionInLinkUrl(start, end, value)) {
852
+ return;
853
+ }
854
+ const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
855
+ let before = value.slice(0, s);
856
+ let selected = value.slice(s, e);
857
+ let after = value.slice(e);
858
+ const colorTagClose = "</span>";
859
+ const anyColorSpanRegex = /<span style="color: ([^"]+)">$/;
860
+ const beforeColorMatch = before.match(anyColorSpanRegex);
861
+ const hasOuterWrap = beforeColorMatch && after.startsWith(colorTagClose);
862
+ if (hasOuterWrap) {
863
+ const existingColor = beforeColorMatch[1];
864
+ let selStart2;
865
+ let selEnd2;
866
+ if (existingColor === color) {
867
+ before = before.slice(0, before.length - beforeColorMatch[0].length);
868
+ after = after.slice(colorTagClose.length);
869
+ this.value = before + selected + after;
870
+ textarea.value = before + selected + after;
871
+ selStart2 = before.length;
872
+ selEnd2 = selStart2 + selected.length;
873
+ } else {
874
+ const newColorTagOpen2 = `<span style="color: ${color}">`;
875
+ before = before.slice(0, before.length - beforeColorMatch[0].length) + newColorTagOpen2;
876
+ this.value = before + selected + after;
877
+ textarea.value = before + selected + after;
878
+ selStart2 = before.length;
879
+ selEnd2 = selStart2 + selected.length;
880
+ }
881
+ requestAnimationFrame(() => {
882
+ textarea.setSelectionRange(selStart2, selEnd2);
883
+ this.updateActiveFormats();
884
+ });
885
+ this.dispatchChange();
886
+ return;
887
+ }
888
+ const colorRegex = /^<span style="color: ([^"]+)">(.*)<\/span>$/s;
889
+ const innerMatch = selected.match(colorRegex);
890
+ if (innerMatch) {
891
+ const existingColor = innerMatch[1];
892
+ const innerText = innerMatch[2];
893
+ if (existingColor === color) {
894
+ selected = innerText;
895
+ } else {
896
+ selected = `<span style="color: ${color}">${innerText}</span>`;
897
+ }
898
+ this.value = before + selected + after;
899
+ textarea.value = before + selected + after;
900
+ const selStart2 = before.length;
901
+ const selEnd2 = selStart2 + (existingColor === color ? innerText.length : innerText.length);
902
+ requestAnimationFrame(() => {
903
+ textarea.setSelectionRange(selStart2, selEnd2);
904
+ this.updateActiveFormats();
905
+ });
906
+ this.dispatchChange();
907
+ return;
908
+ }
909
+ const newColorTagOpen = `<span style="color: ${color}">`;
910
+ const wrapped = `${newColorTagOpen}${selected || ""}${colorTagClose}`;
911
+ this.value = before + wrapped + after;
912
+ textarea.value = before + wrapped + after;
913
+ const selStart = before.length + newColorTagOpen.length;
914
+ const selEnd = selStart + (selected ? selected.length : 4);
915
+ requestAnimationFrame(() => {
916
+ textarea.setSelectionRange(selStart, selEnd);
917
+ this.updateActiveFormats();
918
+ });
919
+ this.dispatchChange();
920
+ });
921
+ }
922
+ /**
923
+ * Handle color selection from the dropdown.
924
+ *
925
+ * @param color - the selected hex color code
926
+ */
927
+ selectColor(color) {
928
+ if (this.savedSelection) {
929
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
930
+ if (textarea) {
931
+ textarea.focus();
932
+ textarea.setSelectionRange(
933
+ this.savedSelection.start,
934
+ this.savedSelection.end
935
+ );
936
+ this.currentSelection = this.savedSelection;
937
+ this.savedSelection = null;
938
+ this.toggleColorWrap(color);
939
+ }
940
+ } else {
941
+ this.toggleColorWrap(color);
942
+ }
943
+ }
944
+ /**
945
+ * Clear color formatting from the current selection or word.
946
+ */
947
+ clearColor() {
948
+ if (this.isReadonly || this.isDisabled) return;
949
+ this.saveUndoStateBeforeChange();
950
+ if (this.savedSelection) {
951
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
952
+ if (textarea) {
953
+ textarea.focus();
954
+ textarea.setSelectionRange(
955
+ this.savedSelection.start,
956
+ this.savedSelection.end
957
+ );
958
+ this.currentSelection = this.savedSelection;
959
+ this.savedSelection = null;
960
+ }
961
+ }
962
+ this.withSelection((textarea, start, end, value) => {
963
+ if (this.isSelectionInLinkUrl(start, end, value)) {
964
+ return;
965
+ }
966
+ const { start: s, end: e } = this.expandSelectionToWord(start, end, value);
967
+ const before = value.slice(0, s);
968
+ let selected = value.slice(s, e);
969
+ const after = value.slice(e);
970
+ const colorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/gs;
971
+ selected = selected.replace(colorRegex, "$2");
972
+ const fullColorRegex = /<span style="color: ([^"]+)">(.*?)<\/span>/g;
973
+ let match;
974
+ let foundMatch = false;
975
+ const searchText = value.slice(
976
+ Math.max(0, s - 100),
977
+ Math.min(value.length, e + 100)
978
+ );
979
+ const searchOffset = Math.max(0, s - 100);
980
+ match = fullColorRegex.exec(searchText);
981
+ while (match !== null) {
982
+ const matchStart = searchOffset + match.index;
983
+ const matchEnd = searchOffset + match.index + match[0].length;
984
+ const spanOpenTag = `<span style="color: ${match[1]}">`;
985
+ const contentStart = searchOffset + match.index + spanOpenTag.length;
986
+ const contentEnd = matchEnd - 7;
987
+ if (contentStart <= s && e <= contentEnd) {
988
+ const beforeContent = value.slice(contentStart, s);
989
+ const afterContent = value.slice(e, contentEnd);
990
+ const newContent = beforeContent + selected + afterContent;
991
+ this.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
992
+ textarea.value = value.slice(0, matchStart) + newContent + value.slice(matchEnd);
993
+ const selStart = matchStart + beforeContent.length;
994
+ const selEnd = selStart + selected.length;
995
+ requestAnimationFrame(() => {
996
+ textarea.setSelectionRange(selStart, selEnd);
997
+ this.updateActiveFormats();
998
+ });
999
+ this.dispatchChange();
1000
+ foundMatch = true;
1001
+ break;
1002
+ }
1003
+ match = fullColorRegex.exec(searchText);
1004
+ }
1005
+ if (!foundMatch) {
1006
+ this.value = before + selected + after;
1007
+ textarea.value = before + selected + after;
1008
+ const selStart = before.length;
1009
+ const selEnd = selStart + selected.length;
1010
+ requestAnimationFrame(() => {
1011
+ textarea.setSelectionRange(selStart, selEnd);
1012
+ this.updateActiveFormats();
1013
+ });
1014
+ this.dispatchChange();
1015
+ }
1016
+ });
1017
+ }
1018
+ /**
1019
+ * Determine the current active heading level (1-3) based on activeFormats.
1020
+ */
1021
+ getActiveHeadingLevel() {
1022
+ if (this.activeFormats.h1) return 1;
1023
+ if (this.activeFormats.h2) return 2;
1024
+ if (this.activeFormats.h3) return 3;
1025
+ return 0;
1026
+ }
1027
+ /**
1028
+ * Save the initial state to the undo stack.
1029
+ */
1030
+ saveInitialUndoState() {
1031
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
1032
+ const initialSelection = textarea ? { start: textarea.selectionStart ?? 0, end: textarea.selectionEnd ?? 0 } : { start: 0, end: 0 };
1033
+ this.undoStack = [{ value: this.value, selection: initialSelection }];
1034
+ this.redoStack = [];
1035
+ this.lastSavedValue = this.value;
1036
+ }
1037
+ /**
1038
+ * Save the current state to the undo stack before making a change.
1039
+ *
1040
+ */
1041
+ saveUndoStateBeforeChange() {
1042
+ if (this.isUndoRedoAction) return;
1043
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
1044
+ const selection = textarea ? { start: textarea.selectionStart ?? 0, end: textarea.selectionEnd ?? 0 } : { start: 0, end: 0 };
1045
+ this.undoStack.push({
1046
+ value: this.value,
1047
+ selection
1048
+ });
1049
+ if (this.undoStack.length > this.MAX_UNDO_STACK_SIZE) {
1050
+ this.undoStack.shift();
1051
+ }
1052
+ this.redoStack = [];
1053
+ }
1054
+ /**
1055
+ * Save the current state to the undo stack if the value has changed.
1056
+ */
1057
+ saveUndoState() {
1058
+ if (this.isUndoRedoAction) return;
1059
+ if (this.value !== this.lastSavedValue) {
1060
+ this.undoStack.push({
1061
+ value: this.lastSavedValue,
1062
+ selection: this.currentSelection
1063
+ });
1064
+ if (this.undoStack.length > this.MAX_UNDO_STACK_SIZE) {
1065
+ this.undoStack.shift();
1066
+ }
1067
+ this.redoStack = [];
1068
+ this.lastSavedValue = this.value;
1069
+ }
1070
+ }
1071
+ /**
1072
+ * Schedule an undo state save after a short delay.
1073
+ */
1074
+ scheduleUndoStateSave() {
1075
+ if (this.undoTimeout) {
1076
+ clearTimeout(this.undoTimeout);
1077
+ }
1078
+ this.undoTimeout = window.setTimeout(() => {
1079
+ this.saveUndoState();
1080
+ this.undoTimeout = null;
1081
+ }, this.UNDO_SAVE_DELAY);
1082
+ }
1083
+ /**
1084
+ * Handle list continuation when Enter is pressed within a list item.
1085
+ * Works against the textarea value/selection.
1086
+ * Returns true if handled.
1087
+ */
1088
+ handleListContinuation() {
1089
+ const textarea = this.textareaEl?.shadowRoot?.querySelector(
1090
+ "textarea"
1091
+ );
1092
+ if (!textarea) return false;
1093
+ const start = textarea.selectionStart ?? 0;
1094
+ const end = textarea.selectionEnd ?? 0;
1095
+ if (start !== end) return false;
1096
+ const value = this.value;
1097
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1098
+ let lineEnd = value.indexOf("\n", start);
1099
+ if (lineEnd === -1) lineEnd = value.length;
1100
+ const currentLine = value.slice(lineStart, lineEnd);
1101
+ const unorderedMatch = currentLine.match(/^(\s*)([-*+])\s*(.*)$/);
1102
+ const orderedMatch = currentLine.match(/^(\s*)(\d+)\.\s*(.*)$/);
1103
+ if (!unorderedMatch && !orderedMatch) {
1104
+ return false;
1105
+ }
1106
+ this.saveUndoStateBeforeChange();
1107
+ if (unorderedMatch) {
1108
+ const indent = unorderedMatch[1] ?? "";
1109
+ const marker = unorderedMatch[2] ?? "-";
1110
+ const content = unorderedMatch[3] ?? "";
1111
+ if (content.trim() === "") {
1112
+ const before2 = value.slice(0, lineStart);
1113
+ const after2 = value.slice(lineEnd);
1114
+ const newValue2 = before2 + (lineEnd === value.length ? "" : "") + after2;
1115
+ this.value = newValue2;
1116
+ textarea.value = newValue2;
1117
+ const newCursor2 = before2.length;
1118
+ requestAnimationFrame(() => {
1119
+ textarea.setSelectionRange(newCursor2, newCursor2);
1120
+ this.updateActiveFormats();
1121
+ });
1122
+ this.dispatchChange();
1123
+ return true;
1124
+ }
1125
+ const before = value.slice(0, start);
1126
+ const after = value.slice(start);
1127
+ const prefix = `
1128
+ ${indent}${marker} `;
1129
+ const newValue = before + prefix + after;
1130
+ this.value = newValue;
1131
+ textarea.value = newValue;
1132
+ const newCursor = start + prefix.length;
1133
+ requestAnimationFrame(() => {
1134
+ textarea.setSelectionRange(newCursor, newCursor);
1135
+ this.updateActiveFormats();
1136
+ });
1137
+ this.dispatchChange();
1138
+ return true;
1139
+ }
1140
+ if (orderedMatch) {
1141
+ const indent = orderedMatch[1] ?? "";
1142
+ const numberStr = orderedMatch[2] ?? "1";
1143
+ const content = orderedMatch[3] ?? "";
1144
+ if (content.trim() === "") {
1145
+ const before2 = value.slice(0, lineStart);
1146
+ const after2 = value.slice(lineEnd);
1147
+ const newValue2 = before2 + (lineEnd === value.length ? "" : "") + after2;
1148
+ this.value = newValue2;
1149
+ textarea.value = newValue2;
1150
+ const newCursor2 = before2.length;
1151
+ requestAnimationFrame(() => {
1152
+ textarea.setSelectionRange(newCursor2, newCursor2);
1153
+ this.updateActiveFormats();
1154
+ });
1155
+ this.dispatchChange();
1156
+ return true;
1157
+ }
1158
+ let nextNumber = parseInt(numberStr, 10) + 1;
1159
+ const beforeText = value.slice(0, lineStart);
1160
+ const beforeLines = beforeText.split("\n");
1161
+ let lastNumberAtThisLevel = 0;
1162
+ for (let i = beforeLines.length - 1; i >= 0; i--) {
1163
+ const line = beforeLines[i];
1164
+ const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1165
+ if (match && match[1] === indent) {
1166
+ lastNumberAtThisLevel = parseInt(match[2], 10);
1167
+ break;
1168
+ } else if (line.trim() !== "" && !line.match(/^\s*[-*+]\s+/) && !match) {
1169
+ break;
1170
+ }
1171
+ }
1172
+ if (lastNumberAtThisLevel > 0) {
1173
+ nextNumber = lastNumberAtThisLevel + 1;
1174
+ }
1175
+ const before = value.slice(0, start);
1176
+ const after = value.slice(start);
1177
+ const prefix = `
1178
+ ${indent}${nextNumber}. `;
1179
+ const newValue = before + prefix + after;
1180
+ const renumberedValue = this.renumberOrderedListItems(
1181
+ newValue,
1182
+ start + prefix.length,
1183
+ indent
1184
+ );
1185
+ this.value = renumberedValue;
1186
+ textarea.value = renumberedValue;
1187
+ const newCursor = start + prefix.length;
1188
+ requestAnimationFrame(() => {
1189
+ textarea.setSelectionRange(newCursor, newCursor);
1190
+ this.updateActiveFormats();
1191
+ });
1192
+ this.dispatchChange();
1193
+ return true;
1194
+ }
1195
+ return false;
1196
+ }
1197
+ /**
1198
+ * Handle Tab key to indent a list item (increase nesting level).
1199
+ * Returns true if handled.
1200
+ */
1201
+ handleListIndent() {
1202
+ const textarea = this.textareaEl?.shadowRoot?.querySelector(
1203
+ "textarea"
1204
+ );
1205
+ if (!textarea) return false;
1206
+ const start = textarea.selectionStart ?? 0;
1207
+ const end = textarea.selectionEnd ?? 0;
1208
+ if (start !== end) return false;
1209
+ const value = this.value;
1210
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1211
+ let lineEnd = value.indexOf("\n", start);
1212
+ if (lineEnd === -1) lineEnd = value.length;
1213
+ const currentLine = value.slice(lineStart, lineEnd);
1214
+ const unorderedMatch = currentLine.match(/^(\s*)([-*+])\s*(.*)$/);
1215
+ const orderedMatch = currentLine.match(/^(\s*)(\d+)\.\s*(.*)$/);
1216
+ if (!unorderedMatch && !orderedMatch) {
1217
+ return false;
1218
+ }
1219
+ this.saveUndoStateBeforeChange();
1220
+ const before = value.slice(0, lineStart);
1221
+ const after = value.slice(lineEnd);
1222
+ const indent = " ";
1223
+ let newLine;
1224
+ let newCursorOffset = 0;
1225
+ if (unorderedMatch) {
1226
+ const currentIndent = unorderedMatch[1] ?? "";
1227
+ const marker = unorderedMatch[2] ?? "-";
1228
+ const content = unorderedMatch[3] ?? "";
1229
+ newLine = `${currentIndent}${indent}${marker} ${content}`;
1230
+ newCursorOffset = indent.length;
1231
+ } else if (orderedMatch) {
1232
+ const currentIndent = orderedMatch[1] ?? "";
1233
+ const content = orderedMatch[3] ?? "";
1234
+ newLine = `${currentIndent}${indent}1. ${content}`;
1235
+ newCursorOffset = indent.length;
1236
+ } else {
1237
+ return false;
1238
+ }
1239
+ const newValue = before + newLine + after;
1240
+ this.value = newValue;
1241
+ textarea.value = newValue;
1242
+ const cursorInLine = start - lineStart;
1243
+ const newCursor = lineStart + cursorInLine + newCursorOffset;
1244
+ requestAnimationFrame(() => {
1245
+ textarea.setSelectionRange(newCursor, newCursor);
1246
+ this.updateActiveFormats();
1247
+ });
1248
+ this.dispatchChange();
1249
+ return true;
1250
+ }
1251
+ /**
1252
+ * Handle Shift+Tab key to outdent a list item (decrease nesting level).
1253
+ * Returns true if handled.
1254
+ */
1255
+ handleListOutdent() {
1256
+ const textarea = this.textareaEl?.shadowRoot?.querySelector(
1257
+ "textarea"
1258
+ );
1259
+ if (!textarea) return false;
1260
+ const start = textarea.selectionStart ?? 0;
1261
+ const end = textarea.selectionEnd ?? 0;
1262
+ if (start !== end) return false;
1263
+ const value = this.value;
1264
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1265
+ let lineEnd = value.indexOf("\n", start);
1266
+ if (lineEnd === -1) lineEnd = value.length;
1267
+ const currentLine = value.slice(lineStart, lineEnd);
1268
+ const unorderedMatch = currentLine.match(/^(\s*)([-*+])\s*(.*)$/);
1269
+ const orderedMatch = currentLine.match(/^(\s*)(\d+)\.\s*(.*)$/);
1270
+ if (!unorderedMatch && !orderedMatch) {
1271
+ return false;
1272
+ }
1273
+ const currentIndent = (unorderedMatch || orderedMatch)?.[1] ?? "";
1274
+ if (currentIndent.length < 4) {
1275
+ return false;
1276
+ }
1277
+ this.saveUndoStateBeforeChange();
1278
+ const before = value.slice(0, lineStart);
1279
+ const after = value.slice(lineEnd);
1280
+ const outdent = " ";
1281
+ let newLine;
1282
+ let newCursorOffset = 0;
1283
+ if (unorderedMatch) {
1284
+ const marker = unorderedMatch[2] ?? "-";
1285
+ const content = unorderedMatch[3] ?? "";
1286
+ const newIndent = currentIndent.slice(outdent.length);
1287
+ newLine = `${newIndent}${marker} ${content}`;
1288
+ newCursorOffset = -outdent.length;
1289
+ } else if (orderedMatch) {
1290
+ const content = orderedMatch[3] ?? "";
1291
+ const newIndent = currentIndent.slice(outdent.length);
1292
+ let newNumber = 1;
1293
+ const beforeLines = before.split("\n");
1294
+ for (let i = beforeLines.length - 1; i >= 0; i--) {
1295
+ const line = beforeLines[i];
1296
+ const match = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1297
+ if (match && match[1] === newIndent) {
1298
+ newNumber = parseInt(match[2], 10) + 1;
1299
+ break;
1300
+ } else if (line.trim() !== "" && !line.match(/^\s*[-*+]\s+/) && !match) {
1301
+ break;
1302
+ }
1303
+ }
1304
+ newLine = `${newIndent}${newNumber}. ${content}`;
1305
+ newCursorOffset = -outdent.length;
1306
+ } else {
1307
+ return false;
1308
+ }
1309
+ let newValue = before + newLine + after;
1310
+ if (orderedMatch) {
1311
+ const newIndent = currentIndent.slice(outdent.length);
1312
+ newValue = this.renumberOrderedListItems(
1313
+ newValue,
1314
+ lineStart + newLine.length,
1315
+ newIndent
1316
+ );
1317
+ }
1318
+ this.value = newValue;
1319
+ textarea.value = newValue;
1320
+ const cursorInLine = start - lineStart;
1321
+ const newCursor = Math.max(
1322
+ lineStart,
1323
+ lineStart + cursorInLine + newCursorOffset
1324
+ );
1325
+ requestAnimationFrame(() => {
1326
+ textarea.setSelectionRange(newCursor, newCursor);
1327
+ this.updateActiveFormats();
1328
+ });
1329
+ this.dispatchChange();
1330
+ return true;
1331
+ }
1332
+ /**
1333
+ * Renumber ordered list items that come after the given position with the same indentation level.
1334
+ *
1335
+ * @param value - The text content to process
1336
+ * @param fromPosition - Start looking for list items from this position
1337
+ * @param targetIndent - Only renumber items with this exact indentation
1338
+ * @returns The text with renumbered list items
1339
+ */
1340
+ renumberOrderedListItems(value, fromPosition, targetIndent) {
1341
+ const lines = value.split("\n");
1342
+ const fromLineIndex = Math.max(
1343
+ 0,
1344
+ value.slice(0, fromPosition).split("\n").length - 1
1345
+ );
1346
+ let nextExpectedNumber = 1;
1347
+ const currentLine = lines[fromLineIndex];
1348
+ const currentMatch = currentLine?.match(/^(\s*)(\d+)\.\s*(.*)$/);
1349
+ if (currentMatch && currentMatch[1] === targetIndent) {
1350
+ nextExpectedNumber = parseInt(currentMatch[2], 10) + 1;
1351
+ } else {
1352
+ for (let i = fromLineIndex - 1; i >= 0; i--) {
1353
+ const line = lines[i];
1354
+ const orderedMatch = line.match(/^(\s*)(\d+)\.\s*(.*)$/);
1355
+ if (orderedMatch && orderedMatch[1] === targetIndent) {
1356
+ nextExpectedNumber = parseInt(orderedMatch[2], 10) + 1;
1357
+ break;
1358
+ }
1359
+ }
1360
+ }
1361
+ let currentNumber = nextExpectedNumber;
1362
+ const orderedRegex = /^(\s*)(\d+)\.\s*(.*)$/;
1363
+ for (let i = fromLineIndex + 1; i < lines.length; i++) {
1364
+ const line = lines[i];
1365
+ const orderedMatch = line.match(orderedRegex);
1366
+ if (orderedMatch && orderedMatch[1] === targetIndent) {
1367
+ const indent = orderedMatch[1];
1368
+ const content = orderedMatch[3];
1369
+ lines[i] = `${indent}${currentNumber}. ${content}`;
1370
+ currentNumber++;
1371
+ } else if (orderedMatch && orderedMatch[1].length < targetIndent.length) {
1372
+ break;
1373
+ } else if (line.trim() !== "" && !orderedMatch && !line.match(/^\s*[-*+]\s+/)) {
1374
+ if (targetIndent === "") {
1375
+ break;
1376
+ }
1377
+ }
1378
+ }
1379
+ return lines.join("\n");
1380
+ }
1381
+ /**
1382
+ * Renumber any ordered lists that come after the given position, starting each new sequence from 1.
1383
+ * This is used when removing list formatting breaks the continuity of a list.
1384
+ *
1385
+ * @param value - The text content to process
1386
+ * @param fromPosition - Start looking for list items from this position
1387
+ * @returns The text with renumbered list sequences
1388
+ */
1389
+ renumberSubsequentOrderedLists(value, fromPosition) {
1390
+ const lines = value.split("\n");
1391
+ const fromLineIndex = Math.max(
1392
+ 0,
1393
+ value.slice(0, fromPosition).split("\n").length - 1
1394
+ );
1395
+ const orderedRegex = /^(\s*)(\d+)\.\s*(.*)$/;
1396
+ let currentSequenceNumber = 1;
1397
+ let inSequence = false;
1398
+ let currentIndent = "";
1399
+ for (let i = fromLineIndex + 1; i < lines.length; i++) {
1400
+ const line = lines[i];
1401
+ const orderedMatch = line.match(orderedRegex);
1402
+ if (orderedMatch) {
1403
+ const indent = orderedMatch[1];
1404
+ const content = orderedMatch[3];
1405
+ if (!inSequence || indent !== currentIndent) {
1406
+ currentSequenceNumber = 1;
1407
+ inSequence = true;
1408
+ currentIndent = indent;
1409
+ }
1410
+ lines[i] = `${indent}${currentSequenceNumber}. ${content}`;
1411
+ currentSequenceNumber++;
1412
+ } else if (line.trim() !== "" && !line.match(/^\s*[-*+]\s+/)) {
1413
+ inSequence = false;
1414
+ }
1415
+ }
1416
+ return lines.join("\n");
1417
+ }
1418
+ /**
1419
+ * Handle backspace to remove empty list items.
1420
+ * Returns true if handled.
1421
+ */
1422
+ handleListBackspace() {
1423
+ const textarea = this.textareaEl?.shadowRoot?.querySelector(
1424
+ "textarea"
1425
+ );
1426
+ if (!textarea) return false;
1427
+ const start = textarea.selectionStart ?? 0;
1428
+ const end = textarea.selectionEnd ?? 0;
1429
+ if (start !== end) return false;
1430
+ const value = this.value;
1431
+ const lineStart = value.lastIndexOf("\n", start - 1) + 1;
1432
+ let lineEnd = value.indexOf("\n", start);
1433
+ if (lineEnd === -1) lineEnd = value.length;
1434
+ const currentLine = value.slice(lineStart, lineEnd);
1435
+ const cursorPositionInLine = start - lineStart;
1436
+ const unorderedMatch = currentLine.match(/^(\s*)([-*+])\s*(.*)$/);
1437
+ const orderedMatch = currentLine.match(/^(\s*)(\d+)\.\s*(.*)$/);
1438
+ if (!unorderedMatch && !orderedMatch) {
1439
+ return false;
1440
+ }
1441
+ let markerEndPosition = 0;
1442
+ let hasContent = false;
1443
+ if (unorderedMatch) {
1444
+ const indent = unorderedMatch[1] ?? "";
1445
+ const marker = unorderedMatch[2] ?? "-";
1446
+ const content = unorderedMatch[3] ?? "";
1447
+ markerEndPosition = indent.length + marker.length + 1;
1448
+ hasContent = content.trim().length > 0;
1449
+ } else if (orderedMatch) {
1450
+ const indent = orderedMatch[1] ?? "";
1451
+ const numberStr = orderedMatch[2] ?? "1";
1452
+ const content = orderedMatch[3] ?? "";
1453
+ markerEndPosition = indent.length + numberStr.length + 2;
1454
+ hasContent = content.trim().length > 0;
1455
+ }
1456
+ if (cursorPositionInLine === markerEndPosition && !hasContent) {
1457
+ this.saveUndoStateBeforeChange();
1458
+ const before = value.slice(0, lineStart);
1459
+ const after = value.slice(
1460
+ lineEnd === value.length ? lineEnd : lineEnd + 1
1461
+ );
1462
+ let newValue = before + after;
1463
+ if (orderedMatch) {
1464
+ const indent = orderedMatch[1] ?? "";
1465
+ newValue = this.renumberOrderedListItems(
1466
+ newValue,
1467
+ before.length,
1468
+ indent
1469
+ );
1470
+ }
1471
+ this.value = newValue;
1472
+ textarea.value = newValue;
1473
+ let newCursor = before.length;
1474
+ if (before.endsWith("\n") && before.length > 1) {
1475
+ newCursor = before.length - 1;
1476
+ }
1477
+ requestAnimationFrame(() => {
1478
+ textarea.setSelectionRange(newCursor, newCursor);
1479
+ this.updateActiveFormats();
1480
+ });
1481
+ this.dispatchChange();
1482
+ return true;
1483
+ }
1484
+ return false;
1485
+ }
1486
+ /**
1487
+ /**
1488
+ * Perform an undo operation, reverting to the previous state.
1489
+ */
1490
+ undo() {
1491
+ if (this.isReadonly || this.isDisabled || this.undoStack.length <= 1) {
1492
+ return;
1493
+ }
1494
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
1495
+ const currentSelection = textarea ? { start: textarea.selectionStart ?? 0, end: textarea.selectionEnd ?? 0 } : { start: 0, end: 0 };
1496
+ this.redoStack.push({ value: this.value, selection: currentSelection });
1497
+ const previousState = this.undoStack.pop();
1498
+ if (!previousState) {
1499
+ return;
1500
+ }
1501
+ this.isUndoRedoAction = true;
1502
+ this.value = previousState.value;
1503
+ this.lastSavedValue = previousState.value;
1504
+ if (textarea) {
1505
+ textarea.value = previousState.value;
1506
+ }
1507
+ requestAnimationFrame(() => {
1508
+ if (textarea) {
1509
+ textarea.setSelectionRange(
1510
+ previousState.selection.start,
1511
+ previousState.selection.end
1512
+ );
1513
+ }
1514
+ this.updateActiveFormats();
1515
+ this.isUndoRedoAction = false;
1516
+ });
1517
+ this.dispatchChange();
1518
+ }
1519
+ /**
1520
+ * Perform a redo operation, reapplying a previously undone state.
1521
+ */
1522
+ redo() {
1523
+ if (this.isReadonly || this.isDisabled || this.redoStack.length === 0) {
1524
+ return;
1525
+ }
1526
+ const textarea = this.textareaEl?.shadowRoot?.querySelector("textarea");
1527
+ const currentSelection = textarea ? { start: textarea.selectionStart ?? 0, end: textarea.selectionEnd ?? 0 } : { start: 0, end: 0 };
1528
+ this.undoStack.push({ value: this.value, selection: currentSelection });
1529
+ const nextState = this.redoStack.pop();
1530
+ if (!nextState) {
1531
+ return;
1532
+ }
1533
+ this.isUndoRedoAction = true;
1534
+ this.value = nextState.value;
1535
+ this.lastSavedValue = nextState.value;
1536
+ if (textarea) {
1537
+ textarea.value = nextState.value;
1538
+ }
1539
+ requestAnimationFrame(() => {
1540
+ if (textarea) {
1541
+ textarea.setSelectionRange(
1542
+ nextState.selection.start,
1543
+ nextState.selection.end
1544
+ );
1545
+ }
1546
+ this.updateActiveFormats();
1547
+ this.isUndoRedoAction = false;
1548
+ });
1549
+ this.dispatchChange();
1550
+ }
1551
+ connectedCallback() {
1552
+ super.connectedCallback();
1553
+ document.addEventListener("click", this.handleOutsideClick);
1554
+ }
1555
+ firstUpdated(changedProperties) {
1556
+ super.firstUpdated(changedProperties);
1557
+ requestAnimationFrame(() => {
1558
+ this.saveInitialUndoState();
1559
+ this.updateActiveFormats();
1560
+ this.addKeyboardListeners();
1561
+ });
1562
+ }
1563
+ disconnectedCallback() {
1564
+ super.disconnectedCallback();
1565
+ document.removeEventListener("click", this.handleOutsideClick);
1566
+ if (this.undoTimeout) {
1567
+ clearTimeout(this.undoTimeout);
1568
+ }
1569
+ this.removeKeyboardListeners();
1570
+ this.savedSelectionForPreview = null;
1571
+ }
1572
+ labelTemplate() {
1573
+ return this.label ? x`<label class="heading-inter-14-bold text-neutral-20 block"
1574
+ >${this.label}</label
1575
+ >` : E;
1576
+ }
1577
+ descriptionTemplate() {
1578
+ return this.description ? x`<div class="paragraph-inter-12-regular text-neutral-20">
1579
+ <lukso-sanitize html-content=${this.description}></lukso-sanitize>
1580
+ </div>` : E;
1581
+ }
1582
+ /**
1583
+ * Restore focus and selection to the textarea after toolbar interactions.
1584
+ */
1585
+ restoreFocusAndSelection() {
1586
+ const ta = this.textareaEl?.shadowRoot?.querySelector(
1587
+ "textarea"
1588
+ );
1589
+ if (ta) {
1590
+ ta.focus();
1591
+ const sel = this.currentSelection;
1592
+ const start = typeof sel.start === "number" ? sel.start : ta.selectionStart ?? 0;
1593
+ const end = typeof sel.end === "number" ? sel.end : ta.selectionEnd ?? 0;
1594
+ ta.setSelectionRange(start, end);
1595
+ }
1596
+ }
1597
+ buttonTemplate(icon, handler, name, isActive = false) {
1598
+ return x`
1599
+ <lukso-tooltip text=${name} placement="top">
1600
+ <lukso-button
1601
+ @click=${() => {
1602
+ this.restoreFocusAndSelection();
1603
+ handler();
1604
+ }}
1605
+ aria-label=${name}
1606
+ aria-pressed=${isActive ? "true" : "false"}
1607
+ type="button"
1608
+ variant="secondary"
1609
+ size="small"
1610
+ custom-class=${this.toolbarButton({ active: isActive })}
1611
+ is-icon
1612
+ >
1613
+ <lukso-icon
1614
+ name=${icon}
1615
+ size="small"
1616
+ pack="vuesax"
1617
+ variant="linear"
1618
+ ></lukso-icon></lukso-button
1619
+ ></lukso-tooltip>
1620
+ `;
1621
+ }
1622
+ toolbarTemplate() {
1623
+ return x`
1624
+ <div class="flex items-center gap-2">
1625
+ <div class=${cn(this.styles().headingMenu())}>
1626
+ <!-- Heading -->
1627
+ <lukso-tooltip text="Heading options" placement="top">
1628
+ <lukso-button
1629
+ id=${this.headingTriggerId}
1630
+ @click=${(e) => {
1631
+ e.stopPropagation();
1632
+ this.isColorDropdownOpen = false;
1633
+ this.isListDropdownOpen = false;
1634
+ this.isHeadingDropdownOpen = !this.isHeadingDropdownOpen;
1635
+ }}
1636
+ aria-expanded=${this.isHeadingDropdownOpen ? "true" : "false"}
1637
+ aria-label="Heading options"
1638
+ variant="secondary"
1639
+ size="small"
1640
+ custom-class=${this.toolbarButton({
1641
+ active: this.getActiveHeadingLevel() > 0
1642
+ })}
1643
+ is-icon
1644
+ >
1645
+ <lukso-icon
1646
+ name="smallcaps"
1647
+ size="small"
1648
+ pack="vuesax"
1649
+ variant="linear"
1650
+ ></lukso-icon>
1651
+ </lukso-button>
1652
+ </lukso-tooltip>
1653
+ <lukso-dropdown
1654
+ id="headingDropdown"
1655
+ trigger-id=""
1656
+ size="medium"
1657
+ ?is-open=${this.isHeadingDropdownOpen}
1658
+ >
1659
+ <lukso-dropdown-option
1660
+ ?is-selected=${this.getActiveHeadingLevel() === 0}
1661
+ @click=${(e) => {
1662
+ e.stopPropagation();
1663
+ this.restoreFocusAndSelection();
1664
+ this.applyHeading(0);
1665
+ this.isHeadingDropdownOpen = false;
1666
+ }}
1667
+ size="medium"
1668
+ >
1669
+ Normal text
1670
+ </lukso-dropdown-option>
1671
+ <lukso-dropdown-option
1672
+ ?is-selected=${this.getActiveHeadingLevel() === 1}
1673
+ @click=${(e) => {
1674
+ e.stopPropagation();
1675
+ this.restoreFocusAndSelection();
1676
+ this.applyHeading(1);
1677
+ this.isHeadingDropdownOpen = false;
1678
+ }}
1679
+ size="medium"
1680
+ >
1681
+ Heading 1
1682
+ </lukso-dropdown-option>
1683
+ <lukso-dropdown-option
1684
+ ?is-selected=${this.getActiveHeadingLevel() === 2}
1685
+ @click=${(e) => {
1686
+ e.stopPropagation();
1687
+ this.restoreFocusAndSelection();
1688
+ this.applyHeading(2);
1689
+ this.isHeadingDropdownOpen = false;
1690
+ }}
1691
+ size="medium"
1692
+ >
1693
+ Heading 2
1694
+ </lukso-dropdown-option>
1695
+ <lukso-dropdown-option
1696
+ ?is-selected=${this.getActiveHeadingLevel() === 3}
1697
+ @click=${(e) => {
1698
+ e.stopPropagation();
1699
+ this.restoreFocusAndSelection();
1700
+ this.applyHeading(3);
1701
+ this.isHeadingDropdownOpen = false;
1702
+ }}
1703
+ size="medium"
1704
+ >
1705
+ Heading 3
1706
+ </lukso-dropdown-option>
1707
+ </lukso-dropdown>
1708
+ </div>
1709
+
1710
+ <!-- Bold -->
1711
+ ${this.buttonTemplate(
1712
+ "text-bold",
1713
+ () => this.toggleWrap("**"),
1714
+ "Bold",
1715
+ this.activeFormats.bold
1716
+ )}
1717
+
1718
+ <!-- Italic -->
1719
+ ${this.buttonTemplate(
1720
+ "text-italic",
1721
+ () => this.toggleWrap("*"),
1722
+ "Italic",
1723
+ this.activeFormats.italic
1724
+ )}
1725
+
1726
+ <!-- List -->
1727
+ <div class=${this.styles().listMenu()}>
1728
+ <lukso-tooltip text="List options" placement="top">
1729
+ <lukso-button
1730
+ id=${this.listTriggerId}
1731
+ @click=${(e) => {
1732
+ e.stopPropagation();
1733
+ this.restoreFocusAndSelection();
1734
+ this.isHeadingDropdownOpen = false;
1735
+ this.isColorDropdownOpen = false;
1736
+ this.isListDropdownOpen = !this.isListDropdownOpen;
1737
+ }}
1738
+ aria-expanded=${this.isListDropdownOpen ? "true" : "false"}
1739
+ aria-label="List options"
1740
+ variant="secondary"
1741
+ size="small"
1742
+ custom-class=${this.toolbarButton({
1743
+ active: this.activeFormats.unorderedList || this.activeFormats.orderedList
1744
+ })}
1745
+ is-icon
1746
+ >
1747
+ <lukso-icon
1748
+ name="task"
1749
+ size="small"
1750
+ pack="vuesax"
1751
+ variant="linear"
1752
+ ></lukso-icon>
1753
+ </lukso-button>
1754
+ </lukso-tooltip>
1755
+ <lukso-dropdown
1756
+ id="listDropdown"
1757
+ trigger-id=""
1758
+ size="medium"
1759
+ ?is-open=${this.isListDropdownOpen}
1760
+ >
1761
+ <lukso-dropdown-option
1762
+ ?is-selected=${this.getActiveListType() === "none"}
1763
+ @click=${(e) => {
1764
+ e.stopPropagation();
1765
+ this.restoreFocusAndSelection();
1766
+ this.applyList("none");
1767
+ this.isListDropdownOpen = false;
1768
+ }}
1769
+ size="medium"
1770
+ >
1771
+ No list
1772
+ </lukso-dropdown-option>
1773
+ <lukso-dropdown-option
1774
+ ?is-selected=${this.getActiveListType() === "unordered"}
1775
+ @click=${(e) => {
1776
+ e.stopPropagation();
1777
+ this.restoreFocusAndSelection();
1778
+ this.applyList("unordered");
1779
+ this.isListDropdownOpen = false;
1780
+ }}
1781
+ size="medium"
1782
+ >
1783
+ Unordered
1784
+ </lukso-dropdown-option>
1785
+ <lukso-dropdown-option
1786
+ ?is-selected=${this.getActiveListType() === "ordered"}
1787
+ @click=${(e) => {
1788
+ e.stopPropagation();
1789
+ this.restoreFocusAndSelection();
1790
+ this.applyList("ordered");
1791
+ this.isListDropdownOpen = false;
1792
+ }}
1793
+ size="medium"
1794
+ >
1795
+ Ordered
1796
+ </lukso-dropdown-option>
1797
+ </lukso-dropdown>
1798
+ </div>
1799
+
1800
+ <!-- Link -->
1801
+ ${this.buttonTemplate(
1802
+ "link",
1803
+ () => this.insertLink(),
1804
+ "Link",
1805
+ this.activeFormats.link
1806
+ )}
1807
+
1808
+ <!-- Color -->
1809
+ <div class=${this.styles().colorMenu()}>
1810
+ <lukso-tooltip text="Text color" placement="top">
1811
+ <lukso-button
1812
+ id=${this.colorTriggerId}
1813
+ @click=${(e) => {
1814
+ e.stopPropagation();
1815
+ this.restoreFocusAndSelection();
1816
+ this.isHeadingDropdownOpen = false;
1817
+ this.isListDropdownOpen = false;
1818
+ if (!this.isColorDropdownOpen) {
1819
+ const ta = this.textareaEl?.shadowRoot?.querySelector("textarea");
1820
+ if (ta) {
1821
+ this.savedSelection = {
1822
+ start: ta.selectionStart ?? 0,
1823
+ end: ta.selectionEnd ?? 0
1824
+ };
1825
+ }
1826
+ }
1827
+ this.isColorDropdownOpen = !this.isColorDropdownOpen;
1828
+ }}
1829
+ aria-expanded=${this.isColorDropdownOpen ? "true" : "false"}
1830
+ aria-pressed=${this.activeFormats.color ? "true" : "false"}
1831
+ aria-label="Text color"
1832
+ variant="secondary"
1833
+ size="small"
1834
+ custom-class=${this.toolbarButton({
1835
+ active: this.activeFormats.color
1836
+ })}
1837
+ is-icon
1838
+ >
1839
+ <div
1840
+ class="size-4 rounded-full"
1841
+ style="background-color: ${this.activeFormats.activeColor};"
1842
+ ></div>
1843
+ </lukso-button>
1844
+ </lukso-tooltip>
1845
+ <lukso-dropdown
1846
+ id="colorDropdown"
1847
+ trigger-id=""
1848
+ size="medium"
1849
+ max-height="300"
1850
+ ?is-open=${this.isColorDropdownOpen}
1851
+ >
1852
+ <div class="grid grid-cols-8 gap-2 p-2 w-[260px]">
1853
+ <div class="col-span-8 mb-2 flex items-center justify-between">
1854
+ <span class="text-xs text-neutral-60">Text Color</span>
1855
+ ${this.activeFormats.color ? x`<button
1856
+ class="text-xs text-neutral-60 hover:text-neutral-20 underline"
1857
+ @click=${(e) => {
1858
+ e.stopPropagation();
1859
+ this.clearColor();
1860
+ this.isColorDropdownOpen = false;
1861
+ }}
1862
+ type="button"
1863
+ >
1864
+ Clear
1865
+ </button>` : E}
1866
+ </div>
1867
+ ${this.colorSamples.map(
1868
+ (color) => x`
1869
+ <button
1870
+ class="w-6 h-6 rounded-4 border transition-all ${this.activeFormats.activeColor === color ? "border-neutral-20 ring-2 ring-purple-51" : "border-neutral-90 hover:border-neutral-60"}"
1871
+ style="background-color: ${color}"
1872
+ title=${color}
1873
+ aria-pressed=${this.activeFormats.activeColor === color ? "true" : "false"}
1874
+ @click=${(e) => {
1875
+ e.stopPropagation();
1876
+ this.selectColor(color);
1877
+ this.isColorDropdownOpen = false;
1878
+ }}
1879
+ ></button>
1880
+ `
1881
+ )}
1882
+ </div>
1883
+ </lukso-dropdown>
1884
+ </div>
1885
+
1886
+ <div class=${this.styles().divider()}></div>
1887
+ </div>
1888
+ `;
1889
+ }
1890
+ render() {
1891
+ const { wrapper, header, toolbar, area, editor, preview } = this.styles({
1892
+ isFullWidth: this.isFullWidth
1893
+ });
1894
+ return x`
1895
+ <div class=${wrapper()}>
1896
+ ${this.labelTemplate()} ${this.descriptionTemplate()}
1897
+
1898
+ <div class=${header()}>
1899
+ <div class=${toolbar()}>${this.toolbarTemplate()}</div>
1900
+ ${this.buttonTemplate(
1901
+ "eye",
1902
+ () => this.togglePreview(),
1903
+ "Toggle preview",
1904
+ this.isPreview
1905
+ )}
1906
+ </div>
1907
+
1908
+ <div class=${area()}>
1909
+ ${!this.isPreview ? x`<div class=${editor()}>
1910
+ <lukso-textarea
1911
+ .value=${this.value}
1912
+ name=${this.name ? this.name : E}
1913
+ size=${this.size ? this.size : E}
1914
+ rows=${this.rows ? this.rows : E}
1915
+ placeholder=${this.placeholder ? this.placeholder : E}
1916
+ error=${this.error ? this.error : E}
1917
+ ?is-full-width=${true}
1918
+ ?is-disabled=${this.isDisabled}
1919
+ ?is-readonly=${this.isReadonly}
1920
+ ?is-non-resizable=${this.isNonResizable}
1921
+ @on-input=${this.handleTextareaInput}
1922
+ @on-key-up=${this.handleTextareaKeyUp}
1923
+ @on-input-click=${this.handleTextareaClick}
1924
+ ></lukso-textarea>
1925
+ </div>` : x`<div class=${preview()}>
1926
+ <lukso-markdown
1927
+ value=${this.value}
1928
+ prose-classes="prose prose-base prose-gray"
1929
+ ></lukso-markdown>
1930
+ </div>`}
1931
+ </div>
1932
+ </div>
1933
+ `;
1934
+ }
1935
+ };
1936
+ __decorateClass([
1937
+ n({ type: String })
1938
+ ], LuksoMarkdownEditor.prototype, "value", 2);
1939
+ __decorateClass([
1940
+ n({ type: String })
1941
+ ], LuksoMarkdownEditor.prototype, "name", 2);
1942
+ __decorateClass([
1943
+ n({ type: String })
1944
+ ], LuksoMarkdownEditor.prototype, "label", 2);
1945
+ __decorateClass([
1946
+ n({ type: String })
1947
+ ], LuksoMarkdownEditor.prototype, "description", 2);
1948
+ __decorateClass([
1949
+ n({ type: String })
1950
+ ], LuksoMarkdownEditor.prototype, "error", 2);
1951
+ __decorateClass([
1952
+ n({ type: Boolean, attribute: "is-full-width" })
1953
+ ], LuksoMarkdownEditor.prototype, "isFullWidth", 2);
1954
+ __decorateClass([
1955
+ n({ type: Boolean, attribute: "is-readonly" })
1956
+ ], LuksoMarkdownEditor.prototype, "isReadonly", 2);
1957
+ __decorateClass([
1958
+ n({ type: Boolean, attribute: "is-disabled" })
1959
+ ], LuksoMarkdownEditor.prototype, "isDisabled", 2);
1960
+ __decorateClass([
1961
+ n({ type: Boolean, attribute: "is-non-resizable" })
1962
+ ], LuksoMarkdownEditor.prototype, "isNonResizable", 2);
1963
+ __decorateClass([
1964
+ n({ type: Boolean })
1965
+ ], LuksoMarkdownEditor.prototype, "autofocus", 2);
1966
+ __decorateClass([
1967
+ n({ type: String, reflect: true })
1968
+ ], LuksoMarkdownEditor.prototype, "size", 2);
1969
+ __decorateClass([
1970
+ n({ type: Boolean, attribute: "is-preview", reflect: true })
1971
+ ], LuksoMarkdownEditor.prototype, "isPreview", 2);
1972
+ __decorateClass([
1973
+ n({ type: Number })
1974
+ ], LuksoMarkdownEditor.prototype, "rows", 2);
1975
+ __decorateClass([
1976
+ n({ type: String })
1977
+ ], LuksoMarkdownEditor.prototype, "placeholder", 2);
1978
+ __decorateClass([
1979
+ r()
1980
+ ], LuksoMarkdownEditor.prototype, "savedSelectionForPreview", 2);
1981
+ __decorateClass([
1982
+ r()
1983
+ ], LuksoMarkdownEditor.prototype, "isHeadingDropdownOpen", 2);
1984
+ __decorateClass([
1985
+ r()
1986
+ ], LuksoMarkdownEditor.prototype, "isColorDropdownOpen", 2);
1987
+ __decorateClass([
1988
+ r()
1989
+ ], LuksoMarkdownEditor.prototype, "isListDropdownOpen", 2);
1990
+ __decorateClass([
1991
+ r()
1992
+ ], LuksoMarkdownEditor.prototype, "currentSelection", 2);
1993
+ __decorateClass([
1994
+ r()
1995
+ ], LuksoMarkdownEditor.prototype, "savedSelection", 2);
1996
+ __decorateClass([
1997
+ r()
1998
+ ], LuksoMarkdownEditor.prototype, "activeFormats", 2);
1999
+ __decorateClass([
2000
+ e("lukso-textarea")
2001
+ ], LuksoMarkdownEditor.prototype, "textareaEl", 2);
2002
+ LuksoMarkdownEditor = __decorateClass([
2003
+ t("lukso-markdown-editor")
2004
+ ], LuksoMarkdownEditor);
2005
+
2006
+ export { LuksoMarkdownEditor };