@jackuait/blok 0.10.0-beta.9 → 0.10.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 (269) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-DDu252IK.mjs → blok-u_68bnlk.mjs} +1617 -1562
  3. package/dist/chunks/{constants-DMW9a31I.mjs → constants-VDhCUk4c.mjs} +56 -48
  4. package/dist/chunks/{i18next-loader-CwsYu0n6.mjs → i18next-loader-CDnSPae_.mjs} +1 -1
  5. package/dist/chunks/{lightweight-i18n-Cvv8CWh4.mjs → lightweight-i18n-DZmo8dAI.mjs} +1 -0
  6. package/dist/chunks/{messages-DG-4DPmP.mjs → messages-1_6UkKLS.mjs} +1 -0
  7. package/dist/{messages-CqXtJTpU.mjs → chunks/messages-4Ck88DYZ2.mjs} +1 -0
  8. package/dist/chunks/{messages-DGL1ySqb2.mjs → messages-8Ld7P_9j2.mjs} +1 -0
  9. package/dist/{messages-DLX_iBDJ.mjs → chunks/messages-BAlZjPcl.mjs} +1 -0
  10. package/dist/chunks/{messages-p1mbe__S.mjs → messages-BHMiK51R.mjs} +1 -0
  11. package/dist/chunks/{messages-Cdf0W9H02.mjs → messages-BJ-vT1SU2.mjs} +1 -0
  12. package/dist/{messages-Smt4GBbj.mjs → chunks/messages-BK8Cp2d0.mjs} +1 -0
  13. package/dist/{messages-Ci0KqX-J.mjs → chunks/messages-BKN3YVIj.mjs} +1 -0
  14. package/dist/chunks/{messages-BXM80fdr2.mjs → messages-BMD37y3q2.mjs} +1 -0
  15. package/dist/{messages-B19o-Teb.mjs → chunks/messages-BONyZroH.mjs} +1 -0
  16. package/dist/{messages-BwHs4cm1.mjs → chunks/messages-BRAoJpOu.mjs} +1 -0
  17. package/dist/{messages-DY4IqlhY.mjs → chunks/messages-BRoa9tGl.mjs} +1 -0
  18. package/dist/chunks/{messages-RInp1ytX.mjs → messages-BbEW9bQz.mjs} +1 -0
  19. package/dist/{messages-BIHc0KHY.mjs → chunks/messages-BeGZqQwz.mjs} +1 -0
  20. package/dist/{messages-CmB406HW.mjs → chunks/messages-BfAcUavP.mjs} +1 -0
  21. package/dist/chunks/{messages-Cu-Wevxs2.mjs → messages-BgM91Lxm2.mjs} +1 -0
  22. package/dist/{messages-BYNcD6uR.mjs → chunks/messages-BlxwW7M6.mjs} +1 -0
  23. package/dist/chunks/{messages-rCd0Rrw6.mjs → messages-Bz0-KNEB.mjs} +1 -0
  24. package/dist/{messages-7QD-X6XT2.mjs → chunks/messages-C0IFfhnp.mjs} +1 -0
  25. package/dist/{messages-Dl5Y2-Ia.mjs → chunks/messages-C15z2r5U.mjs} +1 -0
  26. package/dist/chunks/{messages-MxpWO1db.mjs → messages-C1S9ztpF.mjs} +1 -0
  27. package/dist/chunks/{messages-8IHf7ZP3.mjs → messages-CC_noR8y.mjs} +1 -0
  28. package/dist/chunks/{messages-COO5xmcA.mjs → messages-CD_MnBln.mjs} +1 -0
  29. package/dist/chunks/{messages-BYlSMRkd.mjs → messages-CIfUm1Oa.mjs} +1 -0
  30. package/dist/{messages-BbYq1pk-.mjs → chunks/messages-CPBN4zWc.mjs} +1 -0
  31. package/dist/{messages-DPA-mMWC2.mjs → chunks/messages-CQBo3lmL2.mjs} +1 -0
  32. package/dist/{messages-DnGJD4TL.mjs → chunks/messages-CRF7nNrO.mjs} +1 -0
  33. package/dist/{messages-D8FQWulF2.mjs → chunks/messages-CTCe595D2.mjs} +1 -0
  34. package/dist/{messages-BRZX964b2.mjs → chunks/messages-CW35K1pq.mjs} +1 -0
  35. package/dist/{messages-DnG0ef8t2.mjs → chunks/messages-CZSlfnkO2.mjs} +1 -0
  36. package/dist/chunks/{messages-iS34FHFB.mjs → messages-ChK7v1PV.mjs} +1 -0
  37. package/dist/{messages-BiUGXvYr2.mjs → chunks/messages-Clku7Cf-2.mjs} +1 -0
  38. package/dist/{messages-DIJlIqlQ2.mjs → chunks/messages-CszmHAvQ.mjs} +1 -0
  39. package/dist/chunks/{messages-DWu1r4gc2.mjs → messages-CvANwuht2.mjs} +1 -0
  40. package/dist/{messages-nUVjeh7K.mjs → chunks/messages-CxiURE2X.mjs} +1 -0
  41. package/dist/chunks/{messages-A_MkXDlG.mjs → messages-CxxyR4vY.mjs} +1 -0
  42. package/dist/{messages-ynAe7ewZ.mjs → chunks/messages-D22e9h7V2.mjs} +1 -0
  43. package/dist/{messages-DYTTu0O12.mjs → chunks/messages-D7dx_6k8.mjs} +1 -0
  44. package/dist/chunks/{messages-BA8Iv99Y2.mjs → messages-DBMaLL8b2.mjs} +1 -0
  45. package/dist/{messages-DbySKTKt2.mjs → chunks/messages-DB_-5Xln.mjs} +1 -0
  46. package/dist/{messages-CcF4y-E4.mjs → chunks/messages-DEBy3nuJ2.mjs} +1 -0
  47. package/dist/chunks/{messages-NEqrrYvE2.mjs → messages-DMoERagV2.mjs} +1 -0
  48. package/dist/chunks/{messages-Bxvi1ebN.mjs → messages-DPzHD51Y.mjs} +1 -0
  49. package/dist/chunks/{messages-jfVpL9c-2.mjs → messages-DSrdy9Nw2.mjs} +1 -0
  50. package/dist/chunks/{messages-Cmf6NhSC.mjs → messages-DTN1XGll.mjs} +1 -0
  51. package/dist/{messages-5jvKxQNu2.mjs → chunks/messages-DUeiPraX.mjs} +1 -0
  52. package/dist/chunks/{messages-G416eyjY.mjs → messages-DUr9WAkD.mjs} +1 -0
  53. package/dist/chunks/{messages-Ck81cQkn2.mjs → messages-DVr1sqfI2.mjs} +1 -0
  54. package/dist/{messages-BYmmMDrN2.mjs → chunks/messages-DjvaiALg2.mjs} +1 -0
  55. package/dist/chunks/{messages-D55HRx5O2.mjs → messages-DrfRYiM32.mjs} +1 -0
  56. package/dist/chunks/{messages-B2N4fUi72.mjs → messages-DtoId_bw2.mjs} +1 -0
  57. package/dist/{messages-Bq3F2Tp_.mjs → chunks/messages-Du2BffA7.mjs} +1 -0
  58. package/dist/{messages-CjbnogEC.mjs → chunks/messages-DxHh0O8j2.mjs} +1 -0
  59. package/dist/{messages-BECMxmfX.mjs → chunks/messages-EDMC5ukV.mjs} +1 -0
  60. package/dist/{messages-BTQPpoM42.mjs → chunks/messages-ElIGUi0O2.mjs} +1 -0
  61. package/dist/chunks/{messages-BhzzNkN-.mjs → messages-JSQjKQ8I.mjs} +1 -0
  62. package/dist/{messages-CWIXvnDf2.mjs → chunks/messages-Q7-4ZJLB2.mjs} +1 -0
  63. package/dist/chunks/{messages-DOuS1Qge.mjs → messages-QMOmwcZb.mjs} +1 -0
  64. package/dist/{messages-hWwSRF-2.mjs → chunks/messages-QilfinOn2.mjs} +1 -0
  65. package/dist/{messages-BmAn22OX.mjs → chunks/messages-a07QVz8U.mjs} +1 -0
  66. package/dist/chunks/{messages-BYxLFj7y.mjs → messages-eFd4YYzt.mjs} +1 -0
  67. package/dist/chunks/{messages-BSghd0ez.mjs → messages-euM2m3wQ.mjs} +1 -0
  68. package/dist/chunks/{messages-BVjoM7P0.mjs → messages-kGmxkeFH.mjs} +1 -0
  69. package/dist/{messages-DMr62KiO2.mjs → chunks/messages-oMc7qugU2.mjs} +1 -0
  70. package/dist/chunks/{messages-DzTk8bJ5.mjs → messages-sDdNf8O9.mjs} +1 -0
  71. package/dist/{messages-Bm0Feca1.mjs → chunks/messages-wl8YrvGG.mjs} +1 -0
  72. package/dist/chunks/{messages-BAsb5CgZ.mjs → messages-zt6zdYWh.mjs} +1 -0
  73. package/dist/chunks/{tools-XmzH2rgQ.mjs → tools-1ZFajlGN.mjs} +1619 -1307
  74. package/dist/full.mjs +3 -3
  75. package/dist/locales.mjs +68 -67
  76. package/dist/{messages-F2xRoY1w.mjs → messages-2ZWBTerL.mjs} +1 -0
  77. package/dist/{messages-Dl3Sv6Rq2.mjs → messages-53w0fPZS2.mjs} +1 -0
  78. package/dist/{chunks/messages-BDZA10kl2.mjs → messages-98nQiC7t2.mjs} +1 -0
  79. package/dist/{chunks/messages-JyvWu4rf2.mjs → messages-A96tMxeU.mjs} +1 -0
  80. package/dist/{messages-Ce6KVEbT.mjs → messages-BE_z-zrb.mjs} +1 -0
  81. package/dist/{chunks/messages-CSJ_zb3a2.mjs → messages-BK_LsgY4.mjs} +1 -0
  82. package/dist/{messages-CJTy6JZt.mjs → messages-BbJ7ZXY8.mjs} +1 -0
  83. package/dist/{chunks/messages-DMVXnAYj.mjs → messages-BcVB3osF.mjs} +1 -0
  84. package/dist/{chunks/messages-CSL-6xfb2.mjs → messages-BckDk9aq2.mjs} +1 -0
  85. package/dist/{chunks/messages-C0HvoMPb.mjs → messages-Be_2RHZD.mjs} +1 -0
  86. package/dist/{chunks/messages-Dr0Ekmbz.mjs → messages-BesJaI6A.mjs} +1 -0
  87. package/dist/{chunks/messages-D3zojZ94.mjs → messages-BiTMwiKH.mjs} +1 -0
  88. package/dist/{messages-B1ZUQagA.mjs → messages-BmH2cQHQ.mjs} +1 -0
  89. package/dist/{chunks/messages-Bfnq1xv4.mjs → messages-BrOWqNCu2.mjs} +1 -0
  90. package/dist/{messages-Dnp9N6RU2.mjs → messages-Brd5R-da2.mjs} +1 -0
  91. package/dist/{chunks/messages-DJoNVjqP.mjs → messages-C0GSBBCo2.mjs} +1 -0
  92. package/dist/{messages-Dw__BcTj.mjs → messages-C1vc5584.mjs} +1 -0
  93. package/dist/{messages-aMXpHt5X2.mjs → messages-C6ONf71u2.mjs} +1 -0
  94. package/dist/{chunks/messages-BeFqtIrc2.mjs → messages-C7lJg8fy2.mjs} +1 -0
  95. package/dist/{messages-CSUHBs4c2.mjs → messages-CRNogopy2.mjs} +1 -0
  96. package/dist/{messages-DLlc9QPw.mjs → messages-CT-Kdas6.mjs} +1 -0
  97. package/dist/{chunks/messages-DlLXpgWM2.mjs → messages-CTTmWn4Y2.mjs} +1 -0
  98. package/dist/{messages-D0aw5_0k2.mjs → messages-CZbcxlZt2.mjs} +1 -0
  99. package/dist/{chunks/messages-Bp8qin1R.mjs → messages-C_Qn9SbQ.mjs} +1 -0
  100. package/dist/{messages-96iaAUds2.mjs → messages-CdEASHDp2.mjs} +1 -0
  101. package/dist/{messages-Dy-Y_nEI.mjs → messages-CdduYw-q.mjs} +1 -0
  102. package/dist/{chunks/messages-Je5YvxiY.mjs → messages-Che99vKP.mjs} +1 -0
  103. package/dist/{messages-nlhESX9t.mjs → messages-CisR4PNV.mjs} +1 -0
  104. package/dist/{chunks/messages-BE6lHKwf.mjs → messages-ClGvlFcH2.mjs} +1 -0
  105. package/dist/{chunks/messages-FWfsxpBz.mjs → messages-CnuH-BZK2.mjs} +1 -0
  106. package/dist/{chunks/messages-aZcy0JQq2.mjs → messages-D0005ti32.mjs} +1 -0
  107. package/dist/{messages-B7ieAJBd2.mjs → messages-D05jqBIa2.mjs} +1 -0
  108. package/dist/{messages-DTh9a8mR.mjs → messages-D0lLw9KM.mjs} +1 -0
  109. package/dist/{chunks/messages-ihCjSFJI2.mjs → messages-D3rwCtKn.mjs} +1 -0
  110. package/dist/{chunks/messages-xuqyb6Ff2.mjs → messages-D6VIFnSW.mjs} +1 -0
  111. package/dist/{chunks/messages-KdawW5Na.mjs → messages-D81w6AmW.mjs} +1 -0
  112. package/dist/{chunks/messages-BUVhHx0q2.mjs → messages-DBhvm8NK.mjs} +1 -0
  113. package/dist/{chunks/messages-C7VGpihw.mjs → messages-DK6dA0O2.mjs} +1 -0
  114. package/dist/{messages-rk-A1Wa42.mjs → messages-DKHbt-7l2.mjs} +1 -0
  115. package/dist/{messages-BIoeoik5.mjs → messages-DM4Gjc9h.mjs} +1 -0
  116. package/dist/{chunks/messages-B9kmbUWV.mjs → messages-DODrhcop.mjs} +1 -0
  117. package/dist/{messages-BsycN_JI2.mjs → messages-DOGbHYv-2.mjs} +1 -0
  118. package/dist/{chunks/messages-BQYvBqm2.mjs → messages-DQORja0D.mjs} +1 -0
  119. package/dist/{chunks/messages-CVdpweyf2.mjs → messages-DSmxJWju2.mjs} +1 -0
  120. package/dist/{messages-CR_L_UtK.mjs → messages-DVL0KZE5.mjs} +1 -0
  121. package/dist/{chunks/messages-Cs81Z_Bh.mjs → messages-DYuD5-rO.mjs} +1 -0
  122. package/dist/{chunks/messages-CKBhDGI3.mjs → messages-Ddq3Ce3E2.mjs} +1 -0
  123. package/dist/{messages-xh2eOLvs.mjs → messages-DfFZ6Yj5.mjs} +1 -0
  124. package/dist/{chunks/messages-C6Mpiacw.mjs → messages-Dnd5YSWv.mjs} +1 -0
  125. package/dist/{messages-BJeGJksD.mjs → messages-Do7Xjy0n.mjs} +1 -0
  126. package/dist/{chunks/messages-C3aX3q0H.mjs → messages-DopaMHC42.mjs} +1 -0
  127. package/dist/{messages-dv19AkyJ.mjs → messages-DpJGbx3q.mjs} +1 -0
  128. package/dist/{messages-DBiVgUs2.mjs → messages-DpwMKDV0.mjs} +1 -0
  129. package/dist/{messages-j7o5rT9s.mjs → messages-Dqu4aX9s.mjs} +1 -0
  130. package/dist/{chunks/messages-BjadX8jR2.mjs → messages-E8NjqzWq2.mjs} +1 -0
  131. package/dist/{messages-aWZH50vu2.mjs → messages-JNrYldAa2.mjs} +1 -0
  132. package/dist/{chunks/messages-B4UMuyjT.mjs → messages-LMaR2_bE.mjs} +1 -0
  133. package/dist/{messages-E_ZuzGDt.mjs → messages-LYJbLq_F.mjs} +1 -0
  134. package/dist/{chunks/messages-D9N2MvQx2.mjs → messages-Q5sQeVap2.mjs} +1 -0
  135. package/dist/{chunks/messages-Bphq_Bt3.mjs → messages-Xc0KUbYl.mjs} +1 -0
  136. package/dist/{chunks/messages-DlonA3wa.mjs → messages-_PLyRfVw.mjs} +1 -0
  137. package/dist/{chunks/messages-TRUuyiFB.mjs → messages-apA6BStA.mjs} +1 -0
  138. package/dist/{chunks/messages-B0vPBsWq.mjs → messages-bkGniiaz.mjs} +1 -0
  139. package/dist/{messages-DkLU_rWm.mjs → messages-neGD3WGq.mjs} +1 -0
  140. package/dist/{messages-Ddnj2iTG2.mjs → messages-qbKjjvgd2.mjs} +1 -0
  141. package/dist/{messages-BiiongNz2.mjs → messages-qfvXgPpu2.mjs} +1 -0
  142. package/dist/{messages-Dvn35ksS.mjs → messages-uwK7ktqk.mjs} +1 -0
  143. package/dist/react.mjs +2 -2
  144. package/dist/tools.mjs +2 -2
  145. package/package.json +3 -5
  146. package/src/cli/commands/convert-gdocs/index.ts +26 -0
  147. package/src/cli/commands/convert-html/block-builder.ts +392 -0
  148. package/src/cli/commands/convert-html/id-generator.ts +11 -0
  149. package/src/cli/commands/convert-html/index.ts +23 -0
  150. package/src/cli/commands/convert-html/preprocessor.ts +422 -0
  151. package/src/cli/commands/convert-html/sanitizer.ts +93 -0
  152. package/src/cli/commands/convert-html/types.ts +15 -0
  153. package/src/cli/index.ts +56 -5
  154. package/src/components/block/index.ts +58 -10
  155. package/src/components/constants/data-attributes.ts +10 -0
  156. package/src/components/i18n/locales/am/messages.json +1 -0
  157. package/src/components/i18n/locales/ar/messages.json +1 -0
  158. package/src/components/i18n/locales/az/messages.json +1 -0
  159. package/src/components/i18n/locales/bg/messages.json +1 -0
  160. package/src/components/i18n/locales/bn/messages.json +1 -0
  161. package/src/components/i18n/locales/bs/messages.json +1 -0
  162. package/src/components/i18n/locales/cs/messages.json +1 -0
  163. package/src/components/i18n/locales/da/messages.json +1 -0
  164. package/src/components/i18n/locales/de/messages.json +1 -0
  165. package/src/components/i18n/locales/dv/messages.json +1 -0
  166. package/src/components/i18n/locales/el/messages.json +1 -0
  167. package/src/components/i18n/locales/en/messages.json +1 -0
  168. package/src/components/i18n/locales/es/messages.json +1 -0
  169. package/src/components/i18n/locales/et/messages.json +1 -0
  170. package/src/components/i18n/locales/fa/messages.json +1 -0
  171. package/src/components/i18n/locales/fi/messages.json +1 -0
  172. package/src/components/i18n/locales/fil/messages.json +1 -0
  173. package/src/components/i18n/locales/fr/messages.json +1 -0
  174. package/src/components/i18n/locales/gu/messages.json +1 -0
  175. package/src/components/i18n/locales/he/messages.json +1 -0
  176. package/src/components/i18n/locales/hi/messages.json +1 -0
  177. package/src/components/i18n/locales/hr/messages.json +1 -0
  178. package/src/components/i18n/locales/hu/messages.json +1 -0
  179. package/src/components/i18n/locales/hy/messages.json +1 -0
  180. package/src/components/i18n/locales/id/messages.json +1 -0
  181. package/src/components/i18n/locales/it/messages.json +1 -0
  182. package/src/components/i18n/locales/ja/messages.json +1 -0
  183. package/src/components/i18n/locales/ka/messages.json +1 -0
  184. package/src/components/i18n/locales/km/messages.json +1 -0
  185. package/src/components/i18n/locales/kn/messages.json +1 -0
  186. package/src/components/i18n/locales/ko/messages.json +1 -0
  187. package/src/components/i18n/locales/ku/messages.json +1 -0
  188. package/src/components/i18n/locales/lo/messages.json +1 -0
  189. package/src/components/i18n/locales/lt/messages.json +1 -0
  190. package/src/components/i18n/locales/lv/messages.json +1 -0
  191. package/src/components/i18n/locales/mk/messages.json +1 -0
  192. package/src/components/i18n/locales/ml/messages.json +1 -0
  193. package/src/components/i18n/locales/mn/messages.json +1 -0
  194. package/src/components/i18n/locales/mr/messages.json +1 -0
  195. package/src/components/i18n/locales/ms/messages.json +1 -0
  196. package/src/components/i18n/locales/my/messages.json +1 -0
  197. package/src/components/i18n/locales/ne/messages.json +1 -0
  198. package/src/components/i18n/locales/nl/messages.json +1 -0
  199. package/src/components/i18n/locales/no/messages.json +1 -0
  200. package/src/components/i18n/locales/pa/messages.json +1 -0
  201. package/src/components/i18n/locales/pl/messages.json +1 -0
  202. package/src/components/i18n/locales/ps/messages.json +1 -0
  203. package/src/components/i18n/locales/pt/messages.json +1 -0
  204. package/src/components/i18n/locales/ro/messages.json +1 -0
  205. package/src/components/i18n/locales/ru/messages.json +1 -0
  206. package/src/components/i18n/locales/sd/messages.json +1 -0
  207. package/src/components/i18n/locales/si/messages.json +1 -0
  208. package/src/components/i18n/locales/sk/messages.json +1 -0
  209. package/src/components/i18n/locales/sl/messages.json +1 -0
  210. package/src/components/i18n/locales/sq/messages.json +1 -0
  211. package/src/components/i18n/locales/sr/messages.json +1 -0
  212. package/src/components/i18n/locales/sv/messages.json +1 -0
  213. package/src/components/i18n/locales/sw/messages.json +1 -0
  214. package/src/components/i18n/locales/ta/messages.json +1 -0
  215. package/src/components/i18n/locales/te/messages.json +1 -0
  216. package/src/components/i18n/locales/th/messages.json +1 -0
  217. package/src/components/i18n/locales/tr/messages.json +1 -0
  218. package/src/components/i18n/locales/ug/messages.json +1 -0
  219. package/src/components/i18n/locales/uk/messages.json +1 -0
  220. package/src/components/i18n/locales/ur/messages.json +1 -0
  221. package/src/components/i18n/locales/vi/messages.json +1 -0
  222. package/src/components/i18n/locales/yi/messages.json +1 -0
  223. package/src/components/i18n/locales/zh/messages.json +1 -0
  224. package/src/components/icons/index.ts +29 -18
  225. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +18 -0
  226. package/src/components/modules/blockManager/hierarchy.ts +4 -1
  227. package/src/components/modules/readonly.ts +46 -0
  228. package/src/components/modules/rectangleSelection.ts +25 -5
  229. package/src/components/modules/toolbar/index.ts +96 -19
  230. package/src/components/modules/toolbar/positioning.ts +11 -2
  231. package/src/components/modules/toolbar/styles.ts +0 -2
  232. package/src/components/modules/uiControllers/controllers/blockHover.ts +44 -1
  233. package/src/components/tools/block.ts +10 -0
  234. package/src/components/utils/placeholder.ts +9 -2
  235. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +11 -0
  236. package/src/components/utils/popover/popover-abstract.ts +7 -0
  237. package/src/styles/main.css +16 -0
  238. package/src/tools/callout/constants.ts +2 -1
  239. package/src/tools/callout/dom-builder.ts +13 -1
  240. package/src/tools/callout/index.ts +21 -7
  241. package/src/tools/code/constants.ts +28 -8
  242. package/src/tools/code/dom-builder.ts +133 -64
  243. package/src/tools/code/index.ts +280 -91
  244. package/src/tools/code/language-detector.ts +118 -0
  245. package/src/tools/divider/index.ts +5 -0
  246. package/src/tools/header/index.ts +47 -1
  247. package/src/tools/list/dom-builder.ts +3 -1
  248. package/src/tools/list/index.ts +55 -3
  249. package/src/tools/list/list-helpers.ts +2 -2
  250. package/src/tools/nested-blocks.ts +25 -0
  251. package/src/tools/paragraph/index.ts +47 -6
  252. package/src/tools/quote/index.ts +43 -8
  253. package/src/tools/stub/index.ts +10 -0
  254. package/src/tools/table/index.ts +238 -6
  255. package/src/tools/table/table-add-controls.ts +37 -5
  256. package/src/tools/table/table-cell-blocks.ts +57 -18
  257. package/src/tools/table/table-core.ts +2 -0
  258. package/src/tools/table/table-corner-drag.ts +247 -0
  259. package/src/tools/table/table-operations.ts +41 -14
  260. package/src/tools/toggle/dom-builder.ts +1 -0
  261. package/src/tools/toggle/index.ts +25 -0
  262. package/src/tools/toggle/toggle-lifecycle.ts +5 -4
  263. package/src/types-internal/jsdom.d.ts +9 -0
  264. package/types/tools/adapters/block-tool-adapter.d.ts +6 -0
  265. package/types/tools/block-tool.d.ts +20 -0
  266. package/types/utils/popover/popover-item.d.ts +6 -0
  267. package/bin/blok.mjs +0 -10
  268. package/dist/cli.mjs +0 -37
  269. package/src/tools/code/language-picker.ts +0 -241
