@jackuait/blok 0.7.3-beta.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-CdxHhr5i.mjs → blok-CdiMFzDz.mjs} +2256 -2141
  3. package/dist/chunks/{constants-C_H9o9Ao.mjs → constants-CjWRVOdm.mjs} +227 -190
  4. package/dist/chunks/{i18next-loader-D5HxE5ZQ.mjs → i18next-loader-p-7ioTwr.mjs} +1 -1
  5. package/dist/chunks/{lightweight-i18n-Safdy0ua.mjs → lightweight-i18n-BPeH69Dl.mjs} +14 -1
  6. package/dist/{messages-BbLCMWln2.mjs → chunks/messages--aM83pib2.mjs} +14 -1
  7. package/dist/chunks/{messages-ni0ahgYk2.mjs → messages-4OvVdaG52.mjs} +14 -1
  8. package/dist/chunks/{messages-DnPkoMz1.mjs → messages-5ohIWynJ.mjs} +14 -1
  9. package/dist/chunks/{messages-BjkDJuqh.mjs → messages-5thhSeME.mjs} +14 -1
  10. package/dist/chunks/{messages-hngFJrES2.mjs → messages-8pf7gRQm2.mjs} +14 -1
  11. package/dist/{messages-eCyczLYY.mjs → chunks/messages-B-i-d1Bc.mjs} +14 -1
  12. package/dist/chunks/{messages-CRxkRJRN.mjs → messages-B9zocutJ.mjs} +14 -1
  13. package/dist/{messages-pgqtPci-.mjs → chunks/messages-BAC7nLeM.mjs} +14 -1
  14. package/dist/chunks/{messages-DjOY_EqX.mjs → messages-BARPMN7R.mjs} +14 -1
  15. package/dist/{messages-Bsz7Qgj_.mjs → chunks/messages-BAgTIgPH.mjs} +15 -2
  16. package/dist/chunks/{messages-Cb-x47kY2.mjs → messages-BDTgiBJY2.mjs} +14 -1
  17. package/dist/chunks/{messages-BM1Su_Uy2.mjs → messages-BHf_VcXb2.mjs} +14 -1
  18. package/dist/{messages-wRvz0vQ3.mjs → chunks/messages-BIrkzbBP.mjs} +14 -1
  19. package/dist/{messages-DB9U3VIh.mjs → chunks/messages-BKjLgO5d.mjs} +14 -1
  20. package/dist/{messages-gldjQk7M.mjs → chunks/messages-BMzmli1K.mjs} +14 -1
  21. package/dist/chunks/{messages-CficsmSH2.mjs → messages-BO_jtRbZ2.mjs} +14 -1
  22. package/dist/chunks/{messages-kHTrX3wo2.mjs → messages-B_fFjpX12.mjs} +14 -1
  23. package/dist/{messages-BU9luYgO.mjs → chunks/messages-Bdk5tBNu.mjs} +14 -1
  24. package/dist/{messages-CRMZ79Xf.mjs → chunks/messages-BgaGPFuy2.mjs} +14 -1
  25. package/dist/{messages-DCOKudVN.mjs → chunks/messages-BsYzg2le.mjs} +14 -1
  26. package/dist/{messages-B9qltgXa.mjs → chunks/messages-BtS6JWMT.mjs} +14 -1
  27. package/dist/{messages-Cgy54529.mjs → chunks/messages-BvlSf_pu.mjs} +14 -1
  28. package/dist/{messages-oH0ADQ362.mjs → chunks/messages-C03I_oR-2.mjs} +14 -1
  29. package/dist/{messages-C8iAUPzI.mjs → chunks/messages-CDJLoStb.mjs} +14 -1
  30. package/dist/chunks/{messages-ClOxDE0y2.mjs → messages-CH_cexo62.mjs} +14 -1
  31. package/dist/{messages-DkkrjINb2.mjs → chunks/messages-CIBuZccC.mjs} +14 -1
  32. package/dist/chunks/{messages-BZ45xBlV.mjs → messages-CIZkNCpW.mjs} +14 -1
  33. package/dist/{messages-CJYE0_hr2.mjs → chunks/messages-CIugNDDO2.mjs} +14 -1
  34. package/dist/chunks/{messages-Cn5n0nHe2.mjs → messages-COkQfKa72.mjs} +14 -1
  35. package/dist/{messages-Bw-GC0m5.mjs → chunks/messages-CSpfBhlK2.mjs} +14 -1
  36. package/dist/chunks/{messages-tBHnC2Rj2.mjs → messages-CWp6-Y8f2.mjs} +14 -1
  37. package/dist/chunks/{messages-CteKp81J.mjs → messages-Co4WFeQ8.mjs} +14 -1
  38. package/dist/chunks/{messages-IJhiftj5.mjs → messages-CoRQE_Jc.mjs} +14 -1
  39. package/dist/{messages-DGZQXeav2.mjs → chunks/messages-CuRN1_ep2.mjs} +14 -1
  40. package/dist/{messages-BwdowdYD.mjs → chunks/messages-D0IKicWg.mjs} +14 -1
  41. package/dist/chunks/{messages-1C3OS98e.mjs → messages-D14soBOO.mjs} +14 -1
  42. package/dist/chunks/{messages-nE9Ko73n2.mjs → messages-D1SqLcxI2.mjs} +14 -1
  43. package/dist/{messages-COU4L-pL2.mjs → chunks/messages-D2uBlGXR2.mjs} +14 -1
  44. package/dist/chunks/{messages-DDr8J4FE.mjs → messages-DDpgr0B1.mjs} +14 -1
  45. package/dist/chunks/{messages-C232njMF2.mjs → messages-DEGzGmEQ2.mjs} +14 -1
  46. package/dist/chunks/{messages-6z-ULVyk.mjs → messages-DKChC8Qu.mjs} +14 -1
  47. package/dist/{messages-BP8ZuVaD.mjs → chunks/messages-DKCoHa5D.mjs} +14 -1
  48. package/dist/{messages-ruU_e2LK.mjs → chunks/messages-DPoPrTiZ.mjs} +14 -1
  49. package/dist/chunks/{messages-DziA-L3p.mjs → messages-DQOk-dTH.mjs} +14 -1
  50. package/dist/chunks/{messages-uLIUXFmU.mjs → messages-DUp8NnKT.mjs} +15 -2
  51. package/dist/chunks/{messages-rJdSnvyi.mjs → messages-DVg69mRj.mjs} +14 -1
  52. package/dist/chunks/{messages-Cq6wj6FG.mjs → messages-DeLzropc.mjs} +14 -1
  53. package/dist/chunks/{messages-DDRCk44J2.mjs → messages-Df69rfTF2.mjs} +15 -2
  54. package/dist/chunks/{messages-6EJxSImH.mjs → messages-DfsHFEWa.mjs} +14 -1
  55. package/dist/{messages-MaHxNgKA.mjs → chunks/messages-DgZnRQRS.mjs} +14 -1
  56. package/dist/{messages-B8M4YRFO2.mjs → chunks/messages-DhGHk-Ma2.mjs} +14 -1
  57. package/dist/{messages-3aRjZXpv.mjs → chunks/messages-Di7mfvB8.mjs} +14 -1
  58. package/dist/chunks/{messages-C1iQkKu82.mjs → messages-DlM9TmqS2.mjs} +14 -1
  59. package/dist/chunks/{messages-CejEH4FW2.mjs → messages-DmrsEYQm2.mjs} +14 -1
  60. package/dist/chunks/{messages-DK6pBwD2.mjs → messages-Dr-Ig3sw.mjs} +14 -1
  61. package/dist/{messages-BOxe7ewT.mjs → chunks/messages-DrXNb1gu.mjs} +14 -1
  62. package/dist/chunks/{messages-CvLXClh9.mjs → messages-EDTq4Q52.mjs} +14 -1
  63. package/dist/{messages-huTzItxA.mjs → chunks/messages-Ehx9YYeb2.mjs} +14 -1
  64. package/dist/chunks/{messages-CxTq0x772.mjs → messages-FCmAVA792.mjs} +14 -1
  65. package/dist/{messages-q7HzQPVt.mjs → chunks/messages-LL3Tflph.mjs} +14 -1
  66. package/dist/chunks/{messages-D9qyilS0.mjs → messages-N72K1hw3.mjs} +14 -1
  67. package/dist/chunks/{messages-C9XSSqS5.mjs → messages-Nz8C7Znm.mjs} +15 -2
  68. package/dist/{messages-e-KHuxtY2.mjs → chunks/messages-OIelQDL32.mjs} +14 -1
  69. package/dist/{messages-BQJzUYP-.mjs → chunks/messages-SsrFJhTN.mjs} +14 -1
  70. package/dist/{messages-CVBsztOg.mjs → chunks/messages-ohtcmr1w.mjs} +14 -1
  71. package/dist/chunks/{messages-CVzvKl6U.mjs → messages-stUQR58d.mjs} +14 -1
  72. package/dist/{messages-DlrZrm3s.mjs → chunks/messages-wUoSWFsJ2.mjs} +14 -1
  73. package/dist/chunks/{tools-B0YXCZFW.mjs → tools-B5yNeTZh.mjs} +1286 -872
  74. package/dist/full.mjs +15 -13
  75. package/dist/locales.mjs +81 -68
  76. package/dist/{messages-mVFAkdcY.mjs → messages-1_FCq0It.mjs} +14 -1
  77. package/dist/{chunks/messages-DoDbCS02.mjs → messages-8zo-T-Nx2.mjs} +14 -1
  78. package/dist/{chunks/messages-DacahKek.mjs → messages-B21zLG6b.mjs} +14 -1
  79. package/dist/{messages-BBgyeB_N.mjs → messages-B9ythxux.mjs} +14 -1
  80. package/dist/{messages-C_RPN2GV.mjs → messages-BAZK-8Zb.mjs} +14 -1
  81. package/dist/{messages-D6Sr5cUE.mjs → messages-BB8umWL1.mjs} +14 -1
  82. package/dist/{chunks/messages-Dhe8_mnQ.mjs → messages-BD_U2EnE.mjs} +14 -1
  83. package/dist/{messages-6G0Eia-2.mjs → messages-BRC9E_sp.mjs} +14 -1
  84. package/dist/{chunks/messages-BUy3odZo.mjs → messages-BSLYh59S.mjs} +14 -1
  85. package/dist/{messages-K7ROT6ea.mjs → messages-BSwhWcYw.mjs} +14 -1
  86. package/dist/{messages-BztXgybv2.mjs → messages-BTR3QlIb2.mjs} +14 -1
  87. package/dist/{messages-C0gyqo4h2.mjs → messages-BZXBdD_S2.mjs} +14 -1
  88. package/dist/{messages-BlnZ8CkJ2.mjs → messages-Bm8I_Li12.mjs} +15 -2
  89. package/dist/{chunks/messages-BKtWlK39.mjs → messages-BrcgNZOJ.mjs} +14 -1
  90. package/dist/{messages-Be1CCcsp2.mjs → messages-BzZ8LahA2.mjs} +14 -1
  91. package/dist/{chunks/messages-B_90PYaG.mjs → messages-C4HpNHfK.mjs} +14 -1
  92. package/dist/{chunks/messages-m6bLP64R2.mjs → messages-C5hD5pSd2.mjs} +14 -1
  93. package/dist/{chunks/messages-C3AJz_i6.mjs → messages-C7Rz00Tp.mjs} +14 -1
  94. package/dist/{chunks/messages-C1lqY56F2.mjs → messages-C92tAUYT2.mjs} +14 -1
  95. package/dist/{messages-Kye1BINC.mjs → messages-C9LsEUfG.mjs} +14 -1
  96. package/dist/{chunks/messages-tg78NAmW.mjs → messages-CHWfj4ik.mjs} +14 -1
  97. package/dist/{messages-CaJRIGUu2.mjs → messages-CHeucLGl2.mjs} +14 -1
  98. package/dist/{chunks/messages-DcPtg90i.mjs → messages-CIxT1nSh.mjs} +14 -1
  99. package/dist/{messages-C7R0m6oE2.mjs → messages-CKX9iXIb2.mjs} +14 -1
  100. package/dist/{messages-CzSLUJQt.mjs → messages-CKmmJ9tW.mjs} +14 -1
  101. package/dist/{messages-DpY9s4Qi2.mjs → messages-CTFwu5-h2.mjs} +14 -1
  102. package/dist/{messages-DAssrN5L2.mjs → messages-CTPFrtK92.mjs} +14 -1
  103. package/dist/{messages-BZgGD0zf2.mjs → messages-CWzET_9H2.mjs} +14 -1
  104. package/dist/{messages-CkmVEyEQ2.mjs → messages-CkIRmpfZ2.mjs} +14 -1
  105. package/dist/{messages-B2bHgIcC2.mjs → messages-CmoTIebG2.mjs} +14 -1
  106. package/dist/{messages-DZbsds_k2.mjs → messages-Co26RSCV2.mjs} +14 -1
  107. package/dist/{messages-DrouoDgp.mjs → messages-CqNzlpWi.mjs} +15 -2
  108. package/dist/{messages-m1uf_AMy2.mjs → messages-CrWsU4Xw2.mjs} +14 -1
  109. package/dist/{messages-TseLyyoU.mjs → messages-CsmTziC6.mjs} +14 -1
  110. package/dist/{messages-CAffVeAE2.mjs → messages-CsnglxbV2.mjs} +14 -1
  111. package/dist/{messages-CE305J0p.mjs → messages-Cu7Lr1wp.mjs} +14 -1
  112. package/dist/{chunks/messages-Ul43l29K2.mjs → messages-Cy3Ne_M9.mjs} +14 -1
  113. package/dist/{chunks/messages-CzCqu58X2.mjs → messages-CzZAfGif.mjs} +14 -1
  114. package/dist/{messages-C_4VGaBC.mjs → messages-D5rnT-BC.mjs} +14 -1
  115. package/dist/{chunks/messages-yJmwc3zD.mjs → messages-D8iCBMg7.mjs} +14 -1
  116. package/dist/{chunks/messages-BtNOlsMj.mjs → messages-DDJOu049.mjs} +14 -1
  117. package/dist/{chunks/messages-CnzaTbel.mjs → messages-DDiP6yex.mjs} +14 -1
  118. package/dist/{chunks/messages-DotEkUpQ.mjs → messages-DHJ1fZLL.mjs} +14 -1
  119. package/dist/{chunks/messages-3ePgbbpx2.mjs → messages-DToWAonn2.mjs} +14 -1
  120. package/dist/{messages-BxQ1gzJF2.mjs → messages-DV29fJMD2.mjs} +14 -1
  121. package/dist/{chunks/messages-DJWRON2S.mjs → messages-D_-rh8gl.mjs} +14 -1
  122. package/dist/{chunks/messages-D6SAC8Lc2.mjs → messages-D_cAZ4Ic2.mjs} +14 -1
  123. package/dist/{chunks/messages-pKUiAqlX2.mjs → messages-Dc7ZzqYN.mjs} +14 -1
  124. package/dist/{chunks/messages-JRavIeeW.mjs → messages-DiSeSE8p.mjs} +14 -1
  125. package/dist/{chunks/messages-DbS9Oibb.mjs → messages-Djhu5RJd.mjs} +14 -1
  126. package/dist/{messages-BdJ1lCo_.mjs → messages-Dr9L1psl.mjs} +15 -2
  127. package/dist/{messages-voUPclMU.mjs → messages-EIeWKoc5.mjs} +14 -1
  128. package/dist/{messages-GrVSCmXW.mjs → messages-EwoT2jof.mjs} +14 -1
  129. package/dist/{chunks/messages-B_nVGWdk.mjs → messages-F7cRf-20.mjs} +14 -1
  130. package/dist/{chunks/messages-Dxrg70jo.mjs → messages-JZhs_0pf.mjs} +14 -1
  131. package/dist/{messages-CB0RKGVM.mjs → messages-JwMkLben.mjs} +14 -1
  132. package/dist/{messages-Cb5JJ8C_2.mjs → messages-LyzjEEIj2.mjs} +14 -1
  133. package/dist/{chunks/messages-DQ4VyVJf2.mjs → messages-SepwOOcg.mjs} +14 -1
  134. package/dist/{chunks/messages-DfTU2I8J.mjs → messages-TI0u6Ked.mjs} +14 -1
  135. package/dist/{chunks/messages-D7aoKTPD.mjs → messages-Tx25QErT.mjs} +14 -1
  136. package/dist/{chunks/messages-D9nReG4C2.mjs → messages-bRqMCja-2.mjs} +14 -1
  137. package/dist/{chunks/messages-h474TGR72.mjs → messages-lEyiemqU2.mjs} +14 -1
  138. package/dist/{messages-BENRci-_2.mjs → messages-mVLfVtQX2.mjs} +14 -1
  139. package/dist/{messages-CEhkWwqI.mjs → messages-ouO9js8Z.mjs} +14 -1
  140. package/dist/{messages--XEfVx572.mjs → messages-ouRGTAKo2.mjs} +14 -1
  141. package/dist/{chunks/messages-DNrK8lCg2.mjs → messages-qV14y_oA2.mjs} +14 -1
  142. package/dist/{chunks/messages-Yk__PXZQ.mjs → messages-rM6YFLZH.mjs} +15 -2
  143. package/dist/react.mjs +2 -2
  144. package/dist/tools.mjs +3 -3
  145. package/package.json +2 -2
  146. package/src/components/block/style-manager.ts +1 -1
  147. package/src/components/blocks.ts +26 -54
  148. package/src/components/constants/data-attributes.ts +0 -2
  149. package/src/components/i18n/locales/am/messages.json +14 -1
  150. package/src/components/i18n/locales/ar/messages.json +14 -1
  151. package/src/components/i18n/locales/az/messages.json +14 -1
  152. package/src/components/i18n/locales/bg/messages.json +15 -2
  153. package/src/components/i18n/locales/bn/messages.json +14 -1
  154. package/src/components/i18n/locales/bs/messages.json +14 -1
  155. package/src/components/i18n/locales/cs/messages.json +14 -1
  156. package/src/components/i18n/locales/da/messages.json +14 -1
  157. package/src/components/i18n/locales/de/messages.json +14 -1
  158. package/src/components/i18n/locales/dv/messages.json +14 -1
  159. package/src/components/i18n/locales/el/messages.json +14 -1
  160. package/src/components/i18n/locales/en/messages.json +14 -1
  161. package/src/components/i18n/locales/es/messages.json +14 -1
  162. package/src/components/i18n/locales/et/messages.json +15 -2
  163. package/src/components/i18n/locales/fa/messages.json +14 -1
  164. package/src/components/i18n/locales/fi/messages.json +14 -1
  165. package/src/components/i18n/locales/fil/messages.json +14 -1
  166. package/src/components/i18n/locales/fr/messages.json +14 -1
  167. package/src/components/i18n/locales/gu/messages.json +14 -1
  168. package/src/components/i18n/locales/he/messages.json +14 -1
  169. package/src/components/i18n/locales/hi/messages.json +14 -1
  170. package/src/components/i18n/locales/hr/messages.json +14 -1
  171. package/src/components/i18n/locales/hu/messages.json +14 -1
  172. package/src/components/i18n/locales/hy/messages.json +14 -1
  173. package/src/components/i18n/locales/id/messages.json +14 -1
  174. package/src/components/i18n/locales/it/messages.json +14 -1
  175. package/src/components/i18n/locales/ja/messages.json +14 -1
  176. package/src/components/i18n/locales/ka/messages.json +14 -1
  177. package/src/components/i18n/locales/km/messages.json +14 -1
  178. package/src/components/i18n/locales/kn/messages.json +14 -1
  179. package/src/components/i18n/locales/ko/messages.json +14 -1
  180. package/src/components/i18n/locales/ku/messages.json +14 -1
  181. package/src/components/i18n/locales/lo/messages.json +14 -1
  182. package/src/components/i18n/locales/lt/messages.json +14 -1
  183. package/src/components/i18n/locales/lv/messages.json +14 -1
  184. package/src/components/i18n/locales/mk/messages.json +14 -1
  185. package/src/components/i18n/locales/ml/messages.json +14 -1
  186. package/src/components/i18n/locales/mn/messages.json +14 -1
  187. package/src/components/i18n/locales/mr/messages.json +14 -1
  188. package/src/components/i18n/locales/ms/messages.json +14 -1
  189. package/src/components/i18n/locales/my/messages.json +14 -1
  190. package/src/components/i18n/locales/ne/messages.json +14 -1
  191. package/src/components/i18n/locales/nl/messages.json +14 -1
  192. package/src/components/i18n/locales/no/messages.json +14 -1
  193. package/src/components/i18n/locales/pa/messages.json +14 -1
  194. package/src/components/i18n/locales/pl/messages.json +14 -1
  195. package/src/components/i18n/locales/ps/messages.json +14 -1
  196. package/src/components/i18n/locales/pt/messages.json +14 -1
  197. package/src/components/i18n/locales/ro/messages.json +14 -1
  198. package/src/components/i18n/locales/ru/messages.json +15 -2
  199. package/src/components/i18n/locales/sd/messages.json +14 -1
  200. package/src/components/i18n/locales/si/messages.json +14 -1
  201. package/src/components/i18n/locales/sk/messages.json +14 -1
  202. package/src/components/i18n/locales/sl/messages.json +14 -1
  203. package/src/components/i18n/locales/sq/messages.json +14 -1
  204. package/src/components/i18n/locales/sr/messages.json +14 -1
  205. package/src/components/i18n/locales/sv/messages.json +14 -1
  206. package/src/components/i18n/locales/sw/messages.json +14 -1
  207. package/src/components/i18n/locales/ta/messages.json +14 -1
  208. package/src/components/i18n/locales/te/messages.json +14 -1
  209. package/src/components/i18n/locales/th/messages.json +14 -1
  210. package/src/components/i18n/locales/tr/messages.json +14 -1
  211. package/src/components/i18n/locales/ug/messages.json +14 -1
  212. package/src/components/i18n/locales/uk/messages.json +15 -2
  213. package/src/components/i18n/locales/ur/messages.json +14 -1
  214. package/src/components/i18n/locales/vi/messages.json +14 -1
  215. package/src/components/i18n/locales/yi/messages.json +14 -1
  216. package/src/components/i18n/locales/zh/messages.json +14 -1
  217. package/src/components/icons/index.ts +52 -6
  218. package/src/components/inline-tools/inline-tool-link.ts +202 -5
  219. package/src/components/inline-tools/inline-tool-marker.ts +166 -23
  220. package/src/components/inline-tools/inline-tool-strikethrough.ts +408 -0
  221. package/src/components/inline-tools/inline-tool-underline.ts +408 -0
  222. package/src/components/inline-tools/utils/formatting-range-utils.ts +10 -1
  223. package/src/components/modules/blockManager/blockManager.ts +2 -2
  224. package/src/components/modules/blockManager/operations.ts +2 -2
  225. package/src/components/modules/blockManager/repository.ts +1 -9
  226. package/src/components/modules/blockManager/types.ts +1 -1
  227. package/src/components/modules/drag/operations/DragOperations.ts +45 -6
  228. package/src/components/modules/paste/google-docs-preprocessor.ts +69 -2
  229. package/src/components/modules/paste/handlers/blok-data-handler.ts +96 -19
  230. package/src/components/modules/rectangleSelection.ts +48 -0
  231. package/src/components/modules/renderer.ts +2 -0
  232. package/src/components/modules/toolbar/blockSettings.ts +1 -1
  233. package/src/components/modules/toolbar/index.ts +26 -2
  234. package/src/components/modules/toolbar/plus-button.ts +15 -5
  235. package/src/components/selection/fake-background/index.ts +9 -10
  236. package/src/components/shared/color-picker.ts +116 -95
  237. package/src/components/shared/color-presets.ts +30 -2
  238. package/src/components/ui/toolbox.ts +36 -7
  239. package/src/components/utils/color-mapping.ts +43 -1
  240. package/src/components/utils/color-migration.ts +37 -0
  241. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +4 -3
  242. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +5 -39
  243. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +2 -2
  244. package/src/components/utils/popover/components/popover-item/popover-item.ts +11 -0
  245. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -3
  246. package/src/components/utils/popover/components/search-input/search-input.ts +1 -32
  247. package/src/components/utils/popover/popover-abstract.ts +2 -4
  248. package/src/components/utils/popover/popover-desktop.ts +1 -16
  249. package/src/components/utils/popover/popover-inline.ts +1 -2
  250. package/src/components/utils/popover/popover-mobile.ts +2 -2
  251. package/src/components/utils/popover/popover.const.ts +1 -1
  252. package/src/full.ts +7 -1
  253. package/src/stories/MarkerColors.stories.ts +13 -34
  254. package/src/stories/Table.stories.ts +15 -9
  255. package/src/styles/main.css +313 -15
  256. package/src/tools/header/index.ts +11 -9
  257. package/src/tools/index.ts +4 -0
  258. package/src/tools/list/constants.ts +13 -6
  259. package/src/tools/list/depth-validator.ts +13 -1
  260. package/src/tools/list/dom-builder.ts +6 -3
  261. package/src/tools/list/index.ts +3 -2
  262. package/src/tools/paragraph/index.ts +2 -2
  263. package/src/tools/table/table-cell-blocks.ts +25 -0
  264. package/src/tools/table/table-cell-color-picker.ts +1 -1
  265. package/src/tools/table/table-cell-selection.ts +1 -2
  266. package/src/tools/table/table-core.ts +2 -2
  267. package/src/tools/table/table-grip-visuals.ts +13 -5
  268. package/src/tools/table/table-heading-toggle.ts +15 -9
  269. package/src/tools/table/table-row-col-controls.ts +17 -11
  270. package/src/tools/table/table-row-col-drag.ts +26 -3
  271. package/src/tools/toggle/constants.ts +13 -6
  272. package/src/tools/toggle/index.ts +1 -1
  273. package/types/tools/hook-events.d.ts +6 -0
  274. package/types/utils/popover/popover-item.d.ts +6 -0
  275. package/CHANGELOG.md +0 -119
