@notectl/core 2.2.6 → 2.3.3
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.
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +129 -0
- package/dist/chunks/{AlignmentPlugin-DeD4C4EP.mjs → AlignmentPlugin-C9Rr_R-g.mjs} +2 -2
- package/dist/chunks/{AlignmentPlugin-DeD4C4EP.mjs.map → AlignmentPlugin-C9Rr_R-g.mjs.map} +1 -1
- package/dist/chunks/{AttrRegistry-DcDVyzLm.mjs → AttrRegistry-B3zB02z9.mjs} +2 -2
- package/dist/chunks/AttrRegistry-B3zB02z9.mjs.map +1 -0
- package/dist/chunks/AttributedMarkCommands-BCvmqeFZ.mjs +2 -0
- package/dist/chunks/{AttributedMarkCommands-D4W2Jgpj.mjs.map → AttributedMarkCommands-BCvmqeFZ.mjs.map} +1 -1
- package/dist/chunks/{BidiIsolationPlugin-BddRbDCN.mjs → BidiIsolationPlugin-Cw2KMk-H.mjs} +2 -2
- package/dist/chunks/{BidiIsolationPlugin-BddRbDCN.mjs.map → BidiIsolationPlugin-Cw2KMk-H.mjs.map} +1 -1
- package/dist/chunks/BlockInsertion-CYH2gRkY.mjs +2 -0
- package/dist/chunks/BlockInsertion-CYH2gRkY.mjs.map +1 -0
- package/dist/chunks/BlockquotePlugin-0bZSbkyo.mjs +2 -0
- package/dist/chunks/{BlockquotePlugin-4V7KV_Sb.mjs.map → BlockquotePlugin-0bZSbkyo.mjs.map} +1 -1
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs +2 -0
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs.map +1 -0
- package/dist/chunks/CodeBlockPlugin-BCJ3faXx.mjs +2 -0
- package/dist/chunks/CodeBlockPlugin-BCJ3faXx.mjs.map +1 -0
- package/dist/chunks/{ColorGrid-DPNiXxpD.mjs → ColorGrid-DQEkJ-0y.mjs} +2 -2
- package/dist/chunks/{ColorGrid-DPNiXxpD.mjs.map → ColorGrid-DQEkJ-0y.mjs.map} +1 -1
- package/dist/chunks/{ColorPickerPopup-lcf3jS6G.mjs → ColorPickerPopup-CyF26zIU.mjs} +2 -2
- package/dist/chunks/{ColorPickerPopup-lcf3jS6G.mjs.map → ColorPickerPopup-CyF26zIU.mjs.map} +1 -1
- package/dist/chunks/Commands-D5ND50PQ.mjs +2 -0
- package/dist/chunks/Commands-D5ND50PQ.mjs.map +1 -0
- package/dist/chunks/ContentModel-NpAj7vGW.mjs +2 -0
- package/dist/chunks/ContentModel-NpAj7vGW.mjs.map +1 -0
- package/dist/chunks/Decoration-BOP1TAUd.mjs +2 -0
- package/dist/chunks/Decoration-BOP1TAUd.mjs.map +1 -0
- package/dist/chunks/{DirectionDetection-C7XE0nxW.mjs → DirectionDetection-CDVSEWQ6.mjs} +2 -2
- package/dist/chunks/{DirectionDetection-C7XE0nxW.mjs.map → DirectionDetection-CDVSEWQ6.mjs.map} +1 -1
- package/dist/chunks/Document-827l3P80.mjs +2 -0
- package/dist/chunks/Document-827l3P80.mjs.map +1 -0
- package/dist/chunks/DocumentParser-D2tr3ZKn.mjs +2 -0
- package/dist/chunks/DocumentParser-D2tr3ZKn.mjs.map +1 -0
- package/dist/chunks/EditorEventBoundary-zpYRxryo.mjs +2 -0
- package/dist/chunks/EditorEventBoundary-zpYRxryo.mjs.map +1 -0
- package/dist/chunks/{FontPlugin-DQMj65Bc.mjs → FontPlugin-D041tiLe.mjs} +2 -2
- package/dist/chunks/{FontPlugin-DQMj65Bc.mjs.map → FontPlugin-D041tiLe.mjs.map} +1 -1
- package/dist/chunks/{FontSizePlugin-9StCjEfN.mjs → FontSizePlugin-8CAvj1RD.mjs} +2 -2
- package/dist/chunks/FontSizePlugin-8CAvj1RD.mjs.map +1 -0
- package/dist/chunks/FormulaPlugin-DJ39BtqV.mjs +2 -0
- package/dist/chunks/FormulaPlugin-DJ39BtqV.mjs.map +1 -0
- package/dist/chunks/GapCursorNavigation-DCWOmopl.mjs +2 -0
- package/dist/chunks/{GapCursorNavigation-ycNzuQVH.mjs.map → GapCursorNavigation-DCWOmopl.mjs.map} +1 -1
- package/dist/chunks/GapCursorPlugin-2iR6506y.mjs +2 -0
- package/dist/chunks/{GapCursorPlugin-Zvxjnr82.mjs.map → GapCursorPlugin-2iR6506y.mjs.map} +1 -1
- package/dist/chunks/HTMLUtils-Bhg3_P8B.mjs +2 -0
- package/dist/chunks/HTMLUtils-Bhg3_P8B.mjs.map +1 -0
- package/dist/chunks/{HardBreakPlugin-9DOyCyyM.mjs → HardBreakPlugin-vniSvTgq.mjs} +2 -2
- package/dist/chunks/{HardBreakPlugin-9DOyCyyM.mjs.map → HardBreakPlugin-vniSvTgq.mjs.map} +1 -1
- package/dist/chunks/HeadingPlugin-DUKsLuMd.mjs +2 -0
- package/dist/chunks/{HeadingPlugin-BuGoM9Rz.mjs.map → HeadingPlugin-DUKsLuMd.mjs.map} +1 -1
- package/dist/chunks/{HighlightPlugin-C6pHifVz.mjs → HighlightPlugin-DRTAE1N5.mjs} +2 -2
- package/dist/chunks/{HighlightPlugin-C6pHifVz.mjs.map → HighlightPlugin-DRTAE1N5.mjs.map} +1 -1
- package/dist/chunks/HorizontalRulePlugin-B9OvwrKB.mjs +2 -0
- package/dist/chunks/HorizontalRulePlugin-B9OvwrKB.mjs.map +1 -0
- package/dist/chunks/ImagePlugin-CwxTRr6d.mjs +2 -0
- package/dist/chunks/{ImagePlugin-KpkuE2LH.mjs.map → ImagePlugin-CwxTRr6d.mjs.map} +1 -1
- package/dist/chunks/{InlineCodePlugin-DMr4O7c7.mjs → InlineCodePlugin-uxgMUX_E.mjs} +2 -2
- package/dist/chunks/InlineCodePlugin-uxgMUX_E.mjs.map +1 -0
- package/dist/chunks/InlineStyleMarkSpec-CjSRj4f_.mjs +2 -0
- package/dist/chunks/{InlineStyleMarkSpec-C6LJT7kp.mjs.map → InlineStyleMarkSpec-CjSRj4f_.mjs.map} +1 -1
- package/dist/chunks/LinkPlugin-CvWHbSkb.mjs +2 -0
- package/dist/chunks/LinkPlugin-CvWHbSkb.mjs.map +1 -0
- package/dist/chunks/ListPlugin-BchSFWVn.mjs +2 -0
- package/dist/chunks/ListPlugin-BchSFWVn.mjs.map +1 -0
- package/dist/chunks/MarkInputRule-Bdi8KPus.mjs +2 -0
- package/dist/chunks/MarkInputRule-Bdi8KPus.mjs.map +1 -0
- package/dist/chunks/MarkdownHTMLRegistry-Q7l0d6Ma.mjs +2 -0
- package/dist/chunks/MarkdownHTMLRegistry-Q7l0d6Ma.mjs.map +1 -0
- package/dist/chunks/MarkdownParser-Dp62mfUq.mjs +2 -0
- package/dist/chunks/MarkdownParser-Dp62mfUq.mjs.map +1 -0
- package/dist/chunks/MarkdownSerializer-DBELFAeo.mjs +2 -0
- package/dist/chunks/MarkdownSerializer-DBELFAeo.mjs.map +1 -0
- package/dist/chunks/MathFont-BS8IwYh2.mjs +2 -0
- package/dist/chunks/MathFont-BS8IwYh2.mjs.map +1 -0
- package/dist/chunks/{NodeResolver-ChgLF_IP.mjs → NodeResolver-Dleih-8R.mjs} +2 -2
- package/dist/chunks/{NodeResolver-ChgLF_IP.mjs.map → NodeResolver-Dleih-8R.mjs.map} +1 -1
- package/dist/chunks/NodeSelectionCommands-WH5snxr_.mjs +2 -0
- package/dist/chunks/{NodeSelectionCommands-Dj4VxLdD.mjs.map → NodeSelectionCommands-WH5snxr_.mjs.map} +1 -1
- package/dist/chunks/{PopupManager-DUtaK3WX.mjs → PopupManager-Cj86uDEB.mjs} +2 -2
- package/dist/chunks/{PopupManager-DUtaK3WX.mjs.map → PopupManager-Cj86uDEB.mjs.map} +1 -1
- package/dist/chunks/PopupPositioning-BFh6Ed1D.mjs +2 -0
- package/dist/chunks/{PopupPositioning-BB97-__N.mjs.map → PopupPositioning-BFh6Ed1D.mjs.map} +1 -1
- package/dist/chunks/PrintPlugin-D2javl_d.mjs +2 -0
- package/dist/chunks/PrintPlugin-D2javl_d.mjs.map +1 -0
- package/dist/chunks/{RangeIterator-o8v3msQe.mjs → RangeIterator-DP7Ud3Gp.mjs} +2 -2
- package/dist/chunks/{RangeIterator-o8v3msQe.mjs.map → RangeIterator-DP7Ud3Gp.mjs.map} +1 -1
- package/dist/chunks/SchemaRegistry-CkBN-1yW.mjs +2 -0
- package/dist/chunks/SchemaRegistry-CkBN-1yW.mjs.map +1 -0
- package/dist/chunks/SimpleMark-gTQaSJcO.mjs +2 -0
- package/dist/chunks/{SimpleMark-CaQtFAau.mjs.map → SimpleMark-gTQaSJcO.mjs.map} +1 -1
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs +2 -0
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs.map +1 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs +2 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs.map +1 -0
- package/dist/chunks/StrikethroughPlugin-BdrG1TTY.mjs +2 -0
- package/dist/chunks/StrikethroughPlugin-BdrG1TTY.mjs.map +1 -0
- package/dist/chunks/StyleRuntime-DW6omotc.mjs +2 -0
- package/dist/chunks/StyleRuntime-DW6omotc.mjs.map +1 -0
- package/dist/chunks/{SuperSubPlugin-DVP7VnIz.mjs → SuperSubPlugin-DTZB0Y3D.mjs} +2 -2
- package/dist/chunks/{SuperSubPlugin-DVP7VnIz.mjs.map → SuperSubPlugin-DTZB0Y3D.mjs.map} +1 -1
- package/dist/chunks/TableDimensions-BBhjHNjQ.mjs +2 -0
- package/dist/chunks/TableDimensions-BBhjHNjQ.mjs.map +1 -0
- package/dist/chunks/TablePlugin-CPvg9YZS.mjs +2 -0
- package/dist/chunks/TablePlugin-CPvg9YZS.mjs.map +1 -0
- package/dist/chunks/TextColorPlugin-Ckg3KWvb.mjs +2 -0
- package/dist/chunks/{TextColorPlugin-CEfZQ6Md.mjs.map → TextColorPlugin-Ckg3KWvb.mjs.map} +1 -1
- package/dist/chunks/TextDirectionAutoPlugin-DRGbgTlx.mjs +2 -0
- package/dist/chunks/{TextDirectionAutoPlugin-CzWJjrvG.mjs.map → TextDirectionAutoPlugin-DRGbgTlx.mjs.map} +1 -1
- package/dist/chunks/TextDirectionPlugin-BCeYyJHo.mjs +2 -0
- package/dist/chunks/{TextDirectionPlugin-DHB88HKf.mjs.map → TextDirectionPlugin-BCeYyJHo.mjs.map} +1 -1
- package/dist/chunks/TextFormattingPlugin-BhRxSC8q.mjs +2 -0
- package/dist/chunks/TextFormattingPlugin-BhRxSC8q.mjs.map +1 -0
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs +3 -0
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs.map +1 -0
- package/dist/chunks/ToolbarPlugin-dmTj1jIz.mjs +2 -0
- package/dist/chunks/{ToolbarPlugin-DVGrFf8v.mjs.map → ToolbarPlugin-dmTj1jIz.mjs.map} +1 -1
- package/dist/chunks/VideoPlugin-BugkI7-m.mjs +2 -0
- package/dist/chunks/VideoPlugin-BugkI7-m.mjs.map +1 -0
- package/dist/chunks/WordBoundary-CFpIJDNP.mjs +2 -0
- package/dist/chunks/{WordBoundary-DxeMBRyP.mjs.map → WordBoundary-CFpIJDNP.mjs.map} +1 -1
- package/dist/chunks/XmlSupport-DIXH5on9.mjs +2 -0
- package/dist/chunks/XmlSupport-DIXH5on9.mjs.map +1 -0
- package/dist/chunks/ar-Dg9PdDet.mjs +2 -0
- package/dist/chunks/ar-Dg9PdDet.mjs.map +1 -0
- package/dist/chunks/ar-eOA3UmiE.mjs +2 -0
- package/dist/chunks/{ar-BTShmP2E.mjs.map → ar-eOA3UmiE.mjs.map} +1 -1
- package/dist/chunks/de-DJ5EkXQo.mjs +2 -0
- package/dist/chunks/de-DJ5EkXQo.mjs.map +1 -0
- package/dist/chunks/{de-C--bRmjW.mjs → de-DRbhnS4T.mjs} +2 -2
- package/dist/chunks/{de-C--bRmjW.mjs.map → de-DRbhnS4T.mjs.map} +1 -1
- package/dist/chunks/{es-DFUVd7eO.mjs → es-BQ9dox_E.mjs} +2 -2
- package/dist/chunks/{es-DFUVd7eO.mjs.map → es-BQ9dox_E.mjs.map} +1 -1
- package/dist/chunks/es-CEAJj7aB.mjs +2 -0
- package/dist/chunks/es-CEAJj7aB.mjs.map +1 -0
- package/dist/chunks/fr-DhWQzZy3.mjs +2 -0
- package/dist/chunks/fr-DhWQzZy3.mjs.map +1 -0
- package/dist/chunks/{fr-BaXKyPmX.mjs → fr-NVOinLyF.mjs} +2 -2
- package/dist/chunks/{fr-BaXKyPmX.mjs.map → fr-NVOinLyF.mjs.map} +1 -1
- package/dist/chunks/full-JM0-SwWy.mjs +2 -0
- package/dist/chunks/{full-BBl7mvwH.mjs.map → full-JM0-SwWy.mjs.map} +1 -1
- package/dist/chunks/hi-BsZzSMfL.mjs +2 -0
- package/dist/chunks/hi-BsZzSMfL.mjs.map +1 -0
- package/dist/chunks/{hi-aH9HZG23.mjs → hi-DrC2H81d.mjs} +2 -2
- package/dist/chunks/{hi-aH9HZG23.mjs.map → hi-DrC2H81d.mjs.map} +1 -1
- package/dist/chunks/minimal-DSlR2A27.mjs +2 -0
- package/dist/chunks/{minimal-CtnR4dqp.mjs.map → minimal-DSlR2A27.mjs.map} +1 -1
- package/dist/chunks/pt-BuZ7acHF.mjs +2 -0
- package/dist/chunks/pt-BuZ7acHF.mjs.map +1 -0
- package/dist/chunks/{pt-Hy-0Xnnc.mjs → pt-CFrcl1v_.mjs} +2 -2
- package/dist/chunks/{pt-Hy-0Xnnc.mjs.map → pt-CFrcl1v_.mjs.map} +1 -1
- package/dist/chunks/register-BjsdJ3Aq.mjs +2 -0
- package/dist/chunks/register-BjsdJ3Aq.mjs.map +1 -0
- package/dist/chunks/rolldown-runtime-H_PjY6_i.mjs +1 -0
- package/dist/chunks/ru-CsP6mLxp.mjs +2 -0
- package/dist/chunks/ru-CsP6mLxp.mjs.map +1 -0
- package/dist/chunks/{ru-BFpEOrVp.mjs → ru-gxbzqLL8.mjs} +2 -2
- package/dist/chunks/{ru-BFpEOrVp.mjs.map → ru-gxbzqLL8.mjs.map} +1 -1
- package/dist/chunks/src-5GKFQ2mQ.mjs +2 -0
- package/dist/chunks/src-5GKFQ2mQ.mjs.map +1 -0
- package/dist/chunks/xml-BsV44TrJ.mjs +2 -0
- package/dist/chunks/xml-BsV44TrJ.mjs.map +1 -0
- package/dist/chunks/zh-BFZOM3lF.mjs +2 -0
- package/dist/chunks/{zh-BjoyjClN.mjs.map → zh-BFZOM3lF.mjs.map} +1 -1
- package/dist/chunks/zh-CiIyOm6U.mjs +2 -0
- package/dist/chunks/zh-CiIyOm6U.mjs.map +1 -0
- package/dist/commands/BlockInsertion.d.ts +5 -3
- package/dist/commands/BlockInsertion.d.ts.map +1 -1
- package/dist/commands/SelectionDeletion.d.ts +13 -2
- package/dist/commands/SelectionDeletion.d.ts.map +1 -1
- package/dist/editor/ContentSerializer.d.ts +21 -0
- package/dist/editor/ContentSerializer.d.ts.map +1 -1
- package/dist/editor/EditorConfig.d.ts +17 -0
- package/dist/editor/EditorConfig.d.ts.map +1 -1
- package/dist/editor/EditorConfigController.d.ts +10 -0
- package/dist/editor/EditorConfigController.d.ts.map +1 -1
- package/dist/editor/EditorInitializer.d.ts +4 -0
- package/dist/editor/EditorInitializer.d.ts.map +1 -1
- package/dist/editor/EditorLocale.d.ts +2 -0
- package/dist/editor/EditorLocale.d.ts.map +1 -1
- package/dist/editor/MarkdownMode.d.ts +46 -0
- package/dist/editor/MarkdownMode.d.ts.map +1 -0
- package/dist/editor/NotectlEditor.d.ts +24 -0
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/locales/ar.d.ts.map +1 -1
- package/dist/editor/locales/de.d.ts.map +1 -1
- package/dist/editor/locales/es.d.ts.map +1 -1
- package/dist/editor/locales/fr.d.ts.map +1 -1
- package/dist/editor/locales/hi.d.ts.map +1 -1
- package/dist/editor/locales/pt.d.ts.map +1 -1
- package/dist/editor/locales/ru.d.ts.map +1 -1
- package/dist/editor/locales/zh.d.ts.map +1 -1
- package/dist/editor/styles/list.d.ts +1 -1
- package/dist/editor/styles/list.d.ts.map +1 -1
- package/dist/editor/styles/table.d.ts +1 -1
- package/dist/editor/styles/table.d.ts.map +1 -1
- package/dist/fonts/math.d.ts +3 -0
- package/dist/fonts/math.d.ts.map +1 -0
- package/dist/fonts/math.mjs +1 -0
- package/dist/fonts/starter.d.ts +3 -0
- package/dist/fonts/starter.d.ts.map +1 -0
- package/dist/fonts/starter.mjs +1 -0
- package/dist/fonts.d.ts +4 -4
- package/dist/fonts.mjs +1 -1
- package/dist/full.d.ts +1 -1
- package/dist/full.mjs +1 -1
- package/dist/html.d.ts +1 -0
- package/dist/html.d.ts.map +1 -1
- package/dist/html.mjs +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/input/ClipboardHandler.d.ts +2 -0
- package/dist/input/ClipboardHandler.d.ts.map +1 -1
- package/dist/input/InputHandler.d.ts +7 -0
- package/dist/input/InputHandler.d.ts.map +1 -1
- package/dist/input/InputManager.d.ts +10 -0
- package/dist/input/InputManager.d.ts.map +1 -1
- package/dist/input/KeyboardHandler.d.ts.map +1 -1
- package/dist/input/MarkdownPasteDetector.d.ts +26 -0
- package/dist/input/MarkdownPasteDetector.d.ts.map +1 -0
- package/dist/input/PasteHTMLHandler.d.ts +18 -1
- package/dist/input/PasteHTMLHandler.d.ts.map +1 -1
- package/dist/input/PasteHandler.d.ts +30 -0
- package/dist/input/PasteHandler.d.ts.map +1 -1
- package/dist/input/PasteRichBlockHandler.d.ts.map +1 -1
- package/dist/input/SelectionDocument.d.ts +2 -1
- package/dist/input/SelectionDocument.d.ts.map +1 -1
- package/dist/markdown.d.ts +17 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.mjs +1 -0
- package/dist/model/AttrRegistry.d.ts +2 -2
- package/dist/model/AttrRegistry.d.ts.map +1 -1
- package/dist/model/ContentModel.d.ts +12 -0
- package/dist/model/ContentModel.d.ts.map +1 -1
- package/dist/model/Document.d.ts +28 -3
- package/dist/model/Document.d.ts.map +1 -1
- package/dist/model/HTMLUtils.d.ts +15 -0
- package/dist/model/HTMLUtils.d.ts.map +1 -1
- package/dist/model/InlineNodeSpec.d.ts +7 -1
- package/dist/model/InlineNodeSpec.d.ts.map +1 -1
- package/dist/model/InputRule.d.ts +11 -0
- package/dist/model/InputRule.d.ts.map +1 -1
- package/dist/model/MarkSpec.d.ts +7 -1
- package/dist/model/MarkSpec.d.ts.map +1 -1
- package/dist/model/MarkdownSyntaxRegistry.d.ts +51 -0
- package/dist/model/MarkdownSyntaxRegistry.d.ts.map +1 -0
- package/dist/model/NodeSpec.d.ts +47 -1
- package/dist/model/NodeSpec.d.ts.map +1 -1
- package/dist/model/RichBlockData.d.ts +2 -0
- package/dist/model/RichBlockData.d.ts.map +1 -1
- package/dist/model/SchemaRegistry.d.ts.map +1 -1
- package/dist/model/TableDimensions.d.ts +11 -0
- package/dist/model/TableDimensions.d.ts.map +1 -0
- package/dist/notectl-core.mjs +1 -1
- package/dist/notectl-core.umd.js +1 -1
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/platform/EditorEventBoundary.d.ts +12 -0
- package/dist/platform/EditorEventBoundary.d.ts.map +1 -0
- package/dist/plugins/Plugin.d.ts +7 -0
- package/dist/plugins/Plugin.d.ts.map +1 -1
- package/dist/plugins/PluginContextFactory.d.ts +2 -0
- package/dist/plugins/PluginContextFactory.d.ts.map +1 -1
- package/dist/plugins/PluginManager.d.ts +2 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/bidi-isolation.mjs +1 -1
- package/dist/plugins/blockquote/BlockquotePlugin.d.ts +2 -0
- package/dist/plugins/blockquote/BlockquotePlugin.d.ts.map +1 -1
- package/dist/plugins/blockquote.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockHighlighting.d.ts.map +1 -1
- package/dist/plugins/code-block/CodeBlockTypes.d.ts +2 -0
- package/dist/plugins/code-block/CodeBlockTypes.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font-size/FontSizeOperations.d.ts.map +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/formula/DisplayMathNodeSpec.d.ts.map +1 -1
- package/dist/plugins/formula/FormulaInputRules.d.ts.map +1 -1
- package/dist/plugins/formula/FormulaMarkdownSyntax.d.ts +4 -0
- package/dist/plugins/formula/FormulaMarkdownSyntax.d.ts.map +1 -0
- package/dist/plugins/formula/FormulaPlugin.d.ts.map +1 -1
- package/dist/plugins/formula/FormulaTypes.d.ts +3 -1
- package/dist/plugins/formula/FormulaTypes.d.ts.map +1 -1
- package/dist/plugins/formula/InlineMathNodeSpec.d.ts.map +1 -1
- package/dist/plugins/formula.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +1 -1
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading/HeadingPlugin.d.ts +2 -0
- package/dist/plugins/heading/HeadingPlugin.d.ts.map +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule/HorizontalRulePlugin.d.ts +2 -0
- package/dist/plugins/horizontal-rule/HorizontalRulePlugin.d.ts.map +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image/ImagePlugin.d.ts +14 -0
- package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/inline-code/InlineCodePlugin.d.ts.map +1 -1
- package/dist/plugins/inline-code.mjs +1 -1
- package/dist/plugins/language.mjs +1 -1
- package/dist/plugins/link/LinkPlugin.d.ts +14 -0
- package/dist/plugins/link/LinkPlugin.d.ts.map +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list/ListCommands.d.ts +5 -1
- package/dist/plugins/list/ListCommands.d.ts.map +1 -1
- package/dist/plugins/list/ListInputRules.d.ts.map +1 -1
- package/dist/plugins/list/ListItemContext.d.ts +27 -0
- package/dist/plugins/list/ListItemContext.d.ts.map +1 -0
- package/dist/plugins/list/ListKeyboardHandlers.d.ts.map +1 -1
- package/dist/plugins/list/ListPlugin.d.ts +2 -0
- package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
- package/dist/plugins/list/ListToolbarItems.d.ts.map +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print/PrintContentPreparer.d.ts.map +1 -1
- package/dist/plugins/print/PrintDestinationBridge.d.ts +23 -0
- package/dist/plugins/print/PrintDestinationBridge.d.ts.map +1 -0
- package/dist/plugins/print/PrintHostStyles.d.ts.map +1 -1
- package/dist/plugins/print/PrintServiceImpl.d.ts +5 -1
- package/dist/plugins/print/PrintServiceImpl.d.ts.map +1 -1
- package/dist/plugins/print/PrintStyleCollector.d.ts.map +1 -1
- package/dist/plugins/print.mjs +1 -1
- package/dist/plugins/shared/MarkInputRule.d.ts +9 -0
- package/dist/plugins/shared/MarkInputRule.d.ts.map +1 -0
- package/dist/plugins/shared/PopupPositioning.d.ts +2 -0
- package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -1
- package/dist/plugins/shared/index.d.ts +1 -0
- package/dist/plugins/shared/index.d.ts.map +1 -1
- package/dist/plugins/shared.mjs +1 -1
- package/dist/plugins/smart-paste/SmartPastePlugin.d.ts.map +1 -1
- package/dist/plugins/smart-paste.mjs +1 -1
- package/dist/plugins/strikethrough/StrikethroughPlugin.d.ts +2 -0
- package/dist/plugins/strikethrough/StrikethroughPlugin.d.ts.map +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table/TableClipboard.d.ts +8 -0
- package/dist/plugins/table/TableClipboard.d.ts.map +1 -0
- package/dist/plugins/table/TableCommands.d.ts.map +1 -1
- package/dist/plugins/table/TableContextMenu.d.ts +11 -1
- package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
- package/dist/plugins/table/TableControls.d.ts +6 -1
- package/dist/plugins/table/TableControls.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsDOM.d.ts +11 -2
- package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsLayout.d.ts +12 -3
- package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -1
- package/dist/plugins/table/TableGrid.d.ts +57 -0
- package/dist/plugins/table/TableGrid.d.ts.map +1 -0
- package/dist/plugins/table/TableHelpers.d.ts +4 -0
- package/dist/plugins/table/TableHelpers.d.ts.map +1 -1
- package/dist/plugins/table/TableLocale.d.ts +30 -0
- package/dist/plugins/table/TableLocale.d.ts.map +1 -1
- package/dist/plugins/table/TableNavigation.d.ts +5 -1
- package/dist/plugins/table/TableNavigation.d.ts.map +1 -1
- package/dist/plugins/table/TableNodeViews.d.ts +13 -1
- package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
- package/dist/plugins/table/TablePlugin.d.ts +21 -1
- package/dist/plugins/table/TablePlugin.d.ts.map +1 -1
- package/dist/plugins/table/TableSelection.d.ts.map +1 -1
- package/dist/plugins/table/TableSizeEditor.d.ts +11 -0
- package/dist/plugins/table/TableSizeEditor.d.ts.map +1 -0
- package/dist/plugins/table/TableSizing.d.ts +70 -0
- package/dist/plugins/table/TableSizing.d.ts.map +1 -0
- package/dist/plugins/table/index.d.ts +3 -1
- package/dist/plugins/table/index.d.ts.map +1 -1
- package/dist/plugins/table/locales/ar.d.ts.map +1 -1
- package/dist/plugins/table/locales/de.d.ts.map +1 -1
- package/dist/plugins/table/locales/es.d.ts.map +1 -1
- package/dist/plugins/table/locales/fr.d.ts.map +1 -1
- package/dist/plugins/table/locales/hi.d.ts.map +1 -1
- package/dist/plugins/table/locales/pt.d.ts.map +1 -1
- package/dist/plugins/table/locales/ru.d.ts.map +1 -1
- package/dist/plugins/table/locales/zh.d.ts.map +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-direction-auto.mjs +1 -1
- package/dist/plugins/text-direction.mjs +1 -1
- package/dist/plugins/text-formatting/TextFormattingPlugin.d.ts +9 -0
- package/dist/plugins/text-formatting/TextFormattingPlugin.d.ts.map +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar.mjs +1 -1
- package/dist/plugins/video/VideoCommands.d.ts.map +1 -1
- package/dist/plugins/video.mjs +1 -1
- package/dist/presets/full.d.ts +1 -1
- package/dist/presets/full.mjs +1 -1
- package/dist/presets/minimal.d.ts +1 -1
- package/dist/presets/minimal.mjs +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/register.d.ts +3 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.mjs +1 -0
- package/dist/serialization/DocumentParser.d.ts.map +1 -1
- package/dist/serialization/DocumentSerializer.d.ts +2 -0
- package/dist/serialization/DocumentSerializer.d.ts.map +1 -1
- package/dist/serialization/HTMLSanitization.d.ts +15 -0
- package/dist/serialization/HTMLSanitization.d.ts.map +1 -0
- package/dist/serialization/MarkSerializer.d.ts +12 -0
- package/dist/serialization/MarkSerializer.d.ts.map +1 -1
- package/dist/serialization/MarkdownParser.d.ts +6 -0
- package/dist/serialization/MarkdownParser.d.ts.map +1 -0
- package/dist/serialization/MarkdownSerializer.d.ts +12 -0
- package/dist/serialization/MarkdownSerializer.d.ts.map +1 -0
- package/dist/serialization/MarkdownTypes.d.ts +59 -0
- package/dist/serialization/MarkdownTypes.d.ts.map +1 -0
- package/dist/serialization/TableDimensions.d.ts +37 -0
- package/dist/serialization/TableDimensions.d.ts.map +1 -0
- package/dist/serialization/markdown/BlockSerializers.d.ts +7 -0
- package/dist/serialization/markdown/BlockSerializers.d.ts.map +1 -0
- package/dist/serialization/markdown/BlockTokenizer.d.ts +54 -0
- package/dist/serialization/markdown/BlockTokenizer.d.ts.map +1 -0
- package/dist/serialization/markdown/GfmAutolink.d.ts +20 -0
- package/dist/serialization/markdown/GfmAutolink.d.ts.map +1 -0
- package/dist/serialization/markdown/GfmTableParser.d.ts +26 -0
- package/dist/serialization/markdown/GfmTableParser.d.ts.map +1 -0
- package/dist/serialization/markdown/InlineAst.d.ts +34 -0
- package/dist/serialization/markdown/InlineAst.d.ts.map +1 -0
- package/dist/serialization/markdown/InlineSerializers.d.ts +5 -0
- package/dist/serialization/markdown/InlineSerializers.d.ts.map +1 -0
- package/dist/serialization/markdown/InlineTokenizer.d.ts +5 -0
- package/dist/serialization/markdown/InlineTokenizer.d.ts.map +1 -0
- package/dist/serialization/markdown/LinkReferenceExtractor.d.ts +37 -0
- package/dist/serialization/markdown/LinkReferenceExtractor.d.ts.map +1 -0
- package/dist/serialization/markdown/MarkdownContext.d.ts +32 -0
- package/dist/serialization/markdown/MarkdownContext.d.ts.map +1 -0
- package/dist/serialization/markdown/MarkdownEntities.d.ts +33 -0
- package/dist/serialization/markdown/MarkdownEntities.d.ts.map +1 -0
- package/dist/serialization/markdown/MarkdownEscape.d.ts +33 -0
- package/dist/serialization/markdown/MarkdownEscape.d.ts.map +1 -0
- package/dist/serialization/markdown/MarkdownHTMLRegistry.d.ts +4 -0
- package/dist/serialization/markdown/MarkdownHTMLRegistry.d.ts.map +1 -0
- package/dist/serialization/markdown/MarkdownParseContext.d.ts +23 -0
- package/dist/serialization/markdown/MarkdownParseContext.d.ts.map +1 -0
- package/dist/state/Mapping.d.ts +33 -10
- package/dist/state/Mapping.d.ts.map +1 -1
- package/dist/state/StepApplication.d.ts +3 -1
- package/dist/state/StepApplication.d.ts.map +1 -1
- package/dist/state/StepHandlers.d.ts.map +1 -1
- package/dist/state/StepInversion.d.ts +2 -1
- package/dist/state/StepInversion.d.ts.map +1 -1
- package/dist/state/StepMapping.d.ts +3 -2
- package/dist/state/StepMapping.d.ts.map +1 -1
- package/dist/state/StepMaps.d.ts +3 -2
- package/dist/state/StepMaps.d.ts.map +1 -1
- package/dist/state/Steps.d.ts +34 -2
- package/dist/state/Steps.d.ts.map +1 -1
- package/dist/state/TransactionBuilder.d.ts +7 -1
- package/dist/state/TransactionBuilder.d.ts.map +1 -1
- package/dist/test/MarkdownConformanceUtils.d.ts +12 -0
- package/dist/test/MarkdownConformanceUtils.d.ts.map +1 -0
- package/dist/view/BlockRendering.d.ts +5 -0
- package/dist/view/BlockRendering.d.ts.map +1 -1
- package/dist/view/DecorationRendering.d.ts +6 -0
- package/dist/view/DecorationRendering.d.ts.map +1 -1
- package/dist/view/EditorViewEvents.d.ts.map +1 -1
- package/dist/view/Reconciler.d.ts.map +1 -1
- package/package.json +28 -214
- package/dist/chunks/AttrRegistry-DcDVyzLm.mjs.map +0 -1
- package/dist/chunks/AttributedMarkCommands-D4W2Jgpj.mjs +0 -2
- package/dist/chunks/BlockInsertion-6rR9tIGP.mjs +0 -2
- package/dist/chunks/BlockInsertion-6rR9tIGP.mjs.map +0 -1
- package/dist/chunks/BlockquotePlugin-4V7KV_Sb.mjs +0 -2
- package/dist/chunks/CaretNavigationPlugin-C6XBwp0N.mjs +0 -2
- package/dist/chunks/CaretNavigationPlugin-C6XBwp0N.mjs.map +0 -1
- package/dist/chunks/CodeBlockPlugin-3V2K3RBe.mjs +0 -2
- package/dist/chunks/CodeBlockPlugin-3V2K3RBe.mjs.map +0 -1
- package/dist/chunks/Commands-BIIpx_Zd.mjs +0 -2
- package/dist/chunks/Commands-BIIpx_Zd.mjs.map +0 -1
- package/dist/chunks/Decoration-ZeyVfwJB.mjs +0 -2
- package/dist/chunks/Decoration-ZeyVfwJB.mjs.map +0 -1
- package/dist/chunks/Document-r7vLOAGA.mjs +0 -2
- package/dist/chunks/Document-r7vLOAGA.mjs.map +0 -1
- package/dist/chunks/DocumentParser-CcTSeT1B.mjs +0 -2
- package/dist/chunks/DocumentParser-CcTSeT1B.mjs.map +0 -1
- package/dist/chunks/FontSizePlugin-9StCjEfN.mjs.map +0 -1
- package/dist/chunks/FormulaPlugin-CV5VUxkb.mjs +0 -2
- package/dist/chunks/FormulaPlugin-CV5VUxkb.mjs.map +0 -1
- package/dist/chunks/GapCursorNavigation-ycNzuQVH.mjs +0 -2
- package/dist/chunks/GapCursorPlugin-Zvxjnr82.mjs +0 -2
- package/dist/chunks/HTMLUtils-C6B4UT00.mjs +0 -2
- package/dist/chunks/HTMLUtils-C6B4UT00.mjs.map +0 -1
- package/dist/chunks/HeadingPlugin-BuGoM9Rz.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-0FjHPZuA.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-0FjHPZuA.mjs.map +0 -1
- package/dist/chunks/ImagePlugin-KpkuE2LH.mjs +0 -2
- package/dist/chunks/InlineCodePlugin-DMr4O7c7.mjs.map +0 -1
- package/dist/chunks/InlineStyleMarkSpec-C6LJT7kp.mjs +0 -2
- package/dist/chunks/LinkPlugin-DvZ3MPgL.mjs +0 -2
- package/dist/chunks/LinkPlugin-DvZ3MPgL.mjs.map +0 -1
- package/dist/chunks/ListPlugin-2yl6KiTK.mjs +0 -2
- package/dist/chunks/ListPlugin-2yl6KiTK.mjs.map +0 -1
- package/dist/chunks/NodeSelectionCommands-Dj4VxLdD.mjs +0 -2
- package/dist/chunks/PopupPositioning-BB97-__N.mjs +0 -2
- package/dist/chunks/PrintPlugin-DyIZDY3Q.mjs +0 -2
- package/dist/chunks/PrintPlugin-DyIZDY3Q.mjs.map +0 -1
- package/dist/chunks/PrintTypes-CxU69qu3.mjs +0 -3
- package/dist/chunks/PrintTypes-CxU69qu3.mjs.map +0 -1
- package/dist/chunks/SimpleMark-CaQtFAau.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-Bi_wpTCV.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-Bi_wpTCV.mjs.map +0 -1
- package/dist/chunks/StrikethroughPlugin-BLKDRWtf.mjs +0 -2
- package/dist/chunks/StrikethroughPlugin-BLKDRWtf.mjs.map +0 -1
- package/dist/chunks/StyleRuntime-BFKu52mS.mjs +0 -2
- package/dist/chunks/StyleRuntime-BFKu52mS.mjs.map +0 -1
- package/dist/chunks/TablePlugin-BTH9VRZq.mjs +0 -2
- package/dist/chunks/TablePlugin-BTH9VRZq.mjs.map +0 -1
- package/dist/chunks/TextColorPlugin-CEfZQ6Md.mjs +0 -2
- package/dist/chunks/TextDirectionAutoPlugin-CzWJjrvG.mjs +0 -2
- package/dist/chunks/TextDirectionPlugin-DHB88HKf.mjs +0 -2
- package/dist/chunks/TextFormattingPlugin-Cubp3e7f.mjs +0 -2
- package/dist/chunks/TextFormattingPlugin-Cubp3e7f.mjs.map +0 -1
- package/dist/chunks/ToolbarPlugin-DVGrFf8v.mjs +0 -2
- package/dist/chunks/VideoPlugin-B3uf1Dwp.mjs +0 -2
- package/dist/chunks/VideoPlugin-B3uf1Dwp.mjs.map +0 -1
- package/dist/chunks/WordBoundary-DxeMBRyP.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs.map +0 -1
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs +0 -2
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs.map +0 -1
- package/dist/chunks/ar-BTShmP2E.mjs +0 -2
- package/dist/chunks/ar-jbplYWkC.mjs +0 -2
- package/dist/chunks/ar-jbplYWkC.mjs.map +0 -1
- package/dist/chunks/de-DN-e6G5A.mjs +0 -2
- package/dist/chunks/de-DN-e6G5A.mjs.map +0 -1
- package/dist/chunks/es-CM55WeEN.mjs +0 -2
- package/dist/chunks/es-CM55WeEN.mjs.map +0 -1
- package/dist/chunks/fonts-DQC6Uj4r.mjs +0 -2
- package/dist/chunks/fonts-DQC6Uj4r.mjs.map +0 -1
- package/dist/chunks/fr-C_2i5Ono.mjs +0 -2
- package/dist/chunks/fr-C_2i5Ono.mjs.map +0 -1
- package/dist/chunks/full-BBl7mvwH.mjs +0 -2
- package/dist/chunks/hi-C9fvG91d.mjs +0 -2
- package/dist/chunks/hi-C9fvG91d.mjs.map +0 -1
- package/dist/chunks/minimal-CtnR4dqp.mjs +0 -2
- package/dist/chunks/pt-GqLw5tN0.mjs +0 -2
- package/dist/chunks/pt-GqLw5tN0.mjs.map +0 -1
- package/dist/chunks/ru-4g_qsaLr.mjs +0 -2
- package/dist/chunks/ru-4g_qsaLr.mjs.map +0 -1
- package/dist/chunks/src-C89Cw7ni.mjs +0 -2
- package/dist/chunks/src-C89Cw7ni.mjs.map +0 -1
- package/dist/chunks/zh-BjoyjClN.mjs +0 -2
- package/dist/chunks/zh-DeZP08HI.mjs +0 -2
- package/dist/chunks/zh-DeZP08HI.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-BjsdJ3Aq.mjs","names":[],"sources":["../../src/state/TransactionBuilder.ts","../../src/state/EditorState.ts","../../src/editor/ContentSerializer.ts","../../src/editor/MarkdownMode.ts","../../src/editor/EditorConfigController.ts","../../src/editor/EditorEventEmitter.ts","../../src/input/SelectionDocument.ts","../../src/input/ClipboardHandler.ts","../../src/input/CompositionTracker.ts","../../src/input/InputHandler.ts","../../src/input/KeyDescriptor.ts","../../src/input/KeyboardHandler.ts","../../src/input/MarkdownPasteDetector.ts","../../src/commands/PasteCommand.ts","../../src/input/HTMLParser.ts","../../src/input/LegacyHTMLNormalizer.ts","../../src/input/PasteHTMLHandler.ts","../../src/input/PasteRichBlockHandler.ts","../../src/input/PasteHandler.ts","../../src/input/InputManager.ts","../../src/model/FileHandlerRegistry.ts","../../src/model/InputRuleRegistry.ts","../../src/model/KeymapRegistry.ts","../../src/model/MarkdownSyntaxRegistry.ts","../../src/view/NodeViewRegistry.ts","../../src/plugins/Logger.ts","../../src/plugins/CommandRegistry.ts","../../src/plugins/EventBus.ts","../../src/plugins/MiddlewareChain.ts","../../src/plugins/PluginContextFactory.ts","../../src/plugins/PluginLifecycle.ts","../../src/plugins/RegistrationTracker.ts","../../src/plugins/ServiceRegistry.ts","../../src/plugins/PluginManager.ts","../../src/state/SelectionMapping.ts","../../src/state/History.ts","../../src/state/ReadonlyGuard.ts","../../src/view/MarkRendering.ts","../../src/view/CursorWrapper.ts","../../src/view/EditorViewEvents.ts","../../src/view/EditorViewNavigation.ts","../../src/view/BlockWrapperManagement.ts","../../src/view/InlineRendering.ts","../../src/view/DecorationRendering.ts","../../src/view/BlockRendering.ts","../../src/view/Reconciler.ts","../../src/view/EditorView.ts","../../src/model/BlockTypeLabels.ts","../../src/editor/Announcer.ts","../../src/editor/BuiltinSpecs.ts","../../src/editor/EditorLocale.ts","../../src/editor/EditorDOM.ts","../../src/editor/styles/base.ts","../../src/editor/styles/paper.ts","../../src/editor/styles/reduced-motion.ts","../../src/editor/styles.ts","../../src/editor/EditorThemeController.ts","../../src/editor/PaperLayoutController.ts","../../src/editor/PluginBootstrapper.ts","../../src/editor/EditorInitializer.ts","../../src/editor/EditorLifecycleCoordinator.ts","../../src/editor/EditorStyleCoordinator.ts","../../src/editor/NotectlEditor.ts","../../src/register.ts"],"sourcesContent":["/**\n * Fluent builder for constructing transactions step by step.\n * Tracks a working document copy to auto-derive data for convenience methods.\n */\n\nimport type {\n\tBlockAttrs,\n\tBlockNode,\n\tContentSegment,\n\tDocument,\n\tInlineNode,\n\tMark,\n} from '../model/Document.js';\nimport {\n\tgetBlockContentSegmentsInRange,\n\tgetBlockLength,\n\tgetBlockMarksAtOffset,\n\tgetBlockText,\n\tgetContentAtOffset,\n\tgetInlineChildren,\n\tisInlineNode,\n\ttextSegment,\n} from '../model/Document.js';\nimport { findNode, resolveChildAt, resolveNodeByPath } from '../model/NodeResolver.js';\nimport type { EditorSelection } from '../model/Selection.js';\nimport { createNodeSelection } from '../model/Selection.js';\nimport type { BlockId, NodeTypeName } from '../model/TypeBrands.js';\nimport { findRangesMissingMark, findRangesWithMark } from './InlineContentOps.js';\nimport { Mapping, type StepMap } from './Mapping.js';\nimport { applyStep, getStepMap } from './StepHandlers.js';\nimport { isMoveNodeNoOp } from './Steps.js';\nimport type {\n\tAddMarkStep,\n\tDeleteTextStep,\n\tInsertInlineNodeStep,\n\tInsertNodeStep,\n\tInsertTextStep,\n\tMergeBlocksStep,\n\tMoveNodeStep,\n\tRemoveInlineNodeStep,\n\tRemoveMarkStep,\n\tRemoveNodeStep,\n\tSetBlockTypeStep,\n\tSetInlineNodeAttrStep,\n\tSetNodeAttrStep,\n\tSplitBlockStep,\n\tStep,\n\tTransactionOrigin,\n} from './Steps.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Fluent API for building transactions. */\nexport class TransactionBuilder {\n\tprivate readonly steps: Step[] = [];\n\tprivate readonly stepMaps: StepMap[] = [];\n\tprivate selection: EditorSelection;\n\tprivate storedMarks: readonly Mark[] | null;\n\tprivate readonly selectionBefore: EditorSelection;\n\tprivate readonly origin: TransactionOrigin;\n\tprivate workingDoc: Document | null;\n\tprivate _readonlyAllowed = false;\n\n\tconstructor(\n\t\tcurrentSelection: EditorSelection,\n\t\tcurrentStoredMarks: readonly Mark[] | null,\n\t\torigin: TransactionOrigin = 'api',\n\t\tdoc?: Document,\n\t) {\n\t\tthis.selection = currentSelection;\n\t\tthis.selectionBefore = currentSelection;\n\t\tthis.storedMarks = currentStoredMarks;\n\t\tthis.origin = origin;\n\t\tthis.workingDoc = doc ?? null;\n\t}\n\n\t/** Adds an insert-text step. Updates workingDoc if available. */\n\tinsertText(\n\t\tblockId: BlockId,\n\t\toffset: number,\n\t\ttext: string,\n\t\tmarks: readonly Mark[],\n\t\tsegments?: readonly ContentSegment[],\n\t): this {\n\t\tconst step: InsertTextStep = {\n\t\t\ttype: 'insertText',\n\t\t\tblockId,\n\t\t\toffset,\n\t\t\ttext,\n\t\t\tmarks,\n\t\t\t...(segments ? { segments } : {}),\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Adds a delete-text step with explicit data. Updates workingDoc if available. */\n\tdeleteText(\n\t\tblockId: BlockId,\n\t\tfrom: number,\n\t\tto: number,\n\t\tdeletedText: string,\n\t\tdeletedMarks: readonly Mark[],\n\t\tdeletedSegments?: readonly ContentSegment[],\n\t): this {\n\t\tlet segments: readonly ContentSegment[];\n\t\tif (deletedSegments) {\n\t\t\tsegments = deletedSegments;\n\t\t} else if (this.workingDoc) {\n\t\t\tconst block = findNode(this.workingDoc, blockId);\n\t\t\tsegments = block\n\t\t\t\t? getBlockContentSegmentsInRange(block, from, to)\n\t\t\t\t: [textSegment(deletedText, [...deletedMarks])];\n\t\t} else {\n\t\t\tsegments = [textSegment(deletedText, [...deletedMarks])];\n\t\t}\n\t\tconst step: DeleteTextStep = {\n\t\t\ttype: 'deleteText',\n\t\t\tblockId,\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tdeletedText,\n\t\t\tdeletedMarks,\n\t\t\tdeletedSegments: segments,\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Deletes text at the given range, auto-deriving deletedText, deletedMarks, and deletedSegments\n\t * from the working document. Requires a document to be provided at construction.\n\t */\n\tdeleteTextAt(blockId: BlockId, from: number, to: number): this {\n\t\tconst doc: Document = this.requireDoc('deleteTextAt');\n\t\tconst block: BlockNode = this.requireBlock(doc, blockId);\n\n\t\tconst text = getBlockText(block);\n\t\tconst deletedText = text.slice(from, to);\n\t\tconst deletedMarks = getBlockMarksAtOffset(block, from);\n\t\tconst deletedSegments = getBlockContentSegmentsInRange(block, from, to);\n\n\t\treturn this.deleteText(blockId, from, to, deletedText, deletedMarks, deletedSegments);\n\t}\n\n\t/** Adds a split-block step. Updates workingDoc if available. */\n\tsplitBlock(blockId: BlockId, offset: number, newBlockId: BlockId): this {\n\t\tconst step: SplitBlockStep = { type: 'splitBlock', blockId, offset, newBlockId };\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a merge-blocks step with explicit data. Updates workingDoc if available.\n\t *\n\t * `sourceType` / `sourceAttrs` snapshot the source block's identity so the\n\t * inverse split can restore it on undo. Callers that do not know the source\n\t * identity should prefer {@link mergeBlocksAt}, which derives both from the\n\t * working document.\n\t */\n\tmergeBlocks(\n\t\ttargetBlockId: BlockId,\n\t\tsourceBlockId: BlockId,\n\t\ttargetLengthBefore: number,\n\t\tsourceType?: NodeTypeName,\n\t\tsourceAttrs?: BlockAttrs,\n\t\tsourceHTMLId?: string,\n\t): this {\n\t\tconst step: MergeBlocksStep = {\n\t\t\ttype: 'mergeBlocks',\n\t\t\ttargetBlockId,\n\t\t\tsourceBlockId,\n\t\t\ttargetLengthBefore,\n\t\t\t...(sourceType !== undefined ? { sourceType } : {}),\n\t\t\t...(sourceAttrs ? { sourceAttrs } : {}),\n\t\t\t...(sourceHTMLId !== undefined ? { sourceHTMLId } : {}),\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Merges two blocks, auto-deriving targetLengthBefore and the source\n\t * block's identity (type + attrs) from the working document. Requires a\n\t * document to be provided at construction.\n\t */\n\tmergeBlocksAt(targetBlockId: BlockId, sourceBlockId: BlockId): this {\n\t\tconst doc: Document = this.requireDoc('mergeBlocksAt');\n\t\tconst targetBlock: BlockNode = this.requireBlock(doc, targetBlockId);\n\t\tconst sourceBlock: BlockNode = this.requireBlock(doc, sourceBlockId);\n\n\t\tconst targetLengthBefore = getBlockLength(targetBlock);\n\t\treturn this.mergeBlocks(\n\t\t\ttargetBlockId,\n\t\t\tsourceBlockId,\n\t\t\ttargetLengthBefore,\n\t\t\tsourceBlock.type,\n\t\t\tsourceBlock.attrs,\n\t\t\tsourceBlock.htmlId,\n\t\t);\n\t}\n\n\t/**\n\t * Emits one `AddMarkStep` per maximal sub-range of `[from, to)` that does\n\t * not yet carry a mark of `mark.type`. Sub-ranges that already carry the\n\t * mark type are skipped, so the symmetric inverse never strips a\n\t * pre-existing mark.\n\t *\n\t * Falls back to a single full-range step when no working document is\n\t * available (e.g. direct construction without a doc, used in low-level\n\t * tests).\n\t */\n\taddMark(blockId: BlockId, from: number, to: number, mark: Mark): this {\n\t\tconst block: BlockNode | null = this.tryGetBlock(blockId);\n\t\tif (!block) {\n\t\t\tconst step: AddMarkStep = { type: 'addMark', blockId, from, to, mark };\n\t\t\tthis.steps.push(step);\n\t\t\tthis.advanceDoc(step);\n\t\t\treturn this;\n\t\t}\n\n\t\tconst ranges = findRangesMissingMark(getInlineChildren(block), from, to, mark.type);\n\t\tfor (const range of ranges) {\n\t\t\tconst step: AddMarkStep = {\n\t\t\t\ttype: 'addMark',\n\t\t\t\tblockId,\n\t\t\t\tfrom: range.from,\n\t\t\t\tto: range.to,\n\t\t\t\tmark,\n\t\t\t};\n\t\t\tthis.steps.push(step);\n\t\t\tthis.advanceDoc(step);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Emits one `RemoveMarkStep` per maximal sub-range of `[from, to)` that\n\t * carries a mark of `mark.type`. The step's `mark` is the actual mark\n\t * found in the document (including attrs), so the symmetric inverse\n\t * restores it faithfully. Sub-ranges without the mark are skipped.\n\t *\n\t * Falls back to a single full-range step when no working document is\n\t * available (e.g. direct construction without a doc, used in low-level\n\t * tests).\n\t */\n\tremoveMark(blockId: BlockId, from: number, to: number, mark: Mark): this {\n\t\tconst block: BlockNode | null = this.tryGetBlock(blockId);\n\t\tif (!block) {\n\t\t\tconst step: RemoveMarkStep = { type: 'removeMark', blockId, from, to, mark };\n\t\t\tthis.steps.push(step);\n\t\t\tthis.advanceDoc(step);\n\t\t\treturn this;\n\t\t}\n\n\t\tconst ranges = findRangesWithMark(getInlineChildren(block), from, to, mark.type);\n\t\tfor (const range of ranges) {\n\t\t\tconst step: RemoveMarkStep = {\n\t\t\t\ttype: 'removeMark',\n\t\t\t\tblockId,\n\t\t\t\tfrom: range.from,\n\t\t\t\tto: range.to,\n\t\t\t\tmark: range.mark,\n\t\t\t};\n\t\t\tthis.steps.push(step);\n\t\t\tthis.advanceDoc(step);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Adds a set-block-type step, changing a block's node type and optionally its attrs. */\n\tsetBlockType(blockId: BlockId, nodeType: NodeTypeName, attrs?: BlockAttrs): this {\n\t\tconst doc: Document = this.requireDoc('setBlockType');\n\t\tconst block: BlockNode = this.requireBlock(doc, blockId);\n\t\tconst step: SetBlockTypeStep = {\n\t\t\ttype: 'setBlockType',\n\t\t\tblockId,\n\t\t\tnodeType,\n\t\t\t...(attrs ? { attrs } : {}),\n\t\t\tpreviousNodeType: block.type,\n\t\t\t...(block.attrs ? { previousAttrs: block.attrs } : {}),\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Inserts a node as a child of the parent at the given path and index. */\n\tinsertNode(parentPath: readonly BlockId[], index: number, node: BlockNode): this {\n\t\tconst step: InsertNodeStep = { type: 'insertNode', parentPath, index, node };\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Removes a node at the given index from the parent at the given path. */\n\tremoveNode(parentPath: readonly BlockId[], index: number): this {\n\t\tconst doc: Document = this.requireDoc('removeNode');\n\n\t\tconst removedNode = resolveChildAt(doc, parentPath, index);\n\t\tif (!removedNode) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node at index ${index} not found under parent path [${parentPath.join(', ')}].`,\n\t\t\t);\n\t\t}\n\n\t\tconst step: RemoveNodeStep = { type: 'removeNode', parentPath, index, removedNode };\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Moves an existing block subtree without changing its identity. Source and\n\t * destination coordinates are resolved against the current working document;\n\t * `toIndex` is an insertion slot before the source is removed.\n\t */\n\tmoveNode(\n\t\tfromParentPath: readonly BlockId[],\n\t\tfromIndex: number,\n\t\ttoParentPath: readonly BlockId[],\n\t\ttoIndex: number,\n\t): this {\n\t\tconst doc: Document = this.requireDoc('moveNode');\n\t\tconst movedNode: BlockNode | undefined = resolveChildAt(doc, fromParentPath, fromIndex);\n\t\tif (!movedNode) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node at index ${fromIndex} not found under source path [${fromParentPath.join(', ')}].`,\n\t\t\t);\n\t\t}\n\t\tif (toParentPath.includes(movedNode.id)) {\n\t\t\tthrow new Error(`Cannot move node \"${movedNode.id}\" into its own subtree.`);\n\t\t}\n\n\t\tconst destinationLength: number =\n\t\t\ttoParentPath.length === 0\n\t\t\t\t? doc.children.length\n\t\t\t\t: (resolveNodeByPath(doc, toParentPath)?.children.length ?? -1);\n\t\tif (!Number.isInteger(toIndex) || toIndex < 0 || toIndex > destinationLength) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid destination index ${toIndex} under path [${toParentPath.join(', ')}].`,\n\t\t\t);\n\t\t}\n\n\t\tconst step: MoveNodeStep = {\n\t\t\ttype: 'moveNode',\n\t\t\tfromParentPath,\n\t\t\tfromIndex,\n\t\t\ttoParentPath,\n\t\t\ttoIndex,\n\t\t\tmovedNode,\n\t\t};\n\t\tif (isMoveNodeNoOp(step)) return this;\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Sets attributes on a node at the given path. */\n\tsetNodeAttr(path: readonly BlockId[], attrs: BlockAttrs): this {\n\t\tconst doc: Document = this.requireDoc('setNodeAttr');\n\n\t\tconst node = resolveNodeByPath(doc, path);\n\t\tif (!node) {\n\t\t\tthrow new Error(`Node not found at path [${path.join(', ')}].`);\n\t\t}\n\n\t\tconst step: SetNodeAttrStep = {\n\t\t\ttype: 'setNodeAttr',\n\t\t\tpath,\n\t\t\tattrs,\n\t\t\t...(node.attrs ? { previousAttrs: node.attrs } : {}),\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Inserts an InlineNode at the given offset within a block. */\n\tinsertInlineNode(blockId: BlockId, offset: number, node: InlineNode): this {\n\t\tconst step: InsertInlineNodeStep = {\n\t\t\ttype: 'insertInlineNode',\n\t\t\tblockId,\n\t\t\toffset,\n\t\t\tnode,\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Removes an InlineNode at the given offset, deriving removedNode from workingDoc. */\n\tremoveInlineNode(blockId: BlockId, offset: number): this {\n\t\tconst doc: Document = this.requireDoc('removeInlineNode');\n\t\tconst block: BlockNode = this.requireBlock(doc, blockId);\n\t\tconst content = getContentAtOffset(block, offset);\n\t\tif (!content || content.kind !== 'inline') {\n\t\t\tthrow new Error(`No InlineNode at offset ${offset} in block \"${blockId}\".`);\n\t\t}\n\t\tconst step: RemoveInlineNodeStep = {\n\t\t\ttype: 'removeInlineNode',\n\t\t\tblockId,\n\t\t\toffset,\n\t\t\tremovedNode: content.node,\n\t\t};\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Sets attributes on an InlineNode at the given offset. */\n\tsetInlineNodeAttr(\n\t\tblockId: BlockId,\n\t\toffset: number,\n\t\tattrs: Readonly<Record<string, string | number | boolean>>,\n\t): this {\n\t\tconst doc: Document = this.requireDoc('setInlineNodeAttr');\n\t\tconst block: BlockNode = this.requireBlock(doc, blockId);\n\t\tconst inlineChildren = getInlineChildren(block);\n\t\tlet pos = 0;\n\t\tfor (const child of inlineChildren) {\n\t\t\tif (isInlineNode(child) && pos === offset) {\n\t\t\t\tconst step: SetInlineNodeAttrStep = {\n\t\t\t\t\ttype: 'setInlineNodeAttr',\n\t\t\t\t\tblockId,\n\t\t\t\t\toffset,\n\t\t\t\t\tattrs,\n\t\t\t\t\tpreviousAttrs: child.attrs,\n\t\t\t\t};\n\t\t\t\tthis.steps.push(step);\n\t\t\t\tthis.advanceDoc(step);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tpos += isInlineNode(child) ? 1 : child.text.length;\n\t\t}\n\t\tthrow new Error(`No InlineNode at offset ${offset} in block \"${blockId}\".`);\n\t}\n\n\t/** Marks this transaction as allowed in readonly mode. */\n\treadonlyAllowed(): this {\n\t\tthis._readonlyAllowed = true;\n\t\treturn this;\n\t}\n\n\t/** Sets the selection for the resulting state. */\n\tsetSelection(selection: EditorSelection): this {\n\t\tthis.selection = selection;\n\t\treturn this;\n\t}\n\n\t/** Sets a NodeSelection for the resulting state. */\n\tsetNodeSelection(nodeId: BlockId, path: readonly BlockId[]): this {\n\t\tthis.selection = createNodeSelection(nodeId, path);\n\t\treturn this;\n\t}\n\n\t/** Sets stored marks for the resulting state. */\n\tsetStoredMarks(marks: readonly Mark[] | null, previousMarks: readonly Mark[] | null): this {\n\t\tthis.steps.push({ type: 'setStoredMarks', marks, previousMarks });\n\t\tthis.storedMarks = marks;\n\t\treturn this;\n\t}\n\n\t/** Builds the final transaction. */\n\tbuild(): Transaction {\n\t\treturn {\n\t\t\tsteps: [...this.steps],\n\t\t\tselectionBefore: this.selectionBefore,\n\t\t\tselectionAfter: this.selection,\n\t\t\tstoredMarksAfter: this.storedMarks,\n\t\t\tmapping: Mapping.from(this.stepMaps),\n\t\t\tforwardStepMaps: [...this.stepMaps],\n\t\t\tmetadata: {\n\t\t\t\torigin: this.origin,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t...(this._readonlyAllowed ? { readonlyAllowed: true } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\t/** Returns the working document or throws if not available. */\n\tprivate requireDoc(methodName: string): Document {\n\t\tif (!this.workingDoc) {\n\t\t\tthrow new Error(\n\t\t\t\t`${methodName} requires a document. Use state.transaction() or provide doc to constructor.`,\n\t\t\t);\n\t\t}\n\t\treturn this.workingDoc;\n\t}\n\n\t/** Finds a block in the document or throws if not found. */\n\tprivate requireBlock(doc: Document, blockId: BlockId): BlockNode {\n\t\tconst block: BlockNode | undefined = findNode(doc, blockId);\n\t\tif (!block) {\n\t\t\tthrow new Error(`Block \"${blockId}\" not found in working document.`);\n\t\t}\n\t\treturn block;\n\t}\n\n\t/**\n\t * Returns the resolved block from the working document, or `null` if\n\t * either the document is unavailable or the block is missing. Used by\n\t * mark builders that want a working-doc-aware planning pass with a\n\t * safe fallback when neither precondition holds.\n\t */\n\tprivate tryGetBlock(blockId: BlockId): BlockNode | null {\n\t\tif (!this.workingDoc) return null;\n\t\treturn findNode(this.workingDoc, blockId) ?? null;\n\t}\n\n\t/**\n\t * Advances the working document by applying a step and records its\n\t * position-mapping. The map is computed against the pre-apply document\n\t * (the only state in which `removeNode` can enumerate descendants), so\n\t * the order is: getStepMap → applyStep.\n\t *\n\t * When no working document is available, falls back to the empty\n\t * document — all step types except `removeNode` produce a `StepMap`\n\t * that does not consult the document, and `removeNode` carries its\n\t * `removedNode` in the step payload, so the fallback is safe.\n\t */\n\tprivate advanceDoc(step: Step): void {\n\t\tconst docBefore: Document = this.workingDoc ?? EMPTY_DOC;\n\t\tthis.stepMaps.push(getStepMap(docBefore, step));\n\t\tif (this.workingDoc) {\n\t\t\tthis.workingDoc = applyStep(this.workingDoc, step);\n\t\t}\n\t}\n}\n\nconst EMPTY_DOC: Document = { children: [] };\n","/**\n * Immutable editor state container.\n * Every mutation produces a new EditorState instance.\n */\n\nimport {\n\ttype BlockNode,\n\ttype ChildNode,\n\ttype Document,\n\ttype Mark,\n\tcreateDocument,\n\tgetBlockLength,\n\tisBlockNode,\n\tisLeafBlock,\n} from '../model/Document.js';\nimport { findNode, findNodePath } from '../model/NodeResolver.js';\nimport type { Schema } from '../model/Schema.js';\nimport { defaultSchema } from '../model/Schema.js';\nimport type { EditorSelection, Position } from '../model/Selection.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreatePosition,\n\tcreateSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport { type BlockId, blockId } from '../model/TypeBrands.js';\nimport { applyStep } from './StepHandlers.js';\nimport type { Transaction } from './Transaction.js';\nimport { TransactionBuilder } from './Transaction.js';\n\nexport class EditorState {\n\treadonly doc: Document;\n\treadonly selection: EditorSelection;\n\treadonly storedMarks: readonly Mark[] | null;\n\treadonly schema: Schema;\n\n\tprivate _blockMap: Map<BlockId, BlockNode> | null = null;\n\tprivate _blockOrder: readonly BlockId[] | null = null;\n\n\tprivate constructor(\n\t\tdoc: Document,\n\t\tselection: EditorSelection,\n\t\tstoredMarks: readonly Mark[] | null,\n\t\tschema: Schema,\n\t) {\n\t\tthis.doc = doc;\n\t\tthis.selection = selection;\n\t\tthis.storedMarks = storedMarks;\n\t\tthis.schema = schema;\n\t}\n\n\t/** Creates a new EditorState with default document. */\n\tstatic create(options?: {\n\t\tdoc?: Document;\n\t\tselection?: EditorSelection;\n\t\tschema?: Schema;\n\t}): EditorState {\n\t\tconst schema = options?.schema ?? defaultSchema();\n\t\tconst doc = options?.doc ?? createDocument();\n\t\tconst firstLeaf = findFirstLeafBlock(doc.children);\n\t\tconst selection =\n\t\t\toptions?.selection ?? createCollapsedSelection(firstLeaf ? firstLeaf.id : blockId(''), 0);\n\n\t\treturn new EditorState(doc, selection, null, schema);\n\t}\n\n\t/** Creates a TransactionBuilder from this state. */\n\ttransaction(\n\t\torigin: 'input' | 'paste' | 'command' | 'history' | 'api' = 'api',\n\t): TransactionBuilder {\n\t\treturn new TransactionBuilder(this.selection, this.storedMarks, origin, this.doc);\n\t}\n\n\t/** Applies a transaction and returns a new EditorState. */\n\tapply(tr: Transaction): EditorState {\n\t\tlet doc = this.doc;\n\n\t\tfor (const step of tr.steps) {\n\t\t\tdoc = applyStep(doc, step);\n\t\t}\n\n\t\tconst selection = validateSelection(doc, tr.selectionAfter);\n\t\treturn new EditorState(doc, selection, tr.storedMarksAfter, this.schema);\n\t}\n\n\t/** Finds a block by its ID anywhere in the tree. Uses a lazy-built Map for O(1) lookup. */\n\tgetBlock(blockId: BlockId): BlockNode | undefined {\n\t\tthis._blockMap ??= buildBlockMap(this.doc);\n\t\treturn this._blockMap.get(blockId);\n\t}\n\n\t/** Returns leaf-block IDs in depth-first order. Cached after first call. */\n\tgetBlockOrder(): readonly BlockId[] {\n\t\tthis._blockOrder ??= buildBlockOrder(this.doc);\n\t\treturn this._blockOrder;\n\t}\n\n\t/** Returns the path (array of block IDs) to a node. */\n\tgetNodePath(nodeId: BlockId): BlockId[] | undefined {\n\t\treturn findNodePath(this.doc, nodeId) as BlockId[] | undefined;\n\t}\n\n\t/** Returns the parent BlockNode of a node, or undefined for top-level blocks. */\n\tgetParent(nodeId: BlockId): BlockNode | undefined {\n\t\tconst path = findNodePath(this.doc, nodeId);\n\t\tif (!path || path.length <= 1) return undefined;\n\t\tconst parentId = path[path.length - 2] as BlockId | undefined;\n\t\tif (!parentId) return undefined;\n\t\treturn findNode(this.doc, parentId);\n\t}\n\n\t/** Returns a new state with the given selection validated against this document. */\n\twithSelection(selection: EditorSelection): EditorState {\n\t\tconst validated: EditorSelection = validateSelection(this.doc, selection);\n\t\treturn new EditorState(this.doc, validated, this.storedMarks, this.schema);\n\t}\n\n\t/** Serializes the state to JSON. */\n\ttoJSON(): { readonly doc: Document; readonly selection: EditorSelection } {\n\t\treturn {\n\t\t\tdoc: this.doc,\n\t\t\tselection: this.selection,\n\t\t};\n\t}\n\n\t/** Deserializes a state from JSON. */\n\tstatic fromJSON(\n\t\tjson: { doc: Document; selection: EditorSelection },\n\t\tschema?: Schema,\n\t): EditorState {\n\t\treturn new EditorState(json.doc, json.selection, null, schema ?? defaultSchema());\n\t}\n}\n\n/** Validates a position against the document, clamping or falling back as needed. */\nfunction validatePosition(doc: Document, pos: Position): Position {\n\tconst block = findNode(doc, pos.blockId);\n\tif (block) {\n\t\tconst length = getBlockLength(block);\n\t\tif (pos.offset > length) {\n\t\t\treturn createPosition(pos.blockId, length, pos.path);\n\t\t}\n\t\treturn pos;\n\t}\n\n\tconst firstBlock = doc.children[0];\n\tif (!firstBlock) return pos;\n\treturn createPosition(firstBlock.id, 0);\n}\n\n/** Validates a selection against the document, ensuring blockIds exist and offsets are in bounds. */\nfunction validateSelection(doc: Document, sel: EditorSelection): EditorSelection {\n\tif (isNodeSelection(sel)) {\n\t\tconst node = findNode(doc, sel.nodeId);\n\t\tif (node) return sel;\n\t\t// Node was deleted — fall back to first leaf block\n\t\treturn fallbackSelection(doc, sel);\n\t}\n\tif (isGapCursor(sel)) {\n\t\tconst node = findNode(doc, sel.blockId);\n\t\tif (node) return sel;\n\t\t// Referenced block was deleted — fall back to first leaf block\n\t\treturn fallbackSelection(doc, sel);\n\t}\n\tconst anchor = validatePosition(doc, sel.anchor);\n\tconst head = validatePosition(doc, sel.head);\n\tif (anchor === sel.anchor && head === sel.head) return sel;\n\treturn createSelection(anchor, head);\n}\n\n/** Returns a collapsed selection on the first leaf block, or the original selection if no blocks exist. */\nfunction fallbackSelection(doc: Document, sel: EditorSelection): EditorSelection {\n\tconst leaf = findFirstLeafBlock(doc.children);\n\tif (!leaf) return sel;\n\treturn createCollapsedSelection(leaf.id, 0);\n}\n\n/** Descends into the first child of each container block to find the first leaf block. */\nfunction findFirstLeafBlock(children: readonly ChildNode[]): BlockNode | null {\n\tfor (const child of children) {\n\t\tif (!isBlockNode(child)) continue;\n\t\tif (isLeafBlock(child)) return child;\n\t\tconst nested: BlockNode | null = findFirstLeafBlock(child.children);\n\t\tif (nested) return nested;\n\t}\n\treturn null;\n}\n\n/** Recursively builds a Map of blockId → BlockNode for all nodes in the tree. */\nfunction buildBlockMap(doc: Document): Map<BlockId, BlockNode> {\n\tconst map = new Map<BlockId, BlockNode>();\n\tfunction walk(blocks: readonly ChildNode[]): void {\n\t\tfor (const child of blocks) {\n\t\t\tif (isBlockNode(child)) {\n\t\t\t\tmap.set(child.id, child);\n\t\t\t\twalk(child.children);\n\t\t\t}\n\t\t}\n\t}\n\twalk(doc.children);\n\treturn map;\n}\n\n/** Returns leaf-block IDs in depth-first order. */\nfunction buildBlockOrder(doc: Document): BlockId[] {\n\tconst order: BlockId[] = [];\n\tfunction walk(blocks: readonly ChildNode[]): void {\n\t\tfor (const child of blocks) {\n\t\t\tif (isBlockNode(child)) {\n\t\t\t\tif (isLeafBlock(child)) {\n\t\t\t\t\torder.push(child.id);\n\t\t\t\t} else {\n\t\t\t\t\twalk(child.children);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\twalk(doc.children);\n\treturn order;\n}\n","/**\n * Pure functions for reading and writing editor content in various formats.\n *\n * Extracted from NotectlEditor to keep the Web Component shell thin.\n * Every function is stateless — it receives EditorState / Document and returns data.\n */\n\nimport type { BlockAttrs, BlockNode, ChildNode } from '../model/Document.js';\nimport {\n\ttype Document,\n\tblockAttrsEqual,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgetBlockChildren,\n\tgetBlockLength,\n\tgetBlockText,\n\tisInlineNode,\n\tisLeafBlock,\n\tisTextNode,\n} from '../model/Document.js';\nimport { formatHTML, normalizeHTMLId } from '../model/HTMLUtils.js';\nimport { schemaFromRegistry } from '../model/Schema.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { type BlockId, nodeType } from '../model/TypeBrands.js';\nimport { parseHTMLToDocument } from '../serialization/DocumentParser.js';\nimport {\n\tserializeDocumentToCSS,\n\tserializeDocumentToHTML,\n} from '../serialization/DocumentSerializer.js';\nimport type {\n\tMarkdownParseOptions,\n\tMarkdownSerializeOptions,\n} from '../serialization/MarkdownTypes.js';\nimport type {\n\tContentCSSResult,\n\tContentHTMLOptions,\n\tSetContentHTMLOptions,\n} from '../serialization/index.js';\nimport { EditorState } from '../state/EditorState.js';\n\n/** Returns the document as JSON from the given state. */\nexport function getEditorJSON(state: EditorState): Document {\n\treturn state.doc;\n}\n\n/** Replaces editor state from a JSON document. */\nexport function setEditorJSON(\n\tdoc: Document,\n\tregistry: SchemaRegistry | undefined,\n\treplaceState: (state: EditorState) => void,\n): void {\n\tconst withHTMLIds: Document = normalizeHTMLIds(doc);\n\tconst withMarks: Document = defaultMissingMarks(withHTMLIds);\n\tconst normalized: Document = registry ? normalizeCompositeBlocks(withMarks, registry) : withMarks;\n\tconst schema = registry ? schemaFromRegistry(registry) : undefined;\n\tconst state: EditorState = EditorState.create({\n\t\tdoc: normalized,\n\t\tschema,\n\t});\n\treplaceState(state);\n}\n\n/**\n * Validates semantic HTML IDs at the external JSON boundary. Directly supplied\n * objects do not necessarily come from {@link createBlockNode}, so this pass\n * establishes the same invariant recursively while preserving object identity\n * for documents that are already valid.\n */\nfunction normalizeHTMLIds(doc: Document): Document {\n\tconst children: readonly BlockNode[] = mapPreservingIdentity(doc.children, normalizeBlockHTMLId);\n\treturn children === doc.children ? doc : { children };\n}\n\nfunction normalizeBlockHTMLId(block: BlockNode): BlockNode {\n\tconst children: readonly ChildNode[] = mapPreservingIdentity(block.children, (child) =>\n\t\tisTextNode(child) || isInlineNode(child) ? child : normalizeBlockHTMLId(child),\n\t);\n\tconst rawHTMLId: unknown = (block as { readonly htmlId?: unknown }).htmlId;\n\tconst htmlId: string | undefined = normalizeHTMLId(rawHTMLId);\n\tconst hasHTMLId: boolean = Object.prototype.hasOwnProperty.call(block, 'htmlId');\n\tconst htmlIdUnchanged: boolean = rawHTMLId === htmlId && (htmlId !== undefined || !hasHTMLId);\n\tif (children === block.children && htmlIdUnchanged) return block;\n\n\tconst { htmlId: _rawHTMLId, ...rest } = block as BlockNode & {\n\t\treadonly htmlId?: unknown;\n\t};\n\treturn {\n\t\t...rest,\n\t\t...(htmlId ? { htmlId } : {}),\n\t\tchildren,\n\t};\n}\n\n/**\n * Establishes the model invariant that every text and inline node carries a\n * `marks` array. External JSON may omit the field — notably documents persisted\n * before inline nodes gained marks (#197) — and the view layer reads\n * `child.marks` unconditionally, so a missing array must be defaulted here at\n * the JSON boundary rather than crash the reconciler later. Untouched nodes\n * keep their identity; a document that already satisfies the invariant is\n * returned as-is.\n */\nfunction defaultMissingMarks(doc: Document): Document {\n\tconst children: readonly BlockNode[] = mapPreservingIdentity(doc.children, defaultBlockMarks);\n\treturn children === doc.children ? doc : { children };\n}\n\nfunction defaultBlockMarks(block: BlockNode): BlockNode {\n\tif (!block.children) return block;\n\tconst children: readonly ChildNode[] = mapPreservingIdentity(block.children, (child) => {\n\t\tif (isTextNode(child) || isInlineNode(child)) {\n\t\t\treturn child.marks ? child : { ...child, marks: [] };\n\t\t}\n\t\treturn defaultBlockMarks(child);\n\t});\n\treturn children === block.children ? block : { ...block, children };\n}\n\n/** Maps an array, returning the original array when no element changed. */\nfunction mapPreservingIdentity<T>(items: readonly T[], map: (item: T) => T): readonly T[] {\n\tlet changed = false;\n\tconst mapped: readonly T[] = items.map((item) => {\n\t\tconst next: T = map(item);\n\t\tif (next !== item) changed = true;\n\t\treturn next;\n\t});\n\treturn changed ? mapped : items;\n}\n\n/**\n * Normalizes composite blocks so bare inline children are wrapped in paragraphs.\n * Composite blocks (those with a `content` rule, e.g. table_cell) must contain\n * block-level children. When JSON input provides inline children directly,\n * this wraps them in a paragraph to enforce schema consistency.\n */\nexport function normalizeCompositeBlocks(doc: Document, registry: SchemaRegistry): Document {\n\tconst children: readonly BlockNode[] = doc.children.map((block) =>\n\t\tnormalizeBlock(block, registry),\n\t);\n\treturn { children };\n}\n\n/** Whether a content rule permits block-level children (beyond bare `text`). */\nfunction allowsBlockChildren(content: { readonly allow: readonly string[] }): boolean {\n\treturn content.allow.some((entry) => entry !== 'text');\n}\n\nfunction normalizeBlock(block: BlockNode, registry: SchemaRegistry): BlockNode {\n\tconst spec = registry.getNodeSpec(block.type);\n\tlet normalizedBlock: BlockNode = block;\n\n\tif (spec?.content && allowsBlockChildren(spec.content)) {\n\t\tconst allowsInline: boolean = spec.content.allow.includes('text');\n\n\t\tif (isLeafBlock(block)) {\n\t\t\t// A hybrid block (list_item, #194) with inline children is already a valid\n\t\t\t// leaf. A composite with min: 0 may intentionally be empty (for example a\n\t\t\t// table row fully covered by rowspans); other pure composites get a paragraph.\n\t\t\tif (!allowsInline && !(block.children.length === 0 && spec.content.min === 0)) {\n\t\t\t\tconst children: readonly ChildNode[] | undefined =\n\t\t\t\t\tblock.children.length > 0 ? block.children : undefined;\n\t\t\t\tconst paragraph: BlockNode = createBlockNode(nodeType('paragraph'), children);\n\t\t\t\tnormalizedBlock = createBlockNode(\n\t\t\t\t\tblock.type,\n\t\t\t\t\t[paragraph],\n\t\t\t\t\tblock.id,\n\t\t\t\t\tblock.attrs,\n\t\t\t\t\tblock.htmlId,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// Block children → recurse, so nested composites get their own invariants.\n\t\t\tconst normalized: readonly ChildNode[] = block.children.map((child) => {\n\t\t\t\tif (isTextNode(child) || isInlineNode(child)) return child;\n\t\t\t\treturn normalizeBlock(child, registry);\n\t\t\t});\n\t\t\tnormalizedBlock = createBlockNode(\n\t\t\t\tblock.type,\n\t\t\t\tnormalized,\n\t\t\t\tblock.id,\n\t\t\t\tblock.attrs,\n\t\t\t\tblock.htmlId,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst normalizedAttrs: BlockAttrs | undefined = spec?.normalizeAttrs?.(normalizedBlock);\n\tif (spec?.normalizeAttrs && !blockAttrsEqual(normalizedBlock.attrs, normalizedAttrs)) {\n\t\tconst { attrs: _attrs, ...withoutAttrs } = normalizedBlock;\n\t\tnormalizedBlock = {\n\t\t\t...withoutAttrs,\n\t\t\t...(normalizedAttrs ? { attrs: normalizedAttrs } : {}),\n\t\t};\n\t}\n\treturn spec?.normalizeNode?.(normalizedBlock) ?? normalizedBlock;\n}\n\n/** Returns sanitized HTML representation of the document. */\nexport function getEditorContentHTML(\n\tstate: EditorState,\n\tregistry: SchemaRegistry | undefined,\n\toptions?: ContentHTMLOptions,\n): string | ContentCSSResult {\n\tconst doc: Document = state.doc;\n\n\tif (options?.cssMode === 'classes') {\n\t\tconst result: ContentCSSResult = serializeDocumentToCSS(doc, registry, options);\n\t\treturn options.pretty\n\t\t\t? { html: formatHTML(result.html), css: result.css, styleMap: result.styleMap }\n\t\t\t: result;\n\t}\n\n\tconst html: string = serializeDocumentToHTML(doc, registry, options);\n\treturn options?.pretty ? formatHTML(html) : html;\n}\n\n/** Sets content from HTML (sanitized). Accepts optional `styleMap` for class-based round-trip. */\nexport function setEditorContentHTML(\n\thtml: string,\n\tregistry: SchemaRegistry | undefined,\n\treplaceState: (state: EditorState) => void,\n\toptions?: SetContentHTMLOptions,\n): void {\n\tconst doc: Document = parseHTMLToDocument(html, registry, options);\n\tsetEditorJSON(doc, registry, replaceState);\n}\n\n/**\n * Returns a Markdown representation of the document.\n *\n * The engine is loaded via dynamic `import()` so it stays code-split out of the\n * core bundle (D13) — this helper lives in `editor/`, which is statically\n * reachable from the web component, so a static import here would defeat the\n * split. Hence `async`.\n */\nexport async function getEditorContentMarkdown(\n\tstate: EditorState,\n\tregistry: SchemaRegistry | undefined,\n\toptions?: MarkdownSerializeOptions,\n): Promise<string> {\n\tconst { serializeDocumentToMarkdown } = await import('../serialization/MarkdownSerializer.js');\n\treturn serializeDocumentToMarkdown(state.doc, registry, options);\n}\n\n/**\n * Replaces editor content from Markdown.\n *\n * Reuses existing top-level block IDs in document order (exactly as\n * {@link setEditorText}), so `setContentMarkdown(getContentMarkdown())`\n * preserves block identity and keeps the caret stable for unchanged blocks per\n * the round-trip identity contract (ARCHITECTURE §9.2, D10). Async + lazy: the\n * parser is reached only via dynamic `import()` to keep the engine code-split\n * out of the core bundle (D13).\n */\nexport async function setEditorContentMarkdown(\n\tmarkdown: string,\n\tcurrentState: EditorState,\n\tregistry: SchemaRegistry | undefined,\n\treplaceState: (state: EditorState) => void,\n\toptions?: MarkdownParseOptions,\n): Promise<void> {\n\tconst { parseMarkdownToDocument } = await import('../serialization/MarkdownParser.js');\n\tconst parsed: Document = parseMarkdownToDocument(markdown, registry, options);\n\n\tconst existingIds: readonly BlockId[] = currentState.doc.children.map((b) => b.id);\n\tconst reIdentified: readonly BlockNode[] = parsed.children.map((block, idx) => {\n\t\tconst existing: BlockId | undefined = existingIds[idx];\n\t\treturn existing\n\t\t\t? createBlockNode(block.type, block.children, existing, block.attrs, block.htmlId)\n\t\t\t: block;\n\t});\n\n\tsetEditorJSON({ children: reIdentified }, registry, replaceState);\n}\n\n/** Returns plain text content, descending into container blocks (e.g. blockquote). */\nexport function getEditorText(state: EditorState): string {\n\treturn state.doc.children.map(blockTreeText).join('\\n');\n}\n\n/**\n * Extracts plain text from a block and all its descendants. Leaf blocks yield\n * their inline text; container blocks (blockquote, table, …) join their child\n * blocks' text with newlines, mirroring the line-per-block convention.\n */\nfunction blockTreeText(block: BlockNode): string {\n\tif (isLeafBlock(block)) return getBlockText(block);\n\treturn getBlockChildren(block).map(blockTreeText).join('\\n');\n}\n\n/**\n * Replaces editor content from plain text. Each `\\n` becomes a paragraph.\n *\n * Existing top-level block IDs are reused in document order so that the\n * caret-preserving `replaceState()` keeps the cursor on the same block\n * across `setText(getText())` round-trips. Excess paragraphs receive\n * fresh IDs. When the new text is identical to the current text, the\n * call is a no-op — selection and history remain untouched.\n */\nexport function setEditorText(\n\tvalue: string,\n\tcurrentState: EditorState,\n\tregistry: SchemaRegistry | undefined,\n\treplaceState: (state: EditorState) => void,\n): void {\n\tif (value === getEditorText(currentState)) return;\n\n\tconst lines: readonly string[] = value.split('\\n');\n\tconst existingIds: readonly BlockId[] = currentState.doc.children.map((b) => b.id);\n\tconst blocks: readonly BlockNode[] = lines.map((line, idx) =>\n\t\tcreateBlockNode(nodeType('paragraph'), [createTextNode(line)], existingIds[idx]),\n\t);\n\tconst schema = registry ? schemaFromRegistry(registry) : undefined;\n\tconst next: EditorState = EditorState.create({\n\t\tdoc: { children: blocks },\n\t\tschema,\n\t});\n\treplaceState(next);\n}\n\n/** Returns true if the document is empty (zero blocks, or a single empty paragraph). */\nexport function isEditorEmpty(doc: Document | undefined): boolean {\n\tif (!doc) return true;\n\tif (doc.children.length === 0) return true;\n\tif (doc.children.length > 1) return false;\n\tconst block = doc.children[0];\n\tif (!block) return true;\n\t// getBlockLength counts InlineNodes (e.g. an inline formula) as width 1, so a\n\t// paragraph holding only an atomic inline node is correctly treated as non-empty.\n\treturn block.type === 'paragraph' && getBlockLength(block) === 0;\n}\n","/**\n * Resolution logic for the `markdown` editor config knob.\n *\n * The public `markdown` option controls notectl's *implicit* Markdown behavior:\n * the live \"shorthand\" typing transforms (`# ` -> heading, `**bold**` -> bold,\n * `- ` -> list, ...) and Markdown auto-detection on paste. It does NOT affect\n * the explicit `getContentMarkdown()` / `setContentMarkdown()` API, the toolbar\n * buttons, or keyboard shortcuts, which stay available regardless. Disabling it\n * removes the typed shorthand, not the underlying bold/heading capability.\n *\n * Boundary note: today every registered input rule is a Markdown shorthand, so\n * the global gate disables exactly the Markdown shorthands. A future\n * non-Markdown input rule (e.g. a typography plugin turning `...` into an\n * ellipsis) would also be caught by this switch; such a rule should ship its\n * own opt-out instead of relying on the `markdown` knob.\n */\n\n/** Fine-grained form of the `markdown` config option. */\nexport interface MarkdownConfig {\n\t/**\n\t * Live Markdown \"shorthand\" transforms while typing (`# ` -> heading,\n\t * `**bold**` -> bold, `- ` -> list, `> ` -> quote, etc.). Default `true`.\n\t */\n\treadonly shorthand?: boolean;\n\t/**\n\t * Markdown auto-detection when pasting plain text. `auto` converts text with\n\t * strong block-level Markdown signals (fenced code, GFM table, a run of\n\t * list/heading/quote markers) into rich content; `never` always pastes raw.\n\t * Default `'auto'`.\n\t */\n\treadonly paste?: 'auto' | 'never';\n}\n\n/** Fully-resolved Markdown behavior consumed by the input and paste pipelines. */\nexport interface ResolvedMarkdownMode {\n\treadonly shorthand: boolean;\n\treadonly paste: 'auto' | 'never';\n}\n\nconst ENABLED: ResolvedMarkdownMode = { shorthand: true, paste: 'auto' };\nconst DISABLED: ResolvedMarkdownMode = { shorthand: false, paste: 'never' };\n\n/**\n * Resolves the `markdown` config union into concrete `{ shorthand, paste }` flags.\n *\n * - `undefined` / `true` -> both on (`shorthand: true`, `paste: 'auto'`).\n * - `false` -> both off (`shorthand: false`, `paste: 'never'`).\n * - object -> each axis resolved independently from its default (shorthand on,\n * paste auto); only the keys you set are overridden.\n */\nexport function resolveMarkdownMode(\n\tvalue: boolean | MarkdownConfig | undefined,\n): ResolvedMarkdownMode {\n\tif (value === undefined || value === true) return ENABLED;\n\tif (value === false) return DISABLED;\n\treturn {\n\t\tshorthand: value.shorthand ?? true,\n\t\tpaste: value.paste ?? 'auto',\n\t};\n}\n","/**\n * Manages runtime configuration and HTML attribute changes for the editor.\n *\n * Consolidates the duplicated logic between `attributeChangedCallback` and\n * `configure()` into a single source of truth.\n */\n\nimport { type PaperSize, isValidPaperSize } from '../model/PaperSize.js';\nimport type { PluginManager } from '../plugins/PluginManager.js';\nimport type { NotectlEditorConfig } from './EditorConfig.js';\nimport type { EditorThemeController } from './EditorThemeController.js';\nimport { resolveMarkdownMode } from './MarkdownMode.js';\nimport { type Theme, ThemePreset } from './theme/ThemeTokens.js';\n\n/** External dependencies the config controller needs to apply changes. */\nexport interface ConfigControllerDeps {\n\treadonly contentElement: HTMLElement | null;\n\treadonly editorWrapper: HTMLElement | null;\n\treadonly pluginManager: PluginManager | null;\n\treadonly themeController: EditorThemeController | null;\n\treadonly applyPaperSize: (size: PaperSize | undefined) => void;\n}\n\nexport class EditorConfigController {\n\tprivate config: NotectlEditorConfig = {};\n\n\t/** Returns the current config. */\n\tgetConfig(): NotectlEditorConfig {\n\t\treturn this.config;\n\t}\n\n\t/** Replaces the entire config (used during init). */\n\tsetConfig(config: NotectlEditorConfig): void {\n\t\tthis.config = config;\n\t}\n\n\t/** Merges a partial config into the current config. */\n\tmergeConfig(partial: Partial<NotectlEditorConfig>): void {\n\t\tthis.config = { ...this.config, ...partial };\n\t}\n\n\t/** Handles an HTML attribute change on the custom element. */\n\tapplyAttribute(name: string, newValue: string | null, deps: ConfigControllerDeps): void {\n\t\tif (name === 'placeholder') {\n\t\t\tconst placeholder: string | undefined = newValue ?? undefined;\n\t\t\tthis.config = { ...this.config, placeholder };\n\t\t\tthis.applyPlaceholder(placeholder, deps.contentElement);\n\t\t}\n\n\t\tif (name === 'readonly') {\n\t\t\tconst isReadonly: boolean = newValue !== null;\n\t\t\tthis.applyReadonly(isReadonly, deps.contentElement, deps.pluginManager);\n\t\t\tthis.config = { ...this.config, readonly: isReadonly };\n\t\t}\n\n\t\tif (name === 'theme') {\n\t\t\tconst theme: ThemePreset | undefined = (newValue as ThemePreset | null) ?? undefined;\n\t\t\tthis.config = { ...this.config, theme };\n\t\t\tdeps.themeController?.apply(theme ?? ThemePreset.Light);\n\t\t}\n\n\t\tif (name === 'paper-size') {\n\t\t\tif (newValue === null) {\n\t\t\t\tdeps.applyPaperSize(undefined);\n\t\t\t\tthis.config = { ...this.config, paperSize: undefined };\n\t\t\t} else if (isValidPaperSize(newValue)) {\n\t\t\t\tdeps.applyPaperSize(newValue);\n\t\t\t\tthis.config = { ...this.config, paperSize: newValue };\n\t\t\t}\n\t\t}\n\n\t\tif (name === 'dir') {\n\t\t\tthis.applyDir(\n\t\t\t\tnewValue === 'ltr' || newValue === 'rtl' ? newValue : undefined,\n\t\t\t\tdeps.contentElement,\n\t\t\t\tdeps.editorWrapper,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Applies a partial runtime config update. */\n\tapplyRuntimeConfig(partial: Partial<NotectlEditorConfig>, deps: ConfigControllerDeps): void {\n\t\tif (partial.placeholder !== undefined) {\n\t\t\tthis.applyPlaceholder(partial.placeholder, deps.contentElement);\n\t\t}\n\n\t\tif (partial.readonly !== undefined) {\n\t\t\tthis.applyReadonly(partial.readonly, deps.contentElement, deps.pluginManager);\n\t\t}\n\n\t\tif ('paperSize' in partial) {\n\t\t\tdeps.applyPaperSize(partial.paperSize);\n\t\t}\n\n\t\tif ('dir' in partial) {\n\t\t\tthis.applyDir(partial.dir, deps.contentElement, deps.editorWrapper);\n\t\t}\n\n\t\tthis.config = { ...this.config, ...partial };\n\t}\n\n\t/** Updates the theme in config and applies it. */\n\tapplyTheme(theme: ThemePreset | Theme, themeController: EditorThemeController | null): void {\n\t\tthis.config = { ...this.config, theme };\n\t\tthemeController?.apply(theme);\n\t}\n\n\t/** Returns the current theme setting. */\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.config.theme ?? ThemePreset.Light;\n\t}\n\n\t/** Returns whether the editor is read-only. */\n\tget isReadOnly(): boolean {\n\t\treturn this.config.readonly ?? false;\n\t}\n\n\t/**\n\t * Whether live Markdown \"shorthand\" typing transforms are active (`true` by\n\t * default). Resolved from the `markdown` config option.\n\t */\n\tget markdownShorthand(): boolean {\n\t\treturn resolveMarkdownMode(this.config.markdown).shorthand;\n\t}\n\n\t/**\n\t * Markdown paste auto-detection mode (`auto` by default). Resolved from the\n\t * `markdown` config option.\n\t */\n\tget pasteMarkdown(): 'auto' | 'never' {\n\t\treturn resolveMarkdownMode(this.config.markdown).paste;\n\t}\n\n\t/** Returns the currently configured paper size. */\n\tgetPaperSize(): PaperSize | undefined {\n\t\treturn this.config.paperSize;\n\t}\n\n\t// --- Private helpers ---\n\n\tprivate applyPlaceholder(value: string | undefined, contentEl: HTMLElement | null): void {\n\t\tif (!contentEl) return;\n\t\tconst fallback = contentEl.getAttribute('data-default-placeholder') ?? '';\n\t\tcontentEl.setAttribute('data-placeholder', value ?? fallback);\n\t}\n\n\tprivate applyReadonly(\n\t\treadonly: boolean,\n\t\tcontentEl: HTMLElement | null,\n\t\tpm: PluginManager | null,\n\t): void {\n\t\tif (!contentEl) return;\n\t\tcontentEl.contentEditable = readonly ? 'false' : 'true';\n\t\tif (readonly) {\n\t\t\tcontentEl.setAttribute('aria-readonly', 'true');\n\t\t} else {\n\t\t\tcontentEl.removeAttribute('aria-readonly');\n\t\t}\n\t\tpm?.setReadOnly(readonly);\n\t}\n\n\tprivate applyDir(\n\t\tdir: 'ltr' | 'rtl' | undefined,\n\t\tcontentEl: HTMLElement | null,\n\t\twrapper: HTMLElement | null,\n\t): void {\n\t\tif (!contentEl) return;\n\t\tif (dir) {\n\t\t\twrapper?.setAttribute('dir', dir);\n\t\t\tcontentEl.setAttribute('dir', dir);\n\t\t} else {\n\t\t\twrapper?.removeAttribute('dir');\n\t\t\tcontentEl.removeAttribute('dir');\n\t\t}\n\t}\n}\n","/**\n * Typed event emitter for the editor component.\n *\n * Extracted from NotectlEditor to keep the Web Component shell thin.\n * Provides type-safe on/off/emit for a fixed EventMap.\n */\n\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nexport interface StateChangeEvent {\n\toldState: EditorState;\n\tnewState: EditorState;\n\ttransaction: Transaction;\n}\n\nexport type EditorEventMap = {\n\tstateChange: StateChangeEvent;\n\tselectionChange: { selection: import('../model/Selection.js').EditorSelection };\n\tfocus: undefined;\n\tblur: undefined;\n\tready: undefined;\n};\n\ntype EventCallback<T> = (payload: T) => void;\n\nexport class EditorEventEmitter {\n\tprivate readonly listeners: Map<string, Set<EventCallback<unknown>>> = new Map();\n\n\t/** Registers an event listener. */\n\ton<K extends keyof EditorEventMap>(event: K, callback: EventCallback<EditorEventMap[K]>): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)?.add(callback as EventCallback<unknown>);\n\t}\n\n\t/** Removes an event listener. */\n\toff<K extends keyof EditorEventMap>(event: K, callback: EventCallback<EditorEventMap[K]>): void {\n\t\tthis.listeners.get(event)?.delete(callback as EventCallback<unknown>);\n\t}\n\n\t/** Emits an event to all registered listeners. */\n\temit<K extends keyof EditorEventMap>(event: K, payload: EditorEventMap[K]): void {\n\t\tconst set: Set<EventCallback<unknown>> | undefined = this.listeners.get(event);\n\t\tif (!set) return;\n\n\t\tfor (const cb of set) {\n\t\t\t(cb as EventCallback<EditorEventMap[K]>)(payload);\n\t\t}\n\t}\n\n\t/** Removes all listeners. */\n\tclear(): void {\n\t\tthis.listeners.clear();\n\t}\n}\n","/**\n * Builds a document fragment for a text selection while preserving composite\n * block structure (for example tables) and trimming only the selected leaf content.\n */\n\nimport type { BlockNode, Document, InlineNode, TextNode } from '../model/Document.js';\nimport {\n\tgetBlockChildren,\n\tgetBlockContentSegmentsInRange,\n\tgetBlockLength,\n\tisLeafBlock,\n\tsegmentsToInlineChildren,\n} from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { Selection } from '../model/Selection.js';\nimport { selectionRange } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\n\ninterface SelectionBoundary {\n\treadonly firstBlockId: BlockId;\n\treadonly firstOffset: number;\n\treadonly lastBlockId: BlockId;\n\treadonly lastOffset: number;\n\treadonly selectedLeafIds: ReadonlySet<BlockId>;\n}\n\n/**\n * Creates a partial document containing only the selected content.\n * Composite ancestors are preserved, while first/last leaf blocks are trimmed\n * to the exact selection offsets.\n */\nexport function buildSelectionDocument(\n\tstate: EditorState,\n\tselection: Selection,\n\tregistry?: SchemaRegistry,\n): Document {\n\tconst blockOrder = state.getBlockOrder();\n\tconst range = selectionRange(selection, blockOrder);\n\tconst fromIdx: number = blockOrder.indexOf(range.from.blockId);\n\tconst toIdx: number = blockOrder.indexOf(range.to.blockId);\n\tif (fromIdx === -1 || toIdx === -1 || fromIdx > toIdx) {\n\t\treturn { children: [] };\n\t}\n\n\tconst fromPath = state.getNodePath(range.from.blockId);\n\tconst toPath = state.getNodePath(range.to.blockId);\n\tconst fromRootId = fromPath?.[0];\n\tconst toRootId = toPath?.[0];\n\tif (!fromRootId || !toRootId) {\n\t\treturn { children: [] };\n\t}\n\n\tconst fromRootIdx: number = state.doc.children.findIndex((child) => child.id === fromRootId);\n\tconst toRootIdx: number = state.doc.children.findIndex((child) => child.id === toRootId);\n\tif (fromRootIdx === -1 || toRootIdx === -1 || fromRootIdx > toRootIdx) {\n\t\treturn { children: [] };\n\t}\n\n\tconst boundary: SelectionBoundary = {\n\t\tfirstBlockId: range.from.blockId,\n\t\tfirstOffset: range.from.offset,\n\t\tlastBlockId: range.to.blockId,\n\t\tlastOffset: range.to.offset,\n\t\tselectedLeafIds: new Set(blockOrder.slice(fromIdx, toIdx + 1)),\n\t};\n\n\tconst children: BlockNode[] = [];\n\tfor (let i = fromRootIdx; i <= toRootIdx; i++) {\n\t\tconst root = state.doc.children[i];\n\t\tif (!root) continue;\n\t\tconst sliced = sliceBlock(root, boundary, registry);\n\t\tif (sliced) children.push(sliced);\n\t}\n\n\treturn { children };\n}\n\nfunction sliceBlock(\n\tblock: BlockNode,\n\tboundary: SelectionBoundary,\n\tregistry?: SchemaRegistry,\n): BlockNode | null {\n\tif (isLeafBlock(block)) {\n\t\tif (!boundary.selectedLeafIds.has(block.id)) return null;\n\t\treturn sliceLeafBlock(block, boundary);\n\t}\n\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(block);\n\tconst slicedChildren: BlockNode[] = [];\n\tlet changed = blockChildren.length !== block.children.length;\n\n\tfor (const child of blockChildren) {\n\t\tconst slicedChild = sliceBlock(child, boundary, registry);\n\t\tif (slicedChild) {\n\t\t\tslicedChildren.push(slicedChild);\n\t\t\tif (slicedChild !== child) changed = true;\n\t\t} else {\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\tif (slicedChildren.length === 0) return null;\n\tif (!changed) return block;\n\n\tconst { htmlId: _htmlId, ...rest } = block;\n\tconst slice: BlockNode = {\n\t\t...rest,\n\t\tchildren: slicedChildren,\n\t};\n\treturn registry?.getNodeSpec(block.type)?.transformSelectionSlice?.(block, slice) ?? slice;\n}\n\nfunction sliceLeafBlock(block: BlockNode, boundary: SelectionBoundary): BlockNode {\n\tconst fullLength: number = getBlockLength(block);\n\tconst from: number = block.id === boundary.firstBlockId ? boundary.firstOffset : 0;\n\tconst to: number = block.id === boundary.lastBlockId ? boundary.lastOffset : fullLength;\n\n\tif (from === 0 && to === fullLength) {\n\t\treturn block;\n\t}\n\n\tconst segments = getBlockContentSegmentsInRange(block, from, to);\n\tconst children: readonly (TextNode | InlineNode)[] = segmentsToInlineChildren(segments);\n\n\tconst { htmlId: _htmlId, ...rest } = block;\n\treturn {\n\t\t...rest,\n\t\tchildren,\n\t};\n}\n","/**\n * Clipboard handler: intercepts copy and cut events, serializes the current\n * selection to clipboard data. Works with both NodeSelection (void blocks)\n * and text selections.\n */\n\nimport { deleteNodeSelection, deleteSelectionCommand } from '../commands/Commands.js';\nimport type { BlockNode, Mark } from '../model/Document.js';\nimport {\n\tforEachInlineChildInRange,\n\tgetBlockChildren,\n\tgetBlockLength,\n\tgetBlockText,\n\tisLeafBlock,\n} from '../model/Document.js';\nimport { findNodePath } from '../model/NodeResolver.js';\nimport type { RichBlockData, RichSegment } from '../model/RichBlockData.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { Selection } from '../model/Selection.js';\nimport {\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n\tisTextSelection,\n\tselectionRange,\n} from '../model/Selection.js';\nimport { isEventFromEditorContent } from '../platform/EditorEventBoundary.js';\nimport { setHTMLIdOnFirstTag } from '../serialization/DocumentSerializer.js';\nimport {\n\tbuildMarkOrder,\n\tserializeDocumentToHTML,\n\tserializeMarksToHTML,\n} from '../serialization/index.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { DispatchFn, GetStateFn } from './InputHandler.js';\nimport { buildSelectionDocument } from './SelectionDocument.js';\n\nexport interface ClipboardHandlerOptions {\n\treadonly getState: GetStateFn;\n\treadonly dispatch: DispatchFn;\n\treadonly schemaRegistry?: SchemaRegistry;\n\t/** Force-sync DOM selection to state (selectionchange may fire async). */\n\treadonly syncSelection?: () => void;\n\treadonly isReadOnly?: () => boolean;\n}\n\n/** Custom MIME type for internal block copy/paste round-trips (void/NodeSelection). */\nconst BLOCK_MIME = 'application/x-notectl-block';\n\nexport class ClipboardHandler {\n\tprivate readonly getState: GetStateFn;\n\tprivate readonly dispatch: DispatchFn;\n\tprivate readonly schemaRegistry?: SchemaRegistry;\n\tprivate readonly syncSelection?: () => void;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly handleCopy: (e: ClipboardEvent) => void;\n\tprivate readonly handleCut: (e: ClipboardEvent) => void;\n\n\tconstructor(\n\t\tprivate readonly element: HTMLElement,\n\t\toptions: ClipboardHandlerOptions,\n\t) {\n\t\tthis.getState = options.getState;\n\t\tthis.dispatch = options.dispatch;\n\t\tthis.schemaRegistry = options.schemaRegistry;\n\t\tthis.syncSelection = options.syncSelection;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\n\t\tthis.handleCopy = this.onCopy.bind(this);\n\t\tthis.handleCut = this.onCut.bind(this);\n\t\telement.addEventListener('copy', this.handleCopy);\n\t\telement.addEventListener('cut', this.handleCut);\n\t}\n\n\tprivate onCopy(e: ClipboardEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\t// Sync DOM selection → state first; selectionchange may lag behind\n\t\tthis.syncSelection?.();\n\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\n\t\t// GapCursor: nothing to copy\n\t\tif (isGapCursor(sel)) return;\n\n\t\t// Collapsed selection: let the browser handle it\n\t\tif (isTextSelection(sel) && isCollapsed(sel)) return;\n\n\t\te.preventDefault();\n\t\tconst clipboardData = e.clipboardData;\n\t\tif (!clipboardData) return;\n\n\t\tif (isNodeSelection(sel)) {\n\t\t\tthis.writeNodeSelectionToClipboard(clipboardData, state);\n\t\t} else {\n\t\t\tthis.writeTextSelectionToClipboard(clipboardData, state);\n\t\t}\n\t}\n\n\tprivate onCut(e: ClipboardEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\tif (this.isReadOnly()) return;\n\n\t\t// Sync DOM selection → state first; selectionchange may lag behind\n\t\tthis.syncSelection?.();\n\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\n\t\t// GapCursor: nothing to cut\n\t\tif (isGapCursor(sel)) return;\n\n\t\t// Collapsed selection: nothing to cut\n\t\tif (isTextSelection(sel) && isCollapsed(sel)) return;\n\n\t\te.preventDefault();\n\t\tconst clipboardData = e.clipboardData;\n\t\tif (!clipboardData) return;\n\n\t\t// Write to clipboard first\n\t\tif (isNodeSelection(sel)) {\n\t\t\tthis.writeNodeSelectionToClipboard(clipboardData, state);\n\t\t\tconst tr = deleteNodeSelection(state, sel);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t} else {\n\t\t\tthis.writeTextSelectionToClipboard(clipboardData, state);\n\t\t\tconst tr = deleteSelectionCommand(state);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t}\n\t}\n\n\tprivate writeNodeSelectionToClipboard(clipboardData: DataTransfer, state: EditorState): void {\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return;\n\n\t\tconst block = state.getBlock(sel.nodeId);\n\t\tif (!block) return;\n\n\t\t// Composite nodes carry their content in descendants. The block-only MIME\n\t\t// format and a direct `toHTML(block, '')` call cannot represent that tree\n\t\t// (a selected table used to become an empty table on paste), so serialize\n\t\t// the complete subtree through the canonical document serializer instead.\n\t\tif (!isLeafBlock(block)) {\n\t\t\tif (this.schemaRegistry) {\n\t\t\t\tclipboardData.setData(\n\t\t\t\t\t'text/html',\n\t\t\t\t\tserializeDocumentToHTML({ children: [block] }, this.schemaRegistry),\n\t\t\t\t);\n\t\t\t}\n\t\t\tclipboardData.setData('text/plain', this.plainTextFromBlockTree(block));\n\t\t\treturn;\n\t\t}\n\n\t\t// Internal format: block type + attrs for round-trip paste\n\t\tconst blockData: {\n\t\t\treadonly type: string;\n\t\t\treadonly htmlId?: string;\n\t\t\treadonly attrs?: Record<string, unknown>;\n\t\t} = {\n\t\t\ttype: block.type,\n\t\t\t...(block.htmlId ? { htmlId: block.htmlId } : {}),\n\t\t\t...(block.attrs ? { attrs: { ...block.attrs } } : {}),\n\t\t};\n\t\tclipboardData.setData(BLOCK_MIME, JSON.stringify(blockData));\n\n\t\t// HTML representation via NodeSpec.toHTML if available\n\t\tconst spec = this.schemaRegistry?.getNodeSpec(block.type);\n\t\tif (spec?.toHTML) {\n\t\t\tclipboardData.setData('text/html', setHTMLIdOnFirstTag(spec.toHTML(block, ''), block.htmlId));\n\t\t}\n\n\t\t// Plain text: use alt text for images, empty for other voids\n\t\tconst attrs = block.attrs as Record<string, unknown> | undefined;\n\t\tconst altText: string = typeof attrs?.alt === 'string' ? attrs.alt : '';\n\t\tclipboardData.setData('text/plain', altText);\n\t}\n\n\t/** Returns leaf text for a composite block in document order. */\n\tprivate plainTextFromBlockTree(block: BlockNode): string {\n\t\tif (isLeafBlock(block)) return getBlockText(block);\n\t\treturn getBlockChildren(block)\n\t\t\t.map((child) => this.plainTextFromBlockTree(child))\n\t\t\t.join('\\n');\n\t}\n\n\tprivate writeTextSelectionToClipboard(clipboardData: DataTransfer, state: EditorState): void {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel)) return;\n\t\tif (isGapCursor(sel)) return;\n\t\tif (isCollapsed(sel)) return;\n\n\t\t// When selection spans composite blocks (tables) or void blocks (images),\n\t\t// use full document serialization so HTML round-trips through paste correctly.\n\t\tif (this.selectionRequiresDocumentSerializer(state)) {\n\t\t\tthis.writeCompositeSelectionToClipboard(clipboardData, state);\n\t\t\treturn;\n\t\t}\n\n\t\tconst blockOrder = state.getBlockOrder();\n\t\tconst range = selectionRange(sel, blockOrder);\n\t\tconst fromIdx = blockOrder.indexOf(range.from.blockId);\n\t\tconst toIdx = blockOrder.indexOf(range.to.blockId);\n\n\t\tconst lines: string[] = [];\n\t\tconst richBlocks: RichBlockData[] = [];\n\t\tconst blockEntries: { block: BlockNode; start: number; end: number }[] = [];\n\n\t\tfor (let i = fromIdx; i <= toIdx; i++) {\n\t\t\tconst bid = blockOrder[i];\n\t\t\tif (!bid) continue;\n\t\t\tconst block = state.getBlock(bid);\n\t\t\tif (!block) continue;\n\n\t\t\tconst blockLen = getBlockLength(block);\n\t\t\tconst start = i === fromIdx ? range.from.offset : 0;\n\t\t\tconst end = i === toIdx ? range.to.offset : blockLen;\n\t\t\tconst segments: readonly RichSegment[] = this.extractSegmentsForRange(block, start, end);\n\t\t\tconst sliced: string = this.plainTextFromSegments(segments);\n\t\t\tlines.push(sliced);\n\t\t\tblockEntries.push({ block, start, end });\n\t\t\trichBlocks.push({\n\t\t\t\ttype: block.type,\n\t\t\t\ttext: sliced,\n\t\t\t\t...(start === 0 && end === blockLen && block.htmlId ? { htmlId: block.htmlId } : {}),\n\t\t\t\t...(block.attrs ? { attrs: { ...block.attrs } } : {}),\n\t\t\t\t...(segments.length > 0 ? { segments } : {}),\n\t\t\t});\n\t\t}\n\n\t\tconst plainText: string = lines.join('\\n');\n\t\tclipboardData.setData('text/plain', plainText);\n\n\t\t// Write text/html with inline marks and proper block-level tags.\n\t\tif (this.schemaRegistry) {\n\t\t\tconst markOrder: Map<string, number> = buildMarkOrder(this.schemaRegistry);\n\t\t\tconst multiBlock: boolean = fromIdx !== toIdx;\n\n\t\t\tconst hasStructuredBlock: boolean = richBlocks.some(\n\t\t\t\t(b) => b.htmlId !== undefined || (b.type !== undefined && b.type !== 'paragraph'),\n\t\t\t);\n\t\t\tif (multiBlock || this.containsInlineSegments(richBlocks) || hasStructuredBlock) {\n\t\t\t\tconst html: string = this.serializeBlocksToClipboardHTML(\n\t\t\t\t\tblockEntries,\n\t\t\t\t\tmarkOrder,\n\t\t\t\t\trichBlocks,\n\t\t\t\t);\n\t\t\t\tclipboardData.setData('text/html', html);\n\t\t\t} else {\n\t\t\t\tconst bid = blockOrder[fromIdx];\n\t\t\t\tconst block = bid ? state.getBlock(bid) : undefined;\n\t\t\t\tif (block) {\n\t\t\t\t\tconst inlineHTML: string = this.serializeBlockRangeToHTML(\n\t\t\t\t\t\tblock,\n\t\t\t\t\t\trange.from.offset,\n\t\t\t\t\t\trange.to.offset,\n\t\t\t\t\t\tmarkOrder,\n\t\t\t\t\t);\n\t\t\t\t\tclipboardData.setData('text/html', inlineHTML);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether the current selection requires the full document serializer.\n\t * Returns true when the selection is within a composite root (e.g. a table)\n\t * or spans multiple roots where at least one is composite or void.\n\t */\n\tprivate selectionRequiresDocumentSerializer(state: EditorState): boolean {\n\t\tconst sel: Selection | undefined = this.getTextSelection(state);\n\t\tif (!sel || isCollapsed(sel)) return false;\n\n\t\tconst blockOrder = state.getBlockOrder();\n\t\tconst range = selectionRange(sel, blockOrder);\n\n\t\tconst fromPath = findNodePath(state.doc, range.from.blockId);\n\t\tconst toPath = findNodePath(state.doc, range.to.blockId);\n\t\tif (!fromPath || !toPath) return false;\n\n\t\t// Both endpoints inside the same root — use document serializer for composite roots\n\t\tif (fromPath[0] === toPath[0]) {\n\t\t\tconst rootBlock = state.doc.children.find((c) => c.id === fromPath[0]);\n\t\t\tif (rootBlock && !isLeafBlock(rootBlock)) return true;\n\t\t\treturn false;\n\t\t}\n\n\t\t// Different root blocks — check if any root is composite or void\n\t\tconst fromRootIdx: number = state.doc.children.findIndex((c) => c.id === fromPath[0]);\n\t\tconst toRootIdx: number = state.doc.children.findIndex((c) => c.id === toPath[0]);\n\t\tfor (let i = fromRootIdx; i <= toRootIdx; i++) {\n\t\t\tconst block = state.doc.children[i];\n\t\t\tif (!block) continue;\n\t\t\tif (!isLeafBlock(block)) return true;\n\t\t\tconst spec = this.schemaRegistry?.getNodeSpec(block.type);\n\t\t\tif (spec?.isVoid) return true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/** Returns the text selection if the state has one, undefined otherwise. */\n\tprivate getTextSelection(state: EditorState): Selection | undefined {\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel)) return undefined;\n\t\treturn sel;\n\t}\n\n\t/** Extracts plain text for a block range, ignoring InlineNodes in text/plain output. */\n\tprivate extractPlainTextForRange(block: BlockNode, start: number, end: number): string {\n\t\treturn this.plainTextFromSegments(this.extractSegmentsForRange(block, start, end));\n\t}\n\n\t/** Joins rich text segments into the plain-text form stored on the clipboard. */\n\tprivate plainTextFromSegments(segments: readonly RichSegment[]): string {\n\t\treturn segments.map((segment) => (segment.kind === 'inline' ? '' : segment.text)).join('');\n\t}\n\n\t/**\n\t * Serializes a selection that includes composite blocks (tables) to clipboard.\n\t * Uses serializeDocumentToHTML for HTML to preserve table structure.\n\t * Skips rich clipboard since composite blocks can't be represented as flat data.\n\t */\n\tprivate writeCompositeSelectionToClipboard(\n\t\tclipboardData: DataTransfer,\n\t\tstate: EditorState,\n\t): void {\n\t\tconst sel: Selection | undefined = this.getTextSelection(state);\n\t\tif (!sel) return;\n\t\tconst blockOrder = state.getBlockOrder();\n\t\tconst range = selectionRange(sel, blockOrder);\n\t\tconst fromIdx: number = blockOrder.indexOf(range.from.blockId);\n\t\tconst toIdx: number = blockOrder.indexOf(range.to.blockId);\n\n\t\t// Plain text from leaf blocks\n\t\tconst lines: string[] = [];\n\t\tfor (let i = fromIdx; i <= toIdx; i++) {\n\t\t\tconst bid = blockOrder[i];\n\t\t\tif (!bid) continue;\n\t\t\tconst block = state.getBlock(bid);\n\t\t\tif (!block) continue;\n\t\t\tconst start: number = i === fromIdx ? range.from.offset : 0;\n\t\t\tconst end: number = i === toIdx ? range.to.offset : getBlockLength(block);\n\t\t\tlines.push(this.extractPlainTextForRange(block, start, end));\n\t\t}\n\t\tclipboardData.setData('text/plain', lines.join('\\n'));\n\n\t\t// Serialize only the selected region while preserving composite ancestors\n\t\tif (this.schemaRegistry) {\n\t\t\tconst doc = buildSelectionDocument(state, sel, this.schemaRegistry);\n\t\t\tif (doc.children.length > 0) {\n\t\t\t\tconst html: string = serializeDocumentToHTML(doc, this.schemaRegistry);\n\t\t\t\tclipboardData.setData('text/html', html);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Serializes a range of inline content within a block to an HTML string. */\n\tprivate serializeBlockRangeToHTML(\n\t\tblock: BlockNode,\n\t\tstart: number,\n\t\tend: number,\n\t\tmarkOrder?: Map<string, number>,\n\t): string {\n\t\tconst parts: string[] = [];\n\t\tforEachInlineChildInRange(block, start, end, {\n\t\t\tonText: (text: string, marks: readonly Mark[]): void => {\n\t\t\t\tif (this.schemaRegistry) {\n\t\t\t\t\tparts.push(serializeMarksToHTML(text, marks, this.schemaRegistry, markOrder));\n\t\t\t\t}\n\t\t\t},\n\t\t\tonInline: (node): void => {\n\t\t\t\tconst spec = this.schemaRegistry?.getInlineNodeSpec(node.inlineType);\n\t\t\t\tif (spec?.toHTMLString) {\n\t\t\t\t\tparts.push(spec.toHTMLString(node));\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\treturn parts.join('');\n\t}\n\n\t/** Extracts inline text segments with marks for a block range. */\n\tprivate extractSegmentsForRange(\n\t\tblock: BlockNode,\n\t\tstart: number,\n\t\tend: number,\n\t): readonly RichSegment[] {\n\t\tconst segments: RichSegment[] = [];\n\t\tforEachInlineChildInRange(block, start, end, {\n\t\t\tonText: (text: string, marks: readonly Mark[]): void => {\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext,\n\t\t\t\t\tmarks: marks.map((m: Mark) => ({\n\t\t\t\t\t\ttype: m.type,\n\t\t\t\t\t\t...(m.attrs ? { attrs: { ...m.attrs } } : {}),\n\t\t\t\t\t})),\n\t\t\t\t});\n\t\t\t},\n\t\t\tonInline: (node): void => {\n\t\t\t\tsegments.push({\n\t\t\t\t\tkind: 'inline',\n\t\t\t\t\tinlineType: node.inlineType,\n\t\t\t\t\t...(Object.keys(node.attrs).length > 0 ? { attrs: { ...node.attrs } } : {}),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\treturn segments;\n\t}\n\n\tprivate containsInlineSegments(blocks: readonly RichBlockData[]): boolean {\n\t\treturn blocks.some((block) => block.segments?.some((segment) => segment.kind === 'inline'));\n\t}\n\n\t/**\n\t * Serializes multi-block content to clipboard HTML using proper block-level tags.\n\t * Groups consecutive list_items into <ul>/<ol> wrappers.\n\t * Embeds rich block data as a hidden span for reliable round-trip.\n\t */\n\tprivate serializeBlocksToClipboardHTML(\n\t\tentries: readonly { block: BlockNode; start: number; end: number }[],\n\t\tmarkOrder: Map<string, number>,\n\t\trichBlocks: readonly RichBlockData[],\n\t): string {\n\t\tconst htmlParts: string[] = [];\n\t\tlet listBuffer: string[] = [];\n\t\tlet currentListTag: string | undefined;\n\n\t\tconst flushList = (): void => {\n\t\t\tif (listBuffer.length > 0 && currentListTag) {\n\t\t\t\thtmlParts.push(`<${currentListTag}>${listBuffer.join('')}</${currentListTag}>`);\n\t\t\t\tlistBuffer = [];\n\t\t\t\tcurrentListTag = undefined;\n\t\t\t}\n\t\t};\n\n\t\tfor (const entry of entries) {\n\t\t\tconst { block, start, end } = entry;\n\t\t\tconst completeBlock: boolean = start === 0 && end === getBlockLength(block);\n\t\t\tconst { htmlId: _htmlId, ...blockWithoutHTMLId } = block;\n\t\t\tconst clipboardBlock: BlockNode = {\n\t\t\t\t...blockWithoutHTMLId,\n\t\t\t\t...(completeBlock && block.htmlId ? { htmlId: block.htmlId } : {}),\n\t\t\t};\n\t\t\tconst inlineHTML: string = this.serializeBlockRangeToHTML(block, start, end, markOrder);\n\t\t\tconst spec = this.schemaRegistry?.getNodeSpec(clipboardBlock.type);\n\n\t\t\tif (spec?.wrapper) {\n\t\t\t\tconst wrapperSpec = spec.wrapper(clipboardBlock as Parameters<typeof spec.wrapper>[0]);\n\t\t\t\tconst tag: string = wrapperSpec.tag;\n\n\t\t\t\tif (currentListTag !== tag) {\n\t\t\t\t\tflushList();\n\t\t\t\t\tcurrentListTag = tag;\n\t\t\t\t}\n\n\t\t\t\tif (spec.toHTML) {\n\t\t\t\t\tlistBuffer.push(\n\t\t\t\t\t\tsetHTMLIdOnFirstTag(spec.toHTML(clipboardBlock, inlineHTML), clipboardBlock.htmlId),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tlistBuffer.push(setHTMLIdOnFirstTag(`<li>${inlineHTML}</li>`, clipboardBlock.htmlId));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tflushList();\n\t\t\t\tif (spec?.toHTML) {\n\t\t\t\t\thtmlParts.push(\n\t\t\t\t\t\tsetHTMLIdOnFirstTag(spec.toHTML(clipboardBlock, inlineHTML), clipboardBlock.htmlId),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\thtmlParts.push(setHTMLIdOnFirstTag(`<p>${inlineHTML}</p>`, clipboardBlock.htmlId));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tflushList();\n\n\t\t// Embed rich block data for reliable round-trip\n\t\tconst richJson: string = JSON.stringify(richBlocks)\n\t\t\t.replace(/&/g, '&')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/'/g, ''')\n\t\t\t.replace(/\"/g, '"');\n\t\thtmlParts.push(`<span data-notectl-rich=\"${richJson}\" hidden></span>`);\n\n\t\treturn htmlParts.join('');\n\t}\n\n\tdestroy(): void {\n\t\tthis.element.removeEventListener('copy', this.handleCopy);\n\t\tthis.element.removeEventListener('cut', this.handleCut);\n\t}\n}\n","/**\n * Centralized composition state tracker.\n * Exposes the current IME composition status so that KeyboardHandler,\n * SelectionSync, and Reconciler can guard against DOM mutations\n * that would break an active browser composition session.\n */\n\nimport type { CompositionState } from '../model/CompositionState.js';\nimport type { BlockId } from '../model/TypeBrands.js';\n\nexport class CompositionTracker implements CompositionState {\n\tprivate _composing = false;\n\tprivate _activeBlockId: BlockId | null = null;\n\n\t/** Whether an IME composition session is currently active. */\n\tget isComposing(): boolean {\n\t\treturn this._composing;\n\t}\n\n\t/** The block in which the composition is happening, or null if idle. */\n\tget activeBlockId(): BlockId | null {\n\t\treturn this._activeBlockId;\n\t}\n\n\t/** Marks the start of a composition session in the given block. */\n\tstart(blockId: BlockId): void {\n\t\tthis._composing = true;\n\t\tthis._activeBlockId = blockId;\n\t}\n\n\t/** Marks the end of a composition session. */\n\tend(): void {\n\t\tthis._composing = false;\n\t\tthis._activeBlockId = null;\n\t}\n}\n","/**\n * Input handler: intercepts all beforeinput events, maps them to transactions.\n * After text insertion, checks registered InputRules for pattern matches.\n */\n\nimport {\n\tdeleteBackward,\n\tdeleteForward,\n\tdeleteSoftLineBackward,\n\tdeleteSoftLineForward,\n\tdeleteWordBackward,\n\tdeleteWordForward,\n\tinsertHardBreakCommand,\n\tinsertTextCommand,\n\tsplitBlockCommand,\n} from '../commands/Commands.js';\nimport { type BlockNode, getInlineChildren, isTextNode } from '../model/Document.js';\nimport { INLINE_NODE_PLACEHOLDER } from '../model/InputRule.js';\nimport { isTextSelection } from '../model/Selection.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nimport type { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nimport { isEventFromEditorContent } from '../platform/EditorEventBoundary.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { CompositionTracker } from './CompositionTracker.js';\n\nexport type DispatchFn = (tr: Transaction) => void;\nexport type GetStateFn = () => EditorState;\nexport type UndoFn = () => void;\nexport type RedoFn = () => void;\n\nexport type SyncSelectionFn = () => void;\n\nexport interface InputHandlerOptions {\n\tgetState: GetStateFn;\n\tdispatch: DispatchFn;\n\tsyncSelection: SyncSelectionFn;\n\tinputRuleRegistry?: InputRuleRegistry;\n\tisReadOnly?: () => boolean;\n\t/**\n\t * Gate for live input-rule (Markdown shorthand) processing. When it returns\n\t * `false`, typed shorthand like `# ` or `**bold**` stays literal. Evaluated on\n\t * each keystroke. Defaults to always enabled.\n\t */\n\tshouldApplyInputRules?: () => boolean;\n\tcompositionTracker?: CompositionTracker;\n\tgetTextInputInterceptors?: () => readonly TextInputInterceptorEntry[];\n}\n\nexport class InputHandler {\n\tprivate readonly getState: GetStateFn;\n\tprivate readonly dispatch: DispatchFn;\n\tprivate readonly syncSelection: SyncSelectionFn;\n\tprivate readonly inputRuleRegistry?: InputRuleRegistry;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly shouldApplyInputRules: () => boolean;\n\tprivate readonly compositionTracker: CompositionTracker;\n\tprivate readonly getTextInputInterceptors: () => readonly TextInputInterceptorEntry[];\n\tprivate compositionCommitHandled = false;\n\n\tprivate readonly handleBeforeInput: (e: InputEvent) => void;\n\tprivate readonly handleCompositionStart: (e: CompositionEvent) => void;\n\tprivate readonly handleCompositionEnd: (e: CompositionEvent) => void;\n\n\tconstructor(\n\t\tprivate readonly element: HTMLElement,\n\t\toptions: InputHandlerOptions,\n\t) {\n\t\tthis.getState = options.getState;\n\t\tthis.dispatch = options.dispatch;\n\t\tthis.syncSelection = options.syncSelection;\n\t\tthis.inputRuleRegistry = options.inputRuleRegistry;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\t\tthis.shouldApplyInputRules = options.shouldApplyInputRules ?? (() => true);\n\t\tthis.compositionTracker = options.compositionTracker ?? new CompositionTracker();\n\t\tthis.getTextInputInterceptors = options.getTextInputInterceptors ?? (() => []);\n\n\t\tthis.handleBeforeInput = this.onBeforeInput.bind(this);\n\t\tthis.handleCompositionStart = this.onCompositionStart.bind(this);\n\t\tthis.handleCompositionEnd = this.onCompositionEnd.bind(this);\n\n\t\telement.addEventListener('beforeinput', this.handleBeforeInput);\n\t\telement.addEventListener('compositionstart', this.handleCompositionStart);\n\t\telement.addEventListener('compositionend', this.handleCompositionEnd);\n\t}\n\n\tprivate onBeforeInput(e: InputEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\tif (this.isReadOnly()) return;\n\n\t\t// During composition, let the browser handle it\n\t\tif (this.compositionTracker.isComposing && e.inputType === 'insertCompositionText') {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldHandleBeforeInput(e.inputType)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Sync selection from DOM before processing non-insert operations\n\t\t// (handles arrow key / mouse navigation that doesn't go through our state)\n\t\tconst needsSelectionSync =\n\t\t\te.inputType !== 'insertText' &&\n\t\t\te.inputType !== 'insertCompositionText' &&\n\t\t\te.inputType !== 'insertFromComposition';\n\t\tif (needsSelectionSync) {\n\t\t\tthis.syncSelection();\n\t\t}\n\n\t\te.preventDefault();\n\n\t\tconst state = this.getState();\n\t\tlet tr: Transaction | null = null;\n\n\t\tswitch (e.inputType) {\n\t\t\tcase 'insertText':\n\t\t\t\tif (e.data) {\n\t\t\t\t\ttr =\n\t\t\t\t\t\tthis.runTextInputInterceptors(e.data, state) ??\n\t\t\t\t\t\tinsertTextCommand(state, e.data, 'input');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertReplacementText':\n\t\t\t\tif (e.data) {\n\t\t\t\t\ttr =\n\t\t\t\t\t\tthis.runTextInputInterceptors(e.data, state) ??\n\t\t\t\t\t\tinsertTextCommand(state, e.data, 'input');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertFromComposition':\n\t\t\t\tif (e.data) {\n\t\t\t\t\tthis.compositionCommitHandled = true;\n\t\t\t\t\ttr =\n\t\t\t\t\t\tthis.runTextInputInterceptors(e.data, state) ??\n\t\t\t\t\t\tinsertTextCommand(state, e.data, 'input');\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertParagraph':\n\t\t\t\ttr = splitBlockCommand(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertLineBreak':\n\t\t\t\ttr = insertHardBreakCommand(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteContentBackward':\n\t\t\t\ttr = deleteBackward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteContentForward':\n\t\t\t\ttr = deleteForward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteWordBackward':\n\t\t\t\ttr = deleteWordBackward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteWordForward':\n\t\t\t\ttr = deleteWordForward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteSoftLineBackward':\n\t\t\t\ttr = deleteSoftLineBackward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteSoftLineForward':\n\t\t\t\ttr = deleteSoftLineForward(state);\n\t\t\t\tbreak;\n\n\t\t\tcase 'deleteByCut':\n\t\t\t\t// ClipboardHandler owns cut semantics to avoid duplicate deletions.\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertFromPaste':\n\t\t\t\t// Handled by PasteHandler\n\t\t\t\tbreak;\n\n\t\t\tcase 'insertFromDrop':\n\t\t\t\t// Handled by PasteHandler\n\t\t\t\tbreak;\n\n\t\t\tcase 'formatBold':\n\t\t\tcase 'formatItalic':\n\t\t\tcase 'formatUnderline':\n\t\t\tcase 'historyUndo':\n\t\t\tcase 'historyRedo':\n\t\t\t\t// Handled by KeyboardHandler — just prevent default\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (tr) {\n\t\t\tthis.dispatch(tr);\n\n\t\t\t// Check input rules after text insertion\n\t\t\tif (e.inputType === 'insertText' || e.inputType === 'insertReplacementText') {\n\t\t\t\tthis.checkInputRules();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate onCompositionStart(e: CompositionEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\tconst state = this.getState();\n\t\tif (!isTextSelection(state.selection)) return;\n\t\tthis.compositionCommitHandled = false;\n\t\tthis.compositionTracker.start(state.selection.anchor.blockId);\n\t}\n\n\tprivate onCompositionEnd(e: CompositionEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\tthis.compositionTracker.end();\n\t\tif (this.isReadOnly()) return;\n\t\tif (this.compositionCommitHandled) {\n\t\t\tthis.compositionCommitHandled = false;\n\t\t\treturn;\n\t\t}\n\t\tconst composedText = e.data;\n\t\tif (!composedText) return;\n\n\t\tconst state = this.getState();\n\t\tconst tr = insertTextCommand(state, composedText, 'input');\n\t\tthis.dispatch(tr);\n\t}\n\n\t/**\n\t * Runs registered text-input interceptors in priority order.\n\t * Returns the first non-null transaction, or null to pass through to\n\t * the default `insertTextCommand`.\n\t */\n\tprivate runTextInputInterceptors(text: string, state: EditorState): Transaction | null {\n\t\tconst entries = this.getTextInputInterceptors();\n\t\tif (entries.length === 0) return null;\n\n\t\tfor (const entry of entries) {\n\t\t\tconst tr = entry.interceptor(text, state);\n\t\t\tif (tr) return tr;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate checkInputRules(): void {\n\t\tif (!this.shouldApplyInputRules()) return;\n\t\tif (!this.inputRuleRegistry) return;\n\t\tconst rules = this.inputRuleRegistry.getInputRules();\n\t\tif (rules.length === 0) return;\n\n\t\tconst state = this.getState();\n\t\tif (!isTextSelection(state.selection)) return;\n\t\tconst { anchor } = state.selection;\n\t\tconst block = state.getBlock(anchor.blockId);\n\t\tif (!block) return;\n\n\t\t// Build the text in MODEL-OFFSET space (inline nodes → one placeholder\n\t\t// char) so `match.index`/`end` are real offsets even when inline nodes\n\t\t// precede the match. Plain `getBlockText` drops inline nodes (width 0),\n\t\t// which desyncs `anchor.offset` (width 1) and corrupts the deleted range.\n\t\tconst text = blockTextForRules(block);\n\t\tconst textBefore = text.slice(0, anchor.offset);\n\n\t\tfor (const rule of rules) {\n\t\t\tconst match = rule.pattern.exec(textBefore);\n\t\t\tif (match) {\n\t\t\t\tconst start = match.index;\n\t\t\t\tconst end = start + match[0].length;\n\t\t\t\tconst tr = rule.handler(state, match, start, end);\n\t\t\t\tif (tr) {\n\t\t\t\t\tthis.dispatch(tr);\n\t\t\t\t\treturn; // Only first matching rule fires\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tthis.element.removeEventListener('beforeinput', this.handleBeforeInput);\n\t\tthis.element.removeEventListener('compositionstart', this.handleCompositionStart);\n\t\tthis.element.removeEventListener('compositionend', this.handleCompositionEnd);\n\t}\n}\n\n/**\n * Renders a block's inline content in model-offset space: text verbatim, each\n * inline node as a single {@link INLINE_NODE_PLACEHOLDER}. Indices into the\n * result equal model offsets, which input-rule handlers feed to step builders.\n */\nfunction blockTextForRules(block: BlockNode): string {\n\tlet text = '';\n\tfor (const child of getInlineChildren(block)) {\n\t\ttext += isTextNode(child) ? child.text : INLINE_NODE_PLACEHOLDER;\n\t}\n\treturn text;\n}\n\nfunction shouldHandleBeforeInput(inputType: string): boolean {\n\tswitch (inputType) {\n\t\tcase 'insertText':\n\t\tcase 'insertReplacementText':\n\t\tcase 'insertFromComposition':\n\t\tcase 'insertParagraph':\n\t\tcase 'insertLineBreak':\n\t\tcase 'deleteContentBackward':\n\t\tcase 'deleteContentForward':\n\t\tcase 'deleteWordBackward':\n\t\tcase 'deleteWordForward':\n\t\tcase 'deleteSoftLineBackward':\n\t\tcase 'deleteSoftLineForward':\n\t\tcase 'deleteByCut':\n\t\tcase 'insertFromPaste':\n\t\tcase 'insertFromDrop':\n\t\tcase 'formatBold':\n\t\tcase 'formatItalic':\n\t\tcase 'formatUnderline':\n\t\tcase 'historyUndo':\n\t\tcase 'historyRedo':\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n","/**\n * Key descriptor utilities: convert KeyboardEvents into stable descriptor\n * strings for keymap lookup.\n *\n * Two kinds of descriptor are produced:\n * - A layout-aware descriptor from `event.key` (primary, unchanged behavior).\n * - A physical, layout-independent descriptor from `event.code` (fallback,\n * only for command shortcuts) so Ctrl/Cmd shortcuts resolve by physical key\n * position regardless of keyboard layout (e.g. Cyrillic JCUKEN, where the\n * physical `A` key reports `event.key = 'ф'`).\n */\n\n/** Physical punctuation `KeyboardEvent.code` values mapped to their US-QWERTY base character. */\nconst CODE_PUNCTUATION: Readonly<Record<string, string>> = {\n\tPeriod: '.',\n\tComma: ',',\n\tSlash: '/',\n\tSemicolon: ';',\n\tQuote: \"'\",\n\tBracketLeft: '[',\n\tBracketRight: ']',\n\tBackslash: '\\\\',\n\tMinus: '-',\n\tEqual: '=',\n\tBackquote: '`',\n};\n\n/**\n * US-QWERTY shifted glyph for each digit and punctuation base character. Used\n * so shortcuts bound to a *shifted* glyph (e.g. `Mod-Shift->` for blockquote,\n * `Mod-Shift-+` / `Mod-Shift-_` for font size) also resolve on non-Latin\n * layouts, where the physical key reports a different character. Letters are\n * absent: their shifted form is just the uppercase letter the base map already\n * produces.\n */\nconst SHIFTED_US: Readonly<Record<string, string>> = {\n\t'1': '!',\n\t'2': '@',\n\t'3': '#',\n\t'4': '$',\n\t'5': '%',\n\t'6': '^',\n\t'7': '&',\n\t'8': '*',\n\t'9': '(',\n\t'0': ')',\n\t'`': '~',\n\t'-': '_',\n\t'=': '+',\n\t'[': '{',\n\t']': '}',\n\t'\\\\': '|',\n\t';': ':',\n\t\"'\": '\"',\n\t',': '<',\n\t'.': '>',\n\t'/': '?',\n};\n\n/**\n * Builds the physical `KeyboardEvent.code` to US-QWERTY base-character map:\n * the 26 letters, the 10 digit keys (main row and numpad), and common\n * punctuation. Named keys (Enter, Tab, Arrow*, Home, End) are intentionally\n * absent so the physical fallback never affects them.\n */\nfunction buildCodeToKey(): ReadonlyMap<string, string> {\n\tconst map: Map<string, string> = new Map<string, string>();\n\tfor (let i = 0; i < 26; i++) {\n\t\tconst letter: string = String.fromCharCode(65 + i);\n\t\tmap.set(`Key${letter}`, letter);\n\t}\n\tfor (let digit = 0; digit <= 9; digit++) {\n\t\tmap.set(`Digit${digit}`, String(digit));\n\t\tmap.set(`Numpad${digit}`, String(digit));\n\t}\n\tfor (const [code, char] of Object.entries(CODE_PUNCTUATION)) {\n\t\tmap.set(code, char);\n\t}\n\treturn map;\n}\n\n/** Physical `KeyboardEvent.code` to US-QWERTY base character. Layout-independent. */\nconst CODE_TO_KEY: ReadonlyMap<string, string> = buildCodeToKey();\n\n/** Builds the shared `Mod-Shift-Alt` modifier prefix segments for a key event. */\nfunction modifierPrefix(e: KeyboardEvent): string[] {\n\tconst parts: string[] = [];\n\tif (e.metaKey || e.ctrlKey) parts.push('Mod');\n\tif (e.shiftKey) parts.push('Shift');\n\tif (e.altKey) parts.push('Alt');\n\treturn parts;\n}\n\n/**\n * Normalizes a KeyboardEvent into a consistent, layout-aware key descriptor.\n * Format: `\"Mod-Shift-Alt-Key\"` where Mod = Ctrl/Cmd. Single-character keys are\n * uppercased, Space is named explicitly, and special keys (Enter, Tab, ...)\n * keep their event name.\n */\nexport function normalizeKeyDescriptor(e: KeyboardEvent): string {\n\tconst parts: string[] = modifierPrefix(e);\n\n\tlet key: string = e.key;\n\tif (key === ' ') key = 'Space';\n\telse if (key.length === 1) key = key.toUpperCase();\n\t// For special keys like Enter, Tab, Backspace, keep as-is\n\n\tparts.push(key);\n\treturn parts.join('-');\n}\n\n/**\n * Resolves the unmodified US-QWERTY base character for the physical key, or\n * `null` when a physical fallback must not apply.\n *\n * Gated to command shortcuts: only when Ctrl or Cmd is held, and never for\n * AltGr combinations (Ctrl+Alt without Cmd) which compose characters on many\n * non-Latin layouts and must not be hijacked.\n */\nexport function physicalBaseKey(e: KeyboardEvent): string | null {\n\tif (!(e.ctrlKey || e.metaKey)) return null; // shortcuts only\n\tif (e.ctrlKey && e.altKey && !e.metaKey) return null; // AltGr composes characters\n\treturn CODE_TO_KEY.get(e.code) ?? null;\n}\n\n/**\n * Physical-position key descriptors to try for a command shortcut, in priority\n * order, or an empty array when not applicable (no command modifier, AltGr, or\n * a named key absent from the base map).\n *\n * The US-QWERTY base glyph comes first. When Shift is held, the US-QWERTY\n * *shifted* glyph is appended as a second candidate so shortcuts bound to a\n * shifted glyph (e.g. `Mod-Shift->` for blockquote) also resolve on layouts\n * where that physical key reports a different character. Same\n * `\"Mod-Shift-Alt-Key\"` format as {@link normalizeKeyDescriptor}.\n */\nexport function physicalKeyDescriptors(e: KeyboardEvent): string[] {\n\tconst base: string | null = physicalBaseKey(e);\n\tif (base === null) return [];\n\n\tconst prefix: string[] = modifierPrefix(e);\n\tconst descriptors: string[] = [[...prefix, base].join('-')];\n\n\tif (e.shiftKey) {\n\t\tconst shifted: string | undefined = SHIFTED_US[base];\n\t\tif (shifted !== undefined) descriptors.push([...prefix, shifted].join('-'));\n\t}\n\n\treturn descriptors;\n}\n","/**\n * Keyboard handler: intercepts keydown events for shortcuts.\n * Checks plugin-registered keymaps first, then falls back to built-in\n * structural shortcuts (undo, redo, select all) and NodeSelection navigation.\n *\n * Mark-specific shortcuts (Mod-B, Mod-I, Mod-U) are registered by\n * their respective plugins via keymaps, not hardcoded here.\n */\n\nimport {\n\tdeleteBackwardAtGap,\n\tdeleteForwardAtGap,\n\tdeleteNodeSelection,\n\tinsertTextCommand,\n\tnavigateArrowIntoVoid,\n\tselectAll,\n\tsplitBlockCommand,\n} from '../commands/Commands.js';\nimport type { Keymap } from '../model/Keymap.js';\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport { isGapCursor, isNodeSelection, selectionsEqual } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { isEventFromEditorContent } from '../platform/EditorEventBoundary.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport type { CompositionTracker } from './CompositionTracker.js';\nimport type { DispatchFn, GetStateFn, RedoFn, UndoFn } from './InputHandler.js';\nimport {\n\tnormalizeKeyDescriptor,\n\tphysicalBaseKey,\n\tphysicalKeyDescriptors,\n} from './KeyDescriptor.js';\n\ntype TextDirectionFn = (element: HTMLElement) => 'ltr' | 'rtl';\ntype GapCursorNavigateFn = (\n\tstate: EditorState,\n\tdirection: 'left' | 'right' | 'up' | 'down',\n\tcontainer?: HTMLElement,\n) => Transaction | null;\n\nexport interface KeyboardHandlerOptions {\n\tgetState: GetStateFn;\n\tdispatch: DispatchFn;\n\tundo: UndoFn;\n\tredo: RedoFn;\n\tkeymapRegistry?: KeymapRegistry;\n\tisReadOnly?: () => boolean;\n\tcompositionTracker?: CompositionTracker;\n\tgetTextDirection?: TextDirectionFn;\n\tnavigateFromGapCursor?: GapCursorNavigateFn;\n}\n\n/** Maps arrow key names to navigation directions. */\nconst ARROW_DIRECTIONS: Record<string, 'left' | 'right' | 'up' | 'down'> = {\n\tArrowLeft: 'left',\n\tArrowRight: 'right',\n\tArrowUp: 'up',\n\tArrowDown: 'down',\n};\n\nexport class KeyboardHandler {\n\tprivate readonly getState: GetStateFn;\n\tprivate readonly dispatch: DispatchFn;\n\tprivate readonly undo: UndoFn;\n\tprivate readonly redo: RedoFn;\n\tprivate readonly keymapRegistry?: KeymapRegistry;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly compositionTracker?: CompositionTracker;\n\tprivate readonly getTextDirectionFn: TextDirectionFn;\n\tprivate readonly navigateFromGapCursorFn: GapCursorNavigateFn | undefined;\n\tprivate readonly handleKeydown: (e: KeyboardEvent) => void;\n\n\tconstructor(\n\t\tprivate readonly element: HTMLElement,\n\t\toptions: KeyboardHandlerOptions,\n\t) {\n\t\tthis.getState = options.getState;\n\t\tthis.dispatch = options.dispatch;\n\t\tthis.undo = options.undo;\n\t\tthis.redo = options.redo;\n\t\tthis.keymapRegistry = options.keymapRegistry;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\t\tthis.compositionTracker = options.compositionTracker;\n\t\tthis.getTextDirectionFn = options.getTextDirection ?? (() => 'ltr');\n\t\tthis.navigateFromGapCursorFn = options.navigateFromGapCursor;\n\n\t\tthis.handleKeydown = this.onKeydown.bind(this);\n\t\telement.addEventListener('keydown', this.handleKeydown);\n\t}\n\n\tprivate onKeydown(e: KeyboardEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\t// During IME composition, let the browser handle all keys\n\t\tif (this.compositionTracker?.isComposing) return;\n\n\t\t// Handle NodeSelection keys before plugin keymaps\n\t\tif (this.handleNodeSelectionKeys(e)) return;\n\n\t\t// Handle GapCursor keys (typing, Enter, Backspace, Delete)\n\t\tif (this.handleGapCursorKeys(e)) return;\n\n\t\t// Readonly mode: allow navigation keymaps + escape, block everything else\n\t\tif (this.isReadOnly()) {\n\t\t\tif (this.keymapRegistry) {\n\t\t\t\tconst groups = this.keymapRegistry.getKeymapsByPriority();\n\t\t\t\tif (this.dispatchKeymaps(e, [groups.navigation])) return;\n\t\t\t}\n\t\t\tif (this.handleEscape(e)) return;\n\t\t\treturn;\n\t\t}\n\n\t\t// Normal mode: try plugin keymaps in priority order (context > navigation > default).\n\t\tif (this.keymapRegistry) {\n\t\t\tconst groups = this.keymapRegistry.getKeymapsByPriority();\n\t\t\tif (this.dispatchKeymaps(e, [groups.context, groups.navigation, groups.default])) return;\n\t\t}\n\n\t\t// GapCursor arrow fallback: if no plugin keymap handled the arrow,\n\t\t// provide basic escape from GapCursor state.\n\t\tif (this.handleGapCursorArrowFallback(e)) return;\n\n\t\t// Tab fallback: insert tab character when no plugin handled it\n\t\tif (this.handleTab(e)) return;\n\n\t\t// Escape fallback: exit editor when no plugin handled it (WCAG 2.1.2)\n\t\tif (this.handleEscape(e)) return;\n\n\t\t// Fall back to built-in structural shortcuts (undo, redo, select all).\n\t\tconst mod = e.metaKey || e.ctrlKey;\n\t\tif (!mod) return;\n\n\t\t// Mirror the keymap dispatch precedence: resolve by the layout-aware key\n\t\t// first, then by the physical key position (Cyrillic Ctrl+я -> 'z') only when\n\t\t// the layout key matched no built-in. This keeps Latin layouts whose key and\n\t\t// physical position diverge correct (QWERTZ Ctrl+Z stays undo, not redo) while\n\t\t// fixing non-Latin layouts where the layout key matches nothing.\n\t\tlet action = this.resolveBuiltinShortcut(e.key.toLowerCase(), e.shiftKey);\n\t\tif (action === null) {\n\t\t\tconst physical: string | null = physicalBaseKey(e);\n\t\t\tif (physical !== null) {\n\t\t\t\taction = this.resolveBuiltinShortcut(physical.toLowerCase(), e.shiftKey);\n\t\t\t}\n\t\t}\n\t\tif (action === null) return;\n\n\t\te.preventDefault();\n\t\tif (action === 'undo') {\n\t\t\tthis.undo();\n\t\t\treturn;\n\t\t}\n\t\tif (action === 'redo') {\n\t\t\tthis.redo();\n\t\t\treturn;\n\t\t}\n\t\tconst tr: Transaction | null = selectAll(this.getState());\n\t\tif (tr) this.dispatch(tr);\n\t}\n\n\t/**\n\t * Maps a resolved shortcut letter to the built-in structural action it\n\t * triggers, or `null` when it is not a built-in shortcut. The letter is\n\t * already lowercased and resolved against the relevant layout.\n\t */\n\tprivate resolveBuiltinShortcut(\n\t\tkey: string,\n\t\tshiftKey: boolean,\n\t): 'undo' | 'redo' | 'selectAll' | null {\n\t\tif (key === 'z' && !shiftKey) return 'undo';\n\t\tif ((key === 'z' && shiftKey) || (key === 'y' && !shiftKey)) return 'redo';\n\t\tif (key === 'a' && !shiftKey) return 'selectAll';\n\t\treturn null;\n\t}\n\n\t/**\n\t * Dispatches a key event against the given keymap groups, trying the\n\t * layout-aware descriptor first and a physical-position fallback (command\n\t * shortcuts only) second, so shortcuts resolve regardless of keyboard layout.\n\t */\n\tprivate dispatchKeymaps(e: KeyboardEvent, groups: readonly (readonly Keymap[])[]): boolean {\n\t\tif (this.runDescriptor(e, groups, normalizeKeyDescriptor(e))) return true;\n\t\tfor (const physical of physicalKeyDescriptors(e)) {\n\t\t\tif (this.runDescriptor(e, groups, physical)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Runs the first matching handler for a descriptor across the keymap groups.\n\t * Within each group, later-registered keymaps take precedence (reverse order).\n\t */\n\tprivate runDescriptor(\n\t\te: KeyboardEvent,\n\t\tgroups: readonly (readonly Keymap[])[],\n\t\tdescriptor: string,\n\t): boolean {\n\t\tfor (const keymaps of groups) {\n\t\t\tfor (let i = keymaps.length - 1; i >= 0; i--) {\n\t\t\t\tconst handler = keymaps[i]?.[descriptor];\n\t\t\t\tif (handler?.()) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Handles arrow keys, Enter, Backspace, Delete, Escape when a NodeSelection is active. */\n\tprivate handleNodeSelectionKeys(e: KeyboardEvent): boolean {\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return false;\n\n\t\tconst key = e.key;\n\n\t\t// Arrow keys: navigate away from NodeSelection (only unmodified arrows)\n\t\tif (\n\t\t\t(key === 'ArrowLeft' || key === 'ArrowUp' || key === 'ArrowRight' || key === 'ArrowDown') &&\n\t\t\t!e.shiftKey &&\n\t\t\t!e.metaKey &&\n\t\t\t!e.ctrlKey &&\n\t\t\t!e.altKey\n\t\t) {\n\t\t\tconst direction = ARROW_DIRECTIONS[key];\n\t\t\tif (!direction) return false;\n\t\t\tconst isRtl: boolean = this.isSelectedBlockRtl(sel.nodeId);\n\t\t\tconst tr = navigateArrowIntoVoid(state, direction, isRtl);\n\t\t\tif (tr) {\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.dispatch(tr);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Backspace / Delete: remove the selected void block\n\t\tif (key === 'Backspace' || key === 'Delete') {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr = deleteNodeSelection(state, sel);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Enter: insert paragraph after\n\t\tif (key === 'Enter') {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr = splitBlockCommand(state);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Escape: move to start of next block\n\t\tif (key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tconst tr = navigateArrowIntoVoid(state, 'right');\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Handles keyboard input when a GapCursor is active.\n\t *\n\t * Since the browser has no DOM selection at a GapCursor position,\n\t * no `beforeinput` events fire. This method intercepts printable\n\t * characters, Enter, Backspace, and Delete to dispatch the\n\t * corresponding editor commands.\n\t */\n\tprivate handleGapCursorKeys(e: KeyboardEvent): boolean {\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\t\tif (!isGapCursor(sel)) return false;\n\n\t\tconst key: string = e.key;\n\n\t\t// Arrow keys: let GapCursorPlugin keymaps handle them\n\t\tif (key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Escape / Tab: pass through\n\t\tif (key === 'Escape' || key === 'Tab') return false;\n\n\t\t// Modifier combos (Ctrl/Meta/Alt): let shortcuts like Ctrl+Z through\n\t\tif (e.ctrlKey || e.metaKey || e.altKey) return false;\n\n\t\t// Enter: insert paragraph at gap\n\t\tif (key === 'Enter') {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr: Transaction | null = splitBlockCommand(state);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Backspace: delete backward at gap\n\t\tif (key === 'Backspace') {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr: Transaction | null = deleteBackwardAtGap(state, sel);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Delete: delete forward at gap\n\t\tif (key === 'Delete') {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr: Transaction | null = deleteForwardAtGap(state, sel);\n\t\t\tif (tr) this.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\t// Printable characters: insert text in a new paragraph\n\t\tif (key.length === 1) {\n\t\t\tif (this.isReadOnly()) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\tconst tr: Transaction = insertTextCommand(state, key);\n\t\t\tthis.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Fallback arrow handler for GapCursor when no plugin keymap handled the arrow.\n\t * Provides basic escape from GapCursor state even without GapCursorPlugin.\n\t */\n\tprivate handleGapCursorArrowFallback(e: KeyboardEvent): boolean {\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\t\tif (!isGapCursor(sel)) return false;\n\n\t\tconst key: string = e.key;\n\t\tif (key !== 'ArrowLeft' && key !== 'ArrowRight' && key !== 'ArrowUp' && key !== 'ArrowDown') {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst direction = ARROW_DIRECTIONS[key];\n\t\tif (!direction) return false;\n\t\tif (!this.navigateFromGapCursorFn) return false;\n\t\tconst tr: Transaction | null = this.navigateFromGapCursorFn(state, direction, this.element);\n\t\tif (tr && !selectionsEqual(tr.selectionAfter, sel)) {\n\t\t\te.preventDefault();\n\t\t\tthis.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Inserts a tab character when Tab is pressed and no plugin handled it.\n\t * Shift-Tab is suppressed to prevent focus leaving the editor.\n\t */\n\tprivate handleTab(e: KeyboardEvent): boolean {\n\t\tif (e.key !== 'Tab') return false;\n\t\tif (e.metaKey || e.ctrlKey || e.altKey) return false;\n\n\t\te.preventDefault();\n\n\t\tif (e.shiftKey) return true;\n\n\t\tconst state = this.getState();\n\t\tconst tr: Transaction = insertTextCommand(state, '\\t');\n\t\tthis.dispatch(tr);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Exits the editor when Escape is pressed and no plugin handled it.\n\t * Satisfies WCAG 2.1.2 \"No Keyboard Trap\" — since Tab is intercepted\n\t * for indent/insert, Escape provides the keyboard exit mechanism.\n\t */\n\tprivate handleEscape(e: KeyboardEvent): boolean {\n\t\tif (e.key !== 'Escape') return false;\n\n\t\tthis.element.blur();\n\t\treturn true;\n\t}\n\n\t/** Checks if the block with the given ID is rendered in RTL direction. */\n\tprivate isSelectedBlockRtl(blockId: BlockId): boolean {\n\t\tconst blockEl: Element | null = this.element.querySelector(`[data-block-id=\"${blockId}\"]`);\n\t\treturn blockEl instanceof HTMLElement && this.getTextDirectionFn(blockEl) === 'rtl';\n\t}\n\n\tdestroy(): void {\n\t\tthis.element.removeEventListener('keydown', this.handleKeydown);\n\t}\n}\n","/**\n * Cheap, synchronous Markdown detector for the paste pipeline.\n *\n * Stays in the base bundle (no engine dependency) so the heavy parser can be\n * dynamically imported only on a positive match (D11). Detection is deliberately\n * conservative: it fires only on strong **block-level** signals (a fenced code\n * block, a GFM table, or a run of list/heading/blockquote markers), never on a\n * stray inline `*` or `_`, so ordinary prose pastes through unchanged (D11,\n * \"auto\" mode must never turn normal paste into italic soup).\n */\n\n/** A fenced code block opener. */\nconst FENCE = /(^|\\n) {0,3}(```|~~~)/;\n\n/** A GFM table delimiter row (`| --- | :--: |`). */\nconst TABLE_DELIMITER = /(^|\\n)\\s*\\|?[ :|]*-{3,}[ :|-]*(\\n|$)/;\n\n/** Block-level line markers: ATX heading, list bullet/ordered, blockquote. */\nconst BLOCK_MARKER = /(^|\\n) {0,3}(#{1,6} |[-*+] |\\d{1,9}[.)] |> )/g;\n\n/**\n * Returns true when the plain-text clipboard payload carries strong block-level\n * Markdown signals worth handing to the full parser.\n *\n * Accepted false positive (by design): plain text with two or more `# ` / list\n * lines and no usable `text/html` (for example, a shell script or other code\n * copied from a terminal whose `# ` comments read as ATX headings) is treated\n * as Markdown. Tightening the run-of-markers signal cannot be done without\n * trading it for false negatives on genuine Markdown (a two-item list, `# A` /\n * `## B`), so the boundary is left as-is. The conversion rides the normal paste\n * pipeline, so a single undo reverts it; users who routinely paste plain-text\n * code can set `markdown: { paste: 'never' }` (or `markdown: false`) to opt out\n * entirely.\n */\nexport function looksLikeMarkdown(text: string): boolean {\n\tif (text.length === 0) return false;\n\tif (FENCE.test(text)) return true;\n\tif (text.includes('|') && TABLE_DELIMITER.test(text)) return true;\n\tconst markers: RegExpMatchArray | null = text.match(BLOCK_MARKER);\n\treturn markers !== null && markers.length >= 2;\n}\n","/**\n * PasteCommand: builds transactions from a ContentSlice and EditorState.\n * Handles three strategies: inline paste, single-block paste, multi-block paste.\n */\n\nimport type { ContentSlice, SliceBlock } from '../model/ContentSlice.js';\nimport { segmentsLength } from '../model/ContentSlice.js';\nimport {\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n\tsegmentsToInlineChildren,\n} from '../model/Document.js';\nimport type { ContentSegment } from '../model/Document.js';\nimport { findNodePath } from '../model/NodeResolver.js';\nimport type { GapCursorSelection, Selection } from '../model/Selection.js';\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { BlockId, NodeTypeName } from '../model/TypeBrands.js';\nimport { nodeType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction, TransactionBuilder } from '../state/Transaction.js';\nimport type { InsertPoint } from './CommandHelpers.js';\nimport {\n\textractParentPath,\n\tfindSiblingIndex,\n\tgetSiblings,\n\tresolveInsertPoint,\n} from './CommandHelpers.js';\nimport { addDeleteSelectionSteps } from './Commands.js';\n\n/**\n * Builds a transaction that pastes the given ContentSlice into the editor state.\n * Selects the appropriate strategy based on the slice structure.\n */\nexport function pasteSlice(state: EditorState, slice: ContentSlice): Transaction {\n\tif (slice.blocks.length === 0) {\n\t\treturn state.transaction('paste').setSelection(state.selection).build();\n\t}\n\n\tconst firstBlock: SliceBlock | undefined = slice.blocks[0];\n\tif (!firstBlock) {\n\t\treturn state.transaction('paste').setSelection(state.selection).build();\n\t}\n\n\tif (slice.blocks.length === 1 && firstBlock.type === nodeType('paragraph')) {\n\t\treturn pasteInline(state, firstBlock.segments);\n\t}\n\tif (slice.blocks.length === 1) {\n\t\treturn pasteSingleBlock(state, firstBlock);\n\t}\n\treturn pasteMultiBlock(state, slice);\n}\n\ninterface PasteTarget {\n\treadonly builder: TransactionBuilder;\n\treadonly landingId: BlockId | undefined;\n\treadonly resolved: InsertPoint;\n\treadonly insertBlockId: BlockId;\n\treadonly insertOffset: number;\n}\n\n/**\n * Shared preamble for the text-selection paste strategies: opens a paste\n * builder, deletes any range selection, and resolves the insert position.\n */\nfunction resolvePasteTarget(state: EditorState, sel: Selection): PasteTarget {\n\tconst builder: TransactionBuilder = state.transaction('paste');\n\n\tlet landingId: BlockId | undefined;\n\tif (!isCollapsed(sel)) {\n\t\tlandingId = addDeleteSelectionSteps(state, builder);\n\t}\n\n\tconst resolved: InsertPoint = resolveInsertPoint(sel, state.getBlockOrder());\n\tconst insertBlockId: BlockId = landingId ?? resolved.blockId;\n\tconst insertOffset: number = landingId ? 0 : resolved.offset;\n\n\treturn { builder, landingId, resolved, insertBlockId, insertOffset };\n}\n\n/**\n * Inserts content segments at a position by interleaving `insertText` and\n * `insertInlineNode` steps. Consecutive text segments are coalesced into a single\n * mark-preserving `insertText`. Returns the resulting end offset.\n */\nfunction insertSegmentsAt(\n\tbuilder: TransactionBuilder,\n\tblockId: BlockId,\n\tstartOffset: number,\n\tsegments: readonly ContentSegment[],\n): number {\n\tlet offset: number = startOffset;\n\tlet textRun: ContentSegment[] = [];\n\n\tconst flushTextRun = (): void => {\n\t\tif (textRun.length === 0) return;\n\t\tconst text: string = textRun.map((s) => (s.kind === 'text' ? s.text : '')).join('');\n\t\tif (text.length > 0) {\n\t\t\tbuilder.insertText(blockId, offset, text, [], textRun);\n\t\t\toffset += text.length;\n\t\t}\n\t\ttextRun = [];\n\t};\n\n\tfor (const segment of segments) {\n\t\tif (segment.kind === 'inline') {\n\t\t\tflushTextRun();\n\t\t\tbuilder.insertInlineNode(blockId, offset, segment.node);\n\t\t\toffset += 1;\n\t\t} else {\n\t\t\ttextRun.push(segment);\n\t\t}\n\t}\n\tflushTextRun();\n\n\treturn offset;\n}\n\n/** Case 1: single paragraph — insert segments into current block. */\nfunction pasteInline(state: EditorState, segments: readonly ContentSegment[]): Transaction {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel)) {\n\t\treturn state.transaction('paste').setSelection(sel).build();\n\t}\n\tif (isGapCursor(sel)) {\n\t\treturn pasteInlineAtGap(state, sel, segments);\n\t}\n\tconst { builder, insertBlockId, insertOffset } = resolvePasteTarget(state, sel);\n\tconst endOffset: number = insertSegmentsAt(builder, insertBlockId, insertOffset, segments);\n\tbuilder.setSelection(createCollapsedSelection(insertBlockId, endOffset));\n\n\treturn builder.build();\n}\n\n/** Case 2: single non-paragraph block — change block type and insert segments. */\nfunction pasteSingleBlock(state: EditorState, block: SliceBlock): Transaction {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel)) {\n\t\treturn state.transaction('paste').setSelection(sel).build();\n\t}\n\tif (isGapCursor(sel)) {\n\t\treturn pasteBlocksAtGap(state, sel, [block]);\n\t}\n\tconst { builder, insertBlockId, insertOffset } = resolvePasteTarget(state, sel);\n\n\tbuilder.setBlockType(insertBlockId, block.type, block.attrs);\n\tconst endOffset: number = insertSegmentsAt(builder, insertBlockId, insertOffset, block.segments);\n\tbuilder.setSelection(createCollapsedSelection(insertBlockId, endOffset));\n\n\treturn builder.build();\n}\n\n/** Case 3: multiple blocks — split-insert-merge strategy. */\nfunction pasteMultiBlock(state: EditorState, slice: ContentSlice): Transaction {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel)) {\n\t\treturn state.transaction('paste').setSelection(sel).build();\n\t}\n\tif (isGapCursor(sel)) {\n\t\treturn pasteBlocksAtGap(state, sel, slice.blocks);\n\t}\n\tconst {\n\t\tbuilder,\n\t\tlandingId,\n\t\tresolved,\n\t\tinsertBlockId: blockId,\n\t\tinsertOffset: offset,\n\t} = resolvePasteTarget(state, sel);\n\tconst blockOrder = state.getBlockOrder();\n\n\tlet blockIdx: number;\n\tif (landingId) {\n\t\t// Landing block was inserted at the from-block's root-level position\n\t\tconst fromPath = findNodePath(state.doc, resolved.blockId);\n\t\tconst rootId: string | undefined = fromPath?.[0];\n\t\tblockIdx = rootId ? state.doc.children.findIndex((c) => c.id === rootId) : 0;\n\t} else {\n\t\tblockIdx = blockOrder.indexOf(blockId);\n\t}\n\n\tconst firstSlice: SliceBlock | undefined = slice.blocks[0];\n\tconst lastSlice: SliceBlock | undefined = slice.blocks[slice.blocks.length - 1];\n\tif (!firstSlice || !lastSlice) {\n\t\treturn builder.build();\n\t}\n\tconst middleSlices: readonly SliceBlock[] = slice.blocks.slice(1, -1);\n\n\t// 1. Insert first slice's segments into current block\n\tconst firstEnd: number = insertSegmentsAt(builder, blockId, offset, firstSlice.segments);\n\n\t// 2. Split first, before any retyping, so the tail inherits the caret block's\n\t// ORIGINAL type and attrs. Retyping the prefix before the split would leak\n\t// the first slice's type into the tail (e.g. a heading-first multi-block\n\t// slice would wrongly leave the tail a heading), and a neutral paragraph\n\t// boundary slice would strip a non-paragraph caret block (list item,\n\t// heading) of its type. setBlockType does not shift offsets, so moving it\n\t// past the split is position-neutral.\n\tconst tailBlockId = generateBlockId();\n\tbuilder.splitBlock(blockId, firstEnd, tailBlockId);\n\n\t// 3. Change the prefix block type if the first slice is not a paragraph.\n\tif (firstSlice.type !== nodeType('paragraph')) {\n\t\tbuilder.setBlockType(blockId, firstSlice.type, firstSlice.attrs);\n\t}\n\n\t// 4. Insert middle blocks between first and tail\n\tlet insertAt: number = blockIdx + 1;\n\tfor (const mid of middleSlices) {\n\t\tconst newBlock = createBlockNode(\n\t\t\tmid.type,\n\t\t\tsegmentsToInlineChildren(mid.segments),\n\t\t\tgenerateBlockId(),\n\t\t\tmid.attrs,\n\t\t);\n\t\tbuilder.insertNode([], insertAt, newBlock);\n\t\tinsertAt++;\n\t}\n\n\t// 5. Insert last slice's segments at start of tail block\n\tinsertSegmentsAt(builder, tailBlockId, 0, lastSlice.segments);\n\n\t// 6. Change tail block type if needed\n\tif (lastSlice.type !== nodeType('paragraph')) {\n\t\tbuilder.setBlockType(tailBlockId, lastSlice.type, lastSlice.attrs);\n\t}\n\n\t// 7. Set cursor to end of inserted content\n\tbuilder.setSelection(createCollapsedSelection(tailBlockId, segmentsLength(lastSlice.segments)));\n\n\treturn builder.build();\n}\n\n// --- GapCursor Paste Helpers ---\n\n/**\n * Resolves the insert position for a GapCursor: parent path and index\n * within siblings where new blocks should be inserted.\n */\nfunction gapInsertIndex(\n\tstate: EditorState,\n\tsel: GapCursorSelection,\n): { parentPath: BlockId[]; insertIndex: number } | null {\n\tconst path = findNodePath(state.doc, sel.blockId);\n\tif (!path) return null;\n\n\tconst parentPath: BlockId[] = extractParentPath(path);\n\tconst siblings = getSiblings(state, parentPath);\n\n\tconst index: number = findSiblingIndex(siblings, sel.blockId);\n\tif (index < 0) return null;\n\n\tconst insertIndex: number = sel.side === 'before' ? index : index + 1;\n\treturn { parentPath, insertIndex };\n}\n\n/** Pastes inline segments (single paragraph) at a GapCursor position. */\nfunction pasteInlineAtGap(\n\tstate: EditorState,\n\tsel: GapCursorSelection,\n\tsegments: readonly ContentSegment[],\n): Transaction {\n\tconst gap = gapInsertIndex(state, sel);\n\tif (!gap) {\n\t\treturn state.transaction('paste').setSelection(sel).build();\n\t}\n\n\tconst newId: BlockId = generateBlockId();\n\tconst builder: TransactionBuilder = state.transaction('paste');\n\n\tbuilder.insertNode(\n\t\tgap.parentPath,\n\t\tgap.insertIndex,\n\t\tcreateBlockNode(nodeType('paragraph') as NodeTypeName, [createTextNode('')], newId),\n\t);\n\tconst endOffset: number = insertSegmentsAt(builder, newId, 0, segments);\n\tbuilder.setSelection(createCollapsedSelection(newId, endOffset));\n\n\treturn builder.build();\n}\n\n/** Pastes one or more blocks at a GapCursor position. */\nfunction pasteBlocksAtGap(\n\tstate: EditorState,\n\tsel: GapCursorSelection,\n\tblocks: readonly SliceBlock[],\n): Transaction {\n\tconst gap = gapInsertIndex(state, sel);\n\tif (!gap) {\n\t\treturn state.transaction('paste').setSelection(sel).build();\n\t}\n\n\tconst builder: TransactionBuilder = state.transaction('paste');\n\tlet insertAt: number = gap.insertIndex;\n\tlet lastBlockId: BlockId | undefined;\n\tlet lastLen = 0;\n\n\tfor (const block of blocks) {\n\t\tconst newId: BlockId = generateBlockId();\n\t\tconst newBlock = createBlockNode(\n\t\t\tblock.type,\n\t\t\tsegmentsToInlineChildren(block.segments),\n\t\t\tnewId,\n\t\t\tblock.attrs,\n\t\t);\n\t\tbuilder.insertNode(gap.parentPath, insertAt, newBlock);\n\t\tinsertAt++;\n\t\tlastBlockId = newId;\n\t\tlastLen = segmentsLength(block.segments);\n\t}\n\n\tif (lastBlockId) {\n\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, lastLen));\n\t}\n\n\treturn builder.build();\n}\n","/**\n * HTMLParser: converts sanitized HTML DOM into a ContentSlice.\n * Works on DOM nodes (not strings), is schema-aware, and produces immutable output.\n */\n\nimport type { ContentSlice, SliceBlock } from '../model/ContentSlice.js';\nimport type { ContentSegment, InlineNode, Mark } from '../model/Document.js';\nimport { createInlineNode, inlineSegment, markSetsEqual, textSegment } from '../model/Document.js';\nimport { sanitizeHref } from '../model/HTMLUtils.js';\nimport type { ParseRule } from '../model/ParseRule.js';\nimport type { Schema } from '../model/Schema.js';\nimport { isMarkAllowed, isNodeTypeAllowed } from '../model/Schema.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { InlineTypeName, NodeTypeName } from '../model/TypeBrands.js';\nimport { inlineType, markType, nodeType } from '../model/TypeBrands.js';\nimport { normalizeHTMLWhitespace } from '../serialization/HTMLWhitespace.js';\n\nexport interface HTMLParserOptions {\n\treadonly schema: Schema;\n\treadonly schemaRegistry?: SchemaRegistry;\n}\n\n/** The list flavors the parser recognizes. */\ntype ListType = 'bullet' | 'ordered' | 'checklist';\n\nconst BLOCK_ELEMENTS: ReadonlySet<string> = new Set([\n\t'P',\n\t'DIV',\n\t'H1',\n\t'H2',\n\t'H3',\n\t'H4',\n\t'H5',\n\t'H6',\n\t'BLOCKQUOTE',\n\t'UL',\n\t'OL',\n\t'LI',\n\t'HR',\n\t'PRE',\n\t'TABLE',\n\t'THEAD',\n\t'TBODY',\n\t'TR',\n]);\n\nconst INLINE_ELEMENTS: ReadonlySet<string> = new Set([\n\t'STRONG',\n\t'B',\n\t'EM',\n\t'I',\n\t'U',\n\t'S',\n\t'STRIKE',\n\t'DEL',\n\t'A',\n\t'SPAN',\n\t'BR',\n\t'SUB',\n\t'SUP',\n\t'CODE',\n]);\n\nconst HEADING_PATTERN: RegExp = /^H([1-6])$/;\n\n// --- Fallback mark resolution (when no SchemaRegistry rules match) ---\n\ninterface FallbackMarkDef {\n\treadonly markName: string;\n\treadonly getAttrs?: (el: HTMLElement) => Record<string, unknown> | false;\n}\n\nfunction resolveBoldAttrs(el: HTMLElement): Record<string, unknown> | false {\n\tconst fw: string = el.style.fontWeight;\n\tconst numeric: number = Number.parseInt(fw, 10);\n\tconst isExplicitlyNotBold: boolean = fw === 'normal' || (!Number.isNaN(numeric) && numeric < 700);\n\treturn isExplicitlyNotBold ? false : {};\n}\n\nfunction resolveLinkAttrs(el: HTMLElement): Record<string, unknown> {\n\treturn { href: sanitizeHref(el.getAttribute('href') ?? '') };\n}\n\nconst FALLBACK_MARK_MAP: ReadonlyMap<string, FallbackMarkDef> = new Map([\n\t['STRONG', { markName: 'bold', getAttrs: resolveBoldAttrs }],\n\t['B', { markName: 'bold', getAttrs: resolveBoldAttrs }],\n\t['EM', { markName: 'italic' }],\n\t['I', { markName: 'italic' }],\n\t['U', { markName: 'underline' }],\n\t['S', { markName: 'strikethrough' }],\n\t['STRIKE', { markName: 'strikethrough' }],\n\t['DEL', { markName: 'strikethrough' }],\n\t['A', { markName: 'link', getAttrs: resolveLinkAttrs }],\n]);\n\n// --- Parser ---\n\nexport class HTMLParser {\n\tprivate readonly schema: Schema;\n\tprivate readonly blockParseRules: readonly {\n\t\treadonly rule: ParseRule;\n\t\treadonly type: string;\n\t}[];\n\tprivate readonly markParseRules: readonly {\n\t\treadonly rule: ParseRule;\n\t\treadonly type: string;\n\t}[];\n\tprivate readonly inlineParseRules: readonly {\n\t\treadonly rule: ParseRule;\n\t\treadonly type: string;\n\t}[];\n\tprivate readonly blockTagHandlers: ReadonlyMap<string, (el: HTMLElement) => SliceBlock[]>;\n\n\tconstructor(options: HTMLParserOptions) {\n\t\tthis.schema = options.schema;\n\t\tthis.blockParseRules = options.schemaRegistry?.getBlockParseRules() ?? [];\n\t\tthis.markParseRules = options.schemaRegistry?.getMarkParseRules() ?? [];\n\t\tthis.inlineParseRules = options.schemaRegistry?.getInlineParseRules() ?? [];\n\t\tthis.blockTagHandlers = this.buildBlockTagHandlers();\n\t}\n\n\t/** Parses an HTML fragment and returns a ContentSlice. */\n\tparse(container: DocumentFragment | HTMLElement): ContentSlice {\n\t\t// Collapse insignificant HTML whitespace (newlines/indentation from source\n\t\t// formatting or a browser's clipboard serializer) before walking the tree,\n\t\t// so wrapped text stays a single block instead of splitting at every `\\n`.\n\t\tnormalizeHTMLWhitespace(container);\n\t\tconst blocks: SliceBlock[] = this.parseContainer(container);\n\n\t\tif (blocks.length === 0) {\n\t\t\treturn {\n\t\t\t\tblocks: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: this.resolveBlockType(nodeType('paragraph')),\n\t\t\t\t\t\tsegments: [textSegment('')],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t}\n\n\t\treturn { blocks };\n\t}\n\n\tprivate buildBlockTagHandlers(): ReadonlyMap<string, (el: HTMLElement) => SliceBlock[]> {\n\t\tconst parseParagraph = (el: HTMLElement): SliceBlock[] =>\n\t\t\tthis.parseBlockWithLineBreaks(el, this.resolveBlockType(nodeType('paragraph')));\n\t\tconst parseTable = (el: HTMLElement): SliceBlock[] => this.parseTableAsParagraphs(el);\n\n\t\treturn new Map<string, (el: HTMLElement) => SliceBlock[]>([\n\t\t\t['P', parseParagraph],\n\t\t\t['DIV', parseParagraph],\n\t\t\t['BLOCKQUOTE', (el: HTMLElement) => this.parseBlockquote(el)],\n\t\t\t['UL', (el: HTMLElement) => this.parseList(el, 'bullet', 0)],\n\t\t\t['OL', (el: HTMLElement) => this.parseList(el, 'ordered', 0)],\n\t\t\t['LI', (el: HTMLElement) => this.parseListItem(el, 'bullet', 0)],\n\t\t\t[\n\t\t\t\t'HR',\n\t\t\t\t() => [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: this.resolveBlockType(nodeType('horizontal_rule')),\n\t\t\t\t\t\tsegments: [],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t],\n\t\t\t[\n\t\t\t\t'PRE',\n\t\t\t\t(el: HTMLElement) => [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: this.resolveBlockType(nodeType('paragraph')),\n\t\t\t\t\t\tsegments: this.ensureSegments(this.parsePreContent(el)),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t],\n\t\t\t['TABLE', parseTable],\n\t\t\t['THEAD', parseTable],\n\t\t\t['TBODY', parseTable],\n\t\t\t['TR', parseTable],\n\t\t]);\n\t}\n\n\tprivate parseContainer(container: DocumentFragment | HTMLElement): SliceBlock[] {\n\t\tconst blocks: SliceBlock[] = [];\n\t\tlet pendingSegments: ContentSegment[] = [];\n\n\t\tfor (const child of Array.from(container.childNodes)) {\n\t\t\tif (child.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst el = child as HTMLElement;\n\n\t\t\t\tif (this.isBlockElement(el)) {\n\t\t\t\t\tthis.flushPendingSegments(blocks, pendingSegments);\n\t\t\t\t\tpendingSegments = [];\n\t\t\t\t\tblocks.push(...this.parseBlockElement(el));\n\t\t\t\t} else if (el.children.length > 0 && this.containsBlockDescendants(el)) {\n\t\t\t\t\tthis.flushPendingSegments(blocks, pendingSegments);\n\t\t\t\t\tpendingSegments = [];\n\t\t\t\t\tblocks.push(...this.parseContainerWithMarks(el, this.marksFromElement(el)));\n\t\t\t\t} else {\n\t\t\t\t\tpendingSegments.push(...this.parseInlineNode(child, []));\n\t\t\t\t}\n\t\t\t} else if (child.nodeType === Node.TEXT_NODE) {\n\t\t\t\tconst text: string = child.textContent ?? '';\n\t\t\t\tif (text) {\n\t\t\t\t\tpendingSegments.push(textSegment(text));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.flushPendingSegments(blocks, pendingSegments);\n\t\treturn blocks;\n\t}\n\n\tprivate parseBlockElement(element: HTMLElement): SliceBlock[] {\n\t\tconst headingMatch: RegExpExecArray | null = HEADING_PATTERN.exec(element.tagName);\n\t\tif (headingMatch) {\n\t\t\treturn this.parseHeading(element, Number(headingMatch[1]));\n\t\t}\n\n\t\tconst handler = this.blockTagHandlers.get(element.tagName);\n\t\tif (handler) return handler(element);\n\n\t\treturn this.parseUnknownBlock(element);\n\t}\n\n\tprivate parseHeading(element: HTMLElement, level: number): SliceBlock[] {\n\t\tconst blockType: NodeTypeName = this.resolveBlockType(nodeType('heading'));\n\t\treturn [\n\t\t\t{\n\t\t\t\ttype: blockType,\n\t\t\t\t...(blockType === nodeType('heading') ? { attrs: { level } } : {}),\n\t\t\t\tsegments: this.ensureSegments(this.parseInlineChildren(element, [])),\n\t\t\t},\n\t\t];\n\t}\n\n\tprivate parseUnknownBlock(element: HTMLElement): SliceBlock[] {\n\t\tconst blockMatch = this.matchBlockRule(element);\n\t\tif (blockMatch) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: blockMatch.type,\n\t\t\t\t\t...(blockMatch.attrs ? { attrs: blockMatch.attrs } : {}),\n\t\t\t\t\tsegments: this.ensureSegments(this.parseInlineChildren(element, [])),\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\t\treturn [\n\t\t\t{\n\t\t\t\ttype: this.resolveBlockType(nodeType('paragraph')),\n\t\t\t\tsegments: this.ensureSegments(this.parseInlineChildren(element, [])),\n\t\t\t},\n\t\t];\n\t}\n\n\tprivate parseBlockquote(element: HTMLElement): SliceBlock[] {\n\t\tconst blockType: NodeTypeName = this.resolveBlockType(nodeType('blockquote'));\n\t\tconst children: Node[] = Array.from(element.childNodes);\n\t\tconst hasBlockChildren: boolean = children.some(\n\t\t\t(c: Node) => c.nodeType === Node.ELEMENT_NODE && this.isBlockElement(c as HTMLElement),\n\t\t);\n\n\t\tif (hasBlockChildren) {\n\t\t\tconst innerBlocks: SliceBlock[] = this.parseContainer(element as HTMLElement);\n\t\t\treturn innerBlocks.map(\n\t\t\t\t(b: SliceBlock): SliceBlock => ({\n\t\t\t\t\ttype: blockType,\n\t\t\t\t\tsegments: b.segments,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\ttype: blockType,\n\t\t\t\tsegments: this.ensureSegments(this.parseInlineChildren(element, [])),\n\t\t\t},\n\t\t];\n\t}\n\n\tprivate parseList(element: HTMLElement, listType: ListType, depth: number): SliceBlock[] {\n\t\tconst blocks: SliceBlock[] = [];\n\n\t\tfor (const child of Array.from(element.children)) {\n\t\t\tconst el: HTMLElement = child as HTMLElement;\n\t\t\tif (el.tagName === 'LI') {\n\t\t\t\tblocks.push(...this.parseListItem(el, listType, depth));\n\t\t\t} else if (el.tagName === 'UL') {\n\t\t\t\tblocks.push(...this.parseList(el, 'bullet', depth + 1));\n\t\t\t} else if (el.tagName === 'OL') {\n\t\t\t\tblocks.push(...this.parseList(el, 'ordered', depth + 1));\n\t\t\t}\n\t\t}\n\n\t\treturn blocks;\n\t}\n\n\tprivate parseListItem(element: HTMLElement, listType: ListType, depth: number): SliceBlock[] {\n\t\tconst blocks: SliceBlock[] = [];\n\t\tconst blockType: NodeTypeName = this.resolveBlockType(nodeType('list_item'));\n\n\t\tconst isChecklist: boolean = this.hasCheckbox(element);\n\t\tconst resolvedListType: ListType = isChecklist ? 'checklist' : listType;\n\t\tconst checked: boolean = isChecklist ? this.isCheckboxChecked(element) : false;\n\n\t\tconst inlineSegments: ContentSegment[] = [];\n\t\tconst nestedLists: HTMLElement[] = [];\n\n\t\tfor (const child of Array.from(element.childNodes)) {\n\t\t\tif (child.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\tconst el: HTMLElement = child as HTMLElement;\n\t\t\t\tif (el.tagName === 'UL' || el.tagName === 'OL') {\n\t\t\t\t\tnestedLists.push(el);\n\t\t\t\t} else if (el.tagName === 'INPUT' && el.getAttribute('type') === 'checkbox') {\n\t\t\t\t\t// skip checkbox inputs — handled via block attributes\n\t\t\t\t} else if (this.isBlockElement(el)) {\n\t\t\t\t\tinlineSegments.push(...this.parseInlineChildren(el, []));\n\t\t\t\t} else {\n\t\t\t\t\tinlineSegments.push(...this.parseInlineNode(child, []));\n\t\t\t\t}\n\t\t\t} else if (child.nodeType === Node.TEXT_NODE) {\n\t\t\t\tconst text: string = child.textContent ?? '';\n\t\t\t\tif (text) {\n\t\t\t\t\tinlineSegments.push(textSegment(text));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tblocks.push({\n\t\t\ttype: blockType,\n\t\t\tattrs: {\n\t\t\t\tlistType: resolvedListType,\n\t\t\t\tindent: depth,\n\t\t\t\t...(resolvedListType === 'checklist' ? { checked } : {}),\n\t\t\t},\n\t\t\tsegments: this.ensureSegments(this.normalizeSegments(inlineSegments)),\n\t\t});\n\n\t\tfor (const nested of nestedLists) {\n\t\t\tconst nestedType: ListType = nested.tagName === 'OL' ? 'ordered' : 'bullet';\n\t\t\tblocks.push(...this.parseList(nested, nestedType, depth + 1));\n\t\t}\n\n\t\treturn blocks;\n\t}\n\n\tprivate parseInlineChildren(\n\t\telement: HTMLElement,\n\t\tparentMarks: readonly Mark[],\n\t): ContentSegment[] {\n\t\tconst segments: ContentSegment[] = [];\n\n\t\tfor (const child of Array.from(element.childNodes)) {\n\t\t\tsegments.push(...this.parseInlineNode(child, parentMarks));\n\t\t}\n\n\t\treturn this.normalizeSegments(segments);\n\t}\n\n\tprivate parseInlineNode(node: Node, parentMarks: readonly Mark[]): ContentSegment[] {\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst text: string = node.textContent ?? '';\n\t\t\tif (text) {\n\t\t\t\treturn [textSegment(text, this.resolveMarks(parentMarks))];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tif (node.nodeType !== Node.ELEMENT_NODE) return [];\n\n\t\tconst el: HTMLElement = node as HTMLElement;\n\t\tconst tag: string = el.tagName;\n\n\t\t// `<br>` becomes a newline that `parseBlockWithLineBreaks` splits into blocks;\n\t\t// resolve it before inline-node rules to preserve that block-splitting behavior.\n\t\tif (tag === 'BR') {\n\t\t\treturn [textSegment('\\n', this.resolveMarks(parentMarks))];\n\t\t}\n\n\t\t// Atomic inline nodes (e.g. <math>) declared by plugins via inline parseHTML rules.\n\t\tconst inlineNode: InlineNode | null = this.matchInlineNodeRule(el);\n\t\tif (inlineNode) {\n\t\t\treturn [inlineSegment(inlineNode)];\n\t\t}\n\n\t\tif (this.isBlockElement(el) && !INLINE_ELEMENTS.has(tag)) {\n\t\t\treturn this.parseInlineChildren(el, parentMarks);\n\t\t}\n\n\t\tconst childMarks: readonly Mark[] = this.mergeMarks(parentMarks, this.marksFromElement(el));\n\t\treturn this.parseInlineChildren(el, childMarks);\n\t}\n\n\t/** Matches an element against inline-node parse rules, returning a built InlineNode or null. */\n\tprivate matchInlineNodeRule(el: HTMLElement): InlineNode | null {\n\t\tif (this.inlineParseRules.length === 0) return null;\n\n\t\tconst tag: string = el.tagName.toLowerCase();\n\t\tfor (const entry of this.inlineParseRules) {\n\t\t\tif (entry.rule.tag !== tag) continue;\n\n\t\t\tif (entry.rule.getAttrs) {\n\t\t\t\tconst attrs = entry.rule.getAttrs(el);\n\t\t\t\tif (attrs === false) continue;\n\t\t\t\treturn createInlineNode(\n\t\t\t\t\tinlineType(entry.type) as InlineTypeName,\n\t\t\t\t\tattrs as Readonly<Record<string, string | number | boolean>>,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn createInlineNode(inlineType(entry.type) as InlineTypeName);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate parsePreContent(element: HTMLElement): ContentSegment[] {\n\t\tconst text: string = element.textContent ?? '';\n\t\tif (!text) return [];\n\t\treturn [textSegment(text)];\n\t}\n\n\tprivate parseTableAsParagraphs(element: HTMLElement): SliceBlock[] {\n\t\tconst blocks: SliceBlock[] = [];\n\t\tconst cells: NodeListOf<HTMLTableCellElement> = element.querySelectorAll('td, th');\n\n\t\tfor (const cell of Array.from(cells)) {\n\t\t\tconst segments: ContentSegment[] = this.parseInlineChildren(cell, []);\n\t\t\tconst hasContent: boolean = segments.some(\n\t\t\t\t(s: ContentSegment) => s.kind === 'inline' || s.text.length > 0,\n\t\t\t);\n\t\t\tif (hasContent) {\n\t\t\t\tblocks.push({\n\t\t\t\t\ttype: this.resolveBlockType(nodeType('paragraph')),\n\t\t\t\t\tsegments: this.ensureSegments(segments),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn blocks;\n\t}\n\n\tprivate marksFromElement(element: HTMLElement): readonly Mark[] {\n\t\tconst registryMarks: readonly Mark[] | null = this.matchMarkRules(element);\n\t\tif (registryMarks) return registryMarks;\n\n\t\treturn this.fallbackMarksFromElement(element);\n\t}\n\n\tprivate matchMarkRules(element: HTMLElement): readonly Mark[] | null {\n\t\tif (this.markParseRules.length === 0) return null;\n\n\t\tconst tag: string = element.tagName.toLowerCase();\n\t\tconst marks: Mark[] = [];\n\t\tconst matchedTypes = new Set<string>();\n\n\t\tfor (const entry of this.markParseRules) {\n\t\t\tif (entry.rule.tag !== tag) continue;\n\t\t\tif (matchedTypes.has(entry.type)) continue;\n\n\t\t\tif (entry.rule.getAttrs) {\n\t\t\t\tconst attrs = entry.rule.getAttrs(element);\n\t\t\t\tif (attrs === false) continue;\n\t\t\t\tmarks.push({\n\t\t\t\t\ttype: markType(entry.type),\n\t\t\t\t\t...(Object.keys(attrs).length > 0 ? { attrs } : {}),\n\t\t\t\t} as Mark);\n\t\t\t} else {\n\t\t\t\tmarks.push({ type: markType(entry.type) });\n\t\t\t}\n\t\t\tmatchedTypes.add(entry.type);\n\t\t}\n\n\t\treturn matchedTypes.size > 0 ? marks : null;\n\t}\n\n\tprivate fallbackMarksFromElement(element: HTMLElement): readonly Mark[] {\n\t\tconst def: FallbackMarkDef | undefined = FALLBACK_MARK_MAP.get(element.tagName);\n\t\tif (!def) return [];\n\n\t\tif (def.getAttrs) {\n\t\t\tconst attrs = def.getAttrs(element);\n\t\t\tif (attrs === false) return [];\n\t\t\treturn Object.keys(attrs).length > 0\n\t\t\t\t? [{ type: markType(def.markName), attrs } as Mark]\n\t\t\t\t: [{ type: markType(def.markName) }];\n\t\t}\n\n\t\treturn [{ type: markType(def.markName) }];\n\t}\n\n\tprivate mergeMarks(existing: readonly Mark[], additional: readonly Mark[]): readonly Mark[] {\n\t\tconst result: Mark[] = [...existing];\n\t\tfor (const mark of additional) {\n\t\t\tif (!result.some((m: Mark) => m.type === mark.type)) {\n\t\t\t\tresult.push(mark);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate resolveMarks(rawMarks: readonly Mark[]): readonly Mark[] {\n\t\treturn rawMarks.filter((m: Mark) => isMarkAllowed(this.schema, m.type));\n\t}\n\n\tprivate resolveBlockType(type: NodeTypeName): NodeTypeName {\n\t\tif (isNodeTypeAllowed(this.schema, type)) return type;\n\t\treturn nodeType('paragraph');\n\t}\n\n\tprivate normalizeSegments(segments: readonly ContentSegment[]): ContentSegment[] {\n\t\tif (segments.length === 0) return [];\n\n\t\tconst result: ContentSegment[] = [];\n\t\tfor (const segment of segments) {\n\t\t\t// Inline nodes are atomic and act as a merge barrier between text runs.\n\t\t\tif (segment.kind === 'inline') {\n\t\t\t\tresult.push(segment);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst prev: ContentSegment | undefined = result[result.length - 1];\n\t\t\tif (prev && prev.kind === 'text' && markSetsEqual(prev.marks, segment.marks)) {\n\t\t\t\tresult[result.length - 1] = textSegment(prev.text + segment.text, prev.marks);\n\t\t\t} else if (segment.text.length > 0) {\n\t\t\t\tresult.push(segment);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate ensureSegments(segments: readonly ContentSegment[]): readonly ContentSegment[] {\n\t\tif (segments.length === 0) return [textSegment('')];\n\t\treturn segments;\n\t}\n\n\tprivate matchBlockRule(el: HTMLElement): {\n\t\treadonly type: NodeTypeName;\n\t\treadonly attrs?: Record<string, string | number | boolean>;\n\t} | null {\n\t\tconst tag: string = el.tagName.toLowerCase();\n\t\tfor (const entry of this.blockParseRules) {\n\t\t\tif (entry.rule.tag !== tag) continue;\n\t\t\tif (entry.rule.getAttrs) {\n\t\t\t\tconst attrs = entry.rule.getAttrs(el);\n\t\t\t\tif (attrs === false) continue;\n\t\t\t\treturn {\n\t\t\t\t\ttype: nodeType(entry.type),\n\t\t\t\t\tattrs: attrs as Record<string, string | number | boolean>,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { type: nodeType(entry.type) };\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate isBlockElement(el: HTMLElement): boolean {\n\t\tif (BLOCK_ELEMENTS.has(el.tagName)) return true;\n\t\tconst tag: string = el.tagName.toLowerCase();\n\t\treturn this.blockParseRules.some((entry) => entry.rule.tag === tag);\n\t}\n\n\tprivate hasCheckbox(element: HTMLElement): boolean {\n\t\tconst input: HTMLInputElement | null = element.querySelector('input[type=\"checkbox\"]');\n\t\treturn input !== null;\n\t}\n\n\tprivate isCheckboxChecked(element: HTMLElement): boolean {\n\t\tconst input: HTMLInputElement | null = element.querySelector('input[type=\"checkbox\"]');\n\t\treturn input?.hasAttribute('checked') ?? false;\n\t}\n\n\t/** Checks whether an element contains any block-level descendants. */\n\tprivate containsBlockDescendants(el: HTMLElement): boolean {\n\t\tfor (const child of Array.from(el.children)) {\n\t\t\tif (this.isBlockElement(child as HTMLElement)) return true;\n\t\t\tif (this.containsBlockDescendants(child as HTMLElement)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Parses a container element, prepending inherited marks. */\n\tprivate parseContainerWithMarks(\n\t\tcontainer: HTMLElement,\n\t\tinheritedMarks: readonly Mark[],\n\t): SliceBlock[] {\n\t\tconst innerBlocks: SliceBlock[] = this.parseContainer(container);\n\t\tif (inheritedMarks.length === 0) return innerBlocks;\n\n\t\treturn innerBlocks.map(\n\t\t\t(block: SliceBlock): SliceBlock => ({\n\t\t\t\t...block,\n\t\t\t\tsegments: this.prependMarks(block.segments, inheritedMarks),\n\t\t\t}),\n\t\t);\n\t}\n\n\t/** Merges inherited marks into each segment's mark list. */\n\tprivate prependMarks(\n\t\tsegments: readonly ContentSegment[],\n\t\tmarks: readonly Mark[],\n\t): readonly ContentSegment[] {\n\t\treturn segments.map(\n\t\t\t(s: ContentSegment): ContentSegment =>\n\t\t\t\ts.kind === 'inline' ? s : textSegment(s.text, this.mergeMarks(marks, s.marks)),\n\t\t);\n\t}\n\n\t/**\n\t * Parses a block element, splitting into multiple blocks\n\t * at `<br>` boundaries.\n\t */\n\tprivate parseBlockWithLineBreaks(element: HTMLElement, blockType: NodeTypeName): SliceBlock[] {\n\t\tconst segments: readonly ContentSegment[] = this.parseInlineChildren(element, []);\n\t\tconst hasLineBreak: boolean = segments.some(\n\t\t\t(s: ContentSegment) => s.kind === 'text' && s.text.includes('\\n'),\n\t\t);\n\n\t\tif (!hasLineBreak) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: blockType,\n\t\t\t\t\tsegments: this.ensureSegments(segments),\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\n\t\tconst blocks: SliceBlock[] = [];\n\t\tlet current: ContentSegment[] = [];\n\n\t\tfor (const segment of segments) {\n\t\t\tif (segment.kind === 'inline') {\n\t\t\t\tcurrent.push(segment);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst parts: readonly string[] = segment.text.split('\\n');\n\t\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: blockType,\n\t\t\t\t\t\tsegments: this.ensureSegments(this.normalizeSegments(current)),\n\t\t\t\t\t});\n\t\t\t\t\tcurrent = [];\n\t\t\t\t}\n\t\t\t\tconst part: string = parts[i] ?? '';\n\t\t\t\tif (part) {\n\t\t\t\t\tcurrent.push(textSegment(part, segment.marks));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tblocks.push({\n\t\t\ttype: blockType,\n\t\t\tsegments: this.ensureSegments(this.normalizeSegments(current)),\n\t\t});\n\n\t\treturn blocks;\n\t}\n\n\tprivate flushPendingSegments(blocks: SliceBlock[], segments: ContentSegment[]): void {\n\t\tconst normalized: ContentSegment[] = this.normalizeSegments(segments);\n\t\tif (normalized.length === 0) return;\n\t\t// Skip whitespace-only runs, but keep anything that carries an inline node.\n\t\tconst isBlank: boolean = normalized.every(\n\t\t\t(s: ContentSegment) => s.kind === 'text' && s.text.trim() === '',\n\t\t);\n\t\tif (isBlank) return;\n\n\t\tblocks.push({\n\t\t\ttype: this.resolveBlockType(nodeType('paragraph')),\n\t\t\tsegments: normalized,\n\t\t});\n\t}\n}\n","/**\n * Normalizes legacy HTML elements to their modern CSS equivalents.\n * Converts {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/font | <font>}\n * `color/face/size` attributes to `<span style=\"color/font-family/font-size\">`\n * so that DOMPurify + existing plugin parse rules handle them transparently.\n *\n * Must be called BEFORE DOMPurify sanitization.\n */\n\nimport { setStyleText } from '../style/StyleRuntime.js';\n\n/** Maps HTML `<font size>` values (1–7) to CSS font-size keywords per the HTML 4 spec. */\nconst FONT_SIZE_MAP: Readonly<Record<string, string>> = {\n\t'1': 'x-small',\n\t'2': 'small',\n\t'3': 'medium',\n\t'4': 'large',\n\t'5': 'x-large',\n\t'6': 'xx-large',\n\t'7': 'xxx-large',\n};\n\n/**\n * Normalizes legacy `<font>` elements in-place, converting them to `<span style=\"...\">`.\n *\n * - `color` attribute → `style.color`\n * - `face` attribute → `style.fontFamily`\n * - `size` attribute → mapped to CSS `font-size` keyword (unless inline `font-size` already set)\n * - `<font>` with no meaningful attributes → unwrapped (children promoted to parent)\n */\nexport function normalizeLegacyHTML(container: DocumentFragment | HTMLElement): void {\n\tconst fonts: HTMLElement[] = Array.from(container.querySelectorAll('font'));\n\n\tfor (const font of fonts) {\n\t\tconst color: string | null = font.getAttribute('color');\n\t\tconst face: string | null = font.getAttribute('face');\n\t\tconst size: string | null = font.getAttribute('size');\n\t\tconst existingStyle: string = font.getAttribute('style') ?? '';\n\n\t\tconst hasFormatting: boolean =\n\t\t\tcolor !== null || face !== null || size !== null || existingStyle.length > 0;\n\n\t\tif (!hasFormatting) {\n\t\t\tunwrap(font);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst span: HTMLSpanElement = document.createElement('span');\n\t\tconst styles: string[] = [];\n\n\t\tif (existingStyle) {\n\t\t\tstyles.push(existingStyle.endsWith(';') ? existingStyle : `${existingStyle};`);\n\t\t}\n\n\t\tif (color) {\n\t\t\tstyles.push(`color: ${color};`);\n\t\t}\n\n\t\tif (face) {\n\t\t\tstyles.push(`font-family: ${face};`);\n\t\t}\n\n\t\tif (size && !hasFontSizeInStyle(existingStyle)) {\n\t\t\tconst mapped: string | undefined = FONT_SIZE_MAP[size];\n\t\t\tif (mapped) {\n\t\t\t\tstyles.push(`font-size: ${mapped};`);\n\t\t\t}\n\t\t}\n\n\t\tsetStyleText(span, styles.join(' '));\n\n\t\tmoveChildren(font, span);\n\t\tfont.parentNode?.replaceChild(span, font);\n\t}\n}\n\n/** Checks whether an inline style string already contains a `font-size` declaration. */\nfunction hasFontSizeInStyle(style: string): boolean {\n\treturn style.toLowerCase().includes('font-size');\n}\n\n/** Moves all child nodes from `source` to `target`. */\nfunction moveChildren(source: Node, target: Node): void {\n\twhile (source.firstChild) {\n\t\ttarget.appendChild(source.firstChild);\n\t}\n}\n\n/** Unwraps an element, promoting its children into its parent. */\nfunction unwrap(element: HTMLElement): void {\n\tconst parent: Node | null = element.parentNode;\n\tif (!parent) return;\n\n\twhile (element.firstChild) {\n\t\tparent.insertBefore(element.firstChild, element);\n\t}\n\tparent.removeChild(element);\n}\n","/**\n * Handles paste of HTML and plain text from the system clipboard.\n * Sanitizes HTML with DOMPurify, normalizes legacy markup, and delegates\n * to HTMLParser or DocumentParser as appropriate.\n */\n\nimport DOMPurify from 'dompurify';\nimport {\n\ttype InsertionContext,\n\tcanContainerHoldBlocks,\n\tcloneBlockWithNewIds,\n\tfindBlockRecursive,\n\tresolveRootEscapeContext,\n\tresolveRootInsertionContext,\n} from '../commands/BlockInsertion.js';\nimport {\n\taddDeleteSelectionSteps,\n\tfindLastLeafBlockId,\n\tinsertTextCommand,\n} from '../commands/Commands.js';\nimport { pasteSlice } from '../commands/PasteCommand.js';\nimport { normalizeCompositeBlocks } from '../editor/ContentSerializer.js';\nimport { plainTextSlice } from '../model/ContentSlice.js';\nimport { type BlockNode, generateBlockId, getBlockText } from '../model/Document.js';\nimport { SAFE_URI_REGEXP } from '../model/HTMLUtils.js';\nimport { schemaFromRegistry } from '../model/Schema.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n\tisTextSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { preserveHTMLIdSanitizeConfig } from '../serialization/HTMLSanitization.js';\nimport { parseHTMLToDocument } from '../serialization/index.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { HTMLParser } from './HTMLParser.js';\nimport type { DispatchFn, GetStateFn } from './InputHandler.js';\nimport { normalizeLegacyHTML } from './LegacyHTMLNormalizer.js';\n\n/** Tags forbidden in pre-sanitization (active content that must never reach innerHTML). */\nconst PRE_SANITIZE_FORBID: string[] = [\n\t'script',\n\t'style',\n\t'iframe',\n\t'object',\n\t'embed',\n\t'form',\n\t'noscript',\n];\n\n/** Block roots represented directly by the document model. */\nconst MODELLED_BLOCK_TAGS: ReadonlySet<string> = new Set([\n\t'p',\n\t'div',\n\t'h1',\n\t'h2',\n\t'h3',\n\t'h4',\n\t'h5',\n\t'h6',\n\t'li',\n\t'blockquote',\n\t'table',\n\t'tr',\n\t'td',\n\t'th',\n\t'pre',\n\t'figure',\n\t'hr',\n]);\n\nexport class PasteHTMLHandler {\n\tconstructor(\n\t\tprivate readonly getState: GetStateFn,\n\t\tprivate readonly dispatch: DispatchFn,\n\t\tprivate readonly schemaRegistry: SchemaRegistry | undefined,\n\t\tprivate readonly tryRichPasteFromJson: (json: string) => boolean,\n\t) {}\n\n\t/** Handles HTML or plain text paste from system clipboard. */\n\thandleHTMLOrTextPaste(clipboardData: DataTransfer, plainText: string): void {\n\t\tconst state = this.getState();\n\t\tconst html: string = clipboardData.getData('text/html');\n\n\t\tif (html) {\n\t\t\tthis.handleHTML(html, state);\n\t\t\treturn;\n\t\t}\n\n\t\tif (plainText) this.pastePlainText(plainText);\n\t}\n\n\t/** Inserts plain text through the standard paste pipeline (no HTML interpretation). */\n\tpastePlainText(text: string): void {\n\t\tif (!text) return;\n\t\tconst slice = plainTextSlice(text);\n\t\tthis.dispatch(pasteSlice(this.getState(), slice));\n\t}\n\n\t/** Inserts a pre-built HTML string through the standard paste pipeline. */\n\tpasteHTMLString(html: string): void {\n\t\tthis.handleHTML(html, this.getState());\n\t}\n\n\t/** Sanitizes and processes pasted HTML content. */\n\tprivate handleHTML(html: string, state: EditorState): void {\n\t\t// The pre-sanitize pass scrubs active content while keeping DOMPurify's\n\t\t// broad default allowlist so legacy normalization still sees deprecated\n\t\t// tags (`<font>`, `<center>`, ...). Schema-registered tags/attrs are added\n\t\t// on top (`ADD_TAGS`/`ADD_ATTR`) so plugin markup outside DOMPurify's\n\t\t// defaults survives this first pass — notably MathML `<semantics>` and\n\t\t// `<annotation>` (and its `encoding` attr), which carry a formula's LaTeX\n\t\t// source. Without this they are stripped here before the registry-aware\n\t\t// passes run, dropping the annotation and leaving the formula uneditable.\n\t\tconst preSanitized: string = DOMPurify.sanitize(html, {\n\t\t\tFORBID_TAGS: PRE_SANITIZE_FORBID,\n\t\t\tADD_TAGS: this.schemaRegistry ? this.schemaRegistry.getAllowedTags() : [],\n\t\t\tADD_ATTR: this.schemaRegistry ? this.schemaRegistry.getAllowedAttrs() : [],\n\t\t\tALLOWED_URI_REGEXP: SAFE_URI_REGEXP,\n\t\t\t...preserveHTMLIdSanitizeConfig(),\n\t\t});\n\t\tconst richJson: string | undefined = this.extractRichData(preSanitized);\n\t\tif (richJson && this.tryRichPasteFromJson(richJson)) return;\n\n\t\tconst preTemplate: HTMLTemplateElement = document.createElement('template');\n\t\tpreTemplate.innerHTML = preSanitized;\n\t\tnormalizeLegacyHTML(preTemplate.content);\n\t\tconst normalizedHTML: string = preTemplate.innerHTML;\n\n\t\tconst allowedTags: string[] = this.schemaRegistry\n\t\t\t? this.schemaRegistry.getAllowedTags()\n\t\t\t: ['strong', 'em', 'u', 'b', 'i', 'p', 'br', 'div', 'span'];\n\t\tconst allowedAttrs: string[] = this.schemaRegistry ? this.schemaRegistry.getAllowedAttrs() : [];\n\t\tconst sanitized: string = DOMPurify.sanitize(normalizedHTML, {\n\t\t\tALLOWED_TAGS: allowedTags,\n\t\t\tALLOWED_ATTR: allowedAttrs,\n\t\t\tALLOWED_URI_REGEXP: SAFE_URI_REGEXP,\n\t\t\t...preserveHTMLIdSanitizeConfig(),\n\t\t});\n\n\t\tif (this.schemaRegistry) {\n\t\t\tconst template: HTMLTemplateElement = document.createElement('template');\n\t\t\ttemplate.innerHTML = sanitized;\n\n\t\t\tif (this.requiresDocumentParser(sanitized, template.content)) {\n\t\t\t\tthis.handleDocumentPaste(sanitized, state);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst schema = schemaFromRegistry(this.schemaRegistry);\n\t\t\tconst parser = new HTMLParser({\n\t\t\t\tschema,\n\t\t\t\tschemaRegistry: this.schemaRegistry,\n\t\t\t});\n\t\t\tconst slice = parser.parse(template.content);\n\t\t\tthis.dispatch(pasteSlice(state, slice));\n\t\t} else {\n\t\t\tconst text: string = this.extractTextFromHTML(sanitized);\n\t\t\tif (text) this.dispatch(insertTextCommand(state, text, 'paste'));\n\t\t}\n\t}\n\n\t/** Extracts embedded rich block JSON from HTML (data-notectl-rich). */\n\tprivate extractRichData(html: string): string | undefined {\n\t\tconst template: HTMLTemplateElement = document.createElement('template');\n\t\ttemplate.innerHTML = html;\n\t\tconst richEl: Element | null = template.content.querySelector('[data-notectl-rich]');\n\t\tconst encoded: string | null | undefined = richEl?.getAttribute('data-notectl-rich');\n\t\tif (!encoded) return undefined;\n\t\treturn this.decodeHTMLEntities(encoded);\n\t}\n\n\t/** Decodes HTML entities produced by ClipboardHandler's encoding. */\n\tprivate decodeHTMLEntities(text: string): string {\n\t\treturn text\n\t\t\t.replace(/"/g, '\"')\n\t\t\t.replace(/'/g, \"'\")\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/&/g, '&');\n\t}\n\n\t/**\n\t * Checks whether HTML requires the DocumentParser (tables, blockquotes,\n\t * multi-block list items, void blocks).\n\t */\n\tprivate requiresDocumentParser(html: string, container: DocumentFragment): boolean {\n\t\tif (this.containsModelledBlockID(container)) return true;\n\t\tif (/<table[\\s>]/i.test(html)) return true;\n\t\t// Blockquotes are container blocks (#136): the flat HTMLParser/slice path\n\t\t// cannot represent their nested block children, so route them through the\n\t\t// container-aware DocumentParser to preserve nesting (and to wrap inline\n\t\t// content in a paragraph rather than producing an invalid flat blockquote).\n\t\tif (/<blockquote[\\s>]/i.test(html)) return true;\n\t\t// Multi-block list items are containers too (#194): the slice path would\n\t\t// silently flatten a second paragraph or a nested code block into the\n\t\t// item's inline text.\n\t\tif (/<li[\\s>]/i.test(html) && this.containsMultiBlockListItem(container)) return true;\n\t\treturn this.containsVoidBlockElements(container);\n\t}\n\n\t/** Semantic IDs belong to whole blocks, which the flat slice parser cannot represent. */\n\tprivate containsModelledBlockID(container: DocumentFragment): boolean {\n\t\tconst blockRuleTags: ReadonlySet<string> = new Set(\n\t\t\tthis.schemaRegistry?.getBlockParseRules().map((entry) => entry.rule.tag) ?? [],\n\t\t);\n\t\tfor (const element of Array.from(container.querySelectorAll('[id]'))) {\n\t\t\tconst tag: string = element.tagName.toLowerCase();\n\t\t\tif (MODELLED_BLOCK_TAGS.has(tag) || blockRuleTags.has(tag)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Whether any `<li>` carries block content beyond a single paragraph: two or\n\t * more block-level children, a non-paragraph block child (code, quote,\n\t * heading, rule), or a block child mixed with inline content. Nested lists\n\t * and the checklist checkbox stay on the flat path — the slice model\n\t * represents those losslessly.\n\t */\n\tprivate containsMultiBlockListItem(container: DocumentFragment): boolean {\n\t\tconst nonParagraphBlockTags: ReadonlySet<string> = new Set([\n\t\t\t'PRE',\n\t\t\t'BLOCKQUOTE',\n\t\t\t'TABLE',\n\t\t\t'HR',\n\t\t\t'H1',\n\t\t\t'H2',\n\t\t\t'H3',\n\t\t\t'H4',\n\t\t\t'H5',\n\t\t\t'H6',\n\t\t]);\n\t\tfor (const li of Array.from(container.querySelectorAll('li'))) {\n\t\t\tlet paragraphCount = 0;\n\t\t\tlet hasInlineContent = false;\n\t\t\tfor (const child of Array.from(li.childNodes)) {\n\t\t\t\tif (child.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\t\tconst tag: string = (child as Element).tagName;\n\t\t\t\t\tif (tag === 'UL' || tag === 'OL' || tag === 'INPUT' || tag === 'LI') continue;\n\t\t\t\t\tif (nonParagraphBlockTags.has(tag)) return true;\n\t\t\t\t\tif (tag === 'P' || tag === 'DIV') {\n\t\t\t\t\t\tparagraphCount++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\thasInlineContent = true;\n\t\t\t\t} else if (child.nodeType === Node.TEXT_NODE) {\n\t\t\t\t\tif ((child.textContent ?? '').trim() !== '') hasInlineContent = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (paragraphCount >= 2 || (paragraphCount >= 1 && hasInlineContent)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Checks whether a DOM fragment contains elements matching void block parse rules. */\n\tprivate containsVoidBlockElements(container: DocumentFragment): boolean {\n\t\tif (!this.schemaRegistry) return false;\n\t\tconst blockRules = this.schemaRegistry.getBlockParseRules();\n\t\tfor (const entry of blockRules) {\n\t\t\tconst spec = this.schemaRegistry.getNodeSpec(entry.type);\n\t\t\tif (!spec?.isVoid) continue;\n\t\t\tconst elements: NodeListOf<Element> = container.querySelectorAll(entry.rule.tag);\n\t\t\tfor (const el of Array.from(elements)) {\n\t\t\t\tif (!entry.rule.getAttrs) return true;\n\t\t\t\tconst attrs = entry.rule.getAttrs(el as HTMLElement);\n\t\t\t\tif (attrs !== false) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Handles paste of HTML requiring the DocumentParser (tables, void blocks). */\n\tprivate handleDocumentPaste(html: string, state: EditorState): void {\n\t\tif (!this.schemaRegistry) return;\n\n\t\tconst parsed = parseHTMLToDocument(html, this.schemaRegistry);\n\t\tconst doc = normalizeCompositeBlocks(parsed, this.schemaRegistry);\n\t\tif (doc.children.length === 0) return;\n\n\t\tconst sel = state.selection;\n\t\tconst builder = state.transaction('paste');\n\n\t\tlet landingId: BlockId | undefined;\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tlandingId = addDeleteSelectionSteps(state, builder);\n\t\t}\n\n\t\tconst anchorBlockId: BlockId =\n\t\t\tlandingId ??\n\t\t\t(isNodeSelection(sel) ? sel.nodeId : isGapCursor(sel) ? sel.blockId : sel.anchor.blockId);\n\n\t\tconst ctx = resolveRootInsertionContext(state, anchorBlockId, this.schemaRegistry);\n\t\tif (!ctx) return;\n\n\t\t// Schema guard (#166): a container such as a table cell does not allow every\n\t\t// block type. When the parsed blocks do not fit the target container, escape\n\t\t// the insertion to the document root, placing them after the top-level\n\t\t// ancestor (e.g. the outer table) rather than producing schema-invalid\n\t\t// nesting like a table inside a table cell.\n\t\tconst containerId: BlockId | undefined = ctx.parentPath[ctx.parentPath.length - 1];\n\t\tconst containerType: string | undefined = containerId\n\t\t\t? state.getBlock(containerId)?.type\n\t\t\t: undefined;\n\t\tconst targetCtx: InsertionContext | undefined = canContainerHoldBlocks(\n\t\t\tthis.schemaRegistry,\n\t\t\tcontainerType,\n\t\t\tdoc.children,\n\t\t)\n\t\t\t? ctx\n\t\t\t: resolveRootEscapeContext(state, anchorBlockId);\n\t\tif (!targetCtx) return;\n\n\t\tconst insertOffset: number = isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\tlet insertIndex: number = targetCtx.anchorIndex + insertOffset;\n\t\tlet lastBlockId: BlockId | undefined;\n\t\tlet lastClonedRoot: BlockNode | undefined;\n\n\t\tfor (const block of doc.children) {\n\t\t\tconst newId: BlockId = generateBlockId();\n\t\t\tconst cloned: BlockNode = cloneBlockWithNewIds(block, newId);\n\t\t\tbuilder.insertNode(targetCtx.parentPath, insertIndex, cloned);\n\t\t\tinsertIndex++;\n\t\t\tlastBlockId = findLastLeafBlockId(cloned);\n\t\t\tlastClonedRoot = cloned;\n\t\t}\n\n\t\tif (targetCtx.isAnchorEmpty && !isGapCursor(sel)) {\n\t\t\tbuilder.removeNode(targetCtx.parentPath, targetCtx.anchorIndex);\n\t\t}\n\n\t\tif (lastBlockId && lastClonedRoot) {\n\t\t\tconst lastBlock = findBlockRecursive(lastClonedRoot, lastBlockId);\n\t\t\tconst len: number = lastBlock ? getBlockText(lastBlock).length : 0;\n\t\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, len));\n\t\t}\n\n\t\tthis.dispatch(builder.build());\n\t}\n\n\tprivate extractTextFromHTML(html: string): string {\n\t\tconst template: HTMLTemplateElement = document.createElement('template');\n\t\ttemplate.innerHTML = html;\n\t\treturn template.content.textContent ?? '';\n\t}\n}\n","/**\n * Handles paste of rich block data: internal clipboard (RichBlockData)\n * and application/x-notectl-block JSON.\n *\n * Resolves insertion context (root or table cell) and builds paste transactions.\n */\n\nimport {\n\ttype InsertionContext,\n\tcanContainerHoldBlocks,\n\tcreateBlockFromRichData,\n\tfindTableCellAncestor,\n\tresolveAnchorBlockId,\n\tresolveCellInsertionContext,\n\tresolveRootEscapeContext,\n\tresolveRootInsertionContext,\n\trichBlocksToSlice,\n\tsanitizeAttrs,\n\tvalidateRichBlockData,\n} from '../commands/BlockInsertion.js';\nimport { addDeleteSelectionSteps } from '../commands/Commands.js';\nimport { pasteSlice } from '../commands/PasteCommand.js';\nimport {\n\ttype BlockAttrs,\n\ttype BlockNode,\n\tcreateBlockNode,\n\tgenerateBlockId,\n\tgetBlockLength,\n} from '../model/Document.js';\nimport { normalizeHTMLId } from '../model/HTMLUtils.js';\nimport type { RichBlockData } from '../model/RichBlockData.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisTextSelection,\n} from '../model/Selection.js';\nimport type { BlockId, NodeTypeName } from '../model/TypeBrands.js';\nimport { nodeType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { DispatchFn, GetStateFn } from './InputHandler.js';\n\nexport class PasteRichBlockHandler {\n\tconstructor(\n\t\tprivate readonly getState: GetStateFn,\n\t\tprivate readonly dispatch: DispatchFn,\n\t\tprivate readonly schemaRegistry?: SchemaRegistry,\n\t) {}\n\n\t/** Handles paste of an internal block node from ClipboardHandler. */\n\thandleBlockPaste(json: string): void {\n\t\tlet parsed: { type?: string; htmlId?: unknown; attrs?: Record<string, unknown> };\n\t\ttry {\n\t\t\tparsed = JSON.parse(json) as {\n\t\t\t\ttype?: string;\n\t\t\t\thtmlId?: unknown;\n\t\t\t\tattrs?: Record<string, unknown>;\n\t\t\t};\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tconst typeName: string | undefined = parsed.type;\n\t\tif (!typeName) return;\n\n\t\tconst spec = this.schemaRegistry?.getNodeSpec(typeName);\n\t\tif (this.schemaRegistry && !spec) return;\n\n\t\tconst state = this.getState();\n\t\tconst sel = state.selection;\n\t\tconst anchorBlockId: BlockId = resolveAnchorBlockId(sel);\n\n\t\tconst newBlockId: BlockId = generateBlockId();\n\t\tconst attrs: BlockAttrs | undefined = sanitizeAttrs(parsed.attrs, spec?.attrs) as\n\t\t\t| BlockAttrs\n\t\t\t| undefined;\n\t\tconst newBlock: BlockNode = createBlockNode(\n\t\t\tnodeType(typeName) as NodeTypeName,\n\t\t\tundefined,\n\t\t\tnewBlockId,\n\t\t\tattrs,\n\t\t\tnormalizeHTMLId(parsed.htmlId),\n\t\t);\n\n\t\t// Table cell: insert as last child, unless the cell's content rule forbids\n\t\t// the block type (e.g. a math_display copied into a cell). When it does not\n\t\t// fit, escape to the document root after the outer table rather than nesting\n\t\t// schema-invalidly — mirrors the HTML paste guard (#166).\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\t\tif (cellId) {\n\t\t\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\t\t\tif (this.cellRejects(cell, [newBlock])) {\n\t\t\t\tconst escapeCtx = resolveRootEscapeContext(state, anchorBlockId);\n\t\t\t\tif (!escapeCtx) return;\n\t\t\t\tthis.dispatchNodeInsert(state, escapeCtx.parentPath, escapeCtx.anchorIndex + 1, newBlock);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst ctx = resolveCellInsertionContext(state, anchorBlockId, cellId, this.schemaRegistry);\n\t\t\tif (!ctx || !cell) return;\n\t\t\tthis.dispatchNodeInsert(state, ctx.parentPath, cell.children.length, newBlock);\n\t\t\treturn;\n\t\t}\n\n\t\t// Root: insert after anchor\n\t\tconst ctx = resolveRootInsertionContext(state, anchorBlockId, this.schemaRegistry);\n\t\tif (!ctx) return;\n\n\t\tconst insertOffset: number = isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\tthis.dispatchNodeInsert(state, ctx.parentPath, ctx.anchorIndex + insertOffset, newBlock);\n\t}\n\n\t/** Inserts a single block at the position and node-selects it. */\n\tprivate dispatchNodeInsert(\n\t\tstate: EditorState,\n\t\tparentPath: readonly BlockId[],\n\t\tindex: number,\n\t\tblock: BlockNode,\n\t): void {\n\t\tconst builder = state.transaction('paste');\n\t\tbuilder.insertNode(parentPath, index, block);\n\t\tbuilder.setSelection(createNodeSelection(block.id, [...parentPath, block.id]));\n\t\tthis.dispatch(builder.build());\n\t}\n\n\t/**\n\t * True when the container's content rule forbids any of the given blocks, so\n\t * the paste must not nest them there. Undefined cell or absent registry cannot\n\t * be checked, so they are treated as accepting (graceful degradation).\n\t */\n\tprivate cellRejects(cell: BlockNode | undefined, blocks: readonly BlockNode[]): boolean {\n\t\tif (!cell || !this.schemaRegistry) return false;\n\t\treturn !canContainerHoldBlocks(this.schemaRegistry, cell.type, blocks);\n\t}\n\n\t/** Parses JSON and delegates to handleRichPaste (for HTML-embedded data). */\n\thandleRichPasteFromJson(json: string): boolean {\n\t\tlet blocks: RichBlockData[];\n\t\ttry {\n\t\t\tblocks = JSON.parse(json) as RichBlockData[];\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t\tif (!Array.isArray(blocks) || blocks.length === 0) return false;\n\t\treturn this.handleRichPaste(blocks);\n\t}\n\n\t/**\n\t * Handles paste of rich block data (text selections carrying block structure).\n\t * Returns true if handled, false to fall through to plain-text paste.\n\t */\n\thandleRichPaste(blocks: readonly RichBlockData[]): boolean {\n\t\tif (blocks.length === 0) return false;\n\n\t\tconst hasStructured: boolean = blocks.some(\n\t\t\t(b) =>\n\t\t\t\tb.htmlId !== undefined ||\n\t\t\t\t(b.type !== undefined && b.type !== 'paragraph') ||\n\t\t\t\tb.segments?.some((segment) => segment.kind === 'inline'),\n\t\t);\n\t\tif (!hasStructured && blocks.length <= 1) return false;\n\n\t\tlet state = this.getState();\n\t\tconst sel = state.selection;\n\t\tconst anchorBlockId: BlockId = resolveAnchorBlockId(sel);\n\n\t\t// The canonical split-insert-merge paste strategy (PasteCommand) splits the\n\t\t// caret block at the offset and merges the boundary fragments back in, which\n\t\t// is what restores a cross-block cut/paste round-trip (#165). It only fits a\n\t\t// collapsed caret in a NON-EMPTY block at the document root:\n\t\t// - an empty anchor has no content to split, so whole-block insertion below\n\t\t// (which removes the empty anchor and drops the clipboard blocks verbatim)\n\t\t// round-trips more faithfully and preserves their block types;\n\t\t// - the strategy inserts middle blocks at the document root keyed by flat\n\t\t// block order, so a nested anchor (table cell, blockquote) is out of reach;\n\t\t// - node selections and gap cursors have no text caret to split.\n\t\tconst anchorBlock: BlockNode | undefined = state.getBlock(anchorBlockId);\n\t\tconst isRootChild: boolean = state.doc.children.some((c) => c.id === anchorBlockId);\n\t\tconst carriesHTMLId: boolean = blocks.some((block) => normalizeHTMLId(block.htmlId));\n\t\tconst splitsAtCaret: boolean =\n\t\t\t!carriesHTMLId &&\n\t\t\tisTextSelection(sel) &&\n\t\t\tisCollapsed(sel) &&\n\t\t\tisRootChild &&\n\t\t\tanchorBlock !== undefined &&\n\t\t\tgetBlockLength(anchorBlock) > 0;\n\t\tif (splitsAtCaret) {\n\t\t\tthis.dispatch(pasteSlice(state, richBlocksToSlice(blocks, this.schemaRegistry)));\n\t\t\treturn true;\n\t\t}\n\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tconst delBuilder = state.transaction('paste');\n\t\t\tconst landingId: BlockId | undefined = addDeleteSelectionSteps(state, delBuilder);\n\t\t\tconst delTr = delBuilder.build();\n\t\t\tthis.dispatch(delTr);\n\t\t\tstate = state.apply(delTr);\n\t\t\tif (landingId) {\n\t\t\t\treturn this.resolveAndInsertRichBlocks(blocks, state, landingId);\n\t\t\t}\n\t\t}\n\n\t\treturn this.resolveAndInsertRichBlocks(blocks, state, anchorBlockId);\n\t}\n\n\t/**\n\t * Validates and builds the clipboard blocks, resolves the insertion context\n\t * (cell or root), and inserts them. When the caret is in a table cell whose\n\t * content rule forbids one of the blocks (e.g. a copied code_block, which is a\n\t * leaf block not in the cell's allow-list), the whole run escapes to the\n\t * document root after the outer table instead of nesting schema-invalidly. This\n\t * mirrors the HTML paste guard (#166) for the internal-copy path.\n\t *\n\t * Blocks built here are always leaf blocks (rich clipboard data carries inline\n\t * children only), so the trailing collapsed caret in {@link insertNodes} is\n\t * always valid.\n\t */\n\tprivate resolveAndInsertRichBlocks(\n\t\tblocks: readonly RichBlockData[],\n\t\tstate: EditorState,\n\t\tanchorBlockId: BlockId,\n\t): boolean {\n\t\tconst nodes: BlockNode[] = [];\n\t\tfor (const raw of blocks) {\n\t\t\tconst blockData: RichBlockData | undefined = validateRichBlockData(raw, this.schemaRegistry);\n\t\t\tif (blockData) nodes.push(createBlockFromRichData(blockData));\n\t\t}\n\t\tif (nodes.length === 0) return false;\n\n\t\tconst sel = state.selection;\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\n\t\tif (cellId) {\n\t\t\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\t\t\tif (this.cellRejects(cell, nodes)) {\n\t\t\t\tconst escapeCtx: InsertionContext | undefined = resolveRootEscapeContext(\n\t\t\t\t\tstate,\n\t\t\t\t\tanchorBlockId,\n\t\t\t\t);\n\t\t\t\tif (!escapeCtx) return false;\n\t\t\t\treturn this.insertNodes(nodes, state, escapeCtx, escapeCtx.anchorIndex + 1, false);\n\t\t\t}\n\n\t\t\tconst ctx = resolveCellInsertionContext(state, anchorBlockId, cellId, this.schemaRegistry);\n\t\t\tif (!ctx) return false;\n\t\t\tconst startIndex: number =\n\t\t\t\tctx.anchorIndex >= 0 ? ctx.anchorIndex + 1 : (cell?.children.length ?? 0);\n\t\t\treturn this.insertNodes(nodes, state, ctx, startIndex, true);\n\t\t}\n\n\t\tconst ctx = resolveRootInsertionContext(state, anchorBlockId, this.schemaRegistry);\n\t\tif (!ctx) return false;\n\t\tconst insertOffset: number = isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\treturn this.insertNodes(nodes, state, ctx, ctx.anchorIndex + insertOffset, !isGapCursor(sel));\n\t}\n\n\t/** Inserts pre-built block nodes at the resolved position. */\n\tprivate insertNodes(\n\t\tnodes: readonly BlockNode[],\n\t\tstate: EditorState,\n\t\tcontext: InsertionContext,\n\t\tstartIndex: number,\n\t\tremoveEmptyAnchor: boolean,\n\t): boolean {\n\t\tconst builder = state.transaction('paste');\n\t\tlet insertIndex: number = startIndex;\n\t\tlet lastBlock: BlockNode | undefined;\n\n\t\tfor (const node of nodes) {\n\t\t\tbuilder.insertNode(context.parentPath, insertIndex, node);\n\t\t\tinsertIndex++;\n\t\t\tlastBlock = node;\n\t\t}\n\n\t\tif (removeEmptyAnchor && context.isAnchorEmpty && context.anchorIndex >= 0) {\n\t\t\tbuilder.removeNode(context.parentPath, context.anchorIndex);\n\t\t}\n\n\t\tif (lastBlock) {\n\t\t\tbuilder.setSelection(createCollapsedSelection(lastBlock.id, getBlockLength(lastBlock)));\n\t\t}\n\n\t\tthis.dispatch(builder.build());\n\t\treturn true;\n\t}\n}\n","/**\n * Paste handler: intercepts paste events and routes clipboard content\n * to specialized handlers for HTML, rich blocks, files, and plain text.\n */\n\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { MarkdownSyntaxExtension } from '../model/MarkdownSyntaxRegistry.js';\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { isEventFromEditorContent } from '../platform/EditorEventBoundary.js';\nimport { serializeDocumentToHTML } from '../serialization/DocumentSerializer.js';\nimport type { DispatchFn, GetStateFn } from './InputHandler.js';\nimport { looksLikeMarkdown } from './MarkdownPasteDetector.js';\nimport { PasteHTMLHandler } from './PasteHTMLHandler.js';\nimport { PasteRichBlockHandler } from './PasteRichBlockHandler.js';\n\n/** Markdown auto-detection mode for the paste pipeline. */\nexport type PasteMarkdownMode = 'auto' | 'never';\n\n/** Whether the clipboard `text/html` payload carries real markup worth preferring. */\nfunction hasUsableHtml(html: string): boolean {\n\treturn html.trim() !== '' && /<\\w/.test(html);\n}\n\nexport interface PasteHandlerOptions {\n\treadonly getState: GetStateFn;\n\treadonly dispatch: DispatchFn;\n\treadonly schemaRegistry?: SchemaRegistry;\n\treadonly fileHandlerRegistry?: FileHandlerRegistry;\n\treadonly isReadOnly?: () => boolean;\n\treadonly getPasteInterceptors?: () => readonly PasteInterceptorEntry[];\n\t/** Markdown paste auto-detection. Default `auto`. */\n\treadonly pasteMarkdown?: PasteMarkdownMode;\n\t/** Supplies plugin-contributed Markdown syntax extensions (formula `$...$`). */\n\treadonly getMarkdownSyntaxExtensions?: () => readonly MarkdownSyntaxExtension[];\n\t/** Writes a message to the screen-reader live region after a successful Markdown paste. */\n\treadonly announce?: (text: string) => void;\n\t/** Localized announcement made after Markdown is imported. Defaults to `'Markdown imported'`. */\n\treadonly markdownImportedMessage?: string;\n}\n\nexport class PasteHandler {\n\tprivate readonly getState: GetStateFn;\n\tprivate readonly dispatch: DispatchFn;\n\tprivate readonly schemaRegistry?: SchemaRegistry;\n\tprivate readonly fileHandlerRegistry?: FileHandlerRegistry;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly getPasteInterceptors: () => readonly PasteInterceptorEntry[];\n\tprivate readonly pasteMarkdown: PasteMarkdownMode;\n\tprivate readonly getMarkdownSyntaxExtensions: () => readonly MarkdownSyntaxExtension[];\n\tprivate readonly announce?: (text: string) => void;\n\tprivate readonly markdownImportedMessage: string;\n\tprivate readonly handlePaste: (e: ClipboardEvent) => void;\n\tprivate readonly htmlHandler: PasteHTMLHandler;\n\tprivate readonly richBlockHandler: PasteRichBlockHandler;\n\n\tconstructor(\n\t\tprivate readonly element: HTMLElement,\n\t\toptions: PasteHandlerOptions,\n\t) {\n\t\tthis.getState = options.getState;\n\t\tthis.dispatch = options.dispatch;\n\t\tthis.schemaRegistry = options.schemaRegistry;\n\t\tthis.fileHandlerRegistry = options.fileHandlerRegistry;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\t\tthis.getPasteInterceptors = options.getPasteInterceptors ?? (() => []);\n\t\tthis.pasteMarkdown = options.pasteMarkdown ?? 'auto';\n\t\tthis.getMarkdownSyntaxExtensions = options.getMarkdownSyntaxExtensions ?? (() => []);\n\t\tthis.announce = options.announce;\n\t\tthis.markdownImportedMessage = options.markdownImportedMessage ?? 'Markdown imported';\n\n\t\tthis.richBlockHandler = new PasteRichBlockHandler(\n\t\t\toptions.getState,\n\t\t\toptions.dispatch,\n\t\t\toptions.schemaRegistry,\n\t\t);\n\t\tthis.htmlHandler = new PasteHTMLHandler(\n\t\t\toptions.getState,\n\t\t\toptions.dispatch,\n\t\t\toptions.schemaRegistry,\n\t\t\t(json: string) => this.richBlockHandler.handleRichPasteFromJson(json),\n\t\t);\n\n\t\tthis.handlePaste = this.onPaste.bind(this);\n\t\telement.addEventListener('paste', this.handlePaste);\n\t}\n\n\tprivate onPaste(e: ClipboardEvent): void {\n\t\tif (!isEventFromEditorContent(e, this.element)) return;\n\t\te.preventDefault();\n\t\tif (this.isReadOnly()) return;\n\n\t\tconst clipboardData = e.clipboardData;\n\t\tif (!clipboardData) return;\n\n\t\tconst blockJson: string = clipboardData.getData('application/x-notectl-block');\n\t\tif (blockJson) {\n\t\t\tthis.richBlockHandler.handleBlockPaste(blockJson);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.handleFilePaste(clipboardData)) return;\n\n\t\tconst plainText: string = clipboardData.getData('text/plain');\n\n\t\t// Paste interceptors (plugins can claim the paste before default handling)\n\t\tconst html: string = clipboardData.getData('text/html');\n\t\tif (plainText && this.tryPasteInterceptors(plainText, html)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Markdown branch (D11): after the synchronous interceptor loop, only when\n\t\t// there is no usable HTML (the pipeline prefers HTML), ahead of the\n\t\t// plain-text fallback, and only on a positive cheap synchronous detection.\n\t\t// Clipboard strings are captured before any `await`; `preventDefault()` has\n\t\t// already run, so the async tail (import, parse, dispatch) is safe.\n\t\tif (\n\t\t\tplainText &&\n\t\t\tthis.pasteMarkdown !== 'never' &&\n\t\t\t!hasUsableHtml(html) &&\n\t\t\tlooksLikeMarkdown(plainText)\n\t\t) {\n\t\t\tvoid this.handleMarkdownPaste(plainText);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.htmlHandler.handleHTMLOrTextPaste(clipboardData, plainText);\n\t}\n\n\t/**\n\t * Dynamically imports the Markdown engine, parses the captured text, and\n\t * routes the result through the HTML paste pipeline (which owns block\n\t * splicing at the caret). Only the heavy parser is lazy-loaded; the detector\n\t * stayed synchronous in the base bundle.\n\t *\n\t * `preventDefault()` has already run, so the clipboard would be lost if the\n\t * lazy import, parse, or serialize fails (offline split chunk, strict CSP, a\n\t * parser throw). The whole conversion is guarded; on any failure it degrades\n\t * to a plain-text insertion of the already-captured text. Only the conversion\n\t * is inside the `try` — a throw from the committed paste must not double-fire\n\t * the fallback.\n\t */\n\tprivate async handleMarkdownPaste(text: string): Promise<void> {\n\t\tlet html: string;\n\t\ttry {\n\t\t\tconst { parseMarkdownToDocument } = await import('../serialization/MarkdownParser.js');\n\t\t\tconst doc = parseMarkdownToDocument(text, this.schemaRegistry, {\n\t\t\t\tsyntaxExtensions: this.getMarkdownSyntaxExtensions(),\n\t\t\t});\n\t\t\t// Fresh ids for pasted content (no identity to preserve from the clipboard).\n\t\t\thtml = serializeDocumentToHTML(doc, this.schemaRegistry, { includeBlockIds: false });\n\t\t} catch {\n\t\t\tthis.htmlHandler.pastePlainText(text);\n\t\t\treturn;\n\t\t}\n\t\t// `pasteHTMLString` dispatches synchronously, so the state-change handler has\n\t\t// already cleared the live region by the time we announce; announcing here\n\t\t// (not before the paste) ensures the import message is the surviving text.\n\t\tthis.htmlHandler.pasteHTMLString(html);\n\t\tthis.announce?.(this.markdownImportedMessage);\n\t}\n\n\t/** Runs paste interceptors in priority order. Returns true if one claimed the paste. */\n\tprivate tryPasteInterceptors(plainText: string, html: string): boolean {\n\t\tconst interceptors = this.getPasteInterceptors();\n\t\tconst state = this.getState();\n\t\tfor (const entry of interceptors) {\n\t\t\tconst tr = entry.interceptor(plainText, html, state);\n\t\t\tif (tr) {\n\t\t\t\tthis.dispatch(tr);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Checks for files in clipboard data and delegates to registered handlers. */\n\tprivate handleFilePaste(clipboardData: DataTransfer): boolean {\n\t\tif (!this.fileHandlerRegistry) return false;\n\n\t\tconst files: File[] = Array.from(clipboardData.files);\n\t\tif (files.length === 0) {\n\t\t\tfor (let i = 0; i < clipboardData.items.length; i++) {\n\t\t\t\tconst item = clipboardData.items[i];\n\t\t\t\tif (item && item.kind === 'file') {\n\t\t\t\t\tconst file = item.getAsFile();\n\t\t\t\t\tif (file) files.push(file);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (files.length === 0) return false;\n\n\t\tlet handled = false;\n\t\tfor (const file of files) {\n\t\t\tconst handlers = this.fileHandlerRegistry.matchFileHandlers(file.type);\n\t\t\tfor (const handler of handlers) {\n\t\t\t\tconst result = handler(file, null);\n\t\t\t\tif (result === true || result instanceof Promise) {\n\t\t\t\t\thandled = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn handled;\n\t}\n\n\tdestroy(): void {\n\t\tthis.element.removeEventListener('paste', this.handlePaste);\n\t}\n}\n","/**\n * InputManager: single facade that encapsulates all input handler lifecycle.\n *\n * EditorView previously created and destroyed InputHandler, KeyboardHandler,\n * PasteHandler, ClipboardHandler, and CompositionTracker directly. This facade\n * consolidates that responsibility so the view layer doesn't need to import\n * concrete input classes, maintaining proper layer separation.\n */\n\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport type { MarkdownSyntaxExtension } from '../model/MarkdownSyntaxRegistry.js';\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { ClipboardHandler } from './ClipboardHandler.js';\nimport { CompositionTracker } from './CompositionTracker.js';\nimport { InputHandler } from './InputHandler.js';\nimport { KeyboardHandler } from './KeyboardHandler.js';\nimport { PasteHandler, type PasteMarkdownMode } from './PasteHandler.js';\n\ntype TextDirectionFn = (element: HTMLElement) => 'ltr' | 'rtl';\ntype GapCursorNavigateFn = (\n\tstate: EditorState,\n\tdirection: 'left' | 'right' | 'up' | 'down',\n\tcontainer?: HTMLElement,\n) => Transaction | null;\n\nexport interface InputManagerDeps {\n\treadonly getState: () => EditorState;\n\treadonly dispatch: (tr: Transaction) => void;\n\treadonly syncSelection: () => void;\n\treadonly undo: () => void;\n\treadonly redo: () => void;\n\treadonly schemaRegistry?: SchemaRegistry;\n\treadonly keymapRegistry?: KeymapRegistry;\n\treadonly inputRuleRegistry?: InputRuleRegistry;\n\treadonly fileHandlerRegistry?: FileHandlerRegistry;\n\treadonly isReadOnly: () => boolean;\n\t/** Live gate for Markdown shorthand input rules. Defaults to always enabled. */\n\treadonly shouldApplyInputRules?: () => boolean;\n\treadonly getPasteInterceptors?: () => readonly PasteInterceptorEntry[];\n\treadonly pasteMarkdown?: PasteMarkdownMode;\n\treadonly getMarkdownSyntaxExtensions?: () => readonly MarkdownSyntaxExtension[];\n\treadonly getTextInputInterceptors?: () => readonly TextInputInterceptorEntry[];\n\treadonly getTextDirection?: TextDirectionFn;\n\treadonly navigateFromGapCursor?: GapCursorNavigateFn;\n\t/** Writes a message to the screen-reader live region (for the Markdown paste path). */\n\treadonly announce?: (text: string) => void;\n\t/** Localized \"Markdown imported\" announcement for the auto-detect paste path. */\n\treadonly markdownImportedMessage?: string;\n}\n\nexport class InputManager {\n\treadonly compositionTracker: CompositionTracker;\n\tprivate readonly inputHandler: InputHandler;\n\tprivate readonly keyboardHandler: KeyboardHandler;\n\tprivate readonly pasteHandler: PasteHandler;\n\tprivate readonly clipboardHandler: ClipboardHandler;\n\n\tconstructor(contentElement: HTMLElement, deps: InputManagerDeps) {\n\t\tthis.compositionTracker = new CompositionTracker();\n\n\t\tthis.inputHandler = new InputHandler(contentElement, {\n\t\t\tgetState: deps.getState,\n\t\t\tdispatch: deps.dispatch,\n\t\t\tsyncSelection: deps.syncSelection,\n\t\t\tinputRuleRegistry: deps.inputRuleRegistry,\n\t\t\tisReadOnly: deps.isReadOnly,\n\t\t\tshouldApplyInputRules: deps.shouldApplyInputRules,\n\t\t\tcompositionTracker: this.compositionTracker,\n\t\t\tgetTextInputInterceptors: deps.getTextInputInterceptors,\n\t\t});\n\n\t\tthis.keyboardHandler = new KeyboardHandler(contentElement, {\n\t\t\tgetState: deps.getState,\n\t\t\tdispatch: deps.dispatch,\n\t\t\tundo: deps.undo,\n\t\t\tredo: deps.redo,\n\t\t\tkeymapRegistry: deps.keymapRegistry,\n\t\t\tisReadOnly: deps.isReadOnly,\n\t\t\tcompositionTracker: this.compositionTracker,\n\t\t\tgetTextDirection: deps.getTextDirection,\n\t\t\tnavigateFromGapCursor: deps.navigateFromGapCursor,\n\t\t});\n\n\t\tthis.pasteHandler = new PasteHandler(contentElement, {\n\t\t\tgetState: deps.getState,\n\t\t\tdispatch: deps.dispatch,\n\t\t\tschemaRegistry: deps.schemaRegistry,\n\t\t\tfileHandlerRegistry: deps.fileHandlerRegistry,\n\t\t\tisReadOnly: deps.isReadOnly,\n\t\t\tgetPasteInterceptors: deps.getPasteInterceptors,\n\t\t\tpasteMarkdown: deps.pasteMarkdown,\n\t\t\tgetMarkdownSyntaxExtensions: deps.getMarkdownSyntaxExtensions,\n\t\t\tannounce: deps.announce,\n\t\t\tmarkdownImportedMessage: deps.markdownImportedMessage,\n\t\t});\n\n\t\tthis.clipboardHandler = new ClipboardHandler(contentElement, {\n\t\t\tgetState: deps.getState,\n\t\t\tdispatch: deps.dispatch,\n\t\t\tschemaRegistry: deps.schemaRegistry,\n\t\t\tsyncSelection: deps.syncSelection,\n\t\t\tisReadOnly: deps.isReadOnly,\n\t\t});\n\t}\n\n\t/** Destroys all input handlers and releases event listeners. */\n\tdestroy(): void {\n\t\tthis.inputHandler.destroy();\n\t\tthis.keyboardHandler.destroy();\n\t\tthis.pasteHandler.destroy();\n\t\tthis.clipboardHandler.destroy();\n\t}\n}\n","/**\n * FileHandlerRegistry: manages plugin-registered file handlers\n * for paste and drop operations, with MIME pattern matching.\n */\n\nimport type { Position } from './Selection.js';\n\n/** Handler for a single file pasted or dropped into the editor. */\nexport type FileHandler = (file: File, position: Position | null) => boolean | Promise<boolean>;\n\nexport interface FileHandlerEntry {\n\treadonly pattern: string;\n\treadonly handler: FileHandler;\n}\n\nexport class FileHandlerRegistry {\n\tprivate readonly _fileHandlers: FileHandlerEntry[] = [];\n\n\tregisterFileHandler(pattern: string, handler: FileHandler): void {\n\t\tthis._fileHandlers.push({ pattern, handler });\n\t}\n\n\tgetFileHandlers(): readonly FileHandlerEntry[] {\n\t\treturn this._fileHandlers;\n\t}\n\n\tmatchFileHandlers(mimeType: string): FileHandler[] {\n\t\tconst handlers: FileHandler[] = [];\n\t\tfor (const entry of this._fileHandlers) {\n\t\t\tif (matchMimePattern(entry.pattern, mimeType)) {\n\t\t\t\thandlers.push(entry.handler);\n\t\t\t}\n\t\t}\n\t\treturn handlers;\n\t}\n\n\tremoveFileHandler(handler: FileHandler): void {\n\t\tconst idx = this._fileHandlers.findIndex((e) => e.handler === handler);\n\t\tif (idx !== -1) this._fileHandlers.splice(idx, 1);\n\t}\n\n\tclear(): void {\n\t\tthis._fileHandlers.length = 0;\n\t}\n}\n\n/** Matches a MIME pattern (e.g. 'image/*') against a concrete MIME type. */\nfunction matchMimePattern(pattern: string, mimeType: string): boolean {\n\tif (pattern === '*' || pattern === '*/*') return true;\n\tif (pattern === mimeType) return true;\n\tif (pattern.endsWith('/*')) {\n\t\tconst prefix = pattern.slice(0, -1);\n\t\treturn mimeType.startsWith(prefix);\n\t}\n\treturn false;\n}\n","/**\n * InputRuleRegistry: manages plugin-registered input rules\n * (pattern-based text transformations triggered on text input).\n */\n\nimport type { InputRule } from './InputRule.js';\n\nexport class InputRuleRegistry {\n\tprivate readonly _inputRules: InputRule[] = [];\n\n\tregisterInputRule(rule: InputRule): void {\n\t\tthis._inputRules.push(rule);\n\t}\n\n\tgetInputRules(): readonly InputRule[] {\n\t\treturn this._inputRules;\n\t}\n\n\tremoveInputRule(rule: InputRule): void {\n\t\tconst idx = this._inputRules.indexOf(rule);\n\t\tif (idx !== -1) this._inputRules.splice(idx, 1);\n\t}\n\n\tclear(): void {\n\t\tthis._inputRules.length = 0;\n\t}\n}\n","/**\n * KeymapRegistry: manages plugin-registered keyboard shortcuts\n * with a 3-priority system (context > navigation > default).\n */\n\nimport type { Keymap, KeymapOptions, KeymapPriority } from './Keymap.js';\n\nexport class KeymapRegistry {\n\tprivate readonly _contextKeymaps: Keymap[] = [];\n\tprivate readonly _navigationKeymaps: Keymap[] = [];\n\tprivate readonly _defaultKeymaps: Keymap[] = [];\n\n\tregisterKeymap(keymap: Keymap, options?: KeymapOptions): void {\n\t\tconst priority: KeymapPriority = options?.priority ?? 'default';\n\t\tconst samePriorityKeymaps: Keymap[] = this.keymapArrayForPriority(priority);\n\t\tsamePriorityKeymaps.push(keymap);\n\t}\n\n\t/** Returns all keymaps in priority order: context > navigation > default. */\n\tgetKeymaps(): readonly Keymap[] {\n\t\treturn [...this._contextKeymaps, ...this._navigationKeymaps, ...this._defaultKeymaps];\n\t}\n\n\t/** Returns keymaps grouped by priority level (defensive copies). */\n\tgetKeymapsByPriority(): {\n\t\treadonly context: readonly Keymap[];\n\t\treadonly navigation: readonly Keymap[];\n\t\treadonly default: readonly Keymap[];\n\t} {\n\t\treturn {\n\t\t\tcontext: [...this._contextKeymaps],\n\t\t\tnavigation: [...this._navigationKeymaps],\n\t\t\tdefault: [...this._defaultKeymaps],\n\t\t};\n\t}\n\n\tremoveKeymap(keymap: Keymap): void {\n\t\tfor (const arr of [this._contextKeymaps, this._navigationKeymaps, this._defaultKeymaps]) {\n\t\t\tconst idx: number = arr.indexOf(keymap);\n\t\t\tif (idx !== -1) {\n\t\t\t\tarr.splice(idx, 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis._contextKeymaps.length = 0;\n\t\tthis._navigationKeymaps.length = 0;\n\t\tthis._defaultKeymaps.length = 0;\n\t}\n\n\tprivate keymapArrayForPriority(priority: KeymapPriority): Keymap[] {\n\t\tswitch (priority) {\n\t\t\tcase 'context':\n\t\t\t\treturn this._contextKeymaps;\n\t\t\tcase 'navigation':\n\t\t\t\treturn this._navigationKeymaps;\n\t\t\tcase 'default':\n\t\t\t\treturn this._defaultKeymaps;\n\t\t}\n\t}\n}\n","/**\n * MarkdownSyntaxRegistry: holds plugin-contributed Markdown grammar extensions\n * (e.g. the formula plugin's `$...$`).\n *\n * Lives in `model/` next to {@link InputRuleRegistry} so plugins can contribute\n * Markdown syntax through `PluginContext` WITHOUT importing the `serialization/`\n * layer (which would invert the dependency matrix). The Markdown engine reads\n * these extensions via the `syntaxExtensions` parse option, threaded in by the\n * editor, so the engine never imports `plugins/` and `SchemaRegistry` stays\n * purely structural (D4).\n */\n\n/**\n * A plugin-contributed Markdown grammar extension (D4). Registered via\n * `PluginContext.registerMarkdownSyntax(...)` and threaded into the parser\n * through `MarkdownParseOptions.syntaxExtensions`, so the core grammar never\n * hard-codes plugin syntax such as `$...$` (formula).\n *\n * Defined in `model/` (not `serialization/`) so this layer stays the dependency\n * floor: `serialization/` re-exports the type, plugins import it from here.\n */\nexport interface MarkdownSyntaxExtension {\n\t/** Stable id for the contributing plugin (e.g. `formula`). */\n\treadonly id: string;\n\t/**\n\t * Matches a plugin inline construct starting at `index` in `text`. Returns the\n\t * produced inline-node descriptor plus the number of characters consumed, or\n\t * `null` if no match. Must be linear-time (no backtracking regex).\n\t */\n\treadonly matchInline?: (\n\t\ttext: string,\n\t\tindex: number,\n\t) => {\n\t\treadonly type: string;\n\t\treadonly attrs: Record<string, string | number | boolean>;\n\t\treadonly length: number;\n\t} | null;\n\t/**\n\t * Matches a plugin block construct given the full source lines and the current\n\t * line index. Returns the produced block descriptor plus the number of lines\n\t * consumed, or `null` if no match.\n\t */\n\treadonly matchBlock?: (\n\t\tlines: readonly string[],\n\t\tlineIndex: number,\n\t) => {\n\t\treadonly type: string;\n\t\treadonly attrs: Record<string, string | number | boolean>;\n\t\treadonly linesConsumed: number;\n\t} | null;\n}\n\nexport class MarkdownSyntaxRegistry {\n\tprivate readonly _extensions: MarkdownSyntaxExtension[] = [];\n\n\tregister(extension: MarkdownSyntaxExtension): void {\n\t\tthis._extensions.push(extension);\n\t}\n\n\tgetExtensions(): readonly MarkdownSyntaxExtension[] {\n\t\treturn this._extensions;\n\t}\n\n\tclear(): void {\n\t\tthis._extensions.length = 0;\n\t}\n}\n","/**\n * NodeViewRegistry: manages plugin-registered custom NodeView factories\n * for block-level rendering.\n */\n\nimport type { NodeViewFactory } from './NodeView.js';\n\nexport class NodeViewRegistry {\n\tprivate readonly _nodeViews = new Map<string, NodeViewFactory>();\n\n\tregisterNodeView(type: string, factory: NodeViewFactory): void {\n\t\tif (this._nodeViews.has(type)) {\n\t\t\tthrow new Error(`NodeView for type \"${type}\" is already registered.`);\n\t\t}\n\t\tthis._nodeViews.set(type, factory);\n\t}\n\n\tgetNodeViewFactory(type: string): NodeViewFactory | undefined {\n\t\treturn this._nodeViews.get(type);\n\t}\n\n\tremoveNodeView(type: string): void {\n\t\tthis._nodeViews.delete(type);\n\t}\n\n\tclear(): void {\n\t\tthis._nodeViews.clear();\n\t}\n}\n","/**\n * Structured logger interface for the plugin infrastructure.\n *\n * Rationale:\n * - Direct `console.*` calls in shipped code leak debug artifacts to users\n * and give embedding applications no way to silence, redirect, or\n * instrument editor errors.\n * - A small Logger interface decouples the plugin runtime from its output\n * sink, enabling dependency injection (Clean Code / DIP) and trivial\n * replacement by test doubles.\n *\n * The API deliberately mirrors the widely understood `console` / Pino shape\n * (`error`, `warn`, `info`, `debug`) so that embedders can supply a custom\n * logger with a one-line adapter.\n */\n\n/**\n * Minimal structured logger.\n *\n * All methods accept an optional `cause` — typically the `Error` being\n * reported, but it may carry arbitrary diagnostic context.\n */\nexport interface Logger {\n\terror(message: string, cause?: unknown): void;\n\twarn(message: string, cause?: unknown): void;\n\tinfo(message: string, cause?: unknown): void;\n\tdebug(message: string, cause?: unknown): void;\n}\n\n/**\n * Default logger that forwards to the global `console`.\n *\n * This preserves the prior behavior (errors land in the browser console)\n * while still routing through the Logger interface so embedders can opt\n * into their own sink.\n */\nexport const consoleLogger: Logger = {\n\terror(message: string, cause?: unknown): void {\n\t\tif (cause === undefined) {\n\t\t\tconsole.error(message);\n\t\t} else {\n\t\t\tconsole.error(message, cause);\n\t\t}\n\t},\n\twarn(message: string, cause?: unknown): void {\n\t\tif (cause === undefined) {\n\t\t\tconsole.warn(message);\n\t\t} else {\n\t\t\tconsole.warn(message, cause);\n\t\t}\n\t},\n\tinfo(message: string, cause?: unknown): void {\n\t\tif (cause === undefined) {\n\t\t\tconsole.info(message);\n\t\t} else {\n\t\t\tconsole.info(message, cause);\n\t\t}\n\t},\n\tdebug(message: string, cause?: unknown): void {\n\t\tif (cause === undefined) {\n\t\t\tconsole.debug(message);\n\t\t} else {\n\t\t\tconsole.debug(message, cause);\n\t\t}\n\t},\n};\n\n/**\n * No-op logger that swallows every message.\n *\n * Useful for tests that want full silence, and for embedders that supply\n * their own error-reporting pipeline and do not want the editor to log at all.\n */\nexport const silentLogger: Logger = {\n\terror(): void {},\n\twarn(): void {},\n\tinfo(): void {},\n\tdebug(): void {},\n};\n\n/**\n * Wraps a logger so every message is prefixed with `[scope]`.\n *\n * Scoping keeps call sites concise while still producing messages that\n * clearly identify their origin in aggregated log output.\n */\nexport function scopedLogger(base: Logger, scope: string): Logger {\n\tconst prefix = `[${scope}]`;\n\treturn {\n\t\terror(message: string, cause?: unknown): void {\n\t\t\tbase.error(`${prefix} ${message}`, cause);\n\t\t},\n\t\twarn(message: string, cause?: unknown): void {\n\t\t\tbase.warn(`${prefix} ${message}`, cause);\n\t\t},\n\t\tinfo(message: string, cause?: unknown): void {\n\t\t\tbase.info(`${prefix} ${message}`, cause);\n\t\t},\n\t\tdebug(message: string, cause?: unknown): void {\n\t\t\tbase.debug(`${prefix} ${message}`, cause);\n\t\t},\n\t};\n}\n","/**\n * Manages command registration, execution, and read-only bypass.\n * Extracted from PluginManager for single-responsibility.\n */\n\nimport { type Logger, consoleLogger, scopedLogger } from './Logger.js';\nimport type { CommandEntry, CommandHandler, CommandOptions } from './Plugin.js';\n\nexport class CommandRegistry {\n\tprivate readonly commands = new Map<string, CommandEntry>();\n\tprivate readonlyBypassActive = false;\n\tprivate readonly log: Logger;\n\n\tconstructor(logger: Logger = consoleLogger) {\n\t\tthis.log = scopedLogger(logger, 'CommandRegistry');\n\t}\n\n\t/** Registers a named command. Throws if already registered. */\n\tregister(\n\t\tname: string,\n\t\thandler: CommandHandler,\n\t\tpluginId: string,\n\t\toptions?: CommandOptions,\n\t): void {\n\t\tif (this.commands.has(name)) {\n\t\t\tconst existing = this.commands.get(name);\n\t\t\tthrow new Error(`Command \"${name}\" is already registered by plugin \"${existing?.pluginId}\".`);\n\t\t}\n\t\tconst readonlyAllowed: boolean = options?.readonlyAllowed ?? false;\n\t\tthis.commands.set(name, { name, handler, pluginId, readonlyAllowed });\n\t}\n\n\t/** Returns whether a command can be executed given current read-only state. */\n\tcanExecute(name: string, isReadOnly: boolean): boolean {\n\t\tconst entry = this.commands.get(name);\n\t\tif (!entry) return false;\n\t\tif (isReadOnly && !entry.readonlyAllowed) return false;\n\t\treturn true;\n\t}\n\n\t/** Executes a named command. Returns false if not found or blocked by read-only. */\n\texecute(name: string, isReadOnly: boolean): boolean {\n\t\tconst entry = this.commands.get(name);\n\t\tif (!entry) return false;\n\t\tif (isReadOnly && !entry.readonlyAllowed) return false;\n\n\t\tconst enableBypass: boolean = isReadOnly && entry.readonlyAllowed;\n\t\tif (enableBypass) this.readonlyBypassActive = true;\n\t\ttry {\n\t\t\treturn entry.handler();\n\t\t} catch (err) {\n\t\t\tthis.log.error(`Command \"${name}\" error`, err);\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tif (enableBypass) this.readonlyBypassActive = false;\n\t\t}\n\t}\n\n\t/** Returns true when a readonlyAllowed command is currently executing. */\n\tisReadonlyBypassed(): boolean {\n\t\treturn this.readonlyBypassActive;\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.commands.has(name);\n\t}\n\n\tremove(name: string): void {\n\t\tthis.commands.delete(name);\n\t}\n\n\tclear(): void {\n\t\tthis.commands.clear();\n\t\tthis.readonlyBypassActive = false;\n\t}\n\n\t/** Exposes internal map for ContextFactoryDeps assembly. */\n\tget rawMap(): Map<string, CommandEntry> {\n\t\treturn this.commands;\n\t}\n}\n","/**\n * Standalone event bus with error isolation and type-safe event keys.\n * Each listener is wrapped in try/catch — a failing listener never affects others.\n */\n\nimport { type Logger, consoleLogger, scopedLogger } from './Logger.js';\nimport type { EventKey, PluginEventCallback } from './Plugin.js';\n\nexport class EventBus {\n\tprivate readonly listeners = new Map<string, Set<PluginEventCallback>>();\n\tprivate readonly log: Logger;\n\n\tconstructor(logger: Logger = consoleLogger) {\n\t\tthis.log = scopedLogger(logger, 'EventBus');\n\t}\n\n\t/** Emits an event to all registered listeners. Errors are caught per listener. */\n\temit<T>(key: EventKey<T>, payload: T): void {\n\t\tconst set = this.listeners.get(key.id);\n\t\tif (!set) return;\n\n\t\tfor (const listener of set) {\n\t\t\ttry {\n\t\t\t\tlistener(payload);\n\t\t\t} catch (err) {\n\t\t\t\tthis.log.error(`Listener error on \"${key.id}\"`, err);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Subscribes to an event. Returns an unsubscribe function. */\n\ton<T>(key: EventKey<T>, callback: PluginEventCallback<T>): () => void {\n\t\tconst id = key.id;\n\t\tif (!this.listeners.has(id)) {\n\t\t\tthis.listeners.set(id, new Set());\n\t\t}\n\t\tconst set = this.listeners.get(id) ?? new Set();\n\t\tset.add(callback as PluginEventCallback);\n\n\t\treturn () => {\n\t\t\tset.delete(callback as PluginEventCallback);\n\t\t\tif (set.size === 0) this.listeners.delete(id);\n\t\t};\n\t}\n\n\t/** Removes a specific listener from an event. */\n\toff<T>(key: EventKey<T>, callback: PluginEventCallback<T>): void {\n\t\tconst set = this.listeners.get(key.id);\n\t\tif (!set) return;\n\t\tset.delete(callback as PluginEventCallback);\n\t\tif (set.size === 0) this.listeners.delete(key.id);\n\t}\n\n\t/** Removes all listeners for all events. */\n\tclear(): void {\n\t\tthis.listeners.clear();\n\t}\n}\n","/**\n * Manages middleware and paste interceptor chains with priority-based\n * sorting, cached ordering, and error-isolated dispatch.\n * Extracted from PluginManager for single-responsibility.\n */\n\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { type Logger, consoleLogger, scopedLogger } from './Logger.js';\nimport type { MiddlewareNext } from './Plugin.js';\nimport type { MiddlewareEntry } from './PluginContextFactory.js';\n\n/** Describes a registered middleware for introspection. */\nexport interface MiddlewareInfo {\n\treadonly name: string;\n\treadonly priority: number;\n\treadonly pluginId: string;\n}\n\nexport class MiddlewareChain {\n\tprivate readonly middlewares: MiddlewareEntry[] = [];\n\tprivate middlewareSorted: MiddlewareEntry[] | null = null;\n\tprivate readonly pasteInterceptors: PasteInterceptorEntry[] = [];\n\tprivate pasteInterceptorsSorted: PasteInterceptorEntry[] | null = null;\n\tprivate readonly textInputInterceptors: TextInputInterceptorEntry[] = [];\n\tprivate textInputInterceptorsSorted: TextInputInterceptorEntry[] | null = null;\n\tprivate readonly log: Logger;\n\n\tconstructor(logger: Logger = consoleLogger) {\n\t\tthis.log = scopedLogger(logger, 'MiddlewareChain');\n\t}\n\n\t// --- Middleware ---\n\n\taddMiddleware(entry: MiddlewareEntry): void {\n\t\tthis.middlewares.push(entry);\n\t\tthis.middlewareSorted = null;\n\t}\n\n\tremoveMiddleware(entry: MiddlewareEntry): void {\n\t\tconst idx = this.middlewares.indexOf(entry);\n\t\tif (idx !== -1) this.middlewares.splice(idx, 1);\n\t\tthis.middlewareSorted = null;\n\t}\n\n\t/** Invalidates the sorted middleware cache (used by PluginContextFactory). */\n\tinvalidateMiddlewareSort(): void {\n\t\tthis.middlewareSorted = null;\n\t}\n\n\t/**\n\t * Dispatches a transaction through the middleware chain, then calls finalDispatch.\n\t * If no middleware is registered, calls finalDispatch directly.\n\t */\n\tdispatch(tr: Transaction, state: EditorState, finalDispatch: (tr: Transaction) => void): void {\n\t\tif (this.middlewares.length === 0) {\n\t\t\tfinalDispatch(tr);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sorted = this.getSortedMiddleware();\n\t\tlet index = 0;\n\t\tlet dispatched = false;\n\n\t\tconst next: MiddlewareNext = (currentTr) => {\n\t\t\tif (index < sorted.length) {\n\t\t\t\tconst entry = sorted[index++];\n\t\t\t\tif (!entry) return;\n\t\t\t\tlet called = false;\n\t\t\t\tconst guardedNext: MiddlewareNext = (nextTr) => {\n\t\t\t\t\tif (called) return;\n\t\t\t\t\tcalled = true;\n\t\t\t\t\tnext(nextTr);\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\tentry.middleware(currentTr, state, guardedNext);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.log.error(`Middleware \"${entry.name}\" error`, err);\n\t\t\t\t\tguardedNext(currentTr);\n\t\t\t\t}\n\t\t\t} else if (!dispatched) {\n\t\t\t\tdispatched = true;\n\t\t\t\tfinalDispatch(currentTr);\n\t\t\t}\n\t\t};\n\n\t\tnext(tr);\n\t}\n\n\t/** Returns the middleware chain in execution order, for introspection. */\n\tgetChain(): readonly MiddlewareInfo[] {\n\t\treturn this.getSortedMiddleware().map((entry) => ({\n\t\t\tname: entry.name,\n\t\t\tpriority: entry.priority,\n\t\t\tpluginId: entry.pluginId,\n\t\t}));\n\t}\n\n\t// --- Paste Interceptors ---\n\n\taddPasteInterceptor(entry: PasteInterceptorEntry): void {\n\t\tthis.pasteInterceptors.push(entry);\n\t\tthis.pasteInterceptorsSorted = null;\n\t}\n\n\tremovePasteInterceptor(entry: PasteInterceptorEntry): void {\n\t\tconst idx = this.pasteInterceptors.indexOf(entry);\n\t\tif (idx !== -1) this.pasteInterceptors.splice(idx, 1);\n\t\tthis.pasteInterceptorsSorted = null;\n\t}\n\n\t/** Invalidates the sorted paste interceptor cache (used by PluginContextFactory). */\n\tinvalidatePasteSort(): void {\n\t\tthis.pasteInterceptorsSorted = null;\n\t}\n\n\t/** Returns paste interceptors in priority order. */\n\tgetPasteInterceptors(): readonly PasteInterceptorEntry[] {\n\t\treturn this.getSortedPasteInterceptors();\n\t}\n\n\t// --- Text-Input Interceptors ---\n\n\taddTextInputInterceptor(entry: TextInputInterceptorEntry): void {\n\t\tthis.textInputInterceptors.push(entry);\n\t\tthis.textInputInterceptorsSorted = null;\n\t}\n\n\tremoveTextInputInterceptor(entry: TextInputInterceptorEntry): void {\n\t\tconst idx = this.textInputInterceptors.indexOf(entry);\n\t\tif (idx !== -1) this.textInputInterceptors.splice(idx, 1);\n\t\tthis.textInputInterceptorsSorted = null;\n\t}\n\n\t/** Invalidates the sorted text-input interceptor cache. */\n\tinvalidateTextInputSort(): void {\n\t\tthis.textInputInterceptorsSorted = null;\n\t}\n\n\t/** Returns text-input interceptors in priority order. */\n\tgetTextInputInterceptors(): readonly TextInputInterceptorEntry[] {\n\t\treturn this.getSortedTextInputInterceptors();\n\t}\n\n\t// --- Raw accessors for ContextFactoryDeps ---\n\n\tget rawMiddlewares(): MiddlewareEntry[] {\n\t\treturn this.middlewares;\n\t}\n\n\tget rawPasteInterceptors(): PasteInterceptorEntry[] {\n\t\treturn this.pasteInterceptors;\n\t}\n\n\tget rawTextInputInterceptors(): TextInputInterceptorEntry[] {\n\t\treturn this.textInputInterceptors;\n\t}\n\n\t// --- Lifecycle ---\n\n\tclear(): void {\n\t\tthis.middlewares.length = 0;\n\t\tthis.middlewareSorted = null;\n\t\tthis.pasteInterceptors.length = 0;\n\t\tthis.pasteInterceptorsSorted = null;\n\t\tthis.textInputInterceptors.length = 0;\n\t\tthis.textInputInterceptorsSorted = null;\n\t}\n\n\t// --- Private ---\n\n\tprivate getSortedMiddleware(): MiddlewareEntry[] {\n\t\tif (!this.middlewareSorted) {\n\t\t\tthis.middlewareSorted = [...this.middlewares].sort((a, b) => a.priority - b.priority);\n\t\t}\n\t\treturn this.middlewareSorted;\n\t}\n\n\tprivate getSortedPasteInterceptors(): PasteInterceptorEntry[] {\n\t\tif (!this.pasteInterceptorsSorted) {\n\t\t\tthis.pasteInterceptorsSorted = [...this.pasteInterceptors].sort(\n\t\t\t\t(a, b) => a.priority - b.priority,\n\t\t\t);\n\t\t}\n\t\treturn this.pasteInterceptorsSorted;\n\t}\n\n\tprivate getSortedTextInputInterceptors(): TextInputInterceptorEntry[] {\n\t\tif (!this.textInputInterceptorsSorted) {\n\t\t\tthis.textInputInterceptorsSorted = [...this.textInputInterceptors].sort(\n\t\t\t\t(a, b) => a.priority - b.priority,\n\t\t\t);\n\t\t}\n\t\treturn this.textInputInterceptorsSorted;\n\t}\n}\n","/**\n * Focused factory functions for creating PluginContext instances.\n * Each factory handles one group of related context capabilities,\n * following SRP — extracted from PluginManager.createContext().\n */\n\nimport type { CompositionState } from '../model/CompositionState.js';\nimport type { FileHandler } from '../model/FileHandlerRegistry.js';\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { InputRule } from '../model/InputRule.js';\nimport type { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport type { Keymap, KeymapOptions } from '../model/Keymap.js';\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport type { MarkdownSyntaxRegistry } from '../model/MarkdownSyntaxRegistry.js';\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport type { NodeViewRegistry } from '../view/NodeViewRegistry.js';\nimport type { EventBus } from './EventBus.js';\nimport type { Logger } from './Logger.js';\nimport type {\n\tCommandEntry,\n\tCommandHandler,\n\tCommandOptions,\n\tMiddlewareOptions,\n\tPasteInterceptor,\n\tPasteInterceptorOptions,\n\tPlugin,\n\tPluginConfig,\n\tPluginContext,\n\tPluginEventBus,\n\tServiceKey,\n\tTextInputInterceptor,\n\tTextInputInterceptorOptions,\n\tTransactionMiddleware,\n} from './Plugin.js';\nimport type { BlockTypePickerRegistry } from './heading/BlockTypePickerRegistry.js';\nimport type { ToolbarRegistry } from './toolbar/ToolbarRegistry.js';\n\nconst DEFAULT_PRIORITY = 100;\n\nexport interface MiddlewareEntry {\n\treadonly name: string;\n\treadonly pluginId: string;\n\treadonly middleware: TransactionMiddleware;\n\treadonly priority: number;\n}\n\nexport interface PluginRegistrations {\n\tcommands: string[];\n\tservices: string[];\n\tmiddlewares: MiddlewareEntry[];\n\tpasteInterceptors: PasteInterceptorEntry[];\n\ttextInputInterceptors: TextInputInterceptorEntry[];\n\tunsubscribers: (() => void)[];\n\tnodeSpecs: string[];\n\tmarkSpecs: string[];\n\tinlineNodeSpecs: string[];\n\tnodeViews: string[];\n\tkeymaps: Keymap[];\n\tinputRules: InputRule[];\n\ttoolbarItems: string[];\n\tfileHandlers: FileHandler[];\n\tblockTypePickerEntries: string[];\n\tstylesheets: CSSStyleSheet[];\n}\n\n/** Dependencies needed by the context factory from the PluginManager. */\nexport interface ContextFactoryDeps {\n\treadonly pluginId: string;\n\treadonly logger: Logger;\n\tgetState(): EditorState;\n\tdispatch(transaction: Transaction): void;\n\tgetContainer(): HTMLElement;\n\tgetPluginContainer(position: 'top' | 'bottom'): HTMLElement;\n\tannounce?(text: string): void;\n\thasAnnouncement?(): boolean;\n\treadonly commands: Map<string, CommandEntry>;\n\treadonly services: Map<string, unknown>;\n\treadonly middlewares: MiddlewareEntry[];\n\treadonly pasteInterceptors: PasteInterceptorEntry[];\n\treadonly textInputInterceptors: TextInputInterceptorEntry[];\n\treadonly pluginStyleSheets: CSSStyleSheet[];\n\treadonly plugins: Map<string, Plugin>;\n\treadonly eventBus: EventBus;\n\treadonly schemaRegistry: SchemaRegistry;\n\treadonly keymapRegistry: KeymapRegistry;\n\treadonly inputRuleRegistry: InputRuleRegistry;\n\treadonly markdownSyntaxRegistry: MarkdownSyntaxRegistry;\n\treadonly toolbarRegistry: ToolbarRegistry;\n\treadonly blockTypePickerRegistry: BlockTypePickerRegistry;\n\treadonly fileHandlerRegistry: FileHandlerRegistry;\n\treadonly nodeViewRegistry: NodeViewRegistry;\n\tgetCompositionState(): CompositionState;\n\tisReadOnly(): boolean;\n\tinvalidateMiddlewareSort(): void;\n\tinvalidatePasteSort(): void;\n\tinvalidateTextInputSort(): void;\n\texecuteCommand(name: string): boolean;\n}\n\n/** Creates an empty registration tracker for a plugin. */\nexport function createEmptyRegistrations(): PluginRegistrations {\n\treturn {\n\t\tcommands: [],\n\t\tservices: [],\n\t\tmiddlewares: [],\n\t\tpasteInterceptors: [],\n\t\ttextInputInterceptors: [],\n\t\tunsubscribers: [],\n\t\tnodeSpecs: [],\n\t\tmarkSpecs: [],\n\t\tinlineNodeSpecs: [],\n\t\tnodeViews: [],\n\t\tkeymaps: [],\n\t\tinputRules: [],\n\t\ttoolbarItems: [],\n\t\tfileHandlers: [],\n\t\tblockTypePickerEntries: [],\n\t\tstylesheets: [],\n\t};\n}\n\n// --- Focused Factory Functions ---\n\nfunction createPluginEventBus(eventBus: EventBus, reg: PluginRegistrations): PluginEventBus {\n\treturn {\n\t\temit: (key, payload) => eventBus.emit(key, payload),\n\t\ton: (key, callback) => {\n\t\t\tconst unsub = eventBus.on(key, callback);\n\t\t\treg.unsubscribers.push(unsub);\n\t\t\treturn unsub;\n\t\t},\n\t\toff: (key, callback) => eventBus.off(key, callback),\n\t};\n}\n\nfunction createCommandRegistrar(\n\tpluginId: string,\n\tcommands: Map<string, CommandEntry>,\n\treg: PluginRegistrations,\n\texecuteCommand: (name: string) => boolean,\n): Pick<PluginContext, 'registerCommand' | 'executeCommand'> {\n\treturn {\n\t\tregisterCommand: (name: string, handler: CommandHandler, options?: CommandOptions) => {\n\t\t\tif (commands.has(name)) {\n\t\t\t\tconst existing = commands.get(name);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Command \"${name}\" is already registered by plugin \"${existing?.pluginId}\".`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst readonlyAllowed: boolean = options?.readonlyAllowed ?? false;\n\t\t\tcommands.set(name, { name, handler, pluginId, readonlyAllowed });\n\t\t\treg.commands.push(name);\n\t\t},\n\t\texecuteCommand,\n\t};\n}\n\nfunction createServiceRegistrar(\n\tservices: Map<string, unknown>,\n\treg: PluginRegistrations,\n): Pick<PluginContext, 'registerService' | 'getService'> {\n\treturn {\n\t\tregisterService: <T>(key: ServiceKey<T>, service: T) => {\n\t\t\tif (services.has(key.id)) {\n\t\t\t\tthrow new Error(`Service \"${key.id}\" is already registered by another plugin.`);\n\t\t\t}\n\t\t\tservices.set(key.id, service);\n\t\t\treg.services.push(key.id);\n\t\t},\n\t\tgetService: <T>(key: ServiceKey<T>) => services.get(key.id) as T | undefined,\n\t};\n}\n\nfunction createMiddlewareRegistrar(\n\tdeps: Pick<\n\t\tContextFactoryDeps,\n\t\t| 'pluginId'\n\t\t| 'middlewares'\n\t\t| 'pasteInterceptors'\n\t\t| 'textInputInterceptors'\n\t\t| 'invalidateMiddlewareSort'\n\t\t| 'invalidatePasteSort'\n\t\t| 'invalidateTextInputSort'\n\t>,\n\treg: PluginRegistrations,\n): Pick<\n\tPluginContext,\n\t'registerMiddleware' | 'registerPasteInterceptor' | 'registerTextInputInterceptor'\n> {\n\tconst {\n\t\tpluginId,\n\t\tmiddlewares,\n\t\tpasteInterceptors,\n\t\ttextInputInterceptors,\n\t\tinvalidateMiddlewareSort,\n\t\tinvalidatePasteSort,\n\t\tinvalidateTextInputSort,\n\t} = deps;\n\treturn {\n\t\tregisterMiddleware: (middleware: TransactionMiddleware, options?: MiddlewareOptions) => {\n\t\t\tconst name: string = options?.name ?? (middleware.name || 'anonymous');\n\t\t\tconst priority: number = options?.priority ?? DEFAULT_PRIORITY;\n\t\t\tconst entry: MiddlewareEntry = { name, pluginId, middleware, priority };\n\t\t\tmiddlewares.push(entry);\n\t\t\treg.middlewares.push(entry);\n\t\t\tinvalidateMiddlewareSort();\n\t\t},\n\t\tregisterPasteInterceptor: (\n\t\t\tinterceptor: PasteInterceptor,\n\t\t\toptions?: PasteInterceptorOptions,\n\t\t) => {\n\t\t\tconst name: string = options?.name ?? 'anonymous';\n\t\t\tconst priority: number = options?.priority ?? DEFAULT_PRIORITY;\n\t\t\tconst entry: PasteInterceptorEntry = { name, pluginId, interceptor, priority };\n\t\t\tpasteInterceptors.push(entry);\n\t\t\treg.pasteInterceptors.push(entry);\n\t\t\tinvalidatePasteSort();\n\t\t},\n\t\tregisterTextInputInterceptor: (\n\t\t\tinterceptor: TextInputInterceptor,\n\t\t\toptions?: TextInputInterceptorOptions,\n\t\t) => {\n\t\t\tconst name: string = options?.name ?? 'anonymous';\n\t\t\tconst priority: number = options?.priority ?? DEFAULT_PRIORITY;\n\t\t\tconst entry: TextInputInterceptorEntry = { name, pluginId, interceptor, priority };\n\t\t\ttextInputInterceptors.push(entry);\n\t\t\treg.textInputInterceptors.push(entry);\n\t\t\tinvalidateTextInputSort();\n\t\t},\n\t};\n}\n\nfunction createSchemaRegistrar(\n\tschemaRegistry: SchemaRegistry,\n\treg: PluginRegistrations,\n): Pick<PluginContext, 'registerNodeSpec' | 'registerMarkSpec' | 'registerInlineNodeSpec'> {\n\treturn {\n\t\tregisterNodeSpec: (spec) => {\n\t\t\tschemaRegistry.registerNodeSpec(spec);\n\t\t\treg.nodeSpecs.push(spec.type);\n\t\t},\n\t\tregisterMarkSpec: (spec) => {\n\t\t\tschemaRegistry.registerMarkSpec(spec);\n\t\t\treg.markSpecs.push(spec.type);\n\t\t},\n\t\tregisterInlineNodeSpec: (spec) => {\n\t\t\tschemaRegistry.registerInlineNodeSpec(spec);\n\t\t\treg.inlineNodeSpecs.push(spec.type);\n\t\t},\n\t};\n}\n\nfunction createExtensionRegistrar(\n\tpluginId: string,\n\tdeps: Pick<\n\t\tContextFactoryDeps,\n\t\t| 'nodeViewRegistry'\n\t\t| 'keymapRegistry'\n\t\t| 'inputRuleRegistry'\n\t\t| 'markdownSyntaxRegistry'\n\t\t| 'toolbarRegistry'\n\t\t| 'blockTypePickerRegistry'\n\t\t| 'fileHandlerRegistry'\n\t\t| 'pluginStyleSheets'\n\t>,\n\treg: PluginRegistrations,\n): Pick<\n\tPluginContext,\n\t| 'registerNodeView'\n\t| 'registerKeymap'\n\t| 'registerInputRule'\n\t| 'registerMarkdownSyntax'\n\t| 'registerToolbarItem'\n\t| 'registerBlockTypePickerEntry'\n\t| 'registerFileHandler'\n\t| 'registerStyleSheet'\n> {\n\treturn {\n\t\tregisterNodeView: (type, factory) => {\n\t\t\tdeps.nodeViewRegistry.registerNodeView(type, factory);\n\t\t\treg.nodeViews.push(type);\n\t\t},\n\t\tregisterKeymap: (keymap: Keymap, options?: KeymapOptions) => {\n\t\t\tdeps.keymapRegistry.registerKeymap(keymap, options);\n\t\t\treg.keymaps.push(keymap);\n\t\t},\n\t\tregisterInputRule: (rule) => {\n\t\t\tdeps.inputRuleRegistry.registerInputRule(rule);\n\t\t\treg.inputRules.push(rule);\n\t\t},\n\t\tregisterMarkdownSyntax: (extension) => {\n\t\t\tdeps.markdownSyntaxRegistry.register(extension);\n\t\t},\n\t\tregisterToolbarItem: (item) => {\n\t\t\tdeps.toolbarRegistry.registerToolbarItem(item, pluginId);\n\t\t\treg.toolbarItems.push(item.id);\n\t\t},\n\t\tregisterBlockTypePickerEntry: (entry) => {\n\t\t\tdeps.blockTypePickerRegistry.registerBlockTypePickerEntry(entry);\n\t\t\treg.blockTypePickerEntries.push(entry.id);\n\t\t},\n\t\tregisterFileHandler: (pattern, handler) => {\n\t\t\tdeps.fileHandlerRegistry.registerFileHandler(pattern, handler);\n\t\t\treg.fileHandlers.push(handler);\n\t\t},\n\t\tregisterStyleSheet: (css: string) => {\n\t\t\tconst sheet: CSSStyleSheet = new CSSStyleSheet();\n\t\t\tsheet.replaceSync(css);\n\t\t\tdeps.pluginStyleSheets.push(sheet);\n\t\t\treg.stylesheets.push(sheet);\n\t\t},\n\t};\n}\n\nfunction createRegistryAccessors(\n\tdeps: Pick<\n\t\tContextFactoryDeps,\n\t\t| 'schemaRegistry'\n\t\t| 'keymapRegistry'\n\t\t| 'inputRuleRegistry'\n\t\t| 'fileHandlerRegistry'\n\t\t| 'nodeViewRegistry'\n\t\t| 'toolbarRegistry'\n\t\t| 'blockTypePickerRegistry'\n\t>,\n): Pick<\n\tPluginContext,\n\t| 'getSchemaRegistry'\n\t| 'getKeymapRegistry'\n\t| 'getInputRuleRegistry'\n\t| 'getFileHandlerRegistry'\n\t| 'getNodeViewRegistry'\n\t| 'getToolbarRegistry'\n\t| 'getBlockTypePickerRegistry'\n> {\n\treturn {\n\t\tgetSchemaRegistry: () => deps.schemaRegistry,\n\t\tgetKeymapRegistry: () => deps.keymapRegistry,\n\t\tgetInputRuleRegistry: () => deps.inputRuleRegistry,\n\t\tgetFileHandlerRegistry: () => deps.fileHandlerRegistry,\n\t\tgetNodeViewRegistry: () => deps.nodeViewRegistry,\n\t\tgetToolbarRegistry: () => deps.toolbarRegistry,\n\t\tgetBlockTypePickerRegistry: () => deps.blockTypePickerRegistry,\n\t};\n}\n\n/** Creates a complete PluginContext by composing focused registrar factories. */\nexport function createPluginContext(deps: ContextFactoryDeps): {\n\tcontext: PluginContext;\n\tregistrations: PluginRegistrations;\n} {\n\tconst reg: PluginRegistrations = createEmptyRegistrations();\n\tconst pluginEventBus: PluginEventBus = createPluginEventBus(deps.eventBus, reg);\n\n\tconst context: PluginContext = {\n\t\tgetState: deps.getState,\n\t\tdispatch: deps.dispatch,\n\t\tgetContainer: deps.getContainer,\n\t\tgetPluginContainer: deps.getPluginContainer,\n\t\tisReadOnly: deps.isReadOnly,\n\t\tgetEventBus: () => pluginEventBus,\n\n\t\t...createCommandRegistrar(deps.pluginId, deps.commands, reg, deps.executeCommand),\n\t\t...createServiceRegistrar(deps.services, reg),\n\t\t...createMiddlewareRegistrar(deps, reg),\n\t\t...createSchemaRegistrar(deps.schemaRegistry, reg),\n\t\t...createExtensionRegistrar(deps.pluginId, deps, reg),\n\t\t...createRegistryAccessors(deps),\n\n\t\tupdateConfig: (config: PluginConfig) => {\n\t\t\tconst plugin: Plugin | undefined = deps.plugins.get(deps.pluginId);\n\t\t\tif (plugin?.onConfigure) {\n\t\t\t\ttry {\n\t\t\t\t\tplugin.onConfigure(config);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst scope = `[PluginContext] Plugin \"${deps.pluginId}\" error in onConfigure`;\n\t\t\t\t\tdeps.logger.error(scope, err);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tannounce: (text: string) => {\n\t\t\tdeps.announce?.(text);\n\t\t},\n\t\thasAnnouncement: () => deps.hasAnnouncement?.() ?? false,\n\t\tgetCompositionState: () => deps.getCompositionState(),\n\t};\n\n\treturn { context, registrations: reg };\n}\n","/**\n * Manages plugin ordering, initialization, destruction, and notifications.\n * Extracted from PluginManager for single-responsibility.\n */\n\nimport { DecorationSet } from '../decorations/Decoration.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { type Logger, consoleLogger, scopedLogger } from './Logger.js';\nimport type { Plugin, PluginConfig, PluginContext } from './Plugin.js';\nimport type { RegistrationTracker } from './RegistrationTracker.js';\n\nconst DEFAULT_PRIORITY = 100;\n\nexport interface PluginLifecycleInitOptions {\n\tgetState(): EditorState;\n\tdispatch(transaction: Transaction): void;\n\tgetContainer(): HTMLElement;\n\tgetPluginContainer(position: 'top' | 'bottom'): HTMLElement;\n\tannounce?(text: string): void;\n\thasAnnouncement?(): boolean;\n\tonBeforeReady?(): void | Promise<void>;\n\tisCancelled?(): boolean;\n}\n\n/** Callback injected by the facade to create a PluginContext per plugin. */\nexport type ContextFactory = (\n\tpluginId: string,\n\toptions: PluginLifecycleInitOptions,\n) => PluginContext;\n\nexport class PluginLifecycle {\n\tprivate readonly plugins = new Map<string, Plugin>();\n\tprivate initOrder: string[] = [];\n\tprivate startedInitOrder: string[] = [];\n\tprivate initialized = false;\n\tprivate initializing = false;\n\tprivate readOnly = false;\n\tprivate readonly log: Logger;\n\n\tconstructor(\n\t\tprivate readonly tracker: RegistrationTracker,\n\t\tlogger: Logger = consoleLogger,\n\t) {\n\t\tthis.log = scopedLogger(logger, 'PluginLifecycle');\n\t}\n\n\t// --- Registration ---\n\n\t/** Registers a plugin. Must be called before init(). */\n\tregister(plugin: Plugin): void {\n\t\tif (this.initialized || this.initializing) {\n\t\t\tthrow new Error(`Cannot register plugin \"${plugin.id}\" after initialization.`);\n\t\t}\n\t\tif (this.plugins.has(plugin.id)) {\n\t\t\tthrow new Error(`Plugin \"${plugin.id}\" is already registered.`);\n\t\t}\n\t\tthis.plugins.set(plugin.id, plugin);\n\t}\n\n\t/** Gets a plugin by ID. */\n\tget(id: string): Plugin | undefined {\n\t\treturn this.plugins.get(id);\n\t}\n\n\t/** Returns all registered plugin IDs. */\n\tgetPluginIds(): string[] {\n\t\treturn [...this.plugins.keys()];\n\t}\n\n\t// --- Initialization ---\n\n\t/** Initializes all registered plugins in dependency/priority order. */\n\tasync init(options: PluginLifecycleInitOptions, createContext: ContextFactory): Promise<void> {\n\t\tif (this.initialized || this.initializing) return;\n\t\tthis.initializing = true;\n\t\ttry {\n\t\t\tthis.initOrder = this.resolveOrder();\n\n\t\t\tfor (const id of this.initOrder) {\n\t\t\t\tif (options.isCancelled?.()) return;\n\t\t\t\tconst plugin = this.plugins.get(id);\n\t\t\t\tif (!plugin) continue;\n\t\t\t\tthis.startedInitOrder.push(id);\n\t\t\t\tconst context = createContext(id, options);\n\t\t\t\ttry {\n\t\t\t\t\tawait plugin.init(context);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this.rollbackStartedPlugins();\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.isCancelled?.()) return;\n\n\t\t\tif (options.onBeforeReady) {\n\t\t\t\tawait options.onBeforeReady();\n\t\t\t}\n\n\t\t\tif (options.isCancelled?.()) return;\n\n\t\t\tfor (const id of this.initOrder) {\n\t\t\t\tif (options.isCancelled?.()) return;\n\t\t\t\tconst plugin = this.plugins.get(id);\n\t\t\t\tif (!plugin?.onReady) continue;\n\t\t\t\ttry {\n\t\t\t\t\tawait plugin.onReady();\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.log.error(`Plugin \"${id}\" error in onReady`, err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.initialized = true;\n\t\t} finally {\n\t\t\tthis.initializing = false;\n\t\t}\n\t}\n\n\t// --- Destruction ---\n\n\t/** Destroys all plugins in reverse init order. */\n\tasync destroy(): Promise<void> {\n\t\tconst reversed = [...this.startedInitOrder].reverse();\n\t\tfor (const id of reversed) {\n\t\t\tawait this.destroyPlugin(id);\n\t\t}\n\t\tthis.plugins.clear();\n\t\tthis.initOrder = [];\n\t\tthis.startedInitOrder = [];\n\t\tthis.initialized = false;\n\t\tthis.initializing = false;\n\t}\n\n\t// --- Notifications ---\n\n\t/** Notifies all plugins of a state change, in init order. */\n\tnotifyStateChange(oldState: EditorState, newState: EditorState, tr: Transaction): void {\n\t\tfor (const id of this.initOrder) {\n\t\t\tconst plugin = this.plugins.get(id);\n\t\t\tif (!plugin?.onStateChange) continue;\n\t\t\ttry {\n\t\t\t\tplugin.onStateChange(oldState, newState, tr);\n\t\t\t} catch (err) {\n\t\t\t\tthis.log.error(`Plugin \"${id}\" error in onStateChange`, err);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Collects and merges decorations from all plugins. */\n\tcollectDecorations(state: EditorState, tr?: Transaction): DecorationSet {\n\t\tlet result: DecorationSet = DecorationSet.empty;\n\t\tfor (const id of this.initOrder) {\n\t\t\tconst plugin = this.plugins.get(id);\n\t\t\tif (!plugin?.decorations) continue;\n\t\t\ttry {\n\t\t\t\tconst decos = plugin.decorations(state, tr);\n\t\t\t\tif (!decos.isEmpty) {\n\t\t\t\t\tresult = result.merge(decos);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis.log.error(`Plugin \"${id}\" error in decorations()`, err);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Configures a plugin at runtime via onConfigure(). */\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tconst plugin = this.plugins.get(pluginId);\n\t\tif (!plugin) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found.`);\n\t\t}\n\t\tif (!plugin.onConfigure) return;\n\t\ttry {\n\t\t\tplugin.onConfigure(config);\n\t\t} catch (err) {\n\t\t\tthis.log.error(`Plugin \"${pluginId}\" error in onConfigure`, err);\n\t\t}\n\t}\n\n\t// --- Read-Only ---\n\n\tisReadOnly(): boolean {\n\t\treturn this.readOnly;\n\t}\n\n\t/** Updates read-only state and notifies all plugins. */\n\tsetReadOnly(readonly: boolean): void {\n\t\tif (this.readOnly === readonly) return;\n\t\tthis.readOnly = readonly;\n\t\tfor (const id of this.initOrder) {\n\t\t\tconst plugin = this.plugins.get(id);\n\t\t\tif (!plugin?.onReadOnlyChange) continue;\n\t\t\ttry {\n\t\t\t\tplugin.onReadOnlyChange(readonly);\n\t\t\t} catch (err) {\n\t\t\t\tthis.log.error(`Plugin \"${id}\" error in onReadOnlyChange`, err);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Raw accessors ---\n\n\tget rawPlugins(): Map<string, Plugin> {\n\t\treturn this.plugins;\n\t}\n\n\tget isInitialized(): boolean {\n\t\treturn this.initialized;\n\t}\n\n\t// --- Private ---\n\n\tprivate async destroyPlugin(id: string): Promise<void> {\n\t\tconst plugin = this.plugins.get(id);\n\t\tif (plugin?.destroy) {\n\t\t\ttry {\n\t\t\t\tawait plugin.destroy();\n\t\t\t} catch (err) {\n\t\t\t\tthis.log.error(`Plugin \"${id}\" error in destroy`, err);\n\t\t\t}\n\t\t}\n\t\tthis.tracker.cleanup(id);\n\t}\n\n\tprivate async rollbackStartedPlugins(): Promise<void> {\n\t\tconst reversed = [...this.startedInitOrder].reverse();\n\t\tfor (const id of reversed) {\n\t\t\tawait this.destroyPlugin(id);\n\t\t}\n\t\tthis.startedInitOrder = [];\n\t\tthis.initOrder = [];\n\t\tthis.initialized = false;\n\t}\n\n\t/**\n\t * Resolves plugin initialization order via topological sort + priority.\n\t * Throws on dependency cycles or missing dependencies.\n\t */\n\tprivate resolveOrder(): string[] {\n\t\tconst ids = [...this.plugins.keys()];\n\n\t\tfor (const id of ids) {\n\t\t\tconst plugin = this.plugins.get(id);\n\t\t\tif (!plugin) continue;\n\t\t\tfor (const dep of plugin.dependencies ?? []) {\n\t\t\t\tif (!this.plugins.has(dep)) {\n\t\t\t\t\tthrow new Error(`Plugin \"${id}\" depends on \"${dep}\", which is not registered.`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst inDegree = new Map<string, number>();\n\t\tconst dependents = new Map<string, string[]>();\n\n\t\tfor (const id of ids) {\n\t\t\tinDegree.set(id, 0);\n\t\t\tdependents.set(id, []);\n\t\t}\n\n\t\tfor (const id of ids) {\n\t\t\tconst plugin = this.plugins.get(id);\n\t\t\tconst deps = plugin?.dependencies ?? [];\n\t\t\tinDegree.set(id, deps.length);\n\t\t\tfor (const dep of deps) {\n\t\t\t\tconst depList = dependents.get(dep);\n\t\t\t\tif (depList) depList.push(id);\n\t\t\t}\n\t\t}\n\n\t\tconst queue: string[] = [];\n\t\tfor (const [id, deg] of inDegree) {\n\t\t\tif (deg === 0) queue.push(id);\n\t\t}\n\n\t\tconst sorted: string[] = [];\n\t\twhile (queue.length > 0) {\n\t\t\tqueue.sort((a, b) => {\n\t\t\t\tconst pa = this.plugins.get(a)?.priority ?? DEFAULT_PRIORITY;\n\t\t\t\tconst pb = this.plugins.get(b)?.priority ?? DEFAULT_PRIORITY;\n\t\t\t\treturn pa - pb;\n\t\t\t});\n\n\t\t\tconst id = queue.shift();\n\t\t\tif (!id) break;\n\t\t\tsorted.push(id);\n\n\t\t\tfor (const dep of dependents.get(id) ?? []) {\n\t\t\t\tconst newDeg = (inDegree.get(dep) ?? 0) - 1;\n\t\t\t\tinDegree.set(dep, newDeg);\n\t\t\t\tif (newDeg === 0) queue.push(dep);\n\t\t\t}\n\t\t}\n\n\t\tif (sorted.length !== ids.length) {\n\t\t\tconst missing = ids.filter((id) => !sorted.includes(id));\n\t\t\tthrow new Error(`Circular dependency detected among plugins: ${missing.join(', ')}`);\n\t\t}\n\n\t\treturn sorted;\n\t}\n}\n","/**\n * Tracks per-plugin registrations and provides cleanup on plugin destroy.\n * Extracted from PluginManager for single-responsibility.\n */\n\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { NodeViewRegistry } from '../view/NodeViewRegistry.js';\nimport type { CommandRegistry } from './CommandRegistry.js';\nimport type { MiddlewareChain } from './MiddlewareChain.js';\nimport type { ServiceRegistry } from './ServiceRegistry.js';\nimport type { BlockTypePickerRegistry } from './heading/BlockTypePickerRegistry.js';\nimport type { ToolbarRegistry } from './toolbar/ToolbarRegistry.js';\n\nimport type { PluginRegistrations } from './PluginContextFactory.js';\n\n/** Dependencies needed for cleaning up a plugin's registrations. */\nexport interface RegistrationCleanupDeps {\n\treadonly commandRegistry: CommandRegistry;\n\treadonly serviceRegistry: ServiceRegistry;\n\treadonly middlewareChain: MiddlewareChain;\n\treadonly schemaRegistry: SchemaRegistry;\n\treadonly keymapRegistry: KeymapRegistry;\n\treadonly inputRuleRegistry: InputRuleRegistry;\n\treadonly nodeViewRegistry: NodeViewRegistry;\n\treadonly toolbarRegistry: ToolbarRegistry;\n\treadonly fileHandlerRegistry: FileHandlerRegistry;\n\treadonly blockTypePickerRegistry: BlockTypePickerRegistry;\n}\n\nexport class RegistrationTracker {\n\tprivate readonly registrations = new Map<string, PluginRegistrations>();\n\tprivate readonly pluginStyleSheets: CSSStyleSheet[] = [];\n\n\tconstructor(private readonly deps: RegistrationCleanupDeps) {}\n\n\t/** Associates a plugin's registrations for later cleanup. */\n\ttrack(pluginId: string, registrations: PluginRegistrations): void {\n\t\tthis.registrations.set(pluginId, registrations);\n\t}\n\n\t/** Removes all registrations for a plugin from their respective registries. */\n\tcleanup(pluginId: string): void {\n\t\tconst reg = this.registrations.get(pluginId);\n\t\tif (!reg) return;\n\n\t\tfor (const name of reg.commands) this.deps.commandRegistry.remove(name);\n\t\tfor (const serviceId of reg.services) this.deps.serviceRegistry.remove(serviceId);\n\t\tfor (const entry of reg.middlewares) this.deps.middlewareChain.removeMiddleware(entry);\n\t\tfor (const entry of reg.pasteInterceptors) {\n\t\t\tthis.deps.middlewareChain.removePasteInterceptor(entry);\n\t\t}\n\t\tfor (const entry of reg.textInputInterceptors) {\n\t\t\tthis.deps.middlewareChain.removeTextInputInterceptor(entry);\n\t\t}\n\t\tfor (const unsub of reg.unsubscribers) unsub();\n\n\t\tfor (const type of reg.nodeSpecs) this.deps.schemaRegistry.removeNodeSpec(type);\n\t\tfor (const type of reg.markSpecs) this.deps.schemaRegistry.removeMarkSpec(type);\n\t\tfor (const type of reg.inlineNodeSpecs) {\n\t\t\tthis.deps.schemaRegistry.removeInlineNodeSpec(type);\n\t\t}\n\n\t\tfor (const type of reg.nodeViews) this.deps.nodeViewRegistry.removeNodeView(type);\n\t\tfor (const keymap of reg.keymaps) this.deps.keymapRegistry.removeKeymap(keymap);\n\t\tfor (const rule of reg.inputRules) this.deps.inputRuleRegistry.removeInputRule(rule);\n\t\tfor (const itemId of reg.toolbarItems) {\n\t\t\tthis.deps.toolbarRegistry.removeToolbarItem(itemId);\n\t\t}\n\t\tfor (const handler of reg.fileHandlers) {\n\t\t\tthis.deps.fileHandlerRegistry.removeFileHandler(handler);\n\t\t}\n\t\tfor (const entryId of reg.blockTypePickerEntries) {\n\t\t\tthis.deps.blockTypePickerRegistry.removeBlockTypePickerEntry(entryId);\n\t\t}\n\t\tfor (const sheet of reg.stylesheets) {\n\t\t\tconst idx = this.pluginStyleSheets.indexOf(sheet);\n\t\t\tif (idx !== -1) this.pluginStyleSheets.splice(idx, 1);\n\t\t}\n\n\t\tthis.registrations.delete(pluginId);\n\t}\n\n\tclear(): void {\n\t\tthis.registrations.clear();\n\t\tthis.pluginStyleSheets.length = 0;\n\t}\n\n\t/** Exposes internal stylesheet array for ContextFactoryDeps assembly. */\n\tget rawStyleSheets(): CSSStyleSheet[] {\n\t\treturn this.pluginStyleSheets;\n\t}\n}\n","/**\n * Type-safe service registry for cross-plugin service sharing.\n * Extracted from PluginManager for single-responsibility.\n */\n\nimport type { ServiceKey } from './Plugin.js';\n\nexport class ServiceRegistry {\n\tprivate readonly services = new Map<string, unknown>();\n\n\t/** Registers a service. Throws if the key is already registered. */\n\tregister<T>(key: ServiceKey<T>, service: T): void {\n\t\tif (this.services.has(key.id)) {\n\t\t\tthrow new Error(`Service \"${key.id}\" is already registered.`);\n\t\t}\n\t\tthis.services.set(key.id, service);\n\t}\n\n\t/** Returns a service by typed key, or undefined if not registered. */\n\tget<T>(key: ServiceKey<T>): T | undefined {\n\t\treturn this.services.get(key.id) as T | undefined;\n\t}\n\n\thas(id: string): boolean {\n\t\treturn this.services.has(id);\n\t}\n\n\tremove(id: string): void {\n\t\tthis.services.delete(id);\n\t}\n\n\tclear(): void {\n\t\tthis.services.clear();\n\t}\n\n\t/** Exposes internal map for ContextFactoryDeps assembly. */\n\tget rawMap(): Map<string, unknown> {\n\t\treturn this.services;\n\t}\n}\n","/**\n * Plugin manager facade: composes focused modules for command registry,\n * service registry, middleware chain, registration tracking, and\n * plugin lifecycle management.\n *\n * All public methods delegate to the appropriate sub-module.\n */\n\nimport type { DecorationSet } from '../decorations/Decoration.js';\nimport type { CompositionState } from '../model/CompositionState.js';\nimport { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport { MarkdownSyntaxRegistry } from '../model/MarkdownSyntaxRegistry.js';\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { NodeViewRegistry } from '../view/NodeViewRegistry.js';\nimport { CommandRegistry } from './CommandRegistry.js';\nimport { EventBus } from './EventBus.js';\nimport { type Logger, consoleLogger } from './Logger.js';\nimport { MiddlewareChain } from './MiddlewareChain.js';\nimport type {\n\tEventKey,\n\tPlugin,\n\tPluginConfig,\n\tPluginContext,\n\tPluginEventCallback,\n\tServiceKey,\n} from './Plugin.js';\nimport type { ContextFactoryDeps } from './PluginContextFactory.js';\nimport { createPluginContext } from './PluginContextFactory.js';\nimport { PluginLifecycle } from './PluginLifecycle.js';\nimport { RegistrationTracker } from './RegistrationTracker.js';\nimport { ServiceRegistry } from './ServiceRegistry.js';\nimport { BlockTypePickerRegistry } from './heading/BlockTypePickerRegistry.js';\nimport { ToolbarRegistry } from './toolbar/ToolbarRegistry.js';\n\nexport type { MiddlewareInfo } from './MiddlewareChain.js';\nexport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nexport type { TextInputInterceptorEntry } from '../model/TextInputInterceptor.js';\nexport type { MiddlewareEntry, PluginRegistrations } from './PluginContextFactory.js';\n\n/** Optional dependencies for PluginManager. */\nexport interface PluginManagerOptions {\n\t/**\n\t * Sink for editor runtime errors (plugin lifecycle failures, middleware\n\t * exceptions, event listener errors, command handler crashes).\n\t * Defaults to `consoleLogger`, which forwards to the global `console`.\n\t * Pass `silentLogger` to suppress output, or supply a custom adapter\n\t * to route into your own telemetry / logging pipeline.\n\t */\n\treadonly logger?: Logger;\n}\n\nexport interface PluginManagerInitOptions {\n\tgetState(): EditorState;\n\tdispatch(transaction: Transaction): void;\n\tgetContainer(): HTMLElement;\n\tgetPluginContainer(position: 'top' | 'bottom'): HTMLElement;\n\tannounce?(text: string): void;\n\thasAnnouncement?(): boolean;\n\tgetCompositionState?(): CompositionState;\n\tonBeforeReady?(): void | Promise<void>;\n\tisCancelled?(): boolean;\n}\n\nexport class PluginManager {\n\t// Extension registries (unchanged — already separate classes)\n\treadonly schemaRegistry = new SchemaRegistry();\n\treadonly keymapRegistry = new KeymapRegistry();\n\treadonly inputRuleRegistry = new InputRuleRegistry();\n\treadonly markdownSyntaxRegistry = new MarkdownSyntaxRegistry();\n\treadonly fileHandlerRegistry = new FileHandlerRegistry();\n\treadonly nodeViewRegistry = new NodeViewRegistry();\n\treadonly toolbarRegistry = new ToolbarRegistry();\n\treadonly blockTypePickerRegistry = new BlockTypePickerRegistry();\n\n\t// Extracted sub-modules\n\tprivate readonly logger: Logger;\n\tprivate readonly commandRegistry: CommandRegistry;\n\tprivate readonly serviceRegistry = new ServiceRegistry();\n\tprivate readonly middlewareChain: MiddlewareChain;\n\tprivate readonly eventBus: EventBus;\n\tprivate readonly registrationTracker: RegistrationTracker;\n\tprivate readonly lifecycle: PluginLifecycle;\n\n\tconstructor(options: PluginManagerOptions = {}) {\n\t\tthis.logger = options.logger ?? consoleLogger;\n\t\tthis.commandRegistry = new CommandRegistry(this.logger);\n\t\tthis.middlewareChain = new MiddlewareChain(this.logger);\n\t\tthis.eventBus = new EventBus(this.logger);\n\t\tthis.registrationTracker = new RegistrationTracker({\n\t\t\tcommandRegistry: this.commandRegistry,\n\t\t\tserviceRegistry: this.serviceRegistry,\n\t\t\tmiddlewareChain: this.middlewareChain,\n\t\t\tschemaRegistry: this.schemaRegistry,\n\t\t\tkeymapRegistry: this.keymapRegistry,\n\t\t\tinputRuleRegistry: this.inputRuleRegistry,\n\t\t\tnodeViewRegistry: this.nodeViewRegistry,\n\t\t\ttoolbarRegistry: this.toolbarRegistry,\n\t\t\tfileHandlerRegistry: this.fileHandlerRegistry,\n\t\t\tblockTypePickerRegistry: this.blockTypePickerRegistry,\n\t\t});\n\t\tthis.lifecycle = new PluginLifecycle(this.registrationTracker, this.logger);\n\t}\n\n\t// --- Service (system-level, before init) ---\n\n\tregisterService<T>(key: ServiceKey<T>, service: T): void {\n\t\tthis.serviceRegistry.register(key, service);\n\t}\n\n\t// --- Plugin Registration ---\n\n\tregister(plugin: Plugin): void {\n\t\tthis.lifecycle.register(plugin);\n\t}\n\n\t// --- Initialization ---\n\n\tasync init(options: PluginManagerInitOptions): Promise<void> {\n\t\treturn this.lifecycle.init(options, (id, opts) => this.createContext(id, opts));\n\t}\n\n\t// --- State & Notifications ---\n\n\tnotifyStateChange(oldState: EditorState, newState: EditorState, tr: Transaction): void {\n\t\tthis.lifecycle.notifyStateChange(oldState, newState, tr);\n\t}\n\n\tcollectDecorations(state: EditorState, tr?: Transaction): DecorationSet {\n\t\treturn this.lifecycle.collectDecorations(state, tr);\n\t}\n\n\t// --- Middleware ---\n\n\tdispatchWithMiddleware(\n\t\ttr: Transaction,\n\t\tstate: EditorState,\n\t\tfinalDispatch: (tr: Transaction) => void,\n\t): void {\n\t\tthis.middlewareChain.dispatch(tr, state, finalDispatch);\n\t}\n\n\t// --- Commands ---\n\n\tcanExecuteCommand(name: string): boolean {\n\t\treturn this.commandRegistry.canExecute(name, this.lifecycle.isReadOnly());\n\t}\n\n\texecuteCommand(name: string): boolean {\n\t\treturn this.commandRegistry.execute(name, this.lifecycle.isReadOnly());\n\t}\n\n\tisReadonlyBypassed(): boolean {\n\t\treturn this.commandRegistry.isReadonlyBypassed();\n\t}\n\n\t// --- Plugin Configuration ---\n\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.lifecycle.configurePlugin(pluginId, config);\n\t}\n\n\t// --- Read-Only ---\n\n\tisReadOnly(): boolean {\n\t\treturn this.lifecycle.isReadOnly();\n\t}\n\n\tsetReadOnly(readonly: boolean): void {\n\t\tthis.lifecycle.setReadOnly(readonly);\n\t}\n\n\t// --- Introspection ---\n\n\tgetPluginIds(): string[] {\n\t\treturn this.lifecycle.getPluginIds();\n\t}\n\n\tgetMiddlewareChain(): readonly import('./MiddlewareChain.js').MiddlewareInfo[] {\n\t\treturn this.middlewareChain.getChain();\n\t}\n\n\tgetPasteInterceptors(): readonly PasteInterceptorEntry[] {\n\t\treturn this.middlewareChain.getPasteInterceptors();\n\t}\n\n\tgetTextInputInterceptors(): readonly TextInputInterceptorEntry[] {\n\t\treturn this.middlewareChain.getTextInputInterceptors();\n\t}\n\n\tget(id: string): Plugin | undefined {\n\t\treturn this.lifecycle.get(id);\n\t}\n\n\tgetService<T>(key: ServiceKey<T>): T | undefined {\n\t\treturn this.serviceRegistry.get(key);\n\t}\n\n\tonEvent<T>(key: EventKey<T>, callback: PluginEventCallback<T>): () => void {\n\t\treturn this.eventBus.on(key, callback);\n\t}\n\n\tgetPluginStyleSheets(): readonly CSSStyleSheet[] {\n\t\treturn this.registrationTracker.rawStyleSheets;\n\t}\n\n\t// --- Destruction ---\n\n\tasync destroy(): Promise<void> {\n\t\tawait this.lifecycle.destroy();\n\t\tthis.commandRegistry.clear();\n\t\tthis.serviceRegistry.clear();\n\t\tthis.middlewareChain.clear();\n\t\tthis.registrationTracker.clear();\n\t\tthis.eventBus.clear();\n\t\tthis.schemaRegistry.clear();\n\t\tthis.keymapRegistry.clear();\n\t\tthis.inputRuleRegistry.clear();\n\t\tthis.markdownSyntaxRegistry.clear();\n\t\tthis.fileHandlerRegistry.clear();\n\t\tthis.nodeViewRegistry.clear();\n\t\tthis.toolbarRegistry.clear();\n\t\tthis.blockTypePickerRegistry.clear();\n\t}\n\n\t// --- Private ---\n\n\tprivate createContext(pluginId: string, options: PluginManagerInitOptions): PluginContext {\n\t\tconst getCompositionState: () => CompositionState =\n\t\t\toptions.getCompositionState ?? (() => IDLE_COMPOSITION);\n\t\tconst deps: ContextFactoryDeps = {\n\t\t\tpluginId,\n\t\t\tlogger: this.logger,\n\t\t\tgetState: options.getState,\n\t\t\tdispatch: options.dispatch,\n\t\t\tgetContainer: options.getContainer,\n\t\t\tgetPluginContainer: options.getPluginContainer,\n\t\t\tannounce: options.announce,\n\t\t\thasAnnouncement: options.hasAnnouncement,\n\t\t\tcommands: this.commandRegistry.rawMap,\n\t\t\tservices: this.serviceRegistry.rawMap,\n\t\t\tmiddlewares: this.middlewareChain.rawMiddlewares,\n\t\t\tpasteInterceptors: this.middlewareChain.rawPasteInterceptors,\n\t\t\ttextInputInterceptors: this.middlewareChain.rawTextInputInterceptors,\n\t\t\tpluginStyleSheets: this.registrationTracker.rawStyleSheets,\n\t\t\tplugins: this.lifecycle.rawPlugins,\n\t\t\teventBus: this.eventBus,\n\t\t\tschemaRegistry: this.schemaRegistry,\n\t\t\tkeymapRegistry: this.keymapRegistry,\n\t\t\tinputRuleRegistry: this.inputRuleRegistry,\n\t\t\tmarkdownSyntaxRegistry: this.markdownSyntaxRegistry,\n\t\t\ttoolbarRegistry: this.toolbarRegistry,\n\t\t\tblockTypePickerRegistry: this.blockTypePickerRegistry,\n\t\t\tfileHandlerRegistry: this.fileHandlerRegistry,\n\t\t\tnodeViewRegistry: this.nodeViewRegistry,\n\t\t\tgetCompositionState,\n\t\t\tisReadOnly: () => this.lifecycle.isReadOnly(),\n\t\t\tinvalidateMiddlewareSort: () => this.middlewareChain.invalidateMiddlewareSort(),\n\t\t\tinvalidatePasteSort: () => this.middlewareChain.invalidatePasteSort(),\n\t\t\tinvalidateTextInputSort: () => this.middlewareChain.invalidateTextInputSort(),\n\t\t\texecuteCommand: (name: string) => this.executeCommand(name),\n\t\t};\n\n\t\tconst { context, registrations } = createPluginContext(deps);\n\t\tthis.registrationTracker.track(pluginId, registrations);\n\t\treturn context;\n\t}\n}\n\nconst IDLE_COMPOSITION: CompositionState = { isComposing: false, activeBlockId: null };\n","/**\n * Maps an {@link EditorSelection} through a {@link Mapping}.\n *\n * Used for keeping selection state semantically valid across transactions\n * that affected the document — e.g. restoring the pre-edit cursor on undo\n * when out-of-band transactions interleaved with the user's own edits, or\n * preserving a remote user's caret when a local edit shifts the position\n * space.\n *\n * Semantics:\n *\n * - {@link Selection} (anchor + head): anchor is mapped with `assoc = -1`\n * (sticky left, the natural choice for the \"start\" side), head with\n * `assoc = +1` (sticky right, \"end\" side). When the caller knows the\n * selection's direction (e.g. via {@link selectionRange}), they can\n * override via {@link mapTextSelection}.\n * - {@link NodeSelection}: the selection is kept if and only if the\n * selected node still exists after the mapping. If the block was removed,\n * `null` is returned and the caller must decide on a fallback (e.g.\n * first leaf block, as {@link EditorState.validateSelection} does).\n * - {@link GapCursorSelection}: identical handling to NodeSelection — the\n * referenced block must still exist.\n *\n * The returned value is a brand-new selection only when the mapping\n * changed something; reference equality is preserved for no-op cases so\n * downstream change-detection can short-circuit cheaply.\n */\n\nimport type {\n\tEditorSelection,\n\tGapCursorSelection,\n\tNodeSelection,\n\tPosition,\n\tSelection,\n} from '../model/Selection.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateGapCursor,\n\tcreateNodeSelection,\n\tcreateSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { Mapping } from './Mapping.js';\n\n/**\n * Maps a generic {@link EditorSelection} through a {@link Mapping}.\n *\n * @returns the mapped selection, or `null` if the selection became\n * structurally invalid (node-selection's node disappeared, or\n * gap-cursor's host block disappeared) and the caller must fall\n * back. For ordinary text selections this never returns `null` —\n * positions clamp to the start of removed content as part of\n * {@link Mapping.map}.\n */\nexport function mapSelection(sel: EditorSelection, mapping: Mapping): EditorSelection | null {\n\tif (mapping.isEmpty) return sel;\n\tif (isNodeSelection(sel)) return mapNodeSelection(sel, mapping);\n\tif (isGapCursor(sel)) return mapGapCursor(sel, mapping);\n\treturn mapTextSelection(sel, mapping);\n}\n\n/**\n * Maps a text selection. For non-collapsed selections, anchor uses\n * `assoc=-1` (sticky-left, inclusive-start) and head uses `assoc=+1`\n * (sticky-right, exclusive-end). For *collapsed* selections (a cursor),\n * both endpoints use the same `assoc=-1` so the cursor stays collapsed\n * rather than being torn across a split boundary into two different\n * blocks.\n *\n * Reference equality is preserved when neither endpoint moved.\n */\nexport function mapTextSelection(sel: Selection, mapping: Mapping): Selection {\n\tif (isCollapsed(sel)) {\n\t\tconst pos: Position = mapping.map(sel.anchor, -1);\n\t\tif (pos === sel.anchor) return sel;\n\t\treturn createCollapsedSelection(pos.blockId, pos.offset);\n\t}\n\tconst anchor: Position = mapping.map(sel.anchor, -1);\n\tconst head: Position = mapping.map(sel.head, 1);\n\tif (anchor === sel.anchor && head === sel.head) return sel;\n\treturn createSelection(anchor, head);\n}\n\nfunction mapNodeSelection(sel: NodeSelection, mapping: Mapping): NodeSelection | null {\n\t// A NodeSelection survives only if the targeted node is still reachable.\n\t// The mapping flags positions in removed blocks as `deleted`; we probe\n\t// at offset 0 of the node since NodeSelection has no offset semantics.\n\tconst probe: Position = { blockId: sel.nodeId, offset: 0 };\n\tconst result = mapping.mapResult(probe, -1);\n\tif (result.deleted) return null;\n\t// If the block was merged into another, the probe migrates to the\n\t// target block; the selected node ID is no longer addressable as a\n\t// stand-alone block, so we drop it.\n\tif (result.pos.blockId !== sel.nodeId) return null;\n\treturn createNodeSelection(sel.nodeId, sel.path);\n}\n\nfunction mapGapCursor(sel: GapCursorSelection, mapping: Mapping): GapCursorSelection | null {\n\tconst probe: Position = { blockId: sel.blockId, offset: 0 };\n\tconst result = mapping.mapResult(probe, -1);\n\tif (result.deleted) return null;\n\tif (result.pos.blockId !== sel.blockId) return null;\n\treturn createGapCursor(sel.blockId, sel.side, sel.path);\n}\n","/**\n * History manager for undo/redo with transaction grouping.\n *\n * Undo and redo both rebase their steps through any `interveningMapping`\n * accumulated by {@link HistoryManager.recordIntervening}, so out-of-band\n * transactions (collaboration updates, agent edits, async plugin writes)\n * can land in the document without corrupting the user's undo/redo\n * history. When a step's coordinates cannot survive the rebase (host block\n * removed, range fully eaten by intervening edits), the entire group is\n * abandoned rather than partially applied.\n *\n * Internally, an undo produces a *synthetic* transaction whose steps are\n * the rebased inverses of the original group's steps. The synthetic is\n * pushed onto the redo stack; redo simply runs the same rebase-and-apply\n * algorithm against it (inverting its steps a second time). This keeps\n * undo and redo perfectly symmetric — both paths exercise the same\n * frame-correct rebasing logic, and the rebased step maps from one\n * direction become the forward step maps used to rebase the other.\n */\n\nimport type { Mark } from '../model/Document.js';\nimport type { Document } from '../model/Document.js';\nimport type { EditorState } from './EditorState.js';\nimport { Mapping, type StepMap, invertStepMap, stepMapsEqual } from './Mapping.js';\nimport { mapSelection } from './SelectionMapping.js';\nimport { applyStep, getStepMap, invertStep, mapStep } from './StepHandlers.js';\nimport { deriveStoredMarksBefore } from './StepInversion.js';\nimport type { Step, Transaction } from './Transaction.js';\n\nexport interface HistoryResult {\n\treadonly state: EditorState;\n\treadonly transaction: Transaction;\n}\n\ninterface HistoryGroup {\n\treadonly transactions: readonly Transaction[];\n\treadonly timestamp: number;\n\t/**\n\t * Composed mapping of out-of-band transactions accumulated since this\n\t * group was placed on its stack. Used by {@link HistoryManager.undo} /\n\t * {@link HistoryManager.redo} to fold every step in the group through\n\t * intervening edits — not just the restored selection. Empty by default.\n\t */\n\treadonly interveningMapping: Mapping;\n}\n\nconst DEFAULT_GROUP_TIMEOUT_MS = 500;\nconst DEFAULT_MAX_DEPTH = 100;\n\nexport class HistoryManager {\n\tprivate undoStack: HistoryGroup[] = [];\n\tprivate redoStack: HistoryGroup[] = [];\n\tprivate readonly groupTimeoutMs: number;\n\tprivate readonly maxDepth: number;\n\tprivate lastOrigin: string | null = null;\n\n\tconstructor(options?: { groupTimeoutMs?: number; maxDepth?: number }) {\n\t\tthis.groupTimeoutMs = options?.groupTimeoutMs ?? DEFAULT_GROUP_TIMEOUT_MS;\n\t\tthis.maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;\n\t}\n\n\t/**\n\t * Pushes a transaction onto the undo stack.\n\t * Groups consecutive input transactions within the timeout window.\n\t *\n\t * Pushed transactions never propagate their mapping onto older groups'\n\t * {@link interveningMapping}: linear undo pops newer groups before older\n\t * ones, reverting their effects first, so by the time an older group is\n\t * undone the newer ones' forward maps no longer reflect document state.\n\t * Out-of-band edits that the caller does NOT want undoable should use\n\t * {@link recordIntervening} instead.\n\t */\n\tpush(tr: Transaction): void {\n\t\tif (tr.metadata.origin === 'history') return;\n\n\t\t// Skip transactions with no document changes (selection/stored marks only),\n\t\t// but break grouping so the next insertion starts a new undo group.\n\t\tif (!tr.steps.some((step: Step) => step.type !== 'setStoredMarks')) {\n\t\t\tthis.lastOrigin = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = tr.metadata.timestamp;\n\t\tconst lastGroup = this.undoStack[this.undoStack.length - 1];\n\t\tconst shouldGroup =\n\t\t\tlastGroup &&\n\t\t\ttr.metadata.origin === 'input' &&\n\t\t\tthis.lastOrigin === 'input' &&\n\t\t\tnow - lastGroup.timestamp < this.groupTimeoutMs &&\n\t\t\tthis.isSameInputType(lastGroup.transactions, tr);\n\n\t\tif (shouldGroup && lastGroup) {\n\t\t\tthis.undoStack[this.undoStack.length - 1] = {\n\t\t\t\t...lastGroup,\n\t\t\t\ttransactions: [...lastGroup.transactions, tr],\n\t\t\t\ttimestamp: now,\n\t\t\t};\n\t\t} else {\n\t\t\tthis.undoStack.push({\n\t\t\t\ttransactions: [tr],\n\t\t\t\ttimestamp: now,\n\t\t\t\tinterveningMapping: Mapping.empty,\n\t\t\t});\n\t\t}\n\n\t\tthis.lastOrigin = tr.metadata.origin;\n\n\t\t// New user input invalidates the redo history.\n\t\tthis.redoStack = [];\n\n\t\twhile (this.undoStack.length > this.maxDepth) {\n\t\t\tthis.undoStack.shift();\n\t\t}\n\t}\n\n\t/**\n\t * Records an out-of-band transaction's mapping so subsequent undos *and*\n\t * redos fold their steps and restored selection through it. Use for\n\t * transactions applied to the editor without going through {@link push}\n\t * (e.g. collaboration or agent updates that the user is not supposed to\n\t * undo / redo). Empty mappings are a no-op.\n\t *\n\t * Propagates to **both** stacks so a `undo → intervening → redo` flow\n\t * is safe: the redo group's steps get rebased through the new mapping\n\t * just as the undo group's would have.\n\t */\n\trecordIntervening(mapping: Mapping): void {\n\t\tif (mapping.isEmpty) return;\n\t\textendInterveningMapping(this.undoStack, mapping);\n\t\textendInterveningMapping(this.redoStack, mapping);\n\t}\n\n\t/** Undoes the last group and returns the new state with transaction, or null if nothing to undo. */\n\tundo(state: EditorState): HistoryResult | null {\n\t\tconst group = this.undoStack.pop();\n\t\tif (!group) return null;\n\n\t\tconst replayed = rebaseGroupForReplay(group, state, 'undo');\n\t\tif (replayed === null) {\n\t\t\t// Group cannot survive intervening edits. Drop it entirely; don't\n\t\t\t// restore (state unchanged), don't push to redo (the inverse\n\t\t\t// sequence is invalid). Report nothing-to-undo so the caller's UX\n\t\t\t// reflects that the operation had no effect.\n\t\t\tthis.lastOrigin = null;\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.redoStack.push({\n\t\t\ttransactions: [replayed.transaction],\n\t\t\ttimestamp: Date.now(),\n\t\t\tinterveningMapping: Mapping.empty,\n\t\t});\n\t\tthis.lastOrigin = null;\n\t\treturn { state: replayed.newState, transaction: replayed.transaction };\n\t}\n\n\t/** Redoes the last undone group and returns the new state with transaction, or null if nothing to redo. */\n\tredo(state: EditorState): HistoryResult | null {\n\t\tconst group = this.redoStack.pop();\n\t\tif (!group) return null;\n\n\t\tconst replayed = rebaseGroupForReplay(group, state, 'redo');\n\t\tif (replayed === null) {\n\t\t\tthis.lastOrigin = null;\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.undoStack.push({\n\t\t\ttransactions: [replayed.transaction],\n\t\t\ttimestamp: Date.now(),\n\t\t\tinterveningMapping: Mapping.empty,\n\t\t});\n\t\tthis.lastOrigin = null;\n\t\treturn { state: replayed.newState, transaction: replayed.transaction };\n\t}\n\n\t/** Returns true if there are entries to undo. */\n\tcanUndo(): boolean {\n\t\treturn this.undoStack.length > 0;\n\t}\n\n\t/** Returns true if there are entries to redo. */\n\tcanRedo(): boolean {\n\t\treturn this.redoStack.length > 0;\n\t}\n\n\t/** Clears all history. */\n\tclear(): void {\n\t\tthis.undoStack = [];\n\t\tthis.redoStack = [];\n\t\tthis.lastOrigin = null;\n\t}\n\n\t/**\n\t * Checks if consecutive transactions are of the same \"type\" for grouping.\n\t * Groups text insertion together, deletion together, but not mixed.\n\t */\n\tprivate isSameInputType(existing: readonly Transaction[], incoming: Transaction): boolean {\n\t\tconst lastTr = existing[existing.length - 1];\n\t\tif (!lastTr) return false;\n\n\t\tconst lastStepType = lastTr.steps[0]?.type;\n\t\tconst incomingStepType = incoming.steps[0]?.type;\n\n\t\treturn lastStepType === incomingStepType;\n\t}\n}\n\n// --- Group replay (shared by undo and redo) ---\n\ninterface ReplayedGroup {\n\treadonly newState: EditorState;\n\treadonly transaction: Transaction;\n}\n\n/**\n * Replays a {@link HistoryGroup} against `state`, inverting every step and\n * rebasing each inverse through the group's accumulated `interveningMapping`\n * (plus the running rebase of the steps already applied in this replay).\n *\n * Used for both undo (where `group` holds the original user transactions)\n * and redo (where `group` holds the synthetic produced by the previous\n * undo): in either case we apply the *inverse* of the group's recorded\n * steps, frame-correctly rebased.\n *\n * Returns `null` when any inverse step cannot survive the rebase\n * ({@link mapStep} returned `null`); callers must abandon the entire group\n * rather than apply partial inverses, which would silently corrupt the\n * document.\n *\n * ## Frame-walk\n *\n * A multi-step transaction `tr` has steps `S_1…S_m` taking frames\n * `F_0 →(S_1) F_1 → … →(S_m) F_m`. Each inverse step `invert(S_k)`\n * references coordinates in frame `F_k`. When we replay them in reverse\n * order (`invert(S_m)`, `invert(S_{m-1})`, …), each inverse lives in a\n * progressively older frame, so the running rebase mapping has to be\n * extended on *both* sides at every iteration:\n *\n * - **prepend** the forward map of the original step we just inverted\n * (advances the source frame from `F_k` to `F_{k-1}` for the next\n * inverse);\n * - **append** the forward map of the rebased step we just applied\n * (advances the target frame to the new current document).\n *\n * The identity-filtered {@link Transaction.mapping} would lose the\n * step-to-index correlation, so we read forward maps from\n * {@link Transaction.forwardStepMaps}, which is parallel to `steps`.\n */\nfunction rebaseGroupForReplay(\n\tgroup: HistoryGroup,\n\tstate: EditorState,\n\tdirection: 'undo' | 'redo',\n): ReplayedGroup | null {\n\tlet rebaseMapping: Mapping = group.interveningMapping;\n\tlet currentDoc: Document = state.doc;\n\tconst rebasedSteps: Step[] = [];\n\tconst rebasedStepMaps: StepMap[] = [];\n\n\tfor (let i = group.transactions.length - 1; i >= 0; i--) {\n\t\tconst tr = group.transactions[i];\n\t\tif (!tr) continue;\n\n\t\tfor (let j = tr.steps.length - 1; j >= 0; j--) {\n\t\t\tconst originalStep = tr.steps[j];\n\t\t\tconst originalForwardMap = tr.forwardStepMaps[j];\n\t\t\tif (!originalStep || !originalForwardMap) continue;\n\n\t\t\tconst inverse: Step = invertStep(originalStep);\n\t\t\tconst rebased: Step | null = mapStep(inverse, rebaseMapping, currentDoc);\n\t\t\tif (rebased === null) return null;\n\n\t\t\tconst rebasedMap: StepMap = getStepMap(currentDoc, rebased);\n\t\t\trebasedSteps.push(rebased);\n\t\t\trebasedStepMaps.push(rebasedMap);\n\t\t\tcurrentDoc = applyStep(currentDoc, rebased);\n\n\t\t\trebaseMapping = extendRebaseChain(originalForwardMap, rebaseMapping, rebasedMap);\n\t\t}\n\t}\n\n\tconst storedMarksAfter: readonly Mark[] | null = deriveStoredMarksAfterReplay(group);\n\n\tconst summaryTr: Transaction = {\n\t\tsteps: rebasedSteps,\n\t\tselectionBefore: state.selection,\n\t\tselectionAfter: state.selection, // overwritten below with the mapped selection\n\t\tstoredMarksAfter,\n\t\tmapping: Mapping.from(rebasedStepMaps),\n\t\tforwardStepMaps: rebasedStepMaps,\n\t\tmetadata: {\n\t\t\torigin: 'history',\n\t\t\ttimestamp: Date.now(),\n\t\t\thistoryDirection: direction,\n\t\t},\n\t};\n\n\tlet newState: EditorState = state.apply(summaryTr);\n\n\t// `rebaseMapping` now spans `F_0` (pre-group) → current doc, exactly the\n\t// mapping the original group's pre-edit selection needs to land in the\n\t// post-replay frame.\n\tconst first = group.transactions[0];\n\tif (first) {\n\t\tconst mapped = mapSelection(first.selectionBefore, rebaseMapping);\n\t\tif (mapped !== null) {\n\t\t\tnewState = newState.withSelection(mapped);\n\t\t}\n\t}\n\n\tconst finalTr: Transaction = {\n\t\t...summaryTr,\n\t\tselectionAfter: newState.selection,\n\t};\n\n\treturn { newState, transaction: finalTr };\n}\n\n/**\n * Derives the storedMarks the document should carry after the group's\n * inverse has been applied. For undo of a user-pushed group this is the\n * pre-group storedMarks; for redo of a synthetic this is the pre-undo\n * storedMarks. Both fall out of {@link deriveStoredMarksBefore} applied to\n * the group's first transaction, because the synthetic's inverted\n * `setStoredMarks` step (if any) sits at the head of its step list.\n */\nfunction deriveStoredMarksAfterReplay(group: HistoryGroup): readonly Mark[] | null {\n\tconst first = group.transactions[0];\n\tif (!first) return null;\n\treturn deriveStoredMarksBefore(first);\n}\n\n/**\n * Extends the rebase chain by prepending `originalForwardMap` and appending\n * `rebasedMap` to `existing`. When `existing` is empty AND\n * `originalForwardMap` and `rebasedMap` are mutual inverses (i.e. the rebase\n * was a no-op — the rebased step is the literal inverse of the original),\n * the prepend/append cancel out and we return `existing` unchanged.\n *\n * Why this matters: the natural composition `[forward, inverse]` describes\n * a content-preserving round-trip but is *not* a position-mapping identity\n * (positions interior to a delete-then-reinsert clamp to the deletion\n * start). Without cancellation, the rebase chain accumulates these lossy\n * round-trips on every multi-step group and corrupts subsequent inverse\n * steps' coordinates. With cancellation, the chain stays empty whenever no\n * real intervening edit reshaped the rebased step, which is the common\n * case.\n *\n * Cancellation only fires when nothing sits between the two stepmaps —\n * once an intervening map is in the middle, the round-trip is no longer\n * \"no-op\" because the intervening could have reshaped positions inside the\n * affected range.\n */\nfunction extendRebaseChain(\n\toriginalForwardMap: StepMap,\n\texisting: Mapping,\n\trebasedMap: StepMap,\n): Mapping {\n\tif (existing.isEmpty && stepMapsEqual(invertStepMap(originalForwardMap), rebasedMap)) {\n\t\treturn existing;\n\t}\n\treturn Mapping.from([originalForwardMap]).appendMapping(existing).appendMap(rebasedMap);\n}\n\nfunction extendInterveningMapping(stack: HistoryGroup[], mapping: Mapping): void {\n\tfor (let i = 0; i < stack.length; i++) {\n\t\tconst g = stack[i];\n\t\tif (!g) continue;\n\t\tstack[i] = {\n\t\t\t...g,\n\t\t\tinterveningMapping: g.interveningMapping.appendMapping(mapping),\n\t\t};\n\t}\n}\n","/**\n * Pure guard functions for readonly mode enforcement.\n * Used by the central dispatch guard to determine whether a transaction\n * should be allowed in readonly mode.\n */\n\nimport type { Transaction } from './Transaction.js';\n\n/** Returns true if the transaction contains no document-mutating steps. */\nexport function isSelectionOnlyTransaction(tr: Transaction): boolean {\n\tfor (const step of tr.steps) {\n\t\tif (step.type !== 'setStoredMarks') return false;\n\t}\n\treturn true;\n}\n\n/** Returns true if the transaction is allowed to proceed in readonly mode. */\nexport function isAllowedInReadonly(tr: Transaction): boolean {\n\tif (tr.metadata.readonlyAllowed) return true;\n\treturn isSelectionOnlyTransaction(tr);\n}\n","/**\n * Shared mark rendering utilities used by Reconciler and CursorWrapper.\n *\n * Provides mark rank ordering and DOM element creation for marks,\n * using the SchemaRegistry when available and falling back to\n * built-in defaults.\n */\n\nimport type { MarkAttrsFor } from '../model/AttrRegistry.js';\nimport type { Mark } from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\n\n/** Returns the rank for a mark (lower = outermost in the DOM nesting). */\nexport function getMarkRank(mark: Mark, registry?: SchemaRegistry): number {\n\tif (registry) {\n\t\tconst spec = registry.getMarkSpec(mark.type);\n\t\tif (spec) return spec.rank ?? 100;\n\t}\n\tswitch (mark.type) {\n\t\tcase 'bold':\n\t\t\treturn 0;\n\t\tcase 'italic':\n\t\t\treturn 1;\n\t\tcase 'underline':\n\t\t\treturn 2;\n\t\tdefault:\n\t\t\treturn 100;\n\t}\n}\n\n/**\n * Wraps an inner DOM node with mark elements from inside-out.\n * Marks are sorted by rank (lower = outermost) before wrapping.\n */\nexport function wrapNodeWithMarks(\n\tinnerNode: Node,\n\tmarks: readonly Mark[],\n\tregistry?: SchemaRegistry,\n): Node {\n\tif (marks.length === 0) return innerNode;\n\n\tconst sortedMarks: readonly Mark[] = [...marks].sort(\n\t\t(a, b) => getMarkRank(a, registry) - getMarkRank(b, registry),\n\t);\n\n\tlet current: Node = innerNode;\n\tfor (let i: number = sortedMarks.length - 1; i >= 0; i--) {\n\t\tconst mark: Mark | undefined = sortedMarks[i];\n\t\tif (!mark) continue;\n\t\tconst el: HTMLElement = createMarkElement(mark, registry);\n\t\tel.appendChild(current);\n\t\tcurrent = el;\n\t}\n\treturn current;\n}\n\n/** Creates a DOM element for a mark, using the registry spec or a built-in fallback. */\nexport function createMarkElement(mark: Mark, registry?: SchemaRegistry): HTMLElement {\n\tif (registry) {\n\t\tconst spec = registry.getMarkSpec(mark.type);\n\t\tif (spec) {\n\t\t\treturn spec.toDOM(mark as Omit<Mark, 'attrs'> & { readonly attrs: MarkAttrsFor<string> });\n\t\t}\n\t}\n\tswitch (mark.type) {\n\t\tcase 'bold':\n\t\t\treturn document.createElement('strong');\n\t\tcase 'italic':\n\t\t\treturn document.createElement('em');\n\t\tcase 'underline':\n\t\t\treturn document.createElement('u');\n\t\tdefault:\n\t\t\treturn document.createElement('span');\n\t}\n}\n","/**\n * CursorWrapper: creates a temporary mark-wrapped DOM element during IME\n * composition so that composed text inherits the stored marks (e.g. bold).\n *\n * Without this, toggling Bold with a collapsed cursor and then starting an\n * IME composition would produce unformatted text because the browser has no\n * formatted DOM node to compose into.\n *\n * The wrapper is a `<span data-cursor-wrapper>` containing a zero-width space\n * (`\\u200B`), nested inside mark elements matching the stored marks.\n */\n\nimport type { Mark } from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { isCollapsed, isTextSelection } from '../model/Selection.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { wrapNodeWithMarks } from './MarkRendering.js';\nimport { getSelection, readComposedSelection } from './SelectionSync.js';\n\nconst ZWS = '\\u200B';\nconst CURSOR_WRAPPER_ATTR = 'data-cursor-wrapper';\n\nexport class CursorWrapper {\n\tprivate wrapperElement: HTMLElement | null = null;\n\tprivate readonly container: HTMLElement;\n\tprivate readonly registry: SchemaRegistry | undefined;\n\n\tconstructor(container: HTMLElement, registry?: SchemaRegistry) {\n\t\tthis.container = container;\n\t\tthis.registry = registry;\n\t}\n\n\t/** Whether a cursor wrapper is currently in the DOM. */\n\tget isActive(): boolean {\n\t\treturn this.wrapperElement !== null;\n\t}\n\n\t/**\n\t * Creates a mark-wrapped cursor element if storedMarks are present.\n\t * Places the browser cursor inside the ZWS text node so that IME\n\t * composition inherits the mark formatting.\n\t */\n\tonCompositionStart(state: EditorState): void {\n\t\tthis.cleanup();\n\n\t\tconst marks: readonly Mark[] | null = state.storedMarks;\n\t\tif (!marks || marks.length === 0) return;\n\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel)) return;\n\t\tif (!isCollapsed(sel)) return;\n\n\t\t// Build the wrapper: <span data-cursor-wrapper>ZWS</span>\n\t\tconst wrapper: HTMLElement = document.createElement('span');\n\t\twrapper.setAttribute(CURSOR_WRAPPER_ATTR, '');\n\t\tconst textNode: Text = document.createTextNode(ZWS);\n\n\t\t// Wrap the text node in sorted mark elements (innermost → outermost)\n\t\tconst wrapped: Node = wrapNodeWithMarks(textNode, marks, this.registry);\n\t\twrapper.appendChild(wrapped);\n\n\t\t// Insert at the current DOM cursor position\n\t\tconst domSel: globalThis.Selection | null = getSelection();\n\t\tif (!domSel || domSel.rangeCount === 0) return;\n\n\t\t// Use getComposedRanges in Shadow DOM for correct range\n\t\tlet range: Range;\n\t\tconst composed = readComposedSelection(this.container, domSel);\n\t\tif (composed) {\n\t\t\tconst r = document.createRange();\n\t\t\ttry {\n\t\t\t\tr.setStart(composed.anchorNode, composed.anchorOffset);\n\t\t\t\tr.setEnd(composed.focusNode, composed.focusOffset);\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trange = r;\n\t\t} else {\n\t\t\trange = domSel.getRangeAt(0);\n\t\t}\n\t\trange.insertNode(wrapper);\n\n\t\t// Place browser cursor inside the ZWS text node\n\t\ttry {\n\t\t\tdomSel.collapse(textNode, textNode.length);\n\t\t} catch {\n\t\t\t// May fail in test environments\n\t\t}\n\n\t\tthis.wrapperElement = wrapper;\n\t}\n\n\t/** Removes the wrapper element from the DOM. */\n\tcleanup(): void {\n\t\tif (!this.wrapperElement) return;\n\t\tthis.wrapperElement.remove();\n\t\tthis.wrapperElement = null;\n\t}\n}\n","/**\n * EditorViewEvents: manages DOM event binding and cleanup for the\n * editor view.\n *\n * Handles selection changes, mousedown (NodeSelection,\n * click-below-content), drag-and-drop, and composition events\n * for the CursorWrapper.\n */\n\nimport type { CompositionState } from '../model/CompositionState.js';\nimport { createEmptyParagraph, generateBlockId, getBlockLength } from '../model/Document.js';\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { Position } from '../model/Selection.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisGapCursor,\n\tisNodeSelection,\n\tselectionsEqual,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\nimport {\n\tisEventFromEditorContent,\n\tisNodeFromEditorContent,\n} from '../platform/EditorEventBoundary.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport type { CursorWrapper } from './CursorWrapper.js';\nimport { domPositionFromPoint } from './DomPointUtils.js';\nimport { buildBlockPath } from './DomUtils.js';\nimport { domPositionToState, readSelectionFromDOM } from './SelectionSync.js';\n\nexport interface EventCoordinatorDeps {\n\treadonly contentElement: HTMLElement;\n\treadonly getState: () => EditorState;\n\treadonly dispatch: (tr: Transaction) => void;\n\treadonly applyUpdate: (\n\t\tnewState: EditorState,\n\t\ttr: Transaction,\n\t\tskipSelectionSync?: boolean,\n\t) => void;\n\treadonly isUpdating: () => boolean;\n\treadonly compositionState: CompositionState;\n\treadonly cursorWrapper: CursorWrapper;\n\treadonly isReadOnly: () => boolean;\n\treadonly fileHandlerRegistry?: FileHandlerRegistry;\n\treadonly onMousedown?: () => void;\n}\n\nexport class EditorViewEvents {\n\tprivate readonly deps: EventCoordinatorDeps;\n\tprivate readonly handleSelectionChange: () => void;\n\tprivate readonly handleMousedown: (e: MouseEvent) => void;\n\tprivate readonly handleDragover: (e: DragEvent) => void;\n\tprivate readonly handleDrop: (e: DragEvent) => void;\n\tprivate readonly handleCompositionStart: (event: CompositionEvent) => void;\n\tprivate readonly handleCompositionEnd: (event: CompositionEvent) => void;\n\tprivate pendingNodeSelectionClear = false;\n\tprivate pendingGapCursorClear = false;\n\n\tconstructor(deps: EventCoordinatorDeps) {\n\t\tthis.deps = deps;\n\n\t\tthis.handleCompositionStart = (event: CompositionEvent) => {\n\t\t\tif (!isEventFromEditorContent(event, deps.contentElement)) return;\n\t\t\tdeps.cursorWrapper.onCompositionStart(deps.getState());\n\t\t};\n\t\tthis.handleCompositionEnd = (event: CompositionEvent) => {\n\t\t\tif (!isEventFromEditorContent(event, deps.contentElement)) return;\n\t\t\tdeps.cursorWrapper.cleanup();\n\t\t};\n\t\tdeps.contentElement.addEventListener('compositionstart', this.handleCompositionStart);\n\t\tdeps.contentElement.addEventListener('compositionend', this.handleCompositionEnd);\n\n\t\tthis.handleSelectionChange = this.onSelectionChange.bind(this);\n\t\tdocument.addEventListener('selectionchange', this.handleSelectionChange);\n\n\t\tthis.handleMousedown = this.onMousedown.bind(this);\n\t\tdeps.contentElement.addEventListener('mousedown', this.handleMousedown);\n\n\t\tthis.handleDragover = this.onDragover.bind(this);\n\t\tdeps.contentElement.addEventListener('dragover', this.handleDragover);\n\n\t\tthis.handleDrop = this.onDrop.bind(this);\n\t\tdeps.contentElement.addEventListener('drop', this.handleDrop);\n\t}\n\n\t/** Syncs the DOM selection into editor state. */\n\tsyncSelectionFromDOM(): void {\n\t\tif (this.deps.compositionState.isComposing) return;\n\n\t\tif (this.deps.cursorWrapper.isActive) {\n\t\t\tthis.deps.cursorWrapper.cleanup();\n\t\t}\n\n\t\tconst state: EditorState = this.deps.getState();\n\n\t\tif (isNodeSelection(state.selection) && !this.pendingNodeSelectionClear) {\n\t\t\treturn;\n\t\t}\n\t\tif (isGapCursor(state.selection) && !this.pendingGapCursorClear) {\n\t\t\treturn;\n\t\t}\n\t\tthis.pendingNodeSelectionClear = false;\n\t\tthis.pendingGapCursorClear = false;\n\n\t\tconst sel = readSelectionFromDOM(this.deps.contentElement);\n\t\tif (!sel) return;\n\n\t\tif (selectionsEqual(sel, state.selection)) return;\n\n\t\tconst tr: Transaction = state\n\t\t\t.transaction('input')\n\t\t\t.setSelection(sel)\n\t\t\t.setStoredMarks(null, state.storedMarks)\n\t\t\t.build();\n\n\t\tconst newState: EditorState = state.apply(tr);\n\t\t// Skip syncSelectionToDOM — the DOM is the source of truth here.\n\t\t// Re-writing the selection via setBaseAndExtent() would disrupt\n\t\t// the browser's native drag-selection and double-click tracking.\n\t\tthis.deps.applyUpdate(newState, tr, true);\n\t}\n\n\t/** Removes all event listeners. */\n\tdestroy(): void {\n\t\tconst el: HTMLElement = this.deps.contentElement;\n\t\tel.removeEventListener('compositionstart', this.handleCompositionStart);\n\t\tel.removeEventListener('compositionend', this.handleCompositionEnd);\n\t\tdocument.removeEventListener('selectionchange', this.handleSelectionChange);\n\t\tel.removeEventListener('mousedown', this.handleMousedown);\n\t\tel.removeEventListener('dragover', this.handleDragover);\n\t\tel.removeEventListener('drop', this.handleDrop);\n\t}\n\n\t/** Handles DOM selection changes (clicks, arrow keys). */\n\tprivate onSelectionChange(): void {\n\t\tif (this.deps.isUpdating()) return;\n\n\t\tconst shadowRoot = this.deps.contentElement.getRootNode() as ShadowRoot | Document;\n\t\tconst activeEl =\n\t\t\t'activeElement' in shadowRoot ? shadowRoot.activeElement : document.activeElement;\n\n\t\tif (!this.deps.contentElement.contains(activeEl) && activeEl !== this.deps.contentElement) {\n\t\t\treturn;\n\t\t}\n\t\tif (!isNodeFromEditorContent(activeEl, this.deps.contentElement)) return;\n\n\t\tthis.syncSelectionFromDOM();\n\t}\n\n\t/** Handles mousedown on selectable/void blocks. */\n\tprivate onMousedown(e: MouseEvent): void {\n\t\tif (this.deps.isUpdating()) return;\n\t\tif (!isEventFromEditorContent(e, this.deps.contentElement)) return;\n\t\tthis.deps.onMousedown?.();\n\n\t\tconst target = e.target;\n\t\tif (!(target instanceof HTMLElement)) return;\n\n\t\tconst nearestBlockEl = target.closest('[data-block-id]');\n\n\t\tif (\n\t\t\t!(nearestBlockEl instanceof HTMLElement) ||\n\t\t\t!this.deps.contentElement.contains(nearestBlockEl)\n\t\t) {\n\t\t\tif (this.deps.contentElement.contains(target) || target === this.deps.contentElement) {\n\t\t\t\tthis.handleClickBelowContent(e);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst isNodeSelectable: boolean =\n\t\t\tnearestBlockEl.hasAttribute('data-void') || nearestBlockEl.hasAttribute('data-selectable');\n\n\t\tif (!isNodeSelectable) {\n\t\t\tthis.markPendingSelectionClear();\n\t\t\treturn;\n\t\t}\n\n\t\tconst contentDOM: Element | null = nearestBlockEl.querySelector('[data-content-dom]');\n\t\tif (contentDOM?.contains(target)) {\n\t\t\tthis.markPendingSelectionClear();\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tthis.deps.contentElement.focus();\n\n\t\tconst bid: BlockId = toBlockId(nearestBlockEl.getAttribute('data-block-id') ?? '');\n\t\tconst path: readonly BlockId[] = buildBlockPath(this.deps.contentElement, nearestBlockEl);\n\t\tconst sel = createNodeSelection(bid, path);\n\n\t\tconst state: EditorState = this.deps.getState();\n\t\tconst tr: Transaction = state\n\t\t\t.transaction('input')\n\t\t\t.setSelection(sel)\n\t\t\t.setStoredMarks(null, state.storedMarks)\n\t\t\t.build();\n\n\t\tconst newState: EditorState = state.apply(tr);\n\t\tthis.deps.applyUpdate(newState, tr);\n\t}\n\n\t/**\n\t * When the user clicks below all rendered blocks, appends a\n\t * new paragraph after the last block and focuses it.\n\t */\n\tprivate handleClickBelowContent(e: MouseEvent): void {\n\t\tif (this.deps.isReadOnly()) return;\n\n\t\tconst state: EditorState = this.deps.getState();\n\t\tconst lastRoot = state.doc.children[state.doc.children.length - 1];\n\t\tif (!lastRoot) return;\n\t\tconst lastBlockId: BlockId = lastRoot.id;\n\n\t\tconst lastBlockEl: Element | null = this.deps.contentElement.querySelector(\n\t\t\t`[data-block-id=\"${lastBlockId}\"]`,\n\t\t);\n\t\tif (!lastBlockEl) return;\n\n\t\tconst lastRect: DOMRect = lastBlockEl.getBoundingClientRect();\n\t\tif (e.clientY <= lastRect.bottom) return;\n\n\t\tconst lastBlock = state.getBlock(lastBlockId);\n\t\tif (lastBlock?.type === 'paragraph' && getBlockLength(lastBlock) === 0) {\n\t\t\tconst tr: Transaction = state\n\t\t\t\t.transaction('input')\n\t\t\t\t.setSelection(createCollapsedSelection(lastBlockId, 0))\n\t\t\t\t.build();\n\t\t\tthis.deps.dispatch(tr);\n\t\t\te.preventDefault();\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tthis.deps.contentElement.focus();\n\n\t\tconst newId: BlockId = generateBlockId();\n\t\tconst tr: Transaction = state\n\t\t\t.transaction('input')\n\t\t\t.insertNode([], state.doc.children.length, createEmptyParagraph(newId))\n\t\t\t.setSelection(createCollapsedSelection(newId, 0))\n\t\t\t.build();\n\n\t\tthis.deps.dispatch(tr);\n\t}\n\n\t/** Allows file drop by preventing default on dragover. */\n\tprivate onDragover(e: DragEvent): void {\n\t\tif (!this.deps.fileHandlerRegistry) return;\n\t\tif (!e.dataTransfer) return;\n\t\tif (e.dataTransfer.types.includes('Files')) {\n\t\t\te.preventDefault();\n\t\t}\n\t}\n\n\t/** Handles file drop via registered file handlers. */\n\tprivate onDrop(e: DragEvent): void {\n\t\tif (this.deps.isReadOnly()) return;\n\t\tif (!this.deps.fileHandlerRegistry) return;\n\t\tif (!e.dataTransfer) return;\n\n\t\tconst files: File[] = Array.from(e.dataTransfer.files);\n\t\tif (files.length === 0) return;\n\n\t\tconst position: Position | null = this.getPositionFromPoint(e.clientX, e.clientY);\n\n\t\tlet handled = false;\n\t\tfor (const file of files) {\n\t\t\tconst handlers = this.deps.fileHandlerRegistry.matchFileHandlers(file.type);\n\t\t\tfor (const handler of handlers) {\n\t\t\t\tconst result = handler(file, position);\n\t\t\t\tif (result === true || result instanceof Promise) {\n\t\t\t\t\thandled = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (handled) {\n\t\t\te.preventDefault();\n\t\t}\n\t}\n\n\t/** Converts screen coordinates to an editor Position. */\n\tprivate getPositionFromPoint(x: number, y: number): Position | null {\n\t\tconst root = this.deps.contentElement.getRootNode() as Document | ShadowRoot;\n\t\tconst domPoint = domPositionFromPoint(root, x, y);\n\t\tif (!domPoint) return null;\n\t\tif (!this.deps.contentElement.contains(domPoint.node)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn domPositionToState(this.deps.contentElement, domPoint.node, domPoint.offset);\n\t}\n\n\t/**\n\t * Marks pending clear flags when clicking non-selectable blocks\n\t * while NodeSelection or GapCursor is active.\n\t */\n\tprivate markPendingSelectionClear(): void {\n\t\tconst state: EditorState = this.deps.getState();\n\t\tif (isNodeSelection(state.selection)) {\n\t\t\tthis.pendingNodeSelectionClear = true;\n\t\t}\n\t\tif (isGapCursor(state.selection)) {\n\t\t\tthis.pendingGapCursorClear = true;\n\t\t}\n\t}\n}\n","/**\n * EditorViewNavigation: handles arrow-key navigation including\n * cross-block movement, InlineNode skipping, and vertical goalColumn.\n */\n\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport {\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n\tisTextSelection,\n\tselectionsEqual,\n} from '../model/Selection.js';\nimport { getTextDirection } from '../platform/Platform.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport {\n\ttype CaretDirection,\n\tendOfTextblock,\n\tgetCaretRectFromSelection,\n\tnavigateAcrossBlocks,\n\tnavigateVerticalWithGoalColumn,\n\tskipInlineNode,\n} from './CaretNavigation.js';\nimport { readSelectionFromDOM, syncSelectionToDOM } from './SelectionSync.js';\n\nexport interface NavigationHandlerDeps {\n\treadonly contentElement: HTMLElement;\n\treadonly getState: () => EditorState;\n\treadonly dispatch: (tr: Transaction) => void;\n\treadonly keymapRegistry?: KeymapRegistry;\n}\n\nexport class EditorViewNavigation {\n\tprivate readonly deps: NavigationHandlerDeps;\n\tprivate navigationKeymap: Record<string, () => boolean> | null = null;\n\tprivate goalColumn: number | null = null;\n\tprivate preserveGoalColumn = false;\n\n\tconstructor(deps: NavigationHandlerDeps) {\n\t\tthis.deps = deps;\n\t\tthis.registerKeymaps();\n\t}\n\n\t/**\n\t * Resets goalColumn after an update unless vertical navigation\n\t * is in progress.\n\t */\n\tresetAfterUpdate(): void {\n\t\tif (!this.preserveGoalColumn) {\n\t\t\tthis.goalColumn = null;\n\t\t}\n\t}\n\n\t/** Clears goalColumn unconditionally (e.g. on mousedown). */\n\tclearGoalColumn(): void {\n\t\tthis.goalColumn = null;\n\t}\n\n\t/** Removes navigation keymaps from the registry. */\n\tdestroy(): void {\n\t\tif (this.navigationKeymap && this.deps.keymapRegistry) {\n\t\t\tthis.deps.keymapRegistry.removeKeymap(this.navigationKeymap);\n\t\t\tthis.navigationKeymap = null;\n\t\t}\n\t}\n\n\t/** Registers arrow-key keymaps with navigation priority. */\n\tprivate registerKeymaps(): void {\n\t\tif (!this.deps.keymapRegistry) return;\n\n\t\tconst directions: Record<string, CaretDirection> = {\n\t\t\tArrowLeft: 'left',\n\t\t\tArrowRight: 'right',\n\t\t\tArrowUp: 'up',\n\t\t\tArrowDown: 'down',\n\t\t};\n\n\t\tconst keymap: Record<string, () => boolean> = {};\n\t\tfor (const [key, dir] of Object.entries(directions)) {\n\t\t\tkeymap[key] = () => this.handleArrow(dir);\n\t\t}\n\n\t\tthis.navigationKeymap = keymap;\n\t\tthis.deps.keymapRegistry.registerKeymap(keymap, {\n\t\t\tpriority: 'navigation',\n\t\t});\n\t}\n\n\t/** Handles a navigation arrow key at textblock boundaries. */\n\tprivate handleArrow(direction: CaretDirection): boolean {\n\t\tconst state: EditorState = this.deps.getState();\n\n\t\tif (isNodeSelection(state.selection)) return false;\n\t\tif (isGapCursor(state.selection)) return false;\n\t\tif (!isCollapsed(state.selection)) return false;\n\n\t\tif (direction === 'left' || direction === 'right') {\n\t\t\treturn this.handleHorizontalArrow(direction);\n\t\t}\n\t\treturn this.handleVerticalArrow(direction);\n\t}\n\n\t/** Handles horizontal arrow: InlineNode skip + cross-block. */\n\tprivate handleHorizontalArrow(direction: 'left' | 'right'): boolean {\n\t\tthis.goalColumn = null;\n\t\tconst state: EditorState = this.deps.getState();\n\t\tconst logical: 'left' | 'right' = this.resolveLogicalDirection(state, direction);\n\n\t\tconst inlineSkipTr: Transaction | null = skipInlineNode(state, logical);\n\t\tif (inlineSkipTr) {\n\t\t\tthis.deps.dispatch(inlineSkipTr);\n\t\t\tthis.validateSelectionAfterInlineSkip();\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!endOfTextblock(this.deps.contentElement, state, direction)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst tr: Transaction | null = navigateAcrossBlocks(state, logical);\n\t\tif (tr) {\n\t\t\tthis.deps.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Maps visual horizontal arrow to logical movement direction\n\t * in offset space for the current block direction.\n\t */\n\tprivate resolveLogicalDirection(state: EditorState, visual: 'left' | 'right'): 'left' | 'right' {\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel)) return visual;\n\n\t\tconst blockEl = this.deps.contentElement.querySelector(\n\t\t\t`[data-block-id=\"${sel.anchor.blockId}\"]`,\n\t\t);\n\t\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\n\t\tif (!isRtl) return visual;\n\t\treturn visual === 'left' ? 'right' : 'left';\n\t}\n\n\t/** Handles vertical arrow with goalColumn preservation. */\n\tprivate handleVerticalArrow(direction: 'up' | 'down'): boolean {\n\t\tconst state: EditorState = this.deps.getState();\n\t\tconst domSel: globalThis.Selection | null =\n\t\t\tthis.deps.contentElement.ownerDocument.getSelection?.() ?? null;\n\t\tconst caretRect: DOMRect | null = domSel\n\t\t\t? getCaretRectFromSelection(domSel, this.deps.contentElement)\n\t\t\t: null;\n\n\t\tif (!endOfTextblock(this.deps.contentElement, state, direction, caretRect)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.goalColumn === null && caretRect) {\n\t\t\tthis.goalColumn = caretRect.left;\n\t\t}\n\n\t\tthis.preserveGoalColumn = true;\n\t\tconst tr: Transaction | null = navigateVerticalWithGoalColumn(\n\t\t\tthis.deps.contentElement,\n\t\t\tstate,\n\t\t\tdirection,\n\t\t\tthis.goalColumn,\n\t\t);\n\n\t\tif (tr) {\n\t\t\tthis.deps.dispatch(tr);\n\t\t\tthis.preserveGoalColumn = false;\n\t\t\treturn true;\n\t\t}\n\t\tthis.preserveGoalColumn = false;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Validates DOM cursor after an InlineNode skip.\n\t *\n\t * Chrome can place the cursor inside a `contenteditable=\"false\"`\n\t * element (phantom position), and Firefox may lose the cursor.\n\t * This read-back roundtrip detects drift and forces re-sync.\n\t */\n\tprivate validateSelectionAfterInlineSkip(): void {\n\t\tconst state: EditorState = this.deps.getState();\n\t\tconst domSel = readSelectionFromDOM(this.deps.contentElement);\n\n\t\tif (!domSel) {\n\t\t\tsyncSelectionToDOM(this.deps.contentElement, state.selection);\n\t\t\treturn;\n\t\t}\n\t\tif (!selectionsEqual(domSel, state.selection)) {\n\t\t\tsyncSelectionToDOM(this.deps.contentElement, state.selection);\n\t\t}\n\t}\n}\n","/**\n * Block wrapper management: grouping consecutive blocks into shared\n * wrapper elements (e.g. `<ul>`, `<ol>` for list items).\n *\n * Provides both initial wrapping (`wrapBlocks`) and incremental\n * reconciliation (`reconcileWrappers`) that avoids unnecessary DOM\n * mutations when the wrapper structure is unchanged.\n *\n * No dependencies on other view modules — works purely with DOM and model types.\n */\n\nimport type { BlockNode } from '../model/Document.js';\nimport type { WrapperSpec } from '../model/NodeSpec.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A group of consecutive blocks sharing a wrapper. */\ninterface WrapperGroup {\n\treadonly spec: WrapperSpec;\n\treadonly blockIds: BlockId[];\n}\n\n/** An entry in the top-level container sequence: either a wrapper or a bare block. */\ninterface WrapperEntry {\n\treadonly type: 'wrapper';\n\treadonly key: string;\n\treadonly blockIds: readonly BlockId[];\n}\n\ninterface BareEntry {\n\treadonly type: 'bare';\n\treadonly blockId: BlockId;\n}\n\ntype TopLevelEntry = WrapperEntry | BareEntry;\n\n/** A wrapper entry read from the current DOM, including its element reference. */\ninterface DomWrapperEntry {\n\treadonly type: 'wrapper';\n\treadonly element: HTMLElement;\n\treadonly key: string;\n\treadonly blockIds: readonly BlockId[];\n}\n\ninterface DomBareEntry {\n\treadonly type: 'bare';\n\treadonly element: HTMLElement;\n\treadonly blockId: BlockId;\n}\n\ntype DomTopLevelEntry = DomWrapperEntry | DomBareEntry;\n\n// ---------------------------------------------------------------------------\n// Pure model computation\n// ---------------------------------------------------------------------------\n\n/** Computes wrapper groups from the block model. */\nfunction computeWrapperGroups(\n\tblocks: readonly BlockNode[],\n\tregistry: SchemaRegistry,\n): readonly WrapperGroup[] {\n\tconst groups: WrapperGroup[] = [];\n\tlet currentSpec: WrapperSpec | null = null;\n\tlet currentIds: BlockId[] = [];\n\n\tfor (const block of blocks) {\n\t\tconst nodeSpec = registry.getNodeSpec(block.type);\n\t\tconst wSpec: WrapperSpec | undefined = nodeSpec?.wrapper?.(block as never);\n\n\t\tif (wSpec && currentSpec && wSpec.key === currentSpec.key) {\n\t\t\tcurrentIds.push(block.id);\n\t\t} else if (wSpec) {\n\t\t\tif (currentSpec && currentIds.length > 0) {\n\t\t\t\tgroups.push({ spec: currentSpec, blockIds: currentIds });\n\t\t\t}\n\t\t\tcurrentSpec = wSpec;\n\t\t\tcurrentIds = [block.id];\n\t\t} else {\n\t\t\tif (currentSpec && currentIds.length > 0) {\n\t\t\t\tgroups.push({ spec: currentSpec, blockIds: currentIds });\n\t\t\t}\n\t\t\tcurrentSpec = null;\n\t\t\tcurrentIds = [];\n\t\t}\n\t}\n\tif (currentSpec && currentIds.length > 0) {\n\t\tgroups.push({ spec: currentSpec, blockIds: currentIds });\n\t}\n\n\treturn groups;\n}\n\n/** Builds the desired top-level sequence from blocks and their wrapper groups. */\nfunction computeDesiredSequence(\n\tblocks: readonly BlockNode[],\n\tgroups: readonly WrapperGroup[],\n): readonly TopLevelEntry[] {\n\tconst sequence: TopLevelEntry[] = [];\n\n\t// Build a set of block IDs that belong to wrapper groups\n\tconst wrappedIds = new Set<BlockId>();\n\tfor (const group of groups) {\n\t\tfor (const id of group.blockIds) {\n\t\t\twrappedIds.add(id);\n\t\t}\n\t}\n\n\tlet groupIdx = 0;\n\tfor (const block of blocks) {\n\t\tif (!wrappedIds.has(block.id)) {\n\t\t\tsequence.push({ type: 'bare', blockId: block.id });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check if this block starts a wrapper group\n\t\tconst group: WrapperGroup | undefined = groups[groupIdx];\n\t\tif (group && group.blockIds[0] === block.id) {\n\t\t\tsequence.push({\n\t\t\t\ttype: 'wrapper',\n\t\t\t\tkey: group.spec.key,\n\t\t\t\tblockIds: group.blockIds,\n\t\t\t});\n\t\t\tgroupIdx++;\n\t\t}\n\t\t// Otherwise, this block is part of the current group (already emitted)\n\t}\n\n\treturn sequence;\n}\n\n// ---------------------------------------------------------------------------\n// DOM reading\n// ---------------------------------------------------------------------------\n\n/** Reads the current top-level structure from the DOM. */\nfunction readDomSequence(container: HTMLElement): readonly DomTopLevelEntry[] {\n\tconst sequence: DomTopLevelEntry[] = [];\n\n\tfor (const child of Array.from(container.children)) {\n\t\tif (!(child instanceof HTMLElement)) continue;\n\n\t\tif (child.hasAttribute('data-block-wrapper')) {\n\t\t\tconst key: string = child.getAttribute('data-block-wrapper') ?? '';\n\t\t\tconst blockIds: BlockId[] = [];\n\t\t\tfor (const inner of Array.from(child.children)) {\n\t\t\t\tif (!(inner instanceof HTMLElement)) continue;\n\t\t\t\tconst bid: string | null = inner.getAttribute('data-block-id');\n\t\t\t\tif (bid) blockIds.push(toBlockId(bid));\n\t\t\t}\n\t\t\tsequence.push({ type: 'wrapper', element: child, key, blockIds });\n\t\t} else if (child.hasAttribute('data-block-id')) {\n\t\t\tconst bid: string = child.getAttribute('data-block-id') ?? '';\n\t\t\tsequence.push({ type: 'bare', element: child, blockId: toBlockId(bid) });\n\t\t}\n\t}\n\n\treturn sequence;\n}\n\n// ---------------------------------------------------------------------------\n// Fast-path comparison\n// ---------------------------------------------------------------------------\n\n/** Returns true when the desired and actual sequences are structurally identical. */\nfunction sequencesMatch(\n\tdesired: readonly TopLevelEntry[],\n\tactual: readonly DomTopLevelEntry[],\n): boolean {\n\tif (desired.length !== actual.length) return false;\n\n\tfor (let i = 0; i < desired.length; i++) {\n\t\tconst d: TopLevelEntry | undefined = desired[i];\n\t\tconst a: DomTopLevelEntry | undefined = actual[i];\n\t\tif (!d || !a) return false;\n\n\t\tif (d.type !== a.type) return false;\n\n\t\tif (d.type === 'bare' && a.type === 'bare') {\n\t\t\tif (d.blockId !== a.blockId) return false;\n\t\t} else if (d.type === 'wrapper' && a.type === 'wrapper') {\n\t\t\tif (d.key !== a.key) return false;\n\t\t\tif (d.blockIds.length !== a.blockIds.length) return false;\n\t\t\tfor (let j = 0; j < d.blockIds.length; j++) {\n\t\t\t\tif (d.blockIds[j] !== a.blockIds[j]) return false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// Wrapper attribute sync\n// ---------------------------------------------------------------------------\n\n/** Syncs wrapper element attributes to match the spec, only touching what differs. */\nfunction syncWrapperAttributes(element: HTMLElement, spec: WrapperSpec): void {\n\tif (spec.className && element.className !== spec.className) {\n\t\telement.className = spec.className;\n\t}\n\tif (spec.attrs) {\n\t\tfor (const [key, value] of Object.entries(spec.attrs)) {\n\t\t\tif (element.getAttribute(key) !== value) {\n\t\t\t\telement.setAttribute(key, value);\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Full reconciliation helpers\n// ---------------------------------------------------------------------------\n\n/** Creates a new wrapper DOM element from a WrapperSpec. */\nfunction createWrapperElement(spec: WrapperSpec): HTMLElement {\n\tconst wrapper: HTMLElement = document.createElement(spec.tag);\n\twrapper.setAttribute('data-block-wrapper', spec.key);\n\tif (spec.className) {\n\t\twrapper.className = spec.className;\n\t}\n\tif (spec.attrs) {\n\t\tfor (const [key, value] of Object.entries(spec.attrs)) {\n\t\t\twrapper.setAttribute(key, value);\n\t\t}\n\t}\n\treturn wrapper;\n}\n\n/**\n * Ensures a node appears immediately after `previousNode` among the\n * direct children of `container`. No-op if already in the correct position.\n */\nfunction ensureCorrectPosition(\n\tcontainer: HTMLElement,\n\tnode: HTMLElement,\n\tpreviousNode: HTMLElement | null,\n): void {\n\tconst expectedNext: ChildNode | null = previousNode\n\t\t? previousNode.nextSibling\n\t\t: container.firstChild;\n\n\tif (node === expectedNext) return;\n\t// Node is the first child needed and previousNode is null\n\tif (!previousNode && node.parentNode === container && node === container.firstChild) return;\n\n\tif (previousNode) {\n\t\tpreviousNode.after(node);\n\t} else {\n\t\tcontainer.insertBefore(node, container.firstChild);\n\t}\n}\n\n/**\n * Ensures exactly the specified blocks are inside the wrapper in order.\n * Moves blocks in from other parents as needed.\n */\nfunction reconcileWrapperContents(\n\twrapper: HTMLElement,\n\tdesiredBlockIds: readonly BlockId[],\n\tcontainer: HTMLElement,\n): void {\n\tfor (let i = 0; i < desiredBlockIds.length; i++) {\n\t\tconst bid: BlockId | undefined = desiredBlockIds[i];\n\t\tif (!bid) continue;\n\n\t\tconst expected: HTMLElement | null = container.querySelector(`[data-block-id=\"${bid}\"]`);\n\t\tif (!expected) continue;\n\n\t\tconst currentChild: Element | null = wrapper.children[i] ?? null;\n\t\tif (expected === currentChild) continue;\n\n\t\t// Insert at the correct position\n\t\tif (currentChild) {\n\t\t\twrapper.insertBefore(expected, currentChild);\n\t\t} else {\n\t\t\twrapper.appendChild(expected);\n\t\t}\n\t}\n\n\t// Remove any extra children that shouldn't be in this wrapper\n\tconst desiredSet = new Set<string>(desiredBlockIds);\n\tfor (const child of Array.from(wrapper.children)) {\n\t\tconst bid: string | null = child.getAttribute('data-block-id');\n\t\tif (bid && !desiredSet.has(bid)) {\n\t\t\tcontainer.insertBefore(child, wrapper);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Reconciles wrapper elements in the container to match the desired structure\n * derived from the block model. Uses a fast-path comparison to avoid any DOM\n * mutations when the wrapper structure is unchanged (e.g. selection-only updates).\n */\nexport function reconcileWrappers(\n\tcontainer: HTMLElement,\n\tblocks: readonly BlockNode[],\n\tregistry: SchemaRegistry,\n): void {\n\tconst groups: readonly WrapperGroup[] = computeWrapperGroups(blocks, registry);\n\tconst desired: readonly TopLevelEntry[] = computeDesiredSequence(blocks, groups);\n\tconst actual: readonly DomTopLevelEntry[] = readDomSequence(container);\n\n\t// Fast-path: structure unchanged → sync attrs only, zero structural DOM mutations\n\tif (sequencesMatch(desired, actual)) {\n\t\tlet groupIdx = 0;\n\t\tfor (const entry of actual) {\n\t\t\tif (entry.type === 'wrapper') {\n\t\t\t\tconst group: WrapperGroup | undefined = groups[groupIdx];\n\t\t\t\tif (group) syncWrapperAttributes(entry.element, group.spec);\n\t\t\t\tgroupIdx++;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\t// Full reconciliation: diff and patch\n\t// Build a pool of reusable wrappers keyed by wrapper key\n\tconst wrapperPool = new Map<string, HTMLElement[]>();\n\tfor (const entry of actual) {\n\t\tif (entry.type !== 'wrapper') continue;\n\t\tlet pool: HTMLElement[] | undefined = wrapperPool.get(entry.key);\n\t\tif (!pool) {\n\t\t\tpool = [];\n\t\t\twrapperPool.set(entry.key, pool);\n\t\t}\n\t\tpool.push(entry.element);\n\t}\n\n\tconst usedWrappers = new Set<HTMLElement>();\n\tlet previousTopLevel: HTMLElement | null = null;\n\tlet groupIdx = 0;\n\n\tfor (const entry of desired) {\n\t\tif (entry.type === 'bare') {\n\t\t\tconst blockEl: HTMLElement | null = container.querySelector(\n\t\t\t\t`[data-block-id=\"${entry.blockId}\"]`,\n\t\t\t);\n\t\t\tif (!blockEl) continue;\n\n\t\t\t// Ensure block is a direct child of container at the right position\n\t\t\tif (blockEl.parentNode !== container) {\n\t\t\t\t// Move out of wrapper\n\t\t\t\tif (previousTopLevel) {\n\t\t\t\t\tpreviousTopLevel.after(blockEl);\n\t\t\t\t} else {\n\t\t\t\t\tcontainer.insertBefore(blockEl, container.firstChild);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tensureCorrectPosition(container, blockEl, previousTopLevel);\n\t\t\t}\n\t\t\tpreviousTopLevel = blockEl;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// entry.type === 'wrapper'\n\t\tconst group: WrapperGroup | undefined = groups[groupIdx];\n\t\tif (!group) continue;\n\t\tgroupIdx++;\n\n\t\t// Try to reuse an existing wrapper with matching key\n\t\tconst pool: HTMLElement[] | undefined = wrapperPool.get(entry.key);\n\t\tlet wrapperEl: HTMLElement | null = null;\n\n\t\tif (pool && pool.length > 0) {\n\t\t\tconst candidate: HTMLElement | undefined = pool.shift();\n\t\t\tif (candidate) {\n\t\t\t\t// Check if tag matches — can't change tagName of existing element\n\t\t\t\tif (candidate.tagName.toLowerCase() === group.spec.tag) {\n\t\t\t\t\twrapperEl = candidate;\n\t\t\t\t} else {\n\t\t\t\t\t// Tag mismatch — create new, remove old later\n\t\t\t\t\twrapperEl = createWrapperElement(group.spec);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!wrapperEl) {\n\t\t\twrapperEl = createWrapperElement(group.spec);\n\t\t}\n\n\t\tusedWrappers.add(wrapperEl);\n\t\tsyncWrapperAttributes(wrapperEl, group.spec);\n\n\t\t// Ensure wrapper is at the correct position in container\n\t\tif (wrapperEl.parentNode !== container) {\n\t\t\tif (previousTopLevel) {\n\t\t\t\tpreviousTopLevel.after(wrapperEl);\n\t\t\t} else {\n\t\t\t\tcontainer.insertBefore(wrapperEl, container.firstChild);\n\t\t\t}\n\t\t} else {\n\t\t\tensureCorrectPosition(container, wrapperEl, previousTopLevel);\n\t\t}\n\n\t\t// Ensure correct blocks are inside the wrapper\n\t\treconcileWrapperContents(wrapperEl, entry.blockIds, container);\n\n\t\tpreviousTopLevel = wrapperEl;\n\t}\n\n\t// Remove unused wrappers\n\tfor (const entry of actual) {\n\t\tif (entry.type !== 'wrapper') continue;\n\t\tif (usedWrappers.has(entry.element)) continue;\n\n\t\t// Move any remaining children out\n\t\twhile (entry.element.firstChild) {\n\t\t\tcontainer.insertBefore(entry.element.firstChild, entry.element);\n\t\t}\n\t\tentry.element.remove();\n\t}\n}\n\n/**\n * Groups consecutive blocks that declare the same wrapper key into shared\n * wrapper elements (`<ul>`, `<ol>`, etc.). Used for initial rendering of\n * nested container children (e.g. list items inside table cells).\n */\nexport function wrapBlocks(\n\tcontainer: HTMLElement,\n\tblocks: readonly BlockNode[],\n\tregistry?: SchemaRegistry,\n): void {\n\tif (!registry) return;\n\n\tconst groups: readonly WrapperGroup[] = computeWrapperGroups(blocks, registry);\n\n\tfor (const group of groups) {\n\t\tconst firstEl: HTMLElement | null = container.querySelector(\n\t\t\t`[data-block-id=\"${group.blockIds[0]}\"]`,\n\t\t);\n\t\tif (!firstEl) continue;\n\n\t\tconst wrapper: HTMLElement = createWrapperElement(group.spec);\n\n\t\tfirstEl.before(wrapper);\n\t\tfor (const bid of group.blockIds) {\n\t\t\tconst el: HTMLElement | null = container.querySelector(`[data-block-id=\"${bid}\"]`);\n\t\t\tif (el) {\n\t\t\t\twrapper.appendChild(el);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Returns rendered top-level block elements in visual order.\n * Includes direct children and immediate children of block wrappers.\n */\nexport function getRenderedBlockElements(container: HTMLElement): readonly HTMLElement[] {\n\tconst blocks: HTMLElement[] = [];\n\tfor (const child of Array.from(container.children)) {\n\t\tif (!(child instanceof HTMLElement)) continue;\n\t\tif (child.hasAttribute('data-block-id')) {\n\t\t\tblocks.push(child);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!child.hasAttribute('data-block-wrapper')) continue;\n\n\t\tfor (const wrappedChild of Array.from(child.children)) {\n\t\t\tif (!(wrappedChild instanceof HTMLElement)) continue;\n\t\t\tif (wrappedChild.hasAttribute('data-block-id')) {\n\t\t\t\tblocks.push(wrappedChild);\n\t\t\t}\n\t\t}\n\t}\n\treturn blocks;\n}\n\n/** Removes a rendered block from whichever parent currently owns it. */\nexport function removeBlockElement(el: HTMLElement): void {\n\tel.parentElement?.removeChild(el);\n}\n\n/** Replaces a rendered block in-place, honoring wrapper parents when present. */\nexport function replaceBlockElement(\n\toldEl: HTMLElement,\n\tnewEl: HTMLElement,\n\tcontainer: HTMLElement,\n): void {\n\tconst parent: HTMLElement | null = oldEl.parentElement;\n\tif (parent) {\n\t\tparent.replaceChild(newEl, oldEl);\n\t\treturn;\n\t}\n\tcontainer.replaceChild(newEl, oldEl);\n}\n\n/**\n * Inserts a new rendered block after the given sibling.\n * If the sibling is inside a wrapper, insertion happens inside that wrapper.\n */\nexport function insertAfterPreviousSibling(\n\tcontainer: HTMLElement,\n\tpreviousSibling: Element | null,\n\tnewEl: HTMLElement,\n): void {\n\tif (previousSibling?.parentElement) {\n\t\tconst parent: HTMLElement = previousSibling.parentElement;\n\t\tif (previousSibling.nextSibling) {\n\t\t\tparent.insertBefore(newEl, previousSibling.nextSibling);\n\t\t} else {\n\t\t\tparent.appendChild(newEl);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (container.firstChild) {\n\t\tcontainer.insertBefore(newEl, container.firstChild);\n\t} else {\n\t\tcontainer.appendChild(newEl);\n\t}\n}\n","/**\n * Shared inline rendering utilities: InlineNode rendering and space preservation.\n *\n * Leaf module with no view-layer dependencies — used by both\n * BlockRendering and DecorationRendering to avoid circular imports.\n */\n\nimport type { InlineNode } from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\n\n/** Renders an InlineNode, using InlineNodeSpec.toDOM() or a fallback element. */\nexport function renderInlineNode(node: InlineNode, registry?: SchemaRegistry): HTMLElement {\n\tif (registry) {\n\t\tconst spec = registry.getInlineNodeSpec(node.inlineType);\n\t\tif (spec) {\n\t\t\tconst el: HTMLElement = spec.toDOM(node);\n\t\t\tel.setAttribute('contenteditable', 'false');\n\t\t\treturn el;\n\t\t}\n\t}\n\t// Fallback: generic non-editable span\n\tconst el: HTMLElement = document.createElement('span');\n\tel.setAttribute('data-inline-type', node.inlineType);\n\tel.setAttribute('contenteditable', 'false');\n\treturn el;\n}\n\n/**\n * Converts spaces for contenteditable rendering.\n * Trailing spaces and double spaces are replaced with \\u00a0 (non-breaking space)\n * to prevent the browser from collapsing them.\n */\nexport function preserveSpaces(text: string): string {\n\t// Replace consecutive spaces: alternate regular and non-breaking\n\tlet result: string = text.replace(/ {2}/g, ' \\u00a0');\n\t// If the text ends with a space, replace it with nbsp\n\tif (result.endsWith(' ')) {\n\t\tresult = `${result.slice(0, -1)}\\u00a0`;\n\t}\n\t// If the text starts with a space, replace it with nbsp\n\tif (result.startsWith(' ')) {\n\t\tresult = `\\u00a0${result.slice(1)}`;\n\t}\n\treturn result;\n}\n","/**\n * Decoration rendering: inline decorations and node decorations.\n *\n * Handles splitting text content by decoration boundaries and\n * applying CSS classes/styles from decorations to block elements.\n */\n\nimport type { DecorationAttrs, InlineDecoration } from '../decorations/Decoration.js';\nimport type { InlineNode, TextNode } from '../model/Document.js';\nimport { isInlineNode } from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { appendStyleText, setStyleText } from '../style/StyleRuntime.js';\nimport { preserveSpaces, renderInlineNode } from './InlineRendering.js';\nimport { wrapNodeWithMarks } from './MarkRendering.js';\nimport type { ReconcileOptions } from './Reconciler.js';\n\ninterface AppliedNodeDecorationClasses {\n\treadonly managed: ReadonlySet<string>;\n\treadonly preexisting: ReadonlySet<string>;\n}\n\nconst appliedNodeDecorationClasses: WeakMap<HTMLElement, AppliedNodeDecorationClasses> =\n\tnew WeakMap();\n\n/**\n * Renders inline content with decorations. InlineNodes are width-1 split points\n * rendered as their own elements (not wrapped by decorations).\n *\n * For each child:\n * - TextNode: split by decoration boundaries, render text → marks → decorations\n * - InlineNode: render directly without decoration wrapping\n */\nexport function renderDecoratedContent(\n\tcontainer: HTMLElement,\n\tinlineChildren: readonly (TextNode | InlineNode)[],\n\tinlineDecos: readonly InlineDecoration[],\n\tregistry?: SchemaRegistry,\n): void {\n\tlet globalOffset = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isInlineNode(child)) {\n\t\t\t// InlineNodes are rendered directly (not wrapped by decorations), but their\n\t\t\t// own marks (e.g. a link on an inline image) still wrap the node.\n\t\t\tcontainer.appendChild(\n\t\t\t\twrapNodeWithMarks(renderInlineNode(child, registry), child.marks, registry),\n\t\t\t);\n\t\t\tglobalOffset += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// TextNode: split by decoration boundaries within this node's range\n\t\tconst textFrom: number = globalOffset;\n\t\tconst textTo: number = globalOffset + child.text.length;\n\n\t\tif (child.text.length === 0) {\n\t\t\tglobalOffset = textTo;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Find split points within this text range\n\t\tconst splitSet = new Set<number>();\n\t\tsplitSet.add(textFrom);\n\t\tsplitSet.add(textTo);\n\t\tfor (const deco of inlineDecos) {\n\t\t\tconst dFrom: number = Math.max(textFrom, deco.from);\n\t\t\tconst dTo: number = Math.min(textTo, deco.to);\n\t\t\tif (dFrom > textFrom && dFrom < textTo) splitSet.add(dFrom);\n\t\t\tif (dTo > textFrom && dTo < textTo) splitSet.add(dTo);\n\t\t}\n\t\tconst splits: number[] = [...splitSet].sort((a, b) => a - b);\n\n\t\t// Render micro-segments\n\t\tfor (let i = 0; i < splits.length - 1; i++) {\n\t\t\tconst from: number | undefined = splits[i];\n\t\t\tconst to: number | undefined = splits[i + 1];\n\t\t\tif (from === undefined || to === undefined || from >= to) continue;\n\n\t\t\tconst localFrom: number = from - textFrom;\n\t\t\tconst localTo: number = to - textFrom;\n\t\t\tconst text: string = child.text.slice(localFrom, localTo);\n\n\t\t\t// Find decorations that fully cover this micro-segment\n\t\t\tconst activeDecos: InlineDecoration[] = [];\n\t\t\tfor (const deco of inlineDecos) {\n\t\t\t\tif (deco.from <= from && deco.to >= to) {\n\t\t\t\t\tactiveDecos.push(deco);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Render: text → marks (inner) → decorations (outer)\n\t\t\tconst textNode: Text = document.createTextNode(preserveSpaces(text));\n\t\t\tlet current: Node = wrapNodeWithMarks(textNode, child.marks, registry);\n\n\t\t\t// Wrap with decorations (outermost)\n\t\t\tfor (const deco of activeDecos) {\n\t\t\t\tconst el: HTMLElement = createDecorationElement(deco.attrs);\n\t\t\t\tel.appendChild(current);\n\t\t\t\tcurrent = el;\n\t\t\t}\n\n\t\t\tcontainer.appendChild(current);\n\t\t}\n\n\t\tglobalOffset = textTo;\n\t}\n}\n\n/** Creates a DOM element for an inline decoration. */\nfunction createDecorationElement(attrs: DecorationAttrs): HTMLElement {\n\tconst tagName: string = attrs.nodeName ?? 'span';\n\tconst el: HTMLElement = document.createElement(tagName);\n\tel.setAttribute('data-decoration', 'true');\n\n\tif (attrs.class) {\n\t\tfor (const cls of attrs.class.split(' ')) {\n\t\t\tif (cls) el.classList.add(cls);\n\t\t}\n\t}\n\tif (attrs.style) {\n\t\tsetStyleText(el, attrs.style);\n\t}\n\n\t// Apply any other custom attributes\n\tfor (const [key, value] of Object.entries(attrs)) {\n\t\tif (key === 'class' || key === 'style' || key === 'nodeName') continue;\n\t\tif (value !== undefined) {\n\t\t\tel.setAttribute(key, value);\n\t\t}\n\t}\n\n\treturn el;\n}\n\n/** Applies node decorations (CSS classes/styles) to a block element. */\nexport function applyNodeDecorations(\n\tel: HTMLElement,\n\tbid: BlockId,\n\toptions?: ReconcileOptions,\n): void {\n\tsyncNodeDecorationClasses(el, bid, options);\n\tconst nodeDecos = options?.decorations?.findNode(bid);\n\tif (!nodeDecos || nodeDecos.length === 0) return;\n\n\tfor (const deco of nodeDecos) {\n\t\tif (deco.attrs.style) {\n\t\t\tappendStyleText(el, deco.attrs.style);\n\t\t}\n\t}\n}\n\n/**\n * Synchronizes class-based node decorations without replacing a NodeView.\n * Decoration-owned classes are removed when their decoration disappears,\n * while classes that existed before decoration application are preserved.\n */\nexport function syncNodeDecorationClasses(\n\tel: HTMLElement,\n\tbid: BlockId,\n\toptions?: ReconcileOptions,\n): void {\n\tconst previous: AppliedNodeDecorationClasses | undefined = appliedNodeDecorationClasses.get(el);\n\tif (previous) {\n\t\tfor (const className of previous.managed) {\n\t\t\tif (!previous.preexisting.has(className)) el.classList.remove(className);\n\t\t}\n\t}\n\n\tconst desired = new Set<string>();\n\tfor (const decoration of options?.decorations?.findNode(bid) ?? []) {\n\t\tfor (const className of decoration.attrs.class?.split(/\\s+/) ?? []) {\n\t\t\tif (className) desired.add(className);\n\t\t}\n\t}\n\tif (desired.size === 0) {\n\t\tappliedNodeDecorationClasses.delete(el);\n\t\treturn;\n\t}\n\n\tconst preexisting = new Set<string>();\n\tfor (const className of desired) {\n\t\tif (el.classList.contains(className)) preexisting.add(className);\n\t\telse el.classList.add(className);\n\t}\n\tappliedNodeDecorationClasses.set(el, { managed: desired, preexisting });\n}\n","/**\n * Block and inline content rendering.\n *\n * Renders block nodes to DOM elements using NodeSpec, NodeView,\n * or a fallback paragraph. Renders inline content (text nodes,\n * inline nodes) into block containers.\n */\n\nimport type { InlineDecoration } from '../decorations/Decoration.js';\nimport type { NodeAttrsFor } from '../model/AttrRegistry.js';\nimport type { BlockNode, TextNode } from '../model/Document.js';\nimport {\n\tgetBlockChildren,\n\tgetInlineChildren,\n\tisInlineNode,\n\tisLeafBlock,\n\tisTextNode,\n} from '../model/Document.js';\nimport { normalizeHTMLId } from '../model/HTMLUtils.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { wrapBlocks } from './BlockWrapperManagement.js';\nimport { applyNodeDecorations, renderDecoratedContent } from './DecorationRendering.js';\nimport { createBlockElement } from './DomUtils.js';\nimport { preserveSpaces, renderInlineNode } from './InlineRendering.js';\nimport { wrapNodeWithMarks } from './MarkRendering.js';\nimport type { NodeView } from './NodeView.js';\nimport type { ReconcileOptions } from './Reconciler.js';\n\n/** Renders a block node to a DOM element, using registry specs or NodeViews. */\nexport function renderBlock(\n\tblock: BlockNode,\n\tregistry?: SchemaRegistry,\n\tnodeViews?: Map<string, NodeView>,\n\toptions?: ReconcileOptions,\n): HTMLElement {\n\tconst inlineDecos = options?.decorations?.findInline(block.id);\n\n\t// 1. Try NodeViewFactory\n\tconst nodeViewRegistry = options?.nodeViewRegistry;\n\tif (nodeViewRegistry && registry && nodeViews && options?.getState && options?.dispatch) {\n\t\tconst factory = nodeViewRegistry.getNodeViewFactory(block.type);\n\t\tif (factory) {\n\t\t\tconst nv: NodeView = factory(block, options.getState, options.dispatch);\n\t\t\tnodeViews.set(block.id, nv);\n\n\t\t\t// Mark void blocks\n\t\t\tconst nvSpec = registry.getNodeSpec(block.type);\n\t\t\tif (nvSpec?.isVoid) {\n\t\t\t\tnv.dom.setAttribute('data-void', 'true');\n\t\t\t}\n\t\t\tif (nvSpec?.selectable) {\n\t\t\t\tnv.dom.setAttribute('data-selectable', 'true');\n\t\t\t}\n\n\t\t\t// Mark contentDOM so SelectionSync can find it\n\t\t\tif (nv.contentDOM && nv.contentDOM !== nv.dom) {\n\t\t\t\tnv.contentDOM.setAttribute('data-content-dom', 'true');\n\t\t\t}\n\n\t\t\t// Render children into NodeView content area\n\t\t\tif (isLeafBlock(block) && nv.contentDOM) {\n\t\t\t\t// Leaf blocks: render inline content (TextNodes) into contentDOM\n\t\t\t\trenderBlockContent(nv.contentDOM, block, registry, inlineDecos);\n\t\t\t} else {\n\t\t\t\t// Container blocks: recursively render block children\n\t\t\t\tconst blockChildren = getBlockChildren(block);\n\t\t\t\tconst contentDOMChildren = new Map<HTMLElement, BlockNode[]>();\n\t\t\t\tfor (const child of blockChildren) {\n\t\t\t\t\tconst contentDOM = nv.getContentDOM?.(child.id) ?? nv.contentDOM;\n\t\t\t\t\tif (contentDOM) {\n\t\t\t\t\t\tconst childEl: HTMLElement = renderBlock(child, registry, nodeViews, options);\n\t\t\t\t\t\tcontentDOM.appendChild(childEl);\n\t\t\t\t\t\tlet arr = contentDOMChildren.get(contentDOM);\n\t\t\t\t\t\tif (!arr) {\n\t\t\t\t\t\t\tarr = [];\n\t\t\t\t\t\t\tcontentDOMChildren.set(contentDOM, arr);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tarr.push(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Wrap blocks in each contentDOM (e.g., list items in table cells → <ul>/<ol>)\n\t\t\t\tfor (const [dom, children] of contentDOMChildren) {\n\t\t\t\t\twrapBlocks(dom, children, registry);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsyncBlockHTMLId(nv.dom, block);\n\t\t\tnv.dom.setAttribute('data-block-type', block.type);\n\t\t\tapplyNodeDecorations(nv.dom, block.id, options);\n\t\t\treturn nv.dom;\n\t\t}\n\t}\n\n\t// 2. Try NodeSpec\n\tif (registry) {\n\t\tconst spec = registry.getNodeSpec(block.type);\n\t\tif (spec) {\n\t\t\tconst el: HTMLElement = spec.toDOM(\n\t\t\t\tblock as Omit<BlockNode, 'attrs'> & { readonly attrs: NodeAttrsFor<string> },\n\t\t\t);\n\t\t\tif (spec.isVoid) {\n\t\t\t\tel.setAttribute('data-void', 'true');\n\t\t\t}\n\t\t\tif (spec.selectable) {\n\t\t\t\tel.setAttribute('data-selectable', 'true');\n\t\t\t}\n\t\t\tif (!spec.isVoid) {\n\t\t\t\tif (isLeafBlock(block)) {\n\t\t\t\t\trenderBlockContent(el, block, registry, inlineDecos);\n\t\t\t\t} else {\n\t\t\t\t\t// Render block children recursively\n\t\t\t\t\tconst blockChildren = getBlockChildren(block);\n\t\t\t\t\tfor (const child of blockChildren) {\n\t\t\t\t\t\tconst childEl: HTMLElement = renderBlock(child, registry, nodeViews, options);\n\t\t\t\t\t\tel.appendChild(childEl);\n\t\t\t\t\t}\n\t\t\t\t\t// Wrap blocks (e.g., list items → <ul>/<ol>)\n\t\t\t\t\twrapBlocks(el, blockChildren, registry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsyncBlockHTMLId(el, block);\n\t\t\tel.setAttribute('data-block-type', block.type);\n\t\t\tapplyNodeDecorations(el, block.id, options);\n\t\t\treturn el;\n\t\t}\n\t}\n\n\t// 3. Fallback — render as paragraph\n\treturn renderParagraphFallback(block, registry, inlineDecos);\n}\n\n/**\n * Synchronizes the semantic HTML ID owned by a block without disturbing an\n * unrelated ID a third-party NodeView may put on a freshly created element.\n */\nexport function syncBlockHTMLId(\n\tel: HTMLElement,\n\tblock: BlockNode,\n\tpreviousBlock?: BlockNode,\n): void {\n\tconst htmlId: string | undefined = normalizeHTMLId(block.htmlId);\n\tif (htmlId) {\n\t\tel.setAttribute('id', htmlId);\n\t\treturn;\n\t}\n\n\tconst previousHTMLId: string | undefined = normalizeHTMLId(previousBlock?.htmlId);\n\tif (previousHTMLId && el.getAttribute('id') === previousHTMLId) {\n\t\tel.removeAttribute('id');\n\t}\n}\n\n/** Renders block content (inline children) into a container element. */\nexport function renderBlockContent(\n\tcontainer: HTMLElement,\n\tblock: BlockNode,\n\tregistry?: SchemaRegistry,\n\tinlineDecos?: readonly InlineDecoration[],\n): void {\n\t// Remove stale CursorWrapper spans that may survive into reconciliation\n\tfor (const el of container.querySelectorAll('[data-cursor-wrapper]')) {\n\t\tel.remove();\n\t}\n\n\tconst inlineChildren = getInlineChildren(block);\n\n\t// Empty block: single empty text node → render <br> placeholder\n\tif (\n\t\tinlineChildren.length === 1 &&\n\t\tisTextNode(inlineChildren[0]) &&\n\t\tinlineChildren[0].text === ''\n\t) {\n\t\tcontainer.appendChild(document.createElement('br'));\n\t\treturn;\n\t}\n\n\t// Fast path: no inline decorations\n\tif (!inlineDecos || inlineDecos.length === 0) {\n\t\tfor (const child of inlineChildren) {\n\t\t\tif (isTextNode(child)) {\n\t\t\t\tcontainer.appendChild(renderTextNode(child, registry));\n\t\t\t} else {\n\t\t\t\tcontainer.appendChild(\n\t\t\t\t\twrapNodeWithMarks(renderInlineNode(child, registry), child.marks, registry),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Decorated path: split content into micro-segments\n\t\trenderDecoratedContent(container, inlineChildren, inlineDecos, registry);\n\t}\n\n\t// Trailing <br> hack: when the last child is a hard_break InlineNode,\n\t// browsers won't render an empty line after a trailing <br>.\n\t// Append an extra plain <br> so the cursor can be placed on the new line.\n\tconst lastChild = inlineChildren[inlineChildren.length - 1];\n\tif (lastChild && isInlineNode(lastChild) && lastChild.inlineType === 'hard_break') {\n\t\tcontainer.appendChild(document.createElement('br'));\n\t}\n}\n\n/** Renders a text node with marks as nested inline elements. */\nfunction renderTextNode(node: TextNode, registry?: SchemaRegistry): Node {\n\tif (node.text === '') {\n\t\treturn document.createTextNode('');\n\t}\n\tconst textNode: Text = document.createTextNode(preserveSpaces(node.text));\n\treturn wrapNodeWithMarks(textNode, node.marks, registry);\n}\n\n/** Fallback paragraph rendering when no NodeSpec is found. */\nfunction renderParagraphFallback(\n\tblock: BlockNode,\n\tregistry?: SchemaRegistry,\n\tinlineDecos?: readonly InlineDecoration[],\n): HTMLElement {\n\tconst p: HTMLElement = createBlockElement('p', block.id);\n\tsyncBlockHTMLId(p, block);\n\tp.setAttribute('data-block-type', block.type);\n\trenderBlockContent(p, block, registry, inlineDecos);\n\treturn p;\n}\n","/**\n * Reconciler: diffs old and new state and applies minimal DOM patches.\n * Uses block-level granularity — changed blocks are re-rendered entirely.\n *\n * Orchestrates block rendering, wrapper management, and change detection.\n * Actual rendering logic is delegated to BlockRendering, DecorationRendering,\n * InlineRendering, and BlockWrapperManagement modules.\n */\n\nimport type { Decoration } from '../decorations/Decoration.js';\nimport { type DecorationSet, decorationArraysEqual } from '../decorations/Decoration.js';\nimport type { BlockNode } from '../model/Document.js';\nimport {\n\tblockAttrsEqual,\n\tisInlineNode,\n\tisLeafBlock,\n\tisTextNode,\n\tmarkSetsEqual,\n} from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { renderBlock, renderBlockContent, syncBlockHTMLId } from './BlockRendering.js';\nimport {\n\tgetRenderedBlockElements,\n\tinsertAfterPreviousSibling,\n\treconcileWrappers,\n\tremoveBlockElement,\n\treplaceBlockElement,\n} from './BlockWrapperManagement.js';\nimport { syncNodeDecorationClasses } from './DecorationRendering.js';\nimport type { NodeView } from './NodeView.js';\nimport type { NodeViewRegistry } from './NodeViewRegistry.js';\n\n// Re-exports for backwards compatibility\nexport { renderBlock, renderBlockContent } from './BlockRendering.js';\n\nexport interface ReconcileOptions {\n\tregistry?: SchemaRegistry;\n\tnodeViewRegistry?: NodeViewRegistry;\n\tnodeViews?: Map<string, NodeView>;\n\tgetState?: () => EditorState;\n\tdispatch?: (tr: import('../state/Transaction.js').Transaction) => void;\n\tdecorations?: DecorationSet;\n\toldDecorations?: DecorationSet;\n\tselectedNodeId?: BlockId;\n\tpreviousSelectedNodeId?: BlockId;\n\t/** When set, the block with this ID is skipped during reconciliation to preserve IME composition. */\n\tcompositionBlockId?: BlockId;\n}\n\n/** Reconciles the DOM container to match the new state. */\nexport function reconcile(\n\tcontainer: HTMLElement,\n\toldState: EditorState | null,\n\tnewState: EditorState,\n\toptions?: ReconcileOptions,\n): void {\n\tconst oldBlocks = oldState?.doc.children ?? [];\n\tconst newBlocks = newState.doc.children;\n\tconst registry = options?.registry;\n\tconst nodeViews = options?.nodeViews;\n\n\t// Skip wrapper reconciliation during active IME composition — moving DOM\n\t// nodes breaks the browser's composition session. Wrappers are structural\n\t// (based on block types) and don't change during text composition; the next\n\t// non-composing reconcile will fix them.\n\tconst isComposing = options?.compositionBlockId != null;\n\n\tconst oldBlockMap = new Map<BlockId, HTMLElement>();\n\tfor (const el of getRenderedBlockElements(container)) {\n\t\tconst bid = el.getAttribute('data-block-id');\n\t\tif (bid) oldBlockMap.set(toBlockId(bid), el);\n\t}\n\n\t// Build set of new block IDs for removal detection\n\tconst newBlockIds = new Set(newBlocks.map((b) => b.id));\n\n\t// Remove blocks that no longer exist\n\tfor (const [bid, el] of oldBlockMap) {\n\t\tif (!newBlockIds.has(bid)) {\n\t\t\tremoveBlockElement(el);\n\t\t\toldBlockMap.delete(bid);\n\t\t\t// Destroy NodeView if exists\n\t\t\tconst nv = nodeViews?.get(bid);\n\t\t\tif (nv) {\n\t\t\t\tnv.destroy?.();\n\t\t\t\tnodeViews?.delete(bid);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Mapping from old state for change detection\n\tconst oldBlockById = new Map<string, BlockNode>();\n\tfor (const block of oldBlocks) {\n\t\toldBlockById.set(block.id, block);\n\t}\n\n\t// Insert/update blocks in order\n\tlet previousSibling: Element | null = null;\n\n\tconst selectedNodeId = options?.selectedNodeId;\n\tconst previousSelectedNodeId = options?.previousSelectedNodeId;\n\n\tfor (const block of newBlocks) {\n\t\tconst existingEl = oldBlockMap.get(block.id);\n\t\tconst oldBlock = oldBlockById.get(block.id);\n\n\t\t// Skip reconciliation for the block under active IME composition\n\t\tif (options?.compositionBlockId === block.id && existingEl) {\n\t\t\tpreviousSibling = existingEl;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst oldDecos = options?.oldDecorations?.find(block.id);\n\t\tconst newDecos = options?.decorations?.find(block.id);\n\n\t\tif (existingEl && oldBlock && !blockChanged(oldBlock, block, oldDecos, newDecos)) {\n\t\t\t// Block unchanged — keep existing DOM\n\t\t\tpreviousSibling = existingEl;\n\t\t} else if (existingEl) {\n\t\t\t// Block changed — try NodeView update first\n\t\t\tconst existingNv = nodeViews?.get(block.id);\n\t\t\tif (existingNv) {\n\t\t\t\tconst handled = existingNv.update?.(block) ?? false;\n\t\t\t\tif (handled) {\n\t\t\t\t\tsyncBlockHTMLId(existingNv.dom, block, oldBlock);\n\t\t\t\t\t// Re-render inline content into contentDOM for leaf blocks\n\t\t\t\t\tif (isLeafBlock(block) && existingNv.contentDOM) {\n\t\t\t\t\t\texistingNv.contentDOM.textContent = '';\n\t\t\t\t\t\tconst updatedInlineDecos = options?.decorations?.findInline(block.id);\n\t\t\t\t\t\trenderBlockContent(existingNv.contentDOM, block, registry, updatedInlineDecos);\n\t\t\t\t\t}\n\t\t\t\t\tpreviousSibling = existingNv.dom;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Update not handled — destroy and re-create\n\t\t\t\texistingNv.destroy?.();\n\t\t\t\tnodeViews?.delete(block.id);\n\t\t\t}\n\n\t\t\tconst newEl = renderBlock(block, registry, nodeViews, options);\n\t\t\treplaceBlockElement(existingEl, newEl, container);\n\t\t\tpreviousSibling = newEl;\n\t\t} else {\n\t\t\t// New block — insert after previousSibling\n\t\t\tconst newEl = renderBlock(block, registry, nodeViews, options);\n\t\t\tinsertAfterPreviousSibling(container, previousSibling, newEl);\n\t\t\tpreviousSibling = newEl;\n\t\t}\n\n\t\t// Handle NodeSelection visual state (CSS class only — aria-selected is\n\t\t// invalid on generic block elements like <figure>, <pre>, etc.)\n\t\tif (selectedNodeId === block.id) {\n\t\t\tconst nv = nodeViews?.get(block.id);\n\t\t\tif (nv) {\n\t\t\t\tnv.selectNode?.();\n\t\t\t} else {\n\t\t\t\tconst el = oldBlockMap.get(block.id) ?? previousSibling;\n\t\t\t\tif (el instanceof HTMLElement) {\n\t\t\t\t\tel.classList.add('notectl-node-selected');\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (previousSelectedNodeId === block.id) {\n\t\t\tconst nv = nodeViews?.get(block.id);\n\t\t\tif (nv) {\n\t\t\t\tnv.deselectNode?.();\n\t\t\t} else {\n\t\t\t\tconst el = oldBlockMap.get(block.id) ?? previousSibling;\n\t\t\t\tif (el instanceof HTMLElement) {\n\t\t\t\t\tel.classList.remove('notectl-node-selected');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle selection for nested NodeViews (e.g. images inside table cells)\n\tif (previousSelectedNodeId && previousSelectedNodeId !== selectedNodeId) {\n\t\tconst nested = nodeViews?.get(previousSelectedNodeId);\n\t\tif (nested && !newBlockIds.has(previousSelectedNodeId)) {\n\t\t\tnested.deselectNode?.();\n\t\t}\n\t}\n\tif (selectedNodeId && !newBlockIds.has(selectedNodeId)) {\n\t\tconst nested = nodeViews?.get(selectedNodeId);\n\t\tif (nested) {\n\t\t\tnested.selectNode?.();\n\t\t}\n\t}\n\n\t// Reconcile wrapper elements (e.g. <ul>/<ol> for list items) with minimal\n\t// DOM mutations. When wrapper structure is unchanged, this is a no-op.\n\tif (!isComposing && registry) {\n\t\treconcileWrappers(container, newBlocks, registry);\n\t}\n\n\t// Node decorations may target descendants of an unchanged composite root.\n\t// Synchronize their classes in place so nested NodeViews (table cells, code\n\t// blocks, and plugin containers) do not require destructive parent renders.\n\tfor (const element of container.querySelectorAll<HTMLElement>('[data-block-id]')) {\n\t\tconst rawId: string | null = element.getAttribute('data-block-id');\n\t\tif (rawId) syncNodeDecorationClasses(element, toBlockId(rawId), options);\n\t}\n}\n\n/** Checks whether a block has changed by comparing its children, attrs, and decorations. */\nfunction blockChanged(\n\toldBlock: BlockNode,\n\tnewBlock: BlockNode,\n\toldDecos?: readonly Decoration[],\n\tnewDecos?: readonly Decoration[],\n): boolean {\n\tif (oldBlock.type !== newBlock.type) return true;\n\tif (oldBlock.htmlId !== newBlock.htmlId) return true;\n\tif (oldBlock.children.length !== newBlock.children.length) return true;\n\n\t// Compare attrs\n\tif (!blockAttrsEqual(oldBlock.attrs, newBlock.attrs)) return true;\n\n\tfor (let i = 0; i < oldBlock.children.length; i++) {\n\t\tconst oldChild = oldBlock.children[i];\n\t\tconst newChild = newBlock.children[i];\n\t\tif (!oldChild || !newChild) return true;\n\n\t\tif (isTextNode(oldChild) && isTextNode(newChild)) {\n\t\t\tif (oldChild.text !== newChild.text) return true;\n\t\t\tif (!markSetsEqual(oldChild.marks, newChild.marks)) return true;\n\t\t} else if (isInlineNode(oldChild) && isInlineNode(newChild)) {\n\t\t\tif (oldChild.inlineType !== newChild.inlineType) return true;\n\t\t\tif (!inlineAttrsEqual(oldChild.attrs, newChild.attrs)) return true;\n\t\t\tif (!markSetsEqual(oldChild.marks, newChild.marks)) return true;\n\t\t} else if (\n\t\t\t!isTextNode(oldChild) &&\n\t\t\t!isTextNode(newChild) &&\n\t\t\t!isInlineNode(oldChild) &&\n\t\t\t!isInlineNode(newChild)\n\t\t) {\n\t\t\t// Both are BlockNodes — compare recursively\n\t\t\tif (blockChanged(oldChild as BlockNode, newChild as BlockNode)) return true;\n\t\t} else {\n\t\t\t// Different child types\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Compare decorations\n\tconst oldArr = oldDecos ?? [];\n\tconst newArr = newDecos ?? [];\n\tif (oldArr !== newArr && !decorationArraysEqual(oldArr, newArr)) return true;\n\n\treturn false;\n}\n\n/** Compares two InlineNode attr records for equality. */\nfunction inlineAttrsEqual(\n\ta: Readonly<Record<string, string | number | boolean>>,\n\tb: Readonly<Record<string, string | number | boolean>>,\n): boolean {\n\tconst aKeys = Object.keys(a);\n\tconst bKeys = Object.keys(b);\n\tif (aKeys.length !== bKeys.length) return false;\n\tfor (const key of aKeys) {\n\t\tif (a[key] !== b[key]) return false;\n\t}\n\treturn true;\n}\n","/**\n * EditorView: orchestrates state updates, reconciliation, and\n * selection sync.\n */\n\nimport { DecorationSet } from '../decorations/Decoration.js';\nimport type { CompositionState } from '../model/CompositionState.js';\nimport type { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport type { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { isNodeSelection } from '../model/Selection.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { HistoryManager } from '../state/History.js';\nimport { Mapping } from '../state/Mapping.js';\nimport { isAllowedInReadonly, isSelectionOnlyTransaction } from '../state/ReadonlyGuard.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { CursorWrapper } from './CursorWrapper.js';\nimport { EditorViewEvents } from './EditorViewEvents.js';\nimport { EditorViewNavigation } from './EditorViewNavigation.js';\nimport type { NodeView } from './NodeView.js';\nimport type { NodeViewRegistry } from './NodeViewRegistry.js';\nimport { type ReconcileOptions, reconcile } from './Reconciler.js';\nimport { syncSelectionToDOM } from './SelectionSync.js';\n\nexport type StateChangeCallback = (\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n) => void;\n\nexport interface EditorViewOptions {\n\tstate: EditorState;\n\tschemaRegistry?: SchemaRegistry;\n\tkeymapRegistry?: KeymapRegistry;\n\tfileHandlerRegistry?: FileHandlerRegistry;\n\tnodeViewRegistry?: NodeViewRegistry;\n\tmaxHistoryDepth?: number;\n\tonStateChange?: StateChangeCallback;\n\tgetDecorations?: (state: EditorState, tr?: Transaction) => DecorationSet;\n\tisReadOnly?: () => boolean;\n\t/**\n\t * Returns true while a `readonlyAllowed` command is mid-execution.\n\t * Allows command-driven mutations to bypass the readonly dispatch guard\n\t * even when the resulting transaction does not carry `metadata.readonlyAllowed`.\n\t */\n\tisReadonlyBypassed?: () => boolean;\n\tcompositionState?: CompositionState;\n}\n\nexport class EditorView {\n\tprivate state: EditorState;\n\tprivate readonly contentElement: HTMLElement;\n\treadonly history: HistoryManager;\n\tprivate readonly stateChangeCallbacks: StateChangeCallback[] = [];\n\tprivate isUpdating = false;\n\tprivate readonly schemaRegistry?: SchemaRegistry;\n\tprivate readonly nodeViewRegistry?: NodeViewRegistry;\n\tprivate readonly nodeViews = new Map<string, NodeView>();\n\tprivate decorations: DecorationSet = DecorationSet.empty;\n\tprivate readonly getDecorations?: (state: EditorState, tr?: Transaction) => DecorationSet;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly isReadonlyBypassed: () => boolean;\n\tprivate readonly compositionState: CompositionState;\n\tprivate readonly cursorWrapper: CursorWrapper;\n\tprivate readonly navigation: EditorViewNavigation;\n\tprivate readonly events: EditorViewEvents;\n\n\tconstructor(contentElement: HTMLElement, options: EditorViewOptions) {\n\t\tthis.state = options.state;\n\t\tthis.contentElement = contentElement;\n\t\tthis.schemaRegistry = options.schemaRegistry;\n\t\tthis.nodeViewRegistry = options.nodeViewRegistry;\n\t\tthis.getDecorations = options.getDecorations;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\t\tthis.isReadonlyBypassed = options.isReadonlyBypassed ?? (() => false);\n\t\tthis.compositionState = options.compositionState ?? {\n\t\t\tisComposing: false,\n\t\t\tactiveBlockId: null,\n\t\t};\n\n\t\tthis.history = new HistoryManager({\n\t\t\tmaxDepth: options.maxHistoryDepth ?? 100,\n\t\t});\n\n\t\tif (options.onStateChange) {\n\t\t\tthis.stateChangeCallbacks.push(options.onStateChange);\n\t\t}\n\n\t\tthis.cursorWrapper = new CursorWrapper(contentElement, this.schemaRegistry);\n\n\t\tthis.navigation = new EditorViewNavigation({\n\t\t\tcontentElement,\n\t\t\tgetState: () => this.state,\n\t\t\tdispatch: (tr: Transaction) => this.dispatch(tr),\n\t\t\tkeymapRegistry: options.keymapRegistry,\n\t\t});\n\n\t\tthis.events = new EditorViewEvents({\n\t\t\tcontentElement,\n\t\t\tgetState: () => this.state,\n\t\t\tdispatch: (tr: Transaction) => this.dispatch(tr),\n\t\t\tapplyUpdate: (newState: EditorState, tr: Transaction, skipSelectionSync?: boolean) =>\n\t\t\t\tthis.applyUpdate(newState, tr, { skipSelectionSync }),\n\t\t\tisUpdating: () => this.isUpdating,\n\t\t\tcompositionState: this.compositionState,\n\t\t\tcursorWrapper: this.cursorWrapper,\n\t\t\tisReadOnly: this.isReadOnly,\n\t\t\tfileHandlerRegistry: options.fileHandlerRegistry,\n\t\t\tonMousedown: () => this.navigation.clearGoalColumn(),\n\t\t});\n\n\t\t// Initial render\n\t\tthis.decorations = this.getDecorations?.(this.state) ?? DecorationSet.empty;\n\t\treconcile(contentElement, null, this.state, {\n\t\t\t...this.reconcileOptions(),\n\t\t\tdecorations: this.decorations,\n\t\t});\n\t\tsyncSelectionToDOM(contentElement, this.state.selection);\n\t}\n\n\t/** Returns the current editor state. */\n\tgetState(): EditorState {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Central update cycle: sets state, collects decorations, reconciles\n\t * DOM, syncs selection, and notifies listeners.\n\t * Guarded against re-entrancy.\n\t */\n\tprivate applyUpdate(\n\t\tnewState: EditorState,\n\t\ttr: Transaction,\n\t\toptions?: { readonly pushHistory?: boolean; readonly skipSelectionSync?: boolean },\n\t): void {\n\t\tif (this.isUpdating) return;\n\t\tthis.isUpdating = true;\n\t\ttry {\n\t\t\tconst oldState = this.state;\n\t\t\tthis.state = newState;\n\n\t\t\tif (this.cursorWrapper.isActive && !newState.storedMarks?.length) {\n\t\t\t\tthis.cursorWrapper.cleanup();\n\t\t\t}\n\n\t\t\tif (options?.pushHistory && tr.metadata.origin !== 'history') {\n\t\t\t\tthis.history.push(tr);\n\t\t\t} else if (isSelectionOnlyTransaction(tr)) {\n\t\t\t\t// Selection-only updates are not undoable, but they must still\n\t\t\t\t// break typing groups so later edits don't merge across cursor moves.\n\t\t\t\tthis.history.push(tr);\n\t\t\t}\n\n\t\t\tconst newDecorations = this.getDecorations?.(newState, tr) ?? DecorationSet.empty;\n\t\t\tthis.reconcileAndSync(oldState, newState, newDecorations, options?.skipSelectionSync);\n\n\t\t\tfor (const cb of this.stateChangeCallbacks) {\n\t\t\t\tcb(oldState, newState, tr);\n\t\t\t}\n\t\t\tthis.navigation.resetAfterUpdate();\n\t\t} finally {\n\t\t\tthis.isUpdating = false;\n\t\t}\n\t}\n\n\t/** Dispatches a transaction, updates state, reconciles DOM. */\n\tdispatch(tr: Transaction): void {\n\t\tif (this.isReadOnly() && !isAllowedInReadonly(tr) && !this.isReadonlyBypassed()) {\n\t\t\treturn;\n\t\t}\n\t\tconst newState = this.state.apply(tr);\n\t\tthis.applyUpdate(newState, tr, { pushHistory: true });\n\t}\n\n\t/** Performs undo. */\n\tundo(): void {\n\t\tif (this.isUpdating || this.isReadOnly()) return;\n\t\tconst result = this.history.undo(this.state);\n\t\tif (!result) return;\n\t\tthis.applyUpdate(result.state, result.transaction);\n\t}\n\n\t/** Performs redo. */\n\tredo(): void {\n\t\tif (this.isUpdating || this.isReadOnly()) return;\n\t\tconst result = this.history.redo(this.state);\n\t\tif (!result) return;\n\t\tthis.applyUpdate(result.state, result.transaction);\n\t}\n\n\t/** Registers a state change callback. */\n\tonStateChange(callback: StateChangeCallback): () => void {\n\t\tthis.stateChangeCallbacks.push(callback);\n\t\treturn () => {\n\t\t\tconst idx = this.stateChangeCallbacks.indexOf(callback);\n\t\t\tif (idx !== -1) this.stateChangeCallbacks.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Replaces the editor state without destroying handlers.\n\t *\n\t * The current selection is preserved (validated and clamped against the\n\t * new document); any selection carried on `newState` is ignored. This\n\t * prevents content round-trips such as `setJSON(getJSON())` from resetting\n\t * the caret (see issue #103).\n\t *\n\t * Undo/redo history is cleared because the document identity changes and\n\t * persisted steps would no longer resolve against it.\n\t */\n\treplaceState(newState: EditorState): void {\n\t\tif (this.isUpdating) return;\n\t\tthis.isUpdating = true;\n\t\ttry {\n\t\t\tconst oldState = this.state;\n\t\t\tconst preserved: EditorState = newState.withSelection(oldState.selection);\n\t\t\tthis.state = preserved;\n\t\t\tthis.history.clear();\n\n\t\t\tif (this.cursorWrapper.isActive && !preserved.storedMarks?.length) {\n\t\t\t\tthis.cursorWrapper.cleanup();\n\t\t\t}\n\n\t\t\tconst tr: Transaction = {\n\t\t\t\tsteps: [],\n\t\t\t\tselectionBefore: oldState.selection,\n\t\t\t\tselectionAfter: preserved.selection,\n\t\t\t\tstoredMarksAfter: preserved.storedMarks,\n\t\t\t\tmapping: Mapping.empty,\n\t\t\t\tforwardStepMaps: [],\n\t\t\t\tmetadata: {\n\t\t\t\t\torigin: 'api',\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t};\n\t\t\tconst newDecorations = this.getDecorations?.(preserved, tr) ?? DecorationSet.empty;\n\t\t\tthis.reconcileAndSync(oldState, preserved, newDecorations);\n\n\t\t\tfor (const cb of this.stateChangeCallbacks) {\n\t\t\t\tcb(oldState, preserved, tr);\n\t\t\t}\n\t\t\tthis.navigation.resetAfterUpdate();\n\t\t} finally {\n\t\t\tthis.isUpdating = false;\n\t\t}\n\t}\n\n\t/** Syncs the DOM selection into editor state. */\n\tsyncSelection(): void {\n\t\tthis.events.syncSelectionFromDOM();\n\t}\n\n\t/**\n\t * Reconciles DOM and syncs selection, updating decoration state.\n\t *\n\t * When `skipSelectionSync` is true, the DOM selection is not written\n\t * back. This is used when the update was triggered by reading the\n\t * DOM selection (e.g. mouse click, drag) — the DOM is already correct,\n\t * and re-writing via `setBaseAndExtent` would disrupt native browser\n\t * behaviors like drag-selection and double-click word selection.\n\t */\n\tprivate reconcileAndSync(\n\t\toldState: EditorState,\n\t\tnewState: EditorState,\n\t\tnewDecorations: DecorationSet,\n\t\tskipSelectionSync?: boolean,\n\t): void {\n\t\tconst oldDecorations = this.decorations;\n\t\tthis.decorations = newDecorations;\n\n\t\treconcile(this.contentElement, oldState, newState, {\n\t\t\t...this.reconcileOptions(oldState.selection),\n\t\t\tdecorations: newDecorations,\n\t\t\toldDecorations,\n\t\t\tcompositionBlockId: this.compositionState.isComposing\n\t\t\t\t? (this.compositionState.activeBlockId ?? undefined)\n\t\t\t\t: undefined,\n\t\t});\n\t\tif (!this.compositionState.isComposing && !skipSelectionSync) {\n\t\t\tsyncSelectionToDOM(this.contentElement, newState.selection);\n\t\t}\n\t}\n\n\tprivate reconcileOptions(\n\t\toldSelection?: import('../model/Selection.js').EditorSelection,\n\t): ReconcileOptions {\n\t\tconst selectedNodeId = isNodeSelection(this.state.selection)\n\t\t\t? this.state.selection.nodeId\n\t\t\t: undefined;\n\t\tconst previousSelectedNodeId =\n\t\t\toldSelection && isNodeSelection(oldSelection) ? oldSelection.nodeId : undefined;\n\t\treturn {\n\t\t\tregistry: this.schemaRegistry,\n\t\t\tnodeViewRegistry: this.nodeViewRegistry,\n\t\t\tnodeViews: this.nodeViews,\n\t\t\tgetState: () => this.state,\n\t\t\tdispatch: (tr: Transaction) => this.dispatch(tr),\n\t\t\tselectedNodeId,\n\t\t\tpreviousSelectedNodeId,\n\t\t};\n\t}\n\n\t/** Cleans up all event listeners and handlers. */\n\tdestroy(): void {\n\t\tthis.cursorWrapper.cleanup();\n\t\tthis.events.destroy();\n\t\tthis.navigation.destroy();\n\t\tfor (const nv of this.nodeViews.values()) {\n\t\t\tnv.destroy?.();\n\t\t}\n\t\tthis.nodeViews.clear();\n\t}\n}\n","/**\n * Human-readable labels for block types.\n * Used by accessibility announcements (screen readers) when navigating between blocks.\n */\n\nconst BLOCK_TYPE_LABELS: Readonly<Record<string, string>> = {\n\tparagraph: 'Paragraph',\n\theading: 'Heading',\n\tcode_block: 'Code Block',\n\tblockquote: 'Block Quote',\n\tlist_item: 'List Item',\n\thorizontal_rule: 'Horizontal Rule',\n\timage: 'Image',\n\ttable: 'Table',\n};\n\n/** Returns a human-readable label for a block type (used in screen reader announcements). */\nexport function getBlockTypeLabel(typeName: string, attrs?: Record<string, unknown>): string {\n\tif (typeName === 'heading' && attrs?.level) {\n\t\treturn `Heading ${attrs.level}`;\n\t}\n\treturn BLOCK_TYPE_LABELS[typeName] ?? typeName;\n}\n","/**\n * Announcer: builds screen-reader announcement text for editor state changes.\n * Pure functions — no DOM mutation. The caller applies the result to a live region.\n */\n\nimport { isMarkActive } from '../commands/Commands.js';\nimport { getBlockTypeLabel } from '../model/BlockTypeLabels.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nexport { getBlockTypeLabel } from '../model/BlockTypeLabels.js';\n\n/**\n * Derives an announcement string from a state transition.\n * Returns `null` when no announcement is warranted.\n *\n * Priority order: undo/redo > block-type change > mark toggle.\n */\nexport function buildAnnouncement(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n): string | null {\n\t// Priority 1: Undo/Redo\n\tif (tr.metadata.historyDirection === 'undo') return 'Undo';\n\tif (tr.metadata.historyDirection === 'redo') return 'Redo';\n\n\t// Priority 2: Block type changes\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setBlockType') {\n\t\t\treturn getBlockTypeLabel(step.nodeType, step.attrs);\n\t\t}\n\t}\n\n\t// Priority 3: Mark changes\n\tfor (const mt of newState.schema.markTypes) {\n\t\tconst branded = markType(mt);\n\t\tconst wasActive: boolean = isMarkActive(oldState, branded);\n\t\tconst nowActive: boolean = isMarkActive(newState, branded);\n\t\tif (wasActive !== nowActive) {\n\t\t\treturn `${mt} ${nowActive ? 'on' : 'off'}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n","/**\n * Registers built-in node specs on a SchemaRegistry.\n * Called during editor initialization before plugins.\n *\n * Note: Mark specs (bold, italic, underline) are registered by the\n * TextFormattingPlugin, not here. Only structural node types belong here.\n */\n\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport { createBlockElement } from '../view/DomUtils.js';\n\n/** Registers the built-in paragraph NodeSpec. */\nexport function registerBuiltinSpecs(registry: SchemaRegistry): void {\n\tregistry.registerNodeSpec({\n\t\ttype: 'paragraph',\n\t\tgroup: 'block',\n\t\tcontent: { allow: ['text'] },\n\t\ttoDOM(node) {\n\t\t\treturn createBlockElement('p', node.id);\n\t\t},\n\t\ttoHTML(_node, content) {\n\t\t\treturn `<p>${content || '<br>'}</p>`;\n\t\t},\n\t\tparseHTML: [{ tag: 'p' }, { tag: 'div', priority: 10 }],\n\t\tsanitize: { tags: ['p'] },\n\t});\n}\n","/** Locale interface and default English locale for the editor shell. */\n\nimport { type LocaleModuleMap, loadLocaleModule } from '../plugins/shared/LocaleLoader.js';\n\n// --- Locale Interface ---\n\nexport interface EditorLocale {\n\treadonly ariaLabel: string;\n\treadonly ariaDescription: string;\n\treadonly defaultPlaceholder: string;\n\t/** Live-region announcement made after Markdown is imported (paste or `setContentMarkdown`). */\n\treadonly markdownImported: string;\n}\n\n// --- Default English Locale ---\n\nexport const EDITOR_LOCALE_EN: EditorLocale = {\n\tariaLabel: 'Rich text editor',\n\tariaDescription: 'Press Escape to exit the editor',\n\tdefaultPlaceholder: 'Start typing...',\n\tmarkdownImported: 'Markdown imported',\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: LocaleModuleMap<EditorLocale> = import.meta.glob<{\n\tdefault: EditorLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadEditorLocale(lang: string): Promise<EditorLocale> {\n\treturn loadLocaleModule(localeModules, lang, EDITOR_LOCALE_EN);\n}\n","/**\n * EditorDOM: builds the editor's internal DOM structure.\n * Pure function — returns a struct of freshly created elements.\n */\n\nimport { EDITOR_LOCALE_EN, type EditorLocale } from './EditorLocale.js';\n\nexport interface EditorDOMConfig {\n\treadonly readonly?: boolean;\n\treadonly placeholder?: string;\n\treadonly dir?: 'ltr' | 'rtl';\n\treadonly locale?: EditorLocale;\n}\n\nexport interface EditorDOMElements {\n\treadonly wrapper: HTMLElement;\n\treadonly content: HTMLElement;\n\treadonly topPluginContainer: HTMLElement;\n\treadonly bottomPluginContainer: HTMLElement;\n\treadonly announcer: HTMLElement;\n}\n\n/** Creates the editor DOM tree and returns references to all key elements. */\nexport function createEditorDOM(config: EditorDOMConfig): EditorDOMElements {\n\tconst locale: EditorLocale = config.locale ?? EDITOR_LOCALE_EN;\n\tconst defaultPlaceholder = locale.defaultPlaceholder;\n\n\tconst wrapper: HTMLElement = document.createElement('div');\n\twrapper.className = 'notectl-editor';\n\twrapper.setAttribute('part', 'editor');\n\n\tconst topPluginContainer: HTMLElement = document.createElement('div');\n\ttopPluginContainer.className = 'notectl-plugin-container--top';\n\ttopPluginContainer.setAttribute('part', 'plugin-container plugin-container-top');\n\ttopPluginContainer.setAttribute('data-notectl-no-print', '');\n\n\tconst content: HTMLElement = document.createElement('div');\n\tcontent.className = 'notectl-content';\n\tcontent.setAttribute('part', 'content');\n\tcontent.contentEditable = config.readonly ? 'false' : 'true';\n\tcontent.setAttribute('role', 'textbox');\n\tcontent.setAttribute('aria-multiline', 'true');\n\tcontent.setAttribute('aria-label', locale.ariaLabel);\n\tif (config.readonly) {\n\t\tcontent.setAttribute('aria-readonly', 'true');\n\t}\n\tcontent.setAttribute('aria-description', locale.ariaDescription);\n\tcontent.setAttribute('data-default-placeholder', defaultPlaceholder);\n\tcontent.setAttribute('data-placeholder', config.placeholder ?? defaultPlaceholder);\n\n\tif (config.dir) {\n\t\twrapper.setAttribute('dir', config.dir);\n\t\tcontent.setAttribute('dir', config.dir);\n\t}\n\n\tconst bottomPluginContainer: HTMLElement = document.createElement('div');\n\tbottomPluginContainer.className = 'notectl-plugin-container--bottom';\n\tbottomPluginContainer.setAttribute('part', 'plugin-container plugin-container-bottom');\n\tbottomPluginContainer.setAttribute('data-notectl-no-print', '');\n\n\t// Screen reader announcer\n\tconst announcer: HTMLElement = document.createElement('div');\n\tannouncer.className = 'notectl-sr-only';\n\tannouncer.setAttribute('data-notectl-no-print', '');\n\tannouncer.setAttribute('aria-live', 'polite');\n\tannouncer.setAttribute('aria-atomic', 'true');\n\n\twrapper.appendChild(topPluginContainer);\n\twrapper.appendChild(content);\n\twrapper.appendChild(bottomPluginContainer);\n\twrapper.appendChild(announcer);\n\n\treturn { wrapper, content, topPluginContainer, bottomPluginContainer, announcer };\n}\n","/** Core editor styles — :host, .notectl-editor, .notectl-content, generic utilities. */\nexport const BASE_CSS = `\n/**\n * Public theme tokens.\n *\n * Declared via @property so that:\n * - DevTools surfaces them as first-class CSS variables,\n * - invalid values are rejected at the engine level instead of breaking\n * downstream rules silently,\n * - the values are interpolatable for animations.\n *\n * The registration is scoped to this shadow root. Consumers override these\n * by setting the same custom properties on a parent (e.g. ::host).\n *\n * Components use the three-tier cascade:\n * var(--notectl-<component>-<prop>, var(--notectl-<prop>, <fallback>))\n */\n@property --notectl-bg { syntax: '<color>'; initial-value: #ffffff; inherits: true; }\n@property --notectl-fg { syntax: '<color>'; initial-value: #1f2328; inherits: true; }\n@property --notectl-fg-muted { syntax: '<color>'; initial-value: #6e7781; inherits: true; }\n@property --notectl-border { syntax: '<color>'; initial-value: #d0d7de; inherits: true; }\n@property --notectl-border-focus { syntax: '<color>'; initial-value: #0969da; inherits: true; }\n@property --notectl-primary { syntax: '<color>'; initial-value: #0969da; inherits: true; }\n@property --notectl-primary-fg { syntax: '<color>'; initial-value: #ffffff; inherits: true; }\n@property --notectl-focus-ring { syntax: '<color>'; initial-value: rgba(9, 105, 218, 0.3); inherits: true; }\n\n:host {\n\tdisplay: block;\n\tfont-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n\tfont-size: 16px;\n\tline-height: 1.6;\n\tcolor: var(--notectl-fg);\n}\n\n.notectl-editor {\n\tdisplay: flex;\n\tflex-direction: column;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: var(--notectl-border-radius, 6px);\n\toverflow: hidden;\n\tbackground: var(--notectl-bg);\n\t/* Fill any explicit host height so embedders can size <notectl-editor>\n\t (e.g. height: 500px) and have the toolbar/content/footer fit inside.\n\t border-box keeps the 1px border within the host's height; min-height: 0\n\t lets the flex content shrink below its intrinsic size so internal\n\t scrolling can engage. */\n\tbox-sizing: border-box;\n\theight: 100%;\n\tmin-height: 0;\n}\n\n.notectl-editor:focus-within {\n\tborder-color: var(--notectl-border-focus);\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* Content Area */\n.notectl-content {\n\tflex: 1;\n\tpadding: 12px 16px;\n\toutline: none;\n\tmin-height: var(--notectl-content-min-height, 400px);\n\tmax-height: var(--notectl-content-max-height, none);\n\toverflow-y: auto;\n\tcursor: text;\n\tposition: relative;\n\twhite-space: pre-wrap;\n\tword-wrap: break-word;\n\t-webkit-user-select: text;\n\tuser-select: text;\n\t-moz-tab-size: 4;\n\ttab-size: 4;\n}\n\n.notectl-content p {\n\tmargin: 0;\n\tpadding: 2px 0;\n\tmin-height: 1.6em;\n}\n\n.notectl-content p + p {\n\tmargin-top: 0;\n}\n\n/* Horizontal Rule */\n.notectl-content hr {\n\tborder: none;\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin: 8px 0;\n\tpadding: 0;\n\tcursor: default;\n\tuser-select: none;\n}\n\n/* Placeholder */\n.notectl-content.notectl-content--empty::before {\n\tcontent: attr(data-placeholder);\n\tcolor: var(--notectl-fg-muted);\n\tpointer-events: none;\n\tposition: absolute;\n\ttop: 12px;\n\tinset-inline-start: 16px;\n}\n\n/* Plugin container bottom */\n.notectl-plugin-container--bottom {\n\tborder-top: 1px solid var(--notectl-border);\n\tflex-shrink: 0;\n}\n\n/* Screen reader announcements */\n.notectl-sr-only {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip: rect(0, 0, 0, 0);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Node selection */\n.notectl-node-selected {\n\toutline: 2px solid var(--notectl-primary);\n\toutline-offset: 2px;\n}\n\n/* High-contrast / forced-colors mode (Windows High Contrast, etc.) */\n@media (forced-colors: active) {\n\t.notectl-editor {\n\t\tborder-color: CanvasText;\n\t}\n\t.notectl-editor:focus-within {\n\t\tborder-color: Highlight;\n\t\tbox-shadow: 0 0 0 2px Highlight;\n\t}\n\t.notectl-content {\n\t\tcolor: CanvasText;\n\t}\n\t.notectl-plugin-container--top,\n\t.notectl-plugin-container--bottom {\n\t\tborder-color: CanvasText;\n\t}\n\t.notectl-node-selected {\n\t\toutline-color: Highlight;\n\t}\n}\n`;\n","/** Paper mode styles — viewport, surface, content overrides, and scale transitions. */\n\nimport {\n\tPAPER_MARGIN_HORIZONTAL_PX,\n\tPAPER_MARGIN_TOP_PX,\n\tPAPER_VIEWPORT_PADDING_PX,\n} from '../../model/PaperSize.js';\n\nconst PT: number = PAPER_MARGIN_TOP_PX;\nconst PH: number = PAPER_MARGIN_HORIZONTAL_PX;\nconst VP: number = PAPER_VIEWPORT_PADDING_PX;\n\nexport const PAPER_CSS: string = `\n/* Paper viewport: scroll container with centered gray background */\n.notectl-paper-viewport {\n\tflex: 1;\n\toverflow-y: auto;\n\toverflow-x: hidden;\n\tbackground: var(--notectl-paper-viewport-bg, #e8e8e8);\n\tpadding: ${VP}px 0;\n}\n\n/* Paper surface: white page with shadow, centered */\n.notectl-paper-surface {\n\tmargin: 0 auto;\n\tbackground: var(--notectl-bg);\n\tborder-radius: 2px;\n\tbox-shadow: 0 2px 8px var(--notectl-shadow, rgba(0, 0, 0, 0.15));\n\ttransform-origin: top center;\n\ttransition: transform 0.15s ease-out;\n}\n\n/* Content overrides in paper mode: wider document margins */\n.notectl-editor[data-paper-mode] .notectl-content {\n\tflex: none;\n\tpadding: ${PT}px ${PH}px;\n\tmin-height: auto;\n}\n\n/* Placeholder position override for paper mode */\n.notectl-editor[data-paper-mode] .notectl-content.notectl-content--empty::before {\n\ttop: ${PT}px;\n\tinset-inline-start: ${PH}px;\n}\n\n/* Dark mode: darker viewport background */\n:host([theme=\"dark\"]) .notectl-paper-viewport {\n\t--notectl-paper-viewport-bg: #181825;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.notectl-paper-surface {\n\t\ttransition: none !important;\n\t}\n}\n`;\n","/** Reduced motion preference (WCAG 2.3.3). */\nexport const REDUCED_MOTION_CSS = `\n@media (prefers-reduced-motion: reduce) {\n\t.notectl-toolbar-btn,\n\t.notectl-image__resize-handle,\n\t.notectl-image__size-indicator,\n\t.notectl-color-picker__swatch,\n\t.notectl-font-picker__item,\n\t.notectl-heading-picker__item,\n\t.notectl-font-size-picker__item,\n\t.ntbl-handle,\n\t.ntbl-handle-delete,\n\t.ntbl-insert-line,\n\t.ntbl-insert-btn,\n\t.ntbl-add-zone,\n\t.ntbl-add-icon,\n\t.ntbl-col-bar,\n\t.ntbl-row-bar,\n\t.ntbl-delete-table-btn,\n\t.notectl-code-block__copy {\n\t\ttransition: none !important;\n\t}\n}\n`;\n","/**\n * Editor styles using Adopted Stylesheets for Shadow DOM.\n *\n * All colors reference CSS custom properties set by the ThemeEngine.\n * The theme stylesheet is always injected first, providing all --notectl-* values.\n *\n * Only core styles (base + reduced-motion) live here.\n * Plugin-specific CSS is registered via `PluginContext.registerStyleSheet()`.\n */\n\nimport { BASE_CSS } from './styles/base.js';\nimport { PAPER_CSS } from './styles/paper.js';\nimport { REDUCED_MOTION_CSS } from './styles/reduced-motion.js';\n\n/**\n * Combined CSS for the core editor shell.\n * Plugin styles are added separately via the plugin system.\n */\nconst EDITOR_CSS: string = [BASE_CSS, REDUCED_MOTION_CSS, PAPER_CSS].join('\\n');\n\nlet cachedStyleSheet: CSSStyleSheet | null = null;\n\n/** Returns the shared editor stylesheet. */\nexport function getEditorStyleSheet(): CSSStyleSheet {\n\tif (!cachedStyleSheet) {\n\t\tcachedStyleSheet = new CSSStyleSheet();\n\t\tcachedStyleSheet.replaceSync(EDITOR_CSS);\n\t}\n\treturn cachedStyleSheet;\n}\n","/**\n * EditorThemeController — manages theme lifecycle for a Shadow DOM host.\n * Owns the theme CSSStyleSheet, system-theme media listener, and cleanup.\n */\n\nimport { getEditorStyleSheet } from './styles.js';\nimport { generateThemeCSS } from './theme/ThemeEngine.js';\nimport { type Theme, ThemePreset, resolveTheme } from './theme/ThemeTokens.js';\n\nexport class EditorThemeController {\n\tprivate themeStyleSheet: CSSStyleSheet = new CSSStyleSheet();\n\tprivate systemThemeQuery: MediaQueryList | null = null;\n\tprivate systemThemeHandler: ((e: MediaQueryListEvent) => void) | null = null;\n\tprivate pluginSheets: readonly CSSStyleSheet[] = [];\n\tprivate runtimeSheets: readonly CSSStyleSheet[] = [];\n\tprivate readonly shadow: ShadowRoot;\n\n\tconstructor(shadow: ShadowRoot) {\n\t\tthis.shadow = shadow;\n\t}\n\n\t/** Applies a theme preset or custom Theme to the shadow root. */\n\tapply(theme: ThemePreset | Theme): void {\n\t\tthis.cleanupSystemThemeListener();\n\n\t\tif (theme === ThemePreset.System) {\n\t\t\tthis.setupSystemThemeListener();\n\t\t\tconst resolved: Theme = resolveTheme(this.getSystemTheme());\n\t\t\tthis.setThemeStyleSheet(resolved);\n\t\t} else {\n\t\t\tthis.setThemeStyleSheet(resolveTheme(theme));\n\t\t}\n\t}\n\n\t/** Updates the plugin-registered stylesheets and refreshes adoptedStyleSheets. */\n\tsetPluginStyleSheets(sheets: readonly CSSStyleSheet[]): void {\n\t\tthis.pluginSheets = sheets;\n\t\tthis.refreshAdoptedStyleSheets();\n\t}\n\n\t/** Updates runtime-generated stylesheets (e.g. strict CSP dynamic rules). */\n\tsetRuntimeStyleSheets(sheets: readonly CSSStyleSheet[]): void {\n\t\tthis.runtimeSheets = sheets;\n\t\tthis.refreshAdoptedStyleSheets();\n\t}\n\n\t/** Removes the system-theme listener. */\n\tdestroy(): void {\n\t\tthis.cleanupSystemThemeListener();\n\t}\n\n\tprivate setThemeStyleSheet(theme: Theme): void {\n\t\tthis.themeStyleSheet.replaceSync(generateThemeCSS(theme));\n\t\tthis.refreshAdoptedStyleSheets();\n\t}\n\n\tprivate refreshAdoptedStyleSheets(): void {\n\t\tthis.shadow.adoptedStyleSheets = [\n\t\t\tthis.themeStyleSheet,\n\t\t\tgetEditorStyleSheet(),\n\t\t\t...this.pluginSheets,\n\t\t\t...this.runtimeSheets,\n\t\t];\n\t}\n\n\tprivate setupSystemThemeListener(): void {\n\t\tthis.systemThemeQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\t\tthis.systemThemeHandler = (_e: MediaQueryListEvent): void => {\n\t\t\tconst resolved: Theme = resolveTheme(this.getSystemTheme());\n\t\t\tthis.setThemeStyleSheet(resolved);\n\t\t};\n\t\tthis.systemThemeQuery.addEventListener('change', this.systemThemeHandler);\n\t}\n\n\tprivate cleanupSystemThemeListener(): void {\n\t\tif (this.systemThemeQuery && this.systemThemeHandler) {\n\t\t\tthis.systemThemeQuery.removeEventListener('change', this.systemThemeHandler);\n\t\t}\n\t\tthis.systemThemeQuery = null;\n\t\tthis.systemThemeHandler = null;\n\t}\n\n\tprivate getSystemTheme(): ThemePreset {\n\t\tconst query: MediaQueryList =\n\t\t\tthis.systemThemeQuery ?? window.matchMedia('(prefers-color-scheme: dark)');\n\t\treturn query.matches ? ThemePreset.Dark : ThemePreset.Light;\n\t}\n}\n","/**\n * Manages the paper-mode DOM transformation and scale-to-fit behavior.\n *\n * In paper mode, the content element is reparented into a viewport/surface\n * structure that constrains it to the exact paper width. A ResizeObserver\n * drives CSS `transform: scale()` when the container is narrower than\n * the paper, producing a Google Docs-like shrink-to-fit effect.\n */\n\nimport {\n\tPAPER_VIEWPORT_PADDING_PX,\n\ttype PaperSize,\n\tgetPaperDimensions,\n} from '../model/PaperSize.js';\nimport { setStyleProperty } from '../style/StyleRuntime.js';\n\nexport class PaperLayoutController {\n\tprivate readonly wrapper: HTMLElement;\n\tprivate readonly content: HTMLElement;\n\n\tprivate viewport: HTMLElement | null = null;\n\tprivate surface: HTMLElement | null = null;\n\n\tprivate viewportObserver: ResizeObserver | null = null;\n\tprivate surfaceObserver: ResizeObserver | null = null;\n\n\tprivate currentSize: PaperSize | null = null;\n\tprivate currentPaperWidthPx = 0;\n\tprivate currentPaperHeightPx = 0;\n\tprivate currentScale = 1;\n\tprivate compensationFrameId: number | null = null;\n\n\tconstructor(wrapper: HTMLElement, content: HTMLElement) {\n\t\tthis.wrapper = wrapper;\n\t\tthis.content = content;\n\t}\n\n\t/** Enables, changes, or disables paper mode. Pass `null` to disable. */\n\tapply(paperSize: PaperSize | null): void {\n\t\tif (paperSize === null) {\n\t\t\tthis.disable();\n\t\t\treturn;\n\t\t}\n\n\t\tconst dims = getPaperDimensions(paperSize);\n\t\tthis.currentPaperWidthPx = dims.widthPx;\n\t\tthis.currentPaperHeightPx = dims.heightPx;\n\t\tthis.currentSize = paperSize;\n\n\t\tif (!this.viewport) {\n\t\t\tthis.enable();\n\t\t}\n\n\t\tthis.updateSurfaceDimensions();\n\t\tthis.wrapper.setAttribute('data-paper-mode', paperSize);\n\t}\n\n\t/** Cleans up observers and restores original DOM. */\n\tdestroy(): void {\n\t\tthis.disable();\n\t}\n\n\t/** Returns the currently applied paper size, or null if disabled. */\n\tgetPaperSize(): PaperSize | null {\n\t\treturn this.currentSize;\n\t}\n\n\t// --- Private ---\n\n\tprivate enable(): void {\n\t\t// Create viewport (scroll container)\n\t\tthis.viewport = document.createElement('div');\n\t\tthis.viewport.className = 'notectl-paper-viewport';\n\n\t\t// Create surface (page representation)\n\t\tthis.surface = document.createElement('div');\n\t\tthis.surface.className = 'notectl-paper-surface';\n\n\t\t// Reparent content: remove from wrapper, place inside surface\n\t\tthis.wrapper.removeChild(this.content);\n\t\tthis.surface.appendChild(this.content);\n\t\tthis.viewport.appendChild(this.surface);\n\n\t\t// Insert viewport after top plugin container (index 1 in wrapper children)\n\t\tconst bottomContainer: Element | null = this.wrapper.querySelector(\n\t\t\t'.notectl-plugin-container--bottom',\n\t\t);\n\t\tif (bottomContainer) {\n\t\t\tthis.wrapper.insertBefore(this.viewport, bottomContainer);\n\t\t} else {\n\t\t\tthis.wrapper.appendChild(this.viewport);\n\t\t}\n\n\t\t// Observe viewport width for scale-to-fit\n\t\tthis.viewportObserver = new ResizeObserver((entries) => {\n\t\t\tfor (const entry of entries) {\n\t\t\t\tthis.onViewportResize(entry.contentRect.width);\n\t\t\t}\n\t\t});\n\t\tthis.viewportObserver.observe(this.viewport);\n\n\t\t// Observe surface height to compensate for layout shift from transform\n\t\tthis.surfaceObserver = new ResizeObserver((entries) => {\n\t\t\tfor (const entry of entries) {\n\t\t\t\tthis.onSurfaceResize(entry.contentRect.height);\n\t\t\t}\n\t\t});\n\t\tthis.surfaceObserver.observe(this.surface);\n\t}\n\n\tprivate disable(): void {\n\t\tif (this.compensationFrameId !== null) {\n\t\t\tcancelAnimationFrame(this.compensationFrameId);\n\t\t\tthis.compensationFrameId = null;\n\t\t}\n\t\tthis.viewportObserver?.disconnect();\n\t\tthis.viewportObserver = null;\n\t\tthis.surfaceObserver?.disconnect();\n\t\tthis.surfaceObserver = null;\n\n\t\tif (this.viewport && this.surface) {\n\t\t\t// Reparent content back to wrapper\n\t\t\tthis.surface.removeChild(this.content);\n\n\t\t\tconst bottomContainer: Element | null = this.wrapper.querySelector(\n\t\t\t\t'.notectl-plugin-container--bottom',\n\t\t\t);\n\t\t\tif (bottomContainer) {\n\t\t\t\tthis.wrapper.insertBefore(this.content, bottomContainer);\n\t\t\t} else {\n\t\t\t\tthis.wrapper.appendChild(this.content);\n\t\t\t}\n\n\t\t\t// Remove viewport from wrapper\n\t\t\tthis.wrapper.removeChild(this.viewport);\n\t\t}\n\n\t\tthis.viewport = null;\n\t\tthis.surface = null;\n\t\tthis.currentSize = null;\n\t\tthis.currentPaperWidthPx = 0;\n\t\tthis.currentPaperHeightPx = 0;\n\t\tthis.currentScale = 1;\n\t\tthis.wrapper.removeAttribute('data-paper-mode');\n\t}\n\n\tprivate updateSurfaceDimensions(): void {\n\t\tif (!this.surface) return;\n\t\tsetStyleProperty(this.surface, 'width', `${this.currentPaperWidthPx}px`);\n\t\tsetStyleProperty(this.surface, 'minHeight', `${this.currentPaperHeightPx}px`);\n\t}\n\n\tprivate onViewportResize(viewportWidth: number): void {\n\t\tif (!this.surface || this.currentPaperWidthPx === 0) return;\n\n\t\tconst availableWidth: number = viewportWidth - 2 * PAPER_VIEWPORT_PADDING_PX;\n\t\tconst scale: number = Math.min(1, availableWidth / this.currentPaperWidthPx);\n\t\tthis.currentScale = scale;\n\n\t\tsetStyleProperty(this.surface, 'transform', scale < 1 ? `scale(${scale})` : '');\n\n\t\t// Compensate margin-bottom so parent layout sees correct visual height\n\t\tthis.scheduleHeightCompensation(scale);\n\t}\n\n\tprivate onSurfaceResize(_height: number): void {\n\t\tif (!this.surface || !this.viewport) return;\n\t\tthis.scheduleHeightCompensation(this.currentScale);\n\t}\n\n\tprivate scheduleHeightCompensation(scale: number): void {\n\t\tif (this.compensationFrameId !== null) {\n\t\t\tcancelAnimationFrame(this.compensationFrameId);\n\t\t}\n\t\tthis.compensationFrameId = requestAnimationFrame(() => {\n\t\t\tthis.compensationFrameId = null;\n\t\t\tthis.updateHeightCompensation(scale);\n\t\t});\n\t}\n\n\tprivate updateHeightCompensation(scale: number): void {\n\t\tif (!this.surface) return;\n\n\t\tif (scale >= 1) {\n\t\t\tsetStyleProperty(this.surface, 'marginBottom', '');\n\t\t\treturn;\n\t\t}\n\n\t\t// transform doesn't affect layout size, so the parent sees unscaled height.\n\t\t// Negative margin-bottom compensates: surfaceHeight * (1 - scale).\n\t\tconst surfaceHeight: number = this.surface.scrollHeight;\n\t\tconst visualHeight: number = surfaceHeight * scale;\n\t\tconst compensation: number = visualHeight - surfaceHeight;\n\t\tsetStyleProperty(this.surface, 'marginBottom', `${compensation}px`);\n\t}\n}\n","/**\n * Pure functions that handle plugin auto-registration during editor init.\n *\n * Extracted from NotectlEditor to keep the Web Component shell thin.\n * These functions only need a PluginManager + config — zero coupling to DOM.\n *\n * Static imports are used instead of dynamic `import()` to avoid\n * double-bundling issues when consumers re-bundle @notectl/core.\n */\n\nimport type { Plugin } from '../plugins/Plugin.js';\nimport type { PluginManager } from '../plugins/PluginManager.js';\nimport { CaretNavigationPlugin } from '../plugins/caret-navigation/CaretNavigationPlugin.js';\nimport { GapCursorPlugin } from '../plugins/gap-cursor/GapCursorPlugin.js';\nimport {\n\ttype TextFormattingConfig,\n\tTextFormattingPlugin,\n} from '../plugins/text-formatting/TextFormattingPlugin.js';\nimport type { ToolbarOverflowBehavior } from '../plugins/toolbar/ToolbarOverflowBehavior.js';\nimport {\n\ttype ToolbarGroupConfig,\n\ttype ToolbarLayoutConfig,\n\tToolbarPlugin,\n} from '../plugins/toolbar/ToolbarPlugin.js';\nimport type { ToolbarConfig, ToolbarGroupInput } from './EditorConfig.js';\n\ntype ToolbarInput = ReadonlyArray<ReadonlyArray<Plugin>> | ToolbarConfig;\n\nfunction isToolbarConfig(toolbar: ToolbarInput): toolbar is ToolbarConfig {\n\treturn !Array.isArray(toolbar);\n}\n\ninterface NormalizedPluginGroup {\n\treadonly plugins: ReadonlyArray<Plugin>;\n\treadonly label: string | undefined;\n}\n\nfunction normalizePluginGroup(group: ToolbarGroupInput): NormalizedPluginGroup {\n\tif (Array.isArray(group)) {\n\t\treturn { plugins: group, label: undefined };\n\t}\n\tconst obj: { readonly plugins: ReadonlyArray<Plugin>; readonly label?: string } = group as {\n\t\treadonly plugins: ReadonlyArray<Plugin>;\n\t\treadonly label?: string;\n\t};\n\treturn { plugins: obj.plugins, label: obj.label };\n}\n\n/**\n * Processes the declarative `toolbar` config: registers a ToolbarPlugin\n * with layout groups, then registers all plugins from the toolbar groups.\n * Accepts both the shorthand array and the full ToolbarConfig object.\n */\nexport function processToolbarConfig(pm: PluginManager, toolbar: ToolbarInput | undefined): void {\n\tif (!toolbar) return;\n\n\tconst pluginGroups: ReadonlyArray<ToolbarGroupInput> = isToolbarConfig(toolbar)\n\t\t? toolbar.groups\n\t\t: toolbar;\n\tconst overflow: ToolbarOverflowBehavior | undefined = isToolbarConfig(toolbar)\n\t\t? toolbar.overflow\n\t\t: undefined;\n\n\tconst groups: ToolbarGroupConfig[] = [];\n\tfor (const rawGroup of pluginGroups) {\n\t\tconst { plugins, label } = normalizePluginGroup(rawGroup);\n\t\tconst pluginIds: string[] = [];\n\t\tfor (const plugin of plugins) {\n\t\t\tpluginIds.push(plugin.id);\n\t\t\tpm.register(plugin);\n\t\t}\n\t\tgroups.push(label !== undefined ? { plugins: pluginIds, label } : pluginIds);\n\t}\n\n\tconst layoutConfig: ToolbarLayoutConfig = { groups, overflow };\n\tpm.register(new ToolbarPlugin(layoutConfig));\n}\n\n/**\n * Auto-registers essential plugins if they were not explicitly provided.\n * Includes TextFormattingPlugin, CaretNavigationPlugin, and GapCursorPlugin.\n */\nexport function ensureEssentialPlugins(\n\tpm: PluginManager,\n\tfeatures?: Partial<TextFormattingConfig>,\n): void {\n\tensureTextFormattingPlugin(pm, features);\n\tensureCaretNavigationPlugin(pm);\n\tensureGapCursorPlugin(pm);\n}\n\nfunction ensureTextFormattingPlugin(\n\tpm: PluginManager,\n\tfeatures?: Partial<TextFormattingConfig>,\n): void {\n\tif (pm.get('text-formatting') !== undefined) return;\n\n\tconst config: TextFormattingConfig = {\n\t\tbold: features?.bold ?? true,\n\t\titalic: features?.italic ?? true,\n\t\tunderline: features?.underline ?? true,\n\t};\n\n\tpm.register(new TextFormattingPlugin(config));\n}\n\nfunction ensureCaretNavigationPlugin(pm: PluginManager): void {\n\tif (pm.get('caret-navigation') !== undefined) return;\n\tpm.register(new CaretNavigationPlugin());\n}\n\nfunction ensureGapCursorPlugin(pm: PluginManager): void {\n\tif (pm.get('gap-cursor') !== undefined) return;\n\tpm.register(new GapCursorPlugin());\n}\n","/**\n * Orchestrates editor initialization: theme, DOM, plugins, view, and input setup.\n *\n * Extracted from NotectlEditor to keep the Web Component shell thin.\n * All wiring logic lives here; the editor stores the returned components.\n */\n\nimport { DecorationSet } from '../decorations/Decoration.js';\nimport { LocaleService, LocaleServiceKey } from '../i18n/LocaleService.js';\nimport { InputManager } from '../input/InputManager.js';\nimport type { CompositionState } from '../model/CompositionState.js';\nimport type { Document } from '../model/Document.js';\nimport { schemaFromRegistry } from '../model/Schema.js';\nimport { selectionsEqual } from '../model/Selection.js';\nimport { getTextDirection } from '../platform/Platform.js';\nimport type { Plugin } from '../plugins/Plugin.js';\nimport { PluginManager } from '../plugins/PluginManager.js';\nimport { BEFORE_PRINT } from '../plugins/print/PrintTypes.js';\nimport { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { navigateFromGapCursor } from '../view/CaretNavigation.js';\nimport { EditorView } from '../view/EditorView.js';\nimport { buildAnnouncement } from './Announcer.js';\nimport { registerBuiltinSpecs } from './BuiltinSpecs.js';\nimport { isEditorEmpty } from './ContentSerializer.js';\nimport type { NotectlEditorConfig } from './EditorConfig.js';\nimport type { EditorConfigController } from './EditorConfigController.js';\nimport { type EditorDOMElements, createEditorDOM } from './EditorDOM.js';\nimport type { EditorEventEmitter } from './EditorEventEmitter.js';\nimport { EDITOR_LOCALE_EN, type EditorLocale, loadEditorLocale } from './EditorLocale.js';\nimport type { EditorStyleCoordinator } from './EditorStyleCoordinator.js';\nimport { EditorThemeController } from './EditorThemeController.js';\nimport { PaperLayoutController } from './PaperLayoutController.js';\nimport { ensureEssentialPlugins, processToolbarConfig } from './PluginBootstrapper.js';\nimport { ThemePreset } from './theme/ThemeTokens.js';\n\n/** Dependencies provided by the NotectlEditor host element. */\nexport interface InitializerDeps {\n\treadonly shadow: ShadowRoot;\n\treadonly config: NotectlEditorConfig;\n\t/** Resolved dir from the host element's attribute (fallback when config.dir is unset). */\n\treadonly hostDir: 'ltr' | 'rtl' | undefined;\n\treadonly configController: EditorConfigController;\n\treadonly styleCoordinator: EditorStyleCoordinator;\n\treadonly events: EditorEventEmitter;\n\treadonly preInitPlugins: readonly Plugin[];\n\tisCancelled?(): boolean;\n}\n\n/** Components created during initialization, returned to the editor for storage. */\nexport interface InitResult {\n\treadonly view: EditorView;\n\treadonly inputManager: InputManager;\n\treadonly pluginManager: PluginManager;\n\treadonly domElements: EditorDOMElements;\n\treadonly themeController: EditorThemeController;\n\treadonly paperLayout: PaperLayoutController | null;\n\t/** Writes a message to the screen-reader live region (no-op after teardown). */\n\treadonly announce: (text: string) => void;\n\t/** Localized \"Markdown imported\" announcement, resolved from the editor locale. */\n\treadonly markdownImportedMessage: string;\n\t/** Nulls closure-captured references to prevent stale dispatches after destroy. */\n\treadonly release: () => void;\n}\n\n/** Performs the full editor initialization sequence. Returns null if setup fails. */\nexport async function initializeEditor(deps: InitializerDeps): Promise<InitResult | null> {\n\tconst session: EditorInitSession = new EditorInitSession(deps);\n\treturn session.run();\n}\n\n// --- Internal init session -----------------------------------------------\n\n/**\n * Encapsulates the mutable state and phases of editor initialization.\n *\n * Each phase method is independently readable and focused on a single concern.\n * The class exists to share mutable view/pluginManager references between\n * dispatch, cleanup, and the plugin onBeforeReady callback.\n */\nclass EditorInitSession {\n\tprivate readonly cfg: NotectlEditorConfig;\n\tprivate themeController: EditorThemeController | null = null;\n\tprivate domElements: EditorDOMElements | null = null;\n\tprivate paperLayout: PaperLayoutController | null = null;\n\tprivate pluginManager: PluginManager | null = null;\n\tprivate inputManager: InputManager | null = null;\n\tprivate view: EditorView | null = null;\n\tprivate locale: EditorLocale = EDITOR_LOCALE_EN;\n\tprivate cleanedUp = false;\n\n\tconstructor(private readonly deps: InitializerDeps) {\n\t\tthis.cfg = deps.config;\n\t}\n\n\t/** Writes a message to the screen-reader live region. No-op once the DOM is gone. */\n\tprivate announce(text: string): void {\n\t\tconst announcer: HTMLElement | undefined = this.domElements?.announcer;\n\t\tif (announcer) announcer.textContent = text;\n\t}\n\n\t/** Orchestrates the full initialization sequence. */\n\tasync run(): Promise<InitResult | null> {\n\t\ttry {\n\t\t\tthis.setupTheme();\n\t\t\tif (this.isCancelled()) return this.abort();\n\n\t\t\tconst locale: EditorLocale = await resolveLocale(this.cfg);\n\t\t\tif (this.isCancelled()) return this.abort();\n\t\t\tthis.locale = locale;\n\n\t\t\tthis.setupDOM(locale);\n\t\t\tthis.setupPlugins();\n\t\t\tawait this.initPluginsAndView();\n\n\t\t\tif (\n\t\t\t\tthis.isCancelled() ||\n\t\t\t\t!this.view ||\n\t\t\t\t!this.inputManager ||\n\t\t\t\t!this.pluginManager ||\n\t\t\t\t!this.domElements ||\n\t\t\t\t!this.themeController\n\t\t\t) {\n\t\t\t\treturn this.abort();\n\t\t\t}\n\n\t\t\tconst view: EditorView = this.view;\n\t\t\tconst inputManager: InputManager = this.inputManager;\n\t\t\tconst pluginManager: PluginManager = this.pluginManager;\n\t\t\tconst domElements: EditorDOMElements = this.domElements;\n\t\t\tconst themeController: EditorThemeController = this.themeController;\n\n\t\t\tthis.finalizeSetup(pluginManager, domElements);\n\n\t\t\treturn {\n\t\t\t\tview,\n\t\t\t\tinputManager,\n\t\t\t\tpluginManager,\n\t\t\t\tdomElements,\n\t\t\t\tthemeController,\n\t\t\t\tpaperLayout: this.paperLayout,\n\t\t\t\tannounce: (text: string): void => this.announce(text),\n\t\t\t\tmarkdownImportedMessage: this.locale.markdownImported,\n\t\t\t\trelease: (): void => {\n\t\t\t\t\tthis.view = null;\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tawait this.cleanup();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate setupTheme(): void {\n\t\tthis.themeController = new EditorThemeController(this.deps.shadow);\n\t\tthis.deps.styleCoordinator.setup(this.deps.shadow, this.cfg.styleNonce, this.themeController);\n\t\tthis.themeController.apply(this.cfg.theme ?? ThemePreset.Light);\n\t}\n\n\tprivate setupDOM(locale: EditorLocale): void {\n\t\tthis.domElements = createEditorDOM({\n\t\t\treadonly: this.cfg.readonly,\n\t\t\tplaceholder: this.cfg.placeholder,\n\t\t\tdir: this.cfg.dir ?? this.deps.hostDir,\n\t\t\tlocale,\n\t\t});\n\t\tthis.deps.shadow.appendChild(this.domElements.wrapper);\n\n\t\tif (this.cfg.paperSize) {\n\t\t\tthis.paperLayout = new PaperLayoutController(\n\t\t\t\tthis.domElements.wrapper,\n\t\t\t\tthis.domElements.content,\n\t\t\t);\n\t\t\tthis.paperLayout.apply(this.cfg.paperSize);\n\t\t}\n\t}\n\n\tprivate setupPlugins(): void {\n\t\tif (!this.domElements) return;\n\n\t\tthis.pluginManager = new PluginManager({ logger: this.cfg.logger });\n\t\tthis.pluginManager.registerService(\n\t\t\tLocaleServiceKey,\n\t\t\tnew LocaleService(this.cfg.locale ?? 'browser'),\n\t\t);\n\t\tregisterBuiltinSpecs(this.pluginManager.schemaRegistry);\n\t\tprocessToolbarConfig(this.pluginManager, this.cfg.toolbar);\n\n\t\tfor (const plugin of this.cfg.plugins ?? []) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t\tfor (const plugin of this.deps.preInitPlugins) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t\tensureEssentialPlugins(this.pluginManager, this.cfg.features);\n\n\t\tthis.domElements.content.addEventListener('focus', () =>\n\t\t\tthis.deps.events.emit('focus', undefined),\n\t\t);\n\t\tthis.domElements.content.addEventListener('blur', () =>\n\t\t\tthis.deps.events.emit('blur', undefined),\n\t\t);\n\t}\n\n\tprivate async initPluginsAndView(): Promise<void> {\n\t\tif (!this.pluginManager || !this.domElements || !this.themeController) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst dom: EditorDOMElements = this.domElements;\n\t\tconst pm: PluginManager = this.pluginManager;\n\t\tconst tc: EditorThemeController = this.themeController;\n\n\t\tawait pm.init({\n\t\t\tisCancelled: () => this.isCancelled(),\n\t\t\tgetState: () => {\n\t\t\t\tif (!this.view) throw new Error('View not initialized');\n\t\t\t\treturn this.view.getState();\n\t\t\t},\n\t\t\tdispatch: (tr: Transaction) => this.dispatch(tr),\n\t\t\tgetContainer: () => dom.content,\n\t\t\tgetPluginContainer: (position) =>\n\t\t\t\tposition === 'top' ? dom.topPluginContainer : dom.bottomPluginContainer,\n\t\t\tannounce: (text: string) => this.announce(text),\n\t\t\thasAnnouncement: () => !!dom.announcer?.textContent,\n\t\t\tgetCompositionState: () => {\n\t\t\t\tconst tracker = this.inputManager?.compositionTracker;\n\t\t\t\tif (tracker) return tracker;\n\t\t\t\treturn IDLE_COMPOSITION_STATE;\n\t\t\t},\n\t\t\tonBeforeReady: () => this.createInputAndView(dom, pm, tc),\n\t\t});\n\t}\n\n\t/** Creates InputManager and EditorView. Called synchronously by PluginManager. */\n\tprivate createInputAndView(\n\t\tdom: EditorDOMElements,\n\t\tpm: PluginManager,\n\t\ttc: EditorThemeController,\n\t): void {\n\t\tif (this.isCancelled() || !this.pluginManager || !this.themeController) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst schema = schemaFromRegistry(pm.schemaRegistry);\n\t\tconst state: EditorState = EditorState.create({ schema });\n\n\t\t// Resolved once at init (config-time), mirroring `pasteMarkdown` below.\n\t\t// Runtime toggling of `markdown` is intentionally not supported, so both\n\t\t// axes of the markdown gate stay consistent.\n\t\tconst markdownShorthand: boolean = this.deps.configController.markdownShorthand;\n\n\t\tthis.inputManager = new InputManager(dom.content, {\n\t\t\tgetState: () => {\n\t\t\t\tif (!this.view) throw new Error('View not initialized');\n\t\t\t\treturn this.view.getState();\n\t\t\t},\n\t\t\tdispatch: (tr: Transaction) => this.dispatch(tr),\n\t\t\tsyncSelection: () => this.view?.syncSelection(),\n\t\t\tundo: () => this.view?.undo(),\n\t\t\tredo: () => this.view?.redo(),\n\t\t\tschemaRegistry: pm.schemaRegistry,\n\t\t\tkeymapRegistry: pm.keymapRegistry,\n\t\t\tinputRuleRegistry: pm.inputRuleRegistry,\n\t\t\tfileHandlerRegistry: pm.fileHandlerRegistry,\n\t\t\tisReadOnly: () => this.deps.configController.isReadOnly,\n\t\t\tshouldApplyInputRules: () => markdownShorthand,\n\t\t\tgetPasteInterceptors: () => pm.getPasteInterceptors(),\n\t\t\tpasteMarkdown: this.deps.configController.pasteMarkdown,\n\t\t\tgetMarkdownSyntaxExtensions: () => pm.markdownSyntaxRegistry.getExtensions(),\n\t\t\tgetTextInputInterceptors: () => pm.getTextInputInterceptors(),\n\t\t\tgetTextDirection,\n\t\t\tnavigateFromGapCursor,\n\t\t\tannounce: (text: string) => this.announce(text),\n\t\t\tmarkdownImportedMessage: this.locale.markdownImported,\n\t\t});\n\n\t\tthis.view = new EditorView(dom.content, {\n\t\t\tstate,\n\t\t\tschemaRegistry: pm.schemaRegistry,\n\t\t\tkeymapRegistry: pm.keymapRegistry,\n\t\t\tfileHandlerRegistry: pm.fileHandlerRegistry,\n\t\t\tnodeViewRegistry: pm.nodeViewRegistry,\n\t\t\tmaxHistoryDepth: this.cfg.maxHistoryDepth,\n\t\t\tgetDecorations: (s, tr) => pm.collectDecorations(s, tr) ?? DecorationSet.empty,\n\t\t\tonStateChange: (oldState, newState, tr) => {\n\t\t\t\thandleStateChange(oldState, newState, tr, dom, pm, this.deps.events);\n\t\t\t},\n\t\t\tisReadOnly: () => this.deps.configController.isReadOnly,\n\t\t\tisReadonlyBypassed: () => pm.isReadonlyBypassed(),\n\t\t\tcompositionState: this.inputManager.compositionTracker,\n\t\t});\n\n\t\tupdateEmptyState(dom.content, this.view.getState().doc);\n\n\t\tconst pluginSheets: readonly CSSStyleSheet[] = pm.getPluginStyleSheets();\n\t\tif (pluginSheets.length > 0) {\n\t\t\ttc.setPluginStyleSheets(pluginSheets);\n\t\t}\n\t}\n\n\tprivate finalizeSetup(pm: PluginManager, dom: EditorDOMElements): void {\n\t\tif (this.cfg.readonly) {\n\t\t\tpm.setReadOnly(true);\n\t\t}\n\n\t\tpm.onEvent(BEFORE_PRINT, (event) => {\n\t\t\tif (!event.options.paperSize && this.deps.configController.getPaperSize()) {\n\t\t\t\tevent.options = {\n\t\t\t\t\t...event.options,\n\t\t\t\t\tpaperSize: this.deps.configController.getPaperSize(),\n\t\t\t\t};\n\t\t\t}\n\t\t});\n\n\t\tif (this.cfg.autofocus) {\n\t\t\tconst content: HTMLElement = dom.content;\n\t\t\trequestAnimationFrame(() => content.focus());\n\t\t}\n\t}\n\n\tprivate dispatch(tr: Transaction): void {\n\t\tif (!this.view || !this.pluginManager) return;\n\t\tthis.pluginManager.dispatchWithMiddleware(tr, this.view.getState(), (finalTr) =>\n\t\t\tthis.view?.dispatch(finalTr),\n\t\t);\n\t}\n\n\tprivate isCancelled(): boolean {\n\t\treturn this.deps.isCancelled?.() ?? false;\n\t}\n\n\tprivate async abort(): Promise<null> {\n\t\tawait this.cleanup();\n\t\treturn null;\n\t}\n\n\tprivate async cleanup(): Promise<void> {\n\t\tif (this.cleanedUp) return;\n\t\tthis.cleanedUp = true;\n\n\t\tconst liveView: EditorView | null = this.view;\n\t\tthis.view = null;\n\t\tthis.paperLayout?.destroy();\n\t\tthis.paperLayout = null;\n\t\tthis.deps.styleCoordinator.teardown(this.deps.shadow, this.themeController);\n\t\tthis.themeController?.destroy();\n\t\tthis.themeController = null;\n\t\tthis.inputManager?.destroy();\n\t\tthis.inputManager = null;\n\t\tliveView?.destroy();\n\t\tconst livePluginManager: PluginManager | null = this.pluginManager;\n\t\tthis.pluginManager = null;\n\t\tawait livePluginManager?.destroy();\n\t\tthis.domElements?.wrapper.remove();\n\t\tthis.domElements = null;\n\t}\n}\n\n// --- Standalone helpers --------------------------------------------------\n\nconst IDLE_COMPOSITION_STATE: CompositionState = { isComposing: false, activeBlockId: null };\n\nasync function resolveLocale(cfg: NotectlEditorConfig): Promise<EditorLocale> {\n\tconst localeService = new LocaleService(cfg.locale ?? 'browser');\n\tconst resolvedLang: string = localeService.getLocale();\n\treturn resolvedLang === 'en' ? EDITOR_LOCALE_EN : await loadEditorLocale(resolvedLang);\n}\n\nfunction handleStateChange(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n\tdomElements: EditorDOMElements,\n\tpluginManager: PluginManager,\n\tevents: EditorEventEmitter,\n): void {\n\tconst announcer: HTMLElement | undefined = domElements.announcer;\n\tif (announcer) announcer.textContent = '';\n\n\tpluginManager.notifyStateChange(oldState, newState, tr);\n\tupdateEmptyState(domElements.content, newState.doc);\n\n\tevents.emit('stateChange', { oldState, newState, transaction: tr });\n\n\tif (!selectionsEqual(oldState.selection, newState.selection)) {\n\t\tevents.emit('selectionChange', { selection: newState.selection });\n\t}\n\n\tif (!announcer?.textContent) {\n\t\tconst announcement: string | null = buildAnnouncement(oldState, newState, tr);\n\t\tif (announcement && announcer) {\n\t\t\tannouncer.textContent = announcement;\n\t\t}\n\t}\n}\n\nfunction updateEmptyState(contentEl: HTMLElement, doc: Document | undefined): void {\n\tcontentEl.classList.toggle('notectl-content--empty', isEditorEmpty(doc));\n}\n","/**\n * Manages editor lifecycle state: initialization flag, ready promise, and pre-init plugins.\n *\n * Pure state machine with no DOM or external dependencies.\n */\n\nimport type { Plugin } from '../plugins/Plugin.js';\n\ntype LifecycleState = 'idle' | 'initializing' | 'ready' | 'failed';\n\nexport class EditorLifecycleCoordinator {\n\tprivate state: LifecycleState = 'idle';\n\tprivate readyPromiseResolve: (() => void) | null = null;\n\tprivate readyPromiseReject: ((reason?: unknown) => void) | null = null;\n\tprivate readyPromise: Promise<void>;\n\tprivate preInitPlugins: Plugin[] = [];\n\n\tconstructor() {\n\t\tthis.readyPromise = this.createReadyPromise();\n\t}\n\n\t/** Returns whether the editor has been initialized. */\n\tisInitialized(): boolean {\n\t\treturn this.state === 'initializing' || this.state === 'ready';\n\t}\n\n\t/** Marks the editor as initialized. Returns false if already initialized. */\n\tmarkInitialized(): boolean {\n\t\tif (this.state === 'initializing' || this.state === 'ready') return false;\n\t\tif (this.state === 'failed') {\n\t\t\tthis.readyPromise = this.createReadyPromise();\n\t\t}\n\t\tthis.state = 'initializing';\n\t\treturn true;\n\t}\n\n\t/** Returns a promise that resolves when the editor is ready. */\n\twhenReady(): Promise<void> {\n\t\treturn this.readyPromise;\n\t}\n\n\t/** Resolves the ready promise. */\n\tresolveReady(): void {\n\t\tif (this.state !== 'initializing') return;\n\t\tthis.state = 'ready';\n\t\tthis.readyPromiseResolve?.();\n\t\tthis.readyPromiseResolve = null;\n\t\tthis.readyPromiseReject = null;\n\t}\n\n\t/** Rejects the current ready promise and allows a later retry. */\n\tfailReady(reason: unknown): void {\n\t\tif (this.state !== 'initializing') return;\n\t\tthis.state = 'failed';\n\t\tthis.readyPromiseReject?.(reason);\n\t\tthis.readyPromiseResolve = null;\n\t\tthis.readyPromiseReject = null;\n\t}\n\n\t/** Registers a plugin before initialization. Throws if already initialized. */\n\tregisterPreInitPlugin(plugin: Plugin): void {\n\t\tif (this.state === 'initializing' || this.state === 'ready') {\n\t\t\tthrow new Error(\n\t\t\t\t'Cannot register plugins after initialization. Register before calling init() or adding to DOM.',\n\t\t\t);\n\t\t}\n\t\tthis.preInitPlugins.push(plugin);\n\t}\n\n\t/** Returns and clears the pre-init plugins list. */\n\tconsumePreInitPlugins(): Plugin[] {\n\t\tconst plugins: Plugin[] = this.preInitPlugins;\n\t\tthis.preInitPlugins = [];\n\t\treturn plugins;\n\t}\n\n\t/** Restores previously consumed pre-init plugins for a retry attempt. */\n\trestorePreInitPlugins(plugins: readonly Plugin[]): void {\n\t\tif (plugins.length === 0) return;\n\t\tthis.preInitPlugins = [...plugins, ...this.preInitPlugins];\n\t}\n\n\t/** Resets all lifecycle state, including a fresh ready promise. */\n\treset(): void {\n\t\tthis.state = 'idle';\n\t\tthis.preInitPlugins = [];\n\t\tthis.readyPromise = this.createReadyPromise();\n\t}\n\n\tprivate createReadyPromise(): Promise<void> {\n\t\tconst readyPromise = new Promise<void>((resolve, reject) => {\n\t\t\tthis.readyPromiseResolve = resolve;\n\t\t\tthis.readyPromiseReject = reject;\n\t\t});\n\t\t// Suppress unhandled rejection noise when init fails before a caller awaits whenReady().\n\t\tvoid readyPromise.catch(() => undefined);\n\t\treturn readyPromise;\n\t}\n}\n","/**\n * Manages runtime CSS stylesheet registration for the editor's Shadow DOM.\n *\n * Extracted from NotectlEditor to keep the Web Component shell thin.\n * Owns the runtime CSSStyleSheet and coordinates with EditorThemeController.\n */\n\nimport {\n\tcreateRuntimeStyleSheet,\n\tregisterStyleRoot,\n\tunregisterStyleRoot,\n} from '../style/StyleRuntime.js';\nimport type { EditorThemeController } from './EditorThemeController.js';\n\nexport class EditorStyleCoordinator {\n\tprivate runtimeStyleSheet: CSSStyleSheet | null = null;\n\n\t/** Sets up the runtime stylesheet and registers the shadow root as a style root. */\n\tsetup(\n\t\tshadow: ShadowRoot,\n\t\tnonce: string | undefined,\n\t\tthemeController: EditorThemeController | null,\n\t): void {\n\t\tunregisterStyleRoot(shadow);\n\t\tthis.runtimeStyleSheet = createRuntimeStyleSheet();\n\n\t\tthemeController?.setRuntimeStyleSheets(this.runtimeStyleSheet ? [this.runtimeStyleSheet] : []);\n\n\t\tregisterStyleRoot(shadow, {\n\t\t\tnonce,\n\t\t\tsheet: this.runtimeStyleSheet,\n\t\t});\n\t}\n\n\t/** Tears down the runtime stylesheet and unregisters the shadow root. */\n\tteardown(shadow: ShadowRoot | null, themeController: EditorThemeController | null): void {\n\t\tif (shadow) {\n\t\t\tunregisterStyleRoot(shadow);\n\t\t}\n\t\tthis.runtimeStyleSheet = null;\n\t\tthemeController?.setRuntimeStyleSheets([]);\n\t}\n}\n","/**\n * NotectlEditor Web Component — the public-facing editor element.\n *\n * Thin shell that delegates initialization to EditorInitializer\n * and exposes the public API surface (content, commands, events, state).\n */\n\nimport { selectAll } from '../commands/Commands.js';\nimport type { InputManager } from '../input/InputManager.js';\nimport type { Document } from '../model/Document.js';\nimport type { PaperSize } from '../model/PaperSize.js';\nimport { isMarkAllowed } from '../model/Schema.js';\nimport type {\n\tEventKey,\n\tPlugin,\n\tPluginConfig,\n\tPluginEventCallback,\n\tServiceKey,\n} from '../plugins/Plugin.js';\nimport type { PluginManager } from '../plugins/PluginManager.js';\nimport {\n\tcreateStaticReplicaError,\n\tisStaticHostReplica,\n} from '../plugins/print/StaticHostMarker.js';\nimport type {\n\tMarkdownParseOptions,\n\tMarkdownSerializeOptions,\n} from '../serialization/MarkdownTypes.js';\nimport type {\n\tContentCSSResult,\n\tContentHTMLOptions,\n\tSetContentHTMLOptions,\n} from '../serialization/index.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport type { EditorView } from '../view/EditorView.js';\nimport {\n\tgetEditorContentHTML,\n\tgetEditorContentMarkdown,\n\tgetEditorJSON,\n\tgetEditorText,\n\tisEditorEmpty,\n\tsetEditorContentHTML,\n\tsetEditorContentMarkdown,\n\tsetEditorJSON,\n\tsetEditorText,\n} from './ContentSerializer.js';\nimport { EditorConfigController } from './EditorConfigController.js';\nimport type { EditorDOMElements } from './EditorDOM.js';\nimport { EditorEventEmitter, type EditorEventMap } from './EditorEventEmitter.js';\nimport { initializeEditor } from './EditorInitializer.js';\nimport { EditorLifecycleCoordinator } from './EditorLifecycleCoordinator.js';\nimport { EditorStyleCoordinator } from './EditorStyleCoordinator.js';\nimport type { EditorThemeController } from './EditorThemeController.js';\nimport { PaperLayoutController } from './PaperLayoutController.js';\nimport type { Theme, ThemePreset } from './theme/ThemeTokens.js';\n\nexport type { NotectlEditorConfig, ToolbarConfig } from './EditorConfig.js';\nexport type { StateChangeEvent } from './EditorEventEmitter.js';\nexport type { EditorEventMap } from './EditorEventEmitter.js';\n\nexport class NotectlEditor extends HTMLElement {\n\tprivate view: EditorView | null = null;\n\tprivate inputManager: InputManager | null = null;\n\tprivate pluginManager: PluginManager | null = null;\n\tprivate domElements: EditorDOMElements | null = null;\n\tprivate readonly configController = new EditorConfigController();\n\tprivate readonly events = new EditorEventEmitter();\n\tprivate readonly lifecycle = new EditorLifecycleCoordinator();\n\tprivate readonly styleCoordinator = new EditorStyleCoordinator();\n\tprivate themeController: EditorThemeController | null = null;\n\tprivate paperLayout: PaperLayoutController | null = null;\n\tprivate pendingInitPromise: Promise<import('./EditorInitializer.js').InitResult | null> | null =\n\t\tnull;\n\tprivate cancelPendingInit: (() => void) | null = null;\n\tprivate autoInitToken = 0;\n\tprivate autoInitQueued = false;\n\tprivate initVersion = 0;\n\tprivate releaseInit: (() => void) | null = null;\n\tprivate announce: ((text: string) => void) | null = null;\n\tprivate markdownImportedMessage = 'Markdown imported';\n\n\tstatic get observedAttributes(): string[] {\n\t\treturn ['placeholder', 'readonly', 'theme', 'paper-size', 'dir'];\n\t}\n\n\tconnectedCallback(): void {\n\t\tif (isStaticHostReplica(this)) return;\n\t\tif (this.lifecycle.isInitialized()) return;\n\t\tthis.scheduleAutoInit();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tsetTimeout(() => {\n\t\t\tif (!this.isConnected) {\n\t\t\t\tthis.destroy();\n\t\t\t}\n\t\t}, 0);\n\t}\n\n\tattributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n\t\tthis.configController.applyAttribute(name, newValue, this.getConfigDeps());\n\t}\n\n\t/** Registers a plugin before initialization. */\n\tregisterPlugin(plugin: Plugin): void {\n\t\tthis.lifecycle.registerPreInitPlugin(plugin);\n\t}\n\n\t/**\n\t * Initializes the editor with the given config. Throws on static print\n\t * replicas (`data-notectl-static`): they carry replicated print markup and\n\t * must never boot a live editor over it.\n\t */\n\tasync init(config?: import('./EditorConfig.js').NotectlEditorConfig): Promise<void> {\n\t\tif (isStaticHostReplica(this)) {\n\t\t\tthrow createStaticReplicaError();\n\t\t}\n\t\tthis.cancelAutoInit();\n\t\tif (!this.lifecycle.markInitialized()) return;\n\t\tif (config) this.configController.setConfig(config);\n\n\t\tlet shadow: ShadowRoot;\n\t\ttry {\n\t\t\tshadow = this.ensureFreshShadowRoot();\n\t\t} catch (error) {\n\t\t\tthis.lifecycle.failReady(error);\n\t\t\tthrow error;\n\t\t}\n\t\tconst initVersion = ++this.initVersion;\n\t\tlet cancelled = false;\n\t\tconst cancel = (): void => {\n\t\t\tcancelled = true;\n\t\t};\n\t\tthis.cancelPendingInit = cancel;\n\t\tconst preInitPlugins = this.lifecycle.consumePreInitPlugins();\n\n\t\tconst initPromise = initializeEditor({\n\t\t\tshadow,\n\t\t\tconfig: this.configController.getConfig(),\n\t\t\thostDir: (this.getAttribute('dir') as 'ltr' | 'rtl' | null) ?? undefined,\n\t\t\tconfigController: this.configController,\n\t\t\tstyleCoordinator: this.styleCoordinator,\n\t\t\tevents: this.events,\n\t\t\tpreInitPlugins,\n\t\t\tisCancelled: () => cancelled || initVersion !== this.initVersion,\n\t\t});\n\t\tthis.pendingInitPromise = initPromise;\n\t\ttry {\n\t\t\tconst result = await initPromise;\n\t\t\tif (!result) {\n\t\t\t\tif (cancelled || initVersion !== this.initVersion) return;\n\t\t\t\tthrow new Error('Editor initialization completed without a result.');\n\t\t\t}\n\t\t\tif (initVersion !== this.initVersion) return;\n\n\t\t\tthis.view = result.view;\n\t\t\tthis.inputManager = result.inputManager;\n\t\t\tthis.pluginManager = result.pluginManager;\n\t\t\tthis.domElements = result.domElements;\n\t\t\tthis.themeController = result.themeController;\n\t\t\tthis.paperLayout = result.paperLayout;\n\t\t\tthis.announce = result.announce;\n\t\t\tthis.markdownImportedMessage = result.markdownImportedMessage;\n\t\t\tthis.releaseInit = result.release;\n\n\t\t\tthis.lifecycle.resolveReady();\n\t\t\tthis.events.emit('ready', undefined);\n\t\t} catch (error) {\n\t\t\tif (initVersion === this.initVersion) {\n\t\t\t\tthis.lifecycle.restorePreInitPlugins(preInitPlugins);\n\t\t\t\tthis.lifecycle.failReady(error);\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tif (this.pendingInitPromise === initPromise) {\n\t\t\t\tthis.pendingInitPromise = null;\n\t\t\t\tthis.cancelPendingInit = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Returns whether the editor is in read-only mode. */\n\tget isReadOnly(): boolean {\n\t\treturn this.configController.isReadOnly;\n\t}\n\n\t// --- Content API ---\n\n\t/** Returns the document as JSON. */\n\tgetJSON(): Document {\n\t\tif (!this.view) throw new Error('Editor not initialized');\n\t\treturn getEditorJSON(this.view.getState());\n\t}\n\n\t/** Sets the document from JSON. */\n\tsetJSON(doc: Document): void {\n\t\tthis.assertInitialized();\n\t\tsetEditorJSON(doc, this.pluginManager?.schemaRegistry, (s) => this.replaceState(s));\n\t}\n\n\t/**\n\t * Returns sanitized HTML representation of the document.\n\t *\n\t * By default each block element carries a `data-block-id` attribute. This is\n\t * part of the wire format: it lets `setContentHTML(getContentHTML())`\n\t * preserve block identity so the caret survives content round-trips driven by\n\t * external sync (Angular signal forms, RxJS pipes — see ARCHITECTURE §9.2).\n\t *\n\t * Pass `{ includeBlockIds: false }` for clean export HTML (database storage,\n\t * server-side tag/attribute validation, handoff to another system) with no\n\t * `data-block-id`. Round-trips then generate fresh ids and no longer preserve\n\t * the caret. Works in both `cssMode: 'inline'` and `cssMode: 'classes'`.\n\t */\n\tasync getContentHTML(): Promise<string>;\n\tasync getContentHTML(options: ContentHTMLOptions & { cssMode?: 'inline' }): Promise<string>;\n\tasync getContentHTML(\n\t\toptions: ContentHTMLOptions & { cssMode: 'classes' },\n\t): Promise<ContentCSSResult>;\n\tasync getContentHTML(options?: ContentHTMLOptions): Promise<string | ContentCSSResult> {\n\t\tif (!this.view) throw new Error('Editor not initialized');\n\t\treturn getEditorContentHTML(this.view.getState(), this.pluginManager?.schemaRegistry, options);\n\t}\n\n\t/**\n\t * Sets content from HTML (sanitized). Accepts optional `styleMap` for\n\t * class-based round-trip.\n\t *\n\t * `data-block-id` attributes are adopted as `BlockId`s when they match\n\t * the safe pattern (`[A-Za-z0-9_-]{1,64}`) and don't collide within the\n\t * document; otherwise fresh IDs are generated. This makes\n\t * `setContentHTML(getContentHTML())` identity-preserving (see\n\t * ARCHITECTURE §9.2).\n\t */\n\tasync setContentHTML(html: string, options?: SetContentHTMLOptions): Promise<void> {\n\t\tthis.assertInitialized();\n\t\treturn setEditorContentHTML(\n\t\t\thtml,\n\t\t\tthis.pluginManager?.schemaRegistry,\n\t\t\t(s) => this.replaceState(s),\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Returns a Markdown representation of the document.\n\t *\n\t * Async and genuinely lazy: unlike {@link getContentHTML} (statically bundled),\n\t * the Markdown engine is reached only via dynamic `import()`, so it is\n\t * code-split out of the core bundle and builds that never touch Markdown pay\n\t * nothing (D13). Standard CommonMark/GFM constructs serialize directly;\n\t * superset features emit raw HTML by default (`htmlFallback`), or degrade\n\t * gracefully when it is disabled.\n\t */\n\tasync getContentMarkdown(options?: MarkdownSerializeOptions): Promise<string> {\n\t\tif (!this.view) throw new Error('Editor not initialized');\n\t\treturn getEditorContentMarkdown(\n\t\t\tthis.view.getState(),\n\t\t\tthis.pluginManager?.schemaRegistry,\n\t\t\toptions,\n\t\t);\n\t}\n\n\t/**\n\t * Replaces the document content from Markdown (CommonMark + GFM).\n\t *\n\t * Async and lazy like {@link getContentMarkdown}. Existing top-level block IDs\n\t * are reused in document order so `setContentMarkdown(getContentMarkdown())`\n\t * preserves block identity and keeps the caret stable for unchanged blocks\n\t * (ARCHITECTURE §9.2, D10). Raw HTML embedded in the Markdown is parsed back\n\t * via the HTML parser so superset features survive the round-trip.\n\t */\n\tasync setContentMarkdown(markdown: string, options?: MarkdownParseOptions): Promise<void> {\n\t\tthis.assertInitialized();\n\t\tif (!this.view) return;\n\t\tconst syntaxExtensions = this.pluginManager?.markdownSyntaxRegistry.getExtensions();\n\t\tconst merged: MarkdownParseOptions = {\n\t\t\t...options,\n\t\t\tsyntaxExtensions: options?.syntaxExtensions ?? syntaxExtensions,\n\t\t};\n\t\tawait setEditorContentMarkdown(\n\t\t\tmarkdown,\n\t\t\tthis.view.getState(),\n\t\t\tthis.pluginManager?.schemaRegistry,\n\t\t\t(s) => this.replaceState(s),\n\t\t\tmerged,\n\t\t);\n\t\t// `replaceState` ran synchronously above and cleared the live region (its\n\t\t// api-origin no-step transaction yields no announcement), so this is the\n\t\t// surviving message for screen readers.\n\t\tthis.announce?.(this.markdownImportedMessage);\n\t}\n\n\t/** Returns plain text content. */\n\tgetText(): string {\n\t\tif (!this.view) throw new Error('Editor not initialized');\n\t\treturn getEditorText(this.view.getState());\n\t}\n\n\t/**\n\t * Replaces the document content from plain text. Lines (`\\n`) become\n\t * paragraphs. Existing top-level block IDs are reused in document order\n\t * so the caret survives `setText(getText())` round-trips. When the input\n\t * matches the current text exactly, this is a no-op.\n\t */\n\tsetText(value: string): void {\n\t\tthis.assertInitialized();\n\t\tif (!this.view) return;\n\t\tsetEditorText(value, this.view.getState(), this.pluginManager?.schemaRegistry, (s) =>\n\t\t\tthis.replaceState(s),\n\t\t);\n\t}\n\n\t/** Returns true if the editor is empty (single empty paragraph). */\n\tisEmpty(): boolean {\n\t\tif (!this.view) return true;\n\t\treturn isEditorEmpty(this.view.getState().doc);\n\t}\n\n\t// --- Command API ---\n\n\treadonly commands = {\n\t\ttoggleBold: () => this.executeCommand('toggleBold'),\n\t\ttoggleItalic: () => this.executeCommand('toggleItalic'),\n\t\ttoggleUnderline: () => this.executeCommand('toggleUnderline'),\n\t\tundo: () => {\n\t\t\tif (this.configController.isReadOnly) return;\n\t\t\tthis.view?.undo();\n\t\t},\n\t\tredo: () => {\n\t\t\tif (this.configController.isReadOnly) return;\n\t\t\tthis.view?.redo();\n\t\t},\n\t\tselectAll: () => {\n\t\t\tif (!this.view) return;\n\t\t\tconst tr = selectAll(this.view.getState());\n\t\t\tthis.dispatch(tr);\n\t\t},\n\t};\n\n\t/** Checks whether a command can be executed. */\n\tcan(): {\n\t\ttoggleBold: () => boolean;\n\t\ttoggleItalic: () => boolean;\n\t\ttoggleUnderline: () => boolean;\n\t\tundo: () => boolean;\n\t\tredo: () => boolean;\n\t\tselectAll: () => boolean;\n\t} {\n\t\tconst schema = this.view?.getState().schema;\n\t\tconst readonly: boolean = this.configController.isReadOnly;\n\t\treturn {\n\t\t\ttoggleBold: () => !readonly && (schema ? isMarkAllowed(schema, 'bold') : false),\n\t\t\ttoggleItalic: () => !readonly && (schema ? isMarkAllowed(schema, 'italic') : false),\n\t\t\ttoggleUnderline: () => !readonly && (schema ? isMarkAllowed(schema, 'underline') : false),\n\t\t\tundo: () => !readonly && (this.view?.history.canUndo() ?? false),\n\t\t\tredo: () => !readonly && (this.view?.history.canRedo() ?? false),\n\t\t\tselectAll: () => this.canExecuteCommand('selectAll'),\n\t\t};\n\t}\n\n\t/** Returns whether a named command can be executed. */\n\tcanExecuteCommand(name: string): boolean {\n\t\treturn this.pluginManager?.canExecuteCommand(name) ?? false;\n\t}\n\n\t/** Executes a named command registered by a plugin. */\n\texecuteCommand(name: string): boolean {\n\t\treturn this.pluginManager?.executeCommand(name) ?? false;\n\t}\n\n\t/** Configures a plugin at runtime. */\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.pluginManager?.configurePlugin(pluginId, config);\n\t}\n\n\t/** Returns a registered plugin service by typed key. */\n\tgetService<T>(key: ServiceKey<T>): T | undefined {\n\t\treturn this.pluginManager?.getService(key);\n\t}\n\n\t/** Subscribes to a plugin event. Returns an unsubscribe function. */\n\tonPluginEvent<T>(key: EventKey<T>, callback: PluginEventCallback<T>): () => void {\n\t\treturn this.pluginManager?.onEvent(key, callback) ?? (() => {});\n\t}\n\n\t// --- State API ---\n\n\t/** Returns the current editor state. */\n\tgetState(): EditorState {\n\t\tif (!this.view) throw new Error('Editor not initialized');\n\t\treturn this.view.getState();\n\t}\n\n\t/** Dispatches a transaction (routed through middleware if any). */\n\tdispatch(tr: Transaction): void {\n\t\tif (!this.view || !this.pluginManager) return;\n\t\tthis.pluginManager.dispatchWithMiddleware(tr, this.view.getState(), (finalTr) =>\n\t\t\tthis.view?.dispatch(finalTr),\n\t\t);\n\t}\n\n\t// --- Event API ---\n\n\t/** Registers an event listener. */\n\ton<K extends keyof EditorEventMap>(\n\t\tevent: K,\n\t\tcallback: (payload: EditorEventMap[K]) => void,\n\t): void {\n\t\tthis.events.on(event, callback);\n\t}\n\n\t/** Removes an event listener. */\n\toff<K extends keyof EditorEventMap>(\n\t\tevent: K,\n\t\tcallback: (payload: EditorEventMap[K]) => void,\n\t): void {\n\t\tthis.events.off(event, callback);\n\t}\n\n\t// --- Lifecycle ---\n\n\t/**\n\t * Waits for the editor to be ready. Rejects immediately on static print\n\t * replicas (`data-notectl-static`): they never boot, so the promise would\n\t * otherwise hang forever.\n\t */\n\twhenReady(): Promise<void> {\n\t\tif (isStaticHostReplica(this)) {\n\t\t\treturn Promise.reject(createStaticReplicaError());\n\t\t}\n\t\treturn this.lifecycle.whenReady();\n\t}\n\n\t/** Updates configuration at runtime. */\n\tconfigure(config: Partial<import('./EditorConfig.js').NotectlEditorConfig>): void {\n\t\tthis.configController.applyRuntimeConfig(config, this.getConfigDeps());\n\t}\n\n\t// --- Theme API ---\n\n\t/** Changes the theme at runtime. */\n\tsetTheme(theme: ThemePreset | Theme): void {\n\t\tthis.configController.applyTheme(theme, this.themeController);\n\t}\n\n\t/** Returns the current theme setting. */\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.configController.getTheme();\n\t}\n\n\t// --- Paper Size API ---\n\n\t/** Returns the currently configured paper size, or undefined if fluid layout. */\n\tgetPaperSize(): PaperSize | undefined {\n\t\treturn this.configController.getPaperSize();\n\t}\n\n\t/** Cleans up the editor. Awaiting ensures async plugin teardown completes. */\n\tdestroy(): Promise<void> {\n\t\tthis.cancelAutoInit();\n\t\tthis.initVersion++;\n\t\tthis.cancelPendingInit?.();\n\t\tthis.cancelPendingInit = null;\n\t\tconst pendingInit =\n\t\t\tthis.pendingInitPromise?.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t) ?? Promise.resolve();\n\t\tthis.pendingInitPromise = null;\n\t\tthis.releaseInit?.();\n\t\tthis.releaseInit = null;\n\t\tthis.announce = null;\n\t\tthis.paperLayout?.destroy();\n\t\tthis.paperLayout = null;\n\t\tthis.styleCoordinator.teardown(this.shadowRoot, this.themeController);\n\t\tthis.themeController?.destroy();\n\t\tthis.themeController = null;\n\t\tthis.inputManager?.destroy();\n\t\tthis.inputManager = null;\n\t\tthis.view?.destroy();\n\t\tconst pluginTeardown = this.pluginManager?.destroy() ?? Promise.resolve();\n\t\tthis.view = null;\n\t\tthis.pluginManager = null;\n\t\tthis.lifecycle.reset();\n\t\tthis.events.clear();\n\t\tthis.domElements?.wrapper.remove();\n\t\tthis.domElements = null;\n\t\treturn Promise.all([pluginTeardown, pendingInit]).then(() => undefined);\n\t}\n\n\t/**\n\t * Returns the shadow root the editor boots into, creating it on first use.\n\t * Attaching is deferred out of the constructor: for parser-created elements\n\t * the constructor runs before attributes and children exist, so an eagerly\n\t * attached (empty) shadow root would block a declarative shadow root in the\n\t * markup from attaching — losing replicated print content on pages that\n\t * register the component before parsing (script in `<head>`). A leftover\n\t * declarative root from unmarked markup is emptied to keep the invariant\n\t * that an editor boots from a fresh shadow root; static print replicas\n\t * never reach this (init() throws on them first).\n\t */\n\tprivate ensureFreshShadowRoot(): ShadowRoot {\n\t\tconst existing: ShadowRoot | null = this.shadowRoot;\n\t\tif (!existing) return this.attachShadow({ mode: 'open' });\n\t\texisting.replaceChildren();\n\t\treturn existing;\n\t}\n\n\tprivate getConfigDeps(): import('./EditorConfigController.js').ConfigControllerDeps {\n\t\treturn {\n\t\t\tcontentElement: this.domElements?.content ?? null,\n\t\t\teditorWrapper: this.domElements?.wrapper ?? null,\n\t\t\tpluginManager: this.pluginManager,\n\t\t\tthemeController: this.themeController,\n\t\t\tapplyPaperSize: (size) => this.applyPaperSize(size),\n\t\t};\n\t}\n\n\tprivate scheduleAutoInit(): void {\n\t\tconst token = ++this.autoInitToken;\n\t\tthis.autoInitQueued = true;\n\t\tqueueMicrotask(() => {\n\t\t\tif (!this.isConnected) return;\n\t\t\tif (!this.autoInitQueued || this.autoInitToken !== token) return;\n\t\t\tthis.autoInitQueued = false;\n\t\t\tif (this.lifecycle.isInitialized()) return;\n\t\t\t// Errors are surfaced via whenReady() and the 'failed' lifecycle state.\n\t\t\tvoid this.init().catch(() => undefined);\n\t\t});\n\t}\n\n\tprivate cancelAutoInit(): void {\n\t\tthis.autoInitToken++;\n\t\tthis.autoInitQueued = false;\n\t}\n\n\tprivate applyPaperSize(paperSize: PaperSize | undefined): void {\n\t\tif (!this.domElements) return;\n\n\t\tif (!paperSize) {\n\t\t\tthis.paperLayout?.apply(null);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.paperLayout) {\n\t\t\tthis.paperLayout = new PaperLayoutController(\n\t\t\t\tthis.domElements.wrapper,\n\t\t\t\tthis.domElements.content,\n\t\t\t);\n\t\t}\n\t\tthis.paperLayout.apply(paperSize);\n\t}\n\n\tprivate replaceState(newState: EditorState): void {\n\t\tif (!this.view) return;\n\t\tthis.view.replaceState(newState);\n\t}\n\n\tprivate assertInitialized(): void {\n\t\tif (!this.view) {\n\t\t\tthrow new Error('Editor not initialized');\n\t\t}\n\t}\n}\n\n/** Factory function to create and configure a NotectlEditor instance. */\nexport async function createEditor(\n\tconfig?: import('./EditorConfig.js').NotectlEditorConfig,\n): Promise<NotectlEditor> {\n\tconst editor = document.createElement('notectl-editor') as NotectlEditor;\n\tawait editor.init(config);\n\treturn editor;\n}\n","import { NotectlEditor } from './editor/NotectlEditor.js';\n\n/** Registers the `<notectl-editor>` custom element when a browser registry is available. */\nexport function registerNotectlEditor(): void {\n\tif (typeof customElements !== 'undefined' && !customElements.get('notectl-editor')) {\n\t\tcustomElements.define('notectl-editor', NotectlEditor);\n\t}\n}\n\n// Preserve the root package's existing automatic-registration behavior.\nregisterNotectlEditor();\n"],"mappings":"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"}
|