@shelamkoff/rector 1.0.0

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 (668) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +17 -0
  3. package/README.md +23 -0
  4. package/THIRD_PARTY_LICENSES.txt +298 -0
  5. package/dist/I18n.d.ts +1 -0
  6. package/dist/core/Block.d.ts +110 -0
  7. package/dist/core/Block.js +390 -0
  8. package/dist/core/BlockAnimator.d.ts +39 -0
  9. package/dist/core/BlockAnimator.js +106 -0
  10. package/dist/core/BlockManager.d.ts +163 -0
  11. package/dist/core/BlockManager.js +640 -0
  12. package/dist/core/BlockOperations.d.ts +67 -0
  13. package/dist/core/BlockOperations.js +238 -0
  14. package/dist/core/ChangeNotifier.d.ts +15 -0
  15. package/dist/core/ChangeNotifier.js +55 -0
  16. package/dist/core/CommandDispatcher.d.ts +52 -0
  17. package/dist/core/CommandDispatcher.js +208 -0
  18. package/dist/core/CrossBlockSelection.d.ts +30 -0
  19. package/dist/core/CrossBlockSelection.js +74 -0
  20. package/dist/core/Diagnostics.d.ts +23 -0
  21. package/dist/core/Diagnostics.js +62 -0
  22. package/dist/core/DocumentSchema.d.ts +29 -0
  23. package/dist/core/DocumentSchema.js +147 -0
  24. package/dist/core/DocumentSnapshotStore.d.ts +34 -0
  25. package/dist/core/DocumentSnapshotStore.js +153 -0
  26. package/dist/core/DragManager.d.ts +17 -0
  27. package/dist/core/DragManager.js +223 -0
  28. package/dist/core/EditorFacade.d.ts +154 -0
  29. package/dist/core/EditorFacade.js +361 -0
  30. package/dist/core/EditorHolderOwnership.d.ts +12 -0
  31. package/dist/core/EditorHolderOwnership.js +28 -0
  32. package/dist/core/I18n.d.ts +147 -0
  33. package/dist/core/I18n.js +296 -0
  34. package/dist/core/InlinePatternMatcher.d.ts +26 -0
  35. package/dist/core/InlinePatternMatcher.js +267 -0
  36. package/dist/core/InlinePluginRegistry.d.ts +55 -0
  37. package/dist/core/InlinePluginRegistry.js +128 -0
  38. package/dist/core/KeyboardManager.d.ts +17 -0
  39. package/dist/core/KeyboardManager.js +231 -0
  40. package/dist/core/LifecycleScope.d.ts +19 -0
  41. package/dist/core/LifecycleScope.js +41 -0
  42. package/dist/core/MouseSelectionManager.d.ts +30 -0
  43. package/dist/core/MouseSelectionManager.js +307 -0
  44. package/dist/core/PluginOwnership.d.ts +9 -0
  45. package/dist/core/PluginOwnership.js +40 -0
  46. package/dist/core/PopupManager.d.ts +50 -0
  47. package/dist/core/PopupManager.js +183 -0
  48. package/dist/core/PreservedBlockPlugin.d.ts +11 -0
  49. package/dist/core/PreservedBlockPlugin.js +42 -0
  50. package/dist/core/PublicEditorApi.d.ts +82 -0
  51. package/dist/core/PublicEditorApi.js +158 -0
  52. package/dist/core/SelectionManager.d.ts +50 -0
  53. package/dist/core/SelectionManager.js +205 -0
  54. package/dist/core/ShortcutRegistry.d.ts +29 -0
  55. package/dist/core/ShortcutRegistry.js +106 -0
  56. package/dist/core/SlashCommands.d.ts +34 -0
  57. package/dist/core/SlashCommands.js +483 -0
  58. package/dist/core/StyleInjector.d.ts +1 -0
  59. package/dist/core/StyleInjector.js +1 -0
  60. package/dist/core/Tooltip.d.ts +17 -0
  61. package/dist/core/Tooltip.js +60 -0
  62. package/dist/core/TriggerManager.d.ts +17 -0
  63. package/dist/core/TriggerManager.js +159 -0
  64. package/dist/core/TypeSelector.d.ts +31 -0
  65. package/dist/core/TypeSelector.js +464 -0
  66. package/dist/core/UndoManager.d.ts +72 -0
  67. package/dist/core/UndoManager.js +383 -0
  68. package/dist/core/block-settings-menu/BlockActions.d.ts +81 -0
  69. package/dist/core/block-settings-menu/BlockActions.js +233 -0
  70. package/dist/core/block-settings-menu/BlockSettingsMenu.d.ts +46 -0
  71. package/dist/core/block-settings-menu/BlockSettingsMenu.js +205 -0
  72. package/dist/core/block-settings-menu/MenuBuilder.d.ts +43 -0
  73. package/dist/core/block-settings-menu/MenuBuilder.js +203 -0
  74. package/dist/core/block-settings-menu/MenuPositioner.d.ts +22 -0
  75. package/dist/core/block-settings-menu/MenuPositioner.js +79 -0
  76. package/dist/core/clipboard/Clipboard.d.ts +47 -0
  77. package/dist/core/clipboard/Clipboard.js +653 -0
  78. package/dist/core/clipboard/CrossBlockEditor.d.ts +43 -0
  79. package/dist/core/clipboard/CrossBlockEditor.js +198 -0
  80. package/dist/core/clipboard/PasteRouter.d.ts +27 -0
  81. package/dist/core/clipboard/PasteRouter.js +86 -0
  82. package/dist/core/clipboard/pasteInsert.d.ts +35 -0
  83. package/dist/core/clipboard/pasteInsert.js +215 -0
  84. package/dist/core/clipboard/pasteUtils.d.ts +25 -0
  85. package/dist/core/clipboard/pasteUtils.js +98 -0
  86. package/dist/core/config.d.ts +77 -0
  87. package/dist/core/config.js +100 -0
  88. package/dist/core/constants.d.ts +23 -0
  89. package/dist/core/constants.js +30 -0
  90. package/dist/core/crossBlockConvert.d.ts +38 -0
  91. package/dist/core/crossBlockConvert.js +253 -0
  92. package/dist/core/dom.d.ts +35 -0
  93. package/dist/core/dom.js +65 -0
  94. package/dist/core/editorEvents.d.ts +89 -0
  95. package/dist/core/editorEvents.js +60 -0
  96. package/dist/core/hydrateInlinePlugins.d.ts +9 -0
  97. package/dist/core/hydrateInlinePlugins.js +36 -0
  98. package/dist/core/icons.d.ts +10 -0
  99. package/dist/core/icons.js +21 -0
  100. package/dist/core/index.d.ts +59 -0
  101. package/dist/core/index.js +703 -0
  102. package/dist/core/inline-toolbar/ActionsPanel.d.ts +61 -0
  103. package/dist/core/inline-toolbar/ActionsPanel.js +132 -0
  104. package/dist/core/inline-toolbar/InlinePositioner.d.ts +20 -0
  105. package/dist/core/inline-toolbar/InlinePositioner.js +63 -0
  106. package/dist/core/inline-toolbar/InlineToolbar.d.ts +45 -0
  107. package/dist/core/inline-toolbar/InlineToolbar.js +456 -0
  108. package/dist/core/inline-toolbar/PluginControlsSlot.d.ts +52 -0
  109. package/dist/core/inline-toolbar/PluginControlsSlot.js +118 -0
  110. package/dist/core/inline-toolbar/SelectionTracker.d.ts +63 -0
  111. package/dist/core/inline-toolbar/SelectionTracker.js +173 -0
  112. package/dist/core/inlinePluginInsert.d.ts +13 -0
  113. package/dist/core/inlinePluginInsert.js +56 -0
  114. package/dist/core/locale/en.d.ts +35 -0
  115. package/dist/core/locale/en.js +37 -0
  116. package/dist/core/locale/ru.d.ts +35 -0
  117. package/dist/core/locale/ru.js +37 -0
  118. package/dist/core/menuKeyboardNav.d.ts +15 -0
  119. package/dist/core/menuKeyboardNav.js +52 -0
  120. package/dist/core/sanitize.d.ts +2 -0
  121. package/dist/core/sanitize.js +6 -0
  122. package/dist/core/splitConvert.d.ts +46 -0
  123. package/dist/core/splitConvert.js +190 -0
  124. package/dist/core/textOffset.d.ts +46 -0
  125. package/dist/core/textOffset.js +180 -0
  126. package/dist/core/themes/dark.css +30 -0
  127. package/dist/core/themes/light.css +33 -0
  128. package/dist/core/themes/variables.css +1170 -0
  129. package/dist/core/toolbar/OffcanvasRoot.d.ts +37 -0
  130. package/dist/core/toolbar/OffcanvasRoot.js +127 -0
  131. package/dist/core/toolbar/Toolbar.d.ts +70 -0
  132. package/dist/core/toolbar/Toolbar.js +432 -0
  133. package/dist/core/toolbar/ToolbarPositioner.d.ts +60 -0
  134. package/dist/core/toolbar/ToolbarPositioner.js +188 -0
  135. package/dist/core/toolbar/ToolboxBuilder.d.ts +44 -0
  136. package/dist/core/toolbar/ToolboxBuilder.js +260 -0
  137. package/dist/core/types.d.ts +747 -0
  138. package/dist/core/uid.d.ts +6 -0
  139. package/dist/core/uid.js +29 -0
  140. package/dist/index.d.ts +2 -0
  141. package/dist/index.js +1 -0
  142. package/dist/inline-plugins/README.md +35 -0
  143. package/dist/inline-plugins/README.ru.md +35 -0
  144. package/dist/inline-plugins/color/README.md +61 -0
  145. package/dist/inline-plugins/color/README.ru.md +61 -0
  146. package/dist/inline-plugins/color.d.ts +8 -0
  147. package/dist/inline-plugins/color.js +184 -0
  148. package/dist/inline-plugins/locale/en.d.ts +7 -0
  149. package/dist/inline-plugins/locale/en.js +6 -0
  150. package/dist/inline-plugins/locale/ru.d.ts +7 -0
  151. package/dist/inline-plugins/locale/ru.js +6 -0
  152. package/dist/inline-plugins/mention/README.md +97 -0
  153. package/dist/inline-plugins/mention/README.ru.md +97 -0
  154. package/dist/inline-plugins/mention/index.d.ts +110 -0
  155. package/dist/inline-plugins/mention/index.js +1897 -0
  156. package/dist/inline-plugins/mention/styles.css +183 -0
  157. package/dist/inline-plugins/mention/widget.d.ts +16 -0
  158. package/dist/inline-plugins/mention/widget.js +75 -0
  159. package/dist/inline-tools/align.d.ts +14 -0
  160. package/dist/inline-tools/align.js +120 -0
  161. package/dist/inline-tools/bold.d.ts +8 -0
  162. package/dist/inline-tools/bold.js +12 -0
  163. package/dist/inline-tools/caseTransform.d.ts +8 -0
  164. package/dist/inline-tools/caseTransform.js +87 -0
  165. package/dist/inline-tools/clearFormatting.d.ts +8 -0
  166. package/dist/inline-tools/clearFormatting.js +74 -0
  167. package/dist/inline-tools/code.d.ts +8 -0
  168. package/dist/inline-tools/code.js +12 -0
  169. package/dist/inline-tools/colorPicker.d.ts +7 -0
  170. package/dist/inline-tools/colorPicker.js +271 -0
  171. package/dist/inline-tools/defaults.d.ts +10 -0
  172. package/dist/inline-tools/defaults.js +57 -0
  173. package/dist/inline-tools/fontSize.d.ts +7 -0
  174. package/dist/inline-tools/fontSize.js +524 -0
  175. package/dist/inline-tools/italic.d.ts +8 -0
  176. package/dist/inline-tools/italic.js +12 -0
  177. package/dist/inline-tools/link.d.ts +12 -0
  178. package/dist/inline-tools/link.js +251 -0
  179. package/dist/inline-tools/marker.d.ts +8 -0
  180. package/dist/inline-tools/marker.js +12 -0
  181. package/dist/inline-tools/scriptTool.d.ts +11 -0
  182. package/dist/inline-tools/scriptTool.js +149 -0
  183. package/dist/inline-tools/strikethrough.d.ts +8 -0
  184. package/dist/inline-tools/strikethrough.js +12 -0
  185. package/dist/inline-tools/utils.d.ts +220 -0
  186. package/dist/inline-tools/utils.js +992 -0
  187. package/dist/locale/en.d.ts +311 -0
  188. package/dist/locale/en.js +52 -0
  189. package/dist/locale/ru.d.ts +315 -0
  190. package/dist/locale/ru.js +52 -0
  191. package/dist/plugins/BlockPluginAbstract.d.ts +82 -0
  192. package/dist/plugins/BlockPluginAbstract.js +111 -0
  193. package/dist/plugins/README.md +44 -0
  194. package/dist/plugins/README.ru.md +40 -0
  195. package/dist/plugins/async.d.ts +35 -0
  196. package/dist/plugins/async.js +79 -0
  197. package/dist/plugins/attaches/README.md +86 -0
  198. package/dist/plugins/attaches/README.ru.md +87 -0
  199. package/dist/plugins/attaches/attaches.css +374 -0
  200. package/dist/plugins/attaches/index.d.ts +133 -0
  201. package/dist/plugins/attaches/index.js +921 -0
  202. package/dist/plugins/attaches/locale/en.d.ts +23 -0
  203. package/dist/plugins/attaches/locale/en.js +19 -0
  204. package/dist/plugins/attaches/locale/keys.d.ts +5 -0
  205. package/dist/plugins/attaches/locale/ru.d.ts +25 -0
  206. package/dist/plugins/attaches/locale/ru.js +19 -0
  207. package/dist/plugins/carousel/README.md +108 -0
  208. package/dist/plugins/carousel/README.ru.md +109 -0
  209. package/dist/plugins/carousel/carousel.css +198 -0
  210. package/dist/plugins/carousel/icons.d.ts +12 -0
  211. package/dist/plugins/carousel/icons.js +13 -0
  212. package/dist/plugins/carousel/index.d.ts +137 -0
  213. package/dist/plugins/carousel/index.js +859 -0
  214. package/dist/plugins/carousel/locale/en.d.ts +41 -0
  215. package/dist/plugins/carousel/locale/en.js +40 -0
  216. package/dist/plugins/carousel/locale/keys.d.ts +2 -0
  217. package/dist/plugins/carousel/locale/ru.d.ts +41 -0
  218. package/dist/plugins/carousel/locale/ru.js +40 -0
  219. package/dist/plugins/carousel/state.d.ts +29 -0
  220. package/dist/plugins/carousel/state.js +42 -0
  221. package/dist/plugins/checklist/README.md +54 -0
  222. package/dist/plugins/checklist/README.ru.md +55 -0
  223. package/dist/plugins/checklist/checklist.css +80 -0
  224. package/dist/plugins/checklist/index.d.ts +75 -0
  225. package/dist/plugins/checklist/index.js +407 -0
  226. package/dist/plugins/checklist/locale/en.d.ts +5 -0
  227. package/dist/plugins/checklist/locale/en.js +4 -0
  228. package/dist/plugins/checklist/locale/keys.d.ts +5 -0
  229. package/dist/plugins/checklist/locale/ru.d.ts +5 -0
  230. package/dist/plugins/checklist/locale/ru.js +4 -0
  231. package/dist/plugins/checklist/mapTextFields.d.ts +1 -0
  232. package/dist/plugins/checklist/mapTextFields.js +1 -0
  233. package/dist/plugins/code/README.md +59 -0
  234. package/dist/plugins/code/README.ru.md +60 -0
  235. package/dist/plugins/code/code.css +360 -0
  236. package/dist/plugins/code/index.d.ts +109 -0
  237. package/dist/plugins/code/index.js +1012 -0
  238. package/dist/plugins/code/locale/en.d.ts +11 -0
  239. package/dist/plugins/code/locale/en.js +10 -0
  240. package/dist/plugins/code/locale/keys.d.ts +5 -0
  241. package/dist/plugins/code/locale/ru.d.ts +11 -0
  242. package/dist/plugins/code/locale/ru.js +10 -0
  243. package/dist/plugins/columns/README.md +55 -0
  244. package/dist/plugins/columns/README.ru.md +56 -0
  245. package/dist/plugins/columns/columns.css +103 -0
  246. package/dist/plugins/columns/index.d.ts +58 -0
  247. package/dist/plugins/columns/index.js +236 -0
  248. package/dist/plugins/columns/locale/en.d.ts +6 -0
  249. package/dist/plugins/columns/locale/en.js +5 -0
  250. package/dist/plugins/columns/locale/keys.d.ts +5 -0
  251. package/dist/plugins/columns/locale/ru.d.ts +6 -0
  252. package/dist/plugins/columns/locale/ru.js +5 -0
  253. package/dist/plugins/delimiter/README.md +48 -0
  254. package/dist/plugins/delimiter/README.ru.md +49 -0
  255. package/dist/plugins/delimiter/delimiter.css +22 -0
  256. package/dist/plugins/delimiter/index.d.ts +38 -0
  257. package/dist/plugins/delimiter/index.js +69 -0
  258. package/dist/plugins/delimiter/locale/en.d.ts +4 -0
  259. package/dist/plugins/delimiter/locale/en.js +3 -0
  260. package/dist/plugins/delimiter/locale/keys.d.ts +5 -0
  261. package/dist/plugins/delimiter/locale/ru.d.ts +4 -0
  262. package/dist/plugins/delimiter/locale/ru.js +3 -0
  263. package/dist/plugins/embed/README.md +81 -0
  264. package/dist/plugins/embed/README.ru.md +82 -0
  265. package/dist/plugins/embed/embed.css +558 -0
  266. package/dist/plugins/embed/index.d.ts +271 -0
  267. package/dist/plugins/embed/index.js +967 -0
  268. package/dist/plugins/embed/locale/en.d.ts +20 -0
  269. package/dist/plugins/embed/locale/en.js +19 -0
  270. package/dist/plugins/embed/locale/keys.d.ts +5 -0
  271. package/dist/plugins/embed/locale/ru.d.ts +20 -0
  272. package/dist/plugins/embed/locale/ru.js +19 -0
  273. package/dist/plugins/embed/player.d.ts +1 -0
  274. package/dist/plugins/embed/player.js +1 -0
  275. package/dist/plugins/gallery/README.md +92 -0
  276. package/dist/plugins/gallery/README.ru.md +93 -0
  277. package/dist/plugins/gallery/css.d.ts +52 -0
  278. package/dist/plugins/gallery/css.js +67 -0
  279. package/dist/plugins/gallery/gallery.css +860 -0
  280. package/dist/plugins/gallery/icons.d.ts +13 -0
  281. package/dist/plugins/gallery/icons.js +45 -0
  282. package/dist/plugins/gallery/index.d.ts +126 -0
  283. package/dist/plugins/gallery/index.js +355 -0
  284. package/dist/plugins/gallery/layout.d.ts +35 -0
  285. package/dist/plugins/gallery/layout.js +79 -0
  286. package/dist/plugins/gallery/locale/en.d.ts +36 -0
  287. package/dist/plugins/gallery/locale/en.js +35 -0
  288. package/dist/plugins/gallery/locale/keys.d.ts +5 -0
  289. package/dist/plugins/gallery/locale/ru.d.ts +36 -0
  290. package/dist/plugins/gallery/locale/ru.js +35 -0
  291. package/dist/plugins/gallery/settings.d.ts +25 -0
  292. package/dist/plugins/gallery/settings.js +199 -0
  293. package/dist/plugins/gallery/slot.d.ts +73 -0
  294. package/dist/plugins/gallery/slot.js +260 -0
  295. package/dist/plugins/gallery/state.d.ts +67 -0
  296. package/dist/plugins/gallery/state.js +133 -0
  297. package/dist/plugins/gallery/styles.d.ts +10 -0
  298. package/dist/plugins/gallery/styles.js +24 -0
  299. package/dist/plugins/gallery/uploader.d.ts +31 -0
  300. package/dist/plugins/gallery/uploader.js +111 -0
  301. package/dist/plugins/gallery/view-empty.d.ts +41 -0
  302. package/dist/plugins/gallery/view-empty.js +56 -0
  303. package/dist/plugins/gallery/view-filled.d.ts +55 -0
  304. package/dist/plugins/gallery/view-filled.js +353 -0
  305. package/dist/plugins/heading/HeadingLevelSelect.d.ts +16 -0
  306. package/dist/plugins/heading/HeadingLevelSelect.js +183 -0
  307. package/dist/plugins/heading/README.md +58 -0
  308. package/dist/plugins/heading/README.ru.md +59 -0
  309. package/dist/plugins/heading/heading.css +76 -0
  310. package/dist/plugins/heading/index.d.ts +124 -0
  311. package/dist/plugins/heading/index.js +273 -0
  312. package/dist/plugins/heading/locale/en.d.ts +11 -0
  313. package/dist/plugins/heading/locale/en.js +10 -0
  314. package/dist/plugins/heading/locale/keys.d.ts +5 -0
  315. package/dist/plugins/heading/locale/ru.d.ts +11 -0
  316. package/dist/plugins/heading/locale/ru.js +10 -0
  317. package/dist/plugins/heading/mapTextFields.d.ts +1 -0
  318. package/dist/plugins/heading/mapTextFields.js +1 -0
  319. package/dist/plugins/image/README.md +93 -0
  320. package/dist/plugins/image/README.ru.md +94 -0
  321. package/dist/plugins/image/css.d.ts +47 -0
  322. package/dist/plugins/image/css.js +47 -0
  323. package/dist/plugins/image/icons.d.ts +13 -0
  324. package/dist/plugins/image/icons.js +26 -0
  325. package/dist/plugins/image/image.css +559 -0
  326. package/dist/plugins/image/index.d.ts +129 -0
  327. package/dist/plugins/image/index.js +346 -0
  328. package/dist/plugins/image/locale/en.d.ts +39 -0
  329. package/dist/plugins/image/locale/en.js +38 -0
  330. package/dist/plugins/image/locale/keys.d.ts +5 -0
  331. package/dist/plugins/image/locale/ru.d.ts +39 -0
  332. package/dist/plugins/image/locale/ru.js +38 -0
  333. package/dist/plugins/image/settings.d.ts +23 -0
  334. package/dist/plugins/image/settings.js +347 -0
  335. package/dist/plugins/image/state.d.ts +86 -0
  336. package/dist/plugins/image/state.js +151 -0
  337. package/dist/plugins/image/styles.d.ts +21 -0
  338. package/dist/plugins/image/styles.js +58 -0
  339. package/dist/plugins/image/uploader.d.ts +31 -0
  340. package/dist/plugins/image/uploader.js +92 -0
  341. package/dist/plugins/image/view-empty.d.ts +44 -0
  342. package/dist/plugins/image/view-empty.js +61 -0
  343. package/dist/plugins/image/view-filled.d.ts +49 -0
  344. package/dist/plugins/image/view-filled.js +282 -0
  345. package/dist/plugins/index.d.ts +21 -0
  346. package/dist/plugins/index.js +22 -0
  347. package/dist/plugins/link-preview/README.md +69 -0
  348. package/dist/plugins/link-preview/README.ru.md +70 -0
  349. package/dist/plugins/link-preview/index.d.ts +169 -0
  350. package/dist/plugins/link-preview/index.js +698 -0
  351. package/dist/plugins/link-preview/link-preview.css +254 -0
  352. package/dist/plugins/link-preview/locale/en.d.ts +14 -0
  353. package/dist/plugins/link-preview/locale/en.js +13 -0
  354. package/dist/plugins/link-preview/locale/keys.d.ts +5 -0
  355. package/dist/plugins/link-preview/locale/ru.d.ts +14 -0
  356. package/dist/plugins/link-preview/locale/ru.js +13 -0
  357. package/dist/plugins/list/README.md +59 -0
  358. package/dist/plugins/list/README.ru.md +60 -0
  359. package/dist/plugins/list/index.d.ts +134 -0
  360. package/dist/plugins/list/index.js +566 -0
  361. package/dist/plugins/list/list.css +57 -0
  362. package/dist/plugins/list/locale/en.d.ts +5 -0
  363. package/dist/plugins/list/locale/en.js +4 -0
  364. package/dist/plugins/list/locale/keys.d.ts +5 -0
  365. package/dist/plugins/list/locale/ru.d.ts +5 -0
  366. package/dist/plugins/list/locale/ru.js +4 -0
  367. package/dist/plugins/list/mapTextFields.d.ts +1 -0
  368. package/dist/plugins/list/mapTextFields.js +1 -0
  369. package/dist/plugins/paragraph/README.md +57 -0
  370. package/dist/plugins/paragraph/README.ru.md +58 -0
  371. package/dist/plugins/paragraph/index.d.ts +112 -0
  372. package/dist/plugins/paragraph/index.js +158 -0
  373. package/dist/plugins/paragraph/locale/en.d.ts +5 -0
  374. package/dist/plugins/paragraph/locale/en.js +4 -0
  375. package/dist/plugins/paragraph/locale/keys.d.ts +5 -0
  376. package/dist/plugins/paragraph/locale/ru.d.ts +5 -0
  377. package/dist/plugins/paragraph/locale/ru.js +4 -0
  378. package/dist/plugins/paragraph/mapTextFields.d.ts +1 -0
  379. package/dist/plugins/paragraph/mapTextFields.js +1 -0
  380. package/dist/plugins/paragraph/paragraph.css +56 -0
  381. package/dist/plugins/person/README.md +72 -0
  382. package/dist/plugins/person/README.ru.md +73 -0
  383. package/dist/plugins/person/index.d.ts +189 -0
  384. package/dist/plugins/person/index.js +777 -0
  385. package/dist/plugins/person/locale/en.d.ts +18 -0
  386. package/dist/plugins/person/locale/en.js +17 -0
  387. package/dist/plugins/person/locale/keys.d.ts +5 -0
  388. package/dist/plugins/person/locale/ru.d.ts +18 -0
  389. package/dist/plugins/person/locale/ru.js +17 -0
  390. package/dist/plugins/person/person.css +431 -0
  391. package/dist/plugins/person/socialResolver.d.ts +18 -0
  392. package/dist/plugins/person/socialResolver.js +69 -0
  393. package/dist/plugins/poll/README.md +96 -0
  394. package/dist/plugins/poll/README.ru.md +97 -0
  395. package/dist/plugins/poll/index.d.ts +134 -0
  396. package/dist/plugins/poll/index.js +825 -0
  397. package/dist/plugins/poll/locale/en.d.ts +23 -0
  398. package/dist/plugins/poll/locale/en.js +22 -0
  399. package/dist/plugins/poll/locale/keys.d.ts +5 -0
  400. package/dist/plugins/poll/locale/ru.d.ts +23 -0
  401. package/dist/plugins/poll/locale/ru.js +22 -0
  402. package/dist/plugins/poll/poll.css +400 -0
  403. package/dist/plugins/quote/README.md +55 -0
  404. package/dist/plugins/quote/README.ru.md +56 -0
  405. package/dist/plugins/quote/index.d.ts +81 -0
  406. package/dist/plugins/quote/index.js +165 -0
  407. package/dist/plugins/quote/locale/en.d.ts +6 -0
  408. package/dist/plugins/quote/locale/en.js +5 -0
  409. package/dist/plugins/quote/locale/keys.d.ts +5 -0
  410. package/dist/plugins/quote/locale/ru.d.ts +6 -0
  411. package/dist/plugins/quote/locale/ru.js +5 -0
  412. package/dist/plugins/quote/mapTextFields.d.ts +1 -0
  413. package/dist/plugins/quote/mapTextFields.js +1 -0
  414. package/dist/plugins/quote/quote.css +72 -0
  415. package/dist/plugins/raw/README.md +54 -0
  416. package/dist/plugins/raw/README.ru.md +55 -0
  417. package/dist/plugins/raw/index.d.ts +62 -0
  418. package/dist/plugins/raw/index.js +227 -0
  419. package/dist/plugins/raw/locale/en.d.ts +7 -0
  420. package/dist/plugins/raw/locale/en.js +6 -0
  421. package/dist/plugins/raw/locale/keys.d.ts +5 -0
  422. package/dist/plugins/raw/locale/ru.d.ts +7 -0
  423. package/dist/plugins/raw/locale/ru.js +6 -0
  424. package/dist/plugins/raw/raw.css +127 -0
  425. package/dist/plugins/shared/actionBar.d.ts +19 -0
  426. package/dist/plugins/shared/actionBar.js +35 -0
  427. package/dist/plugins/shared/dropzone.d.ts +106 -0
  428. package/dist/plugins/shared/dropzone.js +107 -0
  429. package/dist/plugins/shared/fileInput.d.ts +44 -0
  430. package/dist/plugins/shared/fileInput.js +62 -0
  431. package/dist/plugins/spoiler/README.md +55 -0
  432. package/dist/plugins/spoiler/README.ru.md +56 -0
  433. package/dist/plugins/spoiler/index.d.ts +61 -0
  434. package/dist/plugins/spoiler/index.js +169 -0
  435. package/dist/plugins/spoiler/locale/en.d.ts +7 -0
  436. package/dist/plugins/spoiler/locale/en.js +6 -0
  437. package/dist/plugins/spoiler/locale/keys.d.ts +5 -0
  438. package/dist/plugins/spoiler/locale/ru.d.ts +7 -0
  439. package/dist/plugins/spoiler/locale/ru.js +6 -0
  440. package/dist/plugins/spoiler/spoiler.css +101 -0
  441. package/dist/plugins/table/README.md +55 -0
  442. package/dist/plugins/table/README.ru.md +56 -0
  443. package/dist/plugins/table/index.d.ts +85 -0
  444. package/dist/plugins/table/index.js +399 -0
  445. package/dist/plugins/table/locale/en.d.ts +9 -0
  446. package/dist/plugins/table/locale/en.js +8 -0
  447. package/dist/plugins/table/locale/keys.d.ts +5 -0
  448. package/dist/plugins/table/locale/ru.d.ts +9 -0
  449. package/dist/plugins/table/locale/ru.js +8 -0
  450. package/dist/plugins/table/table.css +58 -0
  451. package/dist/plugins/toggle/README.md +56 -0
  452. package/dist/plugins/toggle/README.ru.md +57 -0
  453. package/dist/plugins/toggle/index.d.ts +69 -0
  454. package/dist/plugins/toggle/index.js +191 -0
  455. package/dist/plugins/toggle/locale/en.d.ts +7 -0
  456. package/dist/plugins/toggle/locale/en.js +6 -0
  457. package/dist/plugins/toggle/locale/keys.d.ts +5 -0
  458. package/dist/plugins/toggle/locale/ru.d.ts +7 -0
  459. package/dist/plugins/toggle/locale/ru.js +6 -0
  460. package/dist/plugins/toggle/toggle.css +101 -0
  461. package/dist/plugins/warning/README.md +55 -0
  462. package/dist/plugins/warning/README.ru.md +56 -0
  463. package/dist/plugins/warning/index.d.ts +66 -0
  464. package/dist/plugins/warning/index.js +167 -0
  465. package/dist/plugins/warning/locale/en.d.ts +6 -0
  466. package/dist/plugins/warning/locale/en.js +5 -0
  467. package/dist/plugins/warning/locale/keys.d.ts +5 -0
  468. package/dist/plugins/warning/locale/ru.d.ts +6 -0
  469. package/dist/plugins/warning/locale/ru.js +5 -0
  470. package/dist/plugins/warning/warning.css +63 -0
  471. package/dist/renderer/async.d.ts +1 -0
  472. package/dist/renderer/async.js +7 -0
  473. package/dist/renderer/errors.d.ts +27 -0
  474. package/dist/renderer/errors.js +48 -0
  475. package/dist/renderer/index.d.ts +82 -0
  476. package/dist/renderer/index.js +413 -0
  477. package/dist/renderer/inline.d.ts +5 -0
  478. package/dist/renderer/inline.js +61 -0
  479. package/dist/renderer/locale/en.d.ts +35 -0
  480. package/dist/renderer/locale/en.js +29 -0
  481. package/dist/renderer/locale/ru.d.ts +37 -0
  482. package/dist/renderer/locale/ru.js +29 -0
  483. package/dist/renderer/renderers/README.md +7 -0
  484. package/dist/renderer/renderers/README.ru.md +5 -0
  485. package/dist/renderer/renderers/async.d.ts +51 -0
  486. package/dist/renderer/renderers/async.js +99 -0
  487. package/dist/renderer/renderers/attaches/README.md +48 -0
  488. package/dist/renderer/renderers/attaches/README.ru.md +48 -0
  489. package/dist/renderer/renderers/attaches/index.d.ts +37 -0
  490. package/dist/renderer/renderers/attaches/index.js +451 -0
  491. package/dist/renderer/renderers/attaches/locale/en.d.ts +11 -0
  492. package/dist/renderer/renderers/attaches/locale/en.js +7 -0
  493. package/dist/renderer/renderers/attaches/locale/ru.d.ts +13 -0
  494. package/dist/renderer/renderers/attaches/locale/ru.js +7 -0
  495. package/dist/renderer/renderers/attaches/styles.css +196 -0
  496. package/dist/renderer/renderers/carousel/README.md +43 -0
  497. package/dist/renderer/renderers/carousel/README.ru.md +43 -0
  498. package/dist/renderer/renderers/carousel/index.d.ts +7 -0
  499. package/dist/renderer/renderers/carousel/index.js +110 -0
  500. package/dist/renderer/renderers/carousel/styles.css +7 -0
  501. package/dist/renderer/renderers/checklist/README.md +33 -0
  502. package/dist/renderer/renderers/checklist/README.ru.md +33 -0
  503. package/dist/renderer/renderers/checklist/index.d.ts +7 -0
  504. package/dist/renderer/renderers/checklist/index.js +55 -0
  505. package/dist/renderer/renderers/checklist/styles.css +54 -0
  506. package/dist/renderer/renderers/code/README.md +33 -0
  507. package/dist/renderer/renderers/code/README.ru.md +33 -0
  508. package/dist/renderer/renderers/code/index.d.ts +9 -0
  509. package/dist/renderer/renderers/code/index.js +137 -0
  510. package/dist/renderer/renderers/code/locale/en.d.ts +5 -0
  511. package/dist/renderer/renderers/code/locale/en.js +4 -0
  512. package/dist/renderer/renderers/code/locale/ru.d.ts +5 -0
  513. package/dist/renderer/renderers/code/locale/ru.js +4 -0
  514. package/dist/renderer/renderers/code/styles.css +330 -0
  515. package/dist/renderer/renderers/columns/README.md +33 -0
  516. package/dist/renderer/renderers/columns/README.ru.md +33 -0
  517. package/dist/renderer/renderers/columns/index.d.ts +7 -0
  518. package/dist/renderer/renderers/columns/index.js +53 -0
  519. package/dist/renderer/renderers/columns/styles.css +5 -0
  520. package/dist/renderer/renderers/delimiter/README.md +33 -0
  521. package/dist/renderer/renderers/delimiter/README.ru.md +33 -0
  522. package/dist/renderer/renderers/delimiter/index.d.ts +7 -0
  523. package/dist/renderer/renderers/delimiter/index.js +28 -0
  524. package/dist/renderer/renderers/delimiter/styles.css +12 -0
  525. package/dist/renderer/renderers/embed/README.md +35 -0
  526. package/dist/renderer/renderers/embed/README.ru.md +35 -0
  527. package/dist/renderer/renderers/embed/index.d.ts +8 -0
  528. package/dist/renderer/renderers/embed/index.js +66 -0
  529. package/dist/renderer/renderers/embed/styles.css +217 -0
  530. package/dist/renderer/renderers/gallery/README.md +41 -0
  531. package/dist/renderer/renderers/gallery/README.ru.md +41 -0
  532. package/dist/renderer/renderers/gallery/index.d.ts +8 -0
  533. package/dist/renderer/renderers/gallery/index.js +329 -0
  534. package/dist/renderer/renderers/gallery/styles.css +419 -0
  535. package/dist/renderer/renderers/heading/README.md +33 -0
  536. package/dist/renderer/renderers/heading/README.ru.md +33 -0
  537. package/dist/renderer/renderers/heading/index.d.ts +8 -0
  538. package/dist/renderer/renderers/heading/index.js +38 -0
  539. package/dist/renderer/renderers/heading/styles.css +26 -0
  540. package/dist/renderer/renderers/image/README.md +42 -0
  541. package/dist/renderer/renderers/image/README.ru.md +42 -0
  542. package/dist/renderer/renderers/image/index.d.ts +7 -0
  543. package/dist/renderer/renderers/image/index.js +93 -0
  544. package/dist/renderer/renderers/image/styles.css +40 -0
  545. package/dist/renderer/renderers/index.d.ts +47 -0
  546. package/dist/renderer/renderers/index.js +125 -0
  547. package/dist/renderer/renderers/link-preview/README.md +43 -0
  548. package/dist/renderer/renderers/link-preview/README.ru.md +43 -0
  549. package/dist/renderer/renderers/link-preview/index.d.ts +9 -0
  550. package/dist/renderer/renderers/link-preview/index.js +118 -0
  551. package/dist/renderer/renderers/link-preview/styles.css +246 -0
  552. package/dist/renderer/renderers/list/README.md +33 -0
  553. package/dist/renderer/renderers/list/README.ru.md +33 -0
  554. package/dist/renderer/renderers/list/index.d.ts +8 -0
  555. package/dist/renderer/renderers/list/index.js +42 -0
  556. package/dist/renderer/renderers/list/styles.css +20 -0
  557. package/dist/renderer/renderers/paragraph/README.md +33 -0
  558. package/dist/renderer/renderers/paragraph/README.ru.md +33 -0
  559. package/dist/renderer/renderers/paragraph/index.d.ts +7 -0
  560. package/dist/renderer/renderers/paragraph/index.js +35 -0
  561. package/dist/renderer/renderers/paragraph/styles.css +3 -0
  562. package/dist/renderer/renderers/person/README.md +44 -0
  563. package/dist/renderer/renderers/person/README.ru.md +44 -0
  564. package/dist/renderer/renderers/person/index.d.ts +7 -0
  565. package/dist/renderer/renderers/person/index.js +213 -0
  566. package/dist/renderer/renderers/person/locale/en.d.ts +6 -0
  567. package/dist/renderer/renderers/person/locale/en.js +5 -0
  568. package/dist/renderer/renderers/person/locale/ru.d.ts +6 -0
  569. package/dist/renderer/renderers/person/locale/ru.js +5 -0
  570. package/dist/renderer/renderers/person/styles.css +47 -0
  571. package/dist/renderer/renderers/poll/README.md +68 -0
  572. package/dist/renderer/renderers/poll/README.ru.md +68 -0
  573. package/dist/renderer/renderers/poll/index.d.ts +9 -0
  574. package/dist/renderer/renderers/poll/index.js +339 -0
  575. package/dist/renderer/renderers/poll/styles.css +148 -0
  576. package/dist/renderer/renderers/quote/README.md +33 -0
  577. package/dist/renderer/renderers/quote/README.ru.md +33 -0
  578. package/dist/renderer/renderers/quote/index.d.ts +7 -0
  579. package/dist/renderer/renderers/quote/index.js +59 -0
  580. package/dist/renderer/renderers/quote/styles.css +43 -0
  581. package/dist/renderer/renderers/raw/README.md +33 -0
  582. package/dist/renderer/renderers/raw/README.ru.md +33 -0
  583. package/dist/renderer/renderers/raw/index.d.ts +7 -0
  584. package/dist/renderer/renderers/raw/index.js +27 -0
  585. package/dist/renderer/renderers/spoiler/README.md +33 -0
  586. package/dist/renderer/renderers/spoiler/README.ru.md +33 -0
  587. package/dist/renderer/renderers/spoiler/index.d.ts +9 -0
  588. package/dist/renderer/renderers/spoiler/index.js +84 -0
  589. package/dist/renderer/renderers/spoiler/locale/en.d.ts +4 -0
  590. package/dist/renderer/renderers/spoiler/locale/en.js +3 -0
  591. package/dist/renderer/renderers/spoiler/locale/ru.d.ts +4 -0
  592. package/dist/renderer/renderers/spoiler/locale/ru.js +3 -0
  593. package/dist/renderer/renderers/spoiler/styles.css +61 -0
  594. package/dist/renderer/renderers/table/README.md +33 -0
  595. package/dist/renderer/renderers/table/README.ru.md +33 -0
  596. package/dist/renderer/renderers/table/index.d.ts +7 -0
  597. package/dist/renderer/renderers/table/index.js +83 -0
  598. package/dist/renderer/renderers/table/styles.css +34 -0
  599. package/dist/renderer/renderers/toggle/README.md +33 -0
  600. package/dist/renderer/renderers/toggle/README.ru.md +33 -0
  601. package/dist/renderer/renderers/toggle/index.d.ts +7 -0
  602. package/dist/renderer/renderers/toggle/index.js +59 -0
  603. package/dist/renderer/renderers/toggle/styles.css +49 -0
  604. package/dist/renderer/renderers/warning/README.md +33 -0
  605. package/dist/renderer/renderers/warning/README.ru.md +33 -0
  606. package/dist/renderer/renderers/warning/index.d.ts +7 -0
  607. package/dist/renderer/renderers/warning/index.js +62 -0
  608. package/dist/renderer/renderers/warning/styles.css +41 -0
  609. package/dist/renderer/styles/base.css +168 -0
  610. package/dist/renderer/types.d.ts +464 -0
  611. package/dist/shared/blockDataNormalizers.d.ts +14 -0
  612. package/dist/shared/blockDataNormalizers.js +235 -0
  613. package/dist/shared/blockDataValidators.d.ts +81 -0
  614. package/dist/shared/blockDataValidators.js +313 -0
  615. package/dist/shared/blockTypes.d.ts +2 -0
  616. package/dist/shared/blockTypes.js +26 -0
  617. package/dist/shared/carouselData.d.ts +34 -0
  618. package/dist/shared/carouselData.js +111 -0
  619. package/dist/shared/cloneEditorData.d.ts +11 -0
  620. package/dist/shared/cloneEditorData.js +20 -0
  621. package/dist/shared/documentTypes.d.ts +35 -0
  622. package/dist/shared/embedPlayer.d.ts +42 -0
  623. package/dist/shared/embedPlayer.js +141 -0
  624. package/dist/shared/fileUtils.d.ts +33 -0
  625. package/dist/shared/fileUtils.js +109 -0
  626. package/dist/shared/highlightRuntime.d.ts +38 -0
  627. package/dist/shared/highlightRuntime.js +75 -0
  628. package/dist/shared/inlineMarshal.d.ts +82 -0
  629. package/dist/shared/inlineMarshal.js +215 -0
  630. package/dist/shared/jsonData.d.ts +11 -0
  631. package/dist/shared/jsonData.js +53 -0
  632. package/dist/shared/localeTypes.d.ts +11 -0
  633. package/dist/shared/mapTextFields.d.ts +89 -0
  634. package/dist/shared/mapTextFields.js +119 -0
  635. package/dist/shared/pollData.d.ts +90 -0
  636. package/dist/shared/pollData.js +231 -0
  637. package/dist/shared/resolvePath.d.ts +8 -0
  638. package/dist/shared/resolvePath.js +10 -0
  639. package/dist/shared/runtime/dompurify.js +2424 -0
  640. package/dist/shared/runtime/highlightBundle.js +10 -0
  641. package/dist/shared/runtime/jszip.js +13 -0
  642. package/dist/shared/sanitize/allowlist.d.ts +21 -0
  643. package/dist/shared/sanitize/allowlist.js +40 -0
  644. package/dist/shared/sanitize/escapeHtml.d.ts +6 -0
  645. package/dist/shared/sanitize/escapeHtml.js +11 -0
  646. package/dist/shared/sanitize/index.d.ts +8 -0
  647. package/dist/shared/sanitize/index.js +8 -0
  648. package/dist/shared/sanitize/parseInline.d.ts +11 -0
  649. package/dist/shared/sanitize/parseInline.js +25 -0
  650. package/dist/shared/sanitize/sanitizeHtml.d.ts +8 -0
  651. package/dist/shared/sanitize/sanitizeHtml.js +19 -0
  652. package/dist/shared/sanitize/sanitizeRawHtml.d.ts +15 -0
  653. package/dist/shared/sanitize/sanitizeRawHtml.js +162 -0
  654. package/dist/shared/sanitize/sanitizeStyle.d.ts +9 -0
  655. package/dist/shared/sanitize/sanitizeStyle.js +39 -0
  656. package/dist/shared/sanitize/sanitizeUrl.d.ts +28 -0
  657. package/dist/shared/sanitize/sanitizeUrl.js +86 -0
  658. package/dist/shared/sanitize/walker.d.ts +15 -0
  659. package/dist/shared/sanitize/walker.js +98 -0
  660. package/dist/shared/styleRegistry.d.ts +4 -0
  661. package/dist/shared/styleRegistry.js +38 -0
  662. package/dist/shared/textFormat.d.ts +32 -0
  663. package/dist/shared/textFormat.js +48 -0
  664. package/dist/shared/zipRuntime.d.ts +19 -0
  665. package/dist/shared/zipRuntime.js +54 -0
  666. package/dist/styles/editor.css +3 -0
  667. package/dist/types.d.ts +1 -0
  668. package/package.json +149 -0
