@jackuait/blok 0.6.0-beta.9 → 0.6.0

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 (320) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-Bn6Q_o8h.mjs → blok-BAh1rvUC.mjs} +3410 -2927
  3. package/dist/chunks/i18next-B47TKgbU.mjs +1303 -0
  4. package/dist/chunks/{i18next-loader-DjR4d8M7.mjs → i18next-loader-CHtGO6IK.mjs} +2 -2
  5. package/dist/chunks/{index-oe38cp86.mjs → index-DBWWKrDe.mjs} +12 -12
  6. package/dist/chunks/{inline-tool-convert-SRTkyaZn.mjs → inline-tool-convert-DduRc0fF.mjs} +1467 -951
  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 +2 -2
  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 +3190 -1690
  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/i18n/locales/am/messages.json +6 -0
  153. package/src/components/i18n/locales/ar/messages.json +6 -0
  154. package/src/components/i18n/locales/az/messages.json +6 -0
  155. package/src/components/i18n/locales/bg/messages.json +6 -0
  156. package/src/components/i18n/locales/bn/messages.json +6 -0
  157. package/src/components/i18n/locales/bs/messages.json +6 -0
  158. package/src/components/i18n/locales/cs/messages.json +6 -0
  159. package/src/components/i18n/locales/da/messages.json +6 -0
  160. package/src/components/i18n/locales/de/messages.json +6 -0
  161. package/src/components/i18n/locales/dv/messages.json +6 -0
  162. package/src/components/i18n/locales/el/messages.json +6 -0
  163. package/src/components/i18n/locales/en/messages.json +6 -0
  164. package/src/components/i18n/locales/es/messages.json +6 -0
  165. package/src/components/i18n/locales/et/messages.json +6 -0
  166. package/src/components/i18n/locales/fa/messages.json +6 -0
  167. package/src/components/i18n/locales/fi/messages.json +6 -0
  168. package/src/components/i18n/locales/fil/messages.json +6 -0
  169. package/src/components/i18n/locales/fr/messages.json +6 -0
  170. package/src/components/i18n/locales/gu/messages.json +6 -0
  171. package/src/components/i18n/locales/he/messages.json +6 -0
  172. package/src/components/i18n/locales/hi/messages.json +6 -0
  173. package/src/components/i18n/locales/hr/messages.json +6 -0
  174. package/src/components/i18n/locales/hu/messages.json +6 -0
  175. package/src/components/i18n/locales/hy/messages.json +6 -0
  176. package/src/components/i18n/locales/id/messages.json +6 -0
  177. package/src/components/i18n/locales/it/messages.json +6 -0
  178. package/src/components/i18n/locales/ja/messages.json +6 -0
  179. package/src/components/i18n/locales/ka/messages.json +6 -0
  180. package/src/components/i18n/locales/km/messages.json +6 -0
  181. package/src/components/i18n/locales/kn/messages.json +6 -0
  182. package/src/components/i18n/locales/ko/messages.json +6 -0
  183. package/src/components/i18n/locales/ku/messages.json +6 -0
  184. package/src/components/i18n/locales/lo/messages.json +6 -0
  185. package/src/components/i18n/locales/lt/messages.json +6 -0
  186. package/src/components/i18n/locales/lv/messages.json +6 -0
  187. package/src/components/i18n/locales/mk/messages.json +6 -0
  188. package/src/components/i18n/locales/ml/messages.json +6 -0
  189. package/src/components/i18n/locales/mn/messages.json +6 -0
  190. package/src/components/i18n/locales/mr/messages.json +6 -0
  191. package/src/components/i18n/locales/ms/messages.json +6 -0
  192. package/src/components/i18n/locales/my/messages.json +6 -0
  193. package/src/components/i18n/locales/ne/messages.json +6 -0
  194. package/src/components/i18n/locales/nl/messages.json +6 -0
  195. package/src/components/i18n/locales/no/messages.json +6 -0
  196. package/src/components/i18n/locales/pa/messages.json +6 -0
  197. package/src/components/i18n/locales/pl/messages.json +6 -0
  198. package/src/components/i18n/locales/ps/messages.json +6 -0
  199. package/src/components/i18n/locales/pt/messages.json +6 -0
  200. package/src/components/i18n/locales/ro/messages.json +6 -0
  201. package/src/components/i18n/locales/ru/messages.json +6 -0
  202. package/src/components/i18n/locales/sd/messages.json +6 -0
  203. package/src/components/i18n/locales/si/messages.json +6 -0
  204. package/src/components/i18n/locales/sk/messages.json +6 -0
  205. package/src/components/i18n/locales/sl/messages.json +6 -0
  206. package/src/components/i18n/locales/sq/messages.json +6 -0
  207. package/src/components/i18n/locales/sr/messages.json +6 -0
  208. package/src/components/i18n/locales/sv/messages.json +6 -0
  209. package/src/components/i18n/locales/sw/messages.json +6 -0
  210. package/src/components/i18n/locales/ta/messages.json +6 -0
  211. package/src/components/i18n/locales/te/messages.json +6 -0
  212. package/src/components/i18n/locales/th/messages.json +6 -0
  213. package/src/components/i18n/locales/tr/messages.json +6 -0
  214. package/src/components/i18n/locales/ug/messages.json +6 -0
  215. package/src/components/i18n/locales/uk/messages.json +6 -0
  216. package/src/components/i18n/locales/ur/messages.json +6 -0
  217. package/src/components/i18n/locales/vi/messages.json +6 -0
  218. package/src/components/i18n/locales/yi/messages.json +6 -0
  219. package/src/components/i18n/locales/zh/messages.json +6 -0
  220. package/src/components/icons/index.ts +17 -0
  221. package/src/components/inline-tools/inline-tool-link.ts +1 -1
  222. package/src/components/inline-tools/inline-tool-marker.ts +737 -0
  223. package/src/components/inline-tools/utils/formatting-range-utils.ts +6 -3
  224. package/src/components/inline-tools/utils/marker-dom-utils.ts +17 -0
  225. package/src/components/modules/api/blocks.ts +34 -9
  226. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +75 -29
  227. package/src/components/modules/blockEvents/index.ts +13 -5
  228. package/src/components/modules/blockManager/blockManager.ts +81 -2
  229. package/src/components/modules/blockManager/hierarchy.ts +20 -2
  230. package/src/components/modules/blockManager/operations.ts +70 -35
  231. package/src/components/modules/blockManager/repository.ts +22 -0
  232. package/src/components/modules/blockManager/types.ts +3 -1
  233. package/src/components/modules/blockManager/yjs-sync.ts +173 -39
  234. package/src/components/modules/blockSelection.ts +3 -0
  235. package/src/components/modules/crossBlockSelection.ts +11 -3
  236. package/src/components/modules/drag/preview/DragPreview.ts +10 -2
  237. package/src/components/modules/drag/target/DropTargetDetector.ts +100 -11
  238. package/src/components/modules/drag/utils/drag.constants.ts +1 -1
  239. package/src/components/modules/normalizeInlineImages.ts +263 -0
  240. package/src/components/modules/paste/google-docs-preprocessor.ts +197 -0
  241. package/src/components/modules/paste/handlers/base.ts +43 -2
  242. package/src/components/modules/paste/handlers/html-handler.ts +1 -1
  243. package/src/components/modules/paste/handlers/index.ts +1 -0
  244. package/src/components/modules/paste/handlers/table-cells-handler.ts +104 -0
  245. package/src/components/modules/paste/index.ts +20 -3
  246. package/src/components/modules/readonly.ts +8 -2
  247. package/src/components/modules/rectangleSelection.ts +5 -2
  248. package/src/components/modules/renderer.ts +35 -0
  249. package/src/components/modules/saver.ts +52 -2
  250. package/src/components/modules/toolbar/blockSettings.ts +52 -44
  251. package/src/components/modules/toolbar/index.ts +124 -17
  252. package/src/components/modules/toolbar/inline/index.ts +4 -4
  253. package/src/components/modules/toolbar/plus-button.ts +3 -3
  254. package/src/components/modules/toolbar/settings-toggler.ts +3 -3
  255. package/src/components/modules/toolbar/styles.ts +7 -7
  256. package/src/components/modules/ui.ts +6 -6
  257. package/src/components/modules/uiControllers/controllers/blockHover.ts +16 -2
  258. package/src/components/modules/uiControllers/handlers/touch.ts +83 -10
  259. package/src/components/modules/yjs/block-observer.ts +9 -3
  260. package/src/components/modules/yjs/document-store.ts +10 -7
  261. package/src/components/modules/yjs/types.ts +8 -6
  262. package/src/components/modules/yjs/undo-history.ts +90 -11
  263. package/src/components/selection/fake-background/shadows.ts +1 -1
  264. package/src/components/shared/color-picker.ts +211 -0
  265. package/src/components/shared/color-presets.ts +25 -0
  266. package/src/components/ui/toolbox.ts +19 -11
  267. package/src/components/utils/color-mapping.ts +241 -0
  268. package/src/components/utils/notifier/draw.ts +9 -9
  269. package/src/components/utils/placeholder.ts +24 -8
  270. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +2 -2
  271. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +14 -12
  272. package/src/components/utils/popover/components/search-input/search-input.const.ts +2 -2
  273. package/src/components/utils/popover/popover-abstract.ts +27 -3
  274. package/src/components/utils/popover/popover-desktop.ts +26 -3
  275. package/src/components/utils/popover/popover-inline.ts +14 -1
  276. package/src/components/utils/popover/popover-mobile.ts +4 -4
  277. package/src/components/utils/popover/popover.const.ts +2 -2
  278. package/src/components/utils/sanitizer.ts +24 -3
  279. package/src/components/utils/tw.ts +17 -5
  280. package/src/stories/Header.stories.ts +106 -0
  281. package/src/stories/MarkerColors.stories.ts +730 -0
  282. package/src/stories/Popover.stories.ts +1 -3
  283. package/src/stories/Table.stories.ts +1662 -0
  284. package/src/styles/main.css +207 -37
  285. package/src/tools/header/index.ts +1 -1
  286. package/src/tools/index.ts +3 -1
  287. package/src/tools/list/caret-manager.ts +28 -10
  288. package/src/tools/list/constants.ts +2 -2
  289. package/src/tools/list/dom-builder.ts +3 -3
  290. package/src/tools/list/static-configs.ts +0 -1
  291. package/src/tools/paragraph/index.ts +9 -5
  292. package/src/tools/table/core/table-commands.ts +99 -0
  293. package/src/tools/table/core/table-controller.ts +231 -0
  294. package/src/tools/table/core/table-events.ts +102 -0
  295. package/src/tools/table/index.ts +1070 -174
  296. package/src/tools/table/ownership/table-event-broker.ts +74 -0
  297. package/src/tools/table/ownership/table-ownership-registry.ts +126 -0
  298. package/src/tools/table/table-add-controls.ts +85 -15
  299. package/src/tools/table/table-cell-blocks.ts +336 -38
  300. package/src/tools/table/table-cell-clipboard.ts +415 -0
  301. package/src/tools/table/table-cell-color-picker.ts +34 -0
  302. package/src/tools/table/table-cell-selection.ts +264 -15
  303. package/src/tools/table/table-core.ts +3 -42
  304. package/src/tools/table/table-heading-toggle.ts +2 -2
  305. package/src/tools/table/table-model.ts +623 -0
  306. package/src/tools/table/table-operations.ts +59 -78
  307. package/src/tools/table/table-resize.ts +15 -11
  308. package/src/tools/table/table-restrictions.ts +69 -3
  309. package/src/tools/table/table-row-col-action-handler.ts +22 -7
  310. package/src/tools/table/table-row-col-controls.ts +129 -12
  311. package/src/tools/table/table-row-col-drag.ts +14 -0
  312. package/src/tools/table/table-scroll-haze.ts +152 -0
  313. package/src/tools/table/types.ts +22 -1
  314. package/src/tools/table/view/table-cell-blocks-adapter.ts +47 -0
  315. package/src/variants/blok-minimum.ts +13 -0
  316. package/types/api/block.d.ts +13 -0
  317. package/types/api/blocks.d.ts +16 -0
  318. package/types/tools/table.d.ts +2 -0
  319. package/dist/chunks/i18next-CugVlwWp.mjs +0 -1292
  320. package/src/tools/table/data-normalizer.ts +0 -32
