@jackuait/blok 0.6.0-beta.13 → 0.6.0-beta.14

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 (246) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-DzeTRJ_i.mjs → blok-Cl30YOLl.mjs} +1398 -1132
  3. package/dist/chunks/{i18next-loader-CgW4H90H.mjs → i18next-loader-DbTDlMux.mjs} +1 -1
  4. package/dist/chunks/{index-BWKk7PIS.mjs → index-DJmaYswj.mjs} +1 -1
  5. package/dist/chunks/{inline-tool-convert-DrSwadw_.mjs → inline-tool-convert-MjSaP8r7.mjs} +161 -145
  6. package/dist/{messages-B1Aww8q7.mjs → chunks/messages-1mYyrppy.mjs} +1 -0
  7. package/dist/chunks/{messages-DnIhyAJk.mjs → messages-4gHfbcDp.mjs} +1 -0
  8. package/dist/{messages-CQwpzUFp.mjs → chunks/messages-6xYv-U8A.mjs} +1 -0
  9. package/dist/chunks/{messages-CMkNSDTo.mjs → messages-7jqF79FO.mjs} +1 -0
  10. package/dist/chunks/{messages-BSbjsyHY.mjs → messages-B3EUuA7o.mjs} +1 -0
  11. package/dist/{messages-LPVfA-8K.mjs → chunks/messages-B6srtRn7.mjs} +1 -0
  12. package/dist/chunks/{messages-DJDG55Vq.mjs → messages-B85OIs0E.mjs} +1 -0
  13. package/dist/{messages-_ErNTNhk.mjs → chunks/messages-BCYBBG_u.mjs} +1 -0
  14. package/dist/{messages-CZygwLwM.mjs → chunks/messages-BDLNPiaW.mjs} +1 -0
  15. package/dist/{messages-C2htQ_3F.mjs → chunks/messages-BKpNd1Zb.mjs} +1 -0
  16. package/dist/chunks/{messages-CnwibSvh.mjs → messages-BLFUid_o.mjs} +1 -0
  17. package/dist/{messages-CVw84KdI.mjs → chunks/messages-BNPTVKnc.mjs} +1 -0
  18. package/dist/{messages-BMv4xwIr.mjs → chunks/messages-BQPXSkri.mjs} +1 -0
  19. package/dist/{messages-Dz9L52ol.mjs → chunks/messages-BQu6VNDq.mjs} +1 -0
  20. package/dist/{messages-DprmQg6V.mjs → chunks/messages-BRodFKUo.mjs} +1 -0
  21. package/dist/{messages-CznZadDf.mjs → chunks/messages-BWIikA8H.mjs} +1 -0
  22. package/dist/{messages-CqWJcCbY.mjs → chunks/messages-BXQvMilx.mjs} +1 -0
  23. package/dist/chunks/{messages-Z9nEU2xK.mjs → messages-BZ92luiw.mjs} +1 -0
  24. package/dist/chunks/{messages-BC86qLvI.mjs → messages-Bg0Phlcj.mjs} +1 -0
  25. package/dist/chunks/{messages-DnXLrlHh.mjs → messages-Bh6RBIu5.mjs} +1 -0
  26. package/dist/chunks/{messages-DhLKYm2j.mjs → messages-BnFHdIpg.mjs} +1 -0
  27. package/dist/{messages-BU2nlrLK.mjs → chunks/messages-Bq82EVsw.mjs} +1 -0
  28. package/dist/chunks/{messages-Diu6jAaR.mjs → messages-BxbcTiHh.mjs} +1 -0
  29. package/dist/{messages-BoJc_p1r.mjs → chunks/messages-ByO6bSV2.mjs} +1 -0
  30. package/dist/chunks/{messages-BrPFGbM-.mjs → messages-ByUNPiQ4.mjs} +1 -0
  31. package/dist/chunks/{messages-BB5z9Uba.mjs → messages-Byw0EHyf.mjs} +1 -0
  32. package/dist/chunks/{messages-D1Hv8XGo.mjs → messages-C-aP90zq.mjs} +1 -0
  33. package/dist/chunks/{messages-DBRw-7Zc.mjs → messages-C2f44jci.mjs} +1 -0
  34. package/dist/chunks/{messages-CJdUsQ-c.mjs → messages-CKYoAmm9.mjs} +1 -0
  35. package/dist/chunks/{messages-CvGLfqmV.mjs → messages-CSQJrJ_N.mjs} +1 -0
  36. package/dist/chunks/{messages-1fC8IMyX.mjs → messages-CX_o4Cgc.mjs} +1 -0
  37. package/dist/chunks/{messages-BdeLo0N9.mjs → messages-C_tfXllT.mjs} +1 -0
  38. package/dist/chunks/{messages-DBn76jVV.mjs → messages-Cj6TZ_By.mjs} +1 -0
  39. package/dist/{messages-CLhcMlTc.mjs → chunks/messages-CjdYkfzj.mjs} +1 -0
  40. package/dist/{messages-7W4d0DwD.mjs → chunks/messages-CmvWFWR6.mjs} +1 -0
  41. package/dist/{messages-CzTufCHu.mjs → chunks/messages-CnV-kxh6.mjs} +1 -0
  42. package/dist/chunks/{messages-BMXCuEKO.mjs → messages-CpuDfpdi.mjs} +1 -0
  43. package/dist/chunks/{messages-DT4dP5uK.mjs → messages-Cspe1oXk.mjs} +1 -0
  44. package/dist/{messages-D-ZtY5v0.mjs → chunks/messages-D2_d-Jt4.mjs} +1 -0
  45. package/dist/{messages-BELRf6DU.mjs → chunks/messages-DADSbW8l.mjs} +1 -0
  46. package/dist/chunks/{messages-O5tQus_0.mjs → messages-DHSeTQ4S.mjs} +1 -0
  47. package/dist/{messages-BFG6Wlgy.mjs → chunks/messages-DKeO2RsW.mjs} +1 -0
  48. package/dist/chunks/{messages-9SihnaXQ.mjs → messages-DLdlX_dY.mjs} +1 -0
  49. package/dist/chunks/{messages-BL0tXcDf.mjs → messages-DVxlJ4DU.mjs} +1 -0
  50. package/dist/chunks/{messages-CY8_RyFE.mjs → messages-DWSxENRk.mjs} +1 -0
  51. package/dist/{messages-Q7AO_FLv.mjs → chunks/messages-D_bVNZ12.mjs} +1 -0
  52. package/dist/chunks/{messages-BYyy6Wqf.mjs → messages-DdzjVwmG.mjs} +1 -0
  53. package/dist/chunks/{messages-DLfR5bMd.mjs → messages-DhyBKTi0.mjs} +1 -0
  54. package/dist/chunks/{messages-DvFLX36Q.mjs → messages-DlC9jDXa.mjs} +1 -0
  55. package/dist/chunks/{messages-D5C3J9qr.mjs → messages-DmBGk8ed.mjs} +1 -0
  56. package/dist/chunks/{messages-R3hUSvr3.mjs → messages-Dm_VqpU6.mjs} +1 -0
  57. package/dist/chunks/{messages-w7v1GNaE.mjs → messages-Dn9hI_ER.mjs} +1 -0
  58. package/dist/{messages-uKX8WBaD.mjs → chunks/messages-Dssp4AXT.mjs} +1 -0
  59. package/dist/chunks/{messages-Xq8UmkVs.mjs → messages-DzCc0JgS.mjs} +1 -0
  60. package/dist/chunks/{messages-JELdtT6E.mjs → messages-N6THNxo0.mjs} +1 -0
  61. package/dist/chunks/{messages-DqM1LFg5.mjs → messages-OctOxF2l.mjs} +1 -0
  62. package/dist/chunks/{messages-_ncGrKHh.mjs → messages-QT3JA95K.mjs} +1 -0
  63. package/dist/{messages-C9eaarcK.mjs → chunks/messages-QqWK83BF.mjs} +1 -0
  64. package/dist/chunks/{messages-BogRq8lt.mjs → messages-RsGaDzbb.mjs} +1 -0
  65. package/dist/chunks/{messages-Dzwxv9v1.mjs → messages-c7YlI6Wm.mjs} +1 -0
  66. package/dist/{messages-D5iv1Kox.mjs → chunks/messages-lZ8aF-4r.mjs} +1 -0
  67. package/dist/chunks/{messages-7QoX8DkW.mjs → messages-uGDrchjb.mjs} +1 -0
  68. package/dist/chunks/{messages-CKI54h6O.mjs → messages-xc2yNvog.mjs} +1 -0
  69. package/dist/{messages-Bmu_S7GM.mjs → chunks/messages-y5Q-ymIx.mjs} +1 -0
  70. package/dist/{messages-BWF-zUpY.mjs → chunks/messages-yJnyedAd.mjs} +1 -0
  71. package/dist/{messages-kep5wtm4.mjs → chunks/messages-yQ_4upHh.mjs} +1 -0
  72. package/dist/chunks/{messages-C99mq906.mjs → messages-ysbWD56Q.mjs} +1 -0
  73. package/dist/full.mjs +2 -2
  74. package/dist/locales.mjs +68 -67
  75. package/dist/{chunks/messages-B1Aww8q7.mjs → messages-1mYyrppy.mjs} +1 -0
  76. package/dist/{messages-DnIhyAJk.mjs → messages-4gHfbcDp.mjs} +1 -0
  77. package/dist/{chunks/messages-CQwpzUFp.mjs → messages-6xYv-U8A.mjs} +1 -0
  78. package/dist/{messages-CMkNSDTo.mjs → messages-7jqF79FO.mjs} +1 -0
  79. package/dist/{messages-BSbjsyHY.mjs → messages-B3EUuA7o.mjs} +1 -0
  80. package/dist/{chunks/messages-LPVfA-8K.mjs → messages-B6srtRn7.mjs} +1 -0
  81. package/dist/{messages-DJDG55Vq.mjs → messages-B85OIs0E.mjs} +1 -0
  82. package/dist/{chunks/messages-_ErNTNhk.mjs → messages-BCYBBG_u.mjs} +1 -0
  83. package/dist/{chunks/messages-CZygwLwM.mjs → messages-BDLNPiaW.mjs} +1 -0
  84. package/dist/{chunks/messages-C2htQ_3F.mjs → messages-BKpNd1Zb.mjs} +1 -0
  85. package/dist/{messages-CnwibSvh.mjs → messages-BLFUid_o.mjs} +1 -0
  86. package/dist/{chunks/messages-CVw84KdI.mjs → messages-BNPTVKnc.mjs} +1 -0
  87. package/dist/{chunks/messages-BMv4xwIr.mjs → messages-BQPXSkri.mjs} +1 -0
  88. package/dist/{chunks/messages-Dz9L52ol.mjs → messages-BQu6VNDq.mjs} +1 -0
  89. package/dist/{chunks/messages-DprmQg6V.mjs → messages-BRodFKUo.mjs} +1 -0
  90. package/dist/{chunks/messages-CznZadDf.mjs → messages-BWIikA8H.mjs} +1 -0
  91. package/dist/{chunks/messages-CqWJcCbY.mjs → messages-BXQvMilx.mjs} +1 -0
  92. package/dist/{messages-Z9nEU2xK.mjs → messages-BZ92luiw.mjs} +1 -0
  93. package/dist/{messages-BC86qLvI.mjs → messages-Bg0Phlcj.mjs} +1 -0
  94. package/dist/{messages-DnXLrlHh.mjs → messages-Bh6RBIu5.mjs} +1 -0
  95. package/dist/{messages-DhLKYm2j.mjs → messages-BnFHdIpg.mjs} +1 -0
  96. package/dist/{chunks/messages-BU2nlrLK.mjs → messages-Bq82EVsw.mjs} +1 -0
  97. package/dist/{messages-Diu6jAaR.mjs → messages-BxbcTiHh.mjs} +1 -0
  98. package/dist/{chunks/messages-BoJc_p1r.mjs → messages-ByO6bSV2.mjs} +1 -0
  99. package/dist/{messages-BrPFGbM-.mjs → messages-ByUNPiQ4.mjs} +1 -0
  100. package/dist/{messages-BB5z9Uba.mjs → messages-Byw0EHyf.mjs} +1 -0
  101. package/dist/{messages-D1Hv8XGo.mjs → messages-C-aP90zq.mjs} +1 -0
  102. package/dist/{messages-DBRw-7Zc.mjs → messages-C2f44jci.mjs} +1 -0
  103. package/dist/{messages-CJdUsQ-c.mjs → messages-CKYoAmm9.mjs} +1 -0
  104. package/dist/{messages-CvGLfqmV.mjs → messages-CSQJrJ_N.mjs} +1 -0
  105. package/dist/{messages-1fC8IMyX.mjs → messages-CX_o4Cgc.mjs} +1 -0
  106. package/dist/{messages-BdeLo0N9.mjs → messages-C_tfXllT.mjs} +1 -0
  107. package/dist/{messages-DBn76jVV.mjs → messages-Cj6TZ_By.mjs} +1 -0
  108. package/dist/{chunks/messages-CLhcMlTc.mjs → messages-CjdYkfzj.mjs} +1 -0
  109. package/dist/{chunks/messages-7W4d0DwD.mjs → messages-CmvWFWR6.mjs} +1 -0
  110. package/dist/{chunks/messages-CzTufCHu.mjs → messages-CnV-kxh6.mjs} +1 -0
  111. package/dist/{messages-BMXCuEKO.mjs → messages-CpuDfpdi.mjs} +1 -0
  112. package/dist/{messages-DT4dP5uK.mjs → messages-Cspe1oXk.mjs} +1 -0
  113. package/dist/{chunks/messages-D-ZtY5v0.mjs → messages-D2_d-Jt4.mjs} +1 -0
  114. package/dist/{chunks/messages-BELRf6DU.mjs → messages-DADSbW8l.mjs} +1 -0
  115. package/dist/{messages-O5tQus_0.mjs → messages-DHSeTQ4S.mjs} +1 -0
  116. package/dist/{chunks/messages-BFG6Wlgy.mjs → messages-DKeO2RsW.mjs} +1 -0
  117. package/dist/{messages-9SihnaXQ.mjs → messages-DLdlX_dY.mjs} +1 -0
  118. package/dist/{messages-BL0tXcDf.mjs → messages-DVxlJ4DU.mjs} +1 -0
  119. package/dist/{messages-CY8_RyFE.mjs → messages-DWSxENRk.mjs} +1 -0
  120. package/dist/{chunks/messages-Q7AO_FLv.mjs → messages-D_bVNZ12.mjs} +1 -0
  121. package/dist/{messages-BYyy6Wqf.mjs → messages-DdzjVwmG.mjs} +1 -0
  122. package/dist/{messages-DLfR5bMd.mjs → messages-DhyBKTi0.mjs} +1 -0
  123. package/dist/{messages-DvFLX36Q.mjs → messages-DlC9jDXa.mjs} +1 -0
  124. package/dist/{messages-D5C3J9qr.mjs → messages-DmBGk8ed.mjs} +1 -0
  125. package/dist/{messages-R3hUSvr3.mjs → messages-Dm_VqpU6.mjs} +1 -0
  126. package/dist/{messages-w7v1GNaE.mjs → messages-Dn9hI_ER.mjs} +1 -0
  127. package/dist/{chunks/messages-uKX8WBaD.mjs → messages-Dssp4AXT.mjs} +1 -0
  128. package/dist/{messages-Xq8UmkVs.mjs → messages-DzCc0JgS.mjs} +1 -0
  129. package/dist/{messages-JELdtT6E.mjs → messages-N6THNxo0.mjs} +1 -0
  130. package/dist/{messages-DqM1LFg5.mjs → messages-OctOxF2l.mjs} +1 -0
  131. package/dist/{messages-_ncGrKHh.mjs → messages-QT3JA95K.mjs} +1 -0
  132. package/dist/{chunks/messages-C9eaarcK.mjs → messages-QqWK83BF.mjs} +1 -0
  133. package/dist/{messages-BogRq8lt.mjs → messages-RsGaDzbb.mjs} +1 -0
  134. package/dist/{messages-Dzwxv9v1.mjs → messages-c7YlI6Wm.mjs} +1 -0
  135. package/dist/{chunks/messages-D5iv1Kox.mjs → messages-lZ8aF-4r.mjs} +1 -0
  136. package/dist/{messages-7QoX8DkW.mjs → messages-uGDrchjb.mjs} +1 -0
  137. package/dist/{messages-CKI54h6O.mjs → messages-xc2yNvog.mjs} +1 -0
  138. package/dist/{chunks/messages-Bmu_S7GM.mjs → messages-y5Q-ymIx.mjs} +1 -0
  139. package/dist/{chunks/messages-BWF-zUpY.mjs → messages-yJnyedAd.mjs} +1 -0
  140. package/dist/{chunks/messages-kep5wtm4.mjs → messages-yQ_4upHh.mjs} +1 -0
  141. package/dist/{messages-C99mq906.mjs → messages-ysbWD56Q.mjs} +1 -0
  142. package/dist/tools.mjs +887 -701
  143. package/package.json +2 -1
  144. package/src/components/block/style-manager.ts +5 -1
  145. package/src/components/blocks.ts +109 -9
  146. package/src/components/i18n/locales/am/messages.json +1 -0
  147. package/src/components/i18n/locales/ar/messages.json +1 -0
  148. package/src/components/i18n/locales/az/messages.json +1 -0
  149. package/src/components/i18n/locales/bg/messages.json +1 -0
  150. package/src/components/i18n/locales/bn/messages.json +1 -0
  151. package/src/components/i18n/locales/bs/messages.json +1 -0
  152. package/src/components/i18n/locales/cs/messages.json +1 -0
  153. package/src/components/i18n/locales/da/messages.json +1 -0
  154. package/src/components/i18n/locales/de/messages.json +1 -0
  155. package/src/components/i18n/locales/dv/messages.json +1 -0
  156. package/src/components/i18n/locales/el/messages.json +1 -0
  157. package/src/components/i18n/locales/en/messages.json +1 -0
  158. package/src/components/i18n/locales/es/messages.json +1 -0
  159. package/src/components/i18n/locales/et/messages.json +1 -0
  160. package/src/components/i18n/locales/fa/messages.json +1 -0
  161. package/src/components/i18n/locales/fi/messages.json +1 -0
  162. package/src/components/i18n/locales/fil/messages.json +1 -0
  163. package/src/components/i18n/locales/fr/messages.json +1 -0
  164. package/src/components/i18n/locales/gu/messages.json +1 -0
  165. package/src/components/i18n/locales/he/messages.json +1 -0
  166. package/src/components/i18n/locales/hi/messages.json +1 -0
  167. package/src/components/i18n/locales/hr/messages.json +1 -0
  168. package/src/components/i18n/locales/hu/messages.json +1 -0
  169. package/src/components/i18n/locales/hy/messages.json +1 -0
  170. package/src/components/i18n/locales/id/messages.json +1 -0
  171. package/src/components/i18n/locales/it/messages.json +1 -0
  172. package/src/components/i18n/locales/ja/messages.json +1 -0
  173. package/src/components/i18n/locales/ka/messages.json +1 -0
  174. package/src/components/i18n/locales/km/messages.json +1 -0
  175. package/src/components/i18n/locales/kn/messages.json +1 -0
  176. package/src/components/i18n/locales/ko/messages.json +1 -0
  177. package/src/components/i18n/locales/ku/messages.json +1 -0
  178. package/src/components/i18n/locales/lo/messages.json +1 -0
  179. package/src/components/i18n/locales/lt/messages.json +1 -0
  180. package/src/components/i18n/locales/lv/messages.json +1 -0
  181. package/src/components/i18n/locales/mk/messages.json +1 -0
  182. package/src/components/i18n/locales/ml/messages.json +1 -0
  183. package/src/components/i18n/locales/mn/messages.json +1 -0
  184. package/src/components/i18n/locales/mr/messages.json +1 -0
  185. package/src/components/i18n/locales/ms/messages.json +1 -0
  186. package/src/components/i18n/locales/my/messages.json +1 -0
  187. package/src/components/i18n/locales/ne/messages.json +1 -0
  188. package/src/components/i18n/locales/nl/messages.json +1 -0
  189. package/src/components/i18n/locales/no/messages.json +1 -0
  190. package/src/components/i18n/locales/pa/messages.json +1 -0
  191. package/src/components/i18n/locales/pl/messages.json +1 -0
  192. package/src/components/i18n/locales/ps/messages.json +1 -0
  193. package/src/components/i18n/locales/pt/messages.json +1 -0
  194. package/src/components/i18n/locales/ro/messages.json +1 -0
  195. package/src/components/i18n/locales/ru/messages.json +1 -0
  196. package/src/components/i18n/locales/sd/messages.json +1 -0
  197. package/src/components/i18n/locales/si/messages.json +1 -0
  198. package/src/components/i18n/locales/sk/messages.json +1 -0
  199. package/src/components/i18n/locales/sl/messages.json +1 -0
  200. package/src/components/i18n/locales/sq/messages.json +1 -0
  201. package/src/components/i18n/locales/sr/messages.json +1 -0
  202. package/src/components/i18n/locales/sv/messages.json +1 -0
  203. package/src/components/i18n/locales/sw/messages.json +1 -0
  204. package/src/components/i18n/locales/ta/messages.json +1 -0
  205. package/src/components/i18n/locales/te/messages.json +1 -0
  206. package/src/components/i18n/locales/th/messages.json +1 -0
  207. package/src/components/i18n/locales/tr/messages.json +1 -0
  208. package/src/components/i18n/locales/ug/messages.json +1 -0
  209. package/src/components/i18n/locales/uk/messages.json +1 -0
  210. package/src/components/i18n/locales/ur/messages.json +1 -0
  211. package/src/components/i18n/locales/vi/messages.json +1 -0
  212. package/src/components/i18n/locales/yi/messages.json +1 -0
  213. package/src/components/i18n/locales/zh/messages.json +1 -0
  214. package/src/components/icons/index.ts +8 -0
  215. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +51 -10
  216. package/src/components/modules/blockEvents/index.ts +2 -5
  217. package/src/components/modules/blockManager/blockManager.ts +10 -0
  218. package/src/components/modules/blockManager/operations.ts +11 -0
  219. package/src/components/modules/blockManager/repository.ts +22 -0
  220. package/src/components/modules/blockManager/yjs-sync.ts +148 -31
  221. package/src/components/modules/crossBlockSelection.ts +11 -3
  222. package/src/components/modules/drag/preview/DragPreview.ts +8 -0
  223. package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
  224. package/src/components/modules/paste/handlers/base.ts +3 -4
  225. package/src/components/modules/paste/index.ts +1 -1
  226. package/src/components/modules/rectangleSelection.ts +5 -2
  227. package/src/components/modules/toolbar/blockSettings.ts +52 -44
  228. package/src/components/modules/toolbar/index.ts +27 -7
  229. package/src/components/modules/toolbar/inline/index.ts +1 -1
  230. package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
  231. package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
  232. package/src/components/modules/yjs/block-observer.ts +9 -3
  233. package/src/components/modules/yjs/document-store.ts +4 -2
  234. package/src/components/modules/yjs/types.ts +8 -6
  235. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +6 -4
  236. package/src/components/utils/popover/popover-desktop.ts +12 -0
  237. package/src/stories/Popover.stories.ts +0 -2
  238. package/src/styles/main.css +9 -1
  239. package/src/tools/list/caret-manager.ts +28 -10
  240. package/src/tools/table/index.ts +180 -37
  241. package/src/tools/table/table-add-controls.ts +51 -14
  242. package/src/tools/table/table-cell-blocks.ts +41 -2
  243. package/src/tools/table/table-cell-selection.ts +27 -1
  244. package/src/tools/table/table-operations.ts +12 -15
  245. package/src/tools/table/table-row-col-controls.ts +69 -6
  246. package/src/tools/table/table-scroll-haze.ts +152 -0
