@jackuait/blok 0.13.0 → 0.13.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 (330) hide show
  1. package/codemod/migrate-editorjs-to-blok.js +7 -2
  2. package/codemod/test.js +22 -2
  3. package/dist/blok.cjs +1 -1
  4. package/dist/blok.iife.js +29 -29
  5. package/dist/blok.mjs +2 -2
  6. package/dist/chunks/{blok-Cy7bijNZ.mjs → blok-BF9i6G_n.mjs} +2165 -2181
  7. package/dist/chunks/{blok-DsB9tzWP.cjs → blok-C7mseAhu.cjs} +13 -13
  8. package/dist/chunks/constants-BZ9pK9tm.cjs +457 -0
  9. package/dist/chunks/{constants-CnxI96Oy.mjs → constants-jfK6xPl6.mjs} +451 -246
  10. package/dist/chunks/{i18next-loader-BObGIOaH.cjs → i18next-loader-DC1uN-wc.cjs} +1 -1
  11. package/dist/chunks/{i18next-loader-BrDbxldT.mjs → i18next-loader-GIrBCFrC.mjs} +1 -1
  12. package/dist/chunks/{lightweight-i18n-Bw9zJMpT.cjs → lightweight-i18n-Bbl1cNYK.cjs} +1 -1
  13. package/dist/chunks/{lightweight-i18n-CFXiOjGR.mjs → lightweight-i18n-DF9Pupws.mjs} +2 -0
  14. package/dist/chunks/{messages-DLaesq4h2.cjs → messages-1qYt6EhZ2.cjs} +1 -1
  15. package/dist/chunks/{messages-BzCMWDCa.cjs → messages-2fxvN3Nb.cjs} +1 -1
  16. package/dist/chunks/{messages-CloQj-t82.mjs → messages-5AxgjKgf2.mjs} +2 -0
  17. package/dist/{messages-ByaREXXn.mjs → chunks/messages-AgmrT2Id.mjs} +2 -0
  18. package/dist/{messages-DV7zSwcU2.mjs → chunks/messages-AtUsRyWK2.mjs} +2 -0
  19. package/dist/chunks/{messages-23LJg97F.cjs → messages-B1Vs2HmR.cjs} +1 -1
  20. package/dist/{messages-Bgub7yIr.mjs → chunks/messages-B1k4DgVe.mjs} +4 -2
  21. package/dist/chunks/{messages-CyNE9mfW.cjs → messages-B1xgsCNQ.cjs} +1 -1
  22. package/dist/chunks/{messages-CK6NQlei2.mjs → messages-B2zrJyAc2.mjs} +2 -0
  23. package/dist/chunks/{messages-D9KllT5S.mjs → messages-B5CFhyI8.mjs} +2 -0
  24. package/dist/chunks/{messages-BFPJInnK.mjs → messages-B6You-RA.mjs} +7 -5
  25. package/dist/{messages-B8Ps9X7Q.mjs → chunks/messages-B6t1xShv.mjs} +2 -0
  26. package/dist/chunks/{messages-B5P29HKO2.cjs → messages-B780gS332.cjs} +1 -1
  27. package/dist/chunks/{messages-BBD0GeAN.mjs → messages-B7KbtBAE.mjs} +5 -3
  28. package/dist/chunks/{messages-CG1tpK3F.cjs → messages-B7T5Notn.cjs} +1 -1
  29. package/dist/{messages-DemkV5oa2.mjs → chunks/messages-BA2sVGrR2.mjs} +2 -0
  30. package/dist/chunks/{messages-B9q8LdZT.cjs → messages-BE9aZQ1Q.cjs} +1 -1
  31. package/dist/chunks/{messages-CPTia1Ip.cjs → messages-BIQpTYfm.cjs} +1 -1
  32. package/dist/chunks/{messages-BT-EUB5N.cjs → messages-BIRvUJ0t.cjs} +1 -1
  33. package/dist/chunks/{messages-BEkcSJKV.cjs → messages-BIodfkjv.cjs} +1 -1
  34. package/dist/chunks/{messages-_ysQiWQr.cjs → messages-BL-zBcuM.cjs} +1 -1
  35. package/dist/chunks/{messages-CSZTOxAh.mjs → messages-BNxTGhHu.mjs} +2 -0
  36. package/dist/chunks/{messages-CzG5xDaO.mjs → messages-BP77P9ER.mjs} +2 -0
  37. package/dist/chunks/{messages-BEM-ySlb2.cjs → messages-BQr0zpmu2.cjs} +1 -1
  38. package/dist/chunks/{messages-D4P5aWos2.cjs → messages-BTHi6rVW2.cjs} +1 -1
  39. package/dist/chunks/{messages-KTUVKXJd.mjs → messages-BVZONUH9.mjs} +3 -1
  40. package/dist/{messages-BvHpSkAX2.mjs → chunks/messages-BVarbXYD2.mjs} +2 -0
  41. package/dist/{messages-DiOm0iFp.mjs → chunks/messages-BWGqOZ9J.mjs} +2 -0
  42. package/dist/{messages-DIBytW9u.mjs → chunks/messages-BZ4d5yNH.mjs} +2 -0
  43. package/dist/chunks/{messages-WUvriTcu2.cjs → messages-BZOAdcmO2.cjs} +1 -1
  44. package/dist/chunks/{messages-CN9LNhoS2.mjs → messages-B_U10evN2.mjs} +4 -2
  45. package/dist/chunks/{messages-DPrZ8Z6d2.cjs → messages-BbsGeVs_2.cjs} +1 -1
  46. package/dist/chunks/{messages-3_7JWqT0.cjs → messages-Bc1NYJVS.cjs} +1 -1
  47. package/dist/chunks/{messages-D3EU-LRS.cjs → messages-BdORNG8X.cjs} +1 -1
  48. package/dist/chunks/{messages-BIOk-aTQ.cjs → messages-Bg2kheTv.cjs} +1 -1
  49. package/dist/chunks/{messages-DsAkJt8M.cjs → messages-Bj1eQtQw.cjs} +1 -1
  50. package/dist/chunks/{messages-CD6xvMw1.cjs → messages-BkJJCHNK.cjs} +1 -1
  51. package/dist/chunks/{messages-BIN5-zeZ.cjs → messages-BkdGQfIX.cjs} +1 -1
  52. package/dist/chunks/{messages-pKpKbxTh2.cjs → messages-BkgsB-cj2.cjs} +1 -1
  53. package/dist/{messages-DXCKUB0_.mjs → chunks/messages-BmdkDNYv.mjs} +4 -2
  54. package/dist/{messages-B66o_tQ42.mjs → chunks/messages-BmiN0JGP2.mjs} +2 -0
  55. package/dist/chunks/{messages-BG17trOF.cjs → messages-Bn0vGFEP.cjs} +1 -1
  56. package/dist/{messages-BpJopIVL2.mjs → chunks/messages-BoCUgrkI.mjs} +2 -0
  57. package/dist/chunks/{messages-B7dSyfsy.cjs → messages-BoIUXwWe.cjs} +1 -1
  58. package/dist/chunks/{messages-7oeFTHO4.cjs → messages-BpDEh8rW.cjs} +1 -1
  59. package/dist/{messages-DVSJBYhD.mjs → chunks/messages-BuYnNUtU2.mjs} +3 -1
  60. package/dist/{messages-D8SUKrMB.mjs → chunks/messages-BvuEffoe.mjs} +4 -2
  61. package/dist/{messages-DvIwCnTR.mjs → chunks/messages-BvupGuDw2.mjs} +2 -0
  62. package/dist/chunks/{messages-BZr0xR7X.cjs → messages-BwknPZJ8.cjs} +1 -1
  63. package/dist/{messages-5s1gGSm4.mjs → chunks/messages-BxKobpJ22.mjs} +2 -0
  64. package/dist/chunks/{messages-D_OfKMtO2.cjs → messages-ByicRCge2.cjs} +1 -1
  65. package/dist/{messages-DNDonN-x.mjs → chunks/messages-C03LZxma.mjs} +2 -0
  66. package/dist/{messages-CqrBLC_P.mjs → chunks/messages-C24IC_eR.mjs} +2 -0
  67. package/dist/chunks/{messages-CucO-8OV.cjs → messages-C2oBmyTn.cjs} +1 -1
  68. package/dist/chunks/{messages-CXZtG3Xs2.mjs → messages-C4bHKGnB2.mjs} +4 -2
  69. package/dist/chunks/{messages-DTceHV5d2.mjs → messages-C4sIqArW2.mjs} +2 -0
  70. package/dist/chunks/{messages-Ds1jRRkJ2.cjs → messages-C9wBMmxr2.cjs} +1 -1
  71. package/dist/chunks/{messages-DdMhxYfW.cjs → messages-CDK5-8vW.cjs} +1 -1
  72. package/dist/chunks/{messages-CP3f0qkN.cjs → messages-CKHbPcfh.cjs} +1 -1
  73. package/dist/{messages-Dt4Cw2PN2.mjs → chunks/messages-CMRjQYxi2.mjs} +3 -1
  74. package/dist/chunks/{messages-BJVhJhdz.cjs → messages-CNFfwTfw.cjs} +1 -1
  75. package/dist/chunks/{messages-C99hDoeM2.cjs → messages-CNiuofck2.cjs} +1 -1
  76. package/dist/chunks/{messages-CjgjbJ1H2.mjs → messages-COMdnGQV2.mjs} +4 -2
  77. package/dist/{messages-BfsTqQCr2.mjs → chunks/messages-CQg2_vmm.mjs} +2 -0
  78. package/dist/{messages-_OjohrXs.mjs → chunks/messages-CTWXEx_o.mjs} +6 -4
  79. package/dist/{messages-BJBdgowf.mjs → chunks/messages-CWTFEPbA2.mjs} +2 -0
  80. package/dist/chunks/{messages-l73qDsKl.cjs → messages-CWszAGkF.cjs} +1 -1
  81. package/dist/chunks/{messages-BGcdiqDM2.cjs → messages-CWzQNagc2.cjs} +1 -1
  82. package/dist/{messages-CaC0BVP72.mjs → chunks/messages-CXxz5HYQ.mjs} +2 -0
  83. package/dist/chunks/{messages-BvJTJwoq2.cjs → messages-CYf1gv722.cjs} +1 -1
  84. package/dist/{messages-DN_N2jK1.mjs → chunks/messages-CcboYTP8.mjs} +2 -0
  85. package/dist/{messages-DdGG7T30.mjs → chunks/messages-CdWXgq_r.mjs} +2 -0
  86. package/dist/chunks/{messages-pZC8rTng.cjs → messages-CfcCwbQo.cjs} +1 -1
  87. package/dist/chunks/{messages-DFJTssY7.cjs → messages-CkvqOnAR.cjs} +1 -1
  88. package/dist/chunks/{messages-BzbW5tIB2.cjs → messages-Cnrug7nz2.cjs} +1 -1
  89. package/dist/chunks/{messages-BUp0i-9G.cjs → messages-Cr51zCHy.cjs} +1 -1
  90. package/dist/chunks/{messages-Cz63p8bt2.cjs → messages-CsLvvl2F2.cjs} +1 -1
  91. package/dist/chunks/{messages-DrIzpqee.cjs → messages-CuO-Rx4g.cjs} +1 -1
  92. package/dist/chunks/messages-Cv4I3k1W2.cjs +1 -0
  93. package/dist/chunks/{messages-BScbCwsN.cjs → messages-CwHRv6g8.cjs} +1 -1
  94. package/dist/chunks/{messages-EUS1-T0-.mjs → messages-CyCl8la6.mjs} +2 -0
  95. package/dist/chunks/{messages-DOUT2sT62.cjs → messages-CzXnHfGb2.cjs} +1 -1
  96. package/dist/chunks/{messages-ndFXaPuJ.cjs → messages-D2QxLx1a.cjs} +1 -1
  97. package/dist/chunks/{messages-CDLJmxWA.cjs → messages-D4LUPpX_.cjs} +1 -1
  98. package/dist/{messages-DpQjOxzP.mjs → chunks/messages-D7YPlNAK.mjs} +2 -0
  99. package/dist/{messages-S0krg7sB.mjs → chunks/messages-DGHTTk1S2.mjs} +3 -1
  100. package/dist/chunks/{messages-wZhc9mFF2.mjs → messages-DHdoaaFq2.mjs} +6 -4
  101. package/dist/chunks/{messages-C0BMFvdy.mjs → messages-DI9Ifrgt.mjs} +2 -0
  102. package/dist/chunks/messages-DJFL-bxd2.cjs +1 -0
  103. package/dist/chunks/messages-DLEeNpMi2.cjs +1 -0
  104. package/dist/chunks/{messages-BpI1875E.mjs → messages-DNqFlfOd.mjs} +3 -1
  105. package/dist/chunks/{messages-mtoLIsYL.cjs → messages-DTNU_cq0.cjs} +1 -1
  106. package/dist/chunks/{messages-BxXGCWit.cjs → messages-DVKsYqpJ.cjs} +1 -1
  107. package/dist/chunks/{messages-BJNTs8dY.cjs → messages-DXlfz-nC.cjs} +1 -1
  108. package/dist/chunks/{messages-DKdgRSvy.cjs → messages-DbA_Zja3.cjs} +1 -1
  109. package/dist/chunks/{messages-CZ2th1UV2.cjs → messages-Dc2TTEx_2.cjs} +1 -1
  110. package/dist/chunks/{messages-Bh-GPq0T2.mjs → messages-Dd4nzvLj2.mjs} +2 -0
  111. package/dist/chunks/{messages-BISkpo37.cjs → messages-DeDMMmRC.cjs} +1 -1
  112. package/dist/chunks/{messages-C0Fr1grN.mjs → messages-DefZ3ihx.mjs} +2 -0
  113. package/dist/{messages-DC1fWdjB2.mjs → chunks/messages-DhZwMl9x2.mjs} +2 -0
  114. package/dist/chunks/{messages-BYT844Pj.mjs → messages-DiGsu5XN.mjs} +6 -4
  115. package/dist/chunks/{messages-xeJ5eVFo.cjs → messages-DlQNXqzr.cjs} +1 -1
  116. package/dist/{messages-CZYocA-2.mjs → chunks/messages-DlcI-0Sy.mjs} +2 -0
  117. package/dist/{messages-ZJE9VNzB2.mjs → chunks/messages-DmErSGPk2.mjs} +2 -0
  118. package/dist/chunks/{messages-h9Zi_9Gr.cjs → messages-DqMqZLcn.cjs} +1 -1
  119. package/dist/chunks/{messages-D_w-pjpG2.cjs → messages-DqqDuEE22.cjs} +1 -1
  120. package/dist/chunks/{messages-DW0CNDiw.mjs → messages-DsyO86r3.mjs} +2 -0
  121. package/dist/chunks/{messages-DgdjQ2JT.mjs → messages-DzY53I67.mjs} +2 -0
  122. package/dist/chunks/{messages-PeAC6FL2.cjs → messages-DzppdmWe.cjs} +1 -1
  123. package/dist/chunks/{messages-CkCgWAJW2.mjs → messages-GHsufIGi2.mjs} +4 -2
  124. package/dist/chunks/{messages-Diinl7nA2.cjs → messages-HzLF-BQL2.cjs} +1 -1
  125. package/dist/chunks/{messages-CoWmNHyu2.cjs → messages-Jb9n97oP2.cjs} +1 -1
  126. package/dist/chunks/{messages-2sqZ51ZE2.cjs → messages-K_vSdSoF2.cjs} +1 -1
  127. package/dist/{messages-D-K9TXl02.mjs → chunks/messages-LTkIIrSe2.mjs} +6 -4
  128. package/dist/chunks/{messages-zqNVwsuv.mjs → messages-MPF8o3EP.mjs} +2 -0
  129. package/dist/chunks/{messages-5HoTbob5.cjs → messages-Nh7wrRdm.cjs} +1 -1
  130. package/dist/chunks/{messages-B6ALGrrK2.mjs → messages-OSIAf0Wk2.mjs} +5 -3
  131. package/dist/chunks/{messages-P9sjeERx.mjs → messages-OYoN_rp1.mjs} +4 -2
  132. package/dist/chunks/messages-QHS-Ydg_.cjs +1 -0
  133. package/dist/chunks/{messages-DLphNjiM2.cjs → messages-SpiG5vT-2.cjs} +1 -1
  134. package/dist/chunks/{messages-RumMLtL32.mjs → messages-Uc3Uc2862.mjs} +3 -1
  135. package/dist/chunks/{messages-BP9zsQtc.mjs → messages-V8K7-1l2.mjs} +2 -0
  136. package/dist/chunks/{messages-BR1Rd5-u2.cjs → messages-XFaJzdhP2.cjs} +1 -1
  137. package/dist/{messages-B3GO2TNx2.mjs → chunks/messages-Z9mDYT3w.mjs} +2 -0
  138. package/dist/chunks/{messages-CEroCDzi.mjs → messages-ZHNvKiIX.mjs} +2 -0
  139. package/dist/chunks/{messages-C2irZQlh.mjs → messages-bSVgJ1cu.mjs} +3 -1
  140. package/dist/chunks/{messages-DlNTalkx2.mjs → messages-bjSJwsXK2.mjs} +2 -0
  141. package/dist/{messages-D_jk0Apm.mjs → chunks/messages-eyGOcbhV.mjs} +2 -0
  142. package/dist/chunks/{messages-DlvmJJK6.mjs → messages-fSeXd6-z.mjs} +3 -1
  143. package/dist/{messages-BYooY4mP2.mjs → chunks/messages-l5xHQb_m.mjs} +7 -5
  144. package/dist/{messages-D_pfhYg5.mjs → chunks/messages-rEsI_fAk.mjs} +5 -3
  145. package/dist/chunks/{messages-MxqZVywa2.mjs → messages-sqdfYuVj2.mjs} +2 -0
  146. package/dist/chunks/{messages-C-D80jVE.mjs → messages-vQ5kblO8.mjs} +2 -0
  147. package/dist/chunks/{messages-BwNXKlvA2.cjs → messages-y03BGg692.cjs} +1 -1
  148. package/dist/chunks/{tools-1xd7TPfu.mjs → tools-CrrBwp7I.mjs} +1816 -1637
  149. package/dist/chunks/tools-Duq42I9m.cjs +116 -0
  150. package/dist/full.cjs +1 -1
  151. package/dist/full.mjs +3 -3
  152. package/dist/locales.mjs +69 -67
  153. package/dist/{chunks/messages-CkdgHogc2.mjs → messages-1ufJbdRv2.mjs} +3 -1
  154. package/dist/{messages-cBA8dKoi.mjs → messages-3j4yi-DF.mjs} +5 -3
  155. package/dist/{chunks/messages-BGCpxpgG.mjs → messages-5USazVPA2.mjs} +2 -0
  156. package/dist/{chunks/messages-Cf9wwoye2.mjs → messages-7fwJIrld2.mjs} +2 -0
  157. package/dist/{messages-hVVP_eZE.mjs → messages-AycxTjmw.mjs} +2 -0
  158. package/dist/{messages-Bg5tERmS.mjs → messages-B3PWaMcw.mjs} +6 -4
  159. package/dist/{chunks/messages-Diozu4lJ.mjs → messages-B3oFjWq3.mjs} +4 -2
  160. package/dist/{chunks/messages-ByJ-nSXK.mjs → messages-B8Tq9Txy.mjs} +2 -0
  161. package/dist/{messages-CDJP_fbb2.mjs → messages-BDwU9rYR2.mjs} +4 -2
  162. package/dist/{messages-6ukS6d5Z.mjs → messages-BGzpwNrz.mjs} +2 -0
  163. package/dist/{chunks/messages-CHXG6lEA2.mjs → messages-BKCuCTFM.mjs} +2 -0
  164. package/dist/{chunks/messages-aGEPK1g12.mjs → messages-BSHFypGE2.mjs} +2 -0
  165. package/dist/{chunks/messages-OMGVF8X52.mjs → messages-BcD7xtJA.mjs} +3 -1
  166. package/dist/{messages-DA2x_UvX2.mjs → messages-Bj03XD-02.mjs} +6 -4
  167. package/dist/{chunks/messages-C3NrnRWC.mjs → messages-BkTBwYet.mjs} +2 -0
  168. package/dist/{messages-ZjCwxd3_2.mjs → messages-BmWP4vpV2.mjs} +2 -0
  169. package/dist/{chunks/messages-DDtwOfXf.mjs → messages-Bt4TLGth.mjs} +2 -0
  170. package/dist/{chunks/messages-Bly_2YUk.mjs → messages-BtDz-sw92.mjs} +2 -0
  171. package/dist/{messages-CTcNzaJJ2.mjs → messages-C2TRJroV2.mjs} +2 -0
  172. package/dist/{messages-CDRUbL17.mjs → messages-C31VIrlL.mjs} +2 -0
  173. package/dist/{messages-Yg8Y6PlG.mjs → messages-C8RMjnBe.mjs} +2 -0
  174. package/dist/{chunks/messages-BciKgfP4.mjs → messages-C9GFRcVj.mjs} +6 -4
  175. package/dist/{chunks/messages-0cSP25RB.mjs → messages-CAGQtm7T.mjs} +5 -3
  176. package/dist/{messages-_6tu8F-N.mjs → messages-CITYhXUz.mjs} +2 -0
  177. package/dist/{messages-BbaS2xgR2.mjs → messages-CIjSE2_O2.mjs} +5 -3
  178. package/dist/{messages-BQ7jJt9m.mjs → messages-CRR1VRO6.mjs} +2 -0
  179. package/dist/{chunks/messages-Da-9fv3U.mjs → messages-CTzO11jz.mjs} +2 -0
  180. package/dist/{messages-DaSzBuLF2.mjs → messages-CX8egsiA2.mjs} +2 -0
  181. package/dist/{messages-DccIq7002.mjs → messages-C_gdsU952.mjs} +4 -2
  182. package/dist/{messages-CXXQC9Vz.mjs → messages-C_mN4lx0.mjs} +3 -1
  183. package/dist/{chunks/messages-BI7NfKFe.mjs → messages-CacRpQpm.mjs} +4 -2
  184. package/dist/{chunks/messages-BCRpttn3.mjs → messages-Cdgz3urh.mjs} +2 -0
  185. package/dist/{messages-BaC-sNb4.mjs → messages-CgVEHCQ-.mjs} +3 -1
  186. package/dist/{chunks/messages-D_24p8E62.mjs → messages-CgaGkQi32.mjs} +2 -0
  187. package/dist/{chunks/messages-D3AyiRbO.mjs → messages-CmN2OfuB.mjs} +2 -0
  188. package/dist/{chunks/messages-CSierpa_2.mjs → messages-CntTlSE22.mjs} +2 -0
  189. package/dist/{messages-BK1sXq7X.mjs → messages-Cp0fjsey.mjs} +3 -1
  190. package/dist/{messages-DHOj5syv2.mjs → messages-Cx7sqvx02.mjs} +2 -0
  191. package/dist/{messages-CQUbgCd62.mjs → messages-CxX23Jsk2.mjs} +4 -2
  192. package/dist/{chunks/messages-DTqb7T61.mjs → messages-D-rvoUSJ.mjs} +2 -0
  193. package/dist/{messages-Bkbhk9kJ2.mjs → messages-D1Fjr4OK2.mjs} +2 -0
  194. package/dist/{chunks/messages-CSkYL3AP.mjs → messages-D4U4wkYM.mjs} +2 -0
  195. package/dist/{chunks/messages-DwuvQpsD.mjs → messages-D5qgCWmB2.mjs} +7 -5
  196. package/dist/{chunks/messages-a_XmI-x5.mjs → messages-D788KtGe.mjs} +2 -0
  197. package/dist/{chunks/messages-B4-oaYBZ.mjs → messages-DFfhoDWZ2.mjs} +2 -0
  198. package/dist/{messages-BgcWyc4R.mjs → messages-DKsyrVp5.mjs} +2 -0
  199. package/dist/{messages-B_Js-HRK.mjs → messages-DL7JAwpC.mjs} +2 -0
  200. package/dist/{chunks/messages-CSioKJVK2.mjs → messages-DNlrcG5Z.mjs} +2 -0
  201. package/dist/{chunks/messages-vGRE1OPk2.mjs → messages-DPqFBtJR.mjs} +2 -0
  202. package/dist/{chunks/messages-BTKAxpmi.mjs → messages-DQik3_xv2.mjs} +2 -0
  203. package/dist/{messages-BUksRT7Q.mjs → messages-DTkd9ND8.mjs} +7 -5
  204. package/dist/{messages-Br4tspi_2.mjs → messages-DXCyNanc2.mjs} +2 -0
  205. package/dist/{chunks/messages-CdJq8h3E.mjs → messages-D_WzyzUt.mjs} +2 -0
  206. package/dist/{chunks/messages-DC0r7l8x2.mjs → messages-Ddz6eH0-2.mjs} +2 -0
  207. package/dist/{messages-BqCRuWLo.mjs → messages-DiL61awK.mjs} +2 -0
  208. package/dist/{chunks/messages-DXaps_uI.mjs → messages-Dq2WEsEu.mjs} +2 -0
  209. package/dist/{chunks/messages-8qsQ7qZX2.mjs → messages-DqXJJPx9.mjs} +3 -1
  210. package/dist/{messages-jh3qE8dL.mjs → messages-Duk7VVeY.mjs} +3 -1
  211. package/dist/{messages-DQHRgl_P.mjs → messages-DzkZ434Z.mjs} +4 -2
  212. package/dist/{messages-DUdZKtlE.mjs → messages-JKXCsFKZ.mjs} +2 -0
  213. package/dist/{chunks/messages-C9-UAJuq.mjs → messages-NVepzgE3.mjs} +4 -2
  214. package/dist/{messages-omcUe0Za.mjs → messages-PQVh93mt.mjs} +2 -0
  215. package/dist/{chunks/messages-xRQMQj4w2.mjs → messages-ftPYsH6d2.mjs} +6 -4
  216. package/dist/{chunks/messages-Ddb_L6gp2.mjs → messages-gQ5PFa2t2.mjs} +2 -0
  217. package/dist/{messages-DzqQWiFx.mjs → messages-oIa8wahx.mjs} +2 -0
  218. package/dist/{messages-DO62ugjy2.mjs → messages-rpO1POP02.mjs} +3 -1
  219. package/dist/{messages-63r3O0932.mjs → messages-uy3FE24_2.mjs} +4 -2
  220. package/dist/react.cjs +1 -1
  221. package/dist/react.mjs +1 -1
  222. package/dist/tools.cjs +1 -1
  223. package/dist/tools.mjs +2 -2
  224. package/package.json +1 -1
  225. package/src/blok.ts +2 -0
  226. package/src/cli/commands/convert-html/block-builder.ts +13 -3
  227. package/src/components/i18n/locales/am/messages.json +2 -1
  228. package/src/components/i18n/locales/ar/messages.json +2 -1
  229. package/src/components/i18n/locales/az/messages.json +3 -2
  230. package/src/components/i18n/locales/bg/messages.json +2 -1
  231. package/src/components/i18n/locales/bn/messages.json +2 -1
  232. package/src/components/i18n/locales/bs/messages.json +6 -5
  233. package/src/components/i18n/locales/cs/messages.json +4 -3
  234. package/src/components/i18n/locales/da/messages.json +6 -5
  235. package/src/components/i18n/locales/de/messages.json +4 -3
  236. package/src/components/i18n/locales/dv/messages.json +2 -1
  237. package/src/components/i18n/locales/el/messages.json +2 -1
  238. package/src/components/i18n/locales/en/messages.json +2 -0
  239. package/src/components/i18n/locales/es/messages.json +3 -2
  240. package/src/components/i18n/locales/et/messages.json +3 -2
  241. package/src/components/i18n/locales/fa/messages.json +2 -1
  242. package/src/components/i18n/locales/fi/messages.json +3 -2
  243. package/src/components/i18n/locales/fil/messages.json +7 -6
  244. package/src/components/i18n/locales/fr/messages.json +4 -3
  245. package/src/components/i18n/locales/gu/messages.json +2 -1
  246. package/src/components/i18n/locales/he/messages.json +2 -1
  247. package/src/components/i18n/locales/hi/messages.json +2 -1
  248. package/src/components/i18n/locales/hr/messages.json +5 -4
  249. package/src/components/i18n/locales/hu/messages.json +4 -3
  250. package/src/components/i18n/locales/hy/messages.json +2 -1
  251. package/src/components/i18n/locales/id/messages.json +2 -1
  252. package/src/components/i18n/locales/it/messages.json +5 -4
  253. package/src/components/i18n/locales/ja/messages.json +2 -1
  254. package/src/components/i18n/locales/ka/messages.json +2 -1
  255. package/src/components/i18n/locales/km/messages.json +2 -1
  256. package/src/components/i18n/locales/kn/messages.json +2 -1
  257. package/src/components/i18n/locales/ko/messages.json +2 -1
  258. package/src/components/i18n/locales/ku/messages.json +2 -1
  259. package/src/components/i18n/locales/lo/messages.json +2 -1
  260. package/src/components/i18n/locales/lt/messages.json +3 -2
  261. package/src/components/i18n/locales/lv/messages.json +3 -2
  262. package/src/components/i18n/locales/mk/messages.json +2 -1
  263. package/src/components/i18n/locales/ml/messages.json +2 -1
  264. package/src/components/i18n/locales/mn/messages.json +2 -1
  265. package/src/components/i18n/locales/mr/messages.json +2 -1
  266. package/src/components/i18n/locales/ms/messages.json +4 -3
  267. package/src/components/i18n/locales/my/messages.json +2 -1
  268. package/src/components/i18n/locales/ne/messages.json +2 -1
  269. package/src/components/i18n/locales/nl/messages.json +4 -3
  270. package/src/components/i18n/locales/no/messages.json +4 -3
  271. package/src/components/i18n/locales/pa/messages.json +2 -1
  272. package/src/components/i18n/locales/pl/messages.json +4 -3
  273. package/src/components/i18n/locales/ps/messages.json +2 -1
  274. package/src/components/i18n/locales/pt/messages.json +6 -5
  275. package/src/components/i18n/locales/ro/messages.json +3 -2
  276. package/src/components/i18n/locales/ru/messages.json +2 -1
  277. package/src/components/i18n/locales/sd/messages.json +2 -1
  278. package/src/components/i18n/locales/si/messages.json +2 -1
  279. package/src/components/i18n/locales/sk/messages.json +6 -5
  280. package/src/components/i18n/locales/sl/messages.json +5 -4
  281. package/src/components/i18n/locales/sq/messages.json +3 -2
  282. package/src/components/i18n/locales/sr/messages.json +2 -1
  283. package/src/components/i18n/locales/sv/messages.json +7 -6
  284. package/src/components/i18n/locales/sw/messages.json +2 -1
  285. package/src/components/i18n/locales/ta/messages.json +2 -1
  286. package/src/components/i18n/locales/te/messages.json +2 -1
  287. package/src/components/i18n/locales/th/messages.json +2 -1
  288. package/src/components/i18n/locales/tr/messages.json +2 -1
  289. package/src/components/i18n/locales/ug/messages.json +2 -1
  290. package/src/components/i18n/locales/uk/messages.json +2 -1
  291. package/src/components/i18n/locales/ur/messages.json +2 -1
  292. package/src/components/i18n/locales/vi/messages.json +2 -1
  293. package/src/components/i18n/locales/yi/messages.json +2 -1
  294. package/src/components/i18n/locales/zh/messages.json +2 -1
  295. package/src/components/icons/index.ts +6 -3
  296. package/src/components/inline-tools/inline-tool-marker.ts +12 -0
  297. package/src/components/modules/api/blocks.ts +3 -0
  298. package/src/components/modules/paste/google-docs-preprocessor.ts +64 -28
  299. package/src/components/modules/paste/sanitizer-config.ts +10 -1
  300. package/src/components/modules/renderer.ts +7 -1
  301. package/src/components/modules/toolbar/blockSettings.ts +54 -7
  302. package/src/components/modules/toolbar/content-alignment.ts +89 -0
  303. package/src/components/modules/toolbar/index.ts +85 -61
  304. package/src/components/modules/ui.ts +19 -6
  305. package/src/components/modules/uiControllers/controllers/blockHover.ts +20 -0
  306. package/src/components/utils/color-mapping.ts +15 -0
  307. package/src/components/utils/color-migration.ts +10 -0
  308. package/src/components/utils/data-model-transform.ts +200 -0
  309. package/src/components/utils/default-page-colors.ts +130 -0
  310. package/src/components/utils/highlight-block-arrival.ts +50 -0
  311. package/src/components/utils/popover/popover-desktop.ts +23 -1
  312. package/src/styles/image.css +86 -0
  313. package/src/styles/keyframes.css +6 -0
  314. package/src/styles/main.css +17 -0
  315. package/src/tools/database/index.ts +3 -4
  316. package/src/tools/image/dimensions-from-svg.ts +53 -0
  317. package/src/tools/image/dimensions-from-url.ts +87 -0
  318. package/src/tools/image/error-state.ts +7 -2
  319. package/src/tools/image/index.ts +81 -7
  320. package/src/tools/image/probe-dimensions.ts +36 -0
  321. package/src/tools/image/ui.ts +46 -2
  322. package/src/tools/image/uploader.ts +12 -8
  323. package/src/tools/table/table-cell-clipboard.ts +17 -3
  324. package/types/tools/image.d.ts +16 -2
  325. package/dist/chunks/constants-JFG5JxhI.cjs +0 -457
  326. package/dist/chunks/messages-C-FUK4Ki2.cjs +0 -1
  327. package/dist/chunks/messages-CLkrVqVp.cjs +0 -1
  328. package/dist/chunks/messages-D9qYuzOY2.cjs +0 -1
  329. package/dist/chunks/messages-DEdX7dXt2.cjs +0 -1
  330. package/dist/chunks/tools-Cnlc1TxG.cjs +0 -116
