@jackuait/blok 0.6.0-beta.0 → 0.6.0-beta.10

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 (286) 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-Bu9S3SsR.mjs → blok-Buf0btS7.mjs} +2267 -2024
  5. package/dist/chunks/{i18next-loader-CKuXJ0Av.mjs → i18next-loader-CVf_ZfwA.mjs} +1 -1
  6. package/dist/chunks/{index-jtZaryNw.mjs → index-C6jsfLLp.mjs} +1 -1
  7. package/dist/chunks/{inline-tool-convert-CFjyrH30.mjs → inline-tool-convert-BKKEoOqB.mjs} +710 -570
  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 +2005 -1267
  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/block-tunes/block-tune-delete.ts +3 -2
  153. package/src/components/blocks.ts +23 -6
  154. package/src/components/constants/data-attributes.ts +2 -0
  155. package/src/components/i18n/locales/am/messages.json +15 -1
  156. package/src/components/i18n/locales/ar/messages.json +14 -0
  157. package/src/components/i18n/locales/az/messages.json +14 -0
  158. package/src/components/i18n/locales/bg/messages.json +14 -0
  159. package/src/components/i18n/locales/bn/messages.json +25 -11
  160. package/src/components/i18n/locales/bs/messages.json +15 -1
  161. package/src/components/i18n/locales/cs/messages.json +14 -0
  162. package/src/components/i18n/locales/da/messages.json +14 -0
  163. package/src/components/i18n/locales/de/messages.json +14 -0
  164. package/src/components/i18n/locales/dv/messages.json +15 -1
  165. package/src/components/i18n/locales/el/messages.json +15 -1
  166. package/src/components/i18n/locales/en/messages.json +14 -0
  167. package/src/components/i18n/locales/es/messages.json +14 -0
  168. package/src/components/i18n/locales/et/messages.json +14 -0
  169. package/src/components/i18n/locales/fa/messages.json +15 -1
  170. package/src/components/i18n/locales/fi/messages.json +15 -1
  171. package/src/components/i18n/locales/fil/messages.json +20 -6
  172. package/src/components/i18n/locales/fr/messages.json +15 -1
  173. package/src/components/i18n/locales/gu/messages.json +15 -1
  174. package/src/components/i18n/locales/he/messages.json +14 -0
  175. package/src/components/i18n/locales/hi/messages.json +23 -9
  176. package/src/components/i18n/locales/hr/messages.json +14 -0
  177. package/src/components/i18n/locales/hu/messages.json +14 -0
  178. package/src/components/i18n/locales/hy/messages.json +16 -2
  179. package/src/components/i18n/locales/id/messages.json +19 -5
  180. package/src/components/i18n/locales/it/messages.json +14 -0
  181. package/src/components/i18n/locales/ja/messages.json +14 -0
  182. package/src/components/i18n/locales/ka/messages.json +15 -1
  183. package/src/components/i18n/locales/km/messages.json +16 -2
  184. package/src/components/i18n/locales/kn/messages.json +16 -2
  185. package/src/components/i18n/locales/ko/messages.json +14 -0
  186. package/src/components/i18n/locales/ku/messages.json +16 -2
  187. package/src/components/i18n/locales/lo/messages.json +15 -1
  188. package/src/components/i18n/locales/lt/messages.json +15 -1
  189. package/src/components/i18n/locales/lv/messages.json +15 -1
  190. package/src/components/i18n/locales/mk/messages.json +16 -2
  191. package/src/components/i18n/locales/ml/messages.json +16 -2
  192. package/src/components/i18n/locales/mn/messages.json +16 -2
  193. package/src/components/i18n/locales/mr/messages.json +24 -10
  194. package/src/components/i18n/locales/ms/messages.json +17 -3
  195. package/src/components/i18n/locales/my/messages.json +16 -2
  196. package/src/components/i18n/locales/ne/messages.json +24 -10
  197. package/src/components/i18n/locales/nl/messages.json +15 -1
  198. package/src/components/i18n/locales/no/messages.json +16 -2
  199. package/src/components/i18n/locales/pa/messages.json +15 -1
  200. package/src/components/i18n/locales/pl/messages.json +14 -0
  201. package/src/components/i18n/locales/ps/messages.json +17 -3
  202. package/src/components/i18n/locales/pt/messages.json +14 -0
  203. package/src/components/i18n/locales/ro/messages.json +15 -1
  204. package/src/components/i18n/locales/ru/messages.json +14 -0
  205. package/src/components/i18n/locales/sd/messages.json +16 -2
  206. package/src/components/i18n/locales/si/messages.json +23 -9
  207. package/src/components/i18n/locales/sk/messages.json +15 -1
  208. package/src/components/i18n/locales/sl/messages.json +16 -2
  209. package/src/components/i18n/locales/sq/messages.json +16 -2
  210. package/src/components/i18n/locales/sr/messages.json +16 -2
  211. package/src/components/i18n/locales/sv/messages.json +16 -2
  212. package/src/components/i18n/locales/sw/messages.json +16 -2
  213. package/src/components/i18n/locales/ta/messages.json +21 -7
  214. package/src/components/i18n/locales/te/messages.json +40 -26
  215. package/src/components/i18n/locales/th/messages.json +19 -5
  216. package/src/components/i18n/locales/tr/messages.json +15 -1
  217. package/src/components/i18n/locales/ug/messages.json +16 -2
  218. package/src/components/i18n/locales/uk/messages.json +15 -1
  219. package/src/components/i18n/locales/ur/messages.json +15 -1
  220. package/src/components/i18n/locales/vi/messages.json +25 -11
  221. package/src/components/i18n/locales/yi/messages.json +16 -2
  222. package/src/components/i18n/locales/zh/messages.json +15 -1
  223. package/src/components/icons/index.ts +104 -83
  224. package/src/components/modules/api/blocks.ts +35 -2
  225. package/src/components/modules/api/history.ts +64 -0
  226. package/src/components/modules/api/index.ts +2 -0
  227. package/src/components/modules/api/readonly.ts +11 -1
  228. package/src/components/modules/blockEvents/composers/markdownShortcuts.ts +12 -1
  229. package/src/components/modules/blockManager/blockManager.ts +7 -0
  230. package/src/components/modules/blockManager/operations.ts +3 -2
  231. package/src/components/modules/blockManager/types.ts +3 -1
  232. package/src/components/modules/blockManager/yjs-sync.ts +12 -2
  233. package/src/components/modules/index.ts +3 -0
  234. package/src/components/modules/normalizeInlineImages.ts +263 -0
  235. package/src/components/modules/readonly.ts +11 -0
  236. package/src/components/modules/rectangleSelection.ts +19 -3
  237. package/src/components/modules/saver.ts +7 -3
  238. package/src/components/modules/toolbar/blockSettings.ts +3 -3
  239. package/src/components/modules/toolbar/index.ts +72 -14
  240. package/src/components/modules/toolbar/plus-button.ts +24 -3
  241. package/src/components/modules/toolbar/settings-toggler.ts +3 -5
  242. package/src/components/modules/ui.ts +46 -68
  243. package/src/components/modules/uiControllers/controllers/blockHover.ts +49 -61
  244. package/src/components/modules/uiControllers/controllers/keyboard.ts +17 -11
  245. package/src/components/modules/uiControllers/handlers/click.ts +0 -12
  246. package/src/components/modules/yjs/index.ts +23 -0
  247. package/src/components/ui/toolbox.ts +41 -6
  248. package/src/components/utils/placeholder.ts +16 -0
  249. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -1
  250. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -1
  251. package/src/components/utils/popover/index.ts +1 -0
  252. package/src/components/utils/popover/popover-abstract.ts +11 -1
  253. package/src/components/utils/popover/popover-desktop.ts +27 -8
  254. package/src/components/utils/popover/popover-registry.ts +188 -0
  255. package/src/components/utils/sanitizer.ts +23 -2
  256. package/src/components/utils/tooltip.ts +2 -24
  257. package/src/styles/main.css +22 -0
  258. package/src/tools/paragraph/index.ts +12 -4
  259. package/src/tools/table/data-normalizer.ts +1 -0
  260. package/src/tools/table/index.ts +283 -346
  261. package/src/tools/table/table-add-controls.ts +353 -47
  262. package/src/tools/table/table-cell-blocks.ts +95 -7
  263. package/src/tools/table/table-cell-selection.ts +648 -0
  264. package/src/tools/table/table-core.ts +21 -32
  265. package/src/tools/table/table-grip-visuals.ts +96 -0
  266. package/src/tools/table/table-heading-toggle.ts +127 -0
  267. package/src/tools/table/table-operations.ts +475 -0
  268. package/src/tools/table/table-resize.ts +27 -6
  269. package/src/tools/table/table-restrictions.ts +64 -0
  270. package/src/tools/table/table-row-col-action-handler.ts +190 -0
  271. package/src/tools/table/table-row-col-controls.ts +265 -211
  272. package/src/tools/table/table-row-col-drag.ts +4 -4
  273. package/src/tools/table/table-row-col-popover.ts +225 -0
  274. package/src/tools/table/types.ts +4 -0
  275. package/src/types-internal/blok-modules.d.ts +2 -0
  276. package/types/api/blocks.d.ts +8 -0
  277. package/types/api/history.d.ts +33 -0
  278. package/types/api/index.d.ts +1 -0
  279. package/types/api/readonly.d.ts +12 -2
  280. package/types/index.d.ts +10 -0
  281. package/types/tools/table.d.ts +67 -0
  282. package/types/tools-entry.d.ts +4 -0
  283. package/types/utils/popover/popover-item.d.ts +6 -0
  284. package/types/utils/popover/popover.d.ts +7 -0
  285. package/dist/chunks/messages-CySyfkMU.mjs +0 -48
  286. package/dist/messages-CySyfkMU.mjs +0 -48
