@notectl/core 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +129 -0
- package/dist/chunks/{AlignmentPlugin--xAFlRqE.mjs → AlignmentPlugin-C9Rr_R-g.mjs} +2 -2
- package/dist/chunks/{AlignmentPlugin--xAFlRqE.mjs.map → AlignmentPlugin-C9Rr_R-g.mjs.map} +1 -1
- package/dist/chunks/{AttributedMarkCommands-bagbH8Qt.mjs → AttributedMarkCommands-BCvmqeFZ.mjs} +2 -2
- package/dist/chunks/{AttributedMarkCommands-bagbH8Qt.mjs.map → AttributedMarkCommands-BCvmqeFZ.mjs.map} +1 -1
- package/dist/chunks/{BidiIsolationPlugin-DBXyXpgm.mjs → BidiIsolationPlugin-Cw2KMk-H.mjs} +2 -2
- package/dist/chunks/{BidiIsolationPlugin-DBXyXpgm.mjs.map → BidiIsolationPlugin-Cw2KMk-H.mjs.map} +1 -1
- package/dist/chunks/{BlockInsertion-CJusvrDL.mjs → BlockInsertion-CYH2gRkY.mjs} +2 -2
- package/dist/chunks/{BlockInsertion-CJusvrDL.mjs.map → BlockInsertion-CYH2gRkY.mjs.map} +1 -1
- package/dist/chunks/{BlockquotePlugin-Bz2glHwX.mjs → BlockquotePlugin-0bZSbkyo.mjs} +2 -2
- package/dist/chunks/{BlockquotePlugin-Bz2glHwX.mjs.map → BlockquotePlugin-0bZSbkyo.mjs.map} +1 -1
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs +2 -0
- package/dist/chunks/CaretNavigationPlugin-BPZ9b6XR.mjs.map +1 -0
- package/dist/chunks/CodeBlockPlugin-BCJ3faXx.mjs +2 -0
- package/dist/chunks/{CodeBlockPlugin-C1hrdbSZ.mjs.map → CodeBlockPlugin-BCJ3faXx.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-C9RQoEWF.mjs → ColorGrid-DQEkJ-0y.mjs} +2 -2
- package/dist/chunks/{ColorGrid-C9RQoEWF.mjs.map → ColorGrid-DQEkJ-0y.mjs.map} +1 -1
- package/dist/chunks/ColorPickerPopup-CyF26zIU.mjs +2 -0
- package/dist/chunks/{ColorPickerPopup-DiA68Pzz.mjs.map → ColorPickerPopup-CyF26zIU.mjs.map} +1 -1
- package/dist/chunks/Commands-D5ND50PQ.mjs +2 -0
- package/dist/chunks/{Commands-1dFFldi7.mjs.map → Commands-D5ND50PQ.mjs.map} +1 -1
- package/dist/chunks/{Decoration-BpoEOANl.mjs → Decoration-BOP1TAUd.mjs} +2 -2
- package/dist/chunks/{Decoration-BpoEOANl.mjs.map → Decoration-BOP1TAUd.mjs.map} +1 -1
- package/dist/chunks/{FontPlugin-By-TGrQo.mjs → FontPlugin-D041tiLe.mjs} +2 -2
- package/dist/chunks/{FontPlugin-By-TGrQo.mjs.map → FontPlugin-D041tiLe.mjs.map} +1 -1
- package/dist/chunks/{FontSizePlugin-Dln-tYXV.mjs → FontSizePlugin-8CAvj1RD.mjs} +2 -2
- package/dist/chunks/{FontSizePlugin-Dln-tYXV.mjs.map → FontSizePlugin-8CAvj1RD.mjs.map} +1 -1
- package/dist/chunks/{FormulaPlugin-By1befrR.mjs → FormulaPlugin-DJ39BtqV.mjs} +2 -2
- package/dist/chunks/FormulaPlugin-DJ39BtqV.mjs.map +1 -0
- package/dist/chunks/GapCursorNavigation-DCWOmopl.mjs +2 -0
- package/dist/chunks/{GapCursorNavigation-BuXngVu-.mjs.map → GapCursorNavigation-DCWOmopl.mjs.map} +1 -1
- package/dist/chunks/GapCursorPlugin-2iR6506y.mjs +2 -0
- package/dist/chunks/{GapCursorPlugin-DAxVzXRs.mjs.map → GapCursorPlugin-2iR6506y.mjs.map} +1 -1
- package/dist/chunks/{HardBreakPlugin-DlbSlrdq.mjs → HardBreakPlugin-vniSvTgq.mjs} +2 -2
- package/dist/chunks/{HardBreakPlugin-DlbSlrdq.mjs.map → HardBreakPlugin-vniSvTgq.mjs.map} +1 -1
- package/dist/chunks/{HeadingPlugin-uMAjPOk5.mjs → HeadingPlugin-DUKsLuMd.mjs} +2 -2
- package/dist/chunks/{HeadingPlugin-uMAjPOk5.mjs.map → HeadingPlugin-DUKsLuMd.mjs.map} +1 -1
- package/dist/chunks/{HighlightPlugin-Bj7FPkIQ.mjs → HighlightPlugin-DRTAE1N5.mjs} +2 -2
- package/dist/chunks/{HighlightPlugin-Bj7FPkIQ.mjs.map → HighlightPlugin-DRTAE1N5.mjs.map} +1 -1
- package/dist/chunks/HorizontalRulePlugin-B9OvwrKB.mjs +2 -0
- package/dist/chunks/{HorizontalRulePlugin-DUVmTQpX.mjs.map → HorizontalRulePlugin-B9OvwrKB.mjs.map} +1 -1
- package/dist/chunks/{ImagePlugin-FToCOjd5.mjs → ImagePlugin-CwxTRr6d.mjs} +2 -2
- package/dist/chunks/{ImagePlugin-FToCOjd5.mjs.map → ImagePlugin-CwxTRr6d.mjs.map} +1 -1
- package/dist/chunks/{InlineCodePlugin-pISIoGQE.mjs → InlineCodePlugin-uxgMUX_E.mjs} +2 -2
- package/dist/chunks/{InlineCodePlugin-pISIoGQE.mjs.map → InlineCodePlugin-uxgMUX_E.mjs.map} +1 -1
- package/dist/chunks/{LinkPlugin-Ct5Z180S.mjs → LinkPlugin-CvWHbSkb.mjs} +2 -2
- package/dist/chunks/{LinkPlugin-Ct5Z180S.mjs.map → LinkPlugin-CvWHbSkb.mjs.map} +1 -1
- package/dist/chunks/{ListPlugin-DAY5ezNs.mjs → ListPlugin-BchSFWVn.mjs} +2 -2
- package/dist/chunks/{ListPlugin-DAY5ezNs.mjs.map → ListPlugin-BchSFWVn.mjs.map} +1 -1
- package/dist/chunks/{MarkdownHTMLRegistry-CKEx7LkR.mjs → MarkdownHTMLRegistry-Q7l0d6Ma.mjs} +2 -2
- package/dist/chunks/{MarkdownHTMLRegistry-CKEx7LkR.mjs.map → MarkdownHTMLRegistry-Q7l0d6Ma.mjs.map} +1 -1
- package/dist/chunks/{MarkdownParser-CWt3M6jR.mjs → MarkdownParser-Dp62mfUq.mjs} +2 -2
- package/dist/chunks/{MarkdownParser-CWt3M6jR.mjs.map → MarkdownParser-Dp62mfUq.mjs.map} +1 -1
- package/dist/chunks/{MarkdownSerializer-J-i28out.mjs → MarkdownSerializer-DBELFAeo.mjs} +2 -2
- package/dist/chunks/{MarkdownSerializer-J-i28out.mjs.map → MarkdownSerializer-DBELFAeo.mjs.map} +1 -1
- package/dist/chunks/MathFont-BS8IwYh2.mjs +2 -0
- package/dist/chunks/MathFont-BS8IwYh2.mjs.map +1 -0
- package/dist/chunks/NodeSelectionCommands-WH5snxr_.mjs +2 -0
- package/dist/chunks/{NodeSelectionCommands-1PjwUDTJ.mjs.map → NodeSelectionCommands-WH5snxr_.mjs.map} +1 -1
- package/dist/chunks/PluginHelpers-KDuTlmXm.mjs +2 -0
- package/dist/chunks/{PluginHelpers-Dx_0hfDe.mjs.map → PluginHelpers-KDuTlmXm.mjs.map} +1 -1
- package/dist/chunks/{PopupManager-CT-fMZJn.mjs → PopupManager-Cj86uDEB.mjs} +2 -2
- package/dist/chunks/{PopupManager-CT-fMZJn.mjs.map → PopupManager-Cj86uDEB.mjs.map} +1 -1
- package/dist/chunks/PrintPlugin-D2javl_d.mjs +2 -0
- package/dist/chunks/{PrintPlugin-7zz9mhhU.mjs.map → PrintPlugin-D2javl_d.mjs.map} +1 -1
- package/dist/chunks/SimpleMark-gTQaSJcO.mjs +2 -0
- package/dist/chunks/{SimpleMark-7CQcA4RK.mjs.map → SimpleMark-gTQaSJcO.mjs.map} +1 -1
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs +2 -0
- package/dist/chunks/SmartPastePlugin-BvJQ-fOz.mjs.map +1 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs +2 -0
- package/dist/chunks/StarterFonts-BDQPg-cu.mjs.map +1 -0
- package/dist/chunks/{StrikethroughPlugin-C2oWvGc3.mjs → StrikethroughPlugin-BdrG1TTY.mjs} +2 -2
- package/dist/chunks/{StrikethroughPlugin-C2oWvGc3.mjs.map → StrikethroughPlugin-BdrG1TTY.mjs.map} +1 -1
- package/dist/chunks/{SuperSubPlugin-D1xaKrSu.mjs → SuperSubPlugin-DTZB0Y3D.mjs} +2 -2
- package/dist/chunks/{SuperSubPlugin-D1xaKrSu.mjs.map → SuperSubPlugin-DTZB0Y3D.mjs.map} +1 -1
- package/dist/chunks/{TablePlugin-PJe3zwr-.mjs → TablePlugin-CPvg9YZS.mjs} +2 -2
- package/dist/chunks/{TablePlugin-PJe3zwr-.mjs.map → TablePlugin-CPvg9YZS.mjs.map} +1 -1
- package/dist/chunks/{TextColorPlugin-CTd9tfeG.mjs → TextColorPlugin-Ckg3KWvb.mjs} +2 -2
- package/dist/chunks/{TextColorPlugin-CTd9tfeG.mjs.map → TextColorPlugin-Ckg3KWvb.mjs.map} +1 -1
- package/dist/chunks/{TextDirectionAutoPlugin-CPnKVVuI.mjs → TextDirectionAutoPlugin-DRGbgTlx.mjs} +2 -2
- package/dist/chunks/{TextDirectionAutoPlugin-CPnKVVuI.mjs.map → TextDirectionAutoPlugin-DRGbgTlx.mjs.map} +1 -1
- package/dist/chunks/TextDirectionPlugin-BCeYyJHo.mjs +2 -0
- package/dist/chunks/{TextDirectionPlugin-Bns_03vL.mjs.map → TextDirectionPlugin-BCeYyJHo.mjs.map} +1 -1
- package/dist/chunks/TextDirectionService-OFOU6r_s.mjs +2 -0
- package/dist/chunks/{TextDirectionService-BcX07mw2.mjs.map → TextDirectionService-OFOU6r_s.mjs.map} +1 -1
- package/dist/chunks/TextFormattingPlugin-BhRxSC8q.mjs +2 -0
- package/dist/chunks/{TextFormattingPlugin-DBVlePqj.mjs.map → TextFormattingPlugin-BhRxSC8q.mjs.map} +1 -1
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs +3 -0
- package/dist/chunks/ThemeEngine-BUVo_zaT.mjs.map +1 -0
- package/dist/chunks/ToolbarPlugin-dmTj1jIz.mjs +2 -0
- package/dist/chunks/{ToolbarPlugin-DtfGTO_V.mjs.map → ToolbarPlugin-dmTj1jIz.mjs.map} +1 -1
- package/dist/chunks/{VideoPlugin-BrR8g6Rz.mjs → VideoPlugin-BugkI7-m.mjs} +2 -2
- package/dist/chunks/{VideoPlugin-BrR8g6Rz.mjs.map → VideoPlugin-BugkI7-m.mjs.map} +1 -1
- package/dist/chunks/XmlSupport-DIXH5on9.mjs +2 -0
- package/dist/chunks/XmlSupport-DIXH5on9.mjs.map +1 -0
- package/dist/chunks/full-JM0-SwWy.mjs +2 -0
- package/dist/chunks/{full-DSWM4cKQ.mjs.map → full-JM0-SwWy.mjs.map} +1 -1
- package/dist/chunks/minimal-DSlR2A27.mjs +2 -0
- package/dist/chunks/{minimal-CFkkPXef.mjs.map → minimal-DSlR2A27.mjs.map} +1 -1
- package/dist/chunks/register-BjsdJ3Aq.mjs +2 -0
- package/dist/chunks/register-BjsdJ3Aq.mjs.map +1 -0
- package/dist/chunks/src-5GKFQ2mQ.mjs +2 -0
- package/dist/chunks/src-5GKFQ2mQ.mjs.map +1 -0
- package/dist/chunks/xml-BsV44TrJ.mjs +2 -0
- package/dist/chunks/xml-BsV44TrJ.mjs.map +1 -0
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/fonts/math.d.ts +3 -0
- package/dist/fonts/math.d.ts.map +1 -0
- package/dist/fonts/math.mjs +1 -0
- package/dist/fonts/starter.d.ts +3 -0
- package/dist/fonts/starter.d.ts.map +1 -0
- package/dist/fonts/starter.mjs +1 -0
- package/dist/fonts.d.ts +4 -4
- package/dist/fonts.mjs +1 -1
- package/dist/full.d.ts +1 -1
- package/dist/full.mjs +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/markdown.mjs +1 -1
- 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/alignment.mjs +1 -1
- package/dist/plugins/bidi-isolation.mjs +1 -1
- package/dist/plugins/blockquote.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockHighlighting.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/formula/FormulaTypes.d.ts +1 -1
- package/dist/plugins/formula.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +1 -1
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading.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.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/SmartPastePlugin.d.ts.map +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-auto.mjs +1 -1
- 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/plugins/video.mjs +1 -1
- package/dist/presets/full.d.ts +1 -1
- package/dist/presets/full.mjs +1 -1
- package/dist/presets/minimal.d.ts +1 -1
- package/dist/presets/minimal.mjs +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/register.d.ts +3 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.mjs +1 -0
- package/package.json +24 -223
- package/dist/chunks/CaretNavigationPlugin-CNsT0eb4.mjs +0 -2
- package/dist/chunks/CaretNavigationPlugin-CNsT0eb4.mjs.map +0 -1
- package/dist/chunks/CodeBlockPlugin-C1hrdbSZ.mjs +0 -2
- package/dist/chunks/ColorPickerPopup-DiA68Pzz.mjs +0 -2
- package/dist/chunks/Commands-1dFFldi7.mjs +0 -2
- package/dist/chunks/FormulaPlugin-By1befrR.mjs.map +0 -1
- package/dist/chunks/GapCursorNavigation-BuXngVu-.mjs +0 -2
- package/dist/chunks/GapCursorPlugin-DAxVzXRs.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-DUVmTQpX.mjs +0 -2
- package/dist/chunks/NodeSelectionCommands-1PjwUDTJ.mjs +0 -2
- package/dist/chunks/PluginHelpers-Dx_0hfDe.mjs +0 -2
- package/dist/chunks/PrintPlugin-7zz9mhhU.mjs +0 -2
- package/dist/chunks/PrintTypes-CxU69qu3.mjs +0 -3
- package/dist/chunks/PrintTypes-CxU69qu3.mjs.map +0 -1
- package/dist/chunks/SimpleMark-7CQcA4RK.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-CYa_nD9v.mjs +0 -2
- package/dist/chunks/SmartPastePlugin-CYa_nD9v.mjs.map +0 -1
- package/dist/chunks/TextDirectionPlugin-Bns_03vL.mjs +0 -2
- package/dist/chunks/TextDirectionService-BcX07mw2.mjs +0 -2
- package/dist/chunks/TextFormattingPlugin-DBVlePqj.mjs +0 -2
- package/dist/chunks/ToolbarPlugin-DtfGTO_V.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs +0 -2
- package/dist/chunks/XmlDetector-D7TQpw-e.mjs.map +0 -1
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs +0 -2
- package/dist/chunks/XmlSupport-BYiRwlkK.mjs.map +0 -1
- package/dist/chunks/fonts-DQC6Uj4r.mjs +0 -2
- package/dist/chunks/fonts-DQC6Uj4r.mjs.map +0 -1
- package/dist/chunks/full-DSWM4cKQ.mjs +0 -2
- package/dist/chunks/minimal-CFkkPXef.mjs +0 -2
- package/dist/chunks/src-DiAOfuOT.mjs +0 -2
- package/dist/chunks/src-DiAOfuOT.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TablePlugin-PJe3zwr-.mjs","names":[],"sources":["../../src/editor/styles/table.ts","../../src/plugins/table/TableGrid.ts","../../src/plugins/table/TableHelpers.ts","../../src/plugins/table/TableLocale.ts","../../src/plugins/table/TableBorderColor.ts","../../src/plugins/table/TableClipboard.ts","../../src/plugins/table/TableSelection.ts","../../src/plugins/table/TableSizing.ts","../../src/plugins/table/TableCommands.ts","../../src/plugins/table/TableSizeEditor.ts","../../src/plugins/table/TableContextMenu.ts","../../src/plugins/table/TableNavigation.ts","../../src/plugins/table/TableControlsDOM.ts","../../src/plugins/table/TableControlsLayout.ts","../../src/plugins/table/TableControls.ts","../../src/plugins/table/TableNodeViews.ts","../../src/plugins/table/TablePlugin.ts"],"sourcesContent":["/** Table styles — table layout, selection, context menu, controls, insert lines. */\nexport const TABLE_CSS = `\n/* Table */\n.notectl-table-wrapper {\n\tmargin: 8px 0;\n\toverflow-x: auto;\n\tposition: relative;\n}\n\n.notectl-table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\ttable-layout: fixed;\n}\n\n.notectl-table col {\n\tbox-sizing: border-box;\n}\n\n/* Border resolution order: per-table local override → theme component token → theme global token.\n * The inline --ntbl-border-color is set by the toolbar's \"border color\" action and must win\n * over the theme so per-table customizations remain visible after a theme switch.\n */\n.notectl-table td {\n\tborder: 1px solid var(--ntbl-border-color, var(--notectl-table-border, var(--notectl-border)));\n\tbackground: var(--notectl-table-cell-bg, transparent);\n\tpadding: 8px 12px;\n\tmin-width: var(--ntbl-min-column-width, 60px);\n\tvertical-align: top;\n\tmin-height: 1.6em;\n}\n\n.notectl-table th {\n\tborder: 1px solid var(--ntbl-border-color, var(--notectl-table-border, var(--notectl-border)));\n\tbackground: var(--notectl-table-header-bg, var(--notectl-surface-raised));\n\tpadding: 8px 12px;\n\tmin-width: var(--ntbl-min-column-width, 60px);\n\tvertical-align: top;\n\ttext-align: start;\n\tfont-weight: 600;\n}\n\n.notectl-table--borderless td {\n\tborder: 1px dashed rgba(128, 128, 128, 0.15);\n}\n\n@media print {\n\t.notectl-table--borderless td {\n\t\tborder: none;\n\t}\n}\n\n.notectl-table td:focus-within {\n\toutline: 2px solid var(--notectl-primary);\n\toutline-offset: -2px;\n}\n\n.notectl-table-cell--selected {\n\tbackground: var(--notectl-primary-muted);\n}\n\n/* Table Context Menu */\n.notectl-table-context-menu {\n\tbackground: var(--notectl-surface-overlay);\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 6px;\n\tbox-shadow: 0 4px 12px var(--notectl-shadow);\n\tpadding: 4px 0;\n\tmin-width: 180px;\n\tz-index: 10000;\n}\n\n.notectl-table-context-menu button {\n\tdisplay: block;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\ttext-align: start;\n\tborder: none;\n\tbackground: none;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tcolor: var(--notectl-fg);\n\tfont-family: inherit;\n}\n\n.notectl-table-context-menu button:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-table-context-menu hr {\n\tborder: none;\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin: 4px 0;\n}\n\n/* === Table Controls === */\n\n/* Outer container for table + controls */\n.ntbl-container {\n\tposition: relative;\n\tmargin: 8px 0;\n\tpadding-block-start: 24px;\n\tpadding-inline-start: 24px;\n}\n\n.ntbl-container .notectl-table-wrapper {\n\tmargin: 0;\n}\n\n.ntbl-container.notectl-table--selected .notectl-table-wrapper {\n\toutline: 2px solid var(--notectl-primary-fg);\n\toutline-offset: 2px;\n\tborder-radius: 6px;\n}\n\n/* --- Column Handle Bar --- */\n.ntbl-col-bar {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 24px;\n\theight: 20px;\n\tdisplay: flex;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tz-index: 3;\n\tpointer-events: none;\n}\n\n.ntbl-container:hover .ntbl-col-bar,\n.ntbl-container:focus-within .ntbl-col-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-col-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n/* --- Row Handle Bar --- */\n.ntbl-row-bar {\n\tposition: absolute;\n\ttop: 24px;\n\tinset-inline-start: 0;\n\twidth: 20px;\n\tdisplay: flex;\n\tflex-direction: column;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tz-index: 3;\n\tpointer-events: none;\n}\n\n.ntbl-container:hover .ntbl-row-bar,\n.ntbl-container:focus-within .ntbl-row-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-row-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n/* --- Handle (shared base) --- */\n.ntbl-handle {\n\tposition: absolute;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n\ttransition: background 0.15s ease;\n\tborder-radius: 3px;\n}\n\n.ntbl-handle-select {\n\tposition: absolute;\n\tinset: 0;\n\twidth: 100%;\n\theight: 100%;\n\tborder: 0;\n\tborder-radius: inherit;\n\tbackground: transparent;\n\tcursor: pointer;\n}\n\n.ntbl-handle-select[aria-pressed=\"true\"] {\n\tbackground: var(--notectl-primary-muted);\n\tbox-shadow: inset 0 0 0 1px var(--notectl-primary);\n}\n\n.ntbl-handle-select:focus-visible {\n\toutline: none;\n\tbox-shadow: inset 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-col-handle {\n\theight: 100%;\n\tbackground: var(--notectl-hover-bg);\n\tborder-radius: 4px 4px 0 0;\n}\n\n.ntbl-col-handle:hover {\n\tbackground: var(--notectl-border);\n}\n\n.ntbl-row-handle {\n\twidth: 100%;\n\tbackground: var(--notectl-hover-bg);\n\tborder-start-start-radius: 4px;\n\tborder-end-start-radius: 4px;\n}\n\n.ntbl-row-handle:hover {\n\tbackground: var(--notectl-border);\n}\n\n/* --- Handle Delete Button --- */\n.ntbl-handle-delete {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 16px;\n\theight: 16px;\n\tborder: none;\n\tbackground: transparent;\n\tcolor: var(--notectl-fg-muted);\n\tcursor: pointer;\n\tborder-radius: 3px;\n\tpadding: 0;\n\topacity: 0;\n\ttransform: scale(0.7);\n\ttransition: opacity 0.15s, transform 0.15s,\n\t\tbackground 0.15s, color 0.15s;\n\tz-index: 1;\n}\n\n/* --- Logical column/row resize separators --- */\n.ntbl-col-resize-bar,\n.ntbl-row-resize-bar {\n\tposition: absolute;\n\tinset: 0 auto auto 0;\n\tz-index: 8;\n\tpointer-events: none;\n}\n\n.ntbl-resize-separator {\n\tposition: absolute;\n\tdisplay: block;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\topacity: 0;\n\tpointer-events: none;\n\ttouch-action: none;\n}\n\n.ntbl-resize-separator--column {\n\twidth: 12px;\n\ttransform: translateX(-6px);\n\tcursor: col-resize;\n}\n\n.ntbl-resize-separator--row {\n\theight: 12px;\n\ttransform: translateY(-6px);\n\tcursor: row-resize;\n}\n\n.ntbl-container:hover .ntbl-resize-separator,\n.ntbl-container:focus-within .ntbl-resize-separator,\n.ntbl-container.notectl-table--selected .ntbl-resize-separator,\n.ntbl-container--resizing-column .ntbl-resize-separator--column,\n.ntbl-container--resizing-row .ntbl-resize-separator--row {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-resize-separator::after {\n\tcontent: \"\";\n\tposition: absolute;\n\tbackground: var(--notectl-primary);\n\topacity: 0.35;\n}\n\n.ntbl-resize-separator--column::after {\n\tinset: 0 auto 0 5px;\n\twidth: 2px;\n}\n\n.ntbl-resize-separator--row::after {\n\tinset: 5px 0 auto 0;\n\theight: 2px;\n}\n\n.ntbl-resize-separator:hover::after,\n.ntbl-resize-separator:focus-visible::after {\n\topacity: 1;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-resize-separator:focus-visible {\n\toutline: none;\n}\n\n.ntbl-resize-indicator {\n\tposition: absolute;\n\tdisplay: none;\n\tz-index: 12;\n\tpadding: 3px 7px;\n\tborder-radius: 4px;\n\tbackground: var(--notectl-fg);\n\tcolor: var(--notectl-bg);\n\tfont: 12px/1.4 monospace;\n\tpointer-events: none;\n\twhite-space: nowrap;\n}\n\n.ntbl-resize-indicator--visible {\n\tdisplay: block;\n}\n\n.ntbl-container--resizing-column,\n.ntbl-container--resizing-column * {\n\tcursor: col-resize !important;\n\tuser-select: none !important;\n}\n\n.ntbl-container--resizing-row,\n.ntbl-container--resizing-row * {\n\tcursor: row-resize !important;\n\tuser-select: none !important;\n}\n\n.ntbl-handle:hover .ntbl-handle-delete,\n.ntbl-handle:focus-within .ntbl-handle-delete {\n\topacity: 1;\n\ttransform: scale(1);\n}\n\n.ntbl-handle-delete:hover,\n.ntbl-handle-delete:focus-visible {\n\tbackground: var(--notectl-danger-muted);\n\tcolor: var(--notectl-danger);\n}\n\n.ntbl-handle-delete:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Insert Lines --- */\n.ntbl-insert-line {\n\tposition: absolute;\n\tpointer-events: none;\n\topacity: 0;\n\ttransition: opacity 0.15s ease;\n\tz-index: 6;\n}\n\n.ntbl-insert-line--visible {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-insert-line--horizontal {\n\theight: 2px;\n\tbackground: linear-gradient(\n\t\t90deg,\n\t\ttransparent,\n\t\tvar(--notectl-primary) 8%,\n\t\tvar(--notectl-primary) 92%,\n\t\ttransparent\n\t);\n\tbox-shadow: 0 0 6px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-line--vertical {\n\twidth: 2px;\n\tbackground: linear-gradient(\n\t\t180deg,\n\t\ttransparent,\n\t\tvar(--notectl-primary) 8%,\n\t\tvar(--notectl-primary) 92%,\n\t\ttransparent\n\t);\n\tbox-shadow: 0 0 6px var(--notectl-focus-ring);\n}\n\n/* --- Insert Button on Line --- */\n.ntbl-insert-btn {\n\tposition: absolute;\n\twidth: 20px;\n\theight: 20px;\n\tborder-radius: 50%;\n\tbackground: var(--notectl-surface-raised);\n\tborder: 2px solid var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n\tcursor: pointer;\n\tpointer-events: all;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 0;\n\tbox-shadow: 0 2px 8px var(--notectl-focus-ring);\n\ttransition: background 0.15s, color 0.15s,\n\t\ttransform 0.15s, box-shadow 0.15s;\n}\n\n.ntbl-insert-line--horizontal .ntbl-insert-btn {\n\tleft: 50%;\n\ttop: -9px;\n\ttransform: translateX(-50%);\n}\n\n.ntbl-insert-line--vertical .ntbl-insert-btn {\n\ttop: 50%;\n\tinset-inline-start: -9px;\n\ttransform: translateY(-50%);\n}\n\n.ntbl-insert-btn:hover,\n.ntbl-insert-btn:focus-visible {\n\tbackground: var(--notectl-primary);\n\tcolor: var(--notectl-primary-fg);\n\tbox-shadow: 0 2px 12px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-line--horizontal .ntbl-insert-btn:hover {\n\ttransform: translateX(-50%) scale(1.2);\n}\n\n.ntbl-insert-line--vertical .ntbl-insert-btn:hover {\n\ttransform: translateY(-50%) scale(1.2);\n}\n\n/* --- Add Row / Add Column Zones --- */\n.ntbl-add-zone {\n\tposition: absolute;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n\tuser-select: none;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.2s ease,\n\t\tborder-color 0.2s ease;\n}\n\n.ntbl-container:hover .ntbl-add-zone,\n.ntbl-container:focus-within .ntbl-add-zone {\n\topacity: 1;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-add-zone {\n\topacity: 1;\n}\n\n.ntbl-delete-table-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 0;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-danger);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-danger);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n}\n\n.ntbl-container:hover .ntbl-delete-table-btn,\n.ntbl-container:focus-within .ntbl-delete-table-btn,\n.ntbl-container.notectl-table--selected .ntbl-delete-table-btn {\n\topacity: 1;\n}\n\n.ntbl-delete-table-btn:hover,\n.ntbl-delete-table-btn:focus-visible {\n\tbackground: var(--notectl-danger-muted);\n\tborder-color: var(--notectl-danger);\n}\n\n.ntbl-delete-table-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Context Menu Discovery Hint --- */\n.ntbl-context-hint {\n\tposition: absolute;\n\ttop: 4px;\n\tinset-inline-end: 0;\n\tfont-size: 11px;\n\tcolor: var(--notectl-fg-muted);\n\tpadding: 2px 8px;\n\tborder-radius: 4px;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tpointer-events: none;\n\twhite-space: nowrap;\n\tz-index: 5;\n}\n\n.ntbl-container:hover .ntbl-context-hint,\n.ntbl-container:focus-within .ntbl-context-hint,\n.ntbl-container.notectl-table--selected .ntbl-context-hint {\n\topacity: 1;\n}\n\n/* --- Table Actions Button --- */\n.ntbl-actions-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 48px;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-fg-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n\tcursor: pointer;\n\tpadding: 0;\n}\n\n.ntbl-container:hover .ntbl-actions-btn,\n.ntbl-container:focus-within .ntbl-actions-btn,\n.ntbl-container.notectl-table--selected .ntbl-actions-btn {\n\topacity: 1;\n}\n\n.ntbl-actions-btn:hover,\n.ntbl-actions-btn:focus-visible {\n\tbackground: var(--notectl-hover-bg);\n\tborder-color: var(--notectl-primary);\n}\n\n.ntbl-actions-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Border Color Button --- */\n.ntbl-border-color-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 24px;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-fg-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n\tcursor: pointer;\n\tpadding: 0;\n}\n\n.ntbl-container:hover .ntbl-border-color-btn,\n.ntbl-container:focus-within .ntbl-border-color-btn,\n.ntbl-container.notectl-table--selected .ntbl-border-color-btn {\n\topacity: 1;\n}\n\n.ntbl-border-color-btn:hover,\n.ntbl-border-color-btn:focus-visible {\n\tbackground: var(--notectl-hover-bg);\n\tborder-color: var(--notectl-primary);\n}\n\n.ntbl-border-color-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-border-color-swatch {\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 2px;\n\tborder: 1px solid rgba(0, 0, 0, 0.15);\n}\n\n/* --- Context Menu Keyboard Hint --- */\n.notectl-table-context-menu__hint {\n\tpadding: 4px 12px 6px;\n\tfont-size: 11px;\n\tcolor: var(--notectl-fg-muted);\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin-top: 4px;\n\ttext-align: center;\n\tuser-select: none;\n}\n\n/* --- Context Menu Focus --- */\n.notectl-table-context-menu button:focus-visible {\n\toutline: none;\n\tbackground: var(--notectl-primary-muted);\n\tbox-shadow: inset 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.notectl-table-context-menu [role=\"separator\"] {\n\tborder: none;\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin: 4px 0;\n\theight: 0;\n}\n\n/* --- Precise size editor --- */\n.notectl-table-size-editor {\n\tbox-sizing: border-box;\n\twidth: 340px;\n\tmax-width: calc(100vw - 16px);\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.notectl-table-size-editor__title {\n\tmargin-block-end: 10px;\n\tfont-weight: 600;\n}\n\n.notectl-table-size-editor__field {\n\tdisplay: grid;\n\tgap: 4px;\n\tmargin-block-end: 10px;\n\tfont-size: 13px;\n}\n\n.notectl-table-size-editor__input-wrap {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.notectl-table-size-editor input {\n\tbox-sizing: border-box;\n\tmin-width: 0;\n\twidth: 100%;\n\tpadding: 6px 8px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-bg);\n\tcolor: var(--notectl-fg);\n\tfont: inherit;\n}\n\n.notectl-table-size-editor input:focus-visible {\n\toutline: 2px solid var(--notectl-focus-ring);\n\toutline-offset: 1px;\n}\n\n.notectl-table-size-editor input[aria-invalid=\"true\"] {\n\tborder-color: var(--notectl-danger);\n}\n\n.notectl-table-size-editor__unit {\n\tcolor: var(--notectl-fg-muted);\n}\n\n.notectl-table-size-editor__error {\n\tmin-height: 1.2em;\n\tcolor: var(--notectl-danger);\n\tfont-size: 11px;\n}\n\n.notectl-table-size-editor__resets,\n.notectl-table-size-editor__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.notectl-table-size-editor__resets {\n\tpadding-block-end: 10px;\n\tborder-block-end: 1px solid var(--notectl-border);\n}\n\n.notectl-table-size-editor__actions {\n\tjustify-content: flex-end;\n\tpadding-block-start: 10px;\n}\n\n.notectl-table-size-editor button {\n\twidth: auto;\n\tpadding: 6px 8px;\n\tborder-radius: 4px;\n\tfont-size: 12px;\n}\n\n.notectl-table-context-menu .notectl-table-size-editor__apply {\n\tbackground: var(--notectl-primary);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-table-context-menu .notectl-table-size-editor button:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n/* Read-only tables keep semantic dimensions but expose no mutation affordances. */\n.ntbl-container[data-notectl-table-readonly] {\n\tpadding-block-start: 0;\n\tpadding-inline-start: 0;\n}\n\n.ntbl-container[data-notectl-table-readonly] > :is(\n\t.ntbl-col-bar,\n\t.ntbl-row-bar,\n\t.ntbl-col-resize-bar,\n\t.ntbl-row-resize-bar,\n\t.ntbl-insert-line,\n\t.ntbl-add-zone,\n\t.ntbl-delete-table-btn,\n\t.ntbl-border-color-btn,\n\t.ntbl-actions-btn,\n\t.ntbl-context-hint,\n\t.ntbl-resize-indicator\n) {\n\tdisplay: none !important;\n}\n\n.ntbl-add-row {\n\tbottom: 0;\n\theight: 24px;\n\tborder: 1px dashed var(--notectl-border);\n\tborder-end-start-radius: 6px;\n\tborder-end-end-radius: 6px;\n\tborder-block-start: none;\n\tcolor: var(--notectl-fg-muted);\n\ttransform: translateY(100%);\n}\n\n.ntbl-add-row:hover,\n.ntbl-add-row:focus-visible {\n\tbackground: var(--notectl-primary-muted);\n\tborder-color: var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n}\n\n.ntbl-add-row:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-add-col {\n\tinset-inline-end: 0;\n\ttop: 24px;\n\twidth: 24px;\n\tborder: 1px dashed var(--notectl-border);\n\tborder-start-end-radius: 6px;\n\tborder-end-end-radius: 6px;\n\tborder-inline-start: none;\n\tcolor: var(--notectl-fg-muted);\n\ttransform: translateX(100%);\n}\n\n[dir=\"rtl\"] .ntbl-add-col {\n\ttransform: translateX(-100%);\n}\n\n.ntbl-add-col:hover,\n.ntbl-add-col:focus-visible {\n\tbackground: var(--notectl-primary-muted);\n\tborder-color: var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n}\n\n.ntbl-add-col:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-add-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttransition: transform 0.2s ease;\n}\n\n.ntbl-add-zone:hover .ntbl-add-icon {\n\ttransform: scale(1.15);\n}\n\n@media print {\n\t.notectl-table-wrapper {\n\t\toverflow: visible;\n\t}\n\n\t.ntbl-container {\n\t\tpadding: 0;\n\t}\n\n\t.ntbl-container > [data-notectl-no-print] {\n\t\tdisplay: none !important;\n\t}\n}\n`;\n","/**\n * Canonical logical-grid mapping for table rows, cells, colspan, and rowspan.\n *\n * Every table feature uses this module instead of treating a cell's child-array\n * index as its column. Coordinates are zero-based and ranges are inclusive,\n * matching {@link CellRange} in the table selection service.\n */\n\nimport { type BlockNode, getBlockChildren } from '../../model/Document.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\n\n/** Defensive upper bound for malformed externally supplied span attributes. */\nexport const MAX_TABLE_SPAN = 1_000;\n\n/** Maximum logical columns materialized for one untrusted table. */\nexport const MAX_TABLE_COLUMNS = 1_000;\n\n/** One source cell and the logical rectangle it occupies. */\nexport interface TableGridCell {\n\treadonly cell: BlockNode;\n\treadonly sourceRow: BlockNode;\n\treadonly sourceRowIndex: number;\n\treadonly sourceCellIndex: number;\n\treadonly rowStart: number;\n\treadonly rowEnd: number;\n\treadonly columnStart: number;\n\treadonly columnEnd: number;\n\treadonly rowSpan: number;\n\treadonly columnSpan: number;\n}\n\n/** Inclusive logical rectangle. */\nexport interface TableGridRange {\n\treadonly fromRow: number;\n\treadonly fromColumn: number;\n\treadonly toRow: number;\n\treadonly toColumn: number;\n}\n\n/** Read-only logical table map. */\nexport interface TableGrid {\n\treadonly table: BlockNode;\n\treadonly rows: readonly BlockNode[];\n\treadonly rowCount: number;\n\treadonly columnCount: number;\n\treadonly cells: readonly TableGridCell[];\n\tcellAt(row: number, column: number): TableGridCell | undefined;\n\tcellById(cellId: BlockId): TableGridCell | undefined;\n\tcellsInRange(range: TableGridRange): readonly TableGridCell[];\n}\n\n/** Expands an inclusive range to a fixed point so no spanning cell is cut. */\nexport function expandTableGridRange(\n\tgrid: TableGrid,\n\trange: TableGridRange,\n): TableGridRange | null {\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\tlet expanded: TableGridRange = {\n\t\tfromRow: clampRangeIndex(Math.min(range.fromRow, range.toRow), grid.rowCount),\n\t\tfromColumn: clampRangeIndex(Math.min(range.fromColumn, range.toColumn), grid.columnCount),\n\t\ttoRow: clampRangeIndex(Math.max(range.fromRow, range.toRow), grid.rowCount),\n\t\ttoColumn: clampRangeIndex(Math.max(range.fromColumn, range.toColumn), grid.columnCount),\n\t};\n\twhile (true) {\n\t\tlet fromRow: number = expanded.fromRow;\n\t\tlet fromColumn: number = expanded.fromColumn;\n\t\tlet toRow: number = expanded.toRow;\n\t\tlet toColumn: number = expanded.toColumn;\n\t\tfor (const entry of grid.cellsInRange(expanded)) {\n\t\t\tfromRow = Math.min(fromRow, entry.rowStart);\n\t\t\tfromColumn = Math.min(fromColumn, entry.columnStart);\n\t\t\ttoRow = Math.max(toRow, entry.rowEnd - 1);\n\t\t\ttoColumn = Math.max(toColumn, entry.columnEnd - 1);\n\t\t}\n\t\tif (\n\t\t\tfromRow === expanded.fromRow &&\n\t\t\tfromColumn === expanded.fromColumn &&\n\t\t\ttoRow === expanded.toRow &&\n\t\t\ttoColumn === expanded.toColumn\n\t\t) {\n\t\t\treturn expanded;\n\t\t}\n\t\texpanded = { fromRow, fromColumn, toRow, toColumn };\n\t}\n}\n\n/** Converts an untrusted span value to a bounded positive integer. */\nexport function normalizeTableSpan(value: unknown, maximum = MAX_TABLE_SPAN): number {\n\tif (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value)) return 1;\n\tif (value <= 1) return 1;\n\treturn Math.min(value, maximum);\n}\n\n/** Builds the logical grid for a table node. Non-table input yields an empty grid. */\nexport function createTableGrid(table: BlockNode): TableGrid {\n\tconst rows: readonly BlockNode[] =\n\t\ttable.type === 'table' ? getBlockChildren(table).filter((row) => row.type === 'table_row') : [];\n\tconst slots: (TableGridCell | undefined)[][] = Array.from({ length: rows.length }, () => []);\n\tconst cells: TableGridCell[] = [];\n\tconst byId = new Map<BlockId, TableGridCell>();\n\tlet columnCount = 0;\n\n\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\tconst sourceRow: BlockNode | undefined = rows[rowIndex];\n\t\tif (!sourceRow) continue;\n\t\tconst sourceCells: readonly BlockNode[] = getBlockChildren(sourceRow).filter(\n\t\t\t(cell) => cell.type === 'table_cell',\n\t\t);\n\t\tlet column = 0;\n\n\t\tfor (let sourceCellIndex = 0; sourceCellIndex < sourceCells.length; sourceCellIndex++) {\n\t\t\tconst cell: BlockNode | undefined = sourceCells[sourceCellIndex];\n\t\t\tif (!cell) continue;\n\n\t\t\tconst requestedColumnSpan: number = normalizeTableSpan(cell.attrs?.colspan);\n\t\t\tconst requestedRowSpan: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst rowSpan: number = Math.min(requestedRowSpan, rows.length - rowIndex);\n\n\t\t\tconst availableColumn: number | undefined = findAvailableColumn(\n\t\t\t\tslots[rowIndex] ?? [],\n\t\t\t\tcolumn,\n\t\t\t\trequestedColumnSpan,\n\t\t\t);\n\t\t\tif (availableColumn === undefined) continue;\n\t\t\tcolumn = availableColumn;\n\t\t\tconst columnSpan: number = Math.min(requestedColumnSpan, MAX_TABLE_COLUMNS - column);\n\t\t\tconst entry: TableGridCell = {\n\t\t\t\tcell,\n\t\t\t\tsourceRow,\n\t\t\t\tsourceRowIndex: rowIndex,\n\t\t\t\tsourceCellIndex,\n\t\t\t\trowStart: rowIndex,\n\t\t\t\trowEnd: rowIndex + rowSpan,\n\t\t\t\tcolumnStart: column,\n\t\t\t\tcolumnEnd: column + columnSpan,\n\t\t\t\trowSpan,\n\t\t\t\tcolumnSpan,\n\t\t\t};\n\n\t\t\tcells.push(entry);\n\t\t\tbyId.set(cell.id, entry);\n\t\t\tfor (let occupiedRow = entry.rowStart; occupiedRow < entry.rowEnd; occupiedRow++) {\n\t\t\t\tconst rowSlots: (TableGridCell | undefined)[] | undefined = slots[occupiedRow];\n\t\t\t\tif (!rowSlots) continue;\n\t\t\t\tfor (\n\t\t\t\t\tlet occupiedColumn = entry.columnStart;\n\t\t\t\t\toccupiedColumn < entry.columnEnd;\n\t\t\t\t\toccupiedColumn++\n\t\t\t\t) {\n\t\t\t\t\t// Malformed overlapping spans never overwrite the first valid owner.\n\t\t\t\t\trowSlots[occupiedColumn] ??= entry;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcolumn = entry.columnEnd;\n\t\t\tcolumnCount = Math.max(columnCount, entry.columnEnd);\n\t\t}\n\t}\n\n\treturn {\n\t\ttable,\n\t\trows,\n\t\trowCount: rows.length,\n\t\tcolumnCount,\n\t\tcells,\n\t\tcellAt(row: number, column: number): TableGridCell | undefined {\n\t\t\tif (!Number.isInteger(row) || !Number.isInteger(column) || row < 0 || column < 0) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn slots[row]?.[column];\n\t\t},\n\t\tcellById(cellId: BlockId): TableGridCell | undefined {\n\t\t\treturn byId.get(cellId);\n\t\t},\n\t\tcellsInRange(range: TableGridRange): readonly TableGridCell[] {\n\t\t\tconst minRow: number = Math.max(0, Math.min(range.fromRow, range.toRow));\n\t\t\tconst maxRow: number = Math.min(rows.length - 1, Math.max(range.fromRow, range.toRow));\n\t\t\tconst minColumn: number = Math.max(0, Math.min(range.fromColumn, range.toColumn));\n\t\t\tconst maxColumn: number = Math.min(\n\t\t\t\tcolumnCount - 1,\n\t\t\t\tMath.max(range.fromColumn, range.toColumn),\n\t\t\t);\n\t\t\tif (maxRow < minRow || maxColumn < minColumn) return [];\n\n\t\t\treturn cells.filter(\n\t\t\t\t(entry) =>\n\t\t\t\t\tentry.rowStart <= maxRow &&\n\t\t\t\t\tentry.rowEnd > minRow &&\n\t\t\t\t\tentry.columnStart <= maxColumn &&\n\t\t\t\t\tentry.columnEnd > minColumn,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Projects canonical column-width metadata onto a clipboard table slice.\n * Metadata is retained only when the surviving cells cover one contiguous,\n * unambiguous run of the original logical columns.\n */\nexport function projectTableColumnWidthsForSlice(original: BlockNode, slice: BlockNode): BlockNode {\n\tconst retainedIds = new Set<BlockId>();\n\tconst retainedRowIds = new Set<BlockId>();\n\tfor (const row of getBlockChildren(slice)) {\n\t\tretainedRowIds.add(row.id);\n\t\tfor (const cell of getBlockChildren(row)) retainedIds.add(cell.id);\n\t}\n\tconst grid: TableGrid = createTableGrid(original);\n\tconst retainedEntries: readonly TableGridCell[] = grid.cells.filter((entry) =>\n\t\tretainedIds.has(entry.cell.id),\n\t);\n\tif (retainedEntries.length === 0) return withoutColumnWidths(slice);\n\n\tconst firstColumn: number = Math.min(...retainedEntries.map((entry) => entry.columnStart));\n\tconst lastColumn: number = Math.max(...retainedEntries.map((entry) => entry.columnEnd)) - 1;\n\tconst retainedRowIndexes: readonly number[] = grid.rows\n\t\t.map((row, index) => (retainedRowIds.has(row.id) ? index : -1))\n\t\t.filter((index) => index >= 0);\n\tif (retainedRowIndexes.length === 0) return withoutColumnWidths(slice);\n\tconst firstRow: number = Math.min(...retainedRowIndexes);\n\tconst lastRow: number = Math.max(...retainedRowIndexes);\n\tconst normalizedSlice: BlockNode = normalizeSelectionSliceSpans(\n\t\tslice,\n\t\tgrid,\n\t\tfirstRow,\n\t\tlastRow,\n\t\tfirstColumn,\n\t\tlastColumn,\n\t);\n\tfor (const rowIndex of retainedRowIndexes) {\n\t\tconst coveredColumns = new Set<number>();\n\t\tfor (const entry of retainedEntries) {\n\t\t\tif (entry.rowStart > rowIndex || entry.rowEnd <= rowIndex) continue;\n\t\t\tfor (let column = entry.columnStart; column < entry.columnEnd; column++) {\n\t\t\t\tcoveredColumns.add(column);\n\t\t\t}\n\t\t}\n\t\tfor (let column = firstColumn; column <= lastColumn; column++) {\n\t\t\tif (!coveredColumns.has(column)) return withoutColumnWidths(normalizedSlice);\n\t\t}\n\t}\n\n\tconst rawWidths: unknown = original.attrs?.columnWidthsPx;\n\tif (!Array.isArray(rawWidths)) return normalizedSlice;\n\n\tconst projected: (number | null)[] = [];\n\tfor (let column = firstColumn; column <= lastColumn; column++) {\n\t\tconst value: unknown = rawWidths[column];\n\t\tprojected.push(typeof value === 'number' && Number.isFinite(value) ? value : null);\n\t}\n\tif (!projected.some((value) => value !== null)) return withoutColumnWidths(normalizedSlice);\n\n\treturn {\n\t\t...normalizedSlice,\n\t\tattrs: {\n\t\t\t...(normalizedSlice.attrs ?? {}),\n\t\t\tcolumnWidthsPx: Object.freeze(projected),\n\t\t},\n\t};\n}\n\nfunction normalizeSelectionSliceSpans(\n\tslice: BlockNode,\n\tgrid: TableGrid,\n\tfirstRow: number,\n\tlastRow: number,\n\tfirstColumn: number,\n\tlastColumn: number,\n): BlockNode {\n\tlet changed = false;\n\tconst rows: readonly BlockNode[] = getBlockChildren(slice).map((row) => {\n\t\tlet rowChanged = false;\n\t\tconst cells: readonly BlockNode[] = getBlockChildren(row).map((cell) => {\n\t\t\tconst entry: TableGridCell | undefined = grid.cellById(cell.id);\n\t\t\tif (!entry) return cell;\n\t\t\tconst rowSpan: number =\n\t\t\t\tMath.min(entry.rowEnd, lastRow + 1) - Math.max(entry.rowStart, firstRow);\n\t\t\tconst columnSpan: number =\n\t\t\t\tMath.min(entry.columnEnd, lastColumn + 1) - Math.max(entry.columnStart, firstColumn);\n\t\t\tconst previousRowSpan: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst previousColumnSpan: number = normalizeTableSpan(cell.attrs?.colspan);\n\t\t\tif (rowSpan === previousRowSpan && columnSpan === previousColumnSpan) return cell;\n\t\t\trowChanged = true;\n\t\t\tconst { colspan: _colspan, rowspan: _rowspan, ...rest } = cell.attrs ?? {};\n\t\t\tconst attrs = {\n\t\t\t\t...rest,\n\t\t\t\t...(columnSpan > 1 ? { colspan: columnSpan } : {}),\n\t\t\t\t...(rowSpan > 1 ? { rowspan: rowSpan } : {}),\n\t\t\t};\n\t\t\tconst { attrs: _attrs, ...withoutAttrs } = cell;\n\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t});\n\t\tif (!rowChanged) return row;\n\t\tchanged = true;\n\t\treturn { ...row, children: cells };\n\t});\n\treturn changed ? { ...slice, children: rows } : slice;\n}\n\n/**\n * Creates a self-contained table fragment for an inclusive logical rectangle.\n * The requested rectangle first expands to whole spanning cells. Owners are\n * emitted once, and dimensions are projected from their canonical logical\n * columns and rows.\n */\nexport function sliceTableToRange(table: BlockNode, range: TableGridRange): BlockNode | null {\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\tconst expanded: TableGridRange | null = expandTableGridRange(grid, range);\n\tif (!expanded) return null;\n\tconst { fromRow, fromColumn, toRow, toColumn } = expanded;\n\tconst retainedCells: readonly TableGridCell[] = grid.cellsInRange({\n\t\tfromRow,\n\t\tfromColumn,\n\t\ttoRow,\n\t\ttoColumn,\n\t});\n\n\tconst rows: BlockNode[] = [];\n\tconst preservesCompleteRows: boolean = fromColumn === 0 && toColumn === grid.columnCount - 1;\n\tfor (let logicalRow = fromRow; logicalRow <= toRow; logicalRow++) {\n\t\tconst sourceRow: BlockNode | undefined = grid.rows[logicalRow];\n\t\tif (!sourceRow) continue;\n\t\tconst cells: BlockNode[] = retainedCells\n\t\t\t.filter((entry: TableGridCell): boolean => Math.max(entry.rowStart, fromRow) === logicalRow)\n\t\t\t.sort(\n\t\t\t\t(a: TableGridCell, b: TableGridCell): number =>\n\t\t\t\t\tMath.max(a.columnStart, fromColumn) - Math.max(b.columnStart, fromColumn),\n\t\t\t)\n\t\t\t.map((entry: TableGridCell): BlockNode => {\n\t\t\t\tconst rowSpan: number =\n\t\t\t\t\tMath.min(entry.rowEnd, toRow + 1) - Math.max(entry.rowStart, fromRow);\n\t\t\t\tconst columnSpan: number =\n\t\t\t\t\tMath.min(entry.columnEnd, toColumn + 1) - Math.max(entry.columnStart, fromColumn);\n\t\t\t\tconst { colspan: _colspan, rowspan: _rowspan, ...rest } = entry.cell.attrs ?? {};\n\t\t\t\tconst attrs = {\n\t\t\t\t\t...rest,\n\t\t\t\t\t...(columnSpan > 1 ? { colspan: columnSpan } : {}),\n\t\t\t\t\t...(rowSpan > 1 ? { rowspan: rowSpan } : {}),\n\t\t\t\t};\n\t\t\t\tconst { attrs: _attrs, ...withoutAttrs } = entry.cell;\n\t\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t\t});\n\t\tconst { htmlId: _rowHTMLId, ...rowWithoutHTMLId } = sourceRow;\n\t\trows.push({\n\t\t\t...(preservesCompleteRows ? sourceRow : rowWithoutHTMLId),\n\t\t\tchildren: cells,\n\t\t});\n\t}\n\n\tconst { columnWidthsPx: _columnWidthsPx, ...tableAttrs } = table.attrs ?? {};\n\tconst rawWidths: unknown = table.attrs?.columnWidthsPx;\n\tconst widths: (number | null)[] = [];\n\tif (Array.isArray(rawWidths)) {\n\t\tfor (let column = fromColumn; column <= toColumn; column++) {\n\t\t\tconst value: unknown = rawWidths[column];\n\t\t\twidths.push(typeof value === 'number' && Number.isFinite(value) ? value : null);\n\t\t}\n\t}\n\tconst attrs = {\n\t\t...tableAttrs,\n\t\t...(widths.some((width: number | null): boolean => width !== null)\n\t\t\t? { columnWidthsPx: Object.freeze(widths) }\n\t\t\t: {}),\n\t};\n\tconst preservesCompleteTable: boolean =\n\t\tpreservesCompleteRows && fromRow === 0 && toRow === grid.rowCount - 1;\n\tconst { attrs: _attrs, ...tableWithoutAttrs } = table;\n\tconst { htmlId: _tableHTMLId, ...tableWithoutAttrsAndHTMLId } = tableWithoutAttrs;\n\tconst withoutAttrs = preservesCompleteTable ? tableWithoutAttrs : tableWithoutAttrsAndHTMLId;\n\treturn Object.keys(attrs).length > 0\n\t\t? { ...withoutAttrs, children: rows, attrs }\n\t\t: { ...withoutAttrs, children: rows };\n}\n\nfunction withoutColumnWidths(table: BlockNode): BlockNode {\n\tif (!table.attrs || !Object.hasOwn(table.attrs, 'columnWidthsPx')) return table;\n\tconst { columnWidthsPx: _columnWidthsPx, ...attrs } = table.attrs;\n\tconst { attrs: _attrs, ...withoutAttrs } = table;\n\treturn {\n\t\t...withoutAttrs,\n\t\t...(Object.keys(attrs).length > 0 ? { attrs } : {}),\n\t};\n}\n\nfunction findAvailableColumn(\n\trow: readonly (TableGridCell | undefined)[],\n\tstart: number,\n\tspan: number,\n): number | undefined {\n\tlet column: number = Math.max(0, start);\n\twhile (column + span <= MAX_TABLE_COLUMNS) {\n\t\tlet available = true;\n\t\tfor (let offset = 0; offset < span; offset++) {\n\t\t\tif (row[column + offset]) {\n\t\t\t\tavailable = false;\n\t\t\t\tcolumn += offset + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (available) return column;\n\t}\n\treturn undefined;\n}\n\nfunction clampRangeIndex(value: number, upperBound: number): number {\n\tif (!Number.isFinite(value)) return 0;\n\treturn Math.min(upperBound - 1, Math.max(0, Math.trunc(value)));\n}\n","/**\n * Utility functions for table operations: creating table structures,\n * finding table context, and navigating cells.\n */\n\nimport {\n\ttype BlockNode,\n\ttype ChildNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n\tgetBlockChildren,\n} from '../../model/Document.js';\nimport type { BlockId, NodeTypeName } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport { type TableGridCell, createTableGrid } from './TableGrid.js';\n\n/** Context information about a cell within a table. */\nexport interface TableContext {\n\treadonly tableId: BlockId;\n\treadonly tableIndex: number;\n\treadonly rowId: BlockId;\n\treadonly rowIndex: number;\n\treadonly rowEnd: number;\n\treadonly cellId: BlockId;\n\treadonly colIndex: number;\n\treadonly colEnd: number;\n\treadonly rowspan: number;\n\treadonly colspan: number;\n\treadonly totalRows: number;\n\treadonly totalCols: number;\n}\n\n/** Creates a table BlockNode structure with the given dimensions. */\nexport function createTable(rows: number, cols: number): BlockNode {\n\tconst tableId: BlockId = generateBlockId();\n\tconst rowNodes: ChildNode[] = [];\n\n\tfor (let r = 0; r < rows; r++) {\n\t\tconst cellNodes: ChildNode[] = [];\n\t\tfor (let c = 0; c < cols; c++) {\n\t\t\tcellNodes.push(createTableCell());\n\t\t}\n\t\trowNodes.push(\n\t\t\tcreateBlockNode(nodeType('table_row') as NodeTypeName, cellNodes, generateBlockId()),\n\t\t);\n\t}\n\n\treturn createBlockNode(nodeType('table') as NodeTypeName, rowNodes, tableId);\n}\n\n/** Creates a single table row with the given number of cells. */\nexport function createTableRow(cols: number): BlockNode {\n\tconst cellNodes: ChildNode[] = [];\n\tfor (let c = 0; c < cols; c++) {\n\t\tcellNodes.push(createTableCell());\n\t}\n\treturn createBlockNode(nodeType('table_row') as NodeTypeName, cellNodes, generateBlockId());\n}\n\n/** Creates a single empty table cell containing a paragraph. */\nexport function createTableCell(): BlockNode {\n\tconst paragraph: BlockNode = createBlockNode(\n\t\tnodeType('paragraph') as NodeTypeName,\n\t\t[createTextNode('')],\n\t\tgenerateBlockId(),\n\t);\n\treturn createBlockNode(nodeType('table_cell') as NodeTypeName, [paragraph], generateBlockId());\n}\n\n/**\n * Finds table context for a given block ID.\n * Returns null if the block is not inside a table.\n */\nexport function findTableContext(state: EditorState, blockId: BlockId): TableContext | null {\n\tconst path = state.getNodePath(blockId);\n\tif (!path) return null;\n\n\t// Resolve the owning table and cell by identity first. Logical coordinates\n\t// come exclusively from TableGrid; a cell's child-array index is not its\n\t// column when colspan/rowspan are present.\n\tlet tableId: BlockId | null = null;\n\tlet tableNode: BlockNode | null = null;\n\tlet cellId: BlockId | null = null;\n\n\tfor (const id of path) {\n\t\tconst node = state.getBlock(id as BlockId);\n\t\tif (!node) continue;\n\t\tif (node.type === 'table') {\n\t\t\ttableId = id as BlockId;\n\t\t\ttableNode = node;\n\t\t} else if (node.type === 'table_cell') {\n\t\t\tcellId = id as BlockId;\n\t\t}\n\t}\n\n\t// If the block itself is a cell\n\tconst block = state.getBlock(blockId);\n\tif (block?.type === 'table_cell') {\n\t\tcellId = blockId;\n\t}\n\n\tif (!tableId || !tableNode || !cellId) return null;\n\n\t// Find table index in document\n\tconst tableIndex: number = state.doc.children.findIndex((b) => b.id === tableId);\n\n\tconst grid = createTableGrid(tableNode);\n\tconst gridCell: TableGridCell | undefined = grid.cellById(cellId);\n\tif (!gridCell) return null;\n\n\treturn {\n\t\ttableId,\n\t\ttableIndex,\n\t\trowId: gridCell.sourceRow.id,\n\t\trowIndex: gridCell.rowStart,\n\t\trowEnd: gridCell.rowEnd,\n\t\tcellId,\n\t\tcolIndex: gridCell.columnStart,\n\t\tcolEnd: gridCell.columnEnd,\n\t\trowspan: gridCell.rowSpan,\n\t\tcolspan: gridCell.columnSpan,\n\t\ttotalRows: grid.rowCount,\n\t\ttotalCols: grid.columnCount,\n\t};\n}\n\n/**\n * Gets the cell BlockId at the given row and column indices.\n * Returns null if out of bounds.\n */\nexport function getCellAt(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): BlockId | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\treturn createTableGrid(table).cellAt(rowIndex, colIndex)?.cell.id ?? null;\n}\n\n/** Returns all cell IDs in a table in row-major order. */\nexport function getAllCellIds(state: EditorState, tableId: BlockId): readonly BlockId[] {\n\tconst table = state.getBlock(tableId);\n\tif (!table) return [];\n\n\tif (table.type !== 'table') return [];\n\treturn createTableGrid(table).cells.map((entry: TableGridCell) => entry.cell.id);\n}\n\n/** Returns the first leaf-block ID inside a table cell (e.g. the paragraph). */\nexport function getFirstLeafInCell(state: EditorState, cellId: BlockId): BlockId {\n\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\tif (!cell) return cellId;\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(cell);\n\tconst first: BlockNode | undefined = blockChildren[0];\n\tif (!first) return cellId;\n\tlet current: BlockNode = first;\n\twhile (true) {\n\t\tconst children: readonly BlockNode[] = getBlockChildren(current);\n\t\tconst next: BlockNode | undefined = children[0];\n\t\tif (!next) return current.id;\n\t\tcurrent = next;\n\t}\n}\n\n/** Returns the last leaf-block ID inside a table cell. */\nexport function getLastLeafInCell(state: EditorState, cellId: BlockId): BlockId {\n\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\tif (!cell) return cellId;\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(cell);\n\tconst last: BlockNode | undefined = blockChildren[blockChildren.length - 1];\n\tif (!last) return cellId;\n\tlet current: BlockNode = last;\n\twhile (true) {\n\t\tconst children: readonly BlockNode[] = getBlockChildren(current);\n\t\tconst next: BlockNode | undefined = children[children.length - 1];\n\t\tif (!next) return current.id;\n\t\tcurrent = next;\n\t}\n}\n\n/** Checks whether a block is inside a table. */\nexport function isInsideTable(state: EditorState, blockId: BlockId): boolean {\n\tconst path = state.getNodePath(blockId);\n\tif (!path) return false;\n\n\tfor (const id of path) {\n\t\tconst node = state.getBlock(id as BlockId);\n\t\tif (node?.type === 'table') return true;\n\t}\n\treturn false;\n}\n","/**\n * Locale interface and default English locale for the TablePlugin.\n * Allows full i18n of all user-facing strings in the table feature.\n */\n\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\n\n// --- Locale Interface ---\n\nexport interface TableLocale {\n\t// Context Menu\n\treadonly insertRowAbove: string;\n\treadonly insertRowBelow: string;\n\treadonly insertColumnLeft: string;\n\treadonly insertColumnRight: string;\n\treadonly deleteRow: string;\n\treadonly deleteColumn: string;\n\treadonly borderColorLabel: string;\n\treadonly deleteTable: string;\n\n\t// Menu A11y\n\treadonly tableActions: string;\n\treadonly menuKeyboardHint: string;\n\n\t// Controls\n\treadonly insertRow: string;\n\treadonly insertColumn: string;\n\treadonly addRow: string;\n\treadonly addColumn: string;\n\treadonly tableActionsHint: string;\n\treadonly contextMenuHint: string;\n\treadonly borderColor: string;\n\n\t// Sizing\n\treadonly sizeLabel: string;\n\treadonly sizeDialogLabel: string;\n\treadonly columnWidthLabel: string;\n\treadonly rowMinimumHeightLabel: string;\n\treadonly automatic: string;\n\treadonly mixed: string;\n\treadonly apply: string;\n\treadonly cancel: string;\n\treadonly resetColumnWidth: string;\n\treadonly resetRowMinimumHeight: string;\n\treadonly resetAllSizes: string;\n\treadonly invalidDimensionRange: (minimum: number, maximum: number) => string;\n\t/** Receives a zero-based logical row index and renders it as a one-based label. */\n\treadonly selectRowLabel: (rowIndex: number) => string;\n\t/** Receives a zero-based logical column index and renders it as a one-based label. */\n\treadonly selectColumnLabel: (columnIndex: number) => string;\n\t/** Receives a zero-based logical column index and renders it as a one-based label. */\n\treadonly resizeColumnSeparatorLabel: (columnIndex: number) => string;\n\t/** Receives a zero-based logical row index and renders it as a one-based label. */\n\treadonly resizeRowSeparatorLabel: (rowIndex: number) => string;\n\treadonly resizeKeyboardHint: (step: number, largeStep: number) => string;\n\t/** Receives a zero-based logical column index and announces it as one-based. */\n\treadonly announceColumnWidthSet: (columnIndex: number, valuePx: number) => string;\n\t/** Receives a zero-based logical row index and announces it as one-based. */\n\treadonly announceRowMinimumHeightSet: (rowIndex: number, valuePx: number) => string;\n\t/** Receives a zero-based logical column index and announces it as one-based. */\n\treadonly announceColumnWidthReset: (columnIndex: number) => string;\n\t/** Receives a zero-based logical row index and announces it as one-based. */\n\treadonly announceRowMinimumHeightReset: (rowIndex: number) => string;\n\treadonly announceTableSizesReset: string;\n\n\t// Border Color Picker\n\treadonly defaultColor: string;\n\treadonly noBorders: string;\n\treadonly borderColorPicker: string;\n\treadonly announceBorderColorSet: (colorName: string) => string;\n\treadonly announceBorderReset: string;\n\treadonly borderSwatchLabel: (colorName: string) => string;\n\n\t// Command Announcements\n\treadonly announceRowInsertedAbove: string;\n\treadonly announceRowInsertedBelow: string;\n\treadonly announceColumnInserted: (side: string) => string;\n\treadonly announceRowDeleted: string;\n\treadonly announceColumnDeleted: string;\n\treadonly announceTableDeleted: string;\n\n\t// Toolbar\n\treadonly insertTable: string;\n\n\t// NodeView ARIA\n\treadonly tableAriaLabel: (rows: number, cols: number) => string;\n\treadonly tableAriaDescription: string;\n}\n\n// --- Default English Locale ---\n\nexport const TABLE_LOCALE_EN: TableLocale = {\n\t// Context Menu\n\tinsertRowAbove: 'Insert Row Above',\n\tinsertRowBelow: 'Insert Row Below',\n\tinsertColumnLeft: 'Insert Column Left',\n\tinsertColumnRight: 'Insert Column Right',\n\tdeleteRow: 'Delete Row',\n\tdeleteColumn: 'Delete Column',\n\tborderColorLabel: 'Border Color...',\n\tdeleteTable: 'Delete Table',\n\n\t// Menu A11y\n\ttableActions: 'Table actions',\n\tmenuKeyboardHint: '\\u2191\\u2193 Navigate \\u00b7 Enter Select \\u00b7 Esc Close',\n\n\t// Controls\n\tinsertRow: 'Insert row',\n\tinsertColumn: 'Insert column',\n\taddRow: 'Add row',\n\taddColumn: 'Add column',\n\ttableActionsHint: 'Table actions (Right-click or Shift+F10)',\n\tcontextMenuHint: 'Right-click or Shift+F10 for table actions',\n\tborderColor: 'Border color',\n\n\t// Sizing\n\tsizeLabel: 'Size...',\n\tsizeDialogLabel: 'Table size',\n\tcolumnWidthLabel: 'Column width (px)',\n\trowMinimumHeightLabel: 'Row minimum height (px)',\n\tautomatic: 'Automatic',\n\tmixed: 'Mixed',\n\tapply: 'Apply',\n\tcancel: 'Cancel',\n\tresetColumnWidth: 'Reset column width',\n\tresetRowMinimumHeight: 'Reset row minimum height',\n\tresetAllSizes: 'Reset all sizes',\n\tinvalidDimensionRange: (minimum: number, maximum: number) =>\n\t\t`Enter a value from ${minimum} to ${maximum} px.`,\n\tselectRowLabel: (rowIndex: number) => `Select row ${rowIndex + 1}`,\n\tselectColumnLabel: (columnIndex: number) => `Select column ${columnIndex + 1}`,\n\tresizeColumnSeparatorLabel: (columnIndex: number) => `Resize column ${columnIndex + 1}`,\n\tresizeRowSeparatorLabel: (rowIndex: number) => `Resize row ${rowIndex + 1}`,\n\tresizeKeyboardHint: (step: number, largeStep: number) =>\n\t\t`Arrow keys resize by ${step} px; hold Shift for ${largeStep} px.`,\n\tannounceColumnWidthSet: (columnIndex: number, valuePx: number) =>\n\t\t`Column ${columnIndex + 1} width set to ${valuePx} px.`,\n\tannounceRowMinimumHeightSet: (rowIndex: number, valuePx: number) =>\n\t\t`Row ${rowIndex + 1} minimum height set to ${valuePx} px.`,\n\tannounceColumnWidthReset: (columnIndex: number) =>\n\t\t`Column ${columnIndex + 1} width reset to automatic.`,\n\tannounceRowMinimumHeightReset: (rowIndex: number) =>\n\t\t`Row ${rowIndex + 1} minimum height reset to automatic.`,\n\tannounceTableSizesReset: 'Table column widths and row minimum heights reset to automatic.',\n\n\t// Border Color Picker\n\tdefaultColor: 'Default',\n\tnoBorders: 'No borders',\n\tborderColorPicker: 'Border color picker',\n\tannounceBorderColorSet: (colorName: string) => `Table border color set to ${colorName}`,\n\tannounceBorderReset: 'Table borders reset to default',\n\tborderSwatchLabel: (colorName: string) => `Border ${colorName}`,\n\n\t// Command Announcements\n\tannounceRowInsertedAbove: 'Row inserted above',\n\tannounceRowInsertedBelow: 'Row inserted below',\n\tannounceColumnInserted: (side: string) => `Column inserted ${side}`,\n\tannounceRowDeleted: 'Row deleted',\n\tannounceColumnDeleted: 'Column deleted',\n\tannounceTableDeleted: 'Table deleted',\n\n\t// Toolbar\n\tinsertTable: 'Insert Table',\n\n\t// NodeView ARIA\n\ttableAriaLabel: (rows: number, cols: number) => `Table with ${rows} rows and ${cols} columns`,\n\ttableAriaDescription: 'Right-click or press Shift+F10 for table actions',\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: LocaleModuleMap<TableLocale> = import.meta.glob<{\n\tdefault: TableLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadTableLocale(lang: string): Promise<TableLocale> {\n\treturn loadLocaleModule(localeModules, lang, TABLE_LOCALE_EN);\n}\n","/**\n * Border color commands, transaction builder, and accessible color picker\n * for table borders. Supports theme-default, custom hex, and borderless modes.\n * Delegates grid rendering to the shared ColorGrid module.\n */\n\nimport type { BlockAttrs } from '../../model/Document.js';\nimport { isTextSelection } 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 { PluginContext } from '../Plugin.js';\nimport { renderColorGrid } from '../shared/ColorGrid.js';\nimport { getColorName } from '../shared/ColorNames.js';\nimport { findTableContext } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\n\n// --- Border Color Palette ---\n\n/** Subdued, border-appropriate colors: grays, earth tones, muted hues. */\nexport const BORDER_COLOR_PALETTE: readonly string[] = [\n\t'#000000',\n\t'#434343',\n\t'#666666',\n\t'#999999',\n\t'#b7b7b7',\n\t'#cccccc',\n\t'#d9d9d9',\n\t'#efefef',\n\t'#980000',\n\t'#cc0000',\n\t'#e69138',\n\t'#f1c232',\n\t'#6aa84f',\n\t'#45818e',\n\t'#3c78d8',\n\t'#3d85c6',\n\t'#674ea7',\n\t'#a64d79',\n\t'#0b5394',\n\t'#38761d',\n];\n\nconst COLUMNS = 5;\n\n// --- Transaction Builder ---\n\n/**\n * Builds a transaction that sets `borderColor` on a table node.\n * Preserves existing attributes (spread pattern).\n */\nexport function buildSetBorderColorTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tborderColor: string | undefined,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\n\tconst path = state.getNodePath(tableId);\n\tif (!path) return null;\n\n\tif (borderColor === undefined) {\n\t\t// Remove borderColor from attrs, keep everything else\n\t\tconst { borderColor: _, ...rest } = (table.attrs ?? {}) as Record<\n\t\t\tstring,\n\t\t\tstring | number | boolean\n\t\t>;\n\t\tconst cleaned: BlockAttrs = rest;\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setNodeAttr(path, cleaned)\n\t\t\t.setSelection(state.selection)\n\t\t\t.build();\n\t}\n\n\tconst newAttrs: BlockAttrs = { ...table.attrs, borderColor };\n\treturn state\n\t\t.transaction('command')\n\t\t.setNodeAttr(path, newAttrs)\n\t\t.setSelection(state.selection)\n\t\t.build();\n}\n\n// --- Commands ---\n\n/** Sets the border color on the table surrounding the cursor. */\nexport function setTableBorderColor(\n\tcontext: PluginContext,\n\tcolor: string,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildSetBorderColorTransaction(state, tableCtx.tableId, color);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tconst colorName: string = color === 'none' ? 'none' : getColorName(color);\n\tcontext.announce(locale.announceBorderColorSet(colorName));\n\treturn true;\n}\n\n/** Resets the border color to theme default. */\nexport function resetTableBorderColor(\n\tcontext: PluginContext,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildSetBorderColorTransaction(state, tableCtx.tableId, undefined);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceBorderReset);\n\treturn true;\n}\n\n/** Reads the current border color from a table node. */\nexport function getTableBorderColor(state: EditorState, tableId: BlockId): string | undefined {\n\tconst table = state.getBlock(tableId);\n\tif (!table) return undefined;\n\treturn table.attrs?.borderColor as string | undefined;\n}\n\n// --- Border Color Picker ---\n\n/**\n * Renders an accessible border color picker into a container element.\n * Includes \"Default\", \"No borders\", and a WAI-ARIA grid of color swatches.\n */\nexport function renderBorderColorPicker(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\tonClose: () => void,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): void {\n\tcontainer.classList.add('notectl-color-picker');\n\n\tconst currentColor: string | undefined = getTableBorderColor(context.getState(), tableId);\n\n\t// --- Default button ---\n\tconst defaultBtn: HTMLButtonElement = document.createElement('button');\n\tdefaultBtn.type = 'button';\n\tdefaultBtn.className = 'notectl-color-picker__default';\n\tdefaultBtn.textContent = locale.defaultColor;\n\tdefaultBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tresetTableBorderColor(context, locale);\n\t\tonClose();\n\t});\n\tdefaultBtn.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tonClose();\n\t\t}\n\t});\n\tcontainer.appendChild(defaultBtn);\n\n\t// --- No borders button ---\n\tconst noBordersBtn: HTMLButtonElement = document.createElement('button');\n\tnoBordersBtn.type = 'button';\n\tnoBordersBtn.className = 'notectl-color-picker__default';\n\tnoBordersBtn.textContent = locale.noBorders;\n\tnoBordersBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tsetTableBorderColor(context, 'none', locale);\n\t\tonClose();\n\t});\n\tnoBordersBtn.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tonClose();\n\t\t}\n\t});\n\tcontainer.appendChild(noBordersBtn);\n\n\t// --- Color grid (delegated to shared ColorGrid) ---\n\trenderColorGrid(container, {\n\t\tcolors: BORDER_COLOR_PALETTE,\n\t\tcolumns: COLUMNS,\n\t\tariaLabel: locale.borderColorPicker,\n\t\tariaLabelPrefix: '',\n\t\tactiveColor: currentColor ?? null,\n\t\tswatchLabel: locale.borderSwatchLabel,\n\t\ttitleAsName: true,\n\t\tonSelect: (color: string) => {\n\t\t\tsetTableBorderColor(context, color, locale);\n\t\t\tonClose();\n\t\t},\n\t\tonClose,\n\t});\n}\n","/** Clipboard integration for the plugin's logical rectangular cell selection. */\n\nimport {\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n\tgetBlockChildren,\n\tgetBlockText,\n\tisLeafBlock,\n} from '../../model/Document.js';\nimport { createCollapsedSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport { isEventFromEditorContent } from '../../platform/EditorEventBoundary.js';\nimport { serializeDocumentToHTML } from '../../serialization/DocumentSerializer.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { TransactionBuilder } from '../../state/Transaction.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { createTableGrid, sliceTableToRange } from './TableGrid.js';\nimport type { CellRange, TableSelectionService } from './TableSelection.js';\n\n/** Installs copy/cut handling for TableSelectionService ranges. */\nexport function installTableClipboard(\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): () => void {\n\tconst container: HTMLElement = context.getContainer();\n\n\tconst onCopy = (event: ClipboardEvent): void => {\n\t\tif (!isEventFromEditorContent(event, container)) return;\n\t\twriteSelectedTableRange(event, context, selectionService);\n\t};\n\tconst onCut = (event: ClipboardEvent): void => {\n\t\tif (!isEventFromEditorContent(event, container)) return;\n\t\tif (context.isReadOnly?.()) return;\n\t\tif (!writeSelectedTableRange(event, context, selectionService)) return;\n\t\tclearSelectedTableCells(context, selectionService);\n\t};\n\n\t// Capture wins over the generic text clipboard handler on the same editor root.\n\tcontainer.addEventListener('copy', onCopy, true);\n\tcontainer.addEventListener('cut', onCut, true);\n\treturn () => {\n\t\tcontainer.removeEventListener('copy', onCopy, true);\n\t\tcontainer.removeEventListener('cut', onCut, true);\n\t};\n}\n\nfunction writeSelectedTableRange(\n\tevent: ClipboardEvent,\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): boolean {\n\tconst clipboardData: DataTransfer | null = event.clipboardData;\n\tconst range: CellRange | null = selectionService.getSelectedRange();\n\tif (!clipboardData || !range) return false;\n\tconst state: EditorState = context.getState();\n\tconst table: BlockNode | undefined = state.getBlock(range.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst slice: BlockNode | null = sliceTableToRange(table, {\n\t\tfromRow: range.fromRow,\n\t\tfromColumn: range.fromCol,\n\t\ttoRow: range.toRow,\n\t\ttoColumn: range.toCol,\n\t});\n\tif (!slice) return false;\n\n\tevent.preventDefault();\n\tevent.stopImmediatePropagation();\n\tclipboardData.setData(\n\t\t'text/html',\n\t\tserializeDocumentToHTML({ children: [slice] }, context.getSchemaRegistry()),\n\t);\n\tclipboardData.setData('text/plain', tableFragmentPlainText(slice));\n\treturn true;\n}\n\n/** Cut clears each selected logical cell once and keeps table structure/dimensions intact. */\nfunction clearSelectedTableCells(\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): void {\n\tconst range: CellRange | null = selectionService.getSelectedRange();\n\tif (!range) return;\n\tconst state: EditorState = context.getState();\n\tconst table: BlockNode | undefined = state.getBlock(range.tableId);\n\tif (!table || table.type !== 'table') return;\n\tconst cells = createTableGrid(table).cellsInRange({\n\t\tfromRow: range.fromRow,\n\t\tfromColumn: range.fromCol,\n\t\ttoRow: range.toRow,\n\t\ttoColumn: range.toCol,\n\t});\n\tif (cells.length === 0) return;\n\n\tconst builder: TransactionBuilder = state.transaction('input');\n\tlet firstParagraphId: BlockId | null = null;\n\tfor (const entry of cells) {\n\t\tconst path: readonly BlockId[] | undefined = state.getNodePath(entry.cell.id);\n\t\tif (!path) return;\n\t\tconst children: readonly BlockNode[] = getBlockChildren(entry.cell);\n\t\tfor (let index = children.length - 1; index >= 0; index--) builder.removeNode(path, index);\n\t\tconst paragraphId: BlockId = generateBlockId();\n\t\tconst paragraph: BlockNode = createBlockNode(\n\t\t\tnodeType('paragraph'),\n\t\t\t[createTextNode('')],\n\t\t\tparagraphId,\n\t\t);\n\t\tbuilder.insertNode(path, 0, paragraph);\n\t\tfirstParagraphId ??= paragraphId;\n\t}\n\tif (!firstParagraphId) return;\n\tbuilder.setSelection(createCollapsedSelection(firstParagraphId, 0));\n\tcontext.dispatch(builder.build());\n\tselectionService.clearSelectionSilent();\n}\n\n/** Serializes a table fragment as logical TSV, including empty span placeholders. */\nexport function tableFragmentPlainText(table: BlockNode): string {\n\tconst grid = createTableGrid(table);\n\treturn Array.from({ length: grid.rowCount }, (_unused: unknown, row: number): string => {\n\t\treturn Array.from({ length: grid.columnCount }, (_column: unknown, column: number): string => {\n\t\t\tconst entry = grid.cellAt(row, column);\n\t\t\tif (!entry || entry.rowStart !== row || entry.columnStart !== column) return '';\n\t\t\treturn blockTreePlainText(entry.cell);\n\t\t}).join('\\t');\n\t}).join('\\n');\n}\n\nfunction blockTreePlainText(block: BlockNode): string {\n\tif (isLeafBlock(block)) return getBlockText(block);\n\treturn getBlockChildren(block).map(blockTreePlainText).join('\\n');\n}\n","/**\n * Multi-cell selection service for tables.\n * Tracks selected cell range and provides IDs for bulk formatting.\n */\n\nimport { createCollapsedSelection, isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { isEventFromEditorContent } from '../../platform/EditorEventBoundary.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport { ServiceKey } from '../Plugin.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { type TableGrid, createTableGrid, expandTableGridRange } from './TableGrid.js';\nimport { findTableContext, getFirstLeafInCell } from './TableHelpers.js';\n\n/** Rectangular range of cells within a table. */\nexport interface CellRange {\n\treadonly tableId: BlockId;\n\treadonly fromRow: number;\n\treadonly fromCol: number;\n\treadonly toRow: number;\n\treadonly toCol: number;\n}\n\n/** Service for managing multi-cell selection. */\nexport interface TableSelectionService {\n\tgetSelectedRange(): CellRange | null;\n\tsetSelectedRange(range: CellRange | null): void;\n\t/** Clears selection state without dispatching a transaction (for use in decorations). */\n\tclearSelectionSilent(): void;\n\tgetSelectedCellIds(): readonly BlockId[];\n\tisSelected(cellId: BlockId): boolean;\n}\n\nexport const TableSelectionServiceKey = new ServiceKey<TableSelectionService>('tableSelection');\n\n/** Creates and registers the TableSelectionService. */\nexport function createTableSelectionService(context: PluginContext): TableSelectionService {\n\tlet selectedRange: CellRange | null = null;\n\tlet selectedStructure: string | null = null;\n\tlet cachedCellIds: readonly BlockId[] = [];\n\tlet cachedCellIdSet: Set<BlockId> = new Set();\n\n\tfunction updateCache(): void {\n\t\tconst current = resolveCurrentSelection();\n\t\tif (!current) {\n\t\t\tcachedCellIds = [];\n\t\t\tcachedCellIdSet = new Set();\n\t\t\treturn;\n\t\t}\n\n\t\tconst { grid, range } = current;\n\n\t\tconst ids: BlockId[] = grid\n\t\t\t.cellsInRange({\n\t\t\t\tfromRow: range.fromRow,\n\t\t\t\tfromColumn: range.fromCol,\n\t\t\t\ttoRow: range.toRow,\n\t\t\t\ttoColumn: range.toCol,\n\t\t\t})\n\t\t\t.map((entry) => entry.cell.id);\n\n\t\tcachedCellIds = ids;\n\t\tcachedCellIdSet = new Set(ids);\n\t}\n\n\tfunction resolveCurrentSelection(): {\n\t\treadonly range: CellRange;\n\t\treadonly grid: TableGrid;\n\t} | null {\n\t\tif (!selectedRange || !selectedStructure) return null;\n\t\tconst table = context.getState().getBlock(selectedRange.tableId);\n\t\tif (!table || table.type !== 'table') {\n\t\t\tclearInternal();\n\t\t\treturn null;\n\t\t}\n\t\tconst grid: TableGrid = createTableGrid(table);\n\t\tif (tableStructureSignature(grid) !== selectedStructure) {\n\t\t\tclearInternal();\n\t\t\treturn null;\n\t\t}\n\t\treturn { range: selectedRange, grid };\n\t}\n\n\tfunction clearInternal(): void {\n\t\tselectedRange = null;\n\t\tselectedStructure = null;\n\t\tcachedCellIds = [];\n\t\tcachedCellIdSet = new Set();\n\t}\n\n\tconst service: TableSelectionService = {\n\t\tgetSelectedRange(): CellRange | null {\n\t\t\treturn resolveCurrentSelection()?.range ?? null;\n\t\t},\n\n\t\tsetSelectedRange(range: CellRange | null): void {\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tlet focusCellId: BlockId | null = null;\n\t\t\tif (!range) {\n\t\t\t\tclearInternal();\n\t\t\t} else {\n\t\t\t\tconst table = context.getState().getBlock(range.tableId);\n\t\t\t\tconst grid: TableGrid | null = table?.type === 'table' ? createTableGrid(table) : null;\n\t\t\t\tconst coordinates: readonly number[] = [\n\t\t\t\t\trange.fromRow,\n\t\t\t\t\trange.fromCol,\n\t\t\t\t\trange.toRow,\n\t\t\t\t\trange.toCol,\n\t\t\t\t];\n\t\t\t\tconst valid: boolean =\n\t\t\t\t\t!!grid &&\n\t\t\t\t\tcoordinates.every(Number.isInteger) &&\n\t\t\t\t\trange.fromRow >= 0 &&\n\t\t\t\t\trange.toRow >= 0 &&\n\t\t\t\t\trange.fromCol >= 0 &&\n\t\t\t\t\trange.toCol >= 0 &&\n\t\t\t\t\trange.fromRow < grid.rowCount &&\n\t\t\t\t\trange.toRow < grid.rowCount &&\n\t\t\t\t\trange.fromCol < grid.columnCount &&\n\t\t\t\t\trange.toCol < grid.columnCount;\n\t\t\t\tconst expanded =\n\t\t\t\t\tvalid && grid\n\t\t\t\t\t\t? expandTableGridRange(grid, {\n\t\t\t\t\t\t\t\tfromRow: range.fromRow,\n\t\t\t\t\t\t\t\tfromColumn: range.fromCol,\n\t\t\t\t\t\t\t\ttoRow: range.toRow,\n\t\t\t\t\t\t\t\ttoColumn: range.toCol,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: null;\n\t\t\t\tif (!grid || !expanded) {\n\t\t\t\t\tclearInternal();\n\t\t\t\t} else {\n\t\t\t\t\tselectedRange = {\n\t\t\t\t\t\ttableId: range.tableId,\n\t\t\t\t\t\tfromRow: expanded.fromRow,\n\t\t\t\t\t\tfromCol: expanded.fromColumn,\n\t\t\t\t\t\ttoRow: expanded.toRow,\n\t\t\t\t\t\ttoCol: expanded.toColumn,\n\t\t\t\t\t};\n\t\t\t\t\tselectedStructure = tableStructureSignature(grid);\n\t\t\t\t\tfocusCellId = grid.cellAt(expanded.fromRow, expanded.fromColumn)?.cell.id ?? null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdateCache();\n\t\t\tconst currentTable = isTextSelection(state.selection)\n\t\t\t\t? findTableContext(state, state.selection.anchor.blockId)\n\t\t\t\t: null;\n\t\t\tconst selection =\n\t\t\t\tfocusCellId && currentTable?.tableId !== range?.tableId\n\t\t\t\t\t? createCollapsedSelection(getFirstLeafInCell(state, focusCellId), 0)\n\t\t\t\t\t: state.selection;\n\t\t\tcontext.dispatch(state.transaction('api').setSelection(selection).build());\n\t\t},\n\n\t\tclearSelectionSilent(): void {\n\t\t\tclearInternal();\n\t\t},\n\n\t\tgetSelectedCellIds(): readonly BlockId[] {\n\t\t\tupdateCache();\n\t\t\treturn cachedCellIds;\n\t\t},\n\n\t\tisSelected(cellId: BlockId): boolean {\n\t\t\tupdateCache();\n\t\t\treturn cachedCellIdSet.has(cellId);\n\t\t},\n\t};\n\n\tcontext.registerService(TableSelectionServiceKey, service);\n\treturn service;\n}\n\nfunction tableStructureSignature(grid: TableGrid): string {\n\treturn JSON.stringify({\n\t\trows: grid.rows.map((row) => row.id),\n\t\tcells: grid.cells.map((entry) => [\n\t\t\tentry.cell.id,\n\t\t\tentry.rowStart,\n\t\t\tentry.rowEnd,\n\t\t\tentry.columnStart,\n\t\t\tentry.columnEnd,\n\t\t]),\n\t});\n}\n\n/**\n * Installs mouse handlers for multi-cell selection on the editor container.\n * Returns a cleanup function.\n */\nexport function installMouseSelection(\n\tcontext: PluginContext,\n\tservice: TableSelectionService,\n): () => void {\n\tconst container: HTMLElement = context.getContainer();\n\tlet anchorCell: {\n\t\ttableId: BlockId;\n\t\tcellId: BlockId;\n\t\trowStart: number;\n\t\trowEnd: number;\n\t\tcolStart: number;\n\t\tcolEnd: number;\n\t} | null = null;\n\tlet isDragging = false;\n\n\tfunction handleMouseDown(e: MouseEvent): void {\n\t\tif (e.button !== 0 || context.isReadOnly?.()) return;\n\t\tif (!isEventFromEditorContent(e, container)) return;\n\t\tconst target = e.target as HTMLElement;\n\t\tconst cellEl: HTMLElement | null = target.closest('td[data-block-id]');\n\t\tif (!cellEl) {\n\t\t\tanchorCell = null;\n\t\t\tisDragging = false;\n\t\t\tservice.setSelectedRange(null);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!e.shiftKey) {\n\t\t\t// Clear any existing multi-cell selection before starting a new anchor\n\t\t\tservice.setSelectedRange(null);\n\t\t\t// Start new selection anchor\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\t\tconst tableCtx = findTableContext(state, cellId);\n\t\t\tif (!tableCtx) {\n\t\t\t\tanchorCell = null;\n\t\t\t\tisDragging = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tanchorCell = {\n\t\t\t\ttableId: tableCtx.tableId,\n\t\t\t\tcellId: tableCtx.cellId,\n\t\t\t\trowStart: tableCtx.rowIndex,\n\t\t\t\trowEnd: tableCtx.rowEnd,\n\t\t\t\tcolStart: tableCtx.colIndex,\n\t\t\t\tcolEnd: tableCtx.colEnd,\n\t\t\t};\n\t\t\tisDragging = true;\n\t\t\t// Don't set range yet — wait for mousemove to avoid interfering with clicks\n\t\t} else if (anchorCell) {\n\t\t\t// Shift-click: extend selection\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\t\tconst tableCtx = findTableContext(state, cellId);\n\t\t\tif (!tableCtx || tableCtx.tableId !== anchorCell.tableId) {\n\t\t\t\tanchorCell = null;\n\t\t\t\tservice.setSelectedRange(null);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\te.preventDefault();\n\t\t\tservice.setSelectedRange(rangeCoveringCells(anchorCell, tableCtx));\n\t\t}\n\t}\n\n\tfunction handleMouseMove(e: MouseEvent): void {\n\t\tif (context.isReadOnly?.() || !isDragging || !anchorCell) return;\n\n\t\tconst target = e.target as HTMLElement;\n\t\tconst cellEl: HTMLElement | null = target.closest('td[data-block-id]');\n\t\tif (!cellEl) return;\n\n\t\tconst state: EditorState = context.getState();\n\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\tconst tableCtx = findTableContext(state, cellId);\n\t\tif (!tableCtx || tableCtx.tableId !== anchorCell.tableId) return;\n\n\t\t// Only set range if we've moved to a different cell\n\t\tif (tableCtx.cellId !== anchorCell.cellId) {\n\t\t\te.preventDefault();\n\t\t\tservice.setSelectedRange(rangeCoveringCells(anchorCell, tableCtx));\n\t\t}\n\t}\n\n\tfunction handleMouseUp(): void {\n\t\tisDragging = false;\n\t}\n\n\tcontainer.addEventListener('mousedown', handleMouseDown);\n\tcontainer.addEventListener('mousemove', handleMouseMove);\n\tdocument.addEventListener('mouseup', handleMouseUp);\n\n\treturn () => {\n\t\tcontainer.removeEventListener('mousedown', handleMouseDown);\n\t\tcontainer.removeEventListener('mousemove', handleMouseMove);\n\t\tdocument.removeEventListener('mouseup', handleMouseUp);\n\t};\n}\n\nfunction rangeCoveringCells(\n\tanchor: {\n\t\treadonly tableId: BlockId;\n\t\treadonly rowStart: number;\n\t\treadonly rowEnd: number;\n\t\treadonly colStart: number;\n\t\treadonly colEnd: number;\n\t},\n\ttarget: {\n\t\treadonly rowIndex: number;\n\t\treadonly rowEnd: number;\n\t\treadonly colIndex: number;\n\t\treadonly colEnd: number;\n\t},\n): CellRange {\n\treturn {\n\t\ttableId: anchor.tableId,\n\t\tfromRow: Math.min(anchor.rowStart, target.rowIndex),\n\t\tfromCol: Math.min(anchor.colStart, target.colIndex),\n\t\ttoRow: Math.max(anchor.rowEnd, target.rowEnd) - 1,\n\t\ttoCol: Math.max(anchor.colEnd, target.colEnd) - 1,\n\t};\n}\n","/**\n * Persistent table sizing model and public service.\n *\n * Column widths belong to the table's logical-column vector; row minimum\n * heights belong to table_row nodes. Cell and range targets are resolved\n * through TableGrid so colspan/rowspan never leak child-array coordinates into\n * the public API.\n */\n\nimport type { BlockAttrValue, BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { isTextSelection } from '../../model/Selection.js';\nimport {\n\tMAX_TABLE_DIMENSION_PX,\n\tMIN_TABLE_DIMENSION_PX,\n\tnormalizeTableDimensionPx,\n} from '../../model/TableDimensions.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { TransactionBuilder } from '../../state/Transaction.js';\nimport { type PluginContext, ServiceKey } from '../Plugin.js';\nimport { type TableGrid, createTableGrid, expandTableGridRange } from './TableGrid.js';\nimport { findTableContext } from './TableHelpers.js';\nimport {\n\ttype CellRange,\n\ttype TableSelectionService,\n\tTableSelectionServiceKey,\n} from './TableSelection.js';\n\n// --- Configuration ---\n\nexport interface TableSizingConfig {\n\treadonly minColumnWidthPx: number;\n\treadonly minRowHeightPx: number;\n\treadonly maxColumnWidthPx: number;\n\treadonly maxRowHeightPx: number;\n\treadonly keyboardResizeStepPx: number;\n\treadonly keyboardResizeLargeStepPx: number;\n}\n\nexport const DEFAULT_TABLE_SIZING_CONFIG: Readonly<TableSizingConfig> = Object.freeze({\n\tminColumnWidthPx: 60,\n\tminRowHeightPx: 24,\n\tmaxColumnWidthPx: MAX_TABLE_DIMENSION_PX,\n\tmaxRowHeightPx: MAX_TABLE_DIMENSION_PX,\n\tkeyboardResizeStepPx: 8,\n\tkeyboardResizeLargeStepPx: 32,\n});\n\n// --- Public API ---\n\nexport type TableDimensionInput = number | 'auto';\nexport type TableDimensionState = number | 'auto' | 'mixed' | 'unavailable';\nexport type TableSizeDimension = 'columnWidthPx' | 'rowMinHeightPx';\n\nexport interface TableSizeInput {\n\treadonly columnWidthPx?: TableDimensionInput;\n\treadonly rowMinHeightPx?: TableDimensionInput;\n}\n\nexport interface TableSizeState {\n\treadonly columnWidthPx: TableDimensionState;\n\treadonly rowMinHeightPx: TableDimensionState;\n}\n\nexport interface TableCellSizeTarget {\n\treadonly kind: 'cell';\n\treadonly tableId: BlockId;\n\treadonly row: number;\n\treadonly column: number;\n}\n\nexport interface TableRangeSizeTarget {\n\treadonly kind: 'range';\n\treadonly tableId: BlockId;\n\treadonly fromRow: number;\n\treadonly fromColumn: number;\n\treadonly toRow: number;\n\treadonly toColumn: number;\n}\n\nexport interface TableColumnSizeTarget {\n\treadonly kind: 'column';\n\treadonly tableId: BlockId;\n\treadonly column: number;\n}\n\nexport interface TableRowSizeTarget {\n\treadonly kind: 'row';\n\treadonly tableId: BlockId;\n\treadonly row: number;\n}\n\nexport type TableSizeTarget =\n\t| TableCellSizeTarget\n\t| TableRangeSizeTarget\n\t| TableColumnSizeTarget\n\t| TableRowSizeTarget;\n\nexport interface TableSizingService {\n\tgetSelectionSize(): TableSizeState | null;\n\tsetSelectionSize(input: TableSizeInput): boolean;\n\tresetSelectionSize(dimension?: TableSizeDimension): boolean;\n\tgetSize(target: TableSizeTarget): TableSizeState | null;\n\tsetSize(target: TableSizeTarget, input: TableSizeInput): boolean;\n\tresetSize(target: TableSizeTarget, dimension?: TableSizeDimension): boolean;\n}\n\nexport const TableSizingServiceKey = new ServiceKey<TableSizingService>('tableSizing');\n\n/** Creates and registers the strongly typed table sizing service. */\nexport function createTableSizingService(\n\tcontext: PluginContext,\n\tconfig: Partial<TableSizingConfig> = {},\n): TableSizingService {\n\tconst resolvedConfig: TableSizingConfig = resolveTableSizingConfig(config);\n\n\tconst service: TableSizingService = {\n\t\tgetSelectionSize(): TableSizeState | null {\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? readResolvedSize(resolved) : null;\n\t\t},\n\n\t\tsetSelectionSize(input: TableSizeInput): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? applySize(context, resolved, input, resolvedConfig) : false;\n\t\t},\n\n\t\tresetSelectionSize(dimension?: TableSizeDimension): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? resetResolvedSize(context, resolved, dimension, resolvedConfig) : false;\n\t\t},\n\n\t\tgetSize(target: TableSizeTarget): TableSizeState | null {\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? readResolvedSize(resolved) : null;\n\t\t},\n\n\t\tsetSize(target: TableSizeTarget, input: TableSizeInput): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? applySize(context, resolved, input, resolvedConfig) : false;\n\t\t},\n\n\t\tresetSize(target: TableSizeTarget, dimension?: TableSizeDimension): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? resetResolvedSize(context, resolved, dimension, resolvedConfig) : false;\n\t\t},\n\t};\n\n\tcontext.registerService(TableSizingServiceKey, service);\n\treturn service;\n}\n\n/** Resolves and clamps every sizing configuration value into safe global bounds. */\nexport function resolveTableSizingConfig(config: Partial<TableSizingConfig>): TableSizingConfig {\n\tconst minColumnWidthPx: number = normalizeConfigValue(\n\t\tconfig.minColumnWidthPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.minColumnWidthPx,\n\t);\n\tconst minRowHeightPx: number = normalizeConfigValue(\n\t\tconfig.minRowHeightPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.minRowHeightPx,\n\t);\n\tconst maxColumnWidthPx: number = normalizeConfigValue(\n\t\tconfig.maxColumnWidthPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.maxColumnWidthPx,\n\t\tminColumnWidthPx,\n\t);\n\tconst maxRowHeightPx: number = normalizeConfigValue(\n\t\tconfig.maxRowHeightPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.maxRowHeightPx,\n\t\tminRowHeightPx,\n\t);\n\n\treturn {\n\t\tminColumnWidthPx,\n\t\tminRowHeightPx,\n\t\tmaxColumnWidthPx,\n\t\tmaxRowHeightPx,\n\t\tkeyboardResizeStepPx: normalizeConfigValue(\n\t\t\tconfig.keyboardResizeStepPx,\n\t\t\tDEFAULT_TABLE_SIZING_CONFIG.keyboardResizeStepPx,\n\t\t),\n\t\tkeyboardResizeLargeStepPx: normalizeConfigValue(\n\t\t\tconfig.keyboardResizeLargeStepPx,\n\t\t\tDEFAULT_TABLE_SIZING_CONFIG.keyboardResizeLargeStepPx,\n\t\t),\n\t};\n}\n\n// --- Canonical dimension attributes (also used by structural commands) ---\n\n/** Returns a fixed-length logical-width vector. Invalid/missing entries become automatic. */\nexport function readTableColumnWidthsPx(\n\ttable: BlockNode,\n\tcolumnCount: number,\n): readonly (number | null)[] {\n\tconst count: number = Math.max(0, Math.trunc(columnCount));\n\tconst raw: BlockAttrValue | undefined = table.attrs?.columnWidthsPx;\n\tconst values: readonly unknown[] = Array.isArray(raw) ? raw : [];\n\treturn Object.freeze(\n\t\tArray.from({ length: count }, (_unused: unknown, index: number): number | null => {\n\t\t\tconst value: unknown = values[index];\n\t\t\tif (value === null || value === undefined) return null;\n\t\t\treturn normalizeStoredDimension(value);\n\t\t}),\n\t);\n}\n\n/** Replaces the canonical column-width vector while preserving unrelated table attrs. */\nexport function withTableColumnWidthsPx(\n\tattrs: BlockAttrs | undefined,\n\twidths: readonly (number | null)[],\n): BlockAttrs {\n\tconst normalized: readonly (number | null)[] = Object.freeze(\n\t\twidths.map((value: number | null): number | null =>\n\t\t\tvalue === null ? null : normalizeStoredDimension(value),\n\t\t),\n\t);\n\tconst { columnWidthsPx: _columnWidthsPx, ...rest } = attrs ?? {};\n\tif (!normalized.some((value: number | null): boolean => value !== null)) return rest;\n\treturn { ...rest, columnWidthsPx: normalized };\n}\n\n/** Reads a row's canonical minimum height, or null for automatic layout. */\nexport function readTableRowMinHeightPx(row: BlockNode): number | null {\n\treturn normalizeStoredDimension(row.attrs?.minHeightPx);\n}\n\n/** Sets or removes a row's minimum-height attribute while preserving unrelated attrs. */\nexport function withTableRowMinHeightPx(\n\tattrs: BlockAttrs | undefined,\n\tminHeightPx: number | null,\n): BlockAttrs {\n\tconst { minHeightPx: _minHeightPx, ...rest } = attrs ?? {};\n\tif (minHeightPx === null) return rest;\n\tconst normalized: number | null = normalizeStoredDimension(minHeightPx);\n\treturn normalized === null ? rest : { ...rest, minHeightPx: normalized };\n}\n\n// --- Resolution ---\n\ninterface ResolvedTarget {\n\treadonly state: EditorState;\n\treadonly table: BlockNode;\n\treadonly grid: TableGrid;\n\treadonly columns: readonly number[];\n\treadonly rows: readonly number[];\n}\n\nfunction resolveSelectionTarget(context: PluginContext): ResolvedTarget | null {\n\tconst state: EditorState = context.getState();\n\tconst selectionService: TableSelectionService | undefined =\n\t\tcontext.getService(TableSelectionServiceKey);\n\tconst selectedRange: CellRange | null = selectionService?.getSelectedRange() ?? null;\n\tif (selectedRange) {\n\t\treturn resolveRange(state, selectedRange.tableId, selectedRange, true);\n\t}\n\n\tif (!isTextSelection(state.selection)) return null;\n\tconst tableContext = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableContext) return null;\n\treturn resolveRange(\n\t\tstate,\n\t\ttableContext.tableId,\n\t\t{\n\t\t\ttableId: tableContext.tableId,\n\t\t\tfromRow: tableContext.rowIndex,\n\t\t\tfromCol: tableContext.colIndex,\n\t\t\ttoRow: tableContext.rowEnd - 1,\n\t\t\ttoCol: tableContext.colEnd - 1,\n\t\t},\n\t\ttrue,\n\t);\n}\n\nfunction resolveExplicitTarget(state: EditorState, target: TableSizeTarget): ResolvedTarget | null {\n\tconst table: BlockNode | undefined = state.getBlock(target.tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\n\tswitch (target.kind) {\n\t\tcase 'cell': {\n\t\t\tif (!validCoordinate(target.row, grid.rowCount)) return null;\n\t\t\tif (!validCoordinate(target.column, grid.columnCount)) return null;\n\t\t\tconst cell = grid.cellAt(target.row, target.column);\n\t\t\tif (!cell) return null;\n\t\t\treturn makeResolvedTarget(\n\t\t\t\tstate,\n\t\t\t\ttable,\n\t\t\t\tgrid,\n\t\t\t\tcell.rowStart,\n\t\t\t\tcell.rowEnd - 1,\n\t\t\t\tcell.columnStart,\n\t\t\t\tcell.columnEnd - 1,\n\t\t\t);\n\t\t}\n\t\tcase 'range':\n\t\t\treturn resolveRange(\n\t\t\t\tstate,\n\t\t\t\ttarget.tableId,\n\t\t\t\t{\n\t\t\t\t\ttableId: target.tableId,\n\t\t\t\t\tfromRow: target.fromRow,\n\t\t\t\t\tfromCol: target.fromColumn,\n\t\t\t\t\ttoRow: target.toRow,\n\t\t\t\t\ttoCol: target.toColumn,\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t);\n\t\tcase 'column':\n\t\t\tif (!validCoordinate(target.column, grid.columnCount)) return null;\n\t\t\treturn { state, table, grid, columns: [target.column], rows: [] };\n\t\tcase 'row':\n\t\t\tif (!validCoordinate(target.row, grid.rowCount)) return null;\n\t\t\treturn { state, table, grid, columns: [], rows: [target.row] };\n\t}\n}\n\nfunction resolveRange(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trange: CellRange,\n\tstrict: boolean,\n): ResolvedTarget | null {\n\tconst table: BlockNode | undefined = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\n\tconst coordinates: readonly number[] = [range.fromRow, range.toRow, range.fromCol, range.toCol];\n\tif (!coordinates.every(Number.isInteger)) return null;\n\tif (\n\t\tstrict &&\n\t\t(!validCoordinate(range.fromRow, grid.rowCount) ||\n\t\t\t!validCoordinate(range.toRow, grid.rowCount) ||\n\t\t\t!validCoordinate(range.fromCol, grid.columnCount) ||\n\t\t\t!validCoordinate(range.toCol, grid.columnCount))\n\t) {\n\t\treturn null;\n\t}\n\n\tconst fromRow: number = clampIndex(Math.min(range.fromRow, range.toRow), grid.rowCount);\n\tconst toRow: number = clampIndex(Math.max(range.fromRow, range.toRow), grid.rowCount);\n\tconst fromColumn: number = clampIndex(Math.min(range.fromCol, range.toCol), grid.columnCount);\n\tconst toColumn: number = clampIndex(Math.max(range.fromCol, range.toCol), grid.columnCount);\n\tconst expanded = expandTableGridRange(grid, {\n\t\tfromRow,\n\t\tfromColumn,\n\t\ttoRow,\n\t\ttoColumn,\n\t});\n\treturn expanded\n\t\t? makeResolvedTarget(\n\t\t\t\tstate,\n\t\t\t\ttable,\n\t\t\t\tgrid,\n\t\t\t\texpanded.fromRow,\n\t\t\t\texpanded.toRow,\n\t\t\t\texpanded.fromColumn,\n\t\t\t\texpanded.toColumn,\n\t\t\t)\n\t\t: null;\n}\n\nfunction makeResolvedTarget(\n\tstate: EditorState,\n\ttable: BlockNode,\n\tgrid: TableGrid,\n\tfromRow: number,\n\ttoRow: number,\n\tfromColumn: number,\n\ttoColumn: number,\n): ResolvedTarget {\n\treturn {\n\t\tstate,\n\t\ttable,\n\t\tgrid,\n\t\trows: integerRange(fromRow, toRow),\n\t\tcolumns: integerRange(fromColumn, toColumn),\n\t};\n}\n\n// --- Reading and mutation ---\n\nfunction readResolvedSize(target: ResolvedTarget): TableSizeState {\n\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\ttarget.table,\n\t\ttarget.grid.columnCount,\n\t);\n\tconst columnValues: readonly (number | null)[] = target.columns.map(\n\t\t(column: number) => widths[column] ?? null,\n\t);\n\tconst rowValues: readonly (number | null)[] = target.rows.map((rowIndex: number) => {\n\t\tconst row: BlockNode | undefined = target.grid.rows[rowIndex];\n\t\treturn row ? readTableRowMinHeightPx(row) : null;\n\t});\n\n\treturn {\n\t\tcolumnWidthPx: summarizeValues(columnValues),\n\t\trowMinHeightPx: summarizeValues(rowValues),\n\t};\n}\n\nfunction applySize(\n\tcontext: PluginContext,\n\ttarget: ResolvedTarget,\n\tinput: TableSizeInput,\n\tconfig: TableSizingConfig,\n): boolean {\n\tconst hasColumnInput: boolean = Object.prototype.hasOwnProperty.call(input, 'columnWidthPx');\n\tconst hasRowInput: boolean = Object.prototype.hasOwnProperty.call(input, 'rowMinHeightPx');\n\tif (!hasColumnInput && !hasRowInput) return false;\n\tif (hasColumnInput && target.columns.length === 0) return false;\n\tif (hasRowInput && target.rows.length === 0) return false;\n\n\tconst columnValue: NormalizedInput | null = hasColumnInput\n\t\t? normalizeInput(input.columnWidthPx, config.minColumnWidthPx, config.maxColumnWidthPx)\n\t\t: null;\n\tconst rowValue: NormalizedInput | null = hasRowInput\n\t\t? normalizeInput(input.rowMinHeightPx, config.minRowHeightPx, config.maxRowHeightPx)\n\t\t: null;\n\tif ((hasColumnInput && !columnValue) || (hasRowInput && !rowValue)) return false;\n\n\tconst builder: TransactionBuilder = target.state.transaction('command');\n\tlet changed = false;\n\n\tif (columnValue) {\n\t\tconst widths: (number | null)[] = [\n\t\t\t...readTableColumnWidthsPx(target.table, target.grid.columnCount),\n\t\t];\n\t\tfor (const column of target.columns) {\n\t\t\tif (widths[column] !== columnValue.value) {\n\t\t\t\twidths[column] = columnValue.value;\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tconst path: readonly BlockId[] | undefined = target.state.getNodePath(target.table.id);\n\t\t\tif (!path) return false;\n\t\t\tbuilder.setNodeAttr(path, withTableColumnWidthsPx(target.table.attrs, widths));\n\t\t}\n\t}\n\n\tif (rowValue) {\n\t\tfor (const rowIndex of target.rows) {\n\t\t\tconst row: BlockNode | undefined = target.grid.rows[rowIndex];\n\t\t\tif (!row || readTableRowMinHeightPx(row) === rowValue.value) continue;\n\t\t\tconst path: readonly BlockId[] | undefined = target.state.getNodePath(row.id);\n\t\t\tif (!path) return false;\n\t\t\tbuilder.setNodeAttr(path, withTableRowMinHeightPx(row.attrs, rowValue.value));\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\tif (!changed) return false;\n\tbuilder.setSelection(target.state.selection);\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\nfunction resetResolvedSize(\n\tcontext: PluginContext,\n\ttarget: ResolvedTarget,\n\tdimension: TableSizeDimension | undefined,\n\tconfig: TableSizingConfig,\n): boolean {\n\tif (dimension === 'columnWidthPx') {\n\t\treturn applySize(context, target, { columnWidthPx: 'auto' }, config);\n\t}\n\tif (dimension === 'rowMinHeightPx') {\n\t\treturn applySize(context, target, { rowMinHeightPx: 'auto' }, config);\n\t}\n\n\tconst input: TableSizeInput = {\n\t\t...(target.columns.length > 0 ? { columnWidthPx: 'auto' as const } : {}),\n\t\t...(target.rows.length > 0 ? { rowMinHeightPx: 'auto' as const } : {}),\n\t};\n\treturn applySize(context, target, input, config);\n}\n\ninterface NormalizedInput {\n\treadonly value: number | null;\n}\n\nfunction normalizeInput(\n\tvalue: TableDimensionInput | undefined,\n\tminimum: number,\n\tmaximum: number,\n): NormalizedInput | null {\n\tif (value === 'auto') return { value: null };\n\tconst normalized: number | null = normalizeTableDimensionPx(value, minimum, maximum);\n\treturn normalized === null ? null : { value: normalized };\n}\n\nfunction summarizeValues(values: readonly (number | null)[]): TableDimensionState {\n\tif (values.length === 0) return 'unavailable';\n\tconst first: number | null | undefined = values[0];\n\tif (values.some((value: number | null): boolean => value !== first)) return 'mixed';\n\treturn first === null || first === undefined ? 'auto' : first;\n}\n\n// --- Small utilities ---\n\nfunction normalizeStoredDimension(value: unknown): number | null {\n\tif (typeof value !== 'number') return null;\n\tif (value < MIN_TABLE_DIMENSION_PX || value > MAX_TABLE_DIMENSION_PX) return null;\n\treturn normalizeTableDimensionPx(value);\n}\n\nfunction normalizeConfigValue(\n\tvalue: unknown,\n\tfallback: number,\n\tminimum = MIN_TABLE_DIMENSION_PX,\n): number {\n\treturn normalizeTableDimensionPx(value ?? fallback, minimum, MAX_TABLE_DIMENSION_PX) ?? fallback;\n}\n\nfunction validCoordinate(value: number, upperBound: number): boolean {\n\treturn Number.isInteger(value) && value >= 0 && value < upperBound;\n}\n\nfunction clampIndex(value: number, upperBound: number): number {\n\treturn Math.min(upperBound - 1, Math.max(0, value));\n}\n\nfunction integerRange(from: number, to: number): readonly number[] {\n\treturn Array.from({ length: to - from + 1 }, (_unused: unknown, index: number) => from + index);\n}\n","/**\n * Table commands: insert table, add/remove rows and columns, delete table.\n * All commands are registered via PluginContext and operate through transactions.\n *\n * Shared transaction builders (`build*Transaction`) are pure functions that take\n * EditorState + explicit indices and return a Transaction or null. They are used\n * by both commands (via PluginContext) and controls (via getState/dispatch).\n */\n\nimport { insertBlockObjectOnOwnLine } from '../../commands/BlockInsertion.js';\nimport { createSelectionForBlockBoundary } from '../../commands/CommandHelpers.js';\nimport {\n\ttype BlockAttrValue,\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateEmptyParagraph,\n\tgenerateBlockId,\n\tgetBlockChildren,\n} from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisGapCursor,\n\tisNodeSelection,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getSelectedBlockId } from '../shared/PluginHelpers.js';\nimport { type TableGrid, type TableGridCell, createTableGrid } from './TableGrid.js';\nimport {\n\ttype TableContext,\n\tcreateTable,\n\tcreateTableCell,\n\tfindTableContext,\n\tgetCellAt,\n\tgetFirstLeafInCell,\n} from './TableHelpers.js';\nimport type { TableLocale } from './TableLocale.js';\nimport { readTableColumnWidthsPx, withTableColumnWidthsPx } from './TableSizing.js';\n\n// --- Shared Transaction Builders ---\n\n/** Builds a transaction that inserts a new row at the given index. */\nexport function buildInsertRowTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validInsertionIndex(rowIndex, grid.rowCount) || grid.columnCount === 0) return null;\n\n\tconst crossingCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.rowStart < rowIndex && entry.rowEnd > rowIndex,\n\t);\n\tconst tr = state.transaction('command');\n\tfor (const entry of crossingCells) {\n\t\tif (!setCellSpan(state, tr, entry.cell, 'rowspan', entry.rowSpan + 1)) return null;\n\t}\n\n\tconst cells: BlockNode[] = [];\n\tfor (let column = 0; column < grid.columnCount; column++) {\n\t\tconst covered: boolean = crossingCells.some(\n\t\t\t(entry: TableGridCell) => entry.columnStart <= column && entry.columnEnd > column,\n\t\t);\n\t\tif (!covered) cells.push(createTableCell());\n\t}\n\tconst newRow: BlockNode = createBlockNode(nodeType('table_row'), cells);\n\tconst rawRowIndex: number = rawInsertionIndex(table, grid, rowIndex);\n\ttr.insertNode([tableId], rawRowIndex, newRow);\n\n\tconst firstCell = getBlockChildren(newRow)[0];\n\tconst firstLeaf = firstCell ? getBlockChildren(firstCell)[0] : undefined;\n\tif (firstLeaf) {\n\t\ttr.setSelection(createCollapsedSelection(firstLeaf.id, 0));\n\t} else if (firstCell) {\n\t\ttr.setSelection(createCollapsedSelection(firstCell.id, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/** Builds a transaction that inserts a new column at the given index. */\nexport function buildInsertColumnTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tcolIndex: number,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validInsertionIndex(colIndex, grid.columnCount) || grid.rowCount === 0) return null;\n\n\tconst tr = state.transaction('command');\n\tif (hasColumnWidthMetadata(table)) {\n\t\tconst widths: (number | null)[] = [...readTableColumnWidthsPx(table, grid.columnCount)];\n\t\twidths.splice(colIndex, 0, null);\n\t\tconst path = state.getNodePath(tableId);\n\t\tif (!path) return null;\n\t\ttr.setNodeAttr(path, withTableColumnWidthsPx(table.attrs, widths));\n\t}\n\n\tconst crossingCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.columnStart < colIndex && entry.columnEnd > colIndex,\n\t);\n\tfor (const entry of crossingCells) {\n\t\tif (!setCellSpan(state, tr, entry.cell, 'colspan', entry.columnSpan + 1)) return null;\n\t}\n\n\tlet firstInsertedCell: BlockNode | undefined;\n\tfor (let rowIndex = 0; rowIndex < grid.rowCount; rowIndex++) {\n\t\tconst row: BlockNode | undefined = grid.rows[rowIndex];\n\t\tif (!row) continue;\n\t\tconst coveredByCrossingCell: boolean = crossingCells.some(\n\t\t\t(entry: TableGridCell) => entry.rowStart <= rowIndex && entry.rowEnd > rowIndex,\n\t\t);\n\t\tif (coveredByCrossingCell) continue;\n\n\t\tconst newCell = createTableCell();\n\t\tfirstInsertedCell ??= newCell;\n\t\tconst insertionIndex: number = grid.cells.filter(\n\t\t\t(entry: TableGridCell) => entry.sourceRow.id === row.id && entry.columnEnd <= colIndex,\n\t\t).length;\n\t\ttr.insertNode([tableId, row.id], insertionIndex, newCell);\n\t}\n\n\tconst firstLeaf: BlockNode | undefined = firstInsertedCell\n\t\t? getBlockChildren(firstInsertedCell)[0]\n\t\t: undefined;\n\ttr.setSelection(firstLeaf ? createCollapsedSelection(firstLeaf.id, 0) : state.selection);\n\treturn tr.build();\n}\n\n/**\n * Builds a transaction that deletes the row at the given index.\n * If it's the last row, delegates to `createDeleteTableTransaction`.\n * @param preferredCol Column index to place the cursor in after deletion (default 0).\n */\nexport function buildDeleteRowTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n\tpreferredCol = 0,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validExistingIndex(rowIndex, grid.rowCount)) return null;\n\tif (grid.rowCount <= 1) {\n\t\treturn createDeleteTableTransaction(state, tableId);\n\t}\n\n\tconst tr = state.transaction('command');\n\tconst coveringCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.rowStart <= rowIndex && entry.rowEnd > rowIndex,\n\t);\n\tconst movedCells: readonly TableGridCell[] = coveringCells\n\t\t.filter((entry: TableGridCell) => entry.rowStart === rowIndex && entry.rowSpan > 1)\n\t\t.sort((a: TableGridCell, b: TableGridCell) => a.columnStart - b.columnStart);\n\n\tfor (const entry of coveringCells) {\n\t\tif (entry.rowSpan <= 1) continue;\n\t\tif (!setCellSpan(state, tr, entry.cell, 'rowspan', entry.rowSpan - 1)) return null;\n\t}\n\n\tif (movedCells.length > 0) {\n\t\tconst targetRow: BlockNode | undefined = grid.rows[rowIndex + 1];\n\t\tif (!targetRow) return null;\n\t\tconst targetOwnedCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t\t(entry: TableGridCell) => entry.sourceRow.id === targetRow.id,\n\t\t);\n\t\tlet moved = 0;\n\t\tfor (const entry of movedCells) {\n\t\t\tconst insertionIndex: number =\n\t\t\t\ttargetOwnedCells.filter((target: TableGridCell) => target.columnStart < entry.columnStart)\n\t\t\t\t\t.length + moved;\n\t\t\ttr.moveNode(\n\t\t\t\t[tableId, entry.sourceRow.id],\n\t\t\t\tentry.sourceCellIndex - moved,\n\t\t\t\t[tableId, targetRow.id],\n\t\t\t\tinsertionIndex,\n\t\t\t);\n\t\t\tmoved++;\n\t\t}\n\t}\n\n\tconst rawRowIndex: number = rawExistingRowIndex(table, grid, rowIndex);\n\tif (rawRowIndex < 0) return null;\n\ttr.removeNode([tableId], rawRowIndex);\n\n\tconst selectionRow: number = rowIndex > 0 ? rowIndex - 1 : 1;\n\tconst safeColumn: number = Math.min(Math.max(0, preferredCol), grid.columnCount - 1);\n\tconst cellId: BlockId | null = getCellAt(state, tableId, selectionRow, safeColumn);\n\tif (cellId) {\n\t\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\t\ttr.setSelection(createCollapsedSelection(leafId, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/**\n * Builds a transaction that deletes the column at the given index.\n * If it's the last column, delegates to `createDeleteTableTransaction`.\n * @param preferredRow Row index to place the cursor in after deletion (default 0).\n */\nexport function buildDeleteColumnTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tcolIndex: number,\n\tpreferredRow = 0,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validExistingIndex(colIndex, grid.columnCount)) return null;\n\tif (grid.columnCount <= 1) {\n\t\treturn createDeleteTableTransaction(state, tableId);\n\t}\n\n\tconst tr = state.transaction('command');\n\tif (hasColumnWidthMetadata(table)) {\n\t\tconst widths: (number | null)[] = [...readTableColumnWidthsPx(table, grid.columnCount)];\n\t\twidths.splice(colIndex, 1);\n\t\tconst path = state.getNodePath(tableId);\n\t\tif (!path) return null;\n\t\ttr.setNodeAttr(path, withTableColumnWidthsPx(table.attrs, widths));\n\t}\n\n\tconst coveringCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.columnStart <= colIndex && entry.columnEnd > colIndex,\n\t);\n\tfor (const entry of coveringCells) {\n\t\tif (entry.columnSpan <= 1) continue;\n\t\tif (!setCellSpan(state, tr, entry.cell, 'colspan', entry.columnSpan - 1)) return null;\n\t}\n\tfor (const entry of coveringCells\n\t\t.filter((cell: TableGridCell) => cell.columnSpan === 1)\n\t\t.sort((a: TableGridCell, b: TableGridCell) => {\n\t\t\tif (a.sourceRowIndex !== b.sourceRowIndex) return b.sourceRowIndex - a.sourceRowIndex;\n\t\t\treturn b.sourceCellIndex - a.sourceCellIndex;\n\t\t})) {\n\t\ttr.removeNode([tableId, entry.sourceRow.id], entry.sourceCellIndex);\n\t}\n\n\tconst targetCol: number = colIndex > 0 ? colIndex - 1 : 1;\n\tconst safeRow: number = Math.min(Math.max(0, preferredRow), grid.rowCount - 1);\n\tconst cellId: BlockId | null = getCellAt(state, tableId, safeRow, targetCol);\n\tif (cellId) {\n\t\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\t\ttr.setSelection(createCollapsedSelection(leafId, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/**\n * Creates a transaction that removes the given root-level table node.\n * Cursor placement prefers the next root block, then previous.\n * If the table is the only root block, it is replaced with an empty paragraph.\n */\nexport function createDeleteTableTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n): Transaction | null {\n\tconst tableIndex: number = state.doc.children.findIndex((block) => block.id === tableId);\n\tif (tableIndex === -1) return null;\n\n\tif (state.doc.children.length === 1) {\n\t\tconst replacement = createEmptyParagraph(generateBlockId());\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.insertNode([], 0, replacement)\n\t\t\t.removeNode([], 1)\n\t\t\t.setSelection(createCollapsedSelection(replacement.id, 0))\n\t\t\t.build();\n\t}\n\n\tconst tr = state.transaction('command').removeNode([], tableIndex);\n\n\tconst nextRoot = state.doc.children[tableIndex + 1];\n\tif (nextRoot) {\n\t\tconst selection = createSelectionForBlockBoundary(state, nextRoot.id, 'start');\n\t\tif (selection) {\n\t\t\ttr.setSelection(selection);\n\t\t\treturn tr.build();\n\t\t}\n\t}\n\n\tconst prevRoot = state.doc.children[tableIndex - 1];\n\tif (prevRoot) {\n\t\tconst selection = createSelectionForBlockBoundary(state, prevRoot.id, 'end');\n\t\tif (selection) {\n\t\t\ttr.setSelection(selection);\n\t\t\treturn tr.build();\n\t\t}\n\t}\n\n\treturn null;\n}\n\n// --- Commands ---\n\ninterface TableDeletionTarget {\n\treadonly tableId: BlockId;\n}\n\n/**\n * Inserts a table with the given dimensions at the current cursor position.\n * Adds a paragraph after the table for cursor escape.\n * Moves cursor into the first cell.\n */\nexport function insertTable(context: PluginContext, rows: number, cols: number): boolean {\n\tconst state = context.getState();\n\tconst anchorBlockId = getSelectedBlockId(state);\n\tif (!anchorBlockId) return false;\n\n\tconst tableNode = createTable(rows, cols);\n\tconst builder = state.transaction('command');\n\tconst trailing = insertBlockObjectOnOwnLine(state, builder, anchorBlockId, tableNode);\n\tif (!trailing) return false;\n\n\t// Set cursor in first paragraph inside first cell\n\tconst firstRow = getBlockChildren(tableNode)[0];\n\tconst firstCell = firstRow ? getBlockChildren(firstRow)[0] : undefined;\n\tconst firstParagraph = firstCell ? getBlockChildren(firstCell)[0] : undefined;\n\n\tif (firstParagraph) {\n\t\tbuilder.setSelection(createCollapsedSelection(firstParagraph.id, 0));\n\t} else if (firstCell) {\n\t\tbuilder.setSelection(createCollapsedSelection(firstCell.id, 0));\n\t}\n\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\n/** Adds a row above the current row. */\nexport function addRowAbove(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildInsertRowTransaction(state, tableCtx.tableId, tableCtx.rowIndex);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowInsertedAbove);\n\treturn true;\n}\n\n/** Adds a row below the current row. */\nexport function addRowBelow(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildInsertRowTransaction(state, tableCtx.tableId, tableCtx.rowEnd);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowInsertedBelow);\n\treturn true;\n}\n\n/** Adds a column to the left of the current column. */\nexport function addColumnLeft(context: PluginContext, locale: TableLocale): boolean {\n\treturn addColumn(context, 'left', locale);\n}\n\n/** Adds a column to the right of the current column. */\nexport function addColumnRight(context: PluginContext, locale: TableLocale): boolean {\n\treturn addColumn(context, 'right', locale);\n}\n\nfunction addColumn(context: PluginContext, side: 'left' | 'right', locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst insertColIndex: number = side === 'left' ? tableCtx.colIndex : tableCtx.colEnd;\n\tconst tr = buildInsertColumnTransaction(state, tableCtx.tableId, insertColIndex);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceColumnInserted(side));\n\treturn true;\n}\n\n// --- Logical-grid structural helpers ---\n\nfunction validInsertionIndex(index: number, length: number): boolean {\n\treturn Number.isInteger(index) && index >= 0 && index <= length;\n}\n\nfunction validExistingIndex(index: number, length: number): boolean {\n\treturn Number.isInteger(index) && index >= 0 && index < length;\n}\n\nfunction rawInsertionIndex(table: BlockNode, grid: TableGrid, logicalRow: number): number {\n\tif (logicalRow >= grid.rowCount) return table.children.length;\n\tconst row: BlockNode | undefined = grid.rows[logicalRow];\n\tif (!row) return table.children.length;\n\tconst index: number = table.children.findIndex((child) => 'id' in child && child.id === row.id);\n\treturn index === -1 ? table.children.length : index;\n}\n\nfunction rawExistingRowIndex(table: BlockNode, grid: TableGrid, logicalRow: number): number {\n\tconst row: BlockNode | undefined = grid.rows[logicalRow];\n\tif (!row) return -1;\n\treturn table.children.findIndex((child) => 'id' in child && child.id === row.id);\n}\n\nfunction setCellSpan(\n\tstate: EditorState,\n\tbuilder: ReturnType<EditorState['transaction']>,\n\tcell: BlockNode,\n\tkey: 'colspan' | 'rowspan',\n\tvalue: number,\n): boolean {\n\tconst path: readonly BlockId[] | undefined = state.getNodePath(cell.id);\n\tif (!path) return false;\n\tbuilder.setNodeAttr(path, withCellSpan(cell, key, value).attrs ?? {});\n\treturn true;\n}\n\nfunction withCellSpan(cell: BlockNode, key: 'colspan' | 'rowspan', value: number): BlockNode {\n\tconst attrs: Record<string, BlockAttrValue> = { ...(cell.attrs ?? {}) };\n\tdelete attrs[key];\n\tif (value > 1) attrs[key] = value;\n\treturn { ...cell, attrs };\n}\n\nfunction hasColumnWidthMetadata(table: BlockNode): boolean {\n\treturn Object.prototype.hasOwnProperty.call(table.attrs ?? {}, 'columnWidthsPx');\n}\n\n/** Deletes the current row. If it's the last row, deletes the entire table. */\nexport function deleteRow(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildDeleteRowTransaction(\n\t\tstate,\n\t\ttableCtx.tableId,\n\t\ttableCtx.rowIndex,\n\t\ttableCtx.colIndex,\n\t);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowDeleted);\n\treturn true;\n}\n\n/** Deletes the current column. If it's the last column, deletes the entire table. */\nexport function deleteColumn(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildDeleteColumnTransaction(\n\t\tstate,\n\t\ttableCtx.tableId,\n\t\ttableCtx.colIndex,\n\t\ttableCtx.rowIndex,\n\t);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceColumnDeleted);\n\treturn true;\n}\n\n/** Deletes the entire table and moves cursor to surrounding block. */\nexport function deleteTable(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tconst target = resolveTableDeletionTarget(state);\n\tif (!target) return false;\n\n\tconst tr = createDeleteTableTransaction(state, target.tableId);\n\tif (!tr) return false;\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceTableDeleted);\n\treturn true;\n}\n\n/** Selects the surrounding table as a node object. */\nexport function selectTable(context: PluginContext): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel)) {\n\t\tconst selectedNode = state.getBlock(sel.nodeId);\n\t\treturn selectedNode?.type === 'table';\n\t}\n\tif (isGapCursor(sel)) return false;\n\n\tconst tableCtx: TableContext | null = findTableContext(state, sel.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst path = state.getNodePath(tableCtx.tableId);\n\tif (!path) return false;\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setSelection(createNodeSelection(tableCtx.tableId, path))\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Registers all table commands on the given plugin context. */\nexport function registerTableCommands(context: PluginContext, locale: TableLocale): void {\n\tcontext.registerCommand('insertTable', () => insertTable(context, 3, 3));\n\tcontext.registerCommand('addRowAbove', () => addRowAbove(context, locale));\n\tcontext.registerCommand('addRowBelow', () => addRowBelow(context, locale));\n\tcontext.registerCommand('addColumnLeft', () => addColumnLeft(context, locale));\n\tcontext.registerCommand('addColumnRight', () => addColumnRight(context, locale));\n\tcontext.registerCommand('deleteRow', () => deleteRow(context, locale));\n\tcontext.registerCommand('deleteColumn', () => deleteColumn(context, locale));\n\tcontext.registerCommand('selectTable', () => selectTable(context));\n\tcontext.registerCommand('deleteTable', () => deleteTable(context, locale));\n}\n\nfunction resolveTableDeletionTarget(\n\tstate: ReturnType<PluginContext['getState']>,\n): TableDeletionTarget | null {\n\tif (isNodeSelection(state.selection)) {\n\t\tconst selectedNode = state.getBlock(state.selection.nodeId);\n\t\tif (selectedNode?.type !== 'table') return null;\n\t\treturn { tableId: selectedNode.id };\n\t}\n\tif (isGapCursor(state.selection)) return null;\n\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return null;\n\n\treturn { tableId: tableCtx.tableId };\n}\n","/** Accessible precise-size editor shared by the table context and actions menus. */\n\nimport type { PluginContext } from '../Plugin.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport {\n\ttype TableDimensionState,\n\ttype TableSizeInput,\n\ttype TableSizeState,\n\ttype TableSizeTarget,\n\ttype TableSizingConfig,\n\ttype TableSizingService,\n\tTableSizingServiceKey,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\nexport interface TableSizeEditorOptions {\n\treadonly target?: TableSizeTarget;\n\treadonly config?: Partial<TableSizingConfig>;\n\treadonly onClose: () => void;\n}\n\ninterface DimensionField {\n\treadonly input: HTMLInputElement;\n\treadonly error: HTMLDivElement;\n\treadonly minimum: number;\n\treadonly maximum: number;\n\tdirty: boolean;\n}\n\n/** Renders the precise table sizing UI into an existing popup container. */\nexport function renderTableSizeEditor(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\toptions: TableSizeEditorOptions,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): void {\n\tconst service: TableSizingService | undefined = context.getService?.(TableSizingServiceKey);\n\tconst config: TableSizingConfig = resolveTableSizingConfig(options.config ?? {});\n\tconst state: TableSizeState | null = service ? readSize(service, options.target) : null;\n\n\tcontainer.classList.add('notectl-table-size-editor');\n\tcontainer.setAttribute('role', 'dialog');\n\tcontainer.setAttribute('aria-modal', 'true');\n\tcontainer.setAttribute('aria-label', locale.sizeDialogLabel);\n\n\tconst title: HTMLDivElement = document.createElement('div');\n\ttitle.className = 'notectl-table-size-editor__title';\n\ttitle.textContent = locale.sizeDialogLabel;\n\tcontainer.appendChild(title);\n\n\tconst columnField: DimensionField = buildDimensionField(\n\t\tcontainer,\n\t\t'column-width',\n\t\tlocale.columnWidthLabel,\n\t\tstate?.columnWidthPx ?? 'unavailable',\n\t\tconfig.minColumnWidthPx,\n\t\tconfig.maxColumnWidthPx,\n\t\tlocale,\n\t);\n\tconst rowField: DimensionField = buildDimensionField(\n\t\tcontainer,\n\t\t'row-min-height',\n\t\tlocale.rowMinimumHeightLabel,\n\t\tstate?.rowMinHeightPx ?? 'unavailable',\n\t\tconfig.minRowHeightPx,\n\t\tconfig.maxRowHeightPx,\n\t\tlocale,\n\t);\n\n\tconst resetGroup: HTMLDivElement = document.createElement('div');\n\tresetGroup.className = 'notectl-table-size-editor__resets';\n\tconst resetColumn: HTMLButtonElement = buildButton(\n\t\tlocale.resetColumnWidth,\n\t\t'notectl-table-size-editor__reset',\n\t);\n\tconst resetRow: HTMLButtonElement = buildButton(\n\t\tlocale.resetRowMinimumHeight,\n\t\t'notectl-table-size-editor__reset',\n\t);\n\tconst resetAll: HTMLButtonElement = buildButton(\n\t\tlocale.resetAllSizes,\n\t\t'notectl-table-size-editor__reset notectl-table-size-editor__reset--all',\n\t);\n\tresetColumn.disabled = columnField.input.disabled;\n\tresetRow.disabled = rowField.input.disabled;\n\tresetAll.disabled = columnField.input.disabled && rowField.input.disabled;\n\tresetGroup.append(resetColumn, resetRow, resetAll);\n\tcontainer.appendChild(resetGroup);\n\n\tconst actions: HTMLDivElement = document.createElement('div');\n\tactions.className = 'notectl-table-size-editor__actions';\n\tconst cancel: HTMLButtonElement = buildButton(locale.cancel);\n\tconst apply: HTMLButtonElement = buildButton(locale.apply, 'notectl-table-size-editor__apply');\n\tapply.disabled = !service || !!context.isReadOnly?.();\n\tactions.append(cancel, apply);\n\tcontainer.appendChild(actions);\n\n\tcolumnField.input.addEventListener('input', () => {\n\t\tcolumnField.dirty = true;\n\t\tclearError(columnField);\n\t});\n\trowField.input.addEventListener('input', () => {\n\t\trowField.dirty = true;\n\t\tclearError(rowField);\n\t});\n\n\tapply.addEventListener('click', () => {\n\t\tif (!service || context.isReadOnly?.()) return;\n\t\tconst input: MutableTableSizeInput = {};\n\t\tlet valid = true;\n\t\tif (columnField.dirty) {\n\t\t\tconst value: number | null = parseDimension(columnField, locale);\n\t\t\tif (value === null) valid = false;\n\t\t\telse input.columnWidthPx = value;\n\t\t}\n\t\tif (rowField.dirty) {\n\t\t\tconst value: number | null = parseDimension(rowField, locale);\n\t\t\tif (value === null) valid = false;\n\t\t\telse input.rowMinHeightPx = value;\n\t\t}\n\t\tif (!valid) return;\n\t\tif (!columnField.dirty && !rowField.dirty) {\n\t\t\toptions.onClose();\n\t\t\treturn;\n\t\t}\n\t\tif (writeSize(service, options.target, input)) {\n\t\t\tannounceAppliedSize(context, options.target, input, locale);\n\t\t\toptions.onClose();\n\t\t}\n\t});\n\n\tcancel.addEventListener('click', options.onClose);\n\tresetColumn.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target, 'columnWidthPx')) return;\n\t\tsetFieldState(columnField, 'auto', locale);\n\t\tannounceReset(context, options.target, 'columnWidthPx', locale);\n\t});\n\tresetRow.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target, 'rowMinHeightPx')) return;\n\t\tsetFieldState(rowField, 'auto', locale);\n\t\tannounceReset(context, options.target, 'rowMinHeightPx', locale);\n\t});\n\tresetAll.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target)) return;\n\t\tif (!columnField.input.disabled) setFieldState(columnField, 'auto', locale);\n\t\tif (!rowField.input.disabled) setFieldState(rowField, 'auto', locale);\n\t\tcontext.announce(locale.announceTableSizesReset);\n\t});\n\n\tcontainer.addEventListener('keydown', (event: KeyboardEvent) => {\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\toptions.onClose();\n\t\t\treturn;\n\t\t}\n\t\tif (event.key === 'Tab') trapDialogFocus(container, event);\n\t});\n\n\trequestAnimationFrame(() => {\n\t\tconst firstInput: HTMLInputElement | undefined = [columnField.input, rowField.input].find(\n\t\t\t(input: HTMLInputElement): boolean => !input.disabled,\n\t\t);\n\t\t(firstInput ?? cancel).focus();\n\t});\n}\n\nfunction buildDimensionField(\n\tcontainer: HTMLElement,\n\tid: string,\n\tlabelText: string,\n\tstate: TableDimensionState,\n\tminimum: number,\n\tmaximum: number,\n\tlocale: TableLocale,\n): DimensionField {\n\tconst group: HTMLDivElement = document.createElement('div');\n\tgroup.className = 'notectl-table-size-editor__field';\n\tconst label: HTMLLabelElement = document.createElement('label');\n\tlabel.htmlFor = `notectl-table-size-${id}`;\n\tlabel.textContent = labelText;\n\tconst inputWrap: HTMLDivElement = document.createElement('div');\n\tinputWrap.className = 'notectl-table-size-editor__input-wrap';\n\tconst input: HTMLInputElement = document.createElement('input');\n\tinput.id = label.htmlFor;\n\tinput.type = 'number';\n\tinput.inputMode = 'decimal';\n\tinput.step = '1';\n\tinput.min = String(minimum);\n\tinput.max = String(maximum);\n\tinput.setAttribute('aria-describedby', `${input.id}-error`);\n\tconst unit: HTMLSpanElement = document.createElement('span');\n\tunit.className = 'notectl-table-size-editor__unit';\n\tunit.textContent = 'px';\n\tunit.setAttribute('aria-hidden', 'true');\n\tinputWrap.append(input, unit);\n\tconst error: HTMLDivElement = document.createElement('div');\n\terror.id = `${input.id}-error`;\n\terror.className = 'notectl-table-size-editor__error';\n\terror.setAttribute('role', 'alert');\n\tgroup.append(label, inputWrap, error);\n\tcontainer.appendChild(group);\n\n\tconst field: DimensionField = { input, error, minimum, maximum, dirty: false };\n\tsetFieldState(field, state, locale);\n\treturn field;\n}\n\nfunction setFieldState(\n\tfield: DimensionField,\n\tstate: TableDimensionState,\n\tlocale: TableLocale,\n): void {\n\tfield.input.disabled = state === 'unavailable';\n\tfield.input.value = typeof state === 'number' ? String(state) : '';\n\tfield.input.placeholder = state === 'mixed' ? locale.mixed : locale.automatic;\n\tfield.dirty = false;\n\tclearError(field);\n}\n\nfunction parseDimension(field: DimensionField, locale: TableLocale): number | null {\n\tconst raw: string = field.input.value.trim();\n\tconst value: number = Number(raw);\n\tif (raw.length === 0 || !Number.isFinite(value)) {\n\t\tfield.error.textContent = locale.invalidDimensionRange(field.minimum, field.maximum);\n\t\tfield.input.setAttribute('aria-invalid', 'true');\n\t\tfield.input.focus();\n\t\treturn null;\n\t}\n\tconst clamped: number = Math.min(field.maximum, Math.max(field.minimum, value));\n\tfield.input.value = String(clamped);\n\treturn clamped;\n}\n\nfunction clearError(field: DimensionField): void {\n\tfield.error.textContent = '';\n\tfield.input.removeAttribute('aria-invalid');\n}\n\nfunction buildButton(label: string, className = ''): HTMLButtonElement {\n\tconst button: HTMLButtonElement = document.createElement('button');\n\tbutton.type = 'button';\n\tbutton.textContent = label;\n\tbutton.className = className;\n\treturn button;\n}\n\n/** Keeps Tab focus inside the modal size dialog by wrapping at both ends. */\nfunction trapDialogFocus(container: HTMLElement, event: KeyboardEvent): void {\n\tconst focusable: readonly HTMLElement[] = getFocusableDialogElements(container);\n\tconst first: HTMLElement | undefined = focusable[0];\n\tconst last: HTMLElement | undefined = focusable[focusable.length - 1];\n\tif (!first || !last) return;\n\tconst active: Element | null = activeElementWithin(container);\n\tconst wrapToLast: boolean = event.shiftKey && (active === first || !container.contains(active));\n\tconst wrapToFirst: boolean = !event.shiftKey && (active === last || !container.contains(active));\n\tif (!wrapToLast && !wrapToFirst) return;\n\tevent.preventDefault();\n\t(wrapToLast ? last : first).focus();\n}\n\n/** Returns the dialog's enabled, focusable controls in DOM order. */\nfunction getFocusableDialogElements(container: HTMLElement): readonly HTMLElement[] {\n\treturn Array.from(\n\t\tcontainer.querySelectorAll<HTMLInputElement | HTMLButtonElement>('input, button'),\n\t).filter((element: HTMLInputElement | HTMLButtonElement): boolean => !element.disabled);\n}\n\n/** Resolves the active element across a shadow-root or document boundary. */\nfunction activeElementWithin(container: HTMLElement): Element | null {\n\tconst root: Node = container.getRootNode();\n\tif (root instanceof ShadowRoot || root instanceof Document) return root.activeElement;\n\treturn document.activeElement;\n}\n\nfunction readSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n): TableSizeState | null {\n\treturn target ? service.getSize(target) : service.getSelectionSize();\n}\n\nfunction writeSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n\tinput: TableSizeInput,\n): boolean {\n\treturn target ? service.setSize(target, input) : service.setSelectionSize(input);\n}\n\nfunction resetSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n\tdimension?: 'columnWidthPx' | 'rowMinHeightPx',\n): boolean {\n\treturn target ? service.resetSize(target, dimension) : service.resetSelectionSize(dimension);\n}\n\nfunction announceAppliedSize(\n\tcontext: PluginContext,\n\ttarget: TableSizeTarget | undefined,\n\tinput: TableSizeInput,\n\tlocale: TableLocale,\n): void {\n\tconst messages: string[] = [];\n\tif (typeof input.columnWidthPx === 'number') {\n\t\tif (target?.kind === 'column' || target?.kind === 'cell') {\n\t\t\tmessages.push(locale.announceColumnWidthSet(target.column, input.columnWidthPx));\n\t\t} else {\n\t\t\tmessages.push(`${locale.columnWidthLabel}: ${String(input.columnWidthPx)}`);\n\t\t}\n\t}\n\tif (typeof input.rowMinHeightPx === 'number') {\n\t\tif (target?.kind === 'row' || target?.kind === 'cell') {\n\t\t\tmessages.push(locale.announceRowMinimumHeightSet(target.row, input.rowMinHeightPx));\n\t\t} else {\n\t\t\tmessages.push(`${locale.rowMinimumHeightLabel}: ${String(input.rowMinHeightPx)}`);\n\t\t}\n\t}\n\tif (messages.length > 0) context.announce(messages.join(' '));\n}\n\nfunction announceReset(\n\tcontext: PluginContext,\n\ttarget: TableSizeTarget | undefined,\n\tdimension: 'columnWidthPx' | 'rowMinHeightPx',\n\tlocale: TableLocale,\n): void {\n\tif (dimension === 'columnWidthPx') {\n\t\tif (target?.kind === 'column' || target?.kind === 'cell') {\n\t\t\tcontext.announce(locale.announceColumnWidthReset(target.column));\n\t\t} else {\n\t\t\tcontext.announce(locale.resetColumnWidth);\n\t\t}\n\t\treturn;\n\t}\n\tif (target?.kind === 'row' || target?.kind === 'cell') {\n\t\tcontext.announce(locale.announceRowMinimumHeightReset(target.row));\n\t} else {\n\t\tcontext.announce(locale.resetRowMinimumHeight);\n\t}\n}\n\ntype MutableTableSizeInput = {\n\t-readonly [Key in keyof TableSizeInput]?: TableSizeInput[Key];\n};\n","/**\n * Accessible context menu for table operations.\n * Triggered via right-click, Shift-F10 keyboard shortcut, or table actions button.\n * Provides fully keyboard-navigable access to all table actions.\n * Delegates lifecycle to PopupManager when available, falls back to manual DOM.\n * Prevents key events from escaping to the editor via stopPropagation.\n */\n\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { isRtlContext } from '../../platform/Platform.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { applyRovingTabindex } from '../shared/KeyboardNav.js';\nimport type { PopupHandle, PopupManager } from '../shared/PopupManager.js';\nimport { positionPopup } from '../shared/PopupPositioning.js';\nimport { renderBorderColorPicker } from './TableBorderColor.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { renderTableSizeEditor } from './TableSizeEditor.js';\nimport type { TableSizeTarget, TableSizingConfig } from './TableSizing.js';\n\n// --- Types ---\n\nexport interface TableContextMenuHandle {\n\tisOpen(): boolean;\n\tclose(): void;\n\tdestroy(): void;\n}\n\nexport interface TableContextMenuOptions {\n\t/** Explicit logical target used by cell-invoked menus; omitted means current table selection. */\n\treadonly sizeTarget?: TableSizeTarget;\n\treadonly sizingConfig?: Partial<TableSizingConfig>;\n\t/** Element that receives focus after the complete menu closes. */\n\treadonly restoreFocusTo?: HTMLElement | null;\n}\n\ninterface MenuEntry {\n\treadonly label: string;\n\treadonly command: string;\n\treadonly type: 'item';\n}\n\ninterface MenuSeparator {\n\treadonly type: 'separator';\n}\n\ninterface MenuSubmenu {\n\treadonly label: string;\n\treadonly type: 'submenu';\n\treadonly id: string;\n}\n\ntype MenuDefinition = MenuEntry | MenuSeparator | MenuSubmenu;\n\nconst openMenusByContext: WeakMap<PluginContext, Set<() => void>> = new WeakMap();\n\n/** Closes every table context/actions menu owned by one editor instance. */\nexport function closeTableContextMenus(context: PluginContext): void {\n\tfor (const close of [...(openMenusByContext.get(context) ?? [])]) close();\n}\n\n// --- Menu Structure ---\n\nfunction buildMenuItems(locale: TableLocale): readonly MenuDefinition[] {\n\treturn [\n\t\t{ type: 'item', label: locale.insertRowAbove, command: 'addRowAbove' },\n\t\t{ type: 'item', label: locale.insertRowBelow, command: 'addRowBelow' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.insertColumnLeft, command: 'addColumnLeft' },\n\t\t{ type: 'item', label: locale.insertColumnRight, command: 'addColumnRight' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.deleteRow, command: 'deleteRow' },\n\t\t{ type: 'item', label: locale.deleteColumn, command: 'deleteColumn' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'submenu', label: locale.borderColorLabel, id: 'borderColor' },\n\t\t{ type: 'submenu', label: locale.sizeLabel, id: 'size' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.deleteTable, command: 'deleteTable' },\n\t];\n}\n\n// --- Factory ---\n\n/**\n * Creates and displays an accessible context menu for table operations.\n * Attaches to the provided container (shadow root or editor container).\n */\nexport function createTableContextMenu(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\tanchorRect: DOMRect,\n\tonClosed?: () => void,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n\tpopupManager?: PopupManager,\n\toptions: TableContextMenuOptions = {},\n): TableContextMenuHandle {\n\tlet open = true;\n\tlet subPopup: HTMLDivElement | null = null;\n\tlet subHandle: PopupHandle | null = null;\n\n\t// --- Menu element ---\n\tconst menu: HTMLDivElement = document.createElement('div');\n\tmenu.className = 'notectl-table-context-menu';\n\tmenu.setAttribute('role', 'menu');\n\tmenu.setAttribute('aria-label', locale.tableActions);\n\tmenu.setAttribute('contenteditable', 'false');\n\tmenu.setAttribute('data-notectl-no-print', '');\n\n\t// --- Build menu items ---\n\tconst menuItems: HTMLButtonElement[] = [];\n\tlet focusedIndex = 0;\n\n\tconst menuDefs: readonly MenuDefinition[] = buildMenuItems(locale);\n\tfor (const def of menuDefs) {\n\t\tif (def.type === 'separator') {\n\t\t\tconst sep: HTMLDivElement = document.createElement('div');\n\t\t\tsep.setAttribute('role', 'separator');\n\t\t\tmenu.appendChild(sep);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst btn: HTMLButtonElement = document.createElement('button');\n\t\tbtn.type = 'button';\n\t\tbtn.setAttribute('role', 'menuitem');\n\t\tbtn.textContent = def.label;\n\n\t\tif (def.type === 'submenu') {\n\t\t\tbtn.dataset.submenu = def.id;\n\t\t\tbtn.setAttribute('aria-haspopup', 'true');\n\t\t\tbtn.setAttribute('aria-expanded', 'false');\n\t\t}\n\n\t\tif (def.type === 'item') {\n\t\t\tbtn.dataset.command = def.command;\n\t\t}\n\n\t\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t});\n\n\t\tbtn.addEventListener('click', (e: MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tif (def.type === 'item') {\n\t\t\t\tcontext.executeCommand(def.command);\n\t\t\t\tclose();\n\t\t\t} else if (def.type === 'submenu') {\n\t\t\t\topenSubmenu(btn);\n\t\t\t}\n\t\t});\n\n\t\tmenuItems.push(btn);\n\t\tmenu.appendChild(btn);\n\t}\n\n\t// --- Keyboard hint ---\n\tconst hint: HTMLDivElement = document.createElement('div');\n\thint.className = 'notectl-table-context-menu__hint';\n\thint.setAttribute('aria-hidden', 'true');\n\thint.textContent = locale.menuKeyboardHint;\n\tmenu.appendChild(hint);\n\n\tapplyRovingTabindex(menuItems, focusedIndex);\n\n\t// --- Keyboard navigation ---\n\tmenu.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\te.stopPropagation();\n\n\t\tswitch (e.key) {\n\t\t\tcase 'Escape':\n\t\t\t\te.preventDefault();\n\t\t\t\tclose();\n\t\t\t\treturn;\n\n\t\t\tcase 'ArrowDown': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = (focusedIndex + 1) % menuItems.length;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'ArrowUp': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = (focusedIndex - 1 + menuItems.length) % menuItems.length;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'ArrowRight':\n\t\t\tcase 'ArrowLeft': {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst menuDir: string = getComputedStyle(menu).direction;\n\t\t\t\tconst openKey: string = menuDir === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n\t\t\t\tif (e.key === openKey) {\n\t\t\t\t\tconst item = menuItems[focusedIndex];\n\t\t\t\t\tif (item?.dataset.submenu) {\n\t\t\t\t\t\topenSubmenu(item);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcloseSubmenu();\n\t\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'Enter':\n\t\t\tcase ' ': {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst item = menuItems[focusedIndex];\n\t\t\t\tif (!item) return;\n\t\t\t\tif (item.dataset.submenu) {\n\t\t\t\t\topenSubmenu(item);\n\t\t\t\t} else if (item.dataset.command) {\n\t\t\t\t\tcontext.executeCommand(item.dataset.command);\n\t\t\t\t\tclose();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'Home': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = 0;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'End': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = menuItems.length - 1;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t});\n\n\t// --- Submenu handling ---\n\tfunction openSubmenu(trigger: HTMLButtonElement): void {\n\t\tcloseSubmenu();\n\t\ttrigger.setAttribute('aria-expanded', 'true');\n\n\t\tif (popupManager) {\n\t\t\tconst menuHandle: PopupHandle = {\n\t\t\t\tclose: () => close(),\n\t\t\t\tgetElement: () => menu,\n\t\t\t};\n\n\t\t\tsubHandle = popupManager.open({\n\t\t\t\tanchor: trigger,\n\t\t\t\tposition: 'right',\n\t\t\t\treferenceNode: menu,\n\t\t\t\tparent: menuHandle,\n\t\t\t\tonClose: () => {\n\t\t\t\t\tsubHandle = null;\n\t\t\t\t\tsubPopup = null;\n\t\t\t\t\tresetSubmenuTriggers();\n\t\t\t\t},\n\t\t\t\tcontent: (popup: HTMLElement, closeSub: () => void) => {\n\t\t\t\t\tpopup.className = 'notectl-table-context-menu';\n\t\t\t\t\tpopup.setAttribute('contenteditable', 'false');\n\t\t\t\t\tpopup.setAttribute('data-notectl-no-print', '');\n\t\t\t\t\tsubPopup = popup as HTMLDivElement;\n\n\t\t\t\t\tpopup.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\tif (e.target instanceof HTMLInputElement) return;\n\t\t\t\t\t\tconst popupIsRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\t\t\t\tconst backKey: string = popupIsRtl ? 'ArrowRight' : 'ArrowLeft';\n\t\t\t\t\t\tif (e.key === backKey) {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\tcloseSub();\n\t\t\t\t\t\t\ttrigger.focus();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\trenderSubmenuContent(popup, trigger);\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tsubPopup = document.createElement('div');\n\t\t\tsubPopup.className = 'notectl-table-context-menu';\n\t\t\tsubPopup.setAttribute('contenteditable', 'false');\n\t\t\tsubPopup.setAttribute('data-notectl-no-print', '');\n\n\t\t\tsubPopup.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (e.target instanceof HTMLInputElement) return;\n\t\t\t\tconst menuIsRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\t\tconst closeKey: string = menuIsRtl ? 'ArrowRight' : 'ArrowLeft';\n\t\t\t\tif (e.key === closeKey) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tcloseSubmenu();\n\t\t\t\t\ttrigger.focus();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\trenderSubmenuContent(subPopup, trigger);\n\n\t\t\tcontainer.appendChild(subPopup);\n\n\t\t\tconst menuRect: DOMRect = menu.getBoundingClientRect();\n\t\t\tconst isRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\tpositionPopup(subPopup, menuRect, { position: 'right', isRtl });\n\n\t\t\tif (trigger.dataset.submenu !== 'size') {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tconst firstFocusable = subPopup?.querySelector('button') as HTMLElement | null;\n\t\t\t\t\tfirstFocusable?.focus();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction renderSubmenuContent(popup: HTMLElement, trigger: HTMLButtonElement): void {\n\t\tif (trigger.dataset.submenu === 'size') {\n\t\t\trenderTableSizeEditor(\n\t\t\t\tpopup,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\ttarget: options.sizeTarget,\n\t\t\t\t\tconfig: options.sizingConfig,\n\t\t\t\t\tonClose: close,\n\t\t\t\t},\n\t\t\t\tlocale,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trenderBorderColorPicker(popup, context, tableId, close, locale);\n\t}\n\n\tfunction closeSubmenu(): void {\n\t\tif (subHandle) {\n\t\t\tsubHandle.close();\n\t\t\tsubHandle = null;\n\t\t\tsubPopup = null;\n\t\t} else if (subPopup) {\n\t\t\tsubPopup.remove();\n\t\t\tsubPopup = null;\n\t\t}\n\t\tresetSubmenuTriggers();\n\t}\n\n\tfunction resetSubmenuTriggers(): void {\n\t\tfor (const item of menuItems) {\n\t\t\tif (item.dataset.submenu) {\n\t\t\t\titem.setAttribute('aria-expanded', 'false');\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Append before measuring: RTL/right-edge positioning needs the real\n\t// containing block and cannot be computed from a disconnected element.\n\tcontainer.appendChild(menu);\n\tpositionPopup(menu, anchorRect, {\n\t\tposition: 'below-start',\n\t\tisRtl: isRtlContext(container),\n\t});\n\n\t// Focus first item immediately and via rAF as backup\n\tmenuItems[0]?.focus();\n\trequestAnimationFrame(() => {\n\t\tif (open && menuItems[0]) {\n\t\t\tmenuItems[0].focus();\n\t\t}\n\t});\n\n\t// --- Click outside ---\n\tconst onClickOutside = (e: MouseEvent): void => {\n\t\tconst path: EventTarget[] = e.composedPath();\n\t\tif (path.includes(menu) || (subPopup && path.includes(subPopup))) {\n\t\t\treturn;\n\t\t}\n\t\tclose();\n\t};\n\tlet outsideClickTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet outsideListenerAttached = false;\n\toutsideClickTimer = setTimeout(() => {\n\t\toutsideClickTimer = null;\n\t\tdocument.addEventListener('mousedown', onClickOutside, true);\n\t\toutsideListenerAttached = true;\n\t}, 0);\n\n\t// --- Close function ---\n\tfunction close(): void {\n\t\tif (!open) return;\n\t\topen = false;\n\t\tcloseSubmenu();\n\t\tmenu.remove();\n\t\tif (outsideClickTimer !== null) {\n\t\t\tclearTimeout(outsideClickTimer);\n\t\t\toutsideClickTimer = null;\n\t\t}\n\t\tif (outsideListenerAttached) {\n\t\t\tdocument.removeEventListener('mousedown', onClickOutside, true);\n\t\t\toutsideListenerAttached = false;\n\t\t}\n\t\topenMenusByContext.get(context)?.delete(close);\n\t\tonClosed?.();\n\t\tconst restoreTarget: HTMLElement | null | undefined = options.restoreFocusTo;\n\t\tif (restoreTarget?.isConnected) {\n\t\t\trequestAnimationFrame(() => restoreTarget.focus({ preventScroll: true }));\n\t\t}\n\t}\n\n\tconst contextMenus: Set<() => void> = openMenusByContext.get(context) ?? new Set();\n\tcontextMenus.add(close);\n\topenMenusByContext.set(context, contextMenus);\n\n\treturn {\n\t\tisOpen: () => open,\n\t\tclose,\n\t\tdestroy: close,\n\t};\n}\n","/**\n * Keyboard navigation handlers for table cells.\n * Handles Tab, Shift-Tab, arrow keys, Enter, Backspace, Delete, and Escape.\n */\n\nimport { getBlockLength } from '../../model/Document.js';\nimport type { Keymap } from '../../model/Keymap.js';\nimport {\n\ttype Selection,\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisCollapsed,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getDeepActiveHTMLElement } from '../shared/PopupPositioning.js';\nimport { addRowBelow } from './TableCommands.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport { createTableGrid } from './TableGrid.js';\nimport {\n\ttype TableContext,\n\tfindTableContext,\n\tgetCellAt,\n\tgetFirstLeafInCell,\n\tgetLastLeafInCell,\n} from './TableHelpers.js';\nimport type { TableLocale } from './TableLocale.js';\nimport { TableSelectionServiceKey } from './TableSelection.js';\nimport type { TableSizeTarget, TableSizingConfig } from './TableSizing.js';\n\n/** Tracks one keyboard context menu per editor without coupling editor instances. */\nconst activeContextMenus: WeakMap<PluginContext, TableContextMenuHandle> = new WeakMap();\n\n/** Registers all table navigation keymaps. */\nexport function registerTableKeymaps(\n\tcontext: PluginContext,\n\tlocale: TableLocale,\n\tsizingConfig: Partial<TableSizingConfig> = {},\n): void {\n\tconst keymap: Keymap = {\n\t\tTab: () => handleTab(context, locale),\n\t\t'Shift-Tab': () => handleShiftTab(context),\n\t\tBackspace: () => handleBackspace(context),\n\t\tDelete: () => handleDelete(context),\n\t\tArrowDown: () => handleArrowDown(context),\n\t\tArrowUp: () => handleArrowUp(context),\n\t\tArrowRight: () => handleArrowRight(context),\n\t\tArrowLeft: () => handleArrowLeft(context),\n\t\tEscape: () => handleEscape(context),\n\t\t'Shift-F10': () => handleContextMenu(context, locale, sizingConfig),\n\t};\n\n\tcontext.registerKeymap(keymap, { priority: 'context' });\n}\n\n/**\n * Guards against NodeSelection and resolves TableContext.\n * Passes the narrowed text Selection to the handler, avoiding re-narrowing.\n * Returns false (not handled) if the cursor is not inside a table.\n */\nfunction withTableContext(\n\tcontext: PluginContext,\n\thandler: (state: EditorState, sel: Selection, tableCtx: TableContext) => boolean,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst sel: Selection = state.selection;\n\tconst tableCtx: TableContext | null = findTableContext(state, sel.anchor.blockId);\n\tif (!tableCtx) return false;\n\treturn handler(state, sel, tableCtx);\n}\n\n/** Tab: move to next cell. At end of table, add a new row. */\nfunction handleTab(context: PluginContext, locale: TableLocale): boolean {\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\tif (moveToAdjacentSourceCell(context, tableCtx, 1, false)) return true;\n\n\t\treturn context.isReadOnly?.() ? false : addRowBelow(context, locale);\n\t});\n}\n\n/** Shift-Tab: move to previous cell. At start of table, stay put. */\nfunction handleShiftTab(context: PluginContext): boolean {\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, -1, false) || true;\n\t});\n}\n\n/** Backspace at deletion-boundary: select table node (first step of 2-step delete). */\nfunction handleBackspace(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\t\tif (sel.anchor.offset !== 0) return false;\n\n\t\tconst isAtDeletionBoundary: boolean = isFirstSourceCell(state, tableCtx);\n\t\tif (!isAtDeletionBoundary) return false;\n\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\treturn selectTableNode(context, tableCtx.tableId);\n\t});\n}\n\n/** Delete at deletion-boundary: select table node (first step of 2-step delete). */\nfunction handleDelete(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\n\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\tif (!block) return false;\n\n\t\tconst blockLen: number = getBlockLength(block);\n\t\tif (sel.anchor.offset !== blockLen) return false;\n\n\t\tconst isAtDeletionBoundary: boolean = isLastSourceCell(state, tableCtx);\n\t\tif (!isAtDeletionBoundary) return false;\n\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\treturn selectTableNode(context, tableCtx.tableId);\n\t});\n}\n\n/** ArrowDown: move to same column in next row. */\nfunction handleArrowDown(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\tif (tableCtx.rowEnd >= tableCtx.totalRows) {\n\t\t\treturn handleEscape(context);\n\t\t}\n\n\t\treturn moveToCellAndSelect(context, tableCtx.tableId, tableCtx.rowEnd, tableCtx.colIndex);\n\t});\n}\n\n/** ArrowUp: move to same column in previous row. */\nfunction handleArrowUp(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\tif (tableCtx.rowIndex <= 0) return false;\n\n\t\treturn moveToCellAndSelect(context, tableCtx.tableId, tableCtx.rowIndex - 1, tableCtx.colIndex);\n\t});\n}\n\n/** ArrowRight at cell end: move to next cell. */\nfunction handleArrowRight(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\n\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\tif (!block) return false;\n\n\t\tconst blockLen: number = getBlockLength(block);\n\t\tif (sel.anchor.offset !== blockLen) return false;\n\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, 1, false) || true;\n\t});\n}\n\n/** ArrowLeft at cell start: move to previous cell. */\nfunction handleArrowLeft(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\t\tif (sel.anchor.offset !== 0) return false;\n\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, -1, true) || true;\n\t});\n}\n\n/** Escape: move cursor to the paragraph after the table. */\nfunction handleEscape(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, _sel, tableCtx) => {\n\t\tconst nextIndex: number = tableCtx.tableIndex + 1;\n\t\tif (nextIndex >= state.doc.children.length) return false;\n\n\t\tconst nextBlock = state.doc.children[nextIndex];\n\t\tif (!nextBlock) return false;\n\n\t\tconst tr = state\n\t\t\t.transaction('command')\n\t\t\t.setSelection(createCollapsedSelection(nextBlock.id, 0))\n\t\t\t.build();\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t});\n}\n\n/** Shift-F10: open context menu at current cell. */\nfunction handleContextMenu(\n\tcontext: PluginContext,\n\tlocale: TableLocale,\n\tsizingConfig: Partial<TableSizingConfig>,\n): boolean {\n\tif (context.isReadOnly?.()) return false;\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\tconst container: HTMLElement = context.getContainer();\n\n\t\t// Find the cell element in the DOM\n\t\tconst cellEl = container.querySelector(\n\t\t\t`[data-block-id=\"${tableCtx.cellId}\"]`,\n\t\t) as HTMLElement | null;\n\t\tif (!cellEl) return false;\n\n\t\tconst cellRect: DOMRect = cellEl.getBoundingClientRect();\n\t\tconst anchorRect: DOMRect = new DOMRect(\n\t\t\tcellRect.left + cellRect.width / 2,\n\t\t\tcellRect.bottom,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\n\t\t// Find the table container to append the menu\n\t\tconst tableContainer = container.querySelector(\n\t\t\t`[data-block-id=\"${tableCtx.tableId}\"].ntbl-container`,\n\t\t) as HTMLElement | null;\n\n\t\tconst menuContainer: HTMLElement = tableContainer ?? container;\n\n\t\tactiveContextMenus.get(context)?.close();\n\t\tconst restoreFocusTo: HTMLElement | null = getDeepActiveHTMLElement(container) ?? cellEl;\n\t\tconst activeContextMenu: TableContextMenuHandle = createTableContextMenu(\n\t\t\tmenuContainer,\n\t\t\tcontext,\n\t\t\ttableCtx.tableId,\n\t\t\tanchorRect,\n\t\t\t() => {\n\t\t\t\tactiveContextMenus.delete(context);\n\t\t\t},\n\t\t\tlocale,\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\tsizeTarget: resolveTableMenuSizeTarget(context, tableCtx),\n\t\t\t\tsizingConfig,\n\t\t\t\trestoreFocusTo,\n\t\t\t},\n\t\t);\n\t\tactiveContextMenus.set(context, activeContextMenu);\n\n\t\treturn true;\n\t});\n}\n\n/** Uses an active logical table range ahead of the caret cell for menu sizing. */\nexport function resolveTableMenuSizeTarget(\n\tcontext: PluginContext,\n\ttableContext: TableContext,\n): TableSizeTarget | undefined {\n\tconst selectedRange = context.getService?.(TableSelectionServiceKey)?.getSelectedRange();\n\tif (selectedRange?.tableId === tableContext.tableId) return undefined;\n\treturn {\n\t\tkind: 'cell',\n\t\ttableId: tableContext.tableId,\n\t\trow: tableContext.rowIndex,\n\t\tcolumn: tableContext.colIndex,\n\t};\n}\n\nfunction moveToAdjacentSourceCell(\n\tcontext: PluginContext,\n\ttableContext: TableContext,\n\tdelta: -1 | 1,\n\tatEnd: boolean,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst table = state.getBlock(tableContext.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst cells = createTableGrid(table).cells;\n\tconst currentIndex: number = cells.findIndex((entry) => entry.cell.id === tableContext.cellId);\n\tconst target = cells[currentIndex + delta];\n\tif (!target) return false;\n\treturn atEnd\n\t\t? moveToCellAtEnd(context, tableContext.tableId, target.rowStart, target.columnStart)\n\t\t: moveToCellAndSelect(context, tableContext.tableId, target.rowStart, target.columnStart);\n}\n\nfunction isFirstSourceCell(state: EditorState, tableContext: TableContext): boolean {\n\tconst table = state.getBlock(tableContext.tableId);\n\treturn (\n\t\ttable?.type === 'table' && createTableGrid(table).cells[0]?.cell.id === tableContext.cellId\n\t);\n}\n\nfunction isLastSourceCell(state: EditorState, tableContext: TableContext): boolean {\n\tconst table = state.getBlock(tableContext.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst cells = createTableGrid(table).cells;\n\treturn cells[cells.length - 1]?.cell.id === tableContext.cellId;\n}\n\n// --- Cell navigation helpers ---\n\n/** Moves cursor to the start of a cell (first leaf block inside). */\nfunction moveToCellAndSelect(\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst cellId: BlockId | null = getCellAt(state, tableId, rowIndex, colIndex);\n\tif (!cellId) return false;\n\n\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\tconst tr = state.transaction('command').setSelection(createCollapsedSelection(leafId, 0)).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Moves cursor to the end of a cell (last leaf block inside, for ArrowLeft). */\nfunction moveToCellAtEnd(\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst cellId: BlockId | null = getCellAt(state, tableId, rowIndex, colIndex);\n\tif (!cellId) return false;\n\n\tconst leafId: BlockId = getLastLeafInCell(state, cellId);\n\tconst leaf = state.getBlock(leafId);\n\tif (!leaf) return false;\n\n\tconst leafLen: number = getBlockLength(leaf);\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setSelection(createCollapsedSelection(leafId, leafLen))\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Switches current selection to a table NodeSelection. */\nfunction selectTableNode(context: PluginContext, tableId: BlockId): boolean {\n\tconst state: EditorState = context.getState();\n\tconst path = state.getNodePath(tableId);\n\tif (!path) return false;\n\n\tconst tr = state.transaction('input').setSelection(createNodeSelection(tableId, path)).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n","/**\n * Pure DOM factory functions for table controls.\n * No state logic — only element creation and CSS class assignment.\n */\n\nimport { setStyleProperties } from '../../style/StyleRuntime.js';\n\n// --- SVG Icons ---\n\nexport const PLUS_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 12 12\" fill=\"none\">' +\n\t'<path d=\"M6 1v10M1 6h10\" stroke=\"currentColor\" ' +\n\t'stroke-width=\"1.8\" stroke-linecap=\"round\"/></svg>';\n\nexport const DELETE_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" ' +\n\t'viewBox=\"0 0 10 10\" fill=\"none\">' +\n\t'<path d=\"M2 2l6 6M8 2l-6 6\" stroke=\"currentColor\" ' +\n\t'stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>';\n\nexport const TABLE_DELETE_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"none\">' +\n\t'<path d=\"M9 3h6m-9 4h12M10 11v6m4-6v6m-9 3h14l-1-13H6L5 20z\" ' +\n\t'stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" ' +\n\t'stroke-linejoin=\"round\"/></svg>';\n\n// --- DOM Factories ---\n\n/** Creates a button with standard table-control setup (mousedown prevention, ARIA). */\nexport function createButton(\n\tclassName: string,\n\tinnerHTML: string,\n\ttitle: string,\n): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = document.createElement('button');\n\tbtn.className = className;\n\tbtn.innerHTML = innerHTML;\n\tbtn.title = title;\n\tbtn.type = 'button';\n\tbtn.setAttribute('aria-label', title);\n\tbtn.setAttribute('contenteditable', 'false');\n\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t});\n\treturn btn;\n}\n\n/** Builds the insert-line element (horizontal or vertical) with a plus-icon button. */\nexport function buildInsertLine(\n\torientation: 'horizontal' | 'vertical',\n\tlabel: string,\n): HTMLDivElement {\n\tconst line: HTMLDivElement = document.createElement('div');\n\tline.className = `ntbl-insert-line ntbl-insert-line--${orientation}`;\n\tline.setAttribute('contenteditable', 'false');\n\tline.setAttribute('data-notectl-no-print', '');\n\n\tconst btn: HTMLButtonElement = createButton('ntbl-insert-btn', PLUS_SVG, label);\n\tline.appendChild(btn);\n\n\treturn line;\n}\n\n/** Builds a zone-button for adding rows or columns at the table edge. */\nexport function buildAddButton(className: string, title: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = document.createElement('button');\n\tbtn.className = `ntbl-add-zone ${className}`;\n\tbtn.type = 'button';\n\tbtn.setAttribute('contenteditable', 'false');\n\tbtn.setAttribute('aria-label', title);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\tbtn.title = title;\n\n\tconst icon: HTMLSpanElement = document.createElement('span');\n\ticon.className = 'ntbl-add-icon';\n\ticon.innerHTML = PLUS_SVG;\n\tbtn.appendChild(icon);\n\n\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t});\n\n\treturn btn;\n}\n\n/** Builds a handle-bar container (for column or row handles). */\nexport function buildHandleBar(className: string): HTMLDivElement {\n\tconst bar: HTMLDivElement = document.createElement('div');\n\tbar.className = className;\n\tbar.setAttribute('contenteditable', 'false');\n\tbar.setAttribute('data-notectl-no-print', '');\n\treturn bar;\n}\n\n/** Builds a keyboard-focusable logical column/row resize separator. */\nexport function buildResizeSeparator(\n\tkind: 'column' | 'row',\n\tindex: number,\n\tlabel: string,\n\tkeyboardHint: string,\n): HTMLButtonElement {\n\tconst separator: HTMLButtonElement = document.createElement('button');\n\tseparator.type = 'button';\n\tseparator.className = `ntbl-resize-separator ntbl-resize-separator--${kind}`;\n\tseparator.dataset.kind = kind;\n\tseparator.dataset.index = String(index);\n\tseparator.setAttribute('role', 'separator');\n\tseparator.setAttribute('aria-orientation', kind === 'column' ? 'vertical' : 'horizontal');\n\tseparator.setAttribute('aria-label', label);\n\tseparator.setAttribute('contenteditable', 'false');\n\tseparator.setAttribute('data-notectl-no-print', '');\n\tseparator.title = `${label}. ${keyboardHint}`;\n\treturn separator;\n}\n\n/** Builds the live pointer-resize dimension indicator. */\nexport function buildResizeIndicator(): HTMLDivElement {\n\tconst indicator: HTMLDivElement = document.createElement('div');\n\tindicator.className = 'ntbl-resize-indicator';\n\tindicator.setAttribute('aria-hidden', 'true');\n\tindicator.setAttribute('contenteditable', 'false');\n\tindicator.setAttribute('data-notectl-no-print', '');\n\treturn indicator;\n}\n\n/** SVG icon for kebab menu (three vertical dots). */\nexport const ACTIONS_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"currentColor\">' +\n\t'<circle cx=\"12\" cy=\"5\" r=\"2\"/>' +\n\t'<circle cx=\"12\" cy=\"12\" r=\"2\"/>' +\n\t'<circle cx=\"12\" cy=\"19\" r=\"2\"/></svg>';\n\n/** Builds the table actions button (kebab menu icon). */\nexport function buildActionsButton(label: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = createButton('ntbl-actions-btn', ACTIONS_SVG, label);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\treturn btn;\n}\n\n/** Builds the context menu discovery hint shown on first focus. */\nexport function buildContextHint(label: string): HTMLDivElement {\n\tconst hint: HTMLDivElement = document.createElement('div');\n\thint.className = 'ntbl-context-hint';\n\thint.setAttribute('aria-hidden', 'true');\n\thint.setAttribute('contenteditable', 'false');\n\thint.setAttribute('data-notectl-no-print', '');\n\thint.textContent = label;\n\treturn hint;\n}\n\n/** SVG icon for border/grid. */\nexport const BORDER_COLOR_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"none\">' +\n\t'<path d=\"M3 3h18v18H3V3zm0 6h18M3 15h18M9 3v18M15 3v18\" ' +\n\t'stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>';\n\n/** Builds the border color button with a color indicator swatch. */\nexport function buildBorderColorButton(label: string, currentColor?: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = createButton('ntbl-border-color-btn', BORDER_COLOR_SVG, label);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\n\tconst swatch: HTMLSpanElement = document.createElement('span');\n\tswatch.className = 'ntbl-border-color-swatch';\n\tupdateBorderColorSwatch(swatch, currentColor);\n\tbtn.appendChild(swatch);\n\n\treturn btn;\n}\n\n/** Updates the swatch indicator to reflect the current border color. */\nexport function updateBorderColorSwatch(swatch: HTMLElement, color?: string): void {\n\tif (color === 'none') {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: 'transparent',\n\t\t\tborder: '1px dashed rgba(128,128,128,0.4)',\n\t\t});\n\t} else if (color) {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: color,\n\t\t\tborder: '1px solid rgba(0,0,0,0.15)',\n\t\t});\n\t} else {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: 'var(--notectl-border)',\n\t\t\tborder: '1px solid rgba(0,0,0,0.15)',\n\t\t});\n\t}\n}\n\nexport interface TableHandleSelectionOptions {\n\treadonly label: string;\n\treadonly onSelect: (idx: number) => void;\n\treadonly selected?: boolean;\n}\n\n/** Builds a single column or row handle with selection and delete buttons. */\nexport function buildHandle(\n\tclassName: string,\n\tindex: number,\n\tdeleteLabel: string,\n\tonDelete: (idx: number) => void,\n\tselection?: TableHandleSelectionOptions,\n): HTMLDivElement {\n\tconst handle: HTMLDivElement = document.createElement('div');\n\thandle.className = `ntbl-handle ${className}`;\n\thandle.dataset.index = String(index);\n\n\tif (selection) {\n\t\tconst selectBtn: HTMLButtonElement = createButton('ntbl-handle-select', '', selection.label);\n\t\tselectBtn.setAttribute('aria-pressed', selection.selected ? 'true' : 'false');\n\t\tselectBtn.addEventListener('click', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tselection.onSelect(index);\n\t\t});\n\t\thandle.appendChild(selectBtn);\n\t}\n\n\tconst deleteBtn: HTMLButtonElement = createButton('ntbl-handle-delete', DELETE_SVG, deleteLabel);\n\tdeleteBtn.addEventListener('click', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tonDelete(index);\n\t});\n\thandle.appendChild(deleteBtn);\n\n\treturn handle;\n}\n","/**\n * Measurement and positioning logic for table controls.\n * All functions are pure (take numeric/DOM inputs, return structured results).\n */\n\nimport { setStyleProperties, setStyleProperty } from '../../style/StyleRuntime.js';\n\n// --- Types ---\n\nexport interface BorderInfo {\n\treadonly position: number;\n\treadonly index: number;\n}\n\nexport interface NearestBorderResult {\n\treadonly type: 'row' | 'col' | 'none';\n\treadonly border: BorderInfo | null;\n}\n\nexport interface ColumnBox {\n\treadonly index: number;\n\treadonly position: number;\n\treadonly width: number;\n}\n\n// --- Constants ---\n\nexport const BORDER_THRESHOLD: number = 10;\n\n// --- Offset ---\n\n/** Returns the rendered table offset relative to the control container. */\nexport function getTableOffset(\n\ttableEl: HTMLElement,\n\tcontainer: HTMLElement,\n): { top: number; left: number } {\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst containerRect: DOMRect = container.getBoundingClientRect();\n\treturn {\n\t\ttop: tableRect.top - containerRect.top + container.scrollTop,\n\t\tleft: tableRect.left - containerRect.left + container.scrollLeft,\n\t};\n}\n\n// --- Border Measurement ---\n\n/** Measures the Y-positions of inter-row borders (between adjacent <tr> elements). */\nexport function measureRowBorders(tableEl: HTMLTableElement): BorderInfo[] {\n\tconst trs: NodeListOf<HTMLTableRowElement> = tableEl.querySelectorAll(':scope > tbody > tr');\n\tconst tableTop: number = tableEl.getBoundingClientRect().top;\n\tconst borders: BorderInfo[] = [];\n\n\tfor (let i = 1; i < trs.length; i++) {\n\t\tconst tr: HTMLTableRowElement | undefined = trs[i];\n\t\tif (!tr) continue;\n\t\tborders.push({\n\t\t\tposition: Math.round(tr.getBoundingClientRect().top - tableTop),\n\t\t\tindex: i,\n\t\t});\n\t}\n\n\treturn borders;\n}\n\n/** Measures rendered logical column boxes, with an equal-width fallback for incomplete DOMs. */\nexport function measureColumnBoxes(tableEl: HTMLTableElement, fallbackCount = 0): ColumnBox[] {\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst columns: readonly HTMLTableColElement[] = Array.from(\n\t\ttableEl.querySelectorAll(':scope > colgroup > col'),\n\t);\n\tif (columns.length > 0) {\n\t\tconst measured: ColumnBox[] = columns.map((column, index) => {\n\t\t\tconst rect: DOMRect = column.getBoundingClientRect();\n\t\t\treturn {\n\t\t\t\tindex,\n\t\t\t\tposition: rect.left - tableRect.left,\n\t\t\t\twidth: rect.width,\n\t\t\t};\n\t\t});\n\t\tif (measured.every((box) => box.width > 0)) return measured;\n\t}\n\n\tconst count: number = columns.length || fallbackCount;\n\tconst tableWidth: number = tableRect.width || tableEl.offsetWidth;\n\tif (count <= 0 || tableWidth <= 0) return [];\n\tconst width: number = tableWidth / count;\n\treturn Array.from({ length: count }, (_value, index) => ({\n\t\tindex,\n\t\tposition: width * index,\n\t\twidth,\n\t}));\n}\n\n/** Measures inter-column boundaries from rendered `<col>` geometry. */\nexport function measureColBorders(tableEl: HTMLTableElement, fallbackCount?: number): BorderInfo[];\n/** @deprecated Numeric overload retained for compatibility with existing integrations. */\nexport function measureColBorders(tableWidth: number, numCols: number): BorderInfo[];\nexport function measureColBorders(\n\ttableOrWidth: HTMLTableElement | number,\n\tcount = 0,\n): BorderInfo[] {\n\tif (typeof tableOrWidth === 'number') {\n\t\tif (count <= 1) return [];\n\t\tconst width: number = tableOrWidth / count;\n\t\treturn Array.from({ length: count - 1 }, (_value, index) => ({\n\t\t\tposition: Math.round(width * (index + 1)),\n\t\t\tindex: index + 1,\n\t\t}));\n\t}\n\n\tconst boxes: readonly ColumnBox[] = measureColumnBoxes(tableOrWidth, count);\n\tif (boxes.length <= 1) return [];\n\tconst rtl: boolean = getComputedStyle(tableOrWidth).direction === 'rtl';\n\treturn boxes.slice(0, -1).map((box, index) => ({\n\t\tposition: Math.round(rtl ? box.position : box.position + box.width),\n\t\tindex: index + 1,\n\t}));\n}\n\n// --- Positioning ---\n\n/** Positions column handles evenly across the table width. */\nexport function positionColHandles(\n\tcolBar: HTMLDivElement,\n\ttableOrWidth: HTMLTableElement | number,\n\tnumCols = 0,\n): void {\n\tconst handles: HTMLCollection = colBar.children;\n\tif (handles.length === 0) return;\n\n\tconst boxes: readonly ColumnBox[] =\n\t\ttypeof tableOrWidth === 'number'\n\t\t\t? Array.from({ length: numCols }, (_value, index) => ({\n\t\t\t\t\tindex,\n\t\t\t\t\tposition: (tableOrWidth / numCols) * index,\n\t\t\t\t\twidth: tableOrWidth / numCols,\n\t\t\t\t}))\n\t\t\t: measureColumnBoxes(tableOrWidth, numCols);\n\n\tfor (let i = 0; i < handles.length; i++) {\n\t\tconst h = handles[i] as HTMLElement;\n\t\tconst box: ColumnBox | undefined = boxes[i];\n\t\tif (!box) continue;\n\t\tsetStyleProperties(h, {\n\t\t\tleft: `${Math.round(box.position)}px`,\n\t\t\twidth: `${Math.round(box.width)}px`,\n\t\t});\n\t}\n\n\tconst totalWidth: number = boxes.reduce(\n\t\t(maximum, box) => Math.max(maximum, box.position + box.width),\n\t\t0,\n\t);\n\tsetStyleProperty(colBar, 'width', `${Math.round(totalWidth)}px`);\n}\n\n/** Positions row handles to match actual <tr> offsets and heights. */\nexport function positionRowHandles(rowBar: HTMLDivElement, tableEl: HTMLTableElement): void {\n\tconst handles: HTMLCollection = rowBar.children;\n\tif (handles.length === 0) return;\n\n\tconst trs: NodeListOf<HTMLTableRowElement> = tableEl.querySelectorAll(':scope > tbody > tr');\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tlet totalHeight = 0;\n\n\tfor (let i = 0; i < handles.length; i++) {\n\t\tconst h = handles[i] as HTMLElement;\n\t\tconst tr: HTMLTableRowElement | undefined = trs[i];\n\t\tif (tr) {\n\t\t\tconst rect: DOMRect = tr.getBoundingClientRect();\n\t\t\tconst top: number = rect.top - tableRect.top;\n\t\t\tconst height: number = rect.height || tr.offsetHeight;\n\t\t\tsetStyleProperties(h, {\n\t\t\t\ttop: `${top}px`,\n\t\t\t\theight: `${height}px`,\n\t\t\t});\n\t\t\ttotalHeight = top + height;\n\t\t}\n\t}\n\n\tsetStyleProperty(rowBar, 'height', `${totalHeight}px`);\n}\n\n/** Positions add-row and add-column buttons at the table edges. */\nexport function positionAddButtons(\n\taddRowZone: HTMLElement,\n\taddColZone: HTMLElement,\n\ttableEl: HTMLElement,\n\tcontainer: HTMLElement,\n): void {\n\tconst offset = getTableOffset(tableEl, container);\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst tableHeight: number = tableRect.height || tableEl.offsetHeight;\n\tconst tableWidth: number = tableRect.width || tableEl.offsetWidth;\n\n\tsetStyleProperties(addRowZone, {\n\t\twidth: `${tableWidth}px`,\n\t\tleft: `${offset.left}px`,\n\t});\n\n\tsetStyleProperties(addColZone, {\n\t\theight: `${tableHeight}px`,\n\t\ttop: `${offset.top}px`,\n\t});\n}\n\n// --- Border Detection ---\n\n/**\n * Finds the nearest row or column border to the given mouse position.\n * Returns which border is closest (within threshold), preferring the nearer one.\n */\nexport function findNearestBorder(\n\tx: number,\n\ty: number,\n\trowBorders: readonly BorderInfo[],\n\tcolBorders: readonly BorderInfo[],\n): NearestBorderResult {\n\tlet nearestRowDist: number = Number.POSITIVE_INFINITY;\n\tlet nearestRowBorder: BorderInfo | null = null;\n\n\tfor (const border of rowBorders) {\n\t\tconst dist: number = Math.abs(y - border.position);\n\t\tif (dist < nearestRowDist && dist < BORDER_THRESHOLD) {\n\t\t\tnearestRowDist = dist;\n\t\t\tnearestRowBorder = border;\n\t\t}\n\t}\n\n\tlet nearestColDist: number = Number.POSITIVE_INFINITY;\n\tlet nearestColBorder: BorderInfo | null = null;\n\n\tfor (const border of colBorders) {\n\t\tconst dist: number = Math.abs(x - border.position);\n\t\tif (dist < nearestColDist && dist < BORDER_THRESHOLD) {\n\t\t\tnearestColDist = dist;\n\t\t\tnearestColBorder = border;\n\t\t}\n\t}\n\n\tif (nearestRowBorder && (!nearestColBorder || nearestRowDist <= nearestColDist)) {\n\t\treturn { type: 'row', border: nearestRowBorder };\n\t}\n\n\tif (nearestColBorder) {\n\t\treturn { type: 'col', border: nearestColBorder };\n\t}\n\n\treturn { type: 'none', border: null };\n}\n","/**\n * Interactive table controls orchestrator.\n * Wires DOM elements (from TableControlsDOM) with layout logic (from TableControlsLayout)\n * and shared transaction builders (from TableCommands) to produce the interactive overlay.\n */\n\nimport type { BlockNode } from '../../model/Document.js';\nimport { getBlockChildren } from '../../model/Document.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport {\n\tremoveStyleProperty,\n\tsetStyleProperties,\n\tsetStyleProperty,\n} from '../../style/StyleRuntime.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getTableBorderColor, renderBorderColorPicker } from './TableBorderColor.js';\nimport {\n\tbuildDeleteColumnTransaction,\n\tbuildDeleteRowTransaction,\n\tbuildInsertColumnTransaction,\n\tbuildInsertRowTransaction,\n\tcreateDeleteTableTransaction,\n} from './TableCommands.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport {\n\tTABLE_DELETE_SVG,\n\tbuildActionsButton,\n\tbuildAddButton,\n\tbuildBorderColorButton,\n\tbuildContextHint,\n\tbuildHandle,\n\tbuildHandleBar,\n\tbuildInsertLine,\n\tbuildResizeIndicator,\n\tbuildResizeSeparator,\n\tcreateButton,\n\tupdateBorderColorSwatch,\n} from './TableControlsDOM.js';\nimport {\n\tBORDER_THRESHOLD,\n\ttype BorderInfo,\n\tfindNearestBorder,\n\tgetTableOffset,\n\tmeasureColBorders,\n\tmeasureColumnBoxes,\n\tmeasureRowBorders,\n\tpositionAddButtons,\n\tpositionColHandles,\n\tpositionRowHandles,\n} from './TableControlsLayout.js';\nimport { createTableGrid } from './TableGrid.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { type TableSelectionService, TableSelectionServiceKey } from './TableSelection.js';\nimport {\n\tDEFAULT_TABLE_SIZING_CONFIG,\n\ttype TableSizingConfig,\n\ttype TableSizingService,\n\tTableSizingServiceKey,\n\treadTableColumnWidthsPx,\n\treadTableRowMinHeightPx,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\n// --- Types ---\n\nexport interface TableControlsHandle {\n\tupdate(node: BlockNode): void;\n\tsetReadOnly(readonly: boolean): void;\n\tdestroy(): void;\n}\n\nexport interface TableControlsConfig extends TableSizingConfig {\n\treadonly directResize: boolean;\n}\n\nconst DEFAULT_CONTROLS_CONFIG: TableControlsConfig = {\n\t...DEFAULT_TABLE_SIZING_CONFIG,\n\tdirectResize: true,\n};\n\ninterface ResizeSession {\n\treadonly kind: 'column' | 'row';\n\treadonly index: number;\n\treadonly pointerId: number;\n\treadonly startCoordinate: number;\n\treadonly startSize: number;\n\tcurrentSize: number;\n}\n\n// --- Class ---\n\nclass TableControls implements TableControlsHandle {\n\tprivate tableId: BlockId;\n\tprivate numRows: number;\n\tprivate numCols: number;\n\tprivate currentNode: BlockNode;\n\tprivate readOnly = false;\n\tprivate activeRowIndex = -1;\n\tprivate activeColIndex = -1;\n\tprivate resizeSession: ResizeSession | null = null;\n\n\tprivate readonly colBar: HTMLDivElement;\n\tprivate readonly rowBar: HTMLDivElement;\n\tprivate readonly colResizeBar: HTMLDivElement;\n\tprivate readonly rowResizeBar: HTMLDivElement;\n\tprivate readonly resizeIndicator: HTMLDivElement;\n\tprivate readonly insertLineH: HTMLDivElement;\n\tprivate readonly insertLineV: HTMLDivElement;\n\tprivate readonly addRowZone: HTMLButtonElement;\n\tprivate readonly addColZone: HTMLButtonElement;\n\tprivate readonly deleteTableBtn: HTMLButtonElement;\n\tprivate readonly borderColorBtn: HTMLButtonElement | null;\n\tprivate readonly actionsBtn: HTMLButtonElement | null;\n\tprivate readonly contextHint: HTMLDivElement;\n\tprivate readonly observer: ResizeObserver;\n\tprivate readonly wrapperEl: HTMLElement | null;\n\tprivate borderColorPopup: HTMLDivElement | null = null;\n\tprivate activeContextMenu: TableContextMenuHandle | null = null;\n\n\tprivate readonly container: HTMLElement;\n\tprivate readonly tableEl: HTMLTableElement;\n\tprivate readonly getState: () => EditorState;\n\tprivate readonly dispatchFn: (tr: Transaction) => void;\n\tprivate readonly pluginContext: PluginContext | null;\n\tprivate readonly locale: TableLocale;\n\tprivate readonly config: TableControlsConfig;\n\tprivate readonly onWrapperScroll = (): void => this.positionControls();\n\tprivate readonly onPointerMove = (event: PointerEvent): void => this.moveResize(event);\n\tprivate readonly onPointerUp = (event: PointerEvent): void => this.finishResize(event);\n\tprivate readonly onPointerCancel = (event: PointerEvent): void => this.cancelResize(event);\n\tprivate readonly onDocumentKeyDown = (event: KeyboardEvent): void => {\n\t\tif (event.key !== 'Escape' || !this.resizeSession) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.cancelActiveResize();\n\t};\n\n\tprivate readonly onMouseMove = (e: MouseEvent): void => {\n\t\tif (this.readOnly || this.resizeSession) return;\n\t\tconst tableRect: DOMRect = this.tableEl.getBoundingClientRect();\n\t\tconst x: number = e.clientX - tableRect.left;\n\t\tconst y: number = e.clientY - tableRect.top;\n\n\t\tconst inTable: boolean =\n\t\t\tx >= -BORDER_THRESHOLD &&\n\t\t\tx <= tableRect.width + BORDER_THRESHOLD &&\n\t\t\ty >= -BORDER_THRESHOLD &&\n\t\t\ty <= tableRect.height + BORDER_THRESHOLD;\n\n\t\tif (!inTable) {\n\t\t\tthis.hideInsertLines();\n\t\t\treturn;\n\t\t}\n\n\t\tconst rowBorders: BorderInfo[] = measureRowBorders(this.tableEl);\n\t\tconst colBorders: BorderInfo[] = measureColBorders(this.tableEl, this.numCols);\n\t\tconst result = findNearestBorder(x, y, rowBorders, colBorders);\n\n\t\tif (result.type === 'row' && result.border) {\n\t\t\tthis.showHorizontalLine(result.border);\n\t\t\tthis.hideVerticalLine();\n\t\t} else if (result.type === 'col' && result.border) {\n\t\t\tthis.showVerticalLine(result.border);\n\t\t\tthis.hideHorizontalLine();\n\t\t} else {\n\t\t\tthis.hideInsertLines();\n\t\t}\n\t};\n\n\tprivate readonly onMouseLeave = (): void => {\n\t\tthis.hideInsertLines();\n\t};\n\n\tconstructor(\n\t\tcontainer: HTMLElement,\n\t\ttableEl: HTMLTableElement,\n\t\tinitialNode: BlockNode,\n\t\tgetState: () => EditorState,\n\t\tdispatch: (tr: Transaction) => void,\n\t\tpluginContext?: PluginContext,\n\t\tlocale: TableLocale = TABLE_LOCALE_EN,\n\t\tconfig: Partial<TableControlsConfig> = DEFAULT_CONTROLS_CONFIG,\n\t) {\n\t\tthis.container = container;\n\t\tthis.tableEl = tableEl;\n\t\tthis.getState = getState;\n\t\tthis.dispatchFn = dispatch;\n\t\tthis.pluginContext = pluginContext ?? null;\n\t\tthis.locale = locale;\n\t\tthis.config = {\n\t\t\t...resolveTableSizingConfig(config),\n\t\t\tdirectResize: config.directResize ?? true,\n\t\t};\n\t\tthis.wrapperEl = tableEl.closest('.notectl-table-wrapper');\n\n\t\tthis.tableId = initialNode.id;\n\t\tthis.currentNode = initialNode;\n\t\tthis.numRows = createTableGrid(initialNode).rowCount;\n\t\tthis.numCols = this.countCols(initialNode);\n\n\t\t// Create DOM elements\n\t\tthis.colBar = buildHandleBar('ntbl-col-bar');\n\t\tthis.rowBar = buildHandleBar('ntbl-row-bar');\n\t\tthis.colResizeBar = buildHandleBar('ntbl-col-resize-bar');\n\t\tthis.rowResizeBar = buildHandleBar('ntbl-row-resize-bar');\n\t\tthis.resizeIndicator = buildResizeIndicator();\n\t\tthis.insertLineH = buildInsertLine('horizontal', locale.insertRow);\n\t\tthis.insertLineV = buildInsertLine('vertical', locale.insertColumn);\n\t\tthis.addRowZone = buildAddButton('ntbl-add-row', locale.addRow);\n\t\tthis.addColZone = buildAddButton('ntbl-add-col', locale.addColumn);\n\t\tthis.deleteTableBtn = createButton(\n\t\t\t'ntbl-delete-table-btn',\n\t\t\tTABLE_DELETE_SVG,\n\t\t\tlocale.deleteTable,\n\t\t);\n\t\tthis.deleteTableBtn.setAttribute('data-notectl-no-print', '');\n\n\t\t// Border color button and actions button (only when plugin context available)\n\t\tif (pluginContext) {\n\t\t\tconst currentColor: string | undefined = getTableBorderColor(getState(), this.tableId);\n\t\t\tthis.borderColorBtn = buildBorderColorButton(locale.borderColor, currentColor);\n\t\t\tthis.actionsBtn = buildActionsButton(locale.tableActionsHint);\n\t\t} else {\n\t\t\tthis.borderColorBtn = null;\n\t\t\tthis.actionsBtn = null;\n\t\t}\n\n\t\tcontainer.append(\n\t\t\tthis.colBar,\n\t\t\tthis.rowBar,\n\t\t\tthis.insertLineH,\n\t\t\tthis.insertLineV,\n\t\t\tthis.addRowZone,\n\t\t\tthis.addColZone,\n\t\t\tthis.deleteTableBtn,\n\t\t);\n\t\tif (this.config.directResize) {\n\t\t\tcontainer.append(this.colResizeBar, this.rowResizeBar, this.resizeIndicator);\n\t\t}\n\t\tif (this.borderColorBtn) {\n\t\t\tcontainer.appendChild(this.borderColorBtn);\n\t\t}\n\t\tif (this.actionsBtn) {\n\t\t\tcontainer.appendChild(this.actionsBtn);\n\t\t}\n\n\t\tthis.contextHint = buildContextHint(locale.contextMenuHint);\n\t\tcontainer.appendChild(this.contextHint);\n\n\t\tthis.rebuildColHandles();\n\t\tthis.rebuildRowHandles();\n\t\tthis.rebuildResizeSeparators();\n\t\tthis.bindEventListeners();\n\t\tthis.setReadOnly(pluginContext?.isReadOnly?.() ?? false);\n\n\t\tthis.observer = new ResizeObserver(() => {\n\t\t\tthis.positionControls();\n\t\t});\n\t\tthis.observer.observe(tableEl);\n\t\tif (this.wrapperEl) {\n\t\t\tthis.observer.observe(this.wrapperEl);\n\t\t\tthis.wrapperEl.addEventListener('scroll', this.onWrapperScroll, { passive: true });\n\t\t}\n\t\trequestAnimationFrame(() => this.positionControls());\n\t}\n\n\tupdate(node: BlockNode): void {\n\t\tthis.tableId = node.id;\n\t\tthis.currentNode = node;\n\t\tconst newRows: number = createTableGrid(node).rowCount;\n\t\tconst newCols: number = this.countCols(node);\n\n\t\tif (newRows !== this.numRows || newCols !== this.numCols) {\n\t\t\tthis.numRows = newRows;\n\t\t\tthis.numCols = newCols;\n\t\t\tthis.rebuildColHandles();\n\t\t\tthis.rebuildRowHandles();\n\t\t\tthis.rebuildResizeSeparators();\n\t\t}\n\t\tthis.updateHandleSelectionState();\n\n\t\t// Update border color swatch\n\t\tif (this.borderColorBtn) {\n\t\t\tconst swatch = this.borderColorBtn.querySelector('.ntbl-border-color-swatch');\n\t\t\tif (swatch) {\n\t\t\t\tconst color: string | undefined = node.attrs?.borderColor as string | undefined;\n\t\t\t\tupdateBorderColorSwatch(swatch as HTMLElement, color);\n\t\t\t}\n\t\t}\n\n\t\trequestAnimationFrame(() => this.positionControls());\n\t}\n\n\tsetReadOnly(readonly: boolean): void {\n\t\tthis.readOnly = readonly;\n\t\tthis.container.toggleAttribute('data-notectl-table-readonly', readonly);\n\t\tconst roots: readonly HTMLElement[] = [\n\t\t\tthis.colBar,\n\t\t\tthis.rowBar,\n\t\t\tthis.colResizeBar,\n\t\t\tthis.rowResizeBar,\n\t\t\tthis.addRowZone,\n\t\t\tthis.addColZone,\n\t\t\tthis.deleteTableBtn,\n\t\t\tthis.insertLineH,\n\t\t\tthis.insertLineV,\n\t\t\t...(this.borderColorBtn ? [this.borderColorBtn] : []),\n\t\t\t...(this.actionsBtn ? [this.actionsBtn] : []),\n\t\t];\n\t\tfor (const root of roots) {\n\t\t\tif (root instanceof HTMLButtonElement) root.disabled = readonly;\n\t\t\tfor (const button of root.querySelectorAll('button')) button.disabled = readonly;\n\t\t}\n\t\tif (readonly) {\n\t\t\tthis.hideInsertLines();\n\t\t\tthis.cancelActiveResize();\n\t\t\tthis.activeContextMenu?.close();\n\t\t\tthis.activeContextMenu = null;\n\t\t\tthis.closeBorderColorPopup();\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tthis.cancelActiveResize();\n\t\tthis.activeContextMenu?.destroy();\n\t\tthis.closeBorderColorPopup();\n\t\tthis.observer.disconnect();\n\t\tthis.wrapperEl?.removeEventListener('scroll', this.onWrapperScroll);\n\t\tthis.container.removeEventListener('mousemove', this.onMouseMove);\n\t\tthis.container.removeEventListener('mouseleave', this.onMouseLeave);\n\t\tthis.colBar.remove();\n\t\tthis.rowBar.remove();\n\t\tthis.colResizeBar.remove();\n\t\tthis.rowResizeBar.remove();\n\t\tthis.resizeIndicator.remove();\n\t\tthis.insertLineH.remove();\n\t\tthis.insertLineV.remove();\n\t\tthis.addRowZone.remove();\n\t\tthis.addColZone.remove();\n\t\tthis.deleteTableBtn.remove();\n\t\tthis.borderColorBtn?.remove();\n\t\tthis.actionsBtn?.remove();\n\t\tthis.contextHint.remove();\n\t}\n\n\tprivate bindEventListeners(): void {\n\t\tconst insertBtnH = this.insertLineH.querySelector('.ntbl-insert-btn') as HTMLButtonElement;\n\t\tconst insertBtnV = this.insertLineV.querySelector('.ntbl-insert-btn') as HTMLButtonElement;\n\n\t\tinsertBtnH.addEventListener('click', () => {\n\t\t\tif (this.activeRowIndex >= 0) {\n\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\tbuildInsertRowTransaction(this.getState(), this.tableId, this.activeRowIndex),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tinsertBtnV.addEventListener('click', () => {\n\t\t\tif (this.activeColIndex >= 0) {\n\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\tbuildInsertColumnTransaction(this.getState(), this.tableId, this.activeColIndex),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tthis.addRowZone.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(\n\t\t\t\tbuildInsertRowTransaction(this.getState(), this.tableId, this.numRows),\n\t\t\t);\n\t\t});\n\n\t\tthis.addColZone.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(\n\t\t\t\tbuildInsertColumnTransaction(this.getState(), this.tableId, this.numCols),\n\t\t\t);\n\t\t});\n\n\t\tthis.deleteTableBtn.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(createDeleteTableTransaction(this.getState(), this.tableId));\n\t\t});\n\n\t\tif (this.borderColorBtn && this.pluginContext) {\n\t\t\tconst ctx = this.pluginContext;\n\t\t\tthis.borderColorBtn.addEventListener('click', () => {\n\t\t\t\tthis.toggleBorderColorPopup(ctx);\n\t\t\t});\n\t\t}\n\n\t\tif (this.actionsBtn && this.pluginContext) {\n\t\t\tconst ctx = this.pluginContext;\n\t\t\tthis.actionsBtn.addEventListener('click', () => {\n\t\t\t\tthis.openActionsMenu(ctx);\n\t\t\t});\n\t\t}\n\n\t\tthis.container.addEventListener('mousemove', this.onMouseMove);\n\t\tthis.container.addEventListener('mouseleave', this.onMouseLeave);\n\t}\n\n\tprivate openActionsMenu(context: PluginContext): void {\n\t\tif (this.readOnly || context.isReadOnly?.()) return;\n\t\tif (this.activeContextMenu?.isOpen()) {\n\t\t\tthis.activeContextMenu.close();\n\t\t\tthis.activeContextMenu = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.actionsBtn) return;\n\n\t\tconst rect: DOMRect = this.actionsBtn.getBoundingClientRect();\n\n\t\tthis.activeContextMenu = createTableContextMenu(\n\t\t\tthis.container,\n\t\t\tcontext,\n\t\t\tthis.tableId,\n\t\t\trect,\n\t\t\t() => {\n\t\t\t\tthis.activeContextMenu = null;\n\t\t\t},\n\t\t\tthis.locale,\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\tsizingConfig: this.config,\n\t\t\t\trestoreFocusTo: this.actionsBtn,\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate toggleBorderColorPopup(context: PluginContext): void {\n\t\tif (this.readOnly || context.isReadOnly?.()) return;\n\t\tif (this.borderColorPopup) {\n\t\t\tthis.closeBorderColorPopup();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.borderColorPopup = document.createElement('div');\n\t\tthis.borderColorPopup.className = 'notectl-table-context-menu';\n\t\tsetStyleProperties(this.borderColorPopup, {\n\t\t\tposition: 'absolute',\n\t\t\ttop: '24px',\n\t\t\tleft: '24px',\n\t\t\tzIndex: '10000',\n\t\t});\n\t\tthis.borderColorPopup.setAttribute('contenteditable', 'false');\n\n\t\trenderBorderColorPicker(\n\t\t\tthis.borderColorPopup,\n\t\t\tcontext,\n\t\t\tthis.tableId,\n\t\t\t() => {\n\t\t\t\tthis.closeBorderColorPopup();\n\t\t\t},\n\t\t\tthis.locale,\n\t\t);\n\n\t\tthis.container.appendChild(this.borderColorPopup);\n\t}\n\n\tprivate closeBorderColorPopup(): void {\n\t\tif (this.borderColorPopup) {\n\t\t\tthis.borderColorPopup.remove();\n\t\t\tthis.borderColorPopup = null;\n\t\t}\n\t}\n\n\tprivate dispatchTransaction(tr: Transaction | null): void {\n\t\tif (tr && !this.readOnly && !this.pluginContext?.isReadOnly?.()) this.dispatchFn(tr);\n\t}\n\n\tprivate countCols(node: BlockNode): number {\n\t\treturn createTableGrid(node).columnCount;\n\t}\n\n\tprivate positionControls(): void {\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tconst tableRect: DOMRect = this.tableEl.getBoundingClientRect();\n\t\tsetStyleProperties(this.colBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top - 24)}px`,\n\t\t});\n\t\tsetStyleProperty(this.rowBar, 'top', `${String(offset.top)}px`);\n\t\tpositionColHandles(this.colBar, this.tableEl, this.numCols);\n\t\tpositionRowHandles(this.rowBar, this.tableEl);\n\t\tpositionAddButtons(this.addRowZone, this.addColZone, this.tableEl, this.container);\n\t\tif (this.config.directResize) {\n\t\t\tthis.positionResizeSeparators(offset, tableRect);\n\t\t}\n\t}\n\n\tprivate positionResizeSeparators(\n\t\toffset: { readonly top: number; readonly left: number },\n\t\ttableRect: DOMRect,\n\t): void {\n\t\tsetStyleProperties(this.colResizeBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top)}px`,\n\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\theight: `${String(tableRect.height)}px`,\n\t\t});\n\t\tsetStyleProperties(this.rowResizeBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top)}px`,\n\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\theight: `${String(tableRect.height)}px`,\n\t\t});\n\n\t\tconst rtl: boolean = getComputedStyle(this.tableEl).direction === 'rtl';\n\t\tconst boxes = measureColumnBoxes(this.tableEl, this.numCols);\n\t\tfor (let index = 0; index < this.colResizeBar.children.length; index++) {\n\t\t\tconst separator = this.colResizeBar.children[index] as HTMLButtonElement;\n\t\t\tconst box = boxes[index];\n\t\t\tif (!box) continue;\n\t\t\tconst boundary: number = rtl ? box.position : box.position + box.width;\n\t\t\tsetStyleProperties(separator, {\n\t\t\t\tleft: `${String(boundary)}px`,\n\t\t\t\ttop: '0px',\n\t\t\t\theight: `${String(tableRect.height)}px`,\n\t\t\t});\n\t\t\tthis.updateSeparatorValue(separator, 'column', index, box.width);\n\t\t}\n\n\t\tconst rows: readonly HTMLTableRowElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > tbody > tr'),\n\t\t);\n\t\tfor (let index = 0; index < this.rowResizeBar.children.length; index++) {\n\t\t\tconst separator = this.rowResizeBar.children[index] as HTMLButtonElement;\n\t\t\tconst row: HTMLTableRowElement | undefined = rows[index];\n\t\t\tif (!row) continue;\n\t\t\tconst rect: DOMRect = row.getBoundingClientRect();\n\t\t\tconst boundary: number = rect.bottom - tableRect.top;\n\t\t\tsetStyleProperties(separator, {\n\t\t\t\tleft: '0px',\n\t\t\t\ttop: `${String(boundary)}px`,\n\t\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\t});\n\t\t\tthis.updateSeparatorValue(separator, 'row', index, rect.height || row.offsetHeight);\n\t\t}\n\t}\n\n\tprivate updateSeparatorValue(\n\t\tseparator: HTMLButtonElement,\n\t\tkind: 'column' | 'row',\n\t\tindex: number,\n\t\tvalue: number,\n\t): void {\n\t\tconst minimum: number =\n\t\t\tkind === 'column' ? this.config.minColumnWidthPx : this.config.minRowHeightPx;\n\t\tconst maximum: number =\n\t\t\tkind === 'column' ? this.config.maxColumnWidthPx : this.config.maxRowHeightPx;\n\t\tconst rounded: number = Math.round(value);\n\t\tseparator.setAttribute('aria-valuemin', String(minimum));\n\t\tseparator.setAttribute('aria-valuemax', String(maximum));\n\t\tseparator.setAttribute('aria-valuenow', String(rounded));\n\t\tseparator.setAttribute('aria-valuetext', `${String(rounded)} px`);\n\t\tseparator.dataset.index = String(index);\n\t}\n\n\tprivate startResize(event: PointerEvent, kind: 'column' | 'row', index: number): void {\n\t\tif (\n\t\t\tthis.readOnly ||\n\t\t\tthis.pluginContext?.isReadOnly?.() ||\n\t\t\t(event.button !== 0 && event.pointerType !== 'touch')\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst size: number | null = this.getRenderedDimension(kind, index);\n\t\tif (size === null) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.cancelActiveResize();\n\t\tthis.resizeSession = {\n\t\t\tkind,\n\t\t\tindex,\n\t\t\tpointerId: event.pointerId,\n\t\t\tstartCoordinate: kind === 'column' ? event.clientX : event.clientY,\n\t\t\tstartSize: size,\n\t\t\tcurrentSize: size,\n\t\t};\n\t\t(event.currentTarget as HTMLElement | null)?.setPointerCapture?.(event.pointerId);\n\t\tdocument.addEventListener('pointermove', this.onPointerMove);\n\t\tdocument.addEventListener('pointerup', this.onPointerUp);\n\t\tdocument.addEventListener('pointercancel', this.onPointerCancel);\n\t\tdocument.addEventListener('keydown', this.onDocumentKeyDown, true);\n\t\tthis.container.classList.add(`ntbl-container--resizing-${kind}`);\n\t\tthis.updateResizeIndicator(size, event.clientX, event.clientY);\n\t}\n\n\tprivate moveResize(event: PointerEvent): void {\n\t\tconst session: ResizeSession | null = this.resizeSession;\n\t\tif (!session || session.pointerId !== event.pointerId) return;\n\t\tevent.preventDefault();\n\t\tconst coordinate: number = session.kind === 'column' ? event.clientX : event.clientY;\n\t\tlet delta: number = coordinate - session.startCoordinate;\n\t\tif (session.kind === 'column' && getComputedStyle(this.tableEl).direction === 'rtl') {\n\t\t\tdelta *= -1;\n\t\t}\n\t\tconst next: number = this.clampDimension(session.kind, session.startSize + delta);\n\t\tsession.currentSize = next;\n\t\tthis.applyResizePreview(session.kind, session.index, next);\n\t\tthis.updateResizeIndicator(next, event.clientX, event.clientY);\n\t\tthis.positionControls();\n\t}\n\n\tprivate finishResize(event: PointerEvent): void {\n\t\tconst session: ResizeSession | null = this.resizeSession;\n\t\tif (!session || session.pointerId !== event.pointerId) return;\n\t\t(event.currentTarget as HTMLElement | null)?.releasePointerCapture?.(event.pointerId);\n\t\tthis.resizeSession = null;\n\t\tthis.detachResizeListeners();\n\t\tthis.hideResizeIndicator();\n\t\tconst value: number = Math.round(session.currentSize);\n\t\tconst service: TableSizingService | undefined = this.getSizingService();\n\t\tconst changed: boolean =\n\t\t\tsession.kind === 'column'\n\t\t\t\t? (service?.setSize(\n\t\t\t\t\t\t{ kind: 'column', tableId: this.tableId, column: session.index },\n\t\t\t\t\t\t{ columnWidthPx: value },\n\t\t\t\t\t) ?? false)\n\t\t\t\t: (service?.setSize(\n\t\t\t\t\t\t{ kind: 'row', tableId: this.tableId, row: session.index },\n\t\t\t\t\t\t{ rowMinHeightPx: value },\n\t\t\t\t\t) ?? false);\n\t\tif (!changed) this.restoreCanonicalDimensions();\n\t\telse if (session.kind === 'column') {\n\t\t\tthis.pluginContext?.announce(this.locale.announceColumnWidthSet(session.index, value));\n\t\t} else {\n\t\t\tthis.pluginContext?.announce(this.locale.announceRowMinimumHeightSet(session.index, value));\n\t\t}\n\t}\n\n\tprivate cancelResize(event: PointerEvent): void {\n\t\tif (this.resizeSession?.pointerId !== event.pointerId) return;\n\t\tthis.cancelActiveResize();\n\t}\n\n\tprivate cancelActiveResize(): void {\n\t\tif (!this.resizeSession) return;\n\t\tthis.resizeSession = null;\n\t\tthis.detachResizeListeners();\n\t\tthis.hideResizeIndicator();\n\t\tthis.restoreCanonicalDimensions();\n\t\tthis.positionControls();\n\t}\n\n\tprivate detachResizeListeners(): void {\n\t\tdocument.removeEventListener('pointermove', this.onPointerMove);\n\t\tdocument.removeEventListener('pointerup', this.onPointerUp);\n\t\tdocument.removeEventListener('pointercancel', this.onPointerCancel);\n\t\tdocument.removeEventListener('keydown', this.onDocumentKeyDown, true);\n\t\tthis.container.classList.remove('ntbl-container--resizing-column');\n\t\tthis.container.classList.remove('ntbl-container--resizing-row');\n\t}\n\n\tprivate getRenderedDimension(kind: 'column' | 'row', index: number): number | null {\n\t\tif (kind === 'column')\n\t\t\treturn measureColumnBoxes(this.tableEl, this.numCols)[index]?.width ?? null;\n\t\tconst row: HTMLTableRowElement | undefined = Array.from(\n\t\t\tthis.tableEl.querySelectorAll<HTMLTableRowElement>(':scope > tbody > tr'),\n\t\t)[index];\n\t\tif (!row) return null;\n\t\tconst rect: DOMRect = row.getBoundingClientRect();\n\t\treturn rect.height || row.offsetHeight || null;\n\t}\n\n\tprivate clampDimension(kind: 'column' | 'row', value: number): number {\n\t\tconst minimum: number =\n\t\t\tkind === 'column' ? this.config.minColumnWidthPx : this.config.minRowHeightPx;\n\t\tconst maximum: number =\n\t\t\tkind === 'column' ? this.config.maxColumnWidthPx : this.config.maxRowHeightPx;\n\t\treturn Math.min(maximum, Math.max(minimum, value));\n\t}\n\n\tprivate applyResizePreview(kind: 'column' | 'row', index: number, value: number): void {\n\t\tif (kind === 'column') {\n\t\t\tconst column = this.tableEl.querySelectorAll(':scope > colgroup > col')[index] as\n\t\t\t\t| HTMLTableColElement\n\t\t\t\t| undefined;\n\t\t\tif (!column) return;\n\t\t\t// A temporary presentation attribute drives native table layout during\n\t\t\t// pointer movement. Removing the persisted runtime width avoids a stale\n\t\t\t// CSS declaration winning the cascade; no document transaction occurs.\n\t\t\tremoveStyleProperty(column, 'width');\n\t\t\tcolumn.setAttribute('width', String(value));\n\t\t\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\t\t\tthis.currentNode,\n\t\t\t\tthis.numCols,\n\t\t\t);\n\t\t\tconst minimumTableWidth: number = widths.reduce<number>(\n\t\t\t\t(total: number, width: number | null, columnIndex: number): number =>\n\t\t\t\t\ttotal + (columnIndex === index ? value : (width ?? this.config.minColumnWidthPx)),\n\t\t\t\t0,\n\t\t\t);\n\t\t\tsetStyleProperty(this.tableEl, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\t\tif (\n\t\t\t\twidths.length >= this.numCols &&\n\t\t\t\twidths.every(\n\t\t\t\t\t(width: number | null, columnIndex: number): boolean =>\n\t\t\t\t\t\tcolumnIndex === index || width !== null,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tsetStyleProperty(this.tableEl, 'width', `${String(minimumTableWidth)}px`);\n\t\t\t} else {\n\t\t\t\tremoveStyleProperty(this.tableEl, 'width');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst row = this.tableEl.querySelectorAll(':scope > tbody > tr')[index] as\n\t\t\t| HTMLTableRowElement\n\t\t\t| undefined;\n\t\tif (!row) return;\n\t\tremoveStyleProperty(row, 'height');\n\t\trow.setAttribute('height', String(value));\n\t}\n\n\tprivate restoreCanonicalDimensions(): void {\n\t\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\t\tthis.currentNode,\n\t\t\tthis.numCols,\n\t\t);\n\t\tconst columns: readonly HTMLTableColElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > colgroup > col'),\n\t\t);\n\t\tlet minimumTableWidth = 0;\n\t\tfor (let index = 0; index < columns.length; index++) {\n\t\t\tconst column: HTMLTableColElement | undefined = columns[index];\n\t\t\tif (!column) continue;\n\t\t\tconst width: number | null = widths[index] ?? null;\n\t\t\tcolumn.removeAttribute('width');\n\t\t\tif (width === null) {\n\t\t\t\tremoveStyleProperty(column, 'width');\n\t\t\t\tcolumn.removeAttribute('data-notectl-width-px');\n\t\t\t} else {\n\t\t\t\tsetStyleProperty(column, 'width', `${String(width)}px`);\n\t\t\t\tcolumn.setAttribute('data-notectl-width-px', String(width));\n\t\t\t}\n\t\t\tminimumTableWidth += width ?? this.config.minColumnWidthPx;\n\t\t}\n\t\tsetStyleProperty(this.tableEl, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\tif (widths.length >= this.numCols && widths.every((width) => width !== null)) {\n\t\t\tsetStyleProperty(this.tableEl, 'width', `${String(minimumTableWidth)}px`);\n\t\t} else {\n\t\t\tremoveStyleProperty(this.tableEl, 'width');\n\t\t}\n\n\t\tconst rowNodes: readonly BlockNode[] = getBlockChildren(this.currentNode);\n\t\tconst rows: readonly HTMLTableRowElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > tbody > tr'),\n\t\t);\n\t\tfor (let index = 0; index < rows.length; index++) {\n\t\t\tconst row: HTMLTableRowElement | undefined = rows[index];\n\t\t\tconst rowNode: BlockNode | undefined = rowNodes[index];\n\t\t\tif (!row || !rowNode) continue;\n\t\t\trow.removeAttribute('height');\n\t\t\tconst height: number | null = readTableRowMinHeightPx(rowNode);\n\t\t\tif (height === null) {\n\t\t\t\tremoveStyleProperty(row, 'height');\n\t\t\t\trow.removeAttribute('data-notectl-min-height-px');\n\t\t\t} else {\n\t\t\t\tsetStyleProperty(row, 'height', `${String(height)}px`);\n\t\t\t\trow.setAttribute('data-notectl-min-height-px', String(height));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate updateResizeIndicator(value: number, clientX: number, clientY: number): void {\n\t\tconst containerRect: DOMRect = this.container.getBoundingClientRect();\n\t\tthis.resizeIndicator.textContent = `${String(Math.round(value))} px`;\n\t\tsetStyleProperties(this.resizeIndicator, {\n\t\t\tleft: `${String(clientX - containerRect.left + this.container.scrollLeft + 12)}px`,\n\t\t\ttop: `${String(clientY - containerRect.top + this.container.scrollTop + 12)}px`,\n\t\t});\n\t\tthis.resizeIndicator.classList.add('ntbl-resize-indicator--visible');\n\t}\n\n\tprivate hideResizeIndicator(): void {\n\t\tthis.resizeIndicator.classList.remove('ntbl-resize-indicator--visible');\n\t}\n\n\tprivate showHorizontalLine(border: BorderInfo): void {\n\t\tthis.activeRowIndex = border.index;\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tsetStyleProperties(this.insertLineH, {\n\t\t\ttop: `${offset.top + border.position - 1}px`,\n\t\t\tleft: `${offset.left}px`,\n\t\t\twidth: `${this.tableEl.offsetWidth}px`,\n\t\t});\n\t\tthis.insertLineH.classList.add('ntbl-insert-line--visible');\n\t}\n\n\tprivate showVerticalLine(border: BorderInfo): void {\n\t\tthis.activeColIndex = border.index;\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tsetStyleProperties(this.insertLineV, {\n\t\t\tleft: `${offset.left + border.position - 1}px`,\n\t\t\ttop: `${offset.top}px`,\n\t\t\theight: `${this.tableEl.offsetHeight}px`,\n\t\t});\n\t\tthis.insertLineV.classList.add('ntbl-insert-line--visible');\n\t}\n\n\tprivate hideInsertLines(): void {\n\t\tthis.hideHorizontalLine();\n\t\tthis.hideVerticalLine();\n\t}\n\n\tprivate hideHorizontalLine(): void {\n\t\tthis.insertLineH.classList.remove('ntbl-insert-line--visible');\n\t\tthis.activeRowIndex = -1;\n\t}\n\n\tprivate hideVerticalLine(): void {\n\t\tthis.insertLineV.classList.remove('ntbl-insert-line--visible');\n\t\tthis.activeColIndex = -1;\n\t}\n\n\tprivate rebuildColHandles(): void {\n\t\tthis.colBar.innerHTML = '';\n\t\tfor (let i = 0; i < this.numCols; i++) {\n\t\t\tconst handle: HTMLDivElement = buildHandle(\n\t\t\t\t'ntbl-col-handle',\n\t\t\t\ti,\n\t\t\t\tthis.locale.deleteColumn,\n\t\t\t\t(idx: number) => {\n\t\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\t\tbuildDeleteColumnTransaction(this.getState(), this.tableId, idx),\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: this.locale.selectColumnLabel(i),\n\t\t\t\t\tonSelect: (idx: number) => this.selectColumn(idx),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.colBar.appendChild(handle);\n\t\t}\n\t}\n\n\tprivate rebuildRowHandles(): void {\n\t\tthis.rowBar.innerHTML = '';\n\t\tfor (let i = 0; i < this.numRows; i++) {\n\t\t\tconst handle: HTMLDivElement = buildHandle(\n\t\t\t\t'ntbl-row-handle',\n\t\t\t\ti,\n\t\t\t\tthis.locale.deleteRow,\n\t\t\t\t(idx: number) => {\n\t\t\t\t\tthis.dispatchTransaction(buildDeleteRowTransaction(this.getState(), this.tableId, idx));\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: this.locale.selectRowLabel(i),\n\t\t\t\t\tonSelect: (idx: number) => this.selectRow(idx),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.rowBar.appendChild(handle);\n\t\t}\n\t}\n\n\tprivate rebuildResizeSeparators(): void {\n\t\tthis.colResizeBar.innerHTML = '';\n\t\tthis.rowResizeBar.innerHTML = '';\n\t\tif (!this.config.directResize) return;\n\t\tconst keyboardHint: string = this.locale.resizeKeyboardHint(\n\t\t\tthis.config.keyboardResizeStepPx,\n\t\t\tthis.config.keyboardResizeLargeStepPx,\n\t\t);\n\t\tfor (let index = 0; index < this.numCols; index++) {\n\t\t\tconst separator: HTMLButtonElement = buildResizeSeparator(\n\t\t\t\t'column',\n\t\t\t\tindex,\n\t\t\t\tthis.locale.resizeColumnSeparatorLabel(index),\n\t\t\t\tkeyboardHint,\n\t\t\t);\n\t\t\tseparator.addEventListener('pointerdown', (event: PointerEvent) =>\n\t\t\t\tthis.startResize(event, 'column', index),\n\t\t\t);\n\t\t\tseparator.addEventListener('keydown', (event: KeyboardEvent) =>\n\t\t\t\tthis.resizeFromKeyboard(event, 'column', index),\n\t\t\t);\n\t\t\tthis.colResizeBar.appendChild(separator);\n\t\t}\n\t\tfor (let index = 0; index < this.numRows; index++) {\n\t\t\tconst separator: HTMLButtonElement = buildResizeSeparator(\n\t\t\t\t'row',\n\t\t\t\tindex,\n\t\t\t\tthis.locale.resizeRowSeparatorLabel(index),\n\t\t\t\tkeyboardHint,\n\t\t\t);\n\t\t\tseparator.addEventListener('pointerdown', (event: PointerEvent) =>\n\t\t\t\tthis.startResize(event, 'row', index),\n\t\t\t);\n\t\t\tseparator.addEventListener('keydown', (event: KeyboardEvent) =>\n\t\t\t\tthis.resizeFromKeyboard(event, 'row', index),\n\t\t\t);\n\t\t\tthis.rowResizeBar.appendChild(separator);\n\t\t}\n\t\tthis.setReadOnly(this.readOnly);\n\t}\n\n\tprivate resizeFromKeyboard(event: KeyboardEvent, kind: 'column' | 'row', index: number): void {\n\t\tif (this.readOnly || this.pluginContext?.isReadOnly?.()) return;\n\t\tconst service: TableSizingService | undefined = this.getSizingService();\n\t\tif (!service) return;\n\t\tconst separator: HTMLButtonElement = event.currentTarget as HTMLButtonElement;\n\n\t\tif (event.key === 'Delete' || event.key === 'Backspace') {\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tconst target =\n\t\t\t\tkind === 'column'\n\t\t\t\t\t? ({ kind: 'column', tableId: this.tableId, column: index } as const)\n\t\t\t\t\t: ({ kind: 'row', tableId: this.tableId, row: index } as const);\n\t\t\tconst dimension = kind === 'column' ? 'columnWidthPx' : 'rowMinHeightPx';\n\t\t\tif (service.resetSize(target, dimension)) {\n\t\t\t\tthis.restoreKeyboardResizeFocus(separator);\n\t\t\t\tthis.pluginContext?.announce(\n\t\t\t\t\tkind === 'column'\n\t\t\t\t\t\t? this.locale.announceColumnWidthReset(index)\n\t\t\t\t\t\t: this.locale.announceRowMinimumHeightReset(index),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst decrementKey: string = kind === 'column' ? 'ArrowLeft' : 'ArrowUp';\n\t\tconst incrementKey: string = kind === 'column' ? 'ArrowRight' : 'ArrowDown';\n\t\tif (event.key !== decrementKey && event.key !== incrementKey) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tconst current: number | null = this.getRenderedDimension(kind, index);\n\t\tif (current === null) return;\n\t\tconst step: number = event.shiftKey\n\t\t\t? this.config.keyboardResizeLargeStepPx\n\t\t\t: this.config.keyboardResizeStepPx;\n\t\tlet direction: 1 | -1 = event.key === incrementKey ? 1 : -1;\n\t\tif (kind === 'column' && getComputedStyle(this.tableEl).direction === 'rtl') {\n\t\t\tdirection = direction === 1 ? -1 : 1;\n\t\t}\n\t\tconst value: number = Math.round(this.clampDimension(kind, current + step * direction));\n\t\tconst changed: boolean =\n\t\t\tkind === 'column'\n\t\t\t\t? service.setSize(\n\t\t\t\t\t\t{ kind: 'column', tableId: this.tableId, column: index },\n\t\t\t\t\t\t{ columnWidthPx: value },\n\t\t\t\t\t)\n\t\t\t\t: service.setSize(\n\t\t\t\t\t\t{ kind: 'row', tableId: this.tableId, row: index },\n\t\t\t\t\t\t{ rowMinHeightPx: value },\n\t\t\t\t\t);\n\t\tif (!changed) return;\n\t\tthis.restoreKeyboardResizeFocus(separator);\n\t\tthis.pluginContext?.announce(\n\t\t\tkind === 'column'\n\t\t\t\t? this.locale.announceColumnWidthSet(index, value)\n\t\t\t\t: this.locale.announceRowMinimumHeightSet(index, value),\n\t\t);\n\t\tthis.updateSeparatorValue(separator, kind, index, value);\n\t}\n\n\tprivate restoreKeyboardResizeFocus(separator: HTMLButtonElement): void {\n\t\tif (separator.isConnected) separator.focus({ preventScroll: true });\n\t}\n\n\tprivate selectColumn(index: number): void {\n\t\tif (this.readOnly || this.numRows === 0) return;\n\t\tthis.getSelectionService()?.setSelectedRange({\n\t\t\ttableId: this.tableId,\n\t\t\tfromRow: 0,\n\t\t\tfromCol: index,\n\t\t\ttoRow: this.numRows - 1,\n\t\t\ttoCol: index,\n\t\t});\n\t\tthis.updateHandleSelectionState();\n\t}\n\n\tprivate selectRow(index: number): void {\n\t\tif (this.readOnly || this.numCols === 0) return;\n\t\tthis.getSelectionService()?.setSelectedRange({\n\t\t\ttableId: this.tableId,\n\t\t\tfromRow: index,\n\t\t\tfromCol: 0,\n\t\t\ttoRow: index,\n\t\t\ttoCol: this.numCols - 1,\n\t\t});\n\t\tthis.updateHandleSelectionState();\n\t}\n\n\tprivate updateHandleSelectionState(): void {\n\t\tconst range = this.getSelectionService()?.getSelectedRange();\n\t\tfor (let index = 0; index < this.colBar.children.length; index++) {\n\t\t\tconst selected: boolean =\n\t\t\t\t!!range &&\n\t\t\t\trange.tableId === this.tableId &&\n\t\t\t\tMath.min(range.fromCol, range.toCol) === index &&\n\t\t\t\tMath.max(range.fromCol, range.toCol) === index &&\n\t\t\t\tMath.min(range.fromRow, range.toRow) === 0 &&\n\t\t\t\tMath.max(range.fromRow, range.toRow) === this.numRows - 1;\n\t\t\tthis.colBar.children[index]\n\t\t\t\t?.querySelector('.ntbl-handle-select')\n\t\t\t\t?.setAttribute('aria-pressed', selected ? 'true' : 'false');\n\t\t}\n\t\tfor (let index = 0; index < this.rowBar.children.length; index++) {\n\t\t\tconst selected: boolean =\n\t\t\t\t!!range &&\n\t\t\t\trange.tableId === this.tableId &&\n\t\t\t\tMath.min(range.fromRow, range.toRow) === index &&\n\t\t\t\tMath.max(range.fromRow, range.toRow) === index &&\n\t\t\t\tMath.min(range.fromCol, range.toCol) === 0 &&\n\t\t\t\tMath.max(range.fromCol, range.toCol) === this.numCols - 1;\n\t\t\tthis.rowBar.children[index]\n\t\t\t\t?.querySelector('.ntbl-handle-select')\n\t\t\t\t?.setAttribute('aria-pressed', selected ? 'true' : 'false');\n\t\t}\n\t}\n\n\tprivate getSizingService(): TableSizingService | undefined {\n\t\treturn this.pluginContext?.getService?.(TableSizingServiceKey);\n\t}\n\n\tprivate getSelectionService(): TableSelectionService | undefined {\n\t\treturn this.pluginContext?.getService?.(TableSelectionServiceKey);\n\t}\n}\n\n// --- Factory ---\n\n/** Creates table controls for the given table element. */\nexport function createTableControls(\n\tcontainer: HTMLElement,\n\ttableEl: HTMLTableElement,\n\tinitialNode: BlockNode,\n\tgetState: () => EditorState,\n\tdispatch: (tr: Transaction) => void,\n\tpluginContext?: PluginContext,\n\tlocale?: TableLocale,\n\tconfig?: Partial<TableControlsConfig>,\n): TableControlsHandle {\n\treturn new TableControls(\n\t\tcontainer,\n\t\ttableEl,\n\t\tinitialNode,\n\t\tgetState,\n\t\tdispatch,\n\t\tpluginContext,\n\t\tlocale,\n\t\tconfig,\n\t);\n}\n","/**\n * NodeViewFactories for table, table_row, and table_cell.\n * Provides custom DOM rendering with proper HTML table elements and ARIA.\n * The table NodeView includes interactive controls for row/column management.\n */\n\nimport type { BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { blockAttrsEqual, getBlockChildren } from '../../model/Document.js';\nimport type { SchemaRegistry } from '../../model/SchemaRegistry.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport { removeStyleProperty, setStyleProperty } from '../../style/StyleRuntime.js';\nimport type { NodeView, NodeViewFactory } from '../../view/NodeView.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getDeepActiveHTMLElement } from '../shared/PopupPositioning.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport {\n\ttype TableControlsConfig,\n\ttype TableControlsHandle,\n\tcreateTableControls,\n} from './TableControls.js';\nimport { createTableGrid } from './TableGrid.js';\nimport { findTableContext } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { TableSelectionServiceKey } from './TableSelection.js';\n\n/** NodeView subset of the table sizing configuration. */\nexport type TableNodeViewSizingConfig = TableControlsConfig;\n\nconst DEFAULT_SIZING_CONFIG: TableNodeViewSizingConfig = {\n\tminColumnWidthPx: 60,\n\tminRowHeightPx: 24,\n\tmaxColumnWidthPx: 10_000,\n\tmaxRowHeightPx: 10_000,\n\tkeyboardResizeStepPx: 8,\n\tkeyboardResizeLargeStepPx: 32,\n\tdirectResize: true,\n};\n\nexport interface TableNodeViewLifecycle {\n\tonControlsCreated?(controls: TableControlsHandle): void;\n\tonControlsDestroyed?(controls: TableControlsHandle): void;\n}\n\n/**\n * Creates a NodeViewFactory for the table node type.\n * Renders as outer container with controls + table + tbody.\n */\nexport function createTableNodeViewFactory(\n\t_registry: SchemaRegistry,\n\tpluginContext?: PluginContext,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n\tsizingConfig: TableNodeViewSizingConfig = DEFAULT_SIZING_CONFIG,\n\tlifecycle: TableNodeViewLifecycle = {},\n): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\tgetState: () => EditorState,\n\t\tdispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tlet currentNode: BlockNode = node;\n\t\t// Outer container: holds controls + wrapper\n\t\tconst container: HTMLDivElement = document.createElement('div');\n\t\tcontainer.className = 'ntbl-container';\n\t\tcontainer.setAttribute('data-block-id', node.id);\n\t\tcontainer.setAttribute('data-selectable', 'true');\n\n\t\t// Table wrapper: provides overflow scrolling\n\t\tconst wrapper: HTMLDivElement = document.createElement('div');\n\t\twrapper.className = 'notectl-table-wrapper';\n\t\twrapper.setAttribute('part', 'table');\n\n\t\tconst table: HTMLTableElement = document.createElement('table');\n\t\ttable.className = 'notectl-table';\n\t\ttable.setAttribute('role', 'table');\n\n\t\tconst colgroup: HTMLTableColElement = document.createElement('colgroup');\n\t\tcolgroup.className = 'notectl-table-columns';\n\t\ttable.appendChild(colgroup);\n\n\t\tconst rows: readonly BlockNode[] = getBlockChildren(node);\n\t\tconst totalRows: number = rows.length;\n\t\tconst totalCols: number = createTableGrid(node).columnCount;\n\t\ttable.setAttribute('aria-label', locale.tableAriaLabel(totalRows, totalCols));\n\t\ttable.setAttribute('aria-description', locale.tableAriaDescription);\n\n\t\tconst tbody: HTMLTableSectionElement = document.createElement('tbody');\n\t\ttable.appendChild(tbody);\n\t\twrapper.appendChild(table);\n\t\tcontainer.appendChild(wrapper);\n\n\t\t// Apply border color CSS variable from node attrs\n\t\tapplyBorderColor(table, node.attrs?.borderColor as string | undefined);\n\t\tapplyColumnDimensions(table, colgroup, node, sizingConfig);\n\n\t\t// Live region for screen reader announcements\n\t\tconst liveRegion: HTMLDivElement = document.createElement('div');\n\t\tliveRegion.className = 'notectl-sr-only';\n\t\tliveRegion.setAttribute('aria-live', 'polite');\n\t\tliveRegion.setAttribute('aria-atomic', 'true');\n\t\tcontainer.appendChild(liveRegion);\n\n\t\t// Initialize interactive controls\n\t\tconst controls: TableControlsHandle = createTableControls(\n\t\t\tcontainer,\n\t\t\ttable,\n\t\t\tnode,\n\t\t\tgetState,\n\t\t\tdispatch,\n\t\t\tpluginContext,\n\t\t\tlocale,\n\t\t\tsizingConfig,\n\t\t);\n\t\tcontrols.setReadOnly(pluginContext?.isReadOnly?.() ?? false);\n\t\tlifecycle.onControlsCreated?.(controls);\n\t\tlet destroyed = false;\n\t\tconst mountedStyleFrame: number = requestAnimationFrame(() => {\n\t\t\tif (destroyed) return;\n\t\t\t// NodeViews are constructed before insertion into the registered style\n\t\t\t// root. Re-applying after mount migrates fallback inline declarations to\n\t\t\t// CSP-safe runtime tokens through StyleRuntime.\n\t\t\tapplyBorderColor(table, currentNode.attrs?.borderColor as string | undefined);\n\t\t\tapplyColumnDimensions(table, colgroup, currentNode, sizingConfig);\n\t\t\tapplyRenderedRowHeights(tbody, currentNode);\n\t\t});\n\n\t\t// Context menu on right-click\n\t\tlet activeContextMenu: TableContextMenuHandle | null = null;\n\t\tlet currentTableId = node.id;\n\n\t\tconst onContextMenu = (e: MouseEvent): void => {\n\t\t\tif (!pluginContext || pluginContext.isReadOnly?.()) return;\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\n\t\t\tactiveContextMenu?.close();\n\n\t\t\tconst rect: DOMRect = new DOMRect(e.clientX, e.clientY, 0, 0);\n\t\t\tconst cell = (e.target as Element | null)?.closest('td[data-block-id]');\n\t\t\tconst cellId = cell?.getAttribute('data-block-id');\n\t\t\tconst tableContext = cellId\n\t\t\t\t? findTableContext(pluginContext.getState(), cellId as BlockId)\n\t\t\t\t: null;\n\t\t\tconst selectedCell: boolean = cellId\n\t\t\t\t? (pluginContext.getService?.(TableSelectionServiceKey)?.isSelected(cellId as BlockId) ??\n\t\t\t\t\tfalse)\n\t\t\t\t: false;\n\t\t\tconst restoreFocusTo: HTMLElement | null =\n\t\t\t\tgetDeepActiveHTMLElement(container) ?? (cell instanceof HTMLElement ? cell : null);\n\t\t\tactiveContextMenu = createTableContextMenu(\n\t\t\t\tcontainer,\n\t\t\t\tpluginContext,\n\t\t\t\tcurrentTableId,\n\t\t\t\trect,\n\t\t\t\t() => {\n\t\t\t\t\tactiveContextMenu = null;\n\t\t\t\t},\n\t\t\t\tlocale,\n\t\t\t\tundefined,\n\t\t\t\t{\n\t\t\t\t\tsizeTarget:\n\t\t\t\t\t\ttableContext && !selectedCell\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tkind: 'cell',\n\t\t\t\t\t\t\t\t\ttableId: tableContext.tableId,\n\t\t\t\t\t\t\t\t\trow: tableContext.rowIndex,\n\t\t\t\t\t\t\t\t\tcolumn: tableContext.colIndex,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tsizingConfig,\n\t\t\t\t\trestoreFocusTo,\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\t\ttable.addEventListener('contextmenu', onContextMenu);\n\n\t\treturn {\n\t\t\tdom: container,\n\t\t\tcontentDOM: tbody,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table') return false;\n\t\t\t\tconst canUpdateInPlace: boolean = tableContentIsStable(currentNode, updatedNode);\n\t\t\t\tif (!canUpdateInPlace) return false;\n\n\t\t\t\tcurrentTableId = updatedNode.id;\n\t\t\t\tcontainer.setAttribute('data-block-id', updatedNode.id);\n\t\t\t\tcontainer.setAttribute('data-selectable', 'true');\n\t\t\t\tconst updatedRows: readonly BlockNode[] = getBlockChildren(updatedNode);\n\t\t\t\tconst newTotalRows: number = updatedRows.length;\n\t\t\t\tconst newTotalCols: number = createTableGrid(updatedNode).columnCount;\n\t\t\t\ttable.setAttribute('aria-label', locale.tableAriaLabel(newTotalRows, newTotalCols));\n\n\t\t\t\t// Update border color CSS variable\n\t\t\t\tapplyBorderColor(table, updatedNode.attrs?.borderColor as string | undefined);\n\t\t\t\tapplyColumnDimensions(table, colgroup, updatedNode, sizingConfig);\n\t\t\t\tapplyRenderedRowHeights(tbody, updatedNode);\n\n\t\t\t\t// Update controls to reflect new structure\n\t\t\t\tcontrols.update(updatedNode);\n\t\t\t\tcurrentNode = updatedNode;\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\tdestroyed = true;\n\t\t\t\tcancelAnimationFrame(mountedStyleFrame);\n\t\t\t\tactiveContextMenu?.destroy();\n\t\t\t\ttable.removeEventListener('contextmenu', onContextMenu);\n\t\t\t\tlifecycle.onControlsDestroyed?.(controls);\n\t\t\t\tcontrols.destroy();\n\t\t\t},\n\t\t\tselectNode(): void {\n\t\t\t\tcontainer.classList.add('notectl-table--selected');\n\t\t\t},\n\t\t\tdeselectNode(): void {\n\t\t\t\tcontainer.classList.remove('notectl-table--selected');\n\t\t\t},\n\t\t};\n\t};\n}\n\n/** Applies border color CSS variable and borderless class to a table element. */\nfunction applyBorderColor(table: HTMLTableElement, borderColor: string | undefined): void {\n\tif (borderColor === 'none') {\n\t\tsetStyleProperty(table, '--ntbl-border-color', 'transparent');\n\t\ttable.classList.add('notectl-table--borderless');\n\t} else if (borderColor) {\n\t\tsetStyleProperty(table, '--ntbl-border-color', borderColor);\n\t\ttable.classList.remove('notectl-table--borderless');\n\t} else {\n\t\tremoveStyleProperty(table, '--ntbl-border-color');\n\t\ttable.classList.remove('notectl-table--borderless');\n\t}\n}\n\n/**\n * Creates a NodeViewFactory for the table_row node type.\n * Renders as `<tr role=\"row\">`.\n */\nexport function createTableRowNodeViewFactory(_registry: SchemaRegistry): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\t_getState: () => EditorState,\n\t\t_dispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tlet currentNode: BlockNode = node;\n\t\tconst tr: HTMLTableRowElement = document.createElement('tr');\n\t\ttr.setAttribute('data-block-id', node.id);\n\t\ttr.setAttribute('role', 'row');\n\t\ttr.setAttribute('part', 'table-row');\n\t\tapplyRowMinimumHeight(tr, node);\n\t\tlet destroyed = false;\n\t\tconst mountedStyleFrame: number = requestAnimationFrame(() => {\n\t\t\tif (!destroyed) applyRowMinimumHeight(tr, currentNode);\n\t\t});\n\n\t\treturn {\n\t\t\tdom: tr,\n\t\t\tcontentDOM: tr,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table_row') return false;\n\t\t\t\tif (updatedNode.children !== currentNode.children) return false;\n\t\t\t\ttr.setAttribute('data-block-id', updatedNode.id);\n\t\t\t\tapplyRowMinimumHeight(tr, updatedNode);\n\t\t\t\tcurrentNode = updatedNode;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\tdestroyed = true;\n\t\t\t\tcancelAnimationFrame(mountedStyleFrame);\n\t\t\t},\n\t\t};\n\t};\n}\n\n/** Applies canonical widths through a semantic `<colgroup>` and table minimum width. */\nexport function applyColumnDimensions(\n\ttable: HTMLTableElement,\n\tcolgroup: HTMLTableColElement,\n\tnode: BlockNode,\n\tconfig: TableNodeViewSizingConfig = DEFAULT_SIZING_CONFIG,\n): void {\n\tconst columnCount: number = createTableGrid(node).columnCount;\n\tsetStyleProperty(table, '--ntbl-min-column-width', `${String(config.minColumnWidthPx)}px`);\n\twhile (colgroup.children.length > columnCount) colgroup.lastElementChild?.remove();\n\twhile (colgroup.children.length < columnCount) {\n\t\tcolgroup.appendChild(document.createElement('col'));\n\t}\n\n\tconst rawWidths: unknown = node.attrs?.columnWidthsPx;\n\tconst widths: readonly unknown[] = Array.isArray(rawWidths) ? rawWidths : [];\n\tlet minimumTableWidth = 0;\n\tfor (let column = 0; column < columnCount; column++) {\n\t\tconst col = colgroup.children[column] as HTMLTableColElement | undefined;\n\t\tif (!col) continue;\n\t\tcol.removeAttribute('width');\n\t\tconst rawWidth: unknown = widths[column];\n\t\tconst explicitWidth: number | undefined =\n\t\t\ttypeof rawWidth === 'number' && Number.isFinite(rawWidth) && rawWidth > 0\n\t\t\t\t? rawWidth\n\t\t\t\t: undefined;\n\t\tif (explicitWidth !== undefined) {\n\t\t\tsetStyleProperty(col, 'width', `${String(explicitWidth)}px`);\n\t\t\tcol.setAttribute('data-notectl-width-px', String(explicitWidth));\n\t\t} else {\n\t\t\tremoveStyleProperty(col, 'width');\n\t\t\tcol.removeAttribute('data-notectl-width-px');\n\t\t}\n\t\tminimumTableWidth += explicitWidth ?? config.minColumnWidthPx;\n\t}\n\tif (columnCount > 0) {\n\t\tsetStyleProperty(table, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\tif (\n\t\t\twidths.length >= columnCount &&\n\t\t\twidths.slice(0, columnCount).every((width) => typeof width === 'number')\n\t\t) {\n\t\t\tsetStyleProperty(table, 'width', `${String(minimumTableWidth)}px`);\n\t\t} else {\n\t\t\tremoveStyleProperty(table, 'width');\n\t\t}\n\t} else {\n\t\tremoveStyleProperty(table, 'minWidth');\n\t\tremoveStyleProperty(table, 'width');\n\t}\n}\n\n/** Applies a row's table-semantic minimum height (`height` grows with content). */\nexport function applyRowMinimumHeight(row: HTMLTableRowElement, node: BlockNode): void {\n\tconst rawHeight: unknown = node.attrs?.minHeightPx;\n\trow.removeAttribute('height');\n\tif (typeof rawHeight === 'number' && Number.isFinite(rawHeight) && rawHeight > 0) {\n\t\tsetStyleProperty(row, 'height', `${String(rawHeight)}px`);\n\t\trow.setAttribute('data-notectl-min-height-px', String(rawHeight));\n\t} else {\n\t\tremoveStyleProperty(row, 'height');\n\t\trow.removeAttribute('data-notectl-min-height-px');\n\t}\n}\n\nfunction applyRenderedRowHeights(tbody: HTMLTableSectionElement, tableNode: BlockNode): void {\n\tconst renderedRows: readonly HTMLTableRowElement[] = Array.from(\n\t\ttbody.querySelectorAll(':scope > tr'),\n\t);\n\tfor (const rowNode of getBlockChildren(tableNode)) {\n\t\tconst row = renderedRows.find(\n\t\t\t(candidate) => candidate.getAttribute('data-block-id') === rowNode.id,\n\t\t);\n\t\tif (row) applyRowMinimumHeight(row, rowNode);\n\t}\n}\n\n/** True when only table sizing/border attrs or row minimum heights changed. */\nfunction tableContentIsStable(previous: BlockNode, next: BlockNode): boolean {\n\tif (previous.id !== next.id || previous.htmlId !== next.htmlId) return false;\n\tif (!blockAttrsEqual(withoutTableViewAttrs(previous.attrs), withoutTableViewAttrs(next.attrs))) {\n\t\treturn false;\n\t}\n\tconst previousRows: readonly BlockNode[] = getBlockChildren(previous);\n\tconst nextRows: readonly BlockNode[] = getBlockChildren(next);\n\tif (previousRows.length !== nextRows.length) return false;\n\treturn previousRows.every((previousRow, index) => {\n\t\tconst nextRow: BlockNode | undefined = nextRows[index];\n\t\treturn (\n\t\t\tnextRow !== undefined &&\n\t\t\tpreviousRow.id === nextRow.id &&\n\t\t\tpreviousRow.htmlId === nextRow.htmlId &&\n\t\t\tpreviousRow.children === nextRow.children &&\n\t\t\tblockAttrsEqual(withoutRowHeight(previousRow.attrs), withoutRowHeight(nextRow.attrs))\n\t\t);\n\t});\n}\n\nfunction withoutTableViewAttrs(attrs: BlockAttrs | undefined): BlockAttrs | undefined {\n\tif (!attrs) return undefined;\n\tconst { borderColor: _borderColor, columnWidthsPx: _columnWidthsPx, ...rest } = attrs;\n\treturn Object.keys(rest).length > 0 ? rest : undefined;\n}\n\nfunction withoutRowHeight(attrs: BlockAttrs | undefined): BlockAttrs | undefined {\n\tif (!attrs) return undefined;\n\tconst { minHeightPx: _minHeightPx, ...rest } = attrs;\n\treturn Object.keys(rest).length > 0 ? rest : undefined;\n}\n\n/**\n * Creates a NodeViewFactory for the table_cell node type.\n * Renders as `<td role=\"cell\">` with text content rendered inside.\n */\nexport function createTableCellNodeViewFactory(_registry: SchemaRegistry): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\t_getState: () => EditorState,\n\t\t_dispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tconst td: HTMLTableCellElement = document.createElement('td');\n\t\ttd.setAttribute('data-block-id', node.id);\n\t\ttd.setAttribute('role', 'cell');\n\t\ttd.setAttribute('part', 'table-cell');\n\n\t\tconst colspan: number = (node.attrs?.colspan as number | undefined) ?? 1;\n\t\tconst rowspan: number = (node.attrs?.rowspan as number | undefined) ?? 1;\n\t\tif (colspan > 1) td.colSpan = colspan;\n\t\tif (rowspan > 1) td.rowSpan = rowspan;\n\n\t\t// Apply alignment from AlignmentPlugin's patched attribute\n\t\tconst align: string | undefined = node.attrs?.align as string | undefined;\n\t\tif (align && align !== 'left') {\n\t\t\tsetStyleProperty(td, 'textAlign', align);\n\t\t}\n\n\t\treturn {\n\t\t\tdom: td,\n\t\t\tcontentDOM: td,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table_cell') return false;\n\t\t\t\ttd.setAttribute('data-block-id', updatedNode.id);\n\n\t\t\t\tconst newColspan: number = (updatedNode.attrs?.colspan as number | undefined) ?? 1;\n\t\t\t\tconst newRowspan: number = (updatedNode.attrs?.rowspan as number | undefined) ?? 1;\n\t\t\t\tif (newColspan > 1) {\n\t\t\t\t\ttd.colSpan = newColspan;\n\t\t\t\t} else {\n\t\t\t\t\ttd.removeAttribute('colspan');\n\t\t\t\t}\n\t\t\t\tif (newRowspan > 1) {\n\t\t\t\t\ttd.rowSpan = newRowspan;\n\t\t\t\t} else {\n\t\t\t\t\ttd.removeAttribute('rowspan');\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\t// No cleanup needed\n\t\t\t},\n\t\t};\n\t};\n}\n","/**\n * TablePlugin: registers table, table_row, and table_cell node types\n * with NodeSpecs, NodeViews, commands, keyboard navigation, toolbar\n * grid picker, multi-cell selection, and context menu.\n */\n\nimport type { Decoration, DecorationSet } from '../../decorations/Decoration.js';\nimport { node as nodeDecoration } from '../../decorations/Decoration.js';\nimport { DecorationSet as DecorationSetClass } from '../../decorations/Decoration.js';\nimport { TABLE_CSS } from '../../editor/styles/table.js';\nimport type { BlockAttrValue, BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { getBlockChildren } from '../../model/Document.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport type { HTMLExportContext } from '../../model/NodeSpec.js';\nimport { isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport {\n\tTABLE_COLUMN_WIDTH_DATA_ATTRIBUTE,\n\tTABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE,\n\tnormalizeSerializedTableDimensionPx,\n\tserializeTableDimensionAttrs,\n} from '../../serialization/TableDimensions.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isValidHexColor } from '../shared/ColorValidation.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { resetTableBorderColor } from './TableBorderColor.js';\nimport { installTableClipboard } from './TableClipboard.js';\nimport { insertTable, registerTableCommands } from './TableCommands.js';\nimport { closeTableContextMenus } from './TableContextMenu.js';\nimport type { TableControlsHandle } from './TableControls.js';\nimport {\n\tcreateTableGrid,\n\tnormalizeTableSpan,\n\tprojectTableColumnWidthsForSlice,\n} from './TableGrid.js';\nimport { isInsideTable } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale, loadTableLocale } from './TableLocale.js';\nimport { registerTableKeymaps } from './TableNavigation.js';\nimport {\n\tcreateTableCellNodeViewFactory,\n\tcreateTableNodeViewFactory,\n\tcreateTableRowNodeViewFactory,\n} from './TableNodeViews.js';\nimport {\n\ttype TableSelectionService,\n\tcreateTableSelectionService,\n\tinstallMouseSelection,\n} from './TableSelection.js';\nimport {\n\tDEFAULT_TABLE_SIZING_CONFIG,\n\ttype TableSizingConfig,\n\tcreateTableSizingService,\n\treadTableColumnWidthsPx,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\ttable: { borderColor?: string; columnWidthsPx?: readonly (number | null)[] };\n\t\ttable_row: { minHeightPx?: number };\n\t\ttable_cell: { colspan?: number; rowspan?: number };\n\t}\n}\n\n// --- Configuration ---\n\nexport interface TableConfig {\n\t/** Maximum rows in grid picker. Defaults to 8. */\n\treadonly maxPickerRows?: number;\n\t/** Maximum columns in grid picker. Defaults to 8. */\n\treadonly maxPickerCols?: number;\n\t/** Locale for all user-facing strings. Defaults to English. */\n\treadonly locale?: TableLocale;\n\t/** Minimum logical column width in CSS pixels. Defaults to 60. */\n\treadonly minColumnWidthPx?: number;\n\t/** Minimum logical row height in CSS pixels. Defaults to 24. */\n\treadonly minRowHeightPx?: number;\n\t/** Maximum accepted logical column width in CSS pixels. Defaults to 10000. */\n\treadonly maxColumnWidthPx?: number;\n\t/** Maximum accepted logical row height in CSS pixels. Defaults to 10000. */\n\treadonly maxRowHeightPx?: number;\n\t/** Small keyboard resize step in CSS pixels. Defaults to 8. */\n\treadonly keyboardResizeStepPx?: number;\n\t/** Shift-modified keyboard resize step in CSS pixels. Defaults to 32. */\n\treadonly keyboardResizeLargeStepPx?: number;\n\t/** Enables pointer and separator-key resizing. Precise UI/API remain available. Defaults to true. */\n\treadonly directResize?: boolean;\n}\n\ninterface ResolvedTableConfig extends TableSizingConfig {\n\treadonly maxPickerRows: number;\n\treadonly maxPickerCols: number;\n\treadonly locale?: TableLocale;\n\treadonly directResize: boolean;\n}\n\nconst DEFAULT_CONFIG: ResolvedTableConfig = {\n\tmaxPickerRows: 8,\n\tmaxPickerCols: 8,\n\t...DEFAULT_TABLE_SIZING_CONFIG,\n\tdirectResize: true,\n};\n\n// --- SVG Icon ---\n\nconst TABLE_ICON =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">' +\n\t'<path d=\"M3 3h18v18H3V3zm2 2v4h6V5H5zm8 0v4h6V5h-6zm-8 6v4h6v-4H5z' +\n\t'm8 0v4h6v-4h-6zm-8 6v4h6v-4H5zm8 0v4h6v-4h-6z\"/></svg>';\n\n// --- Serialization Constants ---\n\nconst DEFAULT_BORDER_COLOR = '#d0d0d0';\nconst TABLE_BASE_STYLE = 'border-collapse: collapse; table-layout: fixed';\nconst CELL_STYLE = `border: 1px solid var(--ntbl-bc, ${DEFAULT_BORDER_COLOR}); padding: 8px 12px; vertical-align: top`;\n\n// --- Serialization Helpers ---\n\n/** Builds the inline `style` attribute value for the `<table>` element. */\nfunction buildTableStyle(\n\tborderColor: string | undefined,\n\twidths: readonly (number | null)[],\n\tminimumColumnWidthPx: number,\n): string {\n\tconst minimumWidthPx: number = widths.reduce<number>(\n\t\t(total, width) => total + (width ?? minimumColumnWidthPx),\n\t\t0,\n\t);\n\tconst allColumnsExplicit: boolean =\n\t\twidths.length > 0 && widths.every((width): width is number => width !== null);\n\tconst widthStyle: string = allColumnsExplicit\n\t\t? `width: ${String(minimumWidthPx)}px`\n\t\t: 'width: 100%';\n\tconst baseStyle = `${TABLE_BASE_STYLE}; ${widthStyle}; min-width: ${String(minimumWidthPx)}px`;\n\tif (!borderColor) return baseStyle;\n\tif (borderColor === 'none') {\n\t\treturn `${baseStyle}; --ntbl-bc: transparent`;\n\t}\n\tif (isValidHexColor(borderColor)) {\n\t\treturn `${baseStyle}; --ntbl-bc: ${escapeHTML(borderColor)}`;\n\t}\n\treturn baseStyle;\n}\n\n// --- Plugin ---\n\nexport class TablePlugin implements Plugin {\n\treadonly id = 'table';\n\treadonly name = 'Table';\n\treadonly priority = 40;\n\n\tprivate readonly config: ResolvedTableConfig;\n\tprivate locale!: TableLocale;\n\tprivate selectionService: TableSelectionService | null = null;\n\tprivate cleanupMouseSelection: (() => void) | null = null;\n\tprivate cleanupClipboard: (() => void) | null = null;\n\tprivate context: PluginContext | null = null;\n\tprivate readonly controls: Set<TableControlsHandle> = new Set();\n\n\tconstructor(config?: Partial<TableConfig>) {\n\t\tconst sizing: TableSizingConfig = resolveTableSizingConfig(config ?? {});\n\t\tthis.config = {\n\t\t\t...DEFAULT_CONFIG,\n\t\t\t...config,\n\t\t\t...sizing,\n\t\t\tmaxPickerRows: normalizePickerSize(config?.maxPickerRows, DEFAULT_CONFIG.maxPickerRows),\n\t\t\tmaxPickerCols: normalizePickerSize(config?.maxPickerCols, DEFAULT_CONFIG.maxPickerCols),\n\t\t\tdirectResize: config?.directResize ?? true,\n\t\t};\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tTABLE_LOCALE_EN,\n\t\t\tloadTableLocale,\n\t\t);\n\t\tcontext.registerStyleSheet(TABLE_CSS);\n\t\tthis.context = context;\n\n\t\tthis.registerNodeSpecs(context);\n\t\tthis.registerNodeViews(context);\n\t\tregisterTableCommands(context, this.locale);\n\t\tcontext.registerCommand('resetTableBorderColor', () =>\n\t\t\tresetTableBorderColor(context, this.locale),\n\t\t);\n\t\tregisterTableKeymaps(context, this.locale, this.config);\n\t\tthis.registerToolbarItem(context);\n\t\tthis.selectionService = createTableSelectionService(context);\n\t\tcreateTableSizingService(context, this.config);\n\t}\n\n\tonReady(): void {\n\t\tif (this.context && this.selectionService) {\n\t\t\tthis.cleanupMouseSelection = installMouseSelection(this.context, this.selectionService);\n\t\t\tthis.cleanupClipboard = installTableClipboard(this.context, this.selectionService);\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tif (this.context) closeTableContextMenus(this.context);\n\t\tthis.cleanupMouseSelection?.();\n\t\tthis.cleanupMouseSelection = null;\n\t\tthis.cleanupClipboard?.();\n\t\tthis.cleanupClipboard = null;\n\t\tthis.selectionService = null;\n\t\tthis.controls.clear();\n\t\tthis.context = null;\n\t}\n\n\tonReadOnlyChange(readonly: boolean): void {\n\t\tfor (const controls of this.controls) controls.setReadOnly(readonly);\n\t\tif (readonly && this.context) closeTableContextMenus(this.context);\n\t}\n\n\tdecorations(state: EditorState): DecorationSet {\n\t\tif (!this.selectionService) return DecorationSetClass.empty;\n\n\t\tconst range = this.selectionService.getSelectedRange();\n\t\tif (!range) return DecorationSetClass.empty;\n\n\t\t// Side effect: clear stale selection when cursor leaves the table.\n\t\t// Uses clearSelectionSilent() (no dispatch) to avoid re-entrancy,\n\t\t// since decorations() runs inside the update cycle.\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel) || !isInsideTable(state, sel.anchor.blockId)) {\n\t\t\tthis.selectionService.clearSelectionSilent();\n\t\t\treturn DecorationSetClass.empty;\n\t\t}\n\n\t\tconst cellIds: readonly BlockId[] = this.selectionService.getSelectedCellIds();\n\t\tif (cellIds.length === 0) return DecorationSetClass.empty;\n\n\t\tconst decorations: Decoration[] = [];\n\t\tfor (const cellId of cellIds) {\n\t\t\tdecorations.push(nodeDecoration(cellId, { class: 'notectl-table-cell--selected' }));\n\t\t}\n\n\t\treturn DecorationSetClass.create(decorations);\n\t}\n\n\tprivate registerNodeSpecs(context: PluginContext): void {\n\t\tconst config: ResolvedTableConfig = this.config;\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table',\n\t\t\tattrs: {\n\t\t\t\tcolumnWidthsPx: { allowArray: true },\n\t\t\t},\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['table_row'], min: 1 },\n\t\t\tisolating: true,\n\t\t\tselectable: true,\n\t\t\tnormalizeAttrs: (node: BlockNode) => normalizeTableAttrs(node, this.config),\n\t\t\tnormalizeNode: normalizeTableStructure,\n\t\t\ttransformSelectionSlice: projectTableColumnWidthsForSlice,\n\t\t\ttoDOM(node) {\n\t\t\t\tconst wrapper: HTMLDivElement = document.createElement('div');\n\t\t\t\twrapper.className = 'notectl-table-wrapper';\n\t\t\t\twrapper.setAttribute('data-block-id', node.id);\n\t\t\t\twrapper.setAttribute('part', 'table');\n\t\t\t\treturn wrapper;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst borderColor: string | undefined = node.attrs?.borderColor as string | undefined;\n\t\t\t\tconst grid = createTableGrid(node);\n\t\t\t\tconst widths = readTableColumnWidthsPx(node, grid.columnCount);\n\t\t\t\tconst style: string = buildTableStyle(borderColor, widths, config.minColumnWidthPx);\n\t\t\t\tconst attr: string = ctx?.styleAttr(style) ?? ` style=\"${style}\"`;\n\t\t\t\tconst columns: string = widths\n\t\t\t\t\t.map((width) =>\n\t\t\t\t\t\twidth === null\n\t\t\t\t\t\t\t? '<col>'\n\t\t\t\t\t\t\t: `<col${serializeTableDimensionAttrs(TABLE_COLUMN_WIDTH_DATA_ATTRIBUTE, 'width', width, ctx)}>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('');\n\t\t\t\treturn `<table${attr}><colgroup>${columns}</colgroup><tbody>${content}</tbody></table>`;\n\t\t\t},\n\t\t\tsanitize: {\n\t\t\t\ttags: ['table', 'colgroup', 'col', 'thead', 'tbody', 'tfoot'],\n\t\t\t\tattrs: [TABLE_COLUMN_WIDTH_DATA_ATTRIBUTE, 'width', 'span'],\n\t\t\t},\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table_row',\n\t\t\tgroup: 'table_content',\n\t\t\tcontent: { allow: ['table_cell'], min: 0 },\n\t\t\tnormalizeAttrs: (node: BlockNode) => normalizeTableRowAttrs(node, this.config),\n\t\t\ttoDOM(node) {\n\t\t\t\tconst tr: HTMLTableRowElement = document.createElement('tr');\n\t\t\t\ttr.setAttribute('data-block-id', node.id);\n\t\t\t\ttr.setAttribute('role', 'row');\n\t\t\t\ttr.setAttribute('part', 'table-row');\n\t\t\t\treturn tr;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst attrs: string = serializeTableDimensionAttrs(\n\t\t\t\t\tTABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE,\n\t\t\t\t\t'height',\n\t\t\t\t\tnode.attrs?.minHeightPx,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\treturn `<tr${attrs}>${content}</tr>`;\n\t\t\t},\n\t\t\tsanitize: {\n\t\t\t\ttags: ['tr'],\n\t\t\t\tattrs: [TABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE, 'height'],\n\t\t\t},\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table_cell',\n\t\t\tgroup: 'table_content',\n\t\t\tcontent: {\n\t\t\t\tallow: [\n\t\t\t\t\t'paragraph',\n\t\t\t\t\t'list_item',\n\t\t\t\t\t'heading',\n\t\t\t\t\t'blockquote',\n\t\t\t\t\t'image',\n\t\t\t\t\t'video',\n\t\t\t\t\t'horizontal_rule',\n\t\t\t\t],\n\t\t\t\tmin: 1,\n\t\t\t},\n\t\t\tisolating: true,\n\t\t\tnormalizeAttrs: normalizeTableCellAttrs,\n\t\t\ttoDOM(node) {\n\t\t\t\tconst td: HTMLTableCellElement = document.createElement('td');\n\t\t\t\ttd.setAttribute('data-block-id', node.id);\n\t\t\t\ttd.setAttribute('role', 'cell');\n\t\t\t\ttd.setAttribute('part', 'table-cell');\n\t\t\t\treturn td;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst colspan: number = (node.attrs?.colspan as number) ?? 1;\n\t\t\t\tconst rowspan: number = (node.attrs?.rowspan as number) ?? 1;\n\t\t\t\tconst styleAttr: string = ctx?.styleAttr(CELL_STYLE) ?? ` style=\"${CELL_STYLE}\"`;\n\t\t\t\tconst attrs: string[] = [styleAttr];\n\t\t\t\tif (colspan > 1) attrs.push(` colspan=\"${colspan}\"`);\n\t\t\t\tif (rowspan > 1) attrs.push(` rowspan=\"${rowspan}\"`);\n\t\t\t\treturn `<td${attrs.join('')}>${content}</td>`;\n\t\t\t},\n\t\t\tsanitize: { tags: ['td', 'th'], attrs: ['colspan', 'rowspan'] },\n\t\t});\n\t}\n\n\tprivate registerNodeViews(context: PluginContext): void {\n\t\tconst registry = context.getSchemaRegistry();\n\n\t\tcontext.registerNodeView(\n\t\t\t'table',\n\t\t\tcreateTableNodeViewFactory(registry, context, this.locale, this.config, {\n\t\t\t\tonControlsCreated: (controls: TableControlsHandle): void => {\n\t\t\t\t\tthis.controls.add(controls);\n\t\t\t\t},\n\t\t\t\tonControlsDestroyed: (controls: TableControlsHandle): void => {\n\t\t\t\t\tthis.controls.delete(controls);\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tcontext.registerNodeView('table_row', createTableRowNodeViewFactory(registry));\n\t\tcontext.registerNodeView('table_cell', createTableCellNodeViewFactory(registry));\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst maxRows: number = this.config.maxPickerRows ?? 8;\n\t\tconst maxCols: number = this.config.maxPickerCols ?? 8;\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'table',\n\t\t\tgroup: 'insert',\n\t\t\ticon: TABLE_ICON,\n\t\t\tlabel: this.locale.insertTable,\n\t\t\ttooltip: this.locale.insertTable,\n\t\t\tcommand: 'insertTable',\n\t\t\tpopupType: 'gridPicker',\n\t\t\tpopupConfig: {\n\t\t\t\tmaxRows,\n\t\t\t\tmaxCols,\n\t\t\t\tonSelect: (rows: number, cols: number) => {\n\t\t\t\t\tinsertTable(context, rows, cols);\n\t\t\t\t},\n\t\t\t},\n\t\t\tisActive: (state: EditorState) => {\n\t\t\t\tif (!isTextSelection(state.selection)) return false;\n\t\t\t\treturn isInsideTable(state, state.selection.anchor.blockId);\n\t\t\t},\n\t\t});\n\t}\n}\n\nfunction normalizePickerSize(value: unknown, fallback: number): number {\n\treturn typeof value === 'number' && Number.isInteger(value) && value > 0\n\t\t? Math.min(value, 100)\n\t\t: fallback;\n}\n\nfunction normalizeTableAttrs(node: BlockNode, config: TableSizingConfig): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, [\n\t\t'borderColor',\n\t\t'columnWidthsPx',\n\t]);\n\tconst borderColor: unknown = node.attrs?.borderColor;\n\tif (borderColor === 'none' || (typeof borderColor === 'string' && isValidHexColor(borderColor))) {\n\t\tattrs.borderColor = borderColor;\n\t}\n\n\tconst grid = createTableGrid(node);\n\tconst rawWidths: unknown = node.attrs?.columnWidthsPx;\n\tif (Array.isArray(rawWidths) && grid.columnCount > 0) {\n\t\tconst widths: (number | null)[] = Array.from(\n\t\t\t{ length: grid.columnCount },\n\t\t\t(_unused, column): number | null => {\n\t\t\t\tconst parsed: number | undefined = normalizeSerializedTableDimensionPx(rawWidths[column]);\n\t\t\t\tif (parsed === undefined) return null;\n\t\t\t\treturn Math.min(config.maxColumnWidthPx, Math.max(config.minColumnWidthPx, parsed));\n\t\t\t},\n\t\t);\n\t\tif (widths.some((width) => width !== null)) {\n\t\t\tattrs.columnWidthsPx = Object.freeze(widths);\n\t\t}\n\t}\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\nfunction normalizeTableRowAttrs(\n\tnode: BlockNode,\n\tconfig: TableSizingConfig,\n): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, ['minHeightPx']);\n\tconst parsed: number | undefined = normalizeSerializedTableDimensionPx(node.attrs?.minHeightPx);\n\tif (parsed !== undefined) {\n\t\tattrs.minHeightPx = Math.min(config.maxRowHeightPx, Math.max(config.minRowHeightPx, parsed));\n\t}\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\nfunction normalizeTableCellAttrs(node: BlockNode): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, ['colspan', 'rowspan']);\n\tconst colspan: number = normalizeTableSpan(node.attrs?.colspan);\n\tconst rowspan: number = normalizeTableSpan(node.attrs?.rowspan);\n\tif (colspan > 1) attrs.colspan = colspan;\n\tif (rowspan > 1) attrs.rowspan = rowspan;\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\n/** Clamps row spans to the rows that actually exist, preventing latent spans after insertions. */\nfunction normalizeTableStructure(table: BlockNode): BlockNode {\n\tconst rows: readonly BlockNode[] = getBlockChildren(table);\n\tlet changed = false;\n\tconst normalizedRows: readonly BlockNode[] = rows.map((row: BlockNode, rowIndex: number) => {\n\t\tlet rowChanged = false;\n\t\tconst cells: readonly BlockNode[] = getBlockChildren(row).map((cell: BlockNode) => {\n\t\t\tconst requested: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst effective: number = Math.min(requested, rows.length - rowIndex);\n\t\t\tif (effective === requested) return cell;\n\t\t\trowChanged = true;\n\t\t\tconst { rowspan: _rowspan, ...rest } = cell.attrs ?? {};\n\t\t\tconst attrs: MutableBlockAttrs = {\n\t\t\t\t...rest,\n\t\t\t\t...(effective > 1 ? { rowspan: effective } : {}),\n\t\t\t};\n\t\t\tconst { attrs: _attrs, ...withoutAttrs } = cell;\n\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t});\n\t\tif (!rowChanged) return row;\n\t\tchanged = true;\n\t\treturn { ...row, children: cells };\n\t});\n\treturn changed ? { ...table, children: normalizedRows } : table;\n}\n\nfunction copyUnrelatedAttrs(\n\tattrs: BlockAttrs | undefined,\n\texcluded: readonly string[],\n): MutableBlockAttrs {\n\tconst result: MutableBlockAttrs = {};\n\tfor (const [key, value] of Object.entries(attrs ?? {})) {\n\t\tif (excluded.includes(key)) continue;\n\t\tconst normalized: BlockAttrValue | undefined = cloneBlockAttrValue(value);\n\t\tif (normalized !== undefined) result[key] = normalized;\n\t}\n\treturn result;\n}\n\nfunction cloneBlockAttrValue(value: unknown): BlockAttrValue | undefined {\n\tif (typeof value === 'string' || typeof value === 'boolean') return value;\n\tif (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n\tif (!Array.isArray(value)) return undefined;\n\tconst normalized: (string | number | boolean | null)[] = [];\n\tfor (const entry of value) {\n\t\tif (entry === null || typeof entry === 'string' || typeof entry === 'boolean') {\n\t\t\tnormalized.push(entry);\n\t\t} else if (typeof entry === 'number' && Number.isFinite(entry)) {\n\t\t\tnormalized.push(entry);\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn Object.freeze(normalized);\n}\n\ntype MutableBlockAttrs = Record<string, BlockAttrValue>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TablePlugin-CPvg9YZS.mjs","names":[],"sources":["../../src/editor/styles/table.ts","../../src/plugins/table/TableGrid.ts","../../src/plugins/table/TableHelpers.ts","../../src/plugins/table/TableLocale.ts","../../src/plugins/table/TableBorderColor.ts","../../src/plugins/table/TableClipboard.ts","../../src/plugins/table/TableSelection.ts","../../src/plugins/table/TableSizing.ts","../../src/plugins/table/TableCommands.ts","../../src/plugins/table/TableSizeEditor.ts","../../src/plugins/table/TableContextMenu.ts","../../src/plugins/table/TableNavigation.ts","../../src/plugins/table/TableControlsDOM.ts","../../src/plugins/table/TableControlsLayout.ts","../../src/plugins/table/TableControls.ts","../../src/plugins/table/TableNodeViews.ts","../../src/plugins/table/TablePlugin.ts"],"sourcesContent":["/** Table styles — table layout, selection, context menu, controls, insert lines. */\nexport const TABLE_CSS = `\n/* Table */\n.notectl-table-wrapper {\n\tmargin: 8px 0;\n\toverflow-x: auto;\n\tposition: relative;\n}\n\n.notectl-table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\ttable-layout: fixed;\n}\n\n.notectl-table col {\n\tbox-sizing: border-box;\n}\n\n/* Border resolution order: per-table local override → theme component token → theme global token.\n * The inline --ntbl-border-color is set by the toolbar's \"border color\" action and must win\n * over the theme so per-table customizations remain visible after a theme switch.\n */\n.notectl-table td {\n\tborder: 1px solid var(--ntbl-border-color, var(--notectl-table-border, var(--notectl-border)));\n\tbackground: var(--notectl-table-cell-bg, transparent);\n\tpadding: 8px 12px;\n\tmin-width: var(--ntbl-min-column-width, 60px);\n\tvertical-align: top;\n\tmin-height: 1.6em;\n}\n\n.notectl-table th {\n\tborder: 1px solid var(--ntbl-border-color, var(--notectl-table-border, var(--notectl-border)));\n\tbackground: var(--notectl-table-header-bg, var(--notectl-surface-raised));\n\tpadding: 8px 12px;\n\tmin-width: var(--ntbl-min-column-width, 60px);\n\tvertical-align: top;\n\ttext-align: start;\n\tfont-weight: 600;\n}\n\n.notectl-table--borderless td {\n\tborder: 1px dashed rgba(128, 128, 128, 0.15);\n}\n\n@media print {\n\t.notectl-table--borderless td {\n\t\tborder: none;\n\t}\n}\n\n.notectl-table td:focus-within {\n\toutline: 2px solid var(--notectl-primary);\n\toutline-offset: -2px;\n}\n\n.notectl-table-cell--selected {\n\tbackground: var(--notectl-primary-muted);\n}\n\n/* Table Context Menu */\n.notectl-table-context-menu {\n\tbackground: var(--notectl-surface-overlay);\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 6px;\n\tbox-shadow: 0 4px 12px var(--notectl-shadow);\n\tpadding: 4px 0;\n\tmin-width: 180px;\n\tz-index: 10000;\n}\n\n.notectl-table-context-menu button {\n\tdisplay: block;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\ttext-align: start;\n\tborder: none;\n\tbackground: none;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tcolor: var(--notectl-fg);\n\tfont-family: inherit;\n}\n\n.notectl-table-context-menu button:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-table-context-menu hr {\n\tborder: none;\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin: 4px 0;\n}\n\n/* === Table Controls === */\n\n/* Outer container for table + controls */\n.ntbl-container {\n\tposition: relative;\n\tmargin: 8px 0;\n\tpadding-block-start: 24px;\n\tpadding-inline-start: 24px;\n}\n\n.ntbl-container .notectl-table-wrapper {\n\tmargin: 0;\n}\n\n.ntbl-container.notectl-table--selected .notectl-table-wrapper {\n\toutline: 2px solid var(--notectl-primary-fg);\n\toutline-offset: 2px;\n\tborder-radius: 6px;\n}\n\n/* --- Column Handle Bar --- */\n.ntbl-col-bar {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 24px;\n\theight: 20px;\n\tdisplay: flex;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tz-index: 3;\n\tpointer-events: none;\n}\n\n.ntbl-container:hover .ntbl-col-bar,\n.ntbl-container:focus-within .ntbl-col-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-col-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n/* --- Row Handle Bar --- */\n.ntbl-row-bar {\n\tposition: absolute;\n\ttop: 24px;\n\tinset-inline-start: 0;\n\twidth: 20px;\n\tdisplay: flex;\n\tflex-direction: column;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tz-index: 3;\n\tpointer-events: none;\n}\n\n.ntbl-container:hover .ntbl-row-bar,\n.ntbl-container:focus-within .ntbl-row-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-row-bar {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n/* --- Handle (shared base) --- */\n.ntbl-handle {\n\tposition: absolute;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n\ttransition: background 0.15s ease;\n\tborder-radius: 3px;\n}\n\n.ntbl-handle-select {\n\tposition: absolute;\n\tinset: 0;\n\twidth: 100%;\n\theight: 100%;\n\tborder: 0;\n\tborder-radius: inherit;\n\tbackground: transparent;\n\tcursor: pointer;\n}\n\n.ntbl-handle-select[aria-pressed=\"true\"] {\n\tbackground: var(--notectl-primary-muted);\n\tbox-shadow: inset 0 0 0 1px var(--notectl-primary);\n}\n\n.ntbl-handle-select:focus-visible {\n\toutline: none;\n\tbox-shadow: inset 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-col-handle {\n\theight: 100%;\n\tbackground: var(--notectl-hover-bg);\n\tborder-radius: 4px 4px 0 0;\n}\n\n.ntbl-col-handle:hover {\n\tbackground: var(--notectl-border);\n}\n\n.ntbl-row-handle {\n\twidth: 100%;\n\tbackground: var(--notectl-hover-bg);\n\tborder-start-start-radius: 4px;\n\tborder-end-start-radius: 4px;\n}\n\n.ntbl-row-handle:hover {\n\tbackground: var(--notectl-border);\n}\n\n/* --- Handle Delete Button --- */\n.ntbl-handle-delete {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 16px;\n\theight: 16px;\n\tborder: none;\n\tbackground: transparent;\n\tcolor: var(--notectl-fg-muted);\n\tcursor: pointer;\n\tborder-radius: 3px;\n\tpadding: 0;\n\topacity: 0;\n\ttransform: scale(0.7);\n\ttransition: opacity 0.15s, transform 0.15s,\n\t\tbackground 0.15s, color 0.15s;\n\tz-index: 1;\n}\n\n/* --- Logical column/row resize separators --- */\n.ntbl-col-resize-bar,\n.ntbl-row-resize-bar {\n\tposition: absolute;\n\tinset: 0 auto auto 0;\n\tz-index: 8;\n\tpointer-events: none;\n}\n\n.ntbl-resize-separator {\n\tposition: absolute;\n\tdisplay: block;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\topacity: 0;\n\tpointer-events: none;\n\ttouch-action: none;\n}\n\n.ntbl-resize-separator--column {\n\twidth: 12px;\n\ttransform: translateX(-6px);\n\tcursor: col-resize;\n}\n\n.ntbl-resize-separator--row {\n\theight: 12px;\n\ttransform: translateY(-6px);\n\tcursor: row-resize;\n}\n\n.ntbl-container:hover .ntbl-resize-separator,\n.ntbl-container:focus-within .ntbl-resize-separator,\n.ntbl-container.notectl-table--selected .ntbl-resize-separator,\n.ntbl-container--resizing-column .ntbl-resize-separator--column,\n.ntbl-container--resizing-row .ntbl-resize-separator--row {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-resize-separator::after {\n\tcontent: \"\";\n\tposition: absolute;\n\tbackground: var(--notectl-primary);\n\topacity: 0.35;\n}\n\n.ntbl-resize-separator--column::after {\n\tinset: 0 auto 0 5px;\n\twidth: 2px;\n}\n\n.ntbl-resize-separator--row::after {\n\tinset: 5px 0 auto 0;\n\theight: 2px;\n}\n\n.ntbl-resize-separator:hover::after,\n.ntbl-resize-separator:focus-visible::after {\n\topacity: 1;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-resize-separator:focus-visible {\n\toutline: none;\n}\n\n.ntbl-resize-indicator {\n\tposition: absolute;\n\tdisplay: none;\n\tz-index: 12;\n\tpadding: 3px 7px;\n\tborder-radius: 4px;\n\tbackground: var(--notectl-fg);\n\tcolor: var(--notectl-bg);\n\tfont: 12px/1.4 monospace;\n\tpointer-events: none;\n\twhite-space: nowrap;\n}\n\n.ntbl-resize-indicator--visible {\n\tdisplay: block;\n}\n\n.ntbl-container--resizing-column,\n.ntbl-container--resizing-column * {\n\tcursor: col-resize !important;\n\tuser-select: none !important;\n}\n\n.ntbl-container--resizing-row,\n.ntbl-container--resizing-row * {\n\tcursor: row-resize !important;\n\tuser-select: none !important;\n}\n\n.ntbl-handle:hover .ntbl-handle-delete,\n.ntbl-handle:focus-within .ntbl-handle-delete {\n\topacity: 1;\n\ttransform: scale(1);\n}\n\n.ntbl-handle-delete:hover,\n.ntbl-handle-delete:focus-visible {\n\tbackground: var(--notectl-danger-muted);\n\tcolor: var(--notectl-danger);\n}\n\n.ntbl-handle-delete:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Insert Lines --- */\n.ntbl-insert-line {\n\tposition: absolute;\n\tpointer-events: none;\n\topacity: 0;\n\ttransition: opacity 0.15s ease;\n\tz-index: 6;\n}\n\n.ntbl-insert-line--visible {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n.ntbl-insert-line--horizontal {\n\theight: 2px;\n\tbackground: linear-gradient(\n\t\t90deg,\n\t\ttransparent,\n\t\tvar(--notectl-primary) 8%,\n\t\tvar(--notectl-primary) 92%,\n\t\ttransparent\n\t);\n\tbox-shadow: 0 0 6px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-line--vertical {\n\twidth: 2px;\n\tbackground: linear-gradient(\n\t\t180deg,\n\t\ttransparent,\n\t\tvar(--notectl-primary) 8%,\n\t\tvar(--notectl-primary) 92%,\n\t\ttransparent\n\t);\n\tbox-shadow: 0 0 6px var(--notectl-focus-ring);\n}\n\n/* --- Insert Button on Line --- */\n.ntbl-insert-btn {\n\tposition: absolute;\n\twidth: 20px;\n\theight: 20px;\n\tborder-radius: 50%;\n\tbackground: var(--notectl-surface-raised);\n\tborder: 2px solid var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n\tcursor: pointer;\n\tpointer-events: all;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 0;\n\tbox-shadow: 0 2px 8px var(--notectl-focus-ring);\n\ttransition: background 0.15s, color 0.15s,\n\t\ttransform 0.15s, box-shadow 0.15s;\n}\n\n.ntbl-insert-line--horizontal .ntbl-insert-btn {\n\tleft: 50%;\n\ttop: -9px;\n\ttransform: translateX(-50%);\n}\n\n.ntbl-insert-line--vertical .ntbl-insert-btn {\n\ttop: 50%;\n\tinset-inline-start: -9px;\n\ttransform: translateY(-50%);\n}\n\n.ntbl-insert-btn:hover,\n.ntbl-insert-btn:focus-visible {\n\tbackground: var(--notectl-primary);\n\tcolor: var(--notectl-primary-fg);\n\tbox-shadow: 0 2px 12px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-insert-line--horizontal .ntbl-insert-btn:hover {\n\ttransform: translateX(-50%) scale(1.2);\n}\n\n.ntbl-insert-line--vertical .ntbl-insert-btn:hover {\n\ttransform: translateY(-50%) scale(1.2);\n}\n\n/* --- Add Row / Add Column Zones --- */\n.ntbl-add-zone {\n\tposition: absolute;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n\tuser-select: none;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.2s ease,\n\t\tborder-color 0.2s ease;\n}\n\n.ntbl-container:hover .ntbl-add-zone,\n.ntbl-container:focus-within .ntbl-add-zone {\n\topacity: 1;\n}\n\n.ntbl-container.notectl-table--selected .ntbl-add-zone {\n\topacity: 1;\n}\n\n.ntbl-delete-table-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 0;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-danger);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-danger);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n}\n\n.ntbl-container:hover .ntbl-delete-table-btn,\n.ntbl-container:focus-within .ntbl-delete-table-btn,\n.ntbl-container.notectl-table--selected .ntbl-delete-table-btn {\n\topacity: 1;\n}\n\n.ntbl-delete-table-btn:hover,\n.ntbl-delete-table-btn:focus-visible {\n\tbackground: var(--notectl-danger-muted);\n\tborder-color: var(--notectl-danger);\n}\n\n.ntbl-delete-table-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Context Menu Discovery Hint --- */\n.ntbl-context-hint {\n\tposition: absolute;\n\ttop: 4px;\n\tinset-inline-end: 0;\n\tfont-size: 11px;\n\tcolor: var(--notectl-fg-muted);\n\tpadding: 2px 8px;\n\tborder-radius: 4px;\n\topacity: 0;\n\ttransition: opacity 0.2s ease;\n\tpointer-events: none;\n\twhite-space: nowrap;\n\tz-index: 5;\n}\n\n.ntbl-container:hover .ntbl-context-hint,\n.ntbl-container:focus-within .ntbl-context-hint,\n.ntbl-container.notectl-table--selected .ntbl-context-hint {\n\topacity: 1;\n}\n\n/* --- Table Actions Button --- */\n.ntbl-actions-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 48px;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-fg-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n\tcursor: pointer;\n\tpadding: 0;\n}\n\n.ntbl-container:hover .ntbl-actions-btn,\n.ntbl-container:focus-within .ntbl-actions-btn,\n.ntbl-container.notectl-table--selected .ntbl-actions-btn {\n\topacity: 1;\n}\n\n.ntbl-actions-btn:hover,\n.ntbl-actions-btn:focus-visible {\n\tbackground: var(--notectl-hover-bg);\n\tborder-color: var(--notectl-primary);\n}\n\n.ntbl-actions-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n/* --- Border Color Button --- */\n.ntbl-border-color-btn {\n\tposition: absolute;\n\ttop: 0;\n\tinset-inline-start: 24px;\n\twidth: 20px;\n\theight: 20px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-surface-raised);\n\tcolor: var(--notectl-fg-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.2s ease, background 0.15s, border-color 0.15s;\n\tz-index: 7;\n\tcursor: pointer;\n\tpadding: 0;\n}\n\n.ntbl-container:hover .ntbl-border-color-btn,\n.ntbl-container:focus-within .ntbl-border-color-btn,\n.ntbl-container.notectl-table--selected .ntbl-border-color-btn {\n\topacity: 1;\n}\n\n.ntbl-border-color-btn:hover,\n.ntbl-border-color-btn:focus-visible {\n\tbackground: var(--notectl-hover-bg);\n\tborder-color: var(--notectl-primary);\n}\n\n.ntbl-border-color-btn:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-border-color-swatch {\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 2px;\n\tborder: 1px solid rgba(0, 0, 0, 0.15);\n}\n\n/* --- Context Menu Keyboard Hint --- */\n.notectl-table-context-menu__hint {\n\tpadding: 4px 12px 6px;\n\tfont-size: 11px;\n\tcolor: var(--notectl-fg-muted);\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin-top: 4px;\n\ttext-align: center;\n\tuser-select: none;\n}\n\n/* --- Context Menu Focus --- */\n.notectl-table-context-menu button:focus-visible {\n\toutline: none;\n\tbackground: var(--notectl-primary-muted);\n\tbox-shadow: inset 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.notectl-table-context-menu [role=\"separator\"] {\n\tborder: none;\n\tborder-top: 1px solid var(--notectl-border);\n\tmargin: 4px 0;\n\theight: 0;\n}\n\n/* --- Precise size editor --- */\n.notectl-table-size-editor {\n\tbox-sizing: border-box;\n\twidth: 340px;\n\tmax-width: calc(100vw - 16px);\n\tmin-width: 260px;\n\tpadding: 12px;\n}\n\n.notectl-table-size-editor__title {\n\tmargin-block-end: 10px;\n\tfont-weight: 600;\n}\n\n.notectl-table-size-editor__field {\n\tdisplay: grid;\n\tgap: 4px;\n\tmargin-block-end: 10px;\n\tfont-size: 13px;\n}\n\n.notectl-table-size-editor__input-wrap {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.notectl-table-size-editor input {\n\tbox-sizing: border-box;\n\tmin-width: 0;\n\twidth: 100%;\n\tpadding: 6px 8px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tbackground: var(--notectl-bg);\n\tcolor: var(--notectl-fg);\n\tfont: inherit;\n}\n\n.notectl-table-size-editor input:focus-visible {\n\toutline: 2px solid var(--notectl-focus-ring);\n\toutline-offset: 1px;\n}\n\n.notectl-table-size-editor input[aria-invalid=\"true\"] {\n\tborder-color: var(--notectl-danger);\n}\n\n.notectl-table-size-editor__unit {\n\tcolor: var(--notectl-fg-muted);\n}\n\n.notectl-table-size-editor__error {\n\tmin-height: 1.2em;\n\tcolor: var(--notectl-danger);\n\tfont-size: 11px;\n}\n\n.notectl-table-size-editor__resets,\n.notectl-table-size-editor__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n.notectl-table-size-editor__resets {\n\tpadding-block-end: 10px;\n\tborder-block-end: 1px solid var(--notectl-border);\n}\n\n.notectl-table-size-editor__actions {\n\tjustify-content: flex-end;\n\tpadding-block-start: 10px;\n}\n\n.notectl-table-size-editor button {\n\twidth: auto;\n\tpadding: 6px 8px;\n\tborder-radius: 4px;\n\tfont-size: 12px;\n}\n\n.notectl-table-context-menu .notectl-table-size-editor__apply {\n\tbackground: var(--notectl-primary);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-table-context-menu .notectl-table-size-editor button:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n/* Read-only tables keep semantic dimensions but expose no mutation affordances. */\n.ntbl-container[data-notectl-table-readonly] {\n\tpadding-block-start: 0;\n\tpadding-inline-start: 0;\n}\n\n.ntbl-container[data-notectl-table-readonly] > :is(\n\t.ntbl-col-bar,\n\t.ntbl-row-bar,\n\t.ntbl-col-resize-bar,\n\t.ntbl-row-resize-bar,\n\t.ntbl-insert-line,\n\t.ntbl-add-zone,\n\t.ntbl-delete-table-btn,\n\t.ntbl-border-color-btn,\n\t.ntbl-actions-btn,\n\t.ntbl-context-hint,\n\t.ntbl-resize-indicator\n) {\n\tdisplay: none !important;\n}\n\n.ntbl-add-row {\n\tbottom: 0;\n\theight: 24px;\n\tborder: 1px dashed var(--notectl-border);\n\tborder-end-start-radius: 6px;\n\tborder-end-end-radius: 6px;\n\tborder-block-start: none;\n\tcolor: var(--notectl-fg-muted);\n\ttransform: translateY(100%);\n}\n\n.ntbl-add-row:hover,\n.ntbl-add-row:focus-visible {\n\tbackground: var(--notectl-primary-muted);\n\tborder-color: var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n}\n\n.ntbl-add-row:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-add-col {\n\tinset-inline-end: 0;\n\ttop: 24px;\n\twidth: 24px;\n\tborder: 1px dashed var(--notectl-border);\n\tborder-start-end-radius: 6px;\n\tborder-end-end-radius: 6px;\n\tborder-inline-start: none;\n\tcolor: var(--notectl-fg-muted);\n\ttransform: translateX(100%);\n}\n\n[dir=\"rtl\"] .ntbl-add-col {\n\ttransform: translateX(-100%);\n}\n\n.ntbl-add-col:hover,\n.ntbl-add-col:focus-visible {\n\tbackground: var(--notectl-primary-muted);\n\tborder-color: var(--notectl-primary);\n\tcolor: var(--notectl-primary);\n}\n\n.ntbl-add-col:focus-visible {\n\toutline: none;\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.ntbl-add-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttransition: transform 0.2s ease;\n}\n\n.ntbl-add-zone:hover .ntbl-add-icon {\n\ttransform: scale(1.15);\n}\n\n@media print {\n\t.notectl-table-wrapper {\n\t\toverflow: visible;\n\t}\n\n\t.ntbl-container {\n\t\tpadding: 0;\n\t}\n\n\t.ntbl-container > [data-notectl-no-print] {\n\t\tdisplay: none !important;\n\t}\n}\n`;\n","/**\n * Canonical logical-grid mapping for table rows, cells, colspan, and rowspan.\n *\n * Every table feature uses this module instead of treating a cell's child-array\n * index as its column. Coordinates are zero-based and ranges are inclusive,\n * matching {@link CellRange} in the table selection service.\n */\n\nimport { type BlockNode, getBlockChildren } from '../../model/Document.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\n\n/** Defensive upper bound for malformed externally supplied span attributes. */\nexport const MAX_TABLE_SPAN = 1_000;\n\n/** Maximum logical columns materialized for one untrusted table. */\nexport const MAX_TABLE_COLUMNS = 1_000;\n\n/** One source cell and the logical rectangle it occupies. */\nexport interface TableGridCell {\n\treadonly cell: BlockNode;\n\treadonly sourceRow: BlockNode;\n\treadonly sourceRowIndex: number;\n\treadonly sourceCellIndex: number;\n\treadonly rowStart: number;\n\treadonly rowEnd: number;\n\treadonly columnStart: number;\n\treadonly columnEnd: number;\n\treadonly rowSpan: number;\n\treadonly columnSpan: number;\n}\n\n/** Inclusive logical rectangle. */\nexport interface TableGridRange {\n\treadonly fromRow: number;\n\treadonly fromColumn: number;\n\treadonly toRow: number;\n\treadonly toColumn: number;\n}\n\n/** Read-only logical table map. */\nexport interface TableGrid {\n\treadonly table: BlockNode;\n\treadonly rows: readonly BlockNode[];\n\treadonly rowCount: number;\n\treadonly columnCount: number;\n\treadonly cells: readonly TableGridCell[];\n\tcellAt(row: number, column: number): TableGridCell | undefined;\n\tcellById(cellId: BlockId): TableGridCell | undefined;\n\tcellsInRange(range: TableGridRange): readonly TableGridCell[];\n}\n\n/** Expands an inclusive range to a fixed point so no spanning cell is cut. */\nexport function expandTableGridRange(\n\tgrid: TableGrid,\n\trange: TableGridRange,\n): TableGridRange | null {\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\tlet expanded: TableGridRange = {\n\t\tfromRow: clampRangeIndex(Math.min(range.fromRow, range.toRow), grid.rowCount),\n\t\tfromColumn: clampRangeIndex(Math.min(range.fromColumn, range.toColumn), grid.columnCount),\n\t\ttoRow: clampRangeIndex(Math.max(range.fromRow, range.toRow), grid.rowCount),\n\t\ttoColumn: clampRangeIndex(Math.max(range.fromColumn, range.toColumn), grid.columnCount),\n\t};\n\twhile (true) {\n\t\tlet fromRow: number = expanded.fromRow;\n\t\tlet fromColumn: number = expanded.fromColumn;\n\t\tlet toRow: number = expanded.toRow;\n\t\tlet toColumn: number = expanded.toColumn;\n\t\tfor (const entry of grid.cellsInRange(expanded)) {\n\t\t\tfromRow = Math.min(fromRow, entry.rowStart);\n\t\t\tfromColumn = Math.min(fromColumn, entry.columnStart);\n\t\t\ttoRow = Math.max(toRow, entry.rowEnd - 1);\n\t\t\ttoColumn = Math.max(toColumn, entry.columnEnd - 1);\n\t\t}\n\t\tif (\n\t\t\tfromRow === expanded.fromRow &&\n\t\t\tfromColumn === expanded.fromColumn &&\n\t\t\ttoRow === expanded.toRow &&\n\t\t\ttoColumn === expanded.toColumn\n\t\t) {\n\t\t\treturn expanded;\n\t\t}\n\t\texpanded = { fromRow, fromColumn, toRow, toColumn };\n\t}\n}\n\n/** Converts an untrusted span value to a bounded positive integer. */\nexport function normalizeTableSpan(value: unknown, maximum = MAX_TABLE_SPAN): number {\n\tif (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value)) return 1;\n\tif (value <= 1) return 1;\n\treturn Math.min(value, maximum);\n}\n\n/** Builds the logical grid for a table node. Non-table input yields an empty grid. */\nexport function createTableGrid(table: BlockNode): TableGrid {\n\tconst rows: readonly BlockNode[] =\n\t\ttable.type === 'table' ? getBlockChildren(table).filter((row) => row.type === 'table_row') : [];\n\tconst slots: (TableGridCell | undefined)[][] = Array.from({ length: rows.length }, () => []);\n\tconst cells: TableGridCell[] = [];\n\tconst byId = new Map<BlockId, TableGridCell>();\n\tlet columnCount = 0;\n\n\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\tconst sourceRow: BlockNode | undefined = rows[rowIndex];\n\t\tif (!sourceRow) continue;\n\t\tconst sourceCells: readonly BlockNode[] = getBlockChildren(sourceRow).filter(\n\t\t\t(cell) => cell.type === 'table_cell',\n\t\t);\n\t\tlet column = 0;\n\n\t\tfor (let sourceCellIndex = 0; sourceCellIndex < sourceCells.length; sourceCellIndex++) {\n\t\t\tconst cell: BlockNode | undefined = sourceCells[sourceCellIndex];\n\t\t\tif (!cell) continue;\n\n\t\t\tconst requestedColumnSpan: number = normalizeTableSpan(cell.attrs?.colspan);\n\t\t\tconst requestedRowSpan: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst rowSpan: number = Math.min(requestedRowSpan, rows.length - rowIndex);\n\n\t\t\tconst availableColumn: number | undefined = findAvailableColumn(\n\t\t\t\tslots[rowIndex] ?? [],\n\t\t\t\tcolumn,\n\t\t\t\trequestedColumnSpan,\n\t\t\t);\n\t\t\tif (availableColumn === undefined) continue;\n\t\t\tcolumn = availableColumn;\n\t\t\tconst columnSpan: number = Math.min(requestedColumnSpan, MAX_TABLE_COLUMNS - column);\n\t\t\tconst entry: TableGridCell = {\n\t\t\t\tcell,\n\t\t\t\tsourceRow,\n\t\t\t\tsourceRowIndex: rowIndex,\n\t\t\t\tsourceCellIndex,\n\t\t\t\trowStart: rowIndex,\n\t\t\t\trowEnd: rowIndex + rowSpan,\n\t\t\t\tcolumnStart: column,\n\t\t\t\tcolumnEnd: column + columnSpan,\n\t\t\t\trowSpan,\n\t\t\t\tcolumnSpan,\n\t\t\t};\n\n\t\t\tcells.push(entry);\n\t\t\tbyId.set(cell.id, entry);\n\t\t\tfor (let occupiedRow = entry.rowStart; occupiedRow < entry.rowEnd; occupiedRow++) {\n\t\t\t\tconst rowSlots: (TableGridCell | undefined)[] | undefined = slots[occupiedRow];\n\t\t\t\tif (!rowSlots) continue;\n\t\t\t\tfor (\n\t\t\t\t\tlet occupiedColumn = entry.columnStart;\n\t\t\t\t\toccupiedColumn < entry.columnEnd;\n\t\t\t\t\toccupiedColumn++\n\t\t\t\t) {\n\t\t\t\t\t// Malformed overlapping spans never overwrite the first valid owner.\n\t\t\t\t\trowSlots[occupiedColumn] ??= entry;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcolumn = entry.columnEnd;\n\t\t\tcolumnCount = Math.max(columnCount, entry.columnEnd);\n\t\t}\n\t}\n\n\treturn {\n\t\ttable,\n\t\trows,\n\t\trowCount: rows.length,\n\t\tcolumnCount,\n\t\tcells,\n\t\tcellAt(row: number, column: number): TableGridCell | undefined {\n\t\t\tif (!Number.isInteger(row) || !Number.isInteger(column) || row < 0 || column < 0) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn slots[row]?.[column];\n\t\t},\n\t\tcellById(cellId: BlockId): TableGridCell | undefined {\n\t\t\treturn byId.get(cellId);\n\t\t},\n\t\tcellsInRange(range: TableGridRange): readonly TableGridCell[] {\n\t\t\tconst minRow: number = Math.max(0, Math.min(range.fromRow, range.toRow));\n\t\t\tconst maxRow: number = Math.min(rows.length - 1, Math.max(range.fromRow, range.toRow));\n\t\t\tconst minColumn: number = Math.max(0, Math.min(range.fromColumn, range.toColumn));\n\t\t\tconst maxColumn: number = Math.min(\n\t\t\t\tcolumnCount - 1,\n\t\t\t\tMath.max(range.fromColumn, range.toColumn),\n\t\t\t);\n\t\t\tif (maxRow < minRow || maxColumn < minColumn) return [];\n\n\t\t\treturn cells.filter(\n\t\t\t\t(entry) =>\n\t\t\t\t\tentry.rowStart <= maxRow &&\n\t\t\t\t\tentry.rowEnd > minRow &&\n\t\t\t\t\tentry.columnStart <= maxColumn &&\n\t\t\t\t\tentry.columnEnd > minColumn,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Projects canonical column-width metadata onto a clipboard table slice.\n * Metadata is retained only when the surviving cells cover one contiguous,\n * unambiguous run of the original logical columns.\n */\nexport function projectTableColumnWidthsForSlice(original: BlockNode, slice: BlockNode): BlockNode {\n\tconst retainedIds = new Set<BlockId>();\n\tconst retainedRowIds = new Set<BlockId>();\n\tfor (const row of getBlockChildren(slice)) {\n\t\tretainedRowIds.add(row.id);\n\t\tfor (const cell of getBlockChildren(row)) retainedIds.add(cell.id);\n\t}\n\tconst grid: TableGrid = createTableGrid(original);\n\tconst retainedEntries: readonly TableGridCell[] = grid.cells.filter((entry) =>\n\t\tretainedIds.has(entry.cell.id),\n\t);\n\tif (retainedEntries.length === 0) return withoutColumnWidths(slice);\n\n\tconst firstColumn: number = Math.min(...retainedEntries.map((entry) => entry.columnStart));\n\tconst lastColumn: number = Math.max(...retainedEntries.map((entry) => entry.columnEnd)) - 1;\n\tconst retainedRowIndexes: readonly number[] = grid.rows\n\t\t.map((row, index) => (retainedRowIds.has(row.id) ? index : -1))\n\t\t.filter((index) => index >= 0);\n\tif (retainedRowIndexes.length === 0) return withoutColumnWidths(slice);\n\tconst firstRow: number = Math.min(...retainedRowIndexes);\n\tconst lastRow: number = Math.max(...retainedRowIndexes);\n\tconst normalizedSlice: BlockNode = normalizeSelectionSliceSpans(\n\t\tslice,\n\t\tgrid,\n\t\tfirstRow,\n\t\tlastRow,\n\t\tfirstColumn,\n\t\tlastColumn,\n\t);\n\tfor (const rowIndex of retainedRowIndexes) {\n\t\tconst coveredColumns = new Set<number>();\n\t\tfor (const entry of retainedEntries) {\n\t\t\tif (entry.rowStart > rowIndex || entry.rowEnd <= rowIndex) continue;\n\t\t\tfor (let column = entry.columnStart; column < entry.columnEnd; column++) {\n\t\t\t\tcoveredColumns.add(column);\n\t\t\t}\n\t\t}\n\t\tfor (let column = firstColumn; column <= lastColumn; column++) {\n\t\t\tif (!coveredColumns.has(column)) return withoutColumnWidths(normalizedSlice);\n\t\t}\n\t}\n\n\tconst rawWidths: unknown = original.attrs?.columnWidthsPx;\n\tif (!Array.isArray(rawWidths)) return normalizedSlice;\n\n\tconst projected: (number | null)[] = [];\n\tfor (let column = firstColumn; column <= lastColumn; column++) {\n\t\tconst value: unknown = rawWidths[column];\n\t\tprojected.push(typeof value === 'number' && Number.isFinite(value) ? value : null);\n\t}\n\tif (!projected.some((value) => value !== null)) return withoutColumnWidths(normalizedSlice);\n\n\treturn {\n\t\t...normalizedSlice,\n\t\tattrs: {\n\t\t\t...(normalizedSlice.attrs ?? {}),\n\t\t\tcolumnWidthsPx: Object.freeze(projected),\n\t\t},\n\t};\n}\n\nfunction normalizeSelectionSliceSpans(\n\tslice: BlockNode,\n\tgrid: TableGrid,\n\tfirstRow: number,\n\tlastRow: number,\n\tfirstColumn: number,\n\tlastColumn: number,\n): BlockNode {\n\tlet changed = false;\n\tconst rows: readonly BlockNode[] = getBlockChildren(slice).map((row) => {\n\t\tlet rowChanged = false;\n\t\tconst cells: readonly BlockNode[] = getBlockChildren(row).map((cell) => {\n\t\t\tconst entry: TableGridCell | undefined = grid.cellById(cell.id);\n\t\t\tif (!entry) return cell;\n\t\t\tconst rowSpan: number =\n\t\t\t\tMath.min(entry.rowEnd, lastRow + 1) - Math.max(entry.rowStart, firstRow);\n\t\t\tconst columnSpan: number =\n\t\t\t\tMath.min(entry.columnEnd, lastColumn + 1) - Math.max(entry.columnStart, firstColumn);\n\t\t\tconst previousRowSpan: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst previousColumnSpan: number = normalizeTableSpan(cell.attrs?.colspan);\n\t\t\tif (rowSpan === previousRowSpan && columnSpan === previousColumnSpan) return cell;\n\t\t\trowChanged = true;\n\t\t\tconst { colspan: _colspan, rowspan: _rowspan, ...rest } = cell.attrs ?? {};\n\t\t\tconst attrs = {\n\t\t\t\t...rest,\n\t\t\t\t...(columnSpan > 1 ? { colspan: columnSpan } : {}),\n\t\t\t\t...(rowSpan > 1 ? { rowspan: rowSpan } : {}),\n\t\t\t};\n\t\t\tconst { attrs: _attrs, ...withoutAttrs } = cell;\n\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t});\n\t\tif (!rowChanged) return row;\n\t\tchanged = true;\n\t\treturn { ...row, children: cells };\n\t});\n\treturn changed ? { ...slice, children: rows } : slice;\n}\n\n/**\n * Creates a self-contained table fragment for an inclusive logical rectangle.\n * The requested rectangle first expands to whole spanning cells. Owners are\n * emitted once, and dimensions are projected from their canonical logical\n * columns and rows.\n */\nexport function sliceTableToRange(table: BlockNode, range: TableGridRange): BlockNode | null {\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\tconst expanded: TableGridRange | null = expandTableGridRange(grid, range);\n\tif (!expanded) return null;\n\tconst { fromRow, fromColumn, toRow, toColumn } = expanded;\n\tconst retainedCells: readonly TableGridCell[] = grid.cellsInRange({\n\t\tfromRow,\n\t\tfromColumn,\n\t\ttoRow,\n\t\ttoColumn,\n\t});\n\n\tconst rows: BlockNode[] = [];\n\tconst preservesCompleteRows: boolean = fromColumn === 0 && toColumn === grid.columnCount - 1;\n\tfor (let logicalRow = fromRow; logicalRow <= toRow; logicalRow++) {\n\t\tconst sourceRow: BlockNode | undefined = grid.rows[logicalRow];\n\t\tif (!sourceRow) continue;\n\t\tconst cells: BlockNode[] = retainedCells\n\t\t\t.filter((entry: TableGridCell): boolean => Math.max(entry.rowStart, fromRow) === logicalRow)\n\t\t\t.sort(\n\t\t\t\t(a: TableGridCell, b: TableGridCell): number =>\n\t\t\t\t\tMath.max(a.columnStart, fromColumn) - Math.max(b.columnStart, fromColumn),\n\t\t\t)\n\t\t\t.map((entry: TableGridCell): BlockNode => {\n\t\t\t\tconst rowSpan: number =\n\t\t\t\t\tMath.min(entry.rowEnd, toRow + 1) - Math.max(entry.rowStart, fromRow);\n\t\t\t\tconst columnSpan: number =\n\t\t\t\t\tMath.min(entry.columnEnd, toColumn + 1) - Math.max(entry.columnStart, fromColumn);\n\t\t\t\tconst { colspan: _colspan, rowspan: _rowspan, ...rest } = entry.cell.attrs ?? {};\n\t\t\t\tconst attrs = {\n\t\t\t\t\t...rest,\n\t\t\t\t\t...(columnSpan > 1 ? { colspan: columnSpan } : {}),\n\t\t\t\t\t...(rowSpan > 1 ? { rowspan: rowSpan } : {}),\n\t\t\t\t};\n\t\t\t\tconst { attrs: _attrs, ...withoutAttrs } = entry.cell;\n\t\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t\t});\n\t\tconst { htmlId: _rowHTMLId, ...rowWithoutHTMLId } = sourceRow;\n\t\trows.push({\n\t\t\t...(preservesCompleteRows ? sourceRow : rowWithoutHTMLId),\n\t\t\tchildren: cells,\n\t\t});\n\t}\n\n\tconst { columnWidthsPx: _columnWidthsPx, ...tableAttrs } = table.attrs ?? {};\n\tconst rawWidths: unknown = table.attrs?.columnWidthsPx;\n\tconst widths: (number | null)[] = [];\n\tif (Array.isArray(rawWidths)) {\n\t\tfor (let column = fromColumn; column <= toColumn; column++) {\n\t\t\tconst value: unknown = rawWidths[column];\n\t\t\twidths.push(typeof value === 'number' && Number.isFinite(value) ? value : null);\n\t\t}\n\t}\n\tconst attrs = {\n\t\t...tableAttrs,\n\t\t...(widths.some((width: number | null): boolean => width !== null)\n\t\t\t? { columnWidthsPx: Object.freeze(widths) }\n\t\t\t: {}),\n\t};\n\tconst preservesCompleteTable: boolean =\n\t\tpreservesCompleteRows && fromRow === 0 && toRow === grid.rowCount - 1;\n\tconst { attrs: _attrs, ...tableWithoutAttrs } = table;\n\tconst { htmlId: _tableHTMLId, ...tableWithoutAttrsAndHTMLId } = tableWithoutAttrs;\n\tconst withoutAttrs = preservesCompleteTable ? tableWithoutAttrs : tableWithoutAttrsAndHTMLId;\n\treturn Object.keys(attrs).length > 0\n\t\t? { ...withoutAttrs, children: rows, attrs }\n\t\t: { ...withoutAttrs, children: rows };\n}\n\nfunction withoutColumnWidths(table: BlockNode): BlockNode {\n\tif (!table.attrs || !Object.hasOwn(table.attrs, 'columnWidthsPx')) return table;\n\tconst { columnWidthsPx: _columnWidthsPx, ...attrs } = table.attrs;\n\tconst { attrs: _attrs, ...withoutAttrs } = table;\n\treturn {\n\t\t...withoutAttrs,\n\t\t...(Object.keys(attrs).length > 0 ? { attrs } : {}),\n\t};\n}\n\nfunction findAvailableColumn(\n\trow: readonly (TableGridCell | undefined)[],\n\tstart: number,\n\tspan: number,\n): number | undefined {\n\tlet column: number = Math.max(0, start);\n\twhile (column + span <= MAX_TABLE_COLUMNS) {\n\t\tlet available = true;\n\t\tfor (let offset = 0; offset < span; offset++) {\n\t\t\tif (row[column + offset]) {\n\t\t\t\tavailable = false;\n\t\t\t\tcolumn += offset + 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (available) return column;\n\t}\n\treturn undefined;\n}\n\nfunction clampRangeIndex(value: number, upperBound: number): number {\n\tif (!Number.isFinite(value)) return 0;\n\treturn Math.min(upperBound - 1, Math.max(0, Math.trunc(value)));\n}\n","/**\n * Utility functions for table operations: creating table structures,\n * finding table context, and navigating cells.\n */\n\nimport {\n\ttype BlockNode,\n\ttype ChildNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n\tgetBlockChildren,\n} from '../../model/Document.js';\nimport type { BlockId, NodeTypeName } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport { type TableGridCell, createTableGrid } from './TableGrid.js';\n\n/** Context information about a cell within a table. */\nexport interface TableContext {\n\treadonly tableId: BlockId;\n\treadonly tableIndex: number;\n\treadonly rowId: BlockId;\n\treadonly rowIndex: number;\n\treadonly rowEnd: number;\n\treadonly cellId: BlockId;\n\treadonly colIndex: number;\n\treadonly colEnd: number;\n\treadonly rowspan: number;\n\treadonly colspan: number;\n\treadonly totalRows: number;\n\treadonly totalCols: number;\n}\n\n/** Creates a table BlockNode structure with the given dimensions. */\nexport function createTable(rows: number, cols: number): BlockNode {\n\tconst tableId: BlockId = generateBlockId();\n\tconst rowNodes: ChildNode[] = [];\n\n\tfor (let r = 0; r < rows; r++) {\n\t\tconst cellNodes: ChildNode[] = [];\n\t\tfor (let c = 0; c < cols; c++) {\n\t\t\tcellNodes.push(createTableCell());\n\t\t}\n\t\trowNodes.push(\n\t\t\tcreateBlockNode(nodeType('table_row') as NodeTypeName, cellNodes, generateBlockId()),\n\t\t);\n\t}\n\n\treturn createBlockNode(nodeType('table') as NodeTypeName, rowNodes, tableId);\n}\n\n/** Creates a single table row with the given number of cells. */\nexport function createTableRow(cols: number): BlockNode {\n\tconst cellNodes: ChildNode[] = [];\n\tfor (let c = 0; c < cols; c++) {\n\t\tcellNodes.push(createTableCell());\n\t}\n\treturn createBlockNode(nodeType('table_row') as NodeTypeName, cellNodes, generateBlockId());\n}\n\n/** Creates a single empty table cell containing a paragraph. */\nexport function createTableCell(): BlockNode {\n\tconst paragraph: BlockNode = createBlockNode(\n\t\tnodeType('paragraph') as NodeTypeName,\n\t\t[createTextNode('')],\n\t\tgenerateBlockId(),\n\t);\n\treturn createBlockNode(nodeType('table_cell') as NodeTypeName, [paragraph], generateBlockId());\n}\n\n/**\n * Finds table context for a given block ID.\n * Returns null if the block is not inside a table.\n */\nexport function findTableContext(state: EditorState, blockId: BlockId): TableContext | null {\n\tconst path = state.getNodePath(blockId);\n\tif (!path) return null;\n\n\t// Resolve the owning table and cell by identity first. Logical coordinates\n\t// come exclusively from TableGrid; a cell's child-array index is not its\n\t// column when colspan/rowspan are present.\n\tlet tableId: BlockId | null = null;\n\tlet tableNode: BlockNode | null = null;\n\tlet cellId: BlockId | null = null;\n\n\tfor (const id of path) {\n\t\tconst node = state.getBlock(id as BlockId);\n\t\tif (!node) continue;\n\t\tif (node.type === 'table') {\n\t\t\ttableId = id as BlockId;\n\t\t\ttableNode = node;\n\t\t} else if (node.type === 'table_cell') {\n\t\t\tcellId = id as BlockId;\n\t\t}\n\t}\n\n\t// If the block itself is a cell\n\tconst block = state.getBlock(blockId);\n\tif (block?.type === 'table_cell') {\n\t\tcellId = blockId;\n\t}\n\n\tif (!tableId || !tableNode || !cellId) return null;\n\n\t// Find table index in document\n\tconst tableIndex: number = state.doc.children.findIndex((b) => b.id === tableId);\n\n\tconst grid = createTableGrid(tableNode);\n\tconst gridCell: TableGridCell | undefined = grid.cellById(cellId);\n\tif (!gridCell) return null;\n\n\treturn {\n\t\ttableId,\n\t\ttableIndex,\n\t\trowId: gridCell.sourceRow.id,\n\t\trowIndex: gridCell.rowStart,\n\t\trowEnd: gridCell.rowEnd,\n\t\tcellId,\n\t\tcolIndex: gridCell.columnStart,\n\t\tcolEnd: gridCell.columnEnd,\n\t\trowspan: gridCell.rowSpan,\n\t\tcolspan: gridCell.columnSpan,\n\t\ttotalRows: grid.rowCount,\n\t\ttotalCols: grid.columnCount,\n\t};\n}\n\n/**\n * Gets the cell BlockId at the given row and column indices.\n * Returns null if out of bounds.\n */\nexport function getCellAt(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): BlockId | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\treturn createTableGrid(table).cellAt(rowIndex, colIndex)?.cell.id ?? null;\n}\n\n/** Returns all cell IDs in a table in row-major order. */\nexport function getAllCellIds(state: EditorState, tableId: BlockId): readonly BlockId[] {\n\tconst table = state.getBlock(tableId);\n\tif (!table) return [];\n\n\tif (table.type !== 'table') return [];\n\treturn createTableGrid(table).cells.map((entry: TableGridCell) => entry.cell.id);\n}\n\n/** Returns the first leaf-block ID inside a table cell (e.g. the paragraph). */\nexport function getFirstLeafInCell(state: EditorState, cellId: BlockId): BlockId {\n\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\tif (!cell) return cellId;\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(cell);\n\tconst first: BlockNode | undefined = blockChildren[0];\n\tif (!first) return cellId;\n\tlet current: BlockNode = first;\n\twhile (true) {\n\t\tconst children: readonly BlockNode[] = getBlockChildren(current);\n\t\tconst next: BlockNode | undefined = children[0];\n\t\tif (!next) return current.id;\n\t\tcurrent = next;\n\t}\n}\n\n/** Returns the last leaf-block ID inside a table cell. */\nexport function getLastLeafInCell(state: EditorState, cellId: BlockId): BlockId {\n\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\tif (!cell) return cellId;\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(cell);\n\tconst last: BlockNode | undefined = blockChildren[blockChildren.length - 1];\n\tif (!last) return cellId;\n\tlet current: BlockNode = last;\n\twhile (true) {\n\t\tconst children: readonly BlockNode[] = getBlockChildren(current);\n\t\tconst next: BlockNode | undefined = children[children.length - 1];\n\t\tif (!next) return current.id;\n\t\tcurrent = next;\n\t}\n}\n\n/** Checks whether a block is inside a table. */\nexport function isInsideTable(state: EditorState, blockId: BlockId): boolean {\n\tconst path = state.getNodePath(blockId);\n\tif (!path) return false;\n\n\tfor (const id of path) {\n\t\tconst node = state.getBlock(id as BlockId);\n\t\tif (node?.type === 'table') return true;\n\t}\n\treturn false;\n}\n","/**\n * Locale interface and default English locale for the TablePlugin.\n * Allows full i18n of all user-facing strings in the table feature.\n */\n\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\n\n// --- Locale Interface ---\n\nexport interface TableLocale {\n\t// Context Menu\n\treadonly insertRowAbove: string;\n\treadonly insertRowBelow: string;\n\treadonly insertColumnLeft: string;\n\treadonly insertColumnRight: string;\n\treadonly deleteRow: string;\n\treadonly deleteColumn: string;\n\treadonly borderColorLabel: string;\n\treadonly deleteTable: string;\n\n\t// Menu A11y\n\treadonly tableActions: string;\n\treadonly menuKeyboardHint: string;\n\n\t// Controls\n\treadonly insertRow: string;\n\treadonly insertColumn: string;\n\treadonly addRow: string;\n\treadonly addColumn: string;\n\treadonly tableActionsHint: string;\n\treadonly contextMenuHint: string;\n\treadonly borderColor: string;\n\n\t// Sizing\n\treadonly sizeLabel: string;\n\treadonly sizeDialogLabel: string;\n\treadonly columnWidthLabel: string;\n\treadonly rowMinimumHeightLabel: string;\n\treadonly automatic: string;\n\treadonly mixed: string;\n\treadonly apply: string;\n\treadonly cancel: string;\n\treadonly resetColumnWidth: string;\n\treadonly resetRowMinimumHeight: string;\n\treadonly resetAllSizes: string;\n\treadonly invalidDimensionRange: (minimum: number, maximum: number) => string;\n\t/** Receives a zero-based logical row index and renders it as a one-based label. */\n\treadonly selectRowLabel: (rowIndex: number) => string;\n\t/** Receives a zero-based logical column index and renders it as a one-based label. */\n\treadonly selectColumnLabel: (columnIndex: number) => string;\n\t/** Receives a zero-based logical column index and renders it as a one-based label. */\n\treadonly resizeColumnSeparatorLabel: (columnIndex: number) => string;\n\t/** Receives a zero-based logical row index and renders it as a one-based label. */\n\treadonly resizeRowSeparatorLabel: (rowIndex: number) => string;\n\treadonly resizeKeyboardHint: (step: number, largeStep: number) => string;\n\t/** Receives a zero-based logical column index and announces it as one-based. */\n\treadonly announceColumnWidthSet: (columnIndex: number, valuePx: number) => string;\n\t/** Receives a zero-based logical row index and announces it as one-based. */\n\treadonly announceRowMinimumHeightSet: (rowIndex: number, valuePx: number) => string;\n\t/** Receives a zero-based logical column index and announces it as one-based. */\n\treadonly announceColumnWidthReset: (columnIndex: number) => string;\n\t/** Receives a zero-based logical row index and announces it as one-based. */\n\treadonly announceRowMinimumHeightReset: (rowIndex: number) => string;\n\treadonly announceTableSizesReset: string;\n\n\t// Border Color Picker\n\treadonly defaultColor: string;\n\treadonly noBorders: string;\n\treadonly borderColorPicker: string;\n\treadonly announceBorderColorSet: (colorName: string) => string;\n\treadonly announceBorderReset: string;\n\treadonly borderSwatchLabel: (colorName: string) => string;\n\n\t// Command Announcements\n\treadonly announceRowInsertedAbove: string;\n\treadonly announceRowInsertedBelow: string;\n\treadonly announceColumnInserted: (side: string) => string;\n\treadonly announceRowDeleted: string;\n\treadonly announceColumnDeleted: string;\n\treadonly announceTableDeleted: string;\n\n\t// Toolbar\n\treadonly insertTable: string;\n\n\t// NodeView ARIA\n\treadonly tableAriaLabel: (rows: number, cols: number) => string;\n\treadonly tableAriaDescription: string;\n}\n\n// --- Default English Locale ---\n\nexport const TABLE_LOCALE_EN: TableLocale = {\n\t// Context Menu\n\tinsertRowAbove: 'Insert Row Above',\n\tinsertRowBelow: 'Insert Row Below',\n\tinsertColumnLeft: 'Insert Column Left',\n\tinsertColumnRight: 'Insert Column Right',\n\tdeleteRow: 'Delete Row',\n\tdeleteColumn: 'Delete Column',\n\tborderColorLabel: 'Border Color...',\n\tdeleteTable: 'Delete Table',\n\n\t// Menu A11y\n\ttableActions: 'Table actions',\n\tmenuKeyboardHint: '\\u2191\\u2193 Navigate \\u00b7 Enter Select \\u00b7 Esc Close',\n\n\t// Controls\n\tinsertRow: 'Insert row',\n\tinsertColumn: 'Insert column',\n\taddRow: 'Add row',\n\taddColumn: 'Add column',\n\ttableActionsHint: 'Table actions (Right-click or Shift+F10)',\n\tcontextMenuHint: 'Right-click or Shift+F10 for table actions',\n\tborderColor: 'Border color',\n\n\t// Sizing\n\tsizeLabel: 'Size...',\n\tsizeDialogLabel: 'Table size',\n\tcolumnWidthLabel: 'Column width (px)',\n\trowMinimumHeightLabel: 'Row minimum height (px)',\n\tautomatic: 'Automatic',\n\tmixed: 'Mixed',\n\tapply: 'Apply',\n\tcancel: 'Cancel',\n\tresetColumnWidth: 'Reset column width',\n\tresetRowMinimumHeight: 'Reset row minimum height',\n\tresetAllSizes: 'Reset all sizes',\n\tinvalidDimensionRange: (minimum: number, maximum: number) =>\n\t\t`Enter a value from ${minimum} to ${maximum} px.`,\n\tselectRowLabel: (rowIndex: number) => `Select row ${rowIndex + 1}`,\n\tselectColumnLabel: (columnIndex: number) => `Select column ${columnIndex + 1}`,\n\tresizeColumnSeparatorLabel: (columnIndex: number) => `Resize column ${columnIndex + 1}`,\n\tresizeRowSeparatorLabel: (rowIndex: number) => `Resize row ${rowIndex + 1}`,\n\tresizeKeyboardHint: (step: number, largeStep: number) =>\n\t\t`Arrow keys resize by ${step} px; hold Shift for ${largeStep} px.`,\n\tannounceColumnWidthSet: (columnIndex: number, valuePx: number) =>\n\t\t`Column ${columnIndex + 1} width set to ${valuePx} px.`,\n\tannounceRowMinimumHeightSet: (rowIndex: number, valuePx: number) =>\n\t\t`Row ${rowIndex + 1} minimum height set to ${valuePx} px.`,\n\tannounceColumnWidthReset: (columnIndex: number) =>\n\t\t`Column ${columnIndex + 1} width reset to automatic.`,\n\tannounceRowMinimumHeightReset: (rowIndex: number) =>\n\t\t`Row ${rowIndex + 1} minimum height reset to automatic.`,\n\tannounceTableSizesReset: 'Table column widths and row minimum heights reset to automatic.',\n\n\t// Border Color Picker\n\tdefaultColor: 'Default',\n\tnoBorders: 'No borders',\n\tborderColorPicker: 'Border color picker',\n\tannounceBorderColorSet: (colorName: string) => `Table border color set to ${colorName}`,\n\tannounceBorderReset: 'Table borders reset to default',\n\tborderSwatchLabel: (colorName: string) => `Border ${colorName}`,\n\n\t// Command Announcements\n\tannounceRowInsertedAbove: 'Row inserted above',\n\tannounceRowInsertedBelow: 'Row inserted below',\n\tannounceColumnInserted: (side: string) => `Column inserted ${side}`,\n\tannounceRowDeleted: 'Row deleted',\n\tannounceColumnDeleted: 'Column deleted',\n\tannounceTableDeleted: 'Table deleted',\n\n\t// Toolbar\n\tinsertTable: 'Insert Table',\n\n\t// NodeView ARIA\n\ttableAriaLabel: (rows: number, cols: number) => `Table with ${rows} rows and ${cols} columns`,\n\ttableAriaDescription: 'Right-click or press Shift+F10 for table actions',\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: LocaleModuleMap<TableLocale> = import.meta.glob<{\n\tdefault: TableLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadTableLocale(lang: string): Promise<TableLocale> {\n\treturn loadLocaleModule(localeModules, lang, TABLE_LOCALE_EN);\n}\n","/**\n * Border color commands, transaction builder, and accessible color picker\n * for table borders. Supports theme-default, custom hex, and borderless modes.\n * Delegates grid rendering to the shared ColorGrid module.\n */\n\nimport type { BlockAttrs } from '../../model/Document.js';\nimport { isTextSelection } 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 { PluginContext } from '../Plugin.js';\nimport { renderColorGrid } from '../shared/ColorGrid.js';\nimport { getColorName } from '../shared/ColorNames.js';\nimport { findTableContext } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\n\n// --- Border Color Palette ---\n\n/** Subdued, border-appropriate colors: grays, earth tones, muted hues. */\nexport const BORDER_COLOR_PALETTE: readonly string[] = [\n\t'#000000',\n\t'#434343',\n\t'#666666',\n\t'#999999',\n\t'#b7b7b7',\n\t'#cccccc',\n\t'#d9d9d9',\n\t'#efefef',\n\t'#980000',\n\t'#cc0000',\n\t'#e69138',\n\t'#f1c232',\n\t'#6aa84f',\n\t'#45818e',\n\t'#3c78d8',\n\t'#3d85c6',\n\t'#674ea7',\n\t'#a64d79',\n\t'#0b5394',\n\t'#38761d',\n];\n\nconst COLUMNS = 5;\n\n// --- Transaction Builder ---\n\n/**\n * Builds a transaction that sets `borderColor` on a table node.\n * Preserves existing attributes (spread pattern).\n */\nexport function buildSetBorderColorTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tborderColor: string | undefined,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\n\tconst path = state.getNodePath(tableId);\n\tif (!path) return null;\n\n\tif (borderColor === undefined) {\n\t\t// Remove borderColor from attrs, keep everything else\n\t\tconst { borderColor: _, ...rest } = (table.attrs ?? {}) as Record<\n\t\t\tstring,\n\t\t\tstring | number | boolean\n\t\t>;\n\t\tconst cleaned: BlockAttrs = rest;\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setNodeAttr(path, cleaned)\n\t\t\t.setSelection(state.selection)\n\t\t\t.build();\n\t}\n\n\tconst newAttrs: BlockAttrs = { ...table.attrs, borderColor };\n\treturn state\n\t\t.transaction('command')\n\t\t.setNodeAttr(path, newAttrs)\n\t\t.setSelection(state.selection)\n\t\t.build();\n}\n\n// --- Commands ---\n\n/** Sets the border color on the table surrounding the cursor. */\nexport function setTableBorderColor(\n\tcontext: PluginContext,\n\tcolor: string,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildSetBorderColorTransaction(state, tableCtx.tableId, color);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tconst colorName: string = color === 'none' ? 'none' : getColorName(color);\n\tcontext.announce(locale.announceBorderColorSet(colorName));\n\treturn true;\n}\n\n/** Resets the border color to theme default. */\nexport function resetTableBorderColor(\n\tcontext: PluginContext,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildSetBorderColorTransaction(state, tableCtx.tableId, undefined);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceBorderReset);\n\treturn true;\n}\n\n/** Reads the current border color from a table node. */\nexport function getTableBorderColor(state: EditorState, tableId: BlockId): string | undefined {\n\tconst table = state.getBlock(tableId);\n\tif (!table) return undefined;\n\treturn table.attrs?.borderColor as string | undefined;\n}\n\n// --- Border Color Picker ---\n\n/**\n * Renders an accessible border color picker into a container element.\n * Includes \"Default\", \"No borders\", and a WAI-ARIA grid of color swatches.\n */\nexport function renderBorderColorPicker(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\tonClose: () => void,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): void {\n\tcontainer.classList.add('notectl-color-picker');\n\n\tconst currentColor: string | undefined = getTableBorderColor(context.getState(), tableId);\n\n\t// --- Default button ---\n\tconst defaultBtn: HTMLButtonElement = document.createElement('button');\n\tdefaultBtn.type = 'button';\n\tdefaultBtn.className = 'notectl-color-picker__default';\n\tdefaultBtn.textContent = locale.defaultColor;\n\tdefaultBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tresetTableBorderColor(context, locale);\n\t\tonClose();\n\t});\n\tdefaultBtn.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tonClose();\n\t\t}\n\t});\n\tcontainer.appendChild(defaultBtn);\n\n\t// --- No borders button ---\n\tconst noBordersBtn: HTMLButtonElement = document.createElement('button');\n\tnoBordersBtn.type = 'button';\n\tnoBordersBtn.className = 'notectl-color-picker__default';\n\tnoBordersBtn.textContent = locale.noBorders;\n\tnoBordersBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tsetTableBorderColor(context, 'none', locale);\n\t\tonClose();\n\t});\n\tnoBordersBtn.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tonClose();\n\t\t}\n\t});\n\tcontainer.appendChild(noBordersBtn);\n\n\t// --- Color grid (delegated to shared ColorGrid) ---\n\trenderColorGrid(container, {\n\t\tcolors: BORDER_COLOR_PALETTE,\n\t\tcolumns: COLUMNS,\n\t\tariaLabel: locale.borderColorPicker,\n\t\tariaLabelPrefix: '',\n\t\tactiveColor: currentColor ?? null,\n\t\tswatchLabel: locale.borderSwatchLabel,\n\t\ttitleAsName: true,\n\t\tonSelect: (color: string) => {\n\t\t\tsetTableBorderColor(context, color, locale);\n\t\t\tonClose();\n\t\t},\n\t\tonClose,\n\t});\n}\n","/** Clipboard integration for the plugin's logical rectangular cell selection. */\n\nimport {\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n\tgetBlockChildren,\n\tgetBlockText,\n\tisLeafBlock,\n} from '../../model/Document.js';\nimport { createCollapsedSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport { isEventFromEditorContent } from '../../platform/EditorEventBoundary.js';\nimport { serializeDocumentToHTML } from '../../serialization/DocumentSerializer.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { TransactionBuilder } from '../../state/Transaction.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { createTableGrid, sliceTableToRange } from './TableGrid.js';\nimport type { CellRange, TableSelectionService } from './TableSelection.js';\n\n/** Installs copy/cut handling for TableSelectionService ranges. */\nexport function installTableClipboard(\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): () => void {\n\tconst container: HTMLElement = context.getContainer();\n\n\tconst onCopy = (event: ClipboardEvent): void => {\n\t\tif (!isEventFromEditorContent(event, container)) return;\n\t\twriteSelectedTableRange(event, context, selectionService);\n\t};\n\tconst onCut = (event: ClipboardEvent): void => {\n\t\tif (!isEventFromEditorContent(event, container)) return;\n\t\tif (context.isReadOnly?.()) return;\n\t\tif (!writeSelectedTableRange(event, context, selectionService)) return;\n\t\tclearSelectedTableCells(context, selectionService);\n\t};\n\n\t// Capture wins over the generic text clipboard handler on the same editor root.\n\tcontainer.addEventListener('copy', onCopy, true);\n\tcontainer.addEventListener('cut', onCut, true);\n\treturn () => {\n\t\tcontainer.removeEventListener('copy', onCopy, true);\n\t\tcontainer.removeEventListener('cut', onCut, true);\n\t};\n}\n\nfunction writeSelectedTableRange(\n\tevent: ClipboardEvent,\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): boolean {\n\tconst clipboardData: DataTransfer | null = event.clipboardData;\n\tconst range: CellRange | null = selectionService.getSelectedRange();\n\tif (!clipboardData || !range) return false;\n\tconst state: EditorState = context.getState();\n\tconst table: BlockNode | undefined = state.getBlock(range.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst slice: BlockNode | null = sliceTableToRange(table, {\n\t\tfromRow: range.fromRow,\n\t\tfromColumn: range.fromCol,\n\t\ttoRow: range.toRow,\n\t\ttoColumn: range.toCol,\n\t});\n\tif (!slice) return false;\n\n\tevent.preventDefault();\n\tevent.stopImmediatePropagation();\n\tclipboardData.setData(\n\t\t'text/html',\n\t\tserializeDocumentToHTML({ children: [slice] }, context.getSchemaRegistry()),\n\t);\n\tclipboardData.setData('text/plain', tableFragmentPlainText(slice));\n\treturn true;\n}\n\n/** Cut clears each selected logical cell once and keeps table structure/dimensions intact. */\nfunction clearSelectedTableCells(\n\tcontext: PluginContext,\n\tselectionService: TableSelectionService,\n): void {\n\tconst range: CellRange | null = selectionService.getSelectedRange();\n\tif (!range) return;\n\tconst state: EditorState = context.getState();\n\tconst table: BlockNode | undefined = state.getBlock(range.tableId);\n\tif (!table || table.type !== 'table') return;\n\tconst cells = createTableGrid(table).cellsInRange({\n\t\tfromRow: range.fromRow,\n\t\tfromColumn: range.fromCol,\n\t\ttoRow: range.toRow,\n\t\ttoColumn: range.toCol,\n\t});\n\tif (cells.length === 0) return;\n\n\tconst builder: TransactionBuilder = state.transaction('input');\n\tlet firstParagraphId: BlockId | null = null;\n\tfor (const entry of cells) {\n\t\tconst path: readonly BlockId[] | undefined = state.getNodePath(entry.cell.id);\n\t\tif (!path) return;\n\t\tconst children: readonly BlockNode[] = getBlockChildren(entry.cell);\n\t\tfor (let index = children.length - 1; index >= 0; index--) builder.removeNode(path, index);\n\t\tconst paragraphId: BlockId = generateBlockId();\n\t\tconst paragraph: BlockNode = createBlockNode(\n\t\t\tnodeType('paragraph'),\n\t\t\t[createTextNode('')],\n\t\t\tparagraphId,\n\t\t);\n\t\tbuilder.insertNode(path, 0, paragraph);\n\t\tfirstParagraphId ??= paragraphId;\n\t}\n\tif (!firstParagraphId) return;\n\tbuilder.setSelection(createCollapsedSelection(firstParagraphId, 0));\n\tcontext.dispatch(builder.build());\n\tselectionService.clearSelectionSilent();\n}\n\n/** Serializes a table fragment as logical TSV, including empty span placeholders. */\nexport function tableFragmentPlainText(table: BlockNode): string {\n\tconst grid = createTableGrid(table);\n\treturn Array.from({ length: grid.rowCount }, (_unused: unknown, row: number): string => {\n\t\treturn Array.from({ length: grid.columnCount }, (_column: unknown, column: number): string => {\n\t\t\tconst entry = grid.cellAt(row, column);\n\t\t\tif (!entry || entry.rowStart !== row || entry.columnStart !== column) return '';\n\t\t\treturn blockTreePlainText(entry.cell);\n\t\t}).join('\\t');\n\t}).join('\\n');\n}\n\nfunction blockTreePlainText(block: BlockNode): string {\n\tif (isLeafBlock(block)) return getBlockText(block);\n\treturn getBlockChildren(block).map(blockTreePlainText).join('\\n');\n}\n","/**\n * Multi-cell selection service for tables.\n * Tracks selected cell range and provides IDs for bulk formatting.\n */\n\nimport { createCollapsedSelection, isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { isEventFromEditorContent } from '../../platform/EditorEventBoundary.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport { ServiceKey } from '../Plugin.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { type TableGrid, createTableGrid, expandTableGridRange } from './TableGrid.js';\nimport { findTableContext, getFirstLeafInCell } from './TableHelpers.js';\n\n/** Rectangular range of cells within a table. */\nexport interface CellRange {\n\treadonly tableId: BlockId;\n\treadonly fromRow: number;\n\treadonly fromCol: number;\n\treadonly toRow: number;\n\treadonly toCol: number;\n}\n\n/** Service for managing multi-cell selection. */\nexport interface TableSelectionService {\n\tgetSelectedRange(): CellRange | null;\n\tsetSelectedRange(range: CellRange | null): void;\n\t/** Clears selection state without dispatching a transaction (for use in decorations). */\n\tclearSelectionSilent(): void;\n\tgetSelectedCellIds(): readonly BlockId[];\n\tisSelected(cellId: BlockId): boolean;\n}\n\nexport const TableSelectionServiceKey = new ServiceKey<TableSelectionService>('tableSelection');\n\n/** Creates and registers the TableSelectionService. */\nexport function createTableSelectionService(context: PluginContext): TableSelectionService {\n\tlet selectedRange: CellRange | null = null;\n\tlet selectedStructure: string | null = null;\n\tlet cachedCellIds: readonly BlockId[] = [];\n\tlet cachedCellIdSet: Set<BlockId> = new Set();\n\n\tfunction updateCache(): void {\n\t\tconst current = resolveCurrentSelection();\n\t\tif (!current) {\n\t\t\tcachedCellIds = [];\n\t\t\tcachedCellIdSet = new Set();\n\t\t\treturn;\n\t\t}\n\n\t\tconst { grid, range } = current;\n\n\t\tconst ids: BlockId[] = grid\n\t\t\t.cellsInRange({\n\t\t\t\tfromRow: range.fromRow,\n\t\t\t\tfromColumn: range.fromCol,\n\t\t\t\ttoRow: range.toRow,\n\t\t\t\ttoColumn: range.toCol,\n\t\t\t})\n\t\t\t.map((entry) => entry.cell.id);\n\n\t\tcachedCellIds = ids;\n\t\tcachedCellIdSet = new Set(ids);\n\t}\n\n\tfunction resolveCurrentSelection(): {\n\t\treadonly range: CellRange;\n\t\treadonly grid: TableGrid;\n\t} | null {\n\t\tif (!selectedRange || !selectedStructure) return null;\n\t\tconst table = context.getState().getBlock(selectedRange.tableId);\n\t\tif (!table || table.type !== 'table') {\n\t\t\tclearInternal();\n\t\t\treturn null;\n\t\t}\n\t\tconst grid: TableGrid = createTableGrid(table);\n\t\tif (tableStructureSignature(grid) !== selectedStructure) {\n\t\t\tclearInternal();\n\t\t\treturn null;\n\t\t}\n\t\treturn { range: selectedRange, grid };\n\t}\n\n\tfunction clearInternal(): void {\n\t\tselectedRange = null;\n\t\tselectedStructure = null;\n\t\tcachedCellIds = [];\n\t\tcachedCellIdSet = new Set();\n\t}\n\n\tconst service: TableSelectionService = {\n\t\tgetSelectedRange(): CellRange | null {\n\t\t\treturn resolveCurrentSelection()?.range ?? null;\n\t\t},\n\n\t\tsetSelectedRange(range: CellRange | null): void {\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tlet focusCellId: BlockId | null = null;\n\t\t\tif (!range) {\n\t\t\t\tclearInternal();\n\t\t\t} else {\n\t\t\t\tconst table = context.getState().getBlock(range.tableId);\n\t\t\t\tconst grid: TableGrid | null = table?.type === 'table' ? createTableGrid(table) : null;\n\t\t\t\tconst coordinates: readonly number[] = [\n\t\t\t\t\trange.fromRow,\n\t\t\t\t\trange.fromCol,\n\t\t\t\t\trange.toRow,\n\t\t\t\t\trange.toCol,\n\t\t\t\t];\n\t\t\t\tconst valid: boolean =\n\t\t\t\t\t!!grid &&\n\t\t\t\t\tcoordinates.every(Number.isInteger) &&\n\t\t\t\t\trange.fromRow >= 0 &&\n\t\t\t\t\trange.toRow >= 0 &&\n\t\t\t\t\trange.fromCol >= 0 &&\n\t\t\t\t\trange.toCol >= 0 &&\n\t\t\t\t\trange.fromRow < grid.rowCount &&\n\t\t\t\t\trange.toRow < grid.rowCount &&\n\t\t\t\t\trange.fromCol < grid.columnCount &&\n\t\t\t\t\trange.toCol < grid.columnCount;\n\t\t\t\tconst expanded =\n\t\t\t\t\tvalid && grid\n\t\t\t\t\t\t? expandTableGridRange(grid, {\n\t\t\t\t\t\t\t\tfromRow: range.fromRow,\n\t\t\t\t\t\t\t\tfromColumn: range.fromCol,\n\t\t\t\t\t\t\t\ttoRow: range.toRow,\n\t\t\t\t\t\t\t\ttoColumn: range.toCol,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: null;\n\t\t\t\tif (!grid || !expanded) {\n\t\t\t\t\tclearInternal();\n\t\t\t\t} else {\n\t\t\t\t\tselectedRange = {\n\t\t\t\t\t\ttableId: range.tableId,\n\t\t\t\t\t\tfromRow: expanded.fromRow,\n\t\t\t\t\t\tfromCol: expanded.fromColumn,\n\t\t\t\t\t\ttoRow: expanded.toRow,\n\t\t\t\t\t\ttoCol: expanded.toColumn,\n\t\t\t\t\t};\n\t\t\t\t\tselectedStructure = tableStructureSignature(grid);\n\t\t\t\t\tfocusCellId = grid.cellAt(expanded.fromRow, expanded.fromColumn)?.cell.id ?? null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdateCache();\n\t\t\tconst currentTable = isTextSelection(state.selection)\n\t\t\t\t? findTableContext(state, state.selection.anchor.blockId)\n\t\t\t\t: null;\n\t\t\tconst selection =\n\t\t\t\tfocusCellId && currentTable?.tableId !== range?.tableId\n\t\t\t\t\t? createCollapsedSelection(getFirstLeafInCell(state, focusCellId), 0)\n\t\t\t\t\t: state.selection;\n\t\t\tcontext.dispatch(state.transaction('api').setSelection(selection).build());\n\t\t},\n\n\t\tclearSelectionSilent(): void {\n\t\t\tclearInternal();\n\t\t},\n\n\t\tgetSelectedCellIds(): readonly BlockId[] {\n\t\t\tupdateCache();\n\t\t\treturn cachedCellIds;\n\t\t},\n\n\t\tisSelected(cellId: BlockId): boolean {\n\t\t\tupdateCache();\n\t\t\treturn cachedCellIdSet.has(cellId);\n\t\t},\n\t};\n\n\tcontext.registerService(TableSelectionServiceKey, service);\n\treturn service;\n}\n\nfunction tableStructureSignature(grid: TableGrid): string {\n\treturn JSON.stringify({\n\t\trows: grid.rows.map((row) => row.id),\n\t\tcells: grid.cells.map((entry) => [\n\t\t\tentry.cell.id,\n\t\t\tentry.rowStart,\n\t\t\tentry.rowEnd,\n\t\t\tentry.columnStart,\n\t\t\tentry.columnEnd,\n\t\t]),\n\t});\n}\n\n/**\n * Installs mouse handlers for multi-cell selection on the editor container.\n * Returns a cleanup function.\n */\nexport function installMouseSelection(\n\tcontext: PluginContext,\n\tservice: TableSelectionService,\n): () => void {\n\tconst container: HTMLElement = context.getContainer();\n\tlet anchorCell: {\n\t\ttableId: BlockId;\n\t\tcellId: BlockId;\n\t\trowStart: number;\n\t\trowEnd: number;\n\t\tcolStart: number;\n\t\tcolEnd: number;\n\t} | null = null;\n\tlet isDragging = false;\n\n\tfunction handleMouseDown(e: MouseEvent): void {\n\t\tif (e.button !== 0 || context.isReadOnly?.()) return;\n\t\tif (!isEventFromEditorContent(e, container)) return;\n\t\tconst target = e.target as HTMLElement;\n\t\tconst cellEl: HTMLElement | null = target.closest('td[data-block-id]');\n\t\tif (!cellEl) {\n\t\t\tanchorCell = null;\n\t\t\tisDragging = false;\n\t\t\tservice.setSelectedRange(null);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!e.shiftKey) {\n\t\t\t// Clear any existing multi-cell selection before starting a new anchor\n\t\t\tservice.setSelectedRange(null);\n\t\t\t// Start new selection anchor\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\t\tconst tableCtx = findTableContext(state, cellId);\n\t\t\tif (!tableCtx) {\n\t\t\t\tanchorCell = null;\n\t\t\t\tisDragging = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tanchorCell = {\n\t\t\t\ttableId: tableCtx.tableId,\n\t\t\t\tcellId: tableCtx.cellId,\n\t\t\t\trowStart: tableCtx.rowIndex,\n\t\t\t\trowEnd: tableCtx.rowEnd,\n\t\t\t\tcolStart: tableCtx.colIndex,\n\t\t\t\tcolEnd: tableCtx.colEnd,\n\t\t\t};\n\t\t\tisDragging = true;\n\t\t\t// Don't set range yet — wait for mousemove to avoid interfering with clicks\n\t\t} else if (anchorCell) {\n\t\t\t// Shift-click: extend selection\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\t\tconst tableCtx = findTableContext(state, cellId);\n\t\t\tif (!tableCtx || tableCtx.tableId !== anchorCell.tableId) {\n\t\t\t\tanchorCell = null;\n\t\t\t\tservice.setSelectedRange(null);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\te.preventDefault();\n\t\t\tservice.setSelectedRange(rangeCoveringCells(anchorCell, tableCtx));\n\t\t}\n\t}\n\n\tfunction handleMouseMove(e: MouseEvent): void {\n\t\tif (context.isReadOnly?.() || !isDragging || !anchorCell) return;\n\n\t\tconst target = e.target as HTMLElement;\n\t\tconst cellEl: HTMLElement | null = target.closest('td[data-block-id]');\n\t\tif (!cellEl) return;\n\n\t\tconst state: EditorState = context.getState();\n\t\tconst cellId = cellEl.getAttribute('data-block-id') as BlockId;\n\t\tconst tableCtx = findTableContext(state, cellId);\n\t\tif (!tableCtx || tableCtx.tableId !== anchorCell.tableId) return;\n\n\t\t// Only set range if we've moved to a different cell\n\t\tif (tableCtx.cellId !== anchorCell.cellId) {\n\t\t\te.preventDefault();\n\t\t\tservice.setSelectedRange(rangeCoveringCells(anchorCell, tableCtx));\n\t\t}\n\t}\n\n\tfunction handleMouseUp(): void {\n\t\tisDragging = false;\n\t}\n\n\tcontainer.addEventListener('mousedown', handleMouseDown);\n\tcontainer.addEventListener('mousemove', handleMouseMove);\n\tdocument.addEventListener('mouseup', handleMouseUp);\n\n\treturn () => {\n\t\tcontainer.removeEventListener('mousedown', handleMouseDown);\n\t\tcontainer.removeEventListener('mousemove', handleMouseMove);\n\t\tdocument.removeEventListener('mouseup', handleMouseUp);\n\t};\n}\n\nfunction rangeCoveringCells(\n\tanchor: {\n\t\treadonly tableId: BlockId;\n\t\treadonly rowStart: number;\n\t\treadonly rowEnd: number;\n\t\treadonly colStart: number;\n\t\treadonly colEnd: number;\n\t},\n\ttarget: {\n\t\treadonly rowIndex: number;\n\t\treadonly rowEnd: number;\n\t\treadonly colIndex: number;\n\t\treadonly colEnd: number;\n\t},\n): CellRange {\n\treturn {\n\t\ttableId: anchor.tableId,\n\t\tfromRow: Math.min(anchor.rowStart, target.rowIndex),\n\t\tfromCol: Math.min(anchor.colStart, target.colIndex),\n\t\ttoRow: Math.max(anchor.rowEnd, target.rowEnd) - 1,\n\t\ttoCol: Math.max(anchor.colEnd, target.colEnd) - 1,\n\t};\n}\n","/**\n * Persistent table sizing model and public service.\n *\n * Column widths belong to the table's logical-column vector; row minimum\n * heights belong to table_row nodes. Cell and range targets are resolved\n * through TableGrid so colspan/rowspan never leak child-array coordinates into\n * the public API.\n */\n\nimport type { BlockAttrValue, BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { isTextSelection } from '../../model/Selection.js';\nimport {\n\tMAX_TABLE_DIMENSION_PX,\n\tMIN_TABLE_DIMENSION_PX,\n\tnormalizeTableDimensionPx,\n} from '../../model/TableDimensions.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { TransactionBuilder } from '../../state/Transaction.js';\nimport { type PluginContext, ServiceKey } from '../Plugin.js';\nimport { type TableGrid, createTableGrid, expandTableGridRange } from './TableGrid.js';\nimport { findTableContext } from './TableHelpers.js';\nimport {\n\ttype CellRange,\n\ttype TableSelectionService,\n\tTableSelectionServiceKey,\n} from './TableSelection.js';\n\n// --- Configuration ---\n\nexport interface TableSizingConfig {\n\treadonly minColumnWidthPx: number;\n\treadonly minRowHeightPx: number;\n\treadonly maxColumnWidthPx: number;\n\treadonly maxRowHeightPx: number;\n\treadonly keyboardResizeStepPx: number;\n\treadonly keyboardResizeLargeStepPx: number;\n}\n\nexport const DEFAULT_TABLE_SIZING_CONFIG: Readonly<TableSizingConfig> = Object.freeze({\n\tminColumnWidthPx: 60,\n\tminRowHeightPx: 24,\n\tmaxColumnWidthPx: MAX_TABLE_DIMENSION_PX,\n\tmaxRowHeightPx: MAX_TABLE_DIMENSION_PX,\n\tkeyboardResizeStepPx: 8,\n\tkeyboardResizeLargeStepPx: 32,\n});\n\n// --- Public API ---\n\nexport type TableDimensionInput = number | 'auto';\nexport type TableDimensionState = number | 'auto' | 'mixed' | 'unavailable';\nexport type TableSizeDimension = 'columnWidthPx' | 'rowMinHeightPx';\n\nexport interface TableSizeInput {\n\treadonly columnWidthPx?: TableDimensionInput;\n\treadonly rowMinHeightPx?: TableDimensionInput;\n}\n\nexport interface TableSizeState {\n\treadonly columnWidthPx: TableDimensionState;\n\treadonly rowMinHeightPx: TableDimensionState;\n}\n\nexport interface TableCellSizeTarget {\n\treadonly kind: 'cell';\n\treadonly tableId: BlockId;\n\treadonly row: number;\n\treadonly column: number;\n}\n\nexport interface TableRangeSizeTarget {\n\treadonly kind: 'range';\n\treadonly tableId: BlockId;\n\treadonly fromRow: number;\n\treadonly fromColumn: number;\n\treadonly toRow: number;\n\treadonly toColumn: number;\n}\n\nexport interface TableColumnSizeTarget {\n\treadonly kind: 'column';\n\treadonly tableId: BlockId;\n\treadonly column: number;\n}\n\nexport interface TableRowSizeTarget {\n\treadonly kind: 'row';\n\treadonly tableId: BlockId;\n\treadonly row: number;\n}\n\nexport type TableSizeTarget =\n\t| TableCellSizeTarget\n\t| TableRangeSizeTarget\n\t| TableColumnSizeTarget\n\t| TableRowSizeTarget;\n\nexport interface TableSizingService {\n\tgetSelectionSize(): TableSizeState | null;\n\tsetSelectionSize(input: TableSizeInput): boolean;\n\tresetSelectionSize(dimension?: TableSizeDimension): boolean;\n\tgetSize(target: TableSizeTarget): TableSizeState | null;\n\tsetSize(target: TableSizeTarget, input: TableSizeInput): boolean;\n\tresetSize(target: TableSizeTarget, dimension?: TableSizeDimension): boolean;\n}\n\nexport const TableSizingServiceKey = new ServiceKey<TableSizingService>('tableSizing');\n\n/** Creates and registers the strongly typed table sizing service. */\nexport function createTableSizingService(\n\tcontext: PluginContext,\n\tconfig: Partial<TableSizingConfig> = {},\n): TableSizingService {\n\tconst resolvedConfig: TableSizingConfig = resolveTableSizingConfig(config);\n\n\tconst service: TableSizingService = {\n\t\tgetSelectionSize(): TableSizeState | null {\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? readResolvedSize(resolved) : null;\n\t\t},\n\n\t\tsetSelectionSize(input: TableSizeInput): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? applySize(context, resolved, input, resolvedConfig) : false;\n\t\t},\n\n\t\tresetSelectionSize(dimension?: TableSizeDimension): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveSelectionTarget(context);\n\t\t\treturn resolved ? resetResolvedSize(context, resolved, dimension, resolvedConfig) : false;\n\t\t},\n\n\t\tgetSize(target: TableSizeTarget): TableSizeState | null {\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? readResolvedSize(resolved) : null;\n\t\t},\n\n\t\tsetSize(target: TableSizeTarget, input: TableSizeInput): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? applySize(context, resolved, input, resolvedConfig) : false;\n\t\t},\n\n\t\tresetSize(target: TableSizeTarget, dimension?: TableSizeDimension): boolean {\n\t\t\tif (context.isReadOnly()) return false;\n\t\t\tconst resolved: ResolvedTarget | null = resolveExplicitTarget(context.getState(), target);\n\t\t\treturn resolved ? resetResolvedSize(context, resolved, dimension, resolvedConfig) : false;\n\t\t},\n\t};\n\n\tcontext.registerService(TableSizingServiceKey, service);\n\treturn service;\n}\n\n/** Resolves and clamps every sizing configuration value into safe global bounds. */\nexport function resolveTableSizingConfig(config: Partial<TableSizingConfig>): TableSizingConfig {\n\tconst minColumnWidthPx: number = normalizeConfigValue(\n\t\tconfig.minColumnWidthPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.minColumnWidthPx,\n\t);\n\tconst minRowHeightPx: number = normalizeConfigValue(\n\t\tconfig.minRowHeightPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.minRowHeightPx,\n\t);\n\tconst maxColumnWidthPx: number = normalizeConfigValue(\n\t\tconfig.maxColumnWidthPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.maxColumnWidthPx,\n\t\tminColumnWidthPx,\n\t);\n\tconst maxRowHeightPx: number = normalizeConfigValue(\n\t\tconfig.maxRowHeightPx,\n\t\tDEFAULT_TABLE_SIZING_CONFIG.maxRowHeightPx,\n\t\tminRowHeightPx,\n\t);\n\n\treturn {\n\t\tminColumnWidthPx,\n\t\tminRowHeightPx,\n\t\tmaxColumnWidthPx,\n\t\tmaxRowHeightPx,\n\t\tkeyboardResizeStepPx: normalizeConfigValue(\n\t\t\tconfig.keyboardResizeStepPx,\n\t\t\tDEFAULT_TABLE_SIZING_CONFIG.keyboardResizeStepPx,\n\t\t),\n\t\tkeyboardResizeLargeStepPx: normalizeConfigValue(\n\t\t\tconfig.keyboardResizeLargeStepPx,\n\t\t\tDEFAULT_TABLE_SIZING_CONFIG.keyboardResizeLargeStepPx,\n\t\t),\n\t};\n}\n\n// --- Canonical dimension attributes (also used by structural commands) ---\n\n/** Returns a fixed-length logical-width vector. Invalid/missing entries become automatic. */\nexport function readTableColumnWidthsPx(\n\ttable: BlockNode,\n\tcolumnCount: number,\n): readonly (number | null)[] {\n\tconst count: number = Math.max(0, Math.trunc(columnCount));\n\tconst raw: BlockAttrValue | undefined = table.attrs?.columnWidthsPx;\n\tconst values: readonly unknown[] = Array.isArray(raw) ? raw : [];\n\treturn Object.freeze(\n\t\tArray.from({ length: count }, (_unused: unknown, index: number): number | null => {\n\t\t\tconst value: unknown = values[index];\n\t\t\tif (value === null || value === undefined) return null;\n\t\t\treturn normalizeStoredDimension(value);\n\t\t}),\n\t);\n}\n\n/** Replaces the canonical column-width vector while preserving unrelated table attrs. */\nexport function withTableColumnWidthsPx(\n\tattrs: BlockAttrs | undefined,\n\twidths: readonly (number | null)[],\n): BlockAttrs {\n\tconst normalized: readonly (number | null)[] = Object.freeze(\n\t\twidths.map((value: number | null): number | null =>\n\t\t\tvalue === null ? null : normalizeStoredDimension(value),\n\t\t),\n\t);\n\tconst { columnWidthsPx: _columnWidthsPx, ...rest } = attrs ?? {};\n\tif (!normalized.some((value: number | null): boolean => value !== null)) return rest;\n\treturn { ...rest, columnWidthsPx: normalized };\n}\n\n/** Reads a row's canonical minimum height, or null for automatic layout. */\nexport function readTableRowMinHeightPx(row: BlockNode): number | null {\n\treturn normalizeStoredDimension(row.attrs?.minHeightPx);\n}\n\n/** Sets or removes a row's minimum-height attribute while preserving unrelated attrs. */\nexport function withTableRowMinHeightPx(\n\tattrs: BlockAttrs | undefined,\n\tminHeightPx: number | null,\n): BlockAttrs {\n\tconst { minHeightPx: _minHeightPx, ...rest } = attrs ?? {};\n\tif (minHeightPx === null) return rest;\n\tconst normalized: number | null = normalizeStoredDimension(minHeightPx);\n\treturn normalized === null ? rest : { ...rest, minHeightPx: normalized };\n}\n\n// --- Resolution ---\n\ninterface ResolvedTarget {\n\treadonly state: EditorState;\n\treadonly table: BlockNode;\n\treadonly grid: TableGrid;\n\treadonly columns: readonly number[];\n\treadonly rows: readonly number[];\n}\n\nfunction resolveSelectionTarget(context: PluginContext): ResolvedTarget | null {\n\tconst state: EditorState = context.getState();\n\tconst selectionService: TableSelectionService | undefined =\n\t\tcontext.getService(TableSelectionServiceKey);\n\tconst selectedRange: CellRange | null = selectionService?.getSelectedRange() ?? null;\n\tif (selectedRange) {\n\t\treturn resolveRange(state, selectedRange.tableId, selectedRange, true);\n\t}\n\n\tif (!isTextSelection(state.selection)) return null;\n\tconst tableContext = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableContext) return null;\n\treturn resolveRange(\n\t\tstate,\n\t\ttableContext.tableId,\n\t\t{\n\t\t\ttableId: tableContext.tableId,\n\t\t\tfromRow: tableContext.rowIndex,\n\t\t\tfromCol: tableContext.colIndex,\n\t\t\ttoRow: tableContext.rowEnd - 1,\n\t\t\ttoCol: tableContext.colEnd - 1,\n\t\t},\n\t\ttrue,\n\t);\n}\n\nfunction resolveExplicitTarget(state: EditorState, target: TableSizeTarget): ResolvedTarget | null {\n\tconst table: BlockNode | undefined = state.getBlock(target.tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\n\tswitch (target.kind) {\n\t\tcase 'cell': {\n\t\t\tif (!validCoordinate(target.row, grid.rowCount)) return null;\n\t\t\tif (!validCoordinate(target.column, grid.columnCount)) return null;\n\t\t\tconst cell = grid.cellAt(target.row, target.column);\n\t\t\tif (!cell) return null;\n\t\t\treturn makeResolvedTarget(\n\t\t\t\tstate,\n\t\t\t\ttable,\n\t\t\t\tgrid,\n\t\t\t\tcell.rowStart,\n\t\t\t\tcell.rowEnd - 1,\n\t\t\t\tcell.columnStart,\n\t\t\t\tcell.columnEnd - 1,\n\t\t\t);\n\t\t}\n\t\tcase 'range':\n\t\t\treturn resolveRange(\n\t\t\t\tstate,\n\t\t\t\ttarget.tableId,\n\t\t\t\t{\n\t\t\t\t\ttableId: target.tableId,\n\t\t\t\t\tfromRow: target.fromRow,\n\t\t\t\t\tfromCol: target.fromColumn,\n\t\t\t\t\ttoRow: target.toRow,\n\t\t\t\t\ttoCol: target.toColumn,\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t);\n\t\tcase 'column':\n\t\t\tif (!validCoordinate(target.column, grid.columnCount)) return null;\n\t\t\treturn { state, table, grid, columns: [target.column], rows: [] };\n\t\tcase 'row':\n\t\t\tif (!validCoordinate(target.row, grid.rowCount)) return null;\n\t\t\treturn { state, table, grid, columns: [], rows: [target.row] };\n\t}\n}\n\nfunction resolveRange(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trange: CellRange,\n\tstrict: boolean,\n): ResolvedTarget | null {\n\tconst table: BlockNode | undefined = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (grid.rowCount === 0 || grid.columnCount === 0) return null;\n\n\tconst coordinates: readonly number[] = [range.fromRow, range.toRow, range.fromCol, range.toCol];\n\tif (!coordinates.every(Number.isInteger)) return null;\n\tif (\n\t\tstrict &&\n\t\t(!validCoordinate(range.fromRow, grid.rowCount) ||\n\t\t\t!validCoordinate(range.toRow, grid.rowCount) ||\n\t\t\t!validCoordinate(range.fromCol, grid.columnCount) ||\n\t\t\t!validCoordinate(range.toCol, grid.columnCount))\n\t) {\n\t\treturn null;\n\t}\n\n\tconst fromRow: number = clampIndex(Math.min(range.fromRow, range.toRow), grid.rowCount);\n\tconst toRow: number = clampIndex(Math.max(range.fromRow, range.toRow), grid.rowCount);\n\tconst fromColumn: number = clampIndex(Math.min(range.fromCol, range.toCol), grid.columnCount);\n\tconst toColumn: number = clampIndex(Math.max(range.fromCol, range.toCol), grid.columnCount);\n\tconst expanded = expandTableGridRange(grid, {\n\t\tfromRow,\n\t\tfromColumn,\n\t\ttoRow,\n\t\ttoColumn,\n\t});\n\treturn expanded\n\t\t? makeResolvedTarget(\n\t\t\t\tstate,\n\t\t\t\ttable,\n\t\t\t\tgrid,\n\t\t\t\texpanded.fromRow,\n\t\t\t\texpanded.toRow,\n\t\t\t\texpanded.fromColumn,\n\t\t\t\texpanded.toColumn,\n\t\t\t)\n\t\t: null;\n}\n\nfunction makeResolvedTarget(\n\tstate: EditorState,\n\ttable: BlockNode,\n\tgrid: TableGrid,\n\tfromRow: number,\n\ttoRow: number,\n\tfromColumn: number,\n\ttoColumn: number,\n): ResolvedTarget {\n\treturn {\n\t\tstate,\n\t\ttable,\n\t\tgrid,\n\t\trows: integerRange(fromRow, toRow),\n\t\tcolumns: integerRange(fromColumn, toColumn),\n\t};\n}\n\n// --- Reading and mutation ---\n\nfunction readResolvedSize(target: ResolvedTarget): TableSizeState {\n\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\ttarget.table,\n\t\ttarget.grid.columnCount,\n\t);\n\tconst columnValues: readonly (number | null)[] = target.columns.map(\n\t\t(column: number) => widths[column] ?? null,\n\t);\n\tconst rowValues: readonly (number | null)[] = target.rows.map((rowIndex: number) => {\n\t\tconst row: BlockNode | undefined = target.grid.rows[rowIndex];\n\t\treturn row ? readTableRowMinHeightPx(row) : null;\n\t});\n\n\treturn {\n\t\tcolumnWidthPx: summarizeValues(columnValues),\n\t\trowMinHeightPx: summarizeValues(rowValues),\n\t};\n}\n\nfunction applySize(\n\tcontext: PluginContext,\n\ttarget: ResolvedTarget,\n\tinput: TableSizeInput,\n\tconfig: TableSizingConfig,\n): boolean {\n\tconst hasColumnInput: boolean = Object.prototype.hasOwnProperty.call(input, 'columnWidthPx');\n\tconst hasRowInput: boolean = Object.prototype.hasOwnProperty.call(input, 'rowMinHeightPx');\n\tif (!hasColumnInput && !hasRowInput) return false;\n\tif (hasColumnInput && target.columns.length === 0) return false;\n\tif (hasRowInput && target.rows.length === 0) return false;\n\n\tconst columnValue: NormalizedInput | null = hasColumnInput\n\t\t? normalizeInput(input.columnWidthPx, config.minColumnWidthPx, config.maxColumnWidthPx)\n\t\t: null;\n\tconst rowValue: NormalizedInput | null = hasRowInput\n\t\t? normalizeInput(input.rowMinHeightPx, config.minRowHeightPx, config.maxRowHeightPx)\n\t\t: null;\n\tif ((hasColumnInput && !columnValue) || (hasRowInput && !rowValue)) return false;\n\n\tconst builder: TransactionBuilder = target.state.transaction('command');\n\tlet changed = false;\n\n\tif (columnValue) {\n\t\tconst widths: (number | null)[] = [\n\t\t\t...readTableColumnWidthsPx(target.table, target.grid.columnCount),\n\t\t];\n\t\tfor (const column of target.columns) {\n\t\t\tif (widths[column] !== columnValue.value) {\n\t\t\t\twidths[column] = columnValue.value;\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\t\tif (changed) {\n\t\t\tconst path: readonly BlockId[] | undefined = target.state.getNodePath(target.table.id);\n\t\t\tif (!path) return false;\n\t\t\tbuilder.setNodeAttr(path, withTableColumnWidthsPx(target.table.attrs, widths));\n\t\t}\n\t}\n\n\tif (rowValue) {\n\t\tfor (const rowIndex of target.rows) {\n\t\t\tconst row: BlockNode | undefined = target.grid.rows[rowIndex];\n\t\t\tif (!row || readTableRowMinHeightPx(row) === rowValue.value) continue;\n\t\t\tconst path: readonly BlockId[] | undefined = target.state.getNodePath(row.id);\n\t\t\tif (!path) return false;\n\t\t\tbuilder.setNodeAttr(path, withTableRowMinHeightPx(row.attrs, rowValue.value));\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\tif (!changed) return false;\n\tbuilder.setSelection(target.state.selection);\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\nfunction resetResolvedSize(\n\tcontext: PluginContext,\n\ttarget: ResolvedTarget,\n\tdimension: TableSizeDimension | undefined,\n\tconfig: TableSizingConfig,\n): boolean {\n\tif (dimension === 'columnWidthPx') {\n\t\treturn applySize(context, target, { columnWidthPx: 'auto' }, config);\n\t}\n\tif (dimension === 'rowMinHeightPx') {\n\t\treturn applySize(context, target, { rowMinHeightPx: 'auto' }, config);\n\t}\n\n\tconst input: TableSizeInput = {\n\t\t...(target.columns.length > 0 ? { columnWidthPx: 'auto' as const } : {}),\n\t\t...(target.rows.length > 0 ? { rowMinHeightPx: 'auto' as const } : {}),\n\t};\n\treturn applySize(context, target, input, config);\n}\n\ninterface NormalizedInput {\n\treadonly value: number | null;\n}\n\nfunction normalizeInput(\n\tvalue: TableDimensionInput | undefined,\n\tminimum: number,\n\tmaximum: number,\n): NormalizedInput | null {\n\tif (value === 'auto') return { value: null };\n\tconst normalized: number | null = normalizeTableDimensionPx(value, minimum, maximum);\n\treturn normalized === null ? null : { value: normalized };\n}\n\nfunction summarizeValues(values: readonly (number | null)[]): TableDimensionState {\n\tif (values.length === 0) return 'unavailable';\n\tconst first: number | null | undefined = values[0];\n\tif (values.some((value: number | null): boolean => value !== first)) return 'mixed';\n\treturn first === null || first === undefined ? 'auto' : first;\n}\n\n// --- Small utilities ---\n\nfunction normalizeStoredDimension(value: unknown): number | null {\n\tif (typeof value !== 'number') return null;\n\tif (value < MIN_TABLE_DIMENSION_PX || value > MAX_TABLE_DIMENSION_PX) return null;\n\treturn normalizeTableDimensionPx(value);\n}\n\nfunction normalizeConfigValue(\n\tvalue: unknown,\n\tfallback: number,\n\tminimum = MIN_TABLE_DIMENSION_PX,\n): number {\n\treturn normalizeTableDimensionPx(value ?? fallback, minimum, MAX_TABLE_DIMENSION_PX) ?? fallback;\n}\n\nfunction validCoordinate(value: number, upperBound: number): boolean {\n\treturn Number.isInteger(value) && value >= 0 && value < upperBound;\n}\n\nfunction clampIndex(value: number, upperBound: number): number {\n\treturn Math.min(upperBound - 1, Math.max(0, value));\n}\n\nfunction integerRange(from: number, to: number): readonly number[] {\n\treturn Array.from({ length: to - from + 1 }, (_unused: unknown, index: number) => from + index);\n}\n","/**\n * Table commands: insert table, add/remove rows and columns, delete table.\n * All commands are registered via PluginContext and operate through transactions.\n *\n * Shared transaction builders (`build*Transaction`) are pure functions that take\n * EditorState + explicit indices and return a Transaction or null. They are used\n * by both commands (via PluginContext) and controls (via getState/dispatch).\n */\n\nimport { insertBlockObjectOnOwnLine } from '../../commands/BlockInsertion.js';\nimport { createSelectionForBlockBoundary } from '../../commands/CommandHelpers.js';\nimport {\n\ttype BlockAttrValue,\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateEmptyParagraph,\n\tgenerateBlockId,\n\tgetBlockChildren,\n} from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisGapCursor,\n\tisNodeSelection,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getSelectedBlockId } from '../shared/PluginHelpers.js';\nimport { type TableGrid, type TableGridCell, createTableGrid } from './TableGrid.js';\nimport {\n\ttype TableContext,\n\tcreateTable,\n\tcreateTableCell,\n\tfindTableContext,\n\tgetCellAt,\n\tgetFirstLeafInCell,\n} from './TableHelpers.js';\nimport type { TableLocale } from './TableLocale.js';\nimport { readTableColumnWidthsPx, withTableColumnWidthsPx } from './TableSizing.js';\n\n// --- Shared Transaction Builders ---\n\n/** Builds a transaction that inserts a new row at the given index. */\nexport function buildInsertRowTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validInsertionIndex(rowIndex, grid.rowCount) || grid.columnCount === 0) return null;\n\n\tconst crossingCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.rowStart < rowIndex && entry.rowEnd > rowIndex,\n\t);\n\tconst tr = state.transaction('command');\n\tfor (const entry of crossingCells) {\n\t\tif (!setCellSpan(state, tr, entry.cell, 'rowspan', entry.rowSpan + 1)) return null;\n\t}\n\n\tconst cells: BlockNode[] = [];\n\tfor (let column = 0; column < grid.columnCount; column++) {\n\t\tconst covered: boolean = crossingCells.some(\n\t\t\t(entry: TableGridCell) => entry.columnStart <= column && entry.columnEnd > column,\n\t\t);\n\t\tif (!covered) cells.push(createTableCell());\n\t}\n\tconst newRow: BlockNode = createBlockNode(nodeType('table_row'), cells);\n\tconst rawRowIndex: number = rawInsertionIndex(table, grid, rowIndex);\n\ttr.insertNode([tableId], rawRowIndex, newRow);\n\n\tconst firstCell = getBlockChildren(newRow)[0];\n\tconst firstLeaf = firstCell ? getBlockChildren(firstCell)[0] : undefined;\n\tif (firstLeaf) {\n\t\ttr.setSelection(createCollapsedSelection(firstLeaf.id, 0));\n\t} else if (firstCell) {\n\t\ttr.setSelection(createCollapsedSelection(firstCell.id, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/** Builds a transaction that inserts a new column at the given index. */\nexport function buildInsertColumnTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tcolIndex: number,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validInsertionIndex(colIndex, grid.columnCount) || grid.rowCount === 0) return null;\n\n\tconst tr = state.transaction('command');\n\tif (hasColumnWidthMetadata(table)) {\n\t\tconst widths: (number | null)[] = [...readTableColumnWidthsPx(table, grid.columnCount)];\n\t\twidths.splice(colIndex, 0, null);\n\t\tconst path = state.getNodePath(tableId);\n\t\tif (!path) return null;\n\t\ttr.setNodeAttr(path, withTableColumnWidthsPx(table.attrs, widths));\n\t}\n\n\tconst crossingCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.columnStart < colIndex && entry.columnEnd > colIndex,\n\t);\n\tfor (const entry of crossingCells) {\n\t\tif (!setCellSpan(state, tr, entry.cell, 'colspan', entry.columnSpan + 1)) return null;\n\t}\n\n\tlet firstInsertedCell: BlockNode | undefined;\n\tfor (let rowIndex = 0; rowIndex < grid.rowCount; rowIndex++) {\n\t\tconst row: BlockNode | undefined = grid.rows[rowIndex];\n\t\tif (!row) continue;\n\t\tconst coveredByCrossingCell: boolean = crossingCells.some(\n\t\t\t(entry: TableGridCell) => entry.rowStart <= rowIndex && entry.rowEnd > rowIndex,\n\t\t);\n\t\tif (coveredByCrossingCell) continue;\n\n\t\tconst newCell = createTableCell();\n\t\tfirstInsertedCell ??= newCell;\n\t\tconst insertionIndex: number = grid.cells.filter(\n\t\t\t(entry: TableGridCell) => entry.sourceRow.id === row.id && entry.columnEnd <= colIndex,\n\t\t).length;\n\t\ttr.insertNode([tableId, row.id], insertionIndex, newCell);\n\t}\n\n\tconst firstLeaf: BlockNode | undefined = firstInsertedCell\n\t\t? getBlockChildren(firstInsertedCell)[0]\n\t\t: undefined;\n\ttr.setSelection(firstLeaf ? createCollapsedSelection(firstLeaf.id, 0) : state.selection);\n\treturn tr.build();\n}\n\n/**\n * Builds a transaction that deletes the row at the given index.\n * If it's the last row, delegates to `createDeleteTableTransaction`.\n * @param preferredCol Column index to place the cursor in after deletion (default 0).\n */\nexport function buildDeleteRowTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\trowIndex: number,\n\tpreferredCol = 0,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validExistingIndex(rowIndex, grid.rowCount)) return null;\n\tif (grid.rowCount <= 1) {\n\t\treturn createDeleteTableTransaction(state, tableId);\n\t}\n\n\tconst tr = state.transaction('command');\n\tconst coveringCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.rowStart <= rowIndex && entry.rowEnd > rowIndex,\n\t);\n\tconst movedCells: readonly TableGridCell[] = coveringCells\n\t\t.filter((entry: TableGridCell) => entry.rowStart === rowIndex && entry.rowSpan > 1)\n\t\t.sort((a: TableGridCell, b: TableGridCell) => a.columnStart - b.columnStart);\n\n\tfor (const entry of coveringCells) {\n\t\tif (entry.rowSpan <= 1) continue;\n\t\tif (!setCellSpan(state, tr, entry.cell, 'rowspan', entry.rowSpan - 1)) return null;\n\t}\n\n\tif (movedCells.length > 0) {\n\t\tconst targetRow: BlockNode | undefined = grid.rows[rowIndex + 1];\n\t\tif (!targetRow) return null;\n\t\tconst targetOwnedCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t\t(entry: TableGridCell) => entry.sourceRow.id === targetRow.id,\n\t\t);\n\t\tlet moved = 0;\n\t\tfor (const entry of movedCells) {\n\t\t\tconst insertionIndex: number =\n\t\t\t\ttargetOwnedCells.filter((target: TableGridCell) => target.columnStart < entry.columnStart)\n\t\t\t\t\t.length + moved;\n\t\t\ttr.moveNode(\n\t\t\t\t[tableId, entry.sourceRow.id],\n\t\t\t\tentry.sourceCellIndex - moved,\n\t\t\t\t[tableId, targetRow.id],\n\t\t\t\tinsertionIndex,\n\t\t\t);\n\t\t\tmoved++;\n\t\t}\n\t}\n\n\tconst rawRowIndex: number = rawExistingRowIndex(table, grid, rowIndex);\n\tif (rawRowIndex < 0) return null;\n\ttr.removeNode([tableId], rawRowIndex);\n\n\tconst selectionRow: number = rowIndex > 0 ? rowIndex - 1 : 1;\n\tconst safeColumn: number = Math.min(Math.max(0, preferredCol), grid.columnCount - 1);\n\tconst cellId: BlockId | null = getCellAt(state, tableId, selectionRow, safeColumn);\n\tif (cellId) {\n\t\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\t\ttr.setSelection(createCollapsedSelection(leafId, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/**\n * Builds a transaction that deletes the column at the given index.\n * If it's the last column, delegates to `createDeleteTableTransaction`.\n * @param preferredRow Row index to place the cursor in after deletion (default 0).\n */\nexport function buildDeleteColumnTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n\tcolIndex: number,\n\tpreferredRow = 0,\n): Transaction | null {\n\tconst table = state.getBlock(tableId);\n\tif (!table || table.type !== 'table') return null;\n\tconst grid: TableGrid = createTableGrid(table);\n\tif (!validExistingIndex(colIndex, grid.columnCount)) return null;\n\tif (grid.columnCount <= 1) {\n\t\treturn createDeleteTableTransaction(state, tableId);\n\t}\n\n\tconst tr = state.transaction('command');\n\tif (hasColumnWidthMetadata(table)) {\n\t\tconst widths: (number | null)[] = [...readTableColumnWidthsPx(table, grid.columnCount)];\n\t\twidths.splice(colIndex, 1);\n\t\tconst path = state.getNodePath(tableId);\n\t\tif (!path) return null;\n\t\ttr.setNodeAttr(path, withTableColumnWidthsPx(table.attrs, widths));\n\t}\n\n\tconst coveringCells: readonly TableGridCell[] = grid.cells.filter(\n\t\t(entry: TableGridCell) => entry.columnStart <= colIndex && entry.columnEnd > colIndex,\n\t);\n\tfor (const entry of coveringCells) {\n\t\tif (entry.columnSpan <= 1) continue;\n\t\tif (!setCellSpan(state, tr, entry.cell, 'colspan', entry.columnSpan - 1)) return null;\n\t}\n\tfor (const entry of coveringCells\n\t\t.filter((cell: TableGridCell) => cell.columnSpan === 1)\n\t\t.sort((a: TableGridCell, b: TableGridCell) => {\n\t\t\tif (a.sourceRowIndex !== b.sourceRowIndex) return b.sourceRowIndex - a.sourceRowIndex;\n\t\t\treturn b.sourceCellIndex - a.sourceCellIndex;\n\t\t})) {\n\t\ttr.removeNode([tableId, entry.sourceRow.id], entry.sourceCellIndex);\n\t}\n\n\tconst targetCol: number = colIndex > 0 ? colIndex - 1 : 1;\n\tconst safeRow: number = Math.min(Math.max(0, preferredRow), grid.rowCount - 1);\n\tconst cellId: BlockId | null = getCellAt(state, tableId, safeRow, targetCol);\n\tif (cellId) {\n\t\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\t\ttr.setSelection(createCollapsedSelection(leafId, 0));\n\t}\n\n\treturn tr.build();\n}\n\n/**\n * Creates a transaction that removes the given root-level table node.\n * Cursor placement prefers the next root block, then previous.\n * If the table is the only root block, it is replaced with an empty paragraph.\n */\nexport function createDeleteTableTransaction(\n\tstate: EditorState,\n\ttableId: BlockId,\n): Transaction | null {\n\tconst tableIndex: number = state.doc.children.findIndex((block) => block.id === tableId);\n\tif (tableIndex === -1) return null;\n\n\tif (state.doc.children.length === 1) {\n\t\tconst replacement = createEmptyParagraph(generateBlockId());\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.insertNode([], 0, replacement)\n\t\t\t.removeNode([], 1)\n\t\t\t.setSelection(createCollapsedSelection(replacement.id, 0))\n\t\t\t.build();\n\t}\n\n\tconst tr = state.transaction('command').removeNode([], tableIndex);\n\n\tconst nextRoot = state.doc.children[tableIndex + 1];\n\tif (nextRoot) {\n\t\tconst selection = createSelectionForBlockBoundary(state, nextRoot.id, 'start');\n\t\tif (selection) {\n\t\t\ttr.setSelection(selection);\n\t\t\treturn tr.build();\n\t\t}\n\t}\n\n\tconst prevRoot = state.doc.children[tableIndex - 1];\n\tif (prevRoot) {\n\t\tconst selection = createSelectionForBlockBoundary(state, prevRoot.id, 'end');\n\t\tif (selection) {\n\t\t\ttr.setSelection(selection);\n\t\t\treturn tr.build();\n\t\t}\n\t}\n\n\treturn null;\n}\n\n// --- Commands ---\n\ninterface TableDeletionTarget {\n\treadonly tableId: BlockId;\n}\n\n/**\n * Inserts a table with the given dimensions at the current cursor position.\n * Adds a paragraph after the table for cursor escape.\n * Moves cursor into the first cell.\n */\nexport function insertTable(context: PluginContext, rows: number, cols: number): boolean {\n\tconst state = context.getState();\n\tconst anchorBlockId = getSelectedBlockId(state);\n\tif (!anchorBlockId) return false;\n\n\tconst tableNode = createTable(rows, cols);\n\tconst builder = state.transaction('command');\n\tconst trailing = insertBlockObjectOnOwnLine(state, builder, anchorBlockId, tableNode);\n\tif (!trailing) return false;\n\n\t// Set cursor in first paragraph inside first cell\n\tconst firstRow = getBlockChildren(tableNode)[0];\n\tconst firstCell = firstRow ? getBlockChildren(firstRow)[0] : undefined;\n\tconst firstParagraph = firstCell ? getBlockChildren(firstCell)[0] : undefined;\n\n\tif (firstParagraph) {\n\t\tbuilder.setSelection(createCollapsedSelection(firstParagraph.id, 0));\n\t} else if (firstCell) {\n\t\tbuilder.setSelection(createCollapsedSelection(firstCell.id, 0));\n\t}\n\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\n/** Adds a row above the current row. */\nexport function addRowAbove(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildInsertRowTransaction(state, tableCtx.tableId, tableCtx.rowIndex);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowInsertedAbove);\n\treturn true;\n}\n\n/** Adds a row below the current row. */\nexport function addRowBelow(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildInsertRowTransaction(state, tableCtx.tableId, tableCtx.rowEnd);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowInsertedBelow);\n\treturn true;\n}\n\n/** Adds a column to the left of the current column. */\nexport function addColumnLeft(context: PluginContext, locale: TableLocale): boolean {\n\treturn addColumn(context, 'left', locale);\n}\n\n/** Adds a column to the right of the current column. */\nexport function addColumnRight(context: PluginContext, locale: TableLocale): boolean {\n\treturn addColumn(context, 'right', locale);\n}\n\nfunction addColumn(context: PluginContext, side: 'left' | 'right', locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst insertColIndex: number = side === 'left' ? tableCtx.colIndex : tableCtx.colEnd;\n\tconst tr = buildInsertColumnTransaction(state, tableCtx.tableId, insertColIndex);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceColumnInserted(side));\n\treturn true;\n}\n\n// --- Logical-grid structural helpers ---\n\nfunction validInsertionIndex(index: number, length: number): boolean {\n\treturn Number.isInteger(index) && index >= 0 && index <= length;\n}\n\nfunction validExistingIndex(index: number, length: number): boolean {\n\treturn Number.isInteger(index) && index >= 0 && index < length;\n}\n\nfunction rawInsertionIndex(table: BlockNode, grid: TableGrid, logicalRow: number): number {\n\tif (logicalRow >= grid.rowCount) return table.children.length;\n\tconst row: BlockNode | undefined = grid.rows[logicalRow];\n\tif (!row) return table.children.length;\n\tconst index: number = table.children.findIndex((child) => 'id' in child && child.id === row.id);\n\treturn index === -1 ? table.children.length : index;\n}\n\nfunction rawExistingRowIndex(table: BlockNode, grid: TableGrid, logicalRow: number): number {\n\tconst row: BlockNode | undefined = grid.rows[logicalRow];\n\tif (!row) return -1;\n\treturn table.children.findIndex((child) => 'id' in child && child.id === row.id);\n}\n\nfunction setCellSpan(\n\tstate: EditorState,\n\tbuilder: ReturnType<EditorState['transaction']>,\n\tcell: BlockNode,\n\tkey: 'colspan' | 'rowspan',\n\tvalue: number,\n): boolean {\n\tconst path: readonly BlockId[] | undefined = state.getNodePath(cell.id);\n\tif (!path) return false;\n\tbuilder.setNodeAttr(path, withCellSpan(cell, key, value).attrs ?? {});\n\treturn true;\n}\n\nfunction withCellSpan(cell: BlockNode, key: 'colspan' | 'rowspan', value: number): BlockNode {\n\tconst attrs: Record<string, BlockAttrValue> = { ...(cell.attrs ?? {}) };\n\tdelete attrs[key];\n\tif (value > 1) attrs[key] = value;\n\treturn { ...cell, attrs };\n}\n\nfunction hasColumnWidthMetadata(table: BlockNode): boolean {\n\treturn Object.prototype.hasOwnProperty.call(table.attrs ?? {}, 'columnWidthsPx');\n}\n\n/** Deletes the current row. If it's the last row, deletes the entire table. */\nexport function deleteRow(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildDeleteRowTransaction(\n\t\tstate,\n\t\ttableCtx.tableId,\n\t\ttableCtx.rowIndex,\n\t\ttableCtx.colIndex,\n\t);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceRowDeleted);\n\treturn true;\n}\n\n/** Deletes the current column. If it's the last column, deletes the entire table. */\nexport function deleteColumn(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst tr = buildDeleteColumnTransaction(\n\t\tstate,\n\t\ttableCtx.tableId,\n\t\ttableCtx.colIndex,\n\t\ttableCtx.rowIndex,\n\t);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceColumnDeleted);\n\treturn true;\n}\n\n/** Deletes the entire table and moves cursor to surrounding block. */\nexport function deleteTable(context: PluginContext, locale: TableLocale): boolean {\n\tconst state = context.getState();\n\tconst target = resolveTableDeletionTarget(state);\n\tif (!target) return false;\n\n\tconst tr = createDeleteTableTransaction(state, target.tableId);\n\tif (!tr) return false;\n\tcontext.dispatch(tr);\n\tcontext.announce(locale.announceTableDeleted);\n\treturn true;\n}\n\n/** Selects the surrounding table as a node object. */\nexport function selectTable(context: PluginContext): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel)) {\n\t\tconst selectedNode = state.getBlock(sel.nodeId);\n\t\treturn selectedNode?.type === 'table';\n\t}\n\tif (isGapCursor(sel)) return false;\n\n\tconst tableCtx: TableContext | null = findTableContext(state, sel.anchor.blockId);\n\tif (!tableCtx) return false;\n\n\tconst path = state.getNodePath(tableCtx.tableId);\n\tif (!path) return false;\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setSelection(createNodeSelection(tableCtx.tableId, path))\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Registers all table commands on the given plugin context. */\nexport function registerTableCommands(context: PluginContext, locale: TableLocale): void {\n\tcontext.registerCommand('insertTable', () => insertTable(context, 3, 3));\n\tcontext.registerCommand('addRowAbove', () => addRowAbove(context, locale));\n\tcontext.registerCommand('addRowBelow', () => addRowBelow(context, locale));\n\tcontext.registerCommand('addColumnLeft', () => addColumnLeft(context, locale));\n\tcontext.registerCommand('addColumnRight', () => addColumnRight(context, locale));\n\tcontext.registerCommand('deleteRow', () => deleteRow(context, locale));\n\tcontext.registerCommand('deleteColumn', () => deleteColumn(context, locale));\n\tcontext.registerCommand('selectTable', () => selectTable(context));\n\tcontext.registerCommand('deleteTable', () => deleteTable(context, locale));\n}\n\nfunction resolveTableDeletionTarget(\n\tstate: ReturnType<PluginContext['getState']>,\n): TableDeletionTarget | null {\n\tif (isNodeSelection(state.selection)) {\n\t\tconst selectedNode = state.getBlock(state.selection.nodeId);\n\t\tif (selectedNode?.type !== 'table') return null;\n\t\treturn { tableId: selectedNode.id };\n\t}\n\tif (isGapCursor(state.selection)) return null;\n\n\tconst tableCtx: TableContext | null = findTableContext(state, state.selection.anchor.blockId);\n\tif (!tableCtx) return null;\n\n\treturn { tableId: tableCtx.tableId };\n}\n","/** Accessible precise-size editor shared by the table context and actions menus. */\n\nimport type { PluginContext } from '../Plugin.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport {\n\ttype TableDimensionState,\n\ttype TableSizeInput,\n\ttype TableSizeState,\n\ttype TableSizeTarget,\n\ttype TableSizingConfig,\n\ttype TableSizingService,\n\tTableSizingServiceKey,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\nexport interface TableSizeEditorOptions {\n\treadonly target?: TableSizeTarget;\n\treadonly config?: Partial<TableSizingConfig>;\n\treadonly onClose: () => void;\n}\n\ninterface DimensionField {\n\treadonly input: HTMLInputElement;\n\treadonly error: HTMLDivElement;\n\treadonly minimum: number;\n\treadonly maximum: number;\n\tdirty: boolean;\n}\n\n/** Renders the precise table sizing UI into an existing popup container. */\nexport function renderTableSizeEditor(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\toptions: TableSizeEditorOptions,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n): void {\n\tconst service: TableSizingService | undefined = context.getService?.(TableSizingServiceKey);\n\tconst config: TableSizingConfig = resolveTableSizingConfig(options.config ?? {});\n\tconst state: TableSizeState | null = service ? readSize(service, options.target) : null;\n\n\tcontainer.classList.add('notectl-table-size-editor');\n\tcontainer.setAttribute('role', 'dialog');\n\tcontainer.setAttribute('aria-modal', 'true');\n\tcontainer.setAttribute('aria-label', locale.sizeDialogLabel);\n\n\tconst title: HTMLDivElement = document.createElement('div');\n\ttitle.className = 'notectl-table-size-editor__title';\n\ttitle.textContent = locale.sizeDialogLabel;\n\tcontainer.appendChild(title);\n\n\tconst columnField: DimensionField = buildDimensionField(\n\t\tcontainer,\n\t\t'column-width',\n\t\tlocale.columnWidthLabel,\n\t\tstate?.columnWidthPx ?? 'unavailable',\n\t\tconfig.minColumnWidthPx,\n\t\tconfig.maxColumnWidthPx,\n\t\tlocale,\n\t);\n\tconst rowField: DimensionField = buildDimensionField(\n\t\tcontainer,\n\t\t'row-min-height',\n\t\tlocale.rowMinimumHeightLabel,\n\t\tstate?.rowMinHeightPx ?? 'unavailable',\n\t\tconfig.minRowHeightPx,\n\t\tconfig.maxRowHeightPx,\n\t\tlocale,\n\t);\n\n\tconst resetGroup: HTMLDivElement = document.createElement('div');\n\tresetGroup.className = 'notectl-table-size-editor__resets';\n\tconst resetColumn: HTMLButtonElement = buildButton(\n\t\tlocale.resetColumnWidth,\n\t\t'notectl-table-size-editor__reset',\n\t);\n\tconst resetRow: HTMLButtonElement = buildButton(\n\t\tlocale.resetRowMinimumHeight,\n\t\t'notectl-table-size-editor__reset',\n\t);\n\tconst resetAll: HTMLButtonElement = buildButton(\n\t\tlocale.resetAllSizes,\n\t\t'notectl-table-size-editor__reset notectl-table-size-editor__reset--all',\n\t);\n\tresetColumn.disabled = columnField.input.disabled;\n\tresetRow.disabled = rowField.input.disabled;\n\tresetAll.disabled = columnField.input.disabled && rowField.input.disabled;\n\tresetGroup.append(resetColumn, resetRow, resetAll);\n\tcontainer.appendChild(resetGroup);\n\n\tconst actions: HTMLDivElement = document.createElement('div');\n\tactions.className = 'notectl-table-size-editor__actions';\n\tconst cancel: HTMLButtonElement = buildButton(locale.cancel);\n\tconst apply: HTMLButtonElement = buildButton(locale.apply, 'notectl-table-size-editor__apply');\n\tapply.disabled = !service || !!context.isReadOnly?.();\n\tactions.append(cancel, apply);\n\tcontainer.appendChild(actions);\n\n\tcolumnField.input.addEventListener('input', () => {\n\t\tcolumnField.dirty = true;\n\t\tclearError(columnField);\n\t});\n\trowField.input.addEventListener('input', () => {\n\t\trowField.dirty = true;\n\t\tclearError(rowField);\n\t});\n\n\tapply.addEventListener('click', () => {\n\t\tif (!service || context.isReadOnly?.()) return;\n\t\tconst input: MutableTableSizeInput = {};\n\t\tlet valid = true;\n\t\tif (columnField.dirty) {\n\t\t\tconst value: number | null = parseDimension(columnField, locale);\n\t\t\tif (value === null) valid = false;\n\t\t\telse input.columnWidthPx = value;\n\t\t}\n\t\tif (rowField.dirty) {\n\t\t\tconst value: number | null = parseDimension(rowField, locale);\n\t\t\tif (value === null) valid = false;\n\t\t\telse input.rowMinHeightPx = value;\n\t\t}\n\t\tif (!valid) return;\n\t\tif (!columnField.dirty && !rowField.dirty) {\n\t\t\toptions.onClose();\n\t\t\treturn;\n\t\t}\n\t\tif (writeSize(service, options.target, input)) {\n\t\t\tannounceAppliedSize(context, options.target, input, locale);\n\t\t\toptions.onClose();\n\t\t}\n\t});\n\n\tcancel.addEventListener('click', options.onClose);\n\tresetColumn.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target, 'columnWidthPx')) return;\n\t\tsetFieldState(columnField, 'auto', locale);\n\t\tannounceReset(context, options.target, 'columnWidthPx', locale);\n\t});\n\tresetRow.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target, 'rowMinHeightPx')) return;\n\t\tsetFieldState(rowField, 'auto', locale);\n\t\tannounceReset(context, options.target, 'rowMinHeightPx', locale);\n\t});\n\tresetAll.addEventListener('click', () => {\n\t\tif (!service || !resetSize(service, options.target)) return;\n\t\tif (!columnField.input.disabled) setFieldState(columnField, 'auto', locale);\n\t\tif (!rowField.input.disabled) setFieldState(rowField, 'auto', locale);\n\t\tcontext.announce(locale.announceTableSizesReset);\n\t});\n\n\tcontainer.addEventListener('keydown', (event: KeyboardEvent) => {\n\t\tif (event.key === 'Escape') {\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\toptions.onClose();\n\t\t\treturn;\n\t\t}\n\t\tif (event.key === 'Tab') trapDialogFocus(container, event);\n\t});\n\n\trequestAnimationFrame(() => {\n\t\tconst firstInput: HTMLInputElement | undefined = [columnField.input, rowField.input].find(\n\t\t\t(input: HTMLInputElement): boolean => !input.disabled,\n\t\t);\n\t\t(firstInput ?? cancel).focus();\n\t});\n}\n\nfunction buildDimensionField(\n\tcontainer: HTMLElement,\n\tid: string,\n\tlabelText: string,\n\tstate: TableDimensionState,\n\tminimum: number,\n\tmaximum: number,\n\tlocale: TableLocale,\n): DimensionField {\n\tconst group: HTMLDivElement = document.createElement('div');\n\tgroup.className = 'notectl-table-size-editor__field';\n\tconst label: HTMLLabelElement = document.createElement('label');\n\tlabel.htmlFor = `notectl-table-size-${id}`;\n\tlabel.textContent = labelText;\n\tconst inputWrap: HTMLDivElement = document.createElement('div');\n\tinputWrap.className = 'notectl-table-size-editor__input-wrap';\n\tconst input: HTMLInputElement = document.createElement('input');\n\tinput.id = label.htmlFor;\n\tinput.type = 'number';\n\tinput.inputMode = 'decimal';\n\tinput.step = '1';\n\tinput.min = String(minimum);\n\tinput.max = String(maximum);\n\tinput.setAttribute('aria-describedby', `${input.id}-error`);\n\tconst unit: HTMLSpanElement = document.createElement('span');\n\tunit.className = 'notectl-table-size-editor__unit';\n\tunit.textContent = 'px';\n\tunit.setAttribute('aria-hidden', 'true');\n\tinputWrap.append(input, unit);\n\tconst error: HTMLDivElement = document.createElement('div');\n\terror.id = `${input.id}-error`;\n\terror.className = 'notectl-table-size-editor__error';\n\terror.setAttribute('role', 'alert');\n\tgroup.append(label, inputWrap, error);\n\tcontainer.appendChild(group);\n\n\tconst field: DimensionField = { input, error, minimum, maximum, dirty: false };\n\tsetFieldState(field, state, locale);\n\treturn field;\n}\n\nfunction setFieldState(\n\tfield: DimensionField,\n\tstate: TableDimensionState,\n\tlocale: TableLocale,\n): void {\n\tfield.input.disabled = state === 'unavailable';\n\tfield.input.value = typeof state === 'number' ? String(state) : '';\n\tfield.input.placeholder = state === 'mixed' ? locale.mixed : locale.automatic;\n\tfield.dirty = false;\n\tclearError(field);\n}\n\nfunction parseDimension(field: DimensionField, locale: TableLocale): number | null {\n\tconst raw: string = field.input.value.trim();\n\tconst value: number = Number(raw);\n\tif (raw.length === 0 || !Number.isFinite(value)) {\n\t\tfield.error.textContent = locale.invalidDimensionRange(field.minimum, field.maximum);\n\t\tfield.input.setAttribute('aria-invalid', 'true');\n\t\tfield.input.focus();\n\t\treturn null;\n\t}\n\tconst clamped: number = Math.min(field.maximum, Math.max(field.minimum, value));\n\tfield.input.value = String(clamped);\n\treturn clamped;\n}\n\nfunction clearError(field: DimensionField): void {\n\tfield.error.textContent = '';\n\tfield.input.removeAttribute('aria-invalid');\n}\n\nfunction buildButton(label: string, className = ''): HTMLButtonElement {\n\tconst button: HTMLButtonElement = document.createElement('button');\n\tbutton.type = 'button';\n\tbutton.textContent = label;\n\tbutton.className = className;\n\treturn button;\n}\n\n/** Keeps Tab focus inside the modal size dialog by wrapping at both ends. */\nfunction trapDialogFocus(container: HTMLElement, event: KeyboardEvent): void {\n\tconst focusable: readonly HTMLElement[] = getFocusableDialogElements(container);\n\tconst first: HTMLElement | undefined = focusable[0];\n\tconst last: HTMLElement | undefined = focusable[focusable.length - 1];\n\tif (!first || !last) return;\n\tconst active: Element | null = activeElementWithin(container);\n\tconst wrapToLast: boolean = event.shiftKey && (active === first || !container.contains(active));\n\tconst wrapToFirst: boolean = !event.shiftKey && (active === last || !container.contains(active));\n\tif (!wrapToLast && !wrapToFirst) return;\n\tevent.preventDefault();\n\t(wrapToLast ? last : first).focus();\n}\n\n/** Returns the dialog's enabled, focusable controls in DOM order. */\nfunction getFocusableDialogElements(container: HTMLElement): readonly HTMLElement[] {\n\treturn Array.from(\n\t\tcontainer.querySelectorAll<HTMLInputElement | HTMLButtonElement>('input, button'),\n\t).filter((element: HTMLInputElement | HTMLButtonElement): boolean => !element.disabled);\n}\n\n/** Resolves the active element across a shadow-root or document boundary. */\nfunction activeElementWithin(container: HTMLElement): Element | null {\n\tconst root: Node = container.getRootNode();\n\tif (root instanceof ShadowRoot || root instanceof Document) return root.activeElement;\n\treturn document.activeElement;\n}\n\nfunction readSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n): TableSizeState | null {\n\treturn target ? service.getSize(target) : service.getSelectionSize();\n}\n\nfunction writeSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n\tinput: TableSizeInput,\n): boolean {\n\treturn target ? service.setSize(target, input) : service.setSelectionSize(input);\n}\n\nfunction resetSize(\n\tservice: TableSizingService,\n\ttarget: TableSizeTarget | undefined,\n\tdimension?: 'columnWidthPx' | 'rowMinHeightPx',\n): boolean {\n\treturn target ? service.resetSize(target, dimension) : service.resetSelectionSize(dimension);\n}\n\nfunction announceAppliedSize(\n\tcontext: PluginContext,\n\ttarget: TableSizeTarget | undefined,\n\tinput: TableSizeInput,\n\tlocale: TableLocale,\n): void {\n\tconst messages: string[] = [];\n\tif (typeof input.columnWidthPx === 'number') {\n\t\tif (target?.kind === 'column' || target?.kind === 'cell') {\n\t\t\tmessages.push(locale.announceColumnWidthSet(target.column, input.columnWidthPx));\n\t\t} else {\n\t\t\tmessages.push(`${locale.columnWidthLabel}: ${String(input.columnWidthPx)}`);\n\t\t}\n\t}\n\tif (typeof input.rowMinHeightPx === 'number') {\n\t\tif (target?.kind === 'row' || target?.kind === 'cell') {\n\t\t\tmessages.push(locale.announceRowMinimumHeightSet(target.row, input.rowMinHeightPx));\n\t\t} else {\n\t\t\tmessages.push(`${locale.rowMinimumHeightLabel}: ${String(input.rowMinHeightPx)}`);\n\t\t}\n\t}\n\tif (messages.length > 0) context.announce(messages.join(' '));\n}\n\nfunction announceReset(\n\tcontext: PluginContext,\n\ttarget: TableSizeTarget | undefined,\n\tdimension: 'columnWidthPx' | 'rowMinHeightPx',\n\tlocale: TableLocale,\n): void {\n\tif (dimension === 'columnWidthPx') {\n\t\tif (target?.kind === 'column' || target?.kind === 'cell') {\n\t\t\tcontext.announce(locale.announceColumnWidthReset(target.column));\n\t\t} else {\n\t\t\tcontext.announce(locale.resetColumnWidth);\n\t\t}\n\t\treturn;\n\t}\n\tif (target?.kind === 'row' || target?.kind === 'cell') {\n\t\tcontext.announce(locale.announceRowMinimumHeightReset(target.row));\n\t} else {\n\t\tcontext.announce(locale.resetRowMinimumHeight);\n\t}\n}\n\ntype MutableTableSizeInput = {\n\t-readonly [Key in keyof TableSizeInput]?: TableSizeInput[Key];\n};\n","/**\n * Accessible context menu for table operations.\n * Triggered via right-click, Shift-F10 keyboard shortcut, or table actions button.\n * Provides fully keyboard-navigable access to all table actions.\n * Delegates lifecycle to PopupManager when available, falls back to manual DOM.\n * Prevents key events from escaping to the editor via stopPropagation.\n */\n\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { isRtlContext } from '../../platform/Platform.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { applyRovingTabindex } from '../shared/KeyboardNav.js';\nimport type { PopupHandle, PopupManager } from '../shared/PopupManager.js';\nimport { positionPopup } from '../shared/PopupPositioning.js';\nimport { renderBorderColorPicker } from './TableBorderColor.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { renderTableSizeEditor } from './TableSizeEditor.js';\nimport type { TableSizeTarget, TableSizingConfig } from './TableSizing.js';\n\n// --- Types ---\n\nexport interface TableContextMenuHandle {\n\tisOpen(): boolean;\n\tclose(): void;\n\tdestroy(): void;\n}\n\nexport interface TableContextMenuOptions {\n\t/** Explicit logical target used by cell-invoked menus; omitted means current table selection. */\n\treadonly sizeTarget?: TableSizeTarget;\n\treadonly sizingConfig?: Partial<TableSizingConfig>;\n\t/** Element that receives focus after the complete menu closes. */\n\treadonly restoreFocusTo?: HTMLElement | null;\n}\n\ninterface MenuEntry {\n\treadonly label: string;\n\treadonly command: string;\n\treadonly type: 'item';\n}\n\ninterface MenuSeparator {\n\treadonly type: 'separator';\n}\n\ninterface MenuSubmenu {\n\treadonly label: string;\n\treadonly type: 'submenu';\n\treadonly id: string;\n}\n\ntype MenuDefinition = MenuEntry | MenuSeparator | MenuSubmenu;\n\nconst openMenusByContext: WeakMap<PluginContext, Set<() => void>> = new WeakMap();\n\n/** Closes every table context/actions menu owned by one editor instance. */\nexport function closeTableContextMenus(context: PluginContext): void {\n\tfor (const close of [...(openMenusByContext.get(context) ?? [])]) close();\n}\n\n// --- Menu Structure ---\n\nfunction buildMenuItems(locale: TableLocale): readonly MenuDefinition[] {\n\treturn [\n\t\t{ type: 'item', label: locale.insertRowAbove, command: 'addRowAbove' },\n\t\t{ type: 'item', label: locale.insertRowBelow, command: 'addRowBelow' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.insertColumnLeft, command: 'addColumnLeft' },\n\t\t{ type: 'item', label: locale.insertColumnRight, command: 'addColumnRight' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.deleteRow, command: 'deleteRow' },\n\t\t{ type: 'item', label: locale.deleteColumn, command: 'deleteColumn' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'submenu', label: locale.borderColorLabel, id: 'borderColor' },\n\t\t{ type: 'submenu', label: locale.sizeLabel, id: 'size' },\n\t\t{ type: 'separator' },\n\t\t{ type: 'item', label: locale.deleteTable, command: 'deleteTable' },\n\t];\n}\n\n// --- Factory ---\n\n/**\n * Creates and displays an accessible context menu for table operations.\n * Attaches to the provided container (shadow root or editor container).\n */\nexport function createTableContextMenu(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\tanchorRect: DOMRect,\n\tonClosed?: () => void,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n\tpopupManager?: PopupManager,\n\toptions: TableContextMenuOptions = {},\n): TableContextMenuHandle {\n\tlet open = true;\n\tlet subPopup: HTMLDivElement | null = null;\n\tlet subHandle: PopupHandle | null = null;\n\n\t// --- Menu element ---\n\tconst menu: HTMLDivElement = document.createElement('div');\n\tmenu.className = 'notectl-table-context-menu';\n\tmenu.setAttribute('role', 'menu');\n\tmenu.setAttribute('aria-label', locale.tableActions);\n\tmenu.setAttribute('contenteditable', 'false');\n\tmenu.setAttribute('data-notectl-no-print', '');\n\n\t// --- Build menu items ---\n\tconst menuItems: HTMLButtonElement[] = [];\n\tlet focusedIndex = 0;\n\n\tconst menuDefs: readonly MenuDefinition[] = buildMenuItems(locale);\n\tfor (const def of menuDefs) {\n\t\tif (def.type === 'separator') {\n\t\t\tconst sep: HTMLDivElement = document.createElement('div');\n\t\t\tsep.setAttribute('role', 'separator');\n\t\t\tmenu.appendChild(sep);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst btn: HTMLButtonElement = document.createElement('button');\n\t\tbtn.type = 'button';\n\t\tbtn.setAttribute('role', 'menuitem');\n\t\tbtn.textContent = def.label;\n\n\t\tif (def.type === 'submenu') {\n\t\t\tbtn.dataset.submenu = def.id;\n\t\t\tbtn.setAttribute('aria-haspopup', 'true');\n\t\t\tbtn.setAttribute('aria-expanded', 'false');\n\t\t}\n\n\t\tif (def.type === 'item') {\n\t\t\tbtn.dataset.command = def.command;\n\t\t}\n\n\t\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t});\n\n\t\tbtn.addEventListener('click', (e: MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tif (def.type === 'item') {\n\t\t\t\tcontext.executeCommand(def.command);\n\t\t\t\tclose();\n\t\t\t} else if (def.type === 'submenu') {\n\t\t\t\topenSubmenu(btn);\n\t\t\t}\n\t\t});\n\n\t\tmenuItems.push(btn);\n\t\tmenu.appendChild(btn);\n\t}\n\n\t// --- Keyboard hint ---\n\tconst hint: HTMLDivElement = document.createElement('div');\n\thint.className = 'notectl-table-context-menu__hint';\n\thint.setAttribute('aria-hidden', 'true');\n\thint.textContent = locale.menuKeyboardHint;\n\tmenu.appendChild(hint);\n\n\tapplyRovingTabindex(menuItems, focusedIndex);\n\n\t// --- Keyboard navigation ---\n\tmenu.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\te.stopPropagation();\n\n\t\tswitch (e.key) {\n\t\t\tcase 'Escape':\n\t\t\t\te.preventDefault();\n\t\t\t\tclose();\n\t\t\t\treturn;\n\n\t\t\tcase 'ArrowDown': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = (focusedIndex + 1) % menuItems.length;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'ArrowUp': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = (focusedIndex - 1 + menuItems.length) % menuItems.length;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'ArrowRight':\n\t\t\tcase 'ArrowLeft': {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst menuDir: string = getComputedStyle(menu).direction;\n\t\t\t\tconst openKey: string = menuDir === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n\t\t\t\tif (e.key === openKey) {\n\t\t\t\t\tconst item = menuItems[focusedIndex];\n\t\t\t\t\tif (item?.dataset.submenu) {\n\t\t\t\t\t\topenSubmenu(item);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcloseSubmenu();\n\t\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'Enter':\n\t\t\tcase ' ': {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst item = menuItems[focusedIndex];\n\t\t\t\tif (!item) return;\n\t\t\t\tif (item.dataset.submenu) {\n\t\t\t\t\topenSubmenu(item);\n\t\t\t\t} else if (item.dataset.command) {\n\t\t\t\t\tcontext.executeCommand(item.dataset.command);\n\t\t\t\t\tclose();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'Home': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = 0;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase 'End': {\n\t\t\t\te.preventDefault();\n\t\t\t\tfocusedIndex = menuItems.length - 1;\n\t\t\t\tapplyRovingTabindex(menuItems, focusedIndex);\n\t\t\t\tmenuItems[focusedIndex]?.focus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t});\n\n\t// --- Submenu handling ---\n\tfunction openSubmenu(trigger: HTMLButtonElement): void {\n\t\tcloseSubmenu();\n\t\ttrigger.setAttribute('aria-expanded', 'true');\n\n\t\tif (popupManager) {\n\t\t\tconst menuHandle: PopupHandle = {\n\t\t\t\tclose: () => close(),\n\t\t\t\tgetElement: () => menu,\n\t\t\t};\n\n\t\t\tsubHandle = popupManager.open({\n\t\t\t\tanchor: trigger,\n\t\t\t\tposition: 'right',\n\t\t\t\treferenceNode: menu,\n\t\t\t\tparent: menuHandle,\n\t\t\t\tonClose: () => {\n\t\t\t\t\tsubHandle = null;\n\t\t\t\t\tsubPopup = null;\n\t\t\t\t\tresetSubmenuTriggers();\n\t\t\t\t},\n\t\t\t\tcontent: (popup: HTMLElement, closeSub: () => void) => {\n\t\t\t\t\tpopup.className = 'notectl-table-context-menu';\n\t\t\t\t\tpopup.setAttribute('contenteditable', 'false');\n\t\t\t\t\tpopup.setAttribute('data-notectl-no-print', '');\n\t\t\t\t\tsubPopup = popup as HTMLDivElement;\n\n\t\t\t\t\tpopup.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\tif (e.target instanceof HTMLInputElement) return;\n\t\t\t\t\t\tconst popupIsRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\t\t\t\tconst backKey: string = popupIsRtl ? 'ArrowRight' : 'ArrowLeft';\n\t\t\t\t\t\tif (e.key === backKey) {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\tcloseSub();\n\t\t\t\t\t\t\ttrigger.focus();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\trenderSubmenuContent(popup, trigger);\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tsubPopup = document.createElement('div');\n\t\t\tsubPopup.className = 'notectl-table-context-menu';\n\t\t\tsubPopup.setAttribute('contenteditable', 'false');\n\t\t\tsubPopup.setAttribute('data-notectl-no-print', '');\n\n\t\t\tsubPopup.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (e.target instanceof HTMLInputElement) return;\n\t\t\t\tconst menuIsRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\t\tconst closeKey: string = menuIsRtl ? 'ArrowRight' : 'ArrowLeft';\n\t\t\t\tif (e.key === closeKey) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tcloseSubmenu();\n\t\t\t\t\ttrigger.focus();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\trenderSubmenuContent(subPopup, trigger);\n\n\t\t\tcontainer.appendChild(subPopup);\n\n\t\t\tconst menuRect: DOMRect = menu.getBoundingClientRect();\n\t\t\tconst isRtl: boolean = getComputedStyle(menu).direction === 'rtl';\n\t\t\tpositionPopup(subPopup, menuRect, { position: 'right', isRtl });\n\n\t\t\tif (trigger.dataset.submenu !== 'size') {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tconst firstFocusable = subPopup?.querySelector('button') as HTMLElement | null;\n\t\t\t\t\tfirstFocusable?.focus();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction renderSubmenuContent(popup: HTMLElement, trigger: HTMLButtonElement): void {\n\t\tif (trigger.dataset.submenu === 'size') {\n\t\t\trenderTableSizeEditor(\n\t\t\t\tpopup,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\ttarget: options.sizeTarget,\n\t\t\t\t\tconfig: options.sizingConfig,\n\t\t\t\t\tonClose: close,\n\t\t\t\t},\n\t\t\t\tlocale,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trenderBorderColorPicker(popup, context, tableId, close, locale);\n\t}\n\n\tfunction closeSubmenu(): void {\n\t\tif (subHandle) {\n\t\t\tsubHandle.close();\n\t\t\tsubHandle = null;\n\t\t\tsubPopup = null;\n\t\t} else if (subPopup) {\n\t\t\tsubPopup.remove();\n\t\t\tsubPopup = null;\n\t\t}\n\t\tresetSubmenuTriggers();\n\t}\n\n\tfunction resetSubmenuTriggers(): void {\n\t\tfor (const item of menuItems) {\n\t\t\tif (item.dataset.submenu) {\n\t\t\t\titem.setAttribute('aria-expanded', 'false');\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Append before measuring: RTL/right-edge positioning needs the real\n\t// containing block and cannot be computed from a disconnected element.\n\tcontainer.appendChild(menu);\n\tpositionPopup(menu, anchorRect, {\n\t\tposition: 'below-start',\n\t\tisRtl: isRtlContext(container),\n\t});\n\n\t// Focus first item immediately and via rAF as backup\n\tmenuItems[0]?.focus();\n\trequestAnimationFrame(() => {\n\t\tif (open && menuItems[0]) {\n\t\t\tmenuItems[0].focus();\n\t\t}\n\t});\n\n\t// --- Click outside ---\n\tconst onClickOutside = (e: MouseEvent): void => {\n\t\tconst path: EventTarget[] = e.composedPath();\n\t\tif (path.includes(menu) || (subPopup && path.includes(subPopup))) {\n\t\t\treturn;\n\t\t}\n\t\tclose();\n\t};\n\tlet outsideClickTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet outsideListenerAttached = false;\n\toutsideClickTimer = setTimeout(() => {\n\t\toutsideClickTimer = null;\n\t\tdocument.addEventListener('mousedown', onClickOutside, true);\n\t\toutsideListenerAttached = true;\n\t}, 0);\n\n\t// --- Close function ---\n\tfunction close(): void {\n\t\tif (!open) return;\n\t\topen = false;\n\t\tcloseSubmenu();\n\t\tmenu.remove();\n\t\tif (outsideClickTimer !== null) {\n\t\t\tclearTimeout(outsideClickTimer);\n\t\t\toutsideClickTimer = null;\n\t\t}\n\t\tif (outsideListenerAttached) {\n\t\t\tdocument.removeEventListener('mousedown', onClickOutside, true);\n\t\t\toutsideListenerAttached = false;\n\t\t}\n\t\topenMenusByContext.get(context)?.delete(close);\n\t\tonClosed?.();\n\t\tconst restoreTarget: HTMLElement | null | undefined = options.restoreFocusTo;\n\t\tif (restoreTarget?.isConnected) {\n\t\t\trequestAnimationFrame(() => restoreTarget.focus({ preventScroll: true }));\n\t\t}\n\t}\n\n\tconst contextMenus: Set<() => void> = openMenusByContext.get(context) ?? new Set();\n\tcontextMenus.add(close);\n\topenMenusByContext.set(context, contextMenus);\n\n\treturn {\n\t\tisOpen: () => open,\n\t\tclose,\n\t\tdestroy: close,\n\t};\n}\n","/**\n * Keyboard navigation handlers for table cells.\n * Handles Tab, Shift-Tab, arrow keys, Enter, Backspace, Delete, and Escape.\n */\n\nimport { getBlockLength } from '../../model/Document.js';\nimport type { Keymap } from '../../model/Keymap.js';\nimport {\n\ttype Selection,\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisCollapsed,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getDeepActiveHTMLElement } from '../shared/PopupPositioning.js';\nimport { addRowBelow } from './TableCommands.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport { createTableGrid } from './TableGrid.js';\nimport {\n\ttype TableContext,\n\tfindTableContext,\n\tgetCellAt,\n\tgetFirstLeafInCell,\n\tgetLastLeafInCell,\n} from './TableHelpers.js';\nimport type { TableLocale } from './TableLocale.js';\nimport { TableSelectionServiceKey } from './TableSelection.js';\nimport type { TableSizeTarget, TableSizingConfig } from './TableSizing.js';\n\n/** Tracks one keyboard context menu per editor without coupling editor instances. */\nconst activeContextMenus: WeakMap<PluginContext, TableContextMenuHandle> = new WeakMap();\n\n/** Registers all table navigation keymaps. */\nexport function registerTableKeymaps(\n\tcontext: PluginContext,\n\tlocale: TableLocale,\n\tsizingConfig: Partial<TableSizingConfig> = {},\n): void {\n\tconst keymap: Keymap = {\n\t\tTab: () => handleTab(context, locale),\n\t\t'Shift-Tab': () => handleShiftTab(context),\n\t\tBackspace: () => handleBackspace(context),\n\t\tDelete: () => handleDelete(context),\n\t\tArrowDown: () => handleArrowDown(context),\n\t\tArrowUp: () => handleArrowUp(context),\n\t\tArrowRight: () => handleArrowRight(context),\n\t\tArrowLeft: () => handleArrowLeft(context),\n\t\tEscape: () => handleEscape(context),\n\t\t'Shift-F10': () => handleContextMenu(context, locale, sizingConfig),\n\t};\n\n\tcontext.registerKeymap(keymap, { priority: 'context' });\n}\n\n/**\n * Guards against NodeSelection and resolves TableContext.\n * Passes the narrowed text Selection to the handler, avoiding re-narrowing.\n * Returns false (not handled) if the cursor is not inside a table.\n */\nfunction withTableContext(\n\tcontext: PluginContext,\n\thandler: (state: EditorState, sel: Selection, tableCtx: TableContext) => boolean,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\tconst sel: Selection = state.selection;\n\tconst tableCtx: TableContext | null = findTableContext(state, sel.anchor.blockId);\n\tif (!tableCtx) return false;\n\treturn handler(state, sel, tableCtx);\n}\n\n/** Tab: move to next cell. At end of table, add a new row. */\nfunction handleTab(context: PluginContext, locale: TableLocale): boolean {\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\tif (moveToAdjacentSourceCell(context, tableCtx, 1, false)) return true;\n\n\t\treturn context.isReadOnly?.() ? false : addRowBelow(context, locale);\n\t});\n}\n\n/** Shift-Tab: move to previous cell. At start of table, stay put. */\nfunction handleShiftTab(context: PluginContext): boolean {\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, -1, false) || true;\n\t});\n}\n\n/** Backspace at deletion-boundary: select table node (first step of 2-step delete). */\nfunction handleBackspace(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\t\tif (sel.anchor.offset !== 0) return false;\n\n\t\tconst isAtDeletionBoundary: boolean = isFirstSourceCell(state, tableCtx);\n\t\tif (!isAtDeletionBoundary) return false;\n\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\treturn selectTableNode(context, tableCtx.tableId);\n\t});\n}\n\n/** Delete at deletion-boundary: select table node (first step of 2-step delete). */\nfunction handleDelete(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\n\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\tif (!block) return false;\n\n\t\tconst blockLen: number = getBlockLength(block);\n\t\tif (sel.anchor.offset !== blockLen) return false;\n\n\t\tconst isAtDeletionBoundary: boolean = isLastSourceCell(state, tableCtx);\n\t\tif (!isAtDeletionBoundary) return false;\n\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\treturn selectTableNode(context, tableCtx.tableId);\n\t});\n}\n\n/** ArrowDown: move to same column in next row. */\nfunction handleArrowDown(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\tif (tableCtx.rowEnd >= tableCtx.totalRows) {\n\t\t\treturn handleEscape(context);\n\t\t}\n\n\t\treturn moveToCellAndSelect(context, tableCtx.tableId, tableCtx.rowEnd, tableCtx.colIndex);\n\t});\n}\n\n/** ArrowUp: move to same column in previous row. */\nfunction handleArrowUp(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\tif (tableCtx.rowIndex <= 0) return false;\n\n\t\treturn moveToCellAndSelect(context, tableCtx.tableId, tableCtx.rowIndex - 1, tableCtx.colIndex);\n\t});\n}\n\n/** ArrowRight at cell end: move to next cell. */\nfunction handleArrowRight(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\n\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\tif (!block) return false;\n\n\t\tconst blockLen: number = getBlockLength(block);\n\t\tif (sel.anchor.offset !== blockLen) return false;\n\n\t\tconst lastLeaf: BlockId = getLastLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== lastLeaf) return false;\n\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, 1, false) || true;\n\t});\n}\n\n/** ArrowLeft at cell start: move to previous cell. */\nfunction handleArrowLeft(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, sel, tableCtx) => {\n\t\tif (!isCollapsed(sel)) return false;\n\t\tif (sel.anchor.offset !== 0) return false;\n\n\t\tconst firstLeaf: BlockId = getFirstLeafInCell(state, tableCtx.cellId);\n\t\tif (sel.anchor.blockId !== firstLeaf) return false;\n\n\t\treturn moveToAdjacentSourceCell(context, tableCtx, -1, true) || true;\n\t});\n}\n\n/** Escape: move cursor to the paragraph after the table. */\nfunction handleEscape(context: PluginContext): boolean {\n\treturn withTableContext(context, (state, _sel, tableCtx) => {\n\t\tconst nextIndex: number = tableCtx.tableIndex + 1;\n\t\tif (nextIndex >= state.doc.children.length) return false;\n\n\t\tconst nextBlock = state.doc.children[nextIndex];\n\t\tif (!nextBlock) return false;\n\n\t\tconst tr = state\n\t\t\t.transaction('command')\n\t\t\t.setSelection(createCollapsedSelection(nextBlock.id, 0))\n\t\t\t.build();\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t});\n}\n\n/** Shift-F10: open context menu at current cell. */\nfunction handleContextMenu(\n\tcontext: PluginContext,\n\tlocale: TableLocale,\n\tsizingConfig: Partial<TableSizingConfig>,\n): boolean {\n\tif (context.isReadOnly?.()) return false;\n\treturn withTableContext(context, (_state, _sel, tableCtx) => {\n\t\tconst container: HTMLElement = context.getContainer();\n\n\t\t// Find the cell element in the DOM\n\t\tconst cellEl = container.querySelector(\n\t\t\t`[data-block-id=\"${tableCtx.cellId}\"]`,\n\t\t) as HTMLElement | null;\n\t\tif (!cellEl) return false;\n\n\t\tconst cellRect: DOMRect = cellEl.getBoundingClientRect();\n\t\tconst anchorRect: DOMRect = new DOMRect(\n\t\t\tcellRect.left + cellRect.width / 2,\n\t\t\tcellRect.bottom,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\n\t\t// Find the table container to append the menu\n\t\tconst tableContainer = container.querySelector(\n\t\t\t`[data-block-id=\"${tableCtx.tableId}\"].ntbl-container`,\n\t\t) as HTMLElement | null;\n\n\t\tconst menuContainer: HTMLElement = tableContainer ?? container;\n\n\t\tactiveContextMenus.get(context)?.close();\n\t\tconst restoreFocusTo: HTMLElement | null = getDeepActiveHTMLElement(container) ?? cellEl;\n\t\tconst activeContextMenu: TableContextMenuHandle = createTableContextMenu(\n\t\t\tmenuContainer,\n\t\t\tcontext,\n\t\t\ttableCtx.tableId,\n\t\t\tanchorRect,\n\t\t\t() => {\n\t\t\t\tactiveContextMenus.delete(context);\n\t\t\t},\n\t\t\tlocale,\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\tsizeTarget: resolveTableMenuSizeTarget(context, tableCtx),\n\t\t\t\tsizingConfig,\n\t\t\t\trestoreFocusTo,\n\t\t\t},\n\t\t);\n\t\tactiveContextMenus.set(context, activeContextMenu);\n\n\t\treturn true;\n\t});\n}\n\n/** Uses an active logical table range ahead of the caret cell for menu sizing. */\nexport function resolveTableMenuSizeTarget(\n\tcontext: PluginContext,\n\ttableContext: TableContext,\n): TableSizeTarget | undefined {\n\tconst selectedRange = context.getService?.(TableSelectionServiceKey)?.getSelectedRange();\n\tif (selectedRange?.tableId === tableContext.tableId) return undefined;\n\treturn {\n\t\tkind: 'cell',\n\t\ttableId: tableContext.tableId,\n\t\trow: tableContext.rowIndex,\n\t\tcolumn: tableContext.colIndex,\n\t};\n}\n\nfunction moveToAdjacentSourceCell(\n\tcontext: PluginContext,\n\ttableContext: TableContext,\n\tdelta: -1 | 1,\n\tatEnd: boolean,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst table = state.getBlock(tableContext.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst cells = createTableGrid(table).cells;\n\tconst currentIndex: number = cells.findIndex((entry) => entry.cell.id === tableContext.cellId);\n\tconst target = cells[currentIndex + delta];\n\tif (!target) return false;\n\treturn atEnd\n\t\t? moveToCellAtEnd(context, tableContext.tableId, target.rowStart, target.columnStart)\n\t\t: moveToCellAndSelect(context, tableContext.tableId, target.rowStart, target.columnStart);\n}\n\nfunction isFirstSourceCell(state: EditorState, tableContext: TableContext): boolean {\n\tconst table = state.getBlock(tableContext.tableId);\n\treturn (\n\t\ttable?.type === 'table' && createTableGrid(table).cells[0]?.cell.id === tableContext.cellId\n\t);\n}\n\nfunction isLastSourceCell(state: EditorState, tableContext: TableContext): boolean {\n\tconst table = state.getBlock(tableContext.tableId);\n\tif (!table || table.type !== 'table') return false;\n\tconst cells = createTableGrid(table).cells;\n\treturn cells[cells.length - 1]?.cell.id === tableContext.cellId;\n}\n\n// --- Cell navigation helpers ---\n\n/** Moves cursor to the start of a cell (first leaf block inside). */\nfunction moveToCellAndSelect(\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst cellId: BlockId | null = getCellAt(state, tableId, rowIndex, colIndex);\n\tif (!cellId) return false;\n\n\tconst leafId: BlockId = getFirstLeafInCell(state, cellId);\n\tconst tr = state.transaction('command').setSelection(createCollapsedSelection(leafId, 0)).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Moves cursor to the end of a cell (last leaf block inside, for ArrowLeft). */\nfunction moveToCellAtEnd(\n\tcontext: PluginContext,\n\ttableId: BlockId,\n\trowIndex: number,\n\tcolIndex: number,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst cellId: BlockId | null = getCellAt(state, tableId, rowIndex, colIndex);\n\tif (!cellId) return false;\n\n\tconst leafId: BlockId = getLastLeafInCell(state, cellId);\n\tconst leaf = state.getBlock(leafId);\n\tif (!leaf) return false;\n\n\tconst leafLen: number = getBlockLength(leaf);\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setSelection(createCollapsedSelection(leafId, leafLen))\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Switches current selection to a table NodeSelection. */\nfunction selectTableNode(context: PluginContext, tableId: BlockId): boolean {\n\tconst state: EditorState = context.getState();\n\tconst path = state.getNodePath(tableId);\n\tif (!path) return false;\n\n\tconst tr = state.transaction('input').setSelection(createNodeSelection(tableId, path)).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n","/**\n * Pure DOM factory functions for table controls.\n * No state logic — only element creation and CSS class assignment.\n */\n\nimport { setStyleProperties } from '../../style/StyleRuntime.js';\n\n// --- SVG Icons ---\n\nexport const PLUS_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 12 12\" fill=\"none\">' +\n\t'<path d=\"M6 1v10M1 6h10\" stroke=\"currentColor\" ' +\n\t'stroke-width=\"1.8\" stroke-linecap=\"round\"/></svg>';\n\nexport const DELETE_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" ' +\n\t'viewBox=\"0 0 10 10\" fill=\"none\">' +\n\t'<path d=\"M2 2l6 6M8 2l-6 6\" stroke=\"currentColor\" ' +\n\t'stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>';\n\nexport const TABLE_DELETE_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"none\">' +\n\t'<path d=\"M9 3h6m-9 4h12M10 11v6m4-6v6m-9 3h14l-1-13H6L5 20z\" ' +\n\t'stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" ' +\n\t'stroke-linejoin=\"round\"/></svg>';\n\n// --- DOM Factories ---\n\n/** Creates a button with standard table-control setup (mousedown prevention, ARIA). */\nexport function createButton(\n\tclassName: string,\n\tinnerHTML: string,\n\ttitle: string,\n): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = document.createElement('button');\n\tbtn.className = className;\n\tbtn.innerHTML = innerHTML;\n\tbtn.title = title;\n\tbtn.type = 'button';\n\tbtn.setAttribute('aria-label', title);\n\tbtn.setAttribute('contenteditable', 'false');\n\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t});\n\treturn btn;\n}\n\n/** Builds the insert-line element (horizontal or vertical) with a plus-icon button. */\nexport function buildInsertLine(\n\torientation: 'horizontal' | 'vertical',\n\tlabel: string,\n): HTMLDivElement {\n\tconst line: HTMLDivElement = document.createElement('div');\n\tline.className = `ntbl-insert-line ntbl-insert-line--${orientation}`;\n\tline.setAttribute('contenteditable', 'false');\n\tline.setAttribute('data-notectl-no-print', '');\n\n\tconst btn: HTMLButtonElement = createButton('ntbl-insert-btn', PLUS_SVG, label);\n\tline.appendChild(btn);\n\n\treturn line;\n}\n\n/** Builds a zone-button for adding rows or columns at the table edge. */\nexport function buildAddButton(className: string, title: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = document.createElement('button');\n\tbtn.className = `ntbl-add-zone ${className}`;\n\tbtn.type = 'button';\n\tbtn.setAttribute('contenteditable', 'false');\n\tbtn.setAttribute('aria-label', title);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\tbtn.title = title;\n\n\tconst icon: HTMLSpanElement = document.createElement('span');\n\ticon.className = 'ntbl-add-icon';\n\ticon.innerHTML = PLUS_SVG;\n\tbtn.appendChild(icon);\n\n\tbtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t});\n\n\treturn btn;\n}\n\n/** Builds a handle-bar container (for column or row handles). */\nexport function buildHandleBar(className: string): HTMLDivElement {\n\tconst bar: HTMLDivElement = document.createElement('div');\n\tbar.className = className;\n\tbar.setAttribute('contenteditable', 'false');\n\tbar.setAttribute('data-notectl-no-print', '');\n\treturn bar;\n}\n\n/** Builds a keyboard-focusable logical column/row resize separator. */\nexport function buildResizeSeparator(\n\tkind: 'column' | 'row',\n\tindex: number,\n\tlabel: string,\n\tkeyboardHint: string,\n): HTMLButtonElement {\n\tconst separator: HTMLButtonElement = document.createElement('button');\n\tseparator.type = 'button';\n\tseparator.className = `ntbl-resize-separator ntbl-resize-separator--${kind}`;\n\tseparator.dataset.kind = kind;\n\tseparator.dataset.index = String(index);\n\tseparator.setAttribute('role', 'separator');\n\tseparator.setAttribute('aria-orientation', kind === 'column' ? 'vertical' : 'horizontal');\n\tseparator.setAttribute('aria-label', label);\n\tseparator.setAttribute('contenteditable', 'false');\n\tseparator.setAttribute('data-notectl-no-print', '');\n\tseparator.title = `${label}. ${keyboardHint}`;\n\treturn separator;\n}\n\n/** Builds the live pointer-resize dimension indicator. */\nexport function buildResizeIndicator(): HTMLDivElement {\n\tconst indicator: HTMLDivElement = document.createElement('div');\n\tindicator.className = 'ntbl-resize-indicator';\n\tindicator.setAttribute('aria-hidden', 'true');\n\tindicator.setAttribute('contenteditable', 'false');\n\tindicator.setAttribute('data-notectl-no-print', '');\n\treturn indicator;\n}\n\n/** SVG icon for kebab menu (three vertical dots). */\nexport const ACTIONS_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"currentColor\">' +\n\t'<circle cx=\"12\" cy=\"5\" r=\"2\"/>' +\n\t'<circle cx=\"12\" cy=\"12\" r=\"2\"/>' +\n\t'<circle cx=\"12\" cy=\"19\" r=\"2\"/></svg>';\n\n/** Builds the table actions button (kebab menu icon). */\nexport function buildActionsButton(label: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = createButton('ntbl-actions-btn', ACTIONS_SVG, label);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\treturn btn;\n}\n\n/** Builds the context menu discovery hint shown on first focus. */\nexport function buildContextHint(label: string): HTMLDivElement {\n\tconst hint: HTMLDivElement = document.createElement('div');\n\thint.className = 'ntbl-context-hint';\n\thint.setAttribute('aria-hidden', 'true');\n\thint.setAttribute('contenteditable', 'false');\n\thint.setAttribute('data-notectl-no-print', '');\n\thint.textContent = label;\n\treturn hint;\n}\n\n/** SVG icon for border/grid. */\nexport const BORDER_COLOR_SVG: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" ' +\n\t'viewBox=\"0 0 24 24\" fill=\"none\">' +\n\t'<path d=\"M3 3h18v18H3V3zm0 6h18M3 15h18M9 3v18M15 3v18\" ' +\n\t'stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>';\n\n/** Builds the border color button with a color indicator swatch. */\nexport function buildBorderColorButton(label: string, currentColor?: string): HTMLButtonElement {\n\tconst btn: HTMLButtonElement = createButton('ntbl-border-color-btn', BORDER_COLOR_SVG, label);\n\tbtn.setAttribute('data-notectl-no-print', '');\n\n\tconst swatch: HTMLSpanElement = document.createElement('span');\n\tswatch.className = 'ntbl-border-color-swatch';\n\tupdateBorderColorSwatch(swatch, currentColor);\n\tbtn.appendChild(swatch);\n\n\treturn btn;\n}\n\n/** Updates the swatch indicator to reflect the current border color. */\nexport function updateBorderColorSwatch(swatch: HTMLElement, color?: string): void {\n\tif (color === 'none') {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: 'transparent',\n\t\t\tborder: '1px dashed rgba(128,128,128,0.4)',\n\t\t});\n\t} else if (color) {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: color,\n\t\t\tborder: '1px solid rgba(0,0,0,0.15)',\n\t\t});\n\t} else {\n\t\tsetStyleProperties(swatch, {\n\t\t\tbackgroundColor: 'var(--notectl-border)',\n\t\t\tborder: '1px solid rgba(0,0,0,0.15)',\n\t\t});\n\t}\n}\n\nexport interface TableHandleSelectionOptions {\n\treadonly label: string;\n\treadonly onSelect: (idx: number) => void;\n\treadonly selected?: boolean;\n}\n\n/** Builds a single column or row handle with selection and delete buttons. */\nexport function buildHandle(\n\tclassName: string,\n\tindex: number,\n\tdeleteLabel: string,\n\tonDelete: (idx: number) => void,\n\tselection?: TableHandleSelectionOptions,\n): HTMLDivElement {\n\tconst handle: HTMLDivElement = document.createElement('div');\n\thandle.className = `ntbl-handle ${className}`;\n\thandle.dataset.index = String(index);\n\n\tif (selection) {\n\t\tconst selectBtn: HTMLButtonElement = createButton('ntbl-handle-select', '', selection.label);\n\t\tselectBtn.setAttribute('aria-pressed', selection.selected ? 'true' : 'false');\n\t\tselectBtn.addEventListener('click', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tselection.onSelect(index);\n\t\t});\n\t\thandle.appendChild(selectBtn);\n\t}\n\n\tconst deleteBtn: HTMLButtonElement = createButton('ntbl-handle-delete', DELETE_SVG, deleteLabel);\n\tdeleteBtn.addEventListener('click', (e: MouseEvent) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tonDelete(index);\n\t});\n\thandle.appendChild(deleteBtn);\n\n\treturn handle;\n}\n","/**\n * Measurement and positioning logic for table controls.\n * All functions are pure (take numeric/DOM inputs, return structured results).\n */\n\nimport { setStyleProperties, setStyleProperty } from '../../style/StyleRuntime.js';\n\n// --- Types ---\n\nexport interface BorderInfo {\n\treadonly position: number;\n\treadonly index: number;\n}\n\nexport interface NearestBorderResult {\n\treadonly type: 'row' | 'col' | 'none';\n\treadonly border: BorderInfo | null;\n}\n\nexport interface ColumnBox {\n\treadonly index: number;\n\treadonly position: number;\n\treadonly width: number;\n}\n\n// --- Constants ---\n\nexport const BORDER_THRESHOLD: number = 10;\n\n// --- Offset ---\n\n/** Returns the rendered table offset relative to the control container. */\nexport function getTableOffset(\n\ttableEl: HTMLElement,\n\tcontainer: HTMLElement,\n): { top: number; left: number } {\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst containerRect: DOMRect = container.getBoundingClientRect();\n\treturn {\n\t\ttop: tableRect.top - containerRect.top + container.scrollTop,\n\t\tleft: tableRect.left - containerRect.left + container.scrollLeft,\n\t};\n}\n\n// --- Border Measurement ---\n\n/** Measures the Y-positions of inter-row borders (between adjacent <tr> elements). */\nexport function measureRowBorders(tableEl: HTMLTableElement): BorderInfo[] {\n\tconst trs: NodeListOf<HTMLTableRowElement> = tableEl.querySelectorAll(':scope > tbody > tr');\n\tconst tableTop: number = tableEl.getBoundingClientRect().top;\n\tconst borders: BorderInfo[] = [];\n\n\tfor (let i = 1; i < trs.length; i++) {\n\t\tconst tr: HTMLTableRowElement | undefined = trs[i];\n\t\tif (!tr) continue;\n\t\tborders.push({\n\t\t\tposition: Math.round(tr.getBoundingClientRect().top - tableTop),\n\t\t\tindex: i,\n\t\t});\n\t}\n\n\treturn borders;\n}\n\n/** Measures rendered logical column boxes, with an equal-width fallback for incomplete DOMs. */\nexport function measureColumnBoxes(tableEl: HTMLTableElement, fallbackCount = 0): ColumnBox[] {\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst columns: readonly HTMLTableColElement[] = Array.from(\n\t\ttableEl.querySelectorAll(':scope > colgroup > col'),\n\t);\n\tif (columns.length > 0) {\n\t\tconst measured: ColumnBox[] = columns.map((column, index) => {\n\t\t\tconst rect: DOMRect = column.getBoundingClientRect();\n\t\t\treturn {\n\t\t\t\tindex,\n\t\t\t\tposition: rect.left - tableRect.left,\n\t\t\t\twidth: rect.width,\n\t\t\t};\n\t\t});\n\t\tif (measured.every((box) => box.width > 0)) return measured;\n\t}\n\n\tconst count: number = columns.length || fallbackCount;\n\tconst tableWidth: number = tableRect.width || tableEl.offsetWidth;\n\tif (count <= 0 || tableWidth <= 0) return [];\n\tconst width: number = tableWidth / count;\n\treturn Array.from({ length: count }, (_value, index) => ({\n\t\tindex,\n\t\tposition: width * index,\n\t\twidth,\n\t}));\n}\n\n/** Measures inter-column boundaries from rendered `<col>` geometry. */\nexport function measureColBorders(tableEl: HTMLTableElement, fallbackCount?: number): BorderInfo[];\n/** @deprecated Numeric overload retained for compatibility with existing integrations. */\nexport function measureColBorders(tableWidth: number, numCols: number): BorderInfo[];\nexport function measureColBorders(\n\ttableOrWidth: HTMLTableElement | number,\n\tcount = 0,\n): BorderInfo[] {\n\tif (typeof tableOrWidth === 'number') {\n\t\tif (count <= 1) return [];\n\t\tconst width: number = tableOrWidth / count;\n\t\treturn Array.from({ length: count - 1 }, (_value, index) => ({\n\t\t\tposition: Math.round(width * (index + 1)),\n\t\t\tindex: index + 1,\n\t\t}));\n\t}\n\n\tconst boxes: readonly ColumnBox[] = measureColumnBoxes(tableOrWidth, count);\n\tif (boxes.length <= 1) return [];\n\tconst rtl: boolean = getComputedStyle(tableOrWidth).direction === 'rtl';\n\treturn boxes.slice(0, -1).map((box, index) => ({\n\t\tposition: Math.round(rtl ? box.position : box.position + box.width),\n\t\tindex: index + 1,\n\t}));\n}\n\n// --- Positioning ---\n\n/** Positions column handles evenly across the table width. */\nexport function positionColHandles(\n\tcolBar: HTMLDivElement,\n\ttableOrWidth: HTMLTableElement | number,\n\tnumCols = 0,\n): void {\n\tconst handles: HTMLCollection = colBar.children;\n\tif (handles.length === 0) return;\n\n\tconst boxes: readonly ColumnBox[] =\n\t\ttypeof tableOrWidth === 'number'\n\t\t\t? Array.from({ length: numCols }, (_value, index) => ({\n\t\t\t\t\tindex,\n\t\t\t\t\tposition: (tableOrWidth / numCols) * index,\n\t\t\t\t\twidth: tableOrWidth / numCols,\n\t\t\t\t}))\n\t\t\t: measureColumnBoxes(tableOrWidth, numCols);\n\n\tfor (let i = 0; i < handles.length; i++) {\n\t\tconst h = handles[i] as HTMLElement;\n\t\tconst box: ColumnBox | undefined = boxes[i];\n\t\tif (!box) continue;\n\t\tsetStyleProperties(h, {\n\t\t\tleft: `${Math.round(box.position)}px`,\n\t\t\twidth: `${Math.round(box.width)}px`,\n\t\t});\n\t}\n\n\tconst totalWidth: number = boxes.reduce(\n\t\t(maximum, box) => Math.max(maximum, box.position + box.width),\n\t\t0,\n\t);\n\tsetStyleProperty(colBar, 'width', `${Math.round(totalWidth)}px`);\n}\n\n/** Positions row handles to match actual <tr> offsets and heights. */\nexport function positionRowHandles(rowBar: HTMLDivElement, tableEl: HTMLTableElement): void {\n\tconst handles: HTMLCollection = rowBar.children;\n\tif (handles.length === 0) return;\n\n\tconst trs: NodeListOf<HTMLTableRowElement> = tableEl.querySelectorAll(':scope > tbody > tr');\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tlet totalHeight = 0;\n\n\tfor (let i = 0; i < handles.length; i++) {\n\t\tconst h = handles[i] as HTMLElement;\n\t\tconst tr: HTMLTableRowElement | undefined = trs[i];\n\t\tif (tr) {\n\t\t\tconst rect: DOMRect = tr.getBoundingClientRect();\n\t\t\tconst top: number = rect.top - tableRect.top;\n\t\t\tconst height: number = rect.height || tr.offsetHeight;\n\t\t\tsetStyleProperties(h, {\n\t\t\t\ttop: `${top}px`,\n\t\t\t\theight: `${height}px`,\n\t\t\t});\n\t\t\ttotalHeight = top + height;\n\t\t}\n\t}\n\n\tsetStyleProperty(rowBar, 'height', `${totalHeight}px`);\n}\n\n/** Positions add-row and add-column buttons at the table edges. */\nexport function positionAddButtons(\n\taddRowZone: HTMLElement,\n\taddColZone: HTMLElement,\n\ttableEl: HTMLElement,\n\tcontainer: HTMLElement,\n): void {\n\tconst offset = getTableOffset(tableEl, container);\n\tconst tableRect: DOMRect = tableEl.getBoundingClientRect();\n\tconst tableHeight: number = tableRect.height || tableEl.offsetHeight;\n\tconst tableWidth: number = tableRect.width || tableEl.offsetWidth;\n\n\tsetStyleProperties(addRowZone, {\n\t\twidth: `${tableWidth}px`,\n\t\tleft: `${offset.left}px`,\n\t});\n\n\tsetStyleProperties(addColZone, {\n\t\theight: `${tableHeight}px`,\n\t\ttop: `${offset.top}px`,\n\t});\n}\n\n// --- Border Detection ---\n\n/**\n * Finds the nearest row or column border to the given mouse position.\n * Returns which border is closest (within threshold), preferring the nearer one.\n */\nexport function findNearestBorder(\n\tx: number,\n\ty: number,\n\trowBorders: readonly BorderInfo[],\n\tcolBorders: readonly BorderInfo[],\n): NearestBorderResult {\n\tlet nearestRowDist: number = Number.POSITIVE_INFINITY;\n\tlet nearestRowBorder: BorderInfo | null = null;\n\n\tfor (const border of rowBorders) {\n\t\tconst dist: number = Math.abs(y - border.position);\n\t\tif (dist < nearestRowDist && dist < BORDER_THRESHOLD) {\n\t\t\tnearestRowDist = dist;\n\t\t\tnearestRowBorder = border;\n\t\t}\n\t}\n\n\tlet nearestColDist: number = Number.POSITIVE_INFINITY;\n\tlet nearestColBorder: BorderInfo | null = null;\n\n\tfor (const border of colBorders) {\n\t\tconst dist: number = Math.abs(x - border.position);\n\t\tif (dist < nearestColDist && dist < BORDER_THRESHOLD) {\n\t\t\tnearestColDist = dist;\n\t\t\tnearestColBorder = border;\n\t\t}\n\t}\n\n\tif (nearestRowBorder && (!nearestColBorder || nearestRowDist <= nearestColDist)) {\n\t\treturn { type: 'row', border: nearestRowBorder };\n\t}\n\n\tif (nearestColBorder) {\n\t\treturn { type: 'col', border: nearestColBorder };\n\t}\n\n\treturn { type: 'none', border: null };\n}\n","/**\n * Interactive table controls orchestrator.\n * Wires DOM elements (from TableControlsDOM) with layout logic (from TableControlsLayout)\n * and shared transaction builders (from TableCommands) to produce the interactive overlay.\n */\n\nimport type { BlockNode } from '../../model/Document.js';\nimport { getBlockChildren } from '../../model/Document.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport {\n\tremoveStyleProperty,\n\tsetStyleProperties,\n\tsetStyleProperty,\n} from '../../style/StyleRuntime.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getTableBorderColor, renderBorderColorPicker } from './TableBorderColor.js';\nimport {\n\tbuildDeleteColumnTransaction,\n\tbuildDeleteRowTransaction,\n\tbuildInsertColumnTransaction,\n\tbuildInsertRowTransaction,\n\tcreateDeleteTableTransaction,\n} from './TableCommands.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport {\n\tTABLE_DELETE_SVG,\n\tbuildActionsButton,\n\tbuildAddButton,\n\tbuildBorderColorButton,\n\tbuildContextHint,\n\tbuildHandle,\n\tbuildHandleBar,\n\tbuildInsertLine,\n\tbuildResizeIndicator,\n\tbuildResizeSeparator,\n\tcreateButton,\n\tupdateBorderColorSwatch,\n} from './TableControlsDOM.js';\nimport {\n\tBORDER_THRESHOLD,\n\ttype BorderInfo,\n\tfindNearestBorder,\n\tgetTableOffset,\n\tmeasureColBorders,\n\tmeasureColumnBoxes,\n\tmeasureRowBorders,\n\tpositionAddButtons,\n\tpositionColHandles,\n\tpositionRowHandles,\n} from './TableControlsLayout.js';\nimport { createTableGrid } from './TableGrid.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { type TableSelectionService, TableSelectionServiceKey } from './TableSelection.js';\nimport {\n\tDEFAULT_TABLE_SIZING_CONFIG,\n\ttype TableSizingConfig,\n\ttype TableSizingService,\n\tTableSizingServiceKey,\n\treadTableColumnWidthsPx,\n\treadTableRowMinHeightPx,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\n// --- Types ---\n\nexport interface TableControlsHandle {\n\tupdate(node: BlockNode): void;\n\tsetReadOnly(readonly: boolean): void;\n\tdestroy(): void;\n}\n\nexport interface TableControlsConfig extends TableSizingConfig {\n\treadonly directResize: boolean;\n}\n\nconst DEFAULT_CONTROLS_CONFIG: TableControlsConfig = {\n\t...DEFAULT_TABLE_SIZING_CONFIG,\n\tdirectResize: true,\n};\n\ninterface ResizeSession {\n\treadonly kind: 'column' | 'row';\n\treadonly index: number;\n\treadonly pointerId: number;\n\treadonly startCoordinate: number;\n\treadonly startSize: number;\n\tcurrentSize: number;\n}\n\n// --- Class ---\n\nclass TableControls implements TableControlsHandle {\n\tprivate tableId: BlockId;\n\tprivate numRows: number;\n\tprivate numCols: number;\n\tprivate currentNode: BlockNode;\n\tprivate readOnly = false;\n\tprivate activeRowIndex = -1;\n\tprivate activeColIndex = -1;\n\tprivate resizeSession: ResizeSession | null = null;\n\n\tprivate readonly colBar: HTMLDivElement;\n\tprivate readonly rowBar: HTMLDivElement;\n\tprivate readonly colResizeBar: HTMLDivElement;\n\tprivate readonly rowResizeBar: HTMLDivElement;\n\tprivate readonly resizeIndicator: HTMLDivElement;\n\tprivate readonly insertLineH: HTMLDivElement;\n\tprivate readonly insertLineV: HTMLDivElement;\n\tprivate readonly addRowZone: HTMLButtonElement;\n\tprivate readonly addColZone: HTMLButtonElement;\n\tprivate readonly deleteTableBtn: HTMLButtonElement;\n\tprivate readonly borderColorBtn: HTMLButtonElement | null;\n\tprivate readonly actionsBtn: HTMLButtonElement | null;\n\tprivate readonly contextHint: HTMLDivElement;\n\tprivate readonly observer: ResizeObserver;\n\tprivate readonly wrapperEl: HTMLElement | null;\n\tprivate borderColorPopup: HTMLDivElement | null = null;\n\tprivate activeContextMenu: TableContextMenuHandle | null = null;\n\n\tprivate readonly container: HTMLElement;\n\tprivate readonly tableEl: HTMLTableElement;\n\tprivate readonly getState: () => EditorState;\n\tprivate readonly dispatchFn: (tr: Transaction) => void;\n\tprivate readonly pluginContext: PluginContext | null;\n\tprivate readonly locale: TableLocale;\n\tprivate readonly config: TableControlsConfig;\n\tprivate readonly onWrapperScroll = (): void => this.positionControls();\n\tprivate readonly onPointerMove = (event: PointerEvent): void => this.moveResize(event);\n\tprivate readonly onPointerUp = (event: PointerEvent): void => this.finishResize(event);\n\tprivate readonly onPointerCancel = (event: PointerEvent): void => this.cancelResize(event);\n\tprivate readonly onDocumentKeyDown = (event: KeyboardEvent): void => {\n\t\tif (event.key !== 'Escape' || !this.resizeSession) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.cancelActiveResize();\n\t};\n\n\tprivate readonly onMouseMove = (e: MouseEvent): void => {\n\t\tif (this.readOnly || this.resizeSession) return;\n\t\tconst tableRect: DOMRect = this.tableEl.getBoundingClientRect();\n\t\tconst x: number = e.clientX - tableRect.left;\n\t\tconst y: number = e.clientY - tableRect.top;\n\n\t\tconst inTable: boolean =\n\t\t\tx >= -BORDER_THRESHOLD &&\n\t\t\tx <= tableRect.width + BORDER_THRESHOLD &&\n\t\t\ty >= -BORDER_THRESHOLD &&\n\t\t\ty <= tableRect.height + BORDER_THRESHOLD;\n\n\t\tif (!inTable) {\n\t\t\tthis.hideInsertLines();\n\t\t\treturn;\n\t\t}\n\n\t\tconst rowBorders: BorderInfo[] = measureRowBorders(this.tableEl);\n\t\tconst colBorders: BorderInfo[] = measureColBorders(this.tableEl, this.numCols);\n\t\tconst result = findNearestBorder(x, y, rowBorders, colBorders);\n\n\t\tif (result.type === 'row' && result.border) {\n\t\t\tthis.showHorizontalLine(result.border);\n\t\t\tthis.hideVerticalLine();\n\t\t} else if (result.type === 'col' && result.border) {\n\t\t\tthis.showVerticalLine(result.border);\n\t\t\tthis.hideHorizontalLine();\n\t\t} else {\n\t\t\tthis.hideInsertLines();\n\t\t}\n\t};\n\n\tprivate readonly onMouseLeave = (): void => {\n\t\tthis.hideInsertLines();\n\t};\n\n\tconstructor(\n\t\tcontainer: HTMLElement,\n\t\ttableEl: HTMLTableElement,\n\t\tinitialNode: BlockNode,\n\t\tgetState: () => EditorState,\n\t\tdispatch: (tr: Transaction) => void,\n\t\tpluginContext?: PluginContext,\n\t\tlocale: TableLocale = TABLE_LOCALE_EN,\n\t\tconfig: Partial<TableControlsConfig> = DEFAULT_CONTROLS_CONFIG,\n\t) {\n\t\tthis.container = container;\n\t\tthis.tableEl = tableEl;\n\t\tthis.getState = getState;\n\t\tthis.dispatchFn = dispatch;\n\t\tthis.pluginContext = pluginContext ?? null;\n\t\tthis.locale = locale;\n\t\tthis.config = {\n\t\t\t...resolveTableSizingConfig(config),\n\t\t\tdirectResize: config.directResize ?? true,\n\t\t};\n\t\tthis.wrapperEl = tableEl.closest('.notectl-table-wrapper');\n\n\t\tthis.tableId = initialNode.id;\n\t\tthis.currentNode = initialNode;\n\t\tthis.numRows = createTableGrid(initialNode).rowCount;\n\t\tthis.numCols = this.countCols(initialNode);\n\n\t\t// Create DOM elements\n\t\tthis.colBar = buildHandleBar('ntbl-col-bar');\n\t\tthis.rowBar = buildHandleBar('ntbl-row-bar');\n\t\tthis.colResizeBar = buildHandleBar('ntbl-col-resize-bar');\n\t\tthis.rowResizeBar = buildHandleBar('ntbl-row-resize-bar');\n\t\tthis.resizeIndicator = buildResizeIndicator();\n\t\tthis.insertLineH = buildInsertLine('horizontal', locale.insertRow);\n\t\tthis.insertLineV = buildInsertLine('vertical', locale.insertColumn);\n\t\tthis.addRowZone = buildAddButton('ntbl-add-row', locale.addRow);\n\t\tthis.addColZone = buildAddButton('ntbl-add-col', locale.addColumn);\n\t\tthis.deleteTableBtn = createButton(\n\t\t\t'ntbl-delete-table-btn',\n\t\t\tTABLE_DELETE_SVG,\n\t\t\tlocale.deleteTable,\n\t\t);\n\t\tthis.deleteTableBtn.setAttribute('data-notectl-no-print', '');\n\n\t\t// Border color button and actions button (only when plugin context available)\n\t\tif (pluginContext) {\n\t\t\tconst currentColor: string | undefined = getTableBorderColor(getState(), this.tableId);\n\t\t\tthis.borderColorBtn = buildBorderColorButton(locale.borderColor, currentColor);\n\t\t\tthis.actionsBtn = buildActionsButton(locale.tableActionsHint);\n\t\t} else {\n\t\t\tthis.borderColorBtn = null;\n\t\t\tthis.actionsBtn = null;\n\t\t}\n\n\t\tcontainer.append(\n\t\t\tthis.colBar,\n\t\t\tthis.rowBar,\n\t\t\tthis.insertLineH,\n\t\t\tthis.insertLineV,\n\t\t\tthis.addRowZone,\n\t\t\tthis.addColZone,\n\t\t\tthis.deleteTableBtn,\n\t\t);\n\t\tif (this.config.directResize) {\n\t\t\tcontainer.append(this.colResizeBar, this.rowResizeBar, this.resizeIndicator);\n\t\t}\n\t\tif (this.borderColorBtn) {\n\t\t\tcontainer.appendChild(this.borderColorBtn);\n\t\t}\n\t\tif (this.actionsBtn) {\n\t\t\tcontainer.appendChild(this.actionsBtn);\n\t\t}\n\n\t\tthis.contextHint = buildContextHint(locale.contextMenuHint);\n\t\tcontainer.appendChild(this.contextHint);\n\n\t\tthis.rebuildColHandles();\n\t\tthis.rebuildRowHandles();\n\t\tthis.rebuildResizeSeparators();\n\t\tthis.bindEventListeners();\n\t\tthis.setReadOnly(pluginContext?.isReadOnly?.() ?? false);\n\n\t\tthis.observer = new ResizeObserver(() => {\n\t\t\tthis.positionControls();\n\t\t});\n\t\tthis.observer.observe(tableEl);\n\t\tif (this.wrapperEl) {\n\t\t\tthis.observer.observe(this.wrapperEl);\n\t\t\tthis.wrapperEl.addEventListener('scroll', this.onWrapperScroll, { passive: true });\n\t\t}\n\t\trequestAnimationFrame(() => this.positionControls());\n\t}\n\n\tupdate(node: BlockNode): void {\n\t\tthis.tableId = node.id;\n\t\tthis.currentNode = node;\n\t\tconst newRows: number = createTableGrid(node).rowCount;\n\t\tconst newCols: number = this.countCols(node);\n\n\t\tif (newRows !== this.numRows || newCols !== this.numCols) {\n\t\t\tthis.numRows = newRows;\n\t\t\tthis.numCols = newCols;\n\t\t\tthis.rebuildColHandles();\n\t\t\tthis.rebuildRowHandles();\n\t\t\tthis.rebuildResizeSeparators();\n\t\t}\n\t\tthis.updateHandleSelectionState();\n\n\t\t// Update border color swatch\n\t\tif (this.borderColorBtn) {\n\t\t\tconst swatch = this.borderColorBtn.querySelector('.ntbl-border-color-swatch');\n\t\t\tif (swatch) {\n\t\t\t\tconst color: string | undefined = node.attrs?.borderColor as string | undefined;\n\t\t\t\tupdateBorderColorSwatch(swatch as HTMLElement, color);\n\t\t\t}\n\t\t}\n\n\t\trequestAnimationFrame(() => this.positionControls());\n\t}\n\n\tsetReadOnly(readonly: boolean): void {\n\t\tthis.readOnly = readonly;\n\t\tthis.container.toggleAttribute('data-notectl-table-readonly', readonly);\n\t\tconst roots: readonly HTMLElement[] = [\n\t\t\tthis.colBar,\n\t\t\tthis.rowBar,\n\t\t\tthis.colResizeBar,\n\t\t\tthis.rowResizeBar,\n\t\t\tthis.addRowZone,\n\t\t\tthis.addColZone,\n\t\t\tthis.deleteTableBtn,\n\t\t\tthis.insertLineH,\n\t\t\tthis.insertLineV,\n\t\t\t...(this.borderColorBtn ? [this.borderColorBtn] : []),\n\t\t\t...(this.actionsBtn ? [this.actionsBtn] : []),\n\t\t];\n\t\tfor (const root of roots) {\n\t\t\tif (root instanceof HTMLButtonElement) root.disabled = readonly;\n\t\t\tfor (const button of root.querySelectorAll('button')) button.disabled = readonly;\n\t\t}\n\t\tif (readonly) {\n\t\t\tthis.hideInsertLines();\n\t\t\tthis.cancelActiveResize();\n\t\t\tthis.activeContextMenu?.close();\n\t\t\tthis.activeContextMenu = null;\n\t\t\tthis.closeBorderColorPopup();\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tthis.cancelActiveResize();\n\t\tthis.activeContextMenu?.destroy();\n\t\tthis.closeBorderColorPopup();\n\t\tthis.observer.disconnect();\n\t\tthis.wrapperEl?.removeEventListener('scroll', this.onWrapperScroll);\n\t\tthis.container.removeEventListener('mousemove', this.onMouseMove);\n\t\tthis.container.removeEventListener('mouseleave', this.onMouseLeave);\n\t\tthis.colBar.remove();\n\t\tthis.rowBar.remove();\n\t\tthis.colResizeBar.remove();\n\t\tthis.rowResizeBar.remove();\n\t\tthis.resizeIndicator.remove();\n\t\tthis.insertLineH.remove();\n\t\tthis.insertLineV.remove();\n\t\tthis.addRowZone.remove();\n\t\tthis.addColZone.remove();\n\t\tthis.deleteTableBtn.remove();\n\t\tthis.borderColorBtn?.remove();\n\t\tthis.actionsBtn?.remove();\n\t\tthis.contextHint.remove();\n\t}\n\n\tprivate bindEventListeners(): void {\n\t\tconst insertBtnH = this.insertLineH.querySelector('.ntbl-insert-btn') as HTMLButtonElement;\n\t\tconst insertBtnV = this.insertLineV.querySelector('.ntbl-insert-btn') as HTMLButtonElement;\n\n\t\tinsertBtnH.addEventListener('click', () => {\n\t\t\tif (this.activeRowIndex >= 0) {\n\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\tbuildInsertRowTransaction(this.getState(), this.tableId, this.activeRowIndex),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tinsertBtnV.addEventListener('click', () => {\n\t\t\tif (this.activeColIndex >= 0) {\n\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\tbuildInsertColumnTransaction(this.getState(), this.tableId, this.activeColIndex),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\tthis.addRowZone.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(\n\t\t\t\tbuildInsertRowTransaction(this.getState(), this.tableId, this.numRows),\n\t\t\t);\n\t\t});\n\n\t\tthis.addColZone.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(\n\t\t\t\tbuildInsertColumnTransaction(this.getState(), this.tableId, this.numCols),\n\t\t\t);\n\t\t});\n\n\t\tthis.deleteTableBtn.addEventListener('click', () => {\n\t\t\tthis.dispatchTransaction(createDeleteTableTransaction(this.getState(), this.tableId));\n\t\t});\n\n\t\tif (this.borderColorBtn && this.pluginContext) {\n\t\t\tconst ctx = this.pluginContext;\n\t\t\tthis.borderColorBtn.addEventListener('click', () => {\n\t\t\t\tthis.toggleBorderColorPopup(ctx);\n\t\t\t});\n\t\t}\n\n\t\tif (this.actionsBtn && this.pluginContext) {\n\t\t\tconst ctx = this.pluginContext;\n\t\t\tthis.actionsBtn.addEventListener('click', () => {\n\t\t\t\tthis.openActionsMenu(ctx);\n\t\t\t});\n\t\t}\n\n\t\tthis.container.addEventListener('mousemove', this.onMouseMove);\n\t\tthis.container.addEventListener('mouseleave', this.onMouseLeave);\n\t}\n\n\tprivate openActionsMenu(context: PluginContext): void {\n\t\tif (this.readOnly || context.isReadOnly?.()) return;\n\t\tif (this.activeContextMenu?.isOpen()) {\n\t\t\tthis.activeContextMenu.close();\n\t\t\tthis.activeContextMenu = null;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.actionsBtn) return;\n\n\t\tconst rect: DOMRect = this.actionsBtn.getBoundingClientRect();\n\n\t\tthis.activeContextMenu = createTableContextMenu(\n\t\t\tthis.container,\n\t\t\tcontext,\n\t\t\tthis.tableId,\n\t\t\trect,\n\t\t\t() => {\n\t\t\t\tthis.activeContextMenu = null;\n\t\t\t},\n\t\t\tthis.locale,\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\tsizingConfig: this.config,\n\t\t\t\trestoreFocusTo: this.actionsBtn,\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate toggleBorderColorPopup(context: PluginContext): void {\n\t\tif (this.readOnly || context.isReadOnly?.()) return;\n\t\tif (this.borderColorPopup) {\n\t\t\tthis.closeBorderColorPopup();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.borderColorPopup = document.createElement('div');\n\t\tthis.borderColorPopup.className = 'notectl-table-context-menu';\n\t\tsetStyleProperties(this.borderColorPopup, {\n\t\t\tposition: 'absolute',\n\t\t\ttop: '24px',\n\t\t\tleft: '24px',\n\t\t\tzIndex: '10000',\n\t\t});\n\t\tthis.borderColorPopup.setAttribute('contenteditable', 'false');\n\n\t\trenderBorderColorPicker(\n\t\t\tthis.borderColorPopup,\n\t\t\tcontext,\n\t\t\tthis.tableId,\n\t\t\t() => {\n\t\t\t\tthis.closeBorderColorPopup();\n\t\t\t},\n\t\t\tthis.locale,\n\t\t);\n\n\t\tthis.container.appendChild(this.borderColorPopup);\n\t}\n\n\tprivate closeBorderColorPopup(): void {\n\t\tif (this.borderColorPopup) {\n\t\t\tthis.borderColorPopup.remove();\n\t\t\tthis.borderColorPopup = null;\n\t\t}\n\t}\n\n\tprivate dispatchTransaction(tr: Transaction | null): void {\n\t\tif (tr && !this.readOnly && !this.pluginContext?.isReadOnly?.()) this.dispatchFn(tr);\n\t}\n\n\tprivate countCols(node: BlockNode): number {\n\t\treturn createTableGrid(node).columnCount;\n\t}\n\n\tprivate positionControls(): void {\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tconst tableRect: DOMRect = this.tableEl.getBoundingClientRect();\n\t\tsetStyleProperties(this.colBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top - 24)}px`,\n\t\t});\n\t\tsetStyleProperty(this.rowBar, 'top', `${String(offset.top)}px`);\n\t\tpositionColHandles(this.colBar, this.tableEl, this.numCols);\n\t\tpositionRowHandles(this.rowBar, this.tableEl);\n\t\tpositionAddButtons(this.addRowZone, this.addColZone, this.tableEl, this.container);\n\t\tif (this.config.directResize) {\n\t\t\tthis.positionResizeSeparators(offset, tableRect);\n\t\t}\n\t}\n\n\tprivate positionResizeSeparators(\n\t\toffset: { readonly top: number; readonly left: number },\n\t\ttableRect: DOMRect,\n\t): void {\n\t\tsetStyleProperties(this.colResizeBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top)}px`,\n\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\theight: `${String(tableRect.height)}px`,\n\t\t});\n\t\tsetStyleProperties(this.rowResizeBar, {\n\t\t\tleft: `${String(offset.left)}px`,\n\t\t\ttop: `${String(offset.top)}px`,\n\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\theight: `${String(tableRect.height)}px`,\n\t\t});\n\n\t\tconst rtl: boolean = getComputedStyle(this.tableEl).direction === 'rtl';\n\t\tconst boxes = measureColumnBoxes(this.tableEl, this.numCols);\n\t\tfor (let index = 0; index < this.colResizeBar.children.length; index++) {\n\t\t\tconst separator = this.colResizeBar.children[index] as HTMLButtonElement;\n\t\t\tconst box = boxes[index];\n\t\t\tif (!box) continue;\n\t\t\tconst boundary: number = rtl ? box.position : box.position + box.width;\n\t\t\tsetStyleProperties(separator, {\n\t\t\t\tleft: `${String(boundary)}px`,\n\t\t\t\ttop: '0px',\n\t\t\t\theight: `${String(tableRect.height)}px`,\n\t\t\t});\n\t\t\tthis.updateSeparatorValue(separator, 'column', index, box.width);\n\t\t}\n\n\t\tconst rows: readonly HTMLTableRowElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > tbody > tr'),\n\t\t);\n\t\tfor (let index = 0; index < this.rowResizeBar.children.length; index++) {\n\t\t\tconst separator = this.rowResizeBar.children[index] as HTMLButtonElement;\n\t\t\tconst row: HTMLTableRowElement | undefined = rows[index];\n\t\t\tif (!row) continue;\n\t\t\tconst rect: DOMRect = row.getBoundingClientRect();\n\t\t\tconst boundary: number = rect.bottom - tableRect.top;\n\t\t\tsetStyleProperties(separator, {\n\t\t\t\tleft: '0px',\n\t\t\t\ttop: `${String(boundary)}px`,\n\t\t\t\twidth: `${String(tableRect.width)}px`,\n\t\t\t});\n\t\t\tthis.updateSeparatorValue(separator, 'row', index, rect.height || row.offsetHeight);\n\t\t}\n\t}\n\n\tprivate updateSeparatorValue(\n\t\tseparator: HTMLButtonElement,\n\t\tkind: 'column' | 'row',\n\t\tindex: number,\n\t\tvalue: number,\n\t): void {\n\t\tconst minimum: number =\n\t\t\tkind === 'column' ? this.config.minColumnWidthPx : this.config.minRowHeightPx;\n\t\tconst maximum: number =\n\t\t\tkind === 'column' ? this.config.maxColumnWidthPx : this.config.maxRowHeightPx;\n\t\tconst rounded: number = Math.round(value);\n\t\tseparator.setAttribute('aria-valuemin', String(minimum));\n\t\tseparator.setAttribute('aria-valuemax', String(maximum));\n\t\tseparator.setAttribute('aria-valuenow', String(rounded));\n\t\tseparator.setAttribute('aria-valuetext', `${String(rounded)} px`);\n\t\tseparator.dataset.index = String(index);\n\t}\n\n\tprivate startResize(event: PointerEvent, kind: 'column' | 'row', index: number): void {\n\t\tif (\n\t\t\tthis.readOnly ||\n\t\t\tthis.pluginContext?.isReadOnly?.() ||\n\t\t\t(event.button !== 0 && event.pointerType !== 'touch')\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst size: number | null = this.getRenderedDimension(kind, index);\n\t\tif (size === null) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.cancelActiveResize();\n\t\tthis.resizeSession = {\n\t\t\tkind,\n\t\t\tindex,\n\t\t\tpointerId: event.pointerId,\n\t\t\tstartCoordinate: kind === 'column' ? event.clientX : event.clientY,\n\t\t\tstartSize: size,\n\t\t\tcurrentSize: size,\n\t\t};\n\t\t(event.currentTarget as HTMLElement | null)?.setPointerCapture?.(event.pointerId);\n\t\tdocument.addEventListener('pointermove', this.onPointerMove);\n\t\tdocument.addEventListener('pointerup', this.onPointerUp);\n\t\tdocument.addEventListener('pointercancel', this.onPointerCancel);\n\t\tdocument.addEventListener('keydown', this.onDocumentKeyDown, true);\n\t\tthis.container.classList.add(`ntbl-container--resizing-${kind}`);\n\t\tthis.updateResizeIndicator(size, event.clientX, event.clientY);\n\t}\n\n\tprivate moveResize(event: PointerEvent): void {\n\t\tconst session: ResizeSession | null = this.resizeSession;\n\t\tif (!session || session.pointerId !== event.pointerId) return;\n\t\tevent.preventDefault();\n\t\tconst coordinate: number = session.kind === 'column' ? event.clientX : event.clientY;\n\t\tlet delta: number = coordinate - session.startCoordinate;\n\t\tif (session.kind === 'column' && getComputedStyle(this.tableEl).direction === 'rtl') {\n\t\t\tdelta *= -1;\n\t\t}\n\t\tconst next: number = this.clampDimension(session.kind, session.startSize + delta);\n\t\tsession.currentSize = next;\n\t\tthis.applyResizePreview(session.kind, session.index, next);\n\t\tthis.updateResizeIndicator(next, event.clientX, event.clientY);\n\t\tthis.positionControls();\n\t}\n\n\tprivate finishResize(event: PointerEvent): void {\n\t\tconst session: ResizeSession | null = this.resizeSession;\n\t\tif (!session || session.pointerId !== event.pointerId) return;\n\t\t(event.currentTarget as HTMLElement | null)?.releasePointerCapture?.(event.pointerId);\n\t\tthis.resizeSession = null;\n\t\tthis.detachResizeListeners();\n\t\tthis.hideResizeIndicator();\n\t\tconst value: number = Math.round(session.currentSize);\n\t\tconst service: TableSizingService | undefined = this.getSizingService();\n\t\tconst changed: boolean =\n\t\t\tsession.kind === 'column'\n\t\t\t\t? (service?.setSize(\n\t\t\t\t\t\t{ kind: 'column', tableId: this.tableId, column: session.index },\n\t\t\t\t\t\t{ columnWidthPx: value },\n\t\t\t\t\t) ?? false)\n\t\t\t\t: (service?.setSize(\n\t\t\t\t\t\t{ kind: 'row', tableId: this.tableId, row: session.index },\n\t\t\t\t\t\t{ rowMinHeightPx: value },\n\t\t\t\t\t) ?? false);\n\t\tif (!changed) this.restoreCanonicalDimensions();\n\t\telse if (session.kind === 'column') {\n\t\t\tthis.pluginContext?.announce(this.locale.announceColumnWidthSet(session.index, value));\n\t\t} else {\n\t\t\tthis.pluginContext?.announce(this.locale.announceRowMinimumHeightSet(session.index, value));\n\t\t}\n\t}\n\n\tprivate cancelResize(event: PointerEvent): void {\n\t\tif (this.resizeSession?.pointerId !== event.pointerId) return;\n\t\tthis.cancelActiveResize();\n\t}\n\n\tprivate cancelActiveResize(): void {\n\t\tif (!this.resizeSession) return;\n\t\tthis.resizeSession = null;\n\t\tthis.detachResizeListeners();\n\t\tthis.hideResizeIndicator();\n\t\tthis.restoreCanonicalDimensions();\n\t\tthis.positionControls();\n\t}\n\n\tprivate detachResizeListeners(): void {\n\t\tdocument.removeEventListener('pointermove', this.onPointerMove);\n\t\tdocument.removeEventListener('pointerup', this.onPointerUp);\n\t\tdocument.removeEventListener('pointercancel', this.onPointerCancel);\n\t\tdocument.removeEventListener('keydown', this.onDocumentKeyDown, true);\n\t\tthis.container.classList.remove('ntbl-container--resizing-column');\n\t\tthis.container.classList.remove('ntbl-container--resizing-row');\n\t}\n\n\tprivate getRenderedDimension(kind: 'column' | 'row', index: number): number | null {\n\t\tif (kind === 'column')\n\t\t\treturn measureColumnBoxes(this.tableEl, this.numCols)[index]?.width ?? null;\n\t\tconst row: HTMLTableRowElement | undefined = Array.from(\n\t\t\tthis.tableEl.querySelectorAll<HTMLTableRowElement>(':scope > tbody > tr'),\n\t\t)[index];\n\t\tif (!row) return null;\n\t\tconst rect: DOMRect = row.getBoundingClientRect();\n\t\treturn rect.height || row.offsetHeight || null;\n\t}\n\n\tprivate clampDimension(kind: 'column' | 'row', value: number): number {\n\t\tconst minimum: number =\n\t\t\tkind === 'column' ? this.config.minColumnWidthPx : this.config.minRowHeightPx;\n\t\tconst maximum: number =\n\t\t\tkind === 'column' ? this.config.maxColumnWidthPx : this.config.maxRowHeightPx;\n\t\treturn Math.min(maximum, Math.max(minimum, value));\n\t}\n\n\tprivate applyResizePreview(kind: 'column' | 'row', index: number, value: number): void {\n\t\tif (kind === 'column') {\n\t\t\tconst column = this.tableEl.querySelectorAll(':scope > colgroup > col')[index] as\n\t\t\t\t| HTMLTableColElement\n\t\t\t\t| undefined;\n\t\t\tif (!column) return;\n\t\t\t// A temporary presentation attribute drives native table layout during\n\t\t\t// pointer movement. Removing the persisted runtime width avoids a stale\n\t\t\t// CSS declaration winning the cascade; no document transaction occurs.\n\t\t\tremoveStyleProperty(column, 'width');\n\t\t\tcolumn.setAttribute('width', String(value));\n\t\t\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\t\t\tthis.currentNode,\n\t\t\t\tthis.numCols,\n\t\t\t);\n\t\t\tconst minimumTableWidth: number = widths.reduce<number>(\n\t\t\t\t(total: number, width: number | null, columnIndex: number): number =>\n\t\t\t\t\ttotal + (columnIndex === index ? value : (width ?? this.config.minColumnWidthPx)),\n\t\t\t\t0,\n\t\t\t);\n\t\t\tsetStyleProperty(this.tableEl, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\t\tif (\n\t\t\t\twidths.length >= this.numCols &&\n\t\t\t\twidths.every(\n\t\t\t\t\t(width: number | null, columnIndex: number): boolean =>\n\t\t\t\t\t\tcolumnIndex === index || width !== null,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tsetStyleProperty(this.tableEl, 'width', `${String(minimumTableWidth)}px`);\n\t\t\t} else {\n\t\t\t\tremoveStyleProperty(this.tableEl, 'width');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst row = this.tableEl.querySelectorAll(':scope > tbody > tr')[index] as\n\t\t\t| HTMLTableRowElement\n\t\t\t| undefined;\n\t\tif (!row) return;\n\t\tremoveStyleProperty(row, 'height');\n\t\trow.setAttribute('height', String(value));\n\t}\n\n\tprivate restoreCanonicalDimensions(): void {\n\t\tconst widths: readonly (number | null)[] = readTableColumnWidthsPx(\n\t\t\tthis.currentNode,\n\t\t\tthis.numCols,\n\t\t);\n\t\tconst columns: readonly HTMLTableColElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > colgroup > col'),\n\t\t);\n\t\tlet minimumTableWidth = 0;\n\t\tfor (let index = 0; index < columns.length; index++) {\n\t\t\tconst column: HTMLTableColElement | undefined = columns[index];\n\t\t\tif (!column) continue;\n\t\t\tconst width: number | null = widths[index] ?? null;\n\t\t\tcolumn.removeAttribute('width');\n\t\t\tif (width === null) {\n\t\t\t\tremoveStyleProperty(column, 'width');\n\t\t\t\tcolumn.removeAttribute('data-notectl-width-px');\n\t\t\t} else {\n\t\t\t\tsetStyleProperty(column, 'width', `${String(width)}px`);\n\t\t\t\tcolumn.setAttribute('data-notectl-width-px', String(width));\n\t\t\t}\n\t\t\tminimumTableWidth += width ?? this.config.minColumnWidthPx;\n\t\t}\n\t\tsetStyleProperty(this.tableEl, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\tif (widths.length >= this.numCols && widths.every((width) => width !== null)) {\n\t\t\tsetStyleProperty(this.tableEl, 'width', `${String(minimumTableWidth)}px`);\n\t\t} else {\n\t\t\tremoveStyleProperty(this.tableEl, 'width');\n\t\t}\n\n\t\tconst rowNodes: readonly BlockNode[] = getBlockChildren(this.currentNode);\n\t\tconst rows: readonly HTMLTableRowElement[] = Array.from(\n\t\t\tthis.tableEl.querySelectorAll(':scope > tbody > tr'),\n\t\t);\n\t\tfor (let index = 0; index < rows.length; index++) {\n\t\t\tconst row: HTMLTableRowElement | undefined = rows[index];\n\t\t\tconst rowNode: BlockNode | undefined = rowNodes[index];\n\t\t\tif (!row || !rowNode) continue;\n\t\t\trow.removeAttribute('height');\n\t\t\tconst height: number | null = readTableRowMinHeightPx(rowNode);\n\t\t\tif (height === null) {\n\t\t\t\tremoveStyleProperty(row, 'height');\n\t\t\t\trow.removeAttribute('data-notectl-min-height-px');\n\t\t\t} else {\n\t\t\t\tsetStyleProperty(row, 'height', `${String(height)}px`);\n\t\t\t\trow.setAttribute('data-notectl-min-height-px', String(height));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate updateResizeIndicator(value: number, clientX: number, clientY: number): void {\n\t\tconst containerRect: DOMRect = this.container.getBoundingClientRect();\n\t\tthis.resizeIndicator.textContent = `${String(Math.round(value))} px`;\n\t\tsetStyleProperties(this.resizeIndicator, {\n\t\t\tleft: `${String(clientX - containerRect.left + this.container.scrollLeft + 12)}px`,\n\t\t\ttop: `${String(clientY - containerRect.top + this.container.scrollTop + 12)}px`,\n\t\t});\n\t\tthis.resizeIndicator.classList.add('ntbl-resize-indicator--visible');\n\t}\n\n\tprivate hideResizeIndicator(): void {\n\t\tthis.resizeIndicator.classList.remove('ntbl-resize-indicator--visible');\n\t}\n\n\tprivate showHorizontalLine(border: BorderInfo): void {\n\t\tthis.activeRowIndex = border.index;\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tsetStyleProperties(this.insertLineH, {\n\t\t\ttop: `${offset.top + border.position - 1}px`,\n\t\t\tleft: `${offset.left}px`,\n\t\t\twidth: `${this.tableEl.offsetWidth}px`,\n\t\t});\n\t\tthis.insertLineH.classList.add('ntbl-insert-line--visible');\n\t}\n\n\tprivate showVerticalLine(border: BorderInfo): void {\n\t\tthis.activeColIndex = border.index;\n\t\tconst offset = getTableOffset(this.tableEl, this.container);\n\t\tsetStyleProperties(this.insertLineV, {\n\t\t\tleft: `${offset.left + border.position - 1}px`,\n\t\t\ttop: `${offset.top}px`,\n\t\t\theight: `${this.tableEl.offsetHeight}px`,\n\t\t});\n\t\tthis.insertLineV.classList.add('ntbl-insert-line--visible');\n\t}\n\n\tprivate hideInsertLines(): void {\n\t\tthis.hideHorizontalLine();\n\t\tthis.hideVerticalLine();\n\t}\n\n\tprivate hideHorizontalLine(): void {\n\t\tthis.insertLineH.classList.remove('ntbl-insert-line--visible');\n\t\tthis.activeRowIndex = -1;\n\t}\n\n\tprivate hideVerticalLine(): void {\n\t\tthis.insertLineV.classList.remove('ntbl-insert-line--visible');\n\t\tthis.activeColIndex = -1;\n\t}\n\n\tprivate rebuildColHandles(): void {\n\t\tthis.colBar.innerHTML = '';\n\t\tfor (let i = 0; i < this.numCols; i++) {\n\t\t\tconst handle: HTMLDivElement = buildHandle(\n\t\t\t\t'ntbl-col-handle',\n\t\t\t\ti,\n\t\t\t\tthis.locale.deleteColumn,\n\t\t\t\t(idx: number) => {\n\t\t\t\t\tthis.dispatchTransaction(\n\t\t\t\t\t\tbuildDeleteColumnTransaction(this.getState(), this.tableId, idx),\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: this.locale.selectColumnLabel(i),\n\t\t\t\t\tonSelect: (idx: number) => this.selectColumn(idx),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.colBar.appendChild(handle);\n\t\t}\n\t}\n\n\tprivate rebuildRowHandles(): void {\n\t\tthis.rowBar.innerHTML = '';\n\t\tfor (let i = 0; i < this.numRows; i++) {\n\t\t\tconst handle: HTMLDivElement = buildHandle(\n\t\t\t\t'ntbl-row-handle',\n\t\t\t\ti,\n\t\t\t\tthis.locale.deleteRow,\n\t\t\t\t(idx: number) => {\n\t\t\t\t\tthis.dispatchTransaction(buildDeleteRowTransaction(this.getState(), this.tableId, idx));\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: this.locale.selectRowLabel(i),\n\t\t\t\t\tonSelect: (idx: number) => this.selectRow(idx),\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.rowBar.appendChild(handle);\n\t\t}\n\t}\n\n\tprivate rebuildResizeSeparators(): void {\n\t\tthis.colResizeBar.innerHTML = '';\n\t\tthis.rowResizeBar.innerHTML = '';\n\t\tif (!this.config.directResize) return;\n\t\tconst keyboardHint: string = this.locale.resizeKeyboardHint(\n\t\t\tthis.config.keyboardResizeStepPx,\n\t\t\tthis.config.keyboardResizeLargeStepPx,\n\t\t);\n\t\tfor (let index = 0; index < this.numCols; index++) {\n\t\t\tconst separator: HTMLButtonElement = buildResizeSeparator(\n\t\t\t\t'column',\n\t\t\t\tindex,\n\t\t\t\tthis.locale.resizeColumnSeparatorLabel(index),\n\t\t\t\tkeyboardHint,\n\t\t\t);\n\t\t\tseparator.addEventListener('pointerdown', (event: PointerEvent) =>\n\t\t\t\tthis.startResize(event, 'column', index),\n\t\t\t);\n\t\t\tseparator.addEventListener('keydown', (event: KeyboardEvent) =>\n\t\t\t\tthis.resizeFromKeyboard(event, 'column', index),\n\t\t\t);\n\t\t\tthis.colResizeBar.appendChild(separator);\n\t\t}\n\t\tfor (let index = 0; index < this.numRows; index++) {\n\t\t\tconst separator: HTMLButtonElement = buildResizeSeparator(\n\t\t\t\t'row',\n\t\t\t\tindex,\n\t\t\t\tthis.locale.resizeRowSeparatorLabel(index),\n\t\t\t\tkeyboardHint,\n\t\t\t);\n\t\t\tseparator.addEventListener('pointerdown', (event: PointerEvent) =>\n\t\t\t\tthis.startResize(event, 'row', index),\n\t\t\t);\n\t\t\tseparator.addEventListener('keydown', (event: KeyboardEvent) =>\n\t\t\t\tthis.resizeFromKeyboard(event, 'row', index),\n\t\t\t);\n\t\t\tthis.rowResizeBar.appendChild(separator);\n\t\t}\n\t\tthis.setReadOnly(this.readOnly);\n\t}\n\n\tprivate resizeFromKeyboard(event: KeyboardEvent, kind: 'column' | 'row', index: number): void {\n\t\tif (this.readOnly || this.pluginContext?.isReadOnly?.()) return;\n\t\tconst service: TableSizingService | undefined = this.getSizingService();\n\t\tif (!service) return;\n\t\tconst separator: HTMLButtonElement = event.currentTarget as HTMLButtonElement;\n\n\t\tif (event.key === 'Delete' || event.key === 'Backspace') {\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tconst target =\n\t\t\t\tkind === 'column'\n\t\t\t\t\t? ({ kind: 'column', tableId: this.tableId, column: index } as const)\n\t\t\t\t\t: ({ kind: 'row', tableId: this.tableId, row: index } as const);\n\t\t\tconst dimension = kind === 'column' ? 'columnWidthPx' : 'rowMinHeightPx';\n\t\t\tif (service.resetSize(target, dimension)) {\n\t\t\t\tthis.restoreKeyboardResizeFocus(separator);\n\t\t\t\tthis.pluginContext?.announce(\n\t\t\t\t\tkind === 'column'\n\t\t\t\t\t\t? this.locale.announceColumnWidthReset(index)\n\t\t\t\t\t\t: this.locale.announceRowMinimumHeightReset(index),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst decrementKey: string = kind === 'column' ? 'ArrowLeft' : 'ArrowUp';\n\t\tconst incrementKey: string = kind === 'column' ? 'ArrowRight' : 'ArrowDown';\n\t\tif (event.key !== decrementKey && event.key !== incrementKey) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tconst current: number | null = this.getRenderedDimension(kind, index);\n\t\tif (current === null) return;\n\t\tconst step: number = event.shiftKey\n\t\t\t? this.config.keyboardResizeLargeStepPx\n\t\t\t: this.config.keyboardResizeStepPx;\n\t\tlet direction: 1 | -1 = event.key === incrementKey ? 1 : -1;\n\t\tif (kind === 'column' && getComputedStyle(this.tableEl).direction === 'rtl') {\n\t\t\tdirection = direction === 1 ? -1 : 1;\n\t\t}\n\t\tconst value: number = Math.round(this.clampDimension(kind, current + step * direction));\n\t\tconst changed: boolean =\n\t\t\tkind === 'column'\n\t\t\t\t? service.setSize(\n\t\t\t\t\t\t{ kind: 'column', tableId: this.tableId, column: index },\n\t\t\t\t\t\t{ columnWidthPx: value },\n\t\t\t\t\t)\n\t\t\t\t: service.setSize(\n\t\t\t\t\t\t{ kind: 'row', tableId: this.tableId, row: index },\n\t\t\t\t\t\t{ rowMinHeightPx: value },\n\t\t\t\t\t);\n\t\tif (!changed) return;\n\t\tthis.restoreKeyboardResizeFocus(separator);\n\t\tthis.pluginContext?.announce(\n\t\t\tkind === 'column'\n\t\t\t\t? this.locale.announceColumnWidthSet(index, value)\n\t\t\t\t: this.locale.announceRowMinimumHeightSet(index, value),\n\t\t);\n\t\tthis.updateSeparatorValue(separator, kind, index, value);\n\t}\n\n\tprivate restoreKeyboardResizeFocus(separator: HTMLButtonElement): void {\n\t\tif (separator.isConnected) separator.focus({ preventScroll: true });\n\t}\n\n\tprivate selectColumn(index: number): void {\n\t\tif (this.readOnly || this.numRows === 0) return;\n\t\tthis.getSelectionService()?.setSelectedRange({\n\t\t\ttableId: this.tableId,\n\t\t\tfromRow: 0,\n\t\t\tfromCol: index,\n\t\t\ttoRow: this.numRows - 1,\n\t\t\ttoCol: index,\n\t\t});\n\t\tthis.updateHandleSelectionState();\n\t}\n\n\tprivate selectRow(index: number): void {\n\t\tif (this.readOnly || this.numCols === 0) return;\n\t\tthis.getSelectionService()?.setSelectedRange({\n\t\t\ttableId: this.tableId,\n\t\t\tfromRow: index,\n\t\t\tfromCol: 0,\n\t\t\ttoRow: index,\n\t\t\ttoCol: this.numCols - 1,\n\t\t});\n\t\tthis.updateHandleSelectionState();\n\t}\n\n\tprivate updateHandleSelectionState(): void {\n\t\tconst range = this.getSelectionService()?.getSelectedRange();\n\t\tfor (let index = 0; index < this.colBar.children.length; index++) {\n\t\t\tconst selected: boolean =\n\t\t\t\t!!range &&\n\t\t\t\trange.tableId === this.tableId &&\n\t\t\t\tMath.min(range.fromCol, range.toCol) === index &&\n\t\t\t\tMath.max(range.fromCol, range.toCol) === index &&\n\t\t\t\tMath.min(range.fromRow, range.toRow) === 0 &&\n\t\t\t\tMath.max(range.fromRow, range.toRow) === this.numRows - 1;\n\t\t\tthis.colBar.children[index]\n\t\t\t\t?.querySelector('.ntbl-handle-select')\n\t\t\t\t?.setAttribute('aria-pressed', selected ? 'true' : 'false');\n\t\t}\n\t\tfor (let index = 0; index < this.rowBar.children.length; index++) {\n\t\t\tconst selected: boolean =\n\t\t\t\t!!range &&\n\t\t\t\trange.tableId === this.tableId &&\n\t\t\t\tMath.min(range.fromRow, range.toRow) === index &&\n\t\t\t\tMath.max(range.fromRow, range.toRow) === index &&\n\t\t\t\tMath.min(range.fromCol, range.toCol) === 0 &&\n\t\t\t\tMath.max(range.fromCol, range.toCol) === this.numCols - 1;\n\t\t\tthis.rowBar.children[index]\n\t\t\t\t?.querySelector('.ntbl-handle-select')\n\t\t\t\t?.setAttribute('aria-pressed', selected ? 'true' : 'false');\n\t\t}\n\t}\n\n\tprivate getSizingService(): TableSizingService | undefined {\n\t\treturn this.pluginContext?.getService?.(TableSizingServiceKey);\n\t}\n\n\tprivate getSelectionService(): TableSelectionService | undefined {\n\t\treturn this.pluginContext?.getService?.(TableSelectionServiceKey);\n\t}\n}\n\n// --- Factory ---\n\n/** Creates table controls for the given table element. */\nexport function createTableControls(\n\tcontainer: HTMLElement,\n\ttableEl: HTMLTableElement,\n\tinitialNode: BlockNode,\n\tgetState: () => EditorState,\n\tdispatch: (tr: Transaction) => void,\n\tpluginContext?: PluginContext,\n\tlocale?: TableLocale,\n\tconfig?: Partial<TableControlsConfig>,\n): TableControlsHandle {\n\treturn new TableControls(\n\t\tcontainer,\n\t\ttableEl,\n\t\tinitialNode,\n\t\tgetState,\n\t\tdispatch,\n\t\tpluginContext,\n\t\tlocale,\n\t\tconfig,\n\t);\n}\n","/**\n * NodeViewFactories for table, table_row, and table_cell.\n * Provides custom DOM rendering with proper HTML table elements and ARIA.\n * The table NodeView includes interactive controls for row/column management.\n */\n\nimport type { BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { blockAttrsEqual, getBlockChildren } from '../../model/Document.js';\nimport type { SchemaRegistry } from '../../model/SchemaRegistry.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport { removeStyleProperty, setStyleProperty } from '../../style/StyleRuntime.js';\nimport type { NodeView, NodeViewFactory } from '../../view/NodeView.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { getDeepActiveHTMLElement } from '../shared/PopupPositioning.js';\nimport { type TableContextMenuHandle, createTableContextMenu } from './TableContextMenu.js';\nimport {\n\ttype TableControlsConfig,\n\ttype TableControlsHandle,\n\tcreateTableControls,\n} from './TableControls.js';\nimport { createTableGrid } from './TableGrid.js';\nimport { findTableContext } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale } from './TableLocale.js';\nimport { TableSelectionServiceKey } from './TableSelection.js';\n\n/** NodeView subset of the table sizing configuration. */\nexport type TableNodeViewSizingConfig = TableControlsConfig;\n\nconst DEFAULT_SIZING_CONFIG: TableNodeViewSizingConfig = {\n\tminColumnWidthPx: 60,\n\tminRowHeightPx: 24,\n\tmaxColumnWidthPx: 10_000,\n\tmaxRowHeightPx: 10_000,\n\tkeyboardResizeStepPx: 8,\n\tkeyboardResizeLargeStepPx: 32,\n\tdirectResize: true,\n};\n\nexport interface TableNodeViewLifecycle {\n\tonControlsCreated?(controls: TableControlsHandle): void;\n\tonControlsDestroyed?(controls: TableControlsHandle): void;\n}\n\n/**\n * Creates a NodeViewFactory for the table node type.\n * Renders as outer container with controls + table + tbody.\n */\nexport function createTableNodeViewFactory(\n\t_registry: SchemaRegistry,\n\tpluginContext?: PluginContext,\n\tlocale: TableLocale = TABLE_LOCALE_EN,\n\tsizingConfig: TableNodeViewSizingConfig = DEFAULT_SIZING_CONFIG,\n\tlifecycle: TableNodeViewLifecycle = {},\n): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\tgetState: () => EditorState,\n\t\tdispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tlet currentNode: BlockNode = node;\n\t\t// Outer container: holds controls + wrapper\n\t\tconst container: HTMLDivElement = document.createElement('div');\n\t\tcontainer.className = 'ntbl-container';\n\t\tcontainer.setAttribute('data-block-id', node.id);\n\t\tcontainer.setAttribute('data-selectable', 'true');\n\n\t\t// Table wrapper: provides overflow scrolling\n\t\tconst wrapper: HTMLDivElement = document.createElement('div');\n\t\twrapper.className = 'notectl-table-wrapper';\n\t\twrapper.setAttribute('part', 'table');\n\n\t\tconst table: HTMLTableElement = document.createElement('table');\n\t\ttable.className = 'notectl-table';\n\t\ttable.setAttribute('role', 'table');\n\n\t\tconst colgroup: HTMLTableColElement = document.createElement('colgroup');\n\t\tcolgroup.className = 'notectl-table-columns';\n\t\ttable.appendChild(colgroup);\n\n\t\tconst rows: readonly BlockNode[] = getBlockChildren(node);\n\t\tconst totalRows: number = rows.length;\n\t\tconst totalCols: number = createTableGrid(node).columnCount;\n\t\ttable.setAttribute('aria-label', locale.tableAriaLabel(totalRows, totalCols));\n\t\ttable.setAttribute('aria-description', locale.tableAriaDescription);\n\n\t\tconst tbody: HTMLTableSectionElement = document.createElement('tbody');\n\t\ttable.appendChild(tbody);\n\t\twrapper.appendChild(table);\n\t\tcontainer.appendChild(wrapper);\n\n\t\t// Apply border color CSS variable from node attrs\n\t\tapplyBorderColor(table, node.attrs?.borderColor as string | undefined);\n\t\tapplyColumnDimensions(table, colgroup, node, sizingConfig);\n\n\t\t// Live region for screen reader announcements\n\t\tconst liveRegion: HTMLDivElement = document.createElement('div');\n\t\tliveRegion.className = 'notectl-sr-only';\n\t\tliveRegion.setAttribute('aria-live', 'polite');\n\t\tliveRegion.setAttribute('aria-atomic', 'true');\n\t\tcontainer.appendChild(liveRegion);\n\n\t\t// Initialize interactive controls\n\t\tconst controls: TableControlsHandle = createTableControls(\n\t\t\tcontainer,\n\t\t\ttable,\n\t\t\tnode,\n\t\t\tgetState,\n\t\t\tdispatch,\n\t\t\tpluginContext,\n\t\t\tlocale,\n\t\t\tsizingConfig,\n\t\t);\n\t\tcontrols.setReadOnly(pluginContext?.isReadOnly?.() ?? false);\n\t\tlifecycle.onControlsCreated?.(controls);\n\t\tlet destroyed = false;\n\t\tconst mountedStyleFrame: number = requestAnimationFrame(() => {\n\t\t\tif (destroyed) return;\n\t\t\t// NodeViews are constructed before insertion into the registered style\n\t\t\t// root. Re-applying after mount migrates fallback inline declarations to\n\t\t\t// CSP-safe runtime tokens through StyleRuntime.\n\t\t\tapplyBorderColor(table, currentNode.attrs?.borderColor as string | undefined);\n\t\t\tapplyColumnDimensions(table, colgroup, currentNode, sizingConfig);\n\t\t\tapplyRenderedRowHeights(tbody, currentNode);\n\t\t});\n\n\t\t// Context menu on right-click\n\t\tlet activeContextMenu: TableContextMenuHandle | null = null;\n\t\tlet currentTableId = node.id;\n\n\t\tconst onContextMenu = (e: MouseEvent): void => {\n\t\t\tif (!pluginContext || pluginContext.isReadOnly?.()) return;\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\n\t\t\tactiveContextMenu?.close();\n\n\t\t\tconst rect: DOMRect = new DOMRect(e.clientX, e.clientY, 0, 0);\n\t\t\tconst cell = (e.target as Element | null)?.closest('td[data-block-id]');\n\t\t\tconst cellId = cell?.getAttribute('data-block-id');\n\t\t\tconst tableContext = cellId\n\t\t\t\t? findTableContext(pluginContext.getState(), cellId as BlockId)\n\t\t\t\t: null;\n\t\t\tconst selectedCell: boolean = cellId\n\t\t\t\t? (pluginContext.getService?.(TableSelectionServiceKey)?.isSelected(cellId as BlockId) ??\n\t\t\t\t\tfalse)\n\t\t\t\t: false;\n\t\t\tconst restoreFocusTo: HTMLElement | null =\n\t\t\t\tgetDeepActiveHTMLElement(container) ?? (cell instanceof HTMLElement ? cell : null);\n\t\t\tactiveContextMenu = createTableContextMenu(\n\t\t\t\tcontainer,\n\t\t\t\tpluginContext,\n\t\t\t\tcurrentTableId,\n\t\t\t\trect,\n\t\t\t\t() => {\n\t\t\t\t\tactiveContextMenu = null;\n\t\t\t\t},\n\t\t\t\tlocale,\n\t\t\t\tundefined,\n\t\t\t\t{\n\t\t\t\t\tsizeTarget:\n\t\t\t\t\t\ttableContext && !selectedCell\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tkind: 'cell',\n\t\t\t\t\t\t\t\t\ttableId: tableContext.tableId,\n\t\t\t\t\t\t\t\t\trow: tableContext.rowIndex,\n\t\t\t\t\t\t\t\t\tcolumn: tableContext.colIndex,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tsizingConfig,\n\t\t\t\t\trestoreFocusTo,\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\t\ttable.addEventListener('contextmenu', onContextMenu);\n\n\t\treturn {\n\t\t\tdom: container,\n\t\t\tcontentDOM: tbody,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table') return false;\n\t\t\t\tconst canUpdateInPlace: boolean = tableContentIsStable(currentNode, updatedNode);\n\t\t\t\tif (!canUpdateInPlace) return false;\n\n\t\t\t\tcurrentTableId = updatedNode.id;\n\t\t\t\tcontainer.setAttribute('data-block-id', updatedNode.id);\n\t\t\t\tcontainer.setAttribute('data-selectable', 'true');\n\t\t\t\tconst updatedRows: readonly BlockNode[] = getBlockChildren(updatedNode);\n\t\t\t\tconst newTotalRows: number = updatedRows.length;\n\t\t\t\tconst newTotalCols: number = createTableGrid(updatedNode).columnCount;\n\t\t\t\ttable.setAttribute('aria-label', locale.tableAriaLabel(newTotalRows, newTotalCols));\n\n\t\t\t\t// Update border color CSS variable\n\t\t\t\tapplyBorderColor(table, updatedNode.attrs?.borderColor as string | undefined);\n\t\t\t\tapplyColumnDimensions(table, colgroup, updatedNode, sizingConfig);\n\t\t\t\tapplyRenderedRowHeights(tbody, updatedNode);\n\n\t\t\t\t// Update controls to reflect new structure\n\t\t\t\tcontrols.update(updatedNode);\n\t\t\t\tcurrentNode = updatedNode;\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\tdestroyed = true;\n\t\t\t\tcancelAnimationFrame(mountedStyleFrame);\n\t\t\t\tactiveContextMenu?.destroy();\n\t\t\t\ttable.removeEventListener('contextmenu', onContextMenu);\n\t\t\t\tlifecycle.onControlsDestroyed?.(controls);\n\t\t\t\tcontrols.destroy();\n\t\t\t},\n\t\t\tselectNode(): void {\n\t\t\t\tcontainer.classList.add('notectl-table--selected');\n\t\t\t},\n\t\t\tdeselectNode(): void {\n\t\t\t\tcontainer.classList.remove('notectl-table--selected');\n\t\t\t},\n\t\t};\n\t};\n}\n\n/** Applies border color CSS variable and borderless class to a table element. */\nfunction applyBorderColor(table: HTMLTableElement, borderColor: string | undefined): void {\n\tif (borderColor === 'none') {\n\t\tsetStyleProperty(table, '--ntbl-border-color', 'transparent');\n\t\ttable.classList.add('notectl-table--borderless');\n\t} else if (borderColor) {\n\t\tsetStyleProperty(table, '--ntbl-border-color', borderColor);\n\t\ttable.classList.remove('notectl-table--borderless');\n\t} else {\n\t\tremoveStyleProperty(table, '--ntbl-border-color');\n\t\ttable.classList.remove('notectl-table--borderless');\n\t}\n}\n\n/**\n * Creates a NodeViewFactory for the table_row node type.\n * Renders as `<tr role=\"row\">`.\n */\nexport function createTableRowNodeViewFactory(_registry: SchemaRegistry): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\t_getState: () => EditorState,\n\t\t_dispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tlet currentNode: BlockNode = node;\n\t\tconst tr: HTMLTableRowElement = document.createElement('tr');\n\t\ttr.setAttribute('data-block-id', node.id);\n\t\ttr.setAttribute('role', 'row');\n\t\ttr.setAttribute('part', 'table-row');\n\t\tapplyRowMinimumHeight(tr, node);\n\t\tlet destroyed = false;\n\t\tconst mountedStyleFrame: number = requestAnimationFrame(() => {\n\t\t\tif (!destroyed) applyRowMinimumHeight(tr, currentNode);\n\t\t});\n\n\t\treturn {\n\t\t\tdom: tr,\n\t\t\tcontentDOM: tr,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table_row') return false;\n\t\t\t\tif (updatedNode.children !== currentNode.children) return false;\n\t\t\t\ttr.setAttribute('data-block-id', updatedNode.id);\n\t\t\t\tapplyRowMinimumHeight(tr, updatedNode);\n\t\t\t\tcurrentNode = updatedNode;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\tdestroyed = true;\n\t\t\t\tcancelAnimationFrame(mountedStyleFrame);\n\t\t\t},\n\t\t};\n\t};\n}\n\n/** Applies canonical widths through a semantic `<colgroup>` and table minimum width. */\nexport function applyColumnDimensions(\n\ttable: HTMLTableElement,\n\tcolgroup: HTMLTableColElement,\n\tnode: BlockNode,\n\tconfig: TableNodeViewSizingConfig = DEFAULT_SIZING_CONFIG,\n): void {\n\tconst columnCount: number = createTableGrid(node).columnCount;\n\tsetStyleProperty(table, '--ntbl-min-column-width', `${String(config.minColumnWidthPx)}px`);\n\twhile (colgroup.children.length > columnCount) colgroup.lastElementChild?.remove();\n\twhile (colgroup.children.length < columnCount) {\n\t\tcolgroup.appendChild(document.createElement('col'));\n\t}\n\n\tconst rawWidths: unknown = node.attrs?.columnWidthsPx;\n\tconst widths: readonly unknown[] = Array.isArray(rawWidths) ? rawWidths : [];\n\tlet minimumTableWidth = 0;\n\tfor (let column = 0; column < columnCount; column++) {\n\t\tconst col = colgroup.children[column] as HTMLTableColElement | undefined;\n\t\tif (!col) continue;\n\t\tcol.removeAttribute('width');\n\t\tconst rawWidth: unknown = widths[column];\n\t\tconst explicitWidth: number | undefined =\n\t\t\ttypeof rawWidth === 'number' && Number.isFinite(rawWidth) && rawWidth > 0\n\t\t\t\t? rawWidth\n\t\t\t\t: undefined;\n\t\tif (explicitWidth !== undefined) {\n\t\t\tsetStyleProperty(col, 'width', `${String(explicitWidth)}px`);\n\t\t\tcol.setAttribute('data-notectl-width-px', String(explicitWidth));\n\t\t} else {\n\t\t\tremoveStyleProperty(col, 'width');\n\t\t\tcol.removeAttribute('data-notectl-width-px');\n\t\t}\n\t\tminimumTableWidth += explicitWidth ?? config.minColumnWidthPx;\n\t}\n\tif (columnCount > 0) {\n\t\tsetStyleProperty(table, 'minWidth', `${String(minimumTableWidth)}px`);\n\t\tif (\n\t\t\twidths.length >= columnCount &&\n\t\t\twidths.slice(0, columnCount).every((width) => typeof width === 'number')\n\t\t) {\n\t\t\tsetStyleProperty(table, 'width', `${String(minimumTableWidth)}px`);\n\t\t} else {\n\t\t\tremoveStyleProperty(table, 'width');\n\t\t}\n\t} else {\n\t\tremoveStyleProperty(table, 'minWidth');\n\t\tremoveStyleProperty(table, 'width');\n\t}\n}\n\n/** Applies a row's table-semantic minimum height (`height` grows with content). */\nexport function applyRowMinimumHeight(row: HTMLTableRowElement, node: BlockNode): void {\n\tconst rawHeight: unknown = node.attrs?.minHeightPx;\n\trow.removeAttribute('height');\n\tif (typeof rawHeight === 'number' && Number.isFinite(rawHeight) && rawHeight > 0) {\n\t\tsetStyleProperty(row, 'height', `${String(rawHeight)}px`);\n\t\trow.setAttribute('data-notectl-min-height-px', String(rawHeight));\n\t} else {\n\t\tremoveStyleProperty(row, 'height');\n\t\trow.removeAttribute('data-notectl-min-height-px');\n\t}\n}\n\nfunction applyRenderedRowHeights(tbody: HTMLTableSectionElement, tableNode: BlockNode): void {\n\tconst renderedRows: readonly HTMLTableRowElement[] = Array.from(\n\t\ttbody.querySelectorAll(':scope > tr'),\n\t);\n\tfor (const rowNode of getBlockChildren(tableNode)) {\n\t\tconst row = renderedRows.find(\n\t\t\t(candidate) => candidate.getAttribute('data-block-id') === rowNode.id,\n\t\t);\n\t\tif (row) applyRowMinimumHeight(row, rowNode);\n\t}\n}\n\n/** True when only table sizing/border attrs or row minimum heights changed. */\nfunction tableContentIsStable(previous: BlockNode, next: BlockNode): boolean {\n\tif (previous.id !== next.id || previous.htmlId !== next.htmlId) return false;\n\tif (!blockAttrsEqual(withoutTableViewAttrs(previous.attrs), withoutTableViewAttrs(next.attrs))) {\n\t\treturn false;\n\t}\n\tconst previousRows: readonly BlockNode[] = getBlockChildren(previous);\n\tconst nextRows: readonly BlockNode[] = getBlockChildren(next);\n\tif (previousRows.length !== nextRows.length) return false;\n\treturn previousRows.every((previousRow, index) => {\n\t\tconst nextRow: BlockNode | undefined = nextRows[index];\n\t\treturn (\n\t\t\tnextRow !== undefined &&\n\t\t\tpreviousRow.id === nextRow.id &&\n\t\t\tpreviousRow.htmlId === nextRow.htmlId &&\n\t\t\tpreviousRow.children === nextRow.children &&\n\t\t\tblockAttrsEqual(withoutRowHeight(previousRow.attrs), withoutRowHeight(nextRow.attrs))\n\t\t);\n\t});\n}\n\nfunction withoutTableViewAttrs(attrs: BlockAttrs | undefined): BlockAttrs | undefined {\n\tif (!attrs) return undefined;\n\tconst { borderColor: _borderColor, columnWidthsPx: _columnWidthsPx, ...rest } = attrs;\n\treturn Object.keys(rest).length > 0 ? rest : undefined;\n}\n\nfunction withoutRowHeight(attrs: BlockAttrs | undefined): BlockAttrs | undefined {\n\tif (!attrs) return undefined;\n\tconst { minHeightPx: _minHeightPx, ...rest } = attrs;\n\treturn Object.keys(rest).length > 0 ? rest : undefined;\n}\n\n/**\n * Creates a NodeViewFactory for the table_cell node type.\n * Renders as `<td role=\"cell\">` with text content rendered inside.\n */\nexport function createTableCellNodeViewFactory(_registry: SchemaRegistry): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\t_getState: () => EditorState,\n\t\t_dispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\tconst td: HTMLTableCellElement = document.createElement('td');\n\t\ttd.setAttribute('data-block-id', node.id);\n\t\ttd.setAttribute('role', 'cell');\n\t\ttd.setAttribute('part', 'table-cell');\n\n\t\tconst colspan: number = (node.attrs?.colspan as number | undefined) ?? 1;\n\t\tconst rowspan: number = (node.attrs?.rowspan as number | undefined) ?? 1;\n\t\tif (colspan > 1) td.colSpan = colspan;\n\t\tif (rowspan > 1) td.rowSpan = rowspan;\n\n\t\t// Apply alignment from AlignmentPlugin's patched attribute\n\t\tconst align: string | undefined = node.attrs?.align as string | undefined;\n\t\tif (align && align !== 'left') {\n\t\t\tsetStyleProperty(td, 'textAlign', align);\n\t\t}\n\n\t\treturn {\n\t\t\tdom: td,\n\t\t\tcontentDOM: td,\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'table_cell') return false;\n\t\t\t\ttd.setAttribute('data-block-id', updatedNode.id);\n\n\t\t\t\tconst newColspan: number = (updatedNode.attrs?.colspan as number | undefined) ?? 1;\n\t\t\t\tconst newRowspan: number = (updatedNode.attrs?.rowspan as number | undefined) ?? 1;\n\t\t\t\tif (newColspan > 1) {\n\t\t\t\t\ttd.colSpan = newColspan;\n\t\t\t\t} else {\n\t\t\t\t\ttd.removeAttribute('colspan');\n\t\t\t\t}\n\t\t\t\tif (newRowspan > 1) {\n\t\t\t\t\ttd.rowSpan = newRowspan;\n\t\t\t\t} else {\n\t\t\t\t\ttd.removeAttribute('rowspan');\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\tdestroy(): void {\n\t\t\t\t// No cleanup needed\n\t\t\t},\n\t\t};\n\t};\n}\n","/**\n * TablePlugin: registers table, table_row, and table_cell node types\n * with NodeSpecs, NodeViews, commands, keyboard navigation, toolbar\n * grid picker, multi-cell selection, and context menu.\n */\n\nimport type { Decoration, DecorationSet } from '../../decorations/Decoration.js';\nimport { node as nodeDecoration } from '../../decorations/Decoration.js';\nimport { DecorationSet as DecorationSetClass } from '../../decorations/Decoration.js';\nimport { TABLE_CSS } from '../../editor/styles/table.js';\nimport type { BlockAttrValue, BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { getBlockChildren } from '../../model/Document.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport type { HTMLExportContext } from '../../model/NodeSpec.js';\nimport { isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport {\n\tTABLE_COLUMN_WIDTH_DATA_ATTRIBUTE,\n\tTABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE,\n\tnormalizeSerializedTableDimensionPx,\n\tserializeTableDimensionAttrs,\n} from '../../serialization/TableDimensions.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isValidHexColor } from '../shared/ColorValidation.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { resetTableBorderColor } from './TableBorderColor.js';\nimport { installTableClipboard } from './TableClipboard.js';\nimport { insertTable, registerTableCommands } from './TableCommands.js';\nimport { closeTableContextMenus } from './TableContextMenu.js';\nimport type { TableControlsHandle } from './TableControls.js';\nimport {\n\tcreateTableGrid,\n\tnormalizeTableSpan,\n\tprojectTableColumnWidthsForSlice,\n} from './TableGrid.js';\nimport { isInsideTable } from './TableHelpers.js';\nimport { TABLE_LOCALE_EN, type TableLocale, loadTableLocale } from './TableLocale.js';\nimport { registerTableKeymaps } from './TableNavigation.js';\nimport {\n\tcreateTableCellNodeViewFactory,\n\tcreateTableNodeViewFactory,\n\tcreateTableRowNodeViewFactory,\n} from './TableNodeViews.js';\nimport {\n\ttype TableSelectionService,\n\tcreateTableSelectionService,\n\tinstallMouseSelection,\n} from './TableSelection.js';\nimport {\n\tDEFAULT_TABLE_SIZING_CONFIG,\n\ttype TableSizingConfig,\n\tcreateTableSizingService,\n\treadTableColumnWidthsPx,\n\tresolveTableSizingConfig,\n} from './TableSizing.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\ttable: { borderColor?: string; columnWidthsPx?: readonly (number | null)[] };\n\t\ttable_row: { minHeightPx?: number };\n\t\ttable_cell: { colspan?: number; rowspan?: number };\n\t}\n}\n\n// --- Configuration ---\n\nexport interface TableConfig {\n\t/** Maximum rows in grid picker. Defaults to 8. */\n\treadonly maxPickerRows?: number;\n\t/** Maximum columns in grid picker. Defaults to 8. */\n\treadonly maxPickerCols?: number;\n\t/** Locale for all user-facing strings. Defaults to English. */\n\treadonly locale?: TableLocale;\n\t/** Minimum logical column width in CSS pixels. Defaults to 60. */\n\treadonly minColumnWidthPx?: number;\n\t/** Minimum logical row height in CSS pixels. Defaults to 24. */\n\treadonly minRowHeightPx?: number;\n\t/** Maximum accepted logical column width in CSS pixels. Defaults to 10000. */\n\treadonly maxColumnWidthPx?: number;\n\t/** Maximum accepted logical row height in CSS pixels. Defaults to 10000. */\n\treadonly maxRowHeightPx?: number;\n\t/** Small keyboard resize step in CSS pixels. Defaults to 8. */\n\treadonly keyboardResizeStepPx?: number;\n\t/** Shift-modified keyboard resize step in CSS pixels. Defaults to 32. */\n\treadonly keyboardResizeLargeStepPx?: number;\n\t/** Enables pointer and separator-key resizing. Precise UI/API remain available. Defaults to true. */\n\treadonly directResize?: boolean;\n}\n\ninterface ResolvedTableConfig extends TableSizingConfig {\n\treadonly maxPickerRows: number;\n\treadonly maxPickerCols: number;\n\treadonly locale?: TableLocale;\n\treadonly directResize: boolean;\n}\n\nconst DEFAULT_CONFIG: ResolvedTableConfig = {\n\tmaxPickerRows: 8,\n\tmaxPickerCols: 8,\n\t...DEFAULT_TABLE_SIZING_CONFIG,\n\tdirectResize: true,\n};\n\n// --- SVG Icon ---\n\nconst TABLE_ICON =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">' +\n\t'<path d=\"M3 3h18v18H3V3zm2 2v4h6V5H5zm8 0v4h6V5h-6zm-8 6v4h6v-4H5z' +\n\t'm8 0v4h6v-4h-6zm-8 6v4h6v-4H5zm8 0v4h6v-4h-6z\"/></svg>';\n\n// --- Serialization Constants ---\n\nconst DEFAULT_BORDER_COLOR = '#d0d0d0';\nconst TABLE_BASE_STYLE = 'border-collapse: collapse; table-layout: fixed';\nconst CELL_STYLE = `border: 1px solid var(--ntbl-bc, ${DEFAULT_BORDER_COLOR}); padding: 8px 12px; vertical-align: top`;\n\n// --- Serialization Helpers ---\n\n/** Builds the inline `style` attribute value for the `<table>` element. */\nfunction buildTableStyle(\n\tborderColor: string | undefined,\n\twidths: readonly (number | null)[],\n\tminimumColumnWidthPx: number,\n): string {\n\tconst minimumWidthPx: number = widths.reduce<number>(\n\t\t(total, width) => total + (width ?? minimumColumnWidthPx),\n\t\t0,\n\t);\n\tconst allColumnsExplicit: boolean =\n\t\twidths.length > 0 && widths.every((width): width is number => width !== null);\n\tconst widthStyle: string = allColumnsExplicit\n\t\t? `width: ${String(minimumWidthPx)}px`\n\t\t: 'width: 100%';\n\tconst baseStyle = `${TABLE_BASE_STYLE}; ${widthStyle}; min-width: ${String(minimumWidthPx)}px`;\n\tif (!borderColor) return baseStyle;\n\tif (borderColor === 'none') {\n\t\treturn `${baseStyle}; --ntbl-bc: transparent`;\n\t}\n\tif (isValidHexColor(borderColor)) {\n\t\treturn `${baseStyle}; --ntbl-bc: ${escapeHTML(borderColor)}`;\n\t}\n\treturn baseStyle;\n}\n\n// --- Plugin ---\n\nexport class TablePlugin implements Plugin {\n\treadonly id = 'table';\n\treadonly name = 'Table';\n\treadonly priority = 40;\n\n\tprivate readonly config: ResolvedTableConfig;\n\tprivate locale!: TableLocale;\n\tprivate selectionService: TableSelectionService | null = null;\n\tprivate cleanupMouseSelection: (() => void) | null = null;\n\tprivate cleanupClipboard: (() => void) | null = null;\n\tprivate context: PluginContext | null = null;\n\tprivate readonly controls: Set<TableControlsHandle> = new Set();\n\n\tconstructor(config?: Partial<TableConfig>) {\n\t\tconst sizing: TableSizingConfig = resolveTableSizingConfig(config ?? {});\n\t\tthis.config = {\n\t\t\t...DEFAULT_CONFIG,\n\t\t\t...config,\n\t\t\t...sizing,\n\t\t\tmaxPickerRows: normalizePickerSize(config?.maxPickerRows, DEFAULT_CONFIG.maxPickerRows),\n\t\t\tmaxPickerCols: normalizePickerSize(config?.maxPickerCols, DEFAULT_CONFIG.maxPickerCols),\n\t\t\tdirectResize: config?.directResize ?? true,\n\t\t};\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tTABLE_LOCALE_EN,\n\t\t\tloadTableLocale,\n\t\t);\n\t\tcontext.registerStyleSheet(TABLE_CSS);\n\t\tthis.context = context;\n\n\t\tthis.registerNodeSpecs(context);\n\t\tthis.registerNodeViews(context);\n\t\tregisterTableCommands(context, this.locale);\n\t\tcontext.registerCommand('resetTableBorderColor', () =>\n\t\t\tresetTableBorderColor(context, this.locale),\n\t\t);\n\t\tregisterTableKeymaps(context, this.locale, this.config);\n\t\tthis.registerToolbarItem(context);\n\t\tthis.selectionService = createTableSelectionService(context);\n\t\tcreateTableSizingService(context, this.config);\n\t}\n\n\tonReady(): void {\n\t\tif (this.context && this.selectionService) {\n\t\t\tthis.cleanupMouseSelection = installMouseSelection(this.context, this.selectionService);\n\t\t\tthis.cleanupClipboard = installTableClipboard(this.context, this.selectionService);\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tif (this.context) closeTableContextMenus(this.context);\n\t\tthis.cleanupMouseSelection?.();\n\t\tthis.cleanupMouseSelection = null;\n\t\tthis.cleanupClipboard?.();\n\t\tthis.cleanupClipboard = null;\n\t\tthis.selectionService = null;\n\t\tthis.controls.clear();\n\t\tthis.context = null;\n\t}\n\n\tonReadOnlyChange(readonly: boolean): void {\n\t\tfor (const controls of this.controls) controls.setReadOnly(readonly);\n\t\tif (readonly && this.context) closeTableContextMenus(this.context);\n\t}\n\n\tdecorations(state: EditorState): DecorationSet {\n\t\tif (!this.selectionService) return DecorationSetClass.empty;\n\n\t\tconst range = this.selectionService.getSelectedRange();\n\t\tif (!range) return DecorationSetClass.empty;\n\n\t\t// Side effect: clear stale selection when cursor leaves the table.\n\t\t// Uses clearSelectionSilent() (no dispatch) to avoid re-entrancy,\n\t\t// since decorations() runs inside the update cycle.\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel) || !isInsideTable(state, sel.anchor.blockId)) {\n\t\t\tthis.selectionService.clearSelectionSilent();\n\t\t\treturn DecorationSetClass.empty;\n\t\t}\n\n\t\tconst cellIds: readonly BlockId[] = this.selectionService.getSelectedCellIds();\n\t\tif (cellIds.length === 0) return DecorationSetClass.empty;\n\n\t\tconst decorations: Decoration[] = [];\n\t\tfor (const cellId of cellIds) {\n\t\t\tdecorations.push(nodeDecoration(cellId, { class: 'notectl-table-cell--selected' }));\n\t\t}\n\n\t\treturn DecorationSetClass.create(decorations);\n\t}\n\n\tprivate registerNodeSpecs(context: PluginContext): void {\n\t\tconst config: ResolvedTableConfig = this.config;\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table',\n\t\t\tattrs: {\n\t\t\t\tcolumnWidthsPx: { allowArray: true },\n\t\t\t},\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['table_row'], min: 1 },\n\t\t\tisolating: true,\n\t\t\tselectable: true,\n\t\t\tnormalizeAttrs: (node: BlockNode) => normalizeTableAttrs(node, this.config),\n\t\t\tnormalizeNode: normalizeTableStructure,\n\t\t\ttransformSelectionSlice: projectTableColumnWidthsForSlice,\n\t\t\ttoDOM(node) {\n\t\t\t\tconst wrapper: HTMLDivElement = document.createElement('div');\n\t\t\t\twrapper.className = 'notectl-table-wrapper';\n\t\t\t\twrapper.setAttribute('data-block-id', node.id);\n\t\t\t\twrapper.setAttribute('part', 'table');\n\t\t\t\treturn wrapper;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst borderColor: string | undefined = node.attrs?.borderColor as string | undefined;\n\t\t\t\tconst grid = createTableGrid(node);\n\t\t\t\tconst widths = readTableColumnWidthsPx(node, grid.columnCount);\n\t\t\t\tconst style: string = buildTableStyle(borderColor, widths, config.minColumnWidthPx);\n\t\t\t\tconst attr: string = ctx?.styleAttr(style) ?? ` style=\"${style}\"`;\n\t\t\t\tconst columns: string = widths\n\t\t\t\t\t.map((width) =>\n\t\t\t\t\t\twidth === null\n\t\t\t\t\t\t\t? '<col>'\n\t\t\t\t\t\t\t: `<col${serializeTableDimensionAttrs(TABLE_COLUMN_WIDTH_DATA_ATTRIBUTE, 'width', width, ctx)}>`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('');\n\t\t\t\treturn `<table${attr}><colgroup>${columns}</colgroup><tbody>${content}</tbody></table>`;\n\t\t\t},\n\t\t\tsanitize: {\n\t\t\t\ttags: ['table', 'colgroup', 'col', 'thead', 'tbody', 'tfoot'],\n\t\t\t\tattrs: [TABLE_COLUMN_WIDTH_DATA_ATTRIBUTE, 'width', 'span'],\n\t\t\t},\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table_row',\n\t\t\tgroup: 'table_content',\n\t\t\tcontent: { allow: ['table_cell'], min: 0 },\n\t\t\tnormalizeAttrs: (node: BlockNode) => normalizeTableRowAttrs(node, this.config),\n\t\t\ttoDOM(node) {\n\t\t\t\tconst tr: HTMLTableRowElement = document.createElement('tr');\n\t\t\t\ttr.setAttribute('data-block-id', node.id);\n\t\t\t\ttr.setAttribute('role', 'row');\n\t\t\t\ttr.setAttribute('part', 'table-row');\n\t\t\t\treturn tr;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst attrs: string = serializeTableDimensionAttrs(\n\t\t\t\t\tTABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE,\n\t\t\t\t\t'height',\n\t\t\t\t\tnode.attrs?.minHeightPx,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t\treturn `<tr${attrs}>${content}</tr>`;\n\t\t\t},\n\t\t\tsanitize: {\n\t\t\t\ttags: ['tr'],\n\t\t\t\tattrs: [TABLE_ROW_MIN_HEIGHT_DATA_ATTRIBUTE, 'height'],\n\t\t\t},\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'table_cell',\n\t\t\tgroup: 'table_content',\n\t\t\tcontent: {\n\t\t\t\tallow: [\n\t\t\t\t\t'paragraph',\n\t\t\t\t\t'list_item',\n\t\t\t\t\t'heading',\n\t\t\t\t\t'blockquote',\n\t\t\t\t\t'image',\n\t\t\t\t\t'video',\n\t\t\t\t\t'horizontal_rule',\n\t\t\t\t],\n\t\t\t\tmin: 1,\n\t\t\t},\n\t\t\tisolating: true,\n\t\t\tnormalizeAttrs: normalizeTableCellAttrs,\n\t\t\ttoDOM(node) {\n\t\t\t\tconst td: HTMLTableCellElement = document.createElement('td');\n\t\t\t\ttd.setAttribute('data-block-id', node.id);\n\t\t\t\ttd.setAttribute('role', 'cell');\n\t\t\t\ttd.setAttribute('part', 'table-cell');\n\t\t\t\treturn td;\n\t\t\t},\n\t\t\ttoHTML(node, content, ctx?: HTMLExportContext) {\n\t\t\t\tconst colspan: number = (node.attrs?.colspan as number) ?? 1;\n\t\t\t\tconst rowspan: number = (node.attrs?.rowspan as number) ?? 1;\n\t\t\t\tconst styleAttr: string = ctx?.styleAttr(CELL_STYLE) ?? ` style=\"${CELL_STYLE}\"`;\n\t\t\t\tconst attrs: string[] = [styleAttr];\n\t\t\t\tif (colspan > 1) attrs.push(` colspan=\"${colspan}\"`);\n\t\t\t\tif (rowspan > 1) attrs.push(` rowspan=\"${rowspan}\"`);\n\t\t\t\treturn `<td${attrs.join('')}>${content}</td>`;\n\t\t\t},\n\t\t\tsanitize: { tags: ['td', 'th'], attrs: ['colspan', 'rowspan'] },\n\t\t});\n\t}\n\n\tprivate registerNodeViews(context: PluginContext): void {\n\t\tconst registry = context.getSchemaRegistry();\n\n\t\tcontext.registerNodeView(\n\t\t\t'table',\n\t\t\tcreateTableNodeViewFactory(registry, context, this.locale, this.config, {\n\t\t\t\tonControlsCreated: (controls: TableControlsHandle): void => {\n\t\t\t\t\tthis.controls.add(controls);\n\t\t\t\t},\n\t\t\t\tonControlsDestroyed: (controls: TableControlsHandle): void => {\n\t\t\t\t\tthis.controls.delete(controls);\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tcontext.registerNodeView('table_row', createTableRowNodeViewFactory(registry));\n\t\tcontext.registerNodeView('table_cell', createTableCellNodeViewFactory(registry));\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst maxRows: number = this.config.maxPickerRows ?? 8;\n\t\tconst maxCols: number = this.config.maxPickerCols ?? 8;\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'table',\n\t\t\tgroup: 'insert',\n\t\t\ticon: TABLE_ICON,\n\t\t\tlabel: this.locale.insertTable,\n\t\t\ttooltip: this.locale.insertTable,\n\t\t\tcommand: 'insertTable',\n\t\t\tpopupType: 'gridPicker',\n\t\t\tpopupConfig: {\n\t\t\t\tmaxRows,\n\t\t\t\tmaxCols,\n\t\t\t\tonSelect: (rows: number, cols: number) => {\n\t\t\t\t\tinsertTable(context, rows, cols);\n\t\t\t\t},\n\t\t\t},\n\t\t\tisActive: (state: EditorState) => {\n\t\t\t\tif (!isTextSelection(state.selection)) return false;\n\t\t\t\treturn isInsideTable(state, state.selection.anchor.blockId);\n\t\t\t},\n\t\t});\n\t}\n}\n\nfunction normalizePickerSize(value: unknown, fallback: number): number {\n\treturn typeof value === 'number' && Number.isInteger(value) && value > 0\n\t\t? Math.min(value, 100)\n\t\t: fallback;\n}\n\nfunction normalizeTableAttrs(node: BlockNode, config: TableSizingConfig): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, [\n\t\t'borderColor',\n\t\t'columnWidthsPx',\n\t]);\n\tconst borderColor: unknown = node.attrs?.borderColor;\n\tif (borderColor === 'none' || (typeof borderColor === 'string' && isValidHexColor(borderColor))) {\n\t\tattrs.borderColor = borderColor;\n\t}\n\n\tconst grid = createTableGrid(node);\n\tconst rawWidths: unknown = node.attrs?.columnWidthsPx;\n\tif (Array.isArray(rawWidths) && grid.columnCount > 0) {\n\t\tconst widths: (number | null)[] = Array.from(\n\t\t\t{ length: grid.columnCount },\n\t\t\t(_unused, column): number | null => {\n\t\t\t\tconst parsed: number | undefined = normalizeSerializedTableDimensionPx(rawWidths[column]);\n\t\t\t\tif (parsed === undefined) return null;\n\t\t\t\treturn Math.min(config.maxColumnWidthPx, Math.max(config.minColumnWidthPx, parsed));\n\t\t\t},\n\t\t);\n\t\tif (widths.some((width) => width !== null)) {\n\t\t\tattrs.columnWidthsPx = Object.freeze(widths);\n\t\t}\n\t}\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\nfunction normalizeTableRowAttrs(\n\tnode: BlockNode,\n\tconfig: TableSizingConfig,\n): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, ['minHeightPx']);\n\tconst parsed: number | undefined = normalizeSerializedTableDimensionPx(node.attrs?.minHeightPx);\n\tif (parsed !== undefined) {\n\t\tattrs.minHeightPx = Math.min(config.maxRowHeightPx, Math.max(config.minRowHeightPx, parsed));\n\t}\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\nfunction normalizeTableCellAttrs(node: BlockNode): BlockAttrs | undefined {\n\tconst attrs: MutableBlockAttrs = copyUnrelatedAttrs(node.attrs, ['colspan', 'rowspan']);\n\tconst colspan: number = normalizeTableSpan(node.attrs?.colspan);\n\tconst rowspan: number = normalizeTableSpan(node.attrs?.rowspan);\n\tif (colspan > 1) attrs.colspan = colspan;\n\tif (rowspan > 1) attrs.rowspan = rowspan;\n\treturn Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n\n/** Clamps row spans to the rows that actually exist, preventing latent spans after insertions. */\nfunction normalizeTableStructure(table: BlockNode): BlockNode {\n\tconst rows: readonly BlockNode[] = getBlockChildren(table);\n\tlet changed = false;\n\tconst normalizedRows: readonly BlockNode[] = rows.map((row: BlockNode, rowIndex: number) => {\n\t\tlet rowChanged = false;\n\t\tconst cells: readonly BlockNode[] = getBlockChildren(row).map((cell: BlockNode) => {\n\t\t\tconst requested: number = normalizeTableSpan(cell.attrs?.rowspan);\n\t\t\tconst effective: number = Math.min(requested, rows.length - rowIndex);\n\t\t\tif (effective === requested) return cell;\n\t\t\trowChanged = true;\n\t\t\tconst { rowspan: _rowspan, ...rest } = cell.attrs ?? {};\n\t\t\tconst attrs: MutableBlockAttrs = {\n\t\t\t\t...rest,\n\t\t\t\t...(effective > 1 ? { rowspan: effective } : {}),\n\t\t\t};\n\t\t\tconst { attrs: _attrs, ...withoutAttrs } = cell;\n\t\t\treturn Object.keys(attrs).length > 0 ? { ...withoutAttrs, attrs } : withoutAttrs;\n\t\t});\n\t\tif (!rowChanged) return row;\n\t\tchanged = true;\n\t\treturn { ...row, children: cells };\n\t});\n\treturn changed ? { ...table, children: normalizedRows } : table;\n}\n\nfunction copyUnrelatedAttrs(\n\tattrs: BlockAttrs | undefined,\n\texcluded: readonly string[],\n): MutableBlockAttrs {\n\tconst result: MutableBlockAttrs = {};\n\tfor (const [key, value] of Object.entries(attrs ?? {})) {\n\t\tif (excluded.includes(key)) continue;\n\t\tconst normalized: BlockAttrValue | undefined = cloneBlockAttrValue(value);\n\t\tif (normalized !== undefined) result[key] = normalized;\n\t}\n\treturn result;\n}\n\nfunction cloneBlockAttrValue(value: unknown): BlockAttrValue | undefined {\n\tif (typeof value === 'string' || typeof value === 'boolean') return value;\n\tif (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n\tif (!Array.isArray(value)) return undefined;\n\tconst normalized: (string | number | boolean | null)[] = [];\n\tfor (const entry of value) {\n\t\tif (entry === null || typeof entry === 'string' || typeof entry === 'boolean') {\n\t\t\tnormalized.push(entry);\n\t\t} else if (typeof entry === 'number' && Number.isFinite(entry)) {\n\t\t\tnormalized.push(entry);\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn Object.freeze(normalized);\n}\n\ntype MutableBlockAttrs = Record<string, BlockAttrValue>;\n"],"mappings":"w7BCoDA,SAAgB,EACf,EACA,GAEA,GAAsB,IAAlB,EAAK,UAAuC,IAArB,EAAK,YAAmB,OAAO,KAC1D,IAAI,EAA2B,CAC9B,QAAS,EAAgB,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,UACpE,WAAY,EAAgB,KAAK,IAAI,EAAM,WAAY,EAAM,UAAW,EAAK,aAC7E,MAAO,EAAgB,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,UAClE,SAAU,EAAgB,KAAK,IAAI,EAAM,WAAY,EAAM,UAAW,EAAK,cAE5E,OAAa,CACZ,IAAI,EAAkB,EAAS,QAC3B,EAAqB,EAAS,WAC9B,EAAgB,EAAS,MACzB,EAAmB,EAAS,SAChC,IAAK,MAAM,KAAS,EAAK,aAAa,GACrC,EAAU,KAAK,IAAI,EAAS,EAAM,UAClC,EAAa,KAAK,IAAI,EAAY,EAAM,aACxC,EAAQ,KAAK,IAAI,EAAO,EAAM,OAAS,GACvC,EAAW,KAAK,IAAI,EAAU,EAAM,UAAY,GAEjD,GACC,IAAY,EAAS,SACrB,IAAe,EAAS,YACxB,IAAU,EAAS,OACnB,IAAa,EAAS,SAEtB,OAAO,EAER,EAAW,CAAE,UAAS,aAAY,QAAO,WAC1C,CACD,CAGA,SAAgB,EAAmB,EAAgB,EA3ErB,KA4E7B,MAAqB,iBAAV,GAAuB,OAAO,SAAS,IAAW,OAAO,UAAU,GAC1E,GAAS,EAAU,EAChB,KAAK,IAAI,EAAO,GAFsE,CAG9F,CAGA,SAAgB,EAAgB,GAC/B,MAAM,EACU,UAAf,EAAM,KAAmB,EAAiB,GAAO,OAAQ,GAAqB,cAAb,EAAI,MAAwB,GACxF,EAAyC,MAAM,KAAK,CAAE,OAAQ,EAAK,QAAO,IAAS,IACnF,EAAyB,GACzB,iBAAO,IAAI,IACjB,IAAI,EAAc,EAElB,IAAK,IAAI,EAAW,EAAG,EAAW,EAAK,OAAQ,IAAY,CAC1D,MAAM,EAAmC,EAAK,GAC9C,IAAK,EAAW,SAChB,MAAM,EAAoC,EAAiB,GAAW,OACpE,GAAuB,eAAd,EAAK,MAEhB,IAAI,EAAS,EAEb,IAAK,IAAI,EAAkB,EAAG,EAAkB,EAAY,OAAQ,IAAmB,CACtF,MAAM,EAA8B,EAAY,GAChD,IAAK,EAAM,SAEX,MAAM,EAA8B,EAAmB,EAAK,OAAO,SAC7D,EAA2B,EAAmB,EAAK,OAAO,SAC1D,EAAkB,KAAK,IAAI,EAAkB,EAAK,OAAS,GAE3D,EAAsC,EAC3C,EAAM,IAAa,GACnB,EACA,GAED,QAAwB,IAApB,EAA+B,SACnC,EAAS,EACT,MAAM,EAAqB,KAAK,IAAI,EA9GN,IA8G+C,GACvE,EAAuB,CAC5B,OACA,YACA,eAAgB,EAChB,kBACA,SAAU,EACV,OAAQ,EAAW,EACnB,YAAa,EACb,UAAW,EAAS,EACpB,UACA,cAGD,EAAM,KAAK,GACX,EAAK,IAAI,EAAK,GAAI,GAClB,IAAK,IAAI,EAAc,EAAM,SAAU,EAAc,EAAM,OAAQ,IAAe,CACjF,MAAM,EAAsD,EAAM,GAClE,GAAK,EACL,IACC,IAAI,EAAiB,EAAM,YAC3B,EAAiB,EAAM,UACvB,IAGA,EAAS,KAAoB,CAE/B,CACA,EAAS,EAAM,UACf,EAAc,KAAK,IAAI,EAAa,EAAM,UAC3C,CACD,CAEA,MAAO,CACN,QACA,OACA,SAAU,EAAK,OACf,cACA,QACA,MAAA,CAAO,EAAa,GACnB,MAAK,OAAO,UAAU,KAAS,OAAO,UAAU,IAAW,EAAM,GAAK,EAAS,GAG/E,OAAO,EAAM,KAAO,EACrB,EACA,SAAS,GACD,EAAK,IAAI,GAEjB,YAAA,CAAa,GACZ,MAAM,EAAiB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAM,QAAS,EAAM,QAC3D,EAAiB,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,IAAI,EAAM,QAAS,EAAM,QACzE,EAAoB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAM,WAAY,EAAM,WACjE,EAAoB,KAAK,IAC9B,EAAc,EACd,KAAK,IAAI,EAAM,WAAY,EAAM,WAElC,OAAI,EAAS,GAAU,EAAY,EAAkB,GAE9C,EAAM,OACX,GACA,EAAM,UAAY,GAClB,EAAM,OAAS,GACf,EAAM,aAAe,GACrB,EAAM,UAAY,EAErB,EAEF,CAOA,SAAgB,EAAiC,EAAqB,GACrE,MAAM,iBAAc,IAAI,IAClB,iBAAiB,IAAI,IAC3B,IAAK,MAAM,KAAO,EAAiB,GAAQ,CAC1C,EAAe,IAAI,EAAI,IACvB,IAAK,MAAM,KAAQ,EAAiB,GAAM,EAAY,IAAI,EAAK,GAChE,CACA,MAAM,EAAkB,EAAgB,GAClC,EAA4C,EAAK,MAAM,OAAQ,GACpE,EAAY,IAAI,EAAM,KAAK,KAE5B,GAA+B,IAA3B,EAAgB,OAAc,OAAO,EAAoB,GAE7D,MAAM,EAAsB,KAAK,OAAO,EAAgB,IAAK,GAAU,EAAM,cACvE,EAAqB,KAAK,OAAO,EAAgB,IAAK,GAAU,EAAM,YAAc,EACpF,EAAwC,EAAK,KACjD,IAAA,CAAK,EAAK,IAAW,EAAe,IAAI,EAAI,IAAM,GAAQ,GAC1D,OAAQ,GAAU,GAAS,GAC7B,GAAkC,IAA9B,EAAmB,OAAc,OAAO,EAAoB,GAGhE,MAAM,EAwCP,SACC,EACA,EACA,EACA,EACA,EACA,GAEA,IAAI,GAAU,EACd,MAAM,EAA6B,EAAiB,GAAO,IAAK,IAC/D,IAAI,GAAa,EACjB,MAAM,EAA8B,EAAiB,GAAK,IAAK,IAC9D,MAAM,EAAmC,EAAK,SAAS,EAAK,IAC5D,IAAK,EAAO,OAAO,EACnB,MAAM,EACL,KAAK,IAAI,EAAM,OAAQ,EAAU,GAAK,KAAK,IAAI,EAAM,SAAU,GAC1D,EACL,KAAK,IAAI,EAAM,UAAW,EAAa,GAAK,KAAK,IAAI,EAAM,YAAa,GACnE,EAA0B,EAAmB,EAAK,OAAO,SACzD,EAA6B,EAAmB,EAAK,OAAO,SAClE,GAAI,IAAY,GAAmB,IAAe,EAAoB,OAAO,EAC7E,GAAa,EACb,MAAQ,QAAS,EAAU,QAAS,KAAa,GAAS,EAAK,OAAS,CAAC,EACnE,EAAQ,IACV,KACC,EAAa,EAAI,CAAE,QAAS,GAAe,CAAC,KAC5C,EAAU,EAAI,CAAE,QAAS,GAAY,CAAC,IAEnC,MAAO,KAAW,GAAiB,EAC3C,OAAO,OAAO,KAAK,GAAO,OAAS,EAAI,IAAK,EAAc,SAAU,IAErE,OAAK,GACL,GAAU,EACH,IAAK,EAAK,SAAU,IAFH,IAIzB,OAAO,EAAU,IAAK,EAAO,SAAU,GAAS,CACjD,CA5EoC,CAClC,EACA,EAJwB,KAAK,OAAO,GACb,KAAK,OAAO,GAMnC,EACA,GAED,IAAK,MAAM,KAAY,EAAoB,CAC1C,MAAM,iBAAiB,IAAI,IAC3B,IAAK,MAAM,KAAS,EACnB,KAAI,EAAM,SAAW,GAAY,EAAM,QAAU,GACjD,IAAK,IAAI,EAAS,EAAM,YAAa,EAAS,EAAM,UAAW,IAC9D,EAAe,IAAI,GAGrB,IAAK,IAAI,EAAS,EAAa,GAAU,EAAY,IACpD,IAAK,EAAe,IAAI,GAAS,OAAO,EAAoB,EAE9D,CAEA,MAAM,EAAqB,EAAS,OAAO,eAC3C,IAAK,MAAM,QAAQ,GAAY,OAAO,EAEtC,MAAM,EAA+B,GACrC,IAAK,IAAI,EAAS,EAAa,GAAU,EAAY,IAAU,CAC9D,MAAM,EAAiB,EAAU,GACjC,EAAU,KAAsB,iBAAV,GAAsB,OAAO,SAAS,GAAS,EAAQ,KAC9E,CACA,OAAK,EAAU,KAAM,GAAoB,OAAV,GAExB,IACH,EACH,MAAO,IACF,EAAgB,OAAS,CAAC,EAC9B,eAAgB,OAAO,OAAO,KANuB,EAAoB,EAS5E,CAoHA,SAAS,EAAoB,GAC5B,IAAK,EAAM,QAAU,OAAO,OAAO,EAAM,MAAO,kBAAmB,OAAO,EAC1E,MAAQ,eAAgB,KAAoB,GAAU,EAAM,OACpD,MAAO,KAAW,GAAiB,EAC3C,MAAO,IACH,KACC,OAAO,KAAK,GAAO,OAAS,EAAI,CAAE,SAAU,CAAC,EAEnD,CAEA,SAAS,EACR,EACA,EACA,GAEA,IAAI,EAAiB,KAAK,IAAI,EAAG,GACjC,KAAO,EAAS,GAvXgB,KAuXW,CAC1C,IAAI,GAAY,EAChB,IAAK,IAAI,EAAS,EAAG,EAAS,EAAM,IACnC,GAAI,EAAI,EAAS,GAAS,CACzB,GAAY,EACZ,GAAU,EAAS,EACnB,KACD,CAED,GAAI,EAAW,OAAO,CACvB,CAED,CAEA,SAAS,EAAgB,EAAe,GACvC,OAAK,OAAO,SAAS,GACd,KAAK,IAAI,EAAa,EAAG,KAAK,IAAI,EAAG,KAAK,MAAM,KADnB,CAErC,CCzVA,SAAgB,IACf,MAAM,EAAuB,EAC5B,EAAS,aACT,CAAC,EAAe,KAChB,KAED,OAAO,EAAgB,EAAS,cAA+B,CAAC,GAAY,IAC7E,CAMA,SAAgB,EAAiB,EAAoB,GACpD,MAAM,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,KAKlB,IAAI,EAA0B,KAC1B,EAA8B,KAC9B,EAAyB,KAE7B,IAAK,MAAM,KAAM,EAAM,CACtB,MAAM,EAAO,EAAM,SAAS,GACvB,IACa,UAAd,EAAK,MACR,EAAU,EACV,EAAY,GACY,eAAd,EAAK,OACf,EAAS,GAEX,CAQA,GAJoB,eADN,EAAM,SAAS,IAClB,OACV,EAAS,IAGL,IAAY,IAAc,EAAQ,OAAO,KAG9C,MAAM,EAAqB,EAAM,IAAI,SAAS,UAAW,GAAM,EAAE,KAAO,GAElE,EAAO,EAAgB,GACvB,EAAsC,EAAK,SAAS,GAC1D,OAAK,EAEE,CACN,UACA,aACA,MAAO,EAAS,UAAU,GAC1B,SAAU,EAAS,SACnB,OAAQ,EAAS,OACjB,SACA,SAAU,EAAS,YACnB,OAAQ,EAAS,UACjB,QAAS,EAAS,QAClB,QAAS,EAAS,WAClB,UAAW,EAAK,SAChB,UAAW,EAAK,aAdK,IAgBvB,CAMA,SAAgB,EACf,EACA,EACA,EACA,GAEA,MAAM,EAAQ,EAAM,SAAS,GAC7B,OAAK,GAAwB,UAAf,EAAM,KACb,EAAgB,GAAO,OAAO,EAAU,IAAW,KAAK,IAAM,KADxB,IAE9C,CAYA,SAAgB,EAAmB,EAAoB,GACtD,MAAM,EAA8B,EAAM,SAAS,GACnD,IAAK,EAAM,OAAO,EAElB,MAAM,EADsC,EAAiB,GACV,GACnD,IAAK,EAAO,OAAO,EACnB,IAAI,EAAqB,EACzB,OAAa,CAEZ,MAAM,EADiC,EAAiB,GACX,GAC7C,IAAK,EAAM,OAAO,EAAQ,GAC1B,EAAU,CACX,CACD,CAGA,SAAgB,GAAkB,EAAoB,GACrD,MAAM,EAA8B,EAAM,SAAS,GACnD,IAAK,EAAM,OAAO,EAClB,MAAM,EAAsC,EAAiB,GACvD,EAA8B,EAAc,EAAc,OAAS,GACzE,IAAK,EAAM,OAAO,EAClB,IAAI,EAAqB,EACzB,OAAa,CACZ,MAAM,EAAiC,EAAiB,GAClD,EAA8B,EAAS,EAAS,OAAS,GAC/D,IAAK,EAAM,OAAO,EAAQ,GAC1B,EAAU,CACX,CACD,CAGA,SAAgB,GAAc,EAAoB,GACjD,MAAM,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,EAElB,IAAK,MAAM,KAAM,EAEhB,GAAmB,UADN,EAAM,SAAS,IAClB,KAAkB,OAAO,EAEpC,OAAO,CACR,CCvGA,IAAa,GAA+B,CAE3C,eAAgB,mBAChB,eAAgB,mBAChB,iBAAkB,qBAClB,kBAAmB,sBACnB,UAAW,aACX,aAAc,gBACd,iBAAkB,kBAClB,YAAa,eAGb,aAAc,gBACd,iBAAkB,yCAGlB,UAAW,aACX,aAAc,gBACd,OAAQ,UACR,UAAW,aACX,iBAAkB,2CAClB,gBAAiB,6CACjB,YAAa,eAGb,UAAW,UACX,gBAAiB,aACjB,iBAAkB,oBAClB,sBAAuB,0BACvB,UAAW,YACX,MAAO,QACP,MAAO,QACP,OAAQ,SACR,iBAAkB,qBAClB,sBAAuB,2BACvB,cAAe,kBACf,sBAAA,CAAwB,EAAiB,IACxC,sBAAsB,QAAc,QACrC,eAAiB,GAAqB,cAAc,EAAW,IAC/D,kBAAoB,GAAwB,iBAAiB,EAAc,IAC3E,2BAA6B,GAAwB,iBAAiB,EAAc,IACpF,wBAA0B,GAAqB,cAAc,EAAW,IACxE,mBAAA,CAAqB,EAAc,IAClC,wBAAwB,wBAA2B,QACpD,uBAAA,CAAyB,EAAqB,IAC7C,UAAU,EAAc,kBAAkB,QAC3C,4BAAA,CAA8B,EAAkB,IAC/C,OAAO,EAAW,2BAA2B,QAC9C,yBAA2B,GAC1B,UAAU,EAAc,8BACzB,8BAAgC,GAC/B,OAAO,EAAW,uCACnB,wBAAyB,kEAGzB,aAAc,UACd,UAAW,aACX,kBAAmB,sBACnB,uBAAyB,GAAsB,6BAA6B,IAC5E,oBAAqB,iCACrB,kBAAoB,GAAsB,UAAU,IAGpD,yBAA0B,qBAC1B,yBAA0B,qBAC1B,uBAAyB,GAAiB,mBAAmB,IAC7D,mBAAoB,cACpB,sBAAuB,iBACvB,qBAAsB,gBAGtB,YAAa,eAGb,eAAA,CAAiB,EAAc,IAAiB,cAAc,cAAiB,YAC/E,qBAAsB,oDAKjB,kBAA8C,OAAA,OAAA,CAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,uBAIpD,eAAsB,GAAgB,GACrC,OAAO,EAAiB,GAAe,EAAM,GAC9C,CC7JA,IAAa,GAA0C,CACtD,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAWD,SAAgB,GACf,EACA,EACA,GAEA,MAAM,EAAQ,EAAM,SAAS,GAC7B,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAE7C,MAAM,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,KAElB,QAAoB,IAAhB,EAA2B,CAE9B,MAAQ,YAAa,KAAM,GAAU,EAAM,OAAS,CAAC,EAI/C,EAAsB,EAC5B,OAAO,EACL,YAAY,WACZ,YAAY,EAAM,GAClB,aAAa,EAAM,WACnB,OACH,CAEA,MAAM,EAAuB,IAAK,EAAM,MAAO,eAC/C,OAAO,EACL,YAAY,WACZ,YAAY,EAAM,GAClB,aAAa,EAAM,WACnB,OACH,CAKA,SAAgB,GACf,EACA,EACA,EAAsB,IAEtB,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAW,EAAiB,EAAO,EAAM,UAAU,OAAO,SAChE,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GAA+B,EAAO,EAAS,QAAS,GACnE,IAAK,EAAI,OAAO,EAEhB,EAAQ,SAAS,GACjB,MAAM,EAA8B,SAAV,EAAmB,OAAS,EAAa,GAEnE,OADA,EAAQ,SAAS,EAAO,uBAAuB,KACxC,CACR,CAGA,SAAgB,GACf,EACA,EAAsB,IAEtB,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAW,EAAiB,EAAO,EAAM,UAAU,OAAO,SAChE,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GAA+B,EAAO,EAAS,aAAS,GACnE,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,sBACjB,EACR,CAGA,SAAgB,GAAoB,EAAoB,GACvD,MAAM,EAAQ,EAAM,SAAS,GAC7B,GAAK,EACL,OAAO,EAAM,OAAO,WACrB,CAQA,SAAgB,GACf,EACA,EACA,EACA,EACA,EAAsB,IAEtB,EAAU,UAAU,IAAI,wBAExB,MAAM,EAAmC,GAAoB,EAAQ,WAAY,GAG3E,EAAgC,SAAS,cAAc,UAC7D,EAAW,KAAO,SAClB,EAAW,UAAY,gCACvB,EAAW,YAAc,EAAO,aAChC,EAAW,iBAAiB,YAAc,IACzC,EAAE,iBACF,EAAE,kBACF,GAAsB,EAAS,GAC/B,MAED,EAAW,iBAAiB,UAAY,IACzB,WAAV,EAAE,MACL,EAAE,iBACF,OAGF,EAAU,YAAY,GAGtB,MAAM,EAAkC,SAAS,cAAc,UAC/D,EAAa,KAAO,SACpB,EAAa,UAAY,gCACzB,EAAa,YAAc,EAAO,UAClC,EAAa,iBAAiB,YAAc,IAC3C,EAAE,iBACF,EAAE,kBACF,GAAoB,EAAS,OAAQ,GACrC,MAED,EAAa,iBAAiB,UAAY,IAC3B,WAAV,EAAE,MACL,EAAE,iBACF,OAGF,EAAU,YAAY,GAGtB,EAAgB,EAAW,CAC1B,OAAQ,GACR,QAlJc,EAmJd,UAAW,EAAO,kBAClB,gBAAiB,GACjB,YAAa,GAAgB,KAC7B,YAAa,EAAO,kBACpB,aAAa,EACb,SAAW,IACV,GAAoB,EAAS,EAAO,GACpC,KAED,WAEF,CCxJA,SAAS,GACR,EACA,EACA,GAEA,MAAM,EAAqC,EAAM,cAC3C,EAA0B,EAAiB,mBACjD,IAAK,IAAkB,EAAO,OAAO,EAErC,MAAM,EADqB,EAAQ,WACQ,SAAS,EAAM,SAC1D,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,EAC7C,MAAM,EJoPP,SAAkC,EAAkB,GACnD,MAAM,EAAkB,EAAgB,GACxC,GAAsB,IAAlB,EAAK,UAAuC,IAArB,EAAK,YAAmB,OAAO,KAC1D,MAAM,EAAkC,EAAqB,EAAM,GACnE,IAAK,EAAU,OAAO,KACtB,MAAM,QAAE,EAAA,WAAS,EAAA,MAAY,EAAA,SAAO,GAAa,EAC3C,EAA0C,EAAK,aAAa,CACjE,UACA,aACA,QACA,aAGK,EAAoB,GACpB,EAAgD,IAAf,GAAoB,IAAa,EAAK,YAAc,EAC3F,IAAK,IAAI,EAAa,EAAS,GAAc,EAAO,IAAc,CACjE,MAAM,EAAmC,EAAK,KAAK,GACnD,IAAK,EAAW,SAChB,MAAM,EAAqB,EACzB,OAAQ,GAAkC,KAAK,IAAI,EAAM,SAAU,KAAa,GAChF,KAAA,CACC,EAAkB,IAClB,KAAK,IAAI,EAAE,YAAa,GAAc,KAAK,IAAI,EAAE,YAAa,IAE/D,IAAK,IACL,MAAM,EACL,KAAK,IAAI,EAAM,OAAQ,EAAQ,GAAK,KAAK,IAAI,EAAM,SAAU,GACxD,EACL,KAAK,IAAI,EAAM,UAAW,EAAW,GAAK,KAAK,IAAI,EAAM,YAAa,IAC/D,QAAS,EAAU,QAAS,KAAa,GAAS,EAAM,KAAK,OAAS,CAAC,EACzE,EAAQ,IACV,KACC,EAAa,EAAI,CAAE,QAAS,GAAe,CAAC,KAC5C,EAAU,EAAI,CAAE,QAAS,GAAY,CAAC,IAEnC,MAAO,KAAW,GAAiB,EAAM,KACjD,OAAO,OAAO,KAAK,GAAO,OAAS,EAAI,IAAK,EAAc,SAAU,KAE9D,OAAQ,KAAe,GAAqB,EACpD,EAAK,KAAK,IACL,EAAwB,EAAY,EACxC,SAAU,GAEZ,CAEA,MAAQ,eAAgB,KAAoB,GAAe,EAAM,OAAS,CAAC,EACrE,EAAqB,EAAM,OAAO,eAClC,EAA4B,GAClC,GAAI,MAAM,QAAQ,GACjB,IAAK,IAAI,EAAS,EAAY,GAAU,EAAU,IAAU,CAC3D,MAAM,EAAiB,EAAU,GACjC,EAAO,KAAsB,iBAAV,GAAsB,OAAO,SAAS,GAAS,EAAQ,KAC3E,CAED,MAAM,EAAQ,IACV,KACC,EAAO,KAAM,GAA4C,OAAV,GAChD,CAAE,eAAgB,OAAO,OAAO,IAChC,CAAC,GAEC,EACL,GAAqC,IAAZ,GAAiB,IAAU,EAAK,SAAW,GAC7D,MAAO,KAAW,GAAsB,GACxC,OAAQ,KAAiB,GAA+B,EAC1D,EAAe,EAAyB,EAAoB,EAClE,OAAO,OAAO,KAAK,GAAO,OAAS,EAChC,IAAK,EAAc,SAAU,EAAM,SACnC,IAAK,EAAc,SAAU,EACjC,CIxTiC,CAAkB,EAAO,CACxD,QAAS,EAAM,QACf,WAAY,EAAM,QAClB,MAAO,EAAM,MACb,SAAU,EAAM,QAEjB,QAAK,IAEL,EAAM,iBACN,EAAM,2BACN,EAAc,QACb,YACA,EAAwB,CAAE,SAAU,CAAC,IAAU,EAAQ,sBAExD,EAAc,QAAQ,aA6CvB,SAAuC,GACtC,MAAM,EAAO,EAAgB,GAC7B,OAAO,MAAM,KAAK,CAAE,OAAQ,EAAK,UAAS,CAAI,EAAkB,IACxD,MAAM,KAAK,CAAE,OAAQ,EAAK,aAAY,CAAI,EAAkB,KAClE,MAAM,EAAQ,EAAK,OAAO,EAAK,GAC/B,OAAK,GAAS,EAAM,WAAa,GAAO,EAAM,cAAgB,EACvD,GAAmB,EAAM,MAD6C,KAE3E,KAAK,OACN,KAAK,KACT,CAtDqC,CAAuB,KACpD,EACR,CAsDA,SAAS,GAAmB,GAC3B,OAAI,EAAY,GAAe,EAAa,GACrC,EAAiB,GAAO,IAAI,IAAoB,KAAK,KAC7D,CCpGA,IAAa,GAA2B,IAAI,EAAkC,kBA4I9E,SAAS,GAAwB,GAChC,OAAO,KAAK,UAAU,CACrB,KAAM,EAAK,KAAK,IAAK,GAAQ,EAAI,IACjC,MAAO,EAAK,MAAM,IAAK,GAAU,CAChC,EAAM,KAAK,GACX,EAAM,SACN,EAAM,OACN,EAAM,YACN,EAAM,aAGT,CA0GA,SAAS,GACR,EAOA,GAOA,MAAO,CACN,QAAS,EAAO,QAChB,QAAS,KAAK,IAAI,EAAO,SAAU,EAAO,UAC1C,QAAS,KAAK,IAAI,EAAO,SAAU,EAAO,UAC1C,MAAO,KAAK,IAAI,EAAO,OAAQ,EAAO,QAAU,EAChD,MAAO,KAAK,IAAI,EAAO,OAAQ,EAAO,QAAU,EAElD,CCjRA,IAAa,GAA2D,OAAO,OAAO,CACrF,iBAAkB,GAClB,eAAgB,GAChB,iBAAkB,EAClB,eAAgB,EAChB,qBAAsB,EACtB,0BAA2B,KA8Df,GAAwB,IAAI,EAA+B,eAkDxE,SAAgB,GAAyB,GACxC,MAAM,EAA2B,GAChC,EAAO,iBACP,GAA4B,kBAEvB,EAAyB,GAC9B,EAAO,eACP,GAA4B,gBAa7B,MAAO,CACN,mBACA,iBACA,iBAdgC,GAChC,EAAO,iBACP,GAA4B,iBAC5B,GAYA,eAV8B,GAC9B,EAAO,eACP,GAA4B,eAC5B,GAQA,qBAAsB,GACrB,EAAO,qBACP,GAA4B,sBAE7B,0BAA2B,GAC1B,EAAO,0BACP,GAA4B,2BAG/B,CAKA,SAAgB,GACf,EACA,GAEA,MAAM,EAAgB,KAAK,IAAI,EAAG,KAAK,MAAM,IACvC,EAAkC,EAAM,OAAO,eAC/C,EAA6B,MAAM,QAAQ,GAAO,EAAM,GAC9D,OAAO,OAAO,OACb,MAAM,KAAK,CAAE,OAAQ,GAAM,CAAI,EAAkB,KAChD,MAAM,EAAiB,EAAO,GAC9B,OAAI,QAA8C,KAC3C,GAAyB,KAGnC,CAGA,SAAgB,GACf,EACA,GAEA,MAAM,EAAyC,OAAO,OACrD,EAAO,IAAK,GACD,OAAV,EAAiB,KAAO,GAAyB,MAG3C,eAAgB,KAAoB,GAAS,GAAS,CAAC,EAC/D,OAAK,EAAW,KAAM,GAA4C,OAAV,GACjD,IAAK,EAAM,eAAgB,GAD8C,CAEjF,CAGA,SAAgB,GAAwB,GACvC,OAAO,GAAyB,EAAI,OAAO,YAC5C,CAGA,SAAgB,GACf,EACA,GAEA,MAAQ,YAAa,KAAiB,GAAS,GAAS,CAAC,EACzD,GAAoB,OAAhB,EAAsB,OAAO,EACjC,MAAM,EAA4B,GAAyB,GAC3D,OAAsB,OAAf,EAAsB,EAAO,IAAK,EAAM,YAAa,EAC7D,CAYA,SAAS,GAAuB,GAC/B,MAAM,EAAqB,EAAQ,WAG7B,EADL,EAAQ,WAAW,KACsC,oBAAsB,KAChF,GAAI,EACH,OAAO,GAAa,EAAO,EAAc,QAAS,GAAe,GAGlE,IAAK,EAAgB,EAAM,WAAY,OAAO,KAC9C,MAAM,EAAe,EAAiB,EAAO,EAAM,UAAU,OAAO,SACpE,OAAK,EACE,GACN,EACA,EAAa,QACb,CACC,QAAS,EAAa,QACtB,QAAS,EAAa,SACtB,QAAS,EAAa,SACtB,MAAO,EAAa,OAAS,EAC7B,MAAO,EAAa,OAAS,IAE9B,GAXyB,IAa3B,CAEA,SAAS,GAAsB,EAAoB,GAClD,MAAM,EAA+B,EAAM,SAAS,EAAO,SAC3D,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GAExC,OAAQ,EAAO,MACd,IAAK,OAAQ,CACZ,IAAK,GAAgB,EAAO,IAAK,EAAK,UAAW,OAAO,KACxD,IAAK,GAAgB,EAAO,OAAQ,EAAK,aAAc,OAAO,KAC9D,MAAM,EAAO,EAAK,OAAO,EAAO,IAAK,EAAO,QAC5C,OAAK,EACE,GACN,EACA,EACA,EACA,EAAK,SACL,EAAK,OAAS,EACd,EAAK,YACL,EAAK,UAAY,GARA,IAUnB,CACA,IAAK,QACJ,OAAO,GACN,EACA,EAAO,QACP,CACC,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,QAAS,EAAO,WAChB,MAAO,EAAO,MACd,MAAO,EAAO,WAEf,GAEF,IAAK,SACJ,OAAK,GAAgB,EAAO,OAAQ,EAAK,aAClC,CAAE,QAAO,QAAO,OAAM,QAAS,CAAC,EAAO,QAAS,KAAM,IADC,KAE/D,IAAK,MACJ,OAAK,GAAgB,EAAO,IAAK,EAAK,UAC/B,CAAE,QAAO,QAAO,OAAM,QAAS,GAAI,KAAM,CAAC,EAAO,MADA,KAG3D,CAEA,SAAS,GACR,EACA,EACA,EACA,GAEA,MAAM,EAA+B,EAAM,SAAS,GACpD,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GACxC,GAAsB,IAAlB,EAAK,UAAuC,IAArB,EAAK,YAAmB,OAAO,KAG1D,IAAK,CADmC,EAAM,QAAS,EAAM,MAAO,EAAM,QAAS,EAAM,OACxE,MAAM,OAAO,WAAY,OAAO,KACjD,GACC,KACE,GAAgB,EAAM,QAAS,EAAK,YACpC,GAAgB,EAAM,MAAO,EAAK,YAClC,GAAgB,EAAM,QAAS,EAAK,eACpC,GAAgB,EAAM,MAAO,EAAK,cAEpC,OAAO,KAGR,MAAM,EAAkB,GAAW,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,UACxE,EAAgB,GAAW,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,UAGtE,EAAW,EAAqB,EAAM,CAC3C,UACA,WAJ0B,GAAW,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,aAKhF,QACA,SALwB,GAAW,KAAK,IAAI,EAAM,QAAS,EAAM,OAAQ,EAAK,eAO/E,OAAO,EACJ,GACA,EACA,EACA,EACA,EAAS,QACT,EAAS,MACT,EAAS,WACT,EAAS,UAET,IACJ,CAEA,SAAS,GACR,EACA,EACA,EACA,EACA,EACA,EACA,GAEA,MAAO,CACN,QACA,QACA,OACA,KAAM,GAAa,EAAS,GAC5B,QAAS,GAAa,EAAY,GAEpC,CAIA,SAAS,GAAiB,GACzB,MAAM,EAAqC,GAC1C,EAAO,MACP,EAAO,KAAK,aAEP,EAA2C,EAAO,QAAQ,IAC9D,GAAmB,EAAO,IAAW,MAEjC,EAAwC,EAAO,KAAK,IAAK,IAC9D,MAAM,EAA6B,EAAO,KAAK,KAAK,GACpD,OAAO,EAAM,GAAwB,GAAO,OAG7C,MAAO,CACN,cAAe,GAAgB,GAC/B,eAAgB,GAAgB,GAElC,CAEA,SAAS,GACR,EACA,EACA,EACA,GAEA,MAAM,EAA0B,OAAO,UAAU,eAAe,KAAK,EAAO,iBACtE,EAAuB,OAAO,UAAU,eAAe,KAAK,EAAO,kBACzE,IAAK,IAAmB,EAAa,OAAO,EAC5C,GAAI,GAA4C,IAA1B,EAAO,QAAQ,OAAc,OAAO,EAC1D,GAAI,GAAsC,IAAvB,EAAO,KAAK,OAAc,OAAO,EAEpD,MAAM,EAAsC,EACzC,GAAe,EAAM,cAAe,EAAO,iBAAkB,EAAO,kBACpE,KACG,EAAmC,EACtC,GAAe,EAAM,eAAgB,EAAO,eAAgB,EAAO,gBACnE,KACH,GAAK,IAAmB,GAAiB,IAAgB,EAAW,OAAO,EAE3E,MAAM,EAA8B,EAAO,MAAM,YAAY,WAC7D,IAAI,GAAU,EAEd,GAAI,EAAa,CAChB,MAAM,EAA4B,IAC9B,GAAwB,EAAO,MAAO,EAAO,KAAK,cAEtD,IAAK,MAAM,KAAU,EAAO,QACvB,EAAO,KAAY,EAAY,QAClC,EAAO,GAAU,EAAY,MAC7B,GAAU,GAGZ,GAAI,EAAS,CACZ,MAAM,EAAuC,EAAO,MAAM,YAAY,EAAO,MAAM,IACnF,IAAK,EAAM,OAAO,EAClB,EAAQ,YAAY,EAAM,GAAwB,EAAO,MAAM,MAAO,GACvE,CACD,CAEA,GAAI,EACH,IAAK,MAAM,KAAY,EAAO,KAAM,CACnC,MAAM,EAA6B,EAAO,KAAK,KAAK,GACpD,IAAK,GAAO,GAAwB,KAAS,EAAS,MAAO,SAC7D,MAAM,EAAuC,EAAO,MAAM,YAAY,EAAI,IAC1E,IAAK,EAAM,OAAO,EAClB,EAAQ,YAAY,EAAM,GAAwB,EAAI,MAAO,EAAS,QACtE,GAAU,CACX,CAGD,QAAK,IACL,EAAQ,aAAa,EAAO,MAAM,WAClC,EAAQ,SAAS,EAAQ,UAClB,EACR,CAEA,SAAS,GACR,EACA,EACA,EACA,GAEA,OACQ,GAAU,EAAS,EADT,kBAAd,EAC+B,CAAE,cAAe,QAElC,mBAAd,EAC+B,CAAE,eAAgB,QAOnB,IAH7B,EAAO,QAAQ,OAAS,EAAI,CAAE,cAAe,QAAoB,CAAC,KAClE,EAAO,KAAK,OAAS,EAAI,CAAE,eAAgB,QAAoB,CAAC,GARP,EAW/D,CAMA,SAAS,GACR,EACA,EACA,GAEA,GAAc,SAAV,EAAkB,MAAO,CAAE,MAAO,MACtC,MAAM,EAA4B,EAA0B,EAAO,EAAS,GAC5E,OAAsB,OAAf,EAAsB,KAAO,CAAE,MAAO,EAC9C,CAEA,SAAS,GAAgB,GACxB,GAAsB,IAAlB,EAAO,OAAc,MAAO,cAChC,MAAM,EAAmC,EAAO,GAChD,OAAI,EAAO,KAAM,GAAkC,IAAU,GAAe,QACrE,QAAwC,OAAS,CACzD,CAIA,SAAS,GAAyB,GACjC,MAAqB,iBAAV,GACP,EAAA,GAAkC,EAAA,IADA,KAE/B,EAA0B,EAClC,CAEA,SAAS,GACR,EACA,EACA,EAAA,GAEA,OAAO,EAA0B,GAAS,EAAU,EAAA,MAAoC,CACzF,CAEA,SAAS,GAAgB,EAAe,GACvC,OAAO,OAAO,UAAU,IAAU,GAAS,GAAK,EAAQ,CACzD,CAEA,SAAS,GAAW,EAAe,GAClC,OAAO,KAAK,IAAI,EAAa,EAAG,KAAK,IAAI,EAAG,GAC7C,CAEA,SAAS,GAAa,EAAc,GACnC,OAAO,MAAM,KAAK,CAAE,OAAQ,EAAK,EAAO,GAAE,CAAI,EAAkB,IAAkB,EAAO,EAC1F,CCpeA,SAAgB,GACf,EACA,EACA,GAEA,MAAM,EAAQ,EAAM,SAAS,GAC7B,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GACxC,IAAK,GAAoB,EAAU,EAAK,WAAkC,IAArB,EAAK,YAAmB,OAAO,KAEpF,MAAM,EAA0C,EAAK,MAAM,OACzD,GAAyB,EAAM,SAAW,GAAY,EAAM,OAAS,GAEjE,EAAK,EAAM,YAAY,WAC7B,IAAK,MAAM,KAAS,EACnB,IAAK,GAAY,EAAO,EAAI,EAAM,KAAM,UAAW,EAAM,QAAU,GAAI,OAAO,KAG/E,MAAM,EAAqB,GAC3B,IAAK,IAAI,EAAS,EAAG,EAAS,EAAK,YAAa,IACtB,EAAc,KACrC,GAAyB,EAAM,aAAe,GAAU,EAAM,UAAY,IAE9D,EAAM,KAAK,KAE1B,MAAM,EAAoB,EAAgB,EAAS,aAAc,GAC3D,EA8UP,SAA2B,EAAkB,EAAiB,GAC7D,GAAI,GAAc,EAAK,SAAU,OAAO,EAAM,SAAS,OACvD,MAAM,EAA6B,EAAK,KAAK,GAC7C,IAAK,EAAK,OAAO,EAAM,SAAS,OAChC,MAAM,EAAgB,EAAM,SAAS,UAAW,GAAU,OAAQ,GAAS,EAAM,KAAO,EAAI,IAC5F,OAAiB,IAAV,EAAe,EAAM,SAAS,OAAS,CAC/C,CApV6B,CAAkB,EAAO,EAAM,GAC3D,EAAG,WAAW,CAAC,GAAU,EAAa,GAEtC,MAAM,EAAY,EAAiB,GAAQ,GACrC,EAAY,EAAY,EAAiB,GAAW,QAAK,EAO/D,OANI,EACH,EAAG,aAAa,EAAyB,EAAU,GAAI,IAC7C,GACV,EAAG,aAAa,EAAyB,EAAU,GAAI,IAGjD,EAAG,OACX,CAGA,SAAgB,GACf,EACA,EACA,GAEA,MAAM,EAAQ,EAAM,SAAS,GAC7B,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GACxC,IAAK,GAAoB,EAAU,EAAK,cAAkC,IAAlB,EAAK,SAAgB,OAAO,KAEpF,MAAM,EAAK,EAAM,YAAY,WAC7B,GAAI,GAAuB,GAAQ,CAClC,MAAM,EAA4B,IAAI,GAAwB,EAAO,EAAK,cAC1E,EAAO,OAAO,EAAU,EAAG,MAC3B,MAAM,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,KAClB,EAAG,YAAY,EAAM,GAAwB,EAAM,MAAO,GAC3D,CAEA,MAAM,EAA0C,EAAK,MAAM,OACzD,GAAyB,EAAM,YAAc,GAAY,EAAM,UAAY,GAE7E,IAAK,MAAM,KAAS,EACnB,IAAK,GAAY,EAAO,EAAI,EAAM,KAAM,UAAW,EAAM,WAAa,GAAI,OAAO,KAGlF,IAAI,EACJ,IAAK,IAAI,EAAW,EAAG,EAAW,EAAK,SAAU,IAAY,CAC5D,MAAM,EAA6B,EAAK,KAAK,GAC7C,IAAK,EAAK,SAIV,GAHuC,EAAc,KACnD,GAAyB,EAAM,UAAY,GAAY,EAAM,OAAS,GAE7C,SAE3B,MAAM,EAAU,IAChB,IAAsB,EACtB,MAAM,EAAyB,EAAK,MAAM,OACxC,GAAyB,EAAM,UAAU,KAAO,EAAI,IAAM,EAAM,WAAa,GAC7E,OACF,EAAG,WAAW,CAAC,EAAS,EAAI,IAAK,EAAgB,EAClD,CAEA,MAAM,EAAmC,EACtC,EAAiB,GAAmB,QACpC,EAEH,OADA,EAAG,aAAa,EAAY,EAAyB,EAAU,GAAI,GAAK,EAAM,WACvE,EAAG,OACX,CAOA,SAAgB,GACf,EACA,EACA,EACA,EAAe,GAEf,MAAM,EAAQ,EAAM,SAAS,GAC7B,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GACxC,IAAK,GAAmB,EAAU,EAAK,UAAW,OAAO,KACzD,GAAI,EAAK,UAAY,EACpB,OAAO,GAA6B,EAAO,GAG5C,MAAM,EAAK,EAAM,YAAY,WACvB,EAA0C,EAAK,MAAM,OACzD,GAAyB,EAAM,UAAY,GAAY,EAAM,OAAS,GAElE,EAAuC,EAC3C,OAAQ,GAAyB,EAAM,WAAa,GAAY,EAAM,QAAU,GAChF,KAAA,CAAM,EAAkB,IAAqB,EAAE,YAAc,EAAE,aAEjE,IAAK,MAAM,KAAS,EACnB,KAAI,EAAM,SAAW,GAChB,GAAY,EAAO,EAAI,EAAM,KAAM,UAAW,EAAM,QAAU,IAAI,OAAO,KAG/E,GAAI,EAAW,OAAS,EAAG,CAC1B,MAAM,EAAmC,EAAK,KAAK,EAAW,GAC9D,IAAK,EAAW,OAAO,KACvB,MAAM,EAA6C,EAAK,MAAM,OAC5D,GAAyB,EAAM,UAAU,KAAO,EAAU,IAE5D,IAAI,EAAQ,EACZ,IAAK,MAAM,KAAS,EAAY,CAC/B,MAAM,EACL,EAAiB,OAAQ,GAA0B,EAAO,YAAc,EAAM,aAC5E,OAAS,EACZ,EAAG,SACF,CAAC,EAAS,EAAM,UAAU,IAC1B,EAAM,gBAAkB,EACxB,CAAC,EAAS,EAAU,IACpB,GAED,GACD,CACD,CAEA,MAAM,EAgOP,SAA6B,EAAkB,EAAiB,GAC/D,MAAM,EAA6B,EAAK,KAAK,GAC7C,OAAK,EACE,EAAM,SAAS,UAAW,GAAU,OAAQ,GAAS,EAAM,KAAO,EAAI,KAD5D,CAElB,CApO6B,CAAoB,EAAO,EAAM,GAC7D,GAAI,EAAc,EAAG,OAAO,KAC5B,EAAG,WAAW,CAAC,GAAU,GAIzB,MAAM,EAAyB,EAAU,EAAO,EAFnB,EAAW,EAAI,EAAW,EAAI,EAChC,KAAK,IAAI,KAAK,IAAI,EAAG,GAAe,EAAK,YAAc,IAElF,GAAI,EAAQ,CACX,MAAM,EAAkB,EAAmB,EAAO,GAClD,EAAG,aAAa,EAAyB,EAAQ,GAClD,CAEA,OAAO,EAAG,OACX,CAOA,SAAgB,GACf,EACA,EACA,EACA,EAAe,GAEf,MAAM,EAAQ,EAAM,SAAS,GAC7B,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,KAC7C,MAAM,EAAkB,EAAgB,GACxC,IAAK,GAAmB,EAAU,EAAK,aAAc,OAAO,KAC5D,GAAI,EAAK,aAAe,EACvB,OAAO,GAA6B,EAAO,GAG5C,MAAM,EAAK,EAAM,YAAY,WAC7B,GAAI,GAAuB,GAAQ,CAClC,MAAM,EAA4B,IAAI,GAAwB,EAAO,EAAK,cAC1E,EAAO,OAAO,EAAU,GACxB,MAAM,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,KAClB,EAAG,YAAY,EAAM,GAAwB,EAAM,MAAO,GAC3D,CAEA,MAAM,EAA0C,EAAK,MAAM,OACzD,GAAyB,EAAM,aAAe,GAAY,EAAM,UAAY,GAE9E,IAAK,MAAM,KAAS,EACnB,KAAI,EAAM,YAAc,GACnB,GAAY,EAAO,EAAI,EAAM,KAAM,UAAW,EAAM,WAAa,IAAI,OAAO,KAElF,IAAK,MAAM,KAAS,EAClB,OAAQ,GAA4C,IAApB,EAAK,YACrC,KAAA,CAAM,EAAkB,IACpB,EAAE,iBAAmB,EAAE,eAAuB,EAAE,eAAiB,EAAE,eAChE,EAAE,gBAAkB,EAAE,iBAE9B,EAAG,WAAW,CAAC,EAAS,EAAM,UAAU,IAAK,EAAM,iBAGpD,MAAM,EAAoB,EAAW,EAAI,EAAW,EAAI,EAElD,EAAyB,EAAU,EAAO,EADxB,KAAK,IAAI,KAAK,IAAI,EAAG,GAAe,EAAK,SAAW,GACV,GAClE,GAAI,EAAQ,CACX,MAAM,EAAkB,EAAmB,EAAO,GAClD,EAAG,aAAa,EAAyB,EAAQ,GAClD,CAEA,OAAO,EAAG,OACX,CAOA,SAAgB,GACf,EACA,GAEA,MAAM,EAAqB,EAAM,IAAI,SAAS,UAAW,GAAU,EAAM,KAAO,GAChF,IAAmB,IAAf,EAAmB,OAAO,KAE9B,GAAkC,IAA9B,EAAM,IAAI,SAAS,OAAc,CACpC,MAAM,EAAc,EAAqB,KACzC,OAAO,EACL,YAAY,WACZ,WAAW,GAAI,EAAG,GAClB,WAAW,GAAI,GACf,aAAa,EAAyB,EAAY,GAAI,IACtD,OACH,CAEA,MAAM,EAAK,EAAM,YAAY,WAAW,WAAW,GAAI,GAEjD,EAAW,EAAM,IAAI,SAAS,EAAa,GACjD,GAAI,EAAU,CACb,MAAM,EAAY,EAAgC,EAAO,EAAS,GAAI,SACtE,GAAI,EAEH,OADA,EAAG,aAAa,GACT,EAAG,OAEZ,CAEA,MAAM,EAAW,EAAM,IAAI,SAAS,EAAa,GACjD,GAAI,EAAU,CACb,MAAM,EAAY,EAAgC,EAAO,EAAS,GAAI,OACtE,GAAI,EAEH,OADA,EAAG,aAAa,GACT,EAAG,OAEZ,CAEA,OAAO,IACR,CAaA,SAAgB,GAAY,EAAwB,EAAc,GACjE,MAAM,EAAQ,EAAQ,WAChB,EAAgB,EAAmB,GACzC,IAAK,EAAe,OAAO,EAE3B,MAAM,EN/RP,SAA4B,EAAc,GACzC,MAAM,EAAmB,IACnB,EAAwB,GAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,IAAK,CAC9B,MAAM,EAAyB,GAC/B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,IACzB,EAAU,KAAK,KAEhB,EAAS,KACR,EAAgB,EAAS,aAA8B,EAAW,KAEpE,CAEA,OAAO,EAAgB,EAAS,SAA0B,EAAU,EACrE,CMgRmB,CAAY,EAAM,GAC9B,EAAU,EAAM,YAAY,WAElC,IADiB,EAA2B,EAAO,EAAS,EAAe,GAC5D,OAAO,EAGtB,MAAM,EAAW,EAAiB,GAAW,GACvC,EAAY,EAAW,EAAiB,GAAU,QAAK,EACvD,EAAiB,EAAY,EAAiB,GAAW,QAAK,EASpE,OAPI,EACH,EAAQ,aAAa,EAAyB,EAAe,GAAI,IACvD,GACV,EAAQ,aAAa,EAAyB,EAAU,GAAI,IAG7D,EAAQ,SAAS,EAAQ,UAClB,CACR,CAkBA,SAAgB,GAAY,EAAwB,GACnD,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GAA0B,EAAO,EAAS,QAAS,EAAS,QACvE,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,2BACjB,EACR,CAYA,SAAS,GAAU,EAAwB,EAAwB,GAClE,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAkC,SAAT,EAAkB,EAAS,SAAW,EAAS,OACxE,EAAK,GAA6B,EAAO,EAAS,QAAS,GACjE,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,uBAAuB,KACxC,EACR,CAIA,SAAS,GAAoB,EAAe,GAC3C,OAAO,OAAO,UAAU,IAAU,GAAS,GAAK,GAAS,CAC1D,CAEA,SAAS,GAAmB,EAAe,GAC1C,OAAO,OAAO,UAAU,IAAU,GAAS,GAAK,EAAQ,CACzD,CAgBA,SAAS,GACR,EACA,EACA,EACA,EACA,GAEA,MAAM,EAAuC,EAAM,YAAY,EAAK,IACpE,QAAK,IACL,EAAQ,YAAY,EAIrB,SAAsB,EAAiB,EAA4B,GAClE,MAAM,EAAwC,IAAM,EAAK,OAAS,CAAC,GAGnE,cAFO,EAAM,GACT,EAAQ,IAAG,EAAM,GAAO,GACrB,IAAK,EAAM,QACnB,CAT2B,CAAa,EAAM,EAAK,GAAO,OAAS,CAAC,IAC5D,EACR,CASA,SAAS,GAAuB,GAC/B,OAAO,OAAO,UAAU,eAAe,KAAK,EAAM,OAAS,CAAC,EAAG,iBAChE,CAiFA,SAAgB,GAAsB,EAAwB,GAC7D,EAAQ,gBAAgB,cAAA,IAAqB,GAAY,EAAS,EAAG,IACrE,EAAQ,gBAAgB,cAAA,IAvLzB,SAA4B,EAAwB,GACnD,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GAA0B,EAAO,EAAS,QAAS,EAAS,UACvE,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,2BACjB,EACR,CA2K8C,CAAY,EAAS,IAClE,EAAQ,gBAAgB,cAAA,IAAqB,GAAY,EAAS,IAClE,EAAQ,gBAAgB,gBAAA,IA3JzB,SAA8B,EAAwB,GACrD,OAAO,GAAU,EAAS,OAAQ,EACnC,CAyJgD,CAAc,EAAS,IACtE,EAAQ,gBAAgB,iBAAA,IAvJzB,SAA+B,EAAwB,GACtD,OAAO,GAAU,EAAS,QAAS,EACpC,CAqJiD,CAAe,EAAS,IACxE,EAAQ,gBAAgB,YAAA,IApFzB,SAA0B,EAAwB,GACjD,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GACV,EACA,EAAS,QACT,EAAS,SACT,EAAS,UAEV,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,qBACjB,EACR,CAmE4C,CAAU,EAAS,IAC9D,EAAQ,gBAAgB,eAAA,IAjEzB,SAA6B,EAAwB,GACpD,MAAM,EAAQ,EAAQ,WACtB,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAK,GACV,EACA,EAAS,QACT,EAAS,SACT,EAAS,UAEV,QAAK,IAEL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,wBACjB,EACR,CAgD+C,CAAa,EAAS,IACpE,EAAQ,gBAAgB,cAAA,IAjCzB,SAA4B,GAC3B,MAAM,EAAQ,EAAQ,WAChB,EAAM,EAAM,UAElB,GAAI,EAAgB,GAEnB,MAA8B,UADT,EAAM,SAAS,EAAI,SACnB,KAEtB,GAAI,EAAY,GAAM,OAAO,EAE7B,MAAM,EAAgC,EAAiB,EAAO,EAAI,OAAO,SACzE,IAAK,EAAU,OAAO,EAEtB,MAAM,EAAO,EAAM,YAAY,EAAS,SACxC,IAAK,EAAM,OAAO,EAElB,MAAM,EAAK,EACT,YAAY,WACZ,aAAa,EAAoB,EAAS,QAAS,IACnD,QAEF,OADA,EAAQ,SAAS,IACV,CACR,CAW8C,CAAY,IACzD,EAAQ,gBAAgB,cAAA,IA/CzB,SAA4B,EAAwB,GACnD,MAAM,EAAQ,EAAQ,WAChB,EAgDP,SACC,GAEA,GAAI,EAAgB,EAAM,WAAY,CACrC,MAAM,EAAe,EAAM,SAAS,EAAM,UAAU,QACpD,MAA2B,UAAvB,GAAc,KAAyB,KACpC,CAAE,QAAS,EAAa,GAChC,CACA,GAAI,EAAY,EAAM,WAAY,OAAO,KAEzC,MAAM,EAAgC,EAAiB,EAAO,EAAM,UAAU,OAAO,SACrF,OAAK,EAEE,CAAE,QAAS,EAAS,SAFL,IAGvB,CA9DgB,CAA2B,GAC1C,IAAK,EAAQ,OAAO,EAEpB,MAAM,EAAK,GAA6B,EAAO,EAAO,SACtD,QAAK,IACL,EAAQ,SAAS,GACjB,EAAQ,SAAS,EAAO,uBACjB,EACR,CAqC8C,CAAY,EAAS,GACnE,CC/WA,SAAS,GACR,EACA,EACA,EACA,EACA,EACA,EACA,GAEA,MAAM,EAAwB,SAAS,cAAc,OACrD,EAAM,UAAY,mCAClB,MAAM,EAA0B,SAAS,cAAc,SACvD,EAAM,QAAU,sBAAsB,IACtC,EAAM,YAAc,EACpB,MAAM,EAA4B,SAAS,cAAc,OACzD,EAAU,UAAY,wCACtB,MAAM,EAA0B,SAAS,cAAc,SACvD,EAAM,GAAK,EAAM,QACjB,EAAM,KAAO,SACb,EAAM,UAAY,UAClB,EAAM,KAAO,IACb,EAAM,IAAM,OAAO,GACnB,EAAM,IAAM,OAAO,GACnB,EAAM,aAAa,mBAAoB,GAAG,EAAM,YAChD,MAAM,EAAwB,SAAS,cAAc,QACrD,EAAK,UAAY,kCACjB,EAAK,YAAc,KACnB,EAAK,aAAa,cAAe,QACjC,EAAU,OAAO,EAAO,GACxB,MAAM,EAAwB,SAAS,cAAc,OACrD,EAAM,GAAK,GAAG,EAAM,WACpB,EAAM,UAAY,mCAClB,EAAM,aAAa,OAAQ,SAC3B,EAAM,OAAO,EAAO,EAAW,GAC/B,EAAU,YAAY,GAEtB,MAAM,EAAwB,CAAE,QAAO,QAAO,UAAS,UAAS,OAAO,GAEvE,OADA,GAAc,EAAO,EAAO,GACrB,CACR,CAEA,SAAS,GACR,EACA,EACA,GAEA,EAAM,MAAM,SAAqB,gBAAV,EACvB,EAAM,MAAM,MAAyB,iBAAV,EAAqB,OAAO,GAAS,GAChE,EAAM,MAAM,YAAwB,UAAV,EAAoB,EAAO,MAAQ,EAAO,UACpE,EAAM,OAAQ,EACd,GAAW,EACZ,CAEA,SAAS,GAAe,EAAuB,GAC9C,MAAM,EAAc,EAAM,MAAM,MAAM,OAChC,EAAgB,OAAO,GAC7B,GAAmB,IAAf,EAAI,SAAiB,OAAO,SAAS,GAIxC,OAHA,EAAM,MAAM,YAAc,EAAO,sBAAsB,EAAM,QAAS,EAAM,SAC5E,EAAM,MAAM,aAAa,eAAgB,QACzC,EAAM,MAAM,QACL,KAER,MAAM,EAAkB,KAAK,IAAI,EAAM,QAAS,KAAK,IAAI,EAAM,QAAS,IAExE,OADA,EAAM,MAAM,MAAQ,OAAO,GACpB,CACR,CAEA,SAAS,GAAW,GACnB,EAAM,MAAM,YAAc,GAC1B,EAAM,MAAM,gBAAgB,eAC7B,CAEA,SAAS,GAAY,EAAe,EAAY,IAC/C,MAAM,EAA4B,SAAS,cAAc,UAIzD,OAHA,EAAO,KAAO,SACd,EAAO,YAAc,EACrB,EAAO,UAAY,EACZ,CACR,CA6CA,SAAS,GACR,EACA,EACA,GAEA,OAAO,EAAS,EAAQ,UAAU,EAAQ,GAAa,EAAQ,mBAAmB,EACnF,CA0BA,SAAS,GACR,EACA,EACA,EACA,GAEkB,kBAAd,EAQiB,QAAjB,GAAQ,MAAmC,SAAjB,GAAQ,KACrC,EAAQ,SAAS,EAAO,8BAA8B,EAAO,MAE7D,EAAQ,SAAS,EAAO,uBAVH,WAAjB,GAAQ,MAAsC,SAAjB,GAAQ,KACxC,EAAQ,SAAS,EAAO,yBAAyB,EAAO,SAExD,EAAQ,SAAS,EAAO,iBAS3B,CChSA,IAAM,kBAA8D,IAAI,QAGxE,SAAgB,GAAuB,GACtC,IAAK,MAAM,IAAS,IAAK,GAAmB,IAAI,IAAY,IAAM,GACnE,CA4BA,SAAgB,GACf,EACA,EACA,EACA,EACA,EACA,EAAsB,GACtB,EACA,EAAmC,CAAC,GAEpC,IAAI,GAAO,EACP,EAAkC,KAClC,EAAgC,KAGpC,MAAM,EAAuB,SAAS,cAAc,OACpD,EAAK,UAAY,6BACjB,EAAK,aAAa,OAAQ,QAC1B,EAAK,aAAa,aAAc,EAAO,cACvC,EAAK,aAAa,kBAAmB,SACrC,EAAK,aAAa,wBAAyB,IAG3C,MAAM,EAAiC,GACvC,IAAI,EAAe,EAEnB,MAAM,EAlDP,SAAwB,GACvB,MAAO,CACN,CAAE,KAAM,OAAQ,MAAO,EAAO,eAAgB,QAAS,eACvD,CAAE,KAAM,OAAQ,MAAO,EAAO,eAAgB,QAAS,eACvD,CAAE,KAAM,aACR,CAAE,KAAM,OAAQ,MAAO,EAAO,iBAAkB,QAAS,iBACzD,CAAE,KAAM,OAAQ,MAAO,EAAO,kBAAmB,QAAS,kBAC1D,CAAE,KAAM,aACR,CAAE,KAAM,OAAQ,MAAO,EAAO,UAAW,QAAS,aAClD,CAAE,KAAM,OAAQ,MAAO,EAAO,aAAc,QAAS,gBACrD,CAAE,KAAM,aACR,CAAE,KAAM,UAAW,MAAO,EAAO,iBAAkB,GAAI,eACvD,CAAE,KAAM,UAAW,MAAO,EAAO,UAAW,GAAI,QAChD,CAAE,KAAM,aACR,CAAE,KAAM,OAAQ,MAAO,EAAO,YAAa,QAAS,eAEtD,CAkC6C,CAAe,GAC3D,IAAK,MAAM,KAAO,EAAU,CAC3B,GAAiB,cAAb,EAAI,KAAsB,CAC7B,MAAM,EAAsB,SAAS,cAAc,OACnD,EAAI,aAAa,OAAQ,aACzB,EAAK,YAAY,GACjB,QACD,CAEA,MAAM,EAAyB,SAAS,cAAc,UACtD,EAAI,KAAO,SACX,EAAI,aAAa,OAAQ,YACzB,EAAI,YAAc,EAAI,MAEL,YAAb,EAAI,OACP,EAAI,QAAQ,QAAU,EAAI,GAC1B,EAAI,aAAa,gBAAiB,QAClC,EAAI,aAAa,gBAAiB,UAGlB,SAAb,EAAI,OACP,EAAI,QAAQ,QAAU,EAAI,SAG3B,EAAI,iBAAiB,YAAc,IAClC,EAAE,iBACF,EAAE,oBAGH,EAAI,iBAAiB,QAAU,IAC9B,EAAE,kBACe,SAAb,EAAI,MACP,EAAQ,eAAe,EAAI,SAC3B,KACuB,YAAb,EAAI,MACd,EAAY,KAId,EAAU,KAAK,GACf,EAAK,YAAY,EAClB,CAGA,MAAM,EAAuB,SAAS,cAAc,OAoFpD,SAAS,EAAY,GAIpB,GAHA,IACA,EAAQ,aAAa,gBAAiB,QAElC,EAMH,EAAY,EAAa,KAAK,CAC7B,OAAQ,EACR,SAAU,QACV,cAAe,EACf,OAAQ,CARR,MAAA,IAAa,IACb,WAAA,IAAkB,GAQlB,QAAA,KACC,EAAY,KACZ,EAAW,KACX,KAED,QAAA,CAAU,EAAoB,KAC7B,EAAM,UAAY,6BAClB,EAAM,aAAa,kBAAmB,SACtC,EAAM,aAAa,wBAAyB,IAC5C,EAAW,EAEX,EAAM,iBAAiB,UAAY,IAElC,GADA,EAAE,kBACE,EAAE,kBAAkB,iBAAkB,OAE1C,MAAM,EAD2D,QAArC,iBAAiB,GAAM,UACd,aAAe,YAChD,EAAE,MAAQ,IACb,EAAE,iBACF,IACA,EAAQ,WAIV,EAAqB,EAAO,UAGxB,CACN,EAAW,SAAS,cAAc,OAClC,EAAS,UAAY,6BACrB,EAAS,aAAa,kBAAmB,SACzC,EAAS,aAAa,wBAAyB,IAE/C,EAAS,iBAAiB,UAAY,IAErC,GADA,EAAE,kBACE,EAAE,kBAAkB,iBAAkB,OAE1C,MAAM,EAD0D,QAArC,iBAAiB,GAAM,UACb,aAAe,YAChD,EAAE,MAAQ,IACb,EAAE,iBACF,IACA,EAAQ,WAIV,EAAqB,EAAU,GAE/B,EAAU,YAAY,GAEtB,MAAM,EAAoB,EAAK,wBACzB,EAAsD,QAArC,iBAAiB,GAAM,UAC9C,EAAc,EAAU,EAAU,CAAE,SAAU,QAAS,UAEvB,SAA5B,EAAQ,QAAQ,SACnB,sBAAA,MACwB,GAAU,cAAc,YAC/B,SAGnB,CACD,CAEA,SAAS,EAAqB,EAAoB,GACjB,SAA5B,EAAQ,QAAQ,QAapB,GAAwB,EAAO,EAAS,EAAS,EAAO,GD5S1D,SACC,EACA,EACA,EACA,EAAsB,IAEtB,MAAM,EAA0C,EAAQ,aAAa,IAC/D,EAA4B,GAAyB,EAAQ,QAAU,CAAC,GACxE,EAA+B,EA6OtC,SACC,EACA,GAEA,OAAO,EAAS,EAAQ,QAAQ,GAAU,EAAQ,kBACnD,CAlPgD,CAAS,EAAS,EAAQ,QAAU,KAEnF,EAAU,UAAU,IAAI,6BACxB,EAAU,aAAa,OAAQ,UAC/B,EAAU,aAAa,aAAc,QACrC,EAAU,aAAa,aAAc,EAAO,iBAE5C,MAAM,EAAwB,SAAS,cAAc,OACrD,EAAM,UAAY,mCAClB,EAAM,YAAc,EAAO,gBAC3B,EAAU,YAAY,GAEtB,MAAM,EAA8B,GACnC,EACA,eACA,EAAO,iBACP,GAAO,eAAiB,cACxB,EAAO,iBACP,EAAO,iBACP,GAEK,EAA2B,GAChC,EACA,iBACA,EAAO,sBACP,GAAO,gBAAkB,cACzB,EAAO,eACP,EAAO,eACP,GAGK,EAA6B,SAAS,cAAc,OAC1D,EAAW,UAAY,oCACvB,MAAM,EAAiC,GACtC,EAAO,iBACP,oCAEK,EAA8B,GACnC,EAAO,sBACP,oCAEK,EAA8B,GACnC,EAAO,cACP,0EAED,EAAY,SAAW,EAAY,MAAM,SACzC,EAAS,SAAW,EAAS,MAAM,SACnC,EAAS,SAAW,EAAY,MAAM,UAAY,EAAS,MAAM,SACjE,EAAW,OAAO,EAAa,EAAU,GACzC,EAAU,YAAY,GAEtB,MAAM,EAA0B,SAAS,cAAc,OACvD,EAAQ,UAAY,qCACpB,MAAM,EAA4B,GAAY,EAAO,QAC/C,EAA2B,GAAY,EAAO,MAAO,oCAC3D,EAAM,UAAY,KAAa,EAAQ,eACvC,EAAQ,OAAO,EAAQ,GACvB,EAAU,YAAY,GAEtB,EAAY,MAAM,iBAAiB,QAAA,KAClC,EAAY,OAAQ,EACpB,GAAW,KAEZ,EAAS,MAAM,iBAAiB,QAAA,KAC/B,EAAS,OAAQ,EACjB,GAAW,KAGZ,EAAM,iBAAiB,QAAA,KACtB,IAAK,GAAW,EAAQ,eAAgB,OACxC,MAAM,EAA+B,CAAC,EACtC,IAAI,GAAQ,EACZ,GAAI,EAAY,MAAO,CACtB,MAAM,EAAuB,GAAe,EAAa,GAC3C,OAAV,EAAgB,GAAQ,EACvB,EAAM,cAAgB,CAC5B,CACA,GAAI,EAAS,MAAO,CACnB,MAAM,EAAuB,GAAe,EAAU,GACxC,OAAV,EAAgB,GAAQ,EACvB,EAAM,eAAiB,CAC7B,CACK,IACA,EAAY,OAAU,EAAS,MAiKtC,SACC,EACA,EACA,GAEA,OAAO,EAAS,EAAQ,QAAQ,EAAQ,GAAS,EAAQ,iBAAiB,EAC3E,CAnKM,CAAU,EAAS,EAAQ,OAAQ,KA6KzC,SACC,EACA,EACA,EACA,GAEA,MAAM,EAAqB,GACQ,iBAAxB,EAAM,gBACK,WAAjB,GAAQ,MAAsC,SAAjB,GAAQ,KACxC,EAAS,KAAK,EAAO,uBAAuB,EAAO,OAAQ,EAAM,gBAEjE,EAAS,KAAK,GAAG,EAAO,qBAAqB,OAAO,EAAM,mBAGxB,iBAAzB,EAAM,iBACK,QAAjB,GAAQ,MAAmC,SAAjB,GAAQ,KACrC,EAAS,KAAK,EAAO,4BAA4B,EAAO,IAAK,EAAM,iBAEnE,EAAS,KAAK,GAAG,EAAO,0BAA0B,OAAO,EAAM,oBAG7D,EAAS,OAAS,GAAG,EAAQ,SAAS,EAAS,KAAK,KACzD,CAlMG,CAAoB,EAAS,EAAQ,OAAQ,EAAO,GACpD,EAAQ,WALR,EAAQ,aASV,EAAO,iBAAiB,QAAS,EAAQ,SACzC,EAAY,iBAAiB,QAAA,KACvB,GAAY,GAAU,EAAS,EAAQ,OAAQ,mBACpD,GAAc,EAAa,OAAQ,GACnC,GAAc,EAAS,EAAQ,OAAQ,gBAAiB,MAEzD,EAAS,iBAAiB,QAAA,KACpB,GAAY,GAAU,EAAS,EAAQ,OAAQ,oBACpD,GAAc,EAAU,OAAQ,GAChC,GAAc,EAAS,EAAQ,OAAQ,iBAAkB,MAE1D,EAAS,iBAAiB,QAAA,KACpB,GAAY,GAAU,EAAS,EAAQ,UACvC,EAAY,MAAM,UAAU,GAAc,EAAa,OAAQ,GAC/D,EAAS,MAAM,UAAU,GAAc,EAAU,OAAQ,GAC9D,EAAQ,SAAS,EAAO,4BAGzB,EAAU,iBAAiB,UAAY,IACtC,GAAkB,WAAd,EAAM,IAIT,OAHA,EAAM,iBACN,EAAM,uBACN,EAAQ,UAGS,QAAd,EAAM,KA4FZ,SAAyB,EAAwB,GAChD,MAAM,EAaP,SAAoC,GACnC,OAAO,MAAM,KACZ,EAAU,iBAAuD,kBAChE,OAAQ,IAA4D,EAAQ,SAC/E,CAjB2C,CAA2B,GAC/D,EAAiC,EAAU,GAC3C,EAAgC,EAAU,EAAU,OAAS,GACnE,IAAK,IAAU,EAAM,OACrB,MAAM,EAgBP,SAA6B,GAC5B,MAAM,EAAa,EAAU,cAC7B,OAAI,aAAgB,YAAc,aAAgB,SAAiB,EAAK,cACjE,SAAS,aACjB,CApBgC,CAAoB,GAC7C,EAAsB,EAAM,WAAa,IAAW,IAAU,EAAU,SAAS,IACjF,IAAwB,EAAM,UAAa,IAAW,GAAS,EAAU,SAAS,KACnF,GAAe,KACpB,EAAM,kBACL,EAAa,EAAO,GAAO,QAC7B,CAvG2B,CAAgB,EAAW,KAGrD,sBAAA,MACkD,CAAC,EAAY,MAAO,EAAS,OAAO,KACnF,IAAsC,EAAM,WAE/B,GAAQ,SAEzB,CCyJG,CACC,EACA,EACA,CACC,OAAQ,EAAQ,WAChB,OAAQ,EAAQ,aAChB,QAAS,GAEV,EAKH,CAEA,SAAS,IACJ,GACH,EAAU,QACV,EAAY,KACZ,EAAW,MACD,IACV,EAAS,SACT,EAAW,MAEZ,GACD,CAEA,SAAS,IACR,IAAK,MAAM,KAAQ,EACd,EAAK,QAAQ,SAChB,EAAK,aAAa,gBAAiB,QAGtC,CAlMA,EAAK,UAAY,mCACjB,EAAK,aAAa,cAAe,QACjC,EAAK,YAAc,EAAO,iBAC1B,EAAK,YAAY,GAEjB,EAAoB,EAAW,GAG/B,EAAK,iBAAiB,UAAY,IAGjC,OAFA,EAAE,kBAEM,EAAE,KACT,IAAK,SAGJ,OAFA,EAAE,sBACF,IAGD,IAAK,YAKJ,OAJA,EAAE,iBACF,GAAgB,EAAe,GAAK,EAAU,OAC9C,EAAoB,EAAW,QAC/B,EAAU,IAAe,QAI1B,IAAK,UAKJ,OAJA,EAAE,iBACF,GAAgB,EAAe,EAAI,EAAU,QAAU,EAAU,OACjE,EAAoB,EAAW,QAC/B,EAAU,IAAe,QAI1B,IAAK,aACL,IAAK,YAAa,CACjB,EAAE,iBAEF,MAAM,EAA8B,QADZ,iBAAiB,GAAM,UACH,YAAc,aAC1D,GAAI,EAAE,MAAQ,EAAS,CACtB,MAAM,EAAO,EAAU,GACnB,GAAM,QAAQ,SACjB,EAAY,EAEd,MACC,IACA,EAAU,IAAe,QAE1B,MACD,CAEA,IAAK,QACL,IAAK,IAAK,CACT,EAAE,iBACF,MAAM,EAAO,EAAU,GACvB,IAAK,EAAM,OAOX,YANI,EAAK,QAAQ,QAChB,EAAY,GACF,EAAK,QAAQ,UACvB,EAAQ,eAAe,EAAK,QAAQ,SACpC,KAGF,CAEA,IAAK,OAKJ,OAJA,EAAE,iBACF,EAAe,EACf,EAAoB,EAAW,QAC/B,EAAU,IAAe,QAI1B,IAAK,MAKJ,OAJA,EAAE,iBACF,EAAe,EAAU,OAAS,EAClC,EAAoB,EAAW,QAC/B,EAAU,IAAe,WA0H5B,EAAU,YAAY,GACtB,EAAc,EAAM,EAAY,CAC/B,SAAU,cACV,MAAO,EAAa,KAIrB,EAAU,IAAI,QACd,sBAAA,KACK,GAAQ,EAAU,IACrB,EAAU,GAAG,UAKf,MAAM,EAAkB,IACvB,MAAM,EAAsB,EAAE,eAC1B,EAAK,SAAS,IAAU,GAAY,EAAK,SAAS,IAGtD,KAED,IAAI,EAA0D,KAC1D,GAA0B,EAQ9B,SAAS,IACR,IAAK,EAAM,OACX,GAAO,EACP,IACA,EAAK,SACqB,OAAtB,IACH,aAAa,GACb,EAAoB,MAEjB,IACH,SAAS,oBAAoB,YAAa,GAAgB,GAC1D,GAA0B,GAE3B,GAAmB,IAAI,IAAU,OAAO,GACxC,MACA,MAAM,EAAgD,EAAQ,eAC1D,GAAe,aAClB,sBAAA,IAA4B,EAAc,MAAM,CAAE,eAAe,IAEnE,CA1BA,EAAoB,WAAA,KACnB,EAAoB,KACpB,SAAS,iBAAiB,YAAa,GAAgB,GACvD,GAA0B,GACxB,GAwBH,MAAM,EAAgC,GAAmB,IAAI,mBAAY,IAAI,IAI7E,OAHA,EAAa,IAAI,GACjB,GAAmB,IAAI,EAAS,GAEzB,CACN,OAAA,IAAc,EACd,QACA,QAAS,EAEX,CC/XA,IAAM,kBAAqE,IAAI,QA6B/E,SAAS,GACR,EACA,GAEA,MAAM,EAAqB,EAAQ,WACnC,IAAK,EAAgB,EAAM,WAAY,OAAO,EAC9C,MAAM,EAAiB,EAAM,UACvB,EAAgC,EAAiB,EAAO,EAAI,OAAO,SACzE,QAAK,GACE,EAAQ,EAAO,EAAK,EAC5B,CAiHA,SAAS,GAAa,GACrB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAM,KAC9C,MAAM,EAAoB,EAAS,WAAa,EAChD,GAAI,GAAa,EAAM,IAAI,SAAS,OAAQ,OAAO,EAEnD,MAAM,EAAY,EAAM,IAAI,SAAS,GACrC,IAAK,EAAW,OAAO,EAEvB,MAAM,EAAK,EACT,YAAY,WACZ,aAAa,EAAyB,EAAU,GAAI,IACpD,QAEF,OADA,EAAQ,SAAS,IACV,GAET,CA0DA,SAAgB,GACf,EACA,GAGA,IADsB,EAAQ,aAAa,KAA2B,qBACnD,UAAY,EAAa,QAC5C,MAAO,CACN,KAAM,OACN,QAAS,EAAa,QACtB,IAAK,EAAa,SAClB,OAAQ,EAAa,SAEvB,CAEA,SAAS,GACR,EACA,EACA,EACA,GAGA,MAAM,EADqB,EAAQ,WACf,SAAS,EAAa,SAC1C,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,EAC7C,MAAM,EAAQ,EAAgB,GAAO,MAE/B,EAAS,EADc,EAAM,UAAW,GAAU,EAAM,KAAK,KAAO,EAAa,QACnD,GACpC,QAAK,IACE,EAuCR,SACC,EACA,EACA,EACA,GAEA,MAAM,EAAqB,EAAQ,WAC7B,EAAyB,EAAU,EAAO,EAAS,EAAU,GACnE,IAAK,EAAQ,OAAO,EAEpB,MAAM,EAAkB,GAAkB,EAAO,GAC3C,EAAO,EAAM,SAAS,GAC5B,IAAK,EAAM,OAAO,EAElB,MAAM,EAAkB,EAAe,GACjC,EAAK,EACT,YAAY,WACZ,aAAa,EAAyB,EAAQ,IAC9C,QAEF,OADA,EAAQ,SAAS,IACV,CACR,CA3DI,CAAgB,EAAS,EAAa,QAAS,EAAO,SAAU,EAAO,aACvE,GAAoB,EAAS,EAAa,QAAS,EAAO,SAAU,EAAO,aAC/E,CAmBA,SAAS,GACR,EACA,EACA,EACA,GAEA,MAAM,EAAqB,EAAQ,WAC7B,EAAyB,EAAU,EAAO,EAAS,EAAU,GACnE,IAAK,EAAQ,OAAO,EAEpB,MAAM,EAAkB,EAAmB,EAAO,GAC5C,EAAK,EAAM,YAAY,WAAW,aAAa,EAAyB,EAAQ,IAAI,QAE1F,OADA,EAAQ,SAAS,IACV,CACR,CA2BA,SAAS,GAAgB,EAAwB,GAChD,MAAM,EAAqB,EAAQ,WAC7B,EAAO,EAAM,YAAY,GAC/B,IAAK,EAAM,OAAO,EAElB,MAAM,EAAK,EAAM,YAAY,SAAS,aAAa,EAAoB,EAAS,IAAO,QAEvF,OADA,EAAQ,SAAS,IACV,CACR,CC3VA,IAAa,GACZ,kMAqBD,SAAgB,GACf,EACA,EACA,GAEA,MAAM,EAAyB,SAAS,cAAc,UAWtD,OAVA,EAAI,UAAY,EAChB,EAAI,UAAY,EAChB,EAAI,MAAQ,EACZ,EAAI,KAAO,SACX,EAAI,aAAa,aAAc,GAC/B,EAAI,aAAa,kBAAmB,SACpC,EAAI,iBAAiB,YAAc,IAClC,EAAE,iBACF,EAAE,oBAEI,CACR,CAGA,SAAgB,GACf,EACA,GAEA,MAAM,EAAuB,SAAS,cAAc,OACpD,EAAK,UAAY,sCAAsC,IACvD,EAAK,aAAa,kBAAmB,SACrC,EAAK,aAAa,wBAAyB,IAE3C,MAAM,EAAyB,GAAa,kBAAmB,GAAU,GAGzE,OAFA,EAAK,YAAY,GAEV,CACR,CAGA,SAAgB,GAAe,EAAmB,GACjD,MAAM,EAAyB,SAAS,cAAc,UACtD,EAAI,UAAY,iBAAiB,IACjC,EAAI,KAAO,SACX,EAAI,aAAa,kBAAmB,SACpC,EAAI,aAAa,aAAc,GAC/B,EAAI,aAAa,wBAAyB,IAC1C,EAAI,MAAQ,EAEZ,MAAM,EAAwB,SAAS,cAAc,QAUrD,OATA,EAAK,UAAY,gBACjB,EAAK,UAAY,GACjB,EAAI,YAAY,GAEhB,EAAI,iBAAiB,YAAc,IAClC,EAAE,iBACF,EAAE,oBAGI,CACR,CAGA,SAAgB,GAAe,GAC9B,MAAM,EAAsB,SAAS,cAAc,OAInD,OAHA,EAAI,UAAY,EAChB,EAAI,aAAa,kBAAmB,SACpC,EAAI,aAAa,wBAAyB,IACnC,CACR,CAGA,SAAgB,GACf,EACA,EACA,EACA,GAEA,MAAM,EAA+B,SAAS,cAAc,UAW5D,OAVA,EAAU,KAAO,SACjB,EAAU,UAAY,gDAAgD,IACtE,EAAU,QAAQ,KAAO,EACzB,EAAU,QAAQ,MAAQ,OAAO,GACjC,EAAU,aAAa,OAAQ,aAC/B,EAAU,aAAa,mBAA6B,WAAT,EAAoB,WAAa,cAC5E,EAAU,aAAa,aAAc,GACrC,EAAU,aAAa,kBAAmB,SAC1C,EAAU,aAAa,wBAAyB,IAChD,EAAU,MAAQ,GAAG,MAAU,IACxB,CACR,CA2DA,SAAgB,GAAwB,EAAqB,GAE3D,EAAmB,EADN,SAAV,EACwB,CAC1B,gBAAiB,cACjB,OAAQ,oCAEC,EACiB,CAC1B,gBAAiB,EACjB,OAAQ,8BAGkB,CAC1B,gBAAiB,wBACjB,OAAQ,8BAGX,CASA,SAAgB,GACf,EACA,EACA,EACA,EACA,GAEA,MAAM,EAAyB,SAAS,cAAc,OAItD,GAHA,EAAO,UAAY,eAAe,IAClC,EAAO,QAAQ,MAAQ,OAAO,GAE1B,EAAW,CACd,MAAM,EAA+B,GAAa,qBAAsB,GAAI,EAAU,OACtF,EAAU,aAAa,eAAgB,EAAU,SAAW,OAAS,SACrE,EAAU,iBAAiB,QAAU,IACpC,EAAE,iBACF,EAAE,kBACF,EAAU,SAAS,KAEpB,EAAO,YAAY,EACpB,CAEA,MAAM,EAA+B,GAAa,qBAhNlD,qMAgNoF,GAQpF,OAPA,EAAU,iBAAiB,QAAU,IACpC,EAAE,iBACF,EAAE,kBACF,EAAS,KAEV,EAAO,YAAY,GAEZ,CACR,CCzMA,SAAgB,GACf,EACA,GAEA,MAAM,EAAqB,EAAQ,wBAC7B,EAAyB,EAAU,wBACzC,MAAO,CACN,IAAK,EAAU,IAAM,EAAc,IAAM,EAAU,UACnD,KAAM,EAAU,KAAO,EAAc,KAAO,EAAU,WAExD,CAuBA,SAAgB,GAAmB,EAA2B,EAAgB,GAC7E,MAAM,EAAqB,EAAQ,wBAC7B,EAA0C,MAAM,KACrD,EAAQ,iBAAiB,4BAE1B,GAAI,EAAQ,OAAS,EAAG,CACvB,MAAM,EAAwB,EAAQ,IAAA,CAAK,EAAQ,KAClD,MAAM,EAAgB,EAAO,wBAC7B,MAAO,CACN,QACA,SAAU,EAAK,KAAO,EAAU,KAChC,MAAO,EAAK,SAGd,GAAI,EAAS,MAAO,GAAQ,EAAI,MAAQ,GAAI,OAAO,CACpD,CAEA,MAAM,EAAgB,EAAQ,QAAU,EAClC,EAAqB,EAAU,OAAS,EAAQ,YACtD,GAAI,GAAS,GAAK,GAAc,EAAG,MAAO,GAC1C,MAAM,EAAgB,EAAa,EACnC,OAAO,MAAM,KAAK,CAAE,OAAQ,GAAM,CAAI,EAAQ,KAAA,CAC7C,QACA,SAAU,EAAQ,EAClB,UAEF,CCdA,IAAM,GAA+C,IACjD,GACH,cAAc,GAcT,GAAN,MACC,QACA,QACA,QACA,YACA,UAAmB,EACnB,gBAAyB,EACzB,gBAAyB,EACzB,cAA8C,KAE9C,OACA,OACA,aACA,aACA,gBACA,YACA,YACA,WACA,WACA,eACA,eACA,WACA,YACA,SACA,UACA,iBAAkD,KAClD,kBAA2D,KAE3D,UACA,QACA,SACA,WACA,cACA,OACA,OACA,gBAAA,IAA+C,KAAK,mBACpD,cAAkC,GAA8B,KAAK,WAAW,GAChF,YAAgC,GAA8B,KAAK,aAAa,GAChF,gBAAoC,GAA8B,KAAK,aAAa,GACpF,kBAAsC,IACnB,WAAd,EAAM,KAAqB,KAAK,gBACpC,EAAM,iBACN,EAAM,kBACN,KAAK,uBAGN,YAAgC,IAC/B,GAAI,KAAK,UAAY,KAAK,cAAe,OACzC,MAAM,EAAqB,KAAK,QAAQ,wBAClC,EAAY,EAAE,QAAU,EAAU,KAClC,EAAY,EAAE,QAAU,EAAU,IAQxC,KALC,IAAK,IACL,GAAK,EAAU,MAAA,IACf,IAAK,IACL,GAAK,EAAU,OAAA,IAIf,YADA,KAAK,kBAMN,MAAM,EDsDR,SACC,EACA,EACA,EACA,GAEA,IAAI,EAAyB,OAAO,kBAChC,EAAsC,KAE1C,IAAK,MAAM,KAAU,EAAY,CAChC,MAAM,EAAe,KAAK,IAAI,EAAI,EAAO,UACrC,EAAO,GAAkB,EAAA,KAC5B,EAAiB,EACjB,EAAmB,EAErB,CAEA,IAAI,EAAyB,OAAO,kBAChC,EAAsC,KAE1C,IAAK,MAAM,KAAU,EAAY,CAChC,MAAM,EAAe,KAAK,IAAI,EAAI,EAAO,UACrC,EAAO,GAAkB,EAAA,KAC5B,EAAiB,EACjB,EAAmB,EAErB,CAEA,OAAI,KAAsB,GAAoB,GAAkB,GACxD,CAAE,KAAM,MAAO,OAAQ,GAG3B,EACI,CAAE,KAAM,MAAO,OAAQ,GAGxB,CAAE,KAAM,OAAQ,OAAQ,KAChC,CC3FiB,CAAkB,EAAG,ED/GtC,SAAkC,GACjC,MAAM,EAAuC,EAAQ,iBAAiB,uBAChE,EAAmB,EAAQ,wBAAwB,IACnD,EAAwB,GAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAQ,IAAK,CACpC,MAAM,EAAsC,EAAI,GAC3C,GACL,EAAQ,KAAK,CACZ,SAAU,KAAK,MAAM,EAAG,wBAAwB,IAAM,GACtD,MAAO,GAET,CAEA,OAAO,CACR,CC8FmC,CAAkB,KAAK,SD3D1D,SACC,EACA,EAAQ,GAER,GAA4B,iBAAjB,EAA2B,CACrC,GAAI,GAAS,EAAG,MAAO,GACvB,MAAM,EAAgB,EAAe,EACrC,OAAO,MAAM,KAAK,CAAE,OAAQ,EAAQ,GAAE,CAAI,EAAQ,KAAA,CACjD,SAAU,KAAK,MAAM,GAAS,EAAQ,IACtC,MAAO,EAAQ,IAEjB,CAEA,MAAM,EAA8B,GAAmB,EAAc,GACrE,GAAI,EAAM,QAAU,EAAG,MAAO,GAC9B,MAAM,EAA4D,QAA7C,iBAAiB,GAAc,UACpD,OAAO,EAAM,MAAM,GAAG,GAAI,IAAA,CAAK,EAAK,KAAA,CACnC,SAAU,KAAK,MAAM,EAAM,EAAI,SAAW,EAAI,SAAW,EAAI,OAC7D,MAAO,EAAQ,IAEjB,CCwCmC,CAAkB,KAAK,QAAS,KAAK,UAGlD,QAAhB,EAAO,MAAkB,EAAO,QACnC,KAAK,mBAAmB,EAAO,QAC/B,KAAK,oBACqB,QAAhB,EAAO,MAAkB,EAAO,QAC1C,KAAK,iBAAiB,EAAO,QAC7B,KAAK,sBAEL,KAAK,mBAIP,aAAA,KACC,KAAK,mBAGN,WAAA,CACC,EACA,EACA,EACA,EACA,EACA,EACA,EAAsB,GACtB,EAAuC,IAqCvC,GAnCA,KAAK,UAAY,EACjB,KAAK,QAAU,EACf,KAAK,SAAW,EAChB,KAAK,WAAa,EAClB,KAAK,cAAgB,GAAiB,KACtC,KAAK,OAAS,EACd,KAAK,OAAS,IACV,GAAyB,GAC5B,aAAc,EAAO,eAAgB,GAEtC,KAAK,UAAY,EAAQ,QAAQ,0BAEjC,KAAK,QAAU,EAAY,GAC3B,KAAK,YAAc,EACnB,KAAK,QAAU,EAAgB,GAAa,SAC5C,KAAK,QAAU,KAAK,UAAU,GAG9B,KAAK,OAAS,GAAe,gBAC7B,KAAK,OAAS,GAAe,gBAC7B,KAAK,aAAe,GAAe,uBACnC,KAAK,aAAe,GAAe,uBACnC,KAAK,gBFvFP,WACC,MAAM,EAA4B,SAAS,cAAc,OAKzD,OAJA,EAAU,UAAY,wBACtB,EAAU,aAAa,cAAe,QACtC,EAAU,aAAa,kBAAmB,SAC1C,EAAU,aAAa,wBAAyB,IACzC,CACR,CEgFyB,GACvB,KAAK,YAAc,GAAgB,aAAc,EAAO,WACxD,KAAK,YAAc,GAAgB,WAAY,EAAO,cACtD,KAAK,WAAa,GAAe,eAAgB,EAAO,QACxD,KAAK,WAAa,GAAe,eAAgB,EAAO,WACxD,KAAK,eAAiB,GACrB,wBF/LF,8PEiME,EAAO,aAER,KAAK,eAAe,aAAa,wBAAyB,IAGtD,EAAe,CAClB,MAAM,EAAmC,GAAoB,IAAY,KAAK,SAC9E,KAAK,eF3DR,SAAuC,EAAe,GACrD,MAAM,EAAyB,GAAa,wBAP5C,0MAOuF,GACvF,EAAI,aAAa,wBAAyB,IAE1C,MAAM,EAA0B,SAAS,cAAc,QAKvD,OAJA,EAAO,UAAY,2BACnB,GAAwB,EAAQ,GAChC,EAAI,YAAY,GAET,CACR,CEiDyB,CAAuB,EAAO,YAAa,GACjE,KAAK,WFrFR,SAAmC,GAClC,MAAM,EAAyB,GAAa,mBAR5C,4MAQ6E,GAE7E,OADA,EAAI,aAAa,wBAAyB,IACnC,CACR,CEiFqB,CAAmB,EAAO,iBAC7C,MACC,KAAK,eAAiB,KACtB,KAAK,WAAa,KAGnB,EAAU,OACT,KAAK,OACL,KAAK,OACL,KAAK,YACL,KAAK,YACL,KAAK,WACL,KAAK,WACL,KAAK,gBAEF,KAAK,OAAO,cACf,EAAU,OAAO,KAAK,aAAc,KAAK,aAAc,KAAK,iBAEzD,KAAK,gBACR,EAAU,YAAY,KAAK,gBAExB,KAAK,YACR,EAAU,YAAY,KAAK,YAG5B,KAAK,YFvGP,SAAiC,GAChC,MAAM,EAAuB,SAAS,cAAc,OAMpD,OALA,EAAK,UAAY,oBACjB,EAAK,aAAa,cAAe,QACjC,EAAK,aAAa,kBAAmB,SACrC,EAAK,aAAa,wBAAyB,IAC3C,EAAK,YAAc,EACZ,CACR,CE+FqB,CAAiB,EAAO,iBAC3C,EAAU,YAAY,KAAK,aAE3B,KAAK,oBACL,KAAK,oBACL,KAAK,0BACL,KAAK,qBACL,KAAK,YAAY,GAAe,iBAAkB,GAElD,KAAK,SAAW,IAAI,eAAA,KACnB,KAAK,qBAEN,KAAK,SAAS,QAAQ,GAClB,KAAK,YACR,KAAK,SAAS,QAAQ,KAAK,WAC3B,KAAK,UAAU,iBAAiB,SAAU,KAAK,gBAAiB,CAAE,SAAS,KAE5E,sBAAA,IAA4B,KAAK,mBAClC,CAEA,MAAA,CAAO,GACN,KAAK,QAAU,EAAK,GACpB,KAAK,YAAc,EACnB,MAAM,EAAkB,EAAgB,GAAM,SACxC,EAAkB,KAAK,UAAU,GAYvC,GAVI,IAAY,KAAK,SAAW,IAAY,KAAK,UAChD,KAAK,QAAU,EACf,KAAK,QAAU,EACf,KAAK,oBACL,KAAK,oBACL,KAAK,2BAEN,KAAK,6BAGD,KAAK,eAAgB,CACxB,MAAM,EAAS,KAAK,eAAe,cAAc,6BACjD,GAAI,EAAQ,CACX,MAAM,EAA4B,EAAK,OAAO,YAC9C,GAAwB,EAAuB,EAChD,CACD,CAEA,sBAAA,IAA4B,KAAK,mBAClC,CAEA,WAAA,CAAY,GACX,KAAK,SAAW,EAChB,KAAK,UAAU,gBAAgB,8BAA+B,GAC9D,MAAM,EAAgC,CACrC,KAAK,OACL,KAAK,OACL,KAAK,aACL,KAAK,aACL,KAAK,WACL,KAAK,WACL,KAAK,eACL,KAAK,YACL,KAAK,eACD,KAAK,eAAiB,CAAC,KAAK,gBAAkB,MAC9C,KAAK,WAAa,CAAC,KAAK,YAAc,IAE3C,IAAK,MAAM,KAAQ,EAAO,CACrB,aAAgB,oBAAmB,EAAK,SAAW,GACvD,IAAK,MAAM,KAAU,EAAK,iBAAiB,UAAW,EAAO,SAAW,CACzE,CACI,IACH,KAAK,kBACL,KAAK,qBACL,KAAK,mBAAmB,QACxB,KAAK,kBAAoB,KACzB,KAAK,wBAEP,CAEA,OAAA,GACC,KAAK,qBACL,KAAK,mBAAmB,UACxB,KAAK,wBACL,KAAK,SAAS,aACd,KAAK,WAAW,oBAAoB,SAAU,KAAK,iBACnD,KAAK,UAAU,oBAAoB,YAAa,KAAK,aACrD,KAAK,UAAU,oBAAoB,aAAc,KAAK,cACtD,KAAK,OAAO,SACZ,KAAK,OAAO,SACZ,KAAK,aAAa,SAClB,KAAK,aAAa,SAClB,KAAK,gBAAgB,SACrB,KAAK,YAAY,SACjB,KAAK,YAAY,SACjB,KAAK,WAAW,SAChB,KAAK,WAAW,SAChB,KAAK,eAAe,SACpB,KAAK,gBAAgB,SACrB,KAAK,YAAY,SACjB,KAAK,YAAY,QAClB,CAEA,kBAAA,GACC,MAAM,EAAa,KAAK,YAAY,cAAc,oBAC5C,EAAa,KAAK,YAAY,cAAc,oBAkClD,GAhCA,EAAW,iBAAiB,QAAA,KACvB,KAAK,gBAAkB,GAC1B,KAAK,oBACJ,GAA0B,KAAK,WAAY,KAAK,QAAS,KAAK,mBAKjE,EAAW,iBAAiB,QAAA,KACvB,KAAK,gBAAkB,GAC1B,KAAK,oBACJ,GAA6B,KAAK,WAAY,KAAK,QAAS,KAAK,mBAKpE,KAAK,WAAW,iBAAiB,QAAA,KAChC,KAAK,oBACJ,GAA0B,KAAK,WAAY,KAAK,QAAS,KAAK,YAIhE,KAAK,WAAW,iBAAiB,QAAA,KAChC,KAAK,oBACJ,GAA6B,KAAK,WAAY,KAAK,QAAS,KAAK,YAInE,KAAK,eAAe,iBAAiB,QAAA,KACpC,KAAK,oBAAoB,GAA6B,KAAK,WAAY,KAAK,YAGzE,KAAK,gBAAkB,KAAK,cAAe,CAC9C,MAAM,EAAM,KAAK,cACjB,KAAK,eAAe,iBAAiB,QAAA,KACpC,KAAK,uBAAuB,IAE9B,CAEA,GAAI,KAAK,YAAc,KAAK,cAAe,CAC1C,MAAM,EAAM,KAAK,cACjB,KAAK,WAAW,iBAAiB,QAAA,KAChC,KAAK,gBAAgB,IAEvB,CAEA,KAAK,UAAU,iBAAiB,YAAa,KAAK,aAClD,KAAK,UAAU,iBAAiB,aAAc,KAAK,aACpD,CAEA,eAAA,CAAwB,GACvB,GAAI,KAAK,UAAY,EAAQ,eAAgB,OAC7C,GAAI,KAAK,mBAAmB,SAG3B,OAFA,KAAK,kBAAkB,aACvB,KAAK,kBAAoB,MAI1B,IAAK,KAAK,WAAY,OAEtB,MAAM,EAAgB,KAAK,WAAW,wBAEtC,KAAK,kBAAoB,GACxB,KAAK,UACL,EACA,KAAK,QACL,EAAA,KAEC,KAAK,kBAAoB,MAE1B,KAAK,YACL,EACA,CACC,aAAc,KAAK,OACnB,eAAgB,KAAK,YAGxB,CAEA,sBAAA,CAA+B,GAC1B,KAAK,UAAY,EAAQ,iBACzB,KAAK,iBACR,KAAK,yBAIN,KAAK,iBAAmB,SAAS,cAAc,OAC/C,KAAK,iBAAiB,UAAY,6BAClC,EAAmB,KAAK,iBAAkB,CACzC,SAAU,WACV,IAAK,OACL,KAAM,OACN,OAAQ,UAET,KAAK,iBAAiB,aAAa,kBAAmB,SAEtD,GACC,KAAK,iBACL,EACA,KAAK,QAAA,KAEJ,KAAK,yBAEN,KAAK,QAGN,KAAK,UAAU,YAAY,KAAK,mBACjC,CAEA,qBAAA,GACK,KAAK,mBACR,KAAK,iBAAiB,SACtB,KAAK,iBAAmB,KAE1B,CAEA,mBAAA,CAA4B,IACvB,GAAO,KAAK,UAAa,KAAK,eAAe,gBAAgB,KAAK,WAAW,EAClF,CAEA,SAAA,CAAkB,GACjB,OAAO,EAAgB,GAAM,WAC9B,CAEA,gBAAA,GACC,MAAM,EAAS,GAAe,KAAK,QAAS,KAAK,WAC3C,EAAqB,KAAK,QAAQ,wBACxC,EAAmB,KAAK,OAAQ,CAC/B,KAAM,GAAG,OAAO,EAAO,UACvB,IAAK,GAAG,OAAO,EAAO,IAAM,UAE7B,EAAiB,KAAK,OAAQ,MAAO,GAAG,OAAO,EAAO,UDxWxD,SACC,EACA,EACA,EAAU,GAEV,MAAM,EAA0B,EAAO,SACvC,GAAuB,IAAnB,EAAQ,OAAc,OAE1B,MAAM,EACmB,iBAAjB,EACJ,MAAM,KAAK,CAAE,OAAQ,GAAQ,CAAI,EAAQ,KAAA,CACzC,QACA,SAAW,EAAe,EAAW,EACrC,MAAO,EAAe,KAEtB,GAAmB,EAAc,GAErC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACxC,MAAM,EAAI,EAAQ,GACZ,EAA6B,EAAM,GACpC,GACL,EAAmB,EAAG,CACrB,KAAM,GAAG,KAAK,MAAM,EAAI,cACxB,MAAO,GAAG,KAAK,MAAM,EAAI,YAE3B,CAEA,MAAM,EAAqB,EAAM,OAAA,CAC/B,EAAS,IAAQ,KAAK,IAAI,EAAS,EAAI,SAAW,EAAI,OACvD,GAED,EAAiB,EAAQ,QAAS,GAAG,KAAK,MAAM,OACjD,CCyUE,CAAmB,KAAK,OAAQ,KAAK,QAAS,KAAK,SDtUrD,SAAmC,EAAwB,GAC1D,MAAM,EAA0B,EAAO,SACvC,GAAuB,IAAnB,EAAQ,OAAc,OAE1B,MAAM,EAAuC,EAAQ,iBAAiB,uBAChE,EAAqB,EAAQ,wBACnC,IAAI,EAAc,EAElB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACxC,MAAM,EAAI,EAAQ,GACZ,EAAsC,EAAI,GAChD,GAAI,EAAI,CACP,MAAM,EAAgB,EAAG,wBACnB,EAAc,EAAK,IAAM,EAAU,IACnC,EAAiB,EAAK,QAAU,EAAG,aACzC,EAAmB,EAAG,CACrB,IAAK,GAAG,MACR,OAAQ,GAAG,QAEZ,EAAc,EAAM,CACrB,CACD,CAEA,EAAiB,EAAQ,SAAU,GAAG,MACvC,CC+SE,CAAmB,KAAK,OAAQ,KAAK,SD5SvC,SACC,EACA,EACA,EACA,GAEA,MAAM,EAAS,GAAe,EAAS,GACjC,EAAqB,EAAQ,wBAC7B,EAAsB,EAAU,QAAU,EAAQ,aAGxD,EAAmB,EAAY,CAC9B,MAAO,GAHmB,EAAU,OAAS,EAAQ,gBAIrD,KAAM,GAAG,EAAO,WAGjB,EAAmB,EAAY,CAC9B,OAAQ,GAAG,MACX,IAAK,GAAG,EAAO,SAEjB,CCyRE,CAAmB,KAAK,WAAY,KAAK,WAAY,KAAK,QAAS,KAAK,WACpE,KAAK,OAAO,cACf,KAAK,yBAAyB,EAAQ,EAExC,CAEA,wBAAA,CACC,EACA,GAEA,EAAmB,KAAK,aAAc,CACrC,KAAM,GAAG,OAAO,EAAO,UACvB,IAAK,GAAG,OAAO,EAAO,SACtB,MAAO,GAAG,OAAO,EAAU,WAC3B,OAAQ,GAAG,OAAO,EAAU,cAE7B,EAAmB,KAAK,aAAc,CACrC,KAAM,GAAG,OAAO,EAAO,UACvB,IAAK,GAAG,OAAO,EAAO,SACtB,MAAO,GAAG,OAAO,EAAU,WAC3B,OAAQ,GAAG,OAAO,EAAU,cAG7B,MAAM,EAA4D,QAA7C,iBAAiB,KAAK,SAAS,UAC9C,EAAQ,GAAmB,KAAK,QAAS,KAAK,SACpD,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,aAAa,SAAS,OAAQ,IAAS,CACvE,MAAM,EAAY,KAAK,aAAa,SAAS,GACvC,EAAM,EAAM,GAClB,IAAK,EAAK,SACV,MAAM,EAAmB,EAAM,EAAI,SAAW,EAAI,SAAW,EAAI,MACjE,EAAmB,EAAW,CAC7B,KAAM,GAAG,OAAO,OAChB,IAAK,MACL,OAAQ,GAAG,OAAO,EAAU,cAE7B,KAAK,qBAAqB,EAAW,SAAU,EAAO,EAAI,MAC3D,CAEA,MAAM,EAAuC,MAAM,KAClD,KAAK,QAAQ,iBAAiB,wBAE/B,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,aAAa,SAAS,OAAQ,IAAS,CACvE,MAAM,EAAY,KAAK,aAAa,SAAS,GACvC,EAAuC,EAAK,GAClD,IAAK,EAAK,SACV,MAAM,EAAgB,EAAI,wBACpB,EAAmB,EAAK,OAAS,EAAU,IACjD,EAAmB,EAAW,CAC7B,KAAM,MACN,IAAK,GAAG,OAAO,OACf,MAAO,GAAG,OAAO,EAAU,aAE5B,KAAK,qBAAqB,EAAW,MAAO,EAAO,EAAK,QAAU,EAAI,aACvE,CACD,CAEA,oBAAA,CACC,EACA,EACA,EACA,GAEA,MAAM,EACI,WAAT,EAAoB,KAAK,OAAO,iBAAmB,KAAK,OAAO,eAC1D,EACI,WAAT,EAAoB,KAAK,OAAO,iBAAmB,KAAK,OAAO,eAC1D,EAAkB,KAAK,MAAM,GACnC,EAAU,aAAa,gBAAiB,OAAO,IAC/C,EAAU,aAAa,gBAAiB,OAAO,IAC/C,EAAU,aAAa,gBAAiB,OAAO,IAC/C,EAAU,aAAa,iBAAkB,GAAG,OAAO,SACnD,EAAU,QAAQ,MAAQ,OAAO,EAClC,CAEA,WAAA,CAAoB,EAAqB,EAAwB,GAChE,GACC,KAAK,UACL,KAAK,eAAe,gBACF,IAAjB,EAAM,QAAsC,UAAtB,EAAM,YAE7B,OAED,MAAM,EAAsB,KAAK,qBAAqB,EAAM,GAC/C,OAAT,IACJ,EAAM,iBACN,EAAM,kBACN,KAAK,qBACL,KAAK,cAAgB,CACpB,OACA,QACA,UAAW,EAAM,UACjB,gBAA0B,WAAT,EAAoB,EAAM,QAAU,EAAM,QAC3D,UAAW,EACX,YAAa,GAEd,EAAO,eAAsC,oBAAoB,EAAM,WACvE,SAAS,iBAAiB,cAAe,KAAK,eAC9C,SAAS,iBAAiB,YAAa,KAAK,aAC5C,SAAS,iBAAiB,gBAAiB,KAAK,iBAChD,SAAS,iBAAiB,UAAW,KAAK,mBAAmB,GAC7D,KAAK,UAAU,UAAU,IAAI,4BAA4B,KACzD,KAAK,sBAAsB,EAAM,EAAM,QAAS,EAAM,SACvD,CAEA,UAAA,CAAmB,GAClB,MAAM,EAAgC,KAAK,cAC3C,IAAK,GAAW,EAAQ,YAAc,EAAM,UAAW,OACvD,EAAM,iBAEN,IAAI,GADwC,WAAjB,EAAQ,KAAoB,EAAM,QAAU,EAAM,SAC5C,EAAQ,gBACpB,WAAjB,EAAQ,MAAkE,QAA7C,iBAAiB,KAAK,SAAS,YAC/D,IAAS,GAEV,MAAM,EAAe,KAAK,eAAe,EAAQ,KAAM,EAAQ,UAAY,GAC3E,EAAQ,YAAc,EACtB,KAAK,mBAAmB,EAAQ,KAAM,EAAQ,MAAO,GACrD,KAAK,sBAAsB,EAAM,EAAM,QAAS,EAAM,SACtD,KAAK,kBACN,CAEA,YAAA,CAAqB,GACpB,MAAM,EAAgC,KAAK,cAC3C,IAAK,GAAW,EAAQ,YAAc,EAAM,UAAW,OACvD,EAAO,eAAsC,wBAAwB,EAAM,WAC3E,KAAK,cAAgB,KACrB,KAAK,wBACL,KAAK,sBACL,MAAM,EAAgB,KAAK,MAAM,EAAQ,aACnC,EAA0C,KAAK,oBAEnC,WAAjB,EAAQ,KACJ,GAAS,QACV,CAAE,KAAM,SAAU,QAAS,KAAK,QAAS,OAAQ,EAAQ,OACzD,CAAE,cAAe,IAEhB,GAAS,QACV,CAAE,KAAM,MAAO,QAAS,KAAK,QAAS,IAAK,EAAQ,OACnD,CAAE,eAAgB,KAGI,WAAjB,EAAQ,KAChB,KAAK,eAAe,SAAS,KAAK,OAAO,uBAAuB,EAAQ,MAAO,IAE/E,KAAK,eAAe,SAAS,KAAK,OAAO,4BAA4B,EAAQ,MAAO,IAJvE,KAAK,4BAMpB,CAEA,YAAA,CAAqB,GAChB,KAAK,eAAe,YAAc,EAAM,WAC5C,KAAK,oBACN,CAEA,kBAAA,GACM,KAAK,gBACV,KAAK,cAAgB,KACrB,KAAK,wBACL,KAAK,sBACL,KAAK,6BACL,KAAK,mBACN,CAEA,qBAAA,GACC,SAAS,oBAAoB,cAAe,KAAK,eACjD,SAAS,oBAAoB,YAAa,KAAK,aAC/C,SAAS,oBAAoB,gBAAiB,KAAK,iBACnD,SAAS,oBAAoB,UAAW,KAAK,mBAAmB,GAChE,KAAK,UAAU,UAAU,OAAO,mCAChC,KAAK,UAAU,UAAU,OAAO,+BACjC,CAEA,oBAAA,CAA6B,EAAwB,GACpD,GAAa,WAAT,EACH,OAAO,GAAmB,KAAK,QAAS,KAAK,SAAS,IAAQ,OAAS,KACxE,MAAM,EAAuC,MAAM,KAClD,KAAK,QAAQ,iBAAsC,wBAClD,GACF,OAAK,IACiB,EAAI,wBACd,QAAU,EAAI,eAFT,IAGlB,CAEA,cAAA,CAAuB,EAAwB,GAC9C,MAAM,EACI,WAAT,EAAoB,KAAK,OAAO,iBAAmB,KAAK,OAAO,eAC1D,EACI,WAAT,EAAoB,KAAK,OAAO,iBAAmB,KAAK,OAAO,eAChE,OAAO,KAAK,IAAI,EAAS,KAAK,IAAI,EAAS,GAC5C,CAEA,kBAAA,CAA2B,EAAwB,EAAe,GACjE,GAAa,WAAT,EAAmB,CACtB,MAAM,EAAS,KAAK,QAAQ,iBAAiB,2BAA2B,GAGxE,IAAK,EAAQ,OAIb,EAAoB,EAAQ,SAC5B,EAAO,aAAa,QAAS,OAAO,IACpC,MAAM,EAAqC,GAC1C,KAAK,YACL,KAAK,SAEA,EAA4B,EAAO,OAAA,CACvC,EAAe,EAAsB,IACrC,GAAS,IAAgB,EAAQ,EAAS,GAAS,KAAK,OAAO,kBAChE,GAcD,OAZA,EAAiB,KAAK,QAAS,WAAY,GAAG,OAAO,aAEpD,EAAO,QAAU,KAAK,SACtB,EAAO,MAAA,CACL,EAAsB,IACtB,IAAgB,GAAmB,OAAV,GAG3B,EAAiB,KAAK,QAAS,QAAS,GAAG,OAAO,QAElD,EAAoB,KAAK,QAAS,SAGpC,CACA,MAAM,EAAM,KAAK,QAAQ,iBAAiB,uBAAuB,GAG5D,IACL,EAAoB,EAAK,UACzB,EAAI,aAAa,SAAU,OAAO,IACnC,CAEA,0BAAA,GACC,MAAM,EAAqC,GAC1C,KAAK,YACL,KAAK,SAEA,EAA0C,MAAM,KACrD,KAAK,QAAQ,iBAAiB,4BAE/B,IAAI,EAAoB,EACxB,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAQ,OAAQ,IAAS,CACpD,MAAM,EAA0C,EAAQ,GACxD,IAAK,EAAQ,SACb,MAAM,EAAuB,EAAO,IAAU,KAC9C,EAAO,gBAAgB,SACT,OAAV,GACH,EAAoB,EAAQ,SAC5B,EAAO,gBAAgB,2BAEvB,EAAiB,EAAQ,QAAS,GAAG,OAAO,QAC5C,EAAO,aAAa,wBAAyB,OAAO,KAErD,GAAqB,GAAS,KAAK,OAAO,gBAC3C,CACA,EAAiB,KAAK,QAAS,WAAY,GAAG,OAAO,QACjD,EAAO,QAAU,KAAK,SAAW,EAAO,MAAO,GAAoB,OAAV,GAC5D,EAAiB,KAAK,QAAS,QAAS,GAAG,OAAO,QAElD,EAAoB,KAAK,QAAS,SAGnC,MAAM,EAAiC,EAAiB,KAAK,aACvD,EAAuC,MAAM,KAClD,KAAK,QAAQ,iBAAiB,wBAE/B,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IAAS,CACjD,MAAM,EAAuC,EAAK,GAC5C,EAAiC,EAAS,GAChD,IAAK,IAAQ,EAAS,SACtB,EAAI,gBAAgB,UACpB,MAAM,EAAwB,GAAwB,GACvC,OAAX,GACH,EAAoB,EAAK,UACzB,EAAI,gBAAgB,gCAEpB,EAAiB,EAAK,SAAU,GAAG,OAAO,QAC1C,EAAI,aAAa,6BAA8B,OAAO,IAExD,CACD,CAEA,qBAAA,CAA8B,EAAe,EAAiB,GAC7D,MAAM,EAAyB,KAAK,UAAU,wBAC9C,KAAK,gBAAgB,YAAc,GAAG,OAAO,KAAK,MAAM,SACxD,EAAmB,KAAK,gBAAiB,CACxC,KAAM,GAAG,OAAO,EAAU,EAAc,KAAO,KAAK,UAAU,WAAa,QAC3E,IAAK,GAAG,OAAO,EAAU,EAAc,IAAM,KAAK,UAAU,UAAY,UAEzE,KAAK,gBAAgB,UAAU,IAAI,iCACpC,CAEA,mBAAA,GACC,KAAK,gBAAgB,UAAU,OAAO,iCACvC,CAEA,kBAAA,CAA2B,GAC1B,KAAK,eAAiB,EAAO,MAC7B,MAAM,EAAS,GAAe,KAAK,QAAS,KAAK,WACjD,EAAmB,KAAK,YAAa,CACpC,IAAQ,EAAO,IAAM,EAAO,SAAW,EAAlC,KACL,KAAM,GAAG,EAAO,SAChB,MAAO,GAAG,KAAK,QAAQ,kBAExB,KAAK,YAAY,UAAU,IAAI,4BAChC,CAEA,gBAAA,CAAyB,GACxB,KAAK,eAAiB,EAAO,MAC7B,MAAM,EAAS,GAAe,KAAK,QAAS,KAAK,WACjD,EAAmB,KAAK,YAAa,CACpC,KAAS,EAAO,KAAO,EAAO,SAAW,EAAnC,KACN,IAAK,GAAG,EAAO,QACf,OAAQ,GAAG,KAAK,QAAQ,mBAEzB,KAAK,YAAY,UAAU,IAAI,4BAChC,CAEA,eAAA,GACC,KAAK,qBACL,KAAK,kBACN,CAEA,kBAAA,GACC,KAAK,YAAY,UAAU,OAAO,6BAClC,KAAK,gBAAiB,CACvB,CAEA,gBAAA,GACC,KAAK,YAAY,UAAU,OAAO,6BAClC,KAAK,gBAAiB,CACvB,CAEA,iBAAA,GACC,KAAK,OAAO,UAAY,GACxB,IAAK,IAAI,EAAI,EAAG,EAAI,KAAK,QAAS,IAAK,CACtC,MAAM,EAAyB,GAC9B,kBACA,EACA,KAAK,OAAO,aACX,IACA,KAAK,oBACJ,GAA6B,KAAK,WAAY,KAAK,QAAS,KAG9D,CACC,MAAO,KAAK,OAAO,kBAAkB,GACrC,SAAW,GAAgB,KAAK,aAAa,KAG/C,KAAK,OAAO,YAAY,EACzB,CACD,CAEA,iBAAA,GACC,KAAK,OAAO,UAAY,GACxB,IAAK,IAAI,EAAI,EAAG,EAAI,KAAK,QAAS,IAAK,CACtC,MAAM,EAAyB,GAC9B,kBACA,EACA,KAAK,OAAO,UACX,IACA,KAAK,oBAAoB,GAA0B,KAAK,WAAY,KAAK,QAAS,KAEnF,CACC,MAAO,KAAK,OAAO,eAAe,GAClC,SAAW,GAAgB,KAAK,UAAU,KAG5C,KAAK,OAAO,YAAY,EACzB,CACD,CAEA,uBAAA,GAGC,GAFA,KAAK,aAAa,UAAY,GAC9B,KAAK,aAAa,UAAY,IACzB,KAAK,OAAO,aAAc,OAC/B,MAAM,EAAuB,KAAK,OAAO,mBACxC,KAAK,OAAO,qBACZ,KAAK,OAAO,2BAEb,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,QAAS,IAAS,CAClD,MAAM,EAA+B,GACpC,SACA,EACA,KAAK,OAAO,2BAA2B,GACvC,GAED,EAAU,iBAAiB,cAAgB,GAC1C,KAAK,YAAY,EAAO,SAAU,IAEnC,EAAU,iBAAiB,UAAY,GACtC,KAAK,mBAAmB,EAAO,SAAU,IAE1C,KAAK,aAAa,YAAY,EAC/B,CACA,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,QAAS,IAAS,CAClD,MAAM,EAA+B,GACpC,MACA,EACA,KAAK,OAAO,wBAAwB,GACpC,GAED,EAAU,iBAAiB,cAAgB,GAC1C,KAAK,YAAY,EAAO,MAAO,IAEhC,EAAU,iBAAiB,UAAY,GACtC,KAAK,mBAAmB,EAAO,MAAO,IAEvC,KAAK,aAAa,YAAY,EAC/B,CACA,KAAK,YAAY,KAAK,SACvB,CAEA,kBAAA,CAA2B,EAAsB,EAAwB,GACxE,GAAI,KAAK,UAAY,KAAK,eAAe,eAAgB,OACzD,MAAM,EAA0C,KAAK,mBACrD,IAAK,EAAS,OACd,MAAM,EAA+B,EAAM,cAE3C,GAAkB,WAAd,EAAM,KAAkC,cAAd,EAAM,IAAqB,CACxD,EAAM,iBACN,EAAM,kBACN,MAAM,EACI,WAAT,EACI,CAAE,KAAM,SAAU,QAAS,KAAK,QAAS,OAAQ,GACjD,CAAE,KAAM,MAAO,QAAS,KAAK,QAAS,IAAK,GAC1C,EAAqB,WAAT,EAAoB,gBAAkB,iBASxD,YARI,EAAQ,UAAU,EAAQ,KAC7B,KAAK,2BAA2B,GAChC,KAAK,eAAe,SACV,WAAT,EACG,KAAK,OAAO,yBAAyB,GACrC,KAAK,OAAO,8BAA8B,KAIhD,CAEA,MAAM,EAAgC,WAAT,EAAoB,YAAc,UACzD,EAAgC,WAAT,EAAoB,aAAe,YAChE,GAAI,EAAM,MAAQ,GAAgB,EAAM,MAAQ,EAAc,OAC9D,EAAM,iBACN,EAAM,kBACN,MAAM,EAAyB,KAAK,qBAAqB,EAAM,GAC/D,GAAgB,OAAZ,EAAkB,OACtB,MAAM,EAAe,EAAM,SACxB,KAAK,OAAO,0BACZ,KAAK,OAAO,qBACf,IAAI,EAAoB,EAAM,MAAQ,EAAe,GAAI,EAC5C,WAAT,GAAkE,QAA7C,iBAAiB,KAAK,SAAS,YACvD,EAA0B,IAAd,GAAkB,EAAK,GAEpC,MAAM,EAAgB,KAAK,MAAM,KAAK,eAAe,EAAM,EAAU,EAAO,KAElE,WAAT,EACG,EAAQ,QACR,CAAE,KAAM,SAAU,QAAS,KAAK,QAAS,OAAQ,GACjD,CAAE,cAAe,IAEjB,EAAQ,QACR,CAAE,KAAM,MAAO,QAAS,KAAK,QAAS,IAAK,GAC3C,CAAE,eAAgB,OAGtB,KAAK,2BAA2B,GAChC,KAAK,eAAe,SACV,WAAT,EACG,KAAK,OAAO,uBAAuB,EAAO,GAC1C,KAAK,OAAO,4BAA4B,EAAO,IAEnD,KAAK,qBAAqB,EAAW,EAAM,EAAO,GACnD,CAEA,0BAAA,CAAmC,GAC9B,EAAU,aAAa,EAAU,MAAM,CAAE,eAAe,GAC7D,CAEA,YAAA,CAAqB,GAChB,KAAK,UAA6B,IAAjB,KAAK,UAC1B,KAAK,uBAAuB,iBAAiB,CAC5C,QAAS,KAAK,QACd,QAAS,EACT,QAAS,EACT,MAAO,KAAK,QAAU,EACtB,MAAO,IAER,KAAK,6BACN,CAEA,SAAA,CAAkB,GACb,KAAK,UAA6B,IAAjB,KAAK,UAC1B,KAAK,uBAAuB,iBAAiB,CAC5C,QAAS,KAAK,QACd,QAAS,EACT,QAAS,EACT,MAAO,EACP,MAAO,KAAK,QAAU,IAEvB,KAAK,6BACN,CAEA,0BAAA,GACC,MAAM,EAAQ,KAAK,uBAAuB,mBAC1C,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,OAAO,SAAS,OAAQ,IAAS,CACjE,MAAM,IACH,GACF,EAAM,UAAY,KAAK,SACvB,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,GACzC,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,GACA,IAAzC,KAAK,IAAI,EAAM,QAAS,EAAM,QAC9B,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,KAAK,QAAU,EACzD,KAAK,OAAO,SAAS,IAClB,cAAc,wBACd,aAAa,eAAgB,EAAW,OAAS,QACrD,CACA,IAAK,IAAI,EAAQ,EAAG,EAAQ,KAAK,OAAO,SAAS,OAAQ,IAAS,CACjE,MAAM,IACH,GACF,EAAM,UAAY,KAAK,SACvB,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,GACzC,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,GACA,IAAzC,KAAK,IAAI,EAAM,QAAS,EAAM,QAC9B,KAAK,IAAI,EAAM,QAAS,EAAM,SAAW,KAAK,QAAU,EACzD,KAAK,OAAO,SAAS,IAClB,cAAc,wBACd,aAAa,eAAgB,EAAW,OAAS,QACrD,CACD,CAEA,gBAAA,GACC,OAAO,KAAK,eAAe,aAAa,GACzC,CAEA,mBAAA,GACC,OAAO,KAAK,eAAe,aAAa,GACzC,GC99BK,GAAmD,CACxD,iBAAkB,GAClB,eAAgB,GAChB,iBAAkB,IAClB,eAAgB,IAChB,qBAAsB,EACtB,0BAA2B,GAC3B,cAAc,GA0Lf,SAAS,GAAiB,EAAyB,GAC9B,SAAhB,GACH,EAAiB,EAAO,sBAAuB,eAC/C,EAAM,UAAU,IAAI,8BACV,GACV,EAAiB,EAAO,sBAAuB,GAC/C,EAAM,UAAU,OAAO,+BAEvB,EAAoB,EAAO,uBAC3B,EAAM,UAAU,OAAO,6BAEzB,CA2CA,SAAgB,GACf,EACA,EACA,EACA,EAAoC,IAEpC,MAAM,EAAsB,EAAgB,GAAM,YAElD,IADA,EAAiB,EAAO,0BAA2B,GAAG,OAAO,EAAO,uBAC7D,EAAS,SAAS,OAAS,GAAa,EAAS,kBAAkB,SAC1E,KAAO,EAAS,SAAS,OAAS,GACjC,EAAS,YAAY,SAAS,cAAc,QAG7C,MAAM,EAAqB,EAAK,OAAO,eACjC,EAA6B,MAAM,QAAQ,GAAa,EAAY,GAC1E,IAAI,EAAoB,EACxB,IAAK,IAAI,EAAS,EAAG,EAAS,EAAa,IAAU,CACpD,MAAM,EAAM,EAAS,SAAS,GAC9B,IAAK,EAAK,SACV,EAAI,gBAAgB,SACpB,MAAM,EAAoB,EAAO,GAC3B,EACe,iBAAb,GAAyB,OAAO,SAAS,IAAa,EAAW,EACrE,OACA,OACkB,IAAlB,GACH,EAAiB,EAAK,QAAS,GAAG,OAAO,QACzC,EAAI,aAAa,wBAAyB,OAAO,MAEjD,EAAoB,EAAK,SACzB,EAAI,gBAAgB,0BAErB,GAAqB,GAAiB,EAAO,gBAC9C,CACI,EAAc,GACjB,EAAiB,EAAO,WAAY,GAAG,OAAO,QAE7C,EAAO,QAAU,GACjB,EAAO,MAAM,EAAG,GAAa,MAAO,GAA2B,iBAAV,GAErD,EAAiB,EAAO,QAAS,GAAG,OAAO,QAE3C,EAAoB,EAAO,WAG5B,EAAoB,EAAO,YAC3B,EAAoB,EAAO,SAE7B,CAGA,SAAgB,GAAsB,EAA0B,GAC/D,MAAM,EAAqB,EAAK,OAAO,YACvC,EAAI,gBAAgB,UACK,iBAAd,GAA0B,OAAO,SAAS,IAAc,EAAY,GAC9E,EAAiB,EAAK,SAAU,GAAG,OAAO,QAC1C,EAAI,aAAa,6BAA8B,OAAO,MAEtD,EAAoB,EAAK,UACzB,EAAI,gBAAgB,8BAEtB,CAEA,SAAS,GAAwB,EAAgC,GAChE,MAAM,EAA+C,MAAM,KAC1D,EAAM,iBAAiB,gBAExB,IAAK,MAAM,KAAW,EAAiB,GAAY,CAClD,MAAM,EAAM,EAAa,KACvB,GAAc,EAAU,aAAa,mBAAqB,EAAQ,IAEhE,GAAK,GAAsB,EAAK,EACrC,CACD,CAuBA,SAAS,GAAsB,GAC9B,IAAK,EAAO,OACZ,MAAQ,YAAa,EAAc,eAAgB,KAAoB,GAAS,EAChF,OAAO,OAAO,KAAK,GAAM,OAAS,EAAI,OAAO,CAC9C,CAEA,SAAS,GAAiB,GACzB,IAAK,EAAO,OACZ,MAAQ,YAAa,KAAiB,GAAS,EAC/C,OAAO,OAAO,KAAK,GAAM,OAAS,EAAI,OAAO,CAC9C,CC5RA,IAAM,GAAsC,CAC3C,cAAe,EACf,cAAe,KACZ,GACH,cAAc,GAcT,GAAa,oFAgCN,GAAb,MACC,GAAc,QACd,KAAgB,QAChB,SAAoB,GAEpB,OACA,OACA,iBAAyD,KACzD,sBAAqD,KACrD,iBAAgD,KAChD,QAAwC,KACxC,wBAAsD,IAAI,IAE1D,WAAA,CAAY,GACX,MAAM,EAA4B,GAAyB,GAAU,CAAC,GACtE,KAAK,OAAS,IACV,MACA,KACA,EACH,cAAe,GAAoB,GAAQ,cAAe,GAAe,eACzE,cAAe,GAAoB,GAAQ,cAAe,GAAe,eACzE,aAAc,GAAQ,eAAgB,EAExC,CAEA,UAAM,CAAK,GACV,KAAK,aAAe,EACnB,EACA,KAAK,OAAO,OACZ,GACA,IAED,EAAQ,mBhBpLe,g/jBgBqLvB,KAAK,QAAU,EAEf,KAAK,kBAAkB,GACvB,KAAK,kBAAkB,GACvB,GAAsB,EAAS,KAAK,QACpC,EAAQ,gBAAgB,wBAAA,IACvB,GAAsB,EAAS,KAAK,SLxJvC,SACC,EACA,EACA,EAA2C,CAAC,GAe5C,EAAQ,eAAe,CAZtB,IAAA,IAiCF,SAAmB,EAAwB,GAC1C,OAAO,GAAiB,EAAA,CAAU,EAAQ,EAAM,MAC3C,GAAyB,EAAS,EAAU,GAAG,KAE5C,EAAQ,gBAAyB,GAAY,EAAS,GAE/D,CAvCa,CAAU,EAAS,GAC9B,YAAA,IAyCF,SAAwB,GACvB,OAAO,GAAiB,EAAA,CAAU,EAAQ,EAAM,IACxC,GAAyB,EAAS,GAAU,GAAI,KAAU,EAEnE,CA7CqB,CAAe,GAClC,UAAA,IA+CF,SAAyB,GACxB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,IAAK,EAAY,GAAM,OAAO,EAC9B,GAA0B,IAAtB,EAAI,OAAO,OAAc,OAAO,EAGpC,IAiMF,SAA2B,EAAoB,GAC9C,MAAM,EAAQ,EAAM,SAAS,EAAa,SAC1C,MACiB,UAAhB,GAAO,MAAoB,EAAgB,GAAO,MAAM,IAAI,KAAK,KAAO,EAAa,MAEvF,CAvMwC,CAAkB,EAAO,GACpC,OAAO,EAElC,MAAM,EAAqB,EAAmB,EAAO,EAAS,QAC9D,OAAI,EAAI,OAAO,UAAY,GAEpB,GAAgB,EAAS,EAAS,UAE3C,CA5DmB,CAAgB,GACjC,OAAA,IA8DF,SAAsB,GACrB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,IAAK,EAAY,GAAM,OAAO,EAE9B,MAAM,EAAQ,EAAM,SAAS,EAAI,OAAO,SACxC,IAAK,EAAO,OAAO,EAEnB,MAAM,EAAmB,EAAe,GACxC,GAAI,EAAI,OAAO,SAAW,EAAU,OAAO,EAG3C,IAmLF,SAA0B,EAAoB,GAC7C,MAAM,EAAQ,EAAM,SAAS,EAAa,SAC1C,IAAK,GAAwB,UAAf,EAAM,KAAkB,OAAO,EAC7C,MAAM,EAAQ,EAAgB,GAAO,MACrC,OAAO,EAAM,EAAM,OAAS,IAAI,KAAK,KAAO,EAAa,MAC1D,CAzLwC,CAAiB,EAAO,GACnC,OAAO,EAElC,MAAM,EAAoB,GAAkB,EAAO,EAAS,QAC5D,OAAI,EAAI,OAAO,UAAY,GAEpB,GAAgB,EAAS,EAAS,UAE3C,CAhFgB,CAAa,GAC3B,UAAA,IAkFF,SAAyB,GACxB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,MAAM,EAAoB,GAAkB,EAAO,EAAS,QAC5D,OAAI,EAAI,OAAO,UAAY,IAEvB,EAAS,QAAU,EAAS,UACxB,GAAa,GAGd,GAAoB,EAAS,EAAS,QAAS,EAAS,OAAQ,EAAS,YAElF,CA7FmB,CAAgB,GACjC,QAAA,IA+FF,SAAuB,GACtB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,MAAM,EAAqB,EAAmB,EAAO,EAAS,QAC9D,OAAI,EAAI,OAAO,UAAY,KAEvB,EAAS,UAAY,IAElB,GAAoB,EAAS,EAAS,QAAS,EAAS,SAAW,EAAG,EAAS,WAExF,CAxGiB,CAAc,GAC7B,WAAA,IA0GF,SAA0B,GACzB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,IAAK,EAAY,GAAM,OAAO,EAE9B,MAAM,EAAQ,EAAM,SAAS,EAAI,OAAO,SACxC,IAAK,EAAO,OAAO,EAEnB,MAAM,EAAmB,EAAe,GACxC,GAAI,EAAI,OAAO,SAAW,EAAU,OAAO,EAE3C,MAAM,EAAoB,GAAkB,EAAO,EAAS,QAC5D,OAAI,EAAI,OAAO,UAAY,IAEpB,GAAyB,EAAS,EAAU,GAAG,KAAU,IAElE,CAzHoB,CAAiB,GACnC,UAAA,IA2HF,SAAyB,GACxB,OAAO,GAAiB,EAAA,CAAU,EAAO,EAAK,KAC7C,IAAK,EAAY,GAAM,OAAO,EAC9B,GAA0B,IAAtB,EAAI,OAAO,OAAc,OAAO,EAEpC,MAAM,EAAqB,EAAmB,EAAO,EAAS,QAC9D,OAAI,EAAI,OAAO,UAAY,IAEpB,GAAyB,EAAS,GAAU,GAAI,KAAS,IAElE,CArImB,CAAgB,GACjC,OAAA,IAAc,GAAa,GAC3B,YAAA,IAwJF,SACC,EACA,EACA,GAEA,OAAI,EAAQ,gBACL,GAAiB,EAAA,CAAU,EAAQ,EAAM,KAC/C,MAAM,EAAyB,EAAQ,eAGjC,EAAS,EAAU,cACxB,mBAAmB,EAAS,YAE7B,IAAK,EAAQ,OAAO,EAEpB,MAAM,EAAoB,EAAO,wBAC3B,EAAsB,IAAI,QAC/B,EAAS,KAAO,EAAS,MAAQ,EACjC,EAAS,OACT,EACA,GAQK,EAJiB,EAAU,cAChC,mBAAmB,EAAS,6BAGwB,EAErD,GAAmB,IAAI,IAAU,QACjC,MAAM,EAAqC,EAAyB,IAAc,EAC5E,EAA4C,GACjD,EACA,EACA,EAAS,QACT,EAAA,KAEC,GAAmB,OAAO,IAE3B,OACA,EACA,CACC,WAAY,GAA2B,EAAS,GAChD,eACA,mBAKF,OAFA,GAAmB,IAAI,EAAS,IAEzB,GAET,CA5MqB,CAAkB,EAAS,EAAQ,IAGxB,CAAE,SAAU,WAC5C,CKuIE,CAAqB,EAAS,KAAK,OAAQ,KAAK,QAChD,KAAK,oBAAoB,GACzB,KAAK,iBV5JP,SAA4C,GAC3C,IAAI,EAAkC,KAClC,EAAmC,KACnC,EAAoC,GACpC,iBAAgC,IAAI,IAExC,SAAS,IACR,MAAM,EAAU,IAChB,IAAK,EAGJ,OAFA,EAAgB,QAChB,iBAAkB,IAAI,KAIvB,MAAM,KAAE,EAAA,MAAM,GAAU,EAElB,EAAiB,EACrB,aAAa,CACb,QAAS,EAAM,QACf,WAAY,EAAM,QAClB,MAAO,EAAM,MACb,SAAU,EAAM,QAEhB,IAAK,GAAU,EAAM,KAAK,IAE5B,EAAgB,EAChB,EAAkB,IAAI,IAAI,EAC3B,CAEA,SAAS,IAIR,IAAK,IAAkB,EAAmB,OAAO,KACjD,MAAM,EAAQ,EAAQ,WAAW,SAAS,EAAc,SACxD,IAAK,GAAwB,UAAf,EAAM,KAEnB,OADA,IACO,KAER,MAAM,EAAkB,EAAgB,GACxC,OAAI,GAAwB,KAAU,GACrC,IACO,MAED,CAAE,MAAO,EAAe,OAChC,CAEA,SAAS,IACR,EAAgB,KAChB,EAAoB,KACpB,EAAgB,GAChB,iBAAkB,IAAI,GACvB,CAEA,MAAM,EAAiC,CACtC,iBAAA,IACQ,KAA2B,OAAS,KAG5C,gBAAA,CAAiB,GAChB,MAAM,EAAqB,EAAQ,WACnC,IAAI,EAA8B,KAClC,GAAK,EAEE,CACN,MAAM,EAAQ,EAAQ,WAAW,SAAS,EAAM,SAC1C,EAAyC,UAAhB,GAAO,KAAmB,EAAgB,GAAS,KAC5E,EAAiC,CACtC,EAAM,QACN,EAAM,QACN,EAAM,MACN,EAAM,OAaD,EAVH,GACF,EAAY,MAAM,OAAO,YACzB,EAAM,SAAW,GACjB,EAAM,OAAS,GACf,EAAM,SAAW,GACjB,EAAM,OAAS,GACf,EAAM,QAAU,EAAK,UACrB,EAAM,MAAQ,EAAK,UACnB,EAAM,QAAU,EAAK,aACrB,EAAM,MAAQ,EAAK,aAEV,EACN,EAAqB,EAAM,CAC3B,QAAS,EAAM,QACf,WAAY,EAAM,QAClB,MAAO,EAAM,MACb,SAAU,EAAM,QAEhB,KACC,GAAS,GAGb,EAAgB,CACf,QAAS,EAAM,QACf,QAAS,EAAS,QAClB,QAAS,EAAS,WAClB,MAAO,EAAS,MAChB,MAAO,EAAS,UAEjB,EAAoB,GAAwB,GAC5C,EAAc,EAAK,OAAO,EAAS,QAAS,EAAS,aAAa,KAAK,IAAM,MAV7E,GAYF,MA3CC,IA4CD,IACA,MAAM,EAAe,EAAgB,EAAM,WACxC,EAAiB,EAAO,EAAM,UAAU,OAAO,SAC/C,KACG,EACL,GAAe,GAAc,UAAY,GAAO,QAC7C,EAAyB,EAAmB,EAAO,GAAc,GACjE,EAAM,UACV,EAAQ,SAAS,EAAM,YAAY,OAAO,aAAa,GAAW,QACnE,EAEA,oBAAA,GACC,GACD,EAEA,mBAAA,KACC,IACO,GAGR,WAAW,IACV,IACO,EAAgB,IAAI,KAK7B,OADA,EAAQ,gBAAgB,GAA0B,GAC3C,CACR,CUqB0B,CAA4B,GTlFtD,SACC,EACA,EAAqC,CAAC,GAEtC,MAAM,EAAoC,GAAyB,GAE7D,EAA8B,CACnC,gBAAA,GACC,MAAM,EAAkC,GAAuB,GAC/D,OAAO,EAAW,GAAiB,GAAY,IAChD,EAEA,gBAAA,CAAiB,GAChB,GAAI,EAAQ,aAAc,OAAO,EACjC,MAAM,EAAkC,GAAuB,GAC/D,QAAO,GAAW,GAAU,EAAS,EAAU,EAAO,EACvD,EAEA,kBAAA,CAAmB,GAClB,GAAI,EAAQ,aAAc,OAAO,EACjC,MAAM,EAAkC,GAAuB,GAC/D,QAAO,GAAW,GAAkB,EAAS,EAAU,EAAW,EACnE,EAEA,OAAA,CAAQ,GACP,MAAM,EAAkC,GAAsB,EAAQ,WAAY,GAClF,OAAO,EAAW,GAAiB,GAAY,IAChD,EAEA,OAAA,CAAQ,EAAyB,GAChC,GAAI,EAAQ,aAAc,OAAO,EACjC,MAAM,EAAkC,GAAsB,EAAQ,WAAY,GAClF,QAAO,GAAW,GAAU,EAAS,EAAU,EAAO,EACvD,EAEA,SAAA,CAAU,EAAyB,GAClC,GAAI,EAAQ,aAAc,OAAO,EACjC,MAAM,EAAkC,GAAsB,EAAQ,WAAY,GAClF,QAAO,GAAW,GAAkB,EAAS,EAAU,EAAW,EACnE,GAGD,EAAQ,gBAAgB,GAAuB,EAEhD,CSuCE,CAAyB,EAAS,KAAK,OACxC,CAEA,OAAA,GACK,KAAK,SAAW,KAAK,mBACxB,KAAK,sBVRR,SACC,EACA,GAEA,MAAM,EAAyB,EAAQ,eACvC,IAAI,EAOO,KACP,GAAa,EAEjB,SAAS,EAAgB,GACxB,GAAiB,IAAb,EAAE,QAAgB,EAAQ,eAAgB,OAC9C,IAAK,EAAyB,EAAG,GAAY,OAE7C,MAAM,EADS,EAAE,OACyB,QAAQ,qBAClD,IAAK,EAIJ,OAHA,EAAa,KACb,GAAa,OACb,EAAQ,iBAAiB,MAI1B,GAAK,EAAE,UAuBA,GAAI,EAAY,CAItB,MAAM,EAAW,EAFU,EAAQ,WACpB,EAAO,aAAa,kBAEnC,IAAK,GAAY,EAAS,UAAY,EAAW,QAGhD,OAFA,EAAa,UACb,EAAQ,iBAAiB,MAI1B,EAAE,iBACF,EAAQ,iBAAiB,GAAmB,EAAY,GACzD,MApCiB,CAEhB,EAAQ,iBAAiB,MAIzB,MAAM,EAAW,EAFU,EAAQ,WACpB,EAAO,aAAa,kBAEnC,IAAK,EAGJ,OAFA,EAAa,UACb,GAAa,GAId,EAAa,CACZ,QAAS,EAAS,QAClB,OAAQ,EAAS,OACjB,SAAU,EAAS,SACnB,OAAQ,EAAS,OACjB,SAAU,EAAS,SACnB,OAAQ,EAAS,QAElB,GAAa,CAEd,CAcD,CAEA,SAAS,EAAgB,GACxB,GAAI,EAAQ,iBAAmB,IAAe,EAAY,OAG1D,MAAM,EADS,EAAE,OACyB,QAAQ,qBAClD,IAAK,EAAQ,OAIb,MAAM,EAAW,EAFU,EAAQ,WACpB,EAAO,aAAa,kBAE9B,GAAY,EAAS,UAAY,EAAW,SAG7C,EAAS,SAAW,EAAW,SAClC,EAAE,iBACF,EAAQ,iBAAiB,GAAmB,EAAY,IAE1D,CAEA,SAAS,IACR,GAAa,CACd,CAMA,OAJA,EAAU,iBAAiB,YAAa,GACxC,EAAU,iBAAiB,YAAa,GACxC,SAAS,iBAAiB,UAAW,GAErC,KACC,EAAU,oBAAoB,YAAa,GAC3C,EAAU,oBAAoB,YAAa,GAC3C,SAAS,oBAAoB,UAAW,GAE1C,CU1FgC,CAAsB,KAAK,QAAS,KAAK,kBACtE,KAAK,iBXhLR,SACC,EACA,GAEA,MAAM,EAAyB,EAAQ,eAEjC,EAAU,IACV,EAAyB,EAAO,IACrC,GAAwB,EAAO,EAAS,IAEnC,EAAS,IACT,EAAyB,EAAO,KACjC,EAAQ,gBACP,GAAwB,EAAO,EAAS,IA2C/C,SACC,EACA,GAEA,MAAM,EAA0B,EAAiB,mBACjD,IAAK,EAAO,OACZ,MAAM,EAAqB,EAAQ,WAC7B,EAA+B,EAAM,SAAS,EAAM,SAC1D,IAAK,GAAwB,UAAf,EAAM,KAAkB,OACtC,MAAM,EAAQ,EAAgB,GAAO,aAAa,CACjD,QAAS,EAAM,QACf,WAAY,EAAM,QAClB,MAAO,EAAM,MACb,SAAU,EAAM,QAEjB,GAAqB,IAAjB,EAAM,OAAc,OAExB,MAAM,EAA8B,EAAM,YAAY,SACtD,IAAI,EAAmC,KACvC,IAAK,MAAM,KAAS,EAAO,CAC1B,MAAM,EAAuC,EAAM,YAAY,EAAM,KAAK,IAC1E,IAAK,EAAM,OAEX,IAAK,IAAI,EAD8B,EAAiB,EAAM,MACpC,OAAS,EAAG,GAAS,EAAG,IAAS,EAAQ,WAAW,EAAM,GACpF,MAAM,EAAuB,IACvB,EAAuB,EAC5B,EAAS,aACT,CAAC,EAAe,KAChB,GAED,EAAQ,WAAW,EAAM,EAAG,GAC5B,IAAqB,CACtB,CACK,IACL,EAAQ,aAAa,EAAyB,EAAkB,IAChE,EAAQ,SAAS,EAAQ,SACzB,EAAiB,uBAClB,CA/EE,CAAwB,EAAS,KAMlC,OAFA,EAAU,iBAAiB,OAAQ,GAAQ,GAC3C,EAAU,iBAAiB,MAAO,GAAO,GACzC,KACC,EAAU,oBAAoB,OAAQ,GAAQ,GAC9C,EAAU,oBAAoB,MAAO,GAAO,GAE9C,CWwJ2B,CAAsB,KAAK,QAAS,KAAK,kBAEnE,CAEA,OAAA,GACK,KAAK,SAAS,GAAuB,KAAK,SAC9C,KAAK,0BACL,KAAK,sBAAwB,KAC7B,KAAK,qBACL,KAAK,iBAAmB,KACxB,KAAK,iBAAmB,KACxB,KAAK,SAAS,QACd,KAAK,QAAU,IAChB,CAEA,gBAAA,CAAiB,GAChB,IAAK,MAAM,KAAY,KAAK,SAAU,EAAS,YAAY,GACvD,GAAY,KAAK,SAAS,GAAuB,KAAK,QAC3D,CAEA,WAAA,CAAY,GACX,IAAK,KAAK,iBAAkB,OAAO,EAAmB,MAGtD,IADc,KAAK,iBAAiB,mBACxB,OAAO,EAAmB,MAKtC,MAAM,EAAM,EAAM,UAClB,IAAK,EAAgB,KAAS,GAAc,EAAO,EAAI,OAAO,SAE7D,OADA,KAAK,iBAAiB,uBACf,EAAmB,MAG3B,MAAM,EAA8B,KAAK,iBAAiB,qBAC1D,GAAuB,IAAnB,EAAQ,OAAc,OAAO,EAAmB,MAEpD,MAAM,EAA4B,GAClC,IAAK,MAAM,KAAU,EACpB,EAAY,KAAK,EAAe,EAAQ,CAAE,MAAO,kCAGlD,OAAO,EAAmB,OAAO,EAClC,CAEA,iBAAA,CAA0B,GACzB,MAAM,EAA8B,KAAK,OACzC,EAAQ,iBAAiB,CACxB,KAAM,QACN,MAAO,CACN,eAAgB,CAAE,YAAY,IAE/B,MAAO,QACP,QAAS,CAAE,MAAO,CAAC,aAAc,IAAK,GACtC,WAAW,EACX,YAAY,EACZ,eAAiB,GAkJpB,SAA6B,EAAiB,GAC7C,MAAM,EAA2B,GAAmB,EAAK,MAAO,CAC/D,cACA,mBAEK,EAAuB,EAAK,OAAO,aACrB,SAAhB,GAAkD,iBAAhB,GAA4B,EAAgB,MACjF,EAAM,YAAc,GAGrB,MAAM,EAAO,EAAgB,GACvB,EAAqB,EAAK,OAAO,eACvC,GAAI,MAAM,QAAQ,IAAc,EAAK,YAAc,EAAG,CACrD,MAAM,EAA4B,MAAM,KACvC,CAAE,OAAQ,EAAK,aAAY,CAC1B,EAAS,KACT,MAAM,EAA6B,EAAoC,EAAU,IACjF,YAAe,IAAX,EAA6B,KAC1B,KAAK,IAAI,EAAO,iBAAkB,KAAK,IAAI,EAAO,iBAAkB,MAGzE,EAAO,KAAM,GAAoB,OAAV,KAC1B,EAAM,eAAiB,OAAO,OAAO,GAEvC,CACA,OAAO,OAAO,KAAK,GAAO,OAAS,EAAI,OAAQ,CAChD,CA5KwC,CAAoB,EAAM,KAAK,QACpE,cAAe,GACf,wBAAyB,EACzB,KAAA,CAAM,GACL,MAAM,EAA0B,SAAS,cAAc,OAIvD,OAHA,EAAQ,UAAY,wBACpB,EAAQ,aAAa,gBAAiB,EAAK,IAC3C,EAAQ,aAAa,OAAQ,SACtB,CACR,EACA,MAAA,CAAO,EAAM,EAAS,GACrB,MAAM,EAAkC,EAAK,OAAO,YAE9C,EAAS,GAAwB,EAD1B,EAAgB,GACqB,aAC5C,EApJV,SACC,EACA,EACA,GAEA,MAAM,EAAyB,EAAO,OAAA,CACpC,EAAO,IAAU,GAAS,GAAS,GACpC,GAOK,EAAY,mDAJjB,EAAO,OAAS,GAAK,EAAO,MAAO,GAAqC,OAAV,GAE5D,UAAU,OAAO,OACjB,6BACiE,OAAO,OAC3E,OAAK,EACe,SAAhB,EACI,GAAG,4BAEP,EAAgB,GACZ,GAAG,iBAAyB,EAAW,KAExC,EAPkB,CAQ1B,CA6H0B,CAAgB,EAAa,EAAQ,EAAO,kBASlE,MAAO,SARc,GAAK,UAAU,IAAU,WAAW,kBACjC,EACtB,IAAK,GACK,OAAV,EACG,QACA,OAAO,EAA6B,EAAmC,QAAS,EAAO,OAE1F,KAAK,wBACuD,mBAC/D,EACA,SAAU,CACT,KAAM,CAAC,QAAS,WAAY,MAAO,QAAS,QAAS,SACrD,MAAO,CAAC,EAAmC,QAAS,WAItD,EAAQ,iBAAiB,CACxB,KAAM,YACN,MAAO,gBACP,QAAS,CAAE,MAAO,CAAC,cAAe,IAAK,GACvC,eAAiB,GA2IpB,SACC,EACA,GAEA,MAAM,EAA2B,GAAmB,EAAK,MAAO,CAAC,gBAC3D,EAA6B,EAAoC,EAAK,OAAO,aAInF,YAHe,IAAX,IACH,EAAM,YAAc,KAAK,IAAI,EAAO,eAAgB,KAAK,IAAI,EAAO,eAAgB,KAE9E,OAAO,KAAK,GAAO,OAAS,EAAI,OAAQ,CAChD,CArJwC,CAAuB,EAAM,KAAK,QACvE,KAAA,CAAM,GACL,MAAM,EAA0B,SAAS,cAAc,MAIvD,OAHA,EAAG,aAAa,gBAAiB,EAAK,IACtC,EAAG,aAAa,OAAQ,OACxB,EAAG,aAAa,OAAQ,aACjB,CACR,EACA,OAAA,CAAO,EAAM,EAAS,IAOd,MANe,EACrB,EACA,SACA,EAAK,OAAO,YACZ,MAEqB,SAEvB,SAAU,CACT,KAAM,CAAC,MACP,MAAO,CAAC,EAAqC,aAI/C,EAAQ,iBAAiB,CACxB,KAAM,aACN,MAAO,gBACP,QAAS,CACR,MAAO,CACN,YACA,YACA,UACA,aACA,QACA,QACA,mBAED,IAAK,GAEN,WAAW,EACX,eAAgB,GAChB,KAAA,CAAM,GACL,MAAM,EAA2B,SAAS,cAAc,MAIxD,OAHA,EAAG,aAAa,gBAAiB,EAAK,IACtC,EAAG,aAAa,OAAQ,QACxB,EAAG,aAAa,OAAQ,cACjB,CACR,EACA,MAAA,CAAO,EAAM,EAAS,GACrB,MAAM,EAAmB,EAAK,OAAO,SAAsB,EACrD,EAAmB,EAAK,OAAO,SAAsB,EAErD,EAAkB,CADE,GAAK,UAAU,KAAe,WAAW,OAInE,OAFI,EAAU,GAAG,EAAM,KAAK,aAAa,MACrC,EAAU,GAAG,EAAM,KAAK,aAAa,MAClC,MAAM,EAAM,KAAK,OAAO,QAChC,EACA,SAAU,CAAE,KAAM,CAAC,KAAM,MAAO,MAAO,CAAC,UAAW,aAErD,CAEA,iBAAA,CAA0B,GACR,EAAQ,oBAEzB,EAAQ,iBACP,QDlTH,SACC,EACA,EACA,EAAsB,GACtB,EAA0C,GAC1C,EAAoC,CAAC,GAErC,MAAA,CACC,EACA,EACA,KAEA,IAAI,EAAyB,EAE7B,MAAM,EAA4B,SAAS,cAAc,OACzD,EAAU,UAAY,iBACtB,EAAU,aAAa,gBAAiB,EAAK,IAC7C,EAAU,aAAa,kBAAmB,QAG1C,MAAM,EAA0B,SAAS,cAAc,OACvD,EAAQ,UAAY,wBACpB,EAAQ,aAAa,OAAQ,SAE7B,MAAM,EAA0B,SAAS,cAAc,SACvD,EAAM,UAAY,gBAClB,EAAM,aAAa,OAAQ,SAE3B,MAAM,EAAgC,SAAS,cAAc,YAC7D,EAAS,UAAY,wBACrB,EAAM,YAAY,GAGlB,MAAM,EAD6B,EAAiB,GACrB,OACzB,EAAoB,EAAgB,GAAM,YAChD,EAAM,aAAa,aAAc,EAAO,eAAe,EAAW,IAClE,EAAM,aAAa,mBAAoB,EAAO,sBAE9C,MAAM,EAAiC,SAAS,cAAc,SAC9D,EAAM,YAAY,GAClB,EAAQ,YAAY,GACpB,EAAU,YAAY,GAGtB,GAAiB,EAAO,EAAK,OAAO,aACpC,GAAsB,EAAO,EAAU,EAAM,GAG7C,MAAM,EAA6B,SAAS,cAAc,OAC1D,EAAW,UAAY,kBACvB,EAAW,aAAa,YAAa,UACrC,EAAW,aAAa,cAAe,QACvC,EAAU,YAAY,GAGtB,MAAM,ED05BR,SACC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,GAEA,OAAO,IAAI,GACV,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAEF,CC96BwC,CACrC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,GAED,EAAS,YAAY,GAAe,iBAAkB,GACtD,EAAU,oBAAoB,GAC9B,IAAI,GAAY,EAChB,MAAM,EAA4B,sBAAA,KAC7B,IAIJ,GAAiB,EAAO,EAAY,OAAO,aAC3C,GAAsB,EAAO,EAAU,EAAa,GACpD,GAAwB,EAAO,MAIhC,IAAI,EAAmD,KACnD,EAAiB,EAAK,GAE1B,MAAM,EAAiB,IACtB,IAAK,GAAiB,EAAc,eAAgB,OACpD,EAAE,iBACF,EAAE,kBAEF,GAAmB,QAEnB,MAAM,EAAgB,IAAI,QAAQ,EAAE,QAAS,EAAE,QAAS,EAAG,GACrD,EAAQ,EAAE,QAA2B,QAAQ,qBAC7C,EAAS,GAAM,aAAa,iBAC5B,EAAe,EAClB,EAAiB,EAAc,WAAY,GAC3C,KACG,IAAwB,IAC1B,EAAc,aAAa,KAA2B,WAAW,KACnE,GAEI,EACL,EAAyB,KAAe,aAAgB,YAAc,EAAO,MAC9E,EAAoB,GACnB,EACA,EACA,EACA,EAAA,KAEC,EAAoB,MAErB,OACA,EACA,CACC,WACC,IAAiB,EACd,CACA,KAAM,OACN,QAAS,EAAa,QACtB,IAAK,EAAa,SAClB,OAAQ,EAAa,eAErB,EACJ,eACA,oBAMH,OAFA,EAAM,iBAAiB,cAAe,GAE/B,CACN,IAAK,EACL,WAAY,EACZ,MAAA,CAAO,GACN,GAAyB,UAArB,EAAY,KAAkB,OAAO,EAEzC,IA0KJ,SAA8B,EAAqB,GAClD,GAAI,EAAS,KAAO,EAAK,IAAM,EAAS,SAAW,EAAK,OAAQ,OAAO,EACvE,IAAK,EAAgB,GAAsB,EAAS,OAAQ,GAAsB,EAAK,QACtF,OAAO,EAER,MAAM,EAAqC,EAAiB,GACtD,EAAiC,EAAiB,GACxD,OAAI,EAAa,SAAW,EAAS,QAC9B,EAAa,MAAA,CAAO,EAAa,KACvC,MAAM,EAAiC,EAAS,GAChD,YACa,IAAZ,GACA,EAAY,KAAO,EAAQ,IAC3B,EAAY,SAAW,EAAQ,QAC/B,EAAY,WAAa,EAAQ,UACjC,EAAgB,GAAiB,EAAY,OAAQ,GAAiB,EAAQ,SAGjF,CA7LsC,CAAqB,EAAa,GAC7C,OAAO,EAE9B,EAAiB,EAAY,GAC7B,EAAU,aAAa,gBAAiB,EAAY,IACpD,EAAU,aAAa,kBAAmB,QAE1C,MAAM,EADoC,EAAiB,GAClB,OACnC,EAAuB,EAAgB,GAAa,YAY1D,OAXA,EAAM,aAAa,aAAc,EAAO,eAAe,EAAc,IAGrE,GAAiB,EAAO,EAAY,OAAO,aAC3C,GAAsB,EAAO,EAAU,EAAa,GACpD,GAAwB,EAAO,GAG/B,EAAS,OAAO,GAChB,EAAc,GAEP,CACR,EACA,OAAA,GACC,GAAY,EACZ,qBAAqB,GACrB,GAAmB,UACnB,EAAM,oBAAoB,cAAe,GACzC,EAAU,sBAAsB,GAChC,EAAS,SACV,EACA,UAAA,GACC,EAAU,UAAU,IAAI,0BACzB,EACA,YAAA,GACC,EAAU,UAAU,OAAO,0BAC5B,GAGH,CCwIG,CAA2B,EAAU,EAAS,KAAK,OAAQ,KAAK,OAAQ,CACvE,kBAAoB,IACnB,KAAK,SAAS,IAAI,IAEnB,oBAAsB,IACrB,KAAK,SAAS,OAAO,OAIxB,EAAQ,iBAAiB,YD5H1B,CACC,EACA,EACA,KAEA,IAAI,EAAyB,EAC7B,MAAM,EAA0B,SAAS,cAAc,MACvD,EAAG,aAAa,gBAAiB,EAAK,IACtC,EAAG,aAAa,OAAQ,OACxB,EAAG,aAAa,OAAQ,aACxB,GAAsB,EAAI,GAC1B,IAAI,GAAY,EAChB,MAAM,EAA4B,sBAAA,KAC5B,GAAW,GAAsB,EAAI,KAG3C,MAAO,CACN,IAAK,EACL,WAAY,EACZ,OAAO,GACmB,cAArB,EAAY,MACZ,EAAY,WAAa,EAAY,WACzC,EAAG,aAAa,gBAAiB,EAAY,IAC7C,GAAsB,EAAI,GAC1B,EAAc,GACP,GAER,OAAA,GACC,GAAY,EACZ,qBAAqB,EACtB,KC+FD,EAAQ,iBAAiB,aDwB1B,CACC,EACA,EACA,KAEA,MAAM,EAA2B,SAAS,cAAc,MACxD,EAAG,aAAa,gBAAiB,EAAK,IACtC,EAAG,aAAa,OAAQ,QACxB,EAAG,aAAa,OAAQ,cAExB,MAAM,EAAmB,EAAK,OAAO,SAAkC,EACjE,EAAmB,EAAK,OAAO,SAAkC,EACnE,EAAU,IAAG,EAAG,QAAU,GAC1B,EAAU,IAAG,EAAG,QAAU,GAG9B,MAAM,EAA4B,EAAK,OAAO,MAK9C,OAJI,GAAmB,SAAV,GACZ,EAAiB,EAAI,YAAa,GAG5B,CACN,IAAK,EACL,WAAY,EACZ,MAAA,CAAO,GACN,GAAyB,eAArB,EAAY,KAAuB,OAAO,EAC9C,EAAG,aAAa,gBAAiB,EAAY,IAE7C,MAAM,EAAsB,EAAY,OAAO,SAAkC,EAC3E,EAAsB,EAAY,OAAO,SAAkC,EAYjF,OAXI,EAAa,EAChB,EAAG,QAAU,EAEb,EAAG,gBAAgB,WAEhB,EAAa,EAChB,EAAG,QAAU,EAEb,EAAG,gBAAgB,YAGb,CACR,EACA,OAAA,GAEA,ICpEF,CAEA,mBAAA,CAA4B,GAC3B,MAAM,EAAkB,KAAK,OAAO,eAAiB,EAC/C,EAAkB,KAAK,OAAO,eAAiB,EAErD,EAAQ,oBAAoB,CAC3B,GAAI,QACJ,MAAO,SACP,KA3QF,uLA4QE,MAAO,KAAK,OAAO,YACnB,QAAS,KAAK,OAAO,YACrB,QAAS,cACT,UAAW,aACX,YAAa,CACZ,UACA,UACA,SAAA,CAAW,EAAc,KACxB,GAAY,EAAS,EAAM,KAG7B,SAAW,KACL,EAAgB,EAAM,YACpB,GAAc,EAAO,EAAM,UAAU,OAAO,UAGtD,GAGD,SAAS,GAAoB,EAAgB,GAC5C,MAAwB,iBAAV,GAAsB,OAAO,UAAU,IAAU,EAAQ,EACpE,KAAK,IAAI,EAAO,KAChB,CACJ,CA0CA,SAAS,GAAwB,GAChC,MAAM,EAA2B,GAAmB,EAAK,MAAO,CAAC,UAAW,YACtE,EAAkB,EAAmB,EAAK,OAAO,SACjD,EAAkB,EAAmB,EAAK,OAAO,SAGvD,OAFI,EAAU,IAAG,EAAM,QAAU,GAC7B,EAAU,IAAG,EAAM,QAAU,GAC1B,OAAO,KAAK,GAAO,OAAS,EAAI,OAAQ,CAChD,CAGA,SAAS,GAAwB,GAChC,MAAM,EAA6B,EAAiB,GACpD,IAAI,GAAU,EACd,MAAM,EAAuC,EAAK,IAAA,CAAK,EAAgB,KACtE,IAAI,GAAa,EACjB,MAAM,EAA8B,EAAiB,GAAK,IAAK,IAC9D,MAAM,EAAoB,EAAmB,EAAK,OAAO,SACnD,EAAoB,KAAK,IAAI,EAAW,EAAK,OAAS,GAC5D,GAAI,IAAc,EAAW,OAAO,EACpC,GAAa,EACb,MAAQ,QAAS,KAAa,GAAS,EAAK,OAAS,CAAC,EAChD,EAA2B,IAC7B,KACC,EAAY,EAAI,CAAE,QAAS,GAAc,CAAC,IAEvC,MAAO,KAAW,GAAiB,EAC3C,OAAO,OAAO,KAAK,GAAO,OAAS,EAAI,IAAK,EAAc,SAAU,IAErE,OAAK,GACL,GAAU,EACH,IAAK,EAAK,SAAU,IAFH,IAIzB,OAAO,EAAU,IAAK,EAAO,SAAU,GAAmB,CAC3D,CAEA,SAAS,GACR,EACA,GAEA,MAAM,EAA4B,CAAC,EACnC,IAAK,MAAO,EAAK,KAAU,OAAO,QAAQ,GAAS,CAAC,GAAI,CACvD,GAAI,EAAS,SAAS,GAAM,SAC5B,MAAM,EAAyC,GAAoB,QAChD,IAAf,IAA0B,EAAO,GAAO,EAC7C,CACA,OAAO,CACR,CAEA,SAAS,GAAoB,GAC5B,GAAqB,iBAAV,GAAuC,kBAAV,EAAqB,OAAO,EACpE,GAAqB,iBAAV,EAAoB,OAAO,OAAO,SAAS,GAAS,OAAQ,EACvE,IAAK,MAAM,QAAQ,GAAQ,OAC3B,MAAM,EAAmD,GACzD,IAAK,MAAM,KAAS,EACnB,GAAc,OAAV,GAAmC,iBAAV,GAAuC,kBAAV,EACzD,EAAW,KAAK,OACV,IAAqB,iBAAV,IAAsB,OAAO,SAAS,GAGvD,OAFA,EAAW,KAAK,EAEhB,CAGF,OAAO,OAAO,OAAO,EACtB"}
|