@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,921 @@
1
+ import { resolvePath } from '../../shared/resolvePath.js'
2
+ import { triggerFileInput } from '../shared/fileInput.js'
3
+ import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
4
+ import { getFileIcon, getExtension, formatSize, EXT_COLORS } from '../../shared/fileUtils.js'
5
+ import { validateAttachesData } from '../../shared/blockDataValidators.js'
6
+ import { sanitizeUrl } from '../../shared/sanitize/sanitizeUrl.js'
7
+ import { normalizeTextValue } from '../../shared/textFormat.js'
8
+
9
+ const editorStyles = resolvePath('./attaches.css', import.meta.url)
10
+
11
+ const ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1-2-2v-14a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/><path d="M12 11v6"/><path d="M9.5 13.5l2.5-2.5l2.5 2.5"/></svg>'
12
+ const ICON_SELECT = '<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1-2-2v-14a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/><path d="M12 11v6"/><path d="M9.5 13.5l2.5-2.5l2.5 2.5"/></svg>'
13
+ const ICON_UPLOAD = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/><path d="M7 9l5-5l5 5"/><path d="M12 4v12"/></svg>'
14
+ const ICON_TRASH = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7h16"/><path stroke-linecap="round" stroke-linejoin="round" d="M10 11v6"/><path stroke-linecap="round" stroke-linejoin="round" d="M14 11v6"/><path stroke-linecap="round" stroke-linejoin="round" d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12"/><path stroke-linecap="round" stroke-linejoin="round" d="M9 7V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"/></svg>'
15
+ const ICON_CHEVRON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>'
16
+ const ICON_SETTINGS = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>'
17
+ const ICON_FILE_DEFAULT = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2"/></svg>'
18
+ const ICON_FILE_SM = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2"/></svg>'
19
+
20
+ /** @type {string[]} */
21
+ const VARIANTS = ['a', 'b', 'f', 'g']
22
+
23
+ /** @type {Record<string, { icon: string, label: string }>} */
24
+ const VARIANT_META = {
25
+ a: { icon: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1-2-2v-14a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/></svg>', label: 'variantA' },
26
+ b: { icon: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/></svg>', label: 'variantB' },
27
+ f: { icon: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12h18"/><path d="M3 6h18"/><path d="M3 18h18"/></svg>', label: 'variantF' },
28
+ g: { icon: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="3"/></svg>', label: 'variantG' },
29
+ }
30
+
31
+ /**
32
+ * @typedef {{ url: string, name: string, size: number, extension: string }} FileEntry
33
+ * @typedef {Object} AttachesAction
34
+ * @property {string} label Plain-text label shown on the source button.
35
+ * @property {string} [icon] Trusted application-owned icon markup. Never pass user-authored HTML.
36
+ * @property {(context: { signal: AbortSignal }) => Promise<Array<{ url: string, name: string, size?: number, extension?: string }> | null>} handler Opens an application-owned source and returns selected files, or `null` when selection is cancelled.
37
+ * @typedef {Object} AttachesConfig
38
+ * @property {(file: File, context: { signal: AbortSignal }) => Promise<{ url: string, size?: number }>} [uploadFile] Uploads a browser file. Without this callback the plugin creates a temporary object URL valid until the editor is destroyed.
39
+ * @property {AttachesAction[]} [actions] Additional file sources such as a media library.
40
+ * @property {boolean} [injectStyles=true] Whether the editor should load the built-in plugin stylesheet.
41
+ * @property {string} [css] Additional or replacement stylesheet URL, depending on `injectStyles`.
42
+ * @typedef {{
43
+ * data: { files: FileEntry[], variant: string },
44
+ * viewController: AbortController | null,
45
+ * taskControllers: Set<AbortController>,
46
+ * expanded: boolean,
47
+ * context: import('../../core/types').BlockMutationContext,
48
+ * }} AttachesState
49
+ */
50
+
51
+ /** @type {WeakMap<HTMLElement, AttachesState>} */
52
+ const stateMap = new WeakMap()
53
+ let groupSequence = 0
54
+
55
+
56
+ /**
57
+ * File attachment block supporting direct uploads and consumer-defined file
58
+ * source actions such as a media library.
59
+ * @extends {BlockPluginAbstract<AttachesConfig>}
60
+ */
61
+ export class Attaches extends BlockPluginAbstract {
62
+ static isTextBlock = false
63
+ static styles = [editorStyles]
64
+ type = 'attaches'
65
+ icon = ICON
66
+ inlineTools = false
67
+
68
+
69
+ #objectUrls = new Set()
70
+ /**
71
+ * Create an Attaches instance with the supplied consumer configuration.
72
+ * @param {AttachesConfig} [config]
73
+ */
74
+ constructor(config) {
75
+ super(config)
76
+ }
77
+
78
+ /** @returns {AttachesConfig} Consumer configuration for this plugin instance. */
79
+ get #config() {
80
+ return /** @type {AttachesConfig} */ (this._config)
81
+ }
82
+
83
+ /**
84
+ * Return the localized toolbox label for this block.
85
+ * @returns {string} Localized toolbox title.
86
+ */
87
+ get title() { return this._t('title', 'File') }
88
+
89
+ /**
90
+ * Create the editable DOM owned by this block instance.
91
+ * @param {Record<string, unknown>} data
92
+ * @param {import('../../core/types').BlockMutationContext} context
93
+ * @returns {HTMLElement}
94
+ */
95
+ render(data, context) {
96
+ const normalizeSize = (value) => {
97
+ const size = Number(value)
98
+ return Number.isFinite(size) && size >= 0 ? size : 0
99
+ }
100
+ let files
101
+ if (data?.file && !data?.files) {
102
+ const f = /** @type {any} */ (data.file)
103
+ const url = sanitizeUrl(normalizeTextValue(f.url), { policy: 'download', fallback: '' })
104
+ const name = normalizeTextValue(f.name)
105
+ files = url ? [{
106
+ url,
107
+ name,
108
+ size: normalizeSize(f.size),
109
+ extension: normalizeTextValue(f.extension) || getExtension(name),
110
+ }] : []
111
+ } else {
112
+ const inputFiles = Array.isArray(data?.files) ? data.files : []
113
+ files = /** @type {any[]} */ (inputFiles).map(f => ({
114
+ url: sanitizeUrl(normalizeTextValue(f?.url), { policy: 'download', fallback: '' }),
115
+ name: normalizeTextValue(f?.name),
116
+ size: normalizeSize(f?.size),
117
+ extension: normalizeTextValue(f?.extension) || getExtension(normalizeTextValue(f?.name)),
118
+ })).filter(f => f.url)
119
+ }
120
+ const requestedVariant = normalizeTextValue(data?.variant) || 'f'
121
+ const variant = VARIANTS.includes(requestedVariant) ? requestedVariant : 'f'
122
+ const wrapper = document.createElement('div')
123
+ wrapper.classList.add('oe-attaches')
124
+ wrapper.contentEditable = 'false'
125
+ wrapper.tabIndex = -1
126
+ stateMap.set(wrapper, {
127
+ data: { files, variant },
128
+ viewController: null,
129
+ taskControllers: new Set(),
130
+ expanded: false,
131
+ context,
132
+ })
133
+ if (files.length > 0) this.#renderFilled(wrapper)
134
+ else this.#renderSelect(wrapper)
135
+ return wrapper
136
+ }
137
+
138
+ /**
139
+ * Serialize the current block DOM into document data.
140
+ * @param {HTMLElement} el
141
+ * @returns {{ files: FileEntry[], variant: string }}
142
+ */
143
+ save(el) {
144
+ const s = stateMap.get(el)
145
+ if (!s) return { files: [], variant: 'f' }
146
+ this.#syncNames(el)
147
+ return { files: s.data.files.map(f => ({ ...f })), variant: s.data.variant }
148
+ }
149
+
150
+ /**
151
+ * Check whether serialized data satisfies this block's schema.
152
+ * @param {Record<string, unknown>} data
153
+ * @returns {boolean}
154
+ */
155
+ validate(data) {
156
+ return validateAttachesData(data)
157
+ }
158
+
159
+ /**
160
+ * Check whether the block has no meaningful user content.
161
+ * @param {HTMLElement} el
162
+ * @returns {boolean}
163
+ */
164
+ isEmpty(el) {
165
+ const s = stateMap.get(el)
166
+ return !s || s.data.files.length === 0
167
+ }
168
+
169
+ /**
170
+ * Extract neutral text that can initialize another block type.
171
+ * @param {HTMLElement} el
172
+ * @returns {{ text: string }}
173
+ */
174
+ exportData(el) {
175
+ const s = stateMap.get(el)
176
+ return { text: s ? s.data.files.map(f => f.name).join(', ') : '' }
177
+ }
178
+
179
+ /**
180
+ * Abort work and listeners owned by one rendered block.
181
+ * @param {HTMLElement} el
182
+ * @returns {void}
183
+ */
184
+ destroy(el) {
185
+ const s = stateMap.get(el)
186
+ if (s) {
187
+ s.viewController?.abort()
188
+ for (const controller of s.taskControllers) controller.abort()
189
+ s.taskControllers.clear()
190
+ stateMap.delete(el)
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Release temporary local URLs after all block snapshots and undo history
196
+ * owned by this editor have become unreachable.
197
+ * @returns {void}
198
+ */
199
+ dispose() {
200
+ for (const url of this.#objectUrls) URL.revokeObjectURL(url)
201
+ this.#objectUrls.clear()
202
+ }
203
+
204
+ // ── sync editable names ────────────────────────────────────────────────────
205
+
206
+ /** @param {HTMLElement} wrapper @returns {void} */
207
+ #syncNames(wrapper) {
208
+ const s = stateMap.get(wrapper)
209
+ if (!s) return
210
+ wrapper.querySelectorAll('.oe-attaches__name:not(.oe-attaches__name--static)').forEach((el, i) => {
211
+ if (s.data.files[i]) {
212
+ const t = String(el.textContent ?? '').trim()
213
+ if (t) s.data.files[i].name = t
214
+ }
215
+ })
216
+ }
217
+
218
+ // ── dropzone ───────────────────────────────────────────────────────────────
219
+
220
+ /** @param {HTMLElement} wrapper @returns {void} */
221
+ #renderSelect(wrapper) {
222
+ const s = stateMap.get(wrapper)
223
+ if (!s) return
224
+ wrapper.innerHTML = ''
225
+ wrapper.classList.remove('oe-attaches--filled')
226
+ s.viewController?.abort()
227
+ s.viewController = new AbortController()
228
+ const { signal } = s.viewController
229
+
230
+ const select = document.createElement('div')
231
+ select.className = 'oe-attaches__select'
232
+
233
+ if (s.context.readOnly) {
234
+ select.textContent = this._t('emptyReadonly', 'No files')
235
+ wrapper.appendChild(select)
236
+ return
237
+ }
238
+
239
+ const icon = document.createElement('div')
240
+ icon.className = 'oe-attaches__select-icon'
241
+ icon.innerHTML = ICON_SELECT
242
+
243
+ const text = document.createElement('div')
244
+ text.className = 'oe-attaches__select-text'
245
+ const link = document.createElement('button')
246
+ link.type = 'button'
247
+ link.className = 'oe-attaches__select-link'
248
+ link.textContent = this._t('dropzoneUpload', 'Upload')
249
+ link.addEventListener('click', (e) => { e.stopPropagation(); this.#triggerFileInput(wrapper) }, { signal })
250
+ text.append(link, document.createTextNode(' ' + this._t('dropzoneText', 'files from your device or drag and drop them here')))
251
+ select.append(icon, text)
252
+
253
+ const configuredActions = this.#config.actions ?? []
254
+ if (configuredActions.length) {
255
+ const sources = document.createElement('div')
256
+ sources.className = 'oe-attaches__select-actions'
257
+ for (const action of configuredActions) {
258
+ const button = document.createElement('button')
259
+ button.type = 'button'
260
+ button.className = 'oe-attaches__select-action'
261
+ if (action.icon) button.insertAdjacentHTML('afterbegin', action.icon)
262
+ button.append(document.createTextNode(action.label))
263
+ button.addEventListener('click', event => {
264
+ event.stopPropagation()
265
+ void this.#runCustomAction(wrapper, action.handler)
266
+ }, { signal })
267
+ sources.appendChild(button)
268
+ }
269
+ select.appendChild(sources)
270
+ }
271
+
272
+ wrapper.addEventListener('dragover', (e) => { e.preventDefault(); select.classList.add('oe-attaches__select--dragover') }, { signal })
273
+ wrapper.addEventListener('dragleave', () => select.classList.remove('oe-attaches__select--dragover'), { signal })
274
+ wrapper.addEventListener('drop', (e) => {
275
+ e.preventDefault()
276
+ select.classList.remove('oe-attaches__select--dragover')
277
+ if (e.dataTransfer?.files?.length) void this.#handleFiles(wrapper, e.dataTransfer.files)
278
+ }, { signal })
279
+
280
+ wrapper.appendChild(select)
281
+ }
282
+
283
+ // ── filled state (dispatches by variant) ──────────────────────────────────
284
+
285
+ /** @param {HTMLElement} wrapper @returns {void} */
286
+ #renderFilled(wrapper) {
287
+ const s = stateMap.get(wrapper)
288
+ if (!s) return
289
+ this.#syncNames(wrapper)
290
+ wrapper.innerHTML = ''
291
+ wrapper.classList.add('oe-attaches--filled')
292
+ s.viewController?.abort()
293
+ s.viewController = new AbortController()
294
+ const { signal } = s.viewController
295
+ const files = s.data.files
296
+ const v = s.data.variant || 'a'
297
+
298
+ switch (v) {
299
+ case 'b':
300
+ this.#renderPills(wrapper, files, signal)
301
+ break
302
+ case 'f':
303
+ this.#renderNotion(wrapper, files, signal)
304
+ break
305
+ case 'g':
306
+ this.#renderMaterial(wrapper, files, signal)
307
+ break
308
+ default: // 'a'
309
+ if (files.length === 1) wrapper.appendChild(this.#buildCardA(wrapper, files[0], 0, signal))
310
+ else wrapper.appendChild(this.#buildGroupA(wrapper, signal))
311
+ break
312
+ }
313
+
314
+ if (!s.context.readOnly) this.#renderActions(wrapper, signal)
315
+ }
316
+
317
+ // ── Variant A: Minimal ────────────────────────────────────────────────────
318
+
319
+ /**
320
+ * @param {HTMLElement} w Block wrapper that owns the file state.
321
+ * @param {FileEntry} file File represented by this card.
322
+ * @param {number} i File index in the block data.
323
+ * @param {AbortSignal} sig Current view lifecycle signal.
324
+ * @returns {HTMLDivElement}
325
+ */
326
+ #buildCardA(w, file, i, sig) {
327
+ const card = document.createElement('div')
328
+ card.className = 'oe-attaches__card'
329
+ card.appendChild(this.#buildIconA(file))
330
+ const info = document.createElement('div')
331
+ info.className = 'oe-attaches__info'
332
+ info.appendChild(this.#buildNameEl(w, i, file, sig))
333
+ if (file.size) { const m = document.createElement('div'); m.className = 'oe-attaches__meta'; m.textContent = formatSize(file.size); info.appendChild(m) }
334
+ card.append(info, this.#buildRemoveBtn(w, i, sig))
335
+ return card
336
+ }
337
+
338
+ /**
339
+ * @param {HTMLElement} w Block wrapper that owns the group.
340
+ * @param {AbortSignal} sig Current view lifecycle signal.
341
+ * @returns {HTMLDivElement}
342
+ */
343
+ #buildGroupA(w, sig) {
344
+ const s = stateMap.get(w)
345
+ if (!s) return document.createElement('div')
346
+ const files = s.data.files
347
+ const group = document.createElement('div')
348
+ group.className = 'oe-attaches__group'
349
+
350
+ const header = document.createElement('div')
351
+ header.className = 'oe-attaches__group-header'
352
+ const iconWrap = document.createElement('div')
353
+ iconWrap.className = 'oe-attaches__icon'
354
+ iconWrap.innerHTML = ICON_FILE_DEFAULT
355
+ const headerInfo = document.createElement('div')
356
+ headerInfo.className = 'oe-attaches__info'
357
+ const countText = document.createElement('div')
358
+ countText.className = 'oe-attaches__name oe-attaches__name--static'
359
+ countText.textContent = `${files.length} ${this._p('filesCount', files.length, 'files')}`
360
+ headerInfo.appendChild(countText)
361
+ const totalSize = files.reduce((sum, f) => sum + (f.size || 0), 0)
362
+ if (totalSize > 0) { const m = document.createElement('span'); m.className = 'oe-attaches__meta'; m.textContent = formatSize(totalSize); headerInfo.appendChild(m) }
363
+ const chevron = document.createElement('button')
364
+ chevron.type = 'button'
365
+ chevron.className = 'oe-attaches__chevron'
366
+ chevron.innerHTML = ICON_CHEVRON
367
+ chevron.setAttribute('aria-label', this._t('toggleGroup', 'Show or hide files'))
368
+ chevron.setAttribute('aria-expanded', String(s.expanded))
369
+ header.append(iconWrap, headerInfo, chevron)
370
+
371
+ const body = document.createElement('div')
372
+ body.className = 'oe-attaches__group-body'
373
+ body.id = `rector-editor-attaches-group-${++groupSequence}`
374
+ chevron.setAttribute('aria-controls', body.id)
375
+ if (s.expanded) { body.classList.add('oe-attaches__group-body--open'); chevron.classList.add('oe-attaches__chevron--open') }
376
+ for (let i = 0; i < files.length; i++) {
377
+ const row = document.createElement('div')
378
+ row.className = 'oe-attaches__row'
379
+ row.append(this.#buildNameEl(w, i, files[i], sig))
380
+ if (files[i].size) { const sz = document.createElement('span'); sz.className = 'oe-attaches__row-size'; sz.textContent = formatSize(files[i].size); row.appendChild(sz) }
381
+ row.appendChild(this.#buildRemoveBtn(w, i, sig))
382
+ body.appendChild(row)
383
+ }
384
+ const toggle = () => {
385
+ const st = stateMap.get(w)
386
+ if (!st) return
387
+ st.expanded = !st.expanded
388
+ body.classList.toggle('oe-attaches__group-body--open', st.expanded)
389
+ chevron.classList.toggle('oe-attaches__chevron--open', st.expanded)
390
+ chevron.setAttribute('aria-expanded', String(st.expanded))
391
+ }
392
+ header.addEventListener('click', (event) => {
393
+ const target = /** @type {HTMLElement | null} */ (event.target instanceof HTMLElement ? event.target : null)
394
+ if (target?.closest('button, [contenteditable="true"]')) return
395
+ toggle()
396
+ }, { signal: sig })
397
+ chevron.addEventListener('click', (event) => {
398
+ event.stopPropagation()
399
+ toggle()
400
+ }, { signal: sig })
401
+ group.append(header, body)
402
+ return group
403
+ }
404
+
405
+ /** Variant A icon: always default file SVG + extension badge. @param {FileEntry} file @returns {HTMLDivElement} */
406
+ #buildIconA(file) {
407
+ const wrap = document.createElement('div')
408
+ wrap.className = 'oe-attaches__icon'
409
+ wrap.innerHTML = ICON_FILE_DEFAULT
410
+ if (file.extension) {
411
+ const badge = document.createElement('span')
412
+ badge.className = 'oe-attaches__ext'
413
+ badge.textContent = file.extension.toUpperCase()
414
+ const color = EXT_COLORS[file.extension.toLowerCase()]
415
+ if (color) badge.style.backgroundColor = color
416
+ wrap.appendChild(badge)
417
+ }
418
+ return wrap
419
+ }
420
+
421
+ // ── Variant B: Pill ────────────────────────────────────────────────────────
422
+
423
+ /**
424
+ * @param {HTMLElement} w Block wrapper that receives the pill list.
425
+ * @param {FileEntry[]} files Files to render.
426
+ * @param {AbortSignal} sig Current view lifecycle signal.
427
+ * @returns {void}
428
+ */
429
+ #renderPills(w, files, sig) {
430
+ const container = document.createElement('div')
431
+ container.className = 'oe-attaches__pills'
432
+ for (let i = 0; i < files.length; i++) {
433
+ const pill = document.createElement('div')
434
+ pill.className = 'oe-attaches__pill'
435
+ const ic = document.createElement('div')
436
+ ic.className = 'oe-attaches__pill-icon'
437
+ ic.innerHTML = ICON_FILE_SM
438
+ pill.appendChild(ic)
439
+ pill.appendChild(this.#buildNameEl(w, i, files[i], sig))
440
+ if (files[i].size) { const sz = document.createElement('span'); sz.className = 'oe-attaches__pill-size'; sz.textContent = formatSize(files[i].size); pill.appendChild(sz) }
441
+ pill.appendChild(this.#buildRemoveBtn(w, i, sig))
442
+ container.appendChild(pill)
443
+ }
444
+ w.appendChild(container)
445
+ }
446
+
447
+ // ── Variant F: Notion ──────────────────────────────────────────────────────
448
+
449
+ /**
450
+ * @param {HTMLElement} w Block wrapper that receives the table-like list.
451
+ * @param {FileEntry[]} files Files to render.
452
+ * @param {AbortSignal} sig Current view lifecycle signal.
453
+ * @returns {void}
454
+ */
455
+ #renderNotion(w, files, sig) {
456
+ const table = document.createElement('div')
457
+ table.className = 'oe-attaches__notion'
458
+ for (let i = 0; i < files.length; i++) {
459
+ const row = document.createElement('div')
460
+ row.className = 'oe-attaches__notion-row'
461
+ row.appendChild(this.#buildNameEl(w, i, files[i], sig))
462
+ const ext = (files[i].extension || '').toUpperCase()
463
+ if (ext) {
464
+ const tag = document.createElement('span')
465
+ tag.className = 'oe-attaches__notion-tag'
466
+ tag.textContent = ext
467
+ const color = EXT_COLORS[files[i].extension?.toLowerCase()] || null
468
+ if (color) { tag.style.backgroundColor = `${color}20`; tag.style.color = color }
469
+ row.appendChild(tag)
470
+ }
471
+ if (files[i].size) { const sz = document.createElement('span'); sz.className = 'oe-attaches__notion-size'; sz.textContent = formatSize(files[i].size); row.appendChild(sz) }
472
+ row.appendChild(this.#buildRemoveBtn(w, i, sig))
473
+ table.appendChild(row)
474
+ }
475
+ w.appendChild(table)
476
+ }
477
+
478
+ // ── Variant G: Material ────────────────────────────────────────────────────
479
+
480
+ /**
481
+ * @param {HTMLElement} w Block wrapper that receives the material cards.
482
+ * @param {FileEntry[]} files Files to render.
483
+ * @param {AbortSignal} sig Current view lifecycle signal.
484
+ * @returns {void}
485
+ */
486
+ #renderMaterial(w, files, sig) {
487
+ const stack = document.createElement('div')
488
+ stack.className = 'oe-attaches__material'
489
+ for (let i = 0; i < files.length; i++) {
490
+ const card = document.createElement('div')
491
+ card.className = 'oe-attaches__material-card'
492
+ card.appendChild(this.#buildIconG(files[i]))
493
+ const info = document.createElement('div')
494
+ info.className = 'oe-attaches__info'
495
+ info.appendChild(this.#buildNameEl(w, i, files[i], sig))
496
+ if (files[i].size) { const m = document.createElement('div'); m.className = 'oe-attaches__meta'; m.textContent = formatSize(files[i].size); info.appendChild(m) }
497
+ card.append(info, this.#buildRemoveBtn(w, i, sig))
498
+ stack.appendChild(card)
499
+ }
500
+ w.appendChild(stack)
501
+ }
502
+
503
+ /** Variant G icon: custom type-specific icon, no badge. @param {FileEntry} file @returns {HTMLDivElement} */
504
+ #buildIconG(file) {
505
+ const wrap = document.createElement('div')
506
+ wrap.className = 'oe-attaches__material-icon'
507
+ const { svg } = getFileIcon(file.extension)
508
+ wrap.innerHTML = svg
509
+ return wrap
510
+ }
511
+
512
+ // ── shared builders ────────────────────────────────────────────────────────
513
+
514
+ /**
515
+ * @param {HTMLElement} wrapper
516
+ * @param {number} index
517
+ * @param {FileEntry} file
518
+ * @param {AbortSignal} signal
519
+ * @returns {HTMLDivElement}
520
+ */
521
+ #buildNameEl(wrapper, index, file, signal) {
522
+ const state = stateMap.get(wrapper)
523
+ const editable = !state?.context.readOnly
524
+ const el = document.createElement('div')
525
+ el.className = `oe-attaches__name${editable ? '' : ' oe-attaches__name--static'}`
526
+ el.contentEditable = String(editable)
527
+ const fallbackName = this._t('untitled', 'File')
528
+ el.textContent = file.name || fallbackName
529
+ if (!editable) return el
530
+
531
+ el.addEventListener('keydown', (e) => {
532
+ if (e.key === 'Enter') { e.preventDefault(); el.blur() }
533
+ if (!e.ctrlKey && !e.metaKey) e.stopPropagation()
534
+ }, { signal })
535
+ el.addEventListener('blur', () => {
536
+ const s = stateMap.get(wrapper)
537
+ if (s?.data.files[index]) s.data.files[index].name = String(el.textContent ?? '').trim() || fallbackName
538
+ }, { signal })
539
+ return el
540
+ }
541
+
542
+ /**
543
+ * @param {HTMLElement} wrapper
544
+ * @param {number} index
545
+ * @param {AbortSignal} signal
546
+ * @returns {HTMLButtonElement}
547
+ */
548
+ #buildRemoveBtn(wrapper, index, signal) {
549
+ const btn = document.createElement('button')
550
+ btn.type = 'button'
551
+ btn.className = 'oe-attaches__remove'
552
+ btn.innerHTML = '&times;'
553
+ btn.setAttribute('aria-label', this._t('delete', 'Delete'))
554
+ const state = stateMap.get(wrapper)
555
+ if (state?.context.readOnly) {
556
+ btn.hidden = true
557
+ btn.disabled = true
558
+ return btn
559
+ }
560
+ btn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
561
+ btn.addEventListener('click', (e) => {
562
+ e.stopPropagation()
563
+ const st = stateMap.get(wrapper)
564
+ if (!st) return
565
+ st.context.mutate(() => {
566
+ st.data.files.splice(index, 1)
567
+ if (st.data.files.length > 0) {
568
+ this.#renderFilled(wrapper)
569
+ } else {
570
+ this.#cancelTasks(wrapper)
571
+ this.#renderSelect(wrapper)
572
+ }
573
+ })
574
+ }, { signal })
575
+ return btn
576
+ }
577
+
578
+ // ── actions ────────────────────────────────────────────────────────────────
579
+
580
+ /**
581
+ * @param {HTMLElement} wrapper Block wrapper that owns the action bar.
582
+ * @param {AbortSignal} signal Current view lifecycle signal.
583
+ * @returns {void}
584
+ */
585
+ #renderActions(wrapper, signal) {
586
+ const actions = document.createElement('div')
587
+ actions.className = 'oe-attaches__actions'
588
+
589
+ // Settings dropdown (variant selector)
590
+ const dropdown = document.createElement('div')
591
+ dropdown.className = 'oe-attaches__dropdown'
592
+
593
+ const settingsBtn = document.createElement('button')
594
+ settingsBtn.type = 'button'
595
+ settingsBtn.className = 'oe-attaches__action-btn'
596
+ settingsBtn.innerHTML = `${ICON_SETTINGS} ${this._t('settings', 'Settings')}`
597
+ settingsBtn.setAttribute('aria-haspopup', 'true')
598
+ settingsBtn.setAttribute('aria-expanded', 'false')
599
+
600
+ const panel = this.#buildVariantPanel(wrapper, signal)
601
+ const setOpen = (open) => {
602
+ dropdown.classList.toggle('oe-attaches__dropdown--open', open)
603
+ settingsBtn.setAttribute('aria-expanded', String(open))
604
+ }
605
+
606
+ settingsBtn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
607
+ settingsBtn.addEventListener('click', (e) => {
608
+ e.stopPropagation()
609
+ setOpen(!dropdown.classList.contains('oe-attaches__dropdown--open'))
610
+ }, { signal })
611
+
612
+ document.addEventListener('click', (e) => {
613
+ const target = e.target
614
+ if (!(target instanceof globalThis.Node)) return
615
+ if (!dropdown.contains(/** @type {import('../../core/types').DOMNode} */ (target))) setOpen(false)
616
+ }, { signal })
617
+ document.addEventListener('keydown', (event) => {
618
+ if (event.key !== 'Escape' || !dropdown.classList.contains('oe-attaches__dropdown--open')) return
619
+ event.preventDefault()
620
+ setOpen(false)
621
+ settingsBtn.focus()
622
+ }, { signal })
623
+
624
+ dropdown.append(settingsBtn, panel)
625
+ actions.appendChild(dropdown)
626
+
627
+ actions.appendChild(this.#sep())
628
+
629
+ // Add files
630
+ const addBtn = document.createElement('button')
631
+ addBtn.type = 'button'
632
+ addBtn.className = 'oe-attaches__action-btn'
633
+ addBtn.innerHTML = `${ICON_UPLOAD} ${this._t('addFiles', 'Add files')}`
634
+ addBtn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
635
+ addBtn.addEventListener('click', (e) => { e.stopPropagation(); this.#triggerFileInput(wrapper) }, { signal })
636
+ actions.appendChild(addBtn)
637
+
638
+ for (const action of this.#config.actions ?? []) {
639
+ const sourceBtn = document.createElement('button')
640
+ sourceBtn.type = 'button'
641
+ sourceBtn.className = 'oe-attaches__action-btn'
642
+ if (action.icon) sourceBtn.insertAdjacentHTML('afterbegin', action.icon)
643
+ sourceBtn.append(document.createTextNode(` ${action.label}`))
644
+ sourceBtn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
645
+ sourceBtn.addEventListener('click', (e) => {
646
+ e.stopPropagation()
647
+ void this.#runCustomAction(wrapper, action.handler)
648
+ }, { signal })
649
+ actions.appendChild(sourceBtn)
650
+ }
651
+
652
+ actions.appendChild(this.#sep())
653
+
654
+ // Delete all
655
+ const delBtn = document.createElement('button')
656
+ delBtn.type = 'button'
657
+ delBtn.className = 'oe-attaches__action-btn oe-attaches__action-btn--danger'
658
+ delBtn.innerHTML = ICON_TRASH
659
+ delBtn.setAttribute('aria-label', this._t('deleteAll', 'Delete all'))
660
+ delBtn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
661
+ delBtn.addEventListener('click', (e) => {
662
+ e.stopPropagation()
663
+ const st = stateMap.get(wrapper)
664
+ if (!st) return
665
+ st.context.mutate(() => {
666
+ this.#cancelTasks(wrapper)
667
+ st.data = { files: [], variant: st.data.variant }
668
+ this.#renderSelect(wrapper)
669
+ })
670
+ }, { signal })
671
+ actions.appendChild(delBtn)
672
+
673
+ wrapper.appendChild(actions)
674
+ }
675
+
676
+ /**
677
+ * Build the variant selector dropdown panel.
678
+ * @param {HTMLElement} wrapper
679
+ * @param {AbortSignal} signal
680
+ * @returns {HTMLDivElement}
681
+ */
682
+ #buildVariantPanel(wrapper, signal) {
683
+ const s = stateMap.get(wrapper)
684
+ const panel = document.createElement('div')
685
+ panel.className = 'oe-attaches__dropdown-panel'
686
+ panel.setAttribute('role', 'group')
687
+ panel.setAttribute('aria-label', this._t('template', 'Template'))
688
+ panel.addEventListener('click', (e) => e.stopPropagation(), { signal })
689
+
690
+ const title = document.createElement('div')
691
+ title.className = 'oe-attaches__tpl-title'
692
+ title.textContent = this._t('template', 'Template')
693
+ panel.appendChild(title)
694
+
695
+ const grid = document.createElement('div')
696
+ grid.className = 'oe-attaches__tpl-grid'
697
+
698
+ for (const v of VARIANTS) {
699
+ const meta = VARIANT_META[v]
700
+ const btn = document.createElement('button')
701
+ btn.type = 'button'
702
+ btn.className = 'oe-attaches__tpl-btn' + (s?.data.variant === v ? ' oe-attaches__tpl-btn--active' : '')
703
+ btn.innerHTML = meta.icon
704
+ btn.title = this._t(meta.label, meta.label)
705
+ btn.setAttribute('aria-label', btn.title)
706
+ btn.setAttribute('aria-pressed', String(s?.data.variant === v))
707
+ btn.addEventListener('mousedown', (e) => e.preventDefault(), { signal })
708
+ btn.addEventListener('click', () => {
709
+ const st = stateMap.get(wrapper)
710
+ if (!st) return
711
+ st.context.mutate(() => {
712
+ this.#syncNames(wrapper)
713
+ st.data.variant = v
714
+ grid.querySelectorAll('.oe-attaches__tpl-btn').forEach(b => {
715
+ b.classList.remove('oe-attaches__tpl-btn--active')
716
+ b.setAttribute('aria-pressed', 'false')
717
+ })
718
+ btn.classList.add('oe-attaches__tpl-btn--active')
719
+ btn.setAttribute('aria-pressed', 'true')
720
+ // Re-render content only, keep actions intact
721
+ this.#rerenderContent(wrapper)
722
+ })
723
+ }, { signal })
724
+ grid.appendChild(btn)
725
+ }
726
+
727
+ panel.appendChild(grid)
728
+ return panel
729
+ }
730
+
731
+ /**
732
+ * Re-render file content without rebuilding action bar.
733
+ * Preserves the actions element and its event listeners.
734
+ * @param {HTMLElement} wrapper
735
+ * @returns {void}
736
+ */
737
+ #rerenderContent(wrapper) {
738
+ const s = stateMap.get(wrapper)
739
+ if (!s) return
740
+
741
+ // Remove only content elements, keep .oe-attaches__actions intact
742
+ const actions = wrapper.querySelector('.oe-attaches__actions')
743
+ for (const child of [...wrapper.children]) {
744
+ if (child !== actions) child.remove()
745
+ }
746
+
747
+ const signal = s.viewController?.signal
748
+ if (!signal || signal.aborted) return
749
+ const files = s.data.files
750
+ const v = s.data.variant || 'a'
751
+
752
+ switch (v) {
753
+ case 'b': this.#renderPills(wrapper, files, signal); break
754
+ case 'f': this.#renderNotion(wrapper, files, signal); break
755
+ case 'g': this.#renderMaterial(wrapper, files, signal); break
756
+ default:
757
+ if (files.length === 1) wrapper.appendChild(this.#buildCardA(wrapper, files[0], 0, signal))
758
+ else wrapper.appendChild(this.#buildGroupA(wrapper, signal))
759
+ break
760
+ }
761
+
762
+ // Move actions back to the end
763
+ if (actions) wrapper.appendChild(actions)
764
+ }
765
+
766
+ /** @returns {HTMLDivElement} */
767
+ #sep() {
768
+ const s = document.createElement('div')
769
+ s.className = 'oe-attaches__actions-sep'
770
+ return s
771
+ }
772
+
773
+ // ── file handling ──────────────────────────────────────────────────────────
774
+
775
+ /** @param {HTMLElement} wrapper @returns {void} */
776
+ #triggerFileInput(wrapper) {
777
+ const s = stateMap.get(wrapper)
778
+ if (!s || s.context.readOnly) return
779
+ triggerFileInput({
780
+ multiple: true,
781
+ signal: s.viewController?.signal,
782
+ onFiles: (files) => { void this.#handleFiles(wrapper, files) },
783
+ })
784
+ }
785
+
786
+ /**
787
+ * Start an asynchronous additive operation owned by one block.
788
+ * View re-renders do not cancel it; block destruction or explicit removal does.
789
+ * @param {HTMLElement} wrapper
790
+ * @returns {{ state: AttachesState, controller: AbortController } | null}
791
+ */
792
+ #startTask(wrapper) {
793
+ const state = stateMap.get(wrapper)
794
+ if (!state || state.context.readOnly) return null
795
+ const controller = new AbortController()
796
+ state.taskControllers.add(controller)
797
+ wrapper.classList.add('oe-attaches--loading')
798
+ return { state, controller }
799
+ }
800
+
801
+ /**
802
+ * @param {HTMLElement} wrapper Block wrapper whose loading state is updated.
803
+ * @param {AttachesState} state State that owns the asynchronous task.
804
+ * @param {AbortController} controller Completed task controller.
805
+ * @returns {void}
806
+ */
807
+ #finishTask(wrapper, state, controller) {
808
+ state.taskControllers.delete(controller)
809
+ if (state.taskControllers.size === 0) wrapper.classList.remove('oe-attaches--loading')
810
+ }
811
+
812
+ /** @param {HTMLElement} wrapper @returns {void} */
813
+ #cancelTasks(wrapper) {
814
+ const state = stateMap.get(wrapper)
815
+ if (!state) return
816
+ for (const controller of state.taskControllers) controller.abort()
817
+ state.taskControllers.clear()
818
+ wrapper.classList.remove('oe-attaches--loading')
819
+ }
820
+
821
+ /**
822
+ * @param {HTMLElement} wrapper Block wrapper that receives uploaded files.
823
+ * @param {FileList | File[]} fileList Browser files selected or dropped by the user.
824
+ * @returns {Promise<void>}
825
+ */
826
+ async #handleFiles(wrapper, fileList) {
827
+ const files = /** @type {File[]} */ (Array.from(fileList))
828
+ if (files.length === 0) return
829
+ const task = this.#startTask(wrapper)
830
+ if (!task) return
831
+ const { state: s, controller } = task
832
+ const { signal } = controller
833
+
834
+ try {
835
+ const uploadFile = this.#config.uploadFile
836
+ if (uploadFile) {
837
+ const added = []
838
+ for (const file of files) {
839
+ if (signal.aborted) break
840
+ try {
841
+ const ext = getExtension(file.name)
842
+ const result = await uploadFile(file, { signal })
843
+ const url = sanitizeUrl(String(result?.url || ''), { policy: 'download', fallback: '' })
844
+ const resultSize = Number(result?.size)
845
+ const size = Number.isFinite(resultSize) && resultSize >= 0 ? resultSize : file.size
846
+ if (url) added.push({ url, name: file.name, size, extension: ext })
847
+ } catch (error) {
848
+ if (signal.aborted) break
849
+ console.warn(`[Attaches] Failed to upload "${file.name}":`, error)
850
+ }
851
+ }
852
+ if (!signal.aborted && stateMap.get(wrapper) === s && added.length > 0) {
853
+ s.context.mutate(() => {
854
+ s.data.files.push(...added)
855
+ this.#renderFilled(wrapper)
856
+ })
857
+ }
858
+ } else {
859
+ const added = []
860
+ for (const file of files) {
861
+ if (signal.aborted) break
862
+ const ext = getExtension(file.name)
863
+ try {
864
+ const url = URL.createObjectURL(file)
865
+ this.#objectUrls.add(url)
866
+ added.push({ url, name: file.name, size: file.size, extension: ext })
867
+ } catch (error) {
868
+ console.warn(`[Attaches] Failed to open "${file.name}":`, error)
869
+ }
870
+ }
871
+ if (!signal.aborted && stateMap.get(wrapper) === s && added.length > 0) {
872
+ s.context.mutate(() => {
873
+ s.data.files.push(...added)
874
+ this.#renderFilled(wrapper)
875
+ })
876
+ }
877
+ }
878
+ } finally {
879
+ this.#finishTask(wrapper, s, controller)
880
+ }
881
+ }
882
+
883
+ /**
884
+ * Add files selected by an application source such as a media library.
885
+ * One completed selection becomes one editor history operation.
886
+ * @param {HTMLElement} wrapper
887
+ * @param {(context: { signal: AbortSignal }) => Promise<Array<{ url: string, name: string, size?: number, extension?: string }> | null>} handler
888
+ * @returns {Promise<void>}
889
+ */
890
+ async #runCustomAction(wrapper, handler) {
891
+ const task = this.#startTask(wrapper)
892
+ if (!task) return
893
+ const { state: s, controller } = task
894
+ const { signal } = controller
895
+ try {
896
+ const result = await handler({ signal })
897
+ if (!Array.isArray(result) || signal.aborted || stateMap.get(wrapper) !== s) return
898
+ const added = result.flatMap(item => {
899
+ const url = sanitizeUrl(String(item?.url || ''), { policy: 'download', fallback: '' })
900
+ const name = String(item?.name || '').trim()
901
+ if (!url || !name) return []
902
+ const size = Number(item?.size)
903
+ return [{
904
+ url,
905
+ name,
906
+ size: Number.isFinite(size) && size >= 0 ? size : 0,
907
+ extension: String(item?.extension || getExtension(name)),
908
+ }]
909
+ })
910
+ if (!added.length) return
911
+ s.context.mutate(() => {
912
+ s.data.files.push(...added)
913
+ this.#renderFilled(wrapper)
914
+ })
915
+ } catch (error) {
916
+ if (!signal.aborted) console.warn('[Attaches] Application source failed:', error)
917
+ } finally {
918
+ this.#finishTask(wrapper, s, controller)
919
+ }
920
+ }
921
+ }