@@ -0,0 +1,89 @@
1
+ import { DATA_ATTR } from '../../constants';
2
+
3
+ /**
4
+ * Horizontal alignment helpers for the toolbar.
5
+ *
6
+ * The toolbar's inner content wrapper (holding the plus button + drag handle)
7
+ * must align with the block's visible content column. Two layouts exist:
8
+ *
9
+ * 1. Non-stretched block — `[data-blok-element-content]` is centered inside
10
+ * the holder via `mx-auto` + `max-w-blok-content`. The gap between holder
11
+ * left and content left IS the offset we want.
12
+ *
13
+ * 2. Stretched block (e.g. `database`) — the content element fills the holder
14
+ * (max-width removed), so the raw gap is 0. The tool may still re-center
15
+ * its visible content via internal CSS padding. In this case we fall back
16
+ * to the editor's canonical content-column width (`--max-width-content`)
17
+ * and center that inside the holder. Without this the toolbar snaps to
18
+ * the holder's far-left edge, far from the visible content.
19
+ */
20
+
21
+ /**
22
+ * Compute the horizontal offset between the holder's left edge and the block's
23
+ * visible content column. See module doc for the two layout cases.
24
+ * @param targetBlockHolder - Block holder element (has `data-blok-stretched` when stretched)
25
+ * @param contentRect - bounding rect of the block's `[data-blok-element-content]`
26
+ * @param wrapperRect - bounding rect of the toolbar wrapper (co-located with holder)
27
+ */
28
+ export function computeVisualContentOffset(
29
+ targetBlockHolder: HTMLElement,
30
+ contentRect: DOMRect,
31
+ wrapperRect: DOMRect | undefined
32
+ ): number {
33
+ if (wrapperRect === undefined) {
34
+ return 0;
35
+ }
36
+
37
+ const stretched = targetBlockHolder.getAttribute(DATA_ATTR.stretched) === 'true';
38
+
39
+ if (stretched) {
40
+ const maxContent = readMaxContentWidth(targetBlockHolder);
41
+
42
+ if (maxContent !== null && wrapperRect.width > maxContent) {
43
+ return (wrapperRect.width - maxContent) / 2;
44
+ }
45
+ }
46
+
47
+ return Math.max(0, contentRect.left - wrapperRect.left);
48
+ }
49
+
50
+ /**
51
+ * Resolve the visible content width used to clamp the toolbar wrapper's
52
+ * max-width. Stretched blocks still size their visible content to the
53
+ * canonical content column (`--max-width-content`), so clamp to that
54
+ * instead of the full holder width — otherwise the toolbar hover target
55
+ * would span the whole viewport.
56
+ * @param targetBlockHolder - Block holder element
57
+ * @param contentRect - bounding rect of the block's `[data-blok-element-content]`
58
+ * @param wrapperRect - bounding rect of the toolbar wrapper (co-located with holder)
59
+ */
60
+ export function resolveVisualContentWidth(
61
+ targetBlockHolder: HTMLElement,
62
+ contentRect: DOMRect,
63
+ wrapperRect: DOMRect | undefined
64
+ ): number {
65
+ const stretched = targetBlockHolder.getAttribute(DATA_ATTR.stretched) === 'true';
66
+
67
+ if (stretched && wrapperRect !== undefined) {
68
+ const maxContent = readMaxContentWidth(targetBlockHolder);
69
+
70
+ if (maxContent !== null && wrapperRect.width > maxContent) {
71
+ return maxContent;
72
+ }
73
+ }
74
+
75
+ return contentRect.width;
76
+ }
77
+
78
+ /**
79
+ * Read the `--max-width-content` CSS custom property off the given element
80
+ * (inherited from `:root`/`@theme`). Returns `null` if the token is absent
81
+ * or not a positive finite number.
82
+ * @param element - element whose computed style supplies the CSS var
83
+ */
84
+ function readMaxContentWidth(element: HTMLElement): number | null {
85
+ const raw = getComputedStyle(element).getPropertyValue('--max-width-content').trim();
86
+ const value = parseFloat(raw);
87
+
88
+ return Number.isFinite(value) && value > 0 ? value : null;
89
+ }
@@ -17,6 +17,7 @@ import { hide } from '../../utils/tooltip';
17
17
  * Refactored Toolbar module components
