@jackuait/blok 0.6.0-beta.9 → 0.7.0-beta.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 (336) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-Bn6Q_o8h.mjs → blok-ob9Fwr1L.mjs} +3414 -2975
  3. package/dist/chunks/i18next-B47TKgbU.mjs +1303 -0
  4. package/dist/chunks/{i18next-loader-DjR4d8M7.mjs → i18next-loader-Bu3vFvye.mjs} +2 -2
  5. package/dist/chunks/{index-oe38cp86.mjs → index-CZmRzRIX.mjs} +12 -12
  6. package/dist/chunks/{inline-tool-convert-SRTkyaZn.mjs → inline-tool-convert-CvFW2iie.mjs} +1579 -961
  7. package/dist/chunks/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
  8. package/dist/chunks/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
  9. package/dist/{messages-DnXLrlHh.mjs → chunks/messages-4v4MuVEc.mjs} +6 -0
  10. package/dist/chunks/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
  11. package/dist/chunks/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
  12. package/dist/chunks/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
  13. package/dist/{messages-uKX8WBaD.mjs → chunks/messages-96kNZDll.mjs} +6 -0
  14. package/dist/chunks/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
  15. package/dist/{messages-DBn76jVV.mjs → chunks/messages-B217znr-.mjs} +8 -2
  16. package/dist/{messages-DT4dP5uK.mjs → chunks/messages-B8WNljW3.mjs} +6 -0
  17. package/dist/chunks/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
  18. package/dist/{messages-CZygwLwM.mjs → chunks/messages-BI43k_BD.mjs} +6 -0
  19. package/dist/{messages-CzTufCHu.mjs → chunks/messages-BJ6zrz2j.mjs} +6 -0
  20. package/dist/{messages-BoJc_p1r.mjs → chunks/messages-BUl_Rcnj.mjs} +6 -0
  21. package/dist/chunks/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
  22. package/dist/{messages-C2htQ_3F.mjs → chunks/messages-BcpCubnC.mjs} +6 -0
  23. package/dist/{messages-D5C3J9qr.mjs → chunks/messages-Bm-E4iRC.mjs} +6 -0
  24. package/dist/chunks/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
  25. package/dist/chunks/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
  26. package/dist/chunks/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
  27. package/dist/{messages-Dz9L52ol.mjs → chunks/messages-CRJ_mchV.mjs} +6 -0
  28. package/dist/chunks/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
  29. package/dist/chunks/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
  30. package/dist/{messages-R3hUSvr3.mjs → chunks/messages-CfiyT2Wi.mjs} +6 -0
  31. package/dist/{messages-CJdUsQ-c.mjs → chunks/messages-CgTq3QhU.mjs} +6 -0
  32. package/dist/chunks/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
  33. package/dist/{messages-Q7AO_FLv.mjs → chunks/messages-Cjjo7yHR.mjs} +6 -0
  34. package/dist/{messages-C99mq906.mjs → chunks/messages-Cl6ayUaq.mjs} +6 -0
  35. package/dist/chunks/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
  36. package/dist/chunks/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
  37. package/dist/chunks/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
  38. package/dist/{messages-BWF-zUpY.mjs → chunks/messages-CrCYPCk3.mjs} +6 -0
  39. package/dist/{messages-D-ZtY5v0.mjs → chunks/messages-Cs8zmZ3L.mjs} +6 -0
  40. package/dist/{messages-DprmQg6V.mjs → chunks/messages-CzK0LEhb.mjs} +6 -0
  41. package/dist/chunks/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
  42. package/dist/chunks/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
  43. package/dist/chunks/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
  44. package/dist/{messages-kep5wtm4.mjs → chunks/messages-D4qqwVgQ.mjs} +6 -0
  45. package/dist/chunks/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
  46. package/dist/{messages-CY8_RyFE.mjs → chunks/messages-D7u2bmP2.mjs} +6 -0
  47. package/dist/chunks/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
  48. package/dist/{messages-DLfR5bMd.mjs → chunks/messages-D9ndgBnU.mjs} +6 -0
  49. package/dist/{messages-CVw84KdI.mjs → chunks/messages-DDTQgImT.mjs} +6 -0
  50. package/dist/{messages-_ErNTNhk.mjs → chunks/messages-DH_jBeED.mjs} +6 -0
  51. package/dist/chunks/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
  52. package/dist/chunks/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
  53. package/dist/chunks/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
  54. package/dist/chunks/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
  55. package/dist/chunks/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
  56. package/dist/chunks/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
  57. package/dist/{messages-Z9nEU2xK.mjs → chunks/messages-DviiFSv2.mjs} +6 -0
  58. package/dist/chunks/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
  59. package/dist/{messages-w7v1GNaE.mjs → chunks/messages-Dzzn6XoD.mjs} +6 -0
  60. package/dist/{messages-CqWJcCbY.mjs → chunks/messages-GSByFygY.mjs} +6 -0
  61. package/dist/chunks/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
  62. package/dist/chunks/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
  63. package/dist/{messages-BU2nlrLK.mjs → chunks/messages-RonBBCnh.mjs} +6 -0
  64. package/dist/{messages-Bmu_S7GM.mjs → chunks/messages-VDriF5Qy.mjs} +6 -0
  65. package/dist/{messages-CLhcMlTc.mjs → chunks/messages-ZjUAIWb1.mjs} +6 -0
  66. package/dist/{messages-9SihnaXQ.mjs → chunks/messages-b1EdvUm0.mjs} +6 -0
  67. package/dist/{messages-DvFLX36Q.mjs → chunks/messages-begYOTgC.mjs} +6 -0
  68. package/dist/{messages-BMv4xwIr.mjs → chunks/messages-jrncnb-H.mjs} +6 -0
  69. package/dist/{messages-D5iv1Kox.mjs → chunks/messages-nefz1S71.mjs} +6 -0
  70. package/dist/{messages-CQwpzUFp.mjs → chunks/messages-ucTVgS5G.mjs} +6 -0
  71. package/dist/chunks/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
  72. package/dist/{messages-C9eaarcK.mjs → chunks/messages-wmi-iFkH.mjs} +6 -0
  73. package/dist/chunks/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
  74. package/dist/full.mjs +30 -27
  75. package/dist/locales.mjs +90 -84
  76. package/dist/{messages-BogRq8lt.mjs → messages-0AbcLMLm.mjs} +6 -0
  77. package/dist/{messages-DJDG55Vq.mjs → messages-0E0AkrNu.mjs} +6 -0
  78. package/dist/{chunks/messages-DnXLrlHh.mjs → messages-4v4MuVEc.mjs} +6 -0
  79. package/dist/{messages-DnIhyAJk.mjs → messages-62v-CLC-.mjs} +6 -0
  80. package/dist/{messages-Dzwxv9v1.mjs → messages-8DeO60Oo.mjs} +6 -0
  81. package/dist/{messages-B1Aww8q7.mjs → messages-8IPXkrDl.mjs} +6 -0
  82. package/dist/{chunks/messages-uKX8WBaD.mjs → messages-96kNZDll.mjs} +6 -0
  83. package/dist/{messages-BL0tXcDf.mjs → messages-B1FZ8lxU.mjs} +6 -0
  84. package/dist/{chunks/messages-DBn76jVV.mjs → messages-B217znr-.mjs} +8 -2
  85. package/dist/{chunks/messages-DT4dP5uK.mjs → messages-B8WNljW3.mjs} +6 -0
  86. package/dist/{messages-BdeLo0N9.mjs → messages-BC8IN4Bf.mjs} +6 -0
  87. package/dist/{chunks/messages-CZygwLwM.mjs → messages-BI43k_BD.mjs} +6 -0
  88. package/dist/{chunks/messages-CzTufCHu.mjs → messages-BJ6zrz2j.mjs} +6 -0
  89. package/dist/{chunks/messages-BoJc_p1r.mjs → messages-BUl_Rcnj.mjs} +6 -0
  90. package/dist/{messages-CnwibSvh.mjs → messages-BZlmVRwn.mjs} +6 -0
  91. package/dist/{chunks/messages-C2htQ_3F.mjs → messages-BcpCubnC.mjs} +6 -0
  92. package/dist/{chunks/messages-D5C3J9qr.mjs → messages-Bm-E4iRC.mjs} +6 -0
  93. package/dist/{messages-BELRf6DU.mjs → messages-C4jL-90N.mjs} +6 -0
  94. package/dist/{messages-1fC8IMyX.mjs → messages-CDBLbUOQ.mjs} +6 -0
  95. package/dist/{messages-7QoX8DkW.mjs → messages-CH4hrauY.mjs} +6 -0
  96. package/dist/{chunks/messages-Dz9L52ol.mjs → messages-CRJ_mchV.mjs} +6 -0
  97. package/dist/{messages-JELdtT6E.mjs → messages-CW4c4cRk.mjs} +6 -0
  98. package/dist/{messages-CKI54h6O.mjs → messages-C_4otP7U.mjs} +6 -0
  99. package/dist/{chunks/messages-R3hUSvr3.mjs → messages-CfiyT2Wi.mjs} +6 -0
  100. package/dist/{chunks/messages-CJdUsQ-c.mjs → messages-CgTq3QhU.mjs} +6 -0
  101. package/dist/{messages-D1Hv8XGo.mjs → messages-Chb7k3Rg.mjs} +6 -0
  102. package/dist/{chunks/messages-Q7AO_FLv.mjs → messages-Cjjo7yHR.mjs} +6 -0
  103. package/dist/{chunks/messages-C99mq906.mjs → messages-Cl6ayUaq.mjs} +6 -0
  104. package/dist/{messages-Diu6jAaR.mjs → messages-CmR9ftc_.mjs} +6 -0
  105. package/dist/{messages-LPVfA-8K.mjs → messages-Cr49Nt3U.mjs} +6 -0
  106. package/dist/{messages-DqM1LFg5.mjs → messages-Cr94GzbX.mjs} +6 -0
  107. package/dist/{chunks/messages-BWF-zUpY.mjs → messages-CrCYPCk3.mjs} +6 -0
  108. package/dist/{chunks/messages-D-ZtY5v0.mjs → messages-Cs8zmZ3L.mjs} +6 -0
  109. package/dist/{chunks/messages-DprmQg6V.mjs → messages-CzK0LEhb.mjs} +6 -0
  110. package/dist/{messages-BSbjsyHY.mjs → messages-D00x4S8o.mjs} +6 -0
  111. package/dist/{messages-Xq8UmkVs.mjs → messages-D1mn7Zd5.mjs} +6 -0
  112. package/dist/{messages-BC86qLvI.mjs → messages-D2NOpHn9.mjs} +6 -0
  113. package/dist/{chunks/messages-kep5wtm4.mjs → messages-D4qqwVgQ.mjs} +6 -0
  114. package/dist/{messages-7W4d0DwD.mjs → messages-D5S1Dnpm.mjs} +6 -0
  115. package/dist/{chunks/messages-CY8_RyFE.mjs → messages-D7u2bmP2.mjs} +6 -0
  116. package/dist/{messages-BFG6Wlgy.mjs → messages-D85FqxgY.mjs} +6 -0
  117. package/dist/{chunks/messages-DLfR5bMd.mjs → messages-D9ndgBnU.mjs} +6 -0
  118. package/dist/{chunks/messages-CVw84KdI.mjs → messages-DDTQgImT.mjs} +6 -0
  119. package/dist/{chunks/messages-_ErNTNhk.mjs → messages-DH_jBeED.mjs} +6 -0
  120. package/dist/{messages-CMkNSDTo.mjs → messages-DRXWF0PV.mjs} +6 -0
  121. package/dist/{messages-BYyy6Wqf.mjs → messages-DVQvl8Qj.mjs} +6 -0
  122. package/dist/{messages-CznZadDf.mjs → messages-DXktiao_.mjs} +6 -0
  123. package/dist/{messages-DhLKYm2j.mjs → messages-DdK-nFGm.mjs} +6 -0
  124. package/dist/{messages-BMXCuEKO.mjs → messages-DlJbPF2T.mjs} +6 -0
  125. package/dist/{messages-CvGLfqmV.mjs → messages-DnVlmiNT.mjs} +6 -0
  126. package/dist/{chunks/messages-Z9nEU2xK.mjs → messages-DviiFSv2.mjs} +6 -0
  127. package/dist/{messages-BB5z9Uba.mjs → messages-DzqM3Fel.mjs} +6 -0
  128. package/dist/{chunks/messages-w7v1GNaE.mjs → messages-Dzzn6XoD.mjs} +6 -0
  129. package/dist/{chunks/messages-CqWJcCbY.mjs → messages-GSByFygY.mjs} +6 -0
  130. package/dist/{messages-_ncGrKHh.mjs → messages-L_kl2Qvh.mjs} +6 -0
  131. package/dist/{messages-BrPFGbM-.mjs → messages-Phkd7XmE.mjs} +6 -0
  132. package/dist/{chunks/messages-BU2nlrLK.mjs → messages-RonBBCnh.mjs} +6 -0
  133. package/dist/{chunks/messages-Bmu_S7GM.mjs → messages-VDriF5Qy.mjs} +6 -0
  134. package/dist/{chunks/messages-CLhcMlTc.mjs → messages-ZjUAIWb1.mjs} +6 -0
  135. package/dist/{chunks/messages-9SihnaXQ.mjs → messages-b1EdvUm0.mjs} +6 -0
  136. package/dist/{chunks/messages-DvFLX36Q.mjs → messages-begYOTgC.mjs} +6 -0
  137. package/dist/{chunks/messages-BMv4xwIr.mjs → messages-jrncnb-H.mjs} +6 -0
  138. package/dist/{chunks/messages-D5iv1Kox.mjs → messages-nefz1S71.mjs} +6 -0
  139. package/dist/{chunks/messages-CQwpzUFp.mjs → messages-ucTVgS5G.mjs} +6 -0
  140. package/dist/{messages-DBRw-7Zc.mjs → messages-v3GipbFl.mjs} +6 -0
  141. package/dist/{chunks/messages-C9eaarcK.mjs → messages-wmi-iFkH.mjs} +6 -0
  142. package/dist/{messages-O5tQus_0.mjs → messages-yHcs38yI.mjs} +6 -0
  143. package/dist/tools.mjs +3537 -1710
  144. package/dist/vendor.LICENSE.txt +109 -109
  145. package/package.json +43 -57
  146. package/src/blok.ts +12 -0
  147. package/src/components/__module.ts +21 -0
  148. package/src/components/block/api.ts +17 -0
  149. package/src/components/block/style-manager.ts +6 -2
  150. package/src/components/block/tool-renderer.ts +33 -30
  151. package/src/components/blocks.ts +132 -15
  152. package/src/components/constants/data-attributes.ts +7 -0
  153. package/src/components/i18n/locales/am/messages.json +6 -0
  154. package/src/components/i18n/locales/ar/messages.json +6 -0
  155. package/src/components/i18n/locales/az/messages.json +6 -0
  156. package/src/components/i18n/locales/bg/messages.json +6 -0
  157. package/src/components/i18n/locales/bn/messages.json +6 -0
  158. package/src/components/i18n/locales/bs/messages.json +6 -0
  159. package/src/components/i18n/locales/cs/messages.json +6 -0
  160. package/src/components/i18n/locales/da/messages.json +6 -0
  161. package/src/components/i18n/locales/de/messages.json +6 -0
  162. package/src/components/i18n/locales/dv/messages.json +6 -0
  163. package/src/components/i18n/locales/el/messages.json +6 -0
  164. package/src/components/i18n/locales/en/messages.json +6 -0
  165. package/src/components/i18n/locales/es/messages.json +6 -0
  166. package/src/components/i18n/locales/et/messages.json +6 -0
  167. package/src/components/i18n/locales/fa/messages.json +6 -0
  168. package/src/components/i18n/locales/fi/messages.json +6 -0
  169. package/src/components/i18n/locales/fil/messages.json +6 -0
  170. package/src/components/i18n/locales/fr/messages.json +6 -0
  171. package/src/components/i18n/locales/gu/messages.json +6 -0
  172. package/src/components/i18n/locales/he/messages.json +6 -0
  173. package/src/components/i18n/locales/hi/messages.json +6 -0
  174. package/src/components/i18n/locales/hr/messages.json +6 -0
  175. package/src/components/i18n/locales/hu/messages.json +6 -0
  176. package/src/components/i18n/locales/hy/messages.json +6 -0
  177. package/src/components/i18n/locales/id/messages.json +6 -0
  178. package/src/components/i18n/locales/it/messages.json +6 -0
  179. package/src/components/i18n/locales/ja/messages.json +6 -0
  180. package/src/components/i18n/locales/ka/messages.json +6 -0
  181. package/src/components/i18n/locales/km/messages.json +6 -0
  182. package/src/components/i18n/locales/kn/messages.json +6 -0
  183. package/src/components/i18n/locales/ko/messages.json +6 -0
  184. package/src/components/i18n/locales/ku/messages.json +6 -0
  185. package/src/components/i18n/locales/lo/messages.json +6 -0
  186. package/src/components/i18n/locales/lt/messages.json +6 -0
  187. package/src/components/i18n/locales/lv/messages.json +6 -0
  188. package/src/components/i18n/locales/mk/messages.json +6 -0
  189. package/src/components/i18n/locales/ml/messages.json +6 -0
  190. package/src/components/i18n/locales/mn/messages.json +6 -0
  191. package/src/components/i18n/locales/mr/messages.json +6 -0
  192. package/src/components/i18n/locales/ms/messages.json +6 -0
  193. package/src/components/i18n/locales/my/messages.json +6 -0
  194. package/src/components/i18n/locales/ne/messages.json +6 -0
  195. package/src/components/i18n/locales/nl/messages.json +6 -0
  196. package/src/components/i18n/locales/no/messages.json +6 -0
  197. package/src/components/i18n/locales/pa/messages.json +6 -0
  198. package/src/components/i18n/locales/pl/messages.json +6 -0
  199. package/src/components/i18n/locales/ps/messages.json +6 -0
  200. package/src/components/i18n/locales/pt/messages.json +6 -0
  201. package/src/components/i18n/locales/ro/messages.json +6 -0
  202. package/src/components/i18n/locales/ru/messages.json +6 -0
  203. package/src/components/i18n/locales/sd/messages.json +6 -0
  204. package/src/components/i18n/locales/si/messages.json +6 -0
  205. package/src/components/i18n/locales/sk/messages.json +6 -0
  206. package/src/components/i18n/locales/sl/messages.json +6 -0
  207. package/src/components/i18n/locales/sq/messages.json +6 -0
  208. package/src/components/i18n/locales/sr/messages.json +6 -0
  209. package/src/components/i18n/locales/sv/messages.json +6 -0
  210. package/src/components/i18n/locales/sw/messages.json +6 -0
  211. package/src/components/i18n/locales/ta/messages.json +6 -0
  212. package/src/components/i18n/locales/te/messages.json +6 -0
  213. package/src/components/i18n/locales/th/messages.json +6 -0
  214. package/src/components/i18n/locales/tr/messages.json +6 -0
  215. package/src/components/i18n/locales/ug/messages.json +6 -0
  216. package/src/components/i18n/locales/uk/messages.json +6 -0
  217. package/src/components/i18n/locales/ur/messages.json +6 -0
  218. package/src/components/i18n/locales/vi/messages.json +6 -0
  219. package/src/components/i18n/locales/yi/messages.json +6 -0
  220. package/src/components/i18n/locales/zh/messages.json +6 -0
  221. package/src/components/icons/index.ts +61 -7
  222. package/src/components/inline-tools/inline-tool-link.ts +1 -1
  223. package/src/components/inline-tools/inline-tool-marker.ts +737 -0
  224. package/src/components/inline-tools/utils/formatting-range-utils.ts +6 -3
  225. package/src/components/inline-tools/utils/marker-dom-utils.ts +17 -0
  226. package/src/components/modules/api/blocks.ts +34 -9
  227. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +75 -29
  228. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +54 -2
  229. package/src/components/modules/blockEvents/constants.ts +12 -0
  230. package/src/components/modules/blockEvents/index.ts +13 -5
  231. package/src/components/modules/blockManager/blockManager.ts +81 -2
  232. package/src/components/modules/blockManager/hierarchy.ts +20 -2
  233. package/src/components/modules/blockManager/operations.ts +70 -35
  234. package/src/components/modules/blockManager/repository.ts +22 -0
  235. package/src/components/modules/blockManager/types.ts +3 -1
  236. package/src/components/modules/blockManager/yjs-sync.ts +173 -39
  237. package/src/components/modules/blockSelection.ts +3 -0
  238. package/src/components/modules/crossBlockSelection.ts +11 -3
  239. package/src/components/modules/drag/preview/DragPreview.ts +10 -2
  240. package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
  241. package/src/components/modules/drag/utils/drag.constants.ts +1 -1
  242. package/src/components/modules/normalizeInlineImages.ts +263 -0
  243. package/src/components/modules/paste/google-docs-preprocessor.ts +197 -0
  244. package/src/components/modules/paste/handlers/base.ts +43 -2
  245. package/src/components/modules/paste/handlers/html-handler.ts +1 -1
  246. package/src/components/modules/paste/handlers/index.ts +1 -0
  247. package/src/components/modules/paste/handlers/table-cells-handler.ts +104 -0
  248. package/src/components/modules/paste/index.ts +20 -3
  249. package/src/components/modules/readonly.ts +8 -2
  250. package/src/components/modules/rectangleSelection.ts +5 -2
  251. package/src/components/modules/renderer.ts +35 -0
  252. package/src/components/modules/saver.ts +52 -2
  253. package/src/components/modules/toolbar/blockSettings.ts +52 -44
  254. package/src/components/modules/toolbar/index.ts +124 -17
  255. package/src/components/modules/toolbar/inline/index.ts +4 -4
  256. package/src/components/modules/toolbar/plus-button.ts +3 -3
  257. package/src/components/modules/toolbar/settings-toggler.ts +3 -3
  258. package/src/components/modules/toolbar/styles.ts +7 -7
  259. package/src/components/modules/ui.ts +6 -6
  260. package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
  261. package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
  262. package/src/components/modules/yjs/block-observer.ts +9 -3
  263. package/src/components/modules/yjs/document-store.ts +10 -7
  264. package/src/components/modules/yjs/types.ts +8 -6
  265. package/src/components/modules/yjs/undo-history.ts +90 -11
  266. package/src/components/selection/fake-background/shadows.ts +1 -1
  267. package/src/components/shared/color-picker.ts +211 -0
  268. package/src/components/shared/color-presets.ts +25 -0
  269. package/src/components/ui/toolbox.ts +27 -11
  270. package/src/components/utils/color-mapping.ts +241 -0
  271. package/src/components/utils/notifier/draw.ts +9 -9
  272. package/src/components/utils/placeholder.ts +24 -8
  273. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +3 -3
  274. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +15 -12
  275. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
  276. package/src/components/utils/popover/popover-abstract.ts +30 -5
  277. package/src/components/utils/popover/popover-desktop.ts +26 -3
  278. package/src/components/utils/popover/popover-inline.ts +14 -1
  279. package/src/components/utils/popover/popover-mobile.ts +4 -4
  280. package/src/components/utils/popover/popover.const.ts +3 -3
  281. package/src/components/utils/sanitizer.ts +24 -3
  282. package/src/components/utils/tw.ts +17 -5
  283. package/src/full.ts +4 -0
  284. package/src/stories/Header.stories.ts +106 -0
  285. package/src/stories/MarkerColors.stories.ts +730 -0
  286. package/src/stories/Placeholder.stories.ts +7 -2
  287. package/src/stories/Popover.stories.ts +1 -3
  288. package/src/stories/Table.stories.ts +1662 -0
  289. package/src/stories/helpers.ts +2 -0
  290. package/src/styles/main.css +217 -39
  291. package/src/tools/header/index.ts +204 -26
  292. package/src/tools/index.ts +5 -1
  293. package/src/tools/list/caret-manager.ts +28 -10
  294. package/src/tools/list/constants.ts +2 -2
  295. package/src/tools/list/dom-builder.ts +3 -3
  296. package/src/tools/list/static-configs.ts +0 -1
  297. package/src/tools/paragraph/index.ts +9 -5
  298. package/src/tools/table/core/table-commands.ts +99 -0
  299. package/src/tools/table/core/table-controller.ts +231 -0
  300. package/src/tools/table/core/table-events.ts +102 -0
  301. package/src/tools/table/index.ts +1070 -174
  302. package/src/tools/table/ownership/table-event-broker.ts +74 -0
  303. package/src/tools/table/ownership/table-ownership-registry.ts +126 -0
  304. package/src/tools/table/table-add-controls.ts +85 -15
  305. package/src/tools/table/table-cell-blocks.ts +336 -38
  306. package/src/tools/table/table-cell-clipboard.ts +415 -0
  307. package/src/tools/table/table-cell-color-picker.ts +34 -0
  308. package/src/tools/table/table-cell-selection.ts +264 -15
  309. package/src/tools/table/table-core.ts +3 -42
  310. package/src/tools/table/table-heading-toggle.ts +2 -2
  311. package/src/tools/table/table-model.ts +623 -0
  312. package/src/tools/table/table-operations.ts +59 -78
  313. package/src/tools/table/table-resize.ts +15 -11
  314. package/src/tools/table/table-restrictions.ts +69 -3
  315. package/src/tools/table/table-row-col-action-handler.ts +22 -7
  316. package/src/tools/table/table-row-col-controls.ts +129 -12
  317. package/src/tools/table/table-row-col-drag.ts +14 -0
  318. package/src/tools/table/table-scroll-haze.ts +152 -0
  319. package/src/tools/table/types.ts +22 -1
  320. package/src/tools/table/view/table-cell-blocks-adapter.ts +47 -0
  321. package/src/tools/toggle/block-operations.ts +110 -0
  322. package/src/tools/toggle/constants.ts +49 -0
  323. package/src/tools/toggle/dom-builder.ts +125 -0
  324. package/src/tools/toggle/index.ts +280 -0
  325. package/src/tools/toggle/toggle-keyboard.ts +139 -0
  326. package/src/tools/toggle/toggle-lifecycle.ts +80 -0
  327. package/src/tools/toggle/toggle-shortcuts.ts +107 -0
  328. package/src/tools/toggle/types.ts +21 -0
  329. package/src/variants/blok-minimum.ts +13 -0
  330. package/types/api/block.d.ts +13 -0
  331. package/types/api/blocks.d.ts +16 -0
  332. package/types/full.d.ts +2 -0
  333. package/types/tools/table.d.ts +2 -0
  334. package/types/tools-entry.d.ts +2 -1
  335. package/dist/chunks/i18next-CugVlwWp.mjs +0 -1292
  336. package/src/tools/table/data-normalizer.ts +0 -32