@@ -0,0 +1,247 @@
1
+ import { show as showTooltip, hide as hideTooltip } from '../../components/utils/tooltip';
2
+
3
+ const CORNER_DRAG_ATTR = 'data-blok-table-corner-drag';
4
+
5
+ export interface TableCornerDragOptions {
6
+ wrapper: HTMLElement;
7
+ gridEl: HTMLElement;
8
+ onAddRow: () => void;
9
+ onAddColumn: () => void;
10
+ onRemoveLastRow: () => void;
11
+ onRemoveLastColumn: () => void;
12
+ onDragStart: () => void;
13
+ onDragEnd: () => void;
14
+ getTableSize: () => { rows: number; cols: number };
15
+ canRemoveLastRow: () => boolean;
16
+ canRemoveLastColumn: () => boolean;
17
+ onClickAdd?: () => void;
18
+ }
19
+
20
+ const DRAG_THRESHOLD = 5;
21
+
22
+ interface DragState {
23
+ startX: number;
24
+ startY: number;
25
+ unitWidth: number;
26
+ unitHeight: number;
27
+ addedRows: number;
28
+ addedCols: number;
29
+ pointerId: number;
30
+ didDrag: boolean;
31
+ }
32
+
33
+ export class TableCornerDrag {
34
+ private wrapper: HTMLElement;
35
+ private gridEl: HTMLElement;
36
+ private hitZone: HTMLElement;
37
+ private getTableSize: () => { rows: number; cols: number };
38
+ private onAddRow: () => void;
39
+ private onAddColumn: () => void;
40
+ private onRemoveLastRow: () => void;
41
+ private onRemoveLastColumn: () => void;
42
+ private onDragStart: () => void;
43
+ private onDragEnd: () => void;
44
+ private canRemoveLastRow: () => boolean;
45
+ private canRemoveLastColumn: () => boolean;
46
+ private onClickAdd: (() => void) | null;
47
+ private dragState: DragState | null = null;
48
+ private readonly boundMouseEnter: () => void;
49
+ private readonly boundMouseLeave: () => void;
50
+ private readonly boundPointerDown: (e: PointerEvent) => void;
51
+ private readonly boundPointerMove: (e: PointerEvent) => void;
52
+ private readonly boundPointerUp: (e: PointerEvent) => void;
53
+
54
+ constructor(options: TableCornerDragOptions) {
55
+ this.wrapper = options.wrapper;
56
+ this.gridEl = options.gridEl;
57
+ this.getTableSize = options.getTableSize;
58
+ this.onAddRow = options.onAddRow;
59
+ this.onAddColumn = options.onAddColumn;
60
+ this.onRemoveLastRow = options.onRemoveLastRow;
61
+ this.onRemoveLastColumn = options.onRemoveLastColumn;
62
+ this.onDragStart = options.onDragStart;
63
+ this.onDragEnd = options.onDragEnd;
64
+ this.canRemoveLastRow = options.canRemoveLastRow;
65
+ this.canRemoveLastColumn = options.canRemoveLastColumn;
66
+ this.onClickAdd = options.onClickAdd ?? null;
67
+
68
+ this.hitZone = document.createElement('div');
69
+ this.hitZone.setAttribute(CORNER_DRAG_ATTR, '');
70
+ this.hitZone.setAttribute('contenteditable', 'false');
71
+ this.hitZone.style.position = 'absolute';
72
+ this.hitZone.style.width = '36px';
73
+ this.hitZone.style.height = '36px';
74
+ this.hitZone.style.cursor = 'nwse-resize';
75
+ this.hitZone.style.zIndex = '2';
76
+ this.hitZone.style.pointerEvents = 'auto';
77
+ this.hitZone.style.bottom = '-36px';
78
+ this.hitZone.style.right = '-16px';
79
+
80
+ this.boundMouseEnter = this.handleMouseEnter.bind(this);
81
+ this.boundMouseLeave = this.handleMouseLeave.bind(this);
82
+ this.boundPointerDown = this.handlePointerDown.bind(this);
83
+ this.boundPointerMove = this.handlePointerMove.bind(this);
84
+ this.boundPointerUp = this.handlePointerUp.bind(this);
85
+
86
+ this.hitZone.addEventListener('mouseenter', this.boundMouseEnter);
87
+ this.hitZone.addEventListener('mouseleave', this.boundMouseLeave);
88
+ this.hitZone.addEventListener('pointerdown', this.boundPointerDown);
89
+
90
+ this.wrapper.appendChild(this.hitZone);
91
+ }
92
+
93
+ private updateTooltip(): void {
94
+ const size = this.getTableSize();
95
+
96
+ showTooltip(this.hitZone, `${size.cols}\u00D7${size.rows}`, { placement: 'bottom' });
97
+ }
98
+
99
+ private handleMouseEnter(): void {
100
+ this.updateTooltip();
101
+ }
102
+
103
+ private handleMouseLeave(): void {
104
+ if (this.dragState !== null) {
105
+ return;
106
+ }
107
+ hideTooltip();
108
+ }
109
+
110
+ private measureUnitHeight(): number {
111
+ const rows = this.gridEl.querySelectorAll('[data-blok-table-row]');
112
+ const lastRow = rows[rows.length - 1] as HTMLElement | undefined;
113
+
114
+ return lastRow?.offsetHeight || 30;
115
+ }
116
+
117
+ private measureUnitWidth(): number {
118
+ const firstRow = this.gridEl.querySelector('[data-blok-table-row]');
119
+
120
+ if (!firstRow) {
121
+ return 100;
122
+ }
123
+
124
+ const cells = firstRow.querySelectorAll('[data-blok-table-cell]');
125
+ const lastCell = cells[cells.length - 1] as HTMLElement | undefined;
126
+
127
+ return lastCell?.offsetWidth || 100;
128
+ }
129
+
130
+ private handlePointerDown(e: PointerEvent): void {
131
+ this.dragState = {
132
+ startX: e.clientX,
133
+ startY: e.clientY,
134
+ unitWidth: this.measureUnitWidth(),
135
+ unitHeight: this.measureUnitHeight(),
136
+ addedRows: 0,
137
+ addedCols: 0,
138
+ pointerId: e.pointerId,
139
+ didDrag: false,
140
+ };
141
+
142
+ this.updateTooltip();
143
+
144
+ this.hitZone.setPointerCapture(e.pointerId);
145
+ this.hitZone.addEventListener('pointermove', this.boundPointerMove);
146
+ this.hitZone.addEventListener('pointerup', this.boundPointerUp);
147
+ }
148
+
149
+ private handlePointerMove(e: PointerEvent): void {
150
+ if (this.dragState === null) {
151
+ return;
152
+ }
153
+
154
+ const dx = e.clientX - this.dragState.startX;
155
+ const dy = e.clientY - this.dragState.startY;
156
+
157
+ if (!this.dragState.didDrag) {
158
+ const distance = Math.sqrt(dx * dx + dy * dy);
159
+
160
+ if (distance < DRAG_THRESHOLD) {
161
+ return;
162
+ }
163
+
164
+ this.dragState.didDrag = true;
165
+ document.body.style.cursor = 'nwse-resize';
166
+ document.body.style.userSelect = 'none';
167
+ this.onDragStart();
168
+ }
169
+
170
+ const { unitHeight, unitWidth } = this.dragState;
171
+
172
+ const targetRows = Math.trunc(dy / unitHeight);
173
+ const targetCols = Math.trunc(dx / unitWidth);
174
+
175
+ while (this.dragState.addedRows < targetRows) {
176
+ this.onAddRow();
177
+ this.dragState.addedRows++;
178
+ }
179
+
180
+ while (this.dragState.addedRows > targetRows && this.canRemoveLastRow()) {
181
+ this.onRemoveLastRow();
182
+ this.dragState.addedRows--;
183
+ }
184
+
185
+ while (this.dragState.addedCols < targetCols) {
186
+ this.onAddColumn();
187
+ this.dragState.addedCols++;
188
+ }
189
+
190
+ while (this.dragState.addedCols > targetCols && this.canRemoveLastColumn()) {
191
+ this.onRemoveLastColumn();
192
+ this.dragState.addedCols--;
193
+ }
194
+
195
+ this.updateTooltip();
196
+ }
197
+
198
+ private handlePointerUp(_e: PointerEvent): void {
199
+ if (this.dragState === null) {
200
+ return;
201
+ }
202
+
203
+ const { didDrag, pointerId } = this.dragState;
204
+
205
+ this.dragState = null;
206
+ hideTooltip();
207
+ this.hitZone.releasePointerCapture(pointerId);
208
+ this.hitZone.removeEventListener('pointermove', this.boundPointerMove);
209
+ this.hitZone.removeEventListener('pointerup', this.boundPointerUp);
210
+
211
+ if (!didDrag) {
212
+ if (this.onClickAdd) {
213
+ this.onClickAdd();
214
+ } else {
215
+ this.onAddRow();
216
+ this.onAddColumn();
217
+ }
218
+ } else {
219
+ document.body.style.cursor = '';
220
+ document.body.style.userSelect = '';
221
+ this.onDragEnd();
222
+ }
223
+ }
224
+
225
+ public setDisplay(visible: boolean): void {
226
+ this.hitZone.style.display = visible ? '' : 'none';
227
+ }
228
+
229
+ public setInteractive(interactive: boolean): void {
230
+ this.hitZone.style.pointerEvents = interactive ? 'auto' : 'none';
231
+ }
232
+
233
+ public destroy(): void {
234
+ this.hitZone.removeEventListener('mouseenter', this.boundMouseEnter);
235
+ this.hitZone.removeEventListener('mouseleave', this.boundMouseLeave);
236
+ this.hitZone.removeEventListener('pointerdown', this.boundPointerDown);
237
+ this.hitZone.removeEventListener('pointermove', this.boundPointerMove);
238
+ this.hitZone.removeEventListener('pointerup', this.boundPointerUp);
239
+ if (this.dragState?.didDrag) {
240
+ document.body.style.cursor = '';
241
+ document.body.style.userSelect = '';
242
+ }
243
+ this.dragState = null;
244
+ hideTooltip();
245
+ this.hitZone.remove();
246
+ }
247
+ }
@@ -1,4 +1,5 @@
1
1
  import type { API } from '../../../types';
