@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
@@ -1,6 +1,7 @@
1
1
  import type { API } from '../../../types';
2
2
 
3
3
  import { CELL_ATTR, ROW_ATTR } from './table-core';
4
+ import type { TableModel } from './table-model';
4
5
  import type { LegacyCellContent, CellContent } from './types';
5
6
  import { isCellWithBlocks } from './types';
6
7
 
@@ -35,7 +36,10 @@ interface TableCellBlocksOptions {
35
36
  api: API;
36
37
  gridElement: HTMLElement;
37
38
  tableBlockId: string;
39
+ model: TableModel;
38
40
  onNavigateToCell?: CellNavigationCallback;
41
+ /** When true, handleBlockMutation defers events instead of processing immediately. */
42
+ isStructuralOpActive?: () => boolean;
39
43
  }
40
44
 
41
45
  /**
@@ -46,6 +50,7 @@ export class TableCellBlocks {
46
50
  private api: API;
47
51
  private gridElement: HTMLElement;
48
52
  private tableBlockId: string;
53
+ private model: TableModel;
49
54
  private _activeCellWithBlocks: CellPosition | null = null;
50
55
  private onNavigateToCell?: CellNavigationCallback;
51
56
 
@@ -59,18 +64,35 @@ export class TableCellBlocks {
59
64
  private pendingCheckScheduled = false;
60
65
 
61
66
  /**
62
- * Maps a flat-list block index to the cell it was in when block-removed fired.
63
- * Used during replace operations: block-removed fires while the holder is still
64
- * in the cell DOM, then block-added fires at the same index after the holder
65
- * has been removed. This map lets the block-added handler find the correct cell.
67
+ * Maps a removed block's ID to the cell it was in and its flat-list index
68
+ * when block-removed fired.
69
+ *
70
+ * Used during replace operations: block-removed fires while the holder is
71
+ * still in the cell DOM, then block-added fires at the same index after the
72
+ * holder has been removed. This map lets the block-added handler find the
73
+ * correct cell.
74
+ *
75
+ * Keyed by block ID (not flat index) to prevent two classes of bugs:
76
+ * - Non-replace deletion + coincidental same-index insertion claiming the
77
+ * wrong cell.
78
+ * - Cross-table interference when two TableCellBlocks instances both
79
+ * subscribe to the global "block changed" event.
66
80
  */
67
- private removedBlockCells = new Map<number, HTMLElement>();
81
+ private removedBlockCells = new Map<string, { cell: HTMLElement; index: number }>();
82
+
83
+ /** Callback to check if a structural operation is active on the parent Table. */
84
+ private isStructuralOpActive: () => boolean;
85
+
86
+ /** Events deferred during structural operations, replayed or discarded afterward. */
87
+ private deferredEvents: Array<unknown> = [];
68
88
 