@@ -222,11 +222,23 @@ export class Toolbar extends Module<ToolbarNodes> {
222
222
 
223
223
  /**
224
224
  * Set current block to cover the case when the Toolbar showed near hovered Block but caret is set to another Block.
225
- * Skip this when the hovered block was resolved from a table cell, so the toolbox
226
- * can detect the original cell block and hide restricted tools (e.g., table, header).
225
+ * Skip this when:
226
+ * - the hovered block was resolved from a table cell
227
+ * - the current block's holder is nested inside a table cell container
228
+ * (e.g. "/" was typed in a cell while the toolbar was already open from hover,
229
+ * so hoveredBlockIsFromTableCell may be stale/false from the hover resolution)
230
+ *
231
+ * In both cases, overriding currentBlock with the resolved table block
232
+ * would lose the cell-paragraph context the toolbox needs to hide restricted tools.
227
233
  */
228
234
  if (this.hoveredBlock && !this.hoveredBlockIsFromTableCell) {
229
- this.Blok.BlockManager.currentBlock = this.hoveredBlock;
235
+ const currentBlock = this.Blok.BlockManager.currentBlock;
236
+ const isCurrentBlockInsideTableCell = currentBlock !== undefined
237
+ && currentBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
238
+
239
+ if (!isCurrentBlockInsideTableCell) {
240
+ this.Blok.BlockManager.currentBlock = this.hoveredBlock;
241
+ }
230
242
  }
231
243
 
232
244
  this.toolboxInstance.open();
@@ -336,11 +348,30 @@ export class Toolbar extends Module<ToolbarNodes> {
336
348
  }
337
349
 
338
350
  /**
339
- * Track whether the original block is inside a table cell.
340
- * Check the DOM directly rather than relying on resolution success,
341
- * so that the flag is correct even when resolution falls back to the original block.
351
+ * Track whether the hover originated from inside a table cell.
352
+ *
353
+ * Two scenarios:
354
+ * 1. Called with an explicit `target` (via BlockHovered): blockHover.ts resolves
355
+ * cell paragraphs up to the TABLE block before emitting the event, so
356
+ * `unresolvedBlock` is always the TABLE block — its holder is at the top level.
357
+ * Use the raw `target` element to detect if the pointer is inside a cell.
358
+ * 2. Called without args (from activateToolbox / slash menu): `unresolvedBlock`
359
+ * falls back to `BlockManager.currentBlock`, which IS the cell-paragraph.
360
+ * Check the block's holder directly.
361
+ *
362
+ * When this flag is true, the toolbox.open() getter preserves the cell-paragraph
363
+ * as currentBlock so that restricted tools (table, header) can be hidden.
364
+ *
365
+ * NOTE: This flag is NOT used for plus button / settings toggler visibility.
366
+ * Those are handled separately by the focusin listener (tableCellFocusHandler)
367
+ * which detects when the user actually clicks/focuses inside a cell.
342
368
  */
343
- this.hoveredBlockIsFromTableCell = unresolvedBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
369
+ const targetIsInsideCell = target instanceof Element
370
+ && target.closest('[data-blok-table-cell-blocks]') !== null;
371
+ const blockIsInsideCell =
372
+ unresolvedBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
373
+
374
+ this.hoveredBlockIsFromTableCell = targetIsInsideCell || blockIsInsideCell;
344
375
 
345
376
  const targetBlock = this.resolveTableCellBlock(unresolvedBlock);
346
377
 
@@ -362,11 +393,17 @@ export class Toolbar extends Module<ToolbarNodes> {
362
393
  }
363
394
 
364
395
  /**
365
- * Suppress toolbar buttons when the block is inside a table cell.
396
+ * Suppress toolbar buttons when the focused element is inside a table cell.
366
397
  * The toolbar still positions itself for toolbox/slash-search purposes,
367
- * but plus button and settings toggler remain hidden.
398
+ * but plus button and settings toggler remain hidden while a cell has focus.
399
+ *
400
+ * Note: we check focus (activeElement) not hover. On hover alone, the plus
401
+ * button should remain visible so the user can click it to add blocks
402
+ * below the table. Only when the user actually clicks/focuses inside a cell
403
+ * should the buttons be suppressed.
368
404
  */
369
- const displayValue = this.hoveredBlockIsFromTableCell ? 'none' : '';
405
+ const focusIsInsideCell = this.isFocusInsideTableCell();
406
+ const displayValue = focusIsInsideCell ? 'none' : '';
370
407
 
371
408
  plusButton.style.display = displayValue;
372
409
 
@@ -588,6 +625,16 @@ export class Toolbar extends Module<ToolbarNodes> {
588
625
  this.settingsTogglerHandler.skipNextToggle();
589
626
  }
590
627
 
628
+ /**
629
+ * Hides the block actions (plus button and settings toggler) without
630
+ * closing the entire toolbar or setting explicitlyClosed.
631
+ * Used when the toolbar should remain positioned but its action buttons
632
+ * should temporarily step aside (e.g., during typing or inline toolbar use).
633
+ */
634
+ public hideBlockActions(): void {
635
+ this.blockActions.hide();
636
+ }
637
+
591
638
  /**
592
639
  * Resets the explicitlyClosed flag to allow the toolbar to reopen on hover.
593
640
  * Called when drag is cancelled to re-enable hover-based toolbar opening.
@@ -596,6 +643,48 @@ export class Toolbar extends Module<ToolbarNodes> {
596
643
  this.explicitlyClosed = false;
597
644
  }
598
645
 
646
+ /**
647
+ * Checks whether the currently focused element (document.activeElement) is
648
+ * inside a table cell container.
649
+ *
650
+ * Used to decide whether the plus button and settings toggler should be hidden.
651
+ * Focus-based check distinguishes click (buttons hidden) from hover (buttons visible).
652
+ */
653
+ private isFocusInsideTableCell(): boolean {
654
+ const active = document.activeElement;
655
+
656
+ if (!active) {
657
+ return false;
658
+ }
659
+
660
+ return active.closest('[data-blok-table-cell-blocks]') !== null;
661
+ }
662
+
663
+ /**
664
+ * Updates toolbar button visibility based on whether a table cell has focus.
665
+ * Hides plus button and settings toggler when focus is inside a cell;
666
+ * restores them when focus moves to a regular block.
667
+ *
668
+ * Called from the focusin listener so button state is updated immediately
669
+ * on click, without waiting for the next hover/moveAndOpen cycle.
670
+ */
671
+ private updateToolbarButtonsForTableCellFocus(): void {
672
+ const { plusButton, settingsToggler } = this.nodes;
673
+
674
+ if (!plusButton) {
675
+ return;
676
+ }
677
+
678
+ const focusIsInsideCell = this.isFocusInsideTableCell();
679
+ const displayValue = focusIsInsideCell ? 'none' : '';
680
+
681
+ plusButton.style.display = displayValue;
682
+
683
+ if (settingsToggler) {
684
+ settingsToggler.style.display = displayValue;
685
+ }
686
+ }
687
+
599
688
  /**
600
689
  * If the block is inside a table cell, resolve to the parent table block.
601
690
  * This ensures the toolbar shows for the table when clicking/focusing inside cells.
@@ -680,12 +769,12 @@ export class Toolbar extends Module<ToolbarNodes> {
680
769
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- CSS getter now returns Tailwind classes
681
770
  this.CSS.actions,
682
771
  // Narrow mode positioning on non-mobile screens
683
- 'not-mobile:group-data-[blok-narrow=true]:right-[calc(-1*theme(spacing.narrow-mode-right-padding)-5px)]',
772
+ 'not-mobile:group-data-[blok-narrow=true]:right-[calc(-1*(var(--spacing-narrow-mode-right-padding))-5px)]',
684
773
  // RTL narrow mode: use left positioning instead
685
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:right-auto',
686
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:left-[calc(-1*theme(spacing.narrow-mode-right-padding)-5px)]',
774
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:right-auto',
775
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:left-[calc(-1*(var(--spacing-narrow-mode-right-padding))-5px)]',
687
776
  // RTL narrow mode additional left offset
688
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:left-[-5px]',
777
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:left-[-5px]',
689
778
  ]);
690
779
 
691
780
  this.nodes.content = content;
@@ -845,6 +934,18 @@ export class Toolbar extends Module<ToolbarNodes> {
845
934
  this.readOnlyMutableListeners.on(settingsToggler, 'mousedown', this.settingsTogglerHandler.createMousedownHandler(), true);
846
935
  }
847
936
 
937
+ /**
938
+ * Listen for focus changes inside the editor.
939
+ * When the user clicks/tabs into a table cell, hide the plus button and
940
+ * settings toggler. When focus moves to a regular block, restore them.
941
+ *
942
+ * This runs on every focusin event (not throttled) to ensure buttons
943
+ * update immediately on click — no 300ms delay.
944
+ */
945
+ this.readOnlyMutableListeners.on(this.Blok.UI.nodes.wrapper, 'focusin', () => {
946
+ this.updateToolbarButtonsForTableCellFocus();
947
+ });
948
+
848
949
  /**
849
950
  * Subscribe to the 'block-hovered' event if current view is not mobile
850
951
  * @see https://github.com/codex-team/editor.js/issues/1972
@@ -876,11 +977,17 @@ export class Toolbar extends Module<ToolbarNodes> {
876
977
  }
877
978
 
878
979
  /**
879
- * Do not move toolbar if it was explicitly closed (e.g., after block deletion).
880
- * This prevents the toolbar from reopening on subsequent block-hovered events.
980
+ * Do not move toolbar if it was explicitly closed and the user is still
981
+ * hovering the same block. When the user hovers a DIFFERENT block
982
+ * (or hoveredBlock is null after close()), reset the flag and allow
983
+ * the toolbar to reopen — this is an intentional user action.
881
984
  */
882
985
  if (this.explicitlyClosed) {
883
- return;
986
+ if (this.hoveredBlock !== null && this.hoveredBlock === hoveredBlock) {
987
+ return;
988
+ }
989
+
990
+ this.explicitlyClosed = false;
884
991
  }
885
992
 
886
993
  /**
@@ -188,7 +188,7 @@ export class InlineToolbar extends Module<InlineToolbarNodes> {
188
188
  await this.openingPromise;
189
189
  this.openingPromise = null;
190
190
 
191
- this.Blok.Toolbar.close();
191
+ this.Blok.Toolbar.hideBlockActions();
192
192
  }
193
193
 
194
194
  /**
@@ -266,10 +266,10 @@ export class InlineToolbar extends Module<InlineToolbarNodes> {
266
266
  */
267
267
  private make(): void {
268
268
  this.nodes.wrapper = $.make('div', twMerge(
269
- 'absolute top-0 left-0 z-[3] opacity-100 visible',
270
- 'transition-opacity duration-[250ms] ease-out',
269
+ 'absolute top-0 left-0 z-3 opacity-100 visible',
270
+ 'transition-opacity duration-250 ease-out',
271
271
  'will-change-[opacity,left,top]',
272
- '[&_[hidden]]:!hidden'
272
+ '**:[[hidden]]:hidden!'
273
273
  ));
274
274
  this.nodes.wrapper.setAttribute(DATA_ATTR.interface, INLINE_TOOLBAR_INTERFACE_VALUE);
275
275
  this.nodes.wrapper.setAttribute('data-blok-testid', 'inline-toolbar');
@@ -99,14 +99,14 @@ export class PlusButtonHandler {
99
99
  // Keep hover background when toolbox is open
100
100
  'group-data-[blok-toolbox-opened=true]:bg-bg-light',
101
101
  // Mobile styles (static positioning with overlay-pane appearance)
102
- 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-[2]',
102
+ 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-2',
103
103
  'mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile',
104
104
  // RTL styles
105
- 'group-data-[blok-rtl=true]:right-[calc(-1*theme(width.toolbox-btn))] group-data-[blok-rtl=true]:left-auto',
105
+ 'group-data-[blok-rtl=true]:right-[calc(-1*(var(--spacing-toolbox-btn)))] group-data-[blok-rtl=true]:left-auto',
106
106
  // Narrow mode (not-mobile)
107
107
  'not-mobile:group-data-[blok-narrow=true]:left-[5px]',
108
108
  // Narrow mode RTL (not-mobile)
109
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:left-0 not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:right-[5px]'
109
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:left-0 not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:right-[5px]'
110
110
  ),
111
111
  ], {
112
112
  innerHTML: IconPlus,
@@ -121,12 +121,12 @@ export class SettingsTogglerHandler {
121
121
  // Hover (can-hover)
122
122
  'can-hover:hover:bg-bg-light can-hover:hover:cursor-grab',
123
123
  // When toolbox is opened, use pointer cursor on hover
124
- 'group-data-[blok-toolbox-opened=true]:can-hover:hover:cursor-pointer',
124
+ 'can-hover:hover:group-data-[blok-toolbox-opened=true]:cursor-pointer',
125
125
  // When block settings is opened, show hover background and pointer cursor
126
126
  'group-data-[blok-block-settings-opened=true]:bg-bg-light',
127
- 'group-data-[blok-block-settings-opened=true]:can-hover:hover:cursor-pointer',
127
+ 'can-hover:hover:group-data-[blok-block-settings-opened=true]:cursor-pointer',
128
128
  // Mobile styles (static positioning with overlay-pane appearance)
129
- 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-[2]',
129
+ 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-2',
130
130
  'mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile'
131
131
  ),
132
132
  'group-data-[blok-dragging=true]:cursor-grabbing',
@@ -24,7 +24,7 @@ export const getToolbarStyles = (): { [name: string]: string } => {
24
24
  // Mobile styles
25
25
  'mobile:right-auto',
26
26
  // RTL styles
27
- 'group-data-[blok-rtl=true]:right-auto group-data-[blok-rtl=true]:left-[calc(-1*theme(width.toolbox-btn))]',
27
+ 'group-data-[blok-rtl=true]:right-auto group-data-[blok-rtl=true]:left-[calc(-1*(var(--spacing-toolbox-btn)))]',
28
28
  'mobile:group-data-[blok-rtl=true]:ml-0 mobile:group-data-[blok-rtl=true]:mr-auto mobile:group-data-[blok-rtl=true]:pr-0 mobile:group-data-[blok-rtl=true]:pl-[10px]'
29
29
  ),
30
30
  actionsOpened: 'opacity-100',
@@ -40,14 +40,14 @@ export const getToolbarStyles = (): { [name: string]: string } => {
40
40
  // Keep hover background when toolbox is opened
41
41
  'group-data-[blok-toolbox-opened=true]:bg-bg-light',
42
42
  // Mobile styles (static positioning with overlay-pane appearance)
43
- 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-[2]',
43
+ 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-2',
44
44
  'mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile',
45
45
  // RTL styles
46
- 'group-data-[blok-rtl=true]:right-[calc(-1*theme(width.toolbox-btn))] group-data-[blok-rtl=true]:left-auto',
46
+ 'group-data-[blok-rtl=true]:right-[calc(-1*(var(--spacing-toolbox-btn)))] group-data-[blok-rtl=true]:left-auto',
47
47
  // Narrow mode (not-mobile)
48
48
  'not-mobile:group-data-[blok-narrow=true]:left-[5px]',
49
49
  // Narrow mode RTL (not-mobile)
50
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:left-0 not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:right-[5px]'
50
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:left-0 not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:right-[5px]'
51
51
  ),
52
52
  plusButtonShortcutKey: 'text-white',
53
53
  /**
@@ -64,12 +64,12 @@ export const getToolbarStyles = (): { [name: string]: string } => {
64
64
  // Hover (can-hover)
65
65
  'can-hover:hover:bg-bg-light can-hover:hover:cursor-grab',
66
66
  // When toolbox is opened, use pointer cursor on hover
67
- 'group-data-[blok-toolbox-opened=true]:can-hover:hover:cursor-pointer',
67
+ 'can-hover:hover:group-data-[blok-toolbox-opened=true]:cursor-pointer',
68
68
  // When block settings is opened, show hover background and pointer cursor
69
69
  'group-data-[blok-block-settings-opened=true]:bg-bg-light',
70
- 'group-data-[blok-block-settings-opened=true]:can-hover:hover:cursor-pointer',
70
+ 'can-hover:hover:group-data-[blok-block-settings-opened=true]:cursor-pointer',
71
71
  // Mobile styles (static positioning with overlay-pane appearance)
72
- 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-[2]',
72
+ 'mobile:bg-white mobile:border mobile:border-[#e8e8eb] mobile:shadow-overlay-pane mobile:rounded-[6px] mobile:z-2',
73
73
  'mobile:w-toolbox-btn-mobile mobile:h-toolbox-btn-mobile',
74
74
  // Not-mobile styles
75
75
  'not-mobile:w-6'
@@ -414,15 +414,15 @@ export class UI extends Module<UINodes> {
414
414
  'group',
415
415
  'relative',
416
416
  'box-border',
417
- 'z-[1]',
418
- '[&[data-blok-dragging=true]]:cursor-grabbing',
417
+ 'z-1',
418
+ 'data-[blok-dragging=true]:cursor-grabbing',
419
419
  // SVG defaults
420
420
  '[&_svg]:max-h-full',
421
421
  '[&_path]:stroke-current',
422
422
  // Native selection color
423
423
  '[&_::selection]:bg-selection-inline',
424
424
  // Hide placeholder when toolbox is opened
425
- '[&[data-blok-toolbox-opened=true]_[contentEditable=true][data-blok-placeholder]:focus]:before:!opacity-0',
425
+ '[&[data-blok-toolbox-opened=true]_[contentEditable=true][data-blok-placeholder]:focus]:before:opacity-0!',
426
426
  ...(this.isRtl ? [ '[direction:rtl]' ] : []),
427
427
  ]);
428
428
  this.nodes.wrapper.setAttribute(DATA_ATTR.interface, BLOK_INTERFACE_VALUE);
@@ -433,10 +433,10 @@ export class UI extends Module<UINodes> {
433
433
  }
434
434
  this.nodes.redactor = $.make('div', [
435
435
  // Narrow mode: add right margin on non-mobile screens
436
- 'not-mobile:group-data-[blok-narrow=true]:mr-[theme(spacing.narrow-mode-right-padding)]',
436
+ 'not-mobile:group-data-[blok-narrow=true]:mr-(--spacing-narrow-mode-right-padding)',
437
437
  // RTL narrow mode: add left margin instead
438
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:ml-[theme(spacing.narrow-mode-right-padding)]',
439
- 'not-mobile:group-data-[blok-narrow=true]:group-data-[blok-rtl=true]:mr-0',
438
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:ml-(--spacing-narrow-mode-right-padding)',
439
+ 'not-mobile:group-data-[blok-rtl=true]:group-data-[blok-narrow=true]:mr-0',
440
440
  // Firefox empty contenteditable fix
441
441
  '[&_[contenteditable]:empty]:after:content-["\\feff_"]',
442
442
  ]);
@@ -157,13 +157,27 @@ export class BlockHoverController extends Controller {
157
157
  return null;
158
158
  }
159
159
 
160
- const result = blocks.reduce<{ block: Block; distance: number }>((nearest, block) => {
160
+ /**
161
+ * Filter out blocks whose holders are inside a table cell container.
162
+ * Cell blocks should not participate in nearest-block detection —
163
+ * the parent table block should be found instead.
164
+ * This matches the direct-hit path which also resolves cell blocks to their parent table block.
165
+ */
166
+ const topLevelBlocks = blocks.filter(block =>
167
+ block.holder.closest('[data-blok-table-cell-blocks]') === null
168
+ );
169
+
170
+ if (topLevelBlocks.length === 0) {
171
+ return null;
172
+ }
173
+
174
+ const result = topLevelBlocks.reduce<{ block: Block; distance: number }>((nearest, block) => {
161
175
  const rect = block.holder.getBoundingClientRect();
162
176
  const centerY = (rect.top + rect.bottom) / 2;
163
177
  const distance = Math.abs(clientY - centerY);
164
178
 
165
179
  return distance < nearest.distance ? { block, distance } : nearest;
166
- }, { block: blocks[0], distance: Infinity });
180
+ }, { block: topLevelBlocks[0], distance: Infinity });
167
181
 