@@ -191,6 +191,7 @@ export class BlockOperations {
191
191
  replace = false,
192
192
  tunes,
193
193
  skipYjsSync = false,
194
+ appendToWorkingArea = false,
194
195
  } = options;
195
196
 
196
197
  const targetIndex = index ?? this.currentBlockIndex + (replace ? 0 : 1);
@@ -235,7 +236,7 @@ export class BlockOperations {
235
236
  });
236
237
  }
237
238
 
238
- blocksStore.insert(targetIndex, block, replace);
239
+ blocksStore.insert(targetIndex, block, replace, appendToWorkingArea);
239
240
 
240
241
  /**
241
242
  * Force call of didMutated event on Block insertion
@@ -298,7 +299,7 @@ export class BlockOperations {
298
299
  public insertAtEnd(blocksStore: BlocksStore): Block {
299
300
  this.currentBlockIndexValue = this.repository.length - 1;
300
301
 
301
- return this.insert({}, blocksStore);
302
+ return this.insert({ appendToWorkingArea: true }, blocksStore);
302
303
  }
303
304
 
304
305
  /**
@@ -48,6 +48,8 @@ export interface InsertBlockOptions {
48
48
  tunes?: { [name: string]: BlockTuneData };
49
49
  /** Skip syncing to Yjs (caller handles sync separately) */
