@jackuait/blok 0.13.2 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/dist/blok.cjs +1 -1
  2. package/dist/blok.iife.js +30 -30
  3. package/dist/blok.mjs +2 -2
  4. package/dist/chunks/blok-DbYIZS7_.cjs +17 -0
  5. package/dist/chunks/{blok-Du6vYONn.mjs → blok-DuYqYpF7.mjs} +2738 -2322
  6. package/dist/chunks/{constants-DUuYY64n.mjs → constants-DMVxzgHf.mjs} +390 -299
  7. package/dist/chunks/constants-DPty7Dc7.cjs +457 -0
  8. package/dist/chunks/{i18next-loader-GIrBCFrC.mjs → i18next-loader-BEWWek8j.mjs} +1 -1
  9. package/dist/chunks/{i18next-loader-DC1uN-wc.cjs → i18next-loader-BRLYf-CW.cjs} +1 -1
  10. package/dist/chunks/{lightweight-i18n-DF9Pupws.mjs → lightweight-i18n-GbzxWVgl.mjs} +9 -0
  11. package/dist/chunks/lightweight-i18n-lNYVKCfj.cjs +1 -0
  12. package/dist/chunks/messages-085GG-gT.cjs +1 -0
  13. package/dist/{messages-3j4yi-DF.mjs → chunks/messages-46QopBh4.mjs} +9 -0
  14. package/dist/chunks/messages-5kQujjf1.cjs +1 -0
  15. package/dist/chunks/{messages-CTWXEx_o.mjs → messages-6r3Lrqpf.mjs} +9 -0
  16. package/dist/chunks/messages-8vjbwhvK.cjs +1 -0
  17. package/dist/chunks/messages-ACbZDExx.cjs +1 -0
  18. package/dist/chunks/messages-B05XREvI.cjs +1 -0
  19. package/dist/chunks/{messages-CQg2_vmm.mjs → messages-B0y97-C9.mjs} +9 -0
  20. package/dist/chunks/{messages-ZHNvKiIX.mjs → messages-B26pkAIj.mjs} +9 -0
  21. package/dist/chunks/messages-B2J7kDKI.cjs +1 -0
  22. package/dist/chunks/messages-B3jMjjFG.cjs +1 -0
  23. package/dist/chunks/messages-B4_c-uS0.cjs +1 -0
  24. package/dist/chunks/messages-B6zoVYEd.cjs +1 -0
  25. package/dist/chunks/{messages-DI9Ifrgt.mjs → messages-B73uch0z.mjs} +9 -0
  26. package/dist/chunks/{messages-AgmrT2Id.mjs → messages-B74xK8Ux.mjs} +9 -0
  27. package/dist/chunks/messages-B7efcz_S2.cjs +1 -0
  28. package/dist/chunks/{messages-sqdfYuVj2.mjs → messages-B9u3UJRu2.mjs} +9 -0
  29. package/dist/chunks/messages-BB9huoLU.cjs +1 -0
  30. package/dist/{messages-Cx7sqvx02.mjs → chunks/messages-BEzCtzW3.mjs} +9 -0
  31. package/dist/chunks/messages-BGQdk2zV2.cjs +1 -0
  32. package/dist/chunks/messages-BGmUrVtN2.cjs +1 -0
  33. package/dist/chunks/messages-BH-3FR4b2.cjs +1 -0
  34. package/dist/chunks/{messages-BZ4d5yNH.mjs → messages-BHqAQF6C.mjs} +9 -0
  35. package/dist/{messages-DzkZ434Z.mjs → chunks/messages-BI_-Ub99.mjs} +9 -0
  36. package/dist/chunks/messages-BNa6_BYq2.cjs +1 -0
  37. package/dist/chunks/{messages-BvupGuDw2.mjs → messages-BOMNOs8T2.mjs} +9 -0
  38. package/dist/chunks/{messages-fSeXd6-z.mjs → messages-BPYPnKHy.mjs} +9 -0
  39. package/dist/{messages-B3PWaMcw.mjs → chunks/messages-BQmUOeft.mjs} +9 -0
  40. package/dist/chunks/messages-BRfutnCs.cjs +1 -0
  41. package/dist/chunks/messages-BT_0dgL1.cjs +1 -0
  42. package/dist/chunks/{messages-BWGqOZ9J.mjs → messages-BWSXW4jk.mjs} +9 -0
  43. package/dist/chunks/messages-BWpKvkND2.cjs +1 -0
  44. package/dist/chunks/messages-BY6gCAsA2.cjs +1 -0
  45. package/dist/{messages-BDwU9rYR2.mjs → chunks/messages-BbfZo0kw2.mjs} +9 -0
  46. package/dist/{messages-gQ5PFa2t2.mjs → chunks/messages-BfqzgRP72.mjs} +9 -0
  47. package/dist/chunks/messages-Bk2Xs7_v.cjs +1 -0
  48. package/dist/{messages-CmN2OfuB.mjs → chunks/messages-BmkavfpK.mjs} +9 -0
  49. package/dist/chunks/{messages-B1k4DgVe.mjs → messages-BmzMikmq.mjs} +9 -0
  50. package/dist/chunks/messages-BniEGAW32.cjs +1 -0
  51. package/dist/chunks/{messages-BP77P9ER.mjs → messages-BplwYBhA.mjs} +9 -0
  52. package/dist/chunks/{messages-B1xgsCNQ.cjs → messages-BqJ-xe8R.cjs} +1 -1
  53. package/dist/chunks/messages-Bry43U_U.cjs +1 -0
  54. package/dist/{messages-DFfhoDWZ2.mjs → chunks/messages-BuzGjYKw.mjs} +9 -0
  55. package/dist/chunks/messages-ByT63R7v2.cjs +1 -0
  56. package/dist/{messages-C_gdsU952.mjs → chunks/messages-C0S7KViA2.mjs} +9 -0
  57. package/dist/chunks/{messages-DzY53I67.mjs → messages-C0Tt7Odx.mjs} +9 -0
  58. package/dist/{messages-B8Tq9Txy.mjs → chunks/messages-C3A1iLjY.mjs} +9 -0
  59. package/dist/chunks/{messages-B6You-RA.mjs → messages-C3gh1rpr.mjs} +9 -0
  60. package/dist/chunks/{messages-CXxz5HYQ.mjs → messages-C7JE4SUS.mjs} +9 -0
  61. package/dist/chunks/messages-C9IWDnCh2.cjs +1 -0
  62. package/dist/chunks/messages-CBc3S61g2.cjs +1 -0
  63. package/dist/chunks/{messages-DefZ3ihx.mjs → messages-CHpkDOiI.mjs} +9 -0
  64. package/dist/{messages-BcD7xtJA.mjs → chunks/messages-CImQCp-z2.mjs} +9 -0
  65. package/dist/chunks/messages-CJ--z80t2.cjs +1 -0
  66. package/dist/chunks/messages-CNH8YFuv.cjs +1 -0
  67. package/dist/chunks/{messages-bjSJwsXK2.mjs → messages-CQ2KWTbV2.mjs} +9 -0
  68. package/dist/{messages-C8RMjnBe.mjs → chunks/messages-CWAukiLC.mjs} +9 -0
  69. package/dist/{messages-B3oFjWq3.mjs → chunks/messages-C_BUhMkZ.mjs} +9 -0
  70. package/dist/chunks/{messages-BvuEffoe.mjs → messages-CcBT1o1B.mjs} +9 -0
  71. package/dist/chunks/messages-CdqejEof.cjs +1 -0
  72. package/dist/chunks/messages-Chdejshy2.cjs +1 -0
  73. package/dist/{messages-D-rvoUSJ.mjs → chunks/messages-CijMg_B_.mjs} +9 -0
  74. package/dist/{messages-JKXCsFKZ.mjs → chunks/messages-Ciz80Z2u2.mjs} +9 -0
  75. package/dist/{messages-Dq2WEsEu.mjs → chunks/messages-CjuGKMoT.mjs} +9 -0
  76. package/dist/chunks/{messages-BxKobpJ22.mjs → messages-ClOfUECP2.mjs} +9 -0
  77. package/dist/chunks/messages-CrF-Q_ML.cjs +1 -0
  78. package/dist/chunks/messages-Cww_rBCM.cjs +1 -0
  79. package/dist/chunks/messages-CxPVYl2H2.cjs +1 -0
  80. package/dist/chunks/{messages-BA2sVGrR2.mjs → messages-Cyiqo_dk2.mjs} +9 -0
  81. package/dist/{messages-D788KtGe.mjs → chunks/messages-D-u_SWhb.mjs} +9 -0
  82. package/dist/chunks/messages-D0H-D5FQ2.cjs +1 -0
  83. package/dist/chunks/messages-D195RH3A.cjs +1 -0
  84. package/dist/chunks/{messages-Dd4nzvLj2.mjs → messages-D5-DG-_u2.mjs} +9 -0
  85. package/dist/chunks/messages-D6kDQgDP2.cjs +1 -0
  86. package/dist/{messages-CAGQtm7T.mjs → chunks/messages-D8-hiQgf.mjs} +9 -0
  87. package/dist/{messages-ftPYsH6d2.mjs → chunks/messages-D8nb9CKr2.mjs} +9 -0
  88. package/dist/{messages-DPqFBtJR.mjs → chunks/messages-DAd5yWtH2.mjs} +9 -0
  89. package/dist/chunks/messages-DAdsFLpE.cjs +1 -0
  90. package/dist/chunks/messages-DCUsbiUQ.cjs +1 -0
  91. package/dist/{messages-AycxTjmw.mjs → chunks/messages-DCVZaAT9.mjs} +9 -0
  92. package/dist/chunks/messages-DHXttezt2.cjs +1 -0
  93. package/dist/chunks/messages-DIgxCixa.cjs +1 -0
  94. package/dist/chunks/messages-DJ0Wr-Bt2.cjs +1 -0
  95. package/dist/chunks/messages-DJBdlmxR.cjs +1 -0
  96. package/dist/chunks/{messages-5AxgjKgf2.mjs → messages-DJm-uLwr2.mjs} +9 -0
  97. package/dist/chunks/{messages-BVarbXYD2.mjs → messages-DKF4qWhw2.mjs} +9 -0
  98. package/dist/chunks/messages-DLAWSVlE.cjs +1 -0
  99. package/dist/{messages-Bj03XD-02.mjs → chunks/messages-DLI8m6z_2.mjs} +9 -0
  100. package/dist/{messages-CTzO11jz.mjs → chunks/messages-DNiWBVQr.mjs} +9 -0
  101. package/dist/chunks/messages-DNsgR3ZH.cjs +1 -0
  102. package/dist/{messages-CgaGkQi32.mjs → chunks/messages-DPI5-TrO2.mjs} +9 -0
  103. package/dist/chunks/{messages-CyCl8la6.mjs → messages-DQ1z_srP.mjs} +9 -0
  104. package/dist/chunks/{messages-C4bHKGnB2.mjs → messages-DSxYO-_j2.mjs} +9 -0
  105. package/dist/chunks/messages-DTrrEqf02.cjs +1 -0
  106. package/dist/chunks/{messages-D7YPlNAK.mjs → messages-DYLfoakZ.mjs} +9 -0
  107. package/dist/chunks/messages-DZCWq8nI2.cjs +1 -0
  108. package/dist/{messages-PQVh93mt.mjs → chunks/messages-D_JT9POv.mjs} +9 -0
  109. package/dist/chunks/{messages-Uc3Uc2862.mjs → messages-DaTniq4w2.mjs} +9 -0
  110. package/dist/{messages-DiL61awK.mjs → chunks/messages-De2tXe0o.mjs} +9 -0
  111. package/dist/chunks/{messages-l5xHQb_m.mjs → messages-DiwyN3uU.mjs} +9 -0
  112. package/dist/chunks/messages-DkQGwmom.cjs +1 -0
  113. package/dist/chunks/{messages-B6t1xShv.mjs → messages-Dl-C7Pdf.mjs} +9 -0
  114. package/dist/chunks/{messages-bSVgJ1cu.mjs → messages-DlDyYt5x.mjs} +9 -0
  115. package/dist/chunks/messages-Dmhwr_Ow2.cjs +1 -0
  116. package/dist/chunks/messages-DrQQ1Hky.cjs +1 -0
  117. package/dist/chunks/messages-DtQ_pCWn.cjs +1 -0
  118. package/dist/{messages-uy3FE24_2.mjs → chunks/messages-Du-inLpi2.mjs} +9 -0
  119. package/dist/{messages-DqXJJPx9.mjs → chunks/messages-Duu9LiF32.mjs} +9 -0
  120. package/dist/chunks/messages-DxCc14pu2.cjs +1 -0
  121. package/dist/chunks/{messages-DNqFlfOd.mjs → messages-Dz3cVqxu.mjs} +9 -0
  122. package/dist/chunks/messages-EJkYSgsP.cjs +1 -0
  123. package/dist/chunks/messages-F3QeKduK.cjs +1 -0
  124. package/dist/chunks/messages-HfCZ1oPV.cjs +1 -0
  125. package/dist/chunks/{messages-BVZONUH9.mjs → messages-Ji3WDjrp.mjs} +9 -0
  126. package/dist/chunks/messages-MTnjMh07.cjs +1 -0
  127. package/dist/{messages-DL7JAwpC.mjs → chunks/messages-NU3n_1t9.mjs} +9 -0
  128. package/dist/chunks/messages-NpCBoXqi.cjs +1 -0
  129. package/dist/chunks/messages-PMZIH3mH.cjs +1 -0
  130. package/dist/{messages-7fwJIrld2.mjs → chunks/messages-TFu4tdzc2.mjs} +9 -0
  131. package/dist/{messages-BSHFypGE2.mjs → chunks/messages-Ti4ZdnqM2.mjs} +9 -0
  132. package/dist/chunks/messages-UOTfsDv3.cjs +1 -0
  133. package/dist/chunks/messages-WDBEGpNu.cjs +1 -0
  134. package/dist/chunks/messages-Zg6PFQ87.cjs +1 -0
  135. package/dist/chunks/messages-bdVhFVzt2.cjs +1 -0
  136. package/dist/chunks/messages-dnnjc_oR2.cjs +1 -0
  137. package/dist/{messages-BmWP4vpV2.mjs → chunks/messages-fI_5bRwV2.mjs} +9 -0
  138. package/dist/{messages-CIjSE2_O2.mjs → chunks/messages-g80Luov_2.mjs} +9 -0
  139. package/dist/chunks/messages-hlzAtKI5.cjs +1 -0
  140. package/dist/chunks/messages-kuh4lSUE2.cjs +1 -0
  141. package/dist/chunks/messages-m20Eebmt2.cjs +1 -0
  142. package/dist/chunks/messages-o1__8Rul.cjs +1 -0
  143. package/dist/chunks/messages-pBYvH88_.cjs +1 -0
  144. package/dist/chunks/{messages-CMRjQYxi2.mjs → messages-u1hpTCvK2.mjs} +9 -0
  145. package/dist/chunks/{messages-BoCUgrkI.mjs → messages-vq5ZvFTF.mjs} +9 -0
  146. package/dist/chunks/tools-BuCT1-J0.cjs +116 -0
  147. package/dist/chunks/{tools-Tt-NXbRZ.mjs → tools-BwY9bTlg.mjs} +1599 -1460
  148. package/dist/full.cjs +1 -1
  149. package/dist/full.mjs +13 -13
  150. package/dist/locales.mjs +76 -67
  151. package/dist/{chunks/messages-DHdoaaFq2.mjs → messages-2hSOMnir2.mjs} +9 -0
  152. package/dist/{chunks/messages-DlcI-0Sy.mjs → messages-3VCxXECx.mjs} +9 -0
  153. package/dist/{messages-CxX23Jsk2.mjs → messages-8xGEIh_m2.mjs} +9 -0
  154. package/dist/{chunks/messages-DmErSGPk2.mjs → messages-B-QcHnuI2.mjs} +9 -0
  155. package/dist/{messages-BKCuCTFM.mjs → messages-B8zoeMf9.mjs} +9 -0
  156. package/dist/{chunks/messages-BmdkDNYv.mjs → messages-B9CF0GcE.mjs} +9 -0
  157. package/dist/{messages-1ufJbdRv2.mjs → messages-B9W0pRCr2.mjs} +9 -0
  158. package/dist/{messages-DQik3_xv2.mjs → messages-BFM2ljkf2.mjs} +9 -0
  159. package/dist/{chunks/messages-BNxTGhHu.mjs → messages-BLm4FSbz2.mjs} +9 -0
  160. package/dist/{chunks/messages-GHsufIGi2.mjs → messages-BLpeYPKv2.mjs} +9 -0
  161. package/dist/{messages-D_WzyzUt.mjs → messages-BM73sjxy.mjs} +9 -0
  162. package/dist/{messages-CX8egsiA2.mjs → messages-BOpqKi6Y2.mjs} +9 -0
  163. package/dist/{chunks/messages-eyGOcbhV.mjs → messages-BUGdUeTK.mjs} +9 -0
  164. package/dist/{chunks/messages-DhZwMl9x2.mjs → messages-BZm7MzSJ2.mjs} +9 -0
  165. package/dist/{messages-C31VIrlL.mjs → messages-BtBhP1sv.mjs} +9 -0
  166. package/dist/{messages-D1Fjr4OK2.mjs → messages-BthFu3bN2.mjs} +9 -0
  167. package/dist/{chunks/messages-COMdnGQV2.mjs → messages-BurjwjCQ2.mjs} +9 -0
  168. package/dist/{messages-DTkd9ND8.mjs → messages-ByIHkus-.mjs} +9 -0
  169. package/dist/{messages-C9GFRcVj.mjs → messages-ByVKFMH3.mjs} +9 -0
  170. package/dist/{chunks/messages-B5CFhyI8.mjs → messages-C6FDRqcf.mjs} +9 -0
  171. package/dist/{chunks/messages-OSIAf0Wk2.mjs → messages-C9dJ_N542.mjs} +9 -0
  172. package/dist/{messages-CRR1VRO6.mjs → messages-CCw8aJgW.mjs} +9 -0
  173. package/dist/{chunks/messages-AtUsRyWK2.mjs → messages-CO3NEwf22.mjs} +9 -0
  174. package/dist/{chunks/messages-CcboYTP8.mjs → messages-CPB2xkpC.mjs} +9 -0
  175. package/dist/{messages-CITYhXUz.mjs → messages-CPDsEORY.mjs} +9 -0
  176. package/dist/{chunks/messages-B2zrJyAc2.mjs → messages-CYyPOGy7.mjs} +9 -0
  177. package/dist/{chunks/messages-C03LZxma.mjs → messages-CZAODRjY.mjs} +9 -0
  178. package/dist/{messages-Duk7VVeY.mjs → messages-C_rns7Ku.mjs} +9 -0
  179. package/dist/{chunks/messages-CdWXgq_r.mjs → messages-CaUUIQvU.mjs} +9 -0
  180. package/dist/{messages-Cp0fjsey.mjs → messages-Cc-A7Vip.mjs} +9 -0
  181. package/dist/{chunks/messages-B7KbtBAE.mjs → messages-Ccf81JsG.mjs} +9 -0
  182. package/dist/{chunks/messages-rEsI_fAk.mjs → messages-CerTDuSC.mjs} +9 -0
  183. package/dist/{messages-BkTBwYet.mjs → messages-CgmDwRRE.mjs} +9 -0
  184. package/dist/{chunks/messages-DsyO86r3.mjs → messages-Cn3PWtLp.mjs} +9 -0
  185. package/dist/{chunks/messages-CWTFEPbA2.mjs → messages-Cu1rTEkB.mjs} +9 -0
  186. package/dist/{messages-oIa8wahx.mjs → messages-CxEHfUNH.mjs} +9 -0
  187. package/dist/{messages-C_mN4lx0.mjs → messages-D1B41i8X.mjs} +9 -0
  188. package/dist/{messages-NVepzgE3.mjs → messages-D6954zcl.mjs} +9 -0
  189. package/dist/{messages-C2TRJroV2.mjs → messages-DBwWssfL2.mjs} +9 -0
  190. package/dist/{messages-Cdgz3urh.mjs → messages-DFHRMYke.mjs} +9 -0
  191. package/dist/{messages-DXCyNanc2.mjs → messages-DJVrq0Xa2.mjs} +9 -0
  192. package/dist/{chunks/messages-OYoN_rp1.mjs → messages-DMJz28kt.mjs} +9 -0
  193. package/dist/{chunks/messages-B_U10evN2.mjs → messages-DOnOpXFJ2.mjs} +9 -0
  194. package/dist/{messages-5USazVPA2.mjs → messages-DR1Tf-Kj2.mjs} +9 -0
  195. package/dist/{chunks/messages-C4sIqArW2.mjs → messages-DSUXwuzH2.mjs} +9 -0
  196. package/dist/{chunks/messages-DGHTTk1S2.mjs → messages-DTNO7tw1.mjs} +9 -0
  197. package/dist/{messages-Ddz6eH0-2.mjs → messages-DWBtyom42.mjs} +9 -0
  198. package/dist/{messages-DKsyrVp5.mjs → messages-DZQALZIU.mjs} +9 -0
  199. package/dist/{chunks/messages-V8K7-1l2.mjs → messages-D_i86vbQ.mjs} +9 -0
  200. package/dist/{chunks/messages-BmiN0JGP2.mjs → messages-Da1VKvvf2.mjs} +9 -0
  201. package/dist/{messages-D5qgCWmB2.mjs → messages-Ddimgqvp2.mjs} +9 -0
  202. package/dist/{chunks/messages-MPF8o3EP.mjs → messages-DdmXBLUC.mjs} +9 -0
  203. package/dist/{messages-D4U4wkYM.mjs → messages-Devcsnrv.mjs} +9 -0
  204. package/dist/{chunks/messages-DiGsu5XN.mjs → messages-DhS-j4Hp.mjs} +9 -0
  205. package/dist/{messages-BGzpwNrz.mjs → messages-DrRVvVPI.mjs} +9 -0
  206. package/dist/{chunks/messages-Z9mDYT3w.mjs → messages-Ds4gQbVR2.mjs} +9 -0
  207. package/dist/{chunks/messages-vQ5kblO8.mjs → messages-DxkB7LL6.mjs} +9 -0
  208. package/dist/{chunks/messages-LTkIIrSe2.mjs → messages-GgHHrLBU2.mjs} +9 -0
  209. package/dist/{messages-CntTlSE22.mjs → messages-IycPaZe22.mjs} +9 -0
  210. package/dist/{messages-BtDz-sw92.mjs → messages-ST8f_nDx2.mjs} +9 -0
  211. package/dist/{messages-CgVEHCQ-.mjs → messages-SV8lf6F0.mjs} +9 -0
  212. package/dist/{messages-Bt4TLGth.mjs → messages-lVrQmsky.mjs} +9 -0
  213. package/dist/{messages-rpO1POP02.mjs → messages-lcoIGSX82.mjs} +9 -0
  214. package/dist/{chunks/messages-BuYnNUtU2.mjs → messages-mqRVfUUq.mjs} +9 -0
  215. package/dist/{chunks/messages-C24IC_eR.mjs → messages-qWFvoPY1.mjs} +9 -0
  216. package/dist/{messages-CacRpQpm.mjs → messages-yte1QPX5.mjs} +9 -0
  217. package/dist/{messages-DNlrcG5Z.mjs → messages-zyiti_A8.mjs} +9 -0
  218. package/dist/react.cjs +1 -1
  219. package/dist/react.mjs +1 -1
  220. package/dist/tools.cjs +1 -1
  221. package/dist/tools.mjs +3 -3
  222. package/package.json +1 -1
  223. package/src/components/blocks.ts +30 -7
  224. package/src/components/i18n/locales/am/messages.json +9 -0
  225. package/src/components/i18n/locales/ar/messages.json +9 -0
  226. package/src/components/i18n/locales/az/messages.json +9 -0
  227. package/src/components/i18n/locales/bg/messages.json +9 -0
  228. package/src/components/i18n/locales/bn/messages.json +9 -0
  229. package/src/components/i18n/locales/bs/messages.json +9 -0
  230. package/src/components/i18n/locales/cs/messages.json +9 -0
  231. package/src/components/i18n/locales/da/messages.json +9 -0
  232. package/src/components/i18n/locales/de/messages.json +9 -0
  233. package/src/components/i18n/locales/dv/messages.json +9 -0
  234. package/src/components/i18n/locales/el/messages.json +9 -0
  235. package/src/components/i18n/locales/en/messages.json +9 -0
  236. package/src/components/i18n/locales/es/messages.json +9 -0
  237. package/src/components/i18n/locales/et/messages.json +9 -0
  238. package/src/components/i18n/locales/fa/messages.json +9 -0
  239. package/src/components/i18n/locales/fi/messages.json +9 -0
  240. package/src/components/i18n/locales/fil/messages.json +9 -0
  241. package/src/components/i18n/locales/fr/messages.json +9 -0
  242. package/src/components/i18n/locales/gu/messages.json +9 -0
  243. package/src/components/i18n/locales/he/messages.json +9 -0
  244. package/src/components/i18n/locales/hi/messages.json +9 -0
  245. package/src/components/i18n/locales/hr/messages.json +9 -0
  246. package/src/components/i18n/locales/hu/messages.json +9 -0
  247. package/src/components/i18n/locales/hy/messages.json +9 -0
  248. package/src/components/i18n/locales/id/messages.json +9 -0
  249. package/src/components/i18n/locales/it/messages.json +9 -0
  250. package/src/components/i18n/locales/ja/messages.json +9 -0
  251. package/src/components/i18n/locales/ka/messages.json +9 -0
  252. package/src/components/i18n/locales/km/messages.json +9 -0
  253. package/src/components/i18n/locales/kn/messages.json +9 -0
  254. package/src/components/i18n/locales/ko/messages.json +9 -0
  255. package/src/components/i18n/locales/ku/messages.json +9 -0
  256. package/src/components/i18n/locales/lo/messages.json +9 -0
  257. package/src/components/i18n/locales/lt/messages.json +9 -0
  258. package/src/components/i18n/locales/lv/messages.json +9 -0
  259. package/src/components/i18n/locales/mk/messages.json +9 -0
  260. package/src/components/i18n/locales/ml/messages.json +9 -0
  261. package/src/components/i18n/locales/mn/messages.json +9 -0
  262. package/src/components/i18n/locales/mr/messages.json +9 -0
  263. package/src/components/i18n/locales/ms/messages.json +9 -0
  264. package/src/components/i18n/locales/my/messages.json +9 -0
  265. package/src/components/i18n/locales/ne/messages.json +9 -0
  266. package/src/components/i18n/locales/nl/messages.json +9 -0
  267. package/src/components/i18n/locales/no/messages.json +9 -0
  268. package/src/components/i18n/locales/pa/messages.json +9 -0
  269. package/src/components/i18n/locales/pl/messages.json +9 -0
  270. package/src/components/i18n/locales/ps/messages.json +9 -0
  271. package/src/components/i18n/locales/pt/messages.json +9 -0
  272. package/src/components/i18n/locales/ro/messages.json +9 -0
  273. package/src/components/i18n/locales/ru/messages.json +9 -0
  274. package/src/components/i18n/locales/sd/messages.json +9 -0
  275. package/src/components/i18n/locales/si/messages.json +9 -0
  276. package/src/components/i18n/locales/sk/messages.json +9 -0
  277. package/src/components/i18n/locales/sl/messages.json +9 -0
  278. package/src/components/i18n/locales/sq/messages.json +9 -0
  279. package/src/components/i18n/locales/sr/messages.json +9 -0
  280. package/src/components/i18n/locales/sv/messages.json +9 -0
  281. package/src/components/i18n/locales/sw/messages.json +9 -0
  282. package/src/components/i18n/locales/ta/messages.json +9 -0
  283. package/src/components/i18n/locales/te/messages.json +9 -0
  284. package/src/components/i18n/locales/th/messages.json +9 -0
  285. package/src/components/i18n/locales/tr/messages.json +9 -0
  286. package/src/components/i18n/locales/ug/messages.json +9 -0
  287. package/src/components/i18n/locales/uk/messages.json +9 -0
  288. package/src/components/i18n/locales/ur/messages.json +9 -0
  289. package/src/components/i18n/locales/vi/messages.json +9 -0
  290. package/src/components/i18n/locales/yi/messages.json +9 -0
  291. package/src/components/i18n/locales/zh/messages.json +9 -0
  292. package/src/components/icons/index.ts +24 -0
  293. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +67 -6
  294. package/src/components/modules/blockManager/hierarchy.ts +150 -8
  295. package/src/components/modules/blockManager/operations.ts +207 -8
  296. package/src/components/modules/blockManager/yjs-sync.ts +36 -1
  297. package/src/components/modules/caret.ts +205 -31
  298. package/src/components/modules/drag/DragController.ts +219 -2
  299. package/src/components/modules/drag/a11y/DragA11y.ts +9 -1
  300. package/src/components/modules/drag/state/DragStateMachine.ts +1 -1
  301. package/src/components/modules/drag/target/DropTargetDetector.ts +513 -3
  302. package/src/components/modules/drag/utils/drag.constants.ts +9 -0
  303. package/src/components/modules/toolbar/blockSettings.ts +75 -5
  304. package/src/components/modules/toolbar/index.ts +12 -1
  305. package/src/components/modules/tools.ts +57 -1
  306. package/src/components/modules/uiControllers/controllers/blockHover.ts +24 -2
  307. package/src/components/modules/uiControllers/controllers/selection.ts +50 -0
  308. package/src/components/utils/blocks.ts +21 -7
  309. package/src/styles/colors.css +3 -0
  310. package/src/styles/columns.css +105 -0
  311. package/src/styles/main.css +48 -3
  312. package/src/styles/popover-animation.css +8 -0
  313. package/src/tools/column/index.ts +189 -0
  314. package/src/tools/column/types.ts +26 -0
  315. package/src/tools/column-drop.ts +273 -0
  316. package/src/tools/column-list/index.ts +142 -0
  317. package/src/tools/column-list/types.ts +17 -0
  318. package/src/tools/columns/index.ts +21 -0
  319. package/src/tools/columns-shared.ts +343 -0
  320. package/src/tools/divider/index.ts +1 -0
  321. package/src/tools/index.ts +5 -0
  322. package/src/tools/nested-blocks.ts +23 -2
  323. package/src/tools/table/table-restrictions.ts +1 -1
  324. package/types/tools/column-list.d.ts +15 -0
  325. package/types/tools/column.d.ts +13 -0
  326. package/types/tools/tool.d.ts +12 -0
  327. package/types/tools-entry.d.ts +15 -0
  328. package/dist/chunks/blok-Xu27QC2G.cjs +0 -17
  329. package/dist/chunks/constants-DDTVRO2H.cjs +0 -457
  330. package/dist/chunks/lightweight-i18n-Bbl1cNYK.cjs +0 -1
  331. package/dist/chunks/messages-1qYt6EhZ2.cjs +0 -1
  332. package/dist/chunks/messages-2fxvN3Nb.cjs +0 -1
  333. package/dist/chunks/messages-B1Vs2HmR.cjs +0 -1
  334. package/dist/chunks/messages-B780gS332.cjs +0 -1
  335. package/dist/chunks/messages-B7T5Notn.cjs +0 -1
  336. package/dist/chunks/messages-BE9aZQ1Q.cjs +0 -1
  337. package/dist/chunks/messages-BIQpTYfm.cjs +0 -1
  338. package/dist/chunks/messages-BIRvUJ0t.cjs +0 -1
  339. package/dist/chunks/messages-BIodfkjv.cjs +0 -1
  340. package/dist/chunks/messages-BL-zBcuM.cjs +0 -1
  341. package/dist/chunks/messages-BQr0zpmu2.cjs +0 -1
  342. package/dist/chunks/messages-BTHi6rVW2.cjs +0 -1
  343. package/dist/chunks/messages-BZOAdcmO2.cjs +0 -1
  344. package/dist/chunks/messages-BbsGeVs_2.cjs +0 -1
  345. package/dist/chunks/messages-Bc1NYJVS.cjs +0 -1
  346. package/dist/chunks/messages-BdORNG8X.cjs +0 -1
  347. package/dist/chunks/messages-Bg2kheTv.cjs +0 -1
  348. package/dist/chunks/messages-Bj1eQtQw.cjs +0 -1
  349. package/dist/chunks/messages-BkJJCHNK.cjs +0 -1
  350. package/dist/chunks/messages-BkdGQfIX.cjs +0 -1
  351. package/dist/chunks/messages-BkgsB-cj2.cjs +0 -1
  352. package/dist/chunks/messages-Bn0vGFEP.cjs +0 -1
  353. package/dist/chunks/messages-BoIUXwWe.cjs +0 -1
  354. package/dist/chunks/messages-BpDEh8rW.cjs +0 -1
  355. package/dist/chunks/messages-BwknPZJ8.cjs +0 -1
  356. package/dist/chunks/messages-ByicRCge2.cjs +0 -1
  357. package/dist/chunks/messages-C2oBmyTn.cjs +0 -1
  358. package/dist/chunks/messages-C9wBMmxr2.cjs +0 -1
  359. package/dist/chunks/messages-CDK5-8vW.cjs +0 -1
  360. package/dist/chunks/messages-CKHbPcfh.cjs +0 -1
  361. package/dist/chunks/messages-CNFfwTfw.cjs +0 -1
  362. package/dist/chunks/messages-CNiuofck2.cjs +0 -1
  363. package/dist/chunks/messages-CWszAGkF.cjs +0 -1
  364. package/dist/chunks/messages-CWzQNagc2.cjs +0 -1
  365. package/dist/chunks/messages-CYf1gv722.cjs +0 -1
  366. package/dist/chunks/messages-CfcCwbQo.cjs +0 -1
  367. package/dist/chunks/messages-CkvqOnAR.cjs +0 -1
  368. package/dist/chunks/messages-Cnrug7nz2.cjs +0 -1
  369. package/dist/chunks/messages-Cr51zCHy.cjs +0 -1
  370. package/dist/chunks/messages-CsLvvl2F2.cjs +0 -1
  371. package/dist/chunks/messages-CuO-Rx4g.cjs +0 -1
  372. package/dist/chunks/messages-Cv4I3k1W2.cjs +0 -1
  373. package/dist/chunks/messages-CwHRv6g8.cjs +0 -1
  374. package/dist/chunks/messages-CzXnHfGb2.cjs +0 -1
  375. package/dist/chunks/messages-D2QxLx1a.cjs +0 -1
  376. package/dist/chunks/messages-D4LUPpX_.cjs +0 -1
  377. package/dist/chunks/messages-DJFL-bxd2.cjs +0 -1
  378. package/dist/chunks/messages-DLEeNpMi2.cjs +0 -1
  379. package/dist/chunks/messages-DTNU_cq0.cjs +0 -1
  380. package/dist/chunks/messages-DVKsYqpJ.cjs +0 -1
  381. package/dist/chunks/messages-DXlfz-nC.cjs +0 -1
  382. package/dist/chunks/messages-DbA_Zja3.cjs +0 -1
  383. package/dist/chunks/messages-Dc2TTEx_2.cjs +0 -1
  384. package/dist/chunks/messages-DeDMMmRC.cjs +0 -1
  385. package/dist/chunks/messages-DlQNXqzr.cjs +0 -1
  386. package/dist/chunks/messages-DqMqZLcn.cjs +0 -1
  387. package/dist/chunks/messages-DqqDuEE22.cjs +0 -1
  388. package/dist/chunks/messages-DzppdmWe.cjs +0 -1
  389. package/dist/chunks/messages-HzLF-BQL2.cjs +0 -1
  390. package/dist/chunks/messages-Jb9n97oP2.cjs +0 -1
  391. package/dist/chunks/messages-K_vSdSoF2.cjs +0 -1
  392. package/dist/chunks/messages-Nh7wrRdm.cjs +0 -1
  393. package/dist/chunks/messages-QHS-Ydg_.cjs +0 -1
  394. package/dist/chunks/messages-SpiG5vT-2.cjs +0 -1
  395. package/dist/chunks/messages-XFaJzdhP2.cjs +0 -1
  396. package/dist/chunks/messages-y03BGg692.cjs +0 -1
  397. package/dist/chunks/tools-DOuVjow8.cjs +0 -116