168
182
  return result.block;
169
183
  }
@@ -35,15 +35,34 @@ export const createRedactorTouchHandler = (
35
35
  /**
36
36
  * Select clicked Block as Current
37
37
  */
38
- try {
39
- deps.Blok.BlockManager.setCurrentBlockByChildNode(clickedNode);
40
- } catch (_e) {
41
- /**
42
- * If clicked outside first-level Blocks and it is not RectSelection, set Caret to the last empty Block
43
- */
44
- if (!deps.Blok.RectangleSelection.isRectActivated()) {
45
- deps.Blok.Caret.setToTheLastBlock();
46
- }
38
+ const block = deps.Blok.BlockManager.setCurrentBlockByChildNode(clickedNode);
39
+
40
+ /**
41
+ * If clicked outside first-level Blocks and it is not RectSelection, set Caret to the last empty Block
42
+ */
43
+ if (!block && !deps.Blok.RectangleSelection.isRectActivated()) {
44
+ deps.Blok.Caret.setToTheLastBlock();
45
+ }
46
+
47
+ /**
48
+ * If the click landed below the last block's content area (in the holder padding zone),
49
+ * create/focus a new paragraph instead of keeping the block selected.
50
+ */
51
+ const isBelowLastBlock = block !== undefined
52
+ && block === deps.Blok.BlockManager.lastBlock
53
+ && isClickBelowLastBlockContent(block.holder, event);
54
+
55
+ if (isBelowLastBlock && !deps.Blok.ReadOnly.isEnabled) {
56
+ deps.Blok.Caret.setToTheLastBlock();
57
+ deps.Blok.Toolbar.moveAndOpen(deps.Blok.BlockManager.lastBlock);
58
+
59
+ return;
60
+ }
61
+
62
+ if (isBelowLastBlock) {
63
+ deps.Blok.Caret.setToTheLastBlock();
64
+
65
+ return;
47
66
  }
48
67
 
49
68
  /**
@@ -51,7 +70,19 @@ export const createRedactorTouchHandler = (
51
70
  * (used for showing Block Settings toggler after opening and closing Inline Toolbar)
52
71
  */
53
72
  if (!deps.Blok.ReadOnly.isEnabled && !deps.Blok.Toolbar.contains(initialTarget)) {
54
- deps.Blok.Toolbar.moveAndOpen(undefined, clickedNode);
73
+ /**
74
+ * When the clicked node is inside a table cell, resolve to the parent table block
75
+ * so moveAndOpen receives the table block (not undefined / the inner cell paragraph).
76
+ * Without this, moveAndOpen falls back to currentBlock (the cell paragraph), detects
77
+ * it's inside a table cell, and hides the plus button and settings toggler.
78
+ */
79
+ const tableCellContainer = clickedNode.closest?.('[data-blok-table-cell-blocks]');
80
+ const tableBlockWrapper = tableCellContainer?.closest('[data-blok-testid="block-wrapper"]');
81
+ const resolvedBlock = tableBlockWrapper
82
+ ? deps.Blok.BlockManager.getBlockByChildNode(tableBlockWrapper)
83
+ : undefined;
84
+
85
+ deps.Blok.Toolbar.moveAndOpen(resolvedBlock, clickedNode);
55
86
  }
56
87
  };
57
88
  }