50
50
  skipYjsSync?: boolean;
51
+ /** When true, append block to workingArea instead of positioning relative to adjacent blocks */
52
+ appendToWorkingArea?: boolean;
51
53
  }
52
54
 
53
55
  /**
@@ -105,7 +107,7 @@ export interface ConvertBlockOptions {
105
107
  */
106
108
  export type BlocksStore = Blocks & {
107
109
  [index: number]: Block | undefined;
108
- insert(index: number, block: Block, replace?: boolean): void;
110
+ insert(index: number, block: Block, replace?: boolean, appendToWorkingArea?: boolean): void;
109
111
  remove(index: number): void;
110
112
  move(toIndex: number, fromIndex: number, skipDOM?: boolean): void;
111
113
  };
@@ -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,
@@ -0,0 +1,263 @@
1
+ import { generateBlockId } from '../utils/id-generator';
2
+
3
+ /**
4
+ * Minimal block shape expected by the normalizer.
5
+ * Matches the SaverValidatedData shape from the saver pipeline.
6
+ */
7
+ interface NormalizableBlock {
8
+ id?: string;
9
+ tool?: string;
10
+ data?: Record<string, unknown>;
11
+ isValid: boolean;
12
+ parentId?: string | null;
13
+ contentIds?: string[];
14
+ tunes?: Record<string, unknown>;
15
+ time?: number;
16
+ }
17
+
18
+ /**
19
+ * Shape of a table cell within the table's content data.
20
+ */
21
+ interface TableCell {
22
+ blocks: string[];
23
+ }
24
+
25
+ /**
26
+ * Tracking info for a paragraph whose inline images need extraction.
27
+ */
28
+ interface ExtractionInfo {
29
+ parentTableId: string;
30
+ imgSrcs: string[];
31
+ cleanedText: string;
32
+ }
33
+
34
+ /**
35
+ * Regex to match <img> tags and capture their src attribute.
36
+ * Handles both single and double quotes around src value.
37
+ */
38
+ const IMG_TAG_REGEX = /<img\s+[^>]*src=["']([^"']+)["'][^>]*>/g;
39
+
40
+ /**
41
+ * Normalizes inline images in table cell paragraphs by extracting `<img>` tags
42
+ * into standalone image blocks.
43
+ *
44
+ * For each paragraph block whose parent is a table:
45
+ * 1. Extracts all `<img>` tags from the paragraph's text
46
+ * 2. Creates a new image block for each extracted `<img>`
47
+ * 3. Removes the `<img>` tags from the paragraph text
48
+ * 4. Inserts the new image block IDs before the paragraph in the table cell's blocks array
49
+ * 5. Adds the new image block IDs to the table's contentIds
50
+ *
51
+ * @param blocks - array of saved block data
52
+ * @returns new array with inline images extracted into standalone blocks
53
+ */
54
+ export const normalizeInlineImages = <T extends NormalizableBlock>(blocks: T[]): T[] => {
55
+ /**
56
+ * Build a lookup of block id → block for quick parent resolution.
57
+ */
58
+ const blockById = new Map<string, T>();
59
+
60
+ for (const block of blocks) {
61
+ if (block.id !== undefined) {
62
+ blockById.set(block.id, block);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Check if there are any table blocks at all. If not, return input unchanged.
68
+ */
69
+ const hasTable = blocks.some((b) => b.tool === 'table');
70
+
71
+ if (!hasTable) {
72
+ return blocks;
73
+ }
74
+
75
+ const extractionMap = new Map<string, ExtractionInfo>();
76
+
77
+ for (const block of blocks) {
78
+ if (block.tool !== 'paragraph' || block.parentId === undefined || block.parentId === null) {
79
+ continue;
80
+ }
81
+
82
+ const parent = blockById.get(block.parentId);
83
+
84
+ if (parent === undefined || parent.tool !== 'table') {
85
+ continue;
86
+ }
87
+
88
+ const text = block.data?.text;
89
+
90
+ if (typeof text !== 'string') {
91
+ continue;
92
+ }
93
+
94
+ const imgSrcs = Array.from(text.matchAll(IMG_TAG_REGEX), (m) => m[1]);
95
+
96
+ if (imgSrcs.length === 0) {
97
+ continue;
98
+ }
99
+
100
+ /**
101
+ * Remove all <img> tags from text.
102
+ */
103
+ IMG_TAG_REGEX.lastIndex = 0;
104
+ const cleanedText = text.replace(IMG_TAG_REGEX, '');
105
+
106
+ if (block.id !== undefined) {
107
+ extractionMap.set(block.id, {
108
+ parentTableId: block.parentId,
109
+ imgSrcs,
110
+ cleanedText,
111
+ });
112
+ }
113
+ }
114
+
115
+ /**
116
+ * If no paragraphs need extraction, return input unchanged.
117
+ */
118
+ if (extractionMap.size === 0) {
119
+ return blocks;
120
+ }
121
+
122
+ /**
123
+ * Generate image block IDs and build new image blocks.
124
+ * Maps paragraph id → array of new image block entries.
125
+ */
126
+ const newImageBlocksPerParagraph = new Map<string, T[]>();
127
+
128
+ for (const [paragraphId, info] of extractionMap) {
129
+ const imageBlocks: T[] = [];
130
+
131
+ for (const src of info.imgSrcs) {
132
+ const imageBlock = {
133
+ id: generateBlockId(),
134
+ tool: 'image',
135
+ data: { url: src },
136
+ isValid: true,
137
+ parentId: info.parentTableId,
138
+ } as unknown as T;
139
+
140
+ imageBlocks.push(imageBlock);
141
+ }
142
+
143
+ newImageBlocksPerParagraph.set(paragraphId, imageBlocks);
144
+ }
145
+
146
+ /**
147
+ * Clone table blocks and update their content/contentIds with new image block references.
148
+ */
149
+ const updatedTableIds = new Set<string>();
150
+
151
+ for (const info of extractionMap.values()) {
152
+ updatedTableIds.add(info.parentTableId);
153
+ }
154
+
155
+ const clonedTables = new Map<string, T>();
156
+
157
+ for (const tableId of updatedTableIds) {
158
+ const original = blockById.get(tableId);
159
+
160
+ if (original === undefined) {
161
+ continue;
162
+ }
163
+
164
+ const cloned = { ...original };
165
+ const originalData = original.data as { content: TableCell[][] } | undefined;
166
+
167
+ if (originalData?.content !== undefined) {
168
+ /**
169
+ * Deep clone the content array so we can mutate cell blocks arrays.
170
+ */
171
+ const clonedContent: TableCell[][] = originalData.content.map(
172
+ (row) => row.map((cell) => ({ ...cell, blocks: [...cell.blocks] }))
173
+ );
174
+
175
+ cloned.data = { ...original.data, content: clonedContent };
176
+ }
177
+
178
+ cloned.contentIds = original.contentIds !== undefined ? [...original.contentIds] : [];
179
+ clonedTables.set(tableId, cloned);
180
+ }
181
+
182
+ /**
183
+ * Update cloned table cell blocks arrays and contentIds.
184
+ */
185
+ for (const [paragraphId, imageBlocks] of newImageBlocksPerParagraph) {
186
+ const info = extractionMap.get(paragraphId);
187
+
188
+ if (info === undefined) {
189
+ continue;
190
+ }
191
+
192
+ const clonedTable = clonedTables.get(info.parentTableId);
193
+
194
+ if (clonedTable === undefined) {
195
+ continue;
196
+ }
197
+
198
+ const tableData = clonedTable.data as { content: TableCell[][] } | undefined;
199
+
200
+ if (tableData?.content === undefined) {
201
+ continue;
202
+ }
203
+
204
+ const imageBlockIds = imageBlocks.map((b) => b.id ?? '');
205
+
206
+ /**
207
+ * Find the cell containing this paragraph and insert image IDs before the paragraph.
208
+ */
209
+ tableData.content.flat().forEach((cell) => {
210
+ const paragraphIndex = cell.blocks.indexOf(paragraphId);
211
+
212
+ if (paragraphIndex !== -1) {
213
+ cell.blocks.splice(paragraphIndex, 0, ...imageBlockIds);
214
+ }
215
+ });
216
+
217
+ /**
218
+ * Add image block IDs to the table's contentIds.
219
+ */
220
+ if (clonedTable.contentIds !== undefined) {
221
+ clonedTable.contentIds.push(...imageBlockIds);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Build the result array:
227
+ * - Replace table blocks with cloned versions
228
+ * - Replace paragraph blocks with cleaned text versions
229
+ * - Insert image blocks before their source paragraph
230
+ */
231
+ const result: T[] = [];
232
+
233
+ for (const block of blocks) {
234
+ /**
235
+ * If this is a table that was updated, use the cloned version.
236
+ */
237
+ if (block.id !== undefined && clonedTables.has(block.id)) {
238
+ result.push(clonedTables.get(block.id) as T);
239
+ continue;
240
+ }
241
+
242
+ /**
243
+ * If this is a paragraph with images to extract, insert image blocks before it
244
+ * and update its text.
245
+ */
246
+ if (block.id !== undefined && extractionMap.has(block.id)) {
247
+ result.push(...(newImageBlocksPerParagraph.get(block.id) ?? []));
248
+
249
+ const info = extractionMap.get(block.id);
250
+ const updatedParagraph = {
251
+ ...block,
252
+ data: { ...block.data, text: info?.cleanedText ?? '' },
253
+ };
254
+
255
+ result.push(updatedParagraph);
256
+ continue;
257
+ }
258
+
259
+ result.push(block);
260
+ }
261
+
262
+ return result;
263
+ };
@@ -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
  */
@@ -140,11 +140,14 @@ export class RectangleSelection extends Module {
140
140
  const pointerX = pageX - scrollLeft;
141
141
 
142
142
  /**
143
- * Check if pointer is within editor's horizontal bounds.
143
+ * Check if pointer is within the content area's horizontal bounds.
144
144
  * This determines whether we should close the toolbar when starting selection.
145
- * Clicks outside the horizontal bounds (to the left or right) should NOT close the toolbar.
145
+ * Clicks outside the content area (to the left or right, in the margin/toolbar zone)
146
+ * should NOT close the toolbar. Uses UI.contentRect which queries the first block's
147
+ * content element and caches the result.
146
148
  */
147
- const withinEditorHorizontally = pointerX >= editorRect.left && pointerX <= editorRect.right;
149
+ const contentRect = this.Blok.UI.contentRect;
150
+ const withinEditorHorizontally = pointerX >= contentRect.left && pointerX <= contentRect.right;
148
151
 
149
152
  const elemWhereSelectionStart = document.elementFromPoint(pageX - scrollLeft, pointerY);
150
153
 
@@ -224,6 +227,19 @@ export class RectangleSelection extends Module {
224
227
  this.isRectSelectionActivated = false;
225
228
  }
226
229
 
230
+ /**
231
+ * Cancel active rectangle selection.
232
+ * Used when another selection system (e.g., table cell selection) takes priority.
233
+ */
234
+ public cancelActiveSelection(): void {
235
+ if (!this.mousedown && !this.isRectSelectionActivated) {
236
+ return;
237
+ }
238
+
239
+ this.clearSelection();
240
+ this.endSelection();
241
+ }
242
+
227
243
  /**
228
244
  * Sets Module necessary event handlers
229
245
  */
@@ -12,6 +12,7 @@ import type { Block } from '../block';
12
12
  import { getBlokVersion, isEmpty, isObject, log, logLabeled } from '../utils';
13
13
  import { collapseToLegacy, shouldCollapseToLegacy } from '../utils/data-model-transform';
14
14
  import { sanitizeBlocks } from '../utils/sanitizer';
15
+ import { normalizeInlineImages } from './normalizeInlineImages';
15
16
 
16
17
  type SaverValidatedData = ValidatedData & {
17
18
  tunes?: Record<string, BlockTuneData>;
@@ -75,7 +76,9 @@ export class Saver extends Module {
75
76
  this.config.sanitizer as SanitizerConfig
76
77
  );
77
78
 
78
- return this.makeOutput(sanitizedData);
79
+ const normalizedData = normalizeInlineImages(sanitizedData);
80
+
81
+ return this.makeOutput(normalizedData);
79
82
  } catch (error: unknown) {
80
83
  this.lastSaveError = error;
81
84
 
@@ -125,7 +128,9 @@ export class Saver extends Module {
125
128
  const extractedBlocks: OutputData['blocks'] = [];
126
129
 
127
130
  allExtractedData.forEach(({ id, tool, data, tunes, isValid, parentId, contentIds }) => {
128
- if (!isValid) {
131
+ const hasParent = parentId !== undefined && parentId !== null;
132
+
133
+ if (!isValid && !hasParent) {
129
134
  log(`Block «${tool}» skipped because saved data is invalid`);
130
135
 
131
136
  return;
@@ -151,7 +156,6 @@ export class Saver extends Module {
151
156
  }
152
157
 
153
158
  const isTunesEmpty = tunes === undefined || isEmpty(tunes);
154
- const hasParent = parentId !== undefined && parentId !== null;
155
159
  const hasContent = contentIds !== undefined && contentIds.length > 0;
156
160
 
157
161
  const output: OutputData['blocks'][number] = {
@@ -5,7 +5,7 @@ import { BlockAPI } from '../../block/api';
5
5
  import { Dom as $ } from '../../dom';
6
6
  import { BlockSettingsClosed, BlockSettingsOpened, BlokMobileLayoutToggled } from '../../events';
7
7
  import { Flipper } from '../../flipper';
8
- import { IconReplace, IconCross } from '../../icons';
8
+ import { IconReplace, IconTrash } from '../../icons';
9
9
  import { SelectionUtils } from '../../selection/index';
10
10
  import type { BlockToolAdapter } from '../../tools/block';
11
11
  import { isMobileScreen, keyCodes } from '../../utils';
@@ -187,7 +187,6 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
187
187
  trigger: trigger || this.nodes.wrapper,
188
188
  items: await this.getTunesItems(block, commonTunes, toolTunes),
189
189
  scopeElement: this.Blok.API.methods.ui.nodes.redactor,
190
- width: 'auto',
191
190
  messages: {
192
191
  nothingFound: this.Blok.I18n.t('popover.nothingFound'),
193
192
  search: this.Blok.I18n.t('popover.search'),
@@ -375,9 +374,10 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
375
374
  items.push(...commonTunes);
376
375
  } else {
377
376
  items.push({
378
- icon: IconCross,
377
+ icon: IconTrash,
379
378
  title: this.Blok.I18n.t('blockSettings.delete'),
380
379
  name: 'delete',
380
+ isDestructive: true,
381
381
  closeOnActivate: true,
382
382
  onActivate: () => {
383
383
  const { BlockManager, Caret, Toolbar } = this.Blok;
@@ -85,6 +85,13 @@ export class Toolbar extends Module<ToolbarNodes> {
85
85
  */
86
86
  private explicitlyClosed: boolean = false;
87
87
 
88
+ /**
89
+ * Flag to track if the current hovered block was resolved from a table cell block.
90
+ * When true, the toolbar suppresses plus button, settings toggler, and
91
+ * prevents overriding the current block when the toolbox opens.
92
+ */
93
+ private hoveredBlockIsFromTableCell: boolean = false;
94
+
88
95
  /**
89
96
  * Toolbox class instance
90
97
  * It will be created in requestIdleCallback so it can be null in some period of time
@@ -215,8 +222,10 @@ export class Toolbar extends Module<ToolbarNodes> {
215
222
 
216
223
  /**
217
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).
218
227
  */
219
- if (this.hoveredBlock) {
228
+ if (this.hoveredBlock && !this.hoveredBlockIsFromTableCell) {
220
229
  this.Blok.BlockManager.currentBlock = this.hoveredBlock;
221
230
  }
222
231
 
@@ -320,21 +329,20 @@ export class Toolbar extends Module<ToolbarNodes> {
320
329
  /**
321
330
  * If no one Block selected as a Current
322
331
  */
323
- const targetBlock = block ?? this.Blok.BlockManager.currentBlock;
332
+ const unresolvedBlock = block ?? this.Blok.BlockManager.currentBlock;
324
333
 
325
- if (!targetBlock) {
334
+ if (!unresolvedBlock) {
326
335
  return;
327
336
  }
328
337
 
329
338
  /**
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.
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.
332
342
  */
333
- if (targetBlock.holder.closest('[data-blok-table-cell-blocks]')) {
334
- this.close();
343
+ this.hoveredBlockIsFromTableCell = unresolvedBlock.holder.closest('[data-blok-table-cell-blocks]') !== null;
335
344
 
336
- return;
337
- }
345
+ const targetBlock = this.resolveTableCellBlock(unresolvedBlock);
338
346
 
339
347
  /** Clean up draggable on previous block if any */
340
348
  if (this.hoveredBlock && this.hoveredBlock !== targetBlock) {
@@ -353,6 +361,19 @@ export class Toolbar extends Module<ToolbarNodes> {
353
361
  return;
354
362
  }
355
363
 
364
+ /**
365
+ * Suppress toolbar buttons when the block is inside a table cell.
366
+ * The toolbar still positions itself for toolbox/slash-search purposes,
367
+ * but plus button and settings toggler remain hidden.
368
+ */
369
+ const displayValue = this.hoveredBlockIsFromTableCell ? 'none' : '';
370
+
371
+ plusButton.style.display = displayValue;
372
+
373
+ if (settingsToggler) {
374
+ settingsToggler.style.display = displayValue;
375
+ }
376
+
356
377
  const targetBlockHolder = targetBlock.holder;
357
378
  const { isMobile } = this.Blok.UI;
358
379
 
@@ -458,12 +479,22 @@ export class Toolbar extends Module<ToolbarNodes> {
458
479
  this.positioner.setHoveredTarget(null); // No target for multi-block selection
459
480
  this.positioner.resetCachedPosition(); // Reset cached position when moving to a new block
460
481
 
461
- const { wrapper, plusButton } = this.nodes;
482
+ const { wrapper, plusButton, settingsToggler } = this.nodes;
462
483
 
463
484
  if (!wrapper || !plusButton) {
464
485
  return;
465
486
  }
466
487
 
488
+ /**
489
+ * Restore plus button and settings toggler visibility for multi-block selection,
490
+ * in case they were hidden for table cell blocks.
491
+ */
492
+ plusButton.style.display = '';
493
+
494
+ if (settingsToggler) {
495
+ settingsToggler.style.display = '';
496
+ }
497
+
467
498
  const targetBlockHolder = targetBlock.holder;
468
499
 
469
500
  const toolbarY = this.positioner.calculateToolbarY(
@@ -478,9 +509,6 @@ export class Toolbar extends Module<ToolbarNodes> {
478
509
  this.positioner.moveToY(this.nodes, toolbarY);
479
510
  targetBlockHolder.appendChild(wrapper);
480
511
 
481
- /** Set up draggable on the target block using the settings toggler as drag handle */
482
- const { settingsToggler } = this.nodes;
483
-
484
512
  if (settingsToggler && !this.Blok.ReadOnly.isEnabled) {
485
513
  targetBlock.setupDraggable(settingsToggler, this.Blok.DragManager);
486
514
  }
@@ -523,18 +551,24 @@ export class Toolbar extends Module<ToolbarNodes> {
523
551
  * to prevent toolbar from reopening on subsequent block-hovered events
524
552
  */
525
553
  this.hoveredBlock = null;
554
+ this.hoveredBlockIsFromTableCell = false;
526
555
  // Only set explicitlyClosed if not explicitly disabled (e.g., when called from toolbox after block insertion)
527
556
  if (options?.setExplicitlyClosed !== false) {
528
557
  this.explicitlyClosed = true;
529
558
  }
530
559
 
531
560
  /**
532
- * Restore plus button visibility in case it was hidden by other interactions
561
+ * Restore plus button and settings toggler visibility
562
+ * in case they were hidden for table cell blocks
533
563
  */
534
564
  if (this.nodes.plusButton) {
535
565
  this.nodes.plusButton.style.display = '';
536
566
  }
537
567
 
568
+ if (this.nodes.settingsToggler) {
569
+ this.nodes.settingsToggler.style.display = '';
570
+ }
571
+
538
572
  /**
539
573
  * Reset the content offset transform
540
574
  */
@@ -562,6 +596,30 @@ export class Toolbar extends Module<ToolbarNodes> {
562
596
  this.explicitlyClosed = false;
563
597
  }
564
598
 
599
+ /**
600
+ * If the block is inside a table cell, resolve to the parent table block.
601
+ * This ensures the toolbar shows for the table when clicking/focusing inside cells.
602
+ * Uses the DOM attribute directly to avoid cross-module dependency on the table tool.
603
+ *
604
+ * @param block - the block to resolve
605
+ * @returns the parent table block if inside a cell, the original block otherwise
606
+ */
607
+ private resolveTableCellBlock(block: Block): Block {
608
+ const cellBlocksContainer = block.holder.closest('[data-blok-table-cell-blocks]');
609
+
610
+ if (!cellBlocksContainer) {
611
+ return block;
612
+ }
613
+
614
+ const tableBlockHolder = cellBlocksContainer.closest('[data-blok-testid="block-wrapper"]');
615
+
616
+ if (!tableBlockHolder) {
617
+ return block;
618
+ }
619
+
620
+ return this.Blok.BlockManager.getBlockByChildNode(tableBlockHolder) ?? block;
621
+ }
622
+
565
623
  /**
566
624
  * Reset the Toolbar position to prevent DOM height growth, for example after blocks deletion
567
625
  */