@jackuait/blok 0.6.0-beta.4 → 0.6.0-beta.6

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 (256) hide show
  1. package/README.md +16 -169
  2. package/bin/blok.mjs +10 -0
  3. package/dist/blok.mjs +2 -2
  4. package/dist/chunks/{blok-DK-97ZTf.mjs → blok-BOtlKwVO.mjs} +1486 -1354
  5. package/dist/chunks/{i18next-loader-CRollibS.mjs → i18next-loader-CJNShSyT.mjs} +1 -1
  6. package/dist/chunks/{index-jgHmMDND.mjs → index-BUAPAChM.mjs} +1 -1
  7. package/dist/chunks/{inline-tool-convert-BIwvipPw.mjs → inline-tool-convert-UoYdJJic.mjs} +88 -73
  8. package/dist/chunks/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
  9. package/dist/chunks/{messages-CQj2JU2j.mjs → messages-7QoX8DkW.mjs} +23 -9
  10. package/dist/{messages-LvFKBBPa.mjs → chunks/messages-7W4d0DwD.mjs} +15 -1
  11. package/dist/{messages-Bn253WWC.mjs → chunks/messages-9SihnaXQ.mjs} +14 -0
  12. package/dist/{messages-Bf6Y3_GI.mjs → chunks/messages-B1Aww8q7.mjs} +16 -2
  13. package/dist/{messages-pA5TvcAj.mjs → chunks/messages-BB5z9Uba.mjs} +14 -0
  14. package/dist/chunks/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
  15. package/dist/chunks/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
  16. package/dist/chunks/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
  17. package/dist/{messages-B5puUm7R.mjs → chunks/messages-BL0tXcDf.mjs} +15 -1
  18. package/dist/chunks/{messages-zS1AXZ0y.mjs → messages-BMXCuEKO.mjs} +19 -5
  19. package/dist/{messages-CyDU5lz9.mjs → chunks/messages-BMv4xwIr.mjs} +16 -2
  20. package/dist/chunks/{messages-BeUhMpsr.mjs → messages-BSbjsyHY.mjs} +25 -11
  21. package/dist/chunks/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
  22. package/dist/chunks/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
  23. package/dist/{messages-CXHd9SUK.mjs → chunks/messages-BYyy6Wqf.mjs} +14 -0
  24. package/dist/chunks/{messages-DOlC_Tty.mjs → messages-BdeLo0N9.mjs} +24 -10
  25. package/dist/chunks/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
  26. package/dist/chunks/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
  27. package/dist/chunks/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
  28. package/dist/chunks/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
  29. package/dist/chunks/{messages-D00OjS2n.mjs → messages-C2htQ_3F.mjs} +24 -10
  30. package/dist/chunks/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
  31. package/dist/chunks/{messages-CkFT2gle.mjs → messages-C9eaarcK.mjs} +20 -6
  32. package/dist/chunks/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
  33. package/dist/chunks/messages-CKI54h6O.mjs +62 -0
  34. package/dist/{messages-CrsJ1TEJ.mjs → chunks/messages-CLhcMlTc.mjs} +15 -1
  35. package/dist/{messages-CnvW8Slp.mjs → chunks/messages-CMkNSDTo.mjs} +17 -3
  36. package/dist/{messages-BlpqL8vG.mjs → chunks/messages-CQwpzUFp.mjs} +19 -5
  37. package/dist/chunks/{messages-Cu08aLS3.mjs → messages-CVw84KdI.mjs} +21 -7
  38. package/dist/chunks/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
  39. package/dist/chunks/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
  40. package/dist/chunks/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
  41. package/dist/chunks/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
  42. package/dist/{messages-Dg92dXZ5.mjs → chunks/messages-CvGLfqmV.mjs} +14 -0
  43. package/dist/{messages-AHESHJm_.mjs → chunks/messages-CzTufCHu.mjs} +14 -0
  44. package/dist/chunks/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
  45. package/dist/chunks/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
  46. package/dist/chunks/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
  47. package/dist/chunks/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
  48. package/dist/chunks/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
  49. package/dist/{messages-zSzDzXej.mjs → chunks/messages-DBRw-7Zc.mjs} +16 -2
  50. package/dist/chunks/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
  51. package/dist/chunks/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
  52. package/dist/{messages-DDLgIPDF.mjs → chunks/messages-DLfR5bMd.mjs} +16 -2
  53. package/dist/{messages-CbhuIWRJ.mjs → chunks/messages-DT4dP5uK.mjs} +15 -1
  54. package/dist/chunks/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
  55. package/dist/{messages-BPqWKx5Z.mjs → chunks/messages-Diu6jAaR.mjs} +17 -3
  56. package/dist/{messages-BA0rcTCY.mjs → chunks/messages-DnIhyAJk.mjs} +18 -4
  57. package/dist/chunks/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
  58. package/dist/chunks/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
  59. package/dist/{messages-CJoBtXU6.mjs → chunks/messages-DqM1LFg5.mjs} +14 -0
  60. package/dist/chunks/{messages-Cyi2AMmz.mjs → messages-DvFLX36Q.mjs} +25 -11
  61. package/dist/chunks/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
  62. package/dist/chunks/{messages-GC2PhgV3.mjs → messages-Dzwxv9v1.mjs} +23 -9
  63. package/dist/chunks/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
  64. package/dist/{messages-DY94ykcE.mjs → chunks/messages-LPVfA-8K.mjs} +14 -0
  65. package/dist/{messages-Cr-RJ7YB.mjs → chunks/messages-O5tQus_0.mjs} +14 -0
  66. package/dist/chunks/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
  67. package/dist/chunks/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
  68. package/dist/{messages-CUy1vn-b.mjs → chunks/messages-Xq8UmkVs.mjs} +14 -0
  69. package/dist/chunks/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
  70. package/dist/chunks/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
  71. package/dist/chunks/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
  72. package/dist/chunks/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
  73. package/dist/chunks/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
  74. package/dist/chunks/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
  75. package/dist/cli.mjs +50 -0
  76. package/dist/full.mjs +15 -15
  77. package/dist/locales.mjs +102 -88
  78. package/dist/{messages-C5b7hr_E.mjs → messages-1fC8IMyX.mjs} +16 -2
  79. package/dist/{messages-CQj2JU2j.mjs → messages-7QoX8DkW.mjs} +23 -9
  80. package/dist/{chunks/messages-LvFKBBPa.mjs → messages-7W4d0DwD.mjs} +15 -1
  81. package/dist/{chunks/messages-Bn253WWC.mjs → messages-9SihnaXQ.mjs} +14 -0
  82. package/dist/{chunks/messages-Bf6Y3_GI.mjs → messages-B1Aww8q7.mjs} +16 -2
  83. package/dist/{chunks/messages-pA5TvcAj.mjs → messages-BB5z9Uba.mjs} +14 -0
  84. package/dist/{messages-wdqp4610.mjs → messages-BC86qLvI.mjs} +17 -3
  85. package/dist/{messages-o24dK6CU.mjs → messages-BELRf6DU.mjs} +16 -2
  86. package/dist/{messages-CUZ1x1QD.mjs → messages-BFG6Wlgy.mjs} +16 -2
  87. package/dist/{chunks/messages-B5puUm7R.mjs → messages-BL0tXcDf.mjs} +15 -1
  88. package/dist/{messages-zS1AXZ0y.mjs → messages-BMXCuEKO.mjs} +19 -5
  89. package/dist/{chunks/messages-CyDU5lz9.mjs → messages-BMv4xwIr.mjs} +16 -2
  90. package/dist/{messages-BeUhMpsr.mjs → messages-BSbjsyHY.mjs} +25 -11
  91. package/dist/{messages-JGsXAReJ.mjs → messages-BU2nlrLK.mjs} +16 -2
  92. package/dist/{messages-srxrv8Yh.mjs → messages-BWF-zUpY.mjs} +17 -3
  93. package/dist/{chunks/messages-CXHd9SUK.mjs → messages-BYyy6Wqf.mjs} +14 -0
  94. package/dist/{messages-DOlC_Tty.mjs → messages-BdeLo0N9.mjs} +24 -10
  95. package/dist/{messages-B5jGUnOy.mjs → messages-Bmu_S7GM.mjs} +14 -0
  96. package/dist/{messages-BmKCChWZ.mjs → messages-BoJc_p1r.mjs} +14 -0
  97. package/dist/{messages-CvaqJFN-.mjs → messages-BogRq8lt.mjs} +15 -1
  98. package/dist/{messages-NP1myMGI.mjs → messages-BrPFGbM-.mjs} +14 -0
  99. package/dist/{messages-D00OjS2n.mjs → messages-C2htQ_3F.mjs} +24 -10
  100. package/dist/{messages-BiExzWJv.mjs → messages-C99mq906.mjs} +15 -1
  101. package/dist/{messages-CkFT2gle.mjs → messages-C9eaarcK.mjs} +20 -6
  102. package/dist/{messages-BrJHUxQL.mjs → messages-CJdUsQ-c.mjs} +15 -1
  103. package/dist/messages-CKI54h6O.mjs +62 -0
  104. package/dist/{chunks/messages-CrsJ1TEJ.mjs → messages-CLhcMlTc.mjs} +15 -1
  105. package/dist/{chunks/messages-CnvW8Slp.mjs → messages-CMkNSDTo.mjs} +17 -3
  106. package/dist/{chunks/messages-BlpqL8vG.mjs → messages-CQwpzUFp.mjs} +19 -5
  107. package/dist/{messages-Cu08aLS3.mjs → messages-CVw84KdI.mjs} +21 -7
  108. package/dist/{messages-B9Oba7sq.mjs → messages-CY8_RyFE.mjs} +15 -1
  109. package/dist/{messages-B5hdXZwA.mjs → messages-CZygwLwM.mjs} +15 -1
  110. package/dist/{messages-CVeWVKsV.mjs → messages-CnwibSvh.mjs} +14 -0
  111. package/dist/{messages-DbVquYKN.mjs → messages-CqWJcCbY.mjs} +14 -0
  112. package/dist/{chunks/messages-Dg92dXZ5.mjs → messages-CvGLfqmV.mjs} +14 -0
  113. package/dist/{chunks/messages-AHESHJm_.mjs → messages-CzTufCHu.mjs} +14 -0
  114. package/dist/{messages-Cj-t1bdy.mjs → messages-CznZadDf.mjs} +15 -1
  115. package/dist/{messages-DMQIHGRj.mjs → messages-D-ZtY5v0.mjs} +14 -0
  116. package/dist/{messages-rRSHQDCX.mjs → messages-D1Hv8XGo.mjs} +14 -0
  117. package/dist/{messages-0tDXLuyH.mjs → messages-D5C3J9qr.mjs} +15 -1
  118. package/dist/{messages-RvMHb2Ht.mjs → messages-D5iv1Kox.mjs} +16 -2
  119. package/dist/{chunks/messages-zSzDzXej.mjs → messages-DBRw-7Zc.mjs} +16 -2
  120. package/dist/{messages-CeCjVKMW.mjs → messages-DBn76jVV.mjs} +16 -2
  121. package/dist/{messages-C7I_AVH2.mjs → messages-DJDG55Vq.mjs} +16 -2
  122. package/dist/{chunks/messages-DDLgIPDF.mjs → messages-DLfR5bMd.mjs} +16 -2
  123. package/dist/{chunks/messages-CbhuIWRJ.mjs → messages-DT4dP5uK.mjs} +15 -1
  124. package/dist/{messages-B66ZSDCJ.mjs → messages-DhLKYm2j.mjs} +15 -1
  125. package/dist/{chunks/messages-BPqWKx5Z.mjs → messages-Diu6jAaR.mjs} +17 -3
  126. package/dist/{chunks/messages-BA0rcTCY.mjs → messages-DnIhyAJk.mjs} +18 -4
  127. package/dist/{messages-DV6shA9b.mjs → messages-DnXLrlHh.mjs} +14 -0
  128. package/dist/{messages-DcKOuncK.mjs → messages-DprmQg6V.mjs} +16 -2
  129. package/dist/{chunks/messages-CJoBtXU6.mjs → messages-DqM1LFg5.mjs} +14 -0
  130. package/dist/{messages-Cyi2AMmz.mjs → messages-DvFLX36Q.mjs} +25 -11
  131. package/dist/{messages-DnbbyJT3.mjs → messages-Dz9L52ol.mjs} +16 -2
  132. package/dist/{messages-GC2PhgV3.mjs → messages-Dzwxv9v1.mjs} +23 -9
  133. package/dist/{messages-Q4kc_ZtL.mjs → messages-JELdtT6E.mjs} +15 -1
  134. package/dist/{chunks/messages-DY94ykcE.mjs → messages-LPVfA-8K.mjs} +14 -0
  135. package/dist/{chunks/messages-Cr-RJ7YB.mjs → messages-O5tQus_0.mjs} +14 -0
  136. package/dist/{messages-CbMyJSzS.mjs → messages-Q7AO_FLv.mjs} +17 -3
  137. package/dist/{messages-2_xedlYw.mjs → messages-R3hUSvr3.mjs} +15 -1
  138. package/dist/{chunks/messages-CUy1vn-b.mjs → messages-Xq8UmkVs.mjs} +14 -0
  139. package/dist/{messages-BBJgd5jG.mjs → messages-Z9nEU2xK.mjs} +16 -2
  140. package/dist/{messages-Cm9aLHeX.mjs → messages-_ErNTNhk.mjs} +15 -1
  141. package/dist/{messages-JZUhXTuV.mjs → messages-_ncGrKHh.mjs} +16 -2
  142. package/dist/{messages-ftMcCEuO.mjs → messages-kep5wtm4.mjs} +15 -1
  143. package/dist/{messages-DteYq0rv.mjs → messages-uKX8WBaD.mjs} +16 -2
  144. package/dist/{messages-Bdv-IkfG.mjs → messages-w7v1GNaE.mjs} +15 -1
  145. package/dist/tools.mjs +929 -779
  146. package/dist/vendor.LICENSE.txt +1 -1
  147. package/package.json +15 -14
  148. package/src/cli/commands/migration.ts +16 -0
  149. package/src/cli/commands/migrationContent.ts +6 -0
  150. package/src/cli/index.ts +47 -0
  151. package/src/cli/utils/output.ts +10 -0
  152. package/src/components/i18n/locales/am/messages.json +15 -1
  153. package/src/components/i18n/locales/ar/messages.json +14 -0
  154. package/src/components/i18n/locales/az/messages.json +14 -0
  155. package/src/components/i18n/locales/bg/messages.json +14 -0
  156. package/src/components/i18n/locales/bn/messages.json +25 -11
  157. package/src/components/i18n/locales/bs/messages.json +15 -1
  158. package/src/components/i18n/locales/cs/messages.json +14 -0
  159. package/src/components/i18n/locales/da/messages.json +14 -0
  160. package/src/components/i18n/locales/de/messages.json +14 -0
  161. package/src/components/i18n/locales/dv/messages.json +15 -1
  162. package/src/components/i18n/locales/el/messages.json +15 -1
  163. package/src/components/i18n/locales/en/messages.json +14 -0
  164. package/src/components/i18n/locales/es/messages.json +14 -0
  165. package/src/components/i18n/locales/et/messages.json +14 -0
  166. package/src/components/i18n/locales/fa/messages.json +15 -1
  167. package/src/components/i18n/locales/fi/messages.json +15 -1
  168. package/src/components/i18n/locales/fil/messages.json +20 -6
  169. package/src/components/i18n/locales/fr/messages.json +15 -1
  170. package/src/components/i18n/locales/gu/messages.json +15 -1
  171. package/src/components/i18n/locales/he/messages.json +14 -0
  172. package/src/components/i18n/locales/hi/messages.json +23 -9
  173. package/src/components/i18n/locales/hr/messages.json +14 -0
  174. package/src/components/i18n/locales/hu/messages.json +14 -0
  175. package/src/components/i18n/locales/hy/messages.json +16 -2
  176. package/src/components/i18n/locales/id/messages.json +19 -5
  177. package/src/components/i18n/locales/it/messages.json +14 -0
  178. package/src/components/i18n/locales/ja/messages.json +14 -0
  179. package/src/components/i18n/locales/ka/messages.json +15 -1
  180. package/src/components/i18n/locales/km/messages.json +16 -2
  181. package/src/components/i18n/locales/kn/messages.json +16 -2
  182. package/src/components/i18n/locales/ko/messages.json +14 -0
  183. package/src/components/i18n/locales/ku/messages.json +16 -2
  184. package/src/components/i18n/locales/lo/messages.json +15 -1
  185. package/src/components/i18n/locales/lt/messages.json +15 -1
  186. package/src/components/i18n/locales/lv/messages.json +15 -1
  187. package/src/components/i18n/locales/mk/messages.json +16 -2
  188. package/src/components/i18n/locales/ml/messages.json +16 -2
  189. package/src/components/i18n/locales/mn/messages.json +16 -2
  190. package/src/components/i18n/locales/mr/messages.json +24 -10
  191. package/src/components/i18n/locales/ms/messages.json +17 -3
  192. package/src/components/i18n/locales/my/messages.json +16 -2
  193. package/src/components/i18n/locales/ne/messages.json +24 -10
  194. package/src/components/i18n/locales/nl/messages.json +15 -1
  195. package/src/components/i18n/locales/no/messages.json +16 -2
  196. package/src/components/i18n/locales/pa/messages.json +15 -1
  197. package/src/components/i18n/locales/pl/messages.json +14 -0
  198. package/src/components/i18n/locales/ps/messages.json +17 -3
  199. package/src/components/i18n/locales/pt/messages.json +14 -0
  200. package/src/components/i18n/locales/ro/messages.json +15 -1
  201. package/src/components/i18n/locales/ru/messages.json +14 -0
  202. package/src/components/i18n/locales/sd/messages.json +16 -2
  203. package/src/components/i18n/locales/si/messages.json +23 -9
  204. package/src/components/i18n/locales/sk/messages.json +15 -1
  205. package/src/components/i18n/locales/sl/messages.json +16 -2
  206. package/src/components/i18n/locales/sq/messages.json +16 -2
  207. package/src/components/i18n/locales/sr/messages.json +16 -2
  208. package/src/components/i18n/locales/sv/messages.json +16 -2
  209. package/src/components/i18n/locales/sw/messages.json +16 -2
  210. package/src/components/i18n/locales/ta/messages.json +21 -7
  211. package/src/components/i18n/locales/te/messages.json +40 -26
  212. package/src/components/i18n/locales/th/messages.json +19 -5
  213. package/src/components/i18n/locales/tr/messages.json +15 -1
  214. package/src/components/i18n/locales/ug/messages.json +16 -2
  215. package/src/components/i18n/locales/uk/messages.json +15 -1
  216. package/src/components/i18n/locales/ur/messages.json +15 -1
  217. package/src/components/i18n/locales/vi/messages.json +25 -11
  218. package/src/components/i18n/locales/yi/messages.json +16 -2
  219. package/src/components/i18n/locales/zh/messages.json +15 -1
  220. package/src/components/modules/api/blocks.ts +17 -2
  221. package/src/components/modules/api/history.ts +64 -0
  222. package/src/components/modules/api/index.ts +1 -0
  223. package/src/components/modules/api/readonly.ts +11 -1
  224. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +12 -1
  225. package/src/components/modules/blockManager/blockManager.ts +7 -0
  226. package/src/components/modules/blockManager/yjs-sync.ts +12 -2
  227. package/src/components/modules/index.ts +3 -0
  228. package/src/components/modules/readonly.ts +11 -0
  229. package/src/components/modules/toolbar/index.ts +29 -7
  230. package/src/components/modules/ui.ts +46 -68
  231. package/src/components/modules/uiControllers/controllers/blockHover.ts +40 -61
  232. package/src/components/modules/yjs/index.ts +23 -0
  233. package/src/components/ui/toolbox.ts +41 -6
  234. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +3 -1
  235. package/src/components/utils/popover/popover-desktop.ts +27 -8
  236. package/src/tools/table/index.ts +87 -70
  237. package/src/tools/table/table-add-controls.ts +33 -7
  238. package/src/tools/table/table-cell-blocks.ts +77 -5
  239. package/src/tools/table/table-cell-selection.ts +70 -46
  240. package/src/tools/table/table-core.ts +20 -15
  241. package/src/tools/table/table-grip-visuals.ts +4 -4
  242. package/src/tools/table/table-operations.ts +22 -12
  243. package/src/tools/table/table-restrictions.ts +64 -0
  244. package/src/tools/table/table-row-col-action-handler.ts +190 -0
  245. package/src/tools/table/table-row-col-controls.ts +91 -182
  246. package/src/tools/table/table-row-col-drag.ts +4 -4
  247. package/src/tools/table/table-row-col-popover.ts +225 -0
  248. package/src/tools/table/types.ts +2 -0
  249. package/src/types-internal/blok-modules.d.ts +2 -0
  250. package/types/api/history.d.ts +33 -0
  251. package/types/api/index.d.ts +1 -0
  252. package/types/api/readonly.d.ts +12 -2
  253. package/types/index.d.ts +3 -0
  254. package/types/utils/popover/popover.d.ts +7 -0
  255. package/dist/chunks/messages-CySyfkMU.mjs +0 -48
  256. package/dist/messages-CySyfkMU.mjs +0 -48
