@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
@@ -0,0 +1,623 @@
1
+ import type { CellContent, LegacyCellContent, TableData } from './types';
2
+ import { isCellWithBlocks } from './types';
3
+
4
+ /**
5
+ * Validate that a string is a safe CSS color value.
6
+ *
7
+ * Accepts:
8
+ * - 3/4/6/8-digit hex: #rgb, #rgba, #rrggbb, #rrggbbaa
9
+ * - rgb/rgba: rgb(r, g, b) / rgba(r, g, b, a)
10
+ * - hsl/hsla: hsl(h, s%, l%) / hsla(h, s%, l%, a)
11
+ * - The keyword "transparent"
12
+ */
13
+ const isValidCssColor = (value: string): boolean => {
14
+ // Hex: #rgb, #rgba, #rrggbb, #rrggbbaa
15
+ if (/^#[0-9a-f]{3,4}$/i.test(value) || /^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(value)) {
16
+ return true;
17
+ }
18
+
19
+ // rgb/rgba
20
+ if (/^rgba?\(\s*[\d.]+\s*,\s*[\d.]+\s*,\s*[\d.]+\s*(,\s*[\d.]+\s*)?\)$/i.test(value)) {
21
+ return true;
22
+ }
23
+
24
+ // hsl/hsla
25
+ if (/^hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%\s*(,\s*[\d.]+\s*)?\)$/i.test(value)) {
26
+ return true;
27
+ }
28
+
29
+ // Keywords
30
+ if (value === 'transparent') {
31
+ return true;
32
+ }
33
+
34
+ return false;
35
+ };
36
+
37
+ /**
38
+ * Pure data model for table state.
39
+ *
40
+ * Holds the 2D grid of cells, column widths, and metadata flags.
41
+ * Provides O(1) block-to-cell reverse lookup via an internal map.
42
+ *
43
+ * No DOM, no API — just data.
44
+ */
45
+ export class TableModel {
46
+ private contentGrid: CellContent[][];
47
+ private withHeadingsValue: boolean;
48
+ private withHeadingColumnValue: boolean;
49
+ private stretchedValue: boolean;
50
+ private colWidthsValue: number[] | undefined;
51
+ private initialColWidthValue: number | undefined;
52
+
53
+ /** O(1) reverse lookup: blockId -> { row, col } */
54
+ private blockCellMap: Map<string, { row: number; col: number }>;
55
+
56
+ constructor(data?: Partial<TableData>) {
57
+ this.withHeadingsValue = data?.withHeadings ?? false;
58
+ this.withHeadingColumnValue = data?.withHeadingColumn ?? false;
59
+ this.stretchedValue = data?.stretched ?? false;
60
+ this.colWidthsValue = data?.colWidths ? [...data.colWidths] : undefined;
61
+ this.initialColWidthValue = data?.initialColWidth;
62
+
63
+ this.contentGrid = this.normalizeContent(data?.content);
64
+ this.blockCellMap = new Map();
65
+ this.rebuildBlockCellMap();
66
+ }
67
+
68
+ // ─── Dimension getters ──────────────────────────────────────────
69
+
70
+ get rows(): number {
71
+ return this.contentGrid.length;
72
+ }
73
+
74
+ get cols(): number {
75
+ return this.contentGrid.length > 0 ? this.contentGrid[0].length : 0;
76
+ }
77
+
78
+ // ─── Metadata getters ────────────────────────────────────────────
79
+
80
+ get withHeadings(): boolean {
81
+ return this.withHeadingsValue;
82
+ }
83
+
84
+ get withHeadingColumn(): boolean {
85
+ return this.withHeadingColumnValue;
86
+ }
87
+
88
+ get stretched(): boolean {
89
+ return this.stretchedValue;
90
+ }
91
+
92
+ get colWidths(): number[] | undefined {
93
+ return this.colWidthsValue ? [...this.colWidthsValue] : undefined;
94
+ }
95
+
96
+ get initialColWidth(): number | undefined {
97
+ return this.initialColWidthValue;
98
+ }
99
+
100
+ // ─── Snapshot ───────────────────────────────────────────────────
101
+
102
+ /**
103
+ * Return a deep copy of the current state. Mutations to the returned
104
+ * object do not affect the model.
105
+ */
106
+ snapshot(): TableData {
107
+ const base: TableData = {
108
+ withHeadings: this.withHeadingsValue,
109
+ withHeadingColumn: this.withHeadingColumnValue,
110
+ stretched: this.stretchedValue,
111
+ content: this.contentGrid.map(row =>
112
+ row.map(c => {
113
+ const cell: CellContent = { blocks: [...c.blocks] };
114
+
115
+ if (c.color !== undefined) {
116
+ cell.color = c.color;
117
+ }
118
+
119
+ if (c.textColor !== undefined) {
120
+ cell.textColor = c.textColor;
121
+ }
122
+
123
+ return cell;
124
+ })
125
+ ),
126
+ };
127
+
128
+ if (this.colWidthsValue !== undefined) {
129
+ base.colWidths = [...this.colWidthsValue];
130
+ }
131
+
132
+ if (this.initialColWidthValue !== undefined) {
133
+ base.initialColWidth = this.initialColWidthValue;
134
+ }
135
+
136
+ return base;
137
+ }
138
+
139
+ // ─── Cell operations ────────────────────────────────────────────
140
+
141
+ /**
142
+ * O(1) lookup of the cell containing a given block.
143
+ */
144
+ findCellForBlock(blockId: string): { row: number; col: number } | null {
145
+ return this.blockCellMap.get(blockId) ?? null;
146
+ }
147
+
148
+ /**
149
+ * Append a block to the given cell.
150
+ */
151
+ addBlockToCell(row: number, col: number, blockId: string): void {
152
+ if (!this.isInBounds(row, col)) {
153
+ return;
154
+ }
155
+
156
+ // Enforce invariant 5: no block in more than one cell
157
+ const existing = this.blockCellMap.get(blockId);
158
+
159
+ if (existing) {
160
+ const oldCell = this.contentGrid[existing.row][existing.col];
161
+
162
+ oldCell.blocks = oldCell.blocks.filter(id => id !== blockId);
163
+ }
164
+
165
+ this.contentGrid[row][col].blocks.push(blockId);
166
+ this.blockCellMap.set(blockId, { row, col });
167
+ }
168
+
169
+ /**
170
+ * Remove a specific block from the given cell.
171
+ */
172
+ removeBlockFromCell(row: number, col: number, blockId: string): void {
173
+ if (!this.isInBounds(row, col)) {
174
+ return;
175
+ }
176
+
177
+ const cell = this.contentGrid[row][col];
178
+ const idx = cell.blocks.indexOf(blockId);
179
+
180
+ if (idx === -1) {
181
+ return;
182
+ }
183
+
184
+ cell.blocks.splice(idx, 1);
185
+ this.blockCellMap.delete(blockId);
186
+ }
187
+
188
+ /**
189
+ * Replace all blocks in a cell at once.
190
+ */
191
+ setCellBlocks(row: number, col: number, blockIds: string[]): void {
192
+ if (!this.isInBounds(row, col)) {
193
+ return;
194
+ }
195
+
196
+ // Remove old entries from map
197
+ for (const oldId of this.contentGrid[row][col].blocks) {
198
+ this.blockCellMap.delete(oldId);
199
+ }
200
+
201
+ this.contentGrid[row][col].blocks = [...blockIds];
202
+
203
+ // Add new entries to map
204
+ for (const id of blockIds) {
205
+ this.blockCellMap.set(id, { row, col });
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Get a copy of block IDs in a cell.
211
+ */
212
+ getCellBlocks(row: number, col: number): string[] {
213
+ if (!this.isInBounds(row, col)) {
214
+ return [];
215
+ }
216
+
217
+ return [...this.contentGrid[row][col].blocks];
218
+ }
219
+
220
+ /**
221
+ * Set the background color for a cell. Pass undefined to remove.
222
+ */
223
+ setCellColor(row: number, col: number, color: string | undefined): void {
224
+ if (!this.isInBounds(row, col)) {
225
+ return;
226
+ }
227
+
228
+ if (color === undefined) {
229
+ delete this.contentGrid[row][col].color;
230
+ } else if (isValidCssColor(color)) {
231
+ this.contentGrid[row][col].color = color;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Get the background color for a cell, or undefined if none set.
237
+ */
238
+ getCellColor(row: number, col: number): string | undefined {
239
+ if (!this.isInBounds(row, col)) {
240
+ return undefined;
241
+ }
242
+
243
+ return this.contentGrid[row][col].color;
244
+ }
245
+
246
+ /**
247
+ * Set the text color for a cell. Pass undefined to remove.
248
+ */
249
+ setCellTextColor(row: number, col: number, color: string | undefined): void {
250
+ if (!this.isInBounds(row, col)) {
251
+ return;
252
+ }
253
+
254
+ if (color === undefined) {
255
+ delete this.contentGrid[row][col].textColor;
256
+ } else if (isValidCssColor(color)) {
257
+ this.contentGrid[row][col].textColor = color;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Get the text color for a cell, or undefined if none set.
263
+ */
264
+ getCellTextColor(row: number, col: number): string | undefined {
265
+ if (!this.isInBounds(row, col)) {
266
+ return undefined;
267
+ }
268
+
269
+ return this.contentGrid[row][col].textColor;
270
+ }
271
+
272
+ // ─── Row operations ─────────────────────────────────────────────
273
+
274
+ /**
275
+ * Insert an empty row. Returns instruction for the caller.
276
+ */
277
+ addRow(index?: number): { type: 'add-row'; index: number; cellsToPopulate: number } {
278
+ const clampedIndex = index === undefined
279
+ ? this.contentGrid.length
280
+ : Math.min(Math.max(0, index), this.contentGrid.length);
281
+
282
+ const colCount = this.cols;
283
+ const newRow: CellContent[] = Array.from({ length: colCount }, () => ({ blocks: [] }));
284
+
285
+ this.contentGrid.splice(clampedIndex, 0, newRow);
286
+ this.rebuildBlockCellMap();
287
+
288
+ return {
289
+ type: 'add-row',
290
+ index: clampedIndex,
291
+ cellsToPopulate: colCount,
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Remove a row. Returns the block IDs that were in it for cleanup.
297
+ */
298
+ deleteRow(index: number): { type: 'delete-row'; index: number; blocksToDelete: string[] } {
299
+ if (!this.isRowInBounds(index)) {
300
+ return { type: 'delete-row', index, blocksToDelete: [] };
301
+ }
302
+
303
+ const blocksToDelete = this.collectBlocksInRow(index);
304
+
305
+ this.contentGrid.splice(index, 1);
306
+
307
+ for (const id of blocksToDelete) {
308
+ this.blockCellMap.delete(id);
309
+ }
310
+
311
+ this.rebuildBlockCellMap();
312
+
313
+ return { type: 'delete-row', index, blocksToDelete };
314
+ }
315
+
316
+ /**
317
+ * Reorder a row. Returns instruction for the caller.
318
+ */
319
+ moveRow(from: number, to: number): { type: 'move-row'; index: number; toIndex: number } {
320
+ if (!this.isRowInBounds(from) || !this.isRowInBounds(to) || from === to) {
321
+ return { type: 'move-row', index: from, toIndex: to };
322
+ }
323
+
324
+ const [moved] = this.contentGrid.splice(from, 1);
325
+
326
+ this.contentGrid.splice(to, 0, moved);
327
+ this.rebuildBlockCellMap();
328
+
329
+ return { type: 'move-row', index: from, toIndex: to };
330
+ }
331
+
332
+ // ─── Column operations ──────────────────────────────────────────
333
+
334
+ /**
335
+ * Insert an empty column. Returns instruction with cells to populate.
336
+ */
337
+ addColumn(index?: number, width?: number): {
338
+ type: 'add-column';
339
+ index: number;
340
+ cellsToPopulate: Array<{ row: number; col: number }>;
341
+ } {
342
+ const clampedIndex = index === undefined
343
+ ? this.cols
344
+ : Math.min(Math.max(0, index), this.cols);
345
+
346
+ const cellsToPopulate: Array<{ row: number; col: number }> = [];
347
+
348
+ this.contentGrid.forEach((row, r) => {
349
+ row.splice(clampedIndex, 0, { blocks: [] });
350
+ cellsToPopulate.push({ row: r, col: clampedIndex });
351
+ });
352
+
353
+ if (this.colWidthsValue !== undefined) {
354
+ const insertWidth = width ?? 0;
355
+
356
+ this.colWidthsValue.splice(clampedIndex, 0, insertWidth);
357
+ }
358
+
359
+ this.rebuildBlockCellMap();
360
+
361
+ return {
362
+ type: 'add-column',
363
+ index: clampedIndex,
364
+ cellsToPopulate,
365
+ };
366
+ }
367
+
368
+ /**
369
+ * Remove a column. Returns block IDs for cleanup.
370
+ */
371
+ deleteColumn(index: number): { type: 'delete-column'; index: number; blocksToDelete: string[] } {
372
+ if (!this.isColInBounds(index)) {
373
+ return { type: 'delete-column', index, blocksToDelete: [] };
374
+ }
375
+
376
+ const blocksToDelete = this.collectBlocksInColumn(index);
377
+
378
+ for (const row of this.contentGrid) {
379
+ row.splice(index, 1);
380
+ }
381
+
382
+ for (const id of blocksToDelete) {
383
+ this.blockCellMap.delete(id);
384
+ }
385
+
386
+ if (this.colWidthsValue !== undefined) {
387
+ this.colWidthsValue.splice(index, 1);
388
+ }
389
+
390
+ if (this.colWidthsValue?.length === 0) {
391
+ this.colWidthsValue = undefined;
392
+ }
393
+
394
+ this.rebuildBlockCellMap();
395
+
396
+ return { type: 'delete-column', index, blocksToDelete };
397
+ }
398
+
399
+ /**
400
+ * Reorder a column. Returns instruction for the caller.
401
+ */
402
+ moveColumn(from: number, to: number): { type: 'move-column'; index: number; toIndex: number } {
403
+ if (!this.isColInBounds(from) || !this.isColInBounds(to) || from === to) {
404
+ return { type: 'move-column', index: from, toIndex: to };
405
+ }
406
+
407
+ for (const row of this.contentGrid) {
408
+ const [moved] = row.splice(from, 1);
409
+
410
+ row.splice(to, 0, moved);
411
+ }
412
+
413
+ if (this.colWidthsValue !== undefined) {
414
+ const [movedWidth] = this.colWidthsValue.splice(from, 1);
415
+
416
+ this.colWidthsValue.splice(to, 0, movedWidth);
417
+ }
418
+
419
+ this.rebuildBlockCellMap();
420
+
421
+ return { type: 'move-column', index: from, toIndex: to };
422
+ }
423
+
424
+ // ─── replaceAll ─────────────────────────────────────────────────
425
+
426
+ /**
427
+ * Replace the entire model state. Used for setData / undo / redo.
428
+ */
429
+ replaceAll(data: TableData): void {
430
+ this.withHeadingsValue = data.withHeadings;
431
+ this.withHeadingColumnValue = data.withHeadingColumn;
432
+ this.stretchedValue = data.stretched ?? false;
433
+ this.colWidthsValue = data.colWidths ? [...data.colWidths] : undefined;
434
+ this.initialColWidthValue = data.initialColWidth;
435
+
436
+ this.contentGrid = this.normalizeContent(data.content);
437
+ this.blockCellMap.clear();
438
+ this.rebuildBlockCellMap();
439
+ }
440
+
441
+ // ─── Metadata setters ──────────────────────────────────────────
442
+
443
+ setWithHeadings(value: boolean): void {
444
+ this.withHeadingsValue = value;
445
+ }
446
+
447
+ setWithHeadingColumn(value: boolean): void {
448
+ this.withHeadingColumnValue = value;
449
+ }
450
+
451
+ setStretched(value: boolean): void {
452
+ this.stretchedValue = value;
453
+ }
454
+
455
+ setColWidths(widths: number[] | undefined): void {
456
+ this.colWidthsValue = widths ? [...widths] : undefined;
457
+ }
458
+
459
+ setInitialColWidth(value: number | undefined): void {
460
+ this.initialColWidthValue = value;
461
+ }
462
+
463
+ // ─── Invariant validation ───────────────────────────────────────
464
+
465
+ /**
466
+ * Validate all model invariants. Throws if any invariant is violated.
467
+ * Useful for debugging and test assertions.
468
+ *
469
+ * Invariants checked:
470
+ * 1. Rectangular grid: all rows have the same length
471
+ * 2. colWidths sync: colWidths length matches column count when defined
472
+ * 3. blockCellMap consistency: map matches grid contents
473
+ * 4. No duplicate blocks: each block ID appears in exactly one cell
474
+ */
475
+ validateInvariants(): void {
476
+ // Invariant 1: Rectangular grid
477
+ if (this.contentGrid.length > 0) {
478
+ const expectedCols = this.contentGrid[0].length;
479
+
480
+ this.contentGrid.forEach((row, r) => {
481
+ if (row.length !== expectedCols) {
482
+ throw new Error(
483
+ `Invariant violation: row ${r} has ${row.length} columns, expected ${expectedCols}`
484
+ );
485
+ }
486
+ });
487
+ }
488
+
489
+ // Invariant 2: colWidths sync
490
+ if (
491
+ this.colWidthsValue !== undefined &&
492
+ this.contentGrid.length > 0 &&
493
+ this.colWidthsValue.length !== this.contentGrid[0].length
494
+ ) {
495
+ throw new Error(
496
+ `Invariant violation: colWidths has ${this.colWidthsValue.length} entries but grid has ${this.contentGrid[0].length} columns`
497
+ );
498
+ }
499
+
500
+ // Invariant 3 + 4: blockCellMap consistency and no duplicates
501
+ const seenBlocks = new Set<string>();
502
+
503
+ this.contentGrid.forEach((row, r) => {
504
+ row.forEach((cell, c) => {
505
+ for (const blockId of cell.blocks) {
506
+ if (seenBlocks.has(blockId)) {
507
+ throw new Error(
508
+ `Invariant violation: block "${blockId}" appears in multiple cells`
509
+ );
510
+ }
511
+ seenBlocks.add(blockId);
512
+
513
+ const mapped = this.blockCellMap.get(blockId);
514
+
515
+ if (!mapped) {
516
+ throw new Error(
517
+ `Invariant violation: block "${blockId}" at [${r},${c}] not in blockCellMap`
518
+ );
519
+ }
520
+ if (mapped.row !== r || mapped.col !== c) {
521
+ throw new Error(
522
+ `Invariant violation: block "${blockId}" at [${r},${c}] mapped to [${mapped.row},${mapped.col}]`
523
+ );
524
+ }
525
+ }
526
+ });
527
+ });
528
+
529
+ // Check map doesn't have extra entries
530
+ if (this.blockCellMap.size !== seenBlocks.size) {
531
+ throw new Error(
532
+ `Invariant violation: blockCellMap has ${this.blockCellMap.size} entries but grid has ${seenBlocks.size} blocks`
533
+ );
534
+ }
535
+ }
536
+
537
+ // ─── Private helpers ────────────────────────────────────────────
538
+
539
+ /**
540
+ * Normalize legacy content (strings) into CellContent objects.
541
+ */
542
+ private normalizeContent(content?: LegacyCellContent[][]): CellContent[][] {
543
+ if (!content || !Array.isArray(content)) {
544
+ return [];
545
+ }
546
+
547
+ return content.map(row =>
548
+ (row ?? []).map(c => this.normalizeCell(c))
549
+ );
550
+ }
551
+
552
+ /**
553
+ * Normalize a single cell: legacy strings become empty blocks arrays.
554
+ */
555
+ private normalizeCell(cell: LegacyCellContent): CellContent {
556
+ if (isCellWithBlocks(cell)) {
557
+ const normalized: CellContent = { blocks: [...cell.blocks] };
558
+
559
+ if (cell.color !== undefined && isValidCssColor(cell.color)) {
560
+ normalized.color = cell.color;
561
+ }
562
+
563
+ if (cell.textColor !== undefined && isValidCssColor(cell.textColor)) {
564
+ normalized.textColor = cell.textColor;
565
+ }
566
+
567
+ return normalized;
568
+ }
569
+
570
+ return { blocks: [] };
571
+ }
572
+
573
+ /**
574
+ * Rebuild the blockCellMap from the content grid.
575
+ * Called after any structural operation that shifts row/col indices.
576
+ */
577
+ private rebuildBlockCellMap(): void {
578
+ this.blockCellMap.clear();
579
+
580
+ this.contentGrid.forEach((row, r) => {
581
+ row.forEach((cell, c) => {
582
+ for (const blockId of cell.blocks) {
583
+ this.blockCellMap.set(blockId, { row: r, col: c });
584
+ }
585
+ });
586
+ });
587
+ }
588
+
589
+ private isInBounds(row: number, col: number): boolean {
590
+ return row >= 0 && row < this.contentGrid.length &&
591
+ col >= 0 && (this.contentGrid.length === 0 || col < this.contentGrid[row].length);
592
+ }
593
+
594
+ private isRowInBounds(index: number): boolean {
595
+ return index >= 0 && index < this.contentGrid.length;
596
+ }
597
+
598
+ private isColInBounds(index: number): boolean {
599
+ return index >= 0 && this.contentGrid.length > 0 && index < this.contentGrid[0].length;
600
+ }
601
+
602
+ private collectBlocksInRow(rowIndex: number): string[] {
603
+ const blocks: string[] = [];
604
+
605
+ for (const cell of this.contentGrid[rowIndex]) {
606
+ blocks.push(...cell.blocks);
607
+ }
608
+
609
+ return blocks;
610
+ }
611
+
612
+ private collectBlocksInColumn(colIndex: number): string[] {
613
+ const blocks: string[] = [];
614
+
615
+ for (const row of this.contentGrid) {
616
+ if (colIndex < row.length) {
617
+ blocks.push(...row[colIndex].blocks);
618
+ }
619
+ }
620
+
621
+ return blocks;
622
+ }
623
+ }