@jackuait/blok 0.4.3-beta.1 → 0.4.3-beta.10

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 (230) hide show
  1. package/README.md +7 -5
  2. package/dist/blok.mjs +2 -2
  3. package/dist/chunks/{blok-8ptWuVZC.mjs → blok-D85C_iyk.mjs} +1863 -1736
  4. package/dist/chunks/{i18next-loader-bLawSYRV.mjs → i18next-loader-exc8oN3J.mjs} +1 -1
  5. package/dist/chunks/{index-5nYtWZD2.mjs → index-fqgWb6Ue.mjs} +1 -1
  6. package/dist/chunks/{inline-tool-convert-CvMDAIzb.mjs → inline-tool-convert-CYkcoF1r.mjs} +201 -203
  7. package/dist/chunks/{messages-BFiUomgG.mjs → messages--yMo5vYp.mjs} +1 -1
  8. package/dist/chunks/{messages-2434tVOK.mjs → messages-82j7Sb1f.mjs} +1 -1
  9. package/dist/chunks/{messages-p9BZJaFV.mjs → messages-9-pnvSWZ.mjs} +1 -1
  10. package/dist/{messages-D5WeksbV.mjs → chunks/messages-B-b_IFZh.mjs} +1 -1
  11. package/dist/chunks/{messages-ez3w5NBn.mjs → messages-B5OIvtYB.mjs} +1 -1
  12. package/dist/{messages-FTOZNhRD.mjs → chunks/messages-B7zIiLTq.mjs} +1 -1
  13. package/dist/{messages-JF2fzCkK.mjs → chunks/messages-BCidD-VB.mjs} +1 -1
  14. package/dist/chunks/{messages-WYWIbQwo.mjs → messages-BWDnoNq0.mjs} +1 -1
  15. package/dist/chunks/{messages-BIPNHHAV.mjs → messages-BXsoyz5-.mjs} +1 -1
  16. package/dist/{messages-CXHt3eCC.mjs → chunks/messages-BYeiscyc.mjs} +1 -1
  17. package/dist/chunks/{messages-er-kd-VO.mjs → messages-BZ-Ia4tk.mjs} +1 -1
  18. package/dist/chunks/{messages-CmymP_Ar.mjs → messages-BaaIMBL8.mjs} +1 -1
  19. package/dist/chunks/{messages-Dd5iZN3c.mjs → messages-BgbVziWf.mjs} +1 -1
  20. package/dist/chunks/{messages-76-iJV9Q.mjs → messages-BgmPDL9Z.mjs} +1 -1
  21. package/dist/chunks/{messages-BextV3Qh.mjs → messages-Bi0X1QMU.mjs} +1 -1
  22. package/dist/chunks/{messages-QgYhPL-3.mjs → messages-BjZll4R7.mjs} +1 -1
  23. package/dist/{messages-D_FCyfW6.mjs → chunks/messages-BnmcUH66.mjs} +1 -1
  24. package/dist/chunks/{messages-DueVe0F1.mjs → messages-Bo9JrN9m.mjs} +1 -1
  25. package/dist/{messages-f3uXjegd.mjs → chunks/messages-BuKgyEeU.mjs} +1 -1
  26. package/dist/chunks/{messages-ohwI1UGv.mjs → messages-CJQt8nzO.mjs} +1 -1
  27. package/dist/chunks/{messages-D3vTzIpL.mjs → messages-CNRsIuSS.mjs} +1 -1
  28. package/dist/chunks/{messages-qWkXPggi.mjs → messages-CPdlHIEi.mjs} +1 -1
  29. package/dist/{messages-CbmsBrB0.mjs → chunks/messages-CTeYgNo1.mjs} +1 -1
  30. package/dist/{messages-BCm2eudQ.mjs → chunks/messages-CVvHbBlx.mjs} +1 -1
  31. package/dist/{messages-D3GrDwXh.mjs → chunks/messages-CbP4KoKV.mjs} +1 -1
  32. package/dist/chunks/{messages-Dg1OHftD.mjs → messages-CbepBt3K.mjs} +1 -1
  33. package/dist/{messages-BqWaOGMn.mjs → chunks/messages-CbtBGtJT.mjs} +1 -1
  34. package/dist/chunks/{messages-BqkL2_Ro.mjs → messages-CcYGSGRh.mjs} +1 -1
  35. package/dist/{messages-57uL5htT.mjs → chunks/messages-CjksZqgf.mjs} +1 -1
  36. package/dist/chunks/{messages-CFrKE-TN.mjs → messages-Cm4WhO4g.mjs} +1 -1
  37. package/dist/{messages-BiXe9G-O.mjs → chunks/messages-CoY-hJgp.mjs} +1 -1
  38. package/dist/chunks/{messages-qIQ4L4rw.mjs → messages-CpkO-V0C.mjs} +1 -1
  39. package/dist/{messages-DKha57ZU.mjs → chunks/messages-CwYueRo5.mjs} +1 -1
  40. package/dist/chunks/{messages-3DcCwXMF.mjs → messages-D-tDNAzW.mjs} +1 -1
  41. package/dist/{messages-DehM7135.mjs → chunks/messages-D2h_bTrn.mjs} +1 -1
  42. package/dist/chunks/{messages-IQxGfQIV.mjs → messages-D5J2Xlpx.mjs} +1 -1
  43. package/dist/chunks/{messages-Cm0LJLtB.mjs → messages-D8VsV_Ro.mjs} +1 -1
  44. package/dist/chunks/{messages-CHz8VlG-.mjs → messages-D8sk1mxH.mjs} +1 -1
  45. package/dist/chunks/{messages-MOGl7I5v.mjs → messages-D8tlQmPj.mjs} +1 -1
  46. package/dist/chunks/{messages-BiPSFlUG.mjs → messages-D9xzl8Wn.mjs} +1 -1
  47. package/dist/{messages-BoO8gsVD.mjs → chunks/messages-DG2QXu_f.mjs} +1 -1
  48. package/dist/{messages-DGaab4EP.mjs → chunks/messages-DHdkT1vy.mjs} +1 -1
  49. package/dist/{messages-4kMwVAKY.mjs → chunks/messages-DIbeBo-6.mjs} +1 -1
  50. package/dist/{messages-DVbPLd_0.mjs → chunks/messages-DR0sRlko.mjs} +1 -1
  51. package/dist/{messages-BX-DPa-z.mjs → chunks/messages-DRTt_xBc.mjs} +1 -1
  52. package/dist/{messages-CV7OM_qk.mjs → chunks/messages-DT43ZUKr.mjs} +1 -1
  53. package/dist/{messages-Di6Flq-b.mjs → chunks/messages-DT5djAjs.mjs} +1 -1
  54. package/dist/{messages-C6tbPLoj.mjs → chunks/messages-Df6LYOt5.mjs} +1 -1
  55. package/dist/chunks/{messages-BUlwu9mo.mjs → messages-DhdzbzrS.mjs} +1 -1
  56. package/dist/chunks/{messages-Ceo1KtFx.mjs → messages-DiyoZ_Oa.mjs} +1 -1
  57. package/dist/chunks/{messages-8p86Eyf2.mjs → messages-DkJ5aV3V.mjs} +1 -1
  58. package/dist/{messages-Dx3eFwI0.mjs → chunks/messages-DvwZEFTL.mjs} +1 -1
  59. package/dist/{messages-Bl5z_Igo.mjs → chunks/messages-Dw5GZ4YT.mjs} +1 -1
  60. package/dist/chunks/{messages-CLixzySl.mjs → messages-HSxl9MoN.mjs} +1 -1
  61. package/dist/chunks/{messages-DOaujgMW.mjs → messages-JztkG3tZ.mjs} +1 -1
  62. package/dist/{messages-BnsE97ku.mjs → chunks/messages-KLzTvDED.mjs} +1 -1
  63. package/dist/{messages-BBX0p0Pi.mjs → chunks/messages-TulYnz1f.mjs} +1 -1
  64. package/dist/chunks/{messages-D0ohMB5H.mjs → messages-Uexhy7C9.mjs} +1 -1
  65. package/dist/{messages-Dqhhex6e.mjs → chunks/messages-ctRNTfv5.mjs} +1 -1
  66. package/dist/chunks/{messages-CA6T3-gQ.mjs → messages-dIzxSHR2.mjs} +1 -1
  67. package/dist/chunks/{messages-bSf31LJi.mjs → messages-g7Njot5w.mjs} +1 -1
  68. package/dist/chunks/{messages-w5foGze_.mjs → messages-iNIhRBkQ.mjs} +1 -1
  69. package/dist/chunks/{messages-FOtiUoKl.mjs → messages-jmh1Gbfd.mjs} +1 -1
  70. package/dist/chunks/{messages-a6A_LgDv.mjs → messages-oO_lJ-_I.mjs} +1 -1
  71. package/dist/chunks/{messages-BvCkXKX-.mjs → messages-qFesO1Lu.mjs} +1 -1
  72. package/dist/{messages-diGozhTn.mjs → chunks/messages-sSYZ0Mz9.mjs} +1 -1
  73. package/dist/chunks/{messages-CFFPFdWP.mjs → messages-zs9AgeBF.mjs} +1 -1
  74. package/dist/full.mjs +2 -2
  75. package/dist/locales.mjs +68 -68
  76. package/dist/{messages-BFiUomgG.mjs → messages--yMo5vYp.mjs} +1 -1
  77. package/dist/{messages-2434tVOK.mjs → messages-82j7Sb1f.mjs} +1 -1
  78. package/dist/{messages-p9BZJaFV.mjs → messages-9-pnvSWZ.mjs} +1 -1
  79. package/dist/{chunks/messages-D5WeksbV.mjs → messages-B-b_IFZh.mjs} +1 -1
  80. package/dist/{messages-ez3w5NBn.mjs → messages-B5OIvtYB.mjs} +1 -1
  81. package/dist/{chunks/messages-FTOZNhRD.mjs → messages-B7zIiLTq.mjs} +1 -1
  82. package/dist/{chunks/messages-JF2fzCkK.mjs → messages-BCidD-VB.mjs} +1 -1
  83. package/dist/{messages-WYWIbQwo.mjs → messages-BWDnoNq0.mjs} +1 -1
  84. package/dist/{messages-BIPNHHAV.mjs → messages-BXsoyz5-.mjs} +1 -1
  85. package/dist/{chunks/messages-CXHt3eCC.mjs → messages-BYeiscyc.mjs} +1 -1
  86. package/dist/{messages-er-kd-VO.mjs → messages-BZ-Ia4tk.mjs} +1 -1
  87. package/dist/{messages-CmymP_Ar.mjs → messages-BaaIMBL8.mjs} +1 -1
  88. package/dist/{messages-Dd5iZN3c.mjs → messages-BgbVziWf.mjs} +1 -1
  89. package/dist/{messages-76-iJV9Q.mjs → messages-BgmPDL9Z.mjs} +1 -1
  90. package/dist/{messages-BextV3Qh.mjs → messages-Bi0X1QMU.mjs} +1 -1
  91. package/dist/{messages-QgYhPL-3.mjs → messages-BjZll4R7.mjs} +1 -1
  92. package/dist/{chunks/messages-D_FCyfW6.mjs → messages-BnmcUH66.mjs} +1 -1
  93. package/dist/{messages-DueVe0F1.mjs → messages-Bo9JrN9m.mjs} +1 -1
  94. package/dist/{chunks/messages-f3uXjegd.mjs → messages-BuKgyEeU.mjs} +1 -1
  95. package/dist/{messages-ohwI1UGv.mjs → messages-CJQt8nzO.mjs} +1 -1
  96. package/dist/{messages-D3vTzIpL.mjs → messages-CNRsIuSS.mjs} +1 -1
  97. package/dist/{messages-qWkXPggi.mjs → messages-CPdlHIEi.mjs} +1 -1
  98. package/dist/{chunks/messages-CbmsBrB0.mjs → messages-CTeYgNo1.mjs} +1 -1
  99. package/dist/{chunks/messages-BCm2eudQ.mjs → messages-CVvHbBlx.mjs} +1 -1
  100. package/dist/{chunks/messages-D3GrDwXh.mjs → messages-CbP4KoKV.mjs} +1 -1
  101. package/dist/{messages-Dg1OHftD.mjs → messages-CbepBt3K.mjs} +1 -1
  102. package/dist/{chunks/messages-BqWaOGMn.mjs → messages-CbtBGtJT.mjs} +1 -1
  103. package/dist/{messages-BqkL2_Ro.mjs → messages-CcYGSGRh.mjs} +1 -1
  104. package/dist/{chunks/messages-57uL5htT.mjs → messages-CjksZqgf.mjs} +1 -1
  105. package/dist/{messages-CFrKE-TN.mjs → messages-Cm4WhO4g.mjs} +1 -1
  106. package/dist/{chunks/messages-BiXe9G-O.mjs → messages-CoY-hJgp.mjs} +1 -1
  107. package/dist/{messages-qIQ4L4rw.mjs → messages-CpkO-V0C.mjs} +1 -1
  108. package/dist/{chunks/messages-DKha57ZU.mjs → messages-CwYueRo5.mjs} +1 -1
  109. package/dist/{messages-3DcCwXMF.mjs → messages-D-tDNAzW.mjs} +1 -1
  110. package/dist/{chunks/messages-DehM7135.mjs → messages-D2h_bTrn.mjs} +1 -1
  111. package/dist/{messages-IQxGfQIV.mjs → messages-D5J2Xlpx.mjs} +1 -1
  112. package/dist/{messages-Cm0LJLtB.mjs → messages-D8VsV_Ro.mjs} +1 -1
  113. package/dist/{messages-CHz8VlG-.mjs → messages-D8sk1mxH.mjs} +1 -1
  114. package/dist/{messages-MOGl7I5v.mjs → messages-D8tlQmPj.mjs} +1 -1
  115. package/dist/{messages-BiPSFlUG.mjs → messages-D9xzl8Wn.mjs} +1 -1
  116. package/dist/{chunks/messages-BoO8gsVD.mjs → messages-DG2QXu_f.mjs} +1 -1
  117. package/dist/{chunks/messages-DGaab4EP.mjs → messages-DHdkT1vy.mjs} +1 -1
  118. package/dist/{chunks/messages-4kMwVAKY.mjs → messages-DIbeBo-6.mjs} +1 -1
  119. package/dist/{chunks/messages-DVbPLd_0.mjs → messages-DR0sRlko.mjs} +1 -1
  120. package/dist/{chunks/messages-BX-DPa-z.mjs → messages-DRTt_xBc.mjs} +1 -1
  121. package/dist/{chunks/messages-CV7OM_qk.mjs → messages-DT43ZUKr.mjs} +1 -1
  122. package/dist/{chunks/messages-Di6Flq-b.mjs → messages-DT5djAjs.mjs} +1 -1
  123. package/dist/{chunks/messages-C6tbPLoj.mjs → messages-Df6LYOt5.mjs} +1 -1
  124. package/dist/{messages-BUlwu9mo.mjs → messages-DhdzbzrS.mjs} +1 -1
  125. package/dist/{messages-Ceo1KtFx.mjs → messages-DiyoZ_Oa.mjs} +1 -1
  126. package/dist/{messages-8p86Eyf2.mjs → messages-DkJ5aV3V.mjs} +1 -1
  127. package/dist/{chunks/messages-Dx3eFwI0.mjs → messages-DvwZEFTL.mjs} +1 -1
  128. package/dist/{chunks/messages-Bl5z_Igo.mjs → messages-Dw5GZ4YT.mjs} +1 -1
  129. package/dist/{messages-CLixzySl.mjs → messages-HSxl9MoN.mjs} +1 -1
  130. package/dist/{messages-DOaujgMW.mjs → messages-JztkG3tZ.mjs} +1 -1
  131. package/dist/{chunks/messages-BnsE97ku.mjs → messages-KLzTvDED.mjs} +1 -1
  132. package/dist/{chunks/messages-BBX0p0Pi.mjs → messages-TulYnz1f.mjs} +1 -1
  133. package/dist/{messages-D0ohMB5H.mjs → messages-Uexhy7C9.mjs} +1 -1
  134. package/dist/{chunks/messages-Dqhhex6e.mjs → messages-ctRNTfv5.mjs} +1 -1
  135. package/dist/{messages-CA6T3-gQ.mjs → messages-dIzxSHR2.mjs} +1 -1
  136. package/dist/{messages-bSf31LJi.mjs → messages-g7Njot5w.mjs} +1 -1
  137. package/dist/{messages-w5foGze_.mjs → messages-iNIhRBkQ.mjs} +1 -1
  138. package/dist/{messages-FOtiUoKl.mjs → messages-jmh1Gbfd.mjs} +1 -1
  139. package/dist/{messages-a6A_LgDv.mjs → messages-oO_lJ-_I.mjs} +1 -1
  140. package/dist/{messages-BvCkXKX-.mjs → messages-qFesO1Lu.mjs} +1 -1
  141. package/dist/{chunks/messages-diGozhTn.mjs → messages-sSYZ0Mz9.mjs} +1 -1
  142. package/dist/{messages-CFFPFdWP.mjs → messages-zs9AgeBF.mjs} +1 -1
  143. package/dist/tools.mjs +131 -90
  144. package/package.json +3 -3
  145. package/src/components/flipper.ts +17 -1
  146. package/src/components/i18n/locales/am/messages.json +1 -1
  147. package/src/components/i18n/locales/ar/messages.json +1 -1
  148. package/src/components/i18n/locales/az/messages.json +1 -1
  149. package/src/components/i18n/locales/bg/messages.json +1 -1
  150. package/src/components/i18n/locales/bn/messages.json +1 -1
  151. package/src/components/i18n/locales/bs/messages.json +1 -1
  152. package/src/components/i18n/locales/cs/messages.json +1 -1
  153. package/src/components/i18n/locales/da/messages.json +1 -1
  154. package/src/components/i18n/locales/de/messages.json +1 -1
  155. package/src/components/i18n/locales/dv/messages.json +1 -1
  156. package/src/components/i18n/locales/el/messages.json +1 -1
  157. package/src/components/i18n/locales/en/messages.json +1 -1
  158. package/src/components/i18n/locales/es/messages.json +1 -1
  159. package/src/components/i18n/locales/et/messages.json +1 -1
  160. package/src/components/i18n/locales/fa/messages.json +1 -1
  161. package/src/components/i18n/locales/fi/messages.json +1 -1
  162. package/src/components/i18n/locales/fil/messages.json +1 -1
  163. package/src/components/i18n/locales/fr/messages.json +1 -1
  164. package/src/components/i18n/locales/gu/messages.json +1 -1
  165. package/src/components/i18n/locales/he/messages.json +1 -1
  166. package/src/components/i18n/locales/hi/messages.json +1 -1
  167. package/src/components/i18n/locales/hr/messages.json +1 -1
  168. package/src/components/i18n/locales/hu/messages.json +1 -1
  169. package/src/components/i18n/locales/hy/messages.json +1 -1
  170. package/src/components/i18n/locales/id/messages.json +1 -1
  171. package/src/components/i18n/locales/it/messages.json +1 -1
  172. package/src/components/i18n/locales/ja/messages.json +1 -1
  173. package/src/components/i18n/locales/ka/messages.json +1 -1
  174. package/src/components/i18n/locales/km/messages.json +1 -1
  175. package/src/components/i18n/locales/kn/messages.json +1 -1
  176. package/src/components/i18n/locales/ko/messages.json +1 -1
  177. package/src/components/i18n/locales/ku/messages.json +1 -1
  178. package/src/components/i18n/locales/lo/messages.json +1 -1
  179. package/src/components/i18n/locales/lt/messages.json +1 -1
  180. package/src/components/i18n/locales/lv/messages.json +1 -1
  181. package/src/components/i18n/locales/mk/messages.json +1 -1
  182. package/src/components/i18n/locales/ml/messages.json +1 -1
  183. package/src/components/i18n/locales/mn/messages.json +1 -1
  184. package/src/components/i18n/locales/mr/messages.json +1 -1
  185. package/src/components/i18n/locales/ms/messages.json +1 -1
  186. package/src/components/i18n/locales/my/messages.json +1 -1
  187. package/src/components/i18n/locales/ne/messages.json +1 -1
  188. package/src/components/i18n/locales/nl/messages.json +1 -1
  189. package/src/components/i18n/locales/no/messages.json +1 -1
  190. package/src/components/i18n/locales/pa/messages.json +1 -1
  191. package/src/components/i18n/locales/pl/messages.json +1 -1
  192. package/src/components/i18n/locales/ps/messages.json +1 -1
  193. package/src/components/i18n/locales/pt/messages.json +1 -1
  194. package/src/components/i18n/locales/ro/messages.json +1 -1
  195. package/src/components/i18n/locales/ru/messages.json +1 -1
  196. package/src/components/i18n/locales/sd/messages.json +1 -1
  197. package/src/components/i18n/locales/si/messages.json +1 -1
  198. package/src/components/i18n/locales/sk/messages.json +1 -1
  199. package/src/components/i18n/locales/sl/messages.json +1 -1
  200. package/src/components/i18n/locales/sq/messages.json +1 -1
  201. package/src/components/i18n/locales/sr/messages.json +1 -1
  202. package/src/components/i18n/locales/sv/messages.json +1 -1
  203. package/src/components/i18n/locales/sw/messages.json +1 -1
  204. package/src/components/i18n/locales/ta/messages.json +1 -1
  205. package/src/components/i18n/locales/te/messages.json +1 -1
  206. package/src/components/i18n/locales/th/messages.json +1 -1
  207. package/src/components/i18n/locales/tr/messages.json +1 -1
  208. package/src/components/i18n/locales/ug/messages.json +1 -1
  209. package/src/components/i18n/locales/uk/messages.json +1 -1
  210. package/src/components/i18n/locales/ur/messages.json +1 -1
  211. package/src/components/i18n/locales/vi/messages.json +1 -1
  212. package/src/components/i18n/locales/yi/messages.json +1 -1
  213. package/src/components/i18n/locales/zh/messages.json +1 -1
  214. package/src/components/modules/api/i18n.ts +1 -0
  215. package/src/components/modules/dragManager.ts +71 -13
  216. package/src/components/modules/i18n.ts +6 -11
  217. package/src/components/modules/rectangleSelection.ts +62 -19
  218. package/src/components/modules/toolbar/index.ts +2 -2
  219. package/src/components/modules/tools.ts +39 -3
  220. package/src/components/modules/ui.ts +83 -18
  221. package/src/components/selection.ts +2 -12
  222. package/src/components/tools/base.ts +29 -2
  223. package/src/components/tools/block.ts +20 -3
  224. package/src/components/tools/factory.ts +50 -1
  225. package/src/components/utils/tooltip.ts +0 -14
  226. package/src/tools/header/index.ts +82 -0
  227. package/src/tools/index.ts +13 -6
  228. package/src/tools/list/index.ts +2 -2
  229. package/types/api/i18n.d.ts +8 -0
  230. package/types/tools/tool-settings.d.ts +62 -4
