@notectl/core 1.7.1 → 1.7.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/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs → AttrRegistry-D9Jx_RVb.mjs} +2 -2
- package/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs.map → AttrRegistry-D9Jx_RVb.mjs.map} +1 -1
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs +80 -0
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs.map +1 -0
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs → CaretNavigation-7JYpIKbs.mjs} +3 -3
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs.map → CaretNavigation-7JYpIKbs.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-BBkN0AOb.mjs → ColorGrid--lBQ7jCK.mjs} +56 -55
- package/dist/chunks/ColorGrid--lBQ7jCK.mjs.map +1 -0
- package/dist/chunks/{ColorPickerPopup-D3ysvOH-.mjs → ColorPickerPopup-TpIRlXc8.mjs} +15 -15
- package/dist/chunks/ColorPickerPopup-TpIRlXc8.mjs.map +1 -0
- package/dist/chunks/{Document-DfZ1NOjs.mjs → Document-Brtw2jL1.mjs} +6 -6
- package/dist/chunks/Document-Brtw2jL1.mjs.map +1 -0
- package/dist/chunks/{MarkCommands-uybmkw9l.mjs → MarkCommands-6RUJ4L3r.mjs} +3 -3
- package/dist/chunks/MarkCommands-6RUJ4L3r.mjs.map +1 -0
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs → NodeResolver-C2XYkeOi.mjs} +2 -2
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs.map → NodeResolver-C2XYkeOi.mjs.map} +1 -1
- package/dist/chunks/NodeSpec-NZVvcCuH.mjs.map +1 -1
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs +34 -0
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs.map +1 -0
- package/dist/chunks/RangeIterator-Bh4Vl_6D.mjs +16 -0
- package/dist/chunks/{RangeIterator-Bye0UtbY.mjs.map → RangeIterator-Bh4Vl_6D.mjs.map} +1 -1
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs +235 -0
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs.map +1 -0
- package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
- package/dist/chunks/{index-Bt_d4IqB.mjs → index--7pBhSF4.mjs} +74 -73
- package/dist/chunks/index--7pBhSF4.mjs.map +1 -0
- package/dist/chunks/{index-DayTn6Re.mjs → index-BXCkjDax.mjs} +78 -83
- package/dist/chunks/index-BXCkjDax.mjs.map +1 -0
- package/dist/chunks/{index-DGKQhSAl.mjs → index-BXtw5PNN.mjs} +2 -2
- package/dist/chunks/{index-DGKQhSAl.mjs.map → index-BXtw5PNN.mjs.map} +1 -1
- package/dist/chunks/{ToolbarPlugin-B7oJRzpl.mjs → index-BcDRT4K2.mjs} +110 -121
- package/dist/chunks/index-BcDRT4K2.mjs.map +1 -0
- package/dist/chunks/{index-DoCvDkfj.mjs → index-BjFBWBOC.mjs} +23 -25
- package/dist/chunks/index-BjFBWBOC.mjs.map +1 -0
- package/dist/chunks/{index-CrKz_IEn.mjs → index-C1opJocO.mjs} +2 -2
- package/dist/chunks/{index-CrKz_IEn.mjs.map → index-C1opJocO.mjs.map} +1 -1
- package/dist/chunks/{index-CfyS_pgH.mjs → index-CCyqQ0Dz.mjs} +32 -31
- package/dist/chunks/index-CCyqQ0Dz.mjs.map +1 -0
- package/dist/chunks/{index-E7OgN7hM.mjs → index-Cd3uJLP3.mjs} +2 -2
- package/dist/chunks/{index-E7OgN7hM.mjs.map → index-Cd3uJLP3.mjs.map} +1 -1
- package/dist/chunks/{index-yhz2qeeh.mjs → index-DSAuGP4n.mjs} +131 -127
- package/dist/chunks/index-DSAuGP4n.mjs.map +1 -0
- package/dist/chunks/{index-CH1r7DL-.mjs → index-DtRfqsq5.mjs} +211 -210
- package/dist/chunks/index-DtRfqsq5.mjs.map +1 -0
- package/dist/chunks/{index-CFPP456m.mjs → index-DxbQirny.mjs} +8 -8
- package/dist/chunks/{index-CFPP456m.mjs.map → index-DxbQirny.mjs.map} +1 -1
- package/dist/chunks/{index-DuRNETFv.mjs → index-ErZ5_a1o.mjs} +3 -3
- package/dist/chunks/{index-DuRNETFv.mjs.map → index-ErZ5_a1o.mjs.map} +1 -1
- package/dist/chunks/{index-Deag_lVi.mjs → index-WpbK_s_H.mjs} +29 -31
- package/dist/chunks/index-WpbK_s_H.mjs.map +1 -0
- package/dist/chunks/{index-DIvHQtE2.mjs → index-WrqH1wG8.mjs} +15 -14
- package/dist/chunks/index-WrqH1wG8.mjs.map +1 -0
- package/dist/chunks/{index-Dy6SllXW.mjs → index-fsVJLutn.mjs} +3 -3
- package/dist/chunks/{index-Dy6SllXW.mjs.map → index-fsVJLutn.mjs.map} +1 -1
- package/dist/chunks/{index-Ckzqtn5D.mjs → index-fx3KIap3.mjs} +15 -14
- package/dist/chunks/index-fx3KIap3.mjs.map +1 -0
- package/dist/chunks/{index-DmuuFS5D.mjs → index-oOJo-ywB.mjs} +4 -4
- package/dist/chunks/{index-DmuuFS5D.mjs.map → index-oOJo-ywB.mjs.map} +1 -1
- package/dist/chunks/{index-CFJFD4lF.mjs → index-toj88jtL.mjs} +19 -18
- package/dist/chunks/index-toj88jtL.mjs.map +1 -0
- package/dist/commands/AttributedMarkCommands.d.ts.map +1 -1
- package/dist/commands/MarkCommands.d.ts +3 -3
- package/dist/commands/MarkCommands.d.ts.map +1 -1
- package/dist/editor/CSSClassCollector.d.ts +36 -0
- package/dist/editor/CSSClassCollector.d.ts.map +1 -0
- package/dist/editor/ContentHTMLTypes.d.ts +29 -0
- package/dist/editor/ContentHTMLTypes.d.ts.map +1 -0
- package/dist/editor/ContentStyleInjector.d.ts +69 -0
- package/dist/editor/ContentStyleInjector.d.ts.map +1 -0
- package/dist/editor/DocumentParser.d.ts +9 -1
- package/dist/editor/DocumentParser.d.ts.map +1 -1
- package/dist/editor/DocumentSerializer.d.ts +6 -0
- package/dist/editor/DocumentSerializer.d.ts.map +1 -1
- package/dist/editor/EditorThemeController.d.ts +3 -0
- package/dist/editor/EditorThemeController.d.ts.map +1 -1
- package/dist/editor/MarkSerializer.d.ts +7 -0
- package/dist/editor/MarkSerializer.d.ts.map +1 -1
- package/dist/editor/NotectlEditor.d.ts +13 -3
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/PaperLayoutController.d.ts.map +1 -1
- package/dist/editor/styles/font-select.d.ts +1 -1
- package/dist/editor/styles/font-select.d.ts.map +1 -1
- package/dist/editor/styles/font-size-select.d.ts +1 -1
- package/dist/editor/styles/font-size-select.d.ts.map +1 -1
- package/dist/editor/styles/heading-select.d.ts +1 -1
- package/dist/editor/styles/heading-select.d.ts.map +1 -1
- package/dist/editor/styles/toolbar.d.ts +1 -1
- package/dist/editor/styles/toolbar.d.ts.map +1 -1
- package/dist/index.d.ts +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/input/LegacyHTMLNormalizer.d.ts.map +1 -1
- package/dist/model/Document.d.ts +0 -4
- package/dist/model/Document.d.ts.map +1 -1
- package/dist/model/NodeSpec.d.ts +19 -2
- package/dist/model/NodeSpec.d.ts.map +1 -1
- package/dist/notectl-core.mjs +613 -477
- package/dist/notectl-core.mjs.map +1 -1
- package/dist/notectl-core.umd.js +1571 -1225
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockNodeView.d.ts.map +1 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts +0 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts.map +1 -1
- package/dist/plugins/code-block/index.d.ts +2 -1
- package/dist/plugins/code-block/index.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font/FontPlugin.d.ts +0 -1
- package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -2
- package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts +3 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts.map +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +6 -6
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image/ImageNodeView.d.ts.map +1 -1
- package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print/PrintContentPreparer.d.ts.map +1 -1
- package/dist/plugins/shared/ColorGrid.d.ts.map +1 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts +2 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
- package/dist/plugins/shared/PopupManager.d.ts +12 -2
- package/dist/plugins/shared/PopupManager.d.ts.map +1 -1
- package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
- package/dist/plugins/table/TableControls.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -1
- package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
- package/dist/plugins/table/TablePlugin.d.ts.map +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar/ToolbarItem.d.ts +3 -2
- package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts +0 -3
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarRenderers.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarTooltip.d.ts.map +1 -1
- package/dist/plugins/toolbar.mjs +2 -2
- package/dist/presets/PresetTypes.d.ts +1 -1
- package/dist/presets/PresetTypes.d.ts.map +1 -1
- package/dist/style/StyleRuntime.d.ts +34 -0
- package/dist/style/StyleRuntime.d.ts.map +1 -0
- package/dist/view/Reconciler.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs +0 -58
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs.map +0 -1
- package/dist/chunks/ColorGrid-BBkN0AOb.mjs.map +0 -1
- package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs.map +0 -1
- package/dist/chunks/Document-DfZ1NOjs.mjs.map +0 -1
- package/dist/chunks/MarkCommands-uybmkw9l.mjs.map +0 -1
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs +0 -30
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs.map +0 -1
- package/dist/chunks/RangeIterator-Bye0UtbY.mjs +0 -16
- package/dist/chunks/ToolbarPlugin-B7oJRzpl.mjs.map +0 -1
- package/dist/chunks/index-Bt_d4IqB.mjs.map +0 -1
- package/dist/chunks/index-CFJFD4lF.mjs.map +0 -1
- package/dist/chunks/index-CH1r7DL-.mjs.map +0 -1
- package/dist/chunks/index-CfyS_pgH.mjs.map +0 -1
- package/dist/chunks/index-Ckzqtn5D.mjs.map +0 -1
- package/dist/chunks/index-DIvHQtE2.mjs.map +0 -1
- package/dist/chunks/index-DayTn6Re.mjs.map +0 -1
- package/dist/chunks/index-Deag_lVi.mjs.map +0 -1
- package/dist/chunks/index-DoCvDkfj.mjs.map +0 -1
- package/dist/chunks/index-yhz2qeeh.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-DmuuFS5D.mjs","sources":["../../src/model/GraphemeUtils.ts","../../src/commands/WordBoundary.ts","../../src/commands/MovementCommands.ts","../../src/view/ViewMovementCommands.ts","../../src/editor/Announcer.ts","../../src/plugins/caret-navigation/CaretNavigationPlugin.ts"],"sourcesContent":["/**\n * Grapheme-cluster utilities for correct character navigation.\n *\n * Uses `Intl.Segmenter` (with `granularity: 'grapheme'`) to correctly\n * handle multi-code-unit characters such as emoji, ZWJ sequences, and\n * combining characters. Falls back to +1/-1 when `Intl.Segmenter` is\n * not available.\n */\n\nconst SEGMENTER_SUPPORTED: boolean = typeof Intl !== 'undefined' && 'Segmenter' in Intl;\nconst SEGMENTER: Intl.Segmenter | null = SEGMENTER_SUPPORTED\n\t? new Intl.Segmenter(undefined, { granularity: 'grapheme' })\n\t: null;\n\n/**\n * Returns the number of UTF-16 code units in the next grapheme cluster\n * starting at `offset`. Returns 0 if `offset` is at or past the end.\n */\nexport function nextGraphemeSize(text: string, offset: number): number {\n\tif (offset >= text.length || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index >= offset) {\n\t\t\t\treturn segment.segment.length;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/**\n * Returns the number of UTF-16 code units in the grapheme cluster\n * immediately before `offset`. Returns 0 if `offset` is 0 or the\n * string is empty.\n */\nexport function prevGraphemeSize(text: string, offset: number): number {\n\tif (offset <= 0 || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tlet lastSize = 1;\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index + segment.segment.length >= offset) {\n\t\t\t\tlastSize = segment.segment.length;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlastSize = segment.segment.length;\n\t\t}\n\t\treturn lastSize;\n\t}\n\n\treturn 1;\n}\n","/**\n * Word boundary detection for delete-word and word-movement commands.\n * InlineNodes act as word boundaries.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\n\n/**\n * Finds the word boundary backward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryBackward(block: BlockNode, offset: number): number {\n\tlet pos = offset - 1;\n\t// Skip trailing whitespace\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\t// If at InlineNode, delete just it (treat as word boundary)\n\tif (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos;\n\t}\n\t// Skip word characters until whitespace or InlineNode\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\treturn pos + 1;\n}\n\n/**\n * Finds the word boundary forward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryForward(block: BlockNode, offset: number): number {\n\tconst len = getBlockLength(block);\n\tlet pos = offset;\n\t// Skip word characters first\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\t// If at InlineNode and haven't moved, delete just the InlineNode\n\tif (pos === offset && pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos + 1;\n\t}\n\t// Skip trailing whitespace\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\treturn pos;\n}\n","/**\n * Model-based movement commands for programmatic cursor navigation.\n *\n * All functions are pure: they take an EditorState and return a Transaction\n * or `null` (when the movement is not applicable). None touch the DOM.\n *\n * Move commands produce a collapsed cursor. Extend commands keep the anchor\n * and move the head, producing a range selection. All clear storedMarks.\n */\n\nimport {\n\tblockOffsetToTextOffset,\n\tgetBlockLength,\n\tgetBlockText,\n\tgetContentAtOffset,\n} from '../model/Document.js';\nimport { nextGraphemeSize, prevGraphemeSize } from '../model/GraphemeUtils.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolves the effective head position from any text selection. */\nfunction resolveHead(state: EditorState): { blockId: BlockId; offset: number } | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\treturn { blockId: sel.head.blockId, offset: sel.head.offset };\n}\n\n/** Returns the adjacent block ID in the given direction, or `null`. */\nfunction adjacentBlockId(\n\tstate: EditorState,\n\tcurrentBlockId: BlockId,\n\tdirection: 'forward' | 'backward',\n): BlockId | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(currentBlockId);\n\tif (idx < 0) return null;\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\treturn blockOrder[targetIdx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Character Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one character forward, skipping InlineNodes atomically. */\nexport function moveCharacterForward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (offset < blockLen) {\n\t\t// InlineNode: skip atomically (+1)\n\t\tconst content = getContentAtOffset(block, offset);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset + 1);\n\t\t}\n\t\t// Text: advance by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = nextGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset + step);\n\t}\n\n\t// At block end → cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, nextId)) return null;\n\n\tif (isVoidBlock(state, nextId)) return nodeSelTx(state, nextId);\n\n\treturn moveTx(state, nextId, 0);\n}\n\n/** Moves the cursor one character backward, skipping InlineNodes atomically. */\nexport function moveCharacterBackward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\n\tif (offset > 0) {\n\t\t// InlineNode: skip atomically (-1)\n\t\tconst content = getContentAtOffset(block, offset - 1);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset - 1);\n\t\t}\n\t\t// Text: retreat by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = prevGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset - step);\n\t}\n\n\t// At block start → cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, prevId)) return null;\n\n\tif (isVoidBlock(state, prevId)) return nodeSelTx(state, prevId);\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\treturn moveTx(state, prevId, getBlockLength(prevBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Block Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the start of the current block. */\nexport function moveToBlockStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === 0) return null;\n\treturn moveTx(state, blockId, 0);\n}\n\n/** Moves the cursor to the end of the current block. */\nexport function moveToBlockEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === blockLen) return null;\n\treturn moveTx(state, blockId, blockLen);\n}\n\n// ---------------------------------------------------------------------------\n// Document Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the beginning of the document. */\nexport function moveToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\tif (isVoidBlock(state, firstId)) return nodeSelTx(state, firstId);\n\n\treturn moveTx(state, firstId, 0);\n}\n\n/** Moves the cursor to the end of the document. */\nexport function moveToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tif (isVoidBlock(state, lastId)) return nodeSelTx(state, lastId);\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\treturn moveTx(state, lastId, getBlockLength(lastBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Extend (Selection Extension) Commands\n// ---------------------------------------------------------------------------\n\n/** Extends the selection one character forward. */\nexport function extendCharacterForward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (head.offset < blockLen) {\n\t\tconst content = getContentAtOffset(block, head.offset);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: nextGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset + step);\n\t}\n\n\t// Cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, head.blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, nextId)) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, nextId, 0);\n}\n\n/** Extends the selection one character backward. */\nexport function extendCharacterBackward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset > 0) {\n\t\tconst block = state.getBlock(head.blockId);\n\t\tif (!block) return null;\n\t\tconst content = getContentAtOffset(block, head.offset - 1);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: prevGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset - step);\n\t}\n\n\t// Cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, head.blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, prevId)) return null;\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, prevId, getBlockLength(prevBlock));\n}\n\n/** Extends the selection to the start of the current block. */\nexport function extendToBlockStart(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset === 0) return null;\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, 0);\n}\n\n/** Extends the selection to the end of the current block. */\nexport function extendToBlockEnd(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tif (head.offset === blockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, blockLen);\n}\n\n/** Extends the selection to the start of the document. */\nexport function extendToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\t// No-op if head is already at document start\n\tif (sel.head.blockId === firstId && sel.head.offset === 0) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, firstId, 0);\n}\n\n/** Extends the selection to the end of the document. */\nexport function extendToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\n\tconst lastBlockLen: number = getBlockLength(lastBlock);\n\n\t// No-op if head is already at document end\n\tif (sel.head.blockId === lastId && sel.head.offset === lastBlockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, lastId, lastBlockLen);\n}\n","/**\n * View-based movement commands that use the browser's `Selection.modify()` API\n * for word, line-boundary, and line granularity movement.\n *\n * Falls back to model-based commands when `Selection.modify` is unavailable\n * (e.g. in happy-dom test environments).\n */\n\nimport { findWordBoundaryBackward, findWordBoundaryForward } from '../commands/Commands.js';\nimport { moveToBlockEnd, moveToBlockStart } from '../commands/MovementCommands.js';\nimport { getBlockLength, getContentAtOffset } from '../model/Document.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { navigateAcrossBlocks } from './CaretNavigation.js';\nimport { getSelection, readSelectionFromDOM } from './SelectionSync.js';\n\ntype Direction = 'forward' | 'backward';\ntype Granularity = 'word' | 'lineboundary' | 'line';\n\n// ---------------------------------------------------------------------------\n// Core view movement\n// ---------------------------------------------------------------------------\n\n/**\n * Moves the cursor using `Selection.modify('move', direction, granularity)`.\n * Falls back to model-based movement when the API is unavailable.\n */\nexport function viewMove(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackMove(state, direction, granularity);\n\t}\n\n\t// Save original DOM selection\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackMove(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('move', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tconst oldBlockId: BlockId = sel.anchor.blockId;\n\n\t\t// Validate isolating boundary crossing\n\t\tif (newBlockId !== oldBlockId && !canCrossBlockBoundary(state, oldBlockId, newBlockId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newOffset: number = newSel.anchor.offset;\n\n\t\t// InlineNode boundary correction\n\t\tconst block = state.getBlock(newBlockId);\n\t\tif (block) {\n\t\t\tconst content = getContentAtOffset(block, newOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\t// Prefer landing after the InlineNode when moving forward\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewOffset = Math.min(newOffset + 1, getBlockLength(block));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Void block → NodeSelection\n\t\tif (isVoidBlock(state, newBlockId)) return nodeSelTx(state, newBlockId);\n\n\t\treturn moveTx(state, newBlockId, newOffset);\n\t} finally {\n\t\t// Restore original DOM selection (dispatch cycle will set the correct one)\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/**\n * Extends the selection using `Selection.modify('extend', direction, granularity)`.\n * Falls back to model-based extension when the API is unavailable.\n */\nexport function viewExtend(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackExtend(state, direction, granularity);\n\t}\n\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackExtend(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('extend', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\t// Validate isolating boundary crossing for head\n\t\tconst oldHeadBlockId: BlockId = sel.head.blockId;\n\t\tconst newHeadBlockId: BlockId = newSel.head.blockId;\n\n\t\tif (\n\t\t\tnewHeadBlockId !== oldHeadBlockId &&\n\t\t\t!canCrossBlockBoundary(state, oldHeadBlockId, newHeadBlockId)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newHeadOffset: number = newSel.head.offset;\n\n\t\t// InlineNode boundary correction for head position\n\t\tconst headBlock = state.getBlock(newSel.head.blockId);\n\t\tif (headBlock) {\n\t\t\tconst content = getContentAtOffset(headBlock, newHeadOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewHeadOffset = Math.min(newHeadOffset + 1, getBlockLength(headBlock));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn extendTx(\n\t\t\tstate,\n\t\t\tsel.anchor.blockId,\n\t\t\tsel.anchor.offset,\n\t\t\tnewSel.head.blockId,\n\t\t\tnewHeadOffset,\n\t\t);\n\t} finally {\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fallback (model-based) when Selection.modify is unavailable\n// ---------------------------------------------------------------------------\n\nfunction fallbackMove(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tif (granularity === 'word') {\n\t\treturn fallbackWordMove(state, direction);\n\t}\n\tif (granularity === 'lineboundary') {\n\t\treturn direction === 'forward' ? moveToBlockEnd(state) : moveToBlockStart(state);\n\t}\n\t// granularity === 'line' → cross-block\n\tconst caretDir = direction === 'forward' ? 'down' : 'up';\n\treturn navigateAcrossBlocks(state, caretDir);\n}\n\nfunction fallbackExtend(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst head = sel.head;\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tlet newOffset: number = head.offset;\n\n\tif (granularity === 'word') {\n\t\tnewOffset =\n\t\t\tdirection === 'forward'\n\t\t\t\t? findWordBoundaryForward(block, head.offset)\n\t\t\t\t: findWordBoundaryBackward(block, head.offset);\n\t} else if (granularity === 'lineboundary') {\n\t\tnewOffset = direction === 'forward' ? getBlockLength(block) : 0;\n\t} else {\n\t\t// line: extend to next block boundary\n\t\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\t\tconst idx: number = blockOrder.indexOf(head.blockId);\n\t\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\t\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\t\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\t\tif (!targetId) return null;\n\t\tif (!canCrossBlockBoundary(state, head.blockId, targetId)) return null;\n\n\t\tif (isVoidBlock(state, targetId)) {\n\t\t\t// Cannot extend a text selection into a void block\n\t\t\treturn null;\n\t\t}\n\n\t\tconst targetBlock = state.getBlock(targetId);\n\t\tif (!targetBlock) return null;\n\n\t\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, targetId, targetOffset);\n\t}\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, newOffset);\n}\n\nfunction fallbackWordMove(state: EditorState, direction: Direction): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst newOffset: number =\n\t\tdirection === 'forward'\n\t\t\t? findWordBoundaryForward(block, offset)\n\t\t\t: findWordBoundaryBackward(block, offset);\n\n\tif (newOffset === offset) {\n\t\t// At block boundary — try crossing blocks\n\t\treturn fallbackCrossBlock(state, blockId, direction);\n\t}\n\n\treturn moveTx(state, blockId, newOffset);\n}\n\n/**\n * Crosses a block boundary from `blockId` in the given direction.\n * Checks `canCrossBlockBoundary` and handles void blocks (→ NodeSelection).\n */\nfunction fallbackCrossBlock(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tdirection: Direction,\n): Transaction | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(blockId);\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\tif (!targetId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, targetId)) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one word forward. */\nexport function moveWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'word');\n}\n\n/** Moves the cursor one word backward. */\nexport function moveWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'word');\n}\n\n/** Moves the cursor to the start of the line (visual). */\nexport function moveToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'lineboundary');\n}\n\n/** Moves the cursor to the end of the line (visual). */\nexport function moveToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'lineboundary');\n}\n\n/** Moves the cursor one visual line up. */\nexport function moveLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'line');\n}\n\n/** Moves the cursor one visual line down. */\nexport function moveLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'line');\n}\n\n/** Extends the selection one word forward. */\nexport function extendWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'word');\n}\n\n/** Extends the selection one word backward. */\nexport function extendWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'word');\n}\n\n/** Extends the selection to the start of the line. */\nexport function extendToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'lineboundary');\n}\n\n/** Extends the selection to the end of the line. */\nexport function extendToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'lineboundary');\n}\n\n/** Extends the selection one visual line up. */\nexport function extendLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'line');\n}\n\n/** Extends the selection one visual line down. */\nexport function extendLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'line');\n}\n","/**\n * Announcer: builds screen-reader announcement text for editor state changes.\n * Pure functions — no DOM mutation. The caller applies the result to a live region.\n */\n\nimport { isMarkActive } from '../commands/Commands.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nconst BLOCK_TYPE_LABELS: Record<string, string> = {\n\tparagraph: 'Paragraph',\n\theading: 'Heading',\n\tcode_block: 'Code Block',\n\tblockquote: 'Block Quote',\n\tlist_item: 'List Item',\n\thorizontal_rule: 'Horizontal Rule',\n\timage: 'Image',\n\ttable: 'Table',\n};\n\n/** Returns a human-readable label for a block type (used in screen reader announcements). */\nexport function getBlockTypeLabel(typeName: string, attrs?: Record<string, unknown>): string {\n\tif (typeName === 'heading' && attrs?.level) {\n\t\treturn `Heading ${attrs.level}`;\n\t}\n\treturn BLOCK_TYPE_LABELS[typeName] ?? typeName;\n}\n\n/**\n * Derives an announcement string from a state transition.\n * Returns `null` when no announcement is warranted.\n *\n * Priority order: undo/redo > block-type change > mark toggle.\n */\nexport function buildAnnouncement(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n): string | null {\n\t// Priority 1: Undo/Redo\n\tif (tr.metadata.historyDirection === 'undo') return 'Undo';\n\tif (tr.metadata.historyDirection === 'redo') return 'Redo';\n\n\t// Priority 2: Block type changes\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setBlockType') {\n\t\t\treturn getBlockTypeLabel(step.nodeType, step.attrs);\n\t\t}\n\t}\n\n\t// Priority 3: Mark changes\n\tfor (const mt of newState.schema.markTypes) {\n\t\tconst branded = markType(mt);\n\t\tconst wasActive: boolean = isMarkActive(oldState, branded);\n\t\tconst nowActive: boolean = isMarkActive(newState, branded);\n\t\tif (wasActive !== nowActive) {\n\t\t\treturn `${mt} ${nowActive ? 'on' : 'off'}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n","/**\n * CaretNavigationPlugin: registers platform-aware keymaps for all\n * programmatic movement commands (character, word, line, document).\n *\n * Movement priorities use 'navigation' to match GapCursorPlugin.\n * Also announces block-type changes to screen readers on cross-block navigation.\n */\n\nimport {\n\textendCharacterBackward,\n\textendCharacterForward,\n\textendToDocumentEnd,\n\textendToDocumentStart,\n\tmoveToDocumentEnd,\n\tmoveToDocumentStart,\n} from '../../commands/MovementCommands.js';\nimport { getBlockTypeLabel } from '../../editor/Announcer.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } 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 { getTextDirection, isMac } from '../../view/Platform.js';\nimport {\n\textendLineDown,\n\textendLineUp,\n\textendToLineEnd,\n\textendToLineStart,\n\textendWordBackward,\n\textendWordForward,\n\tmoveLineDown,\n\tmoveLineUp,\n\tmoveToLineEnd,\n\tmoveToLineStart,\n\tmoveWordBackward,\n\tmoveWordForward,\n} from '../../view/ViewMovementCommands.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\n\ntype ViewCommandFn = (container: HTMLElement, state: EditorState) => Transaction | null;\ntype ViewCommandKey = `${'word' | 'lineboundary' | 'line'}:${'forward' | 'backward'}`;\ntype ViewCommandLookup = Readonly<Record<ViewCommandKey, ViewCommandFn>>;\n\nconst VIEW_MOVE_FNS: ViewCommandLookup = {\n\t'word:forward': moveWordForward,\n\t'word:backward': moveWordBackward,\n\t'lineboundary:forward': moveToLineEnd,\n\t'lineboundary:backward': moveToLineStart,\n\t'line:forward': moveLineDown,\n\t'line:backward': moveLineUp,\n};\n\nconst VIEW_EXTEND_FNS: ViewCommandLookup = {\n\t'word:forward': extendWordForward,\n\t'word:backward': extendWordBackward,\n\t'lineboundary:forward': extendToLineEnd,\n\t'lineboundary:backward': extendToLineStart,\n\t'line:forward': extendLineDown,\n\t'line:backward': extendLineUp,\n};\n\n/** Debounce delay for block-type announcements (ms). */\nconst ANNOUNCE_DEBOUNCE_MS = 150;\n\nexport class CaretNavigationPlugin implements Plugin {\n\treadonly id = 'caret-navigation';\n\treadonly name = 'Caret Navigation';\n\treadonly priority = 4;\n\n\tprivate context: PluginContext | null = null;\n\tprivate lastAnnouncedBlockId: BlockId | null = null;\n\tprivate announceTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate announce: ((text: string) => void) | null = null;\n\n\tinit(context: PluginContext): void {\n\t\tthis.context = context;\n\t\tthis.announce = (text: string) => context.announce(text);\n\t\tconst mac: boolean = isMac();\n\n\t\tconst keymap: Record<string, () => boolean> = {\n\t\t\t// --- Character movement (no modifier) ---\n\t\t\t// Handled natively by browser for basic cases, but Shift+Arrow\n\t\t\t// needs programmatic extend to correctly clear storedMarks.\n\n\t\t\t// --- Character extend ---\n\t\t\t'Shift-ArrowRight': () => this.extendCharacterVisual('right'),\n\t\t\t'Shift-ArrowLeft': () => this.extendCharacterVisual('left'),\n\n\t\t\t// --- Line extend ---\n\t\t\t'Shift-ArrowUp': () => this.viewExtendCommand('backward', 'line'),\n\t\t\t'Shift-ArrowDown': () => this.viewExtendCommand('forward', 'line'),\n\n\t\t\t// --- Word movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Alt-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Alt-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Line boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'lineboundary'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tHome: () => this.viewMoveCommand('backward', 'lineboundary'),\n\t\t\t\t\t\tEnd: () => this.viewMoveCommand('forward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-Home': () => this.viewExtendCommand('backward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-End': () => this.viewExtendCommand('forward', 'lineboundary'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Document boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowUp': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-ArrowDown': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-ArrowUp': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-ArrowDown': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-Home': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-End': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-Home': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-End': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}),\n\t\t};\n\n\t\tcontext.registerKeymap(keymap, { priority: 'navigation' });\n\t}\n\n\t/** Announces block-type on cross-block cursor movement (debounced). */\n\tonStateChange(_oldState: EditorState, newState: EditorState, _tr: Transaction): void {\n\t\tconst newSel = newState.selection;\n\n\t\t// Only announce for collapsed text selections\n\t\tif (!isCollapsed(newSel) || isNodeSelection(newSel) || isGapCursor(newSel)) {\n\t\t\tif (this.announceTimer !== null) {\n\t\t\t\tclearTimeout(this.announceTimer);\n\t\t\t\tthis.announceTimer = null;\n\t\t\t}\n\t\t\tthis.lastAnnouncedBlockId = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tif (newBlockId === this.lastAnnouncedBlockId) return;\n\n\t\t// First observed block (initial focus) — remember but don't announce\n\t\tif (this.lastAnnouncedBlockId === null) {\n\t\t\tthis.lastAnnouncedBlockId = newBlockId;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.lastAnnouncedBlockId = newBlockId;\n\n\t\t// Debounce rapid navigation\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = setTimeout(() => {\n\t\t\tthis.announceTimer = null;\n\t\t\t// Skip if another plugin already announced (e.g. CodeBlockPlugin's \"Left code block\")\n\t\t\tif (this.context?.hasAnnouncement()) return;\n\t\t\tconst block = newState.getBlock(newBlockId);\n\t\t\tif (!block) return;\n\t\t\tconst label: string = getBlockTypeLabel(\n\t\t\t\tblock.type,\n\t\t\t\tblock.attrs as Record<string, unknown> | undefined,\n\t\t\t);\n\t\t\tthis.announce?.(label);\n\t\t}, ANNOUNCE_DEBOUNCE_MS);\n\t}\n\n\tdestroy(): void {\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = null;\n\t\tthis.announce = null;\n\t\tthis.context = null;\n\t}\n\n\t/** Dispatches a model-based movement command (no DOM needed). */\n\tprivate modelCommand(fn: (state: EditorState) => Transaction | null): boolean {\n\t\tif (!this.context) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Extends selection by one character based on visual left/right intent. */\n\tprivate extendCharacterVisual(visual: 'left' | 'right'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst state: EditorState = this.context.getState();\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(state, visual);\n\t\tconst tr: Transaction | null =\n\t\t\tdir === 'forward' ? extendCharacterForward(state) : extendCharacterBackward(state);\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based movement command (uses container + Selection.modify). */\n\tprivate viewMoveCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_MOVE_FNS, direction, granularity);\n\t}\n\n\t/** Dispatches a view-based move command with visual direction mapping for RTL. */\n\tprivate viewMoveVisual(visual: 'left' | 'right', granularity: 'word' | 'lineboundary'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewMoveCommand(dir, granularity);\n\t}\n\n\t/** Dispatches a view-based extend command. */\n\tprivate viewExtendCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_EXTEND_FNS, direction, granularity);\n\t}\n\n\tprivate dispatchViewCommand(\n\t\tlookup: ViewCommandLookup,\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst fn: ViewCommandFn | undefined = lookup[`${granularity}:${direction}`];\n\t\tif (!fn) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getContainer(), this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based extend command with visual direction mapping for RTL. */\n\tprivate viewExtendVisual(\n\t\tvisual: 'left' | 'right',\n\t\tgranularity: 'word' | 'lineboundary',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewExtendCommand(dir, granularity);\n\t}\n\n\t/**\n\t * Resolves logical movement direction from visual left/right intent.\n\t * In RTL blocks, visual directions are mirrored in offset space.\n\t */\n\tprivate resolveLogicalHorizontalDirection(\n\t\tstate: EditorState,\n\t\tvisual: 'left' | 'right',\n\t): 'forward' | 'backward' {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel) || isGapCursor(sel)) {\n\t\t\treturn visual === 'left' ? 'backward' : 'forward';\n\t\t}\n\t\tconst container: HTMLElement | undefined = this.context?.getContainer();\n\t\tif (!container) return visual === 'left' ? 'backward' : 'forward';\n\t\tconst blockEl: Element | null = container.querySelector(\n\t\t\t`[data-block-id=\"${sel.head.blockId}\"]`,\n\t\t);\n\t\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\t\tif (!isRtl) return visual === 'left' ? 'backward' : 'forward';\n\t\treturn visual === 'left' ? 'forward' : 'backward';\n\t}\n}\n"],"names":["SEGMENTER","Intl","Segmenter","granularity","nextGraphemeSize","text","offset","length","segment","index","prevGraphemeSize","lastSize","findWordBoundaryBackward","block","pos","content","getContentAtOffset","kind","test","char","findWordBoundaryForward","len","getBlockLength","resolveHead","state","sel","selection","isNodeSelection","isGapCursor","blockId","head","adjacentBlockId","currentBlockId","direction","blockOrder","getBlockOrder","idx","indexOf","targetIdx","moveCharacterForward","isCollapsed","moveTx","anchor","getBlock","step","getBlockText","blockOffsetToTextOffset","nextId","canCrossBlockBoundary","isVoidBlock","nodeSelTx","moveCharacterBackward","prevId","prevBlock","moveToBlockStart","moveToBlockEnd","blockLen","moveToDocumentStart","firstId","moveToDocumentEnd","lastId","lastBlock","extendCharacterForward","extendTx","extendCharacterBackward","extendToBlockStart","extendToBlockEnd","extendToDocumentStart","extendToDocumentEnd","lastBlockLen","viewMove","container","domSel","getSelection","modify","fallbackMove","origAnchor","anchorNode","origAnchorOff","anchorOffset","origFocus","focusNode","origFocusOff","focusOffset","newSel","readSelectionFromDOM","newBlockId","oldBlockId","newOffset","Math","min","setBaseAndExtent","viewExtend","fallbackExtend","oldHeadBlockId","newHeadBlockId","newHeadOffset","headBlock","targetId","targetBlock","targetOffset","navigateAcrossBlocks","moveWordForward","moveWordBackward","moveToLineStart","moveToLineEnd","moveLineUp","moveLineDown","extendWordForward","extendWordBackward","extendToLineStart","extendToLineEnd","extendLineUp","extendLineDown","BLOCK_TYPE_LABELS","paragraph","heading","code_block","blockquote","list_item","horizontal_rule","image","table","getBlockTypeLabel","typeName","attrs","level","buildAnnouncement","oldState","newState","tr","metadata","historyDirection","steps","type","nodeType","mt","schema","markTypes","branded","markType","wasActive","isMarkActive","nowActive","VIEW_MOVE_FNS","VIEW_EXTEND_FNS","CaretNavigationPlugin","id","name","priority","context","lastAnnouncedBlockId","announceTimer","announce","this","mac","isMac","keymap","extendCharacterVisual","viewExtendCommand","viewMoveVisual","viewExtendVisual","Home","viewMoveCommand","End","modelCommand","registerKeymap","_oldState","_tr","clearTimeout","setTimeout","hasAnnouncement","label","destroy","fn","getState","dispatch","visual","resolveLogicalHorizontalDirection","dispatchViewCommand","dir","lookup","getContainer","blockEl","querySelector","HTMLElement","getTextDirection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-oOJo-ywB.mjs","sources":["../../src/model/GraphemeUtils.ts","../../src/commands/WordBoundary.ts","../../src/commands/MovementCommands.ts","../../src/view/ViewMovementCommands.ts","../../src/editor/Announcer.ts","../../src/plugins/caret-navigation/CaretNavigationPlugin.ts"],"sourcesContent":["/**\n * Grapheme-cluster utilities for correct character navigation.\n *\n * Uses `Intl.Segmenter` (with `granularity: 'grapheme'`) to correctly\n * handle multi-code-unit characters such as emoji, ZWJ sequences, and\n * combining characters. Falls back to +1/-1 when `Intl.Segmenter` is\n * not available.\n */\n\nconst SEGMENTER_SUPPORTED: boolean = typeof Intl !== 'undefined' && 'Segmenter' in Intl;\nconst SEGMENTER: Intl.Segmenter | null = SEGMENTER_SUPPORTED\n\t? new Intl.Segmenter(undefined, { granularity: 'grapheme' })\n\t: null;\n\n/**\n * Returns the number of UTF-16 code units in the next grapheme cluster\n * starting at `offset`. Returns 0 if `offset` is at or past the end.\n */\nexport function nextGraphemeSize(text: string, offset: number): number {\n\tif (offset >= text.length || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index >= offset) {\n\t\t\t\treturn segment.segment.length;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/**\n * Returns the number of UTF-16 code units in the grapheme cluster\n * immediately before `offset`. Returns 0 if `offset` is 0 or the\n * string is empty.\n */\nexport function prevGraphemeSize(text: string, offset: number): number {\n\tif (offset <= 0 || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tlet lastSize = 1;\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index + segment.segment.length >= offset) {\n\t\t\t\tlastSize = segment.segment.length;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlastSize = segment.segment.length;\n\t\t}\n\t\treturn lastSize;\n\t}\n\n\treturn 1;\n}\n","/**\n * Word boundary detection for delete-word and word-movement commands.\n * InlineNodes act as word boundaries.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\n\n/**\n * Finds the word boundary backward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryBackward(block: BlockNode, offset: number): number {\n\tlet pos = offset - 1;\n\t// Skip trailing whitespace\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\t// If at InlineNode, delete just it (treat as word boundary)\n\tif (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos;\n\t}\n\t// Skip word characters until whitespace or InlineNode\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\treturn pos + 1;\n}\n\n/**\n * Finds the word boundary forward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryForward(block: BlockNode, offset: number): number {\n\tconst len = getBlockLength(block);\n\tlet pos = offset;\n\t// Skip word characters first\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\t// If at InlineNode and haven't moved, delete just the InlineNode\n\tif (pos === offset && pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos + 1;\n\t}\n\t// Skip trailing whitespace\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\treturn pos;\n}\n","/**\n * Model-based movement commands for programmatic cursor navigation.\n *\n * All functions are pure: they take an EditorState and return a Transaction\n * or `null` (when the movement is not applicable). None touch the DOM.\n *\n * Move commands produce a collapsed cursor. Extend commands keep the anchor\n * and move the head, producing a range selection. All clear storedMarks.\n */\n\nimport {\n\tblockOffsetToTextOffset,\n\tgetBlockLength,\n\tgetBlockText,\n\tgetContentAtOffset,\n} from '../model/Document.js';\nimport { nextGraphemeSize, prevGraphemeSize } from '../model/GraphemeUtils.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolves the effective head position from any text selection. */\nfunction resolveHead(state: EditorState): { blockId: BlockId; offset: number } | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\treturn { blockId: sel.head.blockId, offset: sel.head.offset };\n}\n\n/** Returns the adjacent block ID in the given direction, or `null`. */\nfunction adjacentBlockId(\n\tstate: EditorState,\n\tcurrentBlockId: BlockId,\n\tdirection: 'forward' | 'backward',\n): BlockId | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(currentBlockId);\n\tif (idx < 0) return null;\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\treturn blockOrder[targetIdx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Character Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one character forward, skipping InlineNodes atomically. */\nexport function moveCharacterForward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (offset < blockLen) {\n\t\t// InlineNode: skip atomically (+1)\n\t\tconst content = getContentAtOffset(block, offset);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset + 1);\n\t\t}\n\t\t// Text: advance by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = nextGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset + step);\n\t}\n\n\t// At block end → cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, nextId)) return null;\n\n\tif (isVoidBlock(state, nextId)) return nodeSelTx(state, nextId);\n\n\treturn moveTx(state, nextId, 0);\n}\n\n/** Moves the cursor one character backward, skipping InlineNodes atomically. */\nexport function moveCharacterBackward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\n\tif (offset > 0) {\n\t\t// InlineNode: skip atomically (-1)\n\t\tconst content = getContentAtOffset(block, offset - 1);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset - 1);\n\t\t}\n\t\t// Text: retreat by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = prevGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset - step);\n\t}\n\n\t// At block start → cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, prevId)) return null;\n\n\tif (isVoidBlock(state, prevId)) return nodeSelTx(state, prevId);\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\treturn moveTx(state, prevId, getBlockLength(prevBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Block Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the start of the current block. */\nexport function moveToBlockStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === 0) return null;\n\treturn moveTx(state, blockId, 0);\n}\n\n/** Moves the cursor to the end of the current block. */\nexport function moveToBlockEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === blockLen) return null;\n\treturn moveTx(state, blockId, blockLen);\n}\n\n// ---------------------------------------------------------------------------\n// Document Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the beginning of the document. */\nexport function moveToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\tif (isVoidBlock(state, firstId)) return nodeSelTx(state, firstId);\n\n\treturn moveTx(state, firstId, 0);\n}\n\n/** Moves the cursor to the end of the document. */\nexport function moveToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tif (isVoidBlock(state, lastId)) return nodeSelTx(state, lastId);\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\treturn moveTx(state, lastId, getBlockLength(lastBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Extend (Selection Extension) Commands\n// ---------------------------------------------------------------------------\n\n/** Extends the selection one character forward. */\nexport function extendCharacterForward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (head.offset < blockLen) {\n\t\tconst content = getContentAtOffset(block, head.offset);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: nextGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset + step);\n\t}\n\n\t// Cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, head.blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, nextId)) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, nextId, 0);\n}\n\n/** Extends the selection one character backward. */\nexport function extendCharacterBackward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset > 0) {\n\t\tconst block = state.getBlock(head.blockId);\n\t\tif (!block) return null;\n\t\tconst content = getContentAtOffset(block, head.offset - 1);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: prevGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset - step);\n\t}\n\n\t// Cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, head.blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, prevId)) return null;\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, prevId, getBlockLength(prevBlock));\n}\n\n/** Extends the selection to the start of the current block. */\nexport function extendToBlockStart(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset === 0) return null;\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, 0);\n}\n\n/** Extends the selection to the end of the current block. */\nexport function extendToBlockEnd(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tif (head.offset === blockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, blockLen);\n}\n\n/** Extends the selection to the start of the document. */\nexport function extendToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\t// No-op if head is already at document start\n\tif (sel.head.blockId === firstId && sel.head.offset === 0) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, firstId, 0);\n}\n\n/** Extends the selection to the end of the document. */\nexport function extendToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\n\tconst lastBlockLen: number = getBlockLength(lastBlock);\n\n\t// No-op if head is already at document end\n\tif (sel.head.blockId === lastId && sel.head.offset === lastBlockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, lastId, lastBlockLen);\n}\n","/**\n * View-based movement commands that use the browser's `Selection.modify()` API\n * for word, line-boundary, and line granularity movement.\n *\n * Falls back to model-based commands when `Selection.modify` is unavailable\n * (e.g. in happy-dom test environments).\n */\n\nimport { findWordBoundaryBackward, findWordBoundaryForward } from '../commands/Commands.js';\nimport { moveToBlockEnd, moveToBlockStart } from '../commands/MovementCommands.js';\nimport { getBlockLength, getContentAtOffset } from '../model/Document.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { navigateAcrossBlocks } from './CaretNavigation.js';\nimport { getSelection, readSelectionFromDOM } from './SelectionSync.js';\n\ntype Direction = 'forward' | 'backward';\ntype Granularity = 'word' | 'lineboundary' | 'line';\n\n// ---------------------------------------------------------------------------\n// Core view movement\n// ---------------------------------------------------------------------------\n\n/**\n * Moves the cursor using `Selection.modify('move', direction, granularity)`.\n * Falls back to model-based movement when the API is unavailable.\n */\nexport function viewMove(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackMove(state, direction, granularity);\n\t}\n\n\t// Save original DOM selection\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackMove(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('move', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tconst oldBlockId: BlockId = sel.anchor.blockId;\n\n\t\t// Validate isolating boundary crossing\n\t\tif (newBlockId !== oldBlockId && !canCrossBlockBoundary(state, oldBlockId, newBlockId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newOffset: number = newSel.anchor.offset;\n\n\t\t// InlineNode boundary correction\n\t\tconst block = state.getBlock(newBlockId);\n\t\tif (block) {\n\t\t\tconst content = getContentAtOffset(block, newOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\t// Prefer landing after the InlineNode when moving forward\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewOffset = Math.min(newOffset + 1, getBlockLength(block));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Void block → NodeSelection\n\t\tif (isVoidBlock(state, newBlockId)) return nodeSelTx(state, newBlockId);\n\n\t\treturn moveTx(state, newBlockId, newOffset);\n\t} finally {\n\t\t// Restore original DOM selection (dispatch cycle will set the correct one)\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/**\n * Extends the selection using `Selection.modify('extend', direction, granularity)`.\n * Falls back to model-based extension when the API is unavailable.\n */\nexport function viewExtend(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackExtend(state, direction, granularity);\n\t}\n\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackExtend(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('extend', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\t// Validate isolating boundary crossing for head\n\t\tconst oldHeadBlockId: BlockId = sel.head.blockId;\n\t\tconst newHeadBlockId: BlockId = newSel.head.blockId;\n\n\t\tif (\n\t\t\tnewHeadBlockId !== oldHeadBlockId &&\n\t\t\t!canCrossBlockBoundary(state, oldHeadBlockId, newHeadBlockId)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newHeadOffset: number = newSel.head.offset;\n\n\t\t// InlineNode boundary correction for head position\n\t\tconst headBlock = state.getBlock(newSel.head.blockId);\n\t\tif (headBlock) {\n\t\t\tconst content = getContentAtOffset(headBlock, newHeadOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewHeadOffset = Math.min(newHeadOffset + 1, getBlockLength(headBlock));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn extendTx(\n\t\t\tstate,\n\t\t\tsel.anchor.blockId,\n\t\t\tsel.anchor.offset,\n\t\t\tnewSel.head.blockId,\n\t\t\tnewHeadOffset,\n\t\t);\n\t} finally {\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fallback (model-based) when Selection.modify is unavailable\n// ---------------------------------------------------------------------------\n\nfunction fallbackMove(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tif (granularity === 'word') {\n\t\treturn fallbackWordMove(state, direction);\n\t}\n\tif (granularity === 'lineboundary') {\n\t\treturn direction === 'forward' ? moveToBlockEnd(state) : moveToBlockStart(state);\n\t}\n\t// granularity === 'line' → cross-block\n\tconst caretDir = direction === 'forward' ? 'down' : 'up';\n\treturn navigateAcrossBlocks(state, caretDir);\n}\n\nfunction fallbackExtend(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst head = sel.head;\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tlet newOffset: number = head.offset;\n\n\tif (granularity === 'word') {\n\t\tnewOffset =\n\t\t\tdirection === 'forward'\n\t\t\t\t? findWordBoundaryForward(block, head.offset)\n\t\t\t\t: findWordBoundaryBackward(block, head.offset);\n\t} else if (granularity === 'lineboundary') {\n\t\tnewOffset = direction === 'forward' ? getBlockLength(block) : 0;\n\t} else {\n\t\t// line: extend to next block boundary\n\t\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\t\tconst idx: number = blockOrder.indexOf(head.blockId);\n\t\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\t\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\t\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\t\tif (!targetId) return null;\n\t\tif (!canCrossBlockBoundary(state, head.blockId, targetId)) return null;\n\n\t\tif (isVoidBlock(state, targetId)) {\n\t\t\t// Cannot extend a text selection into a void block\n\t\t\treturn null;\n\t\t}\n\n\t\tconst targetBlock = state.getBlock(targetId);\n\t\tif (!targetBlock) return null;\n\n\t\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, targetId, targetOffset);\n\t}\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, newOffset);\n}\n\nfunction fallbackWordMove(state: EditorState, direction: Direction): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst newOffset: number =\n\t\tdirection === 'forward'\n\t\t\t? findWordBoundaryForward(block, offset)\n\t\t\t: findWordBoundaryBackward(block, offset);\n\n\tif (newOffset === offset) {\n\t\t// At block boundary — try crossing blocks\n\t\treturn fallbackCrossBlock(state, blockId, direction);\n\t}\n\n\treturn moveTx(state, blockId, newOffset);\n}\n\n/**\n * Crosses a block boundary from `blockId` in the given direction.\n * Checks `canCrossBlockBoundary` and handles void blocks (→ NodeSelection).\n */\nfunction fallbackCrossBlock(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tdirection: Direction,\n): Transaction | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(blockId);\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\tif (!targetId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, targetId)) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one word forward. */\nexport function moveWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'word');\n}\n\n/** Moves the cursor one word backward. */\nexport function moveWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'word');\n}\n\n/** Moves the cursor to the start of the line (visual). */\nexport function moveToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'lineboundary');\n}\n\n/** Moves the cursor to the end of the line (visual). */\nexport function moveToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'lineboundary');\n}\n\n/** Moves the cursor one visual line up. */\nexport function moveLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'line');\n}\n\n/** Moves the cursor one visual line down. */\nexport function moveLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'line');\n}\n\n/** Extends the selection one word forward. */\nexport function extendWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'word');\n}\n\n/** Extends the selection one word backward. */\nexport function extendWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'word');\n}\n\n/** Extends the selection to the start of the line. */\nexport function extendToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'lineboundary');\n}\n\n/** Extends the selection to the end of the line. */\nexport function extendToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'lineboundary');\n}\n\n/** Extends the selection one visual line up. */\nexport function extendLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'line');\n}\n\n/** Extends the selection one visual line down. */\nexport function extendLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'line');\n}\n","/**\n * Announcer: builds screen-reader announcement text for editor state changes.\n * Pure functions — no DOM mutation. The caller applies the result to a live region.\n */\n\nimport { isMarkActive } from '../commands/Commands.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nconst BLOCK_TYPE_LABELS: Record<string, string> = {\n\tparagraph: 'Paragraph',\n\theading: 'Heading',\n\tcode_block: 'Code Block',\n\tblockquote: 'Block Quote',\n\tlist_item: 'List Item',\n\thorizontal_rule: 'Horizontal Rule',\n\timage: 'Image',\n\ttable: 'Table',\n};\n\n/** Returns a human-readable label for a block type (used in screen reader announcements). */\nexport function getBlockTypeLabel(typeName: string, attrs?: Record<string, unknown>): string {\n\tif (typeName === 'heading' && attrs?.level) {\n\t\treturn `Heading ${attrs.level}`;\n\t}\n\treturn BLOCK_TYPE_LABELS[typeName] ?? typeName;\n}\n\n/**\n * Derives an announcement string from a state transition.\n * Returns `null` when no announcement is warranted.\n *\n * Priority order: undo/redo > block-type change > mark toggle.\n */\nexport function buildAnnouncement(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n): string | null {\n\t// Priority 1: Undo/Redo\n\tif (tr.metadata.historyDirection === 'undo') return 'Undo';\n\tif (tr.metadata.historyDirection === 'redo') return 'Redo';\n\n\t// Priority 2: Block type changes\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setBlockType') {\n\t\t\treturn getBlockTypeLabel(step.nodeType, step.attrs);\n\t\t}\n\t}\n\n\t// Priority 3: Mark changes\n\tfor (const mt of newState.schema.markTypes) {\n\t\tconst branded = markType(mt);\n\t\tconst wasActive: boolean = isMarkActive(oldState, branded);\n\t\tconst nowActive: boolean = isMarkActive(newState, branded);\n\t\tif (wasActive !== nowActive) {\n\t\t\treturn `${mt} ${nowActive ? 'on' : 'off'}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n","/**\n * CaretNavigationPlugin: registers platform-aware keymaps for all\n * programmatic movement commands (character, word, line, document).\n *\n * Movement priorities use 'navigation' to match GapCursorPlugin.\n * Also announces block-type changes to screen readers on cross-block navigation.\n */\n\nimport {\n\textendCharacterBackward,\n\textendCharacterForward,\n\textendToDocumentEnd,\n\textendToDocumentStart,\n\tmoveToDocumentEnd,\n\tmoveToDocumentStart,\n} from '../../commands/MovementCommands.js';\nimport { getBlockTypeLabel } from '../../editor/Announcer.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } 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 { getTextDirection, isMac } from '../../view/Platform.js';\nimport {\n\textendLineDown,\n\textendLineUp,\n\textendToLineEnd,\n\textendToLineStart,\n\textendWordBackward,\n\textendWordForward,\n\tmoveLineDown,\n\tmoveLineUp,\n\tmoveToLineEnd,\n\tmoveToLineStart,\n\tmoveWordBackward,\n\tmoveWordForward,\n} from '../../view/ViewMovementCommands.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\n\ntype ViewCommandFn = (container: HTMLElement, state: EditorState) => Transaction | null;\ntype ViewCommandKey = `${'word' | 'lineboundary' | 'line'}:${'forward' | 'backward'}`;\ntype ViewCommandLookup = Readonly<Record<ViewCommandKey, ViewCommandFn>>;\n\nconst VIEW_MOVE_FNS: ViewCommandLookup = {\n\t'word:forward': moveWordForward,\n\t'word:backward': moveWordBackward,\n\t'lineboundary:forward': moveToLineEnd,\n\t'lineboundary:backward': moveToLineStart,\n\t'line:forward': moveLineDown,\n\t'line:backward': moveLineUp,\n};\n\nconst VIEW_EXTEND_FNS: ViewCommandLookup = {\n\t'word:forward': extendWordForward,\n\t'word:backward': extendWordBackward,\n\t'lineboundary:forward': extendToLineEnd,\n\t'lineboundary:backward': extendToLineStart,\n\t'line:forward': extendLineDown,\n\t'line:backward': extendLineUp,\n};\n\n/** Debounce delay for block-type announcements (ms). */\nconst ANNOUNCE_DEBOUNCE_MS = 150;\n\nexport class CaretNavigationPlugin implements Plugin {\n\treadonly id = 'caret-navigation';\n\treadonly name = 'Caret Navigation';\n\treadonly priority = 4;\n\n\tprivate context: PluginContext | null = null;\n\tprivate lastAnnouncedBlockId: BlockId | null = null;\n\tprivate announceTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate announce: ((text: string) => void) | null = null;\n\n\tinit(context: PluginContext): void {\n\t\tthis.context = context;\n\t\tthis.announce = (text: string) => context.announce(text);\n\t\tconst mac: boolean = isMac();\n\n\t\tconst keymap: Record<string, () => boolean> = {\n\t\t\t// --- Character movement (no modifier) ---\n\t\t\t// Handled natively by browser for basic cases, but Shift+Arrow\n\t\t\t// needs programmatic extend to correctly clear storedMarks.\n\n\t\t\t// --- Character extend ---\n\t\t\t'Shift-ArrowRight': () => this.extendCharacterVisual('right'),\n\t\t\t'Shift-ArrowLeft': () => this.extendCharacterVisual('left'),\n\n\t\t\t// --- Line extend ---\n\t\t\t'Shift-ArrowUp': () => this.viewExtendCommand('backward', 'line'),\n\t\t\t'Shift-ArrowDown': () => this.viewExtendCommand('forward', 'line'),\n\n\t\t\t// --- Word movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Alt-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Alt-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Line boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'lineboundary'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tHome: () => this.viewMoveCommand('backward', 'lineboundary'),\n\t\t\t\t\t\tEnd: () => this.viewMoveCommand('forward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-Home': () => this.viewExtendCommand('backward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-End': () => this.viewExtendCommand('forward', 'lineboundary'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Document boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowUp': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-ArrowDown': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-ArrowUp': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-ArrowDown': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-Home': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-End': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-Home': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-End': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}),\n\t\t};\n\n\t\tcontext.registerKeymap(keymap, { priority: 'navigation' });\n\t}\n\n\t/** Announces block-type on cross-block cursor movement (debounced). */\n\tonStateChange(_oldState: EditorState, newState: EditorState, _tr: Transaction): void {\n\t\tconst newSel = newState.selection;\n\n\t\t// Only announce for collapsed text selections\n\t\tif (!isCollapsed(newSel) || isNodeSelection(newSel) || isGapCursor(newSel)) {\n\t\t\tif (this.announceTimer !== null) {\n\t\t\t\tclearTimeout(this.announceTimer);\n\t\t\t\tthis.announceTimer = null;\n\t\t\t}\n\t\t\tthis.lastAnnouncedBlockId = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tif (newBlockId === this.lastAnnouncedBlockId) return;\n\n\t\t// First observed block (initial focus) — remember but don't announce\n\t\tif (this.lastAnnouncedBlockId === null) {\n\t\t\tthis.lastAnnouncedBlockId = newBlockId;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.lastAnnouncedBlockId = newBlockId;\n\n\t\t// Debounce rapid navigation\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = setTimeout(() => {\n\t\t\tthis.announceTimer = null;\n\t\t\t// Skip if another plugin already announced (e.g. CodeBlockPlugin's \"Left code block\")\n\t\t\tif (this.context?.hasAnnouncement()) return;\n\t\t\tconst block = newState.getBlock(newBlockId);\n\t\t\tif (!block) return;\n\t\t\tconst label: string = getBlockTypeLabel(\n\t\t\t\tblock.type,\n\t\t\t\tblock.attrs as Record<string, unknown> | undefined,\n\t\t\t);\n\t\t\tthis.announce?.(label);\n\t\t}, ANNOUNCE_DEBOUNCE_MS);\n\t}\n\n\tdestroy(): void {\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = null;\n\t\tthis.announce = null;\n\t\tthis.context = null;\n\t}\n\n\t/** Dispatches a model-based movement command (no DOM needed). */\n\tprivate modelCommand(fn: (state: EditorState) => Transaction | null): boolean {\n\t\tif (!this.context) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Extends selection by one character based on visual left/right intent. */\n\tprivate extendCharacterVisual(visual: 'left' | 'right'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst state: EditorState = this.context.getState();\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(state, visual);\n\t\tconst tr: Transaction | null =\n\t\t\tdir === 'forward' ? extendCharacterForward(state) : extendCharacterBackward(state);\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based movement command (uses container + Selection.modify). */\n\tprivate viewMoveCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_MOVE_FNS, direction, granularity);\n\t}\n\n\t/** Dispatches a view-based move command with visual direction mapping for RTL. */\n\tprivate viewMoveVisual(visual: 'left' | 'right', granularity: 'word' | 'lineboundary'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewMoveCommand(dir, granularity);\n\t}\n\n\t/** Dispatches a view-based extend command. */\n\tprivate viewExtendCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_EXTEND_FNS, direction, granularity);\n\t}\n\n\tprivate dispatchViewCommand(\n\t\tlookup: ViewCommandLookup,\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst fn: ViewCommandFn | undefined = lookup[`${granularity}:${direction}`];\n\t\tif (!fn) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getContainer(), this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based extend command with visual direction mapping for RTL. */\n\tprivate viewExtendVisual(\n\t\tvisual: 'left' | 'right',\n\t\tgranularity: 'word' | 'lineboundary',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewExtendCommand(dir, granularity);\n\t}\n\n\t/**\n\t * Resolves logical movement direction from visual left/right intent.\n\t * In RTL blocks, visual directions are mirrored in offset space.\n\t */\n\tprivate resolveLogicalHorizontalDirection(\n\t\tstate: EditorState,\n\t\tvisual: 'left' | 'right',\n\t): 'forward' | 'backward' {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel) || isGapCursor(sel)) {\n\t\t\treturn visual === 'left' ? 'backward' : 'forward';\n\t\t}\n\t\tconst container: HTMLElement | undefined = this.context?.getContainer();\n\t\tif (!container) return visual === 'left' ? 'backward' : 'forward';\n\t\tconst blockEl: Element | null = container.querySelector(\n\t\t\t`[data-block-id=\"${sel.head.blockId}\"]`,\n\t\t);\n\t\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\t\tif (!isRtl) return visual === 'left' ? 'backward' : 'forward';\n\t\treturn visual === 'left' ? 'forward' : 'backward';\n\t}\n}\n"],"names":["SEGMENTER","Intl","Segmenter","granularity","nextGraphemeSize","text","offset","length","segment","index","prevGraphemeSize","lastSize","findWordBoundaryBackward","block","pos","content","getContentAtOffset","kind","test","char","findWordBoundaryForward","len","getBlockLength","resolveHead","state","sel","selection","isNodeSelection","isGapCursor","blockId","head","adjacentBlockId","currentBlockId","direction","blockOrder","getBlockOrder","idx","indexOf","targetIdx","moveCharacterForward","isCollapsed","moveTx","anchor","getBlock","step","getBlockText","blockOffsetToTextOffset","nextId","canCrossBlockBoundary","isVoidBlock","nodeSelTx","moveCharacterBackward","prevId","prevBlock","moveToBlockStart","moveToBlockEnd","blockLen","moveToDocumentStart","firstId","moveToDocumentEnd","lastId","lastBlock","extendCharacterForward","extendTx","extendCharacterBackward","extendToBlockStart","extendToBlockEnd","extendToDocumentStart","extendToDocumentEnd","lastBlockLen","viewMove","container","domSel","getSelection","modify","fallbackMove","origAnchor","anchorNode","origAnchorOff","anchorOffset","origFocus","focusNode","origFocusOff","focusOffset","newSel","readSelectionFromDOM","newBlockId","oldBlockId","newOffset","Math","min","setBaseAndExtent","viewExtend","fallbackExtend","oldHeadBlockId","newHeadBlockId","newHeadOffset","headBlock","targetId","targetBlock","targetOffset","navigateAcrossBlocks","moveWordForward","moveWordBackward","moveToLineStart","moveToLineEnd","moveLineUp","moveLineDown","extendWordForward","extendWordBackward","extendToLineStart","extendToLineEnd","extendLineUp","extendLineDown","BLOCK_TYPE_LABELS","paragraph","heading","code_block","blockquote","list_item","horizontal_rule","image","table","getBlockTypeLabel","typeName","attrs","level","buildAnnouncement","oldState","newState","tr","metadata","historyDirection","steps","type","nodeType","mt","schema","markTypes","branded","markType","wasActive","isMarkActive","nowActive","VIEW_MOVE_FNS","VIEW_EXTEND_FNS","CaretNavigationPlugin","id","name","priority","context","lastAnnouncedBlockId","announceTimer","announce","this","mac","isMac","keymap","extendCharacterVisual","viewExtendCommand","viewMoveVisual","viewExtendVisual","Home","viewMoveCommand","End","modelCommand","registerKeymap","_oldState","_tr","clearTimeout","setTimeout","hasAnnouncement","label","destroy","fn","getState","dispatch","visual","resolveLogicalHorizontalDirection","dispatchViewCommand","dir","lookup","getContainer","blockEl","querySelector","HTMLElement","getTextDirection"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { r as t } from "./resolvePluginLocale-B4w1OkPy.mjs";
|
|
2
|
-
import { f as e } from "./NodeResolver-
|
|
2
|
+
import { f as e } from "./NodeResolver-C2XYkeOi.mjs";
|
|
3
3
|
import { i, h as n } from "./Selection-BRTab5hO.mjs";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { a as l } from "./StyleRuntime-CP4NCwKT.mjs";
|
|
5
|
+
const a = { alignLeft: "Align Left", alignCenter: "Align Center", alignRight: "Align Right", justify: "Justify", toolbarLabel: "Alignment", toolbarTooltip: "Alignment" }, s = { alignLeft: "Linksbündig", alignCenter: "Zentriert", alignRight: "Rechtsbündig", justify: "Blocksatz", toolbarLabel: "Ausrichtung", toolbarTooltip: "Ausrichtung" }, o = { en: a, de: s, es: { alignLeft: "Alinear a la izquierda", alignCenter: "Centrar", alignRight: "Alinear a la derecha", justify: "Justificar", toolbarLabel: "Alineación", toolbarTooltip: "Alineación" }, fr: { alignLeft: "Aligner à gauche", alignCenter: "Centrer", alignRight: "Aligner à droite", justify: "Justifier", toolbarLabel: "Alignement", toolbarTooltip: "Alignement" }, zh: { alignLeft: "左对齐", alignCenter: "居中", alignRight: "右对齐", justify: "两端对齐", toolbarLabel: "对齐", toolbarTooltip: "对齐" }, ru: { alignLeft: "По левому краю", alignCenter: "По центру", alignRight: "По правому краю", justify: "По ширине", toolbarLabel: "Выравнивание", toolbarTooltip: "Выравнивание" }, ar: { alignLeft: "محاذاة لليسار", alignCenter: "توسيط", alignRight: "محاذاة لليمين", justify: "ضبط", toolbarLabel: "المحاذاة", toolbarTooltip: "المحاذاة" }, hi: { alignLeft: "बाएँ संरेखित करें", alignCenter: "केंद्र में", alignRight: "दाएँ संरेखित करें", justify: "समायोजित करें", toolbarLabel: "संरेखण", toolbarTooltip: "संरेखण" } }, r = { alignments: ["left", "center", "right", "justify"], alignableTypes: ["paragraph", "heading", "title", "subtitle", "table_cell", "image"], defaults: { image: "center" } }, g = { left: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/></svg>', center: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/></svg>', right: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/></svg>', justify: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zM3 3v2h18V3H3z"/></svg>' };
|
|
6
|
+
class c {
|
|
6
7
|
id = "alignment";
|
|
7
8
|
name = "Alignment";
|
|
8
9
|
priority = 90;
|
|
@@ -10,10 +11,10 @@ class g {
|
|
|
10
11
|
locale;
|
|
11
12
|
alignableTypes;
|
|
12
13
|
constructor(t2) {
|
|
13
|
-
this.config = { ...
|
|
14
|
+
this.config = { ...r, ...t2, defaults: { ...r.defaults, ...t2?.defaults } };
|
|
14
15
|
}
|
|
15
16
|
init(e2) {
|
|
16
|
-
this.locale = t(
|
|
17
|
+
this.locale = t(o, e2, this.config.locale), this.alignableTypes = new Set(this.config.alignableTypes), this.patchNodeSpecs(e2), this.registerCommands(e2), this.registerKeymaps(e2), this.registerToolbarItem(e2), this.registerMiddleware(e2);
|
|
17
18
|
}
|
|
18
19
|
patchNodeSpecs(t2) {
|
|
19
20
|
const e2 = t2.getSchemaRegistry();
|
|
@@ -24,20 +25,20 @@ class g {
|
|
|
24
25
|
const n2 = i2.toDOM, l2 = this.config.defaults[t3] ?? "left";
|
|
25
26
|
e2.removeNodeSpec(t3), e2.registerNodeSpec({ ...i2, attrs: { ...i2.attrs, align: { default: l2 } }, toDOM(t4) {
|
|
26
27
|
const e3 = n2.call(i2, t4);
|
|
27
|
-
return
|
|
28
|
+
return h(e3, t4), e3;
|
|
28
29
|
} });
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
registerCommands(t2) {
|
|
32
|
-
for (const e2 of this.config.alignments) t2.registerCommand(`align${
|
|
33
|
+
for (const e2 of this.config.alignments) t2.registerCommand(`align${f(e2)}`, () => this.setAlignment(t2, e2));
|
|
33
34
|
}
|
|
34
35
|
registerKeymaps(t2) {
|
|
35
36
|
const e2 = {};
|
|
36
37
|
this.config.alignments.includes("left") && (e2["Mod-Shift-L"] = () => t2.executeCommand("alignLeft")), this.config.alignments.includes("center") && (e2["Mod-Shift-E"] = () => t2.executeCommand("alignCenter")), this.config.alignments.includes("right") && (e2["Mod-Shift-R"] = () => t2.executeCommand("alignRight")), this.config.alignments.includes("justify") && (e2["Mod-Shift-J"] = () => t2.executeCommand("alignJustify")), Object.keys(e2).length > 0 && t2.registerKeymap(e2);
|
|
37
38
|
}
|
|
38
39
|
registerToolbarItem(t2) {
|
|
39
|
-
const e2 = this.config.alignments.map((t3) => ({ label: this.getAlignmentLabel(t3), command: `align${
|
|
40
|
-
t2.registerToolbarItem({ id: "alignment", group: "block", icon:
|
|
40
|
+
const e2 = this.config.alignments.map((t3) => ({ label: this.getAlignmentLabel(t3), command: `align${f(t3)}`, icon: g[t3] }));
|
|
41
|
+
t2.registerToolbarItem({ id: "alignment", group: "block", icon: g.left, label: this.locale.toolbarLabel, tooltip: this.locale.toolbarTooltip, command: "alignLeft", priority: 60, popupType: "dropdown", popupConfig: { items: e2 }, separatorAfter: this.config.separatorAfter, isActive: (t3) => this.isNonDefaultAlignment(t3), isEnabled: (t3) => this.isAlignable(t3) });
|
|
41
42
|
}
|
|
42
43
|
getAlignmentLabel(t2) {
|
|
43
44
|
return { left: this.locale.alignLeft, center: this.locale.alignCenter, right: this.locale.alignRight, justify: this.locale.justify }[t2];
|
|
@@ -83,18 +84,18 @@ class g {
|
|
|
83
84
|
return null != e2 && this.alignableTypes.has(e2.type);
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
|
-
function
|
|
87
|
+
function h(t2, e2) {
|
|
87
88
|
const i2 = e2.attrs?.align;
|
|
88
|
-
"string" == typeof i2 && "left" !== i2 && (t2
|
|
89
|
+
"string" == typeof i2 && "left" !== i2 && l(t2, "textAlign", i2);
|
|
89
90
|
}
|
|
90
|
-
function
|
|
91
|
+
function f(t2) {
|
|
91
92
|
return t2.charAt(0).toUpperCase() + t2.slice(1);
|
|
92
93
|
}
|
|
93
94
|
export {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
c as A,
|
|
96
|
+
g as a,
|
|
97
|
+
o as b,
|
|
98
|
+
s as c,
|
|
99
|
+
a as d
|
|
99
100
|
};
|
|
100
|
-
//# sourceMappingURL=index-
|
|
101
|
+
//# sourceMappingURL=index-toj88jtL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-toj88jtL.mjs","sources":["../../src/plugins/alignment/AlignmentLocale.ts","../../src/plugins/alignment/AlignmentPlugin.ts"],"sourcesContent":["/** Locale interface and default English locale for the AlignmentPlugin. */\n\nexport interface AlignmentLocale {\n\treadonly alignLeft: string;\n\treadonly alignCenter: string;\n\treadonly alignRight: string;\n\treadonly justify: string;\n\treadonly toolbarLabel: string;\n\treadonly toolbarTooltip: string;\n}\n\n// --- Default English Locale ---\n\nexport const ALIGNMENT_LOCALE_EN: AlignmentLocale = {\n\talignLeft: 'Align Left',\n\talignCenter: 'Align Center',\n\talignRight: 'Align Right',\n\tjustify: 'Justify',\n\ttoolbarLabel: 'Alignment',\n\ttoolbarTooltip: 'Alignment',\n};\n\n// --- German Locale ---\n\nexport const ALIGNMENT_LOCALE_DE: AlignmentLocale = {\n\talignLeft: 'Linksbündig',\n\talignCenter: 'Zentriert',\n\talignRight: 'Rechtsbündig',\n\tjustify: 'Blocksatz',\n\ttoolbarLabel: 'Ausrichtung',\n\ttoolbarTooltip: 'Ausrichtung',\n};\n\n// --- Spanish Locale ---\n\nexport const ALIGNMENT_LOCALE_ES: AlignmentLocale = {\n\talignLeft: 'Alinear a la izquierda',\n\talignCenter: 'Centrar',\n\talignRight: 'Alinear a la derecha',\n\tjustify: 'Justificar',\n\ttoolbarLabel: 'Alineación',\n\ttoolbarTooltip: 'Alineación',\n};\n\n// --- French Locale ---\n\nexport const ALIGNMENT_LOCALE_FR: AlignmentLocale = {\n\talignLeft: 'Aligner à gauche',\n\talignCenter: 'Centrer',\n\talignRight: 'Aligner à droite',\n\tjustify: 'Justifier',\n\ttoolbarLabel: 'Alignement',\n\ttoolbarTooltip: 'Alignement',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const ALIGNMENT_LOCALE_ZH: AlignmentLocale = {\n\talignLeft: '左对齐',\n\talignCenter: '居中',\n\talignRight: '右对齐',\n\tjustify: '两端对齐',\n\ttoolbarLabel: '对齐',\n\ttoolbarTooltip: '对齐',\n};\n\n// --- Russian Locale ---\n\nexport const ALIGNMENT_LOCALE_RU: AlignmentLocale = {\n\talignLeft: 'По левому краю',\n\talignCenter: 'По центру',\n\talignRight: 'По правому краю',\n\tjustify: 'По ширине',\n\ttoolbarLabel: 'Выравнивание',\n\ttoolbarTooltip: 'Выравнивание',\n};\n\n// --- Arabic Locale ---\n\nexport const ALIGNMENT_LOCALE_AR: AlignmentLocale = {\n\talignLeft: 'محاذاة لليسار',\n\talignCenter: 'توسيط',\n\talignRight: 'محاذاة لليمين',\n\tjustify: 'ضبط',\n\ttoolbarLabel: 'المحاذاة',\n\ttoolbarTooltip: 'المحاذاة',\n};\n\n// --- Hindi Locale ---\n\nexport const ALIGNMENT_LOCALE_HI: AlignmentLocale = {\n\talignLeft: 'बाएँ संरेखित करें',\n\talignCenter: 'केंद्र में',\n\talignRight: 'दाएँ संरेखित करें',\n\tjustify: 'समायोजित करें',\n\ttoolbarLabel: 'संरेखण',\n\ttoolbarTooltip: 'संरेखण',\n};\n\n// --- Locale Map ---\n\nexport const ALIGNMENT_LOCALES: Record<string, AlignmentLocale> = {\n\ten: ALIGNMENT_LOCALE_EN,\n\tde: ALIGNMENT_LOCALE_DE,\n\tes: ALIGNMENT_LOCALE_ES,\n\tfr: ALIGNMENT_LOCALE_FR,\n\tzh: ALIGNMENT_LOCALE_ZH,\n\tru: ALIGNMENT_LOCALE_RU,\n\tar: ALIGNMENT_LOCALE_AR,\n\thi: ALIGNMENT_LOCALE_HI,\n};\n","/**\n * AlignmentPlugin: adds left/center/right/justify alignment as a block\n * attribute on paragraphs, headings, images, and other alignable types.\n * Patches their NodeSpecs to render the `align` attribute via inline\n * `text-align` style and provides toggle commands, keyboard shortcuts,\n * and a toolbar dropdown. Handles both TextSelection and NodeSelection.\n */\n\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport type { BlockNode } from '../../model/Document.js';\nimport { findNodePath } from '../../model/NodeResolver.js';\nimport { isNodeSelection, isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport { setStyleProperty } from '../../style/StyleRuntime.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { ALIGNMENT_LOCALES, type AlignmentLocale } from './AlignmentLocale.js';\n\n// --- Public Types ---\n\nexport type BlockAlignment = 'left' | 'center' | 'right' | 'justify';\n\nexport interface AlignmentConfig {\n\t/** Which alignments to expose. Defaults to all four. */\n\treadonly alignments: readonly BlockAlignment[];\n\t/** Block types that support alignment. Defaults to paragraph + heading + title + subtitle + table_cell + image. */\n\treadonly alignableTypes: readonly string[];\n\t/** Per-type default alignment (e.g. `{ image: 'center' }`). Falls back to `'left'`. */\n\treadonly defaults: Readonly<Record<string, BlockAlignment>>;\n\t/** When true, a separator is rendered after the toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: AlignmentLocale;\n}\n\n// --- Constants ---\n\nconst DEFAULT_CONFIG: AlignmentConfig = {\n\talignments: ['left', 'center', 'right', 'justify'],\n\talignableTypes: ['paragraph', 'heading', 'title', 'subtitle', 'table_cell', 'image'],\n\tdefaults: { image: 'center' },\n};\n\nexport const ALIGNMENT_ICONS: Readonly<Record<BlockAlignment, string>> = {\n\tleft: '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z\"/></svg>',\n\tcenter:\n\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z\"/></svg>',\n\tright:\n\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z\"/></svg>',\n\tjustify:\n\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zM3 3v2h18V3H3z\"/></svg>',\n};\n\n// --- Plugin ---\n\nexport class AlignmentPlugin implements Plugin {\n\treadonly id = 'alignment';\n\treadonly name = 'Alignment';\n\treadonly priority = 90;\n\n\tprivate readonly config: AlignmentConfig;\n\tprivate locale!: AlignmentLocale;\n\tprivate alignableTypes!: ReadonlySet<string>;\n\n\tconstructor(config?: Partial<AlignmentConfig>) {\n\t\tthis.config = {\n\t\t\t...DEFAULT_CONFIG,\n\t\t\t...config,\n\t\t\tdefaults: { ...DEFAULT_CONFIG.defaults, ...config?.defaults },\n\t\t};\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(ALIGNMENT_LOCALES, context, this.config.locale);\n\n\t\tthis.alignableTypes = new Set(this.config.alignableTypes);\n\t\tthis.patchNodeSpecs(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerKeymaps(context);\n\t\tthis.registerToolbarItem(context);\n\t\tthis.registerMiddleware(context);\n\t}\n\n\t// --- NodeSpec Patching ---\n\n\t/**\n\t * Patches existing NodeSpecs for alignable block types to support the\n\t * `align` attribute and render it as an inline style. Skips types\n\t * that already define an `align` attribute in their spec.\n\t */\n\tprivate patchNodeSpecs(context: PluginContext): void {\n\t\tconst registry = context.getSchemaRegistry();\n\n\t\tfor (const type of this.config.alignableTypes) {\n\t\t\tconst spec = registry.getNodeSpec(type);\n\t\t\tif (!spec) continue;\n\n\t\t\t// Skip types that already declare an `align` attr (e.g. image)\n\t\t\tif (spec.attrs?.align) continue;\n\n\t\t\tconst originalToDOM = spec.toDOM;\n\t\t\tconst defaultAlign: BlockAlignment = this.config.defaults[type] ?? 'left';\n\n\t\t\tregistry.removeNodeSpec(type);\n\t\t\tregistry.registerNodeSpec({\n\t\t\t\t...spec,\n\t\t\t\tattrs: {\n\t\t\t\t\t...spec.attrs,\n\t\t\t\t\talign: { default: defaultAlign },\n\t\t\t\t},\n\t\t\t\ttoDOM(node) {\n\t\t\t\t\tconst el = originalToDOM.call(spec, node);\n\t\t\t\t\tapplyAlignment(el, node);\n\t\t\t\t\treturn el;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\t// --- Commands ---\n\n\tprivate registerCommands(context: PluginContext): void {\n\t\tfor (const alignment of this.config.alignments) {\n\t\t\tcontext.registerCommand(`align${capitalize(alignment)}`, () => {\n\t\t\t\treturn this.setAlignment(context, alignment);\n\t\t\t});\n\t\t}\n\t}\n\n\t// --- Keymaps ---\n\n\tprivate registerKeymaps(context: PluginContext): void {\n\t\tconst bindings: Record<string, () => boolean> = {};\n\n\t\tif (this.config.alignments.includes('left')) {\n\t\t\tbindings['Mod-Shift-L'] = () => context.executeCommand('alignLeft');\n\t\t}\n\t\tif (this.config.alignments.includes('center')) {\n\t\t\tbindings['Mod-Shift-E'] = () => context.executeCommand('alignCenter');\n\t\t}\n\t\tif (this.config.alignments.includes('right')) {\n\t\t\tbindings['Mod-Shift-R'] = () => context.executeCommand('alignRight');\n\t\t}\n\t\tif (this.config.alignments.includes('justify')) {\n\t\t\tbindings['Mod-Shift-J'] = () => context.executeCommand('alignJustify');\n\t\t}\n\n\t\tif (Object.keys(bindings).length > 0) {\n\t\t\tcontext.registerKeymap(bindings);\n\t\t}\n\t}\n\n\t// --- Toolbar ---\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst dropdownItems = this.config.alignments.map((alignment) => ({\n\t\t\tlabel: this.getAlignmentLabel(alignment),\n\t\t\tcommand: `align${capitalize(alignment)}`,\n\t\t\ticon: ALIGNMENT_ICONS[alignment],\n\t\t}));\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'alignment',\n\t\t\tgroup: 'block',\n\t\t\ticon: ALIGNMENT_ICONS.left,\n\t\t\tlabel: this.locale.toolbarLabel,\n\t\t\ttooltip: this.locale.toolbarTooltip,\n\t\t\tcommand: 'alignLeft',\n\t\t\tpriority: 60,\n\t\t\tpopupType: 'dropdown',\n\t\t\tpopupConfig: { items: dropdownItems },\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\tisActive: (state) => this.isNonDefaultAlignment(state),\n\t\t\tisEnabled: (state) => this.isAlignable(state),\n\t\t});\n\t}\n\n\tprivate getAlignmentLabel(alignment: BlockAlignment): string {\n\t\tconst labels: Record<BlockAlignment, string> = {\n\t\t\tleft: this.locale.alignLeft,\n\t\t\tcenter: this.locale.alignCenter,\n\t\t\tright: this.locale.alignRight,\n\t\t\tjustify: this.locale.justify,\n\t\t};\n\t\treturn labels[alignment];\n\t}\n\n\t// --- Middleware ---\n\n\t/**\n\t * Preserves the `align` attribute when other plugins change the block\n\t * type (e.g. paragraph → heading) via `setBlockType`, which replaces attrs.\n\t */\n\tprivate registerMiddleware(context: PluginContext): void {\n\t\tcontext.registerMiddleware(\n\t\t\t(tr, _state, next) => {\n\t\t\t\tlet patched = false;\n\n\t\t\t\tconst patchedSteps = tr.steps.map((step) => {\n\t\t\t\t\tif (step.type !== 'setBlockType') return step;\n\t\t\t\t\tif (!this.alignableTypes.has(step.nodeType)) return step;\n\n\t\t\t\t\tconst prevAlign = step.previousAttrs?.align;\n\t\t\t\t\tif (!prevAlign || prevAlign === 'left') return step;\n\n\t\t\t\t\t// Carry forward align into new attrs\n\t\t\t\t\tpatched = true;\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tattrs: { ...step.attrs, align: prevAlign },\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\tnext(patched ? { ...tr, steps: patchedSteps } : tr);\n\t\t\t},\n\t\t\t{ name: 'alignment:preserve-align' },\n\t\t);\n\t}\n\n\t// --- Alignment Logic ---\n\n\t/**\n\t * Gets the selected block, handling both TextSelection and NodeSelection.\n\t */\n\tprivate getSelectedBlock(state: EditorState): BlockNode | undefined {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel)) {\n\t\t\treturn state.getBlock(sel.nodeId);\n\t\t}\n\t\tif (isTextSelection(sel)) {\n\t\t\treturn state.getBlock(sel.anchor.blockId);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Gets the block ID of the selected block, handling both selection types.\n\t */\n\tprivate getSelectedBlockId(state: EditorState): BlockId | undefined {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel)) return sel.nodeId;\n\t\tif (isTextSelection(sel)) return sel.anchor.blockId;\n\t\treturn undefined;\n\t}\n\n\tprivate setAlignment(context: PluginContext, alignment: BlockAlignment): boolean {\n\t\tconst state = context.getState();\n\t\tconst block = this.getSelectedBlock(state);\n\t\tif (!block || !this.alignableTypes.has(block.type)) return false;\n\n\t\tconst blockId = this.getSelectedBlockId(state);\n\t\tif (!blockId) return false;\n\n\t\tconst path = findNodePath(state.doc, blockId);\n\t\tif (!path) return false;\n\n\t\tconst newAttrs = { ...block.attrs, align: alignment };\n\n\t\tconst tr = state\n\t\t\t.transaction('command')\n\t\t\t.setNodeAttr(path as BlockId[], newAttrs)\n\t\t\t.setSelection(state.selection)\n\t\t\t.build();\n\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t}\n\n\tprivate isNonDefaultAlignment(state: EditorState): boolean {\n\t\tconst block = this.getSelectedBlock(state);\n\t\tif (!block || !this.alignableTypes.has(block.type)) return false;\n\t\tconst align = block.attrs?.align;\n\t\tconst defaultAlign: BlockAlignment = this.config.defaults[block.type] ?? 'left';\n\t\treturn align != null && align !== defaultAlign;\n\t}\n\n\tprivate isAlignable(state: EditorState): boolean {\n\t\tconst block = this.getSelectedBlock(state);\n\t\treturn block != null && this.alignableTypes.has(block.type);\n\t}\n}\n\n// --- Helpers ---\n\nfunction applyAlignment(el: HTMLElement, node: BlockNode): void {\n\tconst align = node.attrs?.align;\n\tif (typeof align === 'string' && align !== 'left') {\n\t\tsetStyleProperty(el, 'textAlign', align);\n\t}\n}\n\nfunction capitalize(s: string): string {\n\treturn s.charAt(0).toUpperCase() + s.slice(1);\n}\n"],"names":["ALIGNMENT_LOCALE_EN","alignLeft","alignCenter","alignRight","justify","toolbarLabel","toolbarTooltip","ALIGNMENT_LOCALE_DE","ALIGNMENT_LOCALES","en","de","es","fr","zh","ru","ar","hi","DEFAULT_CONFIG","alignments","alignableTypes","defaults","image","ALIGNMENT_ICONS","left","center","right","AlignmentPlugin","id","name","priority","config","locale","this","context","resolvePluginLocale","Set","patchNodeSpecs","registerCommands","registerKeymaps","registerToolbarItem","registerMiddleware","registry","getSchemaRegistry","type","spec","getNodeSpec","attrs","align","originalToDOM","toDOM","defaultAlign","removeNodeSpec","registerNodeSpec","default","node","el","call","applyAlignment","alignment","registerCommand","capitalize","setAlignment","bindings","includes","executeCommand","Object","keys","length","registerKeymap","dropdownItems","map","label","getAlignmentLabel","command","icon","group","tooltip","popupType","popupConfig","items","separatorAfter","isActive","state","isNonDefaultAlignment","isEnabled","isAlignable","tr","_state","next","patched","patchedSteps","steps","step","has","nodeType","prevAlign","previousAttrs","sel","selection","isNodeSelection","getBlock","nodeId","isTextSelection","anchor","blockId","getState","block","getSelectedBlock","getSelectedBlockId","path","findNodePath","doc","newAttrs","transaction","setNodeAttr","setSelection","build","dispatch","setStyleProperty","s","charAt","toUpperCase","slice"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttributedMarkCommands.d.ts","sourceRoot":"","sources":["../../src/commands/AttributedMarkCommands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AttributedMarkCommands.d.ts","sourceRoot":"","sources":["../../src/commands/AttributedMarkCommands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAa,IAAI,EAAY,MAAM,sBAAsB,CAAC;AAUtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,IAAI,CAqCtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,YAAY,GACxB,WAAW,GAAG,IAAI,CAgCpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,CAAC,EACzE,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,CAAC,EACf,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;CAAE,KAAK,CAAC,GAAG,IAAI,GAC7F,CAAC,GAAG,IAAI,CAcV;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EACtE,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,CAAC,GACb,OAAO,CAET"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MarkTypeName } from '../model/TypeBrands.js';
|
|
2
2
|
import { EditorState } from '../state/EditorState.js';
|
|
3
3
|
import { Transaction } from '../state/Transaction.js';
|
|
4
4
|
export interface FeatureConfig {
|
|
@@ -10,10 +10,10 @@ export interface FeatureConfig {
|
|
|
10
10
|
* Toggles a mark on the current selection.
|
|
11
11
|
* If collapsed, toggles stored marks. If range, applies/removes from text.
|
|
12
12
|
*/
|
|
13
|
-
export declare function toggleMark(state: EditorState, markType:
|
|
13
|
+
export declare function toggleMark(state: EditorState, markType: MarkTypeName, features?: FeatureConfig): Transaction | null;
|
|
14
14
|
export declare function toggleBold(state: EditorState, features?: FeatureConfig): Transaction | null;
|
|
15
15
|
export declare function toggleItalic(state: EditorState, features?: FeatureConfig): Transaction | null;
|
|
16
16
|
export declare function toggleUnderline(state: EditorState, features?: FeatureConfig): Transaction | null;
|
|
17
17
|
/** Checks if a mark is active at the current selection. */
|
|
18
|
-
export declare function isMarkActive(state: EditorState, markType:
|
|
18
|
+
export declare function isMarkActive(state: EditorState, markType: MarkTypeName): boolean;
|
|
19
19
|
//# sourceMappingURL=MarkCommands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkCommands.d.ts","sourceRoot":"","sources":["../../src/commands/MarkCommands.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"MarkCommands.d.ts","sourceRoot":"","sources":["../../src/commands/MarkCommands.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAK3D,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAID;;;GAGG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,YAAY,EACtB,QAAQ,GAAE,aAA+B,GACvC,WAAW,GAAG,IAAI,CA0CpB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,IAAI,CAE3F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,IAAI,CAE7F;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,IAAI,CAEhG;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAehF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSSClassCollector: maps CSS declarations to unique class names during serialization.
|
|
3
|
+
* Deduplicates identical declaration sets and produces a minimal stylesheet.
|
|
4
|
+
* Uses content-hash (FNV-1a) for deterministic, document-order-independent class names.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Stateful collector that assigns CSS class names to unique declaration sets.
|
|
8
|
+
* Used during a single serialization pass, then produces the collected stylesheet.
|
|
9
|
+
*
|
|
10
|
+
* Class names are deterministic: the same declarations always produce the same
|
|
11
|
+
* class name, regardless of encounter order across documents.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CSSClassCollector {
|
|
14
|
+
/** Maps normalized declarations → class name. */
|
|
15
|
+
private readonly classMap;
|
|
16
|
+
/** Tracks used hashes to handle the (extremely rare) collision case. */
|
|
17
|
+
private readonly usedHashes;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the class name for the given CSS declarations.
|
|
20
|
+
* If the same (normalized) declarations were seen before, returns the existing class.
|
|
21
|
+
*/
|
|
22
|
+
getClassName(declarations: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the semantic alignment class name for a given alignment value.
|
|
25
|
+
* Unlike style classes, alignment classes use descriptive names.
|
|
26
|
+
*/
|
|
27
|
+
getAlignmentClassName(alignment: string): string;
|
|
28
|
+
/** Produces CSS rules for all collected classes. Returns empty string if none collected. */
|
|
29
|
+
toCSS(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a map from class name → CSS declarations for round-trip support.
|
|
32
|
+
* Used by `setContentHTML` to rehydrate class-based HTML.
|
|
33
|
+
*/
|
|
34
|
+
toStyleMap(): ReadonlyMap<string, string>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=CSSClassCollector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CSSClassCollector.d.ts","sourceRoot":"","sources":["../../src/editor/CSSClassCollector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4CH;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC7B,iDAAiD;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0B;IAErD;;;OAGG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAoB1C;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAUhD,4FAA4F;IAC5F,KAAK,IAAI,MAAM;IAUf;;;OAGG;IACH,UAAU,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;CAOzC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for class-based CSS serialization of document HTML.
|
|
3
|
+
*/
|
|
4
|
+
/** Controls whether exported HTML uses inline `style` attributes or CSS class names. */
|
|
5
|
+
export type CSSMode = 'inline' | 'classes';
|
|
6
|
+
/** Options for {@link NotectlEditor.getContentHTML}. */
|
|
7
|
+
export interface ContentHTMLOptions {
|
|
8
|
+
readonly pretty?: boolean;
|
|
9
|
+
readonly cssMode?: CSSMode;
|
|
10
|
+
}
|
|
11
|
+
/** Result of class-based HTML serialization (`cssMode: 'classes'`). */
|
|
12
|
+
export interface ContentCSSResult {
|
|
13
|
+
readonly html: string;
|
|
14
|
+
readonly css: string;
|
|
15
|
+
/**
|
|
16
|
+
* Maps each generated class name to its CSS declarations.
|
|
17
|
+
* Pass to `setContentHTML(html, { styleMap })` for round-trip re-import.
|
|
18
|
+
*/
|
|
19
|
+
readonly styleMap: ReadonlyMap<string, string>;
|
|
20
|
+
}
|
|
21
|
+
/** Options for {@link NotectlEditor.setContentHTML} when importing class-based HTML. */
|
|
22
|
+
export interface SetContentHTMLOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Style map from a previous `getContentHTML({ cssMode: 'classes' })` call.
|
|
25
|
+
* Used to rehydrate class-based HTML back into styled content.
|
|
26
|
+
*/
|
|
27
|
+
readonly styleMap?: ReadonlyMap<string, string>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ContentHTMLTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContentHTMLTypes.d.ts","sourceRoot":"","sources":["../../src/editor/ContentHTMLTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wFAAwF;AACxF,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3C,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,wFAAwF;AACxF,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD"}
|