@@ -12,9 +12,10 @@ export class ReadOnlyAPI extends Module {
12
12
  public get methods(): ReadOnly {
13
13
  const getIsEnabled = (): boolean => this.isEnabled;
14
14
 
15
-
15
+
16
16
  return {
17
17
  toggle: (state): Promise<boolean> => this.toggle(state),
18
+ set: (state): Promise<boolean> => this.set(state),
18
19
  get isEnabled(): boolean {
19
20
  return getIsEnabled();
20
21
  },
@@ -30,6 +31,15 @@ export class ReadOnlyAPI extends Module {
30
31
  return this.Blok.ReadOnly.toggle(state);
31
32
  }
32
33
 
34
+ /**
35
+ * Set read-only mode to the specified boolean state
36
+ * @param {boolean} state - read-only state to set
37
+ * @returns {Promise<boolean>} the new read-only state
38
+ */
39
+ public set(state: boolean): Promise<boolean> {
40
+ return this.Blok.ReadOnly.set(state);
41
+ }
42
+
33
43
  /**
34
44
  * Returns current read-only state
35
45
  */
@@ -153,7 +153,18 @@ export class MarkdownShortcuts extends BlockEventComposer {
153
153
  const { BlockManager, Tools } = this.Blok;
154
154
  const currentBlock = BlockManager.currentBlock;
155
155
 
156
- if (!currentBlock?.tool.isDefault) {
156
+ if (!currentBlock) {
157
+ return false;
158
+ }
159
+
160
+ // Check if inside table cell
161
+ const isInsideTableCell = currentBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
162
+
163
+ if (isInsideTableCell) {
164
+ return false; // Don't convert to header
165
+ }
166
+
167
+ if (!currentBlock.tool.isDefault) {
157
168
  return false;
158
169
  }
159
170
 
@@ -7,6 +7,7 @@
7
7
  import type { BlockToolData, OutputBlockData, PasteEvent } from '../../../../types';
8
8
  import type { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
9
9
  import type { BlockMutationEventMap, BlockMutationType } from '../../../../types/events/block';
10
+ import { BlockAddedMutationType } from '../../../../types/events/block/BlockAdded';
10
11
  import { BlockChangedMutationType } from '../../../../types/events/block/BlockChanged';
11
12
  import { BlockRemovedMutationType } from '../../../../types/events/block/BlockRemoved';
12
13
  import { Module } from '../../__module';
@@ -307,6 +308,12 @@ export class BlockManager extends Module {
307
308
  replaceBlock: (index, newBlock) => {
308
309
  this.blocksStore.replace(index, newBlock);
309
310
  },
311
+ onBlockRemoved: (block, index) => {
312
+ this.blockDidMutated(BlockRemovedMutationType, block, { index });
313
+ },
314
+ onBlockAdded: (block, index) => {
315
+ this.blockDidMutated(BlockAddedMutationType, block, { index });
316
+ },
310
317
  },
311
318
  this.blocksStore
312
319
  );
@@ -43,6 +43,10 @@ export interface SyncHandlers {
43
43
  updateIndentation: (block: Block) => void;
44
44
  /** Called to replace a block at a specific index with a new block instance */
45
45
  replaceBlock: (index: number, newBlock: Block) => void;
46
+ /** Called when a block is removed during undo/redo (before DOM removal) */
47
+ onBlockRemoved: (block: Block, index: number) => void;
48
+ /** Called when a block is added during undo/redo (after insertion) */
49
+ onBlockAdded: (block: Block, index: number) => void;
46
50
  }
47
51
 
48
52
  /**
@@ -273,10 +277,12 @@ export class BlockYjsSync {
273
277
  bindEventsImmediately: true,
274
278
  });
275
279
 
276
- // Insert into blocks store at correct position - caller must handle this
277
- // This is a limitation - we need the blocksStore.insert method
278
280
  this.blocksStore.insert(targetIndex, block);
279
281
 
282
+ // Emit block-added event so listeners (e.g., TableCellBlocks) can
283
+ // claim the block for the correct cell during undo/redo
284
+ this.handlers.onBlockAdded(block, targetIndex);
285
+
280
286
  // Apply indentation if needed
281
287
  if (parentId !== undefined) {
282
288
  this.handlers.updateIndentation(block);
@@ -300,6 +306,10 @@ export class BlockYjsSync {
300
306
  return;
301
307
  }
302
308
 
309
+ // Emit block-removed event BEFORE removal so listeners can inspect
310
+ // the block's DOM position (e.g., which table cell it's in)
311
+ this.handlers.onBlockRemoved(block, index);
312
+
303
313
  // Remove from DOM
304
314
  this.blocksStore.remove(index);
305
315
 
@@ -1,6 +1,7 @@
1
1
  import { BlocksAPI } from './api/blocks';
2
2
  import { CaretAPI } from './api/caret';
3
3
  import { EventsAPI } from './api/events';
4
+ import { HistoryAPI } from './api/history';
4
5
  import { I18nAPI } from './api/i18n';
5
6
  import { API } from './api/index';
6
7
  import { InlineToolbarAPI } from './api/inlineToolbar';
@@ -44,6 +45,7 @@ export {
44
45
  BlocksAPI,
45
46
  CaretAPI,
46
47
  EventsAPI,
48
+ HistoryAPI,
47
49
  I18nAPI,
48
50
  API,
49
51
  InlineToolbarAPI,
@@ -91,6 +93,7 @@ export const Modules = {
91
93
  BlocksAPI,
92
94
  CaretAPI,
93
95
  EventsAPI,
96
+ HistoryAPI,
94
97
  I18nAPI,
95
98
  API,
96
99
  InlineToolbarAPI,
@@ -125,6 +125,17 @@ export class ReadOnly extends Module {
125
125
  return this.readOnlyEnabled;
126
126
  }
127
127
 
128
+ /**
129
+ * Set read-only mode to the specified boolean state
130
+ * Unlike toggle(), this method requires a parameter and does not have default toggle behavior
131
+ * Call all Modules `toggleReadOnly` method and re-render Blok
132
+ * @param state - read-only state to set (required)
133
+ * @returns the new read-only state
134
+ */
135
+ public async set(state: boolean): Promise<boolean> {
136
+ return this.toggle(state);
137
+ }
138
+
128
139
  /**
129
140
  * Throws an error about tools which don't support read-only mode
130
141
  */
@@ -320,17 +320,15 @@ export class Toolbar extends Module<ToolbarNodes> {
320
320
  /**
321
321
  * If no one Block selected as a Current
322
322
  */
323
- const targetBlock = block ?? this.Blok.BlockManager.currentBlock;
323
+ const unresolvedBlock = block ?? this.Blok.BlockManager.currentBlock;
324
324
 
325
- if (!targetBlock) {
325
+ if (!unresolvedBlock) {
326
326
  return;
327
327
  }
328
328
 
329
- /**
330
- * Don't show toolbar for blocks inside table cells.
331
- * Uses the DOM attribute directly to avoid cross-module dependency on the table tool.
332
- */
333
- if (targetBlock.holder.closest('[data-blok-table-cell-blocks]')) {
329
+ const targetBlock = this.resolveTableCellBlock(unresolvedBlock);
330
+
331
+ if (!targetBlock) {
334
332
  this.close();
335
333
 
336
334
  return;
@@ -562,6 +560,30 @@ export class Toolbar extends Module<ToolbarNodes> {
562
560
  this.explicitlyClosed = false;
563
561
  }
564
562
 
563
+ /**
564
+ * If the block is inside a table cell, resolve to the parent table block.
565
+ * This ensures the toolbar shows for the table when clicking/focusing inside cells.
566
+ * Uses the DOM attribute directly to avoid cross-module dependency on the table tool.
567
+ *
568
+ * @param block - the block to resolve
569
+ * @returns the parent table block if inside a cell, the original block otherwise, or null if resolution fails
570
+ */
571
+ private resolveTableCellBlock(block: Block): Block | null {
572
+ const cellBlocksContainer = block.holder.closest('[data-blok-table-cell-blocks]');
573
+
574
+ if (!cellBlocksContainer) {
575
+ return block;
576
+ }
577
+
578
+ const tableBlockHolder = cellBlocksContainer.closest('[data-blok-testid="block-wrapper"]');
579
+
580
+ if (!tableBlockHolder) {
581
+ return null;
582
+ }
583
+
584
+ return this.Blok.BlockManager.getBlockByChildNode(tableBlockHolder) ?? null;
585
+ }
586
+
565
587
  /**
566
588
  * Reset the Toolbar position to prevent DOM height growth, for example after blocks deletion
567
589
  */
@@ -30,6 +30,7 @@ interface UINodes extends Record<string, unknown> {
30
30
  holder: HTMLElement;
31
31
  wrapper: HTMLElement;
32
32
  redactor: HTMLElement;
33
+ bottomZone: HTMLElement;
33
34
  }
34
35
 
35
36
  /**
@@ -191,12 +192,11 @@ export class UI extends Module<UINodes> {
191
192
  this.selectionController.setWrapperElement(this.nodes.wrapper);
192
193
 
193
194
  /**
194
- * Block hover controller needs content rect getter
195
+ * Block hover controller detects hover over blocks and finds nearest block
195
196
  */
196
197
  this.blockHoverController = new BlockHoverController({
197
198
  config: this.config,
198
199
  eventsDispatcher: this.eventsDispatcher,
199
- contentRectGetter: () => this.contentRect,
200
200
  });
201
201
  this.blockHoverController.state = this.Blok;
202
202
 
@@ -452,11 +452,15 @@ export class UI extends Module<UINodes> {
452
452
  }
453
453
 
454
454
  /**
455
- * Set customizable bottom zone height
455
+ * Create dedicated bottom zone element
456
456
  */
457
- this.nodes.redactor.style.paddingBottom = this.config.minHeight + 'px';
457
+ this.nodes.bottomZone = $.make('div', ['cursor-text']);
458
+ this.nodes.bottomZone.setAttribute('data-blok-bottom-zone', '');
459
+ this.nodes.bottomZone.setAttribute('data-blok-testid', 'bottom-zone');
460
+ this.nodes.bottomZone.style.minHeight = this.config.minHeight + 'px';
458
461
 
459
462
  this.nodes.wrapper.appendChild(this.nodes.redactor);
463
+ this.nodes.wrapper.appendChild(this.nodes.bottomZone);
460
464
  this.nodes.holder.appendChild(this.nodes.wrapper);
461
465
 
462
466
  this.bindReadOnlyInsensitiveListeners();
@@ -541,7 +545,16 @@ export class UI extends Module<UINodes> {
541
545
  */
542
546
  private bindReadOnlySensitiveListeners(): void {
543
547
  /**
544
- * Redactor click handler for bottom zone clicks
548
+ * Bottom zone click handler creates new block when clicking below last block
549
+ */
550
+ this.readOnlyMutableListeners.on(this.nodes.bottomZone, 'click', (event: Event) => {
551
+ if (event instanceof MouseEvent) {
552
+ this.bottomZoneClicked(event);
553
+ }
554
+ }, false);
555
+
556
+ /**
557
+ * Redactor click handler for Ctrl+click anchor navigation
545
558
  */
546
559
  this.readOnlyMutableListeners.on(this.nodes.redactor, 'click', (event: Event) => {
547
560
  if (event instanceof MouseEvent) {
@@ -610,98 +623,63 @@ export class UI extends Module<UINodes> {
610
623
  }
611
624
 
612
625
  /**
613
- * All clicks on the redactor zone
614
- * @param {MouseEvent} event - click event
615
- * @description
616
- * - By clicks on the Blok's bottom zone:
617
- * - if last Block is empty, set a Caret to this
618
- * - otherwise, add a new empty Block and set a Caret to that
626
+ * Handle click on the bottom zone element below the last block.
627
+ * Creates a new default block if needed, focuses the last block, and opens the toolbar.
619
628
  */
620
- private redactorClicked(event: MouseEvent): void {
629
+ private bottomZoneClicked(event: MouseEvent): void {
621
630
  if (!Selection.isCollapsed) {
622
631
  return;
623
632
  }
624
633
 
625
- /**
626
- * case when user clicks on anchor element
627
- * if it is clicked via ctrl key, then we open new window with url
628
- */
629
- const element = event.target as Element;
630
- const ctrlKey = event.metaKey || event.ctrlKey;
631
- const shouldOpenAnchorInNewTab = $.isAnchor(element) && ctrlKey;
634
+ const { BlockSelection, BlockManager, Caret, Toolbar } = this.Blok;
632
635
 
633
- if (!shouldOpenAnchorInNewTab) {
634
- this.processBottomZoneClick(event);
636
+ if (BlockSelection.anyBlockSelected) {
637
+ return;
638
+ }
635
639
 
640
+ if (!BlockManager.lastBlock) {
636
641
  return;
637
642
  }
638
643
 
639
644
  event.stopImmediatePropagation();
640
645
  event.stopPropagation();
641
646
 
642
- const href = element.getAttribute('href');
643
-
644
- if (!href) {
645
- return;
647
+ /**
648
+ * Insert a default-block at the bottom if:
649
+ * - last-block is not a default-block (Text)
650
+ * - Or, default-block is not empty
651
+ */
652
+ if (!BlockManager.lastBlock.tool.isDefault || !BlockManager.lastBlock.isEmpty) {
653
+ BlockManager.insertAtEnd();
646
654
  }
647
655
 
648
- const validUrl = getValidUrl(href);
649
-
650
- openTab(validUrl);
656
+ Caret.setToTheLastBlock();
657
+ Toolbar.moveAndOpen(BlockManager.lastBlock);
651
658
  }
652
659
 
653
660
  /**
654
- * Check if user clicks on the Blok's bottom zone:
655
- * - set caret to the last block
656
- * - or add new empty block
657
- * @param event - click event
661
+ * Handle Ctrl+click on anchor elements to open in new tab
658
662
  */
659
- private processBottomZoneClick(event: MouseEvent): void {
660
- const lastBlock = this.Blok.BlockManager.getBlockByIndex(-1);
661
-
662
- if (lastBlock === undefined) {
663
- return;
664
- }
665
-
666
- const lastBlockBottomCoord = $.offset(lastBlock.holder).bottom;
667
- const clickedCoord = event.pageY;
668
- const { BlockSelection } = this.Blok;
669
- const isClickedBottom = event.target instanceof Element &&
670
- event.target.isEqualNode(this.nodes.redactor) &&
671
- /**
672
- * If there is cross block selection started, target will be equal to redactor so we need additional check
673
- */
674
- !BlockSelection.anyBlockSelected &&
675
-
676
- /**
677
- * Prevent caret jumping (to last block) when clicking between blocks
678
- */
679
- lastBlockBottomCoord < clickedCoord;
663
+ private redactorClicked(event: MouseEvent): void {
664
+ const element = event.target as Element;
665
+ const ctrlKey = event.metaKey || event.ctrlKey;
680
666
 
681
- if (!isClickedBottom) {
667
+ if (!$.isAnchor(element) || !ctrlKey) {
682
668
  return;
683
669
  }
684
670
 
685
671
  event.stopImmediatePropagation();
686
672
  event.stopPropagation();
687
673
 
688
- const { BlockManager, Caret, Toolbar } = this.Blok;
674
+ const href = element.getAttribute('href');
689
675
 
690
- /**
691
- * Insert a default-block at the bottom if:
692
- * - last-block is not a default-block (Text)
693
- * to prevent unnecessary tree-walking on Tools with many nodes (for ex. Table)
694
- * - Or, default-block is not empty
695
- */
696
- if (!BlockManager.lastBlock?.tool.isDefault || !BlockManager.lastBlock?.isEmpty) {
697
- BlockManager.insertAtEnd();
676
+ if (!href) {
677
+ return;
698
678
  }
699
679
 
700
- /**
701
- * Set the caret and toolbar to empty Block
702
- */
703
- Caret.setToTheLastBlock();
704
- Toolbar.moveAndOpen(BlockManager.lastBlock);
680
+ const validUrl = getValidUrl(href);
681
+
682
+ openTab(validUrl);
705
683
  }
706
684
 
707
685
  /**
@@ -5,20 +5,15 @@ import { throttle } from '../../../utils';
5
5
  import { Controller } from './_base';
6
6
 
7
7
  /**
8
- * BlockHoverController detects when user hovers over blocks, including extended hover zone.
8
+ * BlockHoverController detects when user hovers over blocks or finds nearest block.
9
9
  *
10
10
  * Responsibilities:
11
11
  * - Listen to mousemove events (throttled)
12
- * - Find block by element hit or extended zone
12
+ * - Find block by element hit or nearest by Y distance
13
13
  * - Emit BlockHovered events
14
14
  * - Track last hovered block to avoid duplicate events
15
15
  */
16
16
  export class BlockHoverController extends Controller {
17
- /**
18
- * Getter function for content rect
19
- */
20
- private contentRectGetter: () => DOMRect;
21
-
22
17
  /**
23
18
  * Used to not emit the same block multiple times to the 'block-hovered' event on every mousemove.
24
19
  * Stores block ID to ensure consistent comparison regardless of how the block was detected.
@@ -42,10 +37,8 @@ export class BlockHoverController extends Controller {
42
37
  constructor(options: {
43
38
  config: Controller['config'];
44
39
  eventsDispatcher: Controller['eventsDispatcher'];
45
- contentRectGetter: () => DOMRect;
46
40
  }) {
47
41
  super(options);
48
- this.contentRectGetter = options.contentRectGetter;
49
42
  }
50
43
 
51
44
  /**
@@ -83,35 +76,11 @@ export class BlockHoverController extends Controller {
83
76
  : closestBlockWrapper;
84
77
 
85
78
  /**
86
- * If no block element found directly, try the extended hover zone
79
+ * If no block element found directly, find the nearest block by Y distance
87
80
  */
88
- const zoneBlock = !hoveredBlockElement
89
- ? this.findBlockInHoverZone(event.clientX, event.clientY)
90
- : null;
91
-
92
- if (zoneBlock !== null && this.blockHoveredState.lastHoveredBlockId !== zoneBlock.id) {
93
- /**
94
- * Emit the event but DON'T set lastHoveredBlockId for hover zone events.
95
- * This allows the event to be emitted again when the mouse enters the actual block element,
96
- * which is important for proper toolbar positioning after cross-block selection.
97
- */
98
- this.eventsDispatcher.emit(BlockHovered, {
99
- block: zoneBlock,
100
- target: zoneBlock.holder,
101
- });
102
- }
103
-
104
- if (zoneBlock !== null) {
105
- return;
106
- }
107
-
108
81
  if (!hoveredBlockElement) {
109
- /**
110
- * When no block is found (mouse left the editor area), reset the hover state.
111
- * This allows hover events to be emitted again when re-entering a block,
112
- * which is important after cross-block selection completes.
113
- */
114
- this.blockHoveredState.lastHoveredBlockId = null;
82
+ this.emitNearestBlockHovered(event.clientY);
83
+
115
84
  return;
116
85
  }
117
86
 
@@ -143,9 +112,8 @@ export class BlockHoverController extends Controller {
143
112
  );
144
113
 
145
114
  /**
146
- * Listen on document to detect hover in the extended zone
147
- * which is outside the wrapper's bounds.
148
- * We filter events to only process those over the editor or in the hover zone.
115
+ * Listen on document to detect hover anywhere on the page.
116
+ * When cursor is not directly on a block, finds the nearest block by Y distance.
149
117
  */
150
118
  this.readOnlyMutableListeners.on(document, 'mousemove', (event: Event) => {
151
119
  throttledHandleBlockHovered(event);
@@ -155,38 +123,49 @@ export class BlockHoverController extends Controller {
155
123
  }
156
124
 
157
125
  /**
158
- * Finds a block by vertical position when cursor is in the hover zone.
159
- * The hover zone extends indefinitely on both sides of the content (left and right),
160
- * allowing the toolbar to follow hover anywhere outside the content area horizontally.
161
- * @param clientX - Cursor X position
126
+ * Finds and emits a BlockHovered event for the nearest block by Y distance.
127
+ * Deduplicates by lastHoveredBlockId to avoid redundant events.
162
128
  * @param clientY - Cursor Y position
163
- * @returns Block at the vertical position, or null if not in hover zone or no block found
164
129
  */
165
- private findBlockInHoverZone(clientX: number, clientY: number): Block | null {
166
- const contentRect = this.contentRectGetter();
130
+ private emitNearestBlockHovered(clientY: number): void {
131
+ const nearestBlock = this.findNearestBlock(clientY);
167
132
 
168
- /**
169
- * Check if cursor is outside the content area horizontally (either left OR right side).
170
- * The zone extends indefinitely on both sides, not limited to HOVER_ZONE_SIZE.
171
- */
172
- const isInHoverZone = clientX < contentRect.left || clientX > contentRect.right;
133
+ if (nearestBlock === null || this.blockHoveredState.lastHoveredBlockId === nearestBlock.id) {
134
+ return;
135
+ }
173
136
 
174
- if (!isInHoverZone) {
137
+ this.blockHoveredState.lastHoveredBlockId = nearestBlock.id;
138
+
139
+ this.eventsDispatcher.emit(BlockHovered, {
140
+ block: nearestBlock,
141
+ target: nearestBlock.holder,
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Finds the nearest block by vertical distance to cursor position.
147
+ * Returns the block whose vertical center is closest to the cursor Y position.
148
+ * If cursor is above all blocks, returns the first block.
149
+ * If cursor is below all blocks, returns the last block.
150
+ * @param clientY - Cursor Y position
151
+ * @returns Nearest block, or null if no blocks exist
152
+ */
153
+ private findNearestBlock(clientY: number): Block | null {
154
+ const blocks = this.Blok.BlockManager.blocks;
155
+
156
+ if (blocks.length === 0) {
175
157
  return null;
176
158
  }
177
159
 
178
- /**
179
- * Find block by Y position
180
- */
181
- for (const block of this.Blok.BlockManager.blocks) {
160
+ const result = blocks.reduce<{ block: Block; distance: number }>((nearest, block) => {
182
161
  const rect = block.holder.getBoundingClientRect();
162
+ const centerY = (rect.top + rect.bottom) / 2;
163
+ const distance = Math.abs(clientY - centerY);
183
164
 
184
- if (clientY >= rect.top && clientY <= rect.bottom) {
185
- return block;
186
- }
187
- }
165
+ return distance < nearest.distance ? { block, distance } : nearest;
166
+ }, { block: blocks[0], distance: Infinity });
188
167
 
189
- return null;
168
+ return result.block;
190
169
  }
191
170
 
192
171
  /**
@@ -189,6 +189,29 @@ export class YjsManager extends Module {
189
189
  this.undoHistory.redo();
190
190
  }
191
191
 
192
+ /**
193
+ * Check if undo is available.
194
+ * @returns true if undo is available
195
+ */
196
+ public canUndo(): boolean {
197
+ return this.undoHistory.canUndo();
198
+ }
199
+
200
+ /**
201
+ * Check if redo is available.
202
+ * @returns true if redo is available
203
+ */
204
+ public canRedo(): boolean {
205
+ return this.undoHistory.canRedo();
206
+ }
207
+
208
+ /**
209
+ * Clear all history.
210
+ */
211
+ public clear(): void {
212
+ this.undoHistory.clear();
213
+ }
214
+
192
215
  /**
193
216
  * Stop capturing changes into current undo group.
194
217
  * Call this to force next change into a new undo entry.
@@ -1,5 +1,7 @@
1
+ import { RESTRICTED_TOOLS } from '../../tools/table/table-restrictions';
1
2
  import { Dom } from '../dom';
2
3
  import { BlokMobileLayoutToggled } from '../events';
4
+ import { SelectionUtils } from '../selection';
3
5
  import type { BlockToolAdapter } from '../tools/block';
4
6
  import type { ToolsCollection } from '../tools/collection';
5
7
  import { beautifyShortcut, capitalize, isMobileScreen } from '../utils';
@@ -155,7 +157,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
155
157
 
156
158
  /**
157
159
  * Whether the toolbox was opened inside a table cell.
158
- * Used to restore the table item visibility on close.
160
+ * Used to restore restricted tool visibility on close.
159
161
  */
160
162
  private isInsideTableCell = false;
161
163
 
@@ -273,17 +275,28 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
273
275
  const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
274
276
 
275
277
  /**
276
- * Hide the table tool when the caret is inside a table cell
277
- * to prevent nested tables.
278
+ * Hide restricted tools (headers, tables) when the caret is inside a table cell.
278
279
  */
279
280
  this.isInsideTableCell = currentBlock !== undefined
280
281
  && currentBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
281
282
 
282
283
  if (this.isInsideTableCell) {
283
- this.popover?.toggleItemHiddenByName('table', true);
284
+ this.toggleRestrictedToolsHidden(true);
284
285
  }
285
286
 
286
287
  this.popover?.show();
288
+
289
+ /**
290
+ * When opening toolbox inside a table cell, position it at the caret
291
+ * instead of at the trigger element (which is outside the table).
292
+ * Must be called after show() so the popover is in the DOM.
293
+ */
294
+ if (this.isInsideTableCell && this.popover instanceof PopoverDesktop) {
295
+ const caretRect = SelectionUtils.rect;
296
+
297
+ this.popover.updatePosition(caretRect);
298
+ }
299
+
287
300
  this.opened = true;
288
301
  this.emit(ToolboxEvent.Opened);
289
302
  this.startListeningToBlockInput();
@@ -294,7 +307,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
294
307
  */
295
308
  public close(): void {
296
309
  if (this.isInsideTableCell) {
297
- this.popover?.toggleItemHiddenByName('table', false);
310
+ this.toggleRestrictedToolsHidden(false);
298
311
  this.isInsideTableCell = false;
299
312
  }
300
313
 
@@ -364,7 +377,7 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
364
377
  */
365
378
  private onPopoverClose = (): void => {
366
379
  if (this.isInsideTableCell) {
367
- this.popover?.toggleItemHiddenByName('table', false);
380
+ this.toggleRestrictedToolsHidden(false);
368
381
  this.isInsideTableCell = false;
369
382
  }
370
383
 
@@ -373,6 +386,28 @@ export class Toolbox extends EventsDispatcher<ToolboxEventMap> {
373
386
  this.emit(ToolboxEvent.Closed);
374
387
  };
375
388
 
389
+ /**
390
+ * Toggles hidden state for all popover items belonging to restricted tools.
391
+ * Handles tools like header that have multiple entries with custom names (header-1, header-2, etc.)
392
+ * by matching item names that equal or start with a restricted tool name.
393
+ */
394
+ private toggleRestrictedToolsHidden(isHidden: boolean): void {
395
+ for (const item of this.toolboxItemsToBeDisplayed) {
396
+ if (!('name' in item) || item.name === undefined) {
397
+ continue;
398
+ }
399
+
400
+ const { name } = item;
401
+ const isRestricted = RESTRICTED_TOOLS.some(
402
+ restricted => name === restricted || name.startsWith(`${restricted}-`)
403
+ );
404
+
405
+ if (isRestricted) {
406
+ this.popover?.toggleItemHiddenByName(name, isHidden);
407
+ }
408
+ }
409
+ }
410
+
376
411
  /**
377
412
  * Returns list of tools that enables the Toolbox (by specifying the 'toolbox' getter)
378
413
  */