@@ -35,6 +35,8 @@ const DRAG_CONFIG = {
35
35
  autoScrollSpeed: 10,
36
36
  /** Throttle delay for drop position announcements (ms) */
37
37
  announcementThrottleMs: 300,
38
+ /** Horizontal distance to the left of blocks where drop is still valid */
39
+ leftDropZone: 50,
38
40
  };
39
41
 
40
42
  /**
@@ -424,20 +426,10 @@ export class DragManager extends Module {
424
426
  return;
425
427
  }
426
428
 
427
- // Find block holder
428
- const blockHolder = elementUnderCursor.closest(createSelector(DATA_ATTR.element)) as HTMLElement | null;
429
+ // Find block holder - first try direct hit, then left drop zone fallback
430
+ const { block: targetBlock, holder: blockHolder } = this.findDropTargetBlock(elementUnderCursor, clientX, clientY);
429
431
 
430
- if (!blockHolder) {
431
- this.dragState.targetBlock = null;
432
- this.dragState.targetEdge = null;
433
-
434
- return;
435
- }
436
-
437
- // Find the block instance
438
- const targetBlock = this.Blok.BlockManager.blocks.find(b => b.holder === blockHolder);
439
-
440
- if (!targetBlock || targetBlock === this.dragState.sourceBlock) {
432
+ if (!blockHolder || !targetBlock || targetBlock === this.dragState.sourceBlock) {
441
433
  this.dragState.targetBlock = null;
442
434
  this.dragState.targetEdge = null;
443
435
 
@@ -1132,6 +1124,72 @@ export class DragManager extends Module {
1132
1124
  return collectDescendants(blockIndex + 1, []);
1133
1125
  }
1134
1126
 
1127
+ /**
1128
+ * Finds the drop target block from an element or by checking the left drop zone.
1129
+ * @param elementUnderCursor - Element directly under the cursor
1130
+ * @param clientX - Cursor X position
1131
+ * @param clientY - Cursor Y position
1132
+ * @returns Object with block and holder, or nulls if no valid target found
1133
+ */
1134
+ private findDropTargetBlock(
1135
+ elementUnderCursor: Element,
1136
+ clientX: number,
1137
+ clientY: number
1138
+ ): { block: Block | undefined; holder: HTMLElement | null } {
1139
+ // First try: find block holder directly under cursor
1140
+ const directHolder = elementUnderCursor.closest(createSelector(DATA_ATTR.element)) as HTMLElement | null;
1141
+
1142
+ if (directHolder) {
1143
+ const block = this.Blok.BlockManager.blocks.find(b => b.holder === directHolder);
1144
+
1145
+ return { block, holder: directHolder };
1146
+ }
1147
+
1148
+ // Fallback: check if cursor is in the left drop zone
1149
+ const leftZoneBlock = this.findBlockInLeftDropZone(clientX, clientY);
1150
+
1151
+ if (leftZoneBlock) {
1152
+ return { block: leftZoneBlock, holder: leftZoneBlock.holder };
1153
+ }
1154
+
1155
+ return { block: undefined, holder: null };
1156
+ }
1157
+
1158
+ /**
1159
+ * Finds a block by vertical position when cursor is in the left drop zone.
1160
+ * Used as a fallback when elementFromPoint doesn't find a block directly.
1161
+ * @param clientX - Cursor X position
1162
+ * @param clientY - Cursor Y position
1163
+ * @returns Block at the vertical position, or null if not in left zone or no block found
1164
+ */
1165
+ private findBlockInLeftDropZone(clientX: number, clientY: number): Block | null {
1166
+ const contentRect = this.Blok.UI.contentRect;
1167
+ const leftEdge = contentRect.left;
1168
+
1169
+ // Check if cursor is within left drop zone (between leftEdge - leftDropZone and leftEdge)
1170
+ const distanceFromEdge = leftEdge - clientX;
1171
+
1172
+ if (distanceFromEdge < 0 || distanceFromEdge > DRAG_CONFIG.leftDropZone) {
1173
+ return null;
1174
+ }
1175
+
1176
+ // Find block by Y position
1177
+ for (const block of this.Blok.BlockManager.blocks) {
1178
+ // Skip source blocks
1179
+ if (this.dragState?.sourceBlocks.includes(block)) {
1180
+ continue;
1181
+ }
1182
+
1183
+ const rect = block.holder.getBoundingClientRect();
1184
+
1185
+ if (clientY >= rect.top && clientY <= rect.bottom) {
1186
+ return block;
1187
+ }
1188
+ }
1189
+
1190
+ return null;
1191
+ }
1192
+
1135
1193
  /**
1136
1194
  * Module destruction
1137
1195
  */
