@notectl/core 1.0.4 → 1.0.5

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 (312) hide show
  1. package/README.md +51 -148
  2. package/dist/chunks/AttrRegistry-DTsHyDVA.mjs +16 -0
  3. package/dist/chunks/AttrRegistry-DTsHyDVA.mjs.map +1 -0
  4. package/dist/chunks/Commands-DLP85Mp5.mjs +461 -0
  5. package/dist/chunks/Commands-DLP85Mp5.mjs.map +1 -0
  6. package/dist/chunks/Document-CI3iCwEP.mjs +177 -0
  7. package/dist/chunks/Document-CI3iCwEP.mjs.map +1 -0
  8. package/dist/chunks/HTMLUtils-0wvCRvJc.mjs +7 -0
  9. package/dist/chunks/HTMLUtils-0wvCRvJc.mjs.map +1 -0
  10. package/dist/chunks/NodeResolver-D68ZLXti.mjs +81 -0
  11. package/dist/chunks/NodeResolver-D68ZLXti.mjs.map +1 -0
  12. package/dist/chunks/NodeSpec-NZVvcCuH.mjs +8 -0
  13. package/dist/chunks/NodeSpec-NZVvcCuH.mjs.map +1 -0
  14. package/dist/chunks/Plugin-B9OZ6Y4c.mjs +15 -0
  15. package/dist/chunks/Plugin-B9OZ6Y4c.mjs.map +1 -0
  16. package/dist/chunks/Selection-D5ZjXoB7.mjs +44 -0
  17. package/dist/chunks/Selection-D5ZjXoB7.mjs.map +1 -0
  18. package/dist/chunks/ToolbarItem-ugDymuXi.mjs +7 -0
  19. package/dist/chunks/ToolbarItem-ugDymuXi.mjs.map +1 -0
  20. package/dist/chunks/ToolbarPlugin-D5VqeBPT.mjs +398 -0
  21. package/dist/chunks/ToolbarPlugin-D5VqeBPT.mjs.map +1 -0
  22. package/dist/chunks/TypeBrands-C09nE2qO.mjs +27 -0
  23. package/dist/chunks/TypeBrands-C09nE2qO.mjs.map +1 -0
  24. package/dist/chunks/color-picker-Csf0wx5a.mjs +5 -0
  25. package/dist/chunks/color-picker-Csf0wx5a.mjs.map +1 -0
  26. package/dist/chunks/index-CPPb-Jah.mjs +569 -0
  27. package/dist/chunks/index-CPPb-Jah.mjs.map +1 -0
  28. package/dist/commands/Commands.d.ts +71 -0
  29. package/dist/commands/Commands.d.ts.map +1 -0
  30. package/dist/commands/PasteCommand.d.ts +9 -0
  31. package/dist/commands/PasteCommand.d.ts.map +1 -0
  32. package/dist/decorations/Decoration.d.ts +76 -0
  33. package/dist/decorations/Decoration.d.ts.map +1 -0
  34. package/dist/decorations/PositionMapping.d.ts +9 -0
  35. package/dist/decorations/PositionMapping.d.ts.map +1 -0
  36. package/dist/editor/Announcer.d.ts +12 -0
  37. package/dist/editor/Announcer.d.ts.map +1 -0
  38. package/dist/editor/DocumentParser.d.ts +5 -0
  39. package/dist/editor/DocumentParser.d.ts.map +1 -0
  40. package/dist/editor/DocumentSerializer.d.ts +5 -0
  41. package/dist/editor/DocumentSerializer.d.ts.map +1 -0
  42. package/dist/editor/EditorDOM.d.ts +18 -0
  43. package/dist/editor/EditorDOM.d.ts.map +1 -0
  44. package/dist/editor/EditorThemeController.d.ts +21 -0
  45. package/dist/editor/EditorThemeController.d.ts.map +1 -0
  46. package/dist/editor/NotectlEditor.d.ts +131 -0
  47. package/dist/editor/NotectlEditor.d.ts.map +1 -0
  48. package/dist/editor/styles/base.d.ts +3 -0
  49. package/dist/editor/styles/base.d.ts.map +1 -0
  50. package/dist/editor/styles/code-block.d.ts +3 -0
  51. package/dist/editor/styles/code-block.d.ts.map +1 -0
  52. package/dist/editor/styles/color-picker.d.ts +3 -0
  53. package/dist/editor/styles/color-picker.d.ts.map +1 -0
  54. package/dist/editor/styles/font-select.d.ts +3 -0
  55. package/dist/editor/styles/font-select.d.ts.map +1 -0
  56. package/dist/editor/styles/font-size-select.d.ts +3 -0
  57. package/dist/editor/styles/font-size-select.d.ts.map +1 -0
  58. package/dist/editor/styles/heading-select.d.ts +3 -0
  59. package/dist/editor/styles/heading-select.d.ts.map +1 -0
  60. package/dist/editor/styles/image.d.ts +3 -0
  61. package/dist/editor/styles/image.d.ts.map +1 -0
  62. package/dist/editor/styles/list.d.ts +3 -0
  63. package/dist/editor/styles/list.d.ts.map +1 -0
  64. package/dist/editor/styles/reduced-motion.d.ts +3 -0
  65. package/dist/editor/styles/reduced-motion.d.ts.map +1 -0
  66. package/dist/editor/styles/table.d.ts +3 -0
  67. package/dist/editor/styles/table.d.ts.map +1 -0
  68. package/dist/editor/styles/toolbar.d.ts +3 -0
  69. package/dist/editor/styles/toolbar.d.ts.map +1 -0
  70. package/dist/editor/styles.d.ts +12 -0
  71. package/dist/editor/styles.d.ts.map +1 -0
  72. package/dist/editor/theme/ThemeEngine.d.ts +6 -0
  73. package/dist/editor/theme/ThemeEngine.d.ts.map +1 -0
  74. package/dist/editor/theme/ThemeTokens.d.ts +73 -0
  75. package/dist/editor/theme/ThemeTokens.d.ts.map +1 -0
  76. package/dist/fonts.d.ts +17 -0
  77. package/dist/fonts.d.ts.map +1 -0
  78. package/dist/fonts.mjs +7 -0
  79. package/dist/fonts.mjs.map +1 -0
  80. package/dist/index.d.ts +81 -2360
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/input/ClipboardHandler.d.ts +29 -0
  83. package/dist/input/ClipboardHandler.d.ts.map +1 -0
  84. package/dist/input/InputHandler.d.ts +32 -0
  85. package/dist/input/InputHandler.d.ts.map +1 -0
  86. package/dist/input/InputRule.d.ts +9 -0
  87. package/dist/input/InputRule.d.ts.map +1 -0
  88. package/dist/input/InternalClipboard.d.ts +23 -0
  89. package/dist/input/InternalClipboard.d.ts.map +1 -0
  90. package/dist/input/KeyboardHandler.d.ts +42 -0
  91. package/dist/input/KeyboardHandler.d.ts.map +1 -0
  92. package/dist/input/Keymap.d.ts +11 -0
  93. package/dist/input/Keymap.d.ts.map +1 -0
  94. package/dist/input/PasteHandler.d.ts +38 -0
  95. package/dist/input/PasteHandler.d.ts.map +1 -0
  96. package/dist/input/SourceNormalizer.d.ts +7 -0
  97. package/dist/input/SourceNormalizer.d.ts.map +1 -0
  98. package/dist/model/AttrRegistry.d.ts +39 -0
  99. package/dist/model/AttrRegistry.d.ts.map +1 -0
  100. package/dist/model/BuiltinSpecs.d.ts +4 -0
  101. package/dist/model/BuiltinSpecs.d.ts.map +1 -0
  102. package/dist/model/ContentModel.d.ts +12 -0
  103. package/dist/model/ContentModel.d.ts.map +1 -0
  104. package/dist/model/ContentSlice.d.ts +17 -0
  105. package/dist/model/ContentSlice.d.ts.map +1 -0
  106. package/dist/model/Document.d.ts +127 -0
  107. package/dist/model/Document.d.ts.map +1 -0
  108. package/dist/model/HTMLParser.d.ts +36 -0
  109. package/dist/model/HTMLParser.d.ts.map +1 -0
  110. package/dist/model/HTMLUtils.d.ts +6 -0
  111. package/dist/model/HTMLUtils.d.ts.map +1 -0
  112. package/dist/model/InlineNodeSpec.d.ts +20 -0
  113. package/dist/model/InlineNodeSpec.d.ts.map +1 -0
  114. package/dist/model/MarkSpec.d.ts +22 -0
  115. package/dist/model/MarkSpec.d.ts.map +1 -0
  116. package/dist/model/NodeResolver.d.ts +36 -0
  117. package/dist/model/NodeResolver.d.ts.map +1 -0
  118. package/dist/model/NodeSpec.d.ts +67 -0
  119. package/dist/model/NodeSpec.d.ts.map +1 -0
  120. package/dist/model/ParseRule.d.ts +11 -0
  121. package/dist/model/ParseRule.d.ts.map +1 -0
  122. package/dist/model/SanitizeConfig.d.ts +9 -0
  123. package/dist/model/SanitizeConfig.d.ts.map +1 -0
  124. package/dist/model/Schema.d.ts +17 -0
  125. package/dist/model/Schema.d.ts.map +1 -0
  126. package/dist/model/SchemaRegistry.d.ts +81 -0
  127. package/dist/model/SchemaRegistry.d.ts.map +1 -0
  128. package/dist/model/Selection.d.ts +52 -0
  129. package/dist/model/Selection.d.ts.map +1 -0
  130. package/dist/model/TypeBrands.d.ts +28 -0
  131. package/dist/model/TypeBrands.d.ts.map +1 -0
  132. package/dist/notectl-core.mjs +2576 -11104
  133. package/dist/notectl-core.mjs.map +1 -1
  134. package/dist/notectl-core.umd.js +6810 -0
  135. package/dist/notectl-core.umd.js.map +1 -0
  136. package/dist/plugins/EventBus.d.ts +13 -0
  137. package/dist/plugins/EventBus.d.ts.map +1 -0
  138. package/dist/plugins/Plugin.d.ts +85 -0
  139. package/dist/plugins/Plugin.d.ts.map +1 -0
  140. package/dist/plugins/PluginManager.d.ts +66 -0
  141. package/dist/plugins/PluginManager.d.ts.map +1 -0
  142. package/dist/plugins/alignment/AlignmentPlugin.d.ts +48 -0
  143. package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -0
  144. package/dist/plugins/alignment/index.d.ts +2 -0
  145. package/dist/plugins/alignment/index.d.ts.map +1 -0
  146. package/dist/plugins/alignment.mjs +92 -0
  147. package/dist/plugins/alignment.mjs.map +1 -0
  148. package/dist/plugins/blockquote/BlockquotePlugin.d.ts +30 -0
  149. package/dist/plugins/blockquote/BlockquotePlugin.d.ts.map +1 -0
  150. package/dist/plugins/blockquote/index.d.ts +2 -0
  151. package/dist/plugins/blockquote/index.d.ts.map +1 -0
  152. package/dist/plugins/blockquote.mjs +58 -0
  153. package/dist/plugins/blockquote.mjs.map +1 -0
  154. package/dist/plugins/code-block/CodeBlockNodeView.d.ts +5 -0
  155. package/dist/plugins/code-block/CodeBlockNodeView.d.ts.map +1 -0
  156. package/dist/plugins/code-block/CodeBlockPlugin.d.ts +135 -0
  157. package/dist/plugins/code-block/CodeBlockPlugin.d.ts.map +1 -0
  158. package/dist/plugins/code-block/index.d.ts +2 -0
  159. package/dist/plugins/code-block/index.d.ts.map +1 -0
  160. package/dist/plugins/code-block.mjs +6 -0
  161. package/dist/plugins/code-block.mjs.map +1 -0
  162. package/dist/plugins/font/FontPlugin.d.ts +85 -0
  163. package/dist/plugins/font/FontPlugin.d.ts.map +1 -0
  164. package/dist/plugins/font/StarterFonts.d.ts +10 -0
  165. package/dist/plugins/font/StarterFonts.d.ts.map +1 -0
  166. package/dist/plugins/font/index.d.ts +2 -0
  167. package/dist/plugins/font/index.d.ts.map +1 -0
  168. package/dist/plugins/font-size/FontSizePlugin.d.ts +65 -0
  169. package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -0
  170. package/dist/plugins/font-size/index.d.ts +2 -0
  171. package/dist/plugins/font-size/index.d.ts.map +1 -0
  172. package/dist/plugins/font-size.mjs +220 -0
  173. package/dist/plugins/font-size.mjs.map +1 -0
  174. package/dist/plugins/font.mjs +186 -0
  175. package/dist/plugins/font.mjs.map +1 -0
  176. package/dist/plugins/hard-break/HardBreakPlugin.d.ts +16 -0
  177. package/dist/plugins/hard-break/HardBreakPlugin.d.ts.map +1 -0
  178. package/dist/plugins/hard-break/index.d.ts +2 -0
  179. package/dist/plugins/hard-break/index.d.ts.map +1 -0
  180. package/dist/plugins/hard-break.mjs +28 -0
  181. package/dist/plugins/hard-break.mjs.map +1 -0
  182. package/dist/plugins/heading/BlockTypePickerEntry.d.ts +20 -0
  183. package/dist/plugins/heading/BlockTypePickerEntry.d.ts.map +1 -0
  184. package/dist/plugins/heading/HeadingPlugin.d.ts +78 -0
  185. package/dist/plugins/heading/HeadingPlugin.d.ts.map +1 -0
  186. package/dist/plugins/heading/index.d.ts +3 -0
  187. package/dist/plugins/heading/index.d.ts.map +1 -0
  188. package/dist/plugins/heading.mjs +188 -0
  189. package/dist/plugins/heading.mjs.map +1 -0
  190. package/dist/plugins/highlight/HighlightPlugin.d.ts +37 -0
  191. package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -0
  192. package/dist/plugins/highlight/index.d.ts +2 -0
  193. package/dist/plugins/highlight/index.d.ts.map +1 -0
  194. package/dist/plugins/highlight.mjs +126 -0
  195. package/dist/plugins/highlight.mjs.map +1 -0
  196. package/dist/plugins/horizontal-rule/HorizontalRulePlugin.d.ts +29 -0
  197. package/dist/plugins/horizontal-rule/HorizontalRulePlugin.d.ts.map +1 -0
  198. package/dist/plugins/horizontal-rule/index.d.ts +2 -0
  199. package/dist/plugins/horizontal-rule/index.d.ts.map +1 -0
  200. package/dist/plugins/horizontal-rule.mjs +59 -0
  201. package/dist/plugins/horizontal-rule.mjs.map +1 -0
  202. package/dist/plugins/image/ImageCommands.d.ts +26 -0
  203. package/dist/plugins/image/ImageCommands.d.ts.map +1 -0
  204. package/dist/plugins/image/ImageNodeView.d.ts +6 -0
  205. package/dist/plugins/image/ImageNodeView.d.ts.map +1 -0
  206. package/dist/plugins/image/ImagePlugin.d.ts +43 -0
  207. package/dist/plugins/image/ImagePlugin.d.ts.map +1 -0
  208. package/dist/plugins/image/ImageUpload.d.ts +48 -0
  209. package/dist/plugins/image/ImageUpload.d.ts.map +1 -0
  210. package/dist/plugins/image/index.d.ts +3 -0
  211. package/dist/plugins/image/index.d.ts.map +1 -0
  212. package/dist/plugins/image.mjs +381 -0
  213. package/dist/plugins/image.mjs.map +1 -0
  214. package/dist/plugins/link/LinkPlugin.d.ts +32 -0
  215. package/dist/plugins/link/LinkPlugin.d.ts.map +1 -0
  216. package/dist/plugins/link/index.d.ts +2 -0
  217. package/dist/plugins/link/index.d.ts.map +1 -0
  218. package/dist/plugins/link.mjs +127 -0
  219. package/dist/plugins/link.mjs.map +1 -0
  220. package/dist/plugins/list/ListPlugin.d.ts +51 -0
  221. package/dist/plugins/list/ListPlugin.d.ts.map +1 -0
  222. package/dist/plugins/list/index.d.ts +2 -0
  223. package/dist/plugins/list/index.d.ts.map +1 -0
  224. package/dist/plugins/list.mjs +193 -0
  225. package/dist/plugins/list.mjs.map +1 -0
  226. package/dist/plugins/strikethrough/StrikethroughPlugin.d.ts +23 -0
  227. package/dist/plugins/strikethrough/StrikethroughPlugin.d.ts.map +1 -0
  228. package/dist/plugins/strikethrough/index.d.ts +2 -0
  229. package/dist/plugins/strikethrough/index.d.ts.map +1 -0
  230. package/dist/plugins/strikethrough.mjs +35 -0
  231. package/dist/plugins/strikethrough.mjs.map +1 -0
  232. package/dist/plugins/super-sub/SuperSubPlugin.d.ts +44 -0
  233. package/dist/plugins/super-sub/SuperSubPlugin.d.ts.map +1 -0
  234. package/dist/plugins/super-sub/index.d.ts +2 -0
  235. package/dist/plugins/super-sub/index.d.ts.map +1 -0
  236. package/dist/plugins/super-sub.mjs +81 -0
  237. package/dist/plugins/super-sub.mjs.map +1 -0
  238. package/dist/plugins/table/TableCommands.d.ts +50 -0
  239. package/dist/plugins/table/TableCommands.d.ts.map +1 -0
  240. package/dist/plugins/table/TableControls.d.ts +10 -0
  241. package/dist/plugins/table/TableControls.d.ts.map +1 -0
  242. package/dist/plugins/table/TableControlsDOM.d.ts +18 -0
  243. package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -0
  244. package/dist/plugins/table/TableControlsLayout.d.ts +34 -0
  245. package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -0
  246. package/dist/plugins/table/TableHelpers.d.ts +39 -0
  247. package/dist/plugins/table/TableHelpers.d.ts.map +1 -0
  248. package/dist/plugins/table/TableNavigation.d.ts +4 -0
  249. package/dist/plugins/table/TableNavigation.d.ts.map +1 -0
  250. package/dist/plugins/table/TableNodeViews.d.ts +18 -0
  251. package/dist/plugins/table/TableNodeViews.d.ts.map +1 -0
  252. package/dist/plugins/table/TablePlugin.d.ts +39 -0
  253. package/dist/plugins/table/TablePlugin.d.ts.map +1 -0
  254. package/dist/plugins/table/TableSelection.d.ts +26 -0
  255. package/dist/plugins/table/TableSelection.d.ts.map +1 -0
  256. package/dist/plugins/table/TableTestUtils.d.ts +45 -0
  257. package/dist/plugins/table/TableTestUtils.d.ts.map +1 -0
  258. package/dist/plugins/table/index.d.ts +4 -0
  259. package/dist/plugins/table/index.d.ts.map +1 -0
  260. package/dist/plugins/table.mjs +663 -0
  261. package/dist/plugins/table.mjs.map +1 -0
  262. package/dist/plugins/text-color/TextColorPlugin.d.ts +37 -0
  263. package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -0
  264. package/dist/plugins/text-color/index.d.ts +2 -0
  265. package/dist/plugins/text-color/index.d.ts.map +1 -0
  266. package/dist/plugins/text-color.mjs +126 -0
  267. package/dist/plugins/text-color.mjs.map +1 -0
  268. package/dist/plugins/text-formatting/TextFormattingPlugin.d.ts +34 -0
  269. package/dist/plugins/text-formatting/TextFormattingPlugin.d.ts.map +1 -0
  270. package/dist/plugins/text-formatting/index.d.ts +2 -0
  271. package/dist/plugins/text-formatting/index.d.ts.map +1 -0
  272. package/dist/plugins/text-formatting.mjs +52 -0
  273. package/dist/plugins/text-formatting.mjs.map +1 -0
  274. package/dist/plugins/toolbar/ToolbarItem.d.ts +66 -0
  275. package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -0
  276. package/dist/plugins/toolbar/ToolbarKeyboardNav.d.ts +25 -0
  277. package/dist/plugins/toolbar/ToolbarKeyboardNav.d.ts.map +1 -0
  278. package/dist/plugins/toolbar/ToolbarPlugin.d.ts +47 -0
  279. package/dist/plugins/toolbar/ToolbarPlugin.d.ts.map +1 -0
  280. package/dist/plugins/toolbar/ToolbarPopupController.d.ts +25 -0
  281. package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -0
  282. package/dist/plugins/toolbar/ToolbarRenderers.d.ts +19 -0
  283. package/dist/plugins/toolbar/ToolbarRenderers.d.ts.map +1 -0
  284. package/dist/plugins/toolbar/ToolbarTooltip.d.ts +17 -0
  285. package/dist/plugins/toolbar/ToolbarTooltip.d.ts.map +1 -0
  286. package/dist/plugins/toolbar/index.d.ts +4 -0
  287. package/dist/plugins/toolbar/index.d.ts.map +1 -0
  288. package/dist/plugins/toolbar.mjs +8 -0
  289. package/dist/plugins/toolbar.mjs.map +1 -0
  290. package/dist/state/EditorState.d.ts +40 -0
  291. package/dist/state/EditorState.d.ts.map +1 -0
  292. package/dist/state/History.d.ts +38 -0
  293. package/dist/state/History.d.ts.map +1 -0
  294. package/dist/state/StepApplication.d.ts +5 -0
  295. package/dist/state/StepApplication.d.ts.map +1 -0
  296. package/dist/state/Transaction.d.ts +185 -0
  297. package/dist/state/Transaction.d.ts.map +1 -0
  298. package/dist/test/PluginTestUtils.d.ts +127 -0
  299. package/dist/test/PluginTestUtils.d.ts.map +1 -0
  300. package/dist/test/TestUtils.d.ts +153 -0
  301. package/dist/test/TestUtils.d.ts.map +1 -0
  302. package/dist/view/EditorView.d.ts +74 -0
  303. package/dist/view/EditorView.d.ts.map +1 -0
  304. package/dist/view/NodeView.d.ts +18 -0
  305. package/dist/view/NodeView.d.ts.map +1 -0
  306. package/dist/view/Reconciler.d.ts +23 -0
  307. package/dist/view/Reconciler.d.ts.map +1 -0
  308. package/dist/view/SelectionSync.d.ts +8 -0
  309. package/dist/view/SelectionSync.d.ts.map +1 -0
  310. package/package.json +112 -11
  311. package/dist/notectl-core.js +0 -1463
  312. package/dist/notectl-core.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"horizontal-rule.mjs","sources":["../../src/plugins/horizontal-rule/HorizontalRulePlugin.ts"],"sourcesContent":["/**\n * HorizontalRulePlugin: registers a horizontal rule (divider) void block type\n * with NodeSpec, insert command, input rule, keyboard shortcut, and toolbar button.\n */\n\nimport { createBlockNode } from '../../model/Document.js';\nimport { createBlockElement } from '../../model/NodeSpec.js';\nimport { createCollapsedSelection, isCollapsed, isNodeSelection } from '../../model/Selection.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { formatShortcut } from '../toolbar/ToolbarItem.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\thorizontal_rule: Record<string, never>;\n\t}\n}\n\n// --- Configuration ---\n\nexport interface HorizontalRuleConfig {\n\t/** When true, a separator is rendered after the toolbar item. */\n\treadonly separatorAfter?: boolean;\n}\n\nconst DEFAULT_CONFIG: HorizontalRuleConfig = {};\n\n// --- Helpers ---\n\n/** Finds the index of the cursor's block among top-level document children. */\nfunction findBlockIndexForCursor(state: EditorState): number {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel)) return -1;\n\treturn state.doc.children.findIndex((b) => b.id === sel.anchor.blockId);\n}\n\n// --- Plugin ---\n\nexport class HorizontalRulePlugin implements Plugin {\n\treadonly id = 'horizontal-rule';\n\treadonly name = 'Horizontal Rule';\n\treadonly priority = 40;\n\n\tprivate readonly config: HorizontalRuleConfig;\n\n\tconstructor(config?: Partial<HorizontalRuleConfig>) {\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.registerNodeSpec(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerKeymap(context);\n\t\tthis.registerInputRule(context);\n\t\tthis.registerToolbarItem(context);\n\t}\n\n\tprivate registerNodeSpec(context: PluginContext): void {\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'horizontal_rule',\n\t\t\tgroup: 'block',\n\t\t\tisVoid: true,\n\t\t\ttoDOM(node) {\n\t\t\t\treturn createBlockElement('hr', node.id);\n\t\t\t},\n\t\t\ttoHTML() {\n\t\t\t\treturn '<hr>';\n\t\t\t},\n\t\t\tparseHTML: [{ tag: 'hr' }],\n\t\t\tsanitize: { tags: ['hr'] },\n\t\t});\n\t}\n\n\tprivate registerCommands(context: PluginContext): void {\n\t\tcontext.registerCommand('insertHorizontalRule', () => {\n\t\t\treturn this.insertHorizontalRule(context);\n\t\t});\n\t}\n\n\tprivate registerKeymap(context: PluginContext): void {\n\t\tcontext.registerKeymap({\n\t\t\t'Mod-Shift-H': () => context.executeCommand('insertHorizontalRule'),\n\t\t});\n\t}\n\n\tprivate registerInputRule(context: PluginContext): void {\n\t\tcontext.registerInputRule({\n\t\t\tpattern: /^-{3,} $/,\n\t\t\thandler(state, _match, _start, end) {\n\t\t\t\tconst sel = state.selection;\n\t\t\t\tif (isNodeSelection(sel)) return null;\n\t\t\t\tif (!isCollapsed(sel)) return null;\n\n\t\t\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\t\t\tif (!block || block.type !== 'paragraph') return null;\n\n\t\t\t\tconst blockIndex: number = findBlockIndexForCursor(state);\n\t\t\t\tif (blockIndex === -1) return null;\n\n\t\t\t\tconst newParagraph = createBlockNode(nodeType('paragraph'));\n\n\t\t\t\treturn state\n\t\t\t\t\t.transaction('input')\n\t\t\t\t\t.deleteTextAt(sel.anchor.blockId, 0, end)\n\t\t\t\t\t.setBlockType(sel.anchor.blockId, nodeType('horizontal_rule'))\n\t\t\t\t\t.insertNode([], blockIndex + 1, newParagraph)\n\t\t\t\t\t.setSelection(createCollapsedSelection(newParagraph.id, 0))\n\t\t\t\t\t.build();\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst icon =\n\t\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M4 11h16v2H4z\"/></svg>';\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'horizontal-rule',\n\t\t\tgroup: 'block',\n\t\t\ticon,\n\t\t\tlabel: 'Horizontal Rule',\n\t\t\ttooltip: `Horizontal Rule (${formatShortcut('Mod-Shift-H')})`,\n\t\t\tcommand: 'insertHorizontalRule',\n\t\t\tpriority: 60,\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\tisActive: () => false,\n\t\t});\n\t}\n\n\t/**\n\t * Inserts a horizontal rule after the current block,\n\t * followed by a new paragraph for continued editing.\n\t */\n\tprivate insertHorizontalRule(context: PluginContext): boolean {\n\t\tconst state: EditorState = context.getState();\n\t\tif (isNodeSelection(state.selection)) return false;\n\t\tconst blockIndex: number = findBlockIndexForCursor(state);\n\t\tif (blockIndex === -1) return false;\n\n\t\tconst hrBlock = createBlockNode(nodeType('horizontal_rule'));\n\t\tconst newParagraph = createBlockNode(nodeType('paragraph'));\n\n\t\tconst tr = state\n\t\t\t.transaction('command')\n\t\t\t.insertNode([], blockIndex + 1, hrBlock)\n\t\t\t.insertNode([], blockIndex + 2, newParagraph)\n\t\t\t.setSelection(createCollapsedSelection(newParagraph.id, 0))\n\t\t\t.build();\n\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t}\n}\n"],"names":["DEFAULT_CONFIG","findBlockIndexForCursor","state","sel","selection","isNodeSelection","doc","children","findIndex","b","id","anchor","blockId","HorizontalRulePlugin","name","priority","config","this","context","registerNodeSpec","registerCommands","registerKeymap","registerInputRule","registerToolbarItem","type","group","isVoid","toDOM","node","createBlockElement","toHTML","parseHTML","tag","sanitize","tags","registerCommand","insertHorizontalRule","executeCommand","pattern","_match","_start","end","isCollapsed","block","getBlock","blockIndex","newParagraph","createBlockNode","nodeType","transaction","deleteTextAt","setBlockType","insertNode","setSelection","createCollapsedSelection","build","icon","label","tooltip","formatShortcut","command","separatorAfter","isActive","getState","hrBlock","tr","dispatch"],"mappings":";;;;;AA4BA,MAAMA,IAAuC,CAAA;AAK7C,SAASC,EAAwBC,IAAAA;AAChC,QAAMC,KAAMD,GAAME;AAClB,SAAIC,EAAgBF,EAAAA,IAAAA,KACbD,GAAMI,IAAIC,SAASC,UAAWC,CAAAA,OAAMA,GAAEC,OAAOP,GAAIQ,OAAOC,OAAAA;AAChE;AAIO,MAAMC,EAAAA;AAAAA,EACHH,KAAK;AAAA,EACLI,OAAO;AAAA,EACPC,WAAW;AAAA,EAEHC;AAAAA,EAEjB,YAAYA,IAAAA;AACXC,SAAKD,SAAS,EAAA,GAAKhB,MAAmBgB,GAAAA;AAAAA,EACvC;AAAA,EAEA,KAAKE,IAAAA;AACJD,SAAKE,iBAAiBD,EAAAA,GACtBD,KAAKG,iBAAiBF,EAAAA,GACtBD,KAAKI,eAAeH,KACpBD,KAAKK,kBAAkBJ,EAAAA,GACvBD,KAAKM,oBAAoBL,EAAAA;AAAAA,EAC1B;AAAA,EAEQ,iBAAiBA,IAAAA;AACxBA,IAAAA,GAAQC,iBAAiB,EACxBK,MAAM,mBACNC,OAAO,SACPC,QAAAA,MACAC,OAAMC,CAAAA,OACEC,EAAmB,MAAMD,GAAKlB,EAAAA,GAEtCoB,QAAA,MACQ,QAERC,WAAW,CAAC,EAAEC,KAAK,SACnBC,UAAU,EAAEC,MAAM,CAAC;EAErB;AAAA,EAEQ,iBAAiBhB,IAAAA;AACxBA,IAAAA,GAAQiB,gBAAgB,wBAAwB,MACxClB,KAAKmB,qBAAqBlB,EAAAA,CAAAA;AAAAA,EAEnC;AAAA,EAEQ,eAAeA,IAAAA;AACtBA,IAAAA,GAAQG,eAAe,EACtB,eAAe,MAAMH,GAAQmB,eAAe,sBAAA,EAAA,CAAA;AAAA,EAE9C;AAAA,EAEQ,kBAAkBnB,IAAAA;AACzBA,IAAAA,GAAQI,kBAAkB,EACzBgB,SAAS,YACT,QAAQpC,IAAOqC,IAAQC,IAAQC;AAC9B,YAAMtC,IAAMD,GAAME;AAClB,UAAIC,EAAgBF,CAAAA,EAAM,QAAO;AACjC,UAAA,CAAKuC,EAAYvC,CAAAA,EAAM,QAAO;AAE9B,YAAMwC,IAAQzC,GAAM0C,SAASzC,EAAIQ,OAAOC,OAAAA;AACxC,UAAA,CAAK+B,KAAwB,gBAAfA,EAAMnB,KAAsB,QAAO;AAEjD,YAAMqB,IAAqB5C,EAAwBC,EAAAA;AACnD,iBAAI2C,EAAmB,QAAO;AAE9B,YAAMC,IAAeC,EAAgBC,EAAS,WAAA,CAAA;AAE9C,aAAO9C,GACL+C,YAAY,OAAA,EACZC,aAAa/C,EAAIQ,OAAOC,SAAS,GAAG6B,IACpCU,aAAahD,EAAIQ,OAAOC,SAASoC,EAAS,iBAAA,CAAA,EAC1CI,WAAW,CAAA,GAAIP,IAAa,GAAGC,CAAAA,EAC/BO,aAAaC,EAAyBR,EAAapC,IAAI,CAAA,CAAA,EACvD6C,MAAAA;AAAAA,IACH,EAAA,CAAA;AAAA,EAEF;AAAA,EAEQ,oBAAoBrC,IAAAA;AAI3BA,IAAAA,GAAQK,oBAAoB,EAC3Bb,IAAI,mBACJe,OAAO,SACP+B,MALA,+FAMAC,OAAO,mBACPC,SAAS,oBAAoBC,EAAe,aAAA,CAAA,KAC5CC,SAAS,wBACT7C,UAAU,IACV8C,gBAAgB5C,KAAKD,OAAO6C,gBAC5BC,UAAU,MAAA;EAEZ;AAAA,EAMQ,qBAAqB5C,IAAAA;AAC5B,UAAMhB,KAAqBgB,GAAQ6C,SAAAA;AACnC,QAAI1D,EAAgBH,GAAME,SAAAA,EAAY,QAAA;AACtC,UAAMyC,KAAqB5C,EAAwBC,EAAAA;AACnD,eAAI2C,GAAmB,QAAA;AAEvB,UAAMmB,KAAUjB,EAAgBC,EAAS,qBACnCF,KAAeC,EAAgBC,EAAS,WAAA,CAAA,GAExCiB,IAAK/D,GACT+C,YAAY,SAAA,EACZG,WAAW,IAAIP,KAAa,GAAGmB,EAAAA,EAC/BZ,WAAW,CAAA,GAAIP,KAAa,GAAGC,EAAAA,EAC/BO,aAAaC,EAAyBR,GAAapC,IAAI,CAAA,CAAA,EACvD6C;AAGF,WADArC,GAAQgD,SAASD,CAAAA,GAAAA;AAAAA,EAElB;AAAA;"}