@@ -1,7 +1,26 @@
1
1
  import type { I18n } from '../../../types/api';
2
2
  import { parseColor } from '../utils/color-mapping';
3
+ import { onHover } from '../utils/tooltip';
3
4
  import { twMerge } from '../utils/tw';
4
- import { COLOR_PRESETS } from './color-presets';
5
+ import { COLOR_PRESETS, COLOR_PRESETS_DARK } from './color-presets';
6
+
7
+ /**
8
+ * Returns the appropriate preset array for the current theme.
9
+ * Checks the data-blok-theme attribute first (explicit override),
10
+ * then falls back to the prefers-color-scheme media query.
11
+ */
12
+ function getActivePresets(): typeof COLOR_PRESETS {
13
+ const theme = document.documentElement.getAttribute('data-blok-theme');
14
+
15
+ if (theme === 'dark') return COLOR_PRESETS_DARK;
16
+ if (theme === 'light') return COLOR_PRESETS;
17
+
18
+ if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
19
+ return COLOR_PRESETS_DARK;
20
+ }
21
+
22
+ return COLOR_PRESETS;
23
+ }
5
24
 
6
25
  /**
7
26
  * Compare two CSS color strings for equality by their parsed RGB tuples.
@@ -23,7 +42,7 @@ function colorsEqual(a: string, b: string): boolean {
23
42
  }
24
43
 
25
44
  /**
26
- * Describes one tab in the color picker (e.g. "Text" or "Background")
45
+ * Describes one section in the color picker (e.g. "Text" or "Background")
27
46
  */