18
18
  */
19
19
  import { ClickDragHandler } from './click-handler';
20
+ import { computeVisualContentOffset, resolveVisualContentWidth } from './content-alignment';
20
21
  import { PlusButtonHandler } from './plus-button';
21
22
  import { ToolbarPositioner } from './positioning';
22
23
  import { SettingsTogglerHandler } from './settings-toggler';
@@ -360,20 +361,47 @@ export class Toolbar extends Module<ToolbarNodes> {
360
361
 
361
362
 
362
363
  /**
363
- * Toggles read-only mode
364
+ * Toggles read-only mode.
365
+ *
366
+ * Read-only (Notion-style): the toolbar and block settings stay alive so the
367
+ * user can still hover a block and copy a link to it. The plus button is
368
+ * hidden and the drag gesture is suppressed (see moveAndOpen + plus-button
369
+ * handler).
364
370
  * @param {boolean} readOnlyEnabled - read-only mode
365
371
  */
366
372
  public toggleReadOnly(readOnlyEnabled: boolean): void {
367
- if (!readOnlyEnabled) {
373
+ /**
374
+ * Draw the toolbar the first time we need it. Previously the toolbar DOM
375
+ * was only built on the readOnly=false path; now read-only also hosts a
376
+ * (restricted) block settings popover, so ensure the DOM exists in both
377
+ * modes. Subsequent toggles just flip plus-button visibility.
378
+ */
379
+ if (this.nodes.wrapper === undefined) {
368
380
  window.requestIdleCallback(async () => {
369
381
  await this.drawUI();
370
382
  this.enableModuleBindings();
383
+ this.applyReadOnlyToPlusButton(readOnlyEnabled);
371
384
  }, { timeout: 2000 });
372
- } else {
373
- this.destroy();
374
- this.Blok.BlockSettings.destroy();
375
- this.disableModuleBindings();
385
+
386
+ return;
376
387
  }
388
+
389
+ this.applyReadOnlyToPlusButton(readOnlyEnabled);
390
+ }
391
+
392
+ /**
393
+ * Hides the plus button in read-only mode, restores its visibility otherwise.
394
+ * Uses inline display:none because the button element already uses inline
395
+ * style toggling elsewhere (moveAndOpen, close, moveAndOpenForMultipleBlocks).
396
+ */
397
+ private applyReadOnlyToPlusButton(readOnlyEnabled: boolean): void {
398
+ const { plusButton } = this.nodes;
399
+
400
+ if (plusButton === undefined) {
401
+ return;
402
+ }
403
+
404
+ plusButton.style.display = readOnlyEnabled ? 'none' : '';
377
405
  }
378
406
 
379
407
  /**
@@ -476,8 +504,9 @@ export class Toolbar extends Module<ToolbarNodes> {
476
504
  * whole table undraggable while the user edits cell text.
477
505
  */
478
506
  const isCalloutFirstChild = this.isFirstChildOfCallout(targetBlock);
507
+ const hidePlusButton = isCalloutFirstChild || this.Blok.ReadOnly.isEnabled;
479
508
 
480
- plusButton.style.display = isCalloutFirstChild ? 'none' : '';
509
+ plusButton.style.display = hidePlusButton ? 'none' : '';
481
510
 
482
511
  if (settingsToggler) {
483
512
  settingsToggler.style.display = isCalloutFirstChild ? 'none' : '';
@@ -594,38 +623,41 @@ export class Toolbar extends Module<ToolbarNodes> {
594
623
  this.restoreSettingsTogglerForLeftEdgeBlock(targetBlock);
595
624
  }
596
625
 
597
- /**
598
- * Sync toolbar content wrapper's position and width with the block content element
599
- * so toolbar buttons align with the block content edge regardless of whether
600
- * the consumer uses CSS margin or overrides max-width (e.g. wide-mode).
601
- *
602
- * Uses getBoundingClientRect to get the actual visual offset rather than reading
603
- * CSS marginLeft, which does not account for cases where max-width is removed
604
- * and the content fills the full container width.
605
- *
606
- * Uses Math.max to guarantee the actions container (positioned via right:100%)
607
- * never extends beyond the left edge of the viewport, which would make the
608
- * drag handle unreachable by pointer events.
609
- *
610
- * For nested blocks (e.g. children inside a callout), the holder is already
611
- * offset from the viewport left by the parent's indentation. In that case we
612
- * only need to ensure the actions don't extend beyond the viewport left edge
613
- * (holderLeft px are available to the left), so the minimum margin is
614
- * max(0, actionsWidth - holderLeft) rather than a flat actionsWidth clamp.
615
- */
616
- if (blockContentElement && this.nodes.content) {
617
- const holderRect = this.nodes.wrapper?.getBoundingClientRect();
618
- const contentRect = blockContentElement.getBoundingClientRect();
619
- const visualOffset = holderRect ? Math.max(0, contentRect.left - holderRect.left) : 0;
620
- const actionsWidth = this.nodes.actions?.offsetWidth ?? 0;
621
- const holderLeft = holderRect ? Math.max(0, holderRect.left) : 0;
622
- const minMarginLeft = Math.max(0, actionsWidth - holderLeft);
623
-
624
- this.nodes.content.style.marginLeft = `${Math.max(visualOffset, minMarginLeft)}px`;
625
- this.nodes.content.style.maxWidth = `${contentRect.width}px`;
626
+ if (blockContentElement) {
627
+ this.syncContentToBlock(targetBlockHolder, blockContentElement);
626
628
  }
627
629
  }
628
630
 
631
+ /**
632
+ * Align the toolbar's inner content wrapper with the block's visible content column.
633
+ * See `content-alignment.ts` for the two-case reasoning (non-stretched vs stretched).
634
+ *
635
+ * `Math.max` with `actionsWidth - holderLeft` guarantees the actions bar
636
+ * (positioned at `right:100%`) never extends past the viewport's left edge,
637
+ * which would make the drag handle unreachable by pointer events. For nested
638
+ * blocks already offset from the viewport, `holderLeft` px-of-slack relax the
639
+ * minimum so buttons aren't pushed into the text content.
640
+ * @param targetBlockHolder - Block holder element
641
+ * @param blockContentElement - `[data-blok-element-content]` element inside the holder
642
+ */
643
+ private syncContentToBlock(targetBlockHolder: HTMLElement, blockContentElement: HTMLElement): void {
644
+ if (this.nodes.content === undefined) {
645
+ return;
646
+ }
647
+
648
+ const wrapperRect = this.nodes.wrapper?.getBoundingClientRect();
649
+ const contentRect = blockContentElement.getBoundingClientRect();
650
+ const visualOffset = computeVisualContentOffset(targetBlockHolder, contentRect, wrapperRect);
651
+ const actionsWidth = this.nodes.actions?.offsetWidth ?? 0;
652
+ const holderLeft = wrapperRect ? Math.max(0, wrapperRect.left) : 0;
653
+ const minMarginLeft = Math.max(0, actionsWidth - holderLeft);
654
+ const effectiveOffset = Math.max(visualOffset, minMarginLeft);
655
+ const contentWidth = resolveVisualContentWidth(targetBlockHolder, contentRect, wrapperRect);
656
+
657
+ this.nodes.content.style.marginLeft = `${effectiveOffset}px`;
658
+ this.nodes.content.style.maxWidth = `${contentWidth}px`;
659
+ }
660
+
629
661
  /**
630
662
  * Move Toolbar to the specified block (or first selected block) and open it for multi-block selection.
631
663
  * Keeps the add button visible so users can still insert blocks while multiple are selected.
@@ -697,8 +729,9 @@ export class Toolbar extends Module<ToolbarNodes> {
697
729
  /**
698
730
  * Restore plus button and settings toggler visibility for multi-block selection,
699
731
  * in case they were hidden for table cell blocks.
732
+ * In read-only mode, keep the plus button hidden.
700
733
  */
701
- plusButton.style.display = '';
734
+ plusButton.style.display = this.Blok.ReadOnly.isEnabled ? 'none' : '';
702
735
  plusButton.style.color = '';
703
736
 
704
737
  if (settingsToggler) {
@@ -742,23 +775,8 @@ export class Toolbar extends Module<ToolbarNodes> {
742
775
 
743
776
  this.open();
744
777
 
745
- /**
746
- * Sync toolbar content wrapper's position and width with the block content element.
747
- * Uses getBoundingClientRect so wide-mode content (max-width: none) is handled correctly.
748
- * Clamp to max(0, actionsWidth - holderLeft) so actions never extend beyond the left
749
- * viewport edge. For nested blocks already offset from the left, a smaller clamp is
750
- * used so buttons are not pushed into the text content.
751
- */
752
- if (blockContentElement && this.nodes.content) {
753
- const holderRect = this.nodes.wrapper?.getBoundingClientRect();
754
- const contentRect = blockContentElement.getBoundingClientRect();
755
- const visualOffset = holderRect ? Math.max(0, contentRect.left - holderRect.left) : 0;
756
- const actionsWidth = this.nodes.actions?.offsetWidth ?? 0;
757
- const holderLeft = holderRect ? Math.max(0, holderRect.left) : 0;
758
- const minMarginLeft = Math.max(0, actionsWidth - holderLeft);
759
-
760
- this.nodes.content.style.marginLeft = `${Math.max(visualOffset, minMarginLeft)}px`;
761
- this.nodes.content.style.maxWidth = `${contentRect.width}px`;
778
+ if (blockContentElement) {
779
+ this.syncContentToBlock(targetBlockHolder, blockContentElement);
762
780
  }
763
781
  }
764
782
 
@@ -767,10 +785,6 @@ export class Toolbar extends Module<ToolbarNodes> {
767
785
  * @param options - Optional configuration
768
786
  */
769
787
  public close(options?: ToolbarCloseOptions): void {
770
- if (this.Blok.ReadOnly.isEnabled) {
771
- return;
772
- }
773
-
774
788
  // eslint-disable-next-line @typescript-eslint/no-deprecated
775
789
  this.nodes.wrapper?.classList.remove(this.CSS.toolbarOpened);
776
790
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -804,10 +818,11 @@ export class Toolbar extends Module<ToolbarNodes> {
804
818
 
805
819
  /**
806
820
  * Restore plus button and settings toggler visibility
807
- * in case they were hidden for table cell blocks
821
+ * in case they were hidden for table cell blocks.
822
+ * In read-only mode, keep the plus button hidden.
808
823
  */
809
824
  if (this.nodes.plusButton) {
810
- this.nodes.plusButton.style.display = '';
825
+ this.nodes.plusButton.style.display = this.Blok.ReadOnly.isEnabled ? 'none' : '';
811
826
  this.nodes.plusButton.style.color = '';
812
827
  }
813
828
 
@@ -948,8 +963,9 @@ export class Toolbar extends Module<ToolbarNodes> {
948
963
  }
949
964
 
950
965
  const isCalloutFirstChild = this.hoveredBlock !== null && this.isFirstChildOfCallout(this.hoveredBlock);
966
+ const hidePlusButton = isCalloutFirstChild || this.Blok.ReadOnly.isEnabled;
951
967
 
952
- plusButton.style.display = isCalloutFirstChild ? 'none' : '';
968
+ plusButton.style.display = hidePlusButton ? 'none' : '';
953
969
 
954
970
  if (settingsToggler) {
955
971
  settingsToggler.style.display = isCalloutFirstChild ? 'none' : '';
@@ -1244,6 +1260,14 @@ export class Toolbar extends Module<ToolbarNodes> {
1244
1260
 
1245
1261
  if (plusButton) {
1246
1262
  this.readOnlyMutableListeners.on(plusButton, 'mousedown', (e) => {
1263
+ /**
1264
+ * Plus button is inert in read-only mode. It is also visually hidden
1265
+ * (see toggleReadOnly), but guard here too in case of programmatic clicks.
1266
+ */
1267
+ if (this.Blok.ReadOnly.isEnabled) {
1268
+ return;
1269
+ }
1270
+
1247
1271
  /**
1248
1272
  * Prevent focus from moving away from the currently-active contenteditable block.
1249
1273
  * Without this, clicking the plus button steals DOM focus, causing subsequent
@@ -261,10 +261,18 @@ export class UI extends Module<UINodes> {
261
261
  */
262
262
  if (readOnlyEnabled) {
263
263
  /**
264
- * Unbind all events
265
- *
264
+ * Unbind editing-only events but keep block hover active so the toolbar
265
+ * can still appear on hover (used by the read-only "copy link to block"
266
+ * popover; see Toolbar.toggleReadOnly).
266
267
  */
267
- this.unbindReadOnlySensitiveListeners();
268
+ this.unbindReadOnlySensitiveListeners({ keepBlockHover: true });
269
+
270
+ /**
271
+ * Ensure block hover detection is active even when the editor starts in
272
+ * read-only mode — bindReadOnlySensitiveListeners() was never called in
273
+ * that path.
274
+ */
275
+ this.blockHoverController?.enable();
268
276
 
269
277
  /**
270
278
  * Set contenteditable="false" on all block content elements
@@ -672,8 +680,11 @@ export class UI extends Module<UINodes> {
672
680
 
673
681
  /**
674
682
  * Unbind events that should work only in read-only mode
683
+ * @param options - when keepBlockHover is true, the block-hover controller
684
+ * stays enabled so the toolbar can still appear on hover. Used when entering
685
+ * read-only mode; editor destroy still disables everything.
675
686
  */
676
- private unbindReadOnlySensitiveListeners(): void {
687
+ private unbindReadOnlySensitiveListeners(options?: { keepBlockHover?: boolean }): void {
677
688
  this.readOnlyMutableListeners.clearAll();
678
689
 
679
690
  /**
@@ -682,9 +693,11 @@ export class UI extends Module<UINodes> {
682
693
  this.keyboardController?.disable();
683
694
 
684
695
  /**
685
- * Disable block hover controller
696
+ * Disable block hover controller unless the caller opted to keep it alive.
686
697
  */
687
- this.blockHoverController?.disable();
698
+ if (options?.keepBlockHover !== true) {
699
+ this.blockHoverController?.disable();
700
+ }
688
701
  }
689
702
 
690
703
  /**
@@ -48,10 +48,22 @@ export class BlockHoverController extends Controller {
48
48
  super(options);
49
49
  }
50
50
 
51
+ /**
52
+ * Whether the controller's listeners are currently bound. Used to make
53
+ * enable() idempotent so repeated calls (e.g. during read-only toggling)
54
+ * do not register the mousemove handler multiple times.
55
+ */
56
+ private isEnabled: boolean = false;
57
+
51
58
  /**
52
59
  * Enable block hover detection
53
60
  */
54
61
  public override enable(): void {
62
+ if (this.isEnabled) {
63
+ return;
64
+ }
65
+
66
+ this.isEnabled = true;
55
67
  /**
56
68
  * Local function that handles block hover detection
57
69
  * Bound to 'this' to preserve context when passed to throttle
@@ -252,6 +264,14 @@ export class BlockHoverController extends Controller {
252
264
  return result.block;
253
265
  }
254
266
 
267
+ /**
268
+ * Disable the controller and clear its listeners.
269
+ */
270
+ public override disable(): void {
271
+ super.disable();
272
+ this.isEnabled = false;
273
+ }
274
+
255
275
  /**
256
276
  * Reset the last hovered block (useful for testing or state reset)
257
277
  */
@@ -1,4 +1,5 @@
1
1
  import { COLOR_PRESETS, COLOR_PRESETS_DARK } from '../shared/color-presets';
2
+ import { isDefaultDarkBackground, isDefaultWhiteBackground } from './default-page-colors';
2
3
 
3
4
  /**
4
5
  * Convert an HSL color (H in degrees, S and L as 0-100 percentages) to an RGB tuple.
@@ -214,6 +215,13 @@ function hslDistance(
214
215
  * @returns the nearest preset hex color, or the input unchanged if unparseable
215
216
  */
216
217
  export function mapToNearestPresetColor(cssColor: string, mode: 'text' | 'bg'): string {
218
+ /* Defense-in-depth: default page backgrounds (white / near-black) would otherwise
219
+ * collapse onto the gray bg preset since gray is the only achromatic bg color.
220
+ * Return the input unchanged so callers preserve the page-default rendering. */
221
+ if (mode === 'bg' && (isDefaultWhiteBackground(cssColor) || isDefaultDarkBackground(cssColor))) {
222
+ return cssColor;
223
+ }
224
+
217
225
  const rgb = parseColor(cssColor);
218
226
 
219
227
  if (rgb === null) {
@@ -251,6 +259,13 @@ export function mapToNearestPresetColor(cssColor: string, mode: 'text' | 'bg'):
251
259
  * @returns the nearest preset name (e.g. 'red'), or null if unparseable
252
260
  */
253
261
  export function mapToNearestPresetName(cssColor: string, mode: 'text' | 'bg'): string | null {
262
+ /* Defense-in-depth: default page backgrounds (white / near-black) would otherwise
263
+ * collapse onto the gray bg preset since gray is the only achromatic bg color.
264
+ * Return null so callers leave the original styling untouched. */
265
+ if (mode === 'bg' && (isDefaultWhiteBackground(cssColor) || isDefaultDarkBackground(cssColor))) {
266
+ return null;
267
+ }
268
+
254
269
  const rgb = parseColor(cssColor);
255
270
 
256
271
  if (rgb === null) {
@@ -1,5 +1,6 @@
1
1
  import { colorVarName } from '../shared/color-presets';
2
2
  import { mapToNearestPresetName } from './color-mapping';
3
+ import { isDefaultDarkBackground, isDefaultWhiteBackground } from './default-page-colors';
3
4
 
4
5
  const PROPS = ['color', 'background-color'] as const;
5
6
  type Prop = typeof PROPS[number];
@@ -27,6 +28,15 @@ export function migrateMarkColors(container: Element): void {
27
28
  continue;
28
29
  }
29
30
 
31
+ /* Strip default page background-colors so persisted defaults don't render
32
+ * as a styled <mark>. mapToNearestPresetName already returns null for
33
+ * these inputs, but null only blocks substitution — it does not remove
34
+ * the existing inline value. */
35
+ if (prop === 'background-color' && (isDefaultWhiteBackground(value) || isDefaultDarkBackground(value))) {
36
+ el.style.removeProperty(prop);
37
+ continue;
38
+ }
39
+
30
40
  const name = mapToNearestPresetName(value, PROP_MODE[prop]);
31
41
 
32
42
  if (name !== null) {
@@ -1238,6 +1238,206 @@ export const normalizeTableChildParents = (blocks: OutputBlockData[]): OutputBlo
1238
1238
  });
1239
1239
  };
1240
1240
 
1241
+ /**
1242
+ * Positional cell-child id minted by the HTML→blok migration: `cell-<row>-<col>`
1243
+ * with 1-based row/col (e.g. `cell-2-1` = row 2, column 1). See the migration
1244
+ * system prompt (EditorJsHtmlConversionSystemPrompt) which instructs the model
1245
+ * to emit one paragraph per non-empty cell with exactly this id.
1246
+ */
1247
+ const DETACHED_CELL_ID_PATTERN = /^cell-([1-9]\d*)-([1-9]\d*)$/;
1248
+
1249
+ interface CellPosition {
1250
+ row: number;
1251
+ col: number;
1252
+ }
1253
+
1254
+ const parseDetachedCellPosition = (id: BlockId | undefined): CellPosition | null => {
1255
+ if (typeof id !== 'string') {
1256
+ return null;
1257
+ }
1258
+ const match = DETACHED_CELL_ID_PATTERN.exec(id);
1259
+
1260
+ if (match === null) {
1261
+ return null;
1262
+ }
1263
+ const row = Number(match[1]) - 1;
1264
+ const col = Number(match[2]) - 1;
1265
+
1266
+ if (row < 0 || col < 0) {
1267
+ return null;
1268
+ }
1269
+ return { row, col };
1270
+ };
1271
+
1272
+ const tableCellIsEmptyAt = (tableBlock: OutputBlockData, row: number, col: number): boolean => {
1273
+ const rows = getTableContentRows(tableBlock.data);
1274
+
1275
+ if (rows === null) {
1276
+ return false;
1277
+ }
1278
+ const rowCells = rows[row];
1279
+
1280
+ if (!Array.isArray(rowCells)) {
1281
+ return false;
1282
+ }
1283
+ const cell = rowCells[col];
1284
+
1285
+ return isCellWithBlockRefs(cell) && cell.blocks.length === 0;
1286
+ };
1287
+
1288
+ const collectReferencedCellChildIds = (blocks: OutputBlockData[]): Set<string> => {
1289
+ const referenced = new Set<string>();
1290
+
1291
+ blocks
1292
+ .filter(block => block.type === 'table')
1293
+ .forEach(tableBlock => {
1294
+ const rows = getTableContentRows(tableBlock.data);
1295
+
1296
+ rows?.forEach(row => {
1297
+ if (!Array.isArray(row)) {
1298
+ return;
1299
+ }
1300
+ row.forEach(cell => {
1301
+ if (!isCellWithBlockRefs(cell)) {
1302
+ return;
1303
+ }
1304
+ cell.blocks.forEach(id => {
1305
+ if (typeof id === 'string') {
1306
+ referenced.add(id);
1307
+ }
1308
+ });
1309
+ });
1310
+ });
1311
+ });
1312
+
1313
+ return referenced;
1314
+ };
1315
+
1316
+ /**
1317
+ * Recover migrated table cells whose text was detached by a pre-fix save.
1318
+ *
1319
+ * The "migration table content loss" bug worked like this: a migrated table
1320
+ * references its cell text via `data.content[r][c].blocks = [<childId>]`, but the
1321
+ * child paragraph lacked `parent === tableId`. A save on an unfixed editor filtered
1322
+ * that child out of the cell (Table.save() keeps only `parentId === tableId`) and
1323
+ * the saver emitted it as a root-level block. The text is NOT lost — it survives
1324
+ * as a detached top-level paragraph — but the cell now reads `{ blocks: [] }`.
1325
+ *
1326
+ * Crucially, the migration minted that paragraph with a POSITIONAL id
1327
+ * (`cell-<row>-<col>`, 1-based) that still encodes which cell it came from. This
1328
+ * pass walks every root-level block whose id matches that pattern and is no longer
1329
+ * referenced by any cell, and — when exactly one table has a matching EMPTY cell at
1330
+ * that position — re-attaches it: the id is restored to `content[row][col].blocks`
1331
+ * and the block's `parent` is set to that table.
1332
+ *
1333
+ * Safety: only empty cells are filled (occupied cells are never overwritten), only
1334
+ * `cell-R-C`-id root blocks are consumed (user-authored blocks never match), blocks
1335
+ * that already carry a `parent` are skipped, and reclamation is skipped entirely
1336
+ * when two or more tables share the matching empty cell (ambiguous — never guess).
1337
+ * The function is non-mutating and returns the same array reference when there is
1338
+ * nothing to reclaim.
1339
+ * @param blocks - flat block array potentially containing detached migrated cells
1340
+ */
1341
+ export const reclaimDetachedTableCells = (blocks: OutputBlockData[]): OutputBlockData[] => {
1342
+ const tableBlocks = blocks.filter(
1343
+ (block): block is OutputBlockData => block.type === 'table' && block.id !== undefined && block.id !== null
1344
+ );
1345
+
1346
+ // The positional id `cell-<row>-<col>` encodes the cell but NOT which table.
1347
+ // With two or more tables the owning table is unidentifiable, so re-attaching
1348
+ // could silently drop text into the wrong table. Only single-table documents
1349
+ // are unambiguous; anything else is left untouched (never guess).
1350
+ if (tableBlocks.length !== 1) {
1351
+ return blocks;
1352
+ }
1353
+
1354
+ const referenced = collectReferencedCellChildIds(blocks);
1355
+
1356
+ // Count id occurrences: a duplicated id is ambiguous and would be regenerated
1357
+ // by the renderer's dedup pass after reclaim (leaving a dangling cell ref), so
1358
+ // such ids must never be reclaimed.
1359
+ const idCounts = new Map<string, number>();
1360
+
1361
+ for (const block of blocks) {
1362
+ if (block.id !== undefined && block.id !== null) {
1363
+ idCounts.set(block.id, (idCounts.get(block.id) ?? 0) + 1);
1364
+ }
1365
+ }
1366
+
1367
+ const reclaimedParent = new Map<BlockId, BlockId>();
1368
+ const cellAdditions = new Map<BlockId, Array<CellPosition & { id: BlockId }>>();
1369
+
1370
+ for (const block of blocks) {
1371
+ if (block.id === undefined || block.id === null) {
1372
+ continue;
1373
+ }
1374
+ if (block.parent !== undefined && block.parent !== null) {
1375
+ continue;
1376
+ }
1377
+ if (referenced.has(block.id)) {
1378
+ continue;
1379
+ }
1380
+ if ((idCounts.get(block.id) ?? 0) > 1) {
1381
+ continue;
1382
+ }
1383
+ const position = parseDetachedCellPosition(block.id);
1384
+
1385
+ if (position === null) {
1386
+ continue;
1387
+ }
1388
+ const matchingTables = tableBlocks.filter(table =>
1389
+ tableCellIsEmptyAt(table, position.row, position.col)
1390
+ );
1391
+
1392
+ if (matchingTables.length !== 1) {
1393
+ continue;
1394
+ }
1395
+ const tableId = matchingTables[0].id as BlockId;
1396
+
1397
+ reclaimedParent.set(block.id, tableId);
1398
+ const additions = cellAdditions.get(tableId) ?? [];
1399
+
1400
+ additions.push({ ...position, id: block.id });
1401
+ cellAdditions.set(tableId, additions);
1402
+ }
1403
+
1404
+ if (reclaimedParent.size === 0) {
1405
+ return blocks;
1406
+ }
1407
+
1408
+ return blocks.map(block => {
1409
+ if (block.id !== undefined && block.id !== null && reclaimedParent.has(block.id)) {
1410
+ return { ...block, parent: reclaimedParent.get(block.id) };
1411
+ }
1412
+
1413
+ if (block.type === 'table' && block.id !== undefined && block.id !== null && cellAdditions.has(block.id)) {
1414
+ const additions = cellAdditions.get(block.id) ?? [];
1415
+ const rows = getTableContentRows(block.data);
1416
+
1417
+ if (rows === null) {
1418
+ return block;
1419
+ }
1420
+ const newContent = rows.map((row, rowIndex) => {
1421
+ if (!Array.isArray(row)) {
1422
+ return row;
1423
+ }
1424
+ return row.map((cell, colIndex) => {
1425
+ const addition = additions.find(a => a.row === rowIndex && a.col === colIndex);
1426
+
1427
+ if (addition === undefined || !isCellWithBlockRefs(cell)) {
1428
+ return cell;
1429
+ }
1430
+ return { ...cell, blocks: [...cell.blocks, addition.id] };
1431
+ });
1432
+ });
1433
+
1434
+ return { ...block, data: { ...(block.data as Record<string, unknown>), content: newContent } } as OutputBlockData;
1435
+ }
1436
+
1437
+ return block;
1438
+ });
1439
+ };
1440
+
1241
1441
  /**
1242
1442
  * Check if transformation is needed based on config and detected format
1243
1443
  */