@@ -10,7 +10,7 @@ import { ListItemDepth } from '../utils/ListItemDepth';
10
10
 
11
11
  export interface DropTarget {
12
12
  block: Block;
13
- edge: 'top' | 'bottom';
13
+ edge: 'top' | 'bottom' | 'left' | 'right';
14
14
  depth: number;
15
15
  parentId: string | null;
16
16
  }
@@ -146,12 +146,34 @@ export class DropTargetDetector {
146
146
  clientY: number,
147
147
  sourceBlock: Block
148
148
  ): DropTarget | null {
149
- const { block: targetBlock, holder: blockHolder } = this.findDropTargetBlock(elementUnderCursor, clientX, clientY);
149
+ // A drop on the inter-column gutter (a resize separator) inserts a NEW column
150
+ // between the two columns it divides — the natural "drop on the divider"
151
+ // gesture. Without this, the separator resolves to the column_list holder and
152
+ // the side-drop is rejected, leaving the gutter a dead zone. Runs first, before
153
+ // findDropTargetBlock climbs to the column_list.
154
+ const gapTarget = this.determineColumnGapTarget(elementUnderCursor, clientY);
155
+
156
+ if (gapTarget && gapTarget.block !== sourceBlock) {
157
+ return gapTarget;
158
+ }
159
+
160
+ const resolved = this.findDropTargetBlock(elementUnderCursor, clientX, clientY);
150
161
 
151
- if (!blockHolder || !targetBlock || targetBlock === sourceBlock) {
162
+ if (!resolved.holder || !resolved.block || resolved.block === sourceBlock) {
152
163
  return null;
153
164
  }
154
165
 
166
+ // Empty space below a column's blocks resolves (via closest) to the column
167
+ // CONTAINER block, whose holder is itself a [data-blok-element]. Left as-is,
168
+ // a top/bottom drop on the container shows an indicator at the column's
169
+ // bottom edge AND reparents at the column_list level — spawning a NEW column
170
+ // beside the one the cursor is over (resolveParentForDrop reads the
171
+ // container's parentId). Redirect into the column instead: target its LAST
172
+ // child with a bottom edge, so the single indicator and the drop both land at
173
+ // the end of the column (stack INTO it), matching a drop over a real block.
174
+ const { block: targetBlock, holder: blockHolder } =
175
+ this.redirectColumnContainerTarget(resolved.block, resolved.holder);
176
+
155
177
  // Prevent dropping into the middle of a multi-block selection
156
178
  if (this.sourceBlocks.length > 1 && this.sourceBlocks.includes(targetBlock)) {
157
179
  return null;
@@ -171,6 +193,16 @@ export class DropTargetDetector {
171
193
  return this.redirectToTableBlock(targetCellContainer, clientY);
172
194
  }
173
195
 
196
+ // Horizontal (side) drop detection: when the cursor hovers near the left/right
197
+ // edge of a target within its central vertical band, route the drop sideways so
198
+ // the integrator can create / extend a column layout. Columns stack below 651px,
199
+ // so disable side-drops entirely on narrow viewports.
200
+ const horizontalTarget = this.determineHorizontalTarget(targetBlock, blockHolder, clientX, clientY);
201
+
202
+ if (horizontalTarget) {
203
+ return horizontalTarget;
204
+ }
205
+
174
206
  // Determine edge (top or bottom half of block)
175
207
  const rect = blockHolder.getBoundingClientRect();
176
208
  const isTopHalf = clientY < rect.top + rect.height / 2;
@@ -201,6 +233,484 @@ export class DropTargetDetector {
201
233
  return this.resolveToggleNesting({ block: targetBlock, edge, depth: targetDepth, parentId: null }, elementUnderCursor);
202
234
  }
203
235
 
236
+ /**
237
+ * Detects a horizontal (side) drop near the left/right edge of the target.
238
+ *
239
+ * Returns a DropTarget with edge 'left' or 'right' when the cursor is on the
240
+ * left/right side of the content box — the outer DRAG_CONFIG.sideZoneRatio of
241
+ * the content width (floored at sideZoneMin) AND the whole margin beyond it, at
242
+ * any distance — while inside the central vertical band (DRAG_CONFIG.sideBandRatio
243
+ * of the block height) to avoid fighting top/bottom near corners. Only the
244
+ * central reorder band between the two side zones falls through to top/bottom.
245
+ * Side-drops are disabled below 651px (columns stack) and for container blocks
246
+ * (column / column_list), which are never drop targets.
247
+ *
248
+ * @param targetBlock - The resolved target block
249
+ * @param blockHolder - The target block's holder element
250
+ * @param clientX - Cursor X position
251
+ * @param clientY - Cursor Y position
252
+ * @returns A horizontal DropTarget, or null to fall through to top/bottom logic
253
+ */
254
+ private determineHorizontalTarget(
255
+ targetBlock: Block,
256
+ blockHolder: HTMLElement,
257
+ clientX: number,
258
+ clientY: number
259
+ ): DropTarget | null {
260
+ // Columns stack below this breakpoint — no side-by-side layout.
261
+ if (window.innerWidth < 651) {
262
+ return null;
263
+ }
264
+
265
+ // A drop in the OUTER editor margin beside a whole column_list resolves (via
266
+ // elementFromPoint hitting the full-width container holder, not a narrow
267
+ // column child, which only lives inside the content row) to the column_list
268
+ // block itself. Treat the entire left/right margin as a new-column dropzone —
269
+ // identical to a standalone block in an empty editor — so existing columns
270
+ // get the SAME outer dropzone: far-left prepends a column at the row's start,
271
+ // far-right appends one at its end.
272
+ if (targetBlock.name === 'column_list') {
273
+ return this.outerColumnListTarget(targetBlock, blockHolder, clientX, clientY);
274
+ }
275
+
276
+ // The column container itself is never a drop target.
277
+ if (targetBlock.name === 'column') {
278
+ return null;
279
+ }
280
+
281
+ // Measure side zones against the visible content box, not the holder. The
282
+ // holder spans the full editor width (gutters included) while content is
283
+ // centered/narrower, so holder-edge zones never sit where the user actually
284
+ // drops. Fall back to the holder when no content element exists (test stubs,
285
+ // tools without the standard wrapper).
286
+ const contentEl = blockHolder.querySelector('[data-blok-element-content]');
287
+ const rect = contentEl instanceof HTMLElement
288
+ ? contentEl.getBoundingClientRect()
289
+ : blockHolder.getBoundingClientRect();
290
+
291
+ // A block INSIDE a column_list gives a SINGLE, unambiguous indicator over a
292
+ // column body: it always falls through to a top/bottom reorder, which
293
+ // reparents the dropped block INTO that column (resolveParentForDrop). The
294
+ // ONLY side-drop (new column) that fires over a body is at the OUTER edge of
295
+ // the whole row — the left edge of the first column or the right edge of the
296
+ // last — which appends a column at the start/end. Between-column insertion is
297
+ // the gutter separator's job (determineColumnGapTarget, runs first). Inner
298
+ // body edges deliberately do NOT side-drop: they used to, which made the
299
+ // indicator flip between a horizontal "stack in" line and a vertical "new
300
+ // column" line as the cursor crossed a column boundary — read by users as
301
+ // "two drop lines". This removes that flip.
302
+ const columnsContainer = blockHolder.closest('[data-blok-columns]');
303
+
304
+ if (columnsContainer instanceof HTMLElement) {
305
+ const bandInset = rect.height * (1 - DRAG_CONFIG.sideBandRatio) / 2;
306
+ const inBand = clientY >= rect.top + bandInset && clientY <= rect.bottom - bandInset;
307
+
308
+ if (!inBand) {
309
+ return null;
310
+ }
311
+
312
+ const sideZone = Math.max(rect.width * DRAG_CONFIG.sideZoneRatio, DRAG_CONFIG.sideZoneMin);
313
+ const nearLeft = clientX <= rect.left + sideZone;
314
+ const nearRight = clientX >= rect.right - sideZone;
315
+
316
+ // Left edge fires only on the FIRST column, right edge only on the LAST —
317
+ // the row's outer edges. Every inner position (including inner edges)
318
+ // falls through to into-column.
319
+ if (nearLeft && this.isFirstColumnChild(targetBlock)) {
320
+ return { block: targetBlock, edge: 'left', depth: 0, parentId: this.findEnclosingColumnId(targetBlock) };
321
+ }
322
+
323
+ if (nearRight && this.isLastColumnChild(targetBlock)) {
324
+ return { block: targetBlock, edge: 'right', depth: 0, parentId: this.findEnclosingColumnId(targetBlock) };
325
+ }
326
+
327
+ return null;
328
+ }
329
+
330
+ // Standalone block (not in a column): keep the Notion-style outer side zones
331
+ // with a central reorder band, gated to the central vertical band so a
332
+ // top/bottom reorder wins near the block's own top/bottom corners.
333
+ const bandInset = rect.height * (1 - DRAG_CONFIG.sideBandRatio) / 2;
334
+ const inBand = clientY >= rect.top + bandInset && clientY <= rect.bottom - bandInset;
335
+
336
+ if (!inBand) {
337
+ return null;
338
+ }
339
+
340
+ // No inner bound: anything left of the left zone boundary counts as a left
341
+ // side-drop (including the entire left margin, at any distance), and likewise
342
+ // on the right. Only the central reorder band between the two zones falls
343
+ // through to top/bottom.
344
+ const sideZone = Math.max(rect.width * DRAG_CONFIG.sideZoneRatio, DRAG_CONFIG.sideZoneMin);
345
+ const nearLeft = clientX <= rect.left + sideZone;
346
+ const nearRight = clientX >= rect.right - sideZone;
347
+
348
+ if (!nearLeft && !nearRight) {
349
+ return null;
350
+ }
351
+
352
+ const edge: 'left' | 'right' = nearLeft ? 'left' : 'right';
353
+
354
+ return {
355
+ block: targetBlock,
356
+ edge,
357
+ depth: 0,
358
+ parentId: this.findEnclosingColumnId(targetBlock),
359
+ };
360
+ }
361
+
362
+ /**
363
+ * Side-drop detection for a drop that resolved to a whole column_list block —
364
+ * i.e. the cursor is in the outer editor margin beside the row, where the
365
+ * full-width container holder (not a narrow column child) sits under the
366
+ * pointer. Mirrors the standalone-block outer zones exactly (central vertical
367
+ * band, outer sideZone with no inner bound so the whole margin is live) so the
368
+ * dropzone is identical whether or not columns already exist.
369
+ *
370
+ * A left margin drop prepends a new column at the start of the row, a right
371
+ * margin drop appends one at the end. It does so by targeting the first/last
372
+ * column's first child block with a 'left'/'right' edge and that column's id as
373
+ * parentId — exactly the shape the row's inner outer-edge side-drop produces,
374
+ * so handleColumnDrop routes both through addColumnToList. Returns null when
375
+ * the row has no resolvable column child, or the cursor falls in the central
376
+ * reorder band / outside the vertical band (→ top/bottom reorders the list).
377
+ */
378
+ private outerColumnListTarget(
379
+ columnListBlock: Block,
380
+ blockHolder: HTMLElement,
381
+ clientX: number,
382
+ clientY: number
383
+ ): DropTarget | null {
384
+ const contentEl = blockHolder.querySelector('[data-blok-element-content]');
385
+ const rect = contentEl instanceof HTMLElement
386
+ ? contentEl.getBoundingClientRect()
387
+ : blockHolder.getBoundingClientRect();
388
+
389
+ const bandInset = rect.height * (1 - DRAG_CONFIG.sideBandRatio) / 2;
390
+ const inBand = clientY >= rect.top + bandInset && clientY <= rect.bottom - bandInset;
391
+
392
+ if (!inBand) {
393
+ return null;
394
+ }
395
+
396
+ const sideZone = Math.max(rect.width * DRAG_CONFIG.sideZoneRatio, DRAG_CONFIG.sideZoneMin);
397
+ const nearLeft = clientX <= rect.left + sideZone;
398
+ const nearRight = clientX >= rect.right - sideZone;
399
+
400
+ if (!nearLeft && !nearRight) {
401
+ return null;
402
+ }
403
+
404
+ const edge: 'left' | 'right' = nearLeft ? 'left' : 'right';
405
+ const columns = this.blockManager.blocks.filter(block => block.parentId === columnListBlock.id);
406
+ const column = edge === 'left' ? columns[0] : columns[columns.length - 1];
407
+
408
+ if (column === undefined) {
409
+ return null;
410
+ }
411
+
412
+ const child = this.blockManager.blocks.find(block => block.parentId === column.id);
413
+
414
+ if (child === undefined) {
415
+ return null;
416
+ }
417
+
418
+ return { block: child, edge, depth: 0, parentId: column.id };
419
+ }
420
+
421
+ /**
422
+ * The column holder element that owns a block, or null if the block is not a
423
+ * direct child of a column. Walks block holder → [data-blok-column] wrapper →
424
+ * enclosing [data-blok-element] (the column's holder).
425
+ */
426
+ private columnHolderOf(targetBlock: Block): HTMLElement | null {
427
+ const columnWrapper = targetBlock.holder.closest('[data-blok-column]');
428
+
429
+ if (!(columnWrapper instanceof HTMLElement)) {
430
+ return null;
431
+ }
432
+
433
+ const columnHolder = columnWrapper.closest(createSelector(DATA_ATTR.element));
434
+
435
+ return columnHolder instanceof HTMLElement ? columnHolder : null;
436
+ }
437
+
438
+ /**
439
+ * Whether a block sits in the FIRST column of its row — i.e. there is no
440
+ * preceding sibling column holder (only separators / nothing before it). Used
441
+ * to gate the left outer-edge side-drop so only the row's far-left edge spawns
442
+ * a column at the start. False when the block is not in a column.
443
+ */
444
+ private isFirstColumnChild(targetBlock: Block): boolean {
445
+ const columnHolder = this.columnHolderOf(targetBlock);
446
+
447
+ return columnHolder !== null && this.previousColumnHolder(columnHolder) === null;
448
+ }
449
+
450
+ /**
451
+ * Whether a block sits in the LAST column of its row — no following sibling
452
+ * column holder. Gates the right outer-edge side-drop. False when not in a
453
+ * column.
454
+ */
455
+ private isLastColumnChild(targetBlock: Block): boolean {
456
+ const columnHolder = this.columnHolderOf(targetBlock);
457
+
458
+ return columnHolder !== null && this.nextColumnHolder(columnHolder) === null;
459
+ }
460
+
461
+ /**
462
+ * The next sibling column holder after `columnHolder` within a column_list,
463
+ * skipping the resize separator that sits between columns. Null if none.
464
+ */
465
+ private nextColumnHolder(element: Element): HTMLElement | null {
466
+ const sibling = element.nextElementSibling;
467
+
468
+ if (sibling === null) {
469
+ return null;
470
+ }
471
+
472
+ if (sibling instanceof HTMLElement && sibling.matches(createSelector(DATA_ATTR.element))) {
473
+ return sibling;
474
+ }
475
+
476
+ return this.nextColumnHolder(sibling);
477
+ }
478
+
479
+ /**
480
+ * The previous sibling column holder before `columnHolder` within a
481
+ * column_list, skipping the resize separator. Null if none (the block is in
482
+ * the first column).
483
+ */
484
+ private previousColumnHolder(element: Element): HTMLElement | null {
485
+ const sibling = element.previousElementSibling;
486
+
487
+ if (sibling === null) {
488
+ return null;
489
+ }
490
+
491
+ if (sibling instanceof HTMLElement && sibling.matches(createSelector(DATA_ATTR.element))) {
492
+ return sibling;
493
+ }
494
+
495
+ return this.previousColumnHolder(sibling);
496
+ }
497
+
498
+ /**
499
+ * The first content block inside a column's holder element, or undefined when
500
+ * the column has no resolvable child block.
501
+ */
502
+ private firstBlockInColumnHolder(columnHolder: HTMLElement): Block | undefined {
503
+ const childHolder = columnHolder.querySelector(createSelector(DATA_ATTR.element));
504
+
505
+ return childHolder instanceof HTMLElement
506
+ ? this.blockManager.blocks.find(block => block.holder === childHolder)
507
+ : undefined;
508
+ }
509
+
510
+ /**
511
+ * The LAST child block of a column, in document order, or undefined when the
512
+ * column has no children. Uses the flat block list filtered by parentId rather
513
+ * than the DOM — a column's children are contiguous and ordered there, and this
514
+ * avoids the column's multi-level wrapper nesting (and nested grandchild blocks,
515
+ * e.g. list items) confusing a DOM walk.
516
+ */
517
+ private lastChildOfColumn(columnBlock: Block): Block | undefined {
518
+ const children = this.blockManager.blocks.filter(block => block.parentId === columnBlock.id);
519
+
520
+ return children[children.length - 1];
521
+ }
522
+
523
+ /**
524
+ * Redirects a drop that resolved to a column CONTAINER block into the column
525
+ * itself. The empty space below a column's blocks resolves (via closest) to the
526
+ * column block, whose holder is a [data-blok-element]; a top/bottom drop there
527
+ * shows a container-edge indicator and spawns a new column. Instead, anchor on
528
+ * the column's LAST child with the caller's later top/bottom logic so the drop
529
+ * appends INTO the column. Non-column targets, and columns with no usable child
530
+ * (or whose last child is itself being dragged), pass through unchanged.
531
+ */
532
+ private redirectColumnContainerTarget(
533
+ block: Block,
534
+ holder: HTMLElement
535
+ ): { block: Block; holder: HTMLElement } {
536
+ if (block.name !== 'column') {
537
+ return { block, holder };
538
+ }
539
+
540
+ const lastChild = this.lastChildOfColumn(block);
541
+
542
+ if (lastChild === undefined || this.sourceBlocks.includes(lastChild)) {
543
+ return { block, holder };
544
+ }
545
+
546
+ return { block: lastChild, holder: lastChild.holder };
547
+ }
548
+
549
+ /**
550
+ * Detects a drop on the inter-column gutter — a resize separator that divides
551
+ * two columns — and routes it to a "between columns" side-drop.
552
+ *
553
+ * The separator's next sibling is the right-adjacent column's holder. We target
554
+ * that column's FIRST inner block with a 'left' edge so the integrator inserts a
555
+ * new column BEFORE it (addColumnToList side 'left'), i.e. between the two
556
+ * columns the separator divides. Reusing a real child block keeps the indicator
557
+ * and drop path identical to an inner-edge side-drop.
558
+ *
559
+ * Returns null (fall through to normal detection) when not on a separator, on a
560
+ * stacked layout (< 651px, separators hidden), or the right column has no
561
+ * resolvable child block.
562
+ *
563
+ * The separator spans the FULL row height, so its lower strip overlaps the
564
+ * empty gap below whichever column is shorter. A drop there reads visually as
565
+ * "the bottom of that column", not "between the columns" — so when clientY lies
566
+ * in one column's dead gap (past its content but still beside the other's),
567
+ * stack INTO that column instead of inserting a new one. Only when both columns
568
+ * have content beside the cursor is it a true between-columns insertion.
569
+ *
570
+ * @param elementUnderCursor - Element directly under the cursor
571
+ * @param clientY - Cursor Y position
572
+ * @returns A DropTarget (between-columns side-drop, or into-column stack), or null
573
+ */
574
+ private determineColumnGapTarget(elementUnderCursor: Element, clientY: number): DropTarget | null {
575
+ // Columns stack below this breakpoint — separators are hidden, no gutter.
576
+ if (window.innerWidth < 651) {
577
+ return null;
578
+ }
579
+
580
+ const resizer = elementUnderCursor.closest('[data-blok-column-resizer]');
581
+
582
+ if (!(resizer instanceof HTMLElement)) {
583
+ return null;
584
+ }
585
+
586
+ // The separator divides a left and a right column. If the cursor is in the
587
+ // dead gap below the shorter one, redirect into it (stack at its end) rather
588
+ // than between the two.
589
+ const gapStack = this.stackIntoGapColumn(resizer, clientY);
590
+
591
+ if (gapStack !== null) {
592
+ return gapStack;
593
+ }
594
+
595
+ // The separator sits between two column holders; the next one is the
596
+ // right-adjacent column. Its first child block is the side-drop target.
597
+ const rightColumnHolder = this.nextColumnHolder(resizer);
598
+ const childBlock = rightColumnHolder !== null
599
+ ? this.firstBlockInColumnHolder(rightColumnHolder)
600
+ : undefined;
601
+
602
+ if (childBlock === undefined) {
603
+ return null;
604
+ }
605
+
606
+ return {
607
+ block: childBlock,
608
+ edge: 'left',
609
+ depth: 0,
610
+ parentId: this.findEnclosingColumnId(childBlock),
611
+ };
612
+ }
613
+
614
+ /**
615
+ * When the cursor sits on a separator but in the empty gap below one of the two
616
+ * columns it divides, returns a bottom-edge target stacking into that (shorter)
617
+ * column's last child. Returns null when the cursor is beside content on BOTH
618
+ * sides (a real between-columns insertion), below/above both, or either column
619
+ * is unresolvable — letting the caller fall back to the between-columns drop.
620
+ */
621
+ private stackIntoGapColumn(resizer: HTMLElement, clientY: number): DropTarget | null {
622
+ const leftHolder = this.previousColumnHolder(resizer);
623
+ const rightHolder = this.nextColumnHolder(resizer);
624
+
625
+ if (leftHolder === null || rightHolder === null) {
626
+ return null;
627
+ }
628
+
629
+ const leftColumn = this.blockManager.blocks.find(block => block.holder === leftHolder);
630
+ const rightColumn = this.blockManager.blocks.find(block => block.holder === rightHolder);
631
+
632
+ if (leftColumn === undefined || rightColumn === undefined) {
633
+ return null;
634
+ }
635
+
636
+ const leftBottom = this.columnContentBottom(leftColumn);
637
+ const rightBottom = this.columnContentBottom(rightColumn);
638
+
639
+ if (leftBottom === null || rightBottom === null) {
640
+ return null;
641
+ }
642
+
643
+ // Cursor past the left column's content but still beside the right's → in the
644
+ // left column's gap. The mirror case targets the right column.
645
+ if (clientY > leftBottom && clientY <= rightBottom) {
646
+ return this.stackIntoColumnTarget(leftColumn);
647
+ }
648
+ if (clientY > rightBottom && clientY <= leftBottom) {
649
+ return this.stackIntoColumnTarget(rightColumn);
650
+ }
651
+
652
+ return null;
653
+ }
654
+
655
+ /**
656
+ * The bottom of a column's content — the bottom of its last child's content box
657
+ * (falling back to the child's holder). Null when the column has no last child.
658
+ */
659
+ private columnContentBottom(columnBlock: Block): number | null {
660
+ const lastChild = this.lastChildOfColumn(columnBlock);
661
+
662
+ if (lastChild === undefined) {
663
+ return null;
664
+ }
665
+
666
+ const content = lastChild.holder.querySelector('[data-blok-element-content]');
667
+ const rect = content instanceof HTMLElement
668
+ ? content.getBoundingClientRect()
669
+ : lastChild.holder.getBoundingClientRect();
670
+
671
+ return rect.bottom;
672
+ }
673
+
674
+ /**
675
+ * A bottom-edge DropTarget that appends into a column (its last child). Null
676
+ * when the column has no usable last child or it is itself being dragged.
677
+ */
678
+ private stackIntoColumnTarget(columnBlock: Block): DropTarget | null {
679
+ const lastChild = this.lastChildOfColumn(columnBlock);
680
+
681
+ if (lastChild === undefined || this.sourceBlocks.includes(lastChild)) {
682
+ return null;
683
+ }
684
+
685
+ return { block: lastChild, edge: 'bottom', depth: 0, parentId: null };
686
+ }
687
+
688
+ /**
689
+ * Walks up the parentId chain from a target block to find the nearest
690
+ * ancestor block whose name is 'column'. Returns that column's id, or null
691
+ * if the target is not inside a column.
692
+ */
693
+ private findEnclosingColumnId(targetBlock: Block): string | null {
694
+ const walk = (parentId: string | null): string | null => {
695
+ if (parentId === null) {
696
+ return null;
697
+ }
698
+
699
+ const parent = this.blockManager.getBlockById(parentId);
700
+
701
+ if (parent === undefined) {
702
+ return null;
703
+ }
704
+ if (parent.name === 'column') {
705
+ return parent.id;
706
+ }
707
+
708
+ return walk(parent.parentId);
709
+ };
710
+
711
+ return walk(targetBlock.parentId);
712
+ }
713
+
204
714
  /**
205
715
  * Calculates the target depth for list item nesting.
206
716
  * When a sourceBlock is provided and is a list item, the depth is calculated
@@ -15,6 +15,15 @@ export const DRAG_CONFIG = {
15
15
  announcementThrottleMs: 300,
16
16
  /** Horizontal distance to the left of blocks where drop is still valid */
17
17
  leftDropZone: 50,
18
+ /**
19
+ * Fraction of the content-box width on each side that triggers a side-drop
20
+ * (Notion-style outer regions). The central remainder is reorder (top/bottom).
21
+ */
22
+ sideZoneRatio: 0.25,
23
+ /** Minimum side-zone width (px) so the zone stays usable on narrow content */
24
+ sideZoneMin: 48,
25
+ /** Only trigger side-drop when the cursor is within this central fraction of the block's height */
26
+ sideBandRatio: 0.6,
18
27
  } as const;
19
28
 
20
29
  /**
@@ -5,7 +5,8 @@ import { BlockAPI } from '../../block/api';
5
5
  import { Dom as $ } from '../../dom';
6
6
  import { BlockSettingsClosed, BlockSettingsOpened, BlokMobileLayoutToggled } from '../../events';
7
7
  import { Flipper } from '../../flipper';
8
- import { IconReplace, IconTrash } from '../../icons';
8
+ import { IconColumns, IconReplace, IconTrash } from '../../icons';
9
+ import { wrapBlocksInColumns } from '../../../tools/column-drop';
9
10
  import { SelectionUtils } from '../../selection/index';
10
11
  import type { BlockToolAdapter } from '../../tools/block';
11
12
  import { isMobileScreen, keyCodes } from '../../utils';
@@ -13,7 +14,7 @@ import { getConvertibleToolsForBlock, getConvertibleToolsForBlocks } from '../..
13
14
  import type { PopoverItemParams, Popover } from '../../utils/popover';
14
15
  import { PopoverDesktop, PopoverMobile, PopoverItemType } from '../../utils/popover';
15
16
  import { css as popoverItemCls } from '../../utils/popover/components/popover-item';
16
- import { translateToolName, translateToolTitle } from '../../utils/tools';
17
+ import { isToolConvertable, translateToolName, translateToolTitle } from '../../utils/tools';
17
18
 
18
19
  import type { PopoverParams } from '@/types/utils/popover/popover';
19
20
  import { PopoverEvent } from '@/types/utils/popover/popover-event';
@@ -203,7 +204,7 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
203
204
  : await block.getActiveToolboxEntry();
204
205
  const contextLabel = ((): string => {
205
206
  if (hasMultipleBlocksSelected) {
206
- return `${selectedBlocks.length} blocks`;
207
+ return this.Blok.I18n.t('blockSettings.blocksSelected', { count: selectedBlocks.length });
207
208
  }
208
209
 
209
210
  if (activeEntry) {
@@ -457,6 +458,51 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
457
458
  return result;
458
459
  }, []);
459
460
 
461
+ /**
462
+ * For a multi-block selection, "Turn into columns" belongs in the same
463
+ * "Convert to" submenu as the other tool conversions. The selected ids are
464
+ * captured HERE, at build time — reading BlockSelection.selectedBlocks
465
+ * inside onActivate returns an empty list, because the document mousedown
466
+ * that fires when the popover item is clicked clears the block selection
467
+ * before onActivate runs.
468
+ */
469
+ if (hasMultipleBlocksSelected) {
470
+ const selectedBlockIds = selectedBlocks.map((selected) => selected.id);
471
+
472
+ convertToItems.push({
473
+ icon: IconColumns,
474
+ title: this.Blok.I18n.t('toolNames.columns'),
475
+ name: 'turn-into-columns',
476
+ closeOnActivate: true,
477
+ onActivate: () => {
478
+ const { Caret, Toolbar, BlockManager } = this.Blok;
479
+ const api = this.Blok.API.methods;
480
+
481
+ const listId = wrapBlocksInColumns(api, selectedBlockIds);
482
+
483
+ if (listId === null) {
484
+ Toolbar.close();
485
+
486
+ return;
487
+ }
488
+
489
+ const firstColumn = api.blocks.getChildren(listId)[0];
490
+ const firstChild = firstColumn !== undefined
491
+ ? api.blocks.getChildren(firstColumn.id)[0]
492
+ : undefined;
493
+ const block = firstChild !== undefined
494
+ ? BlockManager.getBlockById(firstChild.id)
495
+ : undefined;
496
+
497
+ if (block !== undefined) {
498
+ Caret.setToBlock(block, Caret.positions.START);
499
+ }
500
+
501
+ Toolbar.close();
502
+ },
503
+ });
504
+ }
505
+
460
506
  if (convertToItems.length > 0) {
461
507
  items.push({
462
508
  icon: IconReplace,
@@ -667,6 +713,30 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
667
713
  return null;
668
714
  }
669
715
 
716
+ /**
717
+ * Convert only the "roots" of the selection that can actually convert.
718
+ * Mirrors {@link getConvertibleToolsForBlocks}: a block nested under another
719
+ * selected block (e.g. the contents of a selected column_list) rides with
720
+ * its container and must be left intact, and a non-convertible container
721
+ * block (no «export» rule) is skipped. Without this filter the merge path
722
+ * would pull a columns block's inner paragraphs out into the merged block,
723
+ * tearing the columns apart.
724
+ */
725
+ const selectedIds = new Set(blocks.map((block) => block.id));
726
+ const convertibleBlocks = blocks.filter((block) => {
727
+ if (block.parentId !== null && selectedIds.has(block.parentId)) {
728
+ return false;
729
+ }
730
+
731
+ const blockTool = Tools.blockTools.get(block.name);
732
+
733
+ return blockTool === undefined || isToolConvertable(blockTool, 'export');
734
+ });
735
+
736
+ if (convertibleBlocks.length === 0) {
737
+ return null;
738
+ }
739
+
670
740
  /**
671
741
  * Check if the target tool's conversion config import function can handle
672
742
  * newline-separated content to create multiple items (like lists do).
@@ -676,13 +746,13 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
676
746
  const shouldMergeIntoSingleBlock = targetTool && this.canToolMergeMultipleItems(targetTool);
677
747
 
678
748
  if (shouldMergeIntoSingleBlock) {
679
- return this.convertBlocksToSingleMergedBlock(blocks, targetToolName, toolboxData);
749
+ return this.convertBlocksToSingleMergedBlock(convertibleBlocks, targetToolName, toolboxData);
680
750
  }
681
751
 
682
752
  /**
683
753
  * Convert each block individually, maintaining them as separate blocks
684
754
  */
685
- return this.convertBlocksIndividually(blocks, targetToolName, toolboxData);
755
+ return this.convertBlocksIndividually(convertibleBlocks, targetToolName, toolboxData);
686
756
  }
687
757
 
688
758
  /**