28
47
  export interface ColorPickerMode {
29
48
  key: string;
@@ -37,7 +56,6 @@ export interface ColorPickerMode {
37
56
  export interface ColorPickerOptions {
38
57
  i18n: I18n;
39
58
  modes: [ColorPickerMode, ColorPickerMode];
40
- defaultModeIndex?: number;
41
59
  testIdPrefix: string;
42
60
  onColorSelect: (color: string | null, modeKey: string) => void;
43
61
  }
@@ -49,163 +67,166 @@ export interface ColorPickerHandle {
49
67
  element: HTMLDivElement;
50
68
  /**
51
69
  * Set the currently active color for visual indication on the matching swatch.
52
- * Pass null to clear any active indicator.
70
+ * Pass null to clear any active indicator for that section.
53
71
  * @param color - CSS color value or null to clear
54
- * @param modeKey - The mode key (e.g. 'color', 'background-color') to match the correct preset field
72
+ * @param modeKey - The mode key (e.g. 'color', 'background-color') to match the correct section
55
73
  */
56
74
  setActiveColor: (color: string | null, modeKey: string) => void;
57
75
  /**
58
- * Reset the picker state (tab index) back to defaultModeIndex.
59
- * Call this when the picker is reopened to ensure consistent initial state.
76
+ * Reset the picker state back to defaults (clear all active colors).
60
77
  */
61
78
  reset: () => void;
62
79
  }
63
80
 
64
81
  /**
65
- * Base Tailwind classes shared by tab buttons
82
+ * Neutral background for text-mode swatches so they render as visible buttons.
83
+ * Uses a CSS variable so it adapts to dark mode.
66
84
  */
67
- const TAB_BASE_CLASSES = 'flex-1 py-1.5 text-xs text-center rounded-md cursor-pointer border-none outline-hidden transition-colors';
85
+ const SWATCH_NEUTRAL_BG = 'var(--blok-swatch-neutral-bg)';
68
86
 
69
87
  /**
70
- * Neutral background for text-mode swatches so they render as visible buttons
71
- */
72
- const SWATCH_NEUTRAL_BG = '#f7f7f5';
73
-
74
- /**
75
- * Creates a color picker element with two tabs (e.g. Text / Background),
76
- * a 5-column swatch grid, and a "Default" reset button.
88
+ * Creates a color picker element with two always-visible sections (e.g. Text / Background),
89
+ * each containing a 5-column swatch grid with a "Default" reset swatch at position 0.
77
90
  *
78
91
  * Shared between the marker inline tool and the table cell color popover.
79
92
  */
80
93
  export function createColorPicker(options: ColorPickerOptions): ColorPickerHandle {
81
94
  const { i18n, modes, testIdPrefix, onColorSelect } = options;
82
- const defaultModeIndex = options.defaultModeIndex ?? 0;
83
- const state = { modeIndex: defaultModeIndex, activeColor: null as string | null };
95
+ const state = {
96
+ activeColors: Object.fromEntries(modes.map((m) => [m.key, null])) as Record<string, string | null>,
97
+ };
84
98
 
85
99
  const wrapper = document.createElement('div');
86
100
 
87
101
  wrapper.setAttribute('data-blok-testid', `${testIdPrefix}-picker`);
88
- wrapper.className = 'flex flex-col gap-2 p-2';
102
+ wrapper.className = 'flex flex-col gap-3 p-2';
89
103
 
90
104
  /**
91
- * Tab row
105
+ * One grid element per section, stored so we can re-render independently.
92
106
  */
93
- const tabRow = document.createElement('div');
107
+ const sectionGrids: HTMLDivElement[] = [];
94
108
 
95
- tabRow.className = 'flex gap-0.5 mb-0.5';
109
+ /**
110
+ * Build sections once; re-render only the grids on state changes.
111
+ */
112
+ modes.forEach((mode) => {
113
+ const section = document.createElement('div');
96
114
 
97
- const tabButtons: HTMLButtonElement[] = [];
115
+ section.setAttribute('data-blok-testid', `${testIdPrefix}-section-${mode.key}`);
116
+ section.className = 'flex flex-col gap-1';
98
117
 
99
- modes.forEach((mode, modeIndex) => {
100
- const tab = document.createElement('button');
118
+ const title = document.createElement('div');
101
119
 
102
- tab.setAttribute('data-blok-testid', `${testIdPrefix}-tab-${mode.key}`);
103
- tab.textContent = i18n.t(mode.labelKey);
104
- tab.addEventListener('click', () => {
105
- state.modeIndex = modeIndex;
106
- updateTabs();
107
- renderSwatches();
108
- });
109
- tabButtons.push(tab);
110
- tabRow.appendChild(tab);
111
- });
120
+ title.className = 'text-xs font-medium text-text-primary/60 px-0.5';
121
+ title.textContent = i18n.t(mode.labelKey);
112
122
 
113
- const updateTabs = (): void => {
114
- for (const [index, button] of tabButtons.entries()) {
115
- button.className = twMerge(
116
- TAB_BASE_CLASSES,
117
- index === state.modeIndex ? 'bg-item-hover-bg font-medium' : 'bg-transparent hover:bg-item-hover-bg/50'
118
- );
119
- }
120
- };
123
+ const grid = document.createElement('div');
124
+
125
+ grid.className = 'grid gap-1';
126
+ grid.style.gridTemplateColumns = 'repeat(5, 2.25rem)';
127
+ sectionGrids.push(grid);
128
+
129
+ section.appendChild(title);
130
+ section.appendChild(grid);
131
+ wrapper.appendChild(section);
132
+ });
121
133
 
122
134
  /**
123
- * Color grid
135
+ * Render the swatches for one section.
124
136
  */
125
- const grid = document.createElement('div');
137
+ const renderSection = (modeIndex: number): void => {
138
+ const grid = sectionGrids[modeIndex];
139
+ const mode = modes[modeIndex];
140
+ const presets = getActivePresets();
126
141
 
127
- grid.setAttribute('data-blok-testid', `${testIdPrefix}-grid`);
128
- grid.className = 'grid grid-cols-5 gap-1.5';
129
-
130
- const renderSwatches = (): void => {
131
142
  grid.innerHTML = '';
132
143
 
133
- const currentMode = modes[state.modeIndex];
144
+ const activeColorForSection = state.activeColors[mode.key];
145
+
146
+ // Default swatch (first position) — clears the active color for this section
147
+ const defaultSwatch = document.createElement('button');
148
+ const isDefaultActive = activeColorForSection === null;
149
+
150
+ defaultSwatch.setAttribute('data-blok-testid', `${testIdPrefix}-swatch-${mode.key}-default`);
151
+ defaultSwatch.className = twMerge(
152
+ 'w-9 h-9 rounded-md cursor-pointer border-none outline-hidden',
153
+ 'flex items-center justify-center text-sm font-semibold',
154
+ 'transition-[box-shadow,transform] ring-inset hover:ring-2 hover:ring-swatch-ring-hover active:scale-90',
155
+ isDefaultActive && 'ring-2 ring-swatch-ring-hover'
156
+ );
157
+ defaultSwatch.textContent = mode.presetField === 'text' ? 'A' : '';
158
+
159
+ if (mode.presetField === 'text') {
160
+ defaultSwatch.style.color = 'var(--blok-text-primary)';
161
+ defaultSwatch.style.backgroundColor = SWATCH_NEUTRAL_BG;
162
+ } else {
163
+ defaultSwatch.style.backgroundColor = SWATCH_NEUTRAL_BG;
164
+ }
165
+ defaultSwatch.addEventListener('click', () => {
166
+ onColorSelect(null, mode.key);
167
+ });
168
+ const defaultLabel = i18n.t('tools.colorPicker.defaultSwatchLabel')
169
+ .replace('{default}', i18n.t('tools.marker.default'))
170
+ .replace('{mode}', i18n.t(mode.labelKey).toLowerCase());
171
+
172
+ onHover(defaultSwatch, defaultLabel.charAt(0).toUpperCase() + defaultLabel.slice(1), { placement: 'top' });
173
+ grid.appendChild(defaultSwatch);
134
174
 
135
- for (const preset of COLOR_PRESETS) {
175
+ for (const preset of presets) {
136
176
  const swatch = document.createElement('button');
137
- const swatchColor = currentMode.presetField === 'text' ? preset.text : preset.bg;
138
- const isActive = state.activeColor !== null && colorsEqual(swatchColor, state.activeColor);
177
+ const swatchColor = mode.presetField === 'text' ? preset.text : preset.bg;
178
+ const isActive = activeColorForSection !== null && colorsEqual(swatchColor, activeColorForSection);
139
179
 
140
- swatch.setAttribute('data-blok-testid', `${testIdPrefix}-swatch-${preset.name}`);
180
+ swatch.setAttribute('data-blok-testid', `${testIdPrefix}-swatch-${mode.key}-${preset.name}`);
141
181
  swatch.className = twMerge(
142
- 'w-8 h-8 rounded-md cursor-pointer border-none outline-hidden',
182
+ 'w-9 h-9 rounded-md cursor-pointer border-none outline-hidden',
143
183
  'flex items-center justify-center text-sm font-semibold',
144
- 'transition-shadow ring-inset hover:ring-2 hover:ring-black/10',
145
- isActive && 'ring-2 ring-black/30'
184
+ 'transition-[box-shadow,transform] ring-inset hover:ring-2 hover:ring-swatch-ring-hover active:scale-90',
185
+ isActive && 'ring-2 ring-swatch-ring-hover'
146
186
  );
147
- swatch.textContent = 'A';
187
+ swatch.textContent = mode.presetField === 'text' ? 'A' : '';
148
188
 
149
- if (currentMode.presetField === 'text') {
189
+ if (mode.presetField === 'text') {
150
190
  swatch.style.color = preset.text;
151
191
  swatch.style.backgroundColor = SWATCH_NEUTRAL_BG;
152
192
  } else {
153
- swatch.style.color = '';
193
+ swatch.style.color = presets === COLOR_PRESETS_DARK ? preset.text : '#37352f';
154
194
  swatch.style.backgroundColor = preset.bg;
155
195
  }
156
196
 
157
197
  swatch.addEventListener('click', () => {
158
- onColorSelect(swatchColor, currentMode.key);
198
+ onColorSelect(swatchColor, mode.key);
159
199
  });
200
+ const colorLabel = i18n.t('tools.colorPicker.colorSwatchLabel')
201
+ .replace('{color}', i18n.t('tools.colorPicker.color.' + preset.name))
202
+ .replace('{mode}', i18n.t(mode.labelKey).toLowerCase());
203
+
204
+ onHover(swatch, colorLabel.charAt(0).toUpperCase() + colorLabel.slice(1), { placement: 'top' });
160
205
  grid.appendChild(swatch);
161
206
  }
162
207
  };
163
208
 
164
- /**
165
- * Default button
166
- */
167
- const defaultBtn = document.createElement('button');
168
-
169
- defaultBtn.setAttribute('data-blok-testid', `${testIdPrefix}-default-btn`);
170
- defaultBtn.className = twMerge(
171
- 'w-full py-1.5 text-xs text-center rounded-md cursor-pointer',
172
- 'bg-transparent border-none outline-hidden hover:bg-item-hover-bg',
173
- 'mt-0.5 transition-colors'
174
- );
175
- defaultBtn.textContent = i18n.t('tools.marker.default');
176
- defaultBtn.addEventListener('click', () => {
177
- onColorSelect(null, modes[state.modeIndex].key);
178
- });
179
-
180
- /**
181
- * Assemble
182
- */
183
- updateTabs();
184
- renderSwatches();
209
+ const renderAll = (): void => {
210
+ modes.forEach((_, i) => renderSection(i));
211
+ };
185
212
 
186
- wrapper.appendChild(tabRow);
187
- wrapper.appendChild(grid);
188
- wrapper.appendChild(defaultBtn);
213
+ renderAll();
189
214
 
190
215
  return {
191
216
  element: wrapper,
192
217
  setActiveColor: (color: string | null, modeKey: string) => {
193
- state.activeColor = color;
194
-
195
218
  const matchingIndex = modes.findIndex((m) => m.key === modeKey);
196
219
 
197
220
  if (matchingIndex !== -1) {
198
- state.modeIndex = matchingIndex;
199
- updateTabs();
221
+ state.activeColors[modeKey] = color;
222
+ renderSection(matchingIndex);
200
223
  }
201
-
202
- renderSwatches();
203
224
  },
204
225
  reset: () => {
205
- state.modeIndex = defaultModeIndex;
206
- state.activeColor = null;
207
- updateTabs();
208
- renderSwatches();
226
+ for (const mode of modes) {
227
+ state.activeColors[mode.key] = null;
228
+ }
229
+ renderAll();
209
230
  },
210
231
  };
211
232
  }
@@ -8,7 +8,7 @@ export interface ColorPreset {
8
8
  }
9
9
 
10
10
  /**
11
- * Ten Notion-style color presets.
11
+ * Ten Notion-style color presets for light mode.
12
12
  * `text` is used for foreground (text-color mode), `bg` for background swatches.
13
13
  */
14
14
  export const COLOR_PRESETS: ColorPreset[] = [
@@ -17,9 +17,37 @@ export const COLOR_PRESETS: ColorPreset[] = [
17
17
  { name: 'orange', text: '#d9730d', bg: '#fbecdd' },
18
18
  { name: 'yellow', text: '#cb9b00', bg: '#fbf3db' },
19
19
  { name: 'green', text: '#448361', bg: '#edf3ec' },
20
- { name: 'teal', text: '#2b9a8f', bg: '#e4f5f3' },
21
20
  { name: 'blue', text: '#337ea9', bg: '#e7f3f8' },
22
21
  { name: 'purple', text: '#9065b0', bg: '#f6f3f9' },
23
22
  { name: 'pink', text: '#c14c8a', bg: '#f9f0f5' },
24
23
  { name: 'red', text: '#d44c47', bg: '#fdebec' },
25
24
  ];
25
+
26
+ /**
27
+ * Dark-mode adapted presets. Text colors are lightened for readability on dark
28
+ * swatch backgrounds; background colors are deep/muted to integrate with dark UI.
29
+ * All pairs achieve at least 3.8:1 WCAG contrast. Backgrounds are equalized at
30
+ * ~L19% HSL with increased saturation so each hue family is clearly identifiable.
31
+ */
32
+ export const COLOR_PRESETS_DARK: ColorPreset[] = [
33
+ { name: 'gray', text: '#9b9b9b', bg: '#2f2f2f' },
34
+ { name: 'brown', text: '#c59177', bg: '#452a1c' },
35
+ { name: 'orange', text: '#dc8c47', bg: '#4d2f14' },
36
+ { name: 'yellow', text: '#d4ab49', bg: '#544012' },
37
+ { name: 'green', text: '#5db184', bg: '#1e432f' },
38
+ { name: 'blue', text: '#5c9fcc', bg: '#123a54' },
39
+ { name: 'purple', text: '#a67dca', bg: '#341d49' },
40
+ { name: 'pink', text: '#d45e99', bg: '#4b1b33' },
41
+ { name: 'red', text: '#dd5e5a', bg: '#4e1a18' },
42
+ ];
43
+
44
+ /**
45
+ * Construct a CSS custom property reference for a named preset color.
46
+ *
47
+ * @param name - The color preset name (e.g. 'red', 'blue')
48
+ * @param mode - 'text' for foreground, 'bg' for background
49
+ * @returns CSS var reference, e.g. `var(--blok-color-red-text)`
50
+ */
51
+ export function colorVarName(name: string, mode: 'text' | 'bg'): string {
52
+ return `var(--blok-color-${name}-${mode})`;
53
+ }
@@ -167,6 +167,13 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
167
167
  */
168
168
  private isInsideTableCell = false;
169
169
 
170
+ /**
171
+ * Whether the toolbox was opened in slash-search mode (via "/" key or existing slash paragraph).
172
+ * When false (opened via plus button), the input filter uses the full block text as the query
173
+ * instead of requiring a leading "/" and does not close on missing slash.
174
+ */
175
+ private openedWithSlash = true;
176
+
170
177
  /**
171
178
  * Toolbox constructor
172
179
  * @param options - available parameters
@@ -280,8 +287,10 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
280
287
 
281
288
  /**
282
289
  * Open Toolbox with Tools
290
+ * @param withSlash - When true (default), inline search requires "/" and closes on its removal.
291
+ * When false (plus button), the full block text is used as the filter query.
283
292
  */
284
- public open(): void {
293
+ public open(withSlash = true): void {
285
294
  if (this.isEmpty) {
286
295
  return;
287
296
  }
@@ -318,8 +327,20 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
318
327
  const caretRect = SelectionUtils.rect;
319
328
 
320
329
  this.popover.updatePosition(caretRect);
330
+ } else if (!withSlash && this.popover instanceof PopoverDesktop) {
331
+ /**
332
+ * When opened without slash (via plus button), the trigger element (plus button)
333
+ * is at the top of the block. Position the popover below the block's bottom edge
334
+ * instead, so it doesn't overlap the block's placeholder text.
335
+ */
336
+ const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
337
+
338
+ if (currentBlock) {
339
+ this.popover.updatePosition(currentBlock.holder.getBoundingClientRect());
340
+ }
321
341
  }
322
342
 
343
+ this.openedWithSlash = withSlash;
323
344
  this.opened = true;
324
345
  this.emit(ToolboxEvent.Opened);
325
346
  this.startListeningToBlockInput();
@@ -725,7 +746,12 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
725
746
 
726
747
  /**
727
748
  * Handles input events on the block to filter the toolbox.
728
- * Extracts text after "/" and applies it as a filter query.
749
+ *
750
+ * In slash mode (default): extracts text after "/" and filters by it.
751
+ * Closes if "/" is removed.
752
+ *
753
+ * In no-slash mode (opened via plus button): uses full block text as the
754
+ * filter query and does not close on missing "/".
729
755
  */
730
756
  private handleBlockInput = (): void => {
731
757
  if (this.currentContentEditable === null) {
@@ -733,15 +759,18 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
733
759
  }
734
760
 
735
761
  const text = this.currentContentEditable.textContent || '';
736
- const slashIndex = text.lastIndexOf('/');
737
762
 
738
- if (slashIndex === -1) {
739
- this.close();
763
+ if (this.openedWithSlash) {
764
+ const slashIndex = text.lastIndexOf('/');
740
765
 
741
- return;
766
+ if (slashIndex === -1) {
767
+ this.close();
768
+
769
+ return;
770
+ }
742
771
  }
743
772
 
744
- const query = text.slice(slashIndex + 1);
773
+ const query = this.openedWithSlash ? text.slice(text.lastIndexOf('/') + 1) : text;
745
774
 
746
775
  if (this.currentContentEditable instanceof HTMLElement) {
747
776
  this.currentContentEditable.setAttribute(
@@ -1,4 +1,4 @@
1
- import { COLOR_PRESETS } from '../shared/color-presets';
1
+ import { COLOR_PRESETS, COLOR_PRESETS_DARK } from '../shared/color-presets';
2
2
 
3
3
  /**
4
4
  * Convert an HSL color (H in degrees, S and L as 0-100 percentages) to an RGB tuple.
@@ -239,3 +239,45 @@ export function mapToNearestPresetColor(cssColor: string, mode: 'text' | 'bg'):
239
239
 
240
240
  return best.color;
241
241
  }
242
+
243
+ /**
244
+ * Map an arbitrary CSS color to the name of the nearest Blok preset color.
245
+ *
246
+ * Searches both light and dark presets to correctly recover the semantic name
247
+ * from hex values that originated in either theme. First match wins on ties.
248
+ *
249
+ * @param cssColor - CSS color string (hex or rgb)
250
+ * @param mode - 'text' for text color presets, 'bg' for background presets
251
+ * @returns the nearest preset name (e.g. 'red'), or null if unparseable
252
+ */
253
+ export function mapToNearestPresetName(cssColor: string, mode: 'text' | 'bg'): string | null {
254
+ const rgb = parseColor(cssColor);
255
+
256
+ if (rgb === null) {
257
+ return null;
258
+ }
259
+
260
+ const hsl = rgbToHsl(rgb);
261
+ const allPresets = [...COLOR_PRESETS, ...COLOR_PRESETS_DARK];
262
+
263
+ const best = allPresets.reduce<{ name: string; distance: number } | null>(
264
+ (acc, preset) => {
265
+ const presetRgb = parseColor(preset[mode]);
266
+
267
+ if (presetRgb === null) {
268
+ return acc;
269
+ }
270
+
271
+ const distance = hslDistance(hsl, rgbToHsl(presetRgb));
272
+
273
+ if (acc === null || distance < acc.distance) {
274
+ return { name: preset.name, distance };
275
+ }
276
+
277
+ return acc;
278
+ },
279
+ null
280
+ );
281
+
282
+ return best?.name ?? null;
283
+ }
@@ -0,0 +1,37 @@
1
+ import { colorVarName } from '../shared/color-presets';
2
+ import { mapToNearestPresetName } from './color-mapping';
3
+
4
+ const PROPS = ['color', 'background-color'] as const;
5
+ type Prop = typeof PROPS[number];
6
+
7
+ const PROP_MODE: Record<Prop, 'text' | 'bg'> = {
8
+ 'color': 'text',
9
+ 'background-color': 'bg',
10
+ };
11
+
12
+ /**
13
+ * Scan all <mark> elements inside container and replace raw hex color/background-color
14
+ * inline style values with their corresponding CSS custom property references.
15
+ *
16
+ * Safe to call multiple times — already-migrated var() values and 'transparent' are
17
+ * left unchanged.
18
+ *
19
+ * @param container - Root element to search within (e.g. the editor redactor node)
20
+ */
21
+ export function migrateMarkColors(container: Element): void {
22
+ container.querySelectorAll<HTMLElement>('mark').forEach((el) => {
23
+ for (const prop of PROPS) {
24
+ const value = el.style.getPropertyValue(prop);
25
+
26
+ if (!value || value === 'transparent' || value.startsWith('var(')) {
27
+ continue;
28
+ }
29
+
30
+ const name = mapToNearestPresetName(value, PROP_MODE[prop]);
31
+
32
+ if (name !== null) {
33
+ el.style.setProperty(prop, colorVarName(name, PROP_MODE[prop]));
34
+ }
35
+ }
36
+ });
37
+ }
@@ -10,7 +10,7 @@ export const css = {
10
10
  * Note: noHover state is handled via [data-blok-popover-item-no-hover] which disables hover
11
11
  * Priority order: active < hover < focus (focus wins when navigating with keyboard)
12
12
  */
13
- item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-1 text-text-primary mb-px outline-hidden transition-[color,background-color,border-color,opacity,max-height,padding,margin] duration-150 max-h-9 overflow-hidden data-blok-popover-item-active:bg-icon-active-bg data-blok-popover-item-active:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg data-blok-force-hover:cursor-pointer data-blok-force-hover:bg-item-hover-bg data-[blok-focused="true"]:bg-item-focus-bg data-blok-popover-item-no-hover:hover:bg-transparent data-blok-popover-item-no-hover:cursor-default can-hover:data-blok-popover-item-destructive:hover:text-item-destructive-text can-hover:data-blok-popover-item-destructive:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
13
+ item: 'flex items-center select-none border-none bg-transparent rounded-lg px-2 py-1 text-text-primary mb-px outline-hidden max-h-9 overflow-hidden data-blok-popover-item-active:bg-icon-active-bg data-blok-popover-item-active:text-icon-active-text can-hover:hover:cursor-pointer can-hover:hover:bg-item-hover-bg data-blok-force-hover:cursor-pointer data-blok-force-hover:bg-item-hover-bg data-[blok-focused="true"]:bg-item-focus-bg data-blok-popover-item-no-hover:hover:bg-transparent data-blok-popover-item-no-hover:cursor-default can-hover:data-blok-popover-item-destructive:hover:text-item-destructive-text can-hover:data-blok-popover-item-destructive:hover:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-force-hover]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-force-hover]]:bg-item-destructive-hover-bg [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:text-item-destructive-text [&[data-blok-popover-item-destructive][data-blok-focused="true"]]:bg-item-destructive-hover-bg',
14
14
 
15
15
  /**
16
16
  * Item disabled state
@@ -21,7 +21,7 @@ export const css = {
21
21
  /**
22
22
  * Icon container styles
23
23
  */
24
- icon: 'flex items-center justify-center w-7 h-7 shrink-0 rounded-lg bg-popover-icon-bg transition-colors duration-150 [&_svg]:w-icon [&_svg]:h-icon',
24
+ icon: 'flex items-center justify-center w-7 h-7 shrink-0 rounded-lg bg-popover-icon-bg [&_svg]:w-icon [&_svg]:h-icon',
25
25
 
26
26
  /**
27
27
  * Focused state class for DomIterator/Flipper keyboard navigation.
@@ -37,7 +37,8 @@ export const cssInline = {
37
37
  /**
38
38
  * Item in inline context - more compact styling
39
39
  */
40
- item: 'rounded p-1',
40
+ item: 'rounded-lg p-1',
41
+ itemIconOnly: 'aspect-square justify-center',
41
42
  };
42
43
 
43
44
  /**
@@ -305,7 +305,7 @@ export class PopoverItemDefault extends PopoverItem {
305
305
  private createIconElement(icon: string, iconWithGap: boolean, isInline: boolean, isNestedInline: boolean): HTMLElement {
306
306
  const iconEl = document.createElement('div');
307
307
 
308
- iconEl.className = this.getIconClass(iconWithGap, isInline, isNestedInline, false);
308
+ iconEl.className = this.getIconClass(iconWithGap, isInline, isNestedInline);
309
309
  iconEl.setAttribute(DATA_ATTR.popoverItemIcon, '');
310
310
  iconEl.setAttribute('data-blok-testid', 'popover-item-icon');
311
311
  iconEl.innerHTML = icon;
@@ -328,6 +328,7 @@ export class PopoverItemDefault extends PopoverItem {
328
328
  css.item,
329
329
  !isInline && !isNestedInline && 'pl-2 pr-3',
330
330
  isInline && cssInline.item,
331
+ isInline && this.params.icon && cssInline.itemIconOnly,
331
332
  isNestedInline && cssNestedInline.item,
332
333
  this.params.isDisabled && css.itemDisabled
333
334
  );
@@ -336,15 +337,14 @@ export class PopoverItemDefault extends PopoverItem {
336
337
  /**
337
338
  * Gets the icon class based on context
338
339
  */
339
- private getIconClass(iconWithGap: boolean, isInline: boolean, isNestedInline: boolean, isWobbling: boolean): string {
340
+ private getIconClass(iconWithGap: boolean, isInline: boolean, isNestedInline: boolean): string {
340
341
  return twMerge(
341
342
  css.icon,
342
343
  isInline && 'w-auto h-auto bg-transparent [&_svg]:w-icon [&_svg]:h-icon mobile:[&_svg]:w-icon-mobile mobile:[&_svg]:h-icon-mobile',
343
344
  isNestedInline && 'w-toolbox-btn h-toolbox-btn',
344
345
  iconWithGap && 'mr-3',
345
346
  iconWithGap && isInline && 'shadow-none mr-0!',
346
- iconWithGap && isNestedInline && 'mr-2!',
347
- isWobbling && 'animate-wobble'
347
+ iconWithGap && isNestedInline && 'mr-2!'
348
348
  );
349
349
  }
350
350
 
@@ -639,47 +639,13 @@ export class PopoverItemDefault extends PopoverItem {
639
639
  item.onActivate?.(item);
640
640
  this.disableConfirmationMode();
641
641
  } catch {
642
- this.animateError();
642
+ // onActivate threw an error
643
643
  }
644
644
  } else {
645
645
  this.enableConfirmationMode(item.confirmation);
646
646
  }
647
647
  }
648
648
 
649
- /**
650
- * Animates item which symbolizes that error occurred while executing 'onActivate()' callback
651
- */
652
- private animateError(): void {
653
- this.triggerWobble();
654
- }
655
-
656
- /**
657
- * Triggers wobble animation on the icon
658
- */
659
- private triggerWobble(): void {
660
- if (!this.nodes.icon) {
661
- return;
662
- }
663
-
664
- const isInline = this.renderParams?.isInline ?? false;
665
- const isNestedInline = this.renderParams?.isNestedInline ?? false;
666
- const iconWithGap = this.renderParams?.iconWithGap ?? true;
667
-
668
- // Add wobble class
669
- this.nodes.icon.setAttribute(DATA_ATTR.popoverItemWobble, 'true');
670
- this.nodes.icon.className = this.getIconClass(iconWithGap, isInline, isNestedInline, true);
671
-
672
- // Remove wobble after animation ends
673
- const handleAnimationEnd = (): void => {
674
- if (this.nodes.icon) {
675
- this.nodes.icon.removeAttribute(DATA_ATTR.popoverItemWobble);
676
- this.nodes.icon.className = this.getIconClass(iconWithGap, isInline, isNestedInline, false);
677
- }
678
- };
679
-
680
- this.nodes.icon.addEventListener('animationend', handleAnimationEnd, { once: true });
681
- }
682
-
683
649
  /**
684
650
  * Gets reference to the icon element
685
651
  */
@@ -2,7 +2,7 @@
2
2
  * Tailwind CSS classes for popover separator component
3
3
  */
4
4
  export const css = {
5
- container: 'py-1.5 px-2 transition-[opacity,max-height,padding] duration-150 max-h-5 overflow-hidden',
5
+ container: 'py-1.5 px-2 max-h-5 overflow-hidden',
6
6
  containerHidden: 'opacity-0 max-h-0! py-0!',
7
7
  line: 'h-px w-full bg-popover-border/60',
8
8
  };
@@ -12,7 +12,7 @@ export const css = {
12
12
  */
13
13
  export const cssInline = {
14
14
  // Inline context: horizontal separator
15
- container: 'px-1 py-0',
15
+ container: 'px-1 py-0 max-h-none',
16
16
  line: 'h-full w-px',
17
17
  // Nested inline context: back to vertical separator
18
18
  nestedContainer: 'py-1 px-[3px]',