@@ -0,0 +1,992 @@
1
+ import { BLOCK_CLASS } from '../core/constants.js'
2
+ import { CrossBlockSelection } from '../core/CrossBlockSelection.js'
3
+ import { closestBlock, el } from '../core/dom.js'
4
+ import { createSvgIcon, ICON_BACK } from '../core/icons.js'
5
+ import { getTextOffset, editableTextWalker } from '../core/textOffset.js'
6
+
7
+ /** SVG markup for the standard back-navigation icon used in action panels. */
8
+ export { ICON_BACK }
9
+
10
+ /** SVG markup for the bold formatting control. */
11
+ export const ICON_BOLD = createSvgIcon('<path d="M7 5h6a3.5 3.5 0 0 1 0 7h-6z"/><path d="M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7"/>')
12
+ /** SVG markup for the italic formatting control. */
13
+ export const ICON_ITALIC = createSvgIcon('<path d="M11 5l6 0"/><path d="M7 19l6 0"/><path d="M14 5l-4 14"/>')
14
+ /** SVG markup for the link formatting control. */
15
+ export const ICON_LINK = createSvgIcon('<path d="M9 15l6 -6"/><path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464"/><path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463"/>')
16
+ /** SVG markup for the inline-code formatting control. */
17
+ export const ICON_CODE = createSvgIcon('<path d="M7 8l-4 4l4 4"/><path d="M17 8l4 4l-4 4"/>')
18
+ /** SVG markup for the text-highlight formatting control. */
19
+ export const ICON_HIGHLIGHT = createSvgIcon('<path d="M3 19h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4"/><path d="M12.5 5.5l4 4"/><path d="M4.5 13.5l4 4"/><path d="M21 15l-2 2l-4 -4l2 -2z"/>')
20
+ /** SVG markup for the strikethrough formatting control. */
21
+ export const ICON_STRIKETHROUGH = createSvgIcon('<path d="M5 12l14 0"/><path d="M16 6.5a4 2 0 0 0 -4 -1.5h-1a3.5 3.5 0 0 0 0 7h2a3.5 3.5 0 0 1 0 7h-1.5a4 2 0 0 1 -4 -1.5"/>')
22
+ /** SVG markup for the left-alignment control. */
23
+ export const ICON_ALIGN_LEFT = createSvgIcon('<path d="M4 6l16 0"/><path d="M4 12l10 0"/><path d="M4 18l14 0"/>')
24
+ /** SVG markup for the center-alignment control. */
25
+ export const ICON_ALIGN_CENTER = createSvgIcon('<path d="M4 6l16 0"/><path d="M7 12l10 0"/><path d="M5 18l14 0"/>')
26
+ /** SVG markup for the right-alignment control. */
27
+ export const ICON_ALIGN_RIGHT = createSvgIcon('<path d="M4 6l16 0"/><path d="M10 12l10 0"/><path d="M6 18l14 0"/>')
28
+ /** SVG markup for the justified-alignment control. */
29
+ export const ICON_ALIGN_JUSTIFY = createSvgIcon('<path d="M4 6l16 0"/><path d="M4 12l16 0"/><path d="M4 18l16 0"/>')
30
+ /** SVG markup for the remove-link control. */
31
+ export const ICON_UNLINK = createSvgIcon('<path d="M9 15l3 -3m2 -2l1 -1"/><path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464"/><path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463"/><path d="M3 3l18 18"/>')
32
+ /** SVG markup for confirmation controls. */
33
+ export const ICON_CHECK = createSvgIcon('<path d="M5 12l5 5l10 -10"/>')
34
+ /** SVG markup for the clear-formatting control. */
35
+ export const ICON_CLEAR = createSvgIcon('<path d="M17 15l4 4m0 -4l-4 4"/><path d="M7 6v-1h11v1"/><path d="M7 19l4 0"/><path d="M13 5l-4 14"/>')
36
+
37
+ /** Inline tag selector for empty-tag cleanup. */
38
+ const INLINE_TAGS_SELECTOR = 'b, i, s, em, strong, u, mark, code, span, a, sup, sub'
39
+
40
+ /**
41
+ * Like `element.closest(selector)`, but stops at the contenteditable boundary.
42
+ * Prevents matching elements outside the editor (e.g. a `<span>` in the page layout).
43
+ * @param {HTMLElement | null} el
44
+ * @param {string} selector
45
+ * @returns {HTMLElement | null}
46
+ */
47
+ function closestInEditable(el, selector) {
48
+ while (el) {
49
+ if (el.matches(selector)) return el
50
+ if (el.getAttribute('contenteditable') === 'true') return null
51
+ el = el.parentElement
52
+ }
53
+ return null
54
+ }
55
+
56
+ /**
57
+ * Remove empty inline wrapper tags and normalize text nodes.
58
+ * Called after any DOM mutation that unwraps/removes inline formatting.
59
+ * @param {Node | null} parent
60
+ * @returns {void}
61
+ */
62
+ export function removeEmptyInlineTags(parent) {
63
+ if (!parent || parent.nodeType !== Node.ELEMENT_NODE) return
64
+ const empties = /** @type {HTMLElement} */ (parent).querySelectorAll(INLINE_TAGS_SELECTOR)
65
+ for (const el of empties) {
66
+ if (!el.textContent && !el.querySelector('img, br')) el.remove()
67
+ }
68
+ parent.normalize()
69
+ }
70
+
71
+ /**
72
+ * Merge adjacent sibling elements with the same tag name.
73
+ * E.g. `<b>a</b><b>b</b>` → `<b>ab</b>`.
74
+ * Only merges direct siblings; does not recurse into children.
75
+ * @param {Node | null} parent
76
+ * @param {string} tagName — lowercase tag name to merge
77
+ */
78
+ function mergeAdjacentTags(parent, tagName) {
79
+ if (!parent || parent.nodeType !== Node.ELEMENT_NODE) return
80
+ const upper = tagName.toUpperCase()
81
+ // Normalize first so empty text nodes between elements are removed
82
+ parent.normalize()
83
+ /** @type {Node | null} */
84
+ let node = parent.firstChild
85
+ while (node) {
86
+ const next = node.nextSibling
87
+ // Only merge if two same-tag elements are truly DOM-adjacent (no text nodes between them)
88
+ if (node.nodeType === Node.ELEMENT_NODE
89
+ && next?.nodeType === Node.ELEMENT_NODE
90
+ && /** @type {Element} */ (node).tagName === upper
91
+ && /** @type {Element} */ (next).tagName === upper
92
+ && haveSameAttributes(
93
+ /** @type {Element} */ (node),
94
+ /** @type {Element} */ (next),
95
+ )) {
96
+ while (next.firstChild) node.appendChild(next.firstChild)
97
+ next.remove()
98
+ // Don't advance — merged node may have a new adjacent sibling
99
+ } else {
100
+ node = next
101
+ }
102
+ }
103
+ parent.normalize()
104
+ }
105
+
106
+ /**
107
+ * Compare element attributes without depending on their source order.
108
+ * Wrappers with different classes, data or inline styles must not be merged.
109
+ * @param {Element} left
110
+ * @param {Element} right
111
+ * @returns {boolean}
112
+ */
113
+ function haveSameAttributes(left, right) {
114
+ if (left.attributes.length !== right.attributes.length) return false
115
+ for (const attribute of left.attributes) {
116
+ if (right.getAttribute(attribute.name) !== attribute.value) return false
117
+ }
118
+ return true
119
+ }
120
+
121
+ /**
122
+ * Check if content already has a given tag in its wrapper chain.
123
+ * extractContents clones partially-covered inline elements (DOM spec),
124
+ * so we need to detect these to avoid double-wrapping.
125
+ *
126
+ * Handles both single-child chains (`<i><b>text</b></i>`) and
127
+ * multi-child fragments (`<i>a</i><s>b</s>`) — the latter can occur
128
+ * when extractContents splits a parent that had multiple children.
129
+ *
130
+ * @param {Node} content — text node, element, or DocumentFragment
131
+ * @param {string} tag — lowercase tag name to look for
132
+ * @returns {boolean}
133
+ */
134
+ function hasWrapperTag(content, tag) {
135
+ let node = content
136
+ // Unwrap single-child DocumentFragments
137
+ while (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && node.childNodes.length === 1) {
138
+ node = /** @type {Node} */ (node.firstChild)
139
+ }
140
+ // For multi-child fragments: the tag is considered present if the
141
+ // fragment itself IS the unwrapped content (no single wrapper around it).
142
+ // We only need to detect a wrapper that surrounds ALL children.
143
+ // Walk down single-child element chain looking for the tag.
144
+ while (node.nodeType === Node.ELEMENT_NODE) {
145
+ if (/** @type {Element} */ (node).tagName.toLowerCase() === tag) return true
146
+ // Only descend if there's exactly one element child (a wrapper chain)
147
+ const children = /** @type {Element} */ (node).children
148
+ if (children.length !== 1) break
149
+ node = children[0]
150
+ }
151
+ return false
152
+ }
153
+
154
+ /**
155
+ * Wrap content in an inline wrapper tag, but only if the content isn't
156
+ * already wrapped in the same tag (which happens when extractContents
157
+ * clones partially-covered elements per DOM spec).
158
+ *
159
+ * For multi-child fragments (e.g. `<i>a</i><s>b</s>`), wraps each
160
+ * top-level child individually if it lacks the wrapper, avoiding
161
+ * double-wrapping children that already have the tag from extractContents.
162
+ *
163
+ * @param {Node} content — text node, element, or DocumentFragment
164
+ * @param {HTMLElement} wrapperTemplate — cloned empty element to use as wrapper
165
+ * @returns {Node}
166
+ */
167
+ function wrapIfNeeded(content, wrapperTemplate) {
168
+ const tag = wrapperTemplate.tagName.toLowerCase()
169
+
170
+ // Already has the wrapper (single-child chain) — skip
171
+ if (hasWrapperTag(content, tag)) return content
172
+
173
+ // Multi-child fragment: extractContents already cloned partially-covered
174
+ // wrappers for each child individually (DOM spec). Adding another wrapper
175
+ // around the whole fragment would double-wrap children that don't need it.
176
+ // Leave multi-child fragments as-is — the per-child clones are correct.
177
+ if (content.nodeType === Node.DOCUMENT_FRAGMENT_NODE && content.childNodes.length > 1) {
178
+ return content
179
+ }
180
+
181
+ const w = /** @type {HTMLElement} */ (wrapperTemplate.cloneNode(false))
182
+ w.appendChild(content)
183
+ return w
184
+ }
185
+
186
+ /**
187
+ * Wrap or unwrap a selection with an inline tag.
188
+ * @param {string} tagName
189
+ * @param {Range} range
190
+ * @param {(element: HTMLElement) => boolean} [matches]
191
+ * @returns {void}
192
+ */
193
+ export function toggleTag(tagName, range, matches = () => true) {
194
+ const sel = window.getSelection()
195
+ if (!sel || !range) return
196
+
197
+ const parent = range.commonAncestorContainer
198
+ const container = parent.nodeType === Node.ELEMENT_NODE
199
+ ? /** @type {HTMLElement} */ (parent)
200
+ : parent.parentElement
201
+
202
+ // Case 1: Selection is entirely INSIDE an existing tag — unwrap
203
+ let ancestor = closestInEditable(container, tagName)
204
+ while (ancestor && !matches(ancestor)) {
205
+ ancestor = closestInEditable(ancestor.parentElement, tagName)
206
+ }
207
+ if (ancestor) {
208
+ // Check if selection covers the full ancestor content via character offsets
209
+ // (compareBoundaryPoints is unreliable for mixed text/element boundaries)
210
+ const startOff = getTextOffset(ancestor, range.startContainer, range.startOffset)
211
+ const endOff = getTextOffset(ancestor, range.endContainer, range.endOffset)
212
+ const totalLen = ancestor.textContent?.length ?? 0
213
+ const coversStart = startOff === 0
214
+ const coversEnd = endOff >= totalLen
215
+
216
+ if (coversStart && coversEnd) {
217
+ // Full unwrap — selection covers entire ancestor
218
+ const parentNode = ancestor.parentNode
219
+ const firstChild = ancestor.firstChild
220
+ const lastChild = ancestor.lastChild
221
+ while (ancestor.firstChild) {
222
+ parentNode?.insertBefore(ancestor.firstChild, ancestor)
223
+ }
224
+ ancestor.remove()
225
+ if (firstChild && lastChild) {
226
+ try {
227
+ sel.removeAllRanges()
228
+ const newRange = document.createRange()
229
+ newRange.setStartBefore(firstChild)
230
+ newRange.setEndAfter(lastChild)
231
+ sel.addRange(newRange)
232
+ } catch { /* detached */ }
233
+ }
234
+ removeEmptyInlineTags(parentNode)
235
+ } else {
236
+ // Partial unwrap — only remove tag from selected portion
237
+ // Preserves inner inline wrappers (e.g. removing <b> keeps <i>)
238
+ // Result: <b><i>before</i></b><i>selected</i><b><i>after</i></b>
239
+ const parentNode = ancestor.parentNode
240
+ const ref = ancestor.nextSibling
241
+
242
+ // Save selection offsets relative to CE for restoration after DOM mutations
243
+ const ce = ancestor.closest('[contenteditable="true"]') || parentNode
244
+ const savedSelStart = ce ? getTextOffset(ce, range.startContainer, range.startOffset) : -1
245
+ const savedSelEnd = ce ? getTextOffset(ce, range.endContainer, range.endOffset) : -1
246
+
247
+ // Save inner inline wrapper chain (between text node and ancestor, exclusive)
248
+ const innerWrappers = []
249
+ /** @type {Node | null} */
250
+ let n = range.startContainer
251
+ if (n.nodeType === Node.TEXT_NODE) n = n.parentElement
252
+ while (n && n !== ancestor) {
253
+ innerWrappers.push(/** @type {HTMLElement} */ (n).cloneNode(false))
254
+ n = /** @type {HTMLElement} */ (n).parentElement
255
+ }
256
+
257
+ // 1. Extract content AFTER selection
258
+ const afterRange = document.createRange()
259
+ afterRange.setStart(range.endContainer, range.endOffset)
260
+ afterRange.setEndAfter(ancestor.lastChild || ancestor)
261
+ const afterFrag = afterRange.extractContents()
262
+
263
+ // 2. Extract selected content and re-wrap with inner wrappers (preserving other formatting)
264
+ // wrapIfNeeded skips if extractContents already cloned the wrapper (partial coverage).
265
+ /** @type {Node} */
266
+ let wrappedSelected = range.extractContents()
267
+ for (let i = 0; i < innerWrappers.length; i++) {
268
+ const iw = innerWrappers[i]
269
+ if (!iw) continue
270
+ wrappedSelected = wrapIfNeeded(wrappedSelected, /** @type {HTMLElement} */ (iw))
271
+ }
272
+ parentNode?.insertBefore(wrappedSelected, ref)
273
+
274
+ // 4. Re-wrap after content with inner wrappers + ancestor tag
275
+ // extractContents already clones partially-covered inner wrappers,
276
+ // so wrapIfNeeded prevents duplication.
277
+ if (afterFrag.textContent) {
278
+ /** @type {Node} */
279
+ let wrappedAfter = afterFrag
280
+ for (let i = 0; i < innerWrappers.length; i++) {
281
+ const iw = innerWrappers[i]
282
+ if (!iw) continue
283
+ wrappedAfter = wrapIfNeeded(wrappedAfter, /** @type {HTMLElement} */ (iw))
284
+ }
285
+ const afterTag = /** @type {HTMLElement} */ (ancestor.cloneNode(false))
286
+ afterTag.appendChild(wrappedAfter)
287
+ parentNode?.insertBefore(afterTag, ref)
288
+ }
289
+
290
+ // 5. Remove ancestor if empty
291
+ if (!ancestor.textContent) ancestor.remove()
292
+
293
+ // 6. Clean up empty inline tags left by extraction
294
+ removeEmptyInlineTags(parentNode)
295
+ mergeAdjacentTags(parentNode, tagName)
296
+
297
+ // 7. Restore selection from saved offsets
298
+ if (ce && savedSelStart >= 0) {
299
+ const s = findNodeAtOffset(ce, savedSelStart)
300
+ const e = findNodeAtOffset(ce, savedSelEnd, 'end')
301
+ if (s && e) {
302
+ try {
303
+ sel.removeAllRanges()
304
+ const nr = document.createRange()
305
+ nr.setStart(s.node, s.offset)
306
+ nr.setEnd(e.node, e.offset)
307
+ sel.addRange(nr)
308
+ } catch { /* detached */ }
309
+ }
310
+ }
311
+ }
312
+ return
313
+ }
314
+
315
+ // Case 2: Selection CONTAINS descendant tags — unwrap (full or partial)
316
+ if (container) {
317
+ const descendants = container.querySelectorAll(tagName)
318
+ const toUnwrap = []
319
+ for (const el of descendants) {
320
+ if (range.intersectsNode(el) && matches(/** @type {HTMLElement} */ (el))) {
321
+ toUnwrap.push(el)
322
+ }
323
+ }
324
+ if (toUnwrap.length) {
325
+ // Save selection offsets relative to the CE so we can restore after DOM mutations
326
+ const ce = container.closest('[contenteditable="true"]') || container
327
+ const savedStart = getTextOffset(ce, range.startContainer, range.startOffset)
328
+ const savedEnd = getTextOffset(ce, range.endContainer, range.endOffset)
329
+
330
+ // Process in reverse DOM order so extractContents/insertBefore
331
+ // mutations don't invalidate range references for earlier elements.
332
+ for (let ti = toUnwrap.length - 1; ti >= 0; ti--) {
333
+ const el = /** @type {HTMLElement} */ (toUnwrap[ti])
334
+ // Create a clipped range: intersection of selection with this element
335
+ const clipped = document.createRange()
336
+ if (el.contains(range.startContainer)) {
337
+ clipped.setStart(range.startContainer, range.startOffset)
338
+ } else {
339
+ clipped.setStart(el, 0)
340
+ }
341
+ if (el.contains(range.endContainer)) {
342
+ clipped.setEnd(range.endContainer, range.endOffset)
343
+ } else {
344
+ clipped.setEnd(el, el.childNodes.length)
345
+ }
346
+
347
+ const clippedLen = clipped.toString().length
348
+ const fullLen = el.textContent?.length ?? 0
349
+
350
+ if (clippedLen >= fullLen) {
351
+ // Full unwrap
352
+ const p = el.parentNode
353
+ while (el.firstChild) p?.insertBefore(el.firstChild, el)
354
+ el.remove()
355
+ } else {
356
+ // Partial unwrap — split element, preserve inner wrappers
357
+ const parentNode = el.parentNode
358
+ const ref = el.nextSibling
359
+
360
+ // Save inner wrapper chain
361
+ const innerWrappers = []
362
+ /** @type {Node | null} */
363
+ let nn = clipped.startContainer
364
+ if (nn.nodeType === Node.TEXT_NODE) nn = nn.parentElement
365
+ while (nn && nn !== el) {
366
+ innerWrappers.push(/** @type {HTMLElement} */ (nn).cloneNode(false))
367
+ nn = /** @type {HTMLElement} */ (nn).parentElement
368
+ }
369
+
370
+ const afterRange = document.createRange()
371
+ afterRange.setStart(clipped.endContainer, clipped.endOffset)
372
+ afterRange.setEndAfter(el.lastChild || el)
373
+ const afterFrag = afterRange.extractContents()
374
+
375
+ // Re-wrap selected with inner wrappers (skip if extractContents already cloned them)
376
+ /** @type {Node} */
377
+ let wrappedSel = clipped.extractContents()
378
+ for (let i = 0; i < innerWrappers.length; i++) {
379
+ const iw = innerWrappers[i]
380
+ if (!iw) continue
381
+ wrappedSel = wrapIfNeeded(wrappedSel, /** @type {HTMLElement} */ (iw))
382
+ }
383
+ parentNode?.insertBefore(wrappedSel, ref)
384
+
385
+ if (afterFrag.textContent) {
386
+ /** @type {Node} */
387
+ let wrappedAfter = afterFrag
388
+ for (let i = 0; i < innerWrappers.length; i++) {
389
+ const iw = innerWrappers[i]
390
+ if (!iw) continue
391
+ wrappedAfter = wrapIfNeeded(wrappedAfter, /** @type {HTMLElement} */ (iw))
392
+ }
393
+ const afterTag = /** @type {HTMLElement} */ (el.cloneNode(false))
394
+ afterTag.appendChild(wrappedAfter)
395
+ parentNode?.insertBefore(afterTag, ref)
396
+ }
397
+
398
+ if (!el.textContent) el.remove()
399
+ }
400
+ }
401
+
402
+ // Clean up empty inline tags, merge adjacent, and normalize
403
+ removeEmptyInlineTags(container)
404
+ mergeAdjacentTags(container, tagName)
405
+
406
+ // Restore selection from saved offsets
407
+ const s = findNodeAtOffset(ce, savedStart)
408
+ const e = findNodeAtOffset(ce, savedEnd, 'end')
409
+ if (s && e) {
410
+ try {
411
+ sel.removeAllRanges()
412
+ const nr = document.createRange()
413
+ nr.setStart(s.node, s.offset)
414
+ nr.setEnd(e.node, e.offset)
415
+ sel.addRange(nr)
416
+ } catch { /* detached */ }
417
+ }
418
+
419
+ return
420
+ }
421
+ }
422
+
423
+ // Case 3: No existing tag found — wrap selection
424
+ const wrapper = document.createElement(tagName)
425
+ try {
426
+ range.surroundContents(wrapper)
427
+ } catch {
428
+ // surroundContents fails when range crosses element boundaries.
429
+ // Per-text-node wrapping as safe fallback.
430
+ // Use contenteditable for single-block, commonAncestorContainer for cross-block.
431
+ const singleCe = container?.closest('[contenteditable="true"]')
432
+ const walkRoot = singleCe || (range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE
433
+ ? range.commonAncestorContainer
434
+ : range.commonAncestorContainer.parentElement)
435
+ if (walkRoot) {
436
+ const walker = editableTextWalker(walkRoot)
437
+ /** @type {{ node: Text, startOffset: number, endOffset: number }[]} */
438
+ const targets = []
439
+ while (walker.nextNode()) {
440
+ const textNode = /** @type {Text} */ (walker.currentNode)
441
+ if (!range.intersectsNode(textNode)) continue
442
+ let so = 0, eo = textNode.length
443
+ if (textNode === range.startContainer) so = range.startOffset
444
+ if (textNode === range.endContainer) eo = range.endOffset
445
+ if (so >= eo) continue
446
+ targets.push({ node: textNode, startOffset: so, endOffset: eo })
447
+ }
448
+ for (let i = targets.length - 1; i >= 0; i--) {
449
+ const target = targets[i]
450
+ if (!target) continue
451
+ const { node, startOffset, endOffset } = target
452
+ let targetNode = node
453
+ if (endOffset < node.length) node.splitText(endOffset)
454
+ if (startOffset > 0) targetNode = /** @type {Text} */ (node.splitText(startOffset))
455
+ const w = document.createElement(tagName)
456
+ targetNode.parentNode?.insertBefore(w, targetNode)
457
+ w.appendChild(targetNode)
458
+ }
459
+ walkRoot.normalize()
460
+ mergeAdjacentTags(walkRoot, tagName)
461
+ }
462
+ return
463
+ }
464
+
465
+ // Merge with adjacent same-tag siblings before restoring selection.
466
+ // mergeAdjacentTags may absorb `wrapper` into a preceding sibling,
467
+ // so we save the CE-relative offsets and restore from them.
468
+ const mergeParent = wrapper.parentNode
469
+ if (mergeParent) {
470
+ const ce = wrapper.closest('[contenteditable="true"]')
471
+ if (ce) {
472
+ const wrapStart = getTextOffset(ce, wrapper.firstChild || wrapper, 0)
473
+ const wrapEnd = wrapStart + (wrapper.textContent?.length ?? 0)
474
+ mergeAdjacentTags(mergeParent, tagName)
475
+ // Restore selection from saved offsets
476
+ const s = findNodeAtOffset(ce, wrapStart)
477
+ const e = findNodeAtOffset(ce, wrapEnd, 'end')
478
+ if (s && e) {
479
+ try {
480
+ sel.removeAllRanges()
481
+ const nr = document.createRange()
482
+ nr.setStart(s.node, s.offset)
483
+ nr.setEnd(e.node, e.offset)
484
+ sel.addRange(nr)
485
+ } catch { /* detached */ }
486
+ }
487
+ } else {
488
+ // No CE — fallback: select wrapper contents (no merge)
489
+ try {
490
+ sel.removeAllRanges()
491
+ const nr = document.createRange()
492
+ nr.selectNodeContents(wrapper)
493
+ sel.addRange(nr)
494
+ } catch { /* detached */ }
495
+ }
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Find the closest .oe-editor root from a DOM node.
501
+ * Avoids global querySelector — works with multiple editor instances.
502
+ * @param {Node | null | undefined} [node]
503
+ * @returns {HTMLElement | null}
504
+ */
505
+ export function getEditorRoot(node) {
506
+ if (!node) return null
507
+ const el = node.nodeType === Node.ELEMENT_NODE ? /** @type {Element} */ (node) : node.parentElement
508
+ return /** @type {HTMLElement | null} */ (el?.closest('.oe-editor'))
509
+ }
510
+
511
+ /**
512
+ * Clear the stored cross-block range (call after DOM mutations that
513
+ * invalidate the range's node references).
514
+ * @param {import('../types').ICrossBlockSelection | null} [cbs]
515
+ * @param {Node} [contextNode] - any node inside the editor for scoping
516
+ * @returns {void}
517
+ */
518
+ export function clearCrossBlockRange(cbs, contextNode) {
519
+ if (cbs) {
520
+ const editor = getEditorRoot(contextNode ?? cbs.range?.startContainer)
521
+ cbs.deactivate(editor ?? undefined)
522
+ } else {
523
+ const editor = getEditorRoot(contextNode)
524
+ if (editor) editor.classList.remove('oe-editor--cross-selecting')
525
+ CrossBlockSelection.hideHighlight()
526
+ }
527
+ }
528
+
529
+ export { getTextOffset }
530
+
531
+ /**
532
+ * Find the text node and offset at a given character position.
533
+ * @param {Node} container
534
+ * @param {number} charOffset
535
+ * @param {'start' | 'end'} [bias='start'] - when the offset lands exactly at a
536
+ * node boundary, 'start' prefers the beginning of the next node (for range
537
+ * start points), 'end' prefers the end of the current node (for range end
538
+ * points). This avoids creating ranges that inadvertently cross into adjacent
539
+ * inline wrappers.
540
+ * @returns {{ node: Node, offset: number } | null}
541
+ */
542
+ export function findNodeAtOffset(container, charOffset, bias = 'start') {
543
+ const walker = editableTextWalker(container)
544
+ let remaining = charOffset
545
+ /** @type {Text | null} */
546
+ let lastNode = null
547
+ while (walker.nextNode()) {
548
+ lastNode = /** @type {Text} */ (walker.currentNode)
549
+ const len = lastNode.textContent?.length ?? 0
550
+ // Use < for intermediate nodes, <= only for the last node
551
+ if (remaining < len) return { node: lastNode, offset: remaining }
552
+ if (remaining === len) {
553
+ const saved = /** @type {Text} */ (walker.currentNode)
554
+ if (walker.nextNode()) {
555
+ const next = /** @type {Text} */ (walker.currentNode)
556
+ // 'end' bias: stay in current node to avoid crossing into the next wrapper
557
+ if (bias === 'end' && saved.parentElement !== next.parentElement) {
558
+ return { node: saved, offset: remaining }
559
+ }
560
+ // 'start' bias (default): prefer start of next node for clean range boundaries
561
+ return { node: next, offset: 0 }
562
+ }
563
+ // Last node — return end position
564
+ return { node: saved, offset: remaining }
565
+ }
566
+ remaining -= len
567
+ }
568
+ // Clamp to end of last text node
569
+ if (lastNode) return { node: lastNode, offset: lastNode.textContent?.length ?? 0 }
570
+ return null
571
+ }
572
+
573
+ /**
574
+ * Return every editable field owned by one block in DOM order.
575
+ * @param {HTMLElement} block
576
+ * @returns {HTMLElement[]}
577
+ */
578
+ function editableFields(block) {
579
+ const descendants = Array.from(
580
+ block.querySelectorAll('[contenteditable]'),
581
+ element => /** @type {HTMLElement} */ (element),
582
+ )
583
+ return block.matches('[contenteditable]') ? [block, ...descendants] : descendants
584
+ }
585
+
586
+ /**
587
+ * Resolve the editable field that owns a range boundary. A boundary may be an
588
+ * element node positioned between children, so inspect that child before
589
+ * falling back to the first field for legacy or synthetic ranges.
590
+ * @param {HTMLElement} block
591
+ * @param {Node} container
592
+ * @param {number} offset
593
+ * @returns {{ element: HTMLElement, index: number } | null}
594
+ */
595
+ function editableAtBoundary(block, container, offset) {
596
+ const fields = editableFields(block)
597
+ if (!fields.length) return null
598
+
599
+ const containerElement = container.nodeType === Node.ELEMENT_NODE
600
+ ? /** @type {HTMLElement} */ (container)
601
+ : container.parentElement
602
+ let candidate = containerElement?.closest('[contenteditable]') ?? null
603
+
604
+ if (!candidate && container.nodeType === Node.ELEMENT_NODE) {
605
+ const children = container.childNodes
606
+ const child = children[Math.min(offset, children.length - 1)] ?? null
607
+ const childElement = child?.nodeType === Node.ELEMENT_NODE
608
+ ? /** @type {HTMLElement} */ (child)
609
+ : child?.parentElement
610
+ candidate = childElement?.closest('[contenteditable]')
611
+ ?? childElement?.querySelector?.('[contenteditable]')
612
+ ?? null
613
+ }
614
+
615
+ const editable = candidate instanceof HTMLElement ? candidate : null
616
+ const index = editable && block.contains(editable) ? fields.indexOf(editable) : -1
617
+ return index >= 0 ? { element: fields[index], index } : { element: fields[0], index: 0 }
618
+ }
619
+
620
+ /**
621
+ * Save cross-block range as block IDs + character offsets (survives DOM mutations).
622
+ * @param {Range} range
623
+ * @returns {{ editorRoot: HTMLElement, startBlockId: string, endBlockId: string, startFieldIndex: number, endFieldIndex: number, startOffset: number, endOffset: number } | null}
624
+ */
625
+ export function saveCrossBlockOffsets(range) {
626
+ const startBlock = closestBlock(range.startContainer)
627
+ const endBlock = closestBlock(range.endContainer)
628
+ if (!startBlock || !endBlock || startBlock === endBlock) return null
629
+
630
+ const editorRoot = getEditorRoot(startBlock)
631
+ if (!editorRoot || getEditorRoot(endBlock) !== editorRoot) return null
632
+
633
+ const startField = editableAtBoundary(startBlock, range.startContainer, range.startOffset)
634
+ const endField = editableAtBoundary(endBlock, range.endContainer, range.endOffset)
635
+ if (!startField || !endField) return null
636
+
637
+ return {
638
+ editorRoot,
639
+ startBlockId: startBlock.dataset.blockId ?? '',
640
+ endBlockId: endBlock.dataset.blockId ?? '',
641
+ startFieldIndex: startField.index,
642
+ endFieldIndex: endField.index,
643
+ startOffset: getTextOffset(startField.element, range.startContainer, range.startOffset),
644
+ endOffset: getTextOffset(endField.element, range.endContainer, range.endOffset),
645
+ }
646
+ }
647
+
648
+ /**
649
+ * Restore cross-block range from saved offsets. Updates CrossBlockSelection and CSS Highlight.
650
+ * @param {import('../types').ICrossBlockSelection | null} cbs
651
+ * @param {{ editorRoot: HTMLElement, startBlockId: string, endBlockId: string, startFieldIndex?: number, endFieldIndex?: number, startOffset: number, endOffset: number }} offsets
652
+ * @returns {Range | null}
653
+ */
654
+ export function restoreCrossBlockRange(cbs, offsets) {
655
+ const { editorRoot } = offsets
656
+ if (!editorRoot.isConnected) return null
657
+
658
+ // Block IDs are unique only within one editor document. Comparing dataset
659
+ // values also avoids interpolating document-owned IDs into a CSS selector.
660
+ const blocks = editorRoot.querySelectorAll('[data-block-id]')
661
+ const startBlock = Array.from(blocks).find(block => (
662
+ /** @type {HTMLElement} */ (block).dataset.blockId === offsets.startBlockId
663
+ ))
664
+ const endBlock = Array.from(blocks).find(block => (
665
+ /** @type {HTMLElement} */ (block).dataset.blockId === offsets.endBlockId
666
+ ))
667
+ if (!startBlock || !endBlock) return null
668
+
669
+ const startCe = editableFields(/** @type {HTMLElement} */ (startBlock))[offsets.startFieldIndex ?? 0]
670
+ const endCe = editableFields(/** @type {HTMLElement} */ (endBlock))[offsets.endFieldIndex ?? 0]
671
+ if (!startCe || !endCe) return null
672
+
673
+ const start = findNodeAtOffset(startCe, offsets.startOffset)
674
+ const end = findNodeAtOffset(endCe, offsets.endOffset, 'end')
675
+ if (!start || !end) return null
676
+
677
+ const range = document.createRange()
678
+ try {
679
+ range.setStart(start.node, start.offset)
680
+ range.setEnd(end.node, end.offset)
681
+ } catch { return null }
682
+
683
+ // Update stored range and visual highlight
684
+ if (cbs) {
685
+ cbs.activate(range, editorRoot)
686
+ } else {
687
+ editorRoot.classList.add('oe-editor--cross-selecting')
688
+ CrossBlockSelection.showHighlight(range)
689
+ }
690
+
691
+ // Place native caret at end of cross-block range
692
+ try {
693
+ const sel = window.getSelection()
694
+ const caret = document.createRange()
695
+ caret.setStart(range.endContainer, range.endOffset)
696
+ caret.collapse(true)
697
+ sel?.removeAllRanges()
698
+ sel?.addRange(caret)
699
+ } catch { /* detached */ }
700
+
701
+ return range
702
+ }
703
+
704
+ /**
705
+ * Check if a selection spans multiple .oe-block elements.
706
+ * @param {import('../types').ICrossBlockSelection | null} [cbs]
707
+ * @returns {HTMLElement[] | null} Array of plugin-owned block roots, or null if single-block
708
+ */
709
+ export function getSelectedBlockElements(cbs) {
710
+ const sel = window.getSelection()
711
+ if (!sel || sel.isCollapsed || !sel.rangeCount) return null
712
+
713
+ // Native selection clips to focused contenteditable.
714
+ // Use the cross-block range if available.
715
+ const nativeRange = sel.getRangeAt(0)
716
+ const range = cbs?.range || nativeRange
717
+ const startBlock = closestBlock(range.startContainer)
718
+ const endBlock = closestBlock(range.endContainer)
719
+
720
+ if (!startBlock || !endBlock || startBlock === endBlock) return null
721
+
722
+ // Collect all plugin-owned block roots between start and end.
723
+ const blocks = []
724
+ const container = startBlock.parentElement
725
+ if (!container) return null
726
+
727
+ let inside = false
728
+ for (const child of container.children) {
729
+ if (child === startBlock) inside = true
730
+ if (inside && child.classList.contains(BLOCK_CLASS)) {
731
+ const content = child.firstElementChild
732
+ if (content instanceof HTMLElement) blocks.push(content)
733
+ }
734
+ if (child === endBlock) break
735
+ }
736
+
737
+ return blocks.length > 1 ? blocks : null
738
+ }
739
+
740
+ /**
741
+ * Dispatch a synthetic input event on the nearest contenteditable to trigger
742
+ * undo/redo snapshot via EditorFacade.onInput → editor:changed.
743
+ * @param {Node} [contextNode] - any node inside the editor for scoping
744
+ * @returns {void}
745
+ */
746
+ export function notifyEditorChanged(contextNode) {
747
+ const element = contextNode?.nodeType === Node.ELEMENT_NODE
748
+ ? /** @type {Element} */ (contextNode)
749
+ : contextNode?.parentElement
750
+ const ce = element?.closest('[contenteditable="true"]')
751
+ if (ce) ce.dispatchEvent(new InputEvent('input', { bubbles: true }))
752
+ }
753
+
754
+ /**
755
+ * Create a simple tag-based inline tool.
756
+ * @param {string} type
757
+ * @param {string} title
758
+ * @param {string} icon
759
+ * @param {string} tag
760
+ * @param {string} [shortcut]
761
+ * @param {import('../types').ICrossBlockSelection} [cbs]
762
+ * @returns {import('../types').InlineTool}
763
+ */
764
+ export function createSimpleInlineTool(type, title, icon, tag, shortcut, cbs) {
765
+ return {
766
+ type,
767
+ title,
768
+ icon,
769
+ tag,
770
+ shortcut,
771
+
772
+ isActive(selection) {
773
+ const range = selection?.range
774
+ if (!range) return false
775
+
776
+ const ancestor = range.commonAncestorContainer
777
+ const container = ancestor.nodeType === Node.ELEMENT_NODE
778
+ ? /** @type {HTMLElement} */ (ancestor)
779
+ : ancestor.parentElement
780
+
781
+ // Entire selection is inside the tag
782
+ if (closestInEditable(container, tag)) return true
783
+
784
+ // For collapsed caret — closest check above is sufficient
785
+ if (range.collapsed) return false
786
+
787
+ // For non-collapsed: check if ANY text node in range is inside the tag
788
+ if (container) {
789
+ const walker = editableTextWalker(container)
790
+ while (walker.nextNode()) {
791
+ const textNode = walker.currentNode
792
+ if (!range.intersectsNode(textNode)) continue
793
+ // Calculate actual selected length within this text node
794
+ let start = 0
795
+ let end = textNode.textContent?.length ?? 0
796
+ if (textNode === range.startContainer) start = range.startOffset
797
+ if (textNode === range.endContainer) end = range.endOffset
798
+ if (start >= end) continue // zero-length intersection (boundary node)
799
+ const selectedText = (textNode.textContent || '').slice(start, end)
800
+ if (!selectedText.trim()) continue
801
+ if (closestInEditable(textNode.parentElement, tag)) return true
802
+ }
803
+ }
804
+ return false
805
+ },
806
+
807
+ toggle(selection) {
808
+ const range = selection.range
809
+ if (!range) return
810
+ const saved = saveSelectionOffsets(range)
811
+
812
+ toggleTag(tag, range)
813
+
814
+ restoreSelectionOffsets(cbs ?? null, saved)
815
+ },
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Find the contentEditable block element from the current selection.
821
+ * @returns {HTMLElement | null}
822
+ */
823
+ export function getBlockElement() {
824
+ const sel = window.getSelection()
825
+ if (!sel || !sel.anchorNode) return null
826
+ let node = sel.anchorNode.nodeType === Node.ELEMENT_NODE
827
+ ? /** @type {HTMLElement} */ (sel.anchorNode)
828
+ : sel.anchorNode.parentElement
829
+ while (node) {
830
+ if (node.getAttribute && node.getAttribute('contenteditable') === 'true') return node
831
+ node = node.parentElement
832
+ }
833
+ return null
834
+ }
835
+
836
+ /**
837
+ * Find the plugin-owned root of the block containing the current selection.
838
+ * Unlike {@link getBlockElement}, this returns the block content root rather
839
+ * than one nested editable field, so block-level settings survive plugins
840
+ * with several text fields.
841
+ * @returns {HTMLElement | null}
842
+ */
843
+ export function getBlockContentElement() {
844
+ const selection = window.getSelection()
845
+ const block = closestBlock(selection?.anchorNode)
846
+ return block?.firstElementChild instanceof HTMLElement ? block.firstElementChild : null
847
+ }
848
+
849
+ /**
850
+ * Find the contentEditable ancestor of a range's start.
851
+ * Returns null for cross-block ranges (start and end in different contenteditables).
852
+ * @param {Range} range
853
+ * @returns {HTMLElement | null}
854
+ */
855
+ export function getContentEditable(range) {
856
+ const startNode = range.startContainer
857
+ const startEl = startNode.nodeType === Node.ELEMENT_NODE
858
+ ? /** @type {HTMLElement} */ (startNode)
859
+ : startNode.parentElement
860
+ const startCe = startEl?.closest('[contenteditable="true"]') ?? null
861
+
862
+ const endNode = range.endContainer
863
+ const endEl = endNode.nodeType === Node.ELEMENT_NODE
864
+ ? /** @type {HTMLElement} */ (endNode)
865
+ : endNode.parentElement
866
+ const endCe = endEl?.closest('[contenteditable="true"]') ?? null
867
+
868
+ if (startCe !== endCe) return null
869
+
870
+ return /** @type {HTMLElement | null} */ (startCe)
871
+ }
872
+
873
+ /**
874
+ * Resolve the DOM root to walk for range operations.
875
+ * Uses contenteditable for single-block ranges, commonAncestorContainer for cross-block.
876
+ * @param {Range} range
877
+ * @returns {HTMLElement | null}
878
+ */
879
+ export function getWalkRoot(range) {
880
+ return getContentEditable(range)
881
+ || (range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE
882
+ ? /** @type {HTMLElement} */ (range.commonAncestorContainer)
883
+ : range.commonAncestorContainer.parentElement)
884
+ }
885
+
886
+ /**
887
+ * Collect text nodes within a range, clipped to range boundaries.
888
+ * @param {HTMLElement} walkRoot
889
+ * @param {Range} range
890
+ * @returns {{ node: Text, startOffset: number, endOffset: number }[]}
891
+ */
892
+ export function collectTextTargets(walkRoot, range) {
893
+ const walker = editableTextWalker(walkRoot)
894
+ /** @type {{ node: Text, startOffset: number, endOffset: number }[]} */
895
+ const targets = []
896
+ while (walker.nextNode()) {
897
+ const textNode = /** @type {Text} */ (walker.currentNode)
898
+ if (!range.intersectsNode(textNode)) continue
899
+ let startOffset = 0
900
+ let endOffset = textNode.length
901
+ if (textNode === range.startContainer) startOffset = range.startOffset
902
+ if (textNode === range.endContainer) endOffset = range.endOffset
903
+ if (startOffset >= endOffset) continue
904
+ targets.push({ node: textNode, startOffset, endOffset })
905
+ }
906
+ return targets
907
+ }
908
+
909
+ /**
910
+ * Normalize contenteditables after DOM manipulation.
911
+ * @param {HTMLElement | null} singleCe - single contenteditable (null for cross-block)
912
+ * @param {HTMLElement} walkRoot
913
+ * @returns {void}
914
+ */
915
+ export function normalizeAfterEdit(singleCe, walkRoot) {
916
+ if (singleCe) {
917
+ singleCe.normalize()
918
+ } else {
919
+ const blocks = walkRoot.querySelectorAll('[contenteditable]')
920
+ for (const b of blocks) b.normalize()
921
+ }
922
+ }
923
+
924
+ /**
925
+ * @typedef {Object} SavedOffsets
926
+ * @property {{ editorRoot: HTMLElement, startBlockId: string, endBlockId: string, startFieldIndex: number, endFieldIndex: number, startOffset: number, endOffset: number } | null} crossOffsets - Serialized selection spanning multiple editable block fields.
927
+ * @property {{ ce: Node, start: number, end: number } | null} singleOffsets - Text offsets and owning editable node for a selection contained in one field.
928
+ */
929
+
930
+ /**
931
+ * Save selection offsets (cross-block or single-block) before DOM mutation.
932
+ * @param {Range} range
933
+ * @returns {SavedOffsets}
934
+ */
935
+ export function saveSelectionOffsets(range) {
936
+ const crossOffsets = saveCrossBlockOffsets(range)
937
+ /** @type {{ ce: Node, start: number, end: number } | null} */
938
+ let singleOffsets = null
939
+ if (!crossOffsets) {
940
+ const startEl = range.startContainer.nodeType === Node.ELEMENT_NODE
941
+ ? /** @type {HTMLElement} */ (range.startContainer)
942
+ : range.startContainer.parentElement
943
+ const ce = startEl?.closest('[contenteditable="true"]')
944
+ if (ce) {
945
+ singleOffsets = {
946
+ ce,
947
+ start: getTextOffset(ce, range.startContainer, range.startOffset),
948
+ end: getTextOffset(ce, range.endContainer, range.endOffset),
949
+ }
950
+ }
951
+ }
952
+ return { crossOffsets, singleOffsets }
953
+ }
954
+
955
+ /**
956
+ * Restore selection from saved offsets after DOM mutation.
957
+ * @param {import('../types').ICrossBlockSelection | null} cbs
958
+ * @param {SavedOffsets} saved
959
+ * @returns {void}
960
+ */
961
+ export function restoreSelectionOffsets(cbs, saved) {
962
+ if (saved.crossOffsets) {
963
+ restoreCrossBlockRange(cbs, saved.crossOffsets)
964
+ } else if (saved.singleOffsets) {
965
+ const { ce, start, end } = saved.singleOffsets
966
+ const s = findNodeAtOffset(ce, start)
967
+ const e = findNodeAtOffset(ce, end, 'end')
968
+ if (s && e) {
969
+ try {
970
+ const sel = window.getSelection()
971
+ const r = document.createRange()
972
+ r.setStart(s.node, s.offset)
973
+ r.setEnd(e.node, e.offset)
974
+ sel?.removeAllRanges()
975
+ sel?.addRange(r)
976
+ } catch { /* detached */ }
977
+ }
978
+ }
979
+ }
980
+
981
+ /**
982
+ * Create a standard back button for inline toolbar drill-down panels.
983
+ * @param {{ close(): void }} ctx
984
+ * @returns {HTMLElement}
985
+ */
986
+ export function createBackButton(ctx) {
987
+ const backBtn = el('button', 'oe-inline-tool oe-inline-tool--back', { type: 'button' })
988
+ backBtn.innerHTML = ICON_BACK
989
+ backBtn.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation() })
990
+ backBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); ctx.close() })
991
+ return backBtn
992
+ }