@jackuait/blok 0.4.1-beta.0 → 0.4.1-beta.11

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 (402) hide show
  1. package/README.md +138 -17
  2. package/codemod/README.md +45 -7
  3. package/codemod/migrate-editorjs-to-blok.js +960 -92
  4. package/codemod/test.js +780 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-oNSQ3HA6.mjs +13217 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-BdNRw4n4.mjs +43 -0
  9. package/dist/{index-OwEtDFlk.mjs → chunks/index-DHgXmfki.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-CRqgjRim.mjs +1989 -0
  11. package/dist/chunks/messages-0tDXLuyH.mjs +48 -0
  12. package/dist/chunks/messages-2_xedlYw.mjs +48 -0
  13. package/dist/chunks/messages-AHESHJm_.mjs +48 -0
  14. package/dist/chunks/messages-B5hdXZwA.mjs +48 -0
  15. package/dist/chunks/messages-B5jGUnOy.mjs +48 -0
  16. package/dist/chunks/messages-B5puUm7R.mjs +48 -0
  17. package/dist/chunks/messages-B66ZSDCJ.mjs +48 -0
  18. package/dist/chunks/messages-B9Oba7sq.mjs +48 -0
  19. package/dist/chunks/messages-BA0rcTCY.mjs +48 -0
  20. package/dist/chunks/messages-BBJgd5jG.mjs +48 -0
  21. package/dist/chunks/messages-BPqWKx5Z.mjs +48 -0
  22. package/dist/chunks/messages-Bdv-IkfG.mjs +48 -0
  23. package/dist/chunks/messages-BeUhMpsr.mjs +48 -0
  24. package/dist/chunks/messages-Bf6Y3_GI.mjs +48 -0
  25. package/dist/chunks/messages-BiExzWJv.mjs +48 -0
  26. package/dist/chunks/messages-BlpqL8vG.mjs +48 -0
  27. package/dist/chunks/messages-BmKCChWZ.mjs +48 -0
  28. package/dist/chunks/messages-Bn253WWC.mjs +48 -0
  29. package/dist/chunks/messages-BrJHUxQL.mjs +48 -0
  30. package/dist/chunks/messages-C5b7hr_E.mjs +48 -0
  31. package/dist/chunks/messages-C7I_AVH2.mjs +48 -0
  32. package/dist/chunks/messages-CJoBtXU6.mjs +48 -0
  33. package/dist/chunks/messages-CQj2JU2j.mjs +48 -0
  34. package/dist/chunks/messages-CUZ1x1QD.mjs +48 -0
  35. package/dist/chunks/messages-CUy1vn-b.mjs +48 -0
  36. package/dist/chunks/messages-CVeWVKsV.mjs +48 -0
  37. package/dist/chunks/messages-CXHd9SUK.mjs +48 -0
  38. package/dist/chunks/messages-CbMyJSzS.mjs +48 -0
  39. package/dist/chunks/messages-CbhuIWRJ.mjs +48 -0
  40. package/dist/chunks/messages-CeCjVKMW.mjs +48 -0
  41. package/dist/chunks/messages-Cj-t1bdy.mjs +48 -0
  42. package/dist/chunks/messages-CkFT2gle.mjs +48 -0
  43. package/dist/chunks/messages-Cm9aLHeX.mjs +48 -0
  44. package/dist/chunks/messages-CnvW8Slp.mjs +48 -0
  45. package/dist/chunks/messages-Cr-RJ7YB.mjs +48 -0
  46. package/dist/chunks/messages-CrsJ1TEJ.mjs +48 -0
  47. package/dist/chunks/messages-Cu08aLS3.mjs +48 -0
  48. package/dist/chunks/messages-CvaqJFN-.mjs +48 -0
  49. package/dist/chunks/messages-CyDU5lz9.mjs +48 -0
  50. package/dist/chunks/messages-CySyfkMU.mjs +48 -0
  51. package/dist/chunks/messages-Cyi2AMmz.mjs +48 -0
  52. package/dist/chunks/messages-D00OjS2n.mjs +48 -0
  53. package/dist/chunks/messages-DDLgIPDF.mjs +48 -0
  54. package/dist/chunks/messages-DMQIHGRj.mjs +48 -0
  55. package/dist/chunks/messages-DOlC_Tty.mjs +48 -0
  56. package/dist/chunks/messages-DV6shA9b.mjs +48 -0
  57. package/dist/chunks/messages-DY94ykcE.mjs +48 -0
  58. package/dist/chunks/messages-DbVquYKN.mjs +48 -0
  59. package/dist/chunks/messages-DcKOuncK.mjs +48 -0
  60. package/dist/chunks/messages-Dg92dXZ5.mjs +48 -0
  61. package/dist/chunks/messages-DnbbyJT3.mjs +48 -0
  62. package/dist/chunks/messages-DteYq0rv.mjs +48 -0
  63. package/dist/chunks/messages-GC2PhgV3.mjs +48 -0
  64. package/dist/chunks/messages-JGsXAReJ.mjs +48 -0
  65. package/dist/chunks/messages-JZUhXTuV.mjs +48 -0
  66. package/dist/chunks/messages-LvFKBBPa.mjs +48 -0
  67. package/dist/chunks/messages-NP1myMGI.mjs +48 -0
  68. package/dist/chunks/messages-Q4kc_ZtL.mjs +48 -0
  69. package/dist/chunks/messages-RvMHb2Ht.mjs +48 -0
  70. package/dist/chunks/messages-ftMcCEuO.mjs +48 -0
  71. package/dist/chunks/messages-o24dK6CU.mjs +48 -0
  72. package/dist/chunks/messages-pA5TvcAj.mjs +48 -0
  73. package/dist/chunks/messages-rRSHQDCX.mjs +48 -0
  74. package/dist/chunks/messages-srxrv8Yh.mjs +48 -0
  75. package/dist/chunks/messages-wdqp4610.mjs +48 -0
  76. package/dist/chunks/messages-zS1AXZ0y.mjs +48 -0
  77. package/dist/chunks/messages-zSzDzXej.mjs +48 -0
  78. package/dist/full.mjs +50 -0
  79. package/dist/locales.mjs +228 -0
  80. package/dist/messages-0tDXLuyH.mjs +48 -0
  81. package/dist/messages-2_xedlYw.mjs +48 -0
  82. package/dist/messages-AHESHJm_.mjs +48 -0
  83. package/dist/messages-B5hdXZwA.mjs +48 -0
  84. package/dist/messages-B5jGUnOy.mjs +48 -0
  85. package/dist/messages-B5puUm7R.mjs +48 -0
  86. package/dist/messages-B66ZSDCJ.mjs +48 -0
  87. package/dist/messages-B9Oba7sq.mjs +48 -0
  88. package/dist/messages-BA0rcTCY.mjs +48 -0
  89. package/dist/messages-BBJgd5jG.mjs +48 -0
  90. package/dist/messages-BPqWKx5Z.mjs +48 -0
  91. package/dist/messages-Bdv-IkfG.mjs +48 -0
  92. package/dist/messages-BeUhMpsr.mjs +48 -0
  93. package/dist/messages-Bf6Y3_GI.mjs +48 -0
  94. package/dist/messages-BiExzWJv.mjs +48 -0
  95. package/dist/messages-BlpqL8vG.mjs +48 -0
  96. package/dist/messages-BmKCChWZ.mjs +48 -0
  97. package/dist/messages-Bn253WWC.mjs +48 -0
  98. package/dist/messages-BrJHUxQL.mjs +48 -0
  99. package/dist/messages-C5b7hr_E.mjs +48 -0
  100. package/dist/messages-C7I_AVH2.mjs +48 -0
  101. package/dist/messages-CJoBtXU6.mjs +48 -0
  102. package/dist/messages-CQj2JU2j.mjs +48 -0
  103. package/dist/messages-CUZ1x1QD.mjs +48 -0
  104. package/dist/messages-CUy1vn-b.mjs +48 -0
  105. package/dist/messages-CVeWVKsV.mjs +48 -0
  106. package/dist/messages-CXHd9SUK.mjs +48 -0
  107. package/dist/messages-CbMyJSzS.mjs +48 -0
  108. package/dist/messages-CbhuIWRJ.mjs +48 -0
  109. package/dist/messages-CeCjVKMW.mjs +48 -0
  110. package/dist/messages-Cj-t1bdy.mjs +48 -0
  111. package/dist/messages-CkFT2gle.mjs +48 -0
  112. package/dist/messages-Cm9aLHeX.mjs +48 -0
  113. package/dist/messages-CnvW8Slp.mjs +48 -0
  114. package/dist/messages-Cr-RJ7YB.mjs +48 -0
  115. package/dist/messages-CrsJ1TEJ.mjs +48 -0
  116. package/dist/messages-Cu08aLS3.mjs +48 -0
  117. package/dist/messages-CvaqJFN-.mjs +48 -0
  118. package/dist/messages-CyDU5lz9.mjs +48 -0
  119. package/dist/messages-CySyfkMU.mjs +48 -0
  120. package/dist/messages-Cyi2AMmz.mjs +48 -0
  121. package/dist/messages-D00OjS2n.mjs +48 -0
  122. package/dist/messages-DDLgIPDF.mjs +48 -0
  123. package/dist/messages-DMQIHGRj.mjs +48 -0
  124. package/dist/messages-DOlC_Tty.mjs +48 -0
  125. package/dist/messages-DV6shA9b.mjs +48 -0
  126. package/dist/messages-DY94ykcE.mjs +48 -0
  127. package/dist/messages-DbVquYKN.mjs +48 -0
  128. package/dist/messages-DcKOuncK.mjs +48 -0
  129. package/dist/messages-Dg92dXZ5.mjs +48 -0
  130. package/dist/messages-DnbbyJT3.mjs +48 -0
  131. package/dist/messages-DteYq0rv.mjs +48 -0
  132. package/dist/messages-GC2PhgV3.mjs +48 -0
  133. package/dist/messages-JGsXAReJ.mjs +48 -0
  134. package/dist/messages-JZUhXTuV.mjs +48 -0
  135. package/dist/messages-LvFKBBPa.mjs +48 -0
  136. package/dist/messages-NP1myMGI.mjs +48 -0
  137. package/dist/messages-Q4kc_ZtL.mjs +48 -0
  138. package/dist/messages-RvMHb2Ht.mjs +48 -0
  139. package/dist/messages-ftMcCEuO.mjs +48 -0
  140. package/dist/messages-o24dK6CU.mjs +48 -0
  141. package/dist/messages-pA5TvcAj.mjs +48 -0
  142. package/dist/messages-rRSHQDCX.mjs +48 -0
  143. package/dist/messages-srxrv8Yh.mjs +48 -0
  144. package/dist/messages-wdqp4610.mjs +48 -0
  145. package/dist/messages-zS1AXZ0y.mjs +48 -0
  146. package/dist/messages-zSzDzXej.mjs +48 -0
  147. package/dist/tools.mjs +3117 -0
  148. package/dist/vendor.LICENSE.txt +26 -225
  149. package/package.json +63 -24
  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 +1428 -0
  154. package/src/components/block-tunes/block-tune-delete.ts +51 -0
  155. package/src/components/blocks.ts +352 -0
  156. package/src/components/constants/data-attributes.ts +344 -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 +497 -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 +45 -0
  177. package/src/components/i18n/locales/ar/messages.json +45 -0
  178. package/src/components/i18n/locales/az/messages.json +45 -0
  179. package/src/components/i18n/locales/bg/messages.json +45 -0
  180. package/src/components/i18n/locales/bn/messages.json +45 -0
  181. package/src/components/i18n/locales/bs/messages.json +45 -0
  182. package/src/components/i18n/locales/cs/messages.json +45 -0
  183. package/src/components/i18n/locales/da/messages.json +45 -0
  184. package/src/components/i18n/locales/de/messages.json +45 -0
  185. package/src/components/i18n/locales/dv/messages.json +45 -0
  186. package/src/components/i18n/locales/el/messages.json +45 -0
  187. package/src/components/i18n/locales/en/messages.json +45 -0
  188. package/src/components/i18n/locales/es/messages.json +45 -0
  189. package/src/components/i18n/locales/et/messages.json +45 -0
  190. package/src/components/i18n/locales/fa/messages.json +45 -0
  191. package/src/components/i18n/locales/fi/messages.json +45 -0
  192. package/src/components/i18n/locales/fil/messages.json +45 -0
  193. package/src/components/i18n/locales/fr/messages.json +45 -0
  194. package/src/components/i18n/locales/gu/messages.json +45 -0
  195. package/src/components/i18n/locales/he/messages.json +45 -0
  196. package/src/components/i18n/locales/hi/messages.json +45 -0
  197. package/src/components/i18n/locales/hr/messages.json +45 -0
  198. package/src/components/i18n/locales/hu/messages.json +45 -0
  199. package/src/components/i18n/locales/hy/messages.json +45 -0
  200. package/src/components/i18n/locales/id/messages.json +45 -0
  201. package/src/components/i18n/locales/index.ts +231 -0
  202. package/src/components/i18n/locales/it/messages.json +45 -0
  203. package/src/components/i18n/locales/ja/messages.json +45 -0
  204. package/src/components/i18n/locales/ka/messages.json +45 -0
  205. package/src/components/i18n/locales/km/messages.json +45 -0
  206. package/src/components/i18n/locales/kn/messages.json +45 -0
  207. package/src/components/i18n/locales/ko/messages.json +45 -0
  208. package/src/components/i18n/locales/ku/messages.json +45 -0
  209. package/src/components/i18n/locales/lo/messages.json +45 -0
  210. package/src/components/i18n/locales/lt/messages.json +45 -0
  211. package/src/components/i18n/locales/lv/messages.json +45 -0
  212. package/src/components/i18n/locales/mk/messages.json +45 -0
  213. package/src/components/i18n/locales/ml/messages.json +45 -0
  214. package/src/components/i18n/locales/mn/messages.json +45 -0
  215. package/src/components/i18n/locales/mr/messages.json +45 -0
  216. package/src/components/i18n/locales/ms/messages.json +45 -0
  217. package/src/components/i18n/locales/my/messages.json +45 -0
  218. package/src/components/i18n/locales/ne/messages.json +45 -0
  219. package/src/components/i18n/locales/nl/messages.json +45 -0
  220. package/src/components/i18n/locales/no/messages.json +45 -0
  221. package/src/components/i18n/locales/pa/messages.json +45 -0
  222. package/src/components/i18n/locales/pl/messages.json +45 -0
  223. package/src/components/i18n/locales/ps/messages.json +45 -0
  224. package/src/components/i18n/locales/pt/messages.json +45 -0
  225. package/src/components/i18n/locales/ro/messages.json +45 -0
  226. package/src/components/i18n/locales/ru/messages.json +45 -0
  227. package/src/components/i18n/locales/sd/messages.json +45 -0
  228. package/src/components/i18n/locales/si/messages.json +45 -0
  229. package/src/components/i18n/locales/sk/messages.json +45 -0
  230. package/src/components/i18n/locales/sl/messages.json +45 -0
  231. package/src/components/i18n/locales/sq/messages.json +45 -0
  232. package/src/components/i18n/locales/sr/messages.json +45 -0
  233. package/src/components/i18n/locales/sv/messages.json +45 -0
  234. package/src/components/i18n/locales/sw/messages.json +45 -0
  235. package/src/components/i18n/locales/ta/messages.json +45 -0
  236. package/src/components/i18n/locales/te/messages.json +45 -0
  237. package/src/components/i18n/locales/th/messages.json +45 -0
  238. package/src/components/i18n/locales/tr/messages.json +45 -0
  239. package/src/components/i18n/locales/ug/messages.json +45 -0
  240. package/src/components/i18n/locales/uk/messages.json +45 -0
  241. package/src/components/i18n/locales/ur/messages.json +45 -0
  242. package/src/components/i18n/locales/vi/messages.json +45 -0
  243. package/src/components/i18n/locales/yi/messages.json +45 -0
  244. package/src/components/i18n/locales/zh/messages.json +45 -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 +377 -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 +35 -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 +1591 -0
  269. package/src/components/modules/blockManager.ts +1356 -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 +1204 -0
  274. package/src/components/modules/history.ts +1098 -0
  275. package/src/components/modules/i18n.ts +332 -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 +711 -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 +781 -0
  284. package/src/components/modules/toolbar/index.ts +1315 -0
  285. package/src/components/modules/toolbar/inline.ts +956 -0
  286. package/src/components/modules/tools.ts +625 -0
  287. package/src/components/modules/ui.ts +1283 -0
  288. package/src/components/polyfills.ts +113 -0
  289. package/src/components/selection.ts +1179 -0
  290. package/src/components/tools/base.ts +301 -0
  291. package/src/components/tools/block.ts +339 -0
  292. package/src/components/tools/collection.ts +67 -0
  293. package/src/components/tools/factory.ts +138 -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 +601 -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 +680 -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 +186 -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 +178 -0
  330. package/src/components/utils/popover/components/search-input/search-input.types.ts +59 -0
  331. package/src/components/utils/popover/index.ts +13 -0
  332. package/src/components/utils/popover/popover-abstract.ts +457 -0
  333. package/src/components/utils/popover/popover-desktop.ts +676 -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 +110 -0
  344. package/src/components/utils/tooltip.ts +591 -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 +646 -0
  367. package/src/tools/index.ts +45 -0
  368. package/src/tools/list/index.ts +1819 -0
  369. package/src/tools/paragraph/index.ts +412 -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 +9 -1
  378. package/types/api/history.d.ts +7 -0
  379. package/types/api/i18n.d.ts +22 -3
  380. package/types/api/selection.d.ts +6 -0
  381. package/types/api/styles.d.ts +23 -10
  382. package/types/configs/blok-config.d.ts +29 -0
  383. package/types/configs/i18n-config.d.ts +52 -2
  384. package/types/configs/i18n-dictionary.d.ts +16 -90
  385. package/types/data-attributes.d.ts +170 -0
  386. package/types/data-formats/output-data.d.ts +15 -0
  387. package/types/full.d.ts +80 -0
  388. package/types/index.d.ts +30 -13
  389. package/types/locales.d.ts +59 -0
  390. package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
  391. package/types/tools/block-tool.d.ts +9 -0
  392. package/types/tools/header.d.ts +18 -0
  393. package/types/tools/index.d.ts +1 -0
  394. package/types/tools/list.d.ts +91 -0
  395. package/types/tools/paragraph.d.ts +71 -0
  396. package/types/tools/tool-settings.d.ts +92 -6
  397. package/types/tools/tool.d.ts +6 -0
  398. package/types/tools-entry.d.ts +49 -0
  399. package/types/utils/popover/popover-item.d.ts +18 -5
  400. package/types/utils/popover/popover.d.ts +7 -0
  401. package/dist/blok-D_baBvTG.mjs +0 -25795
  402. package/dist/blok.umd.js +0 -181
package/codemod/test.js CHANGED
@@ -5,7 +5,16 @@
5
5
  const {
6
6
  applyTransforms,
7
7
  ensureBlokImport,
8
+ ensureToolsImport,
9
+ splitBlokImports,
10
+ normalizeKey,
11
+ flattenI18nDictionary,
12
+ transformI18nConfig,
13
+ removeI18nMessages,
14
+ I18N_KEY_MAPPINGS,
8
15
  BUNDLED_TOOLS,
16
+ INLINE_TOOLS,
17
+ ALL_TOOLS,
9
18
  IMPORT_TRANSFORMS,
10
19
  TYPE_TRANSFORMS,
11
20
  CLASS_NAME_TRANSFORMS,
@@ -40,28 +49,128 @@ function assertEqual(actual, expected, message = '') {
40
49
 
41
50
  console.log('\n📦 Import Transformations\n');
42
51
 
43
- test('transforms @editorjs/editorjs import', () => {
52
+ test('transforms @editorjs/editorjs default import to named import', () => {
44
53
  const input = `import EditorJS from '@editorjs/editorjs';`;
45
54
  const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
46
- assertEqual(result, `import EditorJS from '@jackuait/blok';`);
55
+ assertEqual(result, `import { Blok } from '@jackuait/blok';`);
56
+ });
57
+
58
+ test('transforms @editorjs/editorjs aliased default import', () => {
59
+ const input = `import Editor from '@editorjs/editorjs';`;
60
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
61
+ assertEqual(result, `import { Blok as Editor } from '@jackuait/blok';`);
47
62
  });
48
63
 
49
64
  test('transforms require statement', () => {
50
65
  const input = `const EditorJS = require('@editorjs/editorjs');`;
51
66
  const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
52
- assertEqual(result, `const EditorJS = require('@jackuait/blok');`);
67
+ assertEqual(result, `const EditorJS = require('@jackuait/blok').Blok;`);
68
+ });
69
+
70
+ test('transforms namespace import to named import', () => {
71
+ const input = `import * as EditorJS from '@editorjs/editorjs';`;
72
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
73
+ assertEqual(result, `import { Blok as EditorJS } from '@jackuait/blok';`);
53
74
  });
54
75
 
55
- test('transforms @editorjs/header import', () => {
76
+ test('transforms destructured default require', () => {
77
+ const input = `const { default: EditorJS } = require('@editorjs/editorjs');`;
78
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
79
+ assertEqual(result, `const { Blok: EditorJS } = require('@jackuait/blok');`);
80
+ });
81
+
82
+ test('transforms dynamic import', () => {
83
+ const input = `const Editor = await import('@editorjs/editorjs');`;
84
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
85
+ assertEqual(result, `const Editor = await import('@jackuait/blok').then(m => ({ default: m.Blok }));`);
86
+ });
87
+
88
+ test('transforms dynamic import with .then(m => m.default) pattern', () => {
89
+ const input = `import('@editorjs/editorjs').then(m => m.default);`;
90
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
91
+ assertEqual(result, `import('@jackuait/blok').then(m => m.Blok);`);
92
+ });
93
+
94
+ test('transforms type-only default import', () => {
95
+ const input = `import type EditorJS from '@editorjs/editorjs';`;
96
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
97
+ assertEqual(result, `import type { Blok as EditorJS } from '@jackuait/blok';`);
98
+ });
99
+
100
+ test('transforms @editorjs/header import to named import', () => {
56
101
  const input = `import Header from '@editorjs/header';`;
57
102
  const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
58
- assertEqual(result, `// Header is now bundled with Blok: use Blok.Header\n`);
103
+ assertEqual(result, `import { Header } from '@jackuait/blok';\n`);
59
104
  });
60
105
 
61
- test('transforms @editorjs/paragraph import', () => {
106
+ test('transforms @editorjs/paragraph import to named import', () => {
62
107
  const input = `import Paragraph from '@editorjs/paragraph';`;
63
108
  const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
64
- assertEqual(result, `// Paragraph is now bundled with Blok: use Blok.Paragraph\n`);
109
+ assertEqual(result, `import { Paragraph } from '@jackuait/blok';\n`);
110
+ });
111
+
112
+ test('transforms @editorjs/list import to named import', () => {
113
+ const input = `import List from '@editorjs/list';`;
114
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
115
+ assertEqual(result, `import { List } from '@jackuait/blok';\n`);
116
+ });
117
+
118
+ // Combined default + named import tests
119
+ test('transforms combined default + named import', () => {
120
+ const input = `import EditorJS, { EditorConfig } from '@editorjs/editorjs';`;
121
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
122
+ assertEqual(result, `import { Blok, EditorConfig } from '@jackuait/blok';`);
123
+ });
124
+
125
+ test('transforms aliased combined default + named import', () => {
126
+ const input = `import Editor, { EditorConfig } from '@editorjs/editorjs';`;
127
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
128
+ assertEqual(result, `import { Blok as Editor, EditorConfig } from '@jackuait/blok';`);
129
+ });
130
+
131
+ // Re-export tests
132
+ test('transforms default re-export as named', () => {
133
+ const input = `export { default as Editor } from '@editorjs/editorjs';`;
134
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
135
+ assertEqual(result, `export { Blok as Editor } from '@jackuait/blok';`);
136
+ });
137
+
138
+ test('transforms default re-export', () => {
139
+ const input = `export { default } from '@editorjs/editorjs';`;
140
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
141
+ assertEqual(result, `export { Blok } from '@jackuait/blok';`);
142
+ });
143
+
144
+ // Dynamic import with destructuring
145
+ test('transforms destructured dynamic import', () => {
146
+ const input = `const { default: Editor } = await import('@editorjs/editorjs');`;
147
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
148
+ assertEqual(result, `const { Blok: Editor } = await import('@jackuait/blok');`);
149
+ });
150
+
151
+ // Tool require statements
152
+ test('transforms Header require statement', () => {
153
+ const input = `const Header = require('@editorjs/header');`;
154
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
155
+ assertEqual(result, `const { Header } = require('@jackuait/blok');`);
156
+ });
157
+
158
+ test('transforms aliased Header require statement', () => {
159
+ const input = `const MyHeader = require('@editorjs/header');`;
160
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
161
+ assertEqual(result, `const { Header: MyHeader } = require('@jackuait/blok');`);
162
+ });
163
+
164
+ test('transforms Paragraph require statement', () => {
165
+ const input = `const Paragraph = require('@editorjs/paragraph');`;
166
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
167
+ assertEqual(result, `const { Paragraph } = require('@jackuait/blok');`);
168
+ });
169
+
170
+ test('transforms List require statement', () => {
171
+ const input = `const List = require('@editorjs/list');`;
172
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
173
+ assertEqual(result, `const { List } = require('@jackuait/blok');`);
65
174
  });
66
175
 
67
176
  // ============================================================================
@@ -193,7 +302,7 @@ test('transforms .ce-popover class', () => {
193
302
  test('transforms .ce-popover--opened class', () => {
194
303
  const input = `.ce-popover--opened { display: block; }`;
195
304
  const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
196
- assertEqual(result, `[data-blok-popover][data-blok-opened="true"] { display: block; }`);
305
+ assertEqual(result, `[data-blok-popover-opened="true"] { display: block; }`);
197
306
  });
198
307
 
199
308
  test('transforms .ce-popover__container class', () => {
@@ -292,22 +401,40 @@ test('transforms getElementById call', () => {
292
401
 
293
402
  console.log('\n🔧 Tool Config Transformations\n');
294
403
 
295
- test('transforms class: Header to class: Blok.Header', () => {
296
- const input = `{ class: Header, config: {} }`;
404
+ test('transforms class: Blok.Header to class: Header', () => {
405
+ const input = `{ class: Blok.Header, config: {} }`;
297
406
  const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
298
- assertEqual(result, `{ class: Blok.Header, config: {} }`);
407
+ assertEqual(result, `{ class: Header, config: {} }`);
299
408
  });
300
409
 
301
- test('transforms class: Paragraph to class: Blok.Paragraph', () => {
302
- const input = `{ class: Paragraph, config: {} }`;
410
+ test('transforms class: Blok.Paragraph to class: Paragraph', () => {
411
+ const input = `{ class: Blok.Paragraph, config: {} }`;
303
412
  const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
304
- assertEqual(result, `{ class: Blok.Paragraph, config: {} }`);
413
+ assertEqual(result, `{ class: Paragraph, config: {} }`);
305
414
  });
306
415
 
307
- test('transforms standalone paragraph: Paragraph reference', () => {
308
- const input = `tools: { paragraph: Paragraph, header: Header }`;
416
+ test('transforms standalone Blok.Paragraph reference', () => {
417
+ const input = `tools: { paragraph: Blok.Paragraph, header: Blok.Header }`;
309
418
  const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
310
- assertEqual(result, `tools: { paragraph: Blok.Paragraph, header: Blok.Header }`);
419
+ assertEqual(result, `tools: { paragraph: Paragraph, header: Header }`);
420
+ });
421
+
422
+ test('transforms class: Blok.List to class: List', () => {
423
+ const input = `{ class: Blok.List, config: {} }`;
424
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
425
+ assertEqual(result, `{ class: List, config: {} }`);
426
+ });
427
+
428
+ test('transforms standalone Blok.List reference', () => {
429
+ const input = `tools: { list: Blok.List }`;
430
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
431
+ assertEqual(result, `tools: { list: List }`);
432
+ });
433
+
434
+ test('does not transform ListConfig or ListItem', () => {
435
+ const input = `import { ListConfig, ListItem } from './types';`;
436
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
437
+ assertEqual(result, `import { ListConfig, ListItem } from './types';`);
311
438
  });
312
439
 
313
440
  // ============================================================================
@@ -347,8 +474,9 @@ const editor = new EditorJS({
347
474
  if (!result.includes("holder: 'blok'")) {
348
475
  throw new Error('Holder not transformed');
349
476
  }
350
- if (!result.includes('class: Blok.Header')) {
351
- throw new Error('Tool class not transformed');
477
+ // Header is imported as named import, class: Header stays as-is (not Blok.Header anymore)
478
+ if (!result.includes('class: Header')) {
479
+ throw new Error('Tool class should remain Header (named import)');
352
480
  }
353
481
  });
354
482
 
@@ -365,116 +493,105 @@ test('does not transform unrelated EditorJS-like strings', () => {
365
493
 
366
494
  console.log('\n📥 Ensure Blok Import\n');
367
495
 
368
- test('adds Blok import when using Blok.Header with no existing import', () => {
496
+ test('adds Header import when Header is used with no existing import', () => {
369
497
  const input = `const editor = new Blok({
370
- tools: { header: Blok.Header }
498
+ tools: { header: Header }
371
499
  });`;
372
500
  const { result, changed } = ensureBlokImport(input);
373
501
  assertEqual(changed, true, 'Should indicate change');
374
- assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
502
+ assertEqual(result.includes("import { Header } from '@jackuait/blok';"), true, 'Should add Header import');
375
503
  });
376
504
 
377
- test('adds Blok import after existing imports', () => {
505
+ test('adds tool imports after existing imports', () => {
378
506
  const input = `import React from 'react';
379
507
  import { useState } from 'react';
380
508
 
381
509
  const editor = new Blok({
382
- tools: { header: Blok.Header }
510
+ tools: { header: Header }
383
511
  });`;
384
512
  const { result, changed } = ensureBlokImport(input);
385
513
  assertEqual(changed, true, 'Should indicate change');
386
- // Check that Blok import is added after existing imports
387
- const blokImportIndex = result.indexOf("import Blok from '@jackuait/blok';");
514
+ // Check that import is added after existing imports
515
+ const headerImportIndex = result.indexOf("import { Header } from '@jackuait/blok';");
388
516
  const lastReactImportIndex = result.indexOf("import { useState } from 'react';");
389
- assertEqual(blokImportIndex > lastReactImportIndex, true, 'Blok import should be after existing imports');
517
+ assertEqual(headerImportIndex > lastReactImportIndex, true, 'Header import should be after existing imports');
390
518
  });
391
519
 
392
- test('adds Blok to named-only import from @jackuait/blok', () => {
393
- const input = `import { BlokConfig } from '@jackuait/blok';
520
+ test('adds missing tools to existing @jackuait/blok import', () => {
521
+ const input = `import { Blok } from '@jackuait/blok';
394
522
 
395
- const config: BlokConfig = {
396
- tools: { header: Blok.Header }
397
- };`;
398
- const { result, changed } = ensureBlokImport(input);
399
- assertEqual(changed, true, 'Should indicate change');
400
- assertEqual(result.includes("import Blok, { BlokConfig } from '@jackuait/blok';"), true, 'Should add Blok default import');
401
- });
402
-
403
- test('adds Blok to named imports when default import has different name', () => {
404
- const input = `import Editor, { BlokConfig } from '@jackuait/blok';
405
-
406
- const config: BlokConfig = {
407
- tools: { header: Blok.Header }
408
- };`;
523
+ const editor = new Blok({
524
+ tools: { header: Header }
525
+ });`;
409
526
  const { result, changed } = ensureBlokImport(input);
410
527
  assertEqual(changed, true, 'Should indicate change');
411
- assertEqual(result.includes("import Editor, { Blok, BlokConfig } from '@jackuait/blok';"), true, 'Should add Blok to named imports');
528
+ assertEqual(result.includes('Header, Blok'), true, 'Should add Header to existing import');
412
529
  });
413
530
 
414
- test('adds Blok as named import when default import has different name (no existing named imports)', () => {
415
- const input = `import Editor from '@jackuait/blok';
531
+ test('adds multiple missing tools to existing import', () => {
532
+ const input = `import { Blok } from '@jackuait/blok';
416
533
 
417
- const editor = new Editor({
418
- tools: { header: Blok.Header }
534
+ const editor = new Blok({
535
+ tools: { header: Header, paragraph: Paragraph }
419
536
  });`;
420
537
  const { result, changed } = ensureBlokImport(input);
421
538
  assertEqual(changed, true, 'Should indicate change');
422
- assertEqual(result.includes("import Editor, { Blok } from '@jackuait/blok';"), true, 'Should add Blok as named import');
539
+ assertEqual(result.includes('Header'), true, 'Should include Header');
540
+ assertEqual(result.includes('Paragraph'), true, 'Should include Paragraph');
423
541
  });
424
542
 
425
- test('does not modify when Blok is already default imported', () => {
426
- const input = `import Blok from '@jackuait/blok';
543
+ test('does not modify when tools are already imported', () => {
544
+ const input = `import { Blok, Header } from '@jackuait/blok';
427
545
 
428
546
  const editor = new Blok({
429
- tools: { header: Blok.Header }
547
+ tools: { header: Header }
430
548
  });`;
431
549
  const { result, changed } = ensureBlokImport(input);
432
550
  assertEqual(changed, false, 'Should not indicate change');
433
551
  assertEqual(result, input, 'Content should be unchanged');
434
552
  });
435
553
 
436
- test('does not modify when Blok is already default imported with named imports', () => {
437
- const input = `import Blok, { BlokConfig } from '@jackuait/blok';
554
+ test('does not modify when no bundled tools are used', () => {
555
+ const input = `import { Blok } from '@jackuait/blok';
438
556
 
439
- const editor = new Blok({
440
- tools: { header: Blok.Header }
441
- });`;
557
+ const editor = new Blok({});`;
442
558
  const { result, changed } = ensureBlokImport(input);
443
- assertEqual(changed, false, 'Should not indicate change');
559
+ assertEqual(changed, false, 'Should not indicate change when no tools used');
444
560
  assertEqual(result, input, 'Content should be unchanged');
445
561
  });
446
562
 
447
- test('does not modify when no Blok tools are used', () => {
448
- const input = `import { BlokConfig } from '@jackuait/blok';
449
-
450
- const config: BlokConfig = {};`;
563
+ test('detects Paragraph usage', () => {
564
+ const input = `const editor = new Blok({
565
+ tools: { paragraph: Paragraph }
566
+ });`;
451
567
  const { result, changed } = ensureBlokImport(input);
452
- assertEqual(changed, false, 'Should not indicate change when no Blok.* tools used');
453
- assertEqual(result, input, 'Content should be unchanged');
568
+ assertEqual(changed, true, 'Should detect Paragraph');
569
+ assertEqual(result.includes("import { Paragraph } from '@jackuait/blok';"), true, 'Should add Paragraph import');
454
570
  });
455
571
 
456
- test('detects Blok.Paragraph usage', () => {
572
+ test('detects List usage', () => {
457
573
  const input = `const editor = new Blok({
458
- tools: { paragraph: Blok.Paragraph }
574
+ tools: { list: List }
459
575
  });`;
460
576
  const { result, changed } = ensureBlokImport(input);
461
- assertEqual(changed, true, 'Should detect Blok.Paragraph');
462
- assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
577
+ assertEqual(changed, true, 'Should detect List');
578
+ assertEqual(result.includes("import { List } from '@jackuait/blok';"), true, 'Should add List import');
463
579
  });
464
580
 
465
- test('handles multiple Blok tools usage', () => {
581
+ test('handles multiple tool usage', () => {
466
582
  const input = `const editor = new Blok({
467
583
  tools: {
468
- header: Blok.Header,
469
- paragraph: Blok.Paragraph
584
+ header: Header,
585
+ paragraph: Paragraph
470
586
  }
471
587
  });`;
472
588
  const { result, changed } = ensureBlokImport(input);
473
- assertEqual(changed, true, 'Should detect multiple Blok tools');
474
- assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
589
+ assertEqual(changed, true, 'Should detect multiple tools');
590
+ assertEqual(result.includes('Header'), true, 'Should add Header import');
591
+ assertEqual(result.includes('Paragraph'), true, 'Should add Paragraph import');
475
592
  });
476
593
 
477
- test('full migration adds Blok import for bundled tools', () => {
594
+ test('full migration adds imports for bundled tools', () => {
478
595
  // This simulates a complete migration from EditorJS with bundled tools
479
596
  const input = `import EditorJS from '@editorjs/editorjs';
480
597
  import Header from '@editorjs/header';
@@ -499,10 +616,596 @@ const editor = new EditorJS({
499
616
  result = applyTransforms(result, TOOL_CONFIG_TRANSFORMS).result;
500
617
  result = ensureBlokImport(result).result;
501
618
 
502
- // After transformation, should have Blok import (since original EditorJS import becomes @jackuait/blok)
619
+ // After transformation, should have @jackuait/blok imports
503
620
  assertEqual(result.includes("from '@jackuait/blok'"), true, 'Should have @jackuait/blok import');
504
- assertEqual(result.includes('class: Blok.Header'), true, 'Should use Blok.Header');
505
- assertEqual(result.includes('class: Blok.Paragraph'), true, 'Should use Blok.Paragraph');
621
+ // With named exports, tools are imported directly, not as Blok.Header
622
+ assertEqual(result.includes('class: Header'), true, 'Should use Header (named import)');
623
+ assertEqual(result.includes('class: Paragraph'), true, 'Should use Paragraph (named import)');
624
+ });
625
+
626
+ // ============================================================================
627
+ // i18n Transformation Tests
628
+ // ============================================================================
629
+
630
+ console.log('\n🌐 i18n Transformations\n');
631
+
632
+ test('flattenI18nDictionary flattens simple nested object', () => {
633
+ const input = {
634
+ ui: {
635
+ toolbar: {
636
+ toolbox: {
637
+ Add: 'Добавить'
638
+ }
639
+ }
640
+ }
641
+ };
642
+ const result = flattenI18nDictionary(input);
643
+ // 'ui.toolbar.toolbox.Add' is mapped to 'toolbox.addBelow' by I18N_KEY_MAPPINGS
644
+ assertEqual(result['toolbox.addBelow'], 'Добавить');
645
+ });
646
+
647
+ test('flattenI18nDictionary flattens deeply nested object', () => {
648
+ const input = {
649
+ ui: {
650
+ blockTunes: {
651
+ toggler: {
652
+ 'Drag to move': 'Перетащите'
653
+ }
654
+ }
655
+ }
656
+ };
657
+ const result = flattenI18nDictionary(input);
658
+ // Keys are normalized to camelCase and mapped to simplified keys
659
+ assertEqual(result['blockSettings.dragToMove'], 'Перетащите');
660
+ });
661
+
662
+ test('flattenI18nDictionary handles multiple namespaces', () => {
663
+ const input = {
664
+ toolNames: {
665
+ Text: 'Текст',
666
+ Bold: 'Жирный'
667
+ },
668
+ tools: {
669
+ link: {
670
+ 'Add a link': 'Добавить ссылку'
671
+ }
672
+ }
673
+ };
674
+ const result = flattenI18nDictionary(input);
675
+ // Keys are normalized to camelCase
676
+ assertEqual(result['toolNames.text'], 'Текст');
677
+ assertEqual(result['toolNames.bold'], 'Жирный');
678
+ assertEqual(result['tools.link.addALink'], 'Добавить ссылку');
679
+ });
680
+
681
+ test('flattenI18nDictionary applies key mappings', () => {
682
+ const input = {
683
+ ui: {
684
+ blockTunes: {
685
+ toggler: {
686
+ 'Click to tune': 'Нажмите для настройки'
687
+ }
688
+ }
689
+ }
690
+ };
691
+ const result = flattenI18nDictionary(input);
692
+ // 'Click to tune' should be mapped to 'blockSettings.clickToOpenMenu'
693
+ assertEqual(result['blockSettings.clickToOpenMenu'], 'Нажмите для настройки');
694
+ assertEqual(result['ui.blockTunes.toggler.Click to tune'], undefined);
695
+ });
696
+
697
+ test('flattenI18nDictionary handles empty object', () => {
698
+ const result = flattenI18nDictionary({});
699
+ assertEqual(Object.keys(result).length, 0);
700
+ });
701
+
702
+ test('transformI18nConfig transforms nested i18n config in JS', () => {
703
+ const input = `const editor = new Blok({
704
+ i18n: {
705
+ messages: {
706
+ toolNames: {
707
+ Text: "Текст",
708
+ Bold: "Жирный"
709
+ }
710
+ }
711
+ }
712
+ });`;
713
+ const { result, changed } = transformI18nConfig(input);
714
+ assertEqual(changed, true, 'Should indicate change');
715
+ // Keys are normalized to camelCase
716
+ assertEqual(result.includes('"toolNames.text": "Текст"'), true, 'Should have flattened toolNames.text');
717
+ assertEqual(result.includes('"toolNames.bold": "Жирный"'), true, 'Should have flattened toolNames.bold');
718
+ });
719
+
720
+ test('transformI18nConfig transforms deeply nested messages', () => {
721
+ const input = `const config = {
722
+ i18n: {
723
+ messages: {
724
+ ui: {
725
+ popover: {
726
+ Search: "Поиск",
727
+ "Nothing found": "Ничего не найдено"
728
+ }
729
+ }
730
+ }
731
+ }
732
+ };`;
733
+ const { result, changed } = transformI18nConfig(input);
734
+ assertEqual(changed, true, 'Should indicate change');
735
+ // Keys are normalized to camelCase and mapped to simplified keys
736
+ assertEqual(result.includes('"popover.search": "Поиск"'), true, 'Should have flattened popover.search');
737
+ assertEqual(result.includes('"popover.nothingFound": "Ничего не найдено"'), true, 'Should have flattened popover.nothingFound');
738
+ });
739
+
740
+ test('transformI18nConfig does not change content without i18n config', () => {
741
+ const input = `const editor = new Blok({
742
+ holder: 'blok',
743
+ tools: {}
744
+ });`;
745
+ const { result, changed } = transformI18nConfig(input);
746
+ assertEqual(changed, false, 'Should not indicate change');
747
+ assertEqual(result, input, 'Content should be unchanged');
748
+ });
749
+
750
+ test('transformI18nConfig skips dynamic content with functions', () => {
751
+ const input = `const editor = new Blok({
752
+ i18n: {
753
+ messages: {
754
+ toolNames: {
755
+ Text: () => getTranslation('text')
756
+ }
757
+ }
758
+ }
759
+ });`;
760
+ const { result, changed } = transformI18nConfig(input);
761
+ assertEqual(changed, false, 'Should not transform dynamic content');
762
+ assertEqual(result, input, 'Content should be unchanged');
763
+ });
764
+
765
+ test('transformI18nConfig handles single quotes', () => {
766
+ const input = `const editor = new Blok({
767
+ i18n: {
768
+ messages: {
769
+ toolNames: {
770
+ Text: 'Текст'
771
+ }
772
+ }
773
+ }
774
+ });`;
775
+ const { result, changed } = transformI18nConfig(input);
776
+ assertEqual(changed, true, 'Should indicate change');
777
+ // Keys are normalized to camelCase
778
+ assertEqual(result.includes('"toolNames.text": "Текст"'), true, 'Should have flattened key with value');
779
+ });
780
+
781
+ test('I18N_KEY_MAPPINGS contains expected mappings', () => {
782
+ // UI key mappings (values use simplified key format)
783
+ assertEqual(I18N_KEY_MAPPINGS['ui.blockTunes.toggler.Click to tune'], 'blockSettings.clickToOpenMenu');
784
+ assertEqual(I18N_KEY_MAPPINGS['ui.blockTunes.toggler.or drag to move'], 'blockSettings.dragToMove');
785
+ assertEqual(I18N_KEY_MAPPINGS['ui.toolbar.toolbox.Add'], 'toolbox.addBelow');
786
+ assertEqual(I18N_KEY_MAPPINGS['ui.inlineToolbar.converter.Convert to'], 'popover.convertTo');
787
+ assertEqual(I18N_KEY_MAPPINGS['ui.popover.Filter'], 'popover.search');
788
+
789
+ // Tool names mappings (values are now camelCase)
790
+ assertEqual(I18N_KEY_MAPPINGS['toolNames.Ordered List'], 'toolNames.numberedList');
791
+ assertEqual(I18N_KEY_MAPPINGS['toolNames.Unordered List'], 'toolNames.bulletedList');
792
+
793
+ // Tools messages mappings (values are now camelCase)
794
+ assertEqual(I18N_KEY_MAPPINGS['tools.stub.The block can not be displayed correctly'], 'tools.stub.blockCannotBeDisplayed');
795
+
796
+ // Block tunes mappings
797
+ assertEqual(I18N_KEY_MAPPINGS['blockTunes.delete.Delete'], 'blockSettings.delete');
798
+
799
+ // Removed keys (mapped to null)
800
+ assertEqual(I18N_KEY_MAPPINGS['blockTunes.moveUp.Move up'], null);
801
+ assertEqual(I18N_KEY_MAPPINGS['blockTunes.moveDown.Move down'], null);
802
+ });
803
+
804
+ test('flattenI18nDictionary applies tool name mappings', () => {
805
+ const input = {
806
+ toolNames: {
807
+ 'Ordered List': 'Нумерованный список',
808
+ 'Unordered List': 'Маркированный список',
809
+ },
810
+ };
811
+ const result = flattenI18nDictionary(input);
812
+ // Keys are mapped and normalized to camelCase
813
+ assertEqual(result['toolNames.numberedList'], 'Нумерованный список');
814
+ assertEqual(result['toolNames.bulletedList'], 'Маркированный список');
815
+ assertEqual(result['toolNames.Ordered List'], undefined);
816
+ assertEqual(result['toolNames.Unordered List'], undefined);
817
+ });
818
+
819
+ test('flattenI18nDictionary applies Filter to Search mapping', () => {
820
+ const input = {
821
+ ui: {
822
+ popover: {
823
+ Filter: 'Фильтр',
824
+ 'Nothing found': 'Ничего не найдено',
825
+ },
826
+ },
827
+ };
828
+ const result = flattenI18nDictionary(input);
829
+ // Keys are mapped to simplified keys
830
+ assertEqual(result['popover.search'], 'Фильтр');
831
+ assertEqual(result['popover.nothingFound'], 'Ничего не найдено');
832
+ assertEqual(result['ui.popover.Filter'], undefined);
833
+ });
834
+
835
+ test('flattenI18nDictionary removes moveUp/moveDown keys', () => {
836
+ const input = {
837
+ blockTunes: {
838
+ delete: {
839
+ Delete: 'Удалить',
840
+ },
841
+ moveUp: {
842
+ 'Move up': 'Переместить вверх',
843
+ },
844
+ moveDown: {
845
+ 'Move down': 'Переместить вниз',
846
+ },
847
+ },
848
+ };
849
+ const result = flattenI18nDictionary(input);
850
+ // Keys are mapped to simplified keys, moveUp/moveDown are removed
851
+ assertEqual(result['blockSettings.delete'], 'Удалить');
852
+ assertEqual(result['blockTunes.moveUp.Move up'], undefined);
853
+ assertEqual(result['blockTunes.moveDown.Move down'], undefined);
854
+ });
855
+
856
+ test('flattenI18nDictionary applies stub message mapping', () => {
857
+ const input = {
858
+ tools: {
859
+ stub: {
860
+ 'The block can not be displayed correctly': 'Блок не может быть отображен',
861
+ },
862
+ },
863
+ };
864
+ const result = flattenI18nDictionary(input);
865
+ // Key is mapped and normalized to camelCase
866
+ assertEqual(result['tools.stub.blockCannotBeDisplayed'], 'Блок не может быть отображен');
867
+ assertEqual(result['tools.stub.The block can not be displayed correctly'], undefined);
868
+ });
869
+
870
+ console.log('\n🔤 normalizeKey\n');
871
+
872
+ test('normalizeKey converts single word to lowercase', () => {
873
+ assertEqual(normalizeKey('toolNames.Text'), 'toolNames.text');
874
+ assertEqual(normalizeKey('toolNames.Bold'), 'toolNames.bold');
875
+ });
876
+
877
+ test('normalizeKey converts multi-word keys to camelCase', () => {
878
+ assertEqual(normalizeKey('popover.Nothing found'), 'popover.nothingFound');
879
+ assertEqual(normalizeKey('toolbox.Click to add below'), 'toolbox.clickToAddBelow');
880
+ });
881
+
882
+ test('normalizeKey handles keys with multiple spaces', () => {
883
+ assertEqual(normalizeKey('tools.stub.The block can not be displayed'), 'tools.stub.theBlockCanNotBeDisplayed');
884
+ });
885
+
886
+ test('normalizeKey preserves namespace segments', () => {
887
+ assertEqual(normalizeKey('blockSettings.Drag to move'), 'blockSettings.dragToMove');
888
+ });
889
+
890
+ console.log('\n🗑️ removeI18nMessages (--use-library-i18n)\n');
891
+
892
+ test('removeI18nMessages removes messages property from i18n config', () => {
893
+ const input = `const editor = new Blok({
894
+ i18n: {
895
+ messages: {
896
+ toolNames: {
897
+ Text: "Текст"
898
+ }
899
+ }
900
+ }
901
+ });`;
902
+ const { result, changed } = removeI18nMessages(input);
903
+ assertEqual(changed, true, 'Should indicate change');
904
+ assertEqual(result.includes('messages'), false, 'Should not contain messages property');
905
+ assertEqual(result.includes('i18n: {}'), true, 'Should have empty i18n config');
906
+ });
907
+
908
+ test('removeI18nMessages preserves locale property', () => {
909
+ const input = `const editor = new Blok({
910
+ i18n: {
911
+ messages: {
912
+ toolNames: { Text: "Текст" }
913
+ },
914
+ locale: 'ru'
915
+ }
916
+ });`;
917
+ const { result, changed } = removeI18nMessages(input);
918
+ assertEqual(changed, true, 'Should indicate change');
919
+ assertEqual(result.includes('messages'), false, 'Should not contain messages property');
920
+ assertEqual(result.includes("locale: 'ru'"), true, 'Should preserve locale property');
921
+ });
922
+
923
+ test('removeI18nMessages preserves direction property', () => {
924
+ const input = `const editor = new Blok({
925
+ i18n: {
926
+ direction: 'rtl',
927
+ messages: {
928
+ toolNames: { Text: "نص" }
929
+ }
930
+ }
931
+ });`;
932
+ const { result, changed } = removeI18nMessages(input);
933
+ assertEqual(changed, true, 'Should indicate change');
934
+ assertEqual(result.includes('messages'), false, 'Should not contain messages property');
935
+ assertEqual(result.includes("direction: 'rtl'"), true, 'Should preserve direction property');
936
+ });
937
+
938
+ test('removeI18nMessages does not change content without messages', () => {
939
+ const input = `const editor = new Blok({
940
+ i18n: {
941
+ locale: 'ru'
942
+ }
943
+ });`;
944
+ const { result, changed } = removeI18nMessages(input);
945
+ assertEqual(changed, false, 'Should not indicate change');
946
+ assertEqual(result, input, 'Content should be unchanged');
947
+ });
948
+
949
+ test('removeI18nMessages does not change content without i18n', () => {
950
+ const input = `const editor = new Blok({
951
+ holder: 'blok',
952
+ tools: {}
953
+ });`;
954
+ const { result, changed } = removeI18nMessages(input);
955
+ assertEqual(changed, false, 'Should not indicate change');
956
+ assertEqual(result, input, 'Content should be unchanged');
957
+ });
958
+
959
+ // ============================================================================
960
+ // Blok Default Import → Named Import Tests
961
+ // ============================================================================
962
+
963
+ console.log('\n🔄 Blok Default Import → Named Import Transformations\n');
964
+
965
+ test('transforms Blok default import to named import', () => {
966
+ const input = `import Blok from '@jackuait/blok';`;
967
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
968
+ assertEqual(result, `import { Blok } from '@jackuait/blok';`);
969
+ });
970
+
971
+ test('transforms aliased Blok default import to named import with alias', () => {
972
+ const input = `import Editor from '@jackuait/blok';`;
973
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
974
+ assertEqual(result, `import { Blok as Editor } from '@jackuait/blok';`);
975
+ });
976
+
977
+ test('transforms combined Blok default + named import', () => {
978
+ const input = `import Blok, { BlokConfig } from '@jackuait/blok';`;
979
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
980
+ assertEqual(result, `import { Blok, BlokConfig } from '@jackuait/blok';`);
981
+ });
982
+
983
+ test('transforms aliased combined Blok default + named import', () => {
984
+ const input = `import Editor, { BlokConfig } from '@jackuait/blok';`;
985
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
986
+ assertEqual(result, `import { Blok as Editor, BlokConfig } from '@jackuait/blok';`);
987
+ });
988
+
989
+ test('transforms type-only Blok default import', () => {
990
+ const input = `import type Blok from '@jackuait/blok';`;
991
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
992
+ assertEqual(result, `import type { Blok } from '@jackuait/blok';`);
993
+ });
994
+
995
+ test('transforms type-only aliased Blok default import', () => {
996
+ const input = `import type Editor from '@jackuait/blok';`;
997
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
998
+ assertEqual(result, `import type { Blok as Editor } from '@jackuait/blok';`);
999
+ });
1000
+
1001
+ test('transforms namespace import from @jackuait/blok', () => {
1002
+ const input = `import * as Blok from '@jackuait/blok';`;
1003
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1004
+ assertEqual(result, `import { Blok } from '@jackuait/blok';`);
1005
+ });
1006
+
1007
+ test('transforms aliased namespace import from @jackuait/blok', () => {
1008
+ const input = `import * as Editor from '@jackuait/blok';`;
1009
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1010
+ assertEqual(result, `import { Blok as Editor } from '@jackuait/blok';`);
1011
+ });
1012
+
1013
+ test('transforms destructured dynamic import from @jackuait/blok', () => {
1014
+ const input = `const { default: Editor } = await import('@jackuait/blok');`;
1015
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1016
+ assertEqual(result, `const { Blok: Editor } = await import('@jackuait/blok');`);
1017
+ });
1018
+
1019
+ test('transforms require().default from @jackuait/blok', () => {
1020
+ const input = `const Blok = require('@jackuait/blok').default;`;
1021
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1022
+ assertEqual(result, `const Blok = require('@jackuait/blok').Blok;`);
1023
+ });
1024
+
1025
+ test('transforms destructured require default from @jackuait/blok', () => {
1026
+ const input = `const { default: Editor } = require('@jackuait/blok');`;
1027
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1028
+ assertEqual(result, `const { Blok: Editor } = require('@jackuait/blok');`);
1029
+ });
1030
+
1031
+ test('transforms re-export default as named from @jackuait/blok', () => {
1032
+ const input = `export { default as Editor } from '@jackuait/blok';`;
1033
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1034
+ assertEqual(result, `export { Blok as Editor } from '@jackuait/blok';`);
1035
+ });
1036
+
1037
+ test('transforms re-export default from @jackuait/blok', () => {
1038
+ const input = `export { default } from '@jackuait/blok';`;
1039
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1040
+ assertEqual(result, `export { Blok } from '@jackuait/blok';`);
1041
+ });
1042
+
1043
+ test('does not transform subpath imports from @jackuait/blok/tools', () => {
1044
+ const input = `import Header from '@jackuait/blok/tools';`;
1045
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1046
+ // Should not match because of the /tools path
1047
+ assertEqual(result, input);
1048
+ });
1049
+
1050
+ test('does not transform named imports from @jackuait/blok', () => {
1051
+ const input = `import { Blok, BlokConfig } from '@jackuait/blok';`;
1052
+ const { result } = applyTransforms(input, IMPORT_TRANSFORMS);
1053
+ // Named imports should not be transformed by the default import transforms
1054
+ assertEqual(result, input);
1055
+ });
1056
+
1057
+ // ============================================================================
1058
+ // Modular Import Tests (Strategy 5)
1059
+ // ============================================================================
1060
+
1061
+ console.log('\n📦 Modular Import Transformations (Strategy 5)\n');
1062
+
1063
+ test('ALL_TOOLS contains block and inline tools', () => {
1064
+ assertEqual(ALL_TOOLS.includes('Header'), true, 'Should include Header');
1065
+ assertEqual(ALL_TOOLS.includes('Paragraph'), true, 'Should include Paragraph');
1066
+ assertEqual(ALL_TOOLS.includes('List'), true, 'Should include List');
1067
+ assertEqual(ALL_TOOLS.includes('Bold'), true, 'Should include Bold');
1068
+ assertEqual(ALL_TOOLS.includes('Italic'), true, 'Should include Italic');
1069
+ assertEqual(ALL_TOOLS.includes('Link'), true, 'Should include Link');
1070
+ assertEqual(ALL_TOOLS.includes('Convert'), true, 'Should include Convert');
1071
+ });
1072
+
1073
+ test('splitBlokImports splits combined import with Blok and Header', () => {
1074
+ const input = `import { Blok, Header } from '@jackuait/blok';`;
1075
+ const { result, changed } = splitBlokImports(input);
1076
+ assertEqual(changed, true, 'Should indicate change');
1077
+ assertEqual(result.includes("from '@jackuait/blok';"), true, 'Should have core import');
1078
+ assertEqual(result.includes("from '@jackuait/blok/tools';"), true, 'Should have tools import');
1079
+ assertEqual(result.includes('Blok'), true, 'Should include Blok');
1080
+ assertEqual(result.includes('Header'), true, 'Should include Header');
1081
+ });
1082
+
1083
+ test('splitBlokImports splits combined import with multiple tools', () => {
1084
+ const input = `import { Blok, Header, Paragraph, Bold } from '@jackuait/blok';`;
1085
+ const { result, changed } = splitBlokImports(input);
1086
+ assertEqual(changed, true, 'Should indicate change');
1087
+ assertEqual(result.includes("import { Blok } from '@jackuait/blok';"), true, 'Should have core-only import');
1088
+ assertEqual(result.includes("import { Header, Paragraph, Bold } from '@jackuait/blok/tools';"), true, 'Should have tools import');
1089
+ });
1090
+
1091
+ test('splitBlokImports handles tools-only import', () => {
1092
+ const input = `import { Header, Paragraph } from '@jackuait/blok';`;
1093
+ const { result, changed } = splitBlokImports(input);
1094
+ assertEqual(changed, true, 'Should indicate change');
1095
+ assertEqual(result.includes("import { Header, Paragraph } from '@jackuait/blok/tools';"), true, 'Should move to tools');
1096
+ assertEqual(result.includes("from '@jackuait/blok';") && !result.includes("/tools"), false, 'Should not have empty core import');
1097
+ });
1098
+
1099
+ test('splitBlokImports does not change core-only import', () => {
1100
+ const input = `import { Blok, BlokConfig } from '@jackuait/blok';`;
1101
+ const { result, changed } = splitBlokImports(input);
1102
+ assertEqual(changed, false, 'Should not indicate change');
1103
+ assertEqual(result, input, 'Content should be unchanged');
1104
+ });
1105
+
1106
+ test('splitBlokImports handles aliased imports', () => {
1107
+ const input = `import { Blok, Header as MyHeader } from '@jackuait/blok';`;
1108
+ const { result, changed } = splitBlokImports(input);
1109
+ assertEqual(changed, true, 'Should indicate change');
1110
+ assertEqual(result.includes("import { Blok } from '@jackuait/blok';"), true, 'Should have core import');
1111
+ assertEqual(result.includes("Header as MyHeader"), true, 'Should preserve alias');
1112
+ assertEqual(result.includes("@jackuait/blok/tools"), true, 'Should have tools import');
1113
+ });
1114
+
1115
+ test('ensureToolsImport adds tools import when no import exists', () => {
1116
+ const input = `const editor = new Blok({
1117
+ tools: { header: Header }
1118
+ });`;
1119
+ const { result, changed } = ensureToolsImport(input);
1120
+ assertEqual(changed, true, 'Should indicate change');
1121
+ assertEqual(result.includes("from '@jackuait/blok/tools';"), true, 'Should add tools import');
1122
+ });
1123
+
1124
+ test('ensureToolsImport adds to existing /tools import', () => {
1125
+ const input = `import { Header } from '@jackuait/blok/tools';
1126
+
1127
+ const editor = new Blok({
1128
+ tools: { header: Header, paragraph: Paragraph }
1129
+ });`;
1130
+ const { result, changed } = ensureToolsImport(input);
1131
+ assertEqual(changed, true, 'Should indicate change');
1132
+ assertEqual(result.includes('Paragraph'), true, 'Should add Paragraph');
1133
+ assertEqual(result.includes("from '@jackuait/blok/tools';"), true, 'Should use tools path');
1134
+ });
1135
+
1136
+ test('ensureToolsImport does not duplicate when tools are in main import', () => {
1137
+ const input = `import { Blok, Header } from '@jackuait/blok';
1138
+
1139
+ const editor = new Blok({
1140
+ tools: { header: Header }
1141
+ });`;
1142
+ const { result, changed } = ensureToolsImport(input);
1143
+ // Tools are in main import, will be moved by splitBlokImports later
1144
+ assertEqual(changed, false, 'Should not add duplicate');
1145
+ });
1146
+
1147
+ test('ensureToolsImport detects inline tools', () => {
1148
+ const input = `const editor = new Blok({
1149
+ tools: { bold: Bold, italic: Italic }
1150
+ });`;
1151
+ const { result, changed } = ensureToolsImport(input);
1152
+ assertEqual(changed, true, 'Should detect inline tools');
1153
+ assertEqual(result.includes('Bold'), true, 'Should add Bold');
1154
+ assertEqual(result.includes('Italic'), true, 'Should add Italic');
1155
+ assertEqual(result.includes("from '@jackuait/blok/tools';"), true, 'Should use tools path');
1156
+ });
1157
+
1158
+ test('transforms Blok.Bold to Bold', () => {
1159
+ const input = `{ class: Blok.Bold, config: {} }`;
1160
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
1161
+ assertEqual(result, `{ class: Bold, config: {} }`);
1162
+ });
1163
+
1164
+ test('transforms Blok.Italic to Italic', () => {
1165
+ const input = `{ class: Blok.Italic, config: {} }`;
1166
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
1167
+ assertEqual(result, `{ class: Italic, config: {} }`);
1168
+ });
1169
+
1170
+ test('transforms Blok.Link to Link', () => {
1171
+ const input = `{ class: Blok.Link, config: {} }`;
1172
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
1173
+ assertEqual(result, `{ class: Link, config: {} }`);
1174
+ });
1175
+
1176
+ test('transforms Blok.Convert to Convert', () => {
1177
+ const input = `{ class: Blok.Convert, config: {} }`;
1178
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
1179
+ assertEqual(result, `{ class: Convert, config: {} }`);
1180
+ });
1181
+
1182
+ test('transforms standalone inline tool references', () => {
1183
+ const input = `tools: { bold: Blok.Bold, italic: Blok.Italic }`;
1184
+ const { result } = applyTransforms(input, TOOL_CONFIG_TRANSFORMS);
1185
+ assertEqual(result, `tools: { bold: Bold, italic: Italic }`);
1186
+ });
1187
+
1188
+ test('full modular migration: old Blok import to new structure', () => {
1189
+ const input = `import { Blok, Header, Paragraph, Bold, Italic } from '@jackuait/blok';
1190
+
1191
+ const editor = new Blok({
1192
+ holder: 'blok',
1193
+ tools: {
1194
+ header: { class: Header },
1195
+ paragraph: { class: Paragraph },
1196
+ },
1197
+ inlineTools: {
1198
+ bold: { class: Bold },
1199
+ italic: { class: Italic },
1200
+ },
1201
+ });`;
1202
+
1203
+ // Apply splitBlokImports
1204
+ const { result: splitResult, changed } = splitBlokImports(input);
1205
+
1206
+ assertEqual(changed, true, 'Should split imports');
1207
+ assertEqual(splitResult.includes("import { Blok } from '@jackuait/blok';"), true, 'Should have core import');
1208
+ assertEqual(splitResult.includes("import { Header, Paragraph, Bold, Italic } from '@jackuait/blok/tools';"), true, 'Should have tools import');
506
1209
  });
507
1210
 
508
1211
  // ============================================================================