@@ -93,3 +124,45 @@ export const getClickedNode = (
93
124
 
94
125
  return initialTarget;
95
126
  }
127
+
128
+ /**
129
+ * Extracts the clientY coordinate from a mouse or touch event
130
+ *
131
+ * @param event - The event to extract clientY from
132
+ * @returns The clientY value, or null if not available
133
+ */
134
+ const getClientY = (event: Event): number | null => {
135
+ if (event instanceof MouseEvent) {
136
+ return event.clientY;
137
+ }
138
+
139
+ if (event instanceof TouchEvent && event.touches.length > 0) {
140
+ return event.touches[0].clientY;
141
+ }
142
+
143
+ return null;
144
+ }
145
+
146
+ /**
147
+ * Checks whether a click event landed below a block's content area.
148
+ * Used to detect clicks in the holder padding zone below the last block.
149
+ *
150
+ * @param blockHolder - The block's holder element
151
+ * @param event - The mouse or touch event
152
+ * @returns True if the click was below the block content's bottom edge
153
+ */
154
+ const isClickBelowLastBlockContent = (
155
+ blockHolder: HTMLElement,
156
+ event: Event
157
+ ): boolean => {
158
+ const clientY = getClientY(event);
159
+
160
+ if (clientY === null) {
161
+ return false;
162
+ }
163
+
164
+ const contentEl = blockHolder.querySelector('[data-blok-element-content]');
165
+ const contentRect = contentEl?.getBoundingClientRect();
166
+
167
+ return contentRect !== undefined && clientY > contentRect.bottom;
168
+ };
@@ -169,9 +169,15 @@ export class BlockObserver {
169
169
  this.emitChange({ type: 'move', blockId, origin });
170
170
  }
