@jackuait/blok 0.4.1-beta.5 → 0.4.1-beta.6

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 (400) hide show
  1. package/README.md +136 -17
  2. package/codemod/README.md +16 -0
  3. package/codemod/migrate-editorjs-to-blok.js +868 -92
  4. package/codemod/test.js +682 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-B5qs7C5l.mjs +12838 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-CTrK3HzG.mjs +43 -0
  9. package/dist/{index-Cl_5rkKS.mjs → chunks/index-DDpzQn-0.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-RBcopmCh.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 +48 -16
  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 -12
  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/header.d.ts +18 -0
  392. package/types/tools/index.d.ts +1 -0
  393. package/types/tools/list.d.ts +91 -0
  394. package/types/tools/paragraph.d.ts +71 -0
  395. package/types/tools/tool-settings.d.ts +16 -2
  396. package/types/tools/tool.d.ts +6 -0
  397. package/types/tools-entry.d.ts +49 -0
  398. package/types/utils/popover/popover-item.d.ts +0 -5
  399. package/dist/blok-DvN73wsH.mjs +0 -19922
  400. package/dist/blok.umd.js +0 -166
@@ -29,28 +29,523 @@ 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
- // Main EditorJS import
391
+ // EditorJS subpath imports (e.g., @editorjs/editorjs/types -> @jackuait/blok/types)
392
+ {
393
+ pattern: /from\s+['"]@editorjs\/editorjs\/([^'"]+)['"]/g,
394
+ replacement: "from '@jackuait/blok/$1'",
395
+ },
396
+ {
397
+ pattern: /require\s*\(\s*['"]@editorjs\/editorjs\/([^'"]+)['"]\s*\)/g,
398
+ replacement: "require('@jackuait/blok/$1')",
399
+ },
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'
35
409
  {
36
- pattern: /from\s+['"]@editorjs\/editorjs['"]/g,
37
- replacement: "from '@jackuait/blok'",
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'
416
+ {
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',
38
434
  },
39
435
  {
40
436
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
41
- 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',
42
488
  },
43
- // Header tool (now bundled)
44
489
  {
45
490
  pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/header['"];?\n?/g,
46
- replacement: '// Header is now bundled with Blok: use Blok.Header\n',
47
- 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',
48
499
  },
49
- // Paragraph tool (now bundled)
50
500
  {
51
501
  pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
52
- replacement: '// Paragraph is now bundled with Blok: use Blok.Paragraph\n',
53
- 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)',
54
549
  },
55
550
  ];
56
551
 
@@ -122,13 +617,13 @@ const CSS_CLASS_TRANSFORMS = [
122
617
  { pattern: /(['"`])ce-inline-toolbar(['"`])/g, replacement: '$1data-blok-testid="inline-toolbar"$2' },
123
618
 
124
619
  // Popover classes (ce-popover)
125
- { 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"]' },
126
621
  { pattern: /\.ce-popover__container(?![\w-])/g, replacement: '[data-blok-popover-container]' },
127
622
  { pattern: /\.ce-popover-item--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
128
623
  { pattern: /\.ce-popover-item(?![\w-])/g, replacement: '[data-blok-testid="popover-item"]' },
129
624
  { pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-popover]' },
130
625
  // Without dot prefix
131
- { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover$2' },
626
+ { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover-opened$2' },
132
627
  { pattern: /(['"`])ce-popover__container(['"`])/g, replacement: '$1data-blok-popover-container$2' },
133
628
  { pattern: /(['"`])ce-popover-item--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
134
629
  { pattern: /(['"`])ce-popover-item(['"`])/g, replacement: '$1data-blok-testid="popover-item"$2' },
@@ -166,17 +661,17 @@ const CSS_CLASS_TRANSFORMS = [
166
661
  { pattern: /(['"`])ce-ragged-right(['"`])/g, replacement: '$1data-blok-ragged-right$2' },
167
662
 
168
663
  // Popover item states and icons
169
- { 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"]' },
170
665
  { pattern: /\.ce-popover-item__icon(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon"]' },
171
666
  { pattern: /\.ce-popover-item__icon--tool(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon-tool"]' },
172
- { 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' },
173
668
  { pattern: /(['"`])ce-popover-item__icon(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon"$2' },
174
669
  { pattern: /(['"`])ce-popover-item__icon--tool(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon-tool"$2' },
175
670
 
176
671
  // Toolbox classes (ce-toolbox)
177
- { 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"]' },
178
673
  { pattern: /\.ce-toolbox(?![\w-])/g, replacement: '[data-blok-toolbox]' },
179
- { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox$2' },
674
+ { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox-opened$2' },
180
675
  { pattern: /(['"`])ce-toolbox(['"`])/g, replacement: '$1data-blok-toolbox$2' },
181
676
 
182
677
  // CDX list classes (cdx-list)
@@ -240,19 +735,141 @@ const HOLDER_TRANSFORMS = [
240
735
  { pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
241
736
  ];
242
737
 
243
- // Bundled tools - add new tools here as they are bundled with Blok
244
- 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];
245
749
 
246
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
247
753
  const TOOL_CONFIG_TRANSFORMS = [
248
- // Handle class property syntax
249
- { pattern: /class:\s*Header(?!Config)/g, replacement: 'class: Blok.Header' },
250
- { pattern: /class:\s*Paragraph(?!Config)/g, replacement: 'class: Blok.Paragraph' },
251
- // Handle standalone tool references in tools config (e.g., `paragraph: Paragraph`)
252
- { pattern: /(\bheader\s*:\s*)Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Header' },
253
- { 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: '' },
254
779
  ];
255
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
799
+ ];
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
+
256
873
  // Text transformations for "EditorJS" string references
257
874
  const TEXT_TRANSFORMS = [
258
875
  // Replace exact "EditorJS" text (preserves case-sensitive matching)
@@ -321,108 +938,185 @@ function applyTransforms(content, transforms, fileName) {
321
938
  }
322
939
 
323
940
  /**
324
- * Ensures that Blok is properly imported when bundled tools (Blok.Header, Blok.Paragraph, etc.) are used.
325
- * 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
326
945
  *
327
946
  * Handles the following scenarios:
328
- * 1. No existing @jackuait/blok import -> adds `import Blok from '@jackuait/blok'`
329
- * 2. Named imports only (e.g., `import { BlokConfig } from '@jackuait/blok'`) -> adds Blok default import
330
- * 3. Default import with different name -> adds Blok to named imports
331
- * 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
332
949
  */
333
950
  function ensureBlokImport(content) {
334
- // Check if content uses any Blok.* tool (e.g., Blok.Header, Blok.Paragraph)
335
- const blokToolPattern = new RegExp(`Blok\\.(${BUNDLED_TOOLS.join('|')})`, 'g');
336
- 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
+ });
337
957
 
338
- if (!usesBlokTools) {
958
+ if (usedTools.length === 0) {
339
959
  return { result: content, changed: false };
340
960
  }
341
961
 
342
- // Check if Blok is already available as a default import
343
- // Matches: import Blok from '@jackuait/blok' or import Blok, { ... } from '@jackuait/blok'
344
- const hasBlokDefaultImport = /import\s+Blok\s*(?:,\s*\{[^}]*\}\s*)?from\s*['"]@jackuait\/blok['"]/.test(content);
962
+ // Check for existing @jackuait/blok import
963
+ const namedImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
964
+ const namedMatch = content.match(namedImportPattern);
345
965
 
346
- if (hasBlokDefaultImport) {
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
+ });
975
+
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
+ }
347
985
  return { result: content, changed: false };
348
986
  }
349
987
 
350
- // Check for existing @jackuait/blok import patterns
351
- const namedOnlyImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
352
- const defaultWithNamedPattern = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
353
- const defaultOnlyPattern = /import\s+(\w+)\s+from\s*['"]@jackuait\/blok['"];?/;
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
+ });
1028
+
1029
+ if (usedTools.length === 0) {
1030
+ return { result: content, changed: false };
1031
+ }
1032
+
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);
354
1040
 
355
1041
  let result = content;
356
1042
 
357
- // Case 1: Named imports only -> add Blok default import
358
- // e.g., `import { BlokConfig } from '@jackuait/blok'` -> `import Blok, { BlokConfig } from '@jackuait/blok'`
359
- const namedOnlyMatch = content.match(namedOnlyImportPattern);
360
- if (namedOnlyMatch) {
361
- const namedImports = namedOnlyMatch[1];
362
- result = content.replace(
363
- namedOnlyImportPattern,
364
- `import Blok, {${namedImports}} from '@jackuait/blok';`
365
- );
366
- return { result, changed: true };
367
- }
368
-
369
- // Case 2: Default import with different name + named imports -> add Blok to named imports
370
- // e.g., `import Editor, { BlokConfig } from '@jackuait/blok'` -> `import Editor, { Blok, BlokConfig } from '@jackuait/blok'`
371
- const defaultWithNamedMatch = content.match(defaultWithNamedPattern);
372
- if (defaultWithNamedMatch) {
373
- const defaultName = defaultWithNamedMatch[1];
374
- const namedImports = defaultWithNamedMatch[2];
375
- // Check if Blok is already in named imports
376
- 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(', ')}`;
377
1054
  result = content.replace(
378
- defaultWithNamedPattern,
379
- `import ${defaultName}, { Blok, ${namedImports.trim()} } from '@jackuait/blok';`
1055
+ toolsImportPattern,
1056
+ `import { ${newImports} } from '@jackuait/blok/tools';`
380
1057
  );
381
1058
  return { result, changed: true };
382
1059
  }
383
1060
  return { result: content, changed: false };
384
1061
  }
385
1062
 
386
- // Case 3: Default import only with different name -> add Blok to named imports
387
- // e.g., `import Editor from '@jackuait/blok'` -> `import Editor, { Blok } from '@jackuait/blok'`
388
- const defaultOnlyMatch = content.match(defaultOnlyPattern);
389
- if (defaultOnlyMatch) {
390
- const defaultName = defaultOnlyMatch[1];
391
- 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()}`;
392
1085
  result = content.replace(
393
- defaultOnlyPattern,
394
- `import ${defaultName}, { Blok } from '@jackuait/blok';`
1086
+ mainImportPattern,
1087
+ `import { ${newImports} } from '@jackuait/blok';`
395
1088
  );
396
1089
  return { result, changed: true };
397
1090
  }
398
1091
  return { result: content, changed: false };
399
1092
  }
400
1093
 
401
- // Case 4: No @jackuait/blok import at all -> add new import at the top (after any existing imports)
402
- // Find the last import statement to insert after it
1094
+ // No @jackuait/blok or @jackuait/blok/tools import -> add new /tools import
403
1095
  const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1096
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok/tools';`;
1097
+
404
1098
  if (importStatements && importStatements.length > 0) {
405
1099
  const lastImport = importStatements[importStatements.length - 1];
406
1100
  const lastImportIndex = content.lastIndexOf(lastImport);
407
1101
  const insertPosition = lastImportIndex + lastImport.length;
408
1102
  result =
409
1103
  content.slice(0, insertPosition) +
410
- "\nimport Blok from '@jackuait/blok';" +
1104
+ '\n' + newImport +
411
1105
  content.slice(insertPosition);
412
1106
  } else {
413
1107
  // No imports found, add at the very beginning (after shebang if present)
414
1108
  const shebangMatch = content.match(/^#!.*\n/);
415
1109
  if (shebangMatch) {
416
- 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);
417
1111
  } else {
418
- result = "import Blok from '@jackuait/blok';\n" + content;
1112
+ result = newImport + '\n' + content;
419
1113
  }
420
1114
  }
421
1115
 
422
1116
  return { result, changed: true };
423
1117
  }
424
1118
 
425
- function transformFile(filePath, dryRun = false) {
1119
+ function transformFile(filePath, dryRun = false, useLibraryI18n = false) {
426
1120
  const content = fs.readFileSync(filePath, 'utf8');
427
1121
  let transformed = content;
428
1122
  const allChanges = [];
@@ -488,12 +1182,28 @@ function transformFile(filePath, dryRun = false) {
488
1182
  allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
489
1183
  }
490
1184
 
491
- // 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)
492
1202
  if (isJsFile) {
493
- const { result, changed } = ensureBlokImport(transformed);
1203
+ const { result, changed } = splitBlokImports(transformed);
494
1204
  if (changed) {
495
1205
  transformed = result;
496
- 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' });
497
1207
  }
498
1208
  }
499
1209
 
@@ -504,6 +1214,25 @@ function transformFile(filePath, dryRun = false) {
504
1214
  allChanges.push(...changes.map((c) => ({ ...c, category: 'text' })));
505
1215
  }
506
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
+
507
1236
  const hasChanges = transformed !== content;
508
1237
 
509
1238
  if (hasChanges && !dryRun) {
@@ -527,7 +1256,7 @@ function updatePackageJson(packageJsonPath, dryRun = false) {
527
1256
  const changes = [];
528
1257
 
529
1258
  // Track dependencies to remove
530
- const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph'];
1259
+ const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph', '@editorjs/list'];
531
1260
  const devDepsToRemove = [...depsToRemove];
532
1261
 
533
1262
  // Check and update dependencies
@@ -580,9 +1309,10 @@ Arguments:
580
1309
  path Directory or file to transform (default: current directory)
581
1310
 
582
1311
  Options:
583
- --dry-run Show changes without modifying files
584
- --verbose Show detailed output for each file
585
- --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
586
1316
 
587
1317
  Examples:
588
1318
  npx -p @jackuait/blok migrate-from-editorjs ./src
@@ -590,17 +1320,24 @@ Examples:
590
1320
  npx -p @jackuait/blok migrate-from-editorjs . --verbose
591
1321
 
592
1322
  What this codemod does:
593
- • 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'
594
1331
  • Updates type names (EditorConfig → BlokConfig)
595
1332
  • Replaces 'new EditorJS()' with 'new Blok()'
596
1333
  • Converts CSS class selectors to data attributes:
597
1334
  - .codex-editor* → [data-blok-editor], [data-blok-redactor], etc.
598
1335
  - .ce-block* → [data-blok-element], [data-blok-selected], etc.
599
1336
  - .ce-toolbar* → [data-blok-toolbar], [data-blok-settings-toggler], etc.
600
- - .ce-toolbox* → [data-blok-toolbox], etc.
1337
+ - .ce-toolbox* → [data-blok-toolbox], [data-blok-toolbox-opened], etc.
601
1338
  - .ce-inline-toolbar, .ce-inline-tool* → [data-blok-testid="inline-*"]
602
- - .ce-popover* → [data-blok-popover], [data-blok-popover-container], etc.
603
- - .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.
604
1341
  - .ce-paragraph, .ce-header → [data-blok-tool="paragraph|header"]
605
1342
  - .cdx-list* → [data-blok-list], [data-blok-list-item], etc.
606
1343
  - .cdx-button, .cdx-input, .cdx-loader → [data-blok-button], etc.
@@ -608,8 +1345,32 @@ What this codemod does:
608
1345
  • Updates data attributes (data-id → data-blok-id)
609
1346
  • Changes default holder from 'editorjs' to 'blok'
610
1347
  • Updates package.json dependencies
611
- Converts bundled tool imports (Header, Paragraph)
612
- 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
613
1374
 
614
1375
  Note: After running, you may need to manually:
615
1376
  • Update any custom tool implementations
@@ -624,6 +1385,7 @@ function main() {
624
1385
  // Parse arguments
625
1386
  const dryRun = args.includes('--dry-run');
626
1387
  const verbose = args.includes('--verbose');
1388
+ const useLibraryI18n = args.includes('--use-library-i18n');
627
1389
  const help = args.includes('--help') || args.includes('-h');
628
1390
 
629
1391
  global.isVerbose = verbose;
@@ -666,7 +1428,7 @@ function main() {
666
1428
 
667
1429
  // Process each file
668
1430
  files.forEach((filePath) => {
669
- const result = transformFile(filePath, dryRun);
1431
+ const result = transformFile(filePath, dryRun, useLibraryI18n);
670
1432
 
671
1433
  if (result.hasChanges) {
672
1434
  stats.filesModified++;
@@ -732,7 +1494,20 @@ module.exports = {
732
1494
  updatePackageJson,
733
1495
  applyTransforms,
734
1496
  ensureBlokImport,
1497
+ ensureToolsImport,
1498
+ splitBlokImports,
1499
+ normalizeKey,
1500
+ flattenI18nDictionary,
1501
+ transformI18nConfig,
1502
+ removeI18nMessages,
1503
+ parseObjectLiteral,
1504
+ findMatchingBrace,
1505
+ objectToString,
1506
+ I18N_KEY_MAPPINGS,
735
1507
  BUNDLED_TOOLS,
1508
+ INLINE_TOOLS,
1509
+ INTERNAL_TOOLS,
1510
+ ALL_TOOLS,
736
1511
  IMPORT_TRANSFORMS,
737
1512
  TYPE_TRANSFORMS,
738
1513
  CLASS_NAME_TRANSFORMS,
@@ -741,5 +1516,6 @@ module.exports = {
741
1516
  SELECTOR_TRANSFORMS,
742
1517
  HOLDER_TRANSFORMS,
743
1518
  TOOL_CONFIG_TRANSFORMS,
1519
+ INTERNAL_TOOL_REMOVAL_TRANSFORMS,
744
1520
  TEXT_TRANSFORMS,
745
1521
  };