@@ -0,0 +1,26 @@
1
+ import { PluginContext } from '../Plugin.js';
2
+ import { ImageAttrs } from './ImageUpload.js';
3
+ /**
4
+ * Inserts an image block. When the cursor is inside a table cell,
5
+ * the image is inserted as a child of the cell. Otherwise it is
6
+ * inserted after the current block at document root.
7
+ */
8
+ export declare function insertImage(context: PluginContext, attrs: Partial<ImageAttrs> & {
9
+ readonly src: string;
10
+ }): boolean;
11
+ /** Removes the currently selected image block (works for nested images too). */
12
+ export declare function removeImage(context: PluginContext): boolean;
13
+ /** Updates attributes on the currently selected image block. */
14
+ export declare function setImageAttr(context: PluginContext, attrs: Partial<ImageAttrs>): boolean;
15
+ /** Minimum image width in pixels (shared with NodeView). */
16
+ export declare const MIN_IMAGE_WIDTH = 50;
17
+ /**
18
+ * Resizes the currently selected image by a pixel delta, preserving aspect ratio.
19
+ * Returns false if no image is selected or the image has no width/height attributes.
20
+ */
21
+ export declare function resizeImageByDelta(context: PluginContext, delta: number, maxWidth: number): boolean;
22
+ /** Resets the currently selected image to its natural size by removing width/height. */
23
+ export declare function resetImageSize(context: PluginContext): boolean;
24
+ /** Registers all image commands on the plugin context. */
25
+ export declare function registerImageCommands(context: PluginContext): void;
26
+ //# sourceMappingURL=ImageCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageCommands.d.ts","sourceRoot":"","sources":["../../../src/plugins/image/ImageCommands.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnD,OAAO,CAqBT;AA6ED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CA6C3D;AAqBD,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAgBxF;AAED,4DAA4D;AAC5D,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACd,OAAO,CAmBT;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAmB9D;AAED,0DAA0D;AAC1D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAQlE"}
@@ -0,0 +1,6 @@
1
+ import { BlockId } from '../../model/TypeBrands.js';
2
+ import { NodeViewFactory } from '../../view/NodeView.js';
3
+ import { ImageKeymap, ImagePluginConfig, UploadState } from './ImageUpload.js';
4
+ /** Creates a NodeViewFactory for image blocks. */
5
+ export declare function createImageNodeViewFactory(config: ImagePluginConfig, uploadStates: Map<BlockId, UploadState>, resolvedKeymap?: Readonly<Record<keyof ImageKeymap, string | null>>): NodeViewFactory;
6
+ //# sourceMappingURL=ImageNodeView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageNodeView.d.ts","sourceRoot":"","sources":["../../../src/plugins/image/ImageNodeView.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,KAAK,EAAY,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA8DpF,kDAAkD;AAClD,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,iBAAiB,EACzB,YAAY,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,EACvC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,GACjE,eAAe,CAiTjB"}
@@ -0,0 +1,43 @@
1
+ import { EditorState } from '../../state/EditorState.js';
2
+ import { Transaction } from '../../state/Transaction.js';
3
+ import { Plugin, PluginContext } from '../Plugin.js';
4
+ import { ImagePluginConfig } from './ImageUpload.js';
5
+ declare module '../../model/AttrRegistry.js' {
6
+ interface NodeAttrRegistry {
7
+ image: {
8
+ src: string;
9
+ alt: string;
10
+ width?: number;
11
+ height?: number;
12
+ align: 'left' | 'center' | 'right';
13
+ };
14
+ }
15
+ }
16
+ export declare class ImagePlugin implements Plugin {
17
+ readonly id = "image";
18
+ readonly name = "Image";
19
+ readonly priority = 45;
20
+ private readonly config;
21
+ private readonly resolvedKeymap;
22
+ private readonly uploadStates;
23
+ private readonly blobUrls;
24
+ private context;
25
+ constructor(config?: Partial<ImagePluginConfig>);
26
+ init(context: PluginContext): void;
27
+ destroy(): void;
28
+ onStateChange(oldState: EditorState, newState: EditorState, _tr: Transaction): void;
29
+ private registerNodeSpec;
30
+ private registerNodeView;
31
+ private registerResizeCommands;
32
+ private registerResizeKeymaps;
33
+ private registerFileHandler;
34
+ private handleFileInsert;
35
+ private uploadFile;
36
+ private registerToolbarItem;
37
+ private renderImagePopup;
38
+ private isAcceptedType;
39
+ private isImageSelected;
40
+ private announceImageSelection;
41
+ private announceCurrentSize;
42
+ }
43
+ //# sourceMappingURL=ImagePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImagePlugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/image/ImagePlugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAS1D,OAAO,EAKN,KAAK,iBAAiB,EAEtB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,QAAQ,6BAA6B,CAAC;IAC5C,UAAU,gBAAgB;QACzB,KAAK,EAAE;YACN,GAAG,EAAE,MAAM,CAAC;YACZ,GAAG,EAAE,MAAM,CAAC;YACZ,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;SACnC,CAAC;KACF;CACD;AAID,qBAAa,WAAY,YAAW,MAAM;IACzC,QAAQ,CAAC,EAAE,WAAW;IACtB,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqD;IACpF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,OAAO,CAA8B;gBAEjC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAK/C,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAYlC,OAAO,IAAI,IAAI;IASf,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI;IAmBnF,OAAO,CAAC,gBAAgB;IA4GxB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,sBAAsB;IAoC9B,OAAO,CAAC,qBAAqB;IAsB7B,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,gBAAgB;YA8BV,UAAU;IAwCxB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,gBAAgB;IA0GxB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,mBAAmB;CAc3B"}
@@ -0,0 +1,48 @@
1
+ import { ServiceKey } from '../Plugin.js';
2
+ export interface ImageAttrs {
3
+ readonly src: string;
4
+ readonly alt: string;
5
+ readonly width?: number;
6
+ readonly height?: number;
7
+ readonly align: 'left' | 'center' | 'right';
8
+ }
9
+ export interface ImageUploadResult {
10
+ readonly url: string;
11
+ readonly width?: number;
12
+ readonly height?: number;
13
+ }
14
+ export interface ImageUploadService {
15
+ upload(file: File): Promise<ImageUploadResult>;
16
+ }
17
+ export declare const IMAGE_UPLOAD_SERVICE: ServiceKey<ImageUploadService>;
18
+ export type UploadState = 'idle' | 'uploading' | 'complete' | 'error';
19
+ /**
20
+ * Configurable keyboard bindings for image resize actions.
21
+ * Omit a slot to use the default; set to `null` to disable the binding.
22
+ *
23
+ * Key descriptor format: `'Mod-Shift-ArrowRight'`, etc.
24
+ * `Mod` resolves to Cmd on macOS, Ctrl on Windows/Linux.
25
+ */
26
+ export interface ImageKeymap {
27
+ readonly growWidth?: string | null;
28
+ readonly shrinkWidth?: string | null;
29
+ readonly growWidthLarge?: string | null;
30
+ readonly shrinkWidthLarge?: string | null;
31
+ readonly resetSize?: string | null;
32
+ }
33
+ export declare const DEFAULT_IMAGE_KEYMAP: Readonly<Record<keyof ImageKeymap, string>>;
34
+ export interface ImagePluginConfig {
35
+ readonly maxWidth: number;
36
+ readonly maxFileSize: number;
37
+ readonly acceptedTypes: readonly string[];
38
+ readonly resizable: boolean;
39
+ readonly separatorAfter?: boolean;
40
+ /** Pixels to grow/shrink per small resize step. @default 10 */
41
+ readonly resizeStep?: number;
42
+ /** Pixels to grow/shrink per large resize step. @default 50 */
43
+ readonly resizeStepLarge?: number;
44
+ /** Customize keyboard bindings for image resize actions. */
45
+ readonly keymap?: ImageKeymap;
46
+ }
47
+ export declare const DEFAULT_IMAGE_CONFIG: ImagePluginConfig;
48
+ //# sourceMappingURL=ImageUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageUpload.d.ts","sourceRoot":"","sources":["../../../src/plugins/image/ImageUpload.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI1C,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CAC5C;AAID,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/C;AAED,eAAO,MAAM,oBAAoB,gCAAqD,CAAC;AAEvF,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAItE;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,WAAW,EAAE,MAAM,CAAC,CAM5E,CAAC;AAIF,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED,eAAO,MAAM,oBAAoB,EAAE,iBAOlC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ImagePlugin } from './ImagePlugin.js';
2
+ export { DEFAULT_IMAGE_KEYMAP, IMAGE_UPLOAD_SERVICE, type ImageAttrs, type ImageKeymap, type ImagePluginConfig, type ImageUploadResult, type ImageUploadService, } from './ImageUpload.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/image/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACvB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,381 @@
1
+ import { e as t } from "../chunks/HTMLUtils-0wvCRvJc.mjs";
2
+ import { c as e } from "../chunks/NodeSpec-NZVvcCuH.mjs";
3
+ import { i as n, c as i, b as r } from "../chunks/Selection-D5ZjXoB7.mjs";
4
+ import { f as o } from "../chunks/ToolbarItem-ugDymuXi.mjs";
5
+ import { a, c as s, u as c } from "../chunks/Document-CI3iCwEP.mjs";
6
+ import { n as l } from "../chunks/TypeBrands-C09nE2qO.mjs";
7
+ import { S as d } from "../chunks/Plugin-B9OZ6Y4c.mjs";
8
+ function g(t2, e2) {
9
+ const i2 = t2.getState(), o2 = i2.selection, d2 = { src: e2.src, alt: e2.alt ?? "", align: e2.align ?? "center", ...void 0 !== e2.width ? { width: e2.width } : {}, ...void 0 !== e2.height ? { height: e2.height } : {} }, g2 = n(o2) ? o2.nodeId : o2.anchor.blockId, m2 = (function(t3, e3) {
10
+ const n2 = t3.getBlock(e3);
11
+ if ("table_cell" === n2?.type) return e3;
12
+ const i3 = t3.getNodePath(e3);
13
+ if (i3) for (const e4 of i3) {
14
+ const n3 = t3.getBlock(e4);
15
+ if ("table_cell" === n3?.type) return e4;
16
+ }
17
+ })(i2, g2);
18
+ return m2 ? (function(t3, e3, n2, i3) {
19
+ const o3 = t3.getNodePath(n2);
20
+ if (!o3) return false;
21
+ const d3 = s(l("image"), [], void 0, i3), g3 = t3.getBlock(n2);
22
+ if (!g3) return false;
23
+ const m3 = t3.transaction("command"), u2 = c(g3);
24
+ for (let t4 = u2.length - 1; t4 >= 0; t4--) {
25
+ const e4 = g3.children.findIndex((e5) => a(e5) && e5.id === u2[t4]?.id);
26
+ -1 !== e4 && m3.removeNode(o3, e4);
27
+ }
28
+ return m3.insertNode(o3, 0, d3), m3.setSelection(r(d3.id, [...o3, d3.id])), e3.dispatch(m3.build()), true;
29
+ })(i2, t2, m2, d2) : (function(t3, e3, n2, i3) {
30
+ const o3 = t3.doc.children.findIndex((t4) => t4.id === n2);
31
+ if (-1 === o3) return false;
32
+ const a2 = s(l("image"), [], void 0, i3), c2 = s(l("paragraph")), d3 = t3.transaction("command").insertNode([], o3 + 1, a2).insertNode([], o3 + 2, c2).setSelection(r(a2.id, [])).build();
33
+ return e3.dispatch(d3), true;
34
+ })(i2, t2, g2, d2);
35
+ }
36
+ function m(t2, e2, i2) {
37
+ const r2 = t2.getState(), o2 = r2.selection;
38
+ if (!n(o2)) return false;
39
+ const a2 = r2.getBlock(o2.nodeId);
40
+ if (!a2 || "image" !== a2.type) return false;
41
+ const s2 = a2.attrs?.width, c2 = a2.attrs?.height;
42
+ if (void 0 === s2 || void 0 === c2) return false;
43
+ const l2 = c2 > 0 ? s2 / c2 : 1, d2 = Math.max(50, Math.min(i2, s2 + e2)), g2 = Math.round(d2 / l2);
44
+ return (d2 !== s2 || g2 !== c2) && (function(t3, e3) {
45
+ const i3 = t3.getState(), r3 = i3.selection;
46
+ if (!n(r3)) return false;
47
+ const o3 = i3.getBlock(r3.nodeId);
48
+ if (!o3 || "image" !== o3.type) return false;
49
+ const a3 = i3.getNodePath(r3.nodeId);
50
+ if (!a3) return false;
51
+ const s3 = { ...o3.attrs ?? {}, ...e3 }, c3 = i3.transaction("command").setNodeAttr(a3, s3).build();
52
+ return t3.dispatch(c3), true;
53
+ })(t2, { width: d2, height: g2 });
54
+ }
55
+ const u = { left: "notectl-image--left", center: "notectl-image--center", right: "notectl-image--right" }, h = { nw: "nwse-resize", ne: "nesw-resize", sw: "nesw-resize", se: "nwse-resize" }, p = { nw: -1, ne: 1, sw: -1, se: 1 }, f = { nw: "Resize top-left", ne: "Resize top-right", sw: "Resize bottom-left", se: "Resize bottom-right" };
56
+ let b = null;
57
+ function v() {
58
+ b && (b.remove(), b = null);
59
+ }
60
+ function x(t2, e2, n2) {
61
+ return (i2, r2, a2) => {
62
+ const s2 = document.createElement("figure");
63
+ s2.className = "notectl-image", s2.setAttribute("data-block-id", i2.id), s2.setAttribute("data-void", "true"), s2.setAttribute("data-selectable", "true");
64
+ const c2 = document.createElement("div");
65
+ c2.className = "notectl-image__container";
66
+ const l2 = document.createElement("img");
67
+ l2.className = "notectl-image__img", l2.draggable = false;
68
+ const d2 = document.createElement("div");
69
+ d2.className = "notectl-image__overlay", c2.appendChild(l2), c2.appendChild(d2), s2.appendChild(c2);
70
+ let g2 = i2.id, m2 = null;
71
+ function x2(t3) {
72
+ const n3 = t3.attrs?.src ?? "", i3 = t3.attrs?.alt ?? "", r3 = t3.attrs?.width, o2 = t3.attrs?.height, a3 = t3.attrs?.align ?? "center";
73
+ l2.src !== n3 && (l2.src = n3), l2.alt = i3, l2.style.width = void 0 !== r3 ? `${r3}px` : "", l2.style.height = void 0 !== o2 ? `${o2}px` : "";
74
+ for (const t4 of Object.values(u)) s2.classList.remove(t4);
75
+ const c3 = u[a3];
76
+ c3 && s2.classList.add(c3);
77
+ const g3 = e2.get(t3.id) ?? "idle", m3 = "uploading" === g3;
78
+ d2.classList.toggle("notectl-image__overlay--uploading", m3), d2.classList.toggle("notectl-image__overlay--error", "error" === g3), d2.textContent = m3 ? "Uploading..." : "error" === g3 ? "Upload failed" : "", m3 ? s2.setAttribute("aria-busy", "true") : s2.removeAttribute("aria-busy"), (function(t4) {
79
+ const e3 = t4.attrs?.alt ?? "", n4 = t4.attrs?.width, i4 = t4.attrs?.height, r4 = [];
80
+ e3 ? r4.push(e3) : r4.push("Image"), void 0 !== n4 && void 0 !== i4 && r4.push(`${n4} by ${i4} pixels`), s2.setAttribute("aria-label", r4.join(", "));
81
+ })(t3);
82
+ }
83
+ x2(i2);
84
+ let y2 = false;
85
+ function w2(e3, n3, i3, o2) {
86
+ let c3 = 0, d3 = 0, g3 = 1;
87
+ const m3 = (e4) => {
88
+ const i4 = e4.clientX - c3, r3 = (a3 = d3 + i4 * p[n3], Math.max(50, Math.min(t2.maxWidth, a3)));
89
+ var a3;
90
+ const s3 = Math.round(r3 / g3);
91
+ l2.style.width = `${r3}px`, l2.style.height = `${s3}px`, o2.textContent = `${Math.round(r3)} × ${s3}`;
92
+ }, u2 = (t3) => {
93
+ document.removeEventListener("pointermove", m3), document.removeEventListener("pointerup", u2), s2.classList.remove("notectl-image--resizing"), o2.classList.remove("notectl-image__size-indicator--visible"), v();
94
+ const e4 = Math.round(l2.getBoundingClientRect().width), n4 = Math.round(e4 / g3);
95
+ !(function(t4, e5, n5) {
96
+ const i4 = r2(), o3 = i4.getBlock(t4);
97
+ if (!o3) return;
98
+ const s3 = o3.attrs?.width, c4 = o3.attrs?.height;
99
+ if (s3 === e5 && c4 === n5) return;
100
+ const l3 = i4.getNodePath(t4);
101
+ if (!l3) return;
102
+ const d4 = { ...o3.attrs ?? {}, width: e5, height: n5 }, g4 = i4.transaction("command").setNodeAttr(l3, d4).build();
103
+ a2(g4);
104
+ })(i3, e4, n4), t3.target?.releasePointerCapture?.(t3.pointerId);
105
+ };
106
+ e3.addEventListener("pointerdown", (t3) => {
107
+ t3.preventDefault(), t3.stopPropagation(), c3 = t3.clientX, d3 = l2.getBoundingClientRect().width;
108
+ const e4 = l2.getBoundingClientRect().height;
109
+ var i4;
110
+ g3 = e4 > 0 ? d3 / e4 : 1, s2.classList.add("notectl-image--resizing"), o2.textContent = `${Math.round(d3)} × ${Math.round(e4)}`, o2.classList.add("notectl-image__size-indicator--visible"), i4 = h[n3], v(), b = document.createElement("style"), b.textContent = `*{cursor:${i4}!important;user-select:none!important}`, document.head.appendChild(b), t3.target.setPointerCapture(t3.pointerId), document.addEventListener("pointermove", m3), document.addEventListener("pointerup", u2);
111
+ });
112
+ }
113
+ function z2() {
114
+ v(), m2 && (m2.remove(), m2 = null);
115
+ }
116
+ return l2.addEventListener("load", () => {
117
+ if (y2) return;
118
+ const e3 = r2(), n3 = e3.getBlock(g2);
119
+ if (!n3 || "image" !== n3.type) return;
120
+ const i3 = void 0 !== n3.attrs?.width, o2 = void 0 !== n3.attrs?.height;
121
+ if (i3 && o2) return void (y2 = true);
122
+ const s3 = l2.naturalWidth, c3 = l2.naturalHeight;
123
+ if (0 === s3 || 0 === c3) return;
124
+ const d3 = Math.min(s3, t2.maxWidth), m3 = s3 > 0 ? c3 / s3 : 1, u2 = Math.round(d3 * m3), h2 = e3.getNodePath(g2);
125
+ if (!h2) return;
126
+ const p2 = { ...n3.attrs ?? {}, width: d3, height: u2 }, f2 = e3.transaction("command").setNodeAttr(h2, p2).build();
127
+ a2(f2), y2 = true;
128
+ }), { dom: s2, contentDOM: null, update: (t3) => "image" === t3.type && (g2 = t3.id, s2.setAttribute("data-block-id", t3.id), x2(t3), true), selectNode() {
129
+ s2.classList.add("notectl-image--selected"), (function(e3) {
130
+ if (!t2.resizable || m2) return;
131
+ m2 = document.createElement("div"), m2.className = "notectl-image__resize-overlay";
132
+ const i3 = document.createElement("div");
133
+ i3.className = "notectl-image__size-indicator", m2.appendChild(i3);
134
+ const r3 = ["nw", "ne", "sw", "se"];
135
+ for (const t3 of r3) {
136
+ const n3 = document.createElement("div");
137
+ n3.className = `notectl-image__resize-handle notectl-image__resize-handle--${t3}`, n3.setAttribute("role", "separator"), n3.setAttribute("aria-label", f[t3]), w2(n3, t3, e3, i3), m2.appendChild(n3);
138
+ }
139
+ if (n2) {
140
+ const t3 = (function(t4) {
141
+ const e4 = t4.shrinkWidth ?? null, n3 = t4.growWidth ?? null;
142
+ return e4 && n3 ? `${o(e4)} / ${o(n3)} to resize` : "";
143
+ })(n2);
144
+ if (t3) {
145
+ const e4 = document.createElement("div");
146
+ e4.className = "notectl-image__keyboard-hint", e4.setAttribute("aria-hidden", "true"), e4.textContent = t3, m2.appendChild(e4);
147
+ }
148
+ }
149
+ c2.appendChild(m2);
150
+ })(g2);
151
+ }, deselectNode() {
152
+ s2.classList.remove("notectl-image--selected"), z2();
153
+ }, destroy() {
154
+ z2();
155
+ } };
156
+ };
157
+ }
158
+ const y = new d("image:upload"), w = { growWidth: "Mod-Shift-ArrowRight", shrinkWidth: "Mod-Shift-ArrowLeft", growWidthLarge: "Mod-Shift-Alt-ArrowRight", shrinkWidthLarge: "Mod-Shift-Alt-ArrowLeft", resetSize: "Mod-Shift-0" }, z = { maxWidth: 800, maxFileSize: 10485760, acceptedTypes: ["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"], resizable: true, resizeStep: 10, resizeStepLarge: 50 };
159
+ class _ {
160
+ id = "image";
161
+ name = "Image";
162
+ priority = 45;
163
+ config;
164
+ resolvedKeymap;
165
+ uploadStates = /* @__PURE__ */ new Map();
166
+ blobUrls = /* @__PURE__ */ new Set();
167
+ context = null;
168
+ constructor(t2) {
169
+ this.config = { ...z, ...t2 }, this.resolvedKeymap = { ...w, ...t2?.keymap };
170
+ }
171
+ init(t2) {
172
+ t2.registerStyleSheet("\n/* Image */\n.notectl-image {\n margin: 8px 0;\n user-select: none;\n line-height: 0;\n}\n\n.notectl-image--center {\n text-align: center;\n}\n\n.notectl-image--left {\n text-align: left;\n}\n\n.notectl-image--right {\n text-align: right;\n}\n\n.notectl-image__container {\n display: inline-block;\n position: relative;\n line-height: 0;\n}\n\n.notectl-image__img {\n display: block;\n max-width: 100%;\n border-radius: 4px;\n}\n\n.notectl-image--selected .notectl-image__container {\n outline: 2px solid var(--notectl-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n/* Alignment toolbar (appears on image selection) */\n.notectl-image__align-toolbar {\n display: flex;\n gap: 2px;\n position: absolute;\n top: -36px;\n left: 50%;\n transform: translateX(-50%);\n background: var(--notectl-surface-overlay);\n border: 1px solid var(--notectl-border);\n border-radius: 6px;\n padding: 2px;\n box-shadow: 0 2px 8px var(--notectl-shadow);\n z-index: 10;\n}\n\n.notectl-image__align-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: var(--notectl-fg);\n}\n\n.notectl-image__align-btn:hover {\n background: var(--notectl-hover-bg);\n}\n\n.notectl-image__align-btn--active {\n background: var(--notectl-active-bg);\n color: var(--notectl-primary-fg);\n}\n\n.notectl-image__align-btn svg {\n width: 16px;\n height: 16px;\n fill: currentColor;\n}\n\n/* Upload overlay */\n.notectl-image__overlay {\n position: absolute;\n inset: 0;\n display: none;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.8);\n border-radius: 4px;\n font-size: 13px;\n color: var(--notectl-fg-muted);\n}\n\n.notectl-image__overlay--uploading {\n display: flex;\n}\n\n.notectl-image__overlay--error {\n display: flex;\n background: var(--notectl-danger-muted);\n color: var(--notectl-danger);\n}\n\n/* Resize overlay + handles */\n.notectl-image__resize-overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n.notectl-image__resize-handle {\n position: absolute;\n width: 10px;\n height: 10px;\n background: var(--notectl-bg);\n border: 2px solid var(--notectl-primary);\n border-radius: 50%;\n pointer-events: all;\n z-index: 2;\n transition: transform 0.1s, background 0.1s;\n}\n\n.notectl-image__resize-handle:hover {\n background: var(--notectl-primary);\n transform: scale(1.3);\n}\n\n.notectl-image--resizing .notectl-image__resize-handle {\n background: var(--notectl-primary);\n}\n\n.notectl-image__resize-handle--nw {\n top: -5px;\n left: -5px;\n cursor: nwse-resize;\n}\n\n.notectl-image__resize-handle--ne {\n top: -5px;\n right: -5px;\n cursor: nesw-resize;\n}\n\n.notectl-image__resize-handle--sw {\n bottom: -5px;\n left: -5px;\n cursor: nesw-resize;\n}\n\n.notectl-image__resize-handle--se {\n bottom: -5px;\n right: -5px;\n cursor: nwse-resize;\n}\n\n/* Size indicator tooltip */\n.notectl-image__size-indicator {\n position: absolute;\n bottom: -28px;\n left: 50%;\n transform: translateX(-50%);\n padding: 2px 8px;\n background: var(--notectl-tooltip-bg);\n color: var(--notectl-tooltip-fg);\n font-size: 11px;\n font-weight: 500;\n border-radius: 4px;\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n z-index: 3;\n}\n\n.notectl-image__size-indicator--visible {\n opacity: 1;\n}\n\n/* Keyboard resize hint (shown on selection, hidden during drag) */\n.notectl-image__keyboard-hint {\n display: none;\n position: absolute;\n bottom: -28px;\n left: 50%;\n transform: translateX(-50%);\n padding: 2px 8px;\n font-size: 11px;\n color: var(--notectl-fg-muted);\n white-space: nowrap;\n pointer-events: none;\n z-index: 3;\n}\n\n.notectl-image--selected .notectl-image__keyboard-hint {\n display: block;\n}\n\n.notectl-image--resizing .notectl-image__keyboard-hint {\n display: none;\n}\n\n/* Resizing feedback */\n.notectl-image--resizing .notectl-image__img {\n opacity: 0.85;\n}\n"), this.context = t2, this.registerNodeSpec(t2), this.registerNodeView(t2), (function(t3) {
173
+ t3.registerCommand("insertImage", () => g(t3, { src: "" })), t3.registerCommand("removeImage", () => (function(t4) {
174
+ const e2 = t4.getState(), r2 = e2.selection;
175
+ if (!n(r2)) return false;
176
+ const o2 = e2.getBlock(r2.nodeId);
177
+ if (!o2 || "image" !== o2.type) return false;
178
+ const s2 = e2.getNodePath(r2.nodeId);
179
+ if (!s2 || 0 === s2.length) return false;
180
+ const c2 = s2.slice(0, -1);
181
+ let l2;
182
+ if (0 === c2.length) l2 = e2.doc;
183
+ else {
184
+ const t5 = c2[c2.length - 1];
185
+ l2 = t5 ? e2.getBlock(t5) : void 0;
186
+ }
187
+ if (!l2) return false;
188
+ const d2 = l2.children.findIndex((t5) => a(t5) && t5.id === r2.nodeId);
189
+ if (-1 === d2) return false;
190
+ const g2 = (function(t5, e3, n2) {
191
+ if (0 === e3.length) {
192
+ const e4 = t5.doc.children, i2 = e4.findIndex((t6) => t6.id === n2), r3 = e4[i2 + 1] ?? e4[i2 - 1];
193
+ return r3?.id;
194
+ }
195
+ return e3[e3.length - 1];
196
+ })(e2, c2, r2.nodeId);
197
+ if (!g2) return false;
198
+ const m2 = e2.transaction("command").removeNode(c2, d2).setSelection(i(g2, 0)).build();
199
+ return t4.dispatch(m2), true;
200
+ })(t3));
201
+ })(t2), this.registerResizeCommands(t2), this.registerResizeKeymaps(t2), this.registerFileHandler(t2), this.registerToolbarItem(t2);
202
+ }
203
+ destroy() {
204
+ for (const t2 of this.blobUrls) URL.revokeObjectURL(t2);
205
+ this.blobUrls.clear(), this.uploadStates.clear(), this.context = null;
206
+ }
207
+ onStateChange(t2, e2, n2) {
208
+ if (!this.context) return;
209
+ for (const t3 of this.uploadStates.keys()) e2.getBlock(t3) || this.uploadStates.delete(t3);
210
+ const i2 = this.isImageSelected(t2), r2 = this.isImageSelected(e2);
211
+ !i2 && r2 && this.announceImageSelection(e2);
212
+ }
213
+ registerNodeSpec(n2) {
214
+ n2.registerNodeSpec({ type: "image", group: "block", isVoid: true, selectable: true, attrs: { src: { default: "" }, alt: { default: "" }, align: { default: "center" } }, toDOM(t2) {
215
+ const n3 = e("figure", t2.id);
216
+ n3.className = "notectl-image", n3.setAttribute("data-void", "true"), n3.setAttribute("data-selectable", "true");
217
+ const i2 = document.createElement("div");
218
+ i2.className = "notectl-image__container";
219
+ const r2 = t2.attrs?.alt ?? "", o2 = t2.attrs?.width, a2 = t2.attrs?.height, s2 = document.createElement("img");
220
+ s2.className = "notectl-image__img", s2.src = t2.attrs?.src ?? "", s2.alt = r2, s2.draggable = false, void 0 !== o2 && (s2.style.width = `${o2}px`), void 0 !== a2 && (s2.style.height = `${a2}px`);
221
+ const c2 = { left: "notectl-image--left", center: "notectl-image--center", right: "notectl-image--right" }[t2.attrs?.align ?? "center"];
222
+ c2 && n3.classList.add(c2);
223
+ const l2 = [r2 || "Image"];
224
+ return void 0 !== o2 && void 0 !== a2 && l2.push(`${o2} by ${a2} pixels`), n3.setAttribute("aria-label", l2.join(", ")), i2.appendChild(s2), n3.appendChild(i2), n3;
225
+ }, toHTML(e2) {
226
+ const n3 = t(e2.attrs?.src ?? ""), i2 = t(e2.attrs?.alt ?? ""), r2 = e2.attrs?.width, o2 = e2.attrs?.height, a2 = e2.attrs?.align ?? "center", s2 = (void 0 !== r2 ? ` width="${r2}"` : "") + (void 0 !== o2 ? ` height="${o2}"` : "");
227
+ return `<figure class="notectl-image notectl-image--${t(a2)}"><img src="${n3}" alt="${i2}"${s2}></figure>`;
228
+ }, parseHTML: [{ tag: "figure", getAttrs(t2) {
229
+ const e2 = t2.querySelector("img");
230
+ if (!e2) return false;
231
+ const n3 = { src: e2.getAttribute("src") ?? "", alt: e2.getAttribute("alt") ?? "", align: "center" }, i2 = e2.getAttribute("width"), r2 = e2.getAttribute("height");
232
+ return i2 && (n3.width = Number.parseInt(i2, 10)), r2 && (n3.height = Number.parseInt(r2, 10)), t2.classList.contains("notectl-image--left") && (n3.align = "left"), t2.classList.contains("notectl-image--right") && (n3.align = "right"), n3;
233
+ } }, { tag: "img", getAttrs(t2) {
234
+ const e2 = { src: t2.getAttribute("src") ?? "", alt: t2.getAttribute("alt") ?? "", align: "center" }, n3 = t2.getAttribute("width"), i2 = t2.getAttribute("height");
235
+ return n3 && (e2.width = Number.parseInt(n3, 10)), i2 && (e2.height = Number.parseInt(i2, 10)), e2;
236
+ } }], sanitize: { tags: ["figure", "img"], attrs: ["src", "alt", "width", "height", "class"] } });
237
+ }
238
+ registerNodeView(t2) {
239
+ t2.registerNodeView("image", x(this.config, this.uploadStates, this.resolvedKeymap));
240
+ }
241
+ registerResizeCommands(t2) {
242
+ const e2 = this.config.resizeStep ?? 10, i2 = this.config.resizeStepLarge ?? 50, r2 = this.config.maxWidth;
243
+ t2.registerCommand("resizeImageGrow", () => {
244
+ const n2 = m(t2, e2, r2);
245
+ return n2 && this.announceCurrentSize(t2), n2;
246
+ }), t2.registerCommand("resizeImageShrink", () => {
247
+ const n2 = m(t2, -e2, r2);
248
+ return n2 && this.announceCurrentSize(t2), n2;
249
+ }), t2.registerCommand("resizeImageGrowLarge", () => {
250
+ const e3 = m(t2, i2, r2);
251
+ return e3 && this.announceCurrentSize(t2), e3;
252
+ }), t2.registerCommand("resizeImageShrinkLarge", () => {
253
+ const e3 = m(t2, -i2, r2);
254
+ return e3 && this.announceCurrentSize(t2), e3;
255
+ }), t2.registerCommand("resetImageSize", () => {
256
+ const e3 = (function(t3) {
257
+ const e4 = t3.getState(), i3 = e4.selection;
258
+ if (!n(i3)) return false;
259
+ const r3 = e4.getBlock(i3.nodeId);
260
+ if (!r3 || "image" !== r3.type) return false;
261
+ const o2 = e4.getNodePath(i3.nodeId);
262
+ if (!o2) return false;
263
+ const a2 = { ...r3.attrs ?? {} }, s2 = Object.fromEntries(Object.entries(a2).filter(([t4]) => "width" !== t4 && "height" !== t4)), c2 = e4.transaction("command").setNodeAttr(o2, s2).build();
264
+ return t3.dispatch(c2), true;
265
+ })(t2);
266
+ return e3 && t2.announce("Image reset to natural size."), e3;
267
+ });
268
+ }
269
+ registerResizeKeymaps(t2) {
270
+ const e2 = {}, n2 = { growWidth: "resizeImageGrow", shrinkWidth: "resizeImageShrink", growWidthLarge: "resizeImageGrowLarge", shrinkWidthLarge: "resizeImageShrinkLarge", resetSize: "resetImageSize" };
271
+ for (const [i2, r2] of Object.entries(n2)) {
272
+ const n3 = this.resolvedKeymap[i2] ?? null;
273
+ n3 && (e2[n3] = () => t2.executeCommand(r2));
274
+ }
275
+ Object.keys(e2).length > 0 && t2.registerKeymap(e2);
276
+ }
277
+ registerFileHandler(t2) {
278
+ t2.registerFileHandler("image/*", async (e2, n2) => {
279
+ const i2 = e2.filter((t3) => this.isAcceptedType(t3.type));
280
+ if (0 === i2.length) return false;
281
+ for (const e3 of i2) this.handleFileInsert(t2, e3);
282
+ return true;
283
+ });
284
+ }
285
+ handleFileInsert(t2, e2) {
286
+ if (e2.size > this.config.maxFileSize) return;
287
+ const i2 = URL.createObjectURL(e2);
288
+ if (this.blobUrls.add(i2), !g(t2, { src: i2 })) return URL.revokeObjectURL(i2), void this.blobUrls.delete(i2);
289
+ const r2 = t2.getState().selection;
290
+ if (!n(r2)) return;
291
+ const o2 = r2.nodeId;
292
+ this.uploadStates.set(o2, "uploading"), t2.getService(y) ? this.uploadFile(t2, e2, o2, i2) : this.uploadStates.set(o2, "complete");
293
+ }
294
+ async uploadFile(t2, e2, n2, i2) {
295
+ const r2 = t2.getService(y);
296
+ if (r2) try {
297
+ const o2 = await r2.upload(e2);
298
+ this.uploadStates.set(n2, "complete");
299
+ const a2 = t2.getState(), s2 = a2.getBlock(n2);
300
+ if (!s2) return;
301
+ const c2 = a2.getNodePath(n2);
302
+ if (!c2) return;
303
+ const l2 = { ...s2.attrs ?? {}, src: o2.url, ...void 0 !== o2.width ? { width: o2.width } : {}, ...void 0 !== o2.height ? { height: o2.height } : {} }, d2 = a2.transaction("command").setNodeAttr(c2, l2).build();
304
+ t2.dispatch(d2), URL.revokeObjectURL(i2), this.blobUrls.delete(i2);
305
+ } catch {
306
+ this.uploadStates.set(n2, "error"), t2.announce("Image upload failed.");
307
+ }
308
+ }
309
+ registerToolbarItem(t2) {
310
+ t2.registerToolbarItem({ id: "image", group: "insert", icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>', label: "Insert Image", tooltip: "Insert Image", command: "insertImage", priority: 50, popupType: "custom", separatorAfter: this.config.separatorAfter, renderPopup: (t3, e2, n2) => {
311
+ this.renderImagePopup(t3, e2, n2);
312
+ } });
313
+ }
314
+ renderImagePopup(t2, e2, n2) {
315
+ t2.style.padding = "8px", t2.style.minWidth = "240px";
316
+ const i2 = document.createElement("input");
317
+ i2.type = "file", i2.accept = this.config.acceptedTypes.join(","), i2.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;opacity:0;";
318
+ const r2 = document.createElement("button");
319
+ r2.type = "button", r2.textContent = "Upload from computer", r2.setAttribute("aria-label", "Upload image from computer"), r2.style.cssText = "display:block;width:100%;padding:8px 12px;cursor:pointer;text-align:center;box-sizing:border-box;border:1px solid var(--notectl-border);border-radius:4px;background:var(--notectl-surface-raised);color:var(--notectl-fg);", r2.addEventListener("mousedown", (t3) => {
320
+ t3.preventDefault(), t3.stopPropagation(), i2.click();
321
+ }), i2.addEventListener("change", () => {
322
+ const t3 = i2.files?.[0];
323
+ t3 && (this.handleFileInsert(e2, t3), n2(), e2.getContainer().focus());
324
+ }), t2.appendChild(i2), t2.appendChild(r2);
325
+ const o2 = document.createElement("div");
326
+ o2.style.cssText = "display:flex;align-items:center;margin:8px 0;color:var(--notectl-fg-muted);font-size:12px;";
327
+ const a2 = document.createElement("span");
328
+ a2.style.cssText = "flex:1;height:1px;background:var(--notectl-border);";
329
+ const s2 = document.createElement("span");
330
+ s2.textContent = "or", s2.style.cssText = "padding:0 8px;";
331
+ const c2 = document.createElement("span");
332
+ c2.style.cssText = "flex:1;height:1px;background:var(--notectl-border);", o2.appendChild(a2), o2.appendChild(s2), o2.appendChild(c2), t2.appendChild(o2);
333
+ const l2 = document.createElement("input");
334
+ l2.type = "url", l2.placeholder = "https://...", l2.setAttribute("aria-label", "Image URL"), l2.style.cssText = "width:100%;padding:6px 8px;box-sizing:border-box;border:1px solid var(--notectl-border);border-radius:4px;background:var(--notectl-bg);color:var(--notectl-fg);";
335
+ const d2 = document.createElement("button");
336
+ d2.type = "button", d2.textContent = "Insert", d2.setAttribute("aria-label", "Insert image"), d2.style.cssText = "width:100%;padding:8px 12px;margin-top:4px;cursor:pointer;border:1px solid var(--notectl-border);border-radius:4px;background:var(--notectl-surface-raised);color:var(--notectl-fg);";
337
+ const m2 = () => {
338
+ const t3 = l2.value.trim();
339
+ t3 && (g(e2, { src: t3 }), n2(), e2.getContainer().focus());
340
+ };
341
+ d2.addEventListener("mousedown", (t3) => {
342
+ t3.preventDefault(), t3.stopPropagation(), m2();
343
+ }), l2.addEventListener("keydown", (t3) => {
344
+ "Enter" === t3.key && (t3.preventDefault(), m2());
345
+ }), t2.appendChild(l2), t2.appendChild(d2), requestAnimationFrame(() => l2.focus());
346
+ }
347
+ isAcceptedType(t2) {
348
+ return this.config.acceptedTypes.some((e2) => e2 === t2 || e2.endsWith("/*") && t2.startsWith(e2.slice(0, -1)));
349
+ }
350
+ isImageSelected(t2) {
351
+ const e2 = t2.selection;
352
+ if (!n(e2)) return false;
353
+ const i2 = t2.getBlock(e2.nodeId);
354
+ return "image" === i2?.type;
355
+ }
356
+ announceImageSelection(t2) {
357
+ if (!this.context) return;
358
+ const e2 = t2.selection;
359
+ if (!n(e2)) return;
360
+ const i2 = t2.getBlock(e2.nodeId);
361
+ if (!i2 || "image" !== i2.type) return;
362
+ const r2 = i2.attrs?.alt ?? "", a2 = i2.attrs?.width, s2 = i2.attrs?.height, c2 = ["Image selected."];
363
+ r2 && c2.push(`Alt text: ${r2}.`), void 0 !== a2 && void 0 !== s2 && c2.push(`Size: ${a2} by ${s2} pixels.`);
364
+ const l2 = this.resolvedKeymap.shrinkWidth ?? null, d2 = this.resolvedKeymap.growWidth ?? null;
365
+ l2 && d2 && c2.push(`${o(l2)} / ${o(d2)} to resize.`), this.context.announce(c2.join(" "));
366
+ }
367
+ announceCurrentSize(t2) {
368
+ const e2 = t2.getState(), i2 = e2.selection;
369
+ if (!n(i2)) return;
370
+ const r2 = e2.getBlock(i2.nodeId);
371
+ if (!r2 || "image" !== r2.type) return;
372
+ const o2 = r2.attrs?.width, a2 = r2.attrs?.height;
373
+ void 0 !== o2 && void 0 !== a2 && t2.announce(`Image resized to ${o2} by ${a2} pixels.`);
374
+ }
375
+ }
376
+ export {
377
+ w as DEFAULT_IMAGE_KEYMAP,
378
+ y as IMAGE_UPLOAD_SERVICE,
379
+ _ as ImagePlugin
380
+ };
381
+ //# sourceMappingURL=image.mjs.map