171
171
 
172
- // Emit pure adds
173
- for (const blockId of pureAdds) {
174
- this.emitChange({ type: 'add', blockId, origin });
172
+ // Emit pure adds — batch when there are multiple so that parent and
173
+ // child blocks can be registered in BlockManager before any lifecycle
174
+ // hooks (like Table.rendered initializeCells) fire.
175
+ if (pureAdds.length === 1) {
176
+ this.emitChange({ type: 'add', blockId: pureAdds[0], origin });
177
+ }
178
+
179
+ if (pureAdds.length > 1) {
180
+ this.emitChange({ type: 'batch-add', blockIds: pureAdds, origin });
175
181
  }
176
182
 
177
183
  // Emit pure removes
@@ -2,6 +2,7 @@ import * as Y from 'yjs';
2
2
 
3
3
  import type { YBlockSerializer, YjsOutputBlockData } from './serializer';
4
4
  import type { TransactionOrigin } from './types';
5
+ import { equals } from '../../utils/object';
5
6
 
6
7
  // Re-export YjsOutputBlockData as DocumentStoreBlockData for consistency
7
8
  type DocumentStoreBlockData = YjsOutputBlockData;
@@ -67,7 +68,7 @@ export class DocumentStore {
67
68
  const yblock = this.serializer.outputDataToYBlock(blockData);
68
69
 
69
70
  this.transact(() => {
70
- const insertIndex = index ?? this.yblocks.length;
71
+ const insertIndex = Math.max(0, Math.min(index ?? this.yblocks.length, this.yblocks.length));
71
72
  this.yblocks.insert(insertIndex, [yblock]);
72
73
  }, 'local');
73
74
 
@@ -121,10 +122,11 @@ export class DocumentStore {
121
122
 
122
123
  this.yblocks.delete(fromIndex, 1);
123
124
 
124
- // toIndex is the final position. Since we just deleted from fromIndex,
125
- // the array is now shorter. The insertion index equals toIndex because
126
- // Y.Array.insert(n, [item]) places item at index n, shifting others right.
127
- this.yblocks.insert(toIndex, [this.serializer.outputDataToYBlock(blockData)]);
125
+ // Clamp toIndex to valid range after deletion shortened the array.
126
+ // An out-of-bounds toIndex means the caller had stale state clamp
127
+ // to array bounds rather than letting Yjs throw "Length exceeded!".
128
+ const clampedToIndex = Math.max(0, Math.min(toIndex, this.yblocks.length));
129
+ this.yblocks.insert(clampedToIndex, [this.serializer.outputDataToYBlock(blockData)]);
128
130
  }, transactionOrigin);
129
131
  }
