@jackuait/blok 0.4.1 → 0.4.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/README.md +136 -17
  2. package/codemod/README.md +45 -7
  3. package/codemod/migrate-editorjs-to-blok.js +951 -92
  4. package/codemod/test.js +780 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-8ptWuVZC.mjs +12838 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-bLawSYRV.mjs +43 -0
  9. package/dist/{index-CBkApZKo.mjs → chunks/index-5nYtWZD2.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-CvMDAIzb.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 +60 -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,8 +29,457 @@ 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 = [
391
+ // ============================================================================
392
+ // Blok default import → named import transformations
393
+ // These handle cases where users incorrectly use default imports with Blok
394
+ // (Blok only exports named exports, not a default export)
395
+ // ============================================================================
396
+
397
+ // Combined default + named imports from @jackuait/blok: import Blok, { BlokConfig } from '@jackuait/blok'
398
+ // → import { Blok, BlokConfig } from '@jackuait/blok'
399
+ // IMPORTANT: Must come before regular default import patterns to avoid partial matching
400
+ {
401
+ pattern: /import\s+Blok\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
402
+ replacement: "import { Blok, $1 } from '@jackuait/blok'",
403
+ note: 'Converted combined default + named import to named import',
404
+ },
405
+ // Aliased combined: import Editor, { BlokConfig } from '@jackuait/blok'
406
+ {
407
+ pattern: /import\s+(\w+)\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
408
+ replacement: "import { Blok as $1, $2 } from '@jackuait/blok'",
409
+ note: 'Converted aliased combined default + named import to named import',
410
+ },
411
+ // Default import: import Blok from '@jackuait/blok' → import { Blok } from '@jackuait/blok'
412
+ {
413
+ pattern: /import\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
414
+ replacement: "import { Blok } from '@jackuait/blok'",
415
+ note: 'Converted default import to named import',
416
+ },
417
+ // Aliased default import: import Editor from '@jackuait/blok' → import { Blok as Editor } from '@jackuait/blok'
418
+ // Must check it's not followed by /tools or /types to avoid matching subpath imports
419
+ {
420
+ pattern: /import\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
421
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
422
+ note: 'Converted aliased default import to named import',
423
+ },
424
+ // Type-only default import: import type Blok from '@jackuait/blok'
425
+ {
426
+ pattern: /import\s+type\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
427
+ replacement: "import type { Blok } from '@jackuait/blok'",
428
+ note: 'Converted type-only default import to named import',
429
+ },
430
+ // Type-only aliased default import: import type Editor from '@jackuait/blok'
431
+ {
432
+ pattern: /import\s+type\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
433
+ replacement: "import type { Blok as $1 } from '@jackuait/blok'",
434
+ note: 'Converted type-only aliased default import to named import',
435
+ },
436
+ // Namespace import: import * as Blok from '@jackuait/blok' → import { Blok } from '@jackuait/blok'
437
+ {
438
+ pattern: /import\s+\*\s+as\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
439
+ replacement: "import { Blok } from '@jackuait/blok'",
440
+ note: 'Converted namespace import to named import',
441
+ },
442
+ // Aliased namespace import: import * as Editor from '@jackuait/blok'
443
+ {
444
+ pattern: /import\s+\*\s+as\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
445
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
446
+ note: 'Converted namespace import to named import with alias',
447
+ },
448
+ // Dynamic import with destructuring: const { default: Editor } = await import('@jackuait/blok')
449
+ {
450
+ pattern: /\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*await\s+import\s*\(\s*['"]@jackuait\/blok['"]\s*\)/g,
451
+ replacement: "{ Blok: $1 } = await import('@jackuait/blok')",
452
+ note: 'Converted destructured dynamic import',
453
+ },
454
+ // Re-export default as named: export { default as Editor } from '@jackuait/blok'
455
+ {
456
+ pattern: /export\s+\{\s*default\s+as\s+(\w+)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
457
+ replacement: "export { Blok as $1 } from '@jackuait/blok'",
458
+ note: 'Converted default re-export to named export',
459
+ },
460
+ // Re-export default: export { default } from '@jackuait/blok'
461
+ {
462
+ pattern: /export\s+\{\s*default\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
463
+ replacement: "export { Blok } from '@jackuait/blok'",
464
+ note: 'Converted default re-export',
465
+ },
466
+ // Require with .default access: require('@jackuait/blok').default
467
+ {
468
+ pattern: /require\s*\(\s*['"]@jackuait\/blok['"]\s*\)\.default/g,
469
+ replacement: "require('@jackuait/blok').Blok",
470
+ note: 'Converted require().default to require().Blok',
471
+ },
472
+ // Destructured require with default: const { default: Editor } = require('@jackuait/blok')
473
+ {
474
+ pattern: /const\s+\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*require\s*\(\s*['"]@jackuait\/blok['"]\s*\)/g,
475
+ replacement: "const { Blok: $1 } = require('@jackuait/blok')",
476
+ note: 'Converted destructured default require to named require',
477
+ },
478
+
479
+ // ============================================================================
480
+ // EditorJS → Blok transformations
481
+ // ============================================================================
482
+
34
483
  // EditorJS subpath imports (e.g., @editorjs/editorjs/types -> @jackuait/blok/types)
35
484
  {
36
485
  pattern: /from\s+['"]@editorjs\/editorjs\/([^'"]+)['"]/g,
@@ -40,26 +489,155 @@ const IMPORT_TRANSFORMS = [
40
489
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs\/([^'"]+)['"]\s*\)/g,
41
490
  replacement: "require('@jackuait/blok/$1')",
42
491
  },
43
- // Main EditorJS import
492
+ // Combined default + named imports: import EditorJS, { EditorConfig } from '@editorjs/editorjs'
493
+ // → import { Blok, EditorConfig } from '@jackuait/blok' (EditorConfig → BlokConfig handled by TYPE_TRANSFORMS)
494
+ // IMPORTANT: Must come before regular default import patterns to avoid partial matching
495
+ {
496
+ pattern: /import\s+EditorJS\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
497
+ replacement: "import { Blok, $1 } from '@jackuait/blok'",
498
+ note: 'Converted combined default + named import',
499
+ },
500
+ // Aliased combined: import Editor, { EditorConfig } from '@editorjs/editorjs'
501
+ {
502
+ pattern: /import\s+(\w+)\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
503
+ replacement: "import { Blok as $1, $2 } from '@jackuait/blok'",
504
+ note: 'Converted aliased combined default + named import',
505
+ },
506
+ // Main EditorJS default import -> Blok named import
507
+ // e.g., import EditorJS from '@editorjs/editorjs' -> import { Blok } from '@jackuait/blok'
508
+ {
509
+ pattern: /import\s+EditorJS\s+from\s+['"]@editorjs\/editorjs['"]/g,
510
+ replacement: "import { Blok } from '@jackuait/blok'",
511
+ note: 'Converted default import to named import',
512
+ },
513
+ // EditorJS default import with alias -> Blok named import with alias
514
+ // e.g., import Editor from '@editorjs/editorjs' -> import { Blok as Editor } from '@jackuait/blok'
44
515
  {
45
- pattern: /from\s+['"]@editorjs\/editorjs['"]/g,
46
- replacement: "from '@jackuait/blok'",
516
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
517
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
518
+ note: 'Converted default import to named import with alias',
519
+ },
520
+ // Destructured require with default: const { default: EditorJS } = require(...)
521
+ // IMPORTANT: Must come before generic require pattern to avoid partial matching
522
+ {
523
+ pattern: /const\s+\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
524
+ replacement: "const { Blok: $1 } = require('@jackuait/blok')",
525
+ note: 'Converted destructured default require to named require',
47
526
  },
48
527
  {
49
528
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
50
- replacement: "require('@jackuait/blok')",
529
+ replacement: "require('@jackuait/blok').Blok",
530
+ },
531
+ // Namespace import: import * as EditorJS from '@editorjs/editorjs'
532
+ // → convert to named import (namespace pattern no longer needed with named exports)
533
+ {
534
+ pattern: /import\s+\*\s+as\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
535
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
536
+ note: 'Converted namespace import to named import (namespace no longer needed)',
537
+ },
538
+ // Dynamic import with .then chaining that expects default (must come before generic dynamic import)
539
+ {
540
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)\s*\.then\s*\(\s*(\w+)\s*=>\s*\1\.default\s*\)/g,
541
+ replacement: "import('@jackuait/blok').then($1 => $1.Blok)",
542
+ note: 'Converted dynamic import .then(m => m.default) pattern',
543
+ },
544
+ // Dynamic import with destructuring: const { default: Editor } = await import('@editorjs/editorjs')
545
+ // IMPORTANT: Must come before generic dynamic import to avoid partial matching
546
+ {
547
+ pattern: /\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*await\s+import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
548
+ replacement: "{ Blok: $1 } = await import('@jackuait/blok')",
549
+ note: 'Converted destructured dynamic import',
550
+ },
551
+ // Dynamic import: import('@editorjs/editorjs')
552
+ {
553
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
554
+ replacement: "import('@jackuait/blok').then(m => ({ default: m.Blok }))",
555
+ note: 'Converted dynamic import (wraps named export as default for compatibility)',
556
+ },
557
+ // Type-only default import: import type EditorJS from '@editorjs/editorjs'
558
+ {
559
+ pattern: /import\s+type\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
560
+ replacement: "import type { Blok as $1 } from '@jackuait/blok'",
561
+ note: 'Converted type-only default import to named import',
562
+ },
563
+ // Re-export default as named: export { default as Editor } from '@editorjs/editorjs'
564
+ {
565
+ pattern: /export\s+\{\s*default\s+as\s+(\w+)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
566
+ replacement: "export { Blok as $1 } from '@jackuait/blok'",
567
+ note: 'Converted default re-export to named export',
568
+ },
569
+ // Re-export default: export { default } from '@editorjs/editorjs'
570
+ {
571
+ pattern: /export\s+\{\s*default\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
572
+ replacement: "export { Blok } from '@jackuait/blok'",
573
+ note: 'Converted default re-export',
574
+ },
575
+ // Header tool (now bundled) - import directly from @jackuait/blok
576
+ {
577
+ pattern: /import\s+Header\s+from\s+['"]@editorjs\/header['"];?\n?/g,
578
+ replacement: "import { Header } from '@jackuait/blok';\n",
579
+ note: 'Header tool is now bundled with Blok',
51
580
  },
52
- // Header tool (now bundled)
53
581
  {
54
582
  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',
583
+ replacement: "import { Header as $1 } from '@jackuait/blok';\n",
584
+ note: 'Header tool is now bundled with Blok (aliased)',
585
+ },
586
+ // Paragraph tool (now bundled) - import directly from @jackuait/blok
587
+ {
588
+ pattern: /import\s+Paragraph\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
589
+ replacement: "import { Paragraph } from '@jackuait/blok';\n",
590
+ note: 'Paragraph tool is now bundled with Blok',
57
591
  },
58
- // Paragraph tool (now bundled)
59
592
  {
60
593
  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',
594
+ replacement: "import { Paragraph as $1 } from '@jackuait/blok';\n",
595
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
596
+ },
597
+ // List tool (now bundled) - import directly from @jackuait/blok
598
+ {
599
+ pattern: /import\s+List\s+from\s+['"]@editorjs\/list['"];?\n?/g,
600
+ replacement: "import { List } from '@jackuait/blok';\n",
601
+ note: 'List tool is now bundled with Blok',
602
+ },
603
+ {
604
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/list['"];?\n?/g,
605
+ replacement: "import { List as $1 } from '@jackuait/blok';\n",
606
+ note: 'List tool is now bundled with Blok (aliased)',
607
+ },
608
+ // Tool require statements (CommonJS)
609
+ // Header require: const Header = require('@editorjs/header')
610
+ {
611
+ pattern: /const\s+Header\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
612
+ replacement: "const { Header } = require('@jackuait/blok')",
613
+ note: 'Header tool is now bundled with Blok',
614
+ },
615
+ {
616
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
617
+ replacement: "const { Header: $1 } = require('@jackuait/blok')",
618
+ note: 'Header tool is now bundled with Blok (aliased)',
619
+ },
620
+ // Paragraph require
621
+ {
622
+ pattern: /const\s+Paragraph\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
623
+ replacement: "const { Paragraph } = require('@jackuait/blok')",
624
+ note: 'Paragraph tool is now bundled with Blok',
625
+ },
626
+ {
627
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
628
+ replacement: "const { Paragraph: $1 } = require('@jackuait/blok')",
629
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
630
+ },
631
+ // List require
632
+ {
633
+ pattern: /const\s+List\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
634
+ replacement: "const { List } = require('@jackuait/blok')",
635
+ note: 'List tool is now bundled with Blok',
636
+ },
637
+ {
638
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
639
+ replacement: "const { List: $1 } = require('@jackuait/blok')",
640
+ note: 'List tool is now bundled with Blok (aliased)',
63
641
  },
64
642
  ];
65
643
 
@@ -131,13 +709,13 @@ const CSS_CLASS_TRANSFORMS = [
131
709
  { pattern: /(['"`])ce-inline-toolbar(['"`])/g, replacement: '$1data-blok-testid="inline-toolbar"$2' },
132
710
 
133
711
  // Popover classes (ce-popover)
134
- { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover][data-blok-opened="true"]' },
712
+ { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover-opened="true"]' },
135
713
  { pattern: /\.ce-popover__container(?![\w-])/g, replacement: '[data-blok-popover-container]' },
136
714
  { pattern: /\.ce-popover-item--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
137
715
  { pattern: /\.ce-popover-item(?![\w-])/g, replacement: '[data-blok-testid="popover-item"]' },
138
716
  { pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-popover]' },
139
717
  // Without dot prefix
140
- { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover$2' },
718
+ { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover-opened$2' },
141
719
  { pattern: /(['"`])ce-popover__container(['"`])/g, replacement: '$1data-blok-popover-container$2' },
142
720
  { pattern: /(['"`])ce-popover-item--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
143
721
  { pattern: /(['"`])ce-popover-item(['"`])/g, replacement: '$1data-blok-testid="popover-item"$2' },
@@ -175,17 +753,17 @@ const CSS_CLASS_TRANSFORMS = [
175
753
  { pattern: /(['"`])ce-ragged-right(['"`])/g, replacement: '$1data-blok-ragged-right$2' },
176
754
 
177
755
  // Popover item states and icons
178
- { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-confirmation="true"]' },
756
+ { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-popover-item-confirmation="true"]' },
179
757
  { pattern: /\.ce-popover-item__icon(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon"]' },
180
758
  { 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' },
759
+ { pattern: /(['"`])ce-popover-item--confirmation(['"`])/g, replacement: '$1data-blok-popover-item-confirmation$2' },
182
760
  { pattern: /(['"`])ce-popover-item__icon(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon"$2' },
183
761
  { pattern: /(['"`])ce-popover-item__icon--tool(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon-tool"$2' },
184
762
 
185
763
  // Toolbox classes (ce-toolbox)
186
- { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox][data-blok-opened="true"]' },
764
+ { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox-opened="true"]' },
187
765
  { pattern: /\.ce-toolbox(?![\w-])/g, replacement: '[data-blok-toolbox]' },
188
- { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox$2' },
766
+ { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox-opened$2' },
189
767
  { pattern: /(['"`])ce-toolbox(['"`])/g, replacement: '$1data-blok-toolbox$2' },
190
768
 
191
769
  // CDX list classes (cdx-list)
@@ -249,19 +827,141 @@ const HOLDER_TRANSFORMS = [
249
827
  { pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
250
828
  ];
251
829
 
252
- // Bundled tools - add new tools here as they are bundled with Blok
253
- const BUNDLED_TOOLS = ['Header', 'Paragraph'];
830
+ // Bundled tools - these are now in a separate entry point (@jackuait/blok/tools)
831
+ const BUNDLED_TOOLS = ['Header', 'Paragraph', 'List'];
832
+
833
+ // Inline tools - also in the tools entry point
834
+ const INLINE_TOOLS = ['Bold', 'Italic', 'Link'];
835
+
836
+ // Internal tools - these are bundled with core and don't need to be imported
837
+ const INTERNAL_TOOLS = ['Convert', 'Delete'];
838
+
839
+ // All tools that should be imported from @jackuait/blok/tools
840
+ const ALL_TOOLS = [...BUNDLED_TOOLS, ...INLINE_TOOLS];
254
841
 
255
842
  // Tool configuration transformations
843
+ // Note: With named exports, tools are imported directly (e.g., { Header } from '@jackuait/blok/tools')
844
+ // so the tool class references don't need the Blok. prefix anymore
256
845
  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' },
846
+ // Convert old Blok.Header style to direct Header reference (for existing Blok users upgrading)
847
+ // Block tools
848
+ { pattern: /class:\s*Blok\.Header(?!Config)/g, replacement: 'class: Header' },
849
+ { pattern: /class:\s*Blok\.Paragraph(?!Config)/g, replacement: 'class: Paragraph' },
850
+ { pattern: /class:\s*Blok\.List(?!Config|Item)/g, replacement: 'class: List' },
851
+ { pattern: /(\bheader\s*:\s*)Blok\.Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Header' },
852
+ { pattern: /(\bparagraph\s*:\s*)Blok\.Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Paragraph' },
853
+ { pattern: /(\blist\s*:\s*)Blok\.List(?!Config|Item)(?=\s*[,}\n])/g, replacement: '$1List' },
854
+ // Inline tools
855
+ { pattern: /class:\s*Blok\.Bold(?!Config)/g, replacement: 'class: Bold' },
856
+ { pattern: /class:\s*Blok\.Italic(?!Config)/g, replacement: 'class: Italic' },
857
+ { pattern: /class:\s*Blok\.Link(?!Config)/g, replacement: 'class: Link' },
858
+ { pattern: /(\bbold\s*:\s*)Blok\.Bold(?!Config)(?=\s*[,}\n])/g, replacement: '$1Bold' },
859
+ { pattern: /(\bitalic\s*:\s*)Blok\.Italic(?!Config)(?=\s*[,}\n])/g, replacement: '$1Italic' },
860
+ { pattern: /(\blink\s*:\s*)Blok\.Link(?!Config)(?=\s*[,}\n])/g, replacement: '$1Link' },
861
+ ];
862
+
863
+ // Transforms to remove internal tools configuration (Convert and Delete are now bundled)
864
+ const INTERNAL_TOOL_REMOVAL_TRANSFORMS = [
865
+ // Remove convertTo/convert configuration lines (with various formats)
866
+ { pattern: /,?\s*convert(?:To)?\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Convert\s*\}\s*,?/g, replacement: '' },
867
+ { pattern: /,?\s*convert(?:To)?\s*:\s*(?:Blok\.)?Convert\s*,?/g, replacement: '' },
868
+ // Remove delete configuration lines
869
+ { pattern: /,?\s*delete\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*\}\s*,?/g, replacement: '' },
870
+ { pattern: /,?\s*delete\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*,?/g, replacement: '' },
871
+ ];
872
+
873
+ // ============================================================================
874
+ // Blok Modular Import Transformations (Strategy 5)
875
+ // ============================================================================
876
+
877
+ /**
878
+ * Transforms for migrating old Blok imports to the new modular structure.
879
+ * After Strategy 3/4, tools are in a separate entry point (@jackuait/blok/tools).
880
+ *
881
+ * Detects and transforms patterns like:
882
+ * import { Blok, Header, Paragraph } from '@jackuait/blok'
883
+ * To:
884
+ * import { Blok } from '@jackuait/blok';
885
+ * import { Header, Paragraph } from '@jackuait/blok/tools';
886
+ */
887
+ const BLOK_MODULAR_IMPORT_TRANSFORMS = [
888
+ // Transform tool imports from old @jackuait/blok to @jackuait/blok/tools
889
+ // These patterns match tools being imported from the main entry point
890
+ // The actual splitting is done in splitBlokImports() function below
263
891
  ];
264
892
 
893
+ /**
894
+ * Splits a combined @jackuait/blok import that contains both core exports and tools
895
+ * into separate imports from the correct entry points.
896
+ *
897
+ * @param {string} content - The file content
898
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
899
+ */
900
+ function splitBlokImports(content) {
901
+ // Pattern to find @jackuait/blok named imports
902
+ const blokImportPattern = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]@jackuait\/blok['"];?/g;
903
+ let result = content;
904
+ let changed = false;
905
+
906
+ // Collect all matches first to avoid regex state issues
907
+ const matches = [];
908
+ let match;
909
+ while ((match = blokImportPattern.exec(content)) !== null) {
910
+ matches.push({
911
+ fullMatch: match[0],
912
+ imports: match[1],
913
+ index: match.index,
914
+ });
915
+ }
916
+
917
+ // Process matches in reverse order to preserve indices
918
+ for (let i = matches.length - 1; i >= 0; i--) {
919
+ const { fullMatch, imports } = matches[i];
920
+
921
+ // Parse the imports
922
+ const importList = imports.split(',').map(s => s.trim()).filter(s => s.length > 0);
923
+
924
+ // Separate core imports from tool imports
925
+ const coreImports = [];
926
+ const toolImports = [];
927
+
928
+ for (const imp of importList) {
929
+ // Handle aliased imports: "Header as MyHeader"
930
+ const importName = imp.split(/\s+as\s+/)[0].trim();
931
+
932
+ if (ALL_TOOLS.includes(importName)) {
933
+ toolImports.push(imp);
934
+ } else {
935
+ coreImports.push(imp);
936
+ }
937
+ }
938
+
939
+ // Only transform if there are tool imports mixed with core imports
940
+ // or if there are only tool imports (they should come from /tools)
941
+ if (toolImports.length > 0) {
942
+ let newImports = '';
943
+
944
+ if (coreImports.length > 0) {
945
+ newImports += `import { ${coreImports.join(', ')} } from '@jackuait/blok';\n`;
946
+ }
947
+
948
+ if (toolImports.length > 0) {
949
+ newImports += `import { ${toolImports.join(', ')} } from '@jackuait/blok/tools';`;
950
+ }
951
+
952
+ // Preserve trailing newline if original had one
953
+ if (!fullMatch.endsWith(';')) {
954
+ newImports = newImports.replace(/;$/, '');
955
+ }
956
+
957
+ result = result.replace(fullMatch, newImports);
958
+ changed = true;
959
+ }
960
+ }
961
+
962
+ return { result, changed };
963
+ }
964
+
265
965
  // Text transformations for "EditorJS" string references
266
966
  const TEXT_TRANSFORMS = [
267
967
  // Replace exact "EditorJS" text (preserves case-sensitive matching)
@@ -330,108 +1030,185 @@ function applyTransforms(content, transforms, fileName) {
330
1030
  }
331
1031
 
332
1032
  /**
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.
1033
+ * Ensures that bundled tools (Header, Paragraph, List) are properly imported when used.
1034
+ * Since Blok now uses named exports, tools are imported directly.
1035
+ *
1036
+ * @deprecated Use ensureToolsImport instead, which imports from @jackuait/blok/tools
335
1037
  *
336
1038
  * 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
1039
+ * 1. Tool used without import -> adds `import { Header, Paragraph, List } from '@jackuait/blok'`
1040
+ * 2. Existing @jackuait/blok import without tools -> adds tools to named imports
341
1041
  */
342
1042
  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);
1043
+ // Check which bundled tools are used in the content
1044
+ const usedTools = BUNDLED_TOOLS.filter(tool => {
1045
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
1046
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
1047
+ return toolPattern.test(content);
1048
+ });
346
1049
 
347
- if (!usesBlokTools) {
1050
+ if (usedTools.length === 0) {
348
1051
  return { result: content, changed: false };
349
1052
  }
350
1053
 
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);
1054
+ // Check for existing @jackuait/blok import
1055
+ const namedImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
1056
+ const namedMatch = content.match(namedImportPattern);
1057
+
1058
+ let result = content;
1059
+
1060
+ if (namedMatch) {
1061
+ // Check which tools are missing from the import
1062
+ const existingImports = namedMatch[1];
1063
+ const missingTools = usedTools.filter(tool => {
1064
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1065
+ return !toolPattern.test(existingImports);
1066
+ });
354
1067
 
355
- if (hasBlokDefaultImport) {
1068
+ if (missingTools.length > 0) {
1069
+ // Add missing tools to existing import
1070
+ const newImports = `${missingTools.join(', ')}, ${existingImports.trim()}`;
1071
+ result = content.replace(
1072
+ namedImportPattern,
1073
+ `import { ${newImports} } from '@jackuait/blok';`
1074
+ );
1075
+ return { result, changed: true };
1076
+ }
356
1077
  return { result: content, changed: false };
357
1078
  }
358
1079
 
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['"];?/;
1080
+ // No @jackuait/blok import at all -> add new import
1081
+ const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1082
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok';`;
1083
+
1084
+ if (importStatements && importStatements.length > 0) {
1085
+ const lastImport = importStatements[importStatements.length - 1];
1086
+ const lastImportIndex = content.lastIndexOf(lastImport);
1087
+ const insertPosition = lastImportIndex + lastImport.length;
1088
+ result =
1089
+ content.slice(0, insertPosition) +
1090
+ '\n' + newImport +
1091
+ content.slice(insertPosition);
1092
+ } else {
1093
+ // No imports found, add at the very beginning (after shebang if present)
1094
+ const shebangMatch = content.match(/^#!.*\n/);
1095
+ if (shebangMatch) {
1096
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
1097
+ } else {
1098
+ result = newImport + '\n' + content;
1099
+ }
1100
+ }
1101
+
1102
+ return { result, changed: true };
1103
+ }
1104
+
1105
+ /**
1106
+ * Ensures that tools are properly imported from @jackuait/blok/tools when used.
1107
+ * This is the modern approach after Strategy 3/4 where tools are in a separate entry point.
1108
+ *
1109
+ * Handles the following scenarios:
1110
+ * 1. Tool used without import -> adds `import { Header, ... } from '@jackuait/blok/tools'`
1111
+ * 2. Existing @jackuait/blok/tools import without tools -> adds tools to named imports
1112
+ */
1113
+ function ensureToolsImport(content) {
1114
+ // Check which tools are used in the content
1115
+ const usedTools = ALL_TOOLS.filter(tool => {
1116
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
1117
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
1118
+ return toolPattern.test(content);
1119
+ });
1120
+
1121
+ if (usedTools.length === 0) {
1122
+ return { result: content, changed: false };
1123
+ }
1124
+
1125
+ // Check for existing @jackuait/blok/tools import
1126
+ const toolsImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok\/tools['"];?/;
1127
+ const toolsMatch = content.match(toolsImportPattern);
1128
+
1129
+ // Also check for @jackuait/blok import (legacy, will be split later)
1130
+ const mainImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
1131
+ const mainMatch = content.match(mainImportPattern);
363
1132
 
364
1133
  let result = content;
365
1134
 
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)) {
1135
+ // Check if tools are already imported from /tools
1136
+ if (toolsMatch) {
1137
+ const existingToolsImports = toolsMatch[1];
1138
+ const missingTools = usedTools.filter(tool => {
1139
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1140
+ return !toolPattern.test(existingToolsImports);
1141
+ });
1142
+
1143
+ if (missingTools.length > 0) {
1144
+ // Add missing tools to existing /tools import
1145
+ const newImports = `${existingToolsImports.trim()}, ${missingTools.join(', ')}`;
386
1146
  result = content.replace(
387
- defaultWithNamedPattern,
388
- `import ${defaultName}, { Blok, ${namedImports.trim()} } from '@jackuait/blok';`
1147
+ toolsImportPattern,
1148
+ `import { ${newImports} } from '@jackuait/blok/tools';`
389
1149
  );
390
1150
  return { result, changed: true };
391
1151
  }
392
1152
  return { result: content, changed: false };
393
1153
  }
394
1154
 
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') {
1155
+ // Check if tools are in the main import (will be split later by splitBlokImports)
1156
+ if (mainMatch) {
1157
+ const existingMainImports = mainMatch[1];
1158
+ const toolsInMain = usedTools.filter(tool => {
1159
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1160
+ return toolPattern.test(existingMainImports);
1161
+ });
1162
+
1163
+ // If all tools are already in main import, don't add duplicate
1164
+ // They'll be moved by splitBlokImports
1165
+ if (toolsInMain.length === usedTools.length) {
1166
+ return { result: content, changed: false };
1167
+ }
1168
+
1169
+ // Some tools missing from both imports - add them to main (will be split later)
1170
+ const missingTools = usedTools.filter(tool => {
1171
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1172
+ return !toolPattern.test(existingMainImports);
1173
+ });
1174
+
1175
+ if (missingTools.length > 0) {
1176
+ const newImports = `${missingTools.join(', ')}, ${existingMainImports.trim()}`;
401
1177
  result = content.replace(
402
- defaultOnlyPattern,
403
- `import ${defaultName}, { Blok } from '@jackuait/blok';`
1178
+ mainImportPattern,
1179
+ `import { ${newImports} } from '@jackuait/blok';`
404
1180
  );
405
1181
  return { result, changed: true };
406
1182
  }
407
1183
  return { result: content, changed: false };
408
1184
  }
409
1185
 
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
1186
+ // No @jackuait/blok or @jackuait/blok/tools import -> add new /tools import
412
1187
  const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1188
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok/tools';`;
1189
+
413
1190
  if (importStatements && importStatements.length > 0) {
414
1191
  const lastImport = importStatements[importStatements.length - 1];
415
1192
  const lastImportIndex = content.lastIndexOf(lastImport);
416
1193
  const insertPosition = lastImportIndex + lastImport.length;
417
1194
  result =
418
1195
  content.slice(0, insertPosition) +
419
- "\nimport Blok from '@jackuait/blok';" +
1196
+ '\n' + newImport +
420
1197
  content.slice(insertPosition);
421
1198
  } else {
422
1199
  // No imports found, add at the very beginning (after shebang if present)
423
1200
  const shebangMatch = content.match(/^#!.*\n/);
424
1201
  if (shebangMatch) {
425
- result = shebangMatch[0] + "import Blok from '@jackuait/blok';\n" + content.slice(shebangMatch[0].length);
1202
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
426
1203
  } else {
427
- result = "import Blok from '@jackuait/blok';\n" + content;
1204
+ result = newImport + '\n' + content;
428
1205
  }
429
1206
  }
430
1207
 
431
1208
  return { result, changed: true };
432
1209
  }
433
1210
 
434
- function transformFile(filePath, dryRun = false) {
1211
+ function transformFile(filePath, dryRun = false, useLibraryI18n = false) {
435
1212
  const content = fs.readFileSync(filePath, 'utf8');
436
1213
  let transformed = content;
437
1214
  const allChanges = [];
@@ -497,12 +1274,28 @@ function transformFile(filePath, dryRun = false) {
497
1274
  allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
498
1275
  }
499
1276
 
500
- // Ensure Blok is imported if bundled tools are used (JS/TS only)
1277
+ // Remove internal tools configuration (Convert and Delete are now bundled) (JS/TS only)
501
1278
  if (isJsFile) {
502
- const { result, changed } = ensureBlokImport(transformed);
1279
+ const { result, changes } = applyTransforms(transformed, INTERNAL_TOOL_REMOVAL_TRANSFORMS, filePath);
1280
+ transformed = result;
1281
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'internal-tools-removal' })));
1282
+ }
1283
+
1284
+ // Ensure tools are imported if bundled tools are used (JS/TS only)
1285
+ if (isJsFile) {
1286
+ const { result, changed } = ensureToolsImport(transformed);
1287
+ if (changed) {
1288
+ transformed = result;
1289
+ allChanges.push({ category: 'imports', pattern: 'ensureToolsImport', count: 1, note: 'Added tools import from @jackuait/blok/tools' });
1290
+ }
1291
+ }
1292
+
1293
+ // Split combined @jackuait/blok imports into core and tools (JS/TS only)
1294
+ if (isJsFile) {
1295
+ const { result, changed } = splitBlokImports(transformed);
503
1296
  if (changed) {
504
1297
  transformed = result;
505
- allChanges.push({ category: 'imports', pattern: 'ensureBlokImport', count: 1, note: 'Added Blok import for bundled tools' });
1298
+ allChanges.push({ category: 'imports', pattern: 'splitBlokImports', count: 1, note: 'Split tools into separate @jackuait/blok/tools import' });
506
1299
  }
507
1300
  }
508
1301
 
@@ -513,6 +1306,25 @@ function transformFile(filePath, dryRun = false) {
513
1306
  allChanges.push(...changes.map((c) => ({ ...c, category: 'text' })));
514
1307
  }
515
1308
 
1309
+ // Apply i18n transforms (JS/TS only)
1310
+ if (isJsFile) {
1311
+ if (useLibraryI18n) {
1312
+ // Remove custom messages to use Blok's built-in translations
1313
+ const { result, changed } = removeI18nMessages(transformed);
1314
+ if (changed) {
1315
+ transformed = result;
1316
+ allChanges.push({ category: 'i18n', pattern: 'removeI18nMessages', count: 1, note: 'Removed custom i18n messages to use library translations' });
1317
+ }
1318
+ } else {
1319
+ // Flatten nested messages to dot-notation
1320
+ const { result, changed } = transformI18nConfig(transformed);
1321
+ if (changed) {
1322
+ transformed = result;
1323
+ allChanges.push({ category: 'i18n', pattern: 'flattenI18nMessages', count: 1, note: 'Flattened nested i18n messages to dot-notation' });
1324
+ }
1325
+ }
1326
+ }
1327
+
516
1328
  const hasChanges = transformed !== content;
517
1329
 
518
1330
  if (hasChanges && !dryRun) {
@@ -536,7 +1348,7 @@ function updatePackageJson(packageJsonPath, dryRun = false) {
536
1348
  const changes = [];
537
1349
 
538
1350
  // Track dependencies to remove
539
- const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph'];
1351
+ const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph', '@editorjs/list'];
540
1352
  const devDepsToRemove = [...depsToRemove];
541
1353
 
542
1354
  // Check and update dependencies
@@ -589,9 +1401,10 @@ Arguments:
589
1401
  path Directory or file to transform (default: current directory)
590
1402
 
591
1403
  Options:
592
- --dry-run Show changes without modifying files
593
- --verbose Show detailed output for each file
594
- --help Show this help message
1404
+ --dry-run Show changes without modifying files
1405
+ --verbose Show detailed output for each file
1406
+ --use-library-i18n Remove custom i18n messages and use Blok's built-in translations
1407
+ --help Show this help message
595
1408
 
596
1409
  Examples:
597
1410
  npx -p @jackuait/blok migrate-from-editorjs ./src
@@ -599,17 +1412,24 @@ Examples:
599
1412
  npx -p @jackuait/blok migrate-from-editorjs . --verbose
600
1413
 
601
1414
  What this codemod does:
602
- • Transforms EditorJS imports to Blok imports
1415
+
1416
+ EditorJS Migration:
1417
+ • Transforms EditorJS imports to Blok named imports:
1418
+ - import EditorJS from '@editorjs/editorjs' → import { Blok } from '@jackuait/blok'
1419
+ - import Editor from '@editorjs/editorjs' → import { Blok as Editor } from '@jackuait/blok'
1420
+ • Updates bundled tool imports (Header, Paragraph, List):
1421
+ - import Header from '@editorjs/header' → import { Header } from '@jackuait/blok/tools'
1422
+ - import List from '@editorjs/list' → import { List } from '@jackuait/blok/tools'
603
1423
  • Updates type names (EditorConfig → BlokConfig)
604
1424
  • Replaces 'new EditorJS()' with 'new Blok()'
605
1425
  • Converts CSS class selectors to data attributes:
606
1426
  - .codex-editor* → [data-blok-editor], [data-blok-redactor], etc.
607
1427
  - .ce-block* → [data-blok-element], [data-blok-selected], etc.
608
1428
  - .ce-toolbar* → [data-blok-toolbar], [data-blok-settings-toggler], etc.
609
- - .ce-toolbox* → [data-blok-toolbox], etc.
1429
+ - .ce-toolbox* → [data-blok-toolbox], [data-blok-toolbox-opened], etc.
610
1430
  - .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.
1431
+ - .ce-popover* → [data-blok-popover], [data-blok-popover-opened], [data-blok-popover-container], etc.
1432
+ - .ce-popover-item* → [data-blok-testid="popover-item*"], [data-blok-popover-item-confirmation], etc.
613
1433
  - .ce-paragraph, .ce-header → [data-blok-tool="paragraph|header"]
614
1434
  - .cdx-list* → [data-blok-list], [data-blok-list-item], etc.
615
1435
  - .cdx-button, .cdx-input, .cdx-loader → [data-blok-button], etc.
@@ -617,8 +1437,32 @@ What this codemod does:
617
1437
  • Updates data attributes (data-id → data-blok-id)
618
1438
  • Changes default holder from 'editorjs' to 'blok'
619
1439
  • Updates package.json dependencies
620
- Converts bundled tool imports (Header, Paragraph)
621
- Ensures Blok is imported when using bundled tools (Blok.Header, etc.)
1440
+ Transforms nested i18n messages to flat dot-notation format with camelCase keys:
1441
+ - { ui: { toolbar: { Add: "..." } } } → { "ui.toolbar.add": "..." }
1442
+ - { toolNames: { "Nothing found": "..." } } → { "toolNames.nothingFound": "..." }
1443
+ • Maps changed i18n keys to their Blok equivalents (camelCase):
1444
+ - "Click to tune" → "clickToOpenMenu"
1445
+ - "or drag to move" → "dragToMove"
1446
+ - "Add" (toolbar) → "clickToAddBelow"
1447
+ - "Filter" (popover) → "search"
1448
+ - "Ordered List" → "numberedList"
1449
+ - "Unordered List" → "bulletedList"
1450
+ • Removes obsolete keys (moveUp/moveDown replaced with drag)
1451
+
1452
+ Old Blok → Modular Blok Migration:
1453
+ • Splits combined imports into core and tools:
1454
+ - import { Blok, Header } from '@jackuait/blok'
1455
+ → import { Blok } from '@jackuait/blok';
1456
+ import { Header } from '@jackuait/blok/tools';
1457
+ • Transforms static property access to direct references:
1458
+ - Blok.Header → Header (with import added)
1459
+ - Blok.Paragraph → Paragraph
1460
+ - Blok.List → List
1461
+ - Blok.Bold, Blok.Italic, Blok.Link → Bold, Italic, Link
1462
+ • Removes internal tools configuration (Convert and Delete are now bundled with core):
1463
+ - convertTo: { class: Convert } → removed (automatically available)
1464
+ - delete: { class: Delete } → removed (automatically available)
1465
+ • Ensures tools are imported from @jackuait/blok/tools when used
622
1466
 
623
1467
  Note: After running, you may need to manually:
624
1468
  • Update any custom tool implementations
@@ -633,6 +1477,7 @@ function main() {
633
1477
  // Parse arguments
634
1478
  const dryRun = args.includes('--dry-run');
635
1479
  const verbose = args.includes('--verbose');
1480
+ const useLibraryI18n = args.includes('--use-library-i18n');
636
1481
  const help = args.includes('--help') || args.includes('-h');
637
1482
 
638
1483
  global.isVerbose = verbose;
@@ -675,7 +1520,7 @@ function main() {
675
1520
 
676
1521
  // Process each file
677
1522
  files.forEach((filePath) => {
678
- const result = transformFile(filePath, dryRun);
1523
+ const result = transformFile(filePath, dryRun, useLibraryI18n);
679
1524
 
680
1525
  if (result.hasChanges) {
681
1526
  stats.filesModified++;
@@ -741,7 +1586,20 @@ module.exports = {
741
1586
  updatePackageJson,
742
1587
  applyTransforms,
743
1588
  ensureBlokImport,
1589
+ ensureToolsImport,
1590
+ splitBlokImports,
1591
+ normalizeKey,
1592
+ flattenI18nDictionary,
1593
+ transformI18nConfig,
1594
+ removeI18nMessages,
1595
+ parseObjectLiteral,
1596
+ findMatchingBrace,
1597
+ objectToString,
1598
+ I18N_KEY_MAPPINGS,
744
1599
  BUNDLED_TOOLS,
1600
+ INLINE_TOOLS,
1601
+ INTERNAL_TOOLS,
1602
+ ALL_TOOLS,
745
1603
  IMPORT_TRANSFORMS,
746
1604
  TYPE_TRANSFORMS,
747
1605
  CLASS_NAME_TRANSFORMS,
@@ -750,5 +1608,6 @@ module.exports = {
750
1608
  SELECTOR_TRANSFORMS,
751
1609
  HOLDER_TRANSFORMS,
752
1610
  TOOL_CONFIG_TRANSFORMS,
1611
+ INTERNAL_TOOL_REMOVAL_TRANSFORMS,
753
1612
  TEXT_TRANSFORMS,
754
1613
  };