@@ -19,10 +19,10 @@ import { announce } from '../../utils/announcer';
19
19
  import { convertStringToBlockData, isBlockConvertable } from '../../utils/blocks';
20
20
  import type { EventsDispatcher } from '../../utils/events';
21
21
  import { sanitizeBlocks, clean, composeSanitizerConfig } from '../../utils/sanitizer';
22
+ import { isInsideTableCell, isRestrictedInTableCell } from '../../../tools/table/table-restrictions';
22
23
  import type { Caret } from '../caret';
23
24
  import type { I18n } from '../i18n';
24
25
  import type { YjsManager } from '../yjs';
25
-
26
26
  import type { BlockFactory } from './factory';
27
27
  import type { BlockHierarchy } from './hierarchy';
28
28
  import type { BlockRepository } from './repository';
@@ -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);
@@ -204,15 +205,33 @@ export class BlockOperations {
204
205
  this.repository.getBlockByIndex(targetIndex)?.unwatchBlockMutations();
205
206
  }
206
207
 
207
- const toolName = tool ?? this.dependencies.config.defaultBlock;
208
+ const resolvedToolName = (() => {
209
+ const name = tool ?? this.dependencies.config.defaultBlock;
208
210
 
209
- if (toolName === undefined) {
210
- throw new Error('Could not insert Block. Tool name is not specified.');
211
- }
211
+ if (name === undefined) {
212
+ throw new Error('Could not insert Block. Tool name is not specified.');
213
+ }
214
+
215
+ // Demote restricted tools to paragraph when inserting inside a table cell.
216
+ // For replace: check the block being replaced (new block takes its DOM position).
217
+ // For insert: check the predecessor block (new block is placed after it in the DOM).
218
+ // Using the block AT targetIndex for non-replace inserts is wrong because that
219
+ // block may be a child paragraph inside a table cell that gets displaced, while
220
+ // the new block actually lands at the top level.
221
+ const neighborBlock = replace
222
+ ? this.repository.getBlockByIndex(targetIndex)
223
+ : (this.repository.getBlockByIndex(targetIndex - 1) ?? this.repository.getBlockByIndex(targetIndex));
224
+
225
+ if (neighborBlock !== undefined && isInsideTableCell(neighborBlock) && isRestrictedInTableCell(name)) {
226
+ return this.dependencies.config.defaultBlock ?? 'paragraph';
227
+ }
228
+
229
+ return name;
230
+ })();
212
231
 