130
132
 
@@ -161,8 +163,9 @@ export class DocumentStore {
161
163
 
162
164
  // Skip if value hasn't changed - this prevents creating unnecessary undo entries
163
165
  // when block data is synced after mutations that don't actually change data
164
- // (e.g., marker updates in list items during undo/redo)
165
- if (currentValue === value) {
166
+ // (e.g., marker updates in list items during undo/redo, or table content
167
+ // arrays that are reference-different but structurally identical)
168
+ if (equals(currentValue, value)) {
166
169
  return;
167
170
  }
168
171
 
@@ -1,11 +1,13 @@
1
1
  /**
2
- * Event emitted when blocks change
2
+ * Event emitted when blocks change.
3
+ *
4
+ * Most events carry a single `blockId`. The `batch-add` type carries
5
+ * multiple IDs so that parent and child blocks can be created together
6
+ * before any lifecycle hooks fire.
3
7
  */
4
- export interface BlockChangeEvent {
5
- type: 'add' | 'remove' | 'update' | 'move';
6
- blockId: string;
7
- origin: TransactionOrigin;
8
- }
8
+ export type BlockChangeEvent =
9
+ | { type: 'add' | 'remove' | 'update' | 'move'; blockId: string; origin: TransactionOrigin }
10
+ | { type: 'batch-add'; blockIds: string[]; origin: TransactionOrigin };
9
11
 
10
12
  /**
11
13
  * Transaction origin types.