@jackuait/blok 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/README.md +136 -17
  2. package/codemod/README.md +16 -0
  3. package/codemod/migrate-editorjs-to-blok.js +859 -92
  4. package/codemod/test.js +682 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-BjgH1REI.mjs +12838 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-DfiUa_gd.mjs +43 -0
  9. package/dist/{index-CBkApZKo.mjs → chunks/index-5m5JWNey.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-Bx5BVd8I.mjs +1988 -0
  11. package/dist/chunks/messages-2434tVOK.mjs +47 -0
  12. package/dist/chunks/messages-3DcCwXMF.mjs +47 -0
  13. package/dist/chunks/messages-4kMwVAKY.mjs +47 -0
  14. package/dist/chunks/messages-57uL5htT.mjs +47 -0
  15. package/dist/chunks/messages-76-iJV9Q.mjs +47 -0
  16. package/dist/chunks/messages-8p86Eyf2.mjs +47 -0
  17. package/dist/chunks/messages-BBX0p0Pi.mjs +47 -0
  18. package/dist/chunks/messages-BCm2eudQ.mjs +47 -0
  19. package/dist/chunks/messages-BFiUomgG.mjs +47 -0
  20. package/dist/chunks/messages-BIPNHHAV.mjs +47 -0
  21. package/dist/chunks/messages-BUlwu9mo.mjs +47 -0
  22. package/dist/chunks/messages-BX-DPa-z.mjs +47 -0
  23. package/dist/chunks/messages-BextV3Qh.mjs +47 -0
  24. package/dist/chunks/messages-BiPSFlUG.mjs +47 -0
  25. package/dist/chunks/messages-BiXe9G-O.mjs +47 -0
  26. package/dist/chunks/messages-Bl5z_Igo.mjs +47 -0
  27. package/dist/chunks/messages-BnsE97ku.mjs +47 -0
  28. package/dist/chunks/messages-BoO8gsVD.mjs +47 -0
  29. package/dist/chunks/messages-BqWaOGMn.mjs +47 -0
  30. package/dist/chunks/messages-BqkL2_Ro.mjs +47 -0
  31. package/dist/chunks/messages-BvCkXKX-.mjs +47 -0
  32. package/dist/chunks/messages-C6tbPLoj.mjs +47 -0
  33. package/dist/chunks/messages-CA6T3-gQ.mjs +47 -0
  34. package/dist/chunks/messages-CFFPFdWP.mjs +47 -0
  35. package/dist/chunks/messages-CFrKE-TN.mjs +47 -0
  36. package/dist/chunks/messages-CHz8VlG-.mjs +47 -0
  37. package/dist/chunks/messages-CLixzySl.mjs +47 -0
  38. package/dist/chunks/messages-CV7OM_qk.mjs +47 -0
  39. package/dist/chunks/messages-CXHt3eCC.mjs +47 -0
  40. package/dist/chunks/messages-CbmsBrB0.mjs +47 -0
  41. package/dist/chunks/messages-Ceo1KtFx.mjs +47 -0
  42. package/dist/chunks/messages-Cm0LJLtB.mjs +47 -0
  43. package/dist/chunks/messages-CmymP_Ar.mjs +47 -0
  44. package/dist/chunks/messages-D0ohMB5H.mjs +47 -0
  45. package/dist/chunks/messages-D3GrDwXh.mjs +47 -0
  46. package/dist/chunks/messages-D3vTzIpL.mjs +47 -0
  47. package/dist/chunks/messages-D5WeksbV.mjs +47 -0
  48. package/dist/chunks/messages-DGaab4EP.mjs +47 -0
  49. package/dist/chunks/messages-DKha57ZU.mjs +47 -0
  50. package/dist/chunks/messages-DOaujgMW.mjs +47 -0
  51. package/dist/chunks/messages-DVbPLd_0.mjs +47 -0
  52. package/dist/chunks/messages-D_FCyfW6.mjs +47 -0
  53. package/dist/chunks/messages-Dd5iZN3c.mjs +47 -0
  54. package/dist/chunks/messages-DehM7135.mjs +47 -0
  55. package/dist/chunks/messages-Dg1OHftD.mjs +47 -0
  56. package/dist/chunks/messages-Di6Flq-b.mjs +47 -0
  57. package/dist/chunks/messages-Dqhhex6e.mjs +47 -0
  58. package/dist/chunks/messages-DueVe0F1.mjs +47 -0
  59. package/dist/chunks/messages-Dx3eFwI0.mjs +47 -0
  60. package/dist/chunks/messages-FOtiUoKl.mjs +47 -0
  61. package/dist/chunks/messages-FTOZNhRD.mjs +47 -0
  62. package/dist/chunks/messages-IQxGfQIV.mjs +47 -0
  63. package/dist/chunks/messages-JF2fzCkK.mjs +47 -0
  64. package/dist/chunks/messages-MOGl7I5v.mjs +47 -0
  65. package/dist/chunks/messages-QgYhPL-3.mjs +47 -0
  66. package/dist/chunks/messages-WYWIbQwo.mjs +47 -0
  67. package/dist/chunks/messages-a6A_LgDv.mjs +47 -0
  68. package/dist/chunks/messages-bSf31LJi.mjs +47 -0
  69. package/dist/chunks/messages-diGozhTn.mjs +47 -0
  70. package/dist/chunks/messages-er-kd-VO.mjs +47 -0
  71. package/dist/chunks/messages-ez3w5NBn.mjs +47 -0
  72. package/dist/chunks/messages-f3uXjegd.mjs +47 -0
  73. package/dist/chunks/messages-ohwI1UGv.mjs +47 -0
  74. package/dist/chunks/messages-p9BZJaFV.mjs +47 -0
  75. package/dist/chunks/messages-qIQ4L4rw.mjs +47 -0
  76. package/dist/chunks/messages-qWkXPggi.mjs +47 -0
  77. package/dist/chunks/messages-w5foGze_.mjs +47 -0
  78. package/dist/full.mjs +50 -0
  79. package/dist/locales.mjs +227 -0
  80. package/dist/messages-2434tVOK.mjs +47 -0
  81. package/dist/messages-3DcCwXMF.mjs +47 -0
  82. package/dist/messages-4kMwVAKY.mjs +47 -0
  83. package/dist/messages-57uL5htT.mjs +47 -0
  84. package/dist/messages-76-iJV9Q.mjs +47 -0
  85. package/dist/messages-8p86Eyf2.mjs +47 -0
  86. package/dist/messages-BBX0p0Pi.mjs +47 -0
  87. package/dist/messages-BCm2eudQ.mjs +47 -0
  88. package/dist/messages-BFiUomgG.mjs +47 -0
  89. package/dist/messages-BIPNHHAV.mjs +47 -0
  90. package/dist/messages-BUlwu9mo.mjs +47 -0
  91. package/dist/messages-BX-DPa-z.mjs +47 -0
  92. package/dist/messages-BextV3Qh.mjs +47 -0
  93. package/dist/messages-BiPSFlUG.mjs +47 -0
  94. package/dist/messages-BiXe9G-O.mjs +47 -0
  95. package/dist/messages-Bl5z_Igo.mjs +47 -0
  96. package/dist/messages-BnsE97ku.mjs +47 -0
  97. package/dist/messages-BoO8gsVD.mjs +47 -0
  98. package/dist/messages-BqWaOGMn.mjs +47 -0
  99. package/dist/messages-BqkL2_Ro.mjs +47 -0
  100. package/dist/messages-BvCkXKX-.mjs +47 -0
  101. package/dist/messages-C6tbPLoj.mjs +47 -0
  102. package/dist/messages-CA6T3-gQ.mjs +47 -0
  103. package/dist/messages-CFFPFdWP.mjs +47 -0
  104. package/dist/messages-CFrKE-TN.mjs +47 -0
  105. package/dist/messages-CHz8VlG-.mjs +47 -0
  106. package/dist/messages-CLixzySl.mjs +47 -0
  107. package/dist/messages-CV7OM_qk.mjs +47 -0
  108. package/dist/messages-CXHt3eCC.mjs +47 -0
  109. package/dist/messages-CbmsBrB0.mjs +47 -0
  110. package/dist/messages-Ceo1KtFx.mjs +47 -0
  111. package/dist/messages-Cm0LJLtB.mjs +47 -0
  112. package/dist/messages-CmymP_Ar.mjs +47 -0
  113. package/dist/messages-D0ohMB5H.mjs +47 -0
  114. package/dist/messages-D3GrDwXh.mjs +47 -0
  115. package/dist/messages-D3vTzIpL.mjs +47 -0
  116. package/dist/messages-D5WeksbV.mjs +47 -0
  117. package/dist/messages-DGaab4EP.mjs +47 -0
  118. package/dist/messages-DKha57ZU.mjs +47 -0
  119. package/dist/messages-DOaujgMW.mjs +47 -0
  120. package/dist/messages-DVbPLd_0.mjs +47 -0
  121. package/dist/messages-D_FCyfW6.mjs +47 -0
  122. package/dist/messages-Dd5iZN3c.mjs +47 -0
  123. package/dist/messages-DehM7135.mjs +47 -0
  124. package/dist/messages-Dg1OHftD.mjs +47 -0
  125. package/dist/messages-Di6Flq-b.mjs +47 -0
  126. package/dist/messages-Dqhhex6e.mjs +47 -0
  127. package/dist/messages-DueVe0F1.mjs +47 -0
  128. package/dist/messages-Dx3eFwI0.mjs +47 -0
  129. package/dist/messages-FOtiUoKl.mjs +47 -0
  130. package/dist/messages-FTOZNhRD.mjs +47 -0
  131. package/dist/messages-IQxGfQIV.mjs +47 -0
  132. package/dist/messages-JF2fzCkK.mjs +47 -0
  133. package/dist/messages-MOGl7I5v.mjs +47 -0
  134. package/dist/messages-QgYhPL-3.mjs +47 -0
  135. package/dist/messages-WYWIbQwo.mjs +47 -0
  136. package/dist/messages-a6A_LgDv.mjs +47 -0
  137. package/dist/messages-bSf31LJi.mjs +47 -0
  138. package/dist/messages-diGozhTn.mjs +47 -0
  139. package/dist/messages-er-kd-VO.mjs +47 -0
  140. package/dist/messages-ez3w5NBn.mjs +47 -0
  141. package/dist/messages-f3uXjegd.mjs +47 -0
  142. package/dist/messages-ohwI1UGv.mjs +47 -0
  143. package/dist/messages-p9BZJaFV.mjs +47 -0
  144. package/dist/messages-qIQ4L4rw.mjs +47 -0
  145. package/dist/messages-qWkXPggi.mjs +47 -0
  146. package/dist/messages-w5foGze_.mjs +47 -0
  147. package/dist/tools.mjs +3073 -0
  148. package/dist/vendor.LICENSE.txt +59 -156
  149. package/package.json +47 -15
  150. package/src/blok.ts +267 -0
  151. package/src/components/__module.ts +139 -0
  152. package/src/components/block/api.ts +155 -0
  153. package/src/components/block/index.ts +1427 -0
  154. package/src/components/block-tunes/block-tune-delete.ts +51 -0
  155. package/src/components/blocks.ts +338 -0
  156. package/src/components/constants/data-attributes.ts +342 -0
  157. package/src/components/constants.ts +76 -0
  158. package/src/components/core.ts +392 -0
  159. package/src/components/dom.ts +773 -0
  160. package/src/components/domIterator.ts +189 -0
  161. package/src/components/errors/critical.ts +5 -0
  162. package/src/components/events/BlockChanged.ts +16 -0
  163. package/src/components/events/BlockHovered.ts +21 -0
  164. package/src/components/events/BlockSettingsClosed.ts +12 -0
  165. package/src/components/events/BlockSettingsOpened.ts +12 -0
  166. package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
  167. package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
  168. package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
  169. package/src/components/events/HistoryStateChanged.ts +19 -0
  170. package/src/components/events/RedactorDomChanged.ts +14 -0
  171. package/src/components/events/index.ts +46 -0
  172. package/src/components/flipper.ts +481 -0
  173. package/src/components/i18n/i18next-loader.ts +84 -0
  174. package/src/components/i18n/lightweight-i18n.ts +86 -0
  175. package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
  176. package/src/components/i18n/locales/am/messages.json +44 -0
  177. package/src/components/i18n/locales/ar/messages.json +44 -0
  178. package/src/components/i18n/locales/az/messages.json +44 -0
  179. package/src/components/i18n/locales/bg/messages.json +44 -0
  180. package/src/components/i18n/locales/bn/messages.json +44 -0
  181. package/src/components/i18n/locales/bs/messages.json +44 -0
  182. package/src/components/i18n/locales/cs/messages.json +44 -0
  183. package/src/components/i18n/locales/da/messages.json +44 -0
  184. package/src/components/i18n/locales/de/messages.json +44 -0
  185. package/src/components/i18n/locales/dv/messages.json +44 -0
  186. package/src/components/i18n/locales/el/messages.json +44 -0
  187. package/src/components/i18n/locales/en/messages.json +44 -0
  188. package/src/components/i18n/locales/es/messages.json +44 -0
  189. package/src/components/i18n/locales/et/messages.json +44 -0
  190. package/src/components/i18n/locales/fa/messages.json +44 -0
  191. package/src/components/i18n/locales/fi/messages.json +44 -0
  192. package/src/components/i18n/locales/fil/messages.json +44 -0
  193. package/src/components/i18n/locales/fr/messages.json +44 -0
  194. package/src/components/i18n/locales/gu/messages.json +44 -0
  195. package/src/components/i18n/locales/he/messages.json +44 -0
  196. package/src/components/i18n/locales/hi/messages.json +44 -0
  197. package/src/components/i18n/locales/hr/messages.json +44 -0
  198. package/src/components/i18n/locales/hu/messages.json +44 -0
  199. package/src/components/i18n/locales/hy/messages.json +44 -0
  200. package/src/components/i18n/locales/id/messages.json +44 -0
  201. package/src/components/i18n/locales/index.ts +225 -0
  202. package/src/components/i18n/locales/it/messages.json +44 -0
  203. package/src/components/i18n/locales/ja/messages.json +44 -0
  204. package/src/components/i18n/locales/ka/messages.json +44 -0
  205. package/src/components/i18n/locales/km/messages.json +44 -0
  206. package/src/components/i18n/locales/kn/messages.json +44 -0
  207. package/src/components/i18n/locales/ko/messages.json +44 -0
  208. package/src/components/i18n/locales/ku/messages.json +44 -0
  209. package/src/components/i18n/locales/lo/messages.json +44 -0
  210. package/src/components/i18n/locales/lt/messages.json +44 -0
  211. package/src/components/i18n/locales/lv/messages.json +44 -0
  212. package/src/components/i18n/locales/mk/messages.json +44 -0
  213. package/src/components/i18n/locales/ml/messages.json +44 -0
  214. package/src/components/i18n/locales/mn/messages.json +44 -0
  215. package/src/components/i18n/locales/mr/messages.json +44 -0
  216. package/src/components/i18n/locales/ms/messages.json +44 -0
  217. package/src/components/i18n/locales/my/messages.json +44 -0
  218. package/src/components/i18n/locales/ne/messages.json +44 -0
  219. package/src/components/i18n/locales/nl/messages.json +44 -0
  220. package/src/components/i18n/locales/no/messages.json +44 -0
  221. package/src/components/i18n/locales/pa/messages.json +44 -0
  222. package/src/components/i18n/locales/pl/messages.json +44 -0
  223. package/src/components/i18n/locales/ps/messages.json +44 -0
  224. package/src/components/i18n/locales/pt/messages.json +44 -0
  225. package/src/components/i18n/locales/ro/messages.json +44 -0
  226. package/src/components/i18n/locales/ru/messages.json +44 -0
  227. package/src/components/i18n/locales/sd/messages.json +44 -0
  228. package/src/components/i18n/locales/si/messages.json +44 -0
  229. package/src/components/i18n/locales/sk/messages.json +44 -0
  230. package/src/components/i18n/locales/sl/messages.json +44 -0
  231. package/src/components/i18n/locales/sq/messages.json +44 -0
  232. package/src/components/i18n/locales/sr/messages.json +44 -0
  233. package/src/components/i18n/locales/sv/messages.json +44 -0
  234. package/src/components/i18n/locales/sw/messages.json +44 -0
  235. package/src/components/i18n/locales/ta/messages.json +44 -0
  236. package/src/components/i18n/locales/te/messages.json +44 -0
  237. package/src/components/i18n/locales/th/messages.json +44 -0
  238. package/src/components/i18n/locales/tr/messages.json +44 -0
  239. package/src/components/i18n/locales/ug/messages.json +44 -0
  240. package/src/components/i18n/locales/uk/messages.json +44 -0
  241. package/src/components/i18n/locales/ur/messages.json +44 -0
  242. package/src/components/i18n/locales/vi/messages.json +44 -0
  243. package/src/components/i18n/locales/yi/messages.json +44 -0
  244. package/src/components/i18n/locales/zh/messages.json +44 -0
  245. package/src/components/icons/index.ts +242 -0
  246. package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
  247. package/src/components/inline-tools/inline-tool-convert.ts +141 -0
  248. package/src/components/inline-tools/inline-tool-italic.ts +500 -0
  249. package/src/components/inline-tools/inline-tool-link.ts +539 -0
  250. package/src/components/modules/api/blocks.ts +363 -0
  251. package/src/components/modules/api/caret.ts +125 -0
  252. package/src/components/modules/api/events.ts +51 -0
  253. package/src/components/modules/api/history.ts +73 -0
  254. package/src/components/modules/api/i18n.ts +33 -0
  255. package/src/components/modules/api/index.ts +39 -0
  256. package/src/components/modules/api/inlineToolbar.ts +33 -0
  257. package/src/components/modules/api/listeners.ts +56 -0
  258. package/src/components/modules/api/notifier.ts +46 -0
  259. package/src/components/modules/api/readonly.ts +39 -0
  260. package/src/components/modules/api/sanitizer.ts +30 -0
  261. package/src/components/modules/api/saver.ts +52 -0
  262. package/src/components/modules/api/selection.ts +48 -0
  263. package/src/components/modules/api/styles.ts +72 -0
  264. package/src/components/modules/api/toolbar.ts +79 -0
  265. package/src/components/modules/api/tools.ts +16 -0
  266. package/src/components/modules/api/tooltip.ts +67 -0
  267. package/src/components/modules/api/ui.ts +36 -0
  268. package/src/components/modules/blockEvents.ts +1375 -0
  269. package/src/components/modules/blockManager.ts +1348 -0
  270. package/src/components/modules/blockSelection.ts +708 -0
  271. package/src/components/modules/caret.ts +853 -0
  272. package/src/components/modules/crossBlockSelection.ts +329 -0
  273. package/src/components/modules/dragManager.ts +1141 -0
  274. package/src/components/modules/history.ts +1098 -0
  275. package/src/components/modules/i18n.ts +325 -0
  276. package/src/components/modules/index.ts +139 -0
  277. package/src/components/modules/modificationsObserver.ts +147 -0
  278. package/src/components/modules/paste.ts +1092 -0
  279. package/src/components/modules/readonly.ts +136 -0
  280. package/src/components/modules/rectangleSelection.ts +668 -0
  281. package/src/components/modules/renderer.ts +155 -0
  282. package/src/components/modules/saver.ts +283 -0
  283. package/src/components/modules/toolbar/blockSettings.ts +776 -0
  284. package/src/components/modules/toolbar/index.ts +1311 -0
  285. package/src/components/modules/toolbar/inline.ts +956 -0
  286. package/src/components/modules/tools.ts +589 -0
  287. package/src/components/modules/ui.ts +1179 -0
  288. package/src/components/polyfills.ts +113 -0
  289. package/src/components/selection.ts +1189 -0
  290. package/src/components/tools/base.ts +274 -0
  291. package/src/components/tools/block.ts +291 -0
  292. package/src/components/tools/collection.ts +67 -0
  293. package/src/components/tools/factory.ts +85 -0
  294. package/src/components/tools/inline.ts +71 -0
  295. package/src/components/tools/tune.ts +33 -0
  296. package/src/components/ui/toolbox.ts +497 -0
  297. package/src/components/utils/announcer.ts +205 -0
  298. package/src/components/utils/api.ts +20 -0
  299. package/src/components/utils/bem.ts +26 -0
  300. package/src/components/utils/blocks.ts +284 -0
  301. package/src/components/utils/caret.ts +1067 -0
  302. package/src/components/utils/data-model-transform.ts +382 -0
  303. package/src/components/utils/events.ts +117 -0
  304. package/src/components/utils/keyboard.ts +60 -0
  305. package/src/components/utils/listeners.ts +296 -0
  306. package/src/components/utils/mutations.ts +39 -0
  307. package/src/components/utils/notifier/draw.ts +190 -0
  308. package/src/components/utils/notifier/index.ts +66 -0
  309. package/src/components/utils/notifier/types.ts +1 -0
  310. package/src/components/utils/notifier.ts +77 -0
  311. package/src/components/utils/placeholder.ts +140 -0
  312. package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
  313. package/src/components/utils/popover/components/hint/hint.ts +46 -0
  314. package/src/components/utils/popover/components/hint/index.ts +6 -0
  315. package/src/components/utils/popover/components/popover-header/index.ts +2 -0
  316. package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
  317. package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
  318. package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
  319. package/src/components/utils/popover/components/popover-item/index.ts +13 -0
  320. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
  321. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +666 -0
  322. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
  323. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
  324. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
  325. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
  326. package/src/components/utils/popover/components/popover-item/popover-item.ts +187 -0
  327. package/src/components/utils/popover/components/search-input/index.ts +2 -0
  328. package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
  329. package/src/components/utils/popover/components/search-input/search-input.ts +181 -0
  330. package/src/components/utils/popover/components/search-input/search-input.types.ts +30 -0
  331. package/src/components/utils/popover/index.ts +13 -0
  332. package/src/components/utils/popover/popover-abstract.ts +448 -0
  333. package/src/components/utils/popover/popover-desktop.ts +643 -0
  334. package/src/components/utils/popover/popover-inline.ts +338 -0
  335. package/src/components/utils/popover/popover-mobile.ts +201 -0
  336. package/src/components/utils/popover/popover.const.ts +81 -0
  337. package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
  338. package/src/components/utils/promise-queue.ts +43 -0
  339. package/src/components/utils/sanitizer.ts +537 -0
  340. package/src/components/utils/scroll-locker.ts +87 -0
  341. package/src/components/utils/shortcut.ts +231 -0
  342. package/src/components/utils/shortcuts.ts +113 -0
  343. package/src/components/utils/tools.ts +105 -0
  344. package/src/components/utils/tooltip.ts +642 -0
  345. package/src/components/utils/tw.ts +241 -0
  346. package/src/components/utils.ts +1081 -0
  347. package/src/env.d.ts +13 -0
  348. package/src/full.ts +69 -0
  349. package/src/locales.ts +51 -0
  350. package/src/stories/Block.stories.ts +498 -0
  351. package/src/stories/EditorModes.stories.ts +505 -0
  352. package/src/stories/Header.stories.ts +137 -0
  353. package/src/stories/InlineToolbar.stories.ts +498 -0
  354. package/src/stories/List.stories.ts +259 -0
  355. package/src/stories/Notifier.stories.ts +340 -0
  356. package/src/stories/Paragraph.stories.ts +112 -0
  357. package/src/stories/Placeholder.stories.ts +319 -0
  358. package/src/stories/Popover.stories.ts +844 -0
  359. package/src/stories/Selection.stories.ts +250 -0
  360. package/src/stories/StubBlock.stories.ts +156 -0
  361. package/src/stories/Toolbar.stories.ts +223 -0
  362. package/src/stories/Toolbox.stories.ts +166 -0
  363. package/src/stories/Tooltip.stories.ts +198 -0
  364. package/src/stories/helpers.ts +463 -0
  365. package/src/styles/main.css +123 -0
  366. package/src/tools/header/index.ts +570 -0
  367. package/src/tools/index.ts +38 -0
  368. package/src/tools/list/index.ts +1803 -0
  369. package/src/tools/paragraph/index.ts +411 -0
  370. package/src/tools/stub/index.ts +107 -0
  371. package/src/types-internal/blok-modules.d.ts +87 -0
  372. package/src/types-internal/html-janitor.d.ts +28 -0
  373. package/src/types-internal/module-config.d.ts +11 -0
  374. package/src/variants/all-locales.ts +155 -0
  375. package/src/variants/blok-maximum.ts +20 -0
  376. package/src/variants/blok-minimum.ts +243 -0
  377. package/types/api/blocks.d.ts +1 -1
  378. package/types/api/i18n.d.ts +5 -3
  379. package/types/api/selection.d.ts +6 -0
  380. package/types/api/styles.d.ts +0 -5
  381. package/types/configs/blok-config.d.ts +21 -0
  382. package/types/configs/i18n-config.d.ts +52 -2
  383. package/types/configs/i18n-dictionary.d.ts +16 -90
  384. package/types/data-attributes.d.ts +169 -0
  385. package/types/data-formats/output-data.d.ts +15 -0
  386. package/types/full.d.ts +80 -0
  387. package/types/index.d.ts +9 -24
  388. package/types/locales.d.ts +59 -0
  389. package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
  390. package/types/tools/block-tool.d.ts +9 -0
  391. package/types/tools/list.d.ts +25 -18
  392. package/types/tools/tool-settings.d.ts +8 -1
  393. package/types/tools/tool.d.ts +6 -0
  394. package/types/tools-entry.d.ts +49 -0
  395. package/types/utils/popover/popover-item.d.ts +0 -5
  396. package/dist/blok-BwPfU8ro.mjs +0 -21510
  397. package/dist/blok.umd.js +0 -198
@@ -29,6 +29,363 @@ const path = require('path');
29
29
 
30
30
  const FILE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html', '.css', '.scss', '.less'];
31
31
 
32
+ // ============================================================================
33
+ // i18n Transformation Utilities
34
+ // ============================================================================
35
+
36
+ /**
37
+ * EditorJS to Blok key mappings for keys that changed.
38
+ * These are applied after flattening to convert EditorJS keys to Blok equivalents.
39
+ *
40
+ * EditorJS keys reference: https://editorjs.io/i18n/
41
+ * Blok keys reference: src/components/i18n/locales/en/messages.json
42
+ */
43
+ const I18N_KEY_MAPPINGS = {
44
+ // UI keys that changed
45
+ 'ui.blockTunes.toggler.Click to tune': 'blockSettings.clickToOpenMenu',
46
+ 'ui.blockTunes.toggler.or drag to move': 'blockSettings.dragToMove',
47
+ 'ui.toolbar.toolbox.Add': 'toolbox.addBelow',
48
+ 'ui.inlineToolbar.converter.Convert to': 'popover.convertTo',
49
+ 'ui.popover.Filter': 'popover.search',
50
+
51
+ // Tool names that changed (EditorJS uses different casing/wording)
52
+ 'toolNames.Ordered List': 'toolNames.numberedList',
53
+ 'toolNames.Unordered List': 'toolNames.bulletedList',
54
+
55
+ // Tools messages that changed
56
+ 'tools.stub.The block can not be displayed correctly': 'tools.stub.blockCannotBeDisplayed',
57
+ 'tools.stub.The block can not be displayed correctly.': 'tools.stub.blockCannotBeDisplayed',
58
+
59
+ // Block tunes that changed in Blok
60
+ 'blockTunes.delete.Delete': 'blockSettings.delete',
61
+
62
+ // Block tunes that are removed in Blok (moveUp/moveDown replaced with drag)
63
+ // These are mapped to null to indicate they should be removed
64
+ 'blockTunes.moveUp.Move up': null,
65
+ 'blockTunes.moveDown.Move down': null,
66
+ };
67
+
68
+ /**
69
+ * Namespace mappings from old verbose prefixes to new simplified prefixes.
70
+ * Applied after camelCase normalization.
71
+ */
72
+ const NAMESPACE_MAPPINGS = {
73
+ 'ui.blockTunes.toggler': 'blockSettings',
74
+ 'ui.toolbar.toolbox': 'toolbox',
75
+ 'ui.popover': 'popover',
76
+ 'blockTunes': 'blockSettings',
77
+ };
78
+
79
+ /**
80
+ * Converts an EditorJS-style key (with English text) to Blok-style (camelCase).
81
+ * Blok uses camelCase for the final segment of translation keys.
82
+ * Also applies namespace simplification for known verbose prefixes.
83
+ * Example: 'ui.popover.Nothing found' → 'popover.nothingFound'
84
+ * Example: 'toolNames.Text' → 'toolNames.text'
85
+ * @param {string} key - The dot-notation key with English text
86
+ * @returns {string} The normalized key with camelCase final segment
87
+ */
88
+ function normalizeKey(key) {
89
+ const parts = key.split('.');
90
+ const lastPart = parts[parts.length - 1];
91
+
92
+ // Convert "Nothing found" → "nothingFound", "Text" → "text"
93
+ const words = lastPart.split(/\s+/);
94
+ const camelCase = words
95
+ .map((word, i) => {
96
+ if (i === 0) {
97
+ // First word is lowercase
98
+ return word.toLowerCase();
99
+ }
100
+ // Subsequent words have first letter capitalized
101
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
102
+ })
103
+ .join('');
104
+
105
+ parts[parts.length - 1] = camelCase;
106
+ let normalizedKey = parts.join('.');
107
+
108
+ // Apply namespace mappings (longest prefix first for correct matching)
109
+ const sortedPrefixes = Object.keys(NAMESPACE_MAPPINGS).sort((a, b) => b.length - a.length);
110
+ for (const oldPrefix of sortedPrefixes) {
111
+ if (normalizedKey.startsWith(oldPrefix + '.')) {
112
+ normalizedKey = NAMESPACE_MAPPINGS[oldPrefix] + normalizedKey.slice(oldPrefix.length);
113
+ break;
114
+ }
115
+ }
116
+
117
+ return normalizedKey;
118
+ }
119
+
120
+ /**
121
+ * Flattens a nested i18n dictionary object to Blok's flat dot-notation format.
122
+ * @param {Object} obj - The nested dictionary object
123
+ * @param {string} prefix - Current key prefix (for recursion)
124
+ * @returns {Object} Flattened dictionary with dot-notation keys
125
+ */
126
+ function flattenI18nDictionary(obj, prefix = '') {
127
+ const result = {};
128
+
129
+ for (const [key, value] of Object.entries(obj)) {
130
+ const newKey = prefix ? `${prefix}.${key}` : key;
131
+
132
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
133
+ Object.assign(result, flattenI18nDictionary(value, newKey));
134
+ } else {
135
+ // Check explicit mappings first, otherwise normalize to camelCase
136
+ let finalKey;
137
+ if (newKey in I18N_KEY_MAPPINGS) {
138
+ finalKey = I18N_KEY_MAPPINGS[newKey];
139
+ } else {
140
+ finalKey = normalizeKey(newKey);
141
+ }
142
+
143
+ // Skip keys that are mapped to null (removed in Blok)
144
+ if (finalKey === null) {
145
+ continue;
146
+ }
147
+
148
+ result[finalKey] = value;
149
+ }
150
+ }
151
+
152
+ return result;
153
+ }
154
+
155
+ /**
156
+ * Parses a JavaScript object literal string into an actual object.
157
+ * Handles simple object literals with string values.
158
+ * @param {string} str - The object literal string
159
+ * @returns {Object|null} Parsed object or null if parsing fails
160
+ */
161
+ function parseObjectLiteral(str) {
162
+ try {
163
+ // Convert single quotes to double quotes for JSON compatibility
164
+ // Handle unquoted keys by quoting them
165
+ let jsonStr = str
166
+ // Replace single quotes with double quotes
167
+ .replace(/'/g, '"')
168
+ // Handle unquoted keys (identifier followed by colon)
169
+ .replace(/(\s*)(\w+)(\s*:\s*)/g, '$1"$2"$3')
170
+ // Remove trailing commas before closing braces/brackets
171
+ .replace(/,(\s*[}\]])/g, '$1');
172
+
173
+ return JSON.parse(jsonStr);
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Converts a flattened dictionary object to a formatted JavaScript object string.
181
+ * @param {Object} obj - The flattened dictionary
182
+ * @param {string} indent - The indentation string
183
+ * @returns {string} Formatted object string
184
+ */
185
+ function objectToString(obj, indent = ' ') {
186
+ const entries = Object.entries(obj);
187
+ if (entries.length === 0) {
188
+ return '{}';
189
+ }
190
+
191
+ const lines = entries.map(([key, value]) => {
192
+ const escapedValue = typeof value === 'string' ? value.replace(/"/g, '\\"') : value;
193
+ return `${indent}"${key}": "${escapedValue}"`;
194
+ });
195
+
196
+ return '{\n' + lines.join(',\n') + '\n' + indent.slice(2) + '}';
197
+ }
198
+
199
+ /**
200
+ * Finds matching brace for nested object parsing.
201
+ * @param {string} str - The string to search
202
+ * @param {number} startIndex - Starting position (should be at opening brace)
203
+ * @returns {number} Index of matching closing brace, or -1 if not found
204
+ */
205
+ function findMatchingBrace(str, startIndex) {
206
+ let depth = 0;
207
+ let inString = false;
208
+ let stringChar = '';
209
+
210
+ for (let i = startIndex; i < str.length; i++) {
211
+ const char = str[i];
212
+ const prevChar = i > 0 ? str[i - 1] : '';
213
+
214
+ // Handle string detection
215
+ if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
216
+ if (!inString) {
217
+ inString = true;
218
+ stringChar = char;
219
+ } else if (char === stringChar) {
220
+ inString = false;
221
+ }
222
+ continue;
223
+ }
224
+
225
+ if (inString) continue;
226
+
227
+ if (char === '{') {
228
+ depth++;
229
+ } else if (char === '}') {
230
+ depth--;
231
+ if (depth === 0) {
232
+ return i;
233
+ }
234
+ }
235
+ }
236
+
237
+ return -1;
238
+ }
239
+
240
+ /**
241
+ * Transforms EditorJS nested i18n messages to Blok flat format.
242
+ * @param {string} content - The file content
243
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
244
+ */
245
+ function transformI18nConfig(content) {
246
+ // Pattern to find i18n: { messages: { ... } } or i18n: { messages: { ... }, ... }
247
+ const i18nStartPattern = /i18n\s*:\s*\{\s*messages\s*:\s*\{/g;
248
+ let match;
249
+ let result = content;
250
+ let changed = false;
251
+ let offset = 0;
252
+
253
+ // Reset lastIndex for the regex
254
+ i18nStartPattern.lastIndex = 0;
255
+
256
+ while ((match = i18nStartPattern.exec(content)) !== null) {
257
+ const messagesStart = match.index + match[0].length - 1; // Position of opening brace of messages
258
+ const messagesEnd = findMatchingBrace(content, messagesStart);
259
+
260
+ if (messagesEnd === -1) continue;
261
+
262
+ const messagesStr = content.substring(messagesStart, messagesEnd + 1);
263
+
264
+ // Skip if it contains functions or arrow functions (dynamic content)
265
+ if (messagesStr.includes('function') || messagesStr.includes('=>')) {
266
+ continue;
267
+ }
268
+
269
+ // Try to parse the messages object
270
+ const messagesObj = parseObjectLiteral(messagesStr);
271
+ if (!messagesObj) continue;
272
+
273
+ // Flatten the dictionary
274
+ const flattened = flattenI18nDictionary(messagesObj);
275
+
276
+ // Detect indentation from the original content
277
+ const lineStart = content.lastIndexOf('\n', match.index) + 1;
278
+ const lineContent = content.substring(lineStart, match.index);
279
+ const baseIndent = lineContent.match(/^\s*/)?.[0] || '';
280
+ const messagesIndent = baseIndent + ' ';
281
+
282
+ // Convert to string with proper formatting
283
+ const flattenedStr = objectToString(flattened, messagesIndent + ' ');
284
+
285
+ // Replace in result (accounting for previous replacements)
286
+ const adjustedStart = messagesStart + offset;
287
+ const adjustedEnd = messagesEnd + 1 + offset;
288
+ result = result.substring(0, adjustedStart) + flattenedStr + result.substring(adjustedEnd);
289
+
290
+ // Update offset for next iteration
291
+ offset += flattenedStr.length - (messagesEnd + 1 - messagesStart);
292
+ changed = true;
293
+ }
294
+
295
+ return { result, changed };
296
+ }
297
+
298
+ /**
299
+ * Removes i18n messages from config to use Blok's built-in library translations.
300
+ * Preserves other i18n properties like locale and direction.
301
+ * @param {string} content - The file content
302
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
303
+ */
304
+ function removeI18nMessages(content) {
305
+ // Pattern to find i18n config with messages property
306
+ const i18nStartPattern = /i18n\s*:\s*\{/g;
307
+ let match;
308
+ let result = content;
309
+ let changed = false;
310
+ let offset = 0;
311
+
312
+ // Reset lastIndex for the regex
313
+ i18nStartPattern.lastIndex = 0;
314
+
315
+ while ((match = i18nStartPattern.exec(content)) !== null) {
316
+ const i18nStart = match.index + match[0].length - 1; // Position of opening brace
317
+ const i18nEnd = findMatchingBrace(content, i18nStart);
318
+
319
+ if (i18nEnd === -1) continue;
320
+
321
+ const i18nContent = content.substring(i18nStart, i18nEnd + 1);
322
+
323
+ // Check if this i18n config has a messages property
324
+ if (!i18nContent.includes('messages')) {
325
+ continue;
326
+ }
327
+
328
+ // Find the messages property and remove it
329
+ // Handle: messages: { ... }, or messages: { ... } (with or without trailing comma)
330
+ const messagesPattern = /,?\s*messages\s*:\s*\{/;
331
+ const messagesMatch = i18nContent.match(messagesPattern);
332
+
333
+ if (!messagesMatch) continue;
334
+
335
+ const messagesStartInContent = i18nContent.indexOf(messagesMatch[0]);
336
+ const messagesObjStart = i18nContent.indexOf('{', messagesStartInContent);
337
+ const messagesObjEnd = findMatchingBrace(i18nContent, messagesObjStart);
338
+
339
+ if (messagesObjEnd === -1) continue;
340
+
341
+ // Determine what to remove (including trailing comma if present)
342
+ let removeStart = messagesStartInContent;
343
+ let removeEnd = messagesObjEnd + 1;
344
+
345
+ // Check for trailing comma after messages object
346
+ const afterMessages = i18nContent.substring(removeEnd);
347
+ const trailingCommaMatch = afterMessages.match(/^\s*,/);
348
+ if (trailingCommaMatch) {
349
+ removeEnd += trailingCommaMatch[0].length;
350
+ }
351
+
352
+ // If messages started with a comma (not first property), include it in removal
353
+ // Otherwise, we need to handle the case where it's the first property
354
+ const beforeMessages = i18nContent.substring(0, messagesStartInContent);
355
+ if (!messagesMatch[0].startsWith(',') && beforeMessages.trim() === '{') {
356
+ // messages is first property, remove trailing comma if any
357
+ }
358
+
359
+ // Build the new i18n content without messages
360
+ let newI18nContent = i18nContent.substring(0, removeStart) + i18nContent.substring(removeEnd);
361
+
362
+ // Clean up: remove leading comma if messages was removed and left one
363
+ newI18nContent = newI18nContent.replace(/\{\s*,/, '{');
364
+
365
+ // Clean up: remove trailing comma before closing brace
366
+ newI18nContent = newI18nContent.replace(/,\s*\}/, ' }');
367
+
368
+ // Clean up empty i18n config: { } -> remove or simplify
369
+ const isEmptyI18n = newI18nContent.replace(/\s/g, '') === '{}';
370
+
371
+ if (isEmptyI18n) {
372
+ // Replace with empty object or keep minimal
373
+ newI18nContent = '{}';
374
+ }
375
+
376
+ // Replace in result (accounting for previous replacements)
377
+ const adjustedI18nStart = i18nStart + offset;
378
+ const adjustedI18nEnd = i18nEnd + 1 + offset;
379
+ result = result.substring(0, adjustedI18nStart) + newI18nContent + result.substring(adjustedI18nEnd);
380
+
381
+ // Update offset for next iteration
382
+ offset += newI18nContent.length - (i18nEnd + 1 - i18nStart);
383
+ changed = true;
384
+ }
385
+
386
+ return { result, changed };
387
+ }
388
+
32
389
  // Import transformations
33
390
  const IMPORT_TRANSFORMS = [
34
391
  // EditorJS subpath imports (e.g., @editorjs/editorjs/types -> @jackuait/blok/types)
@@ -40,26 +397,155 @@ const IMPORT_TRANSFORMS = [
40
397
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs\/([^'"]+)['"]\s*\)/g,
41
398
  replacement: "require('@jackuait/blok/$1')",
42
399
  },
43
- // Main EditorJS import
400
+ // Combined default + named imports: import EditorJS, { EditorConfig } from '@editorjs/editorjs'
401
+ // → import { Blok, EditorConfig } from '@jackuait/blok' (EditorConfig → BlokConfig handled by TYPE_TRANSFORMS)
402
+ // IMPORTANT: Must come before regular default import patterns to avoid partial matching
403
+ {
404
+ pattern: /import\s+EditorJS\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
405
+ replacement: "import { Blok, $1 } from '@jackuait/blok'",
406
+ note: 'Converted combined default + named import',
407
+ },
408
+ // Aliased combined: import Editor, { EditorConfig } from '@editorjs/editorjs'
409
+ {
410
+ pattern: /import\s+(\w+)\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
411
+ replacement: "import { Blok as $1, $2 } from '@jackuait/blok'",
412
+ note: 'Converted aliased combined default + named import',
413
+ },
414
+ // Main EditorJS default import -> Blok named import
415
+ // e.g., import EditorJS from '@editorjs/editorjs' -> import { Blok } from '@jackuait/blok'
44
416
  {
45
- pattern: /from\s+['"]@editorjs\/editorjs['"]/g,
46
- replacement: "from '@jackuait/blok'",
417
+ pattern: /import\s+EditorJS\s+from\s+['"]@editorjs\/editorjs['"]/g,
418
+ replacement: "import { Blok } from '@jackuait/blok'",
419
+ note: 'Converted default import to named import',
420
+ },
421
+ // EditorJS default import with alias -> Blok named import with alias
422
+ // e.g., import Editor from '@editorjs/editorjs' -> import { Blok as Editor } from '@jackuait/blok'
423
+ {
424
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
425
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
426
+ note: 'Converted default import to named import with alias',
427
+ },
428
+ // Destructured require with default: const { default: EditorJS } = require(...)
429
+ // IMPORTANT: Must come before generic require pattern to avoid partial matching
430
+ {
431
+ pattern: /const\s+\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
432
+ replacement: "const { Blok: $1 } = require('@jackuait/blok')",
433
+ note: 'Converted destructured default require to named require',
47
434
  },
48
435
  {
49
436
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
50
- replacement: "require('@jackuait/blok')",
437
+ replacement: "require('@jackuait/blok').Blok",
438
+ },
439
+ // Namespace import: import * as EditorJS from '@editorjs/editorjs'
440
+ // → convert to named import (namespace pattern no longer needed with named exports)
441
+ {
442
+ pattern: /import\s+\*\s+as\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
443
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
444
+ note: 'Converted namespace import to named import (namespace no longer needed)',
445
+ },
446
+ // Dynamic import with .then chaining that expects default (must come before generic dynamic import)
447
+ {
448
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)\s*\.then\s*\(\s*(\w+)\s*=>\s*\1\.default\s*\)/g,
449
+ replacement: "import('@jackuait/blok').then($1 => $1.Blok)",
450
+ note: 'Converted dynamic import .then(m => m.default) pattern',
451
+ },
452
+ // Dynamic import with destructuring: const { default: Editor } = await import('@editorjs/editorjs')
453
+ // IMPORTANT: Must come before generic dynamic import to avoid partial matching
454
+ {
455
+ pattern: /\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*await\s+import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
456
+ replacement: "{ Blok: $1 } = await import('@jackuait/blok')",
457
+ note: 'Converted destructured dynamic import',
458
+ },
459
+ // Dynamic import: import('@editorjs/editorjs')
460
+ {
461
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
462
+ replacement: "import('@jackuait/blok').then(m => ({ default: m.Blok }))",
463
+ note: 'Converted dynamic import (wraps named export as default for compatibility)',
464
+ },
465
+ // Type-only default import: import type EditorJS from '@editorjs/editorjs'
466
+ {
467
+ pattern: /import\s+type\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
468
+ replacement: "import type { Blok as $1 } from '@jackuait/blok'",
469
+ note: 'Converted type-only default import to named import',
470
+ },
471
+ // Re-export default as named: export { default as Editor } from '@editorjs/editorjs'
472
+ {
473
+ pattern: /export\s+\{\s*default\s+as\s+(\w+)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
474
+ replacement: "export { Blok as $1 } from '@jackuait/blok'",
475
+ note: 'Converted default re-export to named export',
476
+ },
477
+ // Re-export default: export { default } from '@editorjs/editorjs'
478
+ {
479
+ pattern: /export\s+\{\s*default\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
480
+ replacement: "export { Blok } from '@jackuait/blok'",
481
+ note: 'Converted default re-export',
482
+ },
483
+ // Header tool (now bundled) - import directly from @jackuait/blok
484
+ {
485
+ pattern: /import\s+Header\s+from\s+['"]@editorjs\/header['"];?\n?/g,
486
+ replacement: "import { Header } from '@jackuait/blok';\n",
487
+ note: 'Header tool is now bundled with Blok',
51
488
  },
52
- // Header tool (now bundled)
53
489
  {
54
490
  pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/header['"];?\n?/g,
55
- replacement: '// Header is now bundled with Blok: use Blok.Header\n',
56
- note: 'Header tool is now bundled',
491
+ replacement: "import { Header as $1 } from '@jackuait/blok';\n",
492
+ note: 'Header tool is now bundled with Blok (aliased)',
493
+ },
494
+ // Paragraph tool (now bundled) - import directly from @jackuait/blok
495
+ {
496
+ pattern: /import\s+Paragraph\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
497
+ replacement: "import { Paragraph } from '@jackuait/blok';\n",
498
+ note: 'Paragraph tool is now bundled with Blok',
57
499
  },
58
- // Paragraph tool (now bundled)
59
500
  {
60
501
  pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
61
- replacement: '// Paragraph is now bundled with Blok: use Blok.Paragraph\n',
62
- note: 'Paragraph tool is now bundled',
502
+ replacement: "import { Paragraph as $1 } from '@jackuait/blok';\n",
503
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
504
+ },
505
+ // List tool (now bundled) - import directly from @jackuait/blok
506
+ {
507
+ pattern: /import\s+List\s+from\s+['"]@editorjs\/list['"];?\n?/g,
508
+ replacement: "import { List } from '@jackuait/blok';\n",
509
+ note: 'List tool is now bundled with Blok',
510
+ },
511
+ {
512
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/list['"];?\n?/g,
513
+ replacement: "import { List as $1 } from '@jackuait/blok';\n",
514
+ note: 'List tool is now bundled with Blok (aliased)',
515
+ },
516
+ // Tool require statements (CommonJS)
517
+ // Header require: const Header = require('@editorjs/header')
518
+ {
519
+ pattern: /const\s+Header\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
520
+ replacement: "const { Header } = require('@jackuait/blok')",
521
+ note: 'Header tool is now bundled with Blok',
522
+ },
523
+ {
524
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
525
+ replacement: "const { Header: $1 } = require('@jackuait/blok')",
526
+ note: 'Header tool is now bundled with Blok (aliased)',
527
+ },
528
+ // Paragraph require
529
+ {
530
+ pattern: /const\s+Paragraph\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
531
+ replacement: "const { Paragraph } = require('@jackuait/blok')",
532
+ note: 'Paragraph tool is now bundled with Blok',
533
+ },
534
+ {
535
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
536
+ replacement: "const { Paragraph: $1 } = require('@jackuait/blok')",
537
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
538
+ },
539
+ // List require
540
+ {
541
+ pattern: /const\s+List\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
542
+ replacement: "const { List } = require('@jackuait/blok')",
543
+ note: 'List tool is now bundled with Blok',
544
+ },
545
+ {
546
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
547
+ replacement: "const { List: $1 } = require('@jackuait/blok')",
548
+ note: 'List tool is now bundled with Blok (aliased)',
63
549
  },
64
550
  ];
65
551
 
@@ -131,13 +617,13 @@ const CSS_CLASS_TRANSFORMS = [
131
617
  { pattern: /(['"`])ce-inline-toolbar(['"`])/g, replacement: '$1data-blok-testid="inline-toolbar"$2' },
132
618
 
133
619
  // Popover classes (ce-popover)
134
- { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover][data-blok-opened="true"]' },
620
+ { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover-opened="true"]' },
135
621
  { pattern: /\.ce-popover__container(?![\w-])/g, replacement: '[data-blok-popover-container]' },
136
622
  { pattern: /\.ce-popover-item--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
137
623
  { pattern: /\.ce-popover-item(?![\w-])/g, replacement: '[data-blok-testid="popover-item"]' },
138
624
  { pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-popover]' },
139
625
  // Without dot prefix
140
- { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover$2' },
626
+ { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover-opened$2' },
141
627
  { pattern: /(['"`])ce-popover__container(['"`])/g, replacement: '$1data-blok-popover-container$2' },
142
628
  { pattern: /(['"`])ce-popover-item--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
143
629
  { pattern: /(['"`])ce-popover-item(['"`])/g, replacement: '$1data-blok-testid="popover-item"$2' },
@@ -175,17 +661,17 @@ const CSS_CLASS_TRANSFORMS = [
175
661
  { pattern: /(['"`])ce-ragged-right(['"`])/g, replacement: '$1data-blok-ragged-right$2' },
176
662
 
177
663
  // Popover item states and icons
178
- { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-confirmation="true"]' },
664
+ { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-popover-item-confirmation="true"]' },
179
665
  { pattern: /\.ce-popover-item__icon(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon"]' },
180
666
  { pattern: /\.ce-popover-item__icon--tool(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon-tool"]' },
181
- { pattern: /(['"`])ce-popover-item--confirmation(['"`])/g, replacement: '$1data-blok-confirmation$2' },
667
+ { pattern: /(['"`])ce-popover-item--confirmation(['"`])/g, replacement: '$1data-blok-popover-item-confirmation$2' },
182
668
  { pattern: /(['"`])ce-popover-item__icon(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon"$2' },
183
669
  { pattern: /(['"`])ce-popover-item__icon--tool(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon-tool"$2' },
184
670
 
185
671
  // Toolbox classes (ce-toolbox)
186
- { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox][data-blok-opened="true"]' },
672
+ { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox-opened="true"]' },
187
673
  { pattern: /\.ce-toolbox(?![\w-])/g, replacement: '[data-blok-toolbox]' },
188
- { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox$2' },
674
+ { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox-opened$2' },
189
675
  { pattern: /(['"`])ce-toolbox(['"`])/g, replacement: '$1data-blok-toolbox$2' },
190
676
 
191
677
  // CDX list classes (cdx-list)
@@ -249,19 +735,141 @@ const HOLDER_TRANSFORMS = [
249
735
  { pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
250
736
  ];
251
737
 
252
- // Bundled tools - add new tools here as they are bundled with Blok
253
- const BUNDLED_TOOLS = ['Header', 'Paragraph'];
738
+ // Bundled tools - these are now in a separate entry point (@jackuait/blok/tools)
739
+ const BUNDLED_TOOLS = ['Header', 'Paragraph', 'List'];
740
+
741
+ // Inline tools - also in the tools entry point
742
+ const INLINE_TOOLS = ['Bold', 'Italic', 'Link'];
743
+
744
+ // Internal tools - these are bundled with core and don't need to be imported
745
+ const INTERNAL_TOOLS = ['Convert', 'Delete'];
746
+
747
+ // All tools that should be imported from @jackuait/blok/tools
748
+ const ALL_TOOLS = [...BUNDLED_TOOLS, ...INLINE_TOOLS];
254
749
 
255
750
  // Tool configuration transformations
751
+ // Note: With named exports, tools are imported directly (e.g., { Header } from '@jackuait/blok/tools')
752
+ // so the tool class references don't need the Blok. prefix anymore
256
753
  const TOOL_CONFIG_TRANSFORMS = [
257
- // Handle class property syntax
258
- { pattern: /class:\s*Header(?!Config)/g, replacement: 'class: Blok.Header' },
259
- { pattern: /class:\s*Paragraph(?!Config)/g, replacement: 'class: Blok.Paragraph' },
260
- // Handle standalone tool references in tools config (e.g., `paragraph: Paragraph`)
261
- { pattern: /(\bheader\s*:\s*)Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Header' },
262
- { pattern: /(\bparagraph\s*:\s*)Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Paragraph' },
754
+ // Convert old Blok.Header style to direct Header reference (for existing Blok users upgrading)
755
+ // Block tools
756
+ { pattern: /class:\s*Blok\.Header(?!Config)/g, replacement: 'class: Header' },
757
+ { pattern: /class:\s*Blok\.Paragraph(?!Config)/g, replacement: 'class: Paragraph' },
758
+ { pattern: /class:\s*Blok\.List(?!Config|Item)/g, replacement: 'class: List' },
759
+ { pattern: /(\bheader\s*:\s*)Blok\.Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Header' },
760
+ { pattern: /(\bparagraph\s*:\s*)Blok\.Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Paragraph' },
761
+ { pattern: /(\blist\s*:\s*)Blok\.List(?!Config|Item)(?=\s*[,}\n])/g, replacement: '$1List' },
762
+ // Inline tools
763
+ { pattern: /class:\s*Blok\.Bold(?!Config)/g, replacement: 'class: Bold' },
764
+ { pattern: /class:\s*Blok\.Italic(?!Config)/g, replacement: 'class: Italic' },
765
+ { pattern: /class:\s*Blok\.Link(?!Config)/g, replacement: 'class: Link' },
766
+ { pattern: /(\bbold\s*:\s*)Blok\.Bold(?!Config)(?=\s*[,}\n])/g, replacement: '$1Bold' },
767
+ { pattern: /(\bitalic\s*:\s*)Blok\.Italic(?!Config)(?=\s*[,}\n])/g, replacement: '$1Italic' },
768
+ { pattern: /(\blink\s*:\s*)Blok\.Link(?!Config)(?=\s*[,}\n])/g, replacement: '$1Link' },
769
+ ];
770
+
771
+ // Transforms to remove internal tools configuration (Convert and Delete are now bundled)
772
+ const INTERNAL_TOOL_REMOVAL_TRANSFORMS = [
773
+ // Remove convertTo/convert configuration lines (with various formats)
774
+ { pattern: /,?\s*convert(?:To)?\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Convert\s*\}\s*,?/g, replacement: '' },
775
+ { pattern: /,?\s*convert(?:To)?\s*:\s*(?:Blok\.)?Convert\s*,?/g, replacement: '' },
776
+ // Remove delete configuration lines
777
+ { pattern: /,?\s*delete\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*\}\s*,?/g, replacement: '' },
778
+ { pattern: /,?\s*delete\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*,?/g, replacement: '' },
779
+ ];
780
+
781
+ // ============================================================================
782
+ // Blok Modular Import Transformations (Strategy 5)
783
+ // ============================================================================
784
+
785
+ /**
786
+ * Transforms for migrating old Blok imports to the new modular structure.
787
+ * After Strategy 3/4, tools are in a separate entry point (@jackuait/blok/tools).
788
+ *
789
+ * Detects and transforms patterns like:
790
+ * import { Blok, Header, Paragraph } from '@jackuait/blok'
791
+ * To:
792
+ * import { Blok } from '@jackuait/blok';
793
+ * import { Header, Paragraph } from '@jackuait/blok/tools';
794
+ */
795
+ const BLOK_MODULAR_IMPORT_TRANSFORMS = [
796
+ // Transform tool imports from old @jackuait/blok to @jackuait/blok/tools
797
+ // These patterns match tools being imported from the main entry point
798
+ // The actual splitting is done in splitBlokImports() function below
263
799
  ];
264
800
 
801
+ /**
802
+ * Splits a combined @jackuait/blok import that contains both core exports and tools
803
+ * into separate imports from the correct entry points.
804
+ *
805
+ * @param {string} content - The file content
806
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
807
+ */
808
+ function splitBlokImports(content) {
809
+ // Pattern to find @jackuait/blok named imports
810
+ const blokImportPattern = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]@jackuait\/blok['"];?/g;
811
+ let result = content;
812
+ let changed = false;
813
+
814
+ // Collect all matches first to avoid regex state issues
815
+ const matches = [];
816
+ let match;
817
+ while ((match = blokImportPattern.exec(content)) !== null) {
818
+ matches.push({
819
+ fullMatch: match[0],
820
+ imports: match[1],
821
+ index: match.index,
822
+ });
823
+ }
824
+
825
+ // Process matches in reverse order to preserve indices
826
+ for (let i = matches.length - 1; i >= 0; i--) {
827
+ const { fullMatch, imports } = matches[i];
828
+
829
+ // Parse the imports
830
+ const importList = imports.split(',').map(s => s.trim()).filter(s => s.length > 0);
831
+
832
+ // Separate core imports from tool imports
833
+ const coreImports = [];
834
+ const toolImports = [];
835
+
836
+ for (const imp of importList) {
837
+ // Handle aliased imports: "Header as MyHeader"
838
+ const importName = imp.split(/\s+as\s+/)[0].trim();
839
+
840
+ if (ALL_TOOLS.includes(importName)) {
841
+ toolImports.push(imp);
842
+ } else {
843
+ coreImports.push(imp);
844
+ }
845
+ }
846
+
847
+ // Only transform if there are tool imports mixed with core imports
848
+ // or if there are only tool imports (they should come from /tools)
849
+ if (toolImports.length > 0) {
850
+ let newImports = '';
851
+
852
+ if (coreImports.length > 0) {
853
+ newImports += `import { ${coreImports.join(', ')} } from '@jackuait/blok';\n`;
854
+ }
855
+
856
+ if (toolImports.length > 0) {
857
+ newImports += `import { ${toolImports.join(', ')} } from '@jackuait/blok/tools';`;
858
+ }
859
+
860
+ // Preserve trailing newline if original had one
861
+ if (!fullMatch.endsWith(';')) {
862
+ newImports = newImports.replace(/;$/, '');
863
+ }
864
+
865
+ result = result.replace(fullMatch, newImports);
866
+ changed = true;
867
+ }
868
+ }
869
+
870
+ return { result, changed };
871
+ }
872
+
265
873
  // Text transformations for "EditorJS" string references
266
874
  const TEXT_TRANSFORMS = [
267
875
  // Replace exact "EditorJS" text (preserves case-sensitive matching)
@@ -330,108 +938,185 @@ function applyTransforms(content, transforms, fileName) {
330
938
  }
331
939
 
332
940
  /**
333
- * Ensures that Blok is properly imported when bundled tools (Blok.Header, Blok.Paragraph, etc.) are used.
334
- * This function checks if the content uses any Blok.* tool references and ensures there's a proper import.
941
+ * Ensures that bundled tools (Header, Paragraph, List) are properly imported when used.
942
+ * Since Blok now uses named exports, tools are imported directly.
943
+ *
944
+ * @deprecated Use ensureToolsImport instead, which imports from @jackuait/blok/tools
335
945
  *
336
946
  * Handles the following scenarios:
337
- * 1. No existing @jackuait/blok import -> adds `import Blok from '@jackuait/blok'`
338
- * 2. Named imports only (e.g., `import { BlokConfig } from '@jackuait/blok'`) -> adds Blok default import
339
- * 3. Default import with different name -> adds Blok to named imports
340
- * 4. Already has Blok default import -> no changes needed
947
+ * 1. Tool used without import -> adds `import { Header, Paragraph, List } from '@jackuait/blok'`
948
+ * 2. Existing @jackuait/blok import without tools -> adds tools to named imports
341
949
  */
342
950
  function ensureBlokImport(content) {
343
- // Check if content uses any Blok.* tool (e.g., Blok.Header, Blok.Paragraph)
344
- const blokToolPattern = new RegExp(`Blok\\.(${BUNDLED_TOOLS.join('|')})`, 'g');
345
- const usesBlokTools = blokToolPattern.test(content);
951
+ // Check which bundled tools are used in the content
952
+ const usedTools = BUNDLED_TOOLS.filter(tool => {
953
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
954
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
955
+ return toolPattern.test(content);
956
+ });
957
+
958
+ if (usedTools.length === 0) {
959
+ return { result: content, changed: false };
960
+ }
961
+
962
+ // Check for existing @jackuait/blok import
963
+ const namedImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
964
+ const namedMatch = content.match(namedImportPattern);
965
+
966
+ let result = content;
967
+
968
+ if (namedMatch) {
969
+ // Check which tools are missing from the import
970
+ const existingImports = namedMatch[1];
971
+ const missingTools = usedTools.filter(tool => {
972
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
973
+ return !toolPattern.test(existingImports);
974
+ });
346
975
 
347
- if (!usesBlokTools) {
976
+ if (missingTools.length > 0) {
977
+ // Add missing tools to existing import
978
+ const newImports = `${missingTools.join(', ')}, ${existingImports.trim()}`;
979
+ result = content.replace(
980
+ namedImportPattern,
981
+ `import { ${newImports} } from '@jackuait/blok';`
982
+ );
983
+ return { result, changed: true };
984
+ }
348
985
  return { result: content, changed: false };
349
986
  }
350
987
 
351
- // Check if Blok is already available as a default import
352
- // Matches: import Blok from '@jackuait/blok' or import Blok, { ... } from '@jackuait/blok'
353
- const hasBlokDefaultImport = /import\s+Blok\s*(?:,\s*\{[^}]*\}\s*)?from\s*['"]@jackuait\/blok['"]/.test(content);
988
+ // No @jackuait/blok import at all -> add new import
989
+ const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
990
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok';`;
991
+
992
+ if (importStatements && importStatements.length > 0) {
993
+ const lastImport = importStatements[importStatements.length - 1];
994
+ const lastImportIndex = content.lastIndexOf(lastImport);
995
+ const insertPosition = lastImportIndex + lastImport.length;
996
+ result =
997
+ content.slice(0, insertPosition) +
998
+ '\n' + newImport +
999
+ content.slice(insertPosition);
1000
+ } else {
1001
+ // No imports found, add at the very beginning (after shebang if present)
1002
+ const shebangMatch = content.match(/^#!.*\n/);
1003
+ if (shebangMatch) {
1004
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
1005
+ } else {
1006
+ result = newImport + '\n' + content;
1007
+ }
1008
+ }
1009
+
1010
+ return { result, changed: true };
1011
+ }
1012
+
1013
+ /**
1014
+ * Ensures that tools are properly imported from @jackuait/blok/tools when used.
1015
+ * This is the modern approach after Strategy 3/4 where tools are in a separate entry point.
1016
+ *
1017
+ * Handles the following scenarios:
1018
+ * 1. Tool used without import -> adds `import { Header, ... } from '@jackuait/blok/tools'`
1019
+ * 2. Existing @jackuait/blok/tools import without tools -> adds tools to named imports
1020
+ */
1021
+ function ensureToolsImport(content) {
1022
+ // Check which tools are used in the content
1023
+ const usedTools = ALL_TOOLS.filter(tool => {
1024
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
1025
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
1026
+ return toolPattern.test(content);
1027
+ });
354
1028
 
355
- if (hasBlokDefaultImport) {
1029
+ if (usedTools.length === 0) {
356
1030
  return { result: content, changed: false };
357
1031
  }
358
1032
 
359
- // Check for existing @jackuait/blok import patterns
360
- const namedOnlyImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
361
- const defaultWithNamedPattern = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
362
- const defaultOnlyPattern = /import\s+(\w+)\s+from\s*['"]@jackuait\/blok['"];?/;
1033
+ // Check for existing @jackuait/blok/tools import
1034
+ const toolsImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok\/tools['"];?/;
1035
+ const toolsMatch = content.match(toolsImportPattern);
1036
+
1037
+ // Also check for @jackuait/blok import (legacy, will be split later)
1038
+ const mainImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
1039
+ const mainMatch = content.match(mainImportPattern);
363
1040
 
364
1041
  let result = content;
365
1042
 
366
- // Case 1: Named imports only -> add Blok default import
367
- // e.g., `import { BlokConfig } from '@jackuait/blok'` -> `import Blok, { BlokConfig } from '@jackuait/blok'`
368
- const namedOnlyMatch = content.match(namedOnlyImportPattern);
369
- if (namedOnlyMatch) {
370
- const namedImports = namedOnlyMatch[1];
371
- result = content.replace(
372
- namedOnlyImportPattern,
373
- `import Blok, {${namedImports}} from '@jackuait/blok';`
374
- );
375
- return { result, changed: true };
376
- }
377
-
378
- // Case 2: Default import with different name + named imports -> add Blok to named imports
379
- // e.g., `import Editor, { BlokConfig } from '@jackuait/blok'` -> `import Editor, { Blok, BlokConfig } from '@jackuait/blok'`
380
- const defaultWithNamedMatch = content.match(defaultWithNamedPattern);
381
- if (defaultWithNamedMatch) {
382
- const defaultName = defaultWithNamedMatch[1];
383
- const namedImports = defaultWithNamedMatch[2];
384
- // Check if Blok is already in named imports
385
- if (!/\bBlok\b/.test(namedImports)) {
1043
+ // Check if tools are already imported from /tools
1044
+ if (toolsMatch) {
1045
+ const existingToolsImports = toolsMatch[1];
1046
+ const missingTools = usedTools.filter(tool => {
1047
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1048
+ return !toolPattern.test(existingToolsImports);
1049
+ });
1050
+
1051
+ if (missingTools.length > 0) {
1052
+ // Add missing tools to existing /tools import
1053
+ const newImports = `${existingToolsImports.trim()}, ${missingTools.join(', ')}`;
386
1054
  result = content.replace(
387
- defaultWithNamedPattern,
388
- `import ${defaultName}, { Blok, ${namedImports.trim()} } from '@jackuait/blok';`
1055
+ toolsImportPattern,
1056
+ `import { ${newImports} } from '@jackuait/blok/tools';`
389
1057
  );
390
1058
  return { result, changed: true };
391
1059
  }
392
1060
  return { result: content, changed: false };
393
1061
  }
394
1062
 
395
- // Case 3: Default import only with different name -> add Blok to named imports
396
- // e.g., `import Editor from '@jackuait/blok'` -> `import Editor, { Blok } from '@jackuait/blok'`
397
- const defaultOnlyMatch = content.match(defaultOnlyPattern);
398
- if (defaultOnlyMatch) {
399
- const defaultName = defaultOnlyMatch[1];
400
- if (defaultName !== 'Blok') {
1063
+ // Check if tools are in the main import (will be split later by splitBlokImports)
1064
+ if (mainMatch) {
1065
+ const existingMainImports = mainMatch[1];
1066
+ const toolsInMain = usedTools.filter(tool => {
1067
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1068
+ return toolPattern.test(existingMainImports);
1069
+ });
1070
+
1071
+ // If all tools are already in main import, don't add duplicate
1072
+ // They'll be moved by splitBlokImports
1073
+ if (toolsInMain.length === usedTools.length) {
1074
+ return { result: content, changed: false };
1075
+ }
1076
+
1077
+ // Some tools missing from both imports - add them to main (will be split later)
1078
+ const missingTools = usedTools.filter(tool => {
1079
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1080
+ return !toolPattern.test(existingMainImports);
1081
+ });
1082
+
1083
+ if (missingTools.length > 0) {
1084
+ const newImports = `${missingTools.join(', ')}, ${existingMainImports.trim()}`;
401
1085
  result = content.replace(
402
- defaultOnlyPattern,
403
- `import ${defaultName}, { Blok } from '@jackuait/blok';`
1086
+ mainImportPattern,
1087
+ `import { ${newImports} } from '@jackuait/blok';`
404
1088
  );
405
1089
  return { result, changed: true };
406
1090
  }
407
1091
  return { result: content, changed: false };
408
1092
  }
409
1093
 
410
- // Case 4: No @jackuait/blok import at all -> add new import at the top (after any existing imports)
411
- // Find the last import statement to insert after it
1094
+ // No @jackuait/blok or @jackuait/blok/tools import -> add new /tools import
412
1095
  const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1096
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok/tools';`;
1097
+
413
1098
  if (importStatements && importStatements.length > 0) {
414
1099
  const lastImport = importStatements[importStatements.length - 1];
415
1100
  const lastImportIndex = content.lastIndexOf(lastImport);
416
1101
  const insertPosition = lastImportIndex + lastImport.length;
417
1102
  result =
418
1103
  content.slice(0, insertPosition) +
419
- "\nimport Blok from '@jackuait/blok';" +
1104
+ '\n' + newImport +
420
1105
  content.slice(insertPosition);
421
1106
  } else {
422
1107
  // No imports found, add at the very beginning (after shebang if present)
423
1108
  const shebangMatch = content.match(/^#!.*\n/);
424
1109
  if (shebangMatch) {
425
- result = shebangMatch[0] + "import Blok from '@jackuait/blok';\n" + content.slice(shebangMatch[0].length);
1110
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
426
1111
  } else {
427
- result = "import Blok from '@jackuait/blok';\n" + content;
1112
+ result = newImport + '\n' + content;
428
1113
  }
429
1114
  }
430
1115
 
431
1116
  return { result, changed: true };
432
1117
  }
433
1118
 
434
- function transformFile(filePath, dryRun = false) {
1119
+ function transformFile(filePath, dryRun = false, useLibraryI18n = false) {
435
1120
  const content = fs.readFileSync(filePath, 'utf8');
436
1121
  let transformed = content;
437
1122
  const allChanges = [];
@@ -497,12 +1182,28 @@ function transformFile(filePath, dryRun = false) {
497
1182
  allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
498
1183
  }
499
1184
 
500
- // Ensure Blok is imported if bundled tools are used (JS/TS only)
1185
+ // Remove internal tools configuration (Convert and Delete are now bundled) (JS/TS only)
1186
+ if (isJsFile) {
1187
+ const { result, changes } = applyTransforms(transformed, INTERNAL_TOOL_REMOVAL_TRANSFORMS, filePath);
1188
+ transformed = result;
1189
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'internal-tools-removal' })));
1190
+ }
1191
+
1192
+ // Ensure tools are imported if bundled tools are used (JS/TS only)
1193
+ if (isJsFile) {
1194
+ const { result, changed } = ensureToolsImport(transformed);
1195
+ if (changed) {
1196
+ transformed = result;
1197
+ allChanges.push({ category: 'imports', pattern: 'ensureToolsImport', count: 1, note: 'Added tools import from @jackuait/blok/tools' });
1198
+ }
1199
+ }
1200
+
1201
+ // Split combined @jackuait/blok imports into core and tools (JS/TS only)
501
1202
  if (isJsFile) {
502
- const { result, changed } = ensureBlokImport(transformed);
1203
+ const { result, changed } = splitBlokImports(transformed);
503
1204
  if (changed) {
504
1205
  transformed = result;
505
- allChanges.push({ category: 'imports', pattern: 'ensureBlokImport', count: 1, note: 'Added Blok import for bundled tools' });
1206
+ allChanges.push({ category: 'imports', pattern: 'splitBlokImports', count: 1, note: 'Split tools into separate @jackuait/blok/tools import' });
506
1207
  }
507
1208
  }
508
1209
 
@@ -513,6 +1214,25 @@ function transformFile(filePath, dryRun = false) {
513
1214
  allChanges.push(...changes.map((c) => ({ ...c, category: 'text' })));
514
1215
  }
515
1216
 
1217
+ // Apply i18n transforms (JS/TS only)
1218
+ if (isJsFile) {
1219
+ if (useLibraryI18n) {
1220
+ // Remove custom messages to use Blok's built-in translations
1221
+ const { result, changed } = removeI18nMessages(transformed);
1222
+ if (changed) {
1223
+ transformed = result;
1224
+ allChanges.push({ category: 'i18n', pattern: 'removeI18nMessages', count: 1, note: 'Removed custom i18n messages to use library translations' });
1225
+ }
1226
+ } else {
1227
+ // Flatten nested messages to dot-notation
1228
+ const { result, changed } = transformI18nConfig(transformed);
1229
+ if (changed) {
1230
+ transformed = result;
1231
+ allChanges.push({ category: 'i18n', pattern: 'flattenI18nMessages', count: 1, note: 'Flattened nested i18n messages to dot-notation' });
1232
+ }
1233
+ }
1234
+ }
1235
+
516
1236
  const hasChanges = transformed !== content;
517
1237
 
518
1238
  if (hasChanges && !dryRun) {
@@ -536,7 +1256,7 @@ function updatePackageJson(packageJsonPath, dryRun = false) {
536
1256
  const changes = [];
537
1257
 
538
1258
  // Track dependencies to remove
539
- const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph'];
1259
+ const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph', '@editorjs/list'];
540
1260
  const devDepsToRemove = [...depsToRemove];
541
1261
 
542
1262
  // Check and update dependencies
@@ -589,9 +1309,10 @@ Arguments:
589
1309
  path Directory or file to transform (default: current directory)
590
1310
 
591
1311
  Options:
592
- --dry-run Show changes without modifying files
593
- --verbose Show detailed output for each file
594
- --help Show this help message
1312
+ --dry-run Show changes without modifying files
1313
+ --verbose Show detailed output for each file
1314
+ --use-library-i18n Remove custom i18n messages and use Blok's built-in translations
1315
+ --help Show this help message
595
1316
 
596
1317
  Examples:
597
1318
  npx -p @jackuait/blok migrate-from-editorjs ./src
@@ -599,17 +1320,24 @@ Examples:
599
1320
  npx -p @jackuait/blok migrate-from-editorjs . --verbose
600
1321
 
601
1322
  What this codemod does:
602
- • Transforms EditorJS imports to Blok imports
1323
+
1324
+ EditorJS Migration:
1325
+ • Transforms EditorJS imports to Blok named imports:
1326
+ - import EditorJS from '@editorjs/editorjs' → import { Blok } from '@jackuait/blok'
1327
+ - import Editor from '@editorjs/editorjs' → import { Blok as Editor } from '@jackuait/blok'
1328
+ • Updates bundled tool imports (Header, Paragraph, List):
1329
+ - import Header from '@editorjs/header' → import { Header } from '@jackuait/blok/tools'
1330
+ - import List from '@editorjs/list' → import { List } from '@jackuait/blok/tools'
603
1331
  • Updates type names (EditorConfig → BlokConfig)
604
1332
  • Replaces 'new EditorJS()' with 'new Blok()'
605
1333
  • Converts CSS class selectors to data attributes:
606
1334
  - .codex-editor* → [data-blok-editor], [data-blok-redactor], etc.
607
1335
  - .ce-block* → [data-blok-element], [data-blok-selected], etc.
608
1336
  - .ce-toolbar* → [data-blok-toolbar], [data-blok-settings-toggler], etc.
609
- - .ce-toolbox* → [data-blok-toolbox], etc.
1337
+ - .ce-toolbox* → [data-blok-toolbox], [data-blok-toolbox-opened], etc.
610
1338
  - .ce-inline-toolbar, .ce-inline-tool* → [data-blok-testid="inline-*"]
611
- - .ce-popover* → [data-blok-popover], [data-blok-popover-container], etc.
612
- - .ce-popover-item* → [data-blok-testid="popover-item*"], [data-blok-confirmation], etc.
1339
+ - .ce-popover* → [data-blok-popover], [data-blok-popover-opened], [data-blok-popover-container], etc.
1340
+ - .ce-popover-item* → [data-blok-testid="popover-item*"], [data-blok-popover-item-confirmation], etc.
613
1341
  - .ce-paragraph, .ce-header → [data-blok-tool="paragraph|header"]
614
1342
  - .cdx-list* → [data-blok-list], [data-blok-list-item], etc.
615
1343
  - .cdx-button, .cdx-input, .cdx-loader → [data-blok-button], etc.
@@ -617,8 +1345,32 @@ What this codemod does:
617
1345
  • Updates data attributes (data-id → data-blok-id)
618
1346
  • Changes default holder from 'editorjs' to 'blok'
619
1347
  • Updates package.json dependencies
620
- Converts bundled tool imports (Header, Paragraph)
621
- Ensures Blok is imported when using bundled tools (Blok.Header, etc.)
1348
+ Transforms nested i18n messages to flat dot-notation format with camelCase keys:
1349
+ - { ui: { toolbar: { Add: "..." } } } → { "ui.toolbar.add": "..." }
1350
+ - { toolNames: { "Nothing found": "..." } } → { "toolNames.nothingFound": "..." }
1351
+ • Maps changed i18n keys to their Blok equivalents (camelCase):
1352
+ - "Click to tune" → "clickToOpenMenu"
1353
+ - "or drag to move" → "dragToMove"
1354
+ - "Add" (toolbar) → "clickToAddBelow"
1355
+ - "Filter" (popover) → "search"
1356
+ - "Ordered List" → "numberedList"
1357
+ - "Unordered List" → "bulletedList"
1358
+ • Removes obsolete keys (moveUp/moveDown replaced with drag)
1359
+
1360
+ Old Blok → Modular Blok Migration:
1361
+ • Splits combined imports into core and tools:
1362
+ - import { Blok, Header } from '@jackuait/blok'
1363
+ → import { Blok } from '@jackuait/blok';
1364
+ import { Header } from '@jackuait/blok/tools';
1365
+ • Transforms static property access to direct references:
1366
+ - Blok.Header → Header (with import added)
1367
+ - Blok.Paragraph → Paragraph
1368
+ - Blok.List → List
1369
+ - Blok.Bold, Blok.Italic, Blok.Link → Bold, Italic, Link
1370
+ • Removes internal tools configuration (Convert and Delete are now bundled with core):
1371
+ - convertTo: { class: Convert } → removed (automatically available)
1372
+ - delete: { class: Delete } → removed (automatically available)
1373
+ • Ensures tools are imported from @jackuait/blok/tools when used
622
1374
 
623
1375
  Note: After running, you may need to manually:
624
1376
  • Update any custom tool implementations
@@ -633,6 +1385,7 @@ function main() {
633
1385
  // Parse arguments
634
1386
  const dryRun = args.includes('--dry-run');
635
1387
  const verbose = args.includes('--verbose');
1388
+ const useLibraryI18n = args.includes('--use-library-i18n');
636
1389
  const help = args.includes('--help') || args.includes('-h');
637
1390
 
638
1391
  global.isVerbose = verbose;
@@ -675,7 +1428,7 @@ function main() {
675
1428
 
676
1429
  // Process each file
677
1430
  files.forEach((filePath) => {
678
- const result = transformFile(filePath, dryRun);
1431
+ const result = transformFile(filePath, dryRun, useLibraryI18n);
679
1432
 
680
1433
  if (result.hasChanges) {
681
1434
  stats.filesModified++;
@@ -741,7 +1494,20 @@ module.exports = {
741
1494
  updatePackageJson,
742
1495
  applyTransforms,
743
1496
  ensureBlokImport,
1497
+ ensureToolsImport,
1498
+ splitBlokImports,
1499
+ normalizeKey,
1500
+ flattenI18nDictionary,
1501
+ transformI18nConfig,
1502
+ removeI18nMessages,
1503
+ parseObjectLiteral,
1504
+ findMatchingBrace,
1505
+ objectToString,
1506
+ I18N_KEY_MAPPINGS,
744
1507
  BUNDLED_TOOLS,
1508
+ INLINE_TOOLS,
1509
+ INTERNAL_TOOLS,
1510
+ ALL_TOOLS,
745
1511
  IMPORT_TRANSFORMS,
746
1512
  TYPE_TRANSFORMS,
747
1513
  CLASS_NAME_TRANSFORMS,
@@ -750,5 +1516,6 @@ module.exports = {
750
1516
  SELECTOR_TRANSFORMS,
751
1517
  HOLDER_TRANSFORMS,
752
1518
  TOOL_CONFIG_TRANSFORMS,
1519
+ INTERNAL_TOOL_REMOVAL_TRANSFORMS,
753
1520
  TEXT_TRANSFORMS,
754
1521
  };