213
232
  // Bind events immediately for user-created blocks so mutations are tracked right away
214
233
  const block = this.factory.composeBlock({
215
- tool: toolName,
234
+ tool: resolvedToolName,
216
235
  bindEventsImmediately: true,
217
236
  ...(id !== undefined && { id }),
218
237
  ...(data !== undefined && { data }),
@@ -235,7 +254,7 @@ export class BlockOperations {
235
254
  });
236
255
  }
237
256
 
238
- blocksStore.insert(targetIndex, block, replace);
257
+ blocksStore.insert(targetIndex, block, replace, appendToWorkingArea);
239
258
 
240
259
  /**
241
260
  * Force call of didMutated event on Block insertion
@@ -245,9 +264,11 @@ export class BlockOperations {
245
264
  });
246
265
 
247
266
  /**
248
- * Sync to Yjs data layer (unless caller is handling sync separately)
267
+ * Sync to Yjs data layer (unless caller is handling sync separately,
268
+ * or we're inside an atomic operation like paste where all Yjs sync
269
+ * is deferred until the operation completes)
249
270
  */
250
- if (!skipYjsSync) {
271
+ if (!skipYjsSync && !this.yjsSync.isSyncingFromYjs) {
251
272
  this.dependencies.YjsManager.addBlock({
252
273
  id: block.id,
253
274
  type: block.name,
@@ -298,7 +319,7 @@ export class BlockOperations {
298
319
  public insertAtEnd(blocksStore: BlocksStore): Block {
299
320
  this.currentBlockIndexValue = this.repository.length - 1;
300
321
 
301
- return this.insert({}, blocksStore);
322
+ return this.insert({ appendToWorkingArea: true }, blocksStore);
302
323
  }
303
324
 
304
325
  /**
@@ -319,6 +340,15 @@ export class BlockOperations {
319
340
  throw new Error('Can\'t find a Block to remove');
320
341
  }
321
342
 
343
+ // Clean up parent's contentIds before removing the block
344
+ const parentBlock = block.parentId !== null
345
+ ? this.repository.getBlockById(block.parentId)
346
+ : undefined;
347
+
348
+ if (parentBlock !== undefined) {
349
+ parentBlock.contentIds = parentBlock.contentIds.filter(id => id !== block.id);
350
+ }
351
+
322
352
  blocksStore.remove(index);
323
353
 
324
354
  /**
@@ -467,6 +497,17 @@ export class BlockOperations {
467
497
  return;
468
498
  }
469
499
 
500
+ // Check if the move would place a restricted tool inside a table cell
501
+ const movingBlock = this.repository.getBlockByIndex(fromIndex);
502
+ const neighborBlock = this.repository.getBlockByIndex(toIndex);
503
+
504
+ if (movingBlock !== undefined && neighborBlock !== undefined &&
505
+ isInsideTableCell(neighborBlock) && isRestrictedInTableCell(movingBlock.name)) {
506
+ log(`Warning during 'move' call: '${movingBlock.name}' is restricted in table cells.`, 'warn');
507
+
508
+ return;
509
+ }
510
+
470
511
  // Suppress stopCapturing to keep DOM + Yjs move as single undo entry
471
512
  this.suppressStopCapturing = true;
472
513
  try {
@@ -765,33 +806,27 @@ export class BlockOperations {
765
806
  replace = false,
766
807
  blocksStore: BlocksStore
767
808
  ): Promise<Block> {
768
- // Insert block without syncing to Yjs yet (we'll sync final state after onPaste)
769
- const block = this.insert({
770
- tool: toolName,
771
- replace,
772
- skipYjsSync: true,
773
- }, blocksStore);
809
+ // Insert block without syncing to Yjs yet.
810
+ // Wrap in atomic operation so that child blocks created during rendered()
811
+ // (e.g., table cell paragraph blocks) also skip Yjs sync.
812
+ const block = this.yjsSync.withAtomicOperation(() => {
813
+ return this.insert({
814
+ tool: toolName,
815
+ replace,
816
+ skipYjsSync: true,
817
+ }, blocksStore);
818
+ });
819
+
820
+ // Wait for the block to be fully rendered before calling onPaste,
821
+ // because onPaste may change the tool's root element and needs
822
+ // mutation watchers to be bound first.
823
+ await block.ready;
774
824
 
775
- // Suppress auto-sync during paste processing
825
+ // Call onPaste within atomic operation so child blocks created
826
+ // during cell initialization also skip Yjs sync.
776
827
  this.yjsSync.withAtomicOperation(() => {
777
- /**
778
- * We need to call onPaste after Block will be ready
779
- * because onPaste could change tool's root element, and we need to do that after block.watchBlockMutations() bound
780
- * to detect tool root element change
781
- * @todo make this.insert() awaitable and remove requestIdleCallback
782
- */
783
- return void block.ready.then(() => {
784
- block.call(BlockToolAPI.ON_PASTE, pasteEvent as unknown as Record<string, unknown>);
785
-
786
- /**
787
- * onPaste might cause the tool to replace its root element (e.g., Header changing level).
788
- * Since mutation observers are set up asynchronously via requestIdleCallback,
789
- * we need to manually refresh the tool element reference here.
790
- */
791
- block.refreshToolRootElement();
792
- }).catch((e) => {
793
- log(`${toolName}: onPaste callback call is failed`, 'error', e);
794
- });
828
+ block.call(BlockToolAPI.ON_PASTE, pasteEvent as unknown as Record<string, unknown>);
829
+ block.refreshToolRootElement();
795
830
  });
796
831
 
797
832
  // Sync final state to Yjs as single operation
@@ -201,6 +201,28 @@ export class BlockRepository {
201
201
  return previousBlocks.find((block) => !!block.inputs.length);
202
202
  }
203
203
 
204
+ /**
205
+ * Walks up the parentId chain and returns the top-level (root) block.
206
+ * If the block has no parent, returns it as-is.
207
+ * Used by selection modules to treat hierarchical blocks (e.g. table cells)
208
+ * as a single unit rather than selecting child blocks individually.
209
+ * @param block - the block to resolve
210
+ * @returns {Block} the root ancestor block
211
+ */
212
+ public resolveToRootBlock(block: Block): Block {
213
+ if (block.parentId === null) {
214
+ return block;
215
+ }
216
+
217
+ const parent = this.getBlockById(block.parentId);
218
+
219
+ if (parent === undefined) {
220
+ return block;
221
+ }
222
+
223
+ return this.resolveToRootBlock(parent);
224
+ }
225
+
204
226
  /**
205
227
  * Get block at a specific index from the blocks store nodes array
206
228
  * @param index - the index
@@ -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
  };
@@ -112,21 +112,43 @@ export class BlockYjsSync {
112
112
  * @param fn - Function to execute
113
113
  * @param options - Options for controlling the atomic operation behavior
114
114
  */
115
- public withAtomicOperation<T>(fn: () => T, options?: { extendThroughRAF?: boolean }): T {
115
+ /**
116
+ * Begin an atomic operation by incrementing sync count and suppressing stop capturing.
117
+ *
118
+ * @returns cleanup function to call when operation completes
119
+ */
120
+ private beginAtomicOperation(): () => void {
116
121
  this.yjsSyncCount++;
117
122
  const operations = this.dependencies.operations;
118
- const shouldExtend = options?.extendThroughRAF === true;
119
123
 
120
124
  if (operations) {
121
125
  operations.suppressStopCapturing = true;
122
126
  }
123
127
 
124
- const decrementSyncCount = (): void => {
128
+ return (): void => {
125
129
  this.yjsSyncCount--;
126
130
  if (operations && this.yjsSyncCount === 0) {
127
131
  operations.suppressStopCapturing = false;
128
132
  }
129
133
  };
134
+ }
135
+
136
+ /**
137
+ * End an atomic operation, optionally deferring cleanup through RAF.
138
+ *
139
+ * @param cleanup - function to call to decrement sync count
140
+ * @param extendThroughRAF - if true, defer cleanup until after next animation frame
141
+ */
142
+ private endAtomicOperation(cleanup: () => void, extendThroughRAF: boolean): void {
143
+ if (extendThroughRAF) {
144
+ requestAnimationFrame(cleanup);
145
+ } else {
146
+ cleanup();
147
+ }
148
+ }
149
+
150
+ public withAtomicOperation<T>(fn: () => T, options?: { extendThroughRAF?: boolean }): T {
151
+ const cleanup = this.beginAtomicOperation();
130
152
 
131
153
  try {
132
154
  const result = fn();
@@ -134,16 +156,34 @@ export class BlockYjsSync {
134
156
  // If extendThroughRAF is true, delay decrementing yjsSyncCount until after requestAnimationFrame callbacks
135
157
  // This ensures that DOM updates scheduled by rendered() hooks don't trigger
136
158
  // block data sync to Yjs, which would create new undo entries and clear the redo stack
137
- if (shouldExtend) {
138
- requestAnimationFrame(decrementSyncCount);
139
- } else {
140
- decrementSyncCount();
141
- }
159
+ this.endAtomicOperation(cleanup, options?.extendThroughRAF === true);
142
160
 
143
161
  return result;
144
162
  } catch (error) {
145
- // If an error occurs, decrement immediately
146
- decrementSyncCount();
163
+ cleanup();
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Async version of withAtomicOperation for operations that return promises.
170
+ * Keeps yjsSyncCount elevated until the async work completes, then optionally
171
+ * extends through RAF to cover deferred DOM callbacks.
172
+ *
173
+ * @param fn - Async function to execute
174
+ * @param options - Options for controlling the atomic operation behavior
175
+ */
176
+ public async withAtomicOperationAsync(
177
+ fn: () => Promise<void>,
178
+ options?: { extendThroughRAF?: boolean }
179
+ ): Promise<void> {
180
+ const cleanup = this.beginAtomicOperation();
181
+
182
+ try {
183
+ await fn();
184
+ this.endAtomicOperation(cleanup, options?.extendThroughRAF === true);
185
+ } catch (error) {
186
+ cleanup();
147
187
  throw error;
148
188
  }
149
189
  }
@@ -165,25 +205,28 @@ export class BlockYjsSync {
165
205
  * @param event - the block change event from YjsManager
166
206
  */
167
207
  private syncBlockFromYjs(event: BlockChangeEvent): void {
168
- const { blockId, type: changeType } = event;
169
-
170
- if (changeType === 'update') {
171
- this.handleYjsUpdate(blockId);
208
+ if (event.type === 'update') {
209
+ this.handleYjsUpdate(event.blockId);
172
210
  return;
173
211
  }
174
212
 
175
- if (changeType === 'move') {
213
+ if (event.type === 'move') {
176
214
  this.handleYjsMove();
177
215
  return;
178
216
  }
179
217
 
180
- if (changeType === 'add') {
181
- this.handleYjsAdd(blockId);
218
+ if (event.type === 'add') {
219
+ this.handleYjsAdd(event.blockId);
220
+ return;
221
+ }
222
+
223
+ if (event.type === 'batch-add') {
224
+ this.handleYjsBatchAdd(event.blockIds);
182
225
  return;
183
226
  }
184
227
 
185
- if (changeType === 'remove') {
186
- this.handleYjsRemove(blockId);
228
+ if (event.type === 'remove') {
229
+ this.handleYjsRemove(event.blockId);
187
230
  }
188
231
  }
189
232
 
@@ -221,19 +264,32 @@ export class BlockYjsSync {
221
264
  bindEventsImmediately: true,
222
265
  });
223
266
 
224
- // Increment counter to prevent syncing back to Yjs during undo/redo
225
- this.yjsSyncCount++;
226
- try {
267
+ // Use atomic operation with RAF extension to prevent DOM mutation observers
268
+ // from syncing back to Yjs after block replacement
269
+ this.withAtomicOperation(() => {
227
270
  this.handlers.replaceBlock(blockIndex, newBlock);
228
- } finally {
229
- this.yjsSyncCount--;
230
- }
271
+ }, { extendThroughRAF: true });
231
272
  } else {
232
- // Just update data
233
- this.yjsSyncCount++;
234
- void block.setData(data).finally(() => {
235
- this.yjsSyncCount--;
236
- });
273
+ // Update data in-place; if tool can't handle it, recreate the block.
274
+ // Use async atomic operation with RAF extension to keep isSyncingFromYjs
275
+ // true through the entire setData lifecycle + one RAF frame, preventing
276
+ // DOM mutation observers from writing back to Yjs and clearing the redo stack.
277
+ void this.withAtomicOperationAsync(async () => {
278
+ const success = await block.setData(data);
279
+
280
+ if (!success) {
281
+ const blockIndex = this.handlers.getBlockIndex(block);
282
+ const newBlock = this.factory.composeBlock({
283
+ id: block.id,
284
+ tool: block.name,
285
+ data,
286
+ tunes: block.preservedTunes,
287
+ bindEventsImmediately: true,
288
+ });
289
+
290
+ this.handlers.replaceBlock(blockIndex, newBlock);
291
+ }
292
+ }, { extendThroughRAF: true });
237
293
  }
238
294
  }
239
295
 
@@ -290,6 +346,80 @@ export class BlockYjsSync {
290
346
  }, { extendThroughRAF: true });
291
347
  }
292
348
 
349
+ /**
350
+ * Handle batch block add from Yjs (undo/redo).
351
+ *
352
+ * When multiple blocks are restored at once (e.g. a table + its cell
353
+ * paragraphs), we use a two-pass approach:
354
+ * 1. Create ALL blocks and insert them into the blocks array (no DOM).
355
+ * 2. Activate each block (DOM insert + RENDERED lifecycle hook).
356
+ *
357
+ * This ensures that when a parent tool's `rendered()` hook fires (pass 2),
358
+ * child blocks already exist in BlockManager, so helpers like
359
+ * `mountBlocksInCell()` can find them by ID.
360
+ */
361
+ private handleYjsBatchAdd(blockIds: string[]): void {
362
+ const yjsBlocks = this.dependencies.YjsManager.toJSON();
363
+
364
+ // Collect blocks to create — skip any that already exist
365
+ const toCreate: Array<{ blockId: string; toolName: string; data: Record<string, unknown>; parentId: string | undefined; targetIndex: number }> = [];
366
+
367
+ for (const blockId of blockIds) {
368
+ if (this.repository.getBlockById(blockId) !== undefined) {
369
+ continue;
370
+ }
371
+
372
+ const yblock = this.dependencies.YjsManager.getBlockById(blockId);
373
+
374
+ if (yblock === undefined) {
375
+ continue;
376
+ }
377
+
378
+ const toolName = yblock.get('type') as string;
379
+ const data = this.dependencies.YjsManager.yMapToObject(yblock.get('data') as YMap<unknown>);
380
+ const parentId = yblock.get('parentId') as string | undefined;
381
+ const targetIndex = yjsBlocks.findIndex((b) => b.id === blockId);
382
+
383
+ if (targetIndex === -1) {
384
+ continue;
385
+ }
386
+
387
+ toCreate.push({ blockId, toolName, data, parentId: parentId ?? undefined, targetIndex });
388
+ }
389
+
390
+ if (toCreate.length === 0) {
391
+ return;
392
+ }
393
+
394
+ this.withAtomicOperation(() => {
395
+ // Pass 1 — create blocks and add to array (no DOM, no RENDERED)
396
+ const created: Array<{ block: Block; targetIndex: number; parentId: string | undefined }> = [];
397
+
398
+ for (const entry of toCreate) {
399
+ const block = this.factory.composeBlock({
400
+ id: entry.blockId,
401
+ tool: entry.toolName,
402
+ data: entry.data,
403
+ parentId: entry.parentId,
404
+ bindEventsImmediately: true,
405
+ });
406
+
407
+ this.blocksStore.addToArray(entry.targetIndex, block);
408
+ created.push({ block, targetIndex: entry.targetIndex, parentId: entry.parentId });
409
+ }
410
+
411
+ // Pass 2 — activate blocks (DOM insert + RENDERED), then emit events
412
+ for (const { block, targetIndex, parentId } of created) {
413
+ this.blocksStore.activateBlock(block);
414
+ this.handlers.onBlockAdded(block, targetIndex);
415
+
416
+ if (parentId !== undefined) {
417
+ this.handlers.updateIndentation(block);
418
+ }
419
+ }
420
+ }, { extendThroughRAF: true });
421
+ }
422
+
293
423
  /**
294
424
  * Handle block remove from Yjs (undo/redo - removing a previously added block)
295
425
  */
@@ -306,17 +436,21 @@ export class BlockYjsSync {
306
436
  return;
307
437
  }
308
438
 
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);
439
+ // Keep Yjs sync state active for the full remove lifecycle so listeners
440
+ // and block.destroy handlers can detect undo/redo-originated removals.
441
+ this.withAtomicOperation(() => {
442
+ // Emit block-removed event BEFORE removal so listeners can inspect
443
+ // the block's DOM position (e.g., which table cell it's in)
444
+ this.handlers.onBlockRemoved(block, index);
312
445
 
313
- // Remove from DOM
314
- this.blocksStore.remove(index);
446
+ // Remove from DOM
447
+ this.blocksStore.remove(index);
315
448
 
316
- // If all blocks removed, insert a default block
317
- if (this.blocksStore.length === 0) {
318
- this.handlers.insertDefaultBlock(true);
319
- }
449
+ // If all blocks removed, insert a default block
450
+ if (this.blocksStore.length === 0) {
451
+ this.handlers.insertDefaultBlock(true);
452
+ }
453
+ });
320
454
  }
321
455
 
322
456
  /**
@@ -65,6 +65,9 @@ export class BlockSelection extends Module {
65
65
  b: {},
66
66
  i: {},
67
67
  u: {},
68
+ mark: {
69
+ style: true,
70
+ },
68
71
  };
69
72
 
70
73
  return composeSanitizerConfig(this.config.sanitizer as SanitizerConfig, baseConfig);
@@ -269,13 +269,21 @@ export class CrossBlockSelection extends Module {
269
269
  return;
270
270
  }
271
271
 
272
- const relatedBlock = BlockManager.getBlockByChildNode(mouseEvent.relatedTarget as Node) || this.lastSelectedBlock;
273
- const targetBlock = BlockManager.getBlockByChildNode(mouseEvent.target as Node);
272
+ const rawRelatedBlock = BlockManager.getBlockByChildNode(mouseEvent.relatedTarget as Node) || this.lastSelectedBlock;
273
+ const rawTargetBlock = BlockManager.getBlockByChildNode(mouseEvent.target as Node);
274
274
 
275
- if (!relatedBlock || !targetBlock) {
275
+ if (!rawRelatedBlock || !rawTargetBlock) {
276
276
  return;
277
277
  }
278
278
 
279
+ /**
280
+ * Resolve child blocks (e.g. paragraphs inside table cells) to their root parent.
281
+ * Without this, dragging across a table would select individual cell blocks
282
+ * from the flat blocks array instead of treating the table as a single unit.
283
+ */
284
+ const relatedBlock = BlockManager.resolveToRootBlock(rawRelatedBlock);
285
+ const targetBlock = BlockManager.resolveToRootBlock(rawTargetBlock);
286
+
279
287
  if (targetBlock === relatedBlock) {
280
288
  return;
281
289
  }
@@ -23,11 +23,19 @@ export class DragPreview {
23
23
  // Reset styles on clone
24
24
  clone.className = twMerge(PREVIEW_STYLES.content, isStretched ? 'max-w-none' : '');
25
25
 
26
+ // Set explicit width on clone so percentage-based children (e.g. table cells)
27
+ // have a sizing reference inside the position:fixed preview container
28
+ const computedWidth = contentElement.getBoundingClientRect().width;
29
+
30
+ if (computedWidth > 0) {
31
+ clone.style.width = `${computedWidth}px`;
32
+ }
33
+
26
34
  // Reset margin on the tool's rendered element (first child) to prevent offset
27
35
  const toolElement = clone.firstElementChild as HTMLElement | null;
28
36
 
29
37
  if (toolElement) {
30
- toolElement.className = twMerge(toolElement.className, '!m-0');
38
+ toolElement.className = twMerge(toolElement.className, 'm-0!');
31
39
  }
32
40
 
33
41
  preview.appendChild(clone);
@@ -100,7 +108,7 @@ export class DragPreview {
100
108
  const toolElement = clone.firstElementChild as HTMLElement | null;
101
109
 
102
110
  if (toolElement) {
103
- toolElement.className = twMerge(toolElement.className, '!m-0');
111
+ toolElement.className = twMerge(toolElement.className, 'm-0!');
104
112
  }
105
113
 
106
114
  // Position with cumulative offset