@@ -204,17 +204,7 @@ export class I18n extends Module {
204
204
  // Set default locale if configured
205
205
  this.applyDefaultLocale(i18nConfig?.defaultLocale);
206
206
 
207
- // Handle custom messages (highest priority)
208
- if (i18nConfig?.messages !== undefined) {
209
- // For custom messages, use lightweight implementation with overrides
210
- this.lightweightI18n.setDictionary(i18nConfig.messages);
211
- this.usingI18next = false;
212
- // Set direction from config or default to 'ltr' for custom messages
213
- this.updateConfigDirection(i18nConfig.direction ?? 'ltr');
214
-
215
- return;
216
- }
217
-
207
+ // Load base translations first
218
208
  const requestedLocale = i18nConfig?.locale;
219
209
 
220
210
  if (requestedLocale === undefined || requestedLocale === 'auto') {
@@ -223,6 +213,11 @@ export class I18n extends Module {
223
213
  await this.setLocale(requestedLocale);
224
214
  }
225
215
 
216
+ // Merge custom messages on top of base translations (if provided)
217
+ if (i18nConfig?.messages !== undefined) {
218
+ this.setDictionary(i18nConfig.messages);
219
+ }
220
+
226
221
  // Update config.i18n.direction so other modules can access it via isRtl getter
227
222
  this.updateConfigDirection(i18nConfig?.direction ?? this.getDirection());
228
223
  }
@@ -116,11 +116,29 @@ export class RectangleSelection extends Module {
116
116
  * Init rect params
117
117
  * @param {number} pageX - X coord of mouse
118
118
  * @param {number} pageY - Y coord of mouse
119
+ * @param {boolean} shiftKey - whether Shift key is held for additive selection
119
120
  */
120
- public startSelection(pageX: number, pageY: number): void {
121
- const scrollLeft = this.getScrollLeft();
121
+ public startSelection(pageX: number, pageY: number, shiftKey = false): void {
122
+ const { UI } = this.Blok;
123
+ const redactor = UI.nodes.redactor;
124
+
125
+ if (!redactor) {
126
+ return;
127
+ }
128
+
129
+ const editorRect = redactor.getBoundingClientRect();
122
130
  const scrollTop = this.getScrollTop();
123
- const elemWhereSelectionStart = document.elementFromPoint(pageX - scrollLeft, pageY - scrollTop);
131
+ const pointerY = pageY - scrollTop;
132
+
133
+ // Check if pointer is within editor's vertical bounds
134
+ const withinEditorVertically = pointerY >= editorRect.top && pointerY <= editorRect.bottom;
135
+
136
+ if (!withinEditorVertically) {
137
+ return;
138
+ }
139
+
140
+ const scrollLeft = this.getScrollLeft();
141
+ const elemWhereSelectionStart = document.elementFromPoint(pageX - scrollLeft, pointerY);
124
142
 
125
143
  if (!elemWhereSelectionStart) {
126
144
  return;
@@ -132,7 +150,11 @@ export class RectangleSelection extends Module {
132
150
  */
133
151
  const startsInsideToolbar = elemWhereSelectionStart.closest(createSelector(DATA_ATTR.toolbar));
134
152
 
135
- if (!startsInsideToolbar) {
153
+ /**
154
+ * When Shift is held, preserve existing selection for additive behavior.
155
+ * Otherwise, clear selection state.
156
+ */
157
+ if (!startsInsideToolbar && !shiftKey) {
136
158
  this.Blok.BlockSelection.allBlocksSelected = false;
137
159
  this.clearSelection();
138
160
  this.stackOfSelected = [];
@@ -141,16 +163,16 @@ export class RectangleSelection extends Module {
141
163
  const selectorsToAvoid = [
142
164
  createSelector(DATA_ATTR.elementContent),
143
165
  createSelector(DATA_ATTR.toolbar),
166
+ createSelector(DATA_ATTR.popover),
144
167
  INLINE_TOOLBAR_INTERFACE_SELECTOR,
145
168
  ];
146
169
 
147
- const startsInsideBlok = elemWhereSelectionStart.closest(createSelector(DATA_ATTR.editor));
148
170
  const startsInSelectorToAvoid = selectorsToAvoid.some((selector) => !!elemWhereSelectionStart.closest(selector));
149
171
 
150
172
  /**
151
- * If selection starts outside of the blok or inside the blocks or on Blok UI elements, do not handle it
173
+ * If selection starts inside the blocks content or on Blok UI elements, do not handle it
152
174
  */
153
- if (!startsInsideBlok || startsInSelectorToAvoid) {
175
+ if (startsInSelectorToAvoid) {
154
176
  return;
155
177
  }
156
178
 
@@ -194,9 +216,9 @@ export class RectangleSelection extends Module {
194
216
  * Sets Module necessary event handlers
195
217
  */
196
218
  private enableModuleBindings(): void {
197
- const { container } = this.genHTML();
219
+ this.genHTML();
198
220
 
199
- this.listeners.on(container, 'mousedown', (event: Event) => {
221
+ this.listeners.on(document.body, 'mousedown', (event: Event) => {
200
222
  this.processMouseDown(event as MouseEvent);
201
223
  }, false);
202
224
 
@@ -245,7 +267,7 @@ export class RectangleSelection extends Module {
245
267
  const startedFromContentEditable = (mouseEvent.target as Element).closest($.allInputsSelector) !== null;
246
268
 
247
269
  if (!startedFromContentEditable) {
248
- this.startSelection(mouseEvent.pageX, mouseEvent.pageY);
270
+ this.startSelection(mouseEvent.pageX, mouseEvent.pageY, mouseEvent.shiftKey);
249
271
  }
250
272
  }
251
273
 
@@ -402,21 +424,38 @@ export class RectangleSelection extends Module {
402
424
  this.mouseY = event.pageY;
403
425
  }
404
426
 
405
- const { rightPos, leftPos, index } = this.genInfoForMouseSelection();
406
- // There is not new block in selection
407
-
408
- const rectIsOnRighSideOfredactor = this.startX > rightPos && this.mouseX > rightPos;
409
- const rectISOnLeftSideOfRedactor = this.startX < leftPos && this.mouseX < leftPos;
410
-
411
- this.rectCrossesBlocks = !(rectIsOnRighSideOfredactor || rectISOnLeftSideOfRedactor);
412
-
413
427
  if (!this.isRectSelectionActivated) {
414
- this.rectCrossesBlocks = false;
415
428
  this.isRectSelectionActivated = true;
416
429
  this.shrinkRectangleToPoint();
417
430
  overlayRectangle.style.display = 'block';
418
431
  }
419
432
 
433
+ const { index } = this.genInfoForMouseSelection();
434
+
435
+ /**
436
+ * Check if the selection rectangle intersects the block holder horizontally.
437
+ * For page-wide selection, we need to verify the rectangle actually reaches the block,
438
+ * not just that the mouse Y position is at the same height as a block.
439
+ */
440
+ this.rectCrossesBlocks = false;
441
+ const block = index !== undefined ? this.Blok.BlockManager.getBlockByIndex(index) : undefined;
442
+
443
+ if (block) {
444
+ const holderRect = block.holder.getBoundingClientRect();
445
+ const scrollLeft = this.getScrollLeft();
446
+
447
+ // Selection rectangle horizontal bounds (in page coordinates)
448
+ const rectLeft = Math.min(this.startX, this.mouseX);
449
+ const rectRight = Math.max(this.startX, this.mouseX);
450
+
451
+ // Block holder horizontal bounds (convert from viewport to page coordinates)
452
+ const holderLeft = holderRect.left + scrollLeft;
453
+ const holderRight = holderRect.right + scrollLeft;
454
+
455
+ // Check for horizontal intersection
456
+ this.rectCrossesBlocks = rectRight >= holderLeft && rectLeft <= holderRight;
457
+ }
458
+
420
459
  this.updateRectangleSize();
421
460
 
422
461
  /**
@@ -517,6 +556,7 @@ export class RectangleSelection extends Module {
517
556
 
518
557
  /**
519
558
  * Collects information needed to determine the behavior of the rectangle
559
+ * For page-wide selection, we check blocks at the center X position but at the actual mouse Y position
520
560
  * @returns {object} index - index next Block, leftPos - start of left border of Block, rightPos - right border
521
561
  */
522
562
  private genInfoForMouseSelection(): {index: number | undefined; leftPos: number; rightPos: number} {
@@ -524,6 +564,9 @@ export class RectangleSelection extends Module {
524
564
  const centerOfRedactor = widthOfRedactor / 2;
525
565
  const scrollTop = this.getScrollTop();
526
566
  const y = this.mouseY - scrollTop;
567
+
568
+ // For page-wide selection: check what block is at the center X, but at the mouse's Y position
569
+ // This allows selection to work even when mouse is in the left/right margins
527
570
  const elementUnderMouse = document.elementFromPoint(centerOfRedactor, y);
528
571
  const lastBlockHolder = this.Blok.BlockManager.lastBlock?.holder;
529
572
  const contentElement = lastBlockHolder?.querySelector(createSelector(DATA_ATTR.elementContent));
@@ -998,9 +998,9 @@ export class Toolbar extends Module<ToolbarNodes> {
998
998
  */
999
999
  this.eventsDispatcher.on(BlockHovered, (data) => {
1000
1000
  /**
1001
- * Do not move toolbar during drag operations
1001
+ * Do not move toolbar during drag or rectangle selection operations
1002
1002
  */
1003
- if (this.Blok.DragManager.isDragging) {
1003
+ if (this.Blok.DragManager.isDragging || this.Blok.RectangleSelection.isRectActivated()) {
1004
1004
  return;
1005
1005
  }
1006
1006
 
@@ -334,18 +334,18 @@ export class Tools extends Module {
334
334
  .map(([toolName, settings]): ChainData => {
335
335
  const toolData: ToolPrepareData = {
336
336
  toolName,
337
- config: (settings.config ?? {}) as ToolConfig,
337
+ config: this.extractToolConfig(settings),
338
338
  };
339
339
 
340
340
  const prepareFunction: ChainData['function'] = async (payload?: unknown) => {
341
- const constructable = settings.class;
341
+ const constructable = settings.class as ToolConstructable | undefined;
342
342
 
343
343
  if (!constructable || !isFunction(constructable.prepare)) {
344
344
  return;
345
345
  }
346
346
 
347
347
  const data = (payload ?? toolData) as ToolPrepareData;
348
- const prepareMethod = constructable.prepare as unknown as ToolPrepareFunction;
348
+ const prepareMethod = constructable.prepare as ToolPrepareFunction;
349
349
 
350
350
  return prepareMethod.call(constructable, data);
351
351
  };
@@ -357,6 +357,42 @@ export class Tools extends Module {
357
357
  });
358
358
  }
359
359
 
360
+ /**
361
+ * Keys that are Blok-level settings (not passed to tool constructor)
362
+ */
363
+ private static readonly BLOK_SETTINGS_KEYS = new Set([
364
+ 'class',
365
+ 'inlineToolbar',
366
+ 'tunes',
367
+ 'shortcut',
368
+ 'toolbox',
369
+ 'config',
370
+ 'isInternal',
371
+ ]);
372
+
373
+ /**
374
+ * Extracts tool configuration from settings.
375
+ * Merges nested config with flat tool-specific options (flat takes precedence).
376
+ * @param settings - Tool settings from user config
377
+ * @returns Merged tool configuration
378
+ */
379
+ private extractToolConfig(settings: ToolSettings): ToolConfig {
380
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
381
+ const nestedConfig = (settings.config ?? {}) as ToolConfig;
382
+
383
+ // Extract non-Blok keys as tool-specific config
384
+ const flatConfig: Record<string, unknown> = {};
385
+
386
+ for (const key of Object.keys(settings)) {
387
+ if (!Tools.BLOK_SETTINGS_KEYS.has(key)) {
388
+ flatConfig[key] = settings[key as keyof typeof settings];
389
+ }
390
+ }
391
+
392
+ // Merge: nested config first, flat config overrides
393
+ return { ...nestedConfig, ...flatConfig } as ToolConfig;
394
+ }
395
+
360
396
  /**
361
397
  * Assign enabled Inline Tools and Block Tunes for Block Tool
362
398
  */
@@ -9,8 +9,15 @@ import { debounce, getValidUrl, isEmpty, openTab, throttle } from '../utils';
9
9
 
10
10
  import { SelectionUtils as Selection } from '../selection';
11
11
  import { Flipper } from '../flipper';
12
+ import type { Block } from '../block';
12
13
  import { mobileScreenBreakpoint } from '../utils';
13
14
 
15
+ /**
16
+ * Horizontal distance from the content edge where block hover is still detected.
17
+ * Extends to the left for LTR layouts, to the right for RTL.
18
+ */
19
+ const HOVER_ZONE_SIZE = 100;
20
+
14
21
  import styles from '../../styles/main.css?inline';
15
22
  import { BlockHovered } from '../events/BlockHovered';
16
23
  import {
@@ -453,43 +460,60 @@ export class UI extends Module<UINodes> {
453
460
  */
454
461
  private watchBlockHoveredEvents(): void {
455
462
  /**
456
- * Used to not emit the same block multiple times to the 'block-hovered' event on every mousemove
463
+ * Used to not emit the same block multiple times to the 'block-hovered' event on every mousemove.
464
+ * Stores block ID to ensure consistent comparison regardless of how the block was detected.
457
465
  */
458
- const blockHoveredState: { lastHovered: Element | null } = {
459
- lastHovered: null,
466
+ const blockHoveredState: { lastHoveredBlockId: string | null } = {
467
+ lastHoveredBlockId: null,
460
468
  };
461
469
 
462
470
  const handleBlockHovered = (event: Event): void => {
463
- const isMouseEvent = typeof MouseEvent !== 'undefined' && event instanceof MouseEvent;
464
- const isTouchEvent = typeof TouchEvent !== 'undefined' && event instanceof TouchEvent;
465
-
466
- if (!isMouseEvent && !isTouchEvent) {
471
+ if (!(event instanceof MouseEvent)) {
467
472
  return;
468
473
  }
469
474
 
470
- const hoveredBlock = (event.target as Element | null)?.closest('[data-blok-testid="block-wrapper"]');
471
-
472
- if (!hoveredBlock) {
473
- return;
474
- }
475
+ const hoveredBlockElement = (event.target as Element | null)?.closest('[data-blok-testid="block-wrapper"]');
475
476
 
476
477
  /**
477
- * For multi-block selection, still emit 'block-hovered' event so toolbar can follow the hovered block.
478
- * The toolbar module will handle the logic of whether to move or not.
478
+ * If no block element found directly, try the extended hover zone
479
479
  */
480
+ const zoneBlock = !hoveredBlockElement
481
+ ? this.findBlockInHoverZone(event.clientX, event.clientY)
482
+ : null;
483
+
484
+ if (zoneBlock !== null && blockHoveredState.lastHoveredBlockId !== zoneBlock.id) {
485
+ blockHoveredState.lastHoveredBlockId = zoneBlock.id;
480
486
 
481
- if (blockHoveredState.lastHovered === hoveredBlock) {
487
+ this.eventsDispatcher.emit(BlockHovered, {
488
+ block: zoneBlock,
489
+ target: zoneBlock.holder,
490
+ });
491
+ }
492
+
493
+ if (zoneBlock !== null) {
482
494
  return;
483
495
  }
484
496
 
485
- blockHoveredState.lastHovered = hoveredBlock;
497
+ if (!hoveredBlockElement) {
498
+ return;
499
+ }
486
500
 
487
- const block = this.Blok.BlockManager.getBlockByChildNode(hoveredBlock);
501
+ const block = this.Blok.BlockManager.getBlockByChildNode(hoveredBlockElement);
488
502
 
489
503
  if (!block) {
490
504
  return;
491
505
  }
492
506
 
507
+ /**
508
+ * For multi-block selection, still emit 'block-hovered' event so toolbar can follow the hovered block.
509
+ * The toolbar module will handle the logic of whether to move or not.
510
+ */
511
+ if (blockHoveredState.lastHoveredBlockId === block.id) {
512
+ return;
513
+ }
514
+
515
+ blockHoveredState.lastHoveredBlockId = block.id;
516
+
493
517
  this.eventsDispatcher.emit(BlockHovered, {
494
518
  block,
495
519
  target: event.target as Element,
@@ -502,13 +526,54 @@ export class UI extends Module<UINodes> {
502
526
  20
503
527
  );
504
528
 
505
- this.readOnlyMutableListeners.on(this.nodes.redactor, 'mousemove', (event: Event) => {
529
+ /**
530
+ * Listen on document to detect hover in the extended zone
531
+ * which is outside the wrapper's bounds.
532
+ * We filter events to only process those over the editor or in the hover zone.
533
+ */
534
+ this.readOnlyMutableListeners.on(document, 'mousemove', (event: Event) => {
506
535
  throttledHandleBlockHovered(event);
507
536
  }, {
508
537
  passive: true,
509
538
  });
510
539
  }
511
540
 
541
+ /**
542
+ * Finds a block by vertical position when cursor is in the extended hover zone.
543
+ * The zone extends HOVER_ZONE_SIZE pixels from the content edge (left for LTR, right for RTL).
544
+ * @param clientX - Cursor X position
545
+ * @param clientY - Cursor Y position
546
+ * @returns Block at the vertical position, or null if not in hover zone or no block found
547
+ */
548
+ private findBlockInHoverZone(clientX: number, clientY: number): Block | null {
549
+ const contentRect = this.contentRect;
550
+
551
+ /**
552
+ * For LTR: check if cursor is within hover zone to the left of content
553
+ * For RTL: check if cursor is within hover zone to the right of content
554
+ */
555
+ const isInHoverZone = this.isRtl
556
+ ? clientX > contentRect.right && clientX <= contentRect.right + HOVER_ZONE_SIZE
557
+ : clientX < contentRect.left && clientX >= contentRect.left - HOVER_ZONE_SIZE;
558
+
559
+ if (!isInHoverZone) {
560
+ return null;
561
+ }
562
+
563
+ /**
564
+ * Find block by Y position
565
+ */
566
+ for (const block of this.Blok.BlockManager.blocks) {
567
+ const rect = block.holder.getBoundingClientRect();
568
+
569
+ if (clientY >= rect.top && clientY <= rect.bottom) {
570
+ return block;
571
+ }
572
+ }
573
+
574
+ return null;
575
+ }
576
+
512
577
  /**
513
578
  * Unbind events that should work only in read-only mode
514
579
  */
@@ -53,11 +53,6 @@ export class SelectionUtils {
53
53
  */
54
54
  public isFakeBackgroundEnabled = false;
55
55
 
56
- /**
57
- * The contenteditable element that had the selection when fake background was enabled
58
- * Used to restore focus and selection when fake background is removed
59
- */
60
- private selectionContainer: HTMLElement | null = null;
61
56
 
62
57
  /**
63
58
  * Returns selected anchor
@@ -401,7 +396,6 @@ export class SelectionUtils {
401
396
  this.removeHighlightSpans();
402
397
 
403
398
  this.isFakeBackgroundEnabled = false;
404
- this.selectionContainer = null;
405
399
  }
406
400
 
407
401
  /**
@@ -454,7 +448,6 @@ export class SelectionUtils {
454
448
  public clearFakeBackground(): void {
455
449
  this.removeOrphanedFakeBackgroundElements();
456
450
  this.isFakeBackgroundEnabled = false;
457
- this.selectionContainer = null;
458
451
  }
459
452
 
460
453
  /**
@@ -478,12 +471,9 @@ export class SelectionUtils {
478
471
  }
479
472
 
480
473
  // Find the contenteditable container that holds the selection
481
- const container = range.commonAncestorContainer;
482
- const element = container.nodeType === Node.ELEMENT_NODE
483
- ? container as HTMLElement
484
- : container.parentElement;
474
+
475
+
485
476
 
486
- this.selectionContainer = element?.closest('[contenteditable="true"]') as HTMLElement | null;
487
477
 
488
478
  // Collect text nodes and wrap them with highlight spans
489
479
  const textNodes = this.collectTextNodes(range);
@@ -7,6 +7,19 @@ import type { InlineToolAdapter as InlineToolAdapterInterface } from '@/types/to
7
7
  import type { BlockToolAdapter as BlockToolAdapterInterface } from '@/types/tools/adapters/block-tool-adapter';
8
8
  import type { BlockTuneAdapter as BlockTuneAdapterInterface } from '@/types/tools/adapters/block-tune-adapter';
9
9
 
10
+ /**
11
+ * Keys that are Blok-level settings (not passed to tool constructor)
12
+ */
13
+ const BLOK_SETTINGS_KEYS = new Set([
14
+ 'class',
15
+ 'inlineToolbar',
16
+ 'tunes',
17
+ 'shortcut',
18
+ 'toolbox',
19
+ 'config',
20
+ 'isInternal',
21
+ ]);
22
+
10
23
  /**
11
24
  * Enum of Tool options provided by user
12
25
  */
@@ -188,10 +201,24 @@ export abstract class BaseToolAdapter<Type extends ToolType = ToolType, ToolClas
188
201
  }
189
202
 
190
203
  /**
191
- * Returns Tool user configuration
204
+ * Returns Tool user configuration.
205
+ * Extracts tool-specific options from flat config and merges with nested config.
192
206
  */
193
207
  public get settings(): ToolConfig {
194
- const config = (this.config[UserSettings.Config] ?? {}) as ToolConfig;
208
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- Internal: reading legacy config for backwards compatibility
209
+ const nestedConfig = (this.config[UserSettings.Config] ?? {}) as ToolConfig;
210
+
211
+ // Extract non-Blok keys as tool-specific config
212
+ const flatConfig: Record<string, unknown> = {};
213
+
214
+ for (const key of Object.keys(this.config)) {
215
+ if (!BLOK_SETTINGS_KEYS.has(key)) {
216
+ flatConfig[key] = this.config[key as keyof typeof this.config];
217
+ }
218
+ }
219
+
220
+ // Merge: nested config first, flat config overrides
221
+ const config = { ...nestedConfig, ...flatConfig } as ToolConfig;
195
222
 
196
223
  if (this.isDefault && !('placeholder' in config) && this.defaultPlaceholder) {
197
224
  config.placeholder = this.defaultPlaceholder;
@@ -51,13 +51,22 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
51
51
  * @param readOnly - True if Blok is in read-only mode
52
52
  */
53
53
  public create(data: BlockToolData, block: BlockAPI, readOnly: boolean): IBlockTool {
54
+ const toolboxEntries = this.toolbox;
55
+
56
+ /**
57
+ * Inject merged toolbox entries into config so tools can use them in renderSettings().
58
+ * This allows tools like Header to show the same options in block settings as in the toolbox.
59
+ */
60
+ const configWithToolbox = toolboxEntries !== undefined
61
+ ? { ...this.settings, _toolboxEntries: toolboxEntries }
62
+ : this.settings;
54
63
 
55
64
  return new this.constructable({
56
65
  data,
57
66
  block,
58
67
  readOnly,
59
68
  api: this.api,
60
- config: this.settings,
69
+ config: configWithToolbox,
61
70
  }) as IBlockTool;
62
71
  }
63
72
 
@@ -194,10 +203,18 @@ export class BlockToolAdapter extends BaseToolAdapter<ToolType.Block, IBlockTool
194
203
  }
195
204
 
196
205
  /**
197
- * Returns enabled inline tools for Tool
206
+ * Returns enabled inline tools for Tool.
207
+ * Defaults to true (all inline tools) unless explicitly set to false or array.
198
208
  */
199
209
  public get enabledInlineTools(): boolean | string[] {
200
- return this.config[UserSettings.EnabledInlineTools] || false;
210
+ const setting = this.config[UserSettings.EnabledInlineTools];
211
+
212
+ // Default to true if not specified
213
+ if (setting === undefined) {
214
+ return true;
215
+ }
216
+
217
+ return setting;
201
218
  }
202
219
 
203
220
  /**