69
89
  constructor(options: TableCellBlocksOptions) {
70
90
  this.api = options.api;
71
91
  this.gridElement = options.gridElement;
72
92
  this.tableBlockId = options.tableBlockId;
93
+ this.model = options.model;
73
94
  this.onNavigateToCell = options.onNavigateToCell;
95
+ this.isStructuralOpActive = options.isStructuralOpActive ?? (() => false);
74
96
 
75
97
  this.api.events.on('block changed', this.handleBlockMutation);
76
98
  this.gridElement.addEventListener('click', this.handleCellBlankSpaceClick);
@@ -260,19 +282,48 @@ export class TableCellBlocks {
260
282
  return;
261
283
  }
262
284
 
263
- const mountedIds = isCellWithBlocks(cellContent)
264
- ? this.mountBlocksInCell(container, cellContent.blocks)
285
+ const referencedBlockIds = isCellWithBlocks(cellContent) && cellContent.blocks.length > 0
286
+ ? [...cellContent.blocks]
287
+ : null;
288
+ const mountedIds = referencedBlockIds
289
+ ? this.mountBlocksInCell(container, referencedBlockIds)
265
290
  : [];
266
291
 
292
+ const cellColorProps: Pick<CellContent, 'color' | 'textColor'> = {};
293
+
294
+ if (isCellWithBlocks(cellContent)) {
295
+ if (cellContent.color !== undefined) {
296
+ cellColorProps.color = cellContent.color;
297
+ }
298
+ if (cellContent.textColor !== undefined) {
299
+ cellColorProps.textColor = cellContent.textColor;
300
+ }
301
+ }
302
+
267
303
  if (mountedIds.length > 0) {
268
- normalizedRow.push({ blocks: mountedIds });
304
+ normalizedRow.push({ blocks: referencedBlockIds ?? mountedIds, ...cellColorProps });
269
305
  } else {
270
- const text = typeof cellContent === 'string' ? cellContent : '';
271
- const block = this.api.blocks.insert('paragraph', { text }, {}, this.api.blocks.getBlocksCount(), false);
272
-
273
- container.appendChild(block.holder);
274
- this.api.blocks.setBlockParent(block.id, this.tableBlockId);
275
- normalizedRow.push({ blocks: [block.id] });
306
+ const text = typeof cellContent === 'string'
307
+ ? cellContent
308
+ : (cellContent.text ?? '');
309
+ const segments = text.split(/<br\s*\/?>/i).map(s => s.trim()).filter(Boolean);
310
+ const textsToInsert = segments.length > 0 ? segments : [text];
311
+ const ids: string[] = [];
312
+
313
+ for (const segmentText of textsToInsert) {
314
+ const block = this.api.blocks.insert('paragraph', { text: segmentText }, {}, this.api.blocks.getBlocksCount(), false);
315
+
316
+ container.appendChild(block.holder);
317
+ this.api.blocks.setBlockParent(block.id, this.tableBlockId);
318
+ ids.push(block.id);
319
+ }
320
+
321
+ normalizedRow.push({
322
+ blocks: referencedBlockIds === null
323
+ ? ids
324
+ : [...referencedBlockIds, ...ids],
325
+ ...cellColorProps,
326
+ });
276
327
  }
277
328
 
278
329
  this.stripPlaceholders(container);
@@ -347,6 +398,12 @@ export class TableCellBlocks {
347
398
  return;
348
399
  }
349
400
 
401
+ // Guard against circular DOM: never append the table block's own holder
402
+ // into one of its descendant cell containers.
403
+ if (block.holder.contains(container)) {
404
+ return;
405
+ }
406
+
350
407
  container.appendChild(block.holder);
351
408
  this.api.blocks.setBlockParent(blockId, this.tableBlockId);
352
409
  this.stripPlaceholders(container);
@@ -408,6 +465,7 @@ export class TableCellBlocks {
408
465
 
409
466
  container.appendChild(block.holder);
410
467
  this.api.blocks.setBlockParent(block.id, this.tableBlockId);
468
+ this.syncBlockToModel(cell, block.id);
411
469
  this.stripPlaceholders(container);
412
470
  }
413
471
 
@@ -417,6 +475,12 @@ export class TableCellBlocks {
417
475
  * When a block is removed, ensure no cell is left empty.
418
476
  */
419
477
  private handleBlockMutation = (data: unknown): void => {
478
+ if (this.isStructuralOpActive()) {
479
+ this.deferredEvents.push(data);
480
+
481
+ return;
482
+ }
483
+
420
484
  if (!this.isBlockMutationEvent(data)) {
421
485
  return;
422
486
  }
@@ -424,22 +488,7 @@ export class TableCellBlocks {
424
488
  const { type, detail } = data.event;
425
489
 
426
490
  if (type === 'block-removed') {
427
- // Record which cell the removed block was in (holder is still in cell DOM
428
- // at this point during a replace operation). This lets the subsequent
429
- // block-added handler find the correct cell even when no adjacent block
430
- // remains in the cell.
431
- this.recordRemovedBlockCell(detail);
432
-
433
- // Schedule deferred empty-cell checks instead of running immediately.
434
- // This avoids creating spurious paragraphs during BlockManager.replace(),
435
- // where block-removed is immediately followed by block-added.
436
- const cells = this.gridElement.querySelectorAll<HTMLElement>(`[${CELL_ATTR}]`);
437
-
438
- cells.forEach(cell => {
439
- this.cellsPendingCheck.add(cell);
440
- });
441
-
442
- this.schedulePendingCellCheck();
491
+ this.handleBlockRemoved(detail);
443
492
 
444
493
  return;
445
494
  }
@@ -448,6 +497,13 @@ export class TableCellBlocks {
448
497
  return;
449
498
  }
450
499
 
500
+ // Never claim the table block itself as a cell content block.
501
+ // This can happen when rendered() creates cell blocks synchronously,
502
+ // polluting currentBlockIndex before the table's own block-added fires.
503
+ if (detail.target.id === this.tableBlockId) {
504
+ return;
505
+ }
506
+
451
507
  const blockIndex = detail.index;
452
508
 
453
509
  if (blockIndex === undefined) {
@@ -456,16 +512,20 @@ export class TableCellBlocks {
456
512
 
457
513
  // Check if a block was just removed at this index (replace operation).
458
514
  // Use the recorded cell so the replacement lands in the correct cell.
459
- const removedCell = this.removedBlockCells.get(blockIndex);
460
-
461
- this.removedBlockCells.delete(blockIndex);
515
+ // The map is keyed by the removed block's ID, so we iterate to find an
516
+ // entry whose stored index matches the newly added block's index.
517
+ const removedEntry = this.findRemovedEntryForIndex(blockIndex);
462
518
 
463
519
  // For replace operations, always move the block to the recorded cell.
464
520
  // blocksStore.insert() places the holder adjacent to the previous block
465
521
  // in the DOM, which may be inside a different cell.
466
- if (removedCell) {
467
- this.claimBlockForCell(removedCell, detail.target.id);
468
- this.cellsPendingCheck.delete(removedCell);
522
+ // Guard: verify the new block actually belongs to this table by checking
523
+ // that an adjacent block is either the table block itself or a block
524
+ // mounted inside this table's grid.
525
+ if (removedEntry && this.isAdjacentToThisTable(blockIndex)) {
526
+ this.claimBlockForCell(removedEntry.cell, detail.target.id);
527
+ this.syncBlockToModel(removedEntry.cell, detail.target.id);
528
+ this.cellsPendingCheck.delete(removedEntry.cell);
469
529
 
470
530
  return;
471
531
  }
@@ -477,6 +537,50 @@ export class TableCellBlocks {
477
537
 
478
538
  if (existingContainer) {
479
539
  this.stripPlaceholders(existingContainer);
540
+ }
541
+
542
+ // Sync to model if holder landed in a cell but isn't tracked yet (e.g. toolbox conversion)
543
+ const untrackedCell = existingContainer && !this.model.findCellForBlock(detail.target.id)
544
+ ? existingContainer.closest<HTMLElement>(`[${CELL_ATTR}]`)
545
+ : null;
546
+
547
+ if (untrackedCell) {
548
+ this.syncBlockToModel(untrackedCell, detail.target.id);
549
+ }
550
+
551
+ if (existingContainer) {
552
+ return;
553
+ }
554
+
555
+ // Only claim blocks whose holder is inside this table's grid.
556
+ // Blocks placed outside the grid (e.g., via undo/restore or API inserts)
557
+ // should not be pulled into a cell by adjacency alone.
558
+ //
559
+ // However, blocks created while the editor's focus is inside this table
560
+ // (e.g., via Enter key or paste in a cell) land outside the grid because
561
+ // insertToDOM walks up from cell blocks to the table holder level.
562
+ // For those, check that the current block at the time of insertion belongs
563
+ // to this table — indicating the user was editing inside a cell.
564
+ if (!this.gridElement.contains(holder)) {
565
+ const currentIndex = this.api.blocks.getCurrentBlockIndex();
566
+ const currentBlock = currentIndex >= 0
567
+ ? this.api.blocks.getBlockByIndex(currentIndex)
568
+ : null;
569
+ const currentBlockInOurTable = currentBlock !== null
570
+ && currentBlock !== undefined
571
+ && this.getOwnedCellForBlock(currentBlock.id) !== null;
572
+
573
+ if (!currentBlockInOurTable) {
574
+ return;
575
+ }
576
+
577
+ const cell = this.findCellForNewBlock(blockIndex);
578
+
579
+ if (cell) {
580
+ this.claimBlockForCell(cell, detail.target.id);
581
+ this.syncBlockToModel(cell, detail.target.id);
582
+ this.cellsPendingCheck.delete(cell);
583
+ }
480
584
 
481
585
  return;
482
586
  }
@@ -486,16 +590,79 @@ export class TableCellBlocks {
486
590
 
487
591
  if (cell) {
488
592
  this.claimBlockForCell(cell, detail.target.id);
593
+ this.syncBlockToModel(cell, detail.target.id);
489
594
  this.cellsPendingCheck.delete(cell);
490
595
  }
491
596
  };
492
597
 
598
+ /**
599
+ * Handle a block-removed event: update the model and schedule an empty-cell check.
600
+ */
601
+ private handleBlockRemoved(detail: { target: { id: string; holder: HTMLElement }; index?: number }): void {
602
+ this.recordRemovedBlockCell(detail);
603
+ const blockId = detail.target.id;
604
+ const cellPos = this.model.findCellForBlock(blockId);
605
+
606
+ if (!cellPos) {
607
+ this.schedulePendingCellCheck();
608
+
609
+ return;
610
+ }
611
+
612
+ this.model.removeBlockFromCell(cellPos.row, cellPos.col, blockId);
613
+ const affectedCell = this.getCell(cellPos.row, cellPos.col);
614
+
615
+ if (affectedCell) {
616
+ this.cellsPendingCheck.add(affectedCell);
617
+ }
618
+
619
+ this.schedulePendingCellCheck();
620
+ }
621
+
622
+ /**
623
+ * Find the DOM cell's row/col position and add the block to the model.
624
+ */
625
+ private syncBlockToModel(cell: HTMLElement, blockId: string): void {
626
+ const pos = this.getCellPosition(cell);
627
+
628
+ if (pos) {
629
+ this.model.addBlockToCell(pos.row, pos.col, blockId);
630
+ }
631
+ }
632
+
633
+ /**
634
+ * Get the row/col position of a cell element within the grid.
635
+ */
636
+ private getCellPosition(cell: HTMLElement): { row: number; col: number } | null {
637
+ const row = cell.closest<HTMLElement>(`[${ROW_ATTR}]`);
638
+
639
+ if (!row) {
640
+ return null;
641
+ }
642
+
643
+ const rows = Array.from(this.gridElement.querySelectorAll(`[${ROW_ATTR}]`));
644
+ const rowIndex = rows.indexOf(row);
645
+
646
+ if (rowIndex < 0) {
647
+ return null;
648
+ }
649
+
650
+ const cells = Array.from(row.querySelectorAll(`[${CELL_ATTR}]`));
651
+ const colIndex = cells.indexOf(cell);
652
+
653
+ if (colIndex < 0) {
654
+ return null;
655
+ }
656
+
657
+ return { row: rowIndex, col: colIndex };
658
+ }
659
+
493
660
  /**
494
661
  * If the removed block's holder is currently inside a cell of this table,
495
662
  * record the mapping so a subsequent block-added at the same index can
496
663
  * find the correct cell.
497
664
  */
498
- private recordRemovedBlockCell(detail: { target: { holder: HTMLElement }; index?: number }): void {
665
+ private recordRemovedBlockCell(detail: { target: { id: string; holder: HTMLElement }; index?: number }): void {
499
666
  if (detail.index === undefined) {
500
667
  return;
501
668
  }
@@ -503,10 +670,118 @@ export class TableCellBlocks {
503
670
  const cell = detail.target.holder.closest<HTMLElement>(`[${CELL_ATTR}]`);
504
671
 
505
672
  if (cell && this.gridElement.contains(cell)) {
506
- this.removedBlockCells.set(detail.index, cell);
673
+ this.removedBlockCells.set(detail.target.id, { cell, index: detail.index });
507
674
  }
508
675
  }
509
676
 
677
+ /**
678
+ * Find a removedBlockCells entry whose stored index matches the given block index.
679
+ * Removes and returns the first match, or null if none found.
680
+ */
681
+ private findRemovedEntryForIndex(blockIndex: number): { cell: HTMLElement; index: number } | null {
682
+ for (const [removedId, entry] of this.removedBlockCells) {
683
+ if (entry.index === blockIndex) {
684
+ this.removedBlockCells.delete(removedId);
685
+
686
+ return entry;
687
+ }
688
+ }
689
+
690
+ return null;
691
+ }
692
+
693
+ /**
694
+ * Resolve a block id to a cell only when this table model explicitly tracks
695
+ * that block in one of this table's cells.
696
+ */
697
+ private getOwnedCellForBlock(blockId: string): HTMLElement | null {
698
+ const cellPos = this.model.findCellForBlock(blockId);
699
+
700
+ if (!cellPos) {
701
+ return null;
702
+ }
703
+
704
+ const cell = this.getCell(cellPos.row, cellPos.col);
705
+
706
+ return cell && this.gridElement.contains(cell) ? cell : null;
707
+ }
708
+
709
+ /**
710
+ * Check whether a block at the given flat-list index belongs to this table's
711
+ * block range. Used to prevent cross-table interference when two
712
+ * TableCellBlocks instances both subscribe to the global "block changed" event.
713
+ *
714
+ * Returns true if:
715
+ * - An adjacent block (index-1 or index+1) is mounted inside a cell of this
716
+ * table's grid, OR
717
+ * - The table block is immediately before this index AND either the index
718
+ * after is also within this table (block in a cell) or does not exist
719
+ * (the new block is the last in the flat list).
720
+ *
721
+ * The table block alone being adjacent is NOT sufficient — a second table
722
+ * could immediately follow this one in the flat list, making the table
723
+ * block adjacent to BOTH tables' blocks.
724
+ */
725
+ private isAdjacentToThisTable(blockIndex: number): boolean {
726
+ const blocksCount = this.api.blocks.getBlocksCount();
727
+
728
+ // Check if any adjacent block is explicitly tracked by this table model.
729
+ for (const offset of [-1, 1]) {
730
+ const adjacentIndex = blockIndex + offset;
731
+
732
+ if (adjacentIndex < 0 || adjacentIndex >= blocksCount) {
733
+ continue;
734
+ }
735
+
736
+ const block = this.api.blocks.getBlockByIndex(adjacentIndex);
737
+
738
+ if (!block) {
739
+ continue;
740
+ }
741
+
742
+ if (this.getOwnedCellForBlock(block.id)) {
743
+ return true;
744
+ }
745
+ }
746
+
747
+ // For single-block-in-cell tables: the table block is at index-1 and
748
+ // either (a) no block follows at index+1, or (b) the block at index+1
749
+ // is inside this table's grid. This avoids matching blocks that belong
750
+ // to a different table immediately following this one.
751
+ if (!this.isTableBlockAtPrevIndex(blockIndex)) {
752
+ return false;
753
+ }
754
+
755
+ const nextIndex = blockIndex + 1;
756
+
757
+ if (nextIndex >= blocksCount) {
758
+ return true;
759
+ }
760
+
761
+ const nextBlock = this.api.blocks.getBlockByIndex(nextIndex);
762
+
763
+ if (!nextBlock) {
764
+ return true;
765
+ }
766
+
767
+ return this.getOwnedCellForBlock(nextBlock.id) !== null;
768
+ }
769
+
770
+ /**
771
+ * Check if this table's block is at the index immediately before the given index.
772
+ */
773
+ private isTableBlockAtPrevIndex(blockIndex: number): boolean {
774
+ const prevIndex = blockIndex - 1;
775
+
776
+ if (prevIndex < 0) {
777
+ return false;
778
+ }
779
+
780
+ const prevBlock = this.api.blocks.getBlockByIndex(prevIndex);
781
+
782
+ return prevBlock?.id === this.tableBlockId;
783
+ }
784
+
510
785
  /**
511
786
  * Schedule a microtask to run ensureCellHasBlock for all cells still pending.
512
787
  * If a block-added event removes a cell from the pending set before the microtask runs,
@@ -661,5 +936,28 @@ export class TableCellBlocks {
661
936
  this._activeCellWithBlocks = null;
662
937
  this.cellsPendingCheck.clear();
663
938
  this.removedBlockCells.clear();
939
+ this.deferredEvents.length = 0;
940
+ }
941
+
942
+ /**
943
+ * Replay all deferred events. Called after interactive structural ops
944
+ * (add/delete/move row/col) complete so block lifecycle events are processed.
945
+ */
946
+ public flushDeferredEvents(): void {
947
+ const events = [...this.deferredEvents];
948
+
949
+ this.deferredEvents.length = 0;
950
+
951
+ for (const data of events) {
952
+ this.handleBlockMutation(data);
953
+ }
954
+ }
955
+
956
+ /**
957
+ * Discard all deferred events. Called after full-rebuild ops (setData, onPaste)
958
+ * where the entire grid is replaced and old events are meaningless.
959
+ */
960
+ public discardDeferredEvents(): void {
961
+ this.deferredEvents.length = 0;
664
962
  }
665
963
  }