@@ -47,6 +47,7 @@ import type { PendingHighlight } from './table-row-col-action-handler';
47
47
  import { TableRowColControls } from './table-row-col-controls';
48
48
  import type { RowColAction } from './table-row-col-controls';
49
49
  import { registerAdditionalRestrictedTools } from './table-restrictions';
50
+ import { TableScrollHaze } from './table-scroll-haze';
50
51
  import type { ClipboardBlockData, LegacyCellContent, TableCellsClipboard, TableData, TableConfig } from './types';
51
52
 
52
53
  const DEFAULT_ROWS = 3;
@@ -59,6 +60,12 @@ const WRAPPER_CLASSES = [
59
60
 
60
61
  const WRAPPER_EDIT_CLASSES = [
61
62
  'relative',
63
+ 'after:content-[""]',
64
+ 'after:absolute',
65
+ 'after:-bottom-10',
66
+ 'after:left-0',
67
+ 'after:right-0',
68
+ 'after:h-10',
62
69
  ];
63
70
 
64
71
  /**
@@ -77,7 +84,11 @@ export class Table implements BlockTool {
77
84
  private rowColControls: TableRowColControls | null = null;
78
85
  private cellBlocks: TableCellBlocks | null = null;
79
86
  private cellSelection: TableCellSelection | null = null;
87
+ private scrollHaze: TableScrollHaze | null = null;
80
88
  private element: HTMLDivElement | null = null;
89
+ private gridElement: HTMLElement | null = null;
90
+ private scrollContainer: HTMLDivElement | null = null;
91
+ private gripOverlay: HTMLDivElement | null = null;
81
92
  private blockId: string | undefined;
82
93
  private pendingHighlight: PendingHighlight | null = null;
83
94
  private isNewTable = false;
@@ -145,16 +156,19 @@ export class Table implements BlockTool {
145
156
  * Execute a structural operation within a Yjs transaction.
146
157
  * Combines the structural op lock (event deferral) with Yjs undo grouping.
147
158
  * Used for interactive operations that should be a single undo entry.
159
+ *
160
+ * @param fn - The structural operation to execute
161
+ * @param discard - If true, discard deferred events (forwarded to runStructuralOp)
148
162
  */
149
- private runTransactedStructuralOp<T>(fn: () => T): T {
163
+ private runTransactedStructuralOp<T>(fn: () => T, discard = false): T {
150
164
  if (!this.api.blocks.transact) {
151
- return this.runStructuralOp(fn);
165
+ return this.runStructuralOp(fn, discard);
152
166
  }
153
167
 
154
168
  const ref = { current: undefined as T | undefined };
155
169
 
156
170
  this.api.blocks.transact(() => {
157
- ref.current = this.runStructuralOp(fn);
171
+ ref.current = this.runStructuralOp(fn, discard);
158
172
  });
159
173
 
160
174
  return ref.current as T;
@@ -174,6 +188,8 @@ export class Table implements BlockTool {
174
188
  this.rowColControls = null;
175
189
  this.cellSelection?.destroy();
176
190
  this.cellSelection = null;
191
+ this.scrollHaze?.destroy();
192
+ this.scrollHaze = null;
177
193
  }
178
194
 
179
195
  /**
@@ -187,6 +203,7 @@ export class Table implements BlockTool {
187
203
  this.initRowColControls(gridEl);
188
204
  this.initCellSelection(gridEl);
189
205
  this.initGridPasteListener(gridEl);
206
+ this.initScrollHaze();
190
207
  }
191
208
 
192
209
  public static get toolbox(): ToolboxConfig {
@@ -224,10 +241,35 @@ export class Table implements BlockTool {
224
241
  };
225
242
  }
226
243
 
244
+ /**
245
+ * Ensure a scroll container exists between the wrapper and the grid.
246
+ * Creates one on demand (e.g. when the first resize converts percent → pixel mode).
247
+ */
248
+ private ensureScrollContainer(): HTMLDivElement {
249
+ if (this.scrollContainer) {
250
+ return this.scrollContainer;
251
+ }
252
+
253
+ const sc = document.createElement('div');
254
+
255
+ sc.setAttribute('data-blok-table-scroll', '');
256
+
257
+ const grid = this.gridElement;
258
+
259
+ if (grid && this.element) {
260
+ this.element.insertBefore(sc, grid);
261
+ sc.appendChild(grid);
262
+ }
263
+
264
+ this.scrollContainer = sc;
265
+
266
+ return sc;
267
+ }
268
+
227
269
  public render(): HTMLDivElement {
228
270
  const wrapper = document.createElement('div');
229
271
 
230
- wrapper.className = twMerge(WRAPPER_CLASSES, !this.readOnly && WRAPPER_EDIT_CLASSES, this.model.colWidths && SCROLL_OVERFLOW_CLASSES);
272
+ wrapper.className = twMerge(WRAPPER_CLASSES, !this.readOnly && WRAPPER_EDIT_CLASSES);
231
273
  wrapper.setAttribute(DATA_ATTR.tool, 'table');
232
274
 
233
275
  if (this.readOnly) {
@@ -249,15 +291,45 @@ export class Table implements BlockTool {
249
291
  applyPixelWidths(gridEl, this.model.colWidths);
250
292
  }
251
293
 
252
- wrapper.appendChild(gridEl);
294
+ this.gridElement = gridEl;
295
+
296
+ if (this.model.colWidths || !this.readOnly) {
297
+ const sc = document.createElement('div');
298
+
299
+ sc.setAttribute('data-blok-table-scroll', '');
300
+
301
+ const overflowClasses = this.model.colWidths ? SCROLL_OVERFLOW_CLASSES : [];
302
+
303
+ sc.classList.add(...overflowClasses);
304
+
305
+ sc.appendChild(gridEl);
306
+ wrapper.appendChild(sc);
307
+ this.scrollContainer = sc;
308
+ } else {
309
+ wrapper.appendChild(gridEl);
310
+ this.scrollContainer = null;
311
+ }
312
+
313
+ if (!this.readOnly) {
314
+ const overlay = document.createElement('div');
315
+
316
+ overlay.setAttribute('data-blok-table-grip-overlay', '');
317
+ overlay.style.position = 'absolute';
318
+ overlay.style.inset = '0';
319
+ overlay.style.pointerEvents = 'none';
320
+ overlay.style.zIndex = '3';
321
+ wrapper.appendChild(overlay);
322
+ this.gripOverlay = overlay;
323
+ }
324
+
253
325
  this.element = wrapper;
254
326
 
255
327
  if (this.model.withHeadings) {
256
- updateHeadingStyles(this.element, this.model.withHeadings);
328
+ updateHeadingStyles(this.gridElement, this.model.withHeadings);
257
329
  }
258
330
 
259
331
  if (this.model.withHeadingColumn) {
260
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
332
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
261
333
  }
262
334
 
263
335
  if (!this.readOnly) {
@@ -273,7 +345,7 @@ export class Table implements BlockTool {
273
345
  return;
274
346
  }
275
347
 
276
- const gridEl = this.element.firstElementChild as HTMLElement;
348
+ const gridEl = this.gridElement;
277
349
 
278
350
  if (!gridEl) {
279
351
  return;
@@ -286,11 +358,12 @@ export class Table implements BlockTool {
286
358
  if (this.readOnly) {
287
359
  mountCellBlocksReadOnly(gridEl, content, this.api, this.blockId ?? '');
288
360
  this.initReadOnlyCellSelection(gridEl);
361
+ this.initScrollHaze();
289
362
 
290
363
  return;
291
364
  }
292
365
 
293
- this.runStructuralOp(() => {
366
+ this.runTransactedStructuralOp(() => {
294
367
  const initializedContent = this.cellBlocks?.initializeCells(content) ?? content;
295
368
 
296
369
  // When a new table is created with empty content, the DOM grid already has
@@ -390,7 +463,7 @@ export class Table implements BlockTool {
390
463
 
391
464
  oldElement.parentNode.replaceChild(newElement, oldElement);
392
465
 
393
- const gridEl = this.element?.firstElementChild as HTMLElement | undefined;
466
+ const gridEl = this.gridElement;
394
467
 
395
468
  if (this.readOnly || !gridEl) {
396
469
  return;
@@ -412,10 +485,29 @@ export class Table implements BlockTool {
412
485
  return;
413
486
  }
414
487
 
415
- this.model.replaceAll({
416
- ...this.model.snapshot(),
417
- content: setDataContent,
418
- });
488
+ // When undoing reverts content to empty, the grid has default dimensions
489
+ // but initializeCells([]) mounted zero blocks. Pre-populate the model
490
+ // with empty cell entries so populateNewCells can place blocks correctly.
491
+ if (this.api.blocks.isSyncingFromYjs && setDataContent.length === 0 && gridEl) {
492
+ const emptyGridContent = Array.from(gridEl.querySelectorAll(`[${ROW_ATTR}]`), (row) => {
493
+ const cellCount = row.querySelectorAll(`[${CELL_ATTR}]`).length;
494
+
495
+ return Array.from({ length: cellCount }, () => ({ blocks: [] as string[] }));
496
+ });
497
+
498
+ this.model.replaceAll({
499
+ ...this.model.snapshot(),
500
+ content: emptyGridContent,
501
+ });
502
+
503
+ populateNewCells(gridEl, this.cellBlocks);
504
+ } else {
505
+ this.model.replaceAll({
506
+ ...this.model.snapshot(),
507
+ content: setDataContent,
508
+ });
509
+ }
510
+
419
511
  this.initialContent = null;
420
512
  }, true);
421
513
 
@@ -469,7 +561,7 @@ export class Table implements BlockTool {
469
561
 
470
562
  oldElement.parentNode.replaceChild(newElement, oldElement);
471
563
 
472
- const gridEl = this.element?.firstElementChild as HTMLElement | undefined;
564
+ const gridEl = this.gridElement;
473
565
 
474
566
  if (!this.readOnly && gridEl) {
475
567
  this.runStructuralOp(() => {
@@ -501,11 +593,13 @@ export class Table implements BlockTool {
501
593
  this.teardownSubsystems();
502
594
  this.cellBlocks?.destroy();
503
595
  this.cellBlocks = null;
596
+ this.gridElement = null;
597
+ this.scrollContainer = null;
504
598
  this.element = null;
505
599
  }
506
600
 
507
601
  public deleteRowWithCleanup(rowIndex: number): void {
508
- const gridEl = this.element?.firstElementChild as HTMLElement | undefined;
602
+ const gridEl = this.gridElement;
509
603
 
510
604
  if (!gridEl) {
511
605
  return;
@@ -520,7 +614,7 @@ export class Table implements BlockTool {
520
614
  }
521
615
 
522
616
  public deleteColumnWithCleanup(colIndex: number): void {
523
- const gridEl = this.element?.firstElementChild as HTMLElement | undefined;
617
+ const gridEl = this.gridElement;
524
618
 
525
619
  if (!gridEl) {
526
620
  return;
@@ -570,8 +664,8 @@ export class Table implements BlockTool {
570
664
  this.grid.addRow(gridEl);
571
665
  this.model.addRow();
572
666
  populateNewCells(gridEl, this.cellBlocks);
573
- updateHeadingStyles(this.element, this.model.withHeadings);
574
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
667
+ updateHeadingStyles(this.gridElement, this.model.withHeadings);
668
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
575
669
  this.initResize(gridEl);
576
670
  this.addControls?.syncRowButtonWidth();
577
671
  this.rowColControls?.refresh();
@@ -588,10 +682,14 @@ export class Table implements BlockTool {
588
682
  this.model.addColumn(undefined, halfWidth);
589
683
  this.model.setColWidths([...colWidths, halfWidth]);
590
684
  populateNewCells(gridEl, this.cellBlocks);
591
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
685
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
592
686
  this.initResize(gridEl);
593
687
  this.addControls?.syncRowButtonWidth();
594
688
  this.rowColControls?.refresh();
689
+
690
+ if (this.scrollContainer) {
691
+ this.scrollContainer.scrollLeft = this.scrollContainer.scrollWidth;
692
+ }
595
693
  });
596
694
  },
597
695
  onDragStart: () => {
@@ -606,8 +704,8 @@ export class Table implements BlockTool {
606
704
  this.grid.addRow(gridEl);
607
705
  this.model.addRow();
608
706
  populateNewCells(gridEl, this.cellBlocks);
609
- updateHeadingStyles(this.element, this.model.withHeadings);
610
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
707
+ updateHeadingStyles(this.gridElement, this.model.withHeadings);
708
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
611
709
  });
612
710
  },
613
711
  onDragRemoveRow: () => {
@@ -636,13 +734,13 @@ export class Table implements BlockTool {
636
734
  this.model.setColWidths(newWidths);
637
735
  applyPixelWidths(gridEl, newWidths);
638
736
  populateNewCells(gridEl, this.cellBlocks);
639
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
737
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
640
738
  this.initResize(gridEl);
641
739
 
642
740
  dragState.addedCols++;
643
741
 
644
- if (this.element) {
645
- this.element.scrollLeft = this.element.scrollWidth;
742
+ if (this.scrollContainer) {
743
+ this.scrollContainer.scrollLeft = this.scrollContainer.scrollWidth;
646
744
  }
647
745
  });
648
746
  },
@@ -677,8 +775,8 @@ export class Table implements BlockTool {
677
775
  this.addControls?.syncRowButtonWidth();
678
776
  this.rowColControls?.refresh();
679
777
 
680
- if (this.element) {
681
- this.element.scrollLeft = dragState.addedCols > 0 ? this.element.scrollWidth : 0;
778
+ if (this.scrollContainer) {
779
+ this.scrollContainer.scrollLeft = dragState.addedCols > 0 ? this.scrollContainer.scrollWidth : 0;
682
780
  }
683
781
 
684
782
  dragState.addedCols = 0;
@@ -695,6 +793,8 @@ export class Table implements BlockTool {
695
793
 
696
794
  this.rowColControls = new TableRowColControls({
697
795
  grid: gridEl,
796
+ overlay: this.gripOverlay ?? undefined,
797
+ scrollContainer: this.scrollContainer ?? undefined,
698
798
  getColumnCount: () => this.grid.getColumnCount(gridEl),
699
799
  getRowCount: () => this.grid.getRowCount(gridEl),
700
800
  isHeadingRow: () => this.model.withHeadings,
@@ -748,7 +848,7 @@ export class Table implements BlockTool {
748
848
  const generationAtStart = this.setDataGeneration;
749
849
 
750
850
  this.runTransactedStructuralOp(() => {
751
- if (generationAtStart !== this.setDataGeneration || this.element?.firstElementChild !== gridEl) {
851
+ if (generationAtStart !== this.setDataGeneration || this.gridElement !== gridEl) {
752
852
  return;
753
853
  }
754
854
 
@@ -779,7 +879,7 @@ export class Table implements BlockTool {
779
879
  },
780
880
  );
781
881
 
782
- if (generationAtStart !== this.setDataGeneration || this.element?.firstElementChild !== gridEl) {
882
+ if (generationAtStart !== this.setDataGeneration || this.gridElement !== gridEl) {
783
883
  return;
784
884
  }
785
885
 
@@ -788,8 +888,8 @@ export class Table implements BlockTool {
788
888
  this.model.setWithHeadingColumn(result.withHeadingColumn);
789
889
  this.pendingHighlight = result.pendingHighlight;
790
890
 
791
- updateHeadingStyles(this.element, this.model.withHeadings);
792
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
891
+ updateHeadingStyles(this.gridElement, this.model.withHeadings);
892
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
793
893
  this.initResize(gridEl);
794
894
  this.addControls?.syncRowButtonWidth();
795
895
  this.rowColControls?.refresh();
@@ -851,7 +951,7 @@ export class Table implements BlockTool {
851
951
  const widths = this.model.colWidths ?? readPixelWidths(gridEl);
852
952
 
853
953
  if (!isPercentMode) {
854
- enableScrollOverflow(this.element);
954
+ enableScrollOverflow(this.ensureScrollContainer());
855
955
  }
856
956
 
857
957
  this.resize = new TableResize(
@@ -859,14 +959,17 @@ export class Table implements BlockTool {
859
959
  widths,
860
960
  (newWidths: number[]) => {
861
961
  this.model.setColWidths(newWidths);
862
- enableScrollOverflow(this.element);
962
+ enableScrollOverflow(this.ensureScrollContainer());
863
963
  this.rowColControls?.positionGrips();
964
+ this.addControls?.syncRowButtonWidth();
965
+ this.scrollHaze?.update();
864
966
  },
865
967
  () => {
866
968
  this.rowColControls?.hideAllGrips();
867
969
  },
868
970
  () => {
869
971
  this.addControls?.syncRowButtonWidth();
972
+ this.scrollHaze?.update();
870
973
  },
871
974
  isPercentMode,
872
975
  );
@@ -895,10 +998,32 @@ export class Table implements BlockTool {
895
998
  clipboardData.setData('text/plain', buildClipboardPlainText(payload));
896
999
  }
897
1000
 
1001
+ private handleCellCopyViaButton(cells: HTMLElement[]): void {
1002
+ const entries = this.collectCellBlockData(cells);
1003
+
1004
+ if (entries.length === 0) {
1005
+ return;
1006
+ }
1007
+
1008
+ const payload = serializeCellsToClipboard(entries);
1009
+ const html = buildClipboardHtml(payload);
1010
+ const plainText = buildClipboardPlainText(payload);
1011
+
1012
+ const htmlBlob = new Blob([html], { type: 'text/html' });
1013
+ const textBlob = new Blob([plainText], { type: 'text/plain' });
1014
+
1015
+ void navigator.clipboard.write([
1016
+ new ClipboardItem({
1017
+ 'text/html': htmlBlob,
1018
+ 'text/plain': textBlob,
1019
+ }),
1020
+ ]);
1021
+ }
1022
+
898
1023
  private collectCellBlockData(
899
1024
  cells: HTMLElement[],
900
1025
  ): Array<{ row: number; col: number; blocks: ClipboardBlockData[] }> {
901
- const gridEl = this.element?.firstElementChild;
1026
+ const gridEl = this.gridElement;
902
1027
 
903
1028
  if (!gridEl) {
904
1029
  return [];
@@ -976,6 +1101,7 @@ export class Table implements BlockTool {
976
1101
  grid: gridEl,
977
1102
  rectangleSelection, // Pass reference
978
1103
  i18n: this.api.i18n,
1104
+ isPopoverOpen: () => this.rowColControls?.isPopoverOpen ?? false,
979
1105
  onSelectionActiveChange: (hasSelection) => {
980
1106
  if (this.resize) {
981
1107
  this.resize.enabled = !hasSelection;
@@ -999,6 +1125,9 @@ export class Table implements BlockTool {
999
1125
  onCut: (cells, clipboardData) => {
1000
1126
  this.handleCellCopy(cells, clipboardData);
1001
1127
  },
1128
+ onCopyViaButton: (cells) => {
1129
+ this.handleCellCopyViaButton(cells);
1130
+ },
1002
1131
  });
1003
1132
  }
1004
1133
 
@@ -1014,9 +1143,23 @@ export class Table implements BlockTool {
1014
1143
  onCopy: (cells, clipboardData) => {
1015
1144
  this.handleCellCopy(cells, clipboardData);
1016
1145
  },
1146
+ onCopyViaButton: (cells) => {
1147
+ this.handleCellCopyViaButton(cells);
1148
+ },
1017
1149
  });
1018
1150
  }
1019
1151
 
1152
+ private initScrollHaze(): void {
1153
+ this.scrollHaze?.destroy();
1154
+
1155
+ if (!this.element || !this.scrollContainer) {
1156
+ return;
1157
+ }
1158
+
1159
+ this.scrollHaze = new TableScrollHaze();
1160
+ this.scrollHaze.init(this.element, this.scrollContainer);
1161
+ }
1162
+
1020
1163
  private initGridPasteListener(gridEl: HTMLElement): void {
1021
1164
  gridEl.addEventListener('paste', (e: ClipboardEvent) => {
1022
1165
  this.handleGridPaste(e, gridEl);
@@ -1150,8 +1293,8 @@ export class Table implements BlockTool {
1150
1293
  this.grid.addRow(gridEl);
1151
1294
  this.model.addRow();
1152
1295
  populateNewCells(gridEl, this.cellBlocks);
1153
- updateHeadingStyles(this.element, this.model.withHeadings);
1154
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
1296
+ updateHeadingStyles(this.gridElement, this.model.withHeadings);
1297
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
1155
1298
  });
1156
1299
 
1157
1300
  // Auto-expand columns
@@ -1165,7 +1308,7 @@ export class Table implements BlockTool {
1165
1308
  this.model.addColumn(undefined, halfWidth);
1166
1309
  this.model.setColWidths([...colWidths, halfWidth]);
1167
1310
  populateNewCells(gridEl, this.cellBlocks);
1168
- updateHeadingColumnStyles(this.element, this.model.withHeadingColumn);
1311
+ updateHeadingColumnStyles(this.gridElement, this.model.withHeadingColumn);
1169
1312
  });
1170
1313
  }
1171
1314
 
@@ -121,7 +121,7 @@ export class TableAddControls {
121
121
  this.addColBtn = this.createAddColumnButton();
122
122
 
123
123
  this.wrapper.appendChild(this.addRowBtn);
124
- this.grid.appendChild(this.addColBtn);
124
+ this.wrapper.appendChild(this.addColBtn);
125
125
  this.syncRowButtonWidth();
126
126
 
127
127
  this.wrapper.addEventListener('mousemove', this.boundMouseMove);
@@ -133,17 +133,51 @@ export class TableAddControls {
133
133
  }
134
134
 
135
135
  /**
136
- * Match the add-row button width to the grid's explicit width.
137
- * When the grid has a pixel width (from colWidths), the button must use
138
- * the same value so it scrolls with the table instead of staying at 100%
139
- * of the wrapper's visible area.
136
+ * Match the add-row button width and horizontal position to the grid.
137
+ *
138
+ * Pixel mode (colWidths set): clamp to the scroll container's visible
139
+ * width so the button never overflows the table boundary.
140
+ *
141
+ * Percent mode (no colWidths): clear `width` and use `left`/`right`
142
+ * constraints so the browser auto-sizes the button to the wrapper's
143
+ * content area, avoiding the wrapper's right padding.
140
144
  */
141
145
  public syncRowButtonWidth(): void {
142
146
  const gridWidth = this.grid.style.width;
143
147
 
144
- this.addRowBtn.style.width = gridWidth && gridWidth.endsWith('px')
145
- ? gridWidth
146
- : '100%';
148
+ if (gridWidth && gridWidth.endsWith('px')) {
149
+ const numericWidth = parseFloat(gridWidth);
150
+ const scrollContainer = this.grid.parentElement;
151
+ const isInsideScrollContainer = scrollContainer !== null && scrollContainer !== this.wrapper;
152
+
153
+ /**
154
+ * When a scroll container exists and the grid overflows it,
155
+ * clamp to the visible width so the button stays within bounds.
156
+ * Guard clientWidth > 0 for pre-layout / jsdom environments.
157
+ */
158
+ const visibleWidth = isInsideScrollContainer && scrollContainer.clientWidth > 0
159
+ ? Math.min(numericWidth, scrollContainer.clientWidth)
160
+ : numericWidth;
161
+
162
+ this.addRowBtn.style.width = `${visibleWidth}px`;
163
+ this.addRowBtn.style.right = '';
164
+ this.addRowBtn.style.left = '0px';
165
+ this.addRowBtn.style.transform = '';
166
+
167
+ this.addColBtn.style.left = `${visibleWidth}px`;
168
+ this.addColBtn.style.right = '';
169
+ } else {
170
+ this.addRowBtn.style.width = '';
171
+ this.addRowBtn.style.left = '0px';
172
+ this.addRowBtn.style.transform = '';
173
+
174
+ const paddingRight = parseFloat(getComputedStyle(this.wrapper).paddingRight) || 0;
175
+
176
+ this.addRowBtn.style.right = `${paddingRight}px`;
177
+
178
+ this.addColBtn.style.left = '';
179
+ this.addColBtn.style.right = '-16px';
180
+ }
147
181
  }
148
182
 
149
183
  /**
@@ -338,8 +372,14 @@ export class TableAddControls {
338
372
 
339
373
  private handleMouseMove(e: MouseEvent): void {
340
374
  const gridRect = this.grid.getBoundingClientRect();
375
+ const scrollContainer = this.grid.parentElement;
376
+ const isInsideScrollContainer = scrollContainer !== null && scrollContainer !== this.wrapper;
377
+ const visibleRight = isInsideScrollContainer
378
+ ? Math.min(gridRect.right, scrollContainer.getBoundingClientRect().right)
379
+ : gridRect.right;
380
+
341
381
  const distFromBottom = Math.abs(e.clientY - gridRect.bottom);
342
- const distFromRight = Math.abs(e.clientX - gridRect.right);
382
+ const distFromRight = Math.abs(e.clientX - visibleRight);
343
383
 
344
384
  if (distFromBottom <= PROXIMITY_PX) {
345
385
  this.showRow();
@@ -430,11 +470,8 @@ export class TableAddControls {
430
470
  btn.style.position = 'absolute';
431
471
  btn.style.left = '0';
432
472
  btn.style.bottom = '-36px';
433
- btn.style.boxSizing = 'content-box';
434
- btn.style.width = '100%';
473
+ btn.style.zIndex = '1';
435
474
  btn.style.height = '32px';
436
- btn.style.padding = '4px 8px 0';
437
- btn.style.marginLeft = '-8px';
438
475
 
439
476
  const visual = document.createElement('div');
440
477
 
@@ -462,7 +499,7 @@ export class TableAddControls {
462
499
  btn.style.opacity = '0';
463
500
  btn.style.pointerEvents = 'none';
464
501
  btn.style.position = 'absolute';
465
- btn.style.right = '-36px';
502
+ btn.style.right = '-16px';
466
503
  btn.style.top = '0px';
467
504
  btn.style.bottom = '0px';
468
505
  btn.style.width = '32px';
@@ -384,6 +384,12 @@ export class TableCellBlocks {
384
384
  return;
385
385
  }
386
386
 
387
+ // Guard against circular DOM: never append the table block's own holder
388
+ // into one of its descendant cell containers.
389
+ if (block.holder.contains(container)) {
390
+ return;
391
+ }
392
+
387
393
  container.appendChild(block.holder);
388
394
  this.api.blocks.setBlockParent(blockId, this.tableBlockId);
389
395
  this.stripPlaceholders(container);
@@ -477,6 +483,13 @@ export class TableCellBlocks {
477
483
  return;
478
484
  }
479
485
 
486
+ // Never claim the table block itself as a cell content block.
487
+ // This can happen when rendered() creates cell blocks synchronously,
488
+ // polluting currentBlockIndex before the table's own block-added fires.
489
+ if (detail.target.id === this.tableBlockId) {
490
+ return;
491
+ }
492
+
480
493
  const blockIndex = detail.index;
481
494
 
482
495
  if (blockIndex === undefined) {
@@ -526,9 +539,35 @@ export class TableCellBlocks {
526
539
  }
527
540
 
528
541
  // Only claim blocks whose holder is inside this table's grid.
529
- // Blocks placed outside the grid (e.g., via replaceWith during toolbox
530
- // insertion) should not be pulled into a cell by adjacency.
542
+ // Blocks placed outside the grid (e.g., via undo/restore or API inserts)
543
+ // should not be pulled into a cell by adjacency alone.
544
+ //
545
+ // However, blocks created while the editor's focus is inside this table
546
+ // (e.g., via Enter key or paste in a cell) land outside the grid because
547
+ // insertToDOM walks up from cell blocks to the table holder level.
548
+ // For those, check that the current block at the time of insertion belongs
549
+ // to this table — indicating the user was editing inside a cell.
531
550
  if (!this.gridElement.contains(holder)) {
551
+ const currentIndex = this.api.blocks.getCurrentBlockIndex();
552
+ const currentBlock = currentIndex >= 0
553
+ ? this.api.blocks.getBlockByIndex(currentIndex)
554
+ : null;
555
+ const currentBlockInOurTable = currentBlock !== null
556
+ && currentBlock !== undefined
557
+ && this.getOwnedCellForBlock(currentBlock.id) !== null;
558
+
559
+ if (!currentBlockInOurTable) {
560
+ return;
561
+ }
562
+
563
+ const cell = this.findCellForNewBlock(blockIndex);
564
+
565
+ if (cell) {
566
+ this.claimBlockForCell(cell, detail.target.id);
567
+ this.syncBlockToModel(cell, detail.target.id);
568
+ this.cellsPendingCheck.delete(cell);
569
+ }
570
+
532
571
  return;
533
572
  }
534
573