@notectl/core 2.0.8 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/AlignmentPlugin-bqK6ze-E.mjs +2 -0
- package/dist/chunks/AlignmentPlugin-bqK6ze-E.mjs.map +1 -0
- package/dist/chunks/BidiIsolationPlugin-SIKxfWXp.mjs +2 -0
- package/dist/chunks/BidiIsolationPlugin-SIKxfWXp.mjs.map +1 -0
- package/dist/chunks/BlockquotePlugin-BEaZSoVT.mjs +2 -0
- package/dist/chunks/BlockquotePlugin-BEaZSoVT.mjs.map +1 -0
- package/dist/chunks/CaretNavigationPlugin-DHl61oc6.mjs +2 -0
- package/dist/chunks/CaretNavigationPlugin-DHl61oc6.mjs.map +1 -0
- package/dist/chunks/CodeBlockPlugin-DglqiSNK.mjs +2 -0
- package/dist/chunks/CodeBlockPlugin-DglqiSNK.mjs.map +1 -0
- package/dist/chunks/ColorGrid-Czm0p2E9.mjs +2 -0
- package/dist/chunks/ColorGrid-Czm0p2E9.mjs.map +1 -0
- package/dist/chunks/ColorPickerPopup-CL-W380M.mjs +2 -0
- package/dist/chunks/ColorPickerPopup-CL-W380M.mjs.map +1 -0
- package/dist/chunks/DirectionDetection-Hun6-7ER.mjs +2 -0
- package/dist/chunks/DirectionDetection-Hun6-7ER.mjs.map +1 -0
- package/dist/chunks/DirectionIcons-CfaDcQ9a.mjs +2 -0
- package/dist/chunks/DirectionIcons-CfaDcQ9a.mjs.map +1 -0
- package/dist/chunks/DocumentParser-CyATBOz-.mjs +2 -0
- package/dist/chunks/DocumentParser-CyATBOz-.mjs.map +1 -0
- package/dist/chunks/FontPlugin-B-Sxa0UW.mjs +2 -0
- package/dist/chunks/FontPlugin-B-Sxa0UW.mjs.map +1 -0
- package/dist/chunks/FontSizePlugin-C1zlyXL2.mjs +2 -0
- package/dist/chunks/FontSizePlugin-C1zlyXL2.mjs.map +1 -0
- package/dist/chunks/GapCursorPlugin-nldun8cE.mjs +2 -0
- package/dist/chunks/GapCursorPlugin-nldun8cE.mjs.map +1 -0
- package/dist/chunks/HTMLUtils-brwcaufP.mjs +2 -0
- package/dist/chunks/HTMLUtils-brwcaufP.mjs.map +1 -0
- package/dist/chunks/HardBreakPlugin-DZRdsE15.mjs +2 -0
- package/dist/chunks/HardBreakPlugin-DZRdsE15.mjs.map +1 -0
- package/dist/chunks/HeadingPlugin-EPAmsEGt.mjs +2 -0
- package/dist/chunks/HeadingPlugin-EPAmsEGt.mjs.map +1 -0
- package/dist/chunks/HighlightPlugin-RN3l6pVJ.mjs +2 -0
- package/dist/chunks/HighlightPlugin-RN3l6pVJ.mjs.map +1 -0
- package/dist/chunks/HorizontalRulePlugin-BJPXzTtJ.mjs +2 -0
- package/dist/chunks/HorizontalRulePlugin-BJPXzTtJ.mjs.map +1 -0
- package/dist/chunks/ImagePlugin-CCEevNfJ.mjs +2 -0
- package/dist/chunks/ImagePlugin-CCEevNfJ.mjs.map +1 -0
- package/dist/chunks/InlineCodePlugin-B7IOgUSI.mjs +2 -0
- package/dist/chunks/InlineCodePlugin-B7IOgUSI.mjs.map +1 -0
- package/dist/chunks/LinkPlugin-Bhwg24lH.mjs +2 -0
- package/dist/chunks/LinkPlugin-Bhwg24lH.mjs.map +1 -0
- package/dist/chunks/ListPlugin-BkG6UO4x.mjs +2 -0
- package/dist/chunks/ListPlugin-BkG6UO4x.mjs.map +1 -0
- package/dist/chunks/PluginHelpers-D-MvWR98.mjs +2 -0
- package/dist/chunks/PluginHelpers-D-MvWR98.mjs.map +1 -0
- package/dist/chunks/PopupKeyboardPatterns-DQdL7RHx.mjs +2 -0
- package/dist/chunks/PopupKeyboardPatterns-DQdL7RHx.mjs.map +1 -0
- package/dist/chunks/PopupManager-CxPzNm-P.mjs +2 -0
- package/dist/chunks/PopupManager-CxPzNm-P.mjs.map +1 -0
- package/dist/chunks/PrintPlugin-DOk5IZRO.mjs +2 -0
- package/dist/chunks/PrintPlugin-DOk5IZRO.mjs.map +1 -0
- package/dist/chunks/PrintTypes-Drk6kLcl.mjs +2 -0
- package/dist/chunks/PrintTypes-Drk6kLcl.mjs.map +1 -0
- package/dist/chunks/SmartPastePlugin-DqKx94sH.mjs +2 -0
- package/dist/chunks/SmartPastePlugin-DqKx94sH.mjs.map +1 -0
- package/dist/chunks/StrikethroughPlugin-vYNzfxdy.mjs +2 -0
- package/dist/chunks/StrikethroughPlugin-vYNzfxdy.mjs.map +1 -0
- package/dist/chunks/SuperSubPlugin-BNsoXWHS.mjs +2 -0
- package/dist/chunks/SuperSubPlugin-BNsoXWHS.mjs.map +1 -0
- package/dist/chunks/TablePlugin-R-hmydrM.mjs +2 -0
- package/dist/chunks/TablePlugin-R-hmydrM.mjs.map +1 -0
- package/dist/chunks/TextColorPlugin-DQ-CB9cr.mjs +2 -0
- package/dist/chunks/TextColorPlugin-DQ-CB9cr.mjs.map +1 -0
- package/dist/chunks/TextDirectionAutoPlugin-DS_gpI8N.mjs +2 -0
- package/dist/chunks/TextDirectionAutoPlugin-DS_gpI8N.mjs.map +1 -0
- package/dist/chunks/TextDirectionPlugin-Bk3CkbYh.mjs +2 -0
- package/dist/chunks/TextDirectionPlugin-Bk3CkbYh.mjs.map +1 -0
- package/dist/chunks/TextDirectionService-DY4M8EwU.mjs +2 -0
- package/dist/chunks/TextDirectionService-DY4M8EwU.mjs.map +1 -0
- package/dist/chunks/TextFormattingPlugin-D13eP6nF.mjs +2 -0
- package/dist/chunks/TextFormattingPlugin-D13eP6nF.mjs.map +1 -0
- package/dist/chunks/ToolbarPlugin-DeH8n5d2.mjs +2 -0
- package/dist/chunks/ToolbarPlugin-DeH8n5d2.mjs.map +1 -0
- package/dist/chunks/TypedKeys-CU7aToqY.mjs +2 -0
- package/dist/chunks/TypedKeys-CU7aToqY.mjs.map +1 -0
- package/dist/chunks/XmlDetector-BtYxzBDa.mjs +2 -0
- package/dist/chunks/XmlDetector-BtYxzBDa.mjs.map +1 -0
- package/dist/chunks/XmlPatterns-DuP8GWVt.mjs +2 -0
- package/dist/chunks/XmlPatterns-DuP8GWVt.mjs.map +1 -0
- package/dist/chunks/XmlSupport-D54BdAJZ.mjs +2 -0
- package/dist/chunks/XmlSupport-D54BdAJZ.mjs.map +1 -0
- package/dist/chunks/ar-2Fkz_6nI.mjs +2 -0
- package/dist/chunks/ar-2Fkz_6nI.mjs.map +1 -0
- package/dist/chunks/ar-4ZPC8daa.mjs +2 -0
- package/dist/chunks/ar-4ZPC8daa.mjs.map +1 -0
- package/dist/chunks/ar-6I20_Djq.mjs +2 -0
- package/dist/chunks/ar-6I20_Djq.mjs.map +1 -0
- package/dist/chunks/ar-B7-Dm70S.mjs +2 -0
- package/dist/chunks/ar-B7-Dm70S.mjs.map +1 -0
- package/dist/chunks/ar-BRe0t96G.mjs +2 -0
- package/dist/chunks/ar-BRe0t96G.mjs.map +1 -0
- package/dist/chunks/ar-BnNyrtdi.mjs +2 -0
- package/dist/chunks/ar-BnNyrtdi.mjs.map +1 -0
- package/dist/chunks/ar-BsMfhWs5.mjs +2 -0
- package/dist/chunks/ar-BsMfhWs5.mjs.map +1 -0
- package/dist/chunks/ar-Bvja-bjE.mjs +2 -0
- package/dist/chunks/ar-Bvja-bjE.mjs.map +1 -0
- package/dist/chunks/ar-BxVbBvSa.mjs +2 -0
- package/dist/chunks/ar-BxVbBvSa.mjs.map +1 -0
- package/dist/chunks/ar-C5dW-co3.mjs +2 -0
- package/dist/chunks/ar-C5dW-co3.mjs.map +1 -0
- package/dist/chunks/ar-CARMr30L.mjs +2 -0
- package/dist/chunks/ar-CARMr30L.mjs.map +1 -0
- package/dist/chunks/ar-CB-pehGw.mjs +2 -0
- package/dist/chunks/ar-CB-pehGw.mjs.map +1 -0
- package/dist/chunks/ar-CEYgAxTM.mjs +2 -0
- package/dist/chunks/ar-CEYgAxTM.mjs.map +1 -0
- package/dist/chunks/ar-CO60qpZJ.mjs +2 -0
- package/dist/chunks/ar-CO60qpZJ.mjs.map +1 -0
- package/dist/chunks/ar-CtI_snUM.mjs +2 -0
- package/dist/chunks/ar-CtI_snUM.mjs.map +1 -0
- package/dist/chunks/ar-Cv1RQvhK.mjs +2 -0
- package/dist/chunks/ar-Cv1RQvhK.mjs.map +1 -0
- package/dist/chunks/ar-D5jV20Tu.mjs +2 -0
- package/dist/chunks/ar-D5jV20Tu.mjs.map +1 -0
- package/dist/chunks/ar-DJN9fQ7Z.mjs +2 -0
- package/dist/chunks/ar-DJN9fQ7Z.mjs.map +1 -0
- package/dist/chunks/ar-DPTsUnwA.mjs +2 -0
- package/dist/chunks/ar-DPTsUnwA.mjs.map +1 -0
- package/dist/chunks/ar-DZJIBD1Q.mjs +2 -0
- package/dist/chunks/ar-DZJIBD1Q.mjs.map +1 -0
- package/dist/chunks/ar-DkkbwV93.mjs +2 -0
- package/dist/chunks/ar-DkkbwV93.mjs.map +1 -0
- package/dist/chunks/ar-DmeMor8Y.mjs +2 -0
- package/dist/chunks/ar-DmeMor8Y.mjs.map +1 -0
- package/dist/chunks/ar-LnE7jPai.mjs +2 -0
- package/dist/chunks/ar-LnE7jPai.mjs.map +1 -0
- package/dist/chunks/ar-P3wW0fmA.mjs +2 -0
- package/dist/chunks/ar-P3wW0fmA.mjs.map +1 -0
- package/dist/chunks/ar-uum0DCfv.mjs +2 -0
- package/dist/chunks/ar-uum0DCfv.mjs.map +1 -0
- package/dist/chunks/color-picker-DlXhHBZ6.mjs +2 -0
- package/dist/chunks/color-picker-DlXhHBZ6.mjs.map +1 -0
- package/dist/chunks/de-8SHQu0rr.mjs +2 -0
- package/dist/chunks/de-8SHQu0rr.mjs.map +1 -0
- package/dist/chunks/de-B8a71A0X.mjs +2 -0
- package/dist/chunks/de-B8a71A0X.mjs.map +1 -0
- package/dist/chunks/de-B9weXoA1.mjs +2 -0
- package/dist/chunks/de-B9weXoA1.mjs.map +1 -0
- package/dist/chunks/de-BKm53tVU.mjs +2 -0
- package/dist/chunks/de-BKm53tVU.mjs.map +1 -0
- package/dist/chunks/de-BLWrnMfX.mjs +2 -0
- package/dist/chunks/de-BLWrnMfX.mjs.map +1 -0
- package/dist/chunks/de-BOujs1pF.mjs +2 -0
- package/dist/chunks/de-BOujs1pF.mjs.map +1 -0
- package/dist/chunks/de-B_1XleAG.mjs +2 -0
- package/dist/chunks/de-B_1XleAG.mjs.map +1 -0
- package/dist/chunks/de-BpmjrOMc.mjs +2 -0
- package/dist/chunks/de-BpmjrOMc.mjs.map +1 -0
- package/dist/chunks/de-C8OHD1fm.mjs +2 -0
- package/dist/chunks/de-C8OHD1fm.mjs.map +1 -0
- package/dist/chunks/de-CAVCXBBR.mjs +2 -0
- package/dist/chunks/de-CAVCXBBR.mjs.map +1 -0
- package/dist/chunks/de-CkMnuozT.mjs +2 -0
- package/dist/chunks/de-CkMnuozT.mjs.map +1 -0
- package/dist/chunks/de-CtDnvnnm.mjs +2 -0
- package/dist/chunks/de-CtDnvnnm.mjs.map +1 -0
- package/dist/chunks/de-Cz26vtXW.mjs +2 -0
- package/dist/chunks/de-Cz26vtXW.mjs.map +1 -0
- package/dist/chunks/de-D0BPSP3j.mjs +2 -0
- package/dist/chunks/de-D0BPSP3j.mjs.map +1 -0
- package/dist/chunks/de-D74v0paw.mjs +2 -0
- package/dist/chunks/de-D74v0paw.mjs.map +1 -0
- package/dist/chunks/de-DKj55F9g.mjs +2 -0
- package/dist/chunks/de-DKj55F9g.mjs.map +1 -0
- package/dist/chunks/de-DYuRb98l.mjs +2 -0
- package/dist/chunks/de-DYuRb98l.mjs.map +1 -0
- package/dist/chunks/de-DaK0-sdo.mjs +2 -0
- package/dist/chunks/de-DaK0-sdo.mjs.map +1 -0
- package/dist/chunks/de-Dia08Wsq.mjs +2 -0
- package/dist/chunks/de-Dia08Wsq.mjs.map +1 -0
- package/dist/chunks/de-Dp_uj2Z3.mjs +2 -0
- package/dist/chunks/de-Dp_uj2Z3.mjs.map +1 -0
- package/dist/chunks/de-DwX1CDNO.mjs +2 -0
- package/dist/chunks/de-DwX1CDNO.mjs.map +1 -0
- package/dist/chunks/de-DyF2mRfw.mjs +2 -0
- package/dist/chunks/de-DyF2mRfw.mjs.map +1 -0
- package/dist/chunks/de-X3BLBlF8.mjs +2 -0
- package/dist/chunks/de-X3BLBlF8.mjs.map +1 -0
- package/dist/chunks/de-dPC3Qk7N.mjs +2 -0
- package/dist/chunks/de-dPC3Qk7N.mjs.map +1 -0
- package/dist/chunks/de-pLGt5S7j.mjs +2 -0
- package/dist/chunks/de-pLGt5S7j.mjs.map +1 -0
- package/dist/chunks/es--xozZF4T.mjs +2 -0
- package/dist/chunks/es--xozZF4T.mjs.map +1 -0
- package/dist/chunks/es-2BjtTusm.mjs +2 -0
- package/dist/chunks/es-2BjtTusm.mjs.map +1 -0
- package/dist/chunks/es-B2rEK7_f.mjs +2 -0
- package/dist/chunks/es-B2rEK7_f.mjs.map +1 -0
- package/dist/chunks/es-B9esmUm1.mjs +2 -0
- package/dist/chunks/es-B9esmUm1.mjs.map +1 -0
- package/dist/chunks/es-BJxkHalP.mjs +2 -0
- package/dist/chunks/es-BJxkHalP.mjs.map +1 -0
- package/dist/chunks/es-BMKsnh3y.mjs +2 -0
- package/dist/chunks/es-BMKsnh3y.mjs.map +1 -0
- package/dist/chunks/es-BXFflrrU.mjs +2 -0
- package/dist/chunks/es-BXFflrrU.mjs.map +1 -0
- package/dist/chunks/es-BbX_lEEN.mjs +2 -0
- package/dist/chunks/es-BbX_lEEN.mjs.map +1 -0
- package/dist/chunks/es-BlHe_tc0.mjs +2 -0
- package/dist/chunks/es-BlHe_tc0.mjs.map +1 -0
- package/dist/chunks/es-Bu6nnEeT.mjs +2 -0
- package/dist/chunks/es-Bu6nnEeT.mjs.map +1 -0
- package/dist/chunks/es-Bx0CbUoY.mjs +2 -0
- package/dist/chunks/es-Bx0CbUoY.mjs.map +1 -0
- package/dist/chunks/es-CE9Tbsb7.mjs +2 -0
- package/dist/chunks/es-CE9Tbsb7.mjs.map +1 -0
- package/dist/chunks/es-COSomns3.mjs +2 -0
- package/dist/chunks/es-COSomns3.mjs.map +1 -0
- package/dist/chunks/es-C_7qthHu.mjs +2 -0
- package/dist/chunks/es-C_7qthHu.mjs.map +1 -0
- package/dist/chunks/es-CirfP3x1.mjs +2 -0
- package/dist/chunks/es-CirfP3x1.mjs.map +1 -0
- package/dist/chunks/es-CpF8eiPr.mjs +2 -0
- package/dist/chunks/es-CpF8eiPr.mjs.map +1 -0
- package/dist/chunks/es-D787y0ui.mjs +2 -0
- package/dist/chunks/es-D787y0ui.mjs.map +1 -0
- package/dist/chunks/es-D8iB5vwQ.mjs +2 -0
- package/dist/chunks/es-D8iB5vwQ.mjs.map +1 -0
- package/dist/chunks/es-DBb78XWu.mjs +2 -0
- package/dist/chunks/es-DBb78XWu.mjs.map +1 -0
- package/dist/chunks/es-DC6mYTD3.mjs +2 -0
- package/dist/chunks/es-DC6mYTD3.mjs.map +1 -0
- package/dist/chunks/es-Dh--mRZs.mjs +2 -0
- package/dist/chunks/es-Dh--mRZs.mjs.map +1 -0
- package/dist/chunks/es-DvPsYqFj.mjs +2 -0
- package/dist/chunks/es-DvPsYqFj.mjs.map +1 -0
- package/dist/chunks/es-DxXimuUa.mjs +2 -0
- package/dist/chunks/es-DxXimuUa.mjs.map +1 -0
- package/dist/chunks/es-mWJptxq3.mjs +2 -0
- package/dist/chunks/es-mWJptxq3.mjs.map +1 -0
- package/dist/chunks/es-qYV--o6U.mjs +2 -0
- package/dist/chunks/es-qYV--o6U.mjs.map +1 -0
- package/dist/chunks/fonts-CxtWyeNB.mjs +2 -0
- package/dist/chunks/fonts-CxtWyeNB.mjs.map +1 -0
- package/dist/chunks/fr--F426oao.mjs +2 -0
- package/dist/chunks/fr--F426oao.mjs.map +1 -0
- package/dist/chunks/fr-B0BPbbNB.mjs +2 -0
- package/dist/chunks/fr-B0BPbbNB.mjs.map +1 -0
- package/dist/chunks/fr-B4UQy0j-.mjs +2 -0
- package/dist/chunks/fr-B4UQy0j-.mjs.map +1 -0
- package/dist/chunks/fr-B4XLxkci.mjs +2 -0
- package/dist/chunks/fr-B4XLxkci.mjs.map +1 -0
- package/dist/chunks/fr-BC1Rfp9S.mjs +2 -0
- package/dist/chunks/fr-BC1Rfp9S.mjs.map +1 -0
- package/dist/chunks/fr-BRGGu0E2.mjs +2 -0
- package/dist/chunks/fr-BRGGu0E2.mjs.map +1 -0
- package/dist/chunks/fr-BoSz1pa9.mjs +2 -0
- package/dist/chunks/fr-BoSz1pa9.mjs.map +1 -0
- package/dist/chunks/fr-Bom3RFIN.mjs +2 -0
- package/dist/chunks/fr-Bom3RFIN.mjs.map +1 -0
- package/dist/chunks/fr-BtnwMUII.mjs +2 -0
- package/dist/chunks/fr-BtnwMUII.mjs.map +1 -0
- package/dist/chunks/fr-C6pglM3H.mjs +2 -0
- package/dist/chunks/fr-C6pglM3H.mjs.map +1 -0
- package/dist/chunks/fr-CDBpS6xE.mjs +2 -0
- package/dist/chunks/fr-CDBpS6xE.mjs.map +1 -0
- package/dist/chunks/fr-CN0ihkUg.mjs +2 -0
- package/dist/chunks/fr-CN0ihkUg.mjs.map +1 -0
- package/dist/chunks/fr-CNNBBeLM.mjs +2 -0
- package/dist/chunks/fr-CNNBBeLM.mjs.map +1 -0
- package/dist/chunks/fr-CVOY2y07.mjs +2 -0
- package/dist/chunks/fr-CVOY2y07.mjs.map +1 -0
- package/dist/chunks/fr-CZAbL_o0.mjs +2 -0
- package/dist/chunks/fr-CZAbL_o0.mjs.map +1 -0
- package/dist/chunks/fr-DJkPmQG_.mjs +2 -0
- package/dist/chunks/fr-DJkPmQG_.mjs.map +1 -0
- package/dist/chunks/fr-DkWdFDsR.mjs +2 -0
- package/dist/chunks/fr-DkWdFDsR.mjs.map +1 -0
- package/dist/chunks/fr-DnQHoPVo.mjs +2 -0
- package/dist/chunks/fr-DnQHoPVo.mjs.map +1 -0
- package/dist/chunks/fr-DsdueaiG.mjs +2 -0
- package/dist/chunks/fr-DsdueaiG.mjs.map +1 -0
- package/dist/chunks/fr-DxNdsa11.mjs +2 -0
- package/dist/chunks/fr-DxNdsa11.mjs.map +1 -0
- package/dist/chunks/fr-IO4_LYvE.mjs +2 -0
- package/dist/chunks/fr-IO4_LYvE.mjs.map +1 -0
- package/dist/chunks/fr-ZKDCEpww.mjs +2 -0
- package/dist/chunks/fr-ZKDCEpww.mjs.map +1 -0
- package/dist/chunks/fr-coufP_et.mjs +2 -0
- package/dist/chunks/fr-coufP_et.mjs.map +1 -0
- package/dist/chunks/fr-lYSOgOK7.mjs +2 -0
- package/dist/chunks/fr-lYSOgOK7.mjs.map +1 -0
- package/dist/chunks/fr-wobtRjcF.mjs +2 -0
- package/dist/chunks/fr-wobtRjcF.mjs.map +1 -0
- package/dist/chunks/full-BMvrL5dE.mjs +2 -0
- package/dist/chunks/full-BMvrL5dE.mjs.map +1 -0
- package/dist/chunks/hi-B0o90MFd.mjs +2 -0
- package/dist/chunks/hi-B0o90MFd.mjs.map +1 -0
- package/dist/chunks/hi-BFko34WG.mjs +2 -0
- package/dist/chunks/hi-BFko34WG.mjs.map +1 -0
- package/dist/chunks/hi-BVxSgaNT.mjs +2 -0
- package/dist/chunks/hi-BVxSgaNT.mjs.map +1 -0
- package/dist/chunks/hi-BirBQ5Ef.mjs +2 -0
- package/dist/chunks/hi-BirBQ5Ef.mjs.map +1 -0
- package/dist/chunks/hi-BnsbcSIX.mjs +2 -0
- package/dist/chunks/hi-BnsbcSIX.mjs.map +1 -0
- package/dist/chunks/hi-BoFuxLtG.mjs +2 -0
- package/dist/chunks/hi-BoFuxLtG.mjs.map +1 -0
- package/dist/chunks/hi-BptyTwAv.mjs +2 -0
- package/dist/chunks/hi-BptyTwAv.mjs.map +1 -0
- package/dist/chunks/hi-BulaYhi_.mjs +2 -0
- package/dist/chunks/hi-BulaYhi_.mjs.map +1 -0
- package/dist/chunks/hi-BzflHG6Y.mjs +2 -0
- package/dist/chunks/hi-BzflHG6Y.mjs.map +1 -0
- package/dist/chunks/hi-C7Hb-gQ_.mjs +2 -0
- package/dist/chunks/hi-C7Hb-gQ_.mjs.map +1 -0
- package/dist/chunks/hi-C_lY7kA9.mjs +2 -0
- package/dist/chunks/hi-C_lY7kA9.mjs.map +1 -0
- package/dist/chunks/hi-CkylXoIm.mjs +2 -0
- package/dist/chunks/hi-CkylXoIm.mjs.map +1 -0
- package/dist/chunks/hi-DGJTbrQi.mjs +2 -0
- package/dist/chunks/hi-DGJTbrQi.mjs.map +1 -0
- package/dist/chunks/hi-DGJgX0fG.mjs +2 -0
- package/dist/chunks/hi-DGJgX0fG.mjs.map +1 -0
- package/dist/chunks/hi-DKDYpjdr.mjs +2 -0
- package/dist/chunks/hi-DKDYpjdr.mjs.map +1 -0
- package/dist/chunks/hi-DfAX8S-T.mjs +2 -0
- package/dist/chunks/hi-DfAX8S-T.mjs.map +1 -0
- package/dist/chunks/hi-Dhq91ipt.mjs +2 -0
- package/dist/chunks/hi-Dhq91ipt.mjs.map +1 -0
- package/dist/chunks/hi-Dup389dM.mjs +2 -0
- package/dist/chunks/hi-Dup389dM.mjs.map +1 -0
- package/dist/chunks/hi-DzRFsYYN.mjs +2 -0
- package/dist/chunks/hi-DzRFsYYN.mjs.map +1 -0
- package/dist/chunks/hi-ObZRwITd.mjs +2 -0
- package/dist/chunks/hi-ObZRwITd.mjs.map +1 -0
- package/dist/chunks/hi-VvUzjfH_.mjs +2 -0
- package/dist/chunks/hi-VvUzjfH_.mjs.map +1 -0
- package/dist/chunks/hi-jWQDQkOX.mjs +2 -0
- package/dist/chunks/hi-jWQDQkOX.mjs.map +1 -0
- package/dist/chunks/hi-l3hIrp8J.mjs +2 -0
- package/dist/chunks/hi-l3hIrp8J.mjs.map +1 -0
- package/dist/chunks/hi-o8Gsu5Ly.mjs +2 -0
- package/dist/chunks/hi-o8Gsu5Ly.mjs.map +1 -0
- package/dist/chunks/hi-sxC2nk3h.mjs +2 -0
- package/dist/chunks/hi-sxC2nk3h.mjs.map +1 -0
- package/dist/chunks/minimal-pBhi4EJI.mjs +2 -0
- package/dist/chunks/minimal-pBhi4EJI.mjs.map +1 -0
- package/dist/chunks/pt--57Ug7ig.mjs +2 -0
- package/dist/chunks/pt--57Ug7ig.mjs.map +1 -0
- package/dist/chunks/pt-4j8Km9Db.mjs +2 -0
- package/dist/chunks/pt-4j8Km9Db.mjs.map +1 -0
- package/dist/chunks/pt-BTGFKCns.mjs +2 -0
- package/dist/chunks/pt-BTGFKCns.mjs.map +1 -0
- package/dist/chunks/pt-BU8Qfuoz.mjs +2 -0
- package/dist/chunks/pt-BU8Qfuoz.mjs.map +1 -0
- package/dist/chunks/pt-BYAOJxIB.mjs +2 -0
- package/dist/chunks/pt-BYAOJxIB.mjs.map +1 -0
- package/dist/chunks/pt-BjNWQm9A.mjs +2 -0
- package/dist/chunks/pt-BjNWQm9A.mjs.map +1 -0
- package/dist/chunks/pt-Bvnb_NpT.mjs +2 -0
- package/dist/chunks/pt-Bvnb_NpT.mjs.map +1 -0
- package/dist/chunks/pt-C2pkn7vo.mjs +2 -0
- package/dist/chunks/pt-C2pkn7vo.mjs.map +1 -0
- package/dist/chunks/pt-C8MhLnWx.mjs +2 -0
- package/dist/chunks/pt-C8MhLnWx.mjs.map +1 -0
- package/dist/chunks/pt-CJfYLSTe.mjs +2 -0
- package/dist/chunks/pt-CJfYLSTe.mjs.map +1 -0
- package/dist/chunks/pt-CdwVIYOK.mjs +2 -0
- package/dist/chunks/pt-CdwVIYOK.mjs.map +1 -0
- package/dist/chunks/pt-CwrVo90O.mjs +2 -0
- package/dist/chunks/pt-CwrVo90O.mjs.map +1 -0
- package/dist/chunks/pt-DD26CKCA.mjs +2 -0
- package/dist/chunks/pt-DD26CKCA.mjs.map +1 -0
- package/dist/chunks/pt-DLbf6uoo.mjs +2 -0
- package/dist/chunks/pt-DLbf6uoo.mjs.map +1 -0
- package/dist/chunks/pt-DNyNHwKh.mjs +2 -0
- package/dist/chunks/pt-DNyNHwKh.mjs.map +1 -0
- package/dist/chunks/pt-DVfMAcXb.mjs +2 -0
- package/dist/chunks/pt-DVfMAcXb.mjs.map +1 -0
- package/dist/chunks/pt-DWSFad3n.mjs +2 -0
- package/dist/chunks/pt-DWSFad3n.mjs.map +1 -0
- package/dist/chunks/pt-DZXY4i4G.mjs +2 -0
- package/dist/chunks/pt-DZXY4i4G.mjs.map +1 -0
- package/dist/chunks/pt-DfUpRhK1.mjs +2 -0
- package/dist/chunks/pt-DfUpRhK1.mjs.map +1 -0
- package/dist/chunks/pt-DlvNEDeW.mjs +2 -0
- package/dist/chunks/pt-DlvNEDeW.mjs.map +1 -0
- package/dist/chunks/pt-DzFyGwB4.mjs +2 -0
- package/dist/chunks/pt-DzFyGwB4.mjs.map +1 -0
- package/dist/chunks/pt-HhQcARW6.mjs +2 -0
- package/dist/chunks/pt-HhQcARW6.mjs.map +1 -0
- package/dist/chunks/pt-SjvTF40s.mjs +2 -0
- package/dist/chunks/pt-SjvTF40s.mjs.map +1 -0
- package/dist/chunks/pt-ihNE0VQJ.mjs +2 -0
- package/dist/chunks/pt-ihNE0VQJ.mjs.map +1 -0
- package/dist/chunks/pt-zfCjhIgy.mjs +2 -0
- package/dist/chunks/pt-zfCjhIgy.mjs.map +1 -0
- package/dist/chunks/ru-0AHodbWv.mjs +2 -0
- package/dist/chunks/ru-0AHodbWv.mjs.map +1 -0
- package/dist/chunks/ru-2MDGmLw0.mjs +2 -0
- package/dist/chunks/ru-2MDGmLw0.mjs.map +1 -0
- package/dist/chunks/ru-3VfYKfTm.mjs +2 -0
- package/dist/chunks/ru-3VfYKfTm.mjs.map +1 -0
- package/dist/chunks/ru-B-gRJ6nQ.mjs +2 -0
- package/dist/chunks/ru-B-gRJ6nQ.mjs.map +1 -0
- package/dist/chunks/ru-B6ITUB-Y.mjs +2 -0
- package/dist/chunks/ru-B6ITUB-Y.mjs.map +1 -0
- package/dist/chunks/ru-BKDWkabk.mjs +2 -0
- package/dist/chunks/ru-BKDWkabk.mjs.map +1 -0
- package/dist/chunks/ru-BMxJsSe3.mjs +2 -0
- package/dist/chunks/ru-BMxJsSe3.mjs.map +1 -0
- package/dist/chunks/ru-Bep1iHQL.mjs +2 -0
- package/dist/chunks/ru-Bep1iHQL.mjs.map +1 -0
- package/dist/chunks/ru-Blj0nqoL.mjs +2 -0
- package/dist/chunks/ru-Blj0nqoL.mjs.map +1 -0
- package/dist/chunks/ru-BqnHkmii.mjs +2 -0
- package/dist/chunks/ru-BqnHkmii.mjs.map +1 -0
- package/dist/chunks/ru-C-4AaPUK.mjs +2 -0
- package/dist/chunks/ru-C-4AaPUK.mjs.map +1 -0
- package/dist/chunks/ru-C9Nv7JJI.mjs +2 -0
- package/dist/chunks/ru-C9Nv7JJI.mjs.map +1 -0
- package/dist/chunks/ru-CJFtr1Ks.mjs +2 -0
- package/dist/chunks/ru-CJFtr1Ks.mjs.map +1 -0
- package/dist/chunks/ru-CN63yWk_.mjs +2 -0
- package/dist/chunks/ru-CN63yWk_.mjs.map +1 -0
- package/dist/chunks/ru-CcFX97-2.mjs +2 -0
- package/dist/chunks/ru-CcFX97-2.mjs.map +1 -0
- package/dist/chunks/ru-D8PhWFSp.mjs +2 -0
- package/dist/chunks/ru-D8PhWFSp.mjs.map +1 -0
- package/dist/chunks/ru-DYAncidS.mjs +2 -0
- package/dist/chunks/ru-DYAncidS.mjs.map +1 -0
- package/dist/chunks/ru-D_zXo-FA.mjs +2 -0
- package/dist/chunks/ru-D_zXo-FA.mjs.map +1 -0
- package/dist/chunks/ru-DhSPV6m0.mjs +2 -0
- package/dist/chunks/ru-DhSPV6m0.mjs.map +1 -0
- package/dist/chunks/ru-LNbpjKCw.mjs +2 -0
- package/dist/chunks/ru-LNbpjKCw.mjs.map +1 -0
- package/dist/chunks/ru-LXi1ONW4.mjs +2 -0
- package/dist/chunks/ru-LXi1ONW4.mjs.map +1 -0
- package/dist/chunks/ru-db7wsWkx.mjs +2 -0
- package/dist/chunks/ru-db7wsWkx.mjs.map +1 -0
- package/dist/chunks/ru-nZ4BjiD0.mjs +2 -0
- package/dist/chunks/ru-nZ4BjiD0.mjs.map +1 -0
- package/dist/chunks/ru-v9zumXd2.mjs +2 -0
- package/dist/chunks/ru-v9zumXd2.mjs.map +1 -0
- package/dist/chunks/ru-wK2ytv3A.mjs +2 -0
- package/dist/chunks/ru-wK2ytv3A.mjs.map +1 -0
- package/dist/chunks/src-CNK6vpUU.mjs +2 -0
- package/dist/chunks/src-CNK6vpUU.mjs.map +1 -0
- package/dist/chunks/zh-9AcCSFm0.mjs +2 -0
- package/dist/chunks/zh-9AcCSFm0.mjs.map +1 -0
- package/dist/chunks/zh-B3bJCfdB.mjs +2 -0
- package/dist/chunks/zh-B3bJCfdB.mjs.map +1 -0
- package/dist/chunks/zh-B46Dt1h7.mjs +2 -0
- package/dist/chunks/zh-B46Dt1h7.mjs.map +1 -0
- package/dist/chunks/zh-BRy2B6Og.mjs +2 -0
- package/dist/chunks/zh-BRy2B6Og.mjs.map +1 -0
- package/dist/chunks/zh-BfYi8wB2.mjs +2 -0
- package/dist/chunks/zh-BfYi8wB2.mjs.map +1 -0
- package/dist/chunks/zh-C0I3L6NA.mjs +2 -0
- package/dist/chunks/zh-C0I3L6NA.mjs.map +1 -0
- package/dist/chunks/zh-C33e4gfg.mjs +2 -0
- package/dist/chunks/zh-C33e4gfg.mjs.map +1 -0
- package/dist/chunks/zh-C9sNwnC1.mjs +2 -0
- package/dist/chunks/zh-C9sNwnC1.mjs.map +1 -0
- package/dist/chunks/zh-CACr0qSo.mjs +2 -0
- package/dist/chunks/zh-CACr0qSo.mjs.map +1 -0
- package/dist/chunks/zh-CU0hd8zq.mjs +2 -0
- package/dist/chunks/zh-CU0hd8zq.mjs.map +1 -0
- package/dist/chunks/zh-CX2zUoWR.mjs +2 -0
- package/dist/chunks/zh-CX2zUoWR.mjs.map +1 -0
- package/dist/chunks/zh-CblZ-34H.mjs +2 -0
- package/dist/chunks/zh-CblZ-34H.mjs.map +1 -0
- package/dist/chunks/zh-CicpU0g8.mjs +2 -0
- package/dist/chunks/zh-CicpU0g8.mjs.map +1 -0
- package/dist/chunks/zh-Cr3KdEZV.mjs +2 -0
- package/dist/chunks/zh-Cr3KdEZV.mjs.map +1 -0
- package/dist/chunks/zh-D24dsCiw.mjs +2 -0
- package/dist/chunks/zh-D24dsCiw.mjs.map +1 -0
- package/dist/chunks/zh-DQtLxQ3s.mjs +2 -0
- package/dist/chunks/zh-DQtLxQ3s.mjs.map +1 -0
- package/dist/chunks/zh-DR1OF5vm.mjs +2 -0
- package/dist/chunks/zh-DR1OF5vm.mjs.map +1 -0
- package/dist/chunks/zh-DS82x1KL.mjs +2 -0
- package/dist/chunks/zh-DS82x1KL.mjs.map +1 -0
- package/dist/chunks/zh-DdJA-AEY.mjs +2 -0
- package/dist/chunks/zh-DdJA-AEY.mjs.map +1 -0
- package/dist/chunks/zh-DpVlDZ9n.mjs +2 -0
- package/dist/chunks/zh-DpVlDZ9n.mjs.map +1 -0
- package/dist/chunks/zh-OWVKcbVR.mjs +2 -0
- package/dist/chunks/zh-OWVKcbVR.mjs.map +1 -0
- package/dist/chunks/zh-d19V68ib.mjs +2 -0
- package/dist/chunks/zh-d19V68ib.mjs.map +1 -0
- package/dist/chunks/zh-fSMt1FnB.mjs +2 -0
- package/dist/chunks/zh-fSMt1FnB.mjs.map +1 -0
- package/dist/chunks/zh-mLlk9Wki.mjs +2 -0
- package/dist/chunks/zh-mLlk9Wki.mjs.map +1 -0
- package/dist/chunks/zh-nnMt8E6m.mjs +2 -0
- package/dist/chunks/zh-nnMt8E6m.mjs.map +1 -0
- package/dist/editor/ContentSerializer.d.ts +10 -0
- package/dist/editor/ContentSerializer.d.ts.map +1 -1
- package/dist/editor/EditorInitializer.d.ts.map +1 -1
- package/dist/editor/NotectlEditor.d.ts +25 -2
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/styles/base.d.ts +1 -1
- package/dist/editor/styles/base.d.ts.map +1 -1
- package/dist/editor/styles/toolbar.d.ts +1 -1
- package/dist/editor/styles/toolbar.d.ts.map +1 -1
- package/dist/fonts.mjs +1 -1
- package/dist/full.d.ts +2 -1
- package/dist/full.d.ts.map +1 -1
- package/dist/full.mjs +1 -1
- package/dist/html.mjs +1 -1
- package/dist/i18n/LocaleService.d.ts +1 -1
- package/dist/i18n/LocaleService.d.ts.map +1 -1
- package/dist/input/HTMLParser.d.ts.map +1 -1
- package/dist/model/HTMLUtils.d.ts +11 -0
- package/dist/model/HTMLUtils.d.ts.map +1 -1
- package/dist/model/TypedKeys.d.ts +23 -0
- package/dist/model/TypedKeys.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/plugins/Plugin.d.ts +2 -12
- package/dist/plugins/Plugin.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/bidi-isolation/BidiIsolationLocale.d.ts +12 -0
- package/dist/plugins/bidi-isolation/BidiIsolationLocale.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/BidiIsolationPlugin.d.ts +34 -0
- package/dist/plugins/bidi-isolation/BidiIsolationPlugin.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/index.d.ts +4 -0
- package/dist/plugins/bidi-isolation/index.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/ar.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/ar.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/de.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/de.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/es.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/es.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/fr.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/fr.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/hi.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/hi.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/pt.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/pt.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/ru.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/ru.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation/locales/zh.d.ts +4 -0
- package/dist/plugins/bidi-isolation/locales/zh.d.ts.map +1 -0
- package/dist/plugins/bidi-isolation.mjs +1 -0
- package/dist/plugins/blockquote.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +1 -1
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/inline-code.mjs +1 -1
- package/dist/plugins/language.mjs +1 -1
- package/dist/plugins/link/LinkLocale.d.ts +1 -0
- package/dist/plugins/link/LinkLocale.d.ts.map +1 -1
- package/dist/plugins/link/LinkPlugin.d.ts.map +1 -1
- package/dist/plugins/link/locales/ar.d.ts.map +1 -1
- package/dist/plugins/link/locales/de.d.ts.map +1 -1
- package/dist/plugins/link/locales/es.d.ts.map +1 -1
- package/dist/plugins/link/locales/fr.d.ts.map +1 -1
- package/dist/plugins/link/locales/hi.d.ts.map +1 -1
- package/dist/plugins/link/locales/pt.d.ts.map +1 -1
- package/dist/plugins/link/locales/ru.d.ts.map +1 -1
- package/dist/plugins/link/locales/zh.d.ts.map +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print.mjs +1 -1
- package/dist/plugins/shared.mjs +1 -1
- package/dist/plugins/smart-paste.mjs +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-direction/DirectionIcons.d.ts +3 -0
- package/dist/plugins/text-direction/DirectionIcons.d.ts.map +1 -0
- package/dist/plugins/text-direction/TextDirectionLocale.d.ts +0 -6
- package/dist/plugins/text-direction/TextDirectionLocale.d.ts.map +1 -1
- package/dist/plugins/text-direction/TextDirectionPlugin.d.ts +3 -30
- package/dist/plugins/text-direction/TextDirectionPlugin.d.ts.map +1 -1
- package/dist/plugins/text-direction/TextDirectionService.d.ts +9 -0
- package/dist/plugins/text-direction/TextDirectionService.d.ts.map +1 -0
- package/dist/plugins/text-direction/index.d.ts +3 -1
- package/dist/plugins/text-direction/index.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/ar.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/de.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/es.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/fr.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/hi.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/pt.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/ru.d.ts.map +1 -1
- package/dist/plugins/text-direction/locales/zh.d.ts.map +1 -1
- package/dist/plugins/text-direction-auto/TextDirectionAutoMiddleware.d.ts +5 -0
- package/dist/plugins/text-direction-auto/TextDirectionAutoMiddleware.d.ts.map +1 -0
- package/dist/plugins/text-direction-auto/TextDirectionAutoPlugin.d.ts +28 -0
- package/dist/plugins/text-direction-auto/TextDirectionAutoPlugin.d.ts.map +1 -0
- package/dist/plugins/text-direction-auto/index.d.ts +2 -0
- package/dist/plugins/text-direction-auto/index.d.ts.map +1 -0
- package/dist/plugins/text-direction-auto.mjs +1 -0
- package/dist/plugins/text-direction.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar.mjs +1 -1
- package/dist/presets/FullPreset.d.ts +3 -1
- package/dist/presets/FullPreset.d.ts.map +1 -1
- package/dist/presets/PresetTypes.d.ts +5 -1
- package/dist/presets/PresetTypes.d.ts.map +1 -1
- package/dist/presets/full.mjs +1 -1
- package/dist/presets/minimal.mjs +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/serialization/BlockIdHTML.d.ts +21 -0
- package/dist/serialization/BlockIdHTML.d.ts.map +1 -0
- package/dist/serialization/DocumentParser.d.ts.map +1 -1
- package/dist/serialization/DocumentSerializer.d.ts.map +1 -1
- package/dist/state/EditorState.d.ts +2 -0
- package/dist/state/EditorState.d.ts.map +1 -1
- package/dist/test/TestUtils.d.ts +17 -0
- package/dist/test/TestUtils.d.ts.map +1 -1
- package/dist/view/EditorView.d.ts +18 -1
- package/dist/view/EditorView.d.ts.map +1 -1
- package/package.json +19 -9
- package/dist/chunks/AlignmentPlugin-D1wmhWE6.mjs +0 -2
- package/dist/chunks/AlignmentPlugin-D1wmhWE6.mjs.map +0 -1
- package/dist/chunks/BlockquotePlugin-Bwny0CP6.mjs +0 -2
- package/dist/chunks/BlockquotePlugin-Bwny0CP6.mjs.map +0 -1
- package/dist/chunks/CaretNavigationPlugin-C-rCDbu0.mjs +0 -2
- package/dist/chunks/CaretNavigationPlugin-C-rCDbu0.mjs.map +0 -1
- package/dist/chunks/CodeBlockPlugin-CNC290_q.mjs +0 -2
- package/dist/chunks/CodeBlockPlugin-CNC290_q.mjs.map +0 -1
- package/dist/chunks/ColorGrid-BUSIBKZH.mjs +0 -2
- package/dist/chunks/ColorGrid-BUSIBKZH.mjs.map +0 -1
- package/dist/chunks/ColorPickerPopup-CvE90IAu.mjs +0 -2
- package/dist/chunks/ColorPickerPopup-CvE90IAu.mjs.map +0 -1
- package/dist/chunks/DocumentParser-Cq_bFYAS.mjs +0 -2
- package/dist/chunks/DocumentParser-Cq_bFYAS.mjs.map +0 -1
- package/dist/chunks/FontPlugin-z0QF1M5q.mjs +0 -2
- package/dist/chunks/FontPlugin-z0QF1M5q.mjs.map +0 -1
- package/dist/chunks/FontSizePlugin-DYKvhu9w.mjs +0 -2
- package/dist/chunks/FontSizePlugin-DYKvhu9w.mjs.map +0 -1
- package/dist/chunks/GapCursorPlugin-uxw7W7vj.mjs +0 -2
- package/dist/chunks/GapCursorPlugin-uxw7W7vj.mjs.map +0 -1
- package/dist/chunks/HTMLUtils-B8xj8Z1x.mjs +0 -2
- package/dist/chunks/HTMLUtils-B8xj8Z1x.mjs.map +0 -1
- package/dist/chunks/HardBreakPlugin-BpXipf8h.mjs +0 -2
- package/dist/chunks/HardBreakPlugin-BpXipf8h.mjs.map +0 -1
- package/dist/chunks/HeadingPlugin-CUN746aD.mjs +0 -2
- package/dist/chunks/HeadingPlugin-CUN746aD.mjs.map +0 -1
- package/dist/chunks/HighlightPlugin-bbAi6W6k.mjs +0 -2
- package/dist/chunks/HighlightPlugin-bbAi6W6k.mjs.map +0 -1
- package/dist/chunks/HorizontalRulePlugin-D8w6paMa.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-D8w6paMa.mjs.map +0 -1
- package/dist/chunks/ImagePlugin-De7lWcZ7.mjs +0 -2
- package/dist/chunks/ImagePlugin-De7lWcZ7.mjs.map +0 -1
- package/dist/chunks/InlineCodePlugin-DKQIYhTb.mjs +0 -2
- package/dist/chunks/InlineCodePlugin-DKQIYhTb.mjs.map +0 -1
- package/dist/chunks/LinkPlugin-BD7mcRHS.mjs +0 -2
- package/dist/chunks/LinkPlugin-BD7mcRHS.mjs.map +0 -1
- package/dist/chunks/ListPlugin-DbixSqMH.mjs +0 -2
- package/dist/chunks/ListPlugin-DbixSqMH.mjs.map +0 -1
- package/dist/chunks/Plugin-BUNGKupW.mjs +0 -2
- package/dist/chunks/Plugin-BUNGKupW.mjs.map +0 -1
- package/dist/chunks/PluginHelpers-D1QF-YaF.mjs +0 -2
- package/dist/chunks/PluginHelpers-D1QF-YaF.mjs.map +0 -1
- package/dist/chunks/PopupKeyboardPatterns-DhcUe29m.mjs +0 -2
- package/dist/chunks/PopupKeyboardPatterns-DhcUe29m.mjs.map +0 -1
- package/dist/chunks/PopupManager-DaTySyZl.mjs +0 -2
- package/dist/chunks/PopupManager-DaTySyZl.mjs.map +0 -1
- package/dist/chunks/PrintPlugin-DVRybXSN.mjs +0 -2
- package/dist/chunks/PrintPlugin-DVRybXSN.mjs.map +0 -1
- package/dist/chunks/PrintTypes-DTV3-mSe.mjs +0 -2
- package/dist/chunks/PrintTypes-DTV3-mSe.mjs.map +0 -1
- package/dist/chunks/SmartPastePlugin-i96yAnXZ.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-i96yAnXZ.mjs.map +0 -1
- package/dist/chunks/StrikethroughPlugin-RGZATXUZ.mjs +0 -2
- package/dist/chunks/StrikethroughPlugin-RGZATXUZ.mjs.map +0 -1
- package/dist/chunks/SuperSubPlugin-BOWFP1Kr.mjs +0 -2
- package/dist/chunks/SuperSubPlugin-BOWFP1Kr.mjs.map +0 -1
- package/dist/chunks/TablePlugin-DZTMBkyu.mjs +0 -2
- package/dist/chunks/TablePlugin-DZTMBkyu.mjs.map +0 -1
- package/dist/chunks/TextColorPlugin-CB3RpTSQ.mjs +0 -2
- package/dist/chunks/TextColorPlugin-CB3RpTSQ.mjs.map +0 -1
- package/dist/chunks/TextDirectionCorePlugin-tNecTcrX.mjs +0 -2
- package/dist/chunks/TextDirectionCorePlugin-tNecTcrX.mjs.map +0 -1
- package/dist/chunks/TextDirectionLocale-_AUfZu9j.mjs +0 -2
- package/dist/chunks/TextDirectionLocale-_AUfZu9j.mjs.map +0 -1
- package/dist/chunks/TextFormattingPlugin-CZ6D-luc.mjs +0 -2
- package/dist/chunks/TextFormattingPlugin-CZ6D-luc.mjs.map +0 -1
- package/dist/chunks/ToolbarPlugin-2K96zYut.mjs +0 -2
- package/dist/chunks/ToolbarPlugin-2K96zYut.mjs.map +0 -1
- package/dist/chunks/XmlDetector-DlRdJOIX.mjs +0 -2
- package/dist/chunks/XmlDetector-DlRdJOIX.mjs.map +0 -1
- package/dist/chunks/XmlPatterns-Dwmn4nrg.mjs +0 -2
- package/dist/chunks/XmlPatterns-Dwmn4nrg.mjs.map +0 -1
- package/dist/chunks/XmlSupport-DG8PEZhO.mjs +0 -2
- package/dist/chunks/XmlSupport-DG8PEZhO.mjs.map +0 -1
- package/dist/chunks/ar--dzNQRg4.mjs +0 -2
- package/dist/chunks/ar--dzNQRg4.mjs.map +0 -1
- package/dist/chunks/ar-13q9ejck.mjs +0 -2
- package/dist/chunks/ar-13q9ejck.mjs.map +0 -1
- package/dist/chunks/ar-7XO2HmsM.mjs +0 -2
- package/dist/chunks/ar-7XO2HmsM.mjs.map +0 -1
- package/dist/chunks/ar-B-n_gMXb.mjs +0 -2
- package/dist/chunks/ar-B-n_gMXb.mjs.map +0 -1
- package/dist/chunks/ar-BXeE6tKj.mjs +0 -2
- package/dist/chunks/ar-BXeE6tKj.mjs.map +0 -1
- package/dist/chunks/ar-Bs3sx3Oz.mjs +0 -2
- package/dist/chunks/ar-Bs3sx3Oz.mjs.map +0 -1
- package/dist/chunks/ar-ByNQ-o3t.mjs +0 -2
- package/dist/chunks/ar-ByNQ-o3t.mjs.map +0 -1
- package/dist/chunks/ar-C9AnO0uV.mjs +0 -2
- package/dist/chunks/ar-C9AnO0uV.mjs.map +0 -1
- package/dist/chunks/ar-CL-NsFoh.mjs +0 -2
- package/dist/chunks/ar-CL-NsFoh.mjs.map +0 -1
- package/dist/chunks/ar-COx4cFpT.mjs +0 -2
- package/dist/chunks/ar-COx4cFpT.mjs.map +0 -1
- package/dist/chunks/ar-CYZYoqcc.mjs +0 -2
- package/dist/chunks/ar-CYZYoqcc.mjs.map +0 -1
- package/dist/chunks/ar-ChOxTdPI.mjs +0 -2
- package/dist/chunks/ar-ChOxTdPI.mjs.map +0 -1
- package/dist/chunks/ar-Cj6amEs9.mjs +0 -2
- package/dist/chunks/ar-Cj6amEs9.mjs.map +0 -1
- package/dist/chunks/ar-CsH2S6Vd.mjs +0 -2
- package/dist/chunks/ar-CsH2S6Vd.mjs.map +0 -1
- package/dist/chunks/ar-D2y_YvMP.mjs +0 -2
- package/dist/chunks/ar-D2y_YvMP.mjs.map +0 -1
- package/dist/chunks/ar-D57VZrg2.mjs +0 -2
- package/dist/chunks/ar-D57VZrg2.mjs.map +0 -1
- package/dist/chunks/ar-DH06YQkP.mjs +0 -2
- package/dist/chunks/ar-DH06YQkP.mjs.map +0 -1
- package/dist/chunks/ar-DJ6K9HnU.mjs +0 -2
- package/dist/chunks/ar-DJ6K9HnU.mjs.map +0 -1
- package/dist/chunks/ar-DMNNIim-.mjs +0 -2
- package/dist/chunks/ar-DMNNIim-.mjs.map +0 -1
- package/dist/chunks/ar-DW0Inc7r.mjs +0 -2
- package/dist/chunks/ar-DW0Inc7r.mjs.map +0 -1
- package/dist/chunks/ar-SwJ-tyIu.mjs +0 -2
- package/dist/chunks/ar-SwJ-tyIu.mjs.map +0 -1
- package/dist/chunks/ar-Vfk7XFaz.mjs +0 -2
- package/dist/chunks/ar-Vfk7XFaz.mjs.map +0 -1
- package/dist/chunks/ar-izXCtb2I.mjs +0 -2
- package/dist/chunks/ar-izXCtb2I.mjs.map +0 -1
- package/dist/chunks/ar-jsznaXQ0.mjs +0 -2
- package/dist/chunks/ar-jsznaXQ0.mjs.map +0 -1
- package/dist/chunks/color-picker-ChYYdC-r.mjs +0 -2
- package/dist/chunks/color-picker-ChYYdC-r.mjs.map +0 -1
- package/dist/chunks/de-3PJCshB7.mjs +0 -2
- package/dist/chunks/de-3PJCshB7.mjs.map +0 -1
- package/dist/chunks/de-B92lssMU.mjs +0 -2
- package/dist/chunks/de-B92lssMU.mjs.map +0 -1
- package/dist/chunks/de-BJ8Lo9rY.mjs +0 -2
- package/dist/chunks/de-BJ8Lo9rY.mjs.map +0 -1
- package/dist/chunks/de-BjNoeX05.mjs +0 -2
- package/dist/chunks/de-BjNoeX05.mjs.map +0 -1
- package/dist/chunks/de-Bktm7FKk.mjs +0 -2
- package/dist/chunks/de-Bktm7FKk.mjs.map +0 -1
- package/dist/chunks/de-BqHw2LVH.mjs +0 -2
- package/dist/chunks/de-BqHw2LVH.mjs.map +0 -1
- package/dist/chunks/de-BzjqF_j0.mjs +0 -2
- package/dist/chunks/de-BzjqF_j0.mjs.map +0 -1
- package/dist/chunks/de-C7Grl0t1.mjs +0 -2
- package/dist/chunks/de-C7Grl0t1.mjs.map +0 -1
- package/dist/chunks/de-CWsXH4_J.mjs +0 -2
- package/dist/chunks/de-CWsXH4_J.mjs.map +0 -1
- package/dist/chunks/de-CWwHCIzX.mjs +0 -2
- package/dist/chunks/de-CWwHCIzX.mjs.map +0 -1
- package/dist/chunks/de-ChHR-g0q.mjs +0 -2
- package/dist/chunks/de-ChHR-g0q.mjs.map +0 -1
- package/dist/chunks/de-CqJFzINs.mjs +0 -2
- package/dist/chunks/de-CqJFzINs.mjs.map +0 -1
- package/dist/chunks/de-DAPQek-i.mjs +0 -2
- package/dist/chunks/de-DAPQek-i.mjs.map +0 -1
- package/dist/chunks/de-DPzOP2Ge.mjs +0 -2
- package/dist/chunks/de-DPzOP2Ge.mjs.map +0 -1
- package/dist/chunks/de-DhenqH4M.mjs +0 -2
- package/dist/chunks/de-DhenqH4M.mjs.map +0 -1
- package/dist/chunks/de-Ds6tfD71.mjs +0 -2
- package/dist/chunks/de-Ds6tfD71.mjs.map +0 -1
- package/dist/chunks/de-DxyfCVlM.mjs +0 -2
- package/dist/chunks/de-DxyfCVlM.mjs.map +0 -1
- package/dist/chunks/de-RSzW4zsb.mjs +0 -2
- package/dist/chunks/de-RSzW4zsb.mjs.map +0 -1
- package/dist/chunks/de-YiJZ87p6.mjs +0 -2
- package/dist/chunks/de-YiJZ87p6.mjs.map +0 -1
- package/dist/chunks/de-e7G64WBM.mjs +0 -2
- package/dist/chunks/de-e7G64WBM.mjs.map +0 -1
- package/dist/chunks/de-oeen0lTL.mjs +0 -2
- package/dist/chunks/de-oeen0lTL.mjs.map +0 -1
- package/dist/chunks/de-p1zboXJW.mjs +0 -2
- package/dist/chunks/de-p1zboXJW.mjs.map +0 -1
- package/dist/chunks/de-q2xHBWVU.mjs +0 -2
- package/dist/chunks/de-q2xHBWVU.mjs.map +0 -1
- package/dist/chunks/de-zjzkzZs9.mjs +0 -2
- package/dist/chunks/de-zjzkzZs9.mjs.map +0 -1
- package/dist/chunks/es-2U-tuiWo.mjs +0 -2
- package/dist/chunks/es-2U-tuiWo.mjs.map +0 -1
- package/dist/chunks/es-8kEYE5eN.mjs +0 -2
- package/dist/chunks/es-8kEYE5eN.mjs.map +0 -1
- package/dist/chunks/es-B2B0W46I.mjs +0 -2
- package/dist/chunks/es-B2B0W46I.mjs.map +0 -1
- package/dist/chunks/es-B8Ne6vhS.mjs +0 -2
- package/dist/chunks/es-B8Ne6vhS.mjs.map +0 -1
- package/dist/chunks/es-BBaS8xrI.mjs +0 -2
- package/dist/chunks/es-BBaS8xrI.mjs.map +0 -1
- package/dist/chunks/es-BJibD3UW.mjs +0 -2
- package/dist/chunks/es-BJibD3UW.mjs.map +0 -1
- package/dist/chunks/es-BUIOf37N.mjs +0 -2
- package/dist/chunks/es-BUIOf37N.mjs.map +0 -1
- package/dist/chunks/es-BcEc0JEI.mjs +0 -2
- package/dist/chunks/es-BcEc0JEI.mjs.map +0 -1
- package/dist/chunks/es-BjomAlG3.mjs +0 -2
- package/dist/chunks/es-BjomAlG3.mjs.map +0 -1
- package/dist/chunks/es-BpgE28ol.mjs +0 -2
- package/dist/chunks/es-BpgE28ol.mjs.map +0 -1
- package/dist/chunks/es-Bq_XkuDG.mjs +0 -2
- package/dist/chunks/es-Bq_XkuDG.mjs.map +0 -1
- package/dist/chunks/es-BtTQw2t5.mjs +0 -2
- package/dist/chunks/es-BtTQw2t5.mjs.map +0 -1
- package/dist/chunks/es-BwmzRdDZ.mjs +0 -2
- package/dist/chunks/es-BwmzRdDZ.mjs.map +0 -1
- package/dist/chunks/es-Bwy7v223.mjs +0 -2
- package/dist/chunks/es-Bwy7v223.mjs.map +0 -1
- package/dist/chunks/es-C0Jm_n57.mjs +0 -2
- package/dist/chunks/es-C0Jm_n57.mjs.map +0 -1
- package/dist/chunks/es-C49UmQIT.mjs +0 -2
- package/dist/chunks/es-C49UmQIT.mjs.map +0 -1
- package/dist/chunks/es-CCAT_-nb.mjs +0 -2
- package/dist/chunks/es-CCAT_-nb.mjs.map +0 -1
- package/dist/chunks/es-CGGqdLIQ.mjs +0 -2
- package/dist/chunks/es-CGGqdLIQ.mjs.map +0 -1
- package/dist/chunks/es-CYKm6CQM.mjs +0 -2
- package/dist/chunks/es-CYKm6CQM.mjs.map +0 -1
- package/dist/chunks/es-C_L1Wdfi.mjs +0 -2
- package/dist/chunks/es-C_L1Wdfi.mjs.map +0 -1
- package/dist/chunks/es-DEo4yDI6.mjs +0 -2
- package/dist/chunks/es-DEo4yDI6.mjs.map +0 -1
- package/dist/chunks/es-D_6MYvFK.mjs +0 -2
- package/dist/chunks/es-D_6MYvFK.mjs.map +0 -1
- package/dist/chunks/es-DcYIJR0T.mjs +0 -2
- package/dist/chunks/es-DcYIJR0T.mjs.map +0 -1
- package/dist/chunks/es-y2SZmX3P.mjs +0 -2
- package/dist/chunks/es-y2SZmX3P.mjs.map +0 -1
- package/dist/chunks/fonts-CG_dDwvT.mjs +0 -2
- package/dist/chunks/fonts-CG_dDwvT.mjs.map +0 -1
- package/dist/chunks/fr-Ame1k629.mjs +0 -2
- package/dist/chunks/fr-Ame1k629.mjs.map +0 -1
- package/dist/chunks/fr-BCN045Ph.mjs +0 -2
- package/dist/chunks/fr-BCN045Ph.mjs.map +0 -1
- package/dist/chunks/fr-BQaiAAa6.mjs +0 -2
- package/dist/chunks/fr-BQaiAAa6.mjs.map +0 -1
- package/dist/chunks/fr-BR61Uevp.mjs +0 -2
- package/dist/chunks/fr-BR61Uevp.mjs.map +0 -1
- package/dist/chunks/fr-Bb-xVkAd.mjs +0 -2
- package/dist/chunks/fr-Bb-xVkAd.mjs.map +0 -1
- package/dist/chunks/fr-BhM3Ykql.mjs +0 -2
- package/dist/chunks/fr-BhM3Ykql.mjs.map +0 -1
- package/dist/chunks/fr-BqQRlsfW.mjs +0 -2
- package/dist/chunks/fr-BqQRlsfW.mjs.map +0 -1
- package/dist/chunks/fr-C81THS0v.mjs +0 -2
- package/dist/chunks/fr-C81THS0v.mjs.map +0 -1
- package/dist/chunks/fr-CC7TYK5K.mjs +0 -2
- package/dist/chunks/fr-CC7TYK5K.mjs.map +0 -1
- package/dist/chunks/fr-CXzpB6GU.mjs +0 -2
- package/dist/chunks/fr-CXzpB6GU.mjs.map +0 -1
- package/dist/chunks/fr-CjLyVnG3.mjs +0 -2
- package/dist/chunks/fr-CjLyVnG3.mjs.map +0 -1
- package/dist/chunks/fr-D66EEW9-.mjs +0 -2
- package/dist/chunks/fr-D66EEW9-.mjs.map +0 -1
- package/dist/chunks/fr-DHDNGQpl.mjs +0 -2
- package/dist/chunks/fr-DHDNGQpl.mjs.map +0 -1
- package/dist/chunks/fr-DOcFqSiX.mjs +0 -2
- package/dist/chunks/fr-DOcFqSiX.mjs.map +0 -1
- package/dist/chunks/fr-DPPHZ6H7.mjs +0 -2
- package/dist/chunks/fr-DPPHZ6H7.mjs.map +0 -1
- package/dist/chunks/fr-DVteHTTh.mjs +0 -2
- package/dist/chunks/fr-DVteHTTh.mjs.map +0 -1
- package/dist/chunks/fr-Dkeg9Am0.mjs +0 -2
- package/dist/chunks/fr-Dkeg9Am0.mjs.map +0 -1
- package/dist/chunks/fr-DnP_hh5Q.mjs +0 -2
- package/dist/chunks/fr-DnP_hh5Q.mjs.map +0 -1
- package/dist/chunks/fr-aDJE54Is.mjs +0 -2
- package/dist/chunks/fr-aDJE54Is.mjs.map +0 -1
- package/dist/chunks/fr-hStmLXCS.mjs +0 -2
- package/dist/chunks/fr-hStmLXCS.mjs.map +0 -1
- package/dist/chunks/fr-hUF51sR5.mjs +0 -2
- package/dist/chunks/fr-hUF51sR5.mjs.map +0 -1
- package/dist/chunks/fr-tK6rWhoz.mjs +0 -2
- package/dist/chunks/fr-tK6rWhoz.mjs.map +0 -1
- package/dist/chunks/fr-wqbTMwkj.mjs +0 -2
- package/dist/chunks/fr-wqbTMwkj.mjs.map +0 -1
- package/dist/chunks/fr-zO4UIZj_.mjs +0 -2
- package/dist/chunks/fr-zO4UIZj_.mjs.map +0 -1
- package/dist/chunks/full-B9IqlP6u.mjs +0 -2
- package/dist/chunks/full-B9IqlP6u.mjs.map +0 -1
- package/dist/chunks/hi-B7zs2lw-.mjs +0 -2
- package/dist/chunks/hi-B7zs2lw-.mjs.map +0 -1
- package/dist/chunks/hi-BE4pkSYX.mjs +0 -2
- package/dist/chunks/hi-BE4pkSYX.mjs.map +0 -1
- package/dist/chunks/hi-BEBdteKo.mjs +0 -2
- package/dist/chunks/hi-BEBdteKo.mjs.map +0 -1
- package/dist/chunks/hi-BFBRjxgB.mjs +0 -2
- package/dist/chunks/hi-BFBRjxgB.mjs.map +0 -1
- package/dist/chunks/hi-BGMkRr1l.mjs +0 -2
- package/dist/chunks/hi-BGMkRr1l.mjs.map +0 -1
- package/dist/chunks/hi-BKJbF0k9.mjs +0 -2
- package/dist/chunks/hi-BKJbF0k9.mjs.map +0 -1
- package/dist/chunks/hi-Bj-wENIe.mjs +0 -2
- package/dist/chunks/hi-Bj-wENIe.mjs.map +0 -1
- package/dist/chunks/hi-Bo_sc83d.mjs +0 -2
- package/dist/chunks/hi-Bo_sc83d.mjs.map +0 -1
- package/dist/chunks/hi-BqdojmVi.mjs +0 -2
- package/dist/chunks/hi-BqdojmVi.mjs.map +0 -1
- package/dist/chunks/hi-BtN2T9la.mjs +0 -2
- package/dist/chunks/hi-BtN2T9la.mjs.map +0 -1
- package/dist/chunks/hi-C2HFcJ8H.mjs +0 -2
- package/dist/chunks/hi-C2HFcJ8H.mjs.map +0 -1
- package/dist/chunks/hi-C9hGhR9D.mjs +0 -2
- package/dist/chunks/hi-C9hGhR9D.mjs.map +0 -1
- package/dist/chunks/hi-CRsCP_UU.mjs +0 -2
- package/dist/chunks/hi-CRsCP_UU.mjs.map +0 -1
- package/dist/chunks/hi-CSatC6e_.mjs +0 -2
- package/dist/chunks/hi-CSatC6e_.mjs.map +0 -1
- package/dist/chunks/hi-Cik-gmhU.mjs +0 -2
- package/dist/chunks/hi-Cik-gmhU.mjs.map +0 -1
- package/dist/chunks/hi-Co2ThMh7.mjs +0 -2
- package/dist/chunks/hi-Co2ThMh7.mjs.map +0 -1
- package/dist/chunks/hi-CrATuZJr.mjs +0 -2
- package/dist/chunks/hi-CrATuZJr.mjs.map +0 -1
- package/dist/chunks/hi-D-r5tA_n.mjs +0 -2
- package/dist/chunks/hi-D-r5tA_n.mjs.map +0 -1
- package/dist/chunks/hi-D0RKvhDO.mjs +0 -2
- package/dist/chunks/hi-D0RKvhDO.mjs.map +0 -1
- package/dist/chunks/hi-DOSbCXy_.mjs +0 -2
- package/dist/chunks/hi-DOSbCXy_.mjs.map +0 -1
- package/dist/chunks/hi-DTUY7wlZ.mjs +0 -2
- package/dist/chunks/hi-DTUY7wlZ.mjs.map +0 -1
- package/dist/chunks/hi-FhcxnL2S.mjs +0 -2
- package/dist/chunks/hi-FhcxnL2S.mjs.map +0 -1
- package/dist/chunks/hi-K71sKi2-.mjs +0 -2
- package/dist/chunks/hi-K71sKi2-.mjs.map +0 -1
- package/dist/chunks/hi-xjChf5ZY.mjs +0 -2
- package/dist/chunks/hi-xjChf5ZY.mjs.map +0 -1
- package/dist/chunks/minimal-J-u7e_pr.mjs +0 -2
- package/dist/chunks/minimal-J-u7e_pr.mjs.map +0 -1
- package/dist/chunks/pt-6uTV9TBd.mjs +0 -2
- package/dist/chunks/pt-6uTV9TBd.mjs.map +0 -1
- package/dist/chunks/pt-7ZYOqmC4.mjs +0 -2
- package/dist/chunks/pt-7ZYOqmC4.mjs.map +0 -1
- package/dist/chunks/pt-B59gJzMS.mjs +0 -2
- package/dist/chunks/pt-B59gJzMS.mjs.map +0 -1
- package/dist/chunks/pt-B65Mk0F_.mjs +0 -2
- package/dist/chunks/pt-B65Mk0F_.mjs.map +0 -1
- package/dist/chunks/pt-B9c20zi-.mjs +0 -2
- package/dist/chunks/pt-B9c20zi-.mjs.map +0 -1
- package/dist/chunks/pt-BRQRHBoW.mjs +0 -2
- package/dist/chunks/pt-BRQRHBoW.mjs.map +0 -1
- package/dist/chunks/pt-BYsPmDNG.mjs +0 -2
- package/dist/chunks/pt-BYsPmDNG.mjs.map +0 -1
- package/dist/chunks/pt-Bau_d96t.mjs +0 -2
- package/dist/chunks/pt-Bau_d96t.mjs.map +0 -1
- package/dist/chunks/pt-Bb3sODVO.mjs +0 -2
- package/dist/chunks/pt-Bb3sODVO.mjs.map +0 -1
- package/dist/chunks/pt-BieXcDHi.mjs +0 -2
- package/dist/chunks/pt-BieXcDHi.mjs.map +0 -1
- package/dist/chunks/pt-BzJZ6tis.mjs +0 -2
- package/dist/chunks/pt-BzJZ6tis.mjs.map +0 -1
- package/dist/chunks/pt-CDBahjbg.mjs +0 -2
- package/dist/chunks/pt-CDBahjbg.mjs.map +0 -1
- package/dist/chunks/pt-CQIn2xA5.mjs +0 -2
- package/dist/chunks/pt-CQIn2xA5.mjs.map +0 -1
- package/dist/chunks/pt-Cx3gdB5p.mjs +0 -2
- package/dist/chunks/pt-Cx3gdB5p.mjs.map +0 -1
- package/dist/chunks/pt-D7n_VUMD.mjs +0 -2
- package/dist/chunks/pt-D7n_VUMD.mjs.map +0 -1
- package/dist/chunks/pt-DEg_u7oB.mjs +0 -2
- package/dist/chunks/pt-DEg_u7oB.mjs.map +0 -1
- package/dist/chunks/pt-DchozfDf.mjs +0 -2
- package/dist/chunks/pt-DchozfDf.mjs.map +0 -1
- package/dist/chunks/pt-DnHYzWZI.mjs +0 -2
- package/dist/chunks/pt-DnHYzWZI.mjs.map +0 -1
- package/dist/chunks/pt-Dne18ay-.mjs +0 -2
- package/dist/chunks/pt-Dne18ay-.mjs.map +0 -1
- package/dist/chunks/pt-DxTZELCn.mjs +0 -2
- package/dist/chunks/pt-DxTZELCn.mjs.map +0 -1
- package/dist/chunks/pt-FnLj_fwL.mjs +0 -2
- package/dist/chunks/pt-FnLj_fwL.mjs.map +0 -1
- package/dist/chunks/pt-QZcGM_AW.mjs +0 -2
- package/dist/chunks/pt-QZcGM_AW.mjs.map +0 -1
- package/dist/chunks/pt-X4oj0zCg.mjs +0 -2
- package/dist/chunks/pt-X4oj0zCg.mjs.map +0 -1
- package/dist/chunks/pt-hgZhGt5D.mjs +0 -2
- package/dist/chunks/pt-hgZhGt5D.mjs.map +0 -1
- package/dist/chunks/ru-333jycve.mjs +0 -2
- package/dist/chunks/ru-333jycve.mjs.map +0 -1
- package/dist/chunks/ru-B278yBd4.mjs +0 -2
- package/dist/chunks/ru-B278yBd4.mjs.map +0 -1
- package/dist/chunks/ru-B2dlVf3F.mjs +0 -2
- package/dist/chunks/ru-B2dlVf3F.mjs.map +0 -1
- package/dist/chunks/ru-BHZdR6yk.mjs +0 -2
- package/dist/chunks/ru-BHZdR6yk.mjs.map +0 -1
- package/dist/chunks/ru-BhXqR_wI.mjs +0 -2
- package/dist/chunks/ru-BhXqR_wI.mjs.map +0 -1
- package/dist/chunks/ru-BkhY_-Tu.mjs +0 -2
- package/dist/chunks/ru-BkhY_-Tu.mjs.map +0 -1
- package/dist/chunks/ru-BqJmK6DG.mjs +0 -2
- package/dist/chunks/ru-BqJmK6DG.mjs.map +0 -1
- package/dist/chunks/ru-BscEW-yr.mjs +0 -2
- package/dist/chunks/ru-BscEW-yr.mjs.map +0 -1
- package/dist/chunks/ru-C91kg2w_.mjs +0 -2
- package/dist/chunks/ru-C91kg2w_.mjs.map +0 -1
- package/dist/chunks/ru-CQoFJYQk.mjs +0 -2
- package/dist/chunks/ru-CQoFJYQk.mjs.map +0 -1
- package/dist/chunks/ru-CYMaDdFA.mjs +0 -2
- package/dist/chunks/ru-CYMaDdFA.mjs.map +0 -1
- package/dist/chunks/ru-ClE-ByL-.mjs +0 -2
- package/dist/chunks/ru-ClE-ByL-.mjs.map +0 -1
- package/dist/chunks/ru-CmLLEJJV.mjs +0 -2
- package/dist/chunks/ru-CmLLEJJV.mjs.map +0 -1
- package/dist/chunks/ru-CmlYwIPH.mjs +0 -2
- package/dist/chunks/ru-CmlYwIPH.mjs.map +0 -1
- package/dist/chunks/ru-D74ItjLR.mjs +0 -2
- package/dist/chunks/ru-D74ItjLR.mjs.map +0 -1
- package/dist/chunks/ru-DR7ji_U_.mjs +0 -2
- package/dist/chunks/ru-DR7ji_U_.mjs.map +0 -1
- package/dist/chunks/ru-DZ0titlK.mjs +0 -2
- package/dist/chunks/ru-DZ0titlK.mjs.map +0 -1
- package/dist/chunks/ru-DftPghQ8.mjs +0 -2
- package/dist/chunks/ru-DftPghQ8.mjs.map +0 -1
- package/dist/chunks/ru-KBX9Y7TT.mjs +0 -2
- package/dist/chunks/ru-KBX9Y7TT.mjs.map +0 -1
- package/dist/chunks/ru-L3ML505C.mjs +0 -2
- package/dist/chunks/ru-L3ML505C.mjs.map +0 -1
- package/dist/chunks/ru-VAZhjO4q.mjs +0 -2
- package/dist/chunks/ru-VAZhjO4q.mjs.map +0 -1
- package/dist/chunks/ru-afocv3DK.mjs +0 -2
- package/dist/chunks/ru-afocv3DK.mjs.map +0 -1
- package/dist/chunks/ru-gUXymxbh.mjs +0 -2
- package/dist/chunks/ru-gUXymxbh.mjs.map +0 -1
- package/dist/chunks/ru-qL1ebv4O.mjs +0 -2
- package/dist/chunks/ru-qL1ebv4O.mjs.map +0 -1
- package/dist/chunks/src-Bq4jFC-Q.mjs +0 -2
- package/dist/chunks/src-Bq4jFC-Q.mjs.map +0 -1
- package/dist/chunks/text-direction-advanced-S4MgVMLX.mjs +0 -2
- package/dist/chunks/text-direction-advanced-S4MgVMLX.mjs.map +0 -1
- package/dist/chunks/zh-B3geJFGW.mjs +0 -2
- package/dist/chunks/zh-B3geJFGW.mjs.map +0 -1
- package/dist/chunks/zh-B4p7M6XB.mjs +0 -2
- package/dist/chunks/zh-B4p7M6XB.mjs.map +0 -1
- package/dist/chunks/zh-BkwaMj9a.mjs +0 -2
- package/dist/chunks/zh-BkwaMj9a.mjs.map +0 -1
- package/dist/chunks/zh-BogAT6ZW.mjs +0 -2
- package/dist/chunks/zh-BogAT6ZW.mjs.map +0 -1
- package/dist/chunks/zh-BvMEmsyF.mjs +0 -2
- package/dist/chunks/zh-BvMEmsyF.mjs.map +0 -1
- package/dist/chunks/zh-BzLh1p5b.mjs +0 -2
- package/dist/chunks/zh-BzLh1p5b.mjs.map +0 -1
- package/dist/chunks/zh-CBMpL9ha.mjs +0 -2
- package/dist/chunks/zh-CBMpL9ha.mjs.map +0 -1
- package/dist/chunks/zh-CIFsVnFl.mjs +0 -2
- package/dist/chunks/zh-CIFsVnFl.mjs.map +0 -1
- package/dist/chunks/zh-CP6_YzAc.mjs +0 -2
- package/dist/chunks/zh-CP6_YzAc.mjs.map +0 -1
- package/dist/chunks/zh-CT4V3EmW.mjs +0 -2
- package/dist/chunks/zh-CT4V3EmW.mjs.map +0 -1
- package/dist/chunks/zh-CZCIluI_.mjs +0 -2
- package/dist/chunks/zh-CZCIluI_.mjs.map +0 -1
- package/dist/chunks/zh-CcMLjYrk.mjs +0 -2
- package/dist/chunks/zh-CcMLjYrk.mjs.map +0 -1
- package/dist/chunks/zh-CfCQsLZa.mjs +0 -2
- package/dist/chunks/zh-CfCQsLZa.mjs.map +0 -1
- package/dist/chunks/zh-CsyBlhtm.mjs +0 -2
- package/dist/chunks/zh-CsyBlhtm.mjs.map +0 -1
- package/dist/chunks/zh-D4eu30KQ.mjs +0 -2
- package/dist/chunks/zh-D4eu30KQ.mjs.map +0 -1
- package/dist/chunks/zh-DSfxjb4Y.mjs +0 -2
- package/dist/chunks/zh-DSfxjb4Y.mjs.map +0 -1
- package/dist/chunks/zh-Dj5jzeIJ.mjs +0 -2
- package/dist/chunks/zh-Dj5jzeIJ.mjs.map +0 -1
- package/dist/chunks/zh-DjY-jmYn.mjs +0 -2
- package/dist/chunks/zh-DjY-jmYn.mjs.map +0 -1
- package/dist/chunks/zh-Dt7zL1Bl.mjs +0 -2
- package/dist/chunks/zh-Dt7zL1Bl.mjs.map +0 -1
- package/dist/chunks/zh-bkCXy34A.mjs +0 -2
- package/dist/chunks/zh-bkCXy34A.mjs.map +0 -1
- package/dist/chunks/zh-cRqBrQM8.mjs +0 -2
- package/dist/chunks/zh-cRqBrQM8.mjs.map +0 -1
- package/dist/chunks/zh-cUJdRfxu.mjs +0 -2
- package/dist/chunks/zh-cUJdRfxu.mjs.map +0 -1
- package/dist/chunks/zh-qo4_i9Vd.mjs +0 -2
- package/dist/chunks/zh-qo4_i9Vd.mjs.map +0 -1
- package/dist/chunks/zh-tpUU3oJa.mjs +0 -2
- package/dist/chunks/zh-tpUU3oJa.mjs.map +0 -1
- package/dist/plugins/text-direction/TextDirectionCorePlugin.d.ts +0 -35
- package/dist/plugins/text-direction/TextDirectionCorePlugin.d.ts.map +0 -1
- package/dist/plugins/text-direction/TextDirectionMiddleware.d.ts +0 -18
- package/dist/plugins/text-direction/TextDirectionMiddleware.d.ts.map +0 -1
- package/dist/plugins/text-direction-advanced/index.d.ts +0 -5
- package/dist/plugins/text-direction-advanced/index.d.ts.map +0 -1
- package/dist/plugins/text-direction-advanced.mjs +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-CNK6vpUU.mjs","names":[],"sources":["../../src/model/SchemaRegistry.ts","../../src/model/ContentModel.ts","../../src/editor/BuiltinSpecs.ts","../../src/state/BlockTreeOps.ts","../../src/state/InlineContentOps.ts","../../src/state/StepApplication.ts","../../src/state/StepInversion.ts","../../src/state/TransactionBuilder.ts","../../src/state/EditorState.ts","../../src/state/ReadonlyGuard.ts","../../src/state/History.ts","../../src/input/KeyboardHandler.ts","../../src/input/SelectionDocument.ts","../../src/input/ClipboardHandler.ts","../../src/input/CompositionTracker.ts","../../src/input/InputHandler.ts","../../src/model/ContentSlice.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/KeymapRegistry.ts","../../src/model/InputRuleRegistry.ts","../../src/model/FileHandlerRegistry.ts","../../src/view/NodeViewRegistry.ts","../../src/view/MarkRendering.ts","../../src/view/CursorWrapper.ts","../../src/plugins/Logger.ts","../../src/plugins/EventBus.ts","../../src/plugins/CommandRegistry.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/i18n/Locale.ts","../../src/model/BlockTypeLabels.ts","../../src/editor/ContentStyleInjector.ts","../../src/editor/ContentSerializer.ts","../../src/editor/EditorConfigController.ts","../../src/editor/EditorEventEmitter.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/editor/Announcer.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"],"sourcesContent":["/**\n * SchemaRegistry: central registry for node specs, mark specs,\n * and inline node specs registered by plugins.\n *\n * Model-only — no imports from input/, plugins/, or view/ layers.\n */\n\nimport type { InlineNodeSpec } from './InlineNodeSpec.js';\nimport type { MarkSpec } from './MarkSpec.js';\nimport type { NodeSpec } from './NodeSpec.js';\nimport type { ParseRule } from './ParseRule.js';\n\nexport class SchemaRegistry {\n\tprivate readonly _nodeSpecs = new Map<string, NodeSpec>();\n\tprivate readonly _markSpecs = new Map<string, MarkSpec>();\n\tprivate readonly _inlineNodeSpecs = new Map<string, InlineNodeSpec>();\n\n\t// --- NodeSpec ---\n\n\tregisterNodeSpec<T extends string>(spec: NodeSpec<T>): void {\n\t\tif (this._nodeSpecs.has(spec.type)) {\n\t\t\tthrow new Error(`NodeSpec for type \"${spec.type}\" is already registered.`);\n\t\t}\n\t\tthis._nodeSpecs.set(spec.type, spec);\n\t}\n\n\tgetNodeSpec(type: string): NodeSpec | undefined {\n\t\treturn this._nodeSpecs.get(type);\n\t}\n\n\tremoveNodeSpec(type: string): void {\n\t\tthis._nodeSpecs.delete(type);\n\t}\n\n\tgetNodeTypes(): string[] {\n\t\treturn [...this._nodeSpecs.keys()];\n\t}\n\n\t// --- MarkSpec ---\n\n\tregisterMarkSpec<T extends string>(spec: MarkSpec<T>): void {\n\t\tif (this._markSpecs.has(spec.type)) {\n\t\t\tthrow new Error(`MarkSpec for type \"${spec.type}\" is already registered.`);\n\t\t}\n\t\tthis._markSpecs.set(spec.type, spec);\n\t}\n\n\tgetMarkSpec(type: string): MarkSpec | undefined {\n\t\treturn this._markSpecs.get(type);\n\t}\n\n\tremoveMarkSpec(type: string): void {\n\t\tthis._markSpecs.delete(type);\n\t}\n\n\tgetMarkTypes(): string[] {\n\t\treturn [...this._markSpecs.keys()];\n\t}\n\n\t// --- InlineNodeSpec ---\n\n\tregisterInlineNodeSpec<T extends string>(spec: InlineNodeSpec<T>): void {\n\t\tif (this._inlineNodeSpecs.has(spec.type)) {\n\t\t\tthrow new Error(`InlineNodeSpec for type \"${spec.type}\" is already registered.`);\n\t\t}\n\t\tthis._inlineNodeSpecs.set(spec.type, spec);\n\t}\n\n\tgetInlineNodeSpec(type: string): InlineNodeSpec | undefined {\n\t\treturn this._inlineNodeSpecs.get(type);\n\t}\n\n\tremoveInlineNodeSpec(type: string): void {\n\t\tthis._inlineNodeSpecs.delete(type);\n\t}\n\n\tgetInlineNodeTypes(): string[] {\n\t\treturn [...this._inlineNodeSpecs.keys()];\n\t}\n\n\t// --- Parse Rules & Sanitize Config ---\n\n\t/** Returns all NodeSpec parseHTML rules, sorted by priority descending. */\n\tgetBlockParseRules(): readonly { readonly rule: ParseRule; readonly type: string }[] {\n\t\treturn this.collectParseRules(this._nodeSpecs);\n\t}\n\n\t/** Returns all InlineNodeSpec parseHTML rules, sorted by priority descending. */\n\tgetInlineParseRules(): readonly { readonly rule: ParseRule; readonly type: string }[] {\n\t\treturn this.collectParseRules(this._inlineNodeSpecs);\n\t}\n\n\t/** Returns all MarkSpec parseHTML rules, sorted by priority descending. */\n\tgetMarkParseRules(): readonly { readonly rule: ParseRule; readonly type: string }[] {\n\t\treturn this.collectParseRules(this._markSpecs);\n\t}\n\n\t/** Returns all allowed HTML tags from base defaults + all spec sanitize configs. */\n\tgetAllowedTags(): string[] {\n\t\treturn [\n\t\t\t...this.collectSanitizeValues(\n\t\t\t\tnew Set(['p', 'br', 'div', 'span']),\n\t\t\t\t(spec) => spec.sanitize?.tags,\n\t\t\t),\n\t\t];\n\t}\n\n\t/** Returns all allowed HTML attributes from base defaults + all spec sanitize configs. */\n\tgetAllowedAttrs(): string[] {\n\t\treturn [\n\t\t\t...this.collectSanitizeValues(new Set(['style', 'dir']), (spec) => spec.sanitize?.attrs),\n\t\t];\n\t}\n\n\tprivate collectParseRules(\n\t\tspecs: ReadonlyMap<string, { readonly parseHTML?: readonly ParseRule[] }>,\n\t): { readonly rule: ParseRule; readonly type: string }[] {\n\t\tconst results: { readonly rule: ParseRule; readonly type: string }[] = [];\n\t\tfor (const [type, spec] of specs) {\n\t\t\tif (spec.parseHTML) {\n\t\t\t\tfor (const rule of spec.parseHTML) {\n\t\t\t\t\tresults.push({ rule, type });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn results.sort((a, b) => (b.rule.priority ?? 50) - (a.rule.priority ?? 50));\n\t}\n\n\tprivate collectSanitizeValues(\n\t\tinitial: Set<string>,\n\t\textractor: (spec: {\n\t\t\treadonly sanitize?: { readonly tags?: readonly string[]; readonly attrs?: readonly string[] };\n\t\t}) => readonly string[] | undefined,\n\t): Set<string> {\n\t\tconst allSpecs: ReadonlyMap<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\treadonly sanitize?: {\n\t\t\t\t\treadonly tags?: readonly string[];\n\t\t\t\t\treadonly attrs?: readonly string[];\n\t\t\t\t};\n\t\t\t}\n\t\t>[] = [this._nodeSpecs, this._inlineNodeSpecs, this._markSpecs];\n\t\tfor (const specMap of allSpecs) {\n\t\t\tfor (const spec of specMap.values()) {\n\t\t\t\tconst values: readonly string[] | undefined = extractor(spec);\n\t\t\t\tif (values) {\n\t\t\t\t\tfor (const value of values) initial.add(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn initial;\n\t}\n\n\t// --- Bulk ---\n\n\tclear(): void {\n\t\tthis._nodeSpecs.clear();\n\t\tthis._markSpecs.clear();\n\t\tthis._inlineNodeSpecs.clear();\n\t}\n}\n","/**\n * Content model validation for nested document structures.\n * Validates whether a parent node type can contain given child types.\n */\n\nimport type { SchemaRegistry } from './SchemaRegistry.js';\n\n/**\n * Checks if a parent node type can contain a child node type,\n * using the content rules and group system from NodeSpec.\n */\nexport function canContain(\n\tregistry: SchemaRegistry,\n\tparentType: string,\n\tchildType: string,\n): boolean {\n\tconst parentSpec = registry.getNodeSpec(parentType);\n\tif (!parentSpec?.content) return true; // No content rule = anything allowed\n\n\tconst childSpec = registry.getNodeSpec(childType);\n\tconst childGroup = childSpec?.group;\n\n\tfor (const allowed of parentSpec.content.allow) {\n\t\tif (allowed === childType) return true;\n\t\tif (allowed === 'text') continue; // 'text' refers to TextNodes, not BlockNodes\n\t\tif (childGroup && allowed === childGroup) return true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Validates whether the given children types are valid for a parent node type.\n * Checks allow list, min/max constraints.\n */\nexport function validateContent(\n\tregistry: SchemaRegistry,\n\tparentType: string,\n\tchildTypes: readonly string[],\n): boolean {\n\tconst parentSpec = registry.getNodeSpec(parentType);\n\tif (!parentSpec?.content) return true;\n\n\tconst { min = 0, max = Number.POSITIVE_INFINITY } = parentSpec.content;\n\n\t// Check count constraints (only for block children, not text)\n\tconst blockChildTypes = childTypes.filter((t) => t !== 'text');\n\tif (blockChildTypes.length < min || blockChildTypes.length > max) return false;\n\n\t// Check each child is allowed\n\tfor (const childType of blockChildTypes) {\n\t\tif (!canContain(registry, parentType, childType)) return false;\n\t}\n\n\treturn true;\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","/**\n * Pure functions for recursive block-tree traversal and transformation.\n * Extracted from StepApplication.ts to keep files under ~500 lines.\n */\n\nimport { type BlockNode, type ChildNode, type Document, isBlockNode } from '../model/Document.js';\n\n/** Finds a block by ID anywhere in the tree and applies a transform. */\nexport function mapBlock(\n\tdoc: Document,\n\tblockId: string,\n\tfn: (block: BlockNode) => BlockNode,\n): Document {\n\treturn {\n\t\tchildren: mapBlockInChildren(doc.children, blockId, fn) as BlockNode[],\n\t};\n}\n\n/** Recursively maps over children to find and transform a block by ID. */\nexport function mapBlockInChildren(\n\tchildren: readonly ChildNode[],\n\tblockId: string,\n\tfn: (block: BlockNode) => BlockNode,\n): ChildNode[] {\n\treturn children.map((child) => {\n\t\tif (!isBlockNode(child)) return child;\n\t\tif (child.id === blockId) return fn(child);\n\t\t// Recurse into block children\n\t\tconst mappedChildren: ChildNode[] = mapBlockInChildren(child.children, blockId, fn);\n\t\tif (mappedChildren === child.children) return child;\n\t\treturn { ...child, children: mappedChildren };\n\t});\n}\n\n/**\n * Maps a node at the given path, replacing it with the result of fn.\n * Navigates down the path immutably, creating new parent nodes as needed.\n */\nexport function mapNodeByPath(\n\tdoc: Document,\n\tpath: readonly string[],\n\tfn: (node: BlockNode) => BlockNode,\n): Document {\n\tif (path.length === 0) return doc;\n\n\tconst rootId: string | undefined = path[0];\n\tif (!rootId) return doc;\n\treturn {\n\t\tchildren: doc.children.map((child) => {\n\t\t\tif (!isBlockNode(child) || child.id !== rootId) return child;\n\t\t\tif (path.length === 1) return fn(child);\n\t\t\treturn mapNodeByPathRecursive(child, path, 1, fn);\n\t\t}),\n\t};\n}\n\n/** Recursive helper for mapNodeByPath — walks one level deeper. */\nexport function mapNodeByPathRecursive(\n\tnode: BlockNode,\n\tpath: readonly string[],\n\tdepth: number,\n\tfn: (node: BlockNode) => BlockNode,\n): BlockNode {\n\tconst targetId: string | undefined = path[depth];\n\tif (!targetId) return node;\n\tconst newChildren: ChildNode[] = node.children.map((child) => {\n\t\tif (!isBlockNode(child) || child.id !== targetId) return child;\n\t\tif (depth === path.length - 1) return fn(child);\n\t\treturn mapNodeByPathRecursive(child, path, depth + 1, fn);\n\t});\n\treturn { ...node, children: newChildren };\n}\n\n/**\n * Searches children for a level where `predicate` matches,\n * then applies `transform` at that level.\n * Recurses into nested BlockNodes if not found at the current level.\n *\n * Returns `null` if no matching level was found.\n */\nexport function findAndTransformChildren(\n\tchildren: readonly ChildNode[],\n\tpredicate: (children: readonly ChildNode[]) => boolean,\n\ttransform: (children: readonly ChildNode[]) => readonly ChildNode[],\n): readonly ChildNode[] | null {\n\tif (predicate(children)) {\n\t\treturn transform(children);\n\t}\n\n\tlet changed = false;\n\tconst newChildren: ChildNode[] = children.map((child) => {\n\t\tif (!isBlockNode(child)) return child;\n\t\tconst result: readonly ChildNode[] | null = findAndTransformChildren(\n\t\t\tchild.children,\n\t\t\tpredicate,\n\t\t\ttransform,\n\t\t);\n\t\tif (result) {\n\t\t\tchanged = true;\n\t\t\treturn { ...child, children: result };\n\t\t}\n\t\treturn child;\n\t});\n\n\treturn changed ? newChildren : null;\n}\n","/**\n * Pure functions for manipulating inline content (TextNode / InlineNode arrays).\n * Extracted from StepApplication.ts to keep files under ~500 lines.\n */\n\nimport {\n\ttype ChildNode,\n\ttype InlineNode,\n\ttype Mark,\n\ttype TextNode,\n\ttype TextSegment,\n\taddMarkToSet,\n\tcreateTextNode,\n\tisInlineNode,\n\tisTextNode,\n\tremoveMarkFromSet,\n} from '../model/Document.js';\n\n/**\n * Inserts text into mixed inline content at the given offset.\n * Handles both TextNode and InlineNode children.\n */\nexport function insertTextIntoInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n\ttext: string,\n\tmarks: readonly Mark[],\n): (TextNode | InlineNode)[] {\n\treturn insertIntoInlineContent(nodes, offset, () => [createTextNode(text, marks)]);\n}\n\n/**\n * Inserts segments into mixed inline content at the given offset.\n */\nexport function insertSegmentsIntoInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n\tsegments: readonly TextSegment[],\n): (TextNode | InlineNode)[] {\n\treturn insertIntoInlineContent(nodes, offset, () =>\n\t\tsegments.map((seg) => createTextNode(seg.text, seg.marks)),\n\t);\n}\n\n/** Shared insertion logic for text and segments into mixed inline content. */\nfunction insertIntoInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n\tcreateInsertedNodes: () => readonly (TextNode | InlineNode)[],\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\tlet inserted = false;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tif (!inserted && offset === pos) {\n\t\t\t\tresult.push(...createInsertedNodes());\n\t\t\t\tinserted = true;\n\t\t\t}\n\t\t\tresult.push(node);\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nodeEnd: number = pos + node.text.length;\n\n\t\tif (!inserted && offset >= pos && offset <= nodeEnd) {\n\t\t\tconst localOffset: number = offset - pos;\n\t\t\tconst before: string = node.text.slice(0, localOffset);\n\t\t\tconst after: string = node.text.slice(localOffset);\n\n\t\t\tif (before) result.push(createTextNode(before, node.marks));\n\t\t\tresult.push(...createInsertedNodes());\n\t\t\tif (after) result.push(createTextNode(after, node.marks));\n\t\t\tinserted = true;\n\t\t} else {\n\t\t\tresult.push(node);\n\t\t}\n\n\t\tpos = nodeEnd;\n\t}\n\n\tif (!inserted) {\n\t\tresult.push(...createInsertedNodes());\n\t}\n\n\treturn result;\n}\n\n/**\n * Deletes content from mixed inline content in the given range.\n * Removes InlineNodes that fall within the range.\n */\nexport function deleteFromInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\tfrom: number,\n\tto: number,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tconst nodeEnd: number = pos + 1;\n\t\t\t// Keep if outside the delete range\n\t\t\tif (nodeEnd <= from || pos >= to) {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t\tpos = nodeEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nodeEnd: number = pos + node.text.length;\n\n\t\tif (nodeEnd <= from || pos >= to) {\n\t\t\tresult.push(node);\n\t\t} else {\n\t\t\tconst deleteFrom: number = Math.max(0, from - pos);\n\t\t\tconst deleteTo: number = Math.min(node.text.length, to - pos);\n\t\t\tconst remaining: string = node.text.slice(0, deleteFrom) + node.text.slice(deleteTo);\n\t\t\tif (remaining.length > 0) {\n\t\t\t\tresult.push(createTextNode(remaining, node.marks));\n\t\t\t}\n\t\t}\n\n\t\tpos = nodeEnd;\n\t}\n\n\treturn result;\n}\n\n/**\n * Slices mixed inline content to the given range.\n * InlineNodes are preserved as atoms (included if within range).\n */\nexport function sliceInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\tfrom: number,\n\tto: number,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tconst nodeEnd: number = pos + 1;\n\t\t\tif (nodeEnd > from && pos < to) {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t\tpos = nodeEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nodeEnd: number = pos + node.text.length;\n\n\t\tif (nodeEnd <= from || pos >= to) {\n\t\t\t// Outside the slice range\n\t\t} else {\n\t\t\tconst sliceFrom: number = Math.max(0, from - pos);\n\t\t\tconst sliceTo: number = Math.min(node.text.length, to - pos);\n\t\t\tconst text: string = node.text.slice(sliceFrom, sliceTo);\n\t\t\tif (text.length > 0) {\n\t\t\t\tresult.push(createTextNode(text, node.marks));\n\t\t\t}\n\t\t}\n\n\t\tpos = nodeEnd;\n\t}\n\n\tif (result.length === 0) {\n\t\tresult.push(createTextNode(''));\n\t}\n\n\treturn result;\n}\n\n/**\n * Applies or removes a mark from mixed inline content in the given range.\n * InlineNodes are passed through unchanged (marks only apply to text).\n */\nexport function applyMarkToInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n\tfrom: number,\n\tto: number,\n\tmark: Mark,\n\tadd: boolean,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tresult.push(node);\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nodeEnd: number = pos + node.text.length;\n\n\t\tif (nodeEnd <= from || pos >= to) {\n\t\t\tresult.push(node);\n\t\t} else if (pos >= from && nodeEnd <= to) {\n\t\t\tconst newMarks: readonly Mark[] = add\n\t\t\t\t? addMarkToSet(node.marks, mark)\n\t\t\t\t: removeMarkFromSet(node.marks, mark.type);\n\t\t\tresult.push(createTextNode(node.text, newMarks));\n\t\t} else {\n\t\t\tconst overlapStart: number = Math.max(0, from - pos);\n\t\t\tconst overlapEnd: number = Math.min(node.text.length, to - pos);\n\n\t\t\tconst beforeText: string = node.text.slice(0, overlapStart);\n\t\t\tconst insideText: string = node.text.slice(overlapStart, overlapEnd);\n\t\t\tconst afterText: string = node.text.slice(overlapEnd);\n\n\t\t\tif (beforeText) result.push(createTextNode(beforeText, node.marks));\n\t\t\tif (insideText) {\n\t\t\t\tconst newMarks: readonly Mark[] = add\n\t\t\t\t\t? addMarkToSet(node.marks, mark)\n\t\t\t\t\t: removeMarkFromSet(node.marks, mark.type);\n\t\t\t\tresult.push(createTextNode(insideText, newMarks));\n\t\t\t}\n\t\t\tif (afterText) result.push(createTextNode(afterText, node.marks));\n\t\t}\n\n\t\tpos = nodeEnd;\n\t}\n\n\treturn result;\n}\n\n/** Inserts an InlineNode at the given offset in mixed inline content. */\nexport function insertInlineNodeAtOffset(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n\tinlineNode: InlineNode,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\tlet inserted = false;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tif (!inserted && offset === pos) {\n\t\t\t\tresult.push(inlineNode);\n\t\t\t\tinserted = true;\n\t\t\t}\n\t\t\tresult.push(node);\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nodeEnd: number = pos + node.text.length;\n\n\t\tif (!inserted && offset >= pos && offset <= nodeEnd) {\n\t\t\tconst localOffset: number = offset - pos;\n\t\t\tconst before: string = node.text.slice(0, localOffset);\n\t\t\tconst after: string = node.text.slice(localOffset);\n\n\t\t\tif (before) result.push(createTextNode(before, node.marks));\n\t\t\tresult.push(inlineNode);\n\t\t\tif (after) result.push(createTextNode(after, node.marks));\n\t\t\tinserted = true;\n\t\t} else {\n\t\t\tresult.push(node);\n\t\t}\n\n\t\tpos = nodeEnd;\n\t}\n\n\tif (!inserted) {\n\t\tresult.push(inlineNode);\n\t}\n\n\treturn result;\n}\n\n/** Removes the InlineNode at the given offset from mixed inline content. */\nexport function removeInlineNodeAtOffset(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tif (pos !== offset) {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tresult.push(node);\n\t\tpos += node.text.length;\n\t}\n\n\treturn result;\n}\n\n/** Replaces the attrs of an InlineNode at the given offset. */\nexport function setInlineNodeAttrsAtOffset(\n\tnodes: readonly (TextNode | InlineNode)[],\n\toffset: number,\n\tattrs: Readonly<Record<string, string | number | boolean>>,\n): (TextNode | InlineNode)[] {\n\tconst result: (TextNode | InlineNode)[] = [];\n\tlet pos = 0;\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tif (pos === offset) {\n\t\t\t\tresult.push({ ...node, attrs });\n\t\t\t} else {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tresult.push(node);\n\t\tpos += node.text.length;\n\t}\n\n\treturn result;\n}\n\n/**\n * Replaces the inline content children of a ChildNode array,\n * preserving any BlockNode children in their original positions.\n */\nexport function replaceInlineChildren(\n\toriginal: readonly ChildNode[],\n\tnewInlineChildren: readonly (TextNode | InlineNode)[],\n): readonly ChildNode[] {\n\t// Fast path: if no block children exist, just return new inline nodes\n\tif (original.every((c) => isTextNode(c) || isInlineNode(c))) {\n\t\treturn newInlineChildren;\n\t}\n\t// Mixed: put inline nodes first, then block nodes (preserves structure)\n\tconst blockChildren: ChildNode[] = original.filter((c) => !isTextNode(c) && !isInlineNode(c));\n\treturn [...newInlineChildren, ...blockChildren];\n}\n","/**\n * Pure step-application functions operating on Document.\n * Extracted from EditorState for reuse in TransactionBuilder.\n */\n\nimport {\n\ttype BlockNode,\n\ttype ChildNode,\n\ttype Document,\n\ttype InlineNode,\n\ttype TextNode,\n\tcreateBlockNode,\n\tgetBlockLength,\n\tgetInlineChildren,\n\tisBlockNode,\n\tnormalizeInlineContent,\n} from '../model/Document.js';\nimport { findAndTransformChildren, mapBlock, mapNodeByPath } from './BlockTreeOps.js';\nimport {\n\tapplyMarkToInlineContent,\n\tdeleteFromInlineContent,\n\tinsertInlineNodeAtOffset,\n\tinsertSegmentsIntoInlineContent,\n\tinsertTextIntoInlineContent,\n\tremoveInlineNodeAtOffset,\n\treplaceInlineChildren,\n\tsetInlineNodeAttrsAtOffset,\n\tsliceInlineContent,\n} from './InlineContentOps.js';\nimport type {\n\tAddMarkStep,\n\tDeleteTextStep,\n\tInsertInlineNodeStep,\n\tInsertNodeStep,\n\tInsertTextStep,\n\tMergeBlocksStep,\n\tRemoveInlineNodeStep,\n\tRemoveMarkStep,\n\tRemoveNodeStep,\n\tSetBlockTypeStep,\n\tSetInlineNodeAttrStep,\n\tSetNodeAttrStep,\n\tSplitBlockStep,\n\tStep,\n} from './Transaction.js';\n\n/** Applies a single step to a document and returns the new document. */\nexport function applyStep(doc: Document, step: Step): Document {\n\tswitch (step.type) {\n\t\tcase 'insertText':\n\t\t\treturn applyInsertText(doc, step);\n\t\tcase 'deleteText':\n\t\t\treturn applyDeleteText(doc, step);\n\t\tcase 'splitBlock':\n\t\t\treturn applySplitBlock(doc, step);\n\t\tcase 'mergeBlocks':\n\t\t\treturn applyMergeBlocks(doc, step);\n\t\tcase 'addMark':\n\t\t\treturn applyAddMark(doc, step);\n\t\tcase 'removeMark':\n\t\t\treturn applyRemoveMark(doc, step);\n\t\tcase 'setStoredMarks':\n\t\t\treturn doc; // Stored marks are handled at the state level, not document\n\t\tcase 'setBlockType':\n\t\t\treturn applySetBlockType(doc, step);\n\t\tcase 'insertNode':\n\t\t\treturn applyInsertNode(doc, step);\n\t\tcase 'removeNode':\n\t\t\treturn applyRemoveNode(doc, step);\n\t\tcase 'setNodeAttr':\n\t\t\treturn applySetNodeAttr(doc, step);\n\t\tcase 'insertInlineNode':\n\t\t\treturn applyInsertInlineNode(doc, step);\n\t\tcase 'removeInlineNode':\n\t\t\treturn applyRemoveInlineNode(doc, step);\n\t\tcase 'setInlineNodeAttr':\n\t\t\treturn applySetInlineNodeAttr(doc, step);\n\t}\n}\n\nfunction applyInsertText(doc: Document, step: InsertTextStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tstep.segments\n\t\t\t? insertSegmentsIntoInlineContent(inline, step.offset, step.segments)\n\t\t\t: insertTextIntoInlineContent(inline, step.offset, step.text, step.marks),\n\t);\n}\n\nfunction applyDeleteText(doc: Document, step: DeleteTextStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tdeleteFromInlineContent(inline, step.from, step.to),\n\t);\n}\n\nfunction applySplitBlock(doc: Document, step: SplitBlockStep): Document {\n\tconst splitAtLevel = (children: readonly ChildNode[]): readonly ChildNode[] => {\n\t\tconst blockIndex: number = children.findIndex((c) => isBlockNode(c) && c.id === step.blockId);\n\t\tif (blockIndex === -1) return children;\n\n\t\tconst block: BlockNode = children[blockIndex] as BlockNode;\n\t\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\t\tconst len: number = getBlockLength(block);\n\t\tconst nodesBeforeSplit: (TextNode | InlineNode)[] = sliceInlineContent(\n\t\t\tinlineChildren,\n\t\t\t0,\n\t\t\tstep.offset,\n\t\t);\n\t\tconst nodesAfterSplit: (TextNode | InlineNode)[] = sliceInlineContent(\n\t\t\tinlineChildren,\n\t\t\tstep.offset,\n\t\t\tlen,\n\t\t);\n\n\t\tconst updatedBlock: BlockNode = {\n\t\t\t...block,\n\t\t\tchildren: normalizeInlineContent(nodesBeforeSplit),\n\t\t};\n\t\tconst newBlock: BlockNode = createBlockNode(\n\t\t\tblock.type,\n\t\t\tnormalizeInlineContent(nodesAfterSplit),\n\t\t\tstep.newBlockId,\n\t\t\tblock.attrs,\n\t\t);\n\n\t\tconst result: ChildNode[] = [...children];\n\t\tresult.splice(blockIndex, 1, updatedBlock, newBlock);\n\t\treturn result;\n\t};\n\n\tconst hasTarget = (children: readonly ChildNode[]): boolean =>\n\t\tchildren.some((c) => isBlockNode(c) && c.id === step.blockId);\n\n\tconst transformed: readonly ChildNode[] | null = findAndTransformChildren(\n\t\tdoc.children,\n\t\thasTarget,\n\t\tsplitAtLevel,\n\t);\n\treturn transformed ? { children: transformed as BlockNode[] } : doc;\n}\n\nfunction applyMergeBlocks(doc: Document, step: MergeBlocksStep): Document {\n\tconst mergeAtLevel = (children: readonly ChildNode[]): readonly ChildNode[] => {\n\t\tconst targetIdx: number = children.findIndex(\n\t\t\t(c) => isBlockNode(c) && c.id === step.targetBlockId,\n\t\t);\n\t\tconst sourceIdx: number = children.findIndex(\n\t\t\t(c) => isBlockNode(c) && c.id === step.sourceBlockId,\n\t\t);\n\t\tif (targetIdx === -1 || sourceIdx === -1) return children;\n\n\t\tconst target: BlockNode = children[targetIdx] as BlockNode;\n\t\tconst source: BlockNode = children[sourceIdx] as BlockNode;\n\t\tconst targetInline: readonly (TextNode | InlineNode)[] = getInlineChildren(target);\n\t\tconst sourceInline: readonly (TextNode | InlineNode)[] = getInlineChildren(source);\n\t\tconst mergedChildren: readonly (TextNode | InlineNode)[] = normalizeInlineContent([\n\t\t\t...targetInline,\n\t\t\t...sourceInline,\n\t\t]);\n\t\tconst mergedBlock: BlockNode = { ...target, children: mergedChildren };\n\n\t\tconst filtered: ChildNode[] = children.filter(\n\t\t\t(c) => !isBlockNode(c) || c.id !== step.sourceBlockId,\n\t\t);\n\t\treturn filtered.map((c) => (isBlockNode(c) && c.id === step.targetBlockId ? mergedBlock : c));\n\t};\n\n\tconst hasTargetAndSource = (children: readonly ChildNode[]): boolean =>\n\t\tchildren.some((c) => isBlockNode(c) && c.id === step.targetBlockId) &&\n\t\tchildren.some((c) => isBlockNode(c) && c.id === step.sourceBlockId);\n\n\tconst transformed: readonly ChildNode[] | null = findAndTransformChildren(\n\t\tdoc.children,\n\t\thasTargetAndSource,\n\t\tmergeAtLevel,\n\t);\n\treturn transformed ? { children: transformed as BlockNode[] } : doc;\n}\n\nfunction applyAddMark(doc: Document, step: AddMarkStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tapplyMarkToInlineContent(inline, step.from, step.to, step.mark, true),\n\t);\n}\n\nfunction applyRemoveMark(doc: Document, step: RemoveMarkStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tapplyMarkToInlineContent(inline, step.from, step.to, step.mark, false),\n\t);\n}\n\n// --- InlineNode Step Application ---\n\nfunction applyInsertInlineNode(doc: Document, step: InsertInlineNodeStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tinsertInlineNodeAtOffset(inline, step.offset, step.node),\n\t);\n}\n\nfunction applyRemoveInlineNode(doc: Document, step: RemoveInlineNodeStep): Document {\n\treturn mapBlockInlineContent(doc, step.blockId, (inline) =>\n\t\tremoveInlineNodeAtOffset(inline, step.offset),\n\t);\n}\n\nfunction applySetInlineNodeAttr(doc: Document, step: SetInlineNodeAttrStep): Document {\n\treturn mapBlockInlineContent(\n\t\tdoc,\n\t\tstep.blockId,\n\t\t(inline) => setInlineNodeAttrsAtOffset(inline, step.offset, step.attrs),\n\t\tfalse,\n\t);\n}\n\n/** Maps inline content of a block, optionally normalizing the result. */\nfunction mapBlockInlineContent(\n\tdoc: Document,\n\tblockId: string,\n\tfn: (inline: readonly (TextNode | InlineNode)[]) => (TextNode | InlineNode)[],\n\tnormalize = true,\n): Document {\n\treturn mapBlock(doc, blockId, (block) => {\n\t\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\t\tconst newChildren: (TextNode | InlineNode)[] = fn(inlineChildren);\n\t\treturn {\n\t\t\t...block,\n\t\t\tchildren: replaceInlineChildren(\n\t\t\t\tblock.children,\n\t\t\t\tnormalize ? normalizeInlineContent(newChildren) : newChildren,\n\t\t\t),\n\t\t};\n\t});\n}\n\nfunction applySetBlockType(doc: Document, step: SetBlockTypeStep): Document {\n\treturn mapBlock(doc, step.blockId, (block) => {\n\t\tconst { attrs: _previousAttrs, ...rest } = block;\n\t\treturn {\n\t\t\t...rest,\n\t\t\ttype: step.nodeType,\n\t\t\t...(step.attrs ? { attrs: step.attrs } : {}),\n\t\t};\n\t});\n}\n\n// --- Structural Step Application ---\n\nfunction applyInsertNode(doc: Document, step: InsertNodeStep): Document {\n\tif (step.parentPath.length === 0) {\n\t\t// Insert at document root\n\t\tconst newChildren: BlockNode[] = [...doc.children];\n\t\tnewChildren.splice(step.index, 0, step.node);\n\t\treturn { children: newChildren };\n\t}\n\n\treturn mapNodeByPath(doc, step.parentPath, (parent) => {\n\t\tconst newChildren: ChildNode[] = [...parent.children];\n\t\tnewChildren.splice(step.index, 0, step.node);\n\t\treturn { ...parent, children: newChildren };\n\t});\n}\n\nfunction applyRemoveNode(doc: Document, step: RemoveNodeStep): Document {\n\tif (step.parentPath.length === 0) {\n\t\t// Remove from document root\n\t\tconst newChildren: BlockNode[] = [...doc.children];\n\t\tnewChildren.splice(step.index, 1);\n\t\treturn { children: newChildren };\n\t}\n\n\treturn mapNodeByPath(doc, step.parentPath, (parent) => {\n\t\tconst newChildren: ChildNode[] = [...parent.children];\n\t\tnewChildren.splice(step.index, 1);\n\t\treturn { ...parent, children: newChildren };\n\t});\n}\n\nfunction applySetNodeAttr(doc: Document, step: SetNodeAttrStep): Document {\n\tconst nodeId: string | undefined = step.path[step.path.length - 1];\n\tif (!nodeId) return doc;\n\n\treturn mapBlock(doc, nodeId, (block) => ({\n\t\t...block,\n\t\tattrs: step.attrs,\n\t}));\n}\n","/**\n * Step inversion logic for undo/redo support.\n * Each step type has a corresponding inverse that reverses its effect.\n */\n\nimport type { Mark } from '../model/Document.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { Step } from './Steps.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Returns a path spread object if the step has a path, or empty object otherwise. */\nfunction optionalPath(step: { readonly path?: readonly BlockId[] }):\n\t| { readonly path: readonly BlockId[] }\n\t| Record<string, never> {\n\treturn step.path ? { path: step.path } : {};\n}\n\n/** Inverts a single step for undo. */\nexport function invertStep(step: Step): Step {\n\tswitch (step.type) {\n\t\tcase 'insertText':\n\t\t\treturn {\n\t\t\t\ttype: 'deleteText',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\tfrom: step.offset,\n\t\t\t\tto: step.offset + step.text.length,\n\t\t\t\tdeletedText: step.text,\n\t\t\t\tdeletedMarks: step.marks,\n\t\t\t\tdeletedSegments: step.segments ?? [{ text: step.text, marks: [...step.marks] }],\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'deleteText':\n\t\t\treturn {\n\t\t\t\ttype: 'insertText',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\toffset: step.from,\n\t\t\t\ttext: step.deletedText,\n\t\t\t\tmarks: step.deletedMarks,\n\t\t\t\tsegments: step.deletedSegments,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'splitBlock':\n\t\t\treturn {\n\t\t\t\ttype: 'mergeBlocks',\n\t\t\t\ttargetBlockId: step.blockId,\n\t\t\t\tsourceBlockId: step.newBlockId,\n\t\t\t\ttargetLengthBefore: step.offset,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'mergeBlocks':\n\t\t\treturn {\n\t\t\t\ttype: 'splitBlock',\n\t\t\t\tblockId: step.targetBlockId,\n\t\t\t\toffset: step.targetLengthBefore,\n\t\t\t\tnewBlockId: step.sourceBlockId,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'addMark':\n\t\t\treturn {\n\t\t\t\ttype: 'removeMark',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\tfrom: step.from,\n\t\t\t\tto: step.to,\n\t\t\t\tmark: step.mark,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'removeMark':\n\t\t\treturn {\n\t\t\t\ttype: 'addMark',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\tfrom: step.from,\n\t\t\t\tto: step.to,\n\t\t\t\tmark: step.mark,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'setStoredMarks':\n\t\t\treturn {\n\t\t\t\ttype: 'setStoredMarks',\n\t\t\t\tmarks: step.previousMarks,\n\t\t\t\tpreviousMarks: step.marks,\n\t\t\t};\n\t\tcase 'setBlockType':\n\t\t\treturn {\n\t\t\t\ttype: 'setBlockType',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\tnodeType: step.previousNodeType,\n\t\t\t\tattrs: step.previousAttrs,\n\t\t\t\tpreviousNodeType: step.nodeType,\n\t\t\t\tpreviousAttrs: step.attrs,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'insertNode':\n\t\t\treturn {\n\t\t\t\ttype: 'removeNode',\n\t\t\t\tparentPath: step.parentPath,\n\t\t\t\tindex: step.index,\n\t\t\t\tremovedNode: step.node,\n\t\t\t};\n\t\tcase 'removeNode':\n\t\t\treturn {\n\t\t\t\ttype: 'insertNode',\n\t\t\t\tparentPath: step.parentPath,\n\t\t\t\tindex: step.index,\n\t\t\t\tnode: step.removedNode,\n\t\t\t};\n\t\tcase 'setNodeAttr':\n\t\t\treturn {\n\t\t\t\ttype: 'setNodeAttr',\n\t\t\t\tpath: step.path,\n\t\t\t\tattrs: step.previousAttrs,\n\t\t\t\tpreviousAttrs: step.attrs,\n\t\t\t};\n\t\tcase 'insertInlineNode':\n\t\t\treturn {\n\t\t\t\ttype: 'removeInlineNode',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\toffset: step.offset,\n\t\t\t\tremovedNode: step.node,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'removeInlineNode':\n\t\t\treturn {\n\t\t\t\ttype: 'insertInlineNode',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\toffset: step.offset,\n\t\t\t\tnode: step.removedNode,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t\tcase 'setInlineNodeAttr':\n\t\t\treturn {\n\t\t\t\ttype: 'setInlineNodeAttr',\n\t\t\t\tblockId: step.blockId,\n\t\t\t\toffset: step.offset,\n\t\t\t\tattrs: step.previousAttrs,\n\t\t\t\tpreviousAttrs: step.attrs,\n\t\t\t\t...optionalPath(step),\n\t\t\t};\n\t}\n}\n\nfunction deriveStoredMarksBefore(tr: Transaction): readonly Mark[] | null {\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setStoredMarks') {\n\t\t\treturn step.previousMarks;\n\t\t}\n\t}\n\treturn tr.storedMarksAfter;\n}\n\n/** Inverts an entire transaction (reverses step order and swaps selections). */\nexport function invertTransaction(tr: Transaction): Transaction {\n\treturn {\n\t\tsteps: tr.steps.map(invertStep).reverse(),\n\t\tselectionBefore: tr.selectionAfter,\n\t\tselectionAfter: tr.selectionBefore,\n\t\tstoredMarksAfter: deriveStoredMarksBefore(tr),\n\t\tmetadata: {\n\t\t\torigin: 'history',\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t};\n}\n","/**\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\tDocument,\n\tInlineNode,\n\tMark,\n\tTextSegment,\n} from '../model/Document.js';\nimport {\n\tgetBlockLength,\n\tgetBlockMarksAtOffset,\n\tgetBlockSegmentsInRange,\n\tgetBlockText,\n\tgetContentAtOffset,\n\tgetInlineChildren,\n\tisBlockNode,\n\tisInlineNode,\n} from '../model/Document.js';\nimport { findNode, 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 { applyStep } from './StepApplication.js';\nimport type {\n\tAddMarkStep,\n\tDeleteTextStep,\n\tInsertInlineNodeStep,\n\tInsertNodeStep,\n\tInsertTextStep,\n\tMergeBlocksStep,\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 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 TextSegment[],\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 TextSegment[],\n\t): this {\n\t\tlet segments: readonly TextSegment[];\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? getBlockSegmentsInRange(block, from, to)\n\t\t\t\t: [{ text: deletedText, marks: [...deletedMarks] }];\n\t\t} else {\n\t\t\tsegments = [{ text: deletedText, marks: [...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 = getBlockSegmentsInRange(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/** Adds a merge-blocks step with explicit targetLengthBefore. Updates workingDoc if available. */\n\tmergeBlocks(targetBlockId: BlockId, sourceBlockId: BlockId, targetLengthBefore: number): 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};\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 from the working document.\n\t * Requires a 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\n\t\tconst targetLengthBefore = getBlockLength(targetBlock);\n\t\treturn this.mergeBlocks(targetBlockId, sourceBlockId, targetLengthBefore);\n\t}\n\n\t/** Adds an add-mark step. Updates workingDoc if available. */\n\taddMark(blockId: BlockId, from: number, to: number, mark: Mark): this {\n\t\tconst step: AddMarkStep = { type: 'addMark', blockId, from, to, mark };\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\n\t\treturn this;\n\t}\n\n\t/** Adds a remove-mark step. Updates workingDoc if available. */\n\tremoveMark(blockId: BlockId, from: number, to: number, mark: Mark): this {\n\t\tconst step: RemoveMarkStep = { type: 'removeMark', blockId, from, to, mark };\n\t\tthis.steps.push(step);\n\t\tthis.advanceDoc(step);\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 = resolveRemovedNode(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/** 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\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/** Advances the working document by applying a step. */\n\tprivate advanceDoc(step: Step): void {\n\t\tif (this.workingDoc) {\n\t\t\tthis.workingDoc = applyStep(this.workingDoc, step);\n\t\t}\n\t}\n}\n\n/** Resolves the block node to be removed from a parent path and index. */\nfunction resolveRemovedNode(\n\tdoc: Document,\n\tparentPath: readonly BlockId[],\n\tindex: number,\n): BlockNode | undefined {\n\tif (parentPath.length === 0) {\n\t\tconst child = doc.children[index];\n\t\treturn child && isBlockNode(child) ? child : undefined;\n\t}\n\tconst parent = resolveNodeByPath(doc, parentPath);\n\tif (!parent) return undefined;\n\tconst child = parent.children[index];\n\treturn child && isBlockNode(child) ? child : undefined;\n}\n","/**\n * Immutable editor state container.\n * Every mutation produces a new EditorState instance.\n */\n\nimport {\n\ttype BlockNode,\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 './StepApplication.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(\n\tchildren: readonly import('../model/Document.js').ChildNode[],\n): 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 import('../model/Document.js').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 import('../model/Document.js').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 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 * History manager for undo/redo with transaction grouping.\n */\n\nimport type { EditorState } from './EditorState.js';\nimport type { Step, Transaction } from './Transaction.js';\nimport { invertTransaction } 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}\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\tpush(tr: Transaction): void {\n\t\t// History and redo transactions are not pushed\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\t// Merge into existing group\n\t\t\tthis.undoStack[this.undoStack.length - 1] = {\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\t// Start new group\n\t\t\tthis.undoStack.push({\n\t\t\t\ttransactions: [tr],\n\t\t\t\ttimestamp: now,\n\t\t\t});\n\t\t}\n\n\t\tthis.lastOrigin = tr.metadata.origin;\n\n\t\t// Clear redo stack on new input\n\t\tthis.redoStack = [];\n\n\t\t// Enforce max depth\n\t\twhile (this.undoStack.length > this.maxDepth) {\n\t\t\tthis.undoStack.shift();\n\t\t}\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\tlet currentState = state;\n\t\tconst allSteps: Step[] = [];\n\n\t\t// Apply inverted transactions in reverse order\n\t\tfor (let i = group.transactions.length - 1; i >= 0; i--) {\n\t\t\tconst tr = group.transactions[i];\n\t\t\tif (!tr) continue;\n\t\t\tconst inverted = invertTransaction(tr);\n\t\t\tcurrentState = currentState.apply(inverted);\n\t\t\tallSteps.push(...inverted.steps);\n\t\t}\n\n\t\t// Push to redo stack\n\t\tthis.redoStack.push({\n\t\t\ttransactions: group.transactions,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\tthis.lastOrigin = null;\n\n\t\tconst transaction: Transaction = {\n\t\t\tsteps: allSteps,\n\t\t\tselectionBefore: state.selection,\n\t\t\tselectionAfter: currentState.selection,\n\t\t\tstoredMarksAfter: currentState.storedMarks,\n\t\t\tmetadata: { origin: 'history', timestamp: Date.now(), historyDirection: 'undo' },\n\t\t};\n\n\t\treturn { state: currentState, 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\tlet currentState = state;\n\t\tconst allSteps: Step[] = [];\n\n\t\t// Re-apply transactions in original order\n\t\tfor (const tr of group.transactions) {\n\t\t\tcurrentState = currentState.apply(tr);\n\t\t\tallSteps.push(...tr.steps);\n\t\t}\n\n\t\t// Push back to undo stack\n\t\tthis.undoStack.push({\n\t\t\ttransactions: group.transactions,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\n\t\tthis.lastOrigin = null;\n\n\t\tconst transaction: Transaction = {\n\t\t\tsteps: allSteps,\n\t\t\tselectionBefore: state.selection,\n\t\t\tselectionAfter: currentState.selection,\n\t\t\tstoredMarksAfter: currentState.storedMarks,\n\t\t\tmetadata: { origin: 'history', timestamp: Date.now(), historyDirection: 'redo' },\n\t\t};\n\n\t\treturn { state: currentState, 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\t// Group same step types (insertText with insertText, deleteText with deleteText)\n\t\treturn lastStepType === incomingStepType;\n\t}\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 { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport { isGapCursor, isNodeSelection, selectionsEqual } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.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';\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\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\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 descriptor: string = normalizeKeyDescriptor(e);\n\t\t\t\tconst navKeymaps = this.keymapRegistry.getKeymapsByPriority().navigation;\n\t\t\t\tfor (let i = navKeymaps.length - 1; i >= 0; i--) {\n\t\t\t\t\tconst handler = navKeymaps[i]?.[descriptor];\n\t\t\t\t\tif (handler?.()) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\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\t// Within each priority, iterate in reverse so later-registered keymaps take precedence.\n\t\tif (this.keymapRegistry) {\n\t\t\tconst descriptor: string = normalizeKeyDescriptor(e);\n\t\t\tconst groups = this.keymapRegistry.getKeymapsByPriority();\n\t\t\tfor (const keymaps of [groups.context, groups.navigation, groups.default]) {\n\t\t\t\tfor (let i = keymaps.length - 1; i >= 0; i--) {\n\t\t\t\t\tconst handler = keymaps[i]?.[descriptor];\n\t\t\t\t\tif (handler?.()) {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\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\n\t\tconst mod = e.metaKey || e.ctrlKey;\n\t\tif (!mod) return;\n\n\t\tconst key = e.key.toLowerCase();\n\t\tconst state = this.getState();\n\t\tlet tr: Transaction | null = null;\n\n\t\tif (key === 'z' && !e.shiftKey) {\n\t\t\te.preventDefault();\n\t\t\tthis.undo();\n\t\t\treturn;\n\t\t}\n\n\t\tif ((key === 'z' && e.shiftKey) || (key === 'y' && !e.shiftKey)) {\n\t\t\te.preventDefault();\n\t\t\tthis.redo();\n\t\t\treturn;\n\t\t}\n\n\t\tif (key === 'a' && !e.shiftKey) {\n\t\t\te.preventDefault();\n\t\t\ttr = selectAll(state);\n\t\t}\n\n\t\tif (tr) {\n\t\t\tthis.dispatch(tr);\n\t\t}\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 =\n\t\t\t\tkey === 'ArrowLeft'\n\t\t\t\t\t? 'left'\n\t\t\t\t\t: key === 'ArrowRight'\n\t\t\t\t\t\t? 'right'\n\t\t\t\t\t\t: key === 'ArrowUp'\n\t\t\t\t\t\t\t? 'up'\n\t\t\t\t\t\t\t: 'down';\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 =\n\t\t\tkey === 'ArrowLeft'\n\t\t\t\t? 'left'\n\t\t\t\t: key === 'ArrowRight'\n\t\t\t\t\t? 'right'\n\t\t\t\t\t: key === 'ArrowUp'\n\t\t\t\t\t\t? 'up'\n\t\t\t\t\t\t: 'down';\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/**\n * Normalizes a KeyboardEvent into a consistent key descriptor string.\n * Format: `\"Mod-Shift-Alt-Key\"` where Mod = Ctrl/Cmd.\n */\nexport function normalizeKeyDescriptor(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\n\tlet key = e.key;\n\t// Normalize common keys\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 * 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\tcreateTextNode,\n\tgetBlockChildren,\n\tgetBlockContentSegmentsInRange,\n\tgetBlockLength,\n\tisLeafBlock,\n\tnormalizeInlineContent,\n} from '../model/Document.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(state: EditorState, selection: Selection): 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);\n\t\tif (sliced) children.push(sliced);\n\t}\n\n\treturn { children };\n}\n\nfunction sliceBlock(block: BlockNode, boundary: SelectionBoundary): 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);\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\treturn {\n\t\t...block,\n\t\tchildren: slicedChildren,\n\t};\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)[] = normalizeInlineContent(\n\t\tsegments.map((segment) =>\n\t\t\tsegment.kind === 'inline' ? segment.node : createTextNode(segment.text, segment.marks),\n\t\t),\n\t);\n\n\treturn {\n\t\t...block,\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\tgetBlockLength,\n\tgetInlineChildren,\n\tisInlineNode,\n\tisLeafBlock,\n\tisTextNode,\n} from '../model/Document.js';\nimport { findNodePath } from '../model/NodeResolver.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 {\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 type { RichBlockData, RichSegment } from './InternalClipboard.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\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 (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// Internal format: block type + attrs for round-trip paste\n\t\tconst blockData: { readonly type: string; readonly attrs?: Record<string, unknown> } = {\n\t\t\ttype: block.type,\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', spec.toHTML(block, ''));\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\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...(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.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);\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 children = getInlineChildren(block);\n\t\tconst parts: string[] = [];\n\t\tlet pos = 0;\n\n\t\tfor (const child of children) {\n\t\t\tconst width: number = isInlineNode(child) ? 1 : child.text.length;\n\t\t\tconst childEnd: number = pos + width;\n\n\t\t\tif (childEnd <= start || pos >= end) {\n\t\t\t\tpos = childEnd;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (isTextNode(child)) {\n\t\t\t\tconst sliceFrom: number = Math.max(0, start - pos);\n\t\t\t\tconst sliceTo: number = Math.min(child.text.length, end - pos);\n\t\t\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\t\t\t\tif (text.length > 0 && this.schemaRegistry) {\n\t\t\t\t\tparts.push(serializeMarksToHTML(text, child.marks, this.schemaRegistry, markOrder));\n\t\t\t\t}\n\t\t\t} else if (isInlineNode(child)) {\n\t\t\t\tconst spec = this.schemaRegistry?.getInlineNodeSpec(child.inlineType);\n\t\t\t\tif (spec?.toHTMLString) {\n\t\t\t\t\tparts.push(spec.toHTMLString(child));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpos = childEnd;\n\t\t}\n\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 children = getInlineChildren(block);\n\t\tconst segments: RichSegment[] = [];\n\t\tlet pos = 0;\n\n\t\tfor (const child of children) {\n\t\t\tconst width: number = isInlineNode(child) ? 1 : child.text.length;\n\t\t\tconst childEnd: number = pos + width;\n\n\t\t\tif (childEnd <= start || pos >= end) {\n\t\t\t\tpos = childEnd;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (isTextNode(child)) {\n\t\t\t\tconst sliceFrom: number = Math.max(0, start - pos);\n\t\t\t\tconst sliceTo: number = Math.min(child.text.length, end - pos);\n\t\t\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\t\t\t\tif (text.length > 0) {\n\t\t\t\t\tconst marks = child.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\tsegments.push({ text, marks });\n\t\t\t\t}\n\t\t\t} else if (isInlineNode(child)) {\n\t\t\t\tsegments.push({\n\t\t\t\t\tkind: 'inline',\n\t\t\t\t\tinlineType: child.inlineType,\n\t\t\t\t\t...(Object.keys(child.attrs).length > 0 ? { attrs: { ...child.attrs } } : {}),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tpos = childEnd;\n\t\t}\n\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 inlineHTML: string = this.serializeBlockRangeToHTML(block, start, end, markOrder);\n\t\t\tconst spec = this.schemaRegistry?.getNodeSpec(block.type);\n\n\t\t\tif (spec?.wrapper) {\n\t\t\t\tconst wrapperSpec = spec.wrapper(block 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(spec.toHTML(block, inlineHTML));\n\t\t\t\t} else {\n\t\t\t\t\tlistBuffer.push(`<li>${inlineHTML}</li>`);\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(spec.toHTML(block, inlineHTML));\n\t\t\t\t} else {\n\t\t\t\t\thtmlParts.push(`<p>${inlineHTML}</p>`);\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 { getBlockText } from '../model/Document.js';\nimport { isTextSelection } from '../model/Selection.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nimport type { InputRuleRegistry } from '../model/InputRuleRegistry.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\tcompositionTracker?: CompositionTracker;\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 compositionTracker: CompositionTracker;\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.compositionTracker = options.compositionTracker ?? new CompositionTracker();\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 (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 = insertTextCommand(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 = insertTextCommand(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 = insertTextCommand(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\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\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\tprivate checkInputRules(): void {\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\tconst text = getBlockText(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\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 * ContentSlice: an immutable fragment of document content for paste operations.\n * Represents \"what to insert\" independent of where it will be inserted.\n */\n\nimport type { BlockAttrs, TextSegment } from './Document.js';\nimport type { NodeTypeName } from './TypeBrands.js';\nimport { nodeType } from './TypeBrands.js';\n\nexport interface SliceBlock {\n\treadonly type: NodeTypeName;\n\treadonly attrs?: BlockAttrs;\n\treadonly segments: readonly TextSegment[];\n}\n\nexport interface ContentSlice {\n\treadonly blocks: readonly SliceBlock[];\n}\n\n/** Concatenates all segment texts within a slice block. */\nexport function segmentsToText(segments: readonly TextSegment[]): string {\n\treturn segments.reduce((acc: string, s: TextSegment) => acc + s.text, '');\n}\n\n/** Returns the total text length of a slice block's segments. */\nexport function segmentsLength(segments: readonly TextSegment[]): number {\n\treturn segments.reduce((acc: number, s: TextSegment) => acc + s.text.length, 0);\n}\n\n/** Creates a single-paragraph content slice from plain text. */\nexport function plainTextSlice(text: string): ContentSlice {\n\tconst lines: readonly string[] = text.split(/\\r?\\n/);\n\tconst blocks: readonly SliceBlock[] = lines.map(\n\t\t(line: string): SliceBlock => ({\n\t\t\ttype: nodeType('paragraph'),\n\t\t\tsegments: [{ text: line, marks: [] }],\n\t\t}),\n\t);\n\treturn {\n\t\tblocks:\n\t\t\tblocks.length > 0\n\t\t\t\t? blocks\n\t\t\t\t: [{ type: nodeType('paragraph'), segments: [{ text: '', marks: [] }] }],\n\t};\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, segmentsToText } from '../model/ContentSlice.js';\nimport { createBlockNode, createTextNode, generateBlockId } from '../model/Document.js';\nimport type { TextSegment } from '../model/Document.js';\nimport { findNodePath } from '../model/NodeResolver.js';\nimport type { GapCursorSelection } 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 {\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\n/** Case 1: single paragraph — insert segments into current block. */\nfunction pasteInline(state: EditorState, segments: readonly TextSegment[]): 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: 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 = resolveInsertPoint(sel, state.getBlockOrder());\n\tconst insertBlockId: BlockId = landingId ?? resolved.blockId;\n\tconst insertOffset: number = landingId ? 0 : resolved.offset;\n\tconst totalLength: number = segmentsLength(segments);\n\tconst text: string = segmentsToText(segments);\n\n\tbuilder.insertText(insertBlockId, insertOffset, text, [], segments);\n\tbuilder.setSelection(createCollapsedSelection(insertBlockId, insertOffset + totalLength));\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: 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 = resolveInsertPoint(sel, state.getBlockOrder());\n\tconst insertBlockId: BlockId = landingId ?? resolved.blockId;\n\tconst insertOffset: number = landingId ? 0 : resolved.offset;\n\tconst totalLength: number = segmentsLength(block.segments);\n\tconst text: string = segmentsToText(block.segments);\n\n\tbuilder.setBlockType(insertBlockId, block.type, block.attrs);\n\tbuilder.insertText(insertBlockId, insertOffset, text, [], block.segments);\n\tbuilder.setSelection(createCollapsedSelection(insertBlockId, insertOffset + totalLength));\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 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 blockOrder = state.getBlockOrder();\n\tconst resolved = resolveInsertPoint(sel, blockOrder);\n\tconst blockId: BlockId = landingId ?? resolved.blockId;\n\tconst offset: number = landingId ? 0 : resolved.offset;\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 firstLen: number = segmentsLength(firstSlice.segments);\n\tif (firstSlice.segments.length > 0 && firstLen > 0) {\n\t\tbuilder.insertText(\n\t\t\tblockId,\n\t\t\toffset,\n\t\t\tsegmentsToText(firstSlice.segments),\n\t\t\t[],\n\t\t\tfirstSlice.segments,\n\t\t);\n\t}\n\n\t// 2. Change block type if 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// 3. Split current block after inserted text\n\tconst splitOffset: number = offset + firstLen;\n\tconst tailBlockId = generateBlockId();\n\tbuilder.splitBlock(blockId, splitOffset, tailBlockId);\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 textNodes = mid.segments.map((s: TextSegment) => createTextNode(s.text, [...s.marks]));\n\t\tconst newBlock = createBlockNode(\n\t\t\tmid.type,\n\t\t\ttextNodes.length > 0 ? textNodes : undefined,\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\tconst lastLen: number = segmentsLength(lastSlice.segments);\n\tif (lastSlice.segments.length > 0 && lastLen > 0) {\n\t\tbuilder.insertText(tailBlockId, 0, segmentsToText(lastSlice.segments), [], lastSlice.segments);\n\t}\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, lastLen));\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 TextSegment[],\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 text: string = segmentsToText(segments);\n\tconst totalLength: number = segmentsLength(segments);\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\tif (text.length > 0) {\n\t\tbuilder.insertText(newId, 0, text, [], segments);\n\t}\n\tbuilder.setSelection(createCollapsedSelection(newId, totalLength));\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 lastTextLen = 0;\n\n\tfor (const block of blocks) {\n\t\tconst newId: BlockId = generateBlockId();\n\t\tconst text: string = segmentsToText(block.segments);\n\t\tconst textNodes = block.segments.map((s: TextSegment) => createTextNode(s.text, [...s.marks]));\n\t\tconst newBlock = createBlockNode(\n\t\t\tblock.type,\n\t\t\ttextNodes.length > 0 ? textNodes : [createTextNode('')],\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\tlastTextLen = text.length;\n\t}\n\n\tif (lastBlockId) {\n\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, lastTextLen));\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 { Mark, TextSegment } from '../model/Document.js';\nimport { markSetsEqual } 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 { NodeTypeName } from '../model/TypeBrands.js';\nimport { markType, nodeType } from '../model/TypeBrands.js';\n\nexport interface HTMLParserOptions {\n\treadonly schema: Schema;\n\treadonly schemaRegistry?: SchemaRegistry;\n}\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 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.blockTagHandlers = this.buildBlockTagHandlers();\n\t}\n\n\t/** Parses an HTML fragment and returns a ContentSlice. */\n\tparse(container: DocumentFragment | HTMLElement): ContentSlice {\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: [{ text: '', marks: [] }],\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: TextSegment[] = [];\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({ text, marks: [] });\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: string, 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: string, 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: string = isChecklist ? 'checklist' : listType;\n\t\tconst checked: boolean = isChecklist ? this.isCheckboxChecked(element) : false;\n\n\t\tconst inlineSegments: TextSegment[] = [];\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({ text, 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\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: string = 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(element: HTMLElement, parentMarks: readonly Mark[]): TextSegment[] {\n\t\tconst segments: TextSegment[] = [];\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[]): TextSegment[] {\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 [{ text, marks: 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\tif (tag === 'BR') {\n\t\t\treturn [{ text: '\\n', marks: this.resolveMarks(parentMarks) }];\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\tprivate parsePreContent(element: HTMLElement): TextSegment[] {\n\t\tconst text: string = element.textContent ?? '';\n\t\tif (!text) return [];\n\t\treturn [{ text, marks: [] }];\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: TextSegment[] = this.parseInlineChildren(cell, []);\n\t\t\tif (segments.length > 0 && segments.some((s: TextSegment) => s.text.length > 0)) {\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 TextSegment[]): TextSegment[] {\n\t\tif (segments.length === 0) return [];\n\n\t\tconst result: TextSegment[] = [];\n\t\tfor (const segment of segments) {\n\t\t\tconst prev: TextSegment | undefined = result[result.length - 1];\n\t\t\tif (prev && markSetsEqual(prev.marks, segment.marks)) {\n\t\t\t\tresult[result.length - 1] = {\n\t\t\t\t\ttext: prev.text + segment.text,\n\t\t\t\t\tmarks: prev.marks,\n\t\t\t\t};\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 TextSegment[]): readonly TextSegment[] {\n\t\tif (segments.length === 0) return [{ text: '', marks: [] }];\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 TextSegment[],\n\t\tmarks: readonly Mark[],\n\t): readonly TextSegment[] {\n\t\treturn segments.map(\n\t\t\t(s: TextSegment): TextSegment => ({\n\t\t\t\ttext: s.text,\n\t\t\t\tmarks: this.mergeMarks(marks, s.marks),\n\t\t\t}),\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 TextSegment[] = this.parseInlineChildren(element, []);\n\t\tconst hasLineBreak: boolean = segments.some((s: TextSegment) => s.text.includes('\\n'));\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: TextSegment[] = [];\n\n\t\tfor (const segment of segments) {\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({ text: part, marks: 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: TextSegment[]): void {\n\t\tconst normalized: TextSegment[] = this.normalizeSegments(segments);\n\t\tif (normalized.length === 0) return;\n\t\tif (normalized.every((s: TextSegment) => s.text.trim() === '')) 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\tcloneBlockWithNewIds,\n\tfindBlockRecursive,\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 { 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 { 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\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) {\n\t\t\tconst slice = plainTextSlice(plainText);\n\t\t\tthis.dispatch(pasteSlice(this.getState(), slice));\n\t\t}\n\t}\n\n\t/** Sanitizes and processes pasted HTML content. */\n\tprivate handleHTML(html: string, state: EditorState): void {\n\t\tconst preSanitized: string = DOMPurify.sanitize(html, {\n\t\t\tFORBID_TAGS: PRE_SANITIZE_FORBID,\n\t\t\tALLOWED_URI_REGEXP: SAFE_URI_REGEXP,\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});\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/** Checks whether HTML requires the DocumentParser (tables, void blocks). */\n\tprivate requiresDocumentParser(html: string, container: DocumentFragment): boolean {\n\t\tif (/<table[\\s>]/i.test(html)) return true;\n\t\treturn this.containsVoidBlockElements(container);\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 doc = parseHTMLToDocument(html, 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\tconst insertOffset: number = isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\tlet insertIndex: number = ctx.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(ctx.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 (ctx.isAnchorEmpty && !isGapCursor(sel)) {\n\t\t\tbuilder.removeNode(ctx.parentPath, ctx.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\tcreateBlockFromRichData,\n\tfindTableCellAncestor,\n\tresolveCellInsertionContext,\n\tresolveRootInsertionContext,\n\tsanitizeAttrs,\n\tvalidateRichBlockData,\n} from '../commands/BlockInsertion.js';\nimport { addDeleteSelectionSteps } from '../commands/Commands.js';\nimport {\n\ttype BlockAttrs,\n\ttype BlockNode,\n\tcreateBlockNode,\n\tgenerateBlockId,\n} from '../model/Document.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\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';\nimport type { RichBlockData } from './InternalClipboard.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; attrs?: Record<string, unknown> };\n\t\ttry {\n\t\t\tparsed = JSON.parse(json) as { type?: string; attrs?: Record<string, unknown> };\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 = isNodeSelection(sel)\n\t\t\t? sel.nodeId\n\t\t\t: isGapCursor(sel)\n\t\t\t\t? sel.blockId\n\t\t\t\t: sel.anchor.blockId;\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);\n\n\t\t// Table cell: insert as last child\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\t\tif (cellId) {\n\t\t\tconst ctx = resolveCellInsertionContext(state, anchorBlockId, cellId, this.schemaRegistry);\n\t\t\tif (!ctx) return;\n\t\t\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\t\t\tif (!cell) return;\n\n\t\t\tconst builder = state.transaction('paste');\n\t\t\tbuilder.insertNode(ctx.parentPath, cell.children.length, newBlock);\n\t\t\tbuilder.setSelection(createNodeSelection(newBlockId, [...ctx.parentPath, newBlockId]));\n\t\t\tthis.dispatch(builder.build());\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\tconst builder = state.transaction('paste');\n\t\tbuilder.insertNode(ctx.parentPath, ctx.anchorIndex + insertOffset, newBlock);\n\t\tbuilder.setSelection(createNodeSelection(newBlockId, [...ctx.parentPath, newBlockId]));\n\t\tthis.dispatch(builder.build());\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\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\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\tconst anchorBlockId: BlockId = isNodeSelection(sel)\n\t\t\t? sel.nodeId\n\t\t\t: isGapCursor(sel)\n\t\t\t\t? sel.blockId\n\t\t\t\t: sel.anchor.blockId;\n\n\t\treturn this.resolveAndInsertRichBlocks(blocks, state, anchorBlockId);\n\t}\n\n\t/** Resolves insertion context (cell or root) and inserts rich blocks. */\n\tprivate resolveAndInsertRichBlocks(\n\t\tblocks: readonly RichBlockData[],\n\t\tstate: EditorState,\n\t\tanchorBlockId: BlockId,\n\t): boolean {\n\t\tconst sel = state.selection;\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\n\t\tif (cellId) {\n\t\t\tconst ctx = resolveCellInsertionContext(state, anchorBlockId, cellId, this.schemaRegistry);\n\t\t\tif (!ctx) return false;\n\t\t\tconst cell: BlockNode | undefined = state.getBlock(cellId);\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.insertRichBlocks(blocks, 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.insertRichBlocks(\n\t\t\tblocks,\n\t\t\tstate,\n\t\t\tctx,\n\t\t\tctx.anchorIndex + insertOffset,\n\t\t\t!isGapCursor(sel),\n\t\t);\n\t}\n\n\t/** Inserts validated rich blocks at the resolved position. */\n\tprivate insertRichBlocks(\n\t\tblocks: readonly RichBlockData[],\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 lastBlockId: BlockId | undefined;\n\t\tlet lastTextLen = 0;\n\n\t\tfor (const raw of blocks) {\n\t\t\tconst blockData = validateRichBlockData(raw, this.schemaRegistry);\n\t\t\tif (!blockData) continue;\n\n\t\t\tconst newBlock: BlockNode = createBlockFromRichData(blockData);\n\t\t\tbuilder.insertNode(context.parentPath, insertIndex, newBlock);\n\t\t\tinsertIndex++;\n\t\t\tlastBlockId = newBlock.id;\n\t\t\tlastTextLen = (blockData.text ?? '').length;\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 (lastBlockId) {\n\t\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, lastTextLen));\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 { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.js';\nimport type { DispatchFn, GetStateFn } from './InputHandler.js';\nimport { PasteHTMLHandler } from './PasteHTMLHandler.js';\nimport { PasteRichBlockHandler } from './PasteRichBlockHandler.js';\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}\n\nexport class PasteHandler {\n\tprivate readonly getState: GetStateFn;\n\tprivate readonly dispatch: DispatchFn;\n\tprivate readonly fileHandlerRegistry?: FileHandlerRegistry;\n\tprivate readonly isReadOnly: () => boolean;\n\tprivate readonly getPasteInterceptors: () => readonly PasteInterceptorEntry[];\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.fileHandlerRegistry = options.fileHandlerRegistry;\n\t\tthis.isReadOnly = options.isReadOnly ?? (() => false);\n\t\tthis.getPasteInterceptors = options.getPasteInterceptors ?? (() => []);\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\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\tthis.htmlHandler.handleHTMLOrTextPaste(clipboardData, plainText);\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 { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.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 } 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\treadonly getPasteInterceptors?: () => readonly PasteInterceptorEntry[];\n\treadonly getTextDirection?: TextDirectionFn;\n\treadonly navigateFromGapCursor?: GapCursorNavigateFn;\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\tcompositionTracker: this.compositionTracker,\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});\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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 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 * 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 { 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 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// --- 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\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}\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","/**\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 { 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 { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport type { SchemaRegistry } from '../model/SchemaRegistry.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\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\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 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 toolbarRegistry: ToolbarRegistry;\n\treadonly blockTypePickerRegistry: BlockTypePickerRegistry;\n\treadonly fileHandlerRegistry: FileHandlerRegistry;\n\treadonly nodeViewRegistry: NodeViewRegistry;\n\tisReadOnly(): boolean;\n\tinvalidateMiddlewareSort(): void;\n\tinvalidatePasteSort(): 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\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\tpluginId: string,\n\tmiddlewares: MiddlewareEntry[],\n\tpasteInterceptors: PasteInterceptorEntry[],\n\treg: PluginRegistrations,\n\tinvalidateMiddlewareSort: () => void,\n\tinvalidatePasteSort: () => void,\n): Pick<PluginContext, 'registerMiddleware' | 'registerPasteInterceptor'> {\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};\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| '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| '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\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(\n\t\t\tdeps.pluginId,\n\t\t\tdeps.middlewares,\n\t\t\tdeps.pasteInterceptors,\n\t\t\treg,\n\t\t\tdeps.invalidateMiddlewareSort,\n\t\t\tdeps.invalidatePasteSort,\n\t\t),\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};\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 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 { FileHandlerRegistry } from '../model/FileHandlerRegistry.js';\nimport { InputRuleRegistry } from '../model/InputRuleRegistry.js';\nimport { KeymapRegistry } from '../model/KeymapRegistry.js';\nimport type { PasteInterceptorEntry } from '../model/PasteInterceptor.js';\nimport { SchemaRegistry } from '../model/SchemaRegistry.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 { 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\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 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\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.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 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\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\ttoolbarRegistry: this.toolbarRegistry,\n\t\t\tblockTypePickerRegistry: this.blockTypePickerRegistry,\n\t\t\tfileHandlerRegistry: this.fileHandlerRegistry,\n\t\t\tnodeViewRegistry: this.nodeViewRegistry,\n\t\t\tisReadOnly: () => this.lifecycle.isReadOnly(),\n\t\t\tinvalidateMiddlewareSort: () => this.middlewareChain.invalidateMiddlewareSort(),\n\t\t\tinvalidatePasteSort: () => this.middlewareChain.invalidatePasteSort(),\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","/**\n * Supported locale identifiers for the notectl editor.\n * Use `Locale.BROWSER` to auto-detect from navigator.language.\n */\nexport const Locale = {\n\tEN: 'en',\n\tDE: 'de',\n\tES: 'es',\n\tFR: 'fr',\n\tZH: 'zh',\n\tRU: 'ru',\n\tAR: 'ar',\n\tHI: 'hi',\n\tPT: 'pt',\n\tBROWSER: 'browser',\n} as const;\n\nexport type Locale = (typeof Locale)[keyof typeof Locale];\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 * ContentStyleInjector: CSP-compliant utilities for injecting collected CSS\n * into the document. Supports nonce-based `<style>` elements and adoptedStyleSheets.\n */\n\n/** Options for {@link injectContentStyles}. */\nexport interface InjectStylesOptions {\n\t/** Nonce value for CSP compliance. Added as `nonce` attribute on the `<style>` element. */\n\treadonly nonce?: string;\n\t/** Target document. Defaults to `globalThis.document`. */\n\treadonly document?: Document;\n\t/** Container element to append the `<style>` to. Defaults to `document.head`. */\n\treadonly container?: HTMLElement;\n\t/**\n\t * Unique ID for the `<style>` element. When provided and an element with this ID\n\t * already exists, its content is replaced (avoiding duplicates on re-renders).\n\t */\n\treadonly id?: string;\n}\n\n/**\n * Injects CSS into the document via a `<style>` element.\n * Returns the created/updated element for manual cleanup.\n *\n * @example\n * ```ts\n * const result = editor.getContentHTML({ cssMode: 'classes' });\n * const style = injectContentStyles(result.css, { nonce: 'abc123' });\n * // Later: removeContentStyles('notectl-content', document);\n * ```\n */\nexport function injectContentStyles(css: string, options?: InjectStylesOptions): HTMLStyleElement {\n\tconst doc: Document = options?.document ?? globalThis.document;\n\tconst container: HTMLElement = options?.container ?? doc.head;\n\n\t// Reuse existing element if ID matches\n\tif (options?.id) {\n\t\tconst existing: HTMLElement | null = doc.getElementById(options.id);\n\t\tif (existing instanceof HTMLStyleElement) {\n\t\t\texisting.textContent = css;\n\t\t\tif (options.nonce) {\n\t\t\t\texisting.setAttribute('nonce', options.nonce);\n\t\t\t}\n\t\t\treturn existing;\n\t\t}\n\t}\n\n\tconst style: HTMLStyleElement = doc.createElement('style');\n\tstyle.textContent = css;\n\n\tif (options?.id) {\n\t\tstyle.id = options.id;\n\t}\n\tif (options?.nonce) {\n\t\tstyle.setAttribute('nonce', options.nonce);\n\t}\n\n\tcontainer.appendChild(style);\n\treturn style;\n}\n\n/**\n * Removes a previously injected `<style>` element by its ID.\n *\n * @param id - The ID of the `<style>` element to remove.\n * @param doc - Target document. Defaults to `globalThis.document`.\n */\nexport function removeContentStyles(id: string, doc?: Document): void {\n\tconst target: Document = doc ?? globalThis.document;\n\tconst element: HTMLElement | null = target.getElementById(id);\n\tif (element) {\n\t\telement.remove();\n\t}\n}\n\n/** Options for {@link adoptContentStyles}. */\nexport interface AdoptStylesOptions {\n\t/** Target document or ShadowRoot. Defaults to `globalThis.document`. */\n\treadonly target?: Document | ShadowRoot;\n\t/**\n\t * When true, replaces any previously adopted notectl sheet on the same target.\n\t * When false (default), appends a new sheet.\n\t */\n\treadonly replace?: boolean;\n}\n\n/** WeakSet tracking sheets created by {@link adoptContentStyles}. */\nconst notectlSheets: WeakSet<CSSStyleSheet> = new WeakSet();\n\n/**\n * Injects CSS via `adoptedStyleSheets` — no DOM element, no nonce needed.\n * Works with both `Document` and `ShadowRoot`, making it ideal for Web Components.\n *\n * Returns the created `CSSStyleSheet` for manual cleanup via {@link removeAdoptedStyles}.\n *\n * @example\n * ```ts\n * const result = editor.getContentHTML({ cssMode: 'classes' });\n * const sheet = adoptContentStyles(result.css);\n * // Later: removeAdoptedStyles(sheet);\n * ```\n */\nexport function adoptContentStyles(css: string, options?: AdoptStylesOptions): CSSStyleSheet {\n\tconst target: Document | ShadowRoot = options?.target ?? globalThis.document;\n\tconst sheet = new CSSStyleSheet();\n\tsheet.replaceSync(css);\n\tnotectlSheets.add(sheet);\n\n\tif (options?.replace) {\n\t\ttarget.adoptedStyleSheets = target.adoptedStyleSheets.filter((s) => !notectlSheets.has(s));\n\t}\n\n\ttarget.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet];\n\treturn sheet;\n}\n\n/**\n * Removes a specific adopted stylesheet from its target.\n *\n * @param sheet - The `CSSStyleSheet` returned by {@link adoptContentStyles}.\n * @param target - Target document or ShadowRoot. Defaults to `globalThis.document`.\n */\nexport function removeAdoptedStyles(sheet: CSSStyleSheet, target?: Document | ShadowRoot): void {\n\tconst root: Document | ShadowRoot = target ?? globalThis.document;\n\troot.adoptedStyleSheets = root.adoptedStyleSheets.filter((s) => s !== sheet);\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 { BlockNode, ChildNode } from '../model/Document.js';\nimport {\n\ttype Document,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgetBlockText,\n\tisInlineNode,\n\tisLeafBlock,\n\tisTextNode,\n} from '../model/Document.js';\nimport { formatHTML } 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\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 normalized: Document = registry ? normalizeCompositeBlocks(doc, registry) : doc;\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 * 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/** Leaf blocks use `content: { allow: ['text'] }`; composite blocks allow block types. */\nfunction isCompositeContentRule(content: { readonly allow: readonly string[] }): boolean {\n\treturn content.allow.length > 0 && !content.allow.includes('text');\n}\n\nfunction normalizeBlock(block: BlockNode, registry: SchemaRegistry): BlockNode {\n\tconst spec = registry.getNodeSpec(block.type);\n\n\t// No content rule, or content allows 'text' → leaf block, no normalization needed\n\tif (!spec?.content || !isCompositeContentRule(spec.content)) return block;\n\n\t// Composite block with all-inline children → wrap in paragraph\n\tif (isLeafBlock(block)) {\n\t\tconst children: readonly ChildNode[] | undefined =\n\t\t\tblock.children.length > 0 ? block.children : undefined;\n\t\tconst paragraph: BlockNode = createBlockNode(nodeType('paragraph'), children);\n\t\treturn createBlockNode(block.type, [paragraph], block.id, block.attrs);\n\t}\n\n\t// Composite block with block children → recurse\n\tconst normalized: readonly ChildNode[] = block.children.map((child) => {\n\t\tif (isTextNode(child) || isInlineNode(child)) return child;\n\t\treturn normalizeBlock(child, registry);\n\t});\n\treturn createBlockNode(block.type, normalized, block.id, block.attrs);\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);\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);\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/** Returns plain text content. */\nexport function getEditorText(state: EditorState): string {\n\treturn state.doc.children.map((b) => getBlockText(b)).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\treturn block.type === 'paragraph' && getBlockText(block) === '';\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 { 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/** 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 * 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 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: () => void;\n\tprivate readonly handleCompositionEnd: () => void;\n\tprivate pendingNodeSelectionClear = false;\n\tprivate pendingGapCursorClear = false;\n\n\tconstructor(deps: EventCoordinatorDeps) {\n\t\tthis.deps = deps;\n\n\t\tthis.handleCompositionStart = () => {\n\t\t\tdeps.cursorWrapper.onCompositionStart(deps.getState());\n\t\t};\n\t\tthis.handleCompositionEnd = () => {\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\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\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\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\n\t\t\tcontainer.appendChild(renderInlineNode(child, registry));\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\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.class) {\n\t\t\tfor (const cls of deco.attrs.class.split(' ')) {\n\t\t\t\tif (cls) el.classList.add(cls);\n\t\t\t}\n\t\t}\n\t\tif (deco.attrs.style) {\n\t\t\tappendStyleText(el, deco.attrs.style);\n\t\t}\n\t}\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 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\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\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/** 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(renderInlineNode(child, registry));\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\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 { isInlineNode, isLeafBlock, isTextNode, markSetsEqual } 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 } from './BlockRendering.js';\nimport {\n\tgetRenderedBlockElements,\n\tinsertAfterPreviousSibling,\n\treconcileWrappers,\n\tremoveBlockElement,\n\treplaceBlockElement,\n} from './BlockWrapperManagement.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\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\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.children.length !== newBlock.children.length) return true;\n\n\t// Compare attrs\n\tconst oldAttrs = oldBlock.attrs;\n\tconst newAttrs = newBlock.attrs;\n\tif (oldAttrs !== newAttrs) {\n\t\tif (!oldAttrs || !newAttrs) return true;\n\t\tconst oldKeys = Object.keys(oldAttrs);\n\t\tconst newKeys = Object.keys(newAttrs);\n\t\tif (oldKeys.length !== newKeys.length) return true;\n\t\tfor (const key of oldKeys) {\n\t\t\tif (oldAttrs[key] !== newAttrs[key]) return true;\n\t\t}\n\t}\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} 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 { 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\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 * 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","/** 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}\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};\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\n\tconst topPluginContainer: HTMLElement = document.createElement('div');\n\ttopPluginContainer.className = 'notectl-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.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('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: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","/** 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 { type ToolbarLayoutConfig, ToolbarPlugin } from '../plugins/toolbar/ToolbarPlugin.js';\nimport type { ToolbarConfig } 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\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<ReadonlyArray<Plugin>> = 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: string[][] = [];\n\tfor (const group of pluginGroups) {\n\t\tconst pluginIds: string[] = [];\n\t\tfor (const plugin of group) {\n\t\t\tpluginIds.push(plugin.id);\n\t\t\tpm.register(plugin);\n\t\t}\n\t\tgroups.push(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 { 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/** 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 cleanedUp = false;\n\n\tconstructor(private readonly deps: InitializerDeps) {\n\t\tthis.cfg = deps.config;\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\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\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) => {\n\t\t\t\tif (dom.announcer) dom.announcer.textContent = text;\n\t\t\t},\n\t\t\thasAnnouncement: () => !!dom.announcer?.textContent,\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\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\tgetPasteInterceptors: () => pm.getPasteInterceptors(),\n\t\t\tgetTextDirection,\n\t\t\tnavigateFromGapCursor,\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\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 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\tgetEditorJSON,\n\tgetEditorText,\n\tisEditorEmpty,\n\tsetEditorContentHTML,\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\n\tconstructor() {\n\t\tsuper();\n\t\tthis.attachShadow({ mode: 'open' });\n\t}\n\n\tstatic get observedAttributes(): string[] {\n\t\treturn ['placeholder', 'readonly', 'theme', 'paper-size', 'dir'];\n\t}\n\n\tconnectedCallback(): void {\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/** Initializes the editor with the given config. */\n\tasync init(config?: import('./EditorConfig.js').NotectlEditorConfig): Promise<void> {\n\t\tthis.cancelAutoInit();\n\t\tif (!this.lifecycle.markInitialized()) return;\n\t\tif (config) this.configController.setConfig(config);\n\n\t\tconst shadow: ShadowRoot | null = this.shadowRoot;\n\t\tif (!shadow) {\n\t\t\tconst error = new Error('Editor shadow root not available.');\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.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 * Each block element carries a `data-block-id` attribute. This is part of\n\t * the wire format: it lets `setContentHTML(getContentHTML())` preserve\n\t * block identity so the caret survives content round-trips driven by\n\t * external sync (Angular signal forms, RxJS pipes — see ARCHITECTURE §9.1).\n\t */\n\tasync getContentHTML(): Promise<string>;\n\tasync getContentHTML(options: { pretty?: boolean }): 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.1).\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/** 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/** Waits for the editor to be ready. */\n\twhenReady(): Promise<void> {\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.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\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// Register custom element (guarded for SSR / non-browser environments)\nif (typeof customElements !== 'undefined' && !customElements.get('notectl-editor')) {\n\tcustomElements.define('notectl-editor', NotectlEditor);\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"],"mappings":"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"}
|