2
+ import { DATA_ATTR } from '../../components/constants/data-attributes';
2
3
 
3
4
  import type { TableCellBlocks } from './table-cell-blocks';
4
5
  import { CELL_BLOCKS_ATTR } from './table-cell-blocks';
@@ -288,16 +289,28 @@ export const mountCellBlocksReadOnly = (
288
289
 
289
290
  if (!isCellWithBlocks(cellContent)) {
290
291
  // Read-only render path must not mutate block state.
291
- // Use innerHTML so that legacy HTML markup (e.g. <b>bold</b>) is
292
- // interpreted by the browser rather than shown as literal text.
293
- container.innerHTML = cellContent;
292
+ // Wrap in a div with leading-[1.5] so the line-height matches paragraph
293
+ // blocks used in edit mode (where legacy strings are converted to real
294
+ // paragraph blocks with that line-height). Without this wrapper, the
295
+ // text inherits the cell's leading-none, producing shorter cells.
296
+ const wrapper = document.createElement('div');
297
+
298
+ wrapper.className = 'leading-[1.5]';
299
+ wrapper.innerHTML = cellContent;
300
+ container.replaceChildren(wrapper);
294
301
 
295
302
  return;
296
303
  }
297
304
 
298
- // If this container previously rendered legacy text, clear it before mounting holders.
299
- if (!hasExistingBlocks && (container.textContent ?? '').length > 0) {
300
- container.textContent = '';
305
+ // Clear the container before (re-)mounting block holders.
306
+ // This covers two cases:
307
+ // 1. Legacy text was previously rendered and needs to be replaced with blocks.
308
+ // 2. Block holders are already mounted (e.g. from edit mode before a
309
+ // setReadOnly toggle) — without clearing, the clone-guard below would
310
+ // duplicate every holder because it detects them inside a
311
+ // [data-blok-nested-blocks] container and appends a cloneNode(true).
312
+ if (hasExistingBlocks || (container.textContent ?? '').length > 0) {
313
+ container.replaceChildren();
301
314
  }
302
315
 
303
316
  for (const blockId of cellContent.blocks) {
@@ -313,12 +326,20 @@ export const mountCellBlocksReadOnly = (
313
326
  continue;
314
327
  }
315
328
 
329
+ // Skip blocks that don't belong to this table.
330
+ // Corrupted data may contain cross-table references; mounting them
331
+ // would steal (or clone) DOM nodes from the other table.
332
+ if (block.parentId !== _tableBlockId) {
333
+ continue;
334
+ }
335
+
316
336
  // Guard: if the block holder is already inside another table cell's
317
- // blocks container, skip it. Without this check, appendChild would
318
- // move (steal) the DOM node from the first table, leaving its cell
319
- // empty. This can happen when corrupted data references the same
320
- // block in multiple tables.
321
- if (block.holder.closest(`[${CELL_BLOCKS_ATTR}]`)) {
337
+ // blocks container, clone its visual content instead of moving (stealing)
338
+ // the DOM node. This can happen when corrupted data references the same
339
+ // block in multiple tables. In read-only mode a deep clone is safe
340
+ // because the content is non-interactive.
341
+ if (block.holder.closest(`[${DATA_ATTR.nestedBlocks}]`)) {
342
+ container.appendChild(block.holder.cloneNode(true));
322
343
  continue;
323
344
  }
324
345
 
@@ -374,8 +395,8 @@ export const normalizeTableData = (
374
395
  export const setupKeyboardNavigation = (
375
396
  gridEl: HTMLElement,
376
397
  cellBlocks: TableCellBlocks | null,
377
- ): void => {
378
- gridEl.addEventListener('keydown', (event: KeyboardEvent) => {
398
+ ): (() => void) => {
399
+ const handler = (event: KeyboardEvent): void => {
379
400
  const target = event.target as HTMLElement;
380
401
  const cell = target.closest<HTMLElement>(`[${CELL_ATTR}]`);
381
402
 
@@ -388,7 +409,13 @@ export const setupKeyboardNavigation = (
388
409
  if (position) {
389
410
  cellBlocks?.handleKeyDown(event, position);
390
411
  }
391
- });
412
+ };
413
+
414
+ gridEl.addEventListener('keydown', handler);
415
+
416
+ return () => {
417
+ gridEl.removeEventListener('keydown', handler);
418
+ };
392
419
  };
393
420
 
394
421
  export const SCROLL_OVERFLOW_CLASSES = ['overflow-x-auto', 'overflow-y-hidden'];
@@ -88,6 +88,7 @@ export const buildToggleItem = (context: ToggleDOMBuilderContext): ToggleBuildRe
88
88
  const childContainerElement = document.createElement('div');
89
89
  childContainerElement.className = TOGGLE_CHILDREN_STYLES;
90
90
  childContainerElement.setAttribute(TOGGLE_ATTR.toggleChildren, '');
91
+ childContainerElement.setAttribute(DATA_ATTR.nestedBlocks, '');
91
92
  // Block DOM mutations inside the children container from triggering the toggle tool's
92
93
  // didMutated → syncBlockDataToYjs path. Child block insertions/removals are tracked
93
94
  // via the block hierarchy (parentId / contentIds) and must not create spurious Yjs
@@ -229,6 +229,31 @@ export class ToggleItem implements BlockTool {
229
229
  this.setOpenState(false);
230
230
  }
231
231
 
232
+ public setReadOnly(state: boolean): void {
233
+ if (!this._element) {
234
+ return;
235
+ }
236
+
237
+ const wasReadOnly = this.readOnly;
238
+
239
+ this.readOnly = state;
240
+
241
+ // Toggle contentEditable on the content element
242
+ if (this._contentElement) {
243
+ this._contentElement.contentEditable = state ? 'false' : 'true';
244
+ }
245
+
246
+ // Manage block changed event subscription
247
+ if (state && !wasReadOnly) {
248
+ this.api.events.off('block changed', this.handleBlockChanged);
249
+ } else if (!state && wasReadOnly) {
250
+ this.api.events.on('block changed', this.handleBlockChanged);
251
+ }
252
+
253
+ // Update body placeholder visibility (hidden in read-only mode)
254
+ this.updateBodyPlaceholderVisibility();
255
+ }
256
+
232
257
  public removed(): void {
233
258
  this.api.events.off('block changed', this.handleBlockChanged);
234
259
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import type { API } from '../../../types';
8
8
 
9
+ import { mountChildBlocks } from '../nested-blocks';
9
10
  import { setupPlaceholder } from '../../components/utils/placeholder';
10
11
 
11
12
  import { TOGGLE_ATTR } from './constants';
@@ -94,11 +95,11 @@ export const updateChildrenVisibility = (
94
95
  arrowElement.focus();
95
96
  }
96
97
 
97
- for (const child of children) {
98
- if (childContainer && child.holder.parentElement !== childContainer) {
99
- childContainer.appendChild(child.holder);
100
- }
98
+ if (childContainer) {
99
+ mountChildBlocks(childContainer, children);
100
+ }
101
101
 
102
+ for (const child of children) {
102
103
  if (isOpen) {
103
104
  child.holder.classList.remove('hidden');
104
105
  } else {
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Declaration for jsdom (used in CLI for HTML conversion)
3
+ */
4
+ declare module 'jsdom' {
5
+ export class JSDOM {
6
+ constructor(html: string);
7
+ window: typeof globalThis;
8
+ }
9
+ }
@@ -35,6 +35,12 @@ interface BlockToolAdapter extends BaseToolAdapter<ToolType.Block, BlockTool>{
35
35
  */
36
36
  isReadOnlySupported: boolean;
37
37
 
38
+ /**
39
+ * Returns true if the Tool's prototype has a setReadOnly method,
40
+ * enabling the in-place read-only toggle path (no save/clear/render cycle).
41
+ */
42
+ supportsInPlaceReadOnly: boolean;
43
+
38
44
  /**
39
45
  * Returns true if Tool supports linebreaks
40
46
  */
@@ -87,6 +87,26 @@ export interface BlockTool extends BaseTool {
87
87
  * @returns Object with left offset in pixels, or undefined if no offset should be applied
88
88
  */
89
89
  getContentOffset?(hoveredElement: Element): { left: number } | undefined;
90
+
91
+ /**
92
+ * Returns the element that the toolbar should vertically center on.
93
+ * Used by tools whose editable area is deeply nested below non-editable UI
94
+ * (e.g., a header bar), where the default contenteditable-descendant search
95
+ * would position the toolbar too far down inside the block.
96
+ *
97
+ * Return undefined to use the default positioning logic.
98
+ */
99
+ getToolbarAnchorElement?(): HTMLElement | undefined;
100
+
101
+ /**
102
+ * Called when read-only mode is toggled without re-rendering the block.
103
+ * Implementations should update the DOM in place: toggle contentEditable,
104
+ * bind/unbind event listeners, show/hide interactive elements, etc.
105
+ *
106
+ * Optional — tools without this method trigger a full save/clear/render
107
+ * fallback when read-only mode is toggled.
108
+ */
109
+ setReadOnly?(state: boolean): void;
90
110
  }
91
111
 
92
112
  /**
@@ -151,6 +151,12 @@ export interface PopoverItemDefaultBaseParams {
151
151
  */
152
152
  icon?: string;
153
153
 
154
+ /**
155
+ * Icon to be displayed on the trailing (right) side of the item.
156
+ * Rendered without a box background, suitable for indicators like a checkmark.
157
+ */
158
+ trailingIcon?: string;
159
+
154
160
  /**
155
161
  * Additional displayed text
156
162
  */
package/bin/blok.mjs DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFileSync } from 'node:fs';
4
- import { run } from '../dist/cli.mjs';
5
-
6
- const version = process.env.npm_package_version
7
- || JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version;
8
- const args = process.argv.slice(2);
9
-
10
- run(args, version);
package/dist/cli.mjs DELETED
@@ -1,37 +0,0 @@
1
- import * as e from "node:fs";
2
- //#region src/cli/commands/migrationContent.ts
3
- var t = "# Migrating from EditorJS to Blok\n\nThis guide covers the breaking changes when migrating from EditorJS to Blok.\n\n## Table of Contents\n\n- [Core Changes](#core-changes)\n- [Data Attributes](#data-attributes)\n- [CSS Classes](#css-classes)\n- [Bundled Tools](#bundled-tools)\n - [Tool Configuration](#tool-configuration)\n - [Lifecycle Hooks](#lifecycle-hooks)\n - [Delimiter → Divider](#delimiter--divider)\n- [Configuration Defaults](#configuration-defaults)\n- [New API Methods](#new-api-methods)\n- [DOM Selectors](#dom-selectors)\n- [E2E Test Selectors](#e2e-test-selectors)\n\n---\n\n## Core Changes\n\n### Class Name\n\n```diff\n- import EditorJS from '@editorjs/editorjs';\n+ import Blok from '@jackuait/blok';\n\n- const editor = new EditorJS({ ... });\n+ const editor = new Blok({ ... });\n```\n\n### Default Holder\n\nThe default holder ID changed from `editorjs` to `blok`:\n\n```diff\n- <div id=\"editorjs\"></div>\n+ <div id=\"blok\"></div>\n```\n\nOr specify explicitly:\n\n```javascript\nconst editor = new Blok({\n holder: 'my-editor', // works the same as before\n});\n```\n\n### TypeScript Types\n\n```diff\n- import type { EditorConfig, OutputData } from '@editorjs/editorjs';\n+ import type { BlokConfig, OutputData } from '@jackuait/blok';\n```\n\n---\n\n## Data Attributes\n\nBlok uses `data-blok-*` attributes instead of EditorJS's mixed naming conventions.\n\n| EditorJS | Blok |\n|----------|------|\n| `data-id` | `data-blok-id` |\n| `data-item-name` | `data-blok-item-name` |\n| `data-empty` | `data-blok-empty` |\n| `.ce-block--selected` (class) | `data-blok-selected=\"true\"` |\n| — | `data-blok-component` (tool name) |\n| — | `data-blok-interface` (element type) |\n| — | `data-blok-testid` (testing) |\n| — | `data-blok-opened` (toolbar state) |\n| — | `data-blok-placeholder` |\n| — | `data-blok-stretched` |\n| — | `data-blok-focused` |\n| — | `data-blok-popover-opened` |\n| — | `data-blok-tool` (tool name on rendered element) |\n| — | `data-blok-dragging` (block being dragged) |\n| — | `data-blok-hidden` (hidden state) |\n| — | `data-blok-rtl` (RTL mode) |\n| — | `data-blok-drag-handle` (drag handle element) |\n| — | `data-blok-overlay` (selection overlay) |\n| — | `data-blok-overlay-rectangle` (selection rectangle) |\n\n### Querying Blocks\n\n```diff\n- document.querySelector('[data-id=\"abc123\"]');\n+ document.querySelector('[data-blok-id=\"abc123\"]');\n\n- document.querySelector('[data-item-name=\"bold\"]');\n+ document.querySelector('[data-blok-item-name=\"bold\"]');\n```\n\n---\n\n## CSS Classes\n\nBlok replaces BEM class names with data attributes for selection.\n\n### Editor Wrapper\n\n| EditorJS | Blok |\n|----------|------|\n| `.codex-editor` | `[data-blok-editor]` |\n| `.codex-editor__redactor` | `[data-blok-redactor]` |\n| `.codex-editor--rtl` | `[data-blok-rtl=\"true\"]` |\n\n### Block Elements\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-block` | `[data-blok-element]` |\n| `.ce-block--selected` | `[data-blok-selected=\"true\"]` |\n| `.ce-block--stretched` | `[data-blok-stretched=\"true\"]` |\n| `.ce-block--focused` | `[data-blok-focused=\"true\"]` |\n| `.ce-block__content` | `[data-blok-element-content]` |\n\n### Toolbar\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-toolbar` | `[data-blok-toolbar]` |\n| `.ce-toolbar__plus` | `[data-blok-testid=\"plus-button\"]` |\n| `.ce-toolbar__settings-btn` | `[data-blok-settings-toggler]` |\n| `.ce-toolbar__actions` | `[data-blok-testid=\"toolbar-actions\"]` |\n| `.ce-toolbox` | `[data-blok-toolbox]` |\n| `.ce-toolbox--opened` | `[data-blok-toolbox][data-blok-opened=\"true\"]` |\n\n### Inline Toolbar\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-inline-toolbar` | `[data-blok-testid=\"inline-toolbar\"]` |\n| `.ce-inline-tool` | `[data-blok-testid=\"inline-tool\"]` |\n| `.ce-inline-tool--link` | `[data-blok-testid=\"inline-tool-link\"]` |\n| `.ce-inline-tool--bold` | `[data-blok-testid=\"inline-tool-bold\"]` |\n| `.ce-inline-tool--italic` | `[data-blok-testid=\"inline-tool-italic\"]` |\n\n### Popover\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-popover` | `[data-blok-popover]` |\n| `.ce-popover--opened` | `[data-blok-popover][data-blok-opened=\"true\"]` |\n| `.ce-popover__container` | `[data-blok-popover-container]` |\n| `.ce-popover-item` | `[data-blok-testid=\"popover-item\"]` |\n| `.ce-popover-item--focused` | `[data-blok-focused=\"true\"]` |\n| `.ce-popover-item--confirmation` | `[data-blok-confirmation=\"true\"]` |\n| `.ce-popover-item__icon` | `[data-blok-testid=\"popover-item-icon\"]` |\n| `.ce-popover-item__icon--tool` | `[data-blok-testid=\"popover-item-icon-tool\"]` |\n\n### Tool-Specific Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-paragraph` | `[data-blok-tool=\"paragraph\"]` |\n| `.ce-header` | `[data-blok-tool=\"header\"]` |\n\n### Conversion Toolbar & Settings\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-conversion-toolbar` | `[data-blok-testid=\"conversion-toolbar\"]` |\n| `.ce-conversion-tool` | `[data-blok-testid=\"conversion-tool\"]` |\n| `.ce-settings` | `[data-blok-testid=\"block-settings\"]` |\n| `.ce-tune` | `[data-blok-testid=\"block-tune\"]` |\n\n### Other Elements\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-stub` | `[data-blok-stub]` |\n| `.ce-drag-handle` | `[data-blok-drag-handle]` |\n| `.ce-ragged-right` | `[data-blok-ragged-right=\"true\"]` |\n\n### CDX List Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.cdx-list` | `[data-blok-list]` |\n| `.cdx-list__item` | `[data-blok-list-item]` |\n| `.cdx-list--ordered` | `[data-blok-list=\"ordered\"]` |\n| `.cdx-list--unordered` | `[data-blok-list=\"unordered\"]` |\n\n### CDX Utility Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.cdx-button` | `[data-blok-button]` |\n| `.cdx-input` | `[data-blok-input]` |\n| `.cdx-loader` | `[data-blok-loader]` |\n| `.cdx-search-field` | `[data-blok-search-field]` |\n\n---\n\n## Bundled Tools\n\nBlok includes Header and Paragraph tools. No external packages needed:\n\n```diff\n- import Header from '@editorjs/header';\n- import Paragraph from '@editorjs/paragraph';\n\n+ import Blok from '@jackuait/blok';\n\nconst editor = new Blok({\n tools: {\n- header: Header,\n- paragraph: Paragraph,\n+ header: Blok.Header,\n+ paragraph: Blok.Paragraph, // optional, it's the default\n },\n});\n```\n\n### Tool Configuration\n\nBoth bundled tools accept configuration options:\n\n#### HeaderConfig\n\n```typescript\nimport type { HeaderConfig } from '@jackuait/blok';\n\nconst editor = new Blok({\n tools: {\n header: {\n class: Blok.Header,\n config: {\n placeholder: 'Enter a heading',\n levels: [2, 3, 4], // Restrict to H2-H4 only\n defaultLevel: 2, // Default to H2\n } as HeaderConfig,\n },\n },\n});\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `placeholder` | `string` | `''` | Placeholder text for empty header |\n| `levels` | `number[]` | `[1,2,3,4,5,6]` | Available heading levels (1-6) |\n| `defaultLevel` | `number` | `2` | Default heading level |\n\n#### ParagraphConfig\n\n```typescript\nimport type { ParagraphConfig } from '@jackuait/blok';\n\nconst editor = new Blok({\n tools: {\n paragraph: {\n class: Blok.Paragraph,\n config: {\n placeholder: 'Start typing...',\n preserveBlank: true,\n } as ParagraphConfig,\n },\n },\n});\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `placeholder` | `string` | `''` | Placeholder text for empty paragraph |\n| `preserveBlank` | `boolean` | `false` | Keep empty paragraphs when saving |\n\n### Delimiter → Divider\n\nEditor.js's `@editorjs/delimiter` is replaced by Blok's built-in `divider` tool:\n\n```diff\n- import Delimiter from '@editorjs/delimiter';\n\nconst editor = new Blok({\n tools: {\n- delimiter: Delimiter,\n+ divider: Blok.Divider,\n },\n});\n```\n\n**Saved data migration:** The block type name changed from `\"delimiter\"` to `\"divider\"`. The data format is identical (empty `{}`):\n\n```diff\n {\n- \"type\": \"delimiter\",\n+ \"type\": \"divider\",\n \"data\": {}\n }\n```\n\n> **Note:** Blok automatically recognizes `\"delimiter\"` blocks and renders them as dividers, so existing articles work without data migration. However, renaming the type in your database is recommended for consistency.\n\n---\n\n## Configuration Defaults\n\n| Option | EditorJS | Blok |\n|--------|----------|------|\n| `holder` | `\"editorjs\"` | `\"blok\"` |\n| `defaultBlock` | `\"paragraph\"` | `\"paragraph\"` |\n\n---\n\n## New API Methods\n\nBlok exposes shorthand methods directly on the instance:\n\n```javascript\nconst editor = new Blok({ ... });\n\n// Shorthand methods\nawait editor.save(); // Same as editor.saver.save()\neditor.clear(); // Same as editor.blocks.clear()\nawait editor.render(data);// Same as editor.blocks.render(data)\neditor.focus(); // Same as editor.caret.focus()\n\n// Event methods\neditor.on('change', handler); // Same as editor.events.on()\neditor.off('change', handler); // Same as editor.events.off()\neditor.emit('custom', data); // Same as editor.events.emit()\n```\n\n---\n\n## DOM Selectors\n\nBlok uses consistent `data-blok-interface` attributes for major UI components:\n\n```javascript\n// Find the editor wrapper\nconst editorWrapper = document.querySelector('[data-blok-interface=\"blok\"]');\n\n// Find inline toolbar\nconst inlineToolbar = document.querySelector('[data-blok-interface=\"inline-toolbar\"]');\n\n// Find tooltip\nconst tooltip = document.querySelector('[data-blok-interface=\"tooltip\"]');\n```\n\nThese selectors are stable and recommended for programmatic access to Blok's UI components.\n\n### Key Changes\n\n1. **Prefix custom classes** with `blok-` instead of `ce-` or `cdx-`\n2. **Use `data-blok-*`** attributes instead of `data-*`\n\n---\n\n## E2E Test Selectors\n\nUpdate your test selectors to use Blok's `data-blok-testid` attributes:\n\n| EditorJS | Blok |\n|----------|------|\n| `[data-cy=editorjs]` | `[data-blok-testid=\"blok-editor\"]` |\n| `.ce-block` | `[data-blok-element]` |\n| `.ce-block__content` | `[data-blok-element-content]` |\n| `.ce-toolbar` | `[data-blok-toolbar]` |\n| `.ce-toolbar__plus` | `[data-blok-testid=\"plus-button\"]` |\n| `.ce-toolbar__settings-btn` | `[data-blok-settings-toggler]` |\n| `.ce-toolbar__actions` | `[data-blok-testid=\"toolbar-actions\"]` |\n| `.ce-toolbox` | `[data-blok-toolbox]` |\n| `.ce-inline-toolbar` | `[data-blok-testid=\"inline-toolbar\"]` |\n| `.ce-popover` | `[data-blok-popover]` |\n| `.ce-popover-item` | `[data-blok-testid=\"popover-item\"]` |\n| `[data-item-name=\"...\"]` | `[data-blok-item-name=\"...\"]` |\n\n### Playwright Example\n\n```diff\n// EditorJS\n- await page.locator('[data-cy=editorjs] .ce-block').click();\n- await page.locator('.ce-toolbar__settings-btn').click();\n- await page.locator('[data-item-name=\"delete\"]').click();\n\n// Blok\n+ await page.locator('[data-blok-element]').click();\n+ await page.locator('[data-blok-settings-toggler]').click();\n+ await page.locator('[data-blok-item-name=\"delete\"]').click();\n```\n\n### Additional Test Selectors\n\n| Element | Selector |\n|---------|----------|\n| Block tunes popover | `[data-blok-testid=\"block-tunes-popover\"]` |\n| Popover search input | `[data-blok-testid=\"popover-search-input\"]` |\n| Popover item title | `[data-blok-testid=\"popover-item-title\"]` |\n| Popover overlay | `[data-blok-testid=\"popover-overlay\"]` |\n| Inline tool input | `[data-blok-testid=\"inline-tool-input\"]` |\n| Tooltip | `[data-blok-testid=\"tooltip\"]` |\n| Redactor | `[data-blok-testid=\"redactor\"]` |\n| Toolbox | `[data-blok-testid=\"toolbox\"]` |\n| Toolbox popover | `[data-blok-testid=\"toolbox-popover\"]` |\n| Selection overlay | `[data-blok-testid=\"overlay\"]` |\n| Selection rectangle | `[data-blok-testid=\"overlay-rectangle\"]` |\n\n---\n\n### State Selectors\n\n| State | Selector |\n|-------|----------|\n| Toolbar opened | `[data-blok-opened=\"true\"]` |\n| Block selected | `[data-blok-selected=\"true\"]` |\n| Popover opened | `[data-blok-popover-opened=\"true\"]` |\n| Item focused | `[data-blok-focused=\"true\"]` |\n| Item disabled | `[data-blok-disabled=\"true\"]` |\n| Nested popover | `[data-blok-nested=\"true\"]` |\n| Toolbox opened | `[data-blok-toolbox-opened=\"true\"]` |\n| Block dragging | `[data-blok-dragging=\"true\"]` |\n| Element hidden | `[data-blok-hidden=\"true\"]` |\n| RTL mode | `[data-blok-rtl=\"true\"]` |\n\n---\n\n## Quick Checklist\n\n- [ ] Replace `new EditorJS` with `new Blok`\n- [ ] Update imports from `@editorjs/*` to `@jackuait/blok`\n- [ ] Change holder from `editorjs` to `blok` (or specify explicitly)\n- [ ] Use bundled tools: `Blok.Header`, `Blok.Paragraph`\n- [ ] Replace `@editorjs/delimiter` with built-in `Blok.Divider`\n- [ ] Update `EditorConfig` type to `BlokConfig`\n- [ ] Replace `data-id` with `data-blok-id` in queries\n- [ ] Replace `data-item-name` with `data-blok-item-name`\n- [ ] Replace `.ce-*` selectors with `[data-blok-*]` attributes\n- [ ] Replace `.cdx-*` selectors with `[data-blok-*]` attributes\n- [ ] Update E2E test selectors\n", n = (e) => [
4
- "# Blok Migration Guide (for LLM-assisted migration)",
5
- "",
6
- "> This document contains everything needed to migrate a project from EditorJS to Blok.",
7
- "> Apply these changes systematically to the user's codebase.",
8
- `> Current Blok version: ${e}`,
9
- "",
10
- "---",
11
- ""
12
- ].join("\n") + t, r = (t, n) => {
13
- n ? (e.writeFileSync(n, t, "utf-8"), process.stderr.write(`Written to ${n}\n`)) : process.stdout.write(t);
14
- }, i = "Usage: blok [options]\n\nOptions:\n --migration Output the EditorJS to Blok migration guide (LLM-friendly)\n --output <file> Write output to a file instead of stdout\n --help Show this help message\n\nExamples:\n npx @jackuait/blok --migration\n npx @jackuait/blok --migration | pbcopy\n npx @jackuait/blok --migration --output migration-guide.md\n", a = (e) => {
15
- if (e.includes("--help")) return { command: "help" };
16
- if (e.includes("--migration")) {
17
- let t = e.indexOf("--output");
18
- return {
19
- command: "migration",
20
- output: t === -1 ? void 0 : e[t + 1]
21
- };
22
- }
23
- return { command: null };
24
- }, o = (e, t) => {
25
- let { command: o, output: s } = a(e);
26
- switch (o) {
27
- case "migration":
28
- r(n(t), s);
29
- break;
30
- case "help":
31
- case null:
32
- process.stdout.write(i);
33
- break;
34
- }
35
- };
36
- //#endregion
37
- export { o as run };