@notectl/core 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs → AttrRegistry-Ci0qAvtS.mjs} +2 -2
  3. package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs.map → AttrRegistry-Ci0qAvtS.mjs.map} +1 -1
  4. package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs +58 -0
  5. package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs.map +1 -0
  6. package/dist/chunks/{CaretNavigation-CAJXoaae.mjs → CaretNavigation-qLfeL0So.mjs} +56 -56
  7. package/dist/chunks/{CaretNavigation-CAJXoaae.mjs.map → CaretNavigation-qLfeL0So.mjs.map} +1 -1
  8. package/dist/chunks/ColorGrid-BBkN0AOb.mjs +80 -0
  9. package/dist/chunks/ColorGrid-BBkN0AOb.mjs.map +1 -0
  10. package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs +37 -0
  11. package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs.map +1 -0
  12. package/dist/chunks/ColorValidation-CV4f1wu6.mjs +39 -0
  13. package/dist/chunks/ColorValidation-CV4f1wu6.mjs.map +1 -0
  14. package/dist/chunks/{Document-CuwE73FF.mjs → Document-DfZ1NOjs.mjs} +15 -15
  15. package/dist/chunks/{Document-CuwE73FF.mjs.map → Document-DfZ1NOjs.mjs.map} +1 -1
  16. package/dist/chunks/MarkCommands-uybmkw9l.mjs +94 -0
  17. package/dist/chunks/MarkCommands-uybmkw9l.mjs.map +1 -0
  18. package/dist/chunks/{NavigationUtils-CyW0Vo8A.mjs → NavigationUtils-Cspt2ZrK.mjs} +3 -3
  19. package/dist/chunks/{NavigationUtils-CyW0Vo8A.mjs.map → NavigationUtils-Cspt2ZrK.mjs.map} +1 -1
  20. package/dist/chunks/{NodeResolver-B-wzthAQ.mjs → NodeResolver-CzLmmrph.mjs} +2 -2
  21. package/dist/chunks/{NodeResolver-B-wzthAQ.mjs.map → NodeResolver-CzLmmrph.mjs.map} +1 -1
  22. package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs +30 -0
  23. package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs.map +1 -0
  24. package/dist/chunks/RangeIterator-Bye0UtbY.mjs +16 -0
  25. package/dist/chunks/RangeIterator-Bye0UtbY.mjs.map +1 -0
  26. package/dist/chunks/{Selection-DmFDwiPJ.mjs → Selection-BRTab5hO.mjs} +7 -7
  27. package/dist/chunks/{Selection-DmFDwiPJ.mjs.map → Selection-BRTab5hO.mjs.map} +1 -1
  28. package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
  29. package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs → ToolbarKeyboardNav-DhpL8zNV.mjs} +3 -3
  30. package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs.map → ToolbarKeyboardNav-DhpL8zNV.mjs.map} +1 -1
  31. package/dist/chunks/{ToolbarPlugin-B6JETWaw.mjs → ToolbarPlugin-B7oJRzpl.mjs} +339 -127
  32. package/dist/chunks/ToolbarPlugin-B7oJRzpl.mjs.map +1 -0
  33. package/dist/chunks/{index-CPyHuEzh.mjs → index-Bt_d4IqB.mjs} +12 -12
  34. package/dist/chunks/{index-CPyHuEzh.mjs.map → index-Bt_d4IqB.mjs.map} +1 -1
  35. package/dist/chunks/{index-BigJ0z6n.mjs → index-CFJFD4lF.mjs} +8 -8
  36. package/dist/chunks/{index-BigJ0z6n.mjs.map → index-CFJFD4lF.mjs.map} +1 -1
  37. package/dist/chunks/{index-D67mP19g.mjs → index-CFPP456m.mjs} +13 -13
  38. package/dist/chunks/{index-D67mP19g.mjs.map → index-CFPP456m.mjs.map} +1 -1
  39. package/dist/chunks/{index-DQryjfJs.mjs → index-CH1r7DL-.mjs} +259 -300
  40. package/dist/chunks/index-CH1r7DL-.mjs.map +1 -0
  41. package/dist/chunks/{index-tBDVaSjG.mjs → index-CfyS_pgH.mjs} +28 -21
  42. package/dist/chunks/index-CfyS_pgH.mjs.map +1 -0
  43. package/dist/chunks/{index-DIAXWwgJ.mjs → index-Ckzqtn5D.mjs} +17 -14
  44. package/dist/chunks/{index-DIAXWwgJ.mjs.map → index-Ckzqtn5D.mjs.map} +1 -1
  45. package/dist/chunks/{index-BDMP86F4.mjs → index-CrKz_IEn.mjs} +35 -35
  46. package/dist/chunks/{index-BDMP86F4.mjs.map → index-CrKz_IEn.mjs.map} +1 -1
  47. package/dist/chunks/{index-B0UFAfyb.mjs → index-DGKQhSAl.mjs} +5 -5
  48. package/dist/chunks/{index-B0UFAfyb.mjs.map → index-DGKQhSAl.mjs.map} +1 -1
  49. package/dist/chunks/{index-83QoUENW.mjs → index-DIvHQtE2.mjs} +12 -9
  50. package/dist/chunks/{index-83QoUENW.mjs.map → index-DIvHQtE2.mjs.map} +1 -1
  51. package/dist/chunks/index-DayTn6Re.mjs +166 -0
  52. package/dist/chunks/index-DayTn6Re.mjs.map +1 -0
  53. package/dist/chunks/index-Deag_lVi.mjs +121 -0
  54. package/dist/chunks/index-Deag_lVi.mjs.map +1 -0
  55. package/dist/chunks/{index--z_Txzej.mjs → index-DmuuFS5D.mjs} +6 -6
  56. package/dist/chunks/{index--z_Txzej.mjs.map → index-DmuuFS5D.mjs.map} +1 -1
  57. package/dist/chunks/{index-DRZwd-1W.mjs → index-DoCvDkfj.mjs} +79 -96
  58. package/dist/chunks/index-DoCvDkfj.mjs.map +1 -0
  59. package/dist/chunks/index-DuRNETFv.mjs +296 -0
  60. package/dist/chunks/index-DuRNETFv.mjs.map +1 -0
  61. package/dist/chunks/{index-CPjf45-o.mjs → index-Dum8yE_t.mjs} +14 -14
  62. package/dist/chunks/{index-CPjf45-o.mjs.map → index-Dum8yE_t.mjs.map} +1 -1
  63. package/dist/chunks/{index-BWXkaC2Z.mjs → index-Dy6SllXW.mjs} +53 -65
  64. package/dist/chunks/index-Dy6SllXW.mjs.map +1 -0
  65. package/dist/chunks/{index-D8tbbW97.mjs → index-E7OgN7hM.mjs} +2 -2
  66. package/dist/chunks/{index-D8tbbW97.mjs.map → index-E7OgN7hM.mjs.map} +1 -1
  67. package/dist/chunks/{index-D4GKlc-A.mjs → index-yhz2qeeh.mjs} +224 -222
  68. package/dist/chunks/index-yhz2qeeh.mjs.map +1 -0
  69. package/dist/commands/AttributedMarkCommands.d.ts +46 -0
  70. package/dist/commands/AttributedMarkCommands.d.ts.map +1 -0
  71. package/dist/commands/CommandHelpers.d.ts +21 -0
  72. package/dist/commands/CommandHelpers.d.ts.map +1 -0
  73. package/dist/commands/Commands.d.ts +2 -8
  74. package/dist/commands/Commands.d.ts.map +1 -1
  75. package/dist/commands/DeleteCommands.d.ts.map +1 -1
  76. package/dist/commands/GapCursorCommands.d.ts.map +1 -1
  77. package/dist/commands/NodeSelectionCommands.d.ts.map +1 -1
  78. package/dist/commands/PasteCommand.d.ts.map +1 -1
  79. package/dist/commands/RangeIterator.d.ts +10 -0
  80. package/dist/commands/RangeIterator.d.ts.map +1 -0
  81. package/dist/editor/DocumentParser.d.ts.map +1 -1
  82. package/dist/editor/DocumentSerializer.d.ts +2 -0
  83. package/dist/editor/DocumentSerializer.d.ts.map +1 -1
  84. package/dist/editor/MarkSerializer.d.ts +11 -0
  85. package/dist/editor/MarkSerializer.d.ts.map +1 -0
  86. package/dist/editor/NotectlEditor.d.ts +6 -3
  87. package/dist/editor/NotectlEditor.d.ts.map +1 -1
  88. package/dist/editor/PaperLayoutController.d.ts +2 -0
  89. package/dist/editor/PaperLayoutController.d.ts.map +1 -1
  90. package/dist/editor/styles/font-select.d.ts +1 -1
  91. package/dist/editor/styles/font-select.d.ts.map +1 -1
  92. package/dist/editor/styles/font-size-select.d.ts +1 -1
  93. package/dist/editor/styles/font-size-select.d.ts.map +1 -1
  94. package/dist/editor/styles/heading-select.d.ts +1 -1
  95. package/dist/editor/styles/heading-select.d.ts.map +1 -1
  96. package/dist/editor/styles/toolbar.d.ts +1 -1
  97. package/dist/editor/styles/toolbar.d.ts.map +1 -1
  98. package/dist/index.d.ts +11 -2
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/input/ClipboardHandler.d.ts +0 -2
  101. package/dist/input/ClipboardHandler.d.ts.map +1 -1
  102. package/dist/model/MarkSpec.d.ts +6 -0
  103. package/dist/model/MarkSpec.d.ts.map +1 -1
  104. package/dist/notectl-core.mjs +2245 -2085
  105. package/dist/notectl-core.mjs.map +1 -1
  106. package/dist/notectl-core.umd.js +5568 -5345
  107. package/dist/notectl-core.umd.js.map +1 -1
  108. package/dist/plugins/PluginManager.d.ts +2 -0
  109. package/dist/plugins/PluginManager.d.ts.map +1 -1
  110. package/dist/plugins/alignment.mjs +3 -3
  111. package/dist/plugins/blockquote.mjs +2 -2
  112. package/dist/plugins/caret-navigation.mjs +1 -1
  113. package/dist/plugins/code-block.mjs +3 -3
  114. package/dist/plugins/font/FontPlugin.d.ts +0 -6
  115. package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
  116. package/dist/plugins/font-size/FontSizeOperations.d.ts.map +1 -1
  117. package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -5
  118. package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
  119. package/dist/plugins/font-size.mjs +2 -2
  120. package/dist/plugins/font.mjs +2 -2
  121. package/dist/plugins/gap-cursor.mjs +7 -7
  122. package/dist/plugins/hard-break.mjs +2 -25
  123. package/dist/plugins/hard-break.mjs.map +1 -1
  124. package/dist/plugins/heading/HeadingBlockTypePicker.d.ts +1 -7
  125. package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
  126. package/dist/plugins/heading/HeadingPlugin.d.ts +0 -6
  127. package/dist/plugins/heading/HeadingPlugin.d.ts.map +1 -1
  128. package/dist/plugins/heading.mjs +2 -2
  129. package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
  130. package/dist/plugins/highlight.mjs +2 -2
  131. package/dist/plugins/horizontal-rule.mjs +2 -2
  132. package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
  133. package/dist/plugins/image.mjs +3 -3
  134. package/dist/plugins/link/LinkPlugin.d.ts.map +1 -1
  135. package/dist/plugins/link.mjs +2 -2
  136. package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
  137. package/dist/plugins/list.mjs +2 -2
  138. package/dist/plugins/shared/ColorGrid.d.ts +25 -0
  139. package/dist/plugins/shared/ColorGrid.d.ts.map +1 -0
  140. package/dist/plugins/shared/ColorMarkOperations.d.ts.map +1 -1
  141. package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
  142. package/dist/plugins/shared/ColorValidation.d.ts +13 -2
  143. package/dist/plugins/shared/ColorValidation.d.ts.map +1 -1
  144. package/dist/plugins/shared/PopupKeyboardPatterns.d.ts +43 -0
  145. package/dist/plugins/shared/PopupKeyboardPatterns.d.ts.map +1 -0
  146. package/dist/plugins/shared/PopupManager.d.ts +45 -0
  147. package/dist/plugins/shared/PopupManager.d.ts.map +1 -0
  148. package/dist/plugins/shared/PopupPositioning.d.ts +21 -0
  149. package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -0
  150. package/dist/plugins/strikethrough.mjs +2 -2
  151. package/dist/plugins/super-sub.mjs +2 -2
  152. package/dist/plugins/table/TableBorderColor.d.ts.map +1 -1
  153. package/dist/plugins/table/TableContextMenu.d.ts +2 -1
  154. package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
  155. package/dist/plugins/table.mjs +10 -10
  156. package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
  157. package/dist/plugins/text-color.mjs +2 -2
  158. package/dist/plugins/text-formatting.mjs +1 -1
  159. package/dist/plugins/toolbar/ToolbarItem.d.ts +11 -2
  160. package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
  161. package/dist/plugins/toolbar/ToolbarOverflowController.d.ts +5 -1
  162. package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
  163. package/dist/plugins/toolbar/ToolbarPlugin.d.ts +1 -0
  164. package/dist/plugins/toolbar/ToolbarPlugin.d.ts.map +1 -1
  165. package/dist/plugins/toolbar/ToolbarPopupController.d.ts +9 -1
  166. package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
  167. package/dist/plugins/toolbar/index.d.ts +1 -1
  168. package/dist/plugins/toolbar/index.d.ts.map +1 -1
  169. package/dist/plugins/toolbar.mjs +1 -1
  170. package/dist/presets/FullPreset.d.ts +16 -0
  171. package/dist/presets/FullPreset.d.ts.map +1 -0
  172. package/dist/presets/MinimalPreset.d.ts +7 -0
  173. package/dist/presets/MinimalPreset.d.ts.map +1 -0
  174. package/dist/presets/PresetTypes.d.ts +50 -0
  175. package/dist/presets/PresetTypes.d.ts.map +1 -0
  176. package/dist/state/BlockTreeOps.d.ts +21 -0
  177. package/dist/state/BlockTreeOps.d.ts.map +1 -0
  178. package/dist/state/InlineContentOps.d.ts +37 -0
  179. package/dist/state/InlineContentOps.d.ts.map +1 -0
  180. package/dist/state/StepApplication.d.ts.map +1 -1
  181. package/dist/test/PluginTestUtils.d.ts +4 -0
  182. package/dist/test/PluginTestUtils.d.ts.map +1 -1
  183. package/package.json +2 -2
  184. package/dist/chunks/ColorNames-BdsHwA2f.mjs +0 -59
  185. package/dist/chunks/ColorNames-BdsHwA2f.mjs.map +0 -1
  186. package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs +0 -125
  187. package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs.map +0 -1
  188. package/dist/chunks/Commands-CicwYoTQ.mjs +0 -286
  189. package/dist/chunks/Commands-CicwYoTQ.mjs.map +0 -1
  190. package/dist/chunks/MarkCommands-CC37WA7Z.mjs +0 -94
  191. package/dist/chunks/MarkCommands-CC37WA7Z.mjs.map +0 -1
  192. package/dist/chunks/ToolbarPlugin-B6JETWaw.mjs.map +0 -1
  193. package/dist/chunks/index-BWXkaC2Z.mjs.map +0 -1
  194. package/dist/chunks/index-D4GKlc-A.mjs.map +0 -1
  195. package/dist/chunks/index-DQryjfJs.mjs.map +0 -1
  196. package/dist/chunks/index-DRZwd-1W.mjs.map +0 -1
  197. package/dist/chunks/index-DUC1X43Z.mjs +0 -209
  198. package/dist/chunks/index-DUC1X43Z.mjs.map +0 -1
  199. package/dist/chunks/index-DipAT_Q9.mjs +0 -191
  200. package/dist/chunks/index-DipAT_Q9.mjs.map +0 -1
  201. package/dist/chunks/index-tBDVaSjG.mjs.map +0 -1
package/README.md CHANGED
@@ -136,8 +136,8 @@ See the [plugin documentation](https://samyssmile.github.io/notectl/plugins/over
136
136
  Read and write content in any format:
137
137
 
138
138
  ```ts
139
- editor.getHTML(); // export as HTML
140
- editor.setHTML('<p>Hello <strong>world</strong></p>'); // import HTML
139
+ editor.getContentHTML(); // export as HTML
140
+ editor.setContentHTML('<p>Hello <strong>world</strong></p>'); // import HTML
141
141
  editor.getJSON(); // structured JSON
142
142
  editor.getText(); // plain text
143
143
  editor.isEmpty(); // check if empty
@@ -1,4 +1,4 @@
1
- import { i as n } from "./Document-CuwE73FF.mjs";
1
+ import { i as n } from "./Document-DfZ1NOjs.mjs";
2
2
  function t(n2, t2) {
3
3
  return n2.type === t2;
4
4
  }
@@ -13,4 +13,4 @@ export {
13
13
  r as b,
14
14
  e as i
15
15
  };
16
- //# sourceMappingURL=AttrRegistry-I-VdmRnN.mjs.map
16
+ //# sourceMappingURL=AttrRegistry-Ci0qAvtS.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrRegistry-I-VdmRnN.mjs","sources":["../../src/model/AttrRegistry.ts"],"sourcesContent":["/**\n * Module-augmentable attribute registries for type-safe node/mark attributes.\n *\n * Plugins extend these interfaces via `declare module`:\n * @example\n * declare module '../../model/AttrRegistry.js' {\n * interface NodeAttrRegistry {\n * heading: { level: HeadingLevel; align?: BlockAlignment };\n * }\n * }\n */\n\nimport type { BlockAlignment } from '../plugins/alignment/AlignmentPlugin.js';\nimport type { BlockNode, InlineNode, Mark } from './Document.js';\nimport { isInlineNode } from './Document.js';\n\n/** Plugins augment this interface to register typed node attributes. */\nexport interface NodeAttrRegistry {\n\tparagraph: { align?: BlockAlignment };\n}\n\n/** Plugins augment this interface to register typed mark attributes. */\nexport interface MarkAttrRegistry {\n\tbold: Record<string, never>;\n\titalic: Record<string, never>;\n\tunderline: Record<string, never>;\n}\n\n/** Resolves typed attributes for known node types, falls back for unknown. */\nexport type NodeAttrsFor<T extends string> = T extends keyof NodeAttrRegistry\n\t? NodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Resolves typed attributes for known mark types, falls back for unknown. */\nexport type MarkAttrsFor<T extends string> = T extends keyof MarkAttrRegistry\n\t? MarkAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows a BlockNode to a typed variant with known attributes. */\nexport function isNodeOfType<T extends keyof NodeAttrRegistry>(\n\tnode: BlockNode,\n\ttype: T,\n): node is BlockNode & { readonly type: T; readonly attrs: NodeAttrRegistry[T] } {\n\treturn (node.type as string) === type;\n}\n\n/** Narrows a Mark to a typed variant with known attributes. */\nexport function isMarkOfType<T extends keyof MarkAttrRegistry>(\n\tmark: Mark,\n\ttype: T,\n): mark is Mark & { readonly type: T; readonly attrs: MarkAttrRegistry[T] } {\n\treturn (mark.type as string) === type;\n}\n\n// --- InlineNode Attribute Registry ---\n\n/** Plugins augment this interface to register typed inline node attributes. */\n// biome-ignore lint/suspicious/noEmptyInterface: module augmentation requires interface\nexport interface InlineNodeAttrRegistry {}\n\n/** Resolves typed attributes for known inline node types, falls back for unknown. */\nexport type InlineNodeAttrsFor<T extends string> = T extends keyof InlineNodeAttrRegistry\n\t? InlineNodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows an InlineNode to a typed variant with known attributes. */\nexport function isInlineNodeOfType<T extends keyof InlineNodeAttrRegistry>(\n\tnode: InlineNode,\n\tinlineNodeType: T,\n): node is InlineNode & {\n\treadonly inlineType: T;\n\treadonly attrs: InlineNodeAttrRegistry[T];\n} {\n\treturn isInlineNode(node) && (node.inlineType as string) === inlineNodeType;\n}\n"],"names":["isNodeOfType","node","type","isMarkOfType","mark","isInlineNodeOfType","inlineNodeType","isInlineNode","inlineType"],"mappings":";AAuCO,SAASA,EACfC,IACAC,IAAAA;AAEA,SAAQD,GAAKC,SAAoBA;AAClC;AAGO,SAASC,EACfC,IACAF,IAAAA;AAEA,SAAQE,GAAKF,SAAoBA;AAClC;AAcO,SAASG,EACfJ,IACAK,IAAAA;AAKA,SAAOC,EAAaN,EAAAA,KAAUA,GAAKO,eAA0BF;AAC9D;"}
1
+ {"version":3,"file":"AttrRegistry-Ci0qAvtS.mjs","sources":["../../src/model/AttrRegistry.ts"],"sourcesContent":["/**\n * Module-augmentable attribute registries for type-safe node/mark attributes.\n *\n * Plugins extend these interfaces via `declare module`:\n * @example\n * declare module '../../model/AttrRegistry.js' {\n * interface NodeAttrRegistry {\n * heading: { level: HeadingLevel; align?: BlockAlignment };\n * }\n * }\n */\n\nimport type { BlockAlignment } from '../plugins/alignment/AlignmentPlugin.js';\nimport type { BlockNode, InlineNode, Mark } from './Document.js';\nimport { isInlineNode } from './Document.js';\n\n/** Plugins augment this interface to register typed node attributes. */\nexport interface NodeAttrRegistry {\n\tparagraph: { align?: BlockAlignment };\n}\n\n/** Plugins augment this interface to register typed mark attributes. */\nexport interface MarkAttrRegistry {\n\tbold: Record<string, never>;\n\titalic: Record<string, never>;\n\tunderline: Record<string, never>;\n}\n\n/** Resolves typed attributes for known node types, falls back for unknown. */\nexport type NodeAttrsFor<T extends string> = T extends keyof NodeAttrRegistry\n\t? NodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Resolves typed attributes for known mark types, falls back for unknown. */\nexport type MarkAttrsFor<T extends string> = T extends keyof MarkAttrRegistry\n\t? MarkAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows a BlockNode to a typed variant with known attributes. */\nexport function isNodeOfType<T extends keyof NodeAttrRegistry>(\n\tnode: BlockNode,\n\ttype: T,\n): node is BlockNode & { readonly type: T; readonly attrs: NodeAttrRegistry[T] } {\n\treturn (node.type as string) === type;\n}\n\n/** Narrows a Mark to a typed variant with known attributes. */\nexport function isMarkOfType<T extends keyof MarkAttrRegistry>(\n\tmark: Mark,\n\ttype: T,\n): mark is Mark & { readonly type: T; readonly attrs: MarkAttrRegistry[T] } {\n\treturn (mark.type as string) === type;\n}\n\n// --- InlineNode Attribute Registry ---\n\n/** Plugins augment this interface to register typed inline node attributes. */\n// biome-ignore lint/suspicious/noEmptyInterface: module augmentation requires interface\nexport interface InlineNodeAttrRegistry {}\n\n/** Resolves typed attributes for known inline node types, falls back for unknown. */\nexport type InlineNodeAttrsFor<T extends string> = T extends keyof InlineNodeAttrRegistry\n\t? InlineNodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows an InlineNode to a typed variant with known attributes. */\nexport function isInlineNodeOfType<T extends keyof InlineNodeAttrRegistry>(\n\tnode: InlineNode,\n\tinlineNodeType: T,\n): node is InlineNode & {\n\treadonly inlineType: T;\n\treadonly attrs: InlineNodeAttrRegistry[T];\n} {\n\treturn isInlineNode(node) && (node.inlineType as string) === inlineNodeType;\n}\n"],"names":["isNodeOfType","node","type","isMarkOfType","mark","isInlineNodeOfType","inlineNodeType","isInlineNode","inlineType"],"mappings":";AAuCO,SAASA,EACfC,IACAC,IAAAA;AAEA,SAAQD,GAAKC,SAAoBA;AAClC;AAGO,SAASC,EACfC,IACAF,IAAAA;AAEA,SAAQE,GAAKF,SAAoBA;AAClC;AAcO,SAASG,EACfJ,IACAK,IAAAA;AAKA,SAAOC,EAAaN,EAAAA,KAAUA,GAAKO,eAA0BF;AAC9D;"}
@@ -0,0 +1,58 @@
1
+ import { i as t } from "./AttrRegistry-Ci0qAvtS.mjs";
2
+ import { g as r, h as n } from "./Document-DfZ1NOjs.mjs";
3
+ import { i as o, a as e, b as s, s as c } from "./Selection-BRTab5hO.mjs";
4
+ import { m as a } from "./TypeBrands-C09nE2qO.mjs";
5
+ import { f as i } from "./RangeIterator-Bye0UtbY.mjs";
6
+ function l(t2, n2) {
7
+ const l2 = t2.selection;
8
+ if (o(l2) || e(l2)) return null;
9
+ const u2 = a(n2.type);
10
+ if (s(l2)) {
11
+ const o2 = t2.getBlock(l2.anchor.blockId);
12
+ if (!o2) return null;
13
+ const e2 = [...(t2.storedMarks ?? r(o2, l2.anchor.offset)).filter((t3) => t3.type !== u2), n2];
14
+ return t2.transaction("command").setStoredMarks(e2, t2.storedMarks).setSelection(l2).build();
15
+ }
16
+ const f2 = c(l2, t2.getBlockOrder()), m2 = t2.transaction("command");
17
+ return i(t2, f2, (t3, r2, o2) => {
18
+ m2.removeMark(t3, r2, o2, { type: u2 }), m2.addMark(t3, r2, o2, n2);
19
+ }), m2.setSelection(l2), m2.build();
20
+ }
21
+ function u(t2, a2) {
22
+ const l2 = t2.selection;
23
+ if (o(l2) || e(l2)) return null;
24
+ if (s(l2)) {
25
+ const o2 = t2.getBlock(l2.anchor.blockId);
26
+ if (!o2) return null;
27
+ const e2 = t2.storedMarks ?? r(o2, l2.anchor.offset);
28
+ if (!n(e2, a2)) return null;
29
+ const s2 = e2.filter((t3) => t3.type !== a2);
30
+ return t2.transaction("command").setStoredMarks(s2, t2.storedMarks).setSelection(l2).build();
31
+ }
32
+ const u2 = c(l2, t2.getBlockOrder()), f2 = t2.transaction("command");
33
+ return i(t2, u2, (t3, r2, n2) => {
34
+ f2.removeMark(t3, r2, n2, { type: a2 });
35
+ }), f2.setSelection(l2), f2.build();
36
+ }
37
+ function f(n2, c2, a2) {
38
+ const i2 = n2.selection;
39
+ if (o(i2) || e(i2)) return null;
40
+ if (s(i2) && n2.storedMarks) {
41
+ const r2 = n2.storedMarks.find((t2) => t2.type === c2);
42
+ return r2 && t(r2, c2) ? a2(r2) : null;
43
+ }
44
+ const l2 = n2.getBlock(i2.anchor.blockId);
45
+ if (!l2) return null;
46
+ const u2 = r(l2, i2.anchor.offset).find((t2) => t2.type === c2);
47
+ return u2 && t(u2, c2) ? a2(u2) : null;
48
+ }
49
+ function m(t2, r2) {
50
+ return null !== f(t2, r2, () => true);
51
+ }
52
+ export {
53
+ l as a,
54
+ f as g,
55
+ m as i,
56
+ u as r
57
+ };
58
+ //# sourceMappingURL=AttributedMarkCommands-BETuHXj8.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttributedMarkCommands-BETuHXj8.mjs","sources":["../../src/commands/AttributedMarkCommands.ts"],"sourcesContent":["/**\n * Generic helpers for attributed marks (marks with key/value attrs).\n *\n * Unlike boolean marks (`bold`, `italic`) that toggle on/off, attributed\n * marks (`font`, `fontSize`, `textColor`, `highlight`) carry data and use\n * **replace** semantics: applying a new value removes the old one first.\n *\n * These helpers extract the duplicated collapsed-vs-range branching that\n * was previously copy-pasted across ColorMarkOperations, FontSizeOperations,\n * and FontPlugin.\n */\n\nimport { isMarkOfType } from '../model/AttrRegistry.js';\nimport type { MarkAttrRegistry } from '../model/AttrRegistry.js';\nimport type { Mark } from '../model/Document.js';\nimport { getBlockMarksAtOffset, hasMark } from '../model/Document.js';\nimport { isCollapsed, isGapCursor, isNodeSelection, selectionRange } from '../model/Selection.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { MarkTypeName } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { forEachBlockInRange } from './RangeIterator.js';\n\n/**\n * Applies an attributed mark to the current selection.\n *\n * - **Collapsed cursor**: replaces stored marks (filters out old mark of the\n * same type, appends the new one).\n * - **Range selection**: removes then re-adds the mark in every block span.\n *\n * Returns the built `Transaction`, or `null` when the selection is\n * unsupported (node-selection / gap-cursor) or the anchor block is missing.\n */\nexport function applyAttributedMark(state: EditorState, mark: Mark): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst typeName: MarkTypeName = markType(mark.type as string);\n\n\tif (isCollapsed(sel)) {\n\t\tconst anchorBlock = state.getBlock(sel.anchor.blockId);\n\t\tif (!anchorBlock) return null;\n\t\tconst currentMarks: readonly Mark[] =\n\t\t\tstate.storedMarks ?? getBlockMarksAtOffset(anchorBlock, sel.anchor.offset);\n\t\tconst withoutMark: readonly Mark[] = currentMarks.filter((m) => m.type !== typeName);\n\t\tconst newMarks: readonly Mark[] = [...withoutMark, mark];\n\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setStoredMarks(newMarks, state.storedMarks)\n\t\t\t.setSelection(sel)\n\t\t\t.build();\n\t}\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\n\tforEachBlockInRange(state, range, (blockId, from, to) => {\n\t\tbuilder.removeMark(blockId, from, to, { type: typeName });\n\t\tbuilder.addMark(blockId, from, to, mark);\n\t});\n\n\tbuilder.setSelection(sel);\n\treturn builder.build();\n}\n\n/**\n * Removes an attributed mark from the current selection.\n *\n * - **Collapsed cursor**: filters the mark out of stored marks.\n * Returns `null` when the mark was not present.\n * - **Range selection**: removes the mark across all block spans.\n *\n * Returns the built `Transaction`, or `null` when the selection is\n * unsupported or the mark is absent at a collapsed cursor.\n */\nexport function removeAttributedMark(\n\tstate: EditorState,\n\tmarkTypeName: MarkTypeName,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (isCollapsed(sel)) {\n\t\tconst anchorBlock = state.getBlock(sel.anchor.blockId);\n\t\tif (!anchorBlock) return null;\n\t\tconst currentMarks: readonly Mark[] =\n\t\t\tstate.storedMarks ?? getBlockMarksAtOffset(anchorBlock, sel.anchor.offset);\n\t\tif (!hasMark(currentMarks, markTypeName)) return null;\n\n\t\tconst newMarks: readonly Mark[] = currentMarks.filter((m) => m.type !== markTypeName);\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setStoredMarks(newMarks, state.storedMarks)\n\t\t\t.setSelection(sel)\n\t\t\t.build();\n\t}\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\n\tforEachBlockInRange(state, range, (blockId, from, to) => {\n\t\tbuilder.removeMark(blockId, from, to, { type: markTypeName });\n\t});\n\n\tbuilder.setSelection(sel);\n\treturn builder.build();\n}\n\n/**\n * Extracts a typed attribute value from the mark at the current selection.\n *\n * Looks at stored marks (collapsed w/ stored), block marks at cursor offset\n * (collapsed w/o stored), or anchor-block marks (range).\n *\n * @param extractFn — Narrows the found mark and returns the desired value,\n * or `null` when the mark's attrs don't match expectations.\n */\nexport function getMarkAttrAtSelection<K extends keyof MarkAttrRegistry, V>(\n\tstate: EditorState,\n\tmarkTypeName: K,\n\textractFn: (mark: Mark & { readonly type: K; readonly attrs: MarkAttrRegistry[K] }) => V | null,\n): V | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (isCollapsed(sel) && state.storedMarks) {\n\t\tconst found = state.storedMarks.find((m) => (m.type as string) === markTypeName);\n\t\treturn found && isMarkOfType(found, markTypeName) ? extractFn(found) : null;\n\t}\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return null;\n\tconst marks: readonly Mark[] = getBlockMarksAtOffset(block, sel.anchor.offset);\n\tconst found = marks.find((m) => (m.type as string) === markTypeName);\n\treturn found && isMarkOfType(found, markTypeName) ? extractFn(found) : null;\n}\n\n/**\n * Returns `true` when the given attributed mark type is present at the\n * current selection (collapsed or range).\n */\nexport function isAttributedMarkActive<K extends keyof MarkAttrRegistry>(\n\tstate: EditorState,\n\tmarkTypeName: K,\n): boolean {\n\treturn getMarkAttrAtSelection(state, markTypeName, () => true as const) !== null;\n}\n"],"names":["applyAttributedMark","state","mark","sel","selection","isNodeSelection","isGapCursor","typeName","markType","type","isCollapsed","anchorBlock","getBlock","anchor","blockId","newMarks","storedMarks","getBlockMarksAtOffset","offset","filter","m","transaction","setStoredMarks","setSelection","build","range","selectionRange","getBlockOrder","builder","forEachBlockInRange","from","to","removeMark","addMark","removeAttributedMark","markTypeName","currentMarks","hasMark","getMarkAttrAtSelection","extractFn","found","find","isMarkOfType","block","isAttributedMarkActive"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { b as t, l as e } from "./Document-CuwE73FF.mjs";
2
- import { i as n, c as o } from "./NavigationUtils-CyW0Vo8A.mjs";
3
- import { f as r, c as i, e as l, a as f, i as c, g as s, b as u } from "./Selection-DmFDwiPJ.mjs";
4
- import { f as a } from "./NodeResolver-B-wzthAQ.mjs";
1
+ import { a as t, e } from "./Document-DfZ1NOjs.mjs";
2
+ import { i as n, c as o } from "./NavigationUtils-Cspt2ZrK.mjs";
3
+ import { c as r, d as i, e as f, a as l, i as c, f as s, b as u } from "./Selection-BRTab5hO.mjs";
4
+ import { f as a } from "./NodeResolver-CzLmmrph.mjs";
5
5
  import { g as d } from "./Platform-S_xqIqcM.mjs";
6
6
  import { b as g } from "./TypeBrands-C09nE2qO.mjs";
7
7
  function h(t2, e2, n2) {
@@ -9,7 +9,7 @@ function h(t2, e2, n2) {
9
9
  }
10
10
  function b(t2, e2) {
11
11
  const n2 = a(t2.doc, e2) ?? [];
12
- return t2.transaction("input").setSelection(l(e2, n2)).setStoredMarks(null, t2.storedMarks).build();
12
+ return t2.transaction("input").setSelection(f(e2, n2)).setStoredMarks(null, t2.storedMarks).build();
13
13
  }
14
14
  function m(t2, e2, n2, o2, i2) {
15
15
  return t2.transaction("input").setSelection(r({ blockId: e2, offset: n2 }, { blockId: o2, offset: i2 })).setStoredMarks(null, t2.storedMarks).build();
@@ -37,7 +37,7 @@ function k(t2) {
37
37
  function p(t2, e2) {
38
38
  const n2 = k(t2);
39
39
  if (!n2) return;
40
- if (f(e2)) return void n2.removeAllRanges();
40
+ if (l(e2)) return void n2.removeAllRanges();
41
41
  if (c(e2)) {
42
42
  const o3 = t2.querySelector(`[data-block-id="${e2.nodeId}"]`);
43
43
  if (!o3) return;
@@ -62,8 +62,8 @@ function T(t2) {
62
62
  const n2 = e2.anchorNode, o2 = e2.focusNode;
63
63
  if (!n2 || !o2) return null;
64
64
  if (!t2.contains(n2) || !t2.contains(o2)) return null;
65
- const i2 = I(t2, n2, e2.anchorOffset), l2 = I(t2, o2, e2.focusOffset);
66
- return i2 && l2 ? r(i2, l2) : null;
65
+ const i2 = I(t2, n2, e2.anchorOffset), f2 = I(t2, o2, e2.focusOffset);
66
+ return i2 && f2 ? r(i2, f2) : null;
67
67
  }
68
68
  function E(t2, e2) {
69
69
  let n2 = null;
@@ -81,23 +81,23 @@ function E(t2, e2) {
81
81
  if (1 === o2.childNodes.length && "BR" === o2.firstChild?.nodeName) return { node: o2, offset: 0 };
82
82
  let r2 = e2.offset;
83
83
  const i2 = y(o2);
84
- let l2 = i2.nextNode();
85
- for (; l2; ) {
86
- if (l2.nodeType === Node.TEXT_NODE) {
87
- const t3 = l2.textContent?.length ?? 0;
88
- if (r2 <= t3) return { node: l2, offset: r2 };
84
+ let f2 = i2.nextNode();
85
+ for (; f2; ) {
86
+ if (f2.nodeType === Node.TEXT_NODE) {
87
+ const t3 = f2.textContent?.length ?? 0;
88
+ if (r2 <= t3) return { node: f2, offset: r2 };
89
89
  r2 -= t3;
90
- } else if (l2 instanceof HTMLElement && "false" === l2.getAttribute("contenteditable")) {
90
+ } else if (f2 instanceof HTMLElement && "false" === f2.getAttribute("contenteditable")) {
91
91
  if (0 === r2) {
92
- const t3 = l2.parentNode;
93
- if (t3) return { node: t3, offset: A(t3, l2) };
92
+ const t3 = f2.parentNode;
93
+ if (t3) return { node: t3, offset: A(t3, f2) };
94
94
  }
95
95
  r2 -= 1;
96
96
  }
97
- l2 = i2.nextNode();
97
+ f2 = i2.nextNode();
98
98
  }
99
- const f2 = o2.lastChild;
100
- return f2 ? f2.nodeType === Node.TEXT_NODE ? { node: f2, offset: f2.textContent?.length ?? 0 } : { node: o2, offset: o2.childNodes.length } : { node: o2, offset: 0 };
99
+ const l2 = o2.lastChild;
100
+ return l2 ? l2.nodeType === Node.TEXT_NODE ? { node: l2, offset: l2.textContent?.length ?? 0 } : { node: o2, offset: o2.childNodes.length } : { node: o2, offset: 0 };
101
101
  }
102
102
  function I(t2, e2, n2) {
103
103
  const o2 = (function(t3, e3) {
@@ -111,27 +111,27 @@ function I(t2, e2, n2) {
111
111
  if (!o2) return null;
112
112
  const r2 = o2.getAttribute("data-block-id");
113
113
  if (!r2) return null;
114
- const i2 = g(r2), l2 = (function(t3, e3) {
114
+ const i2 = g(r2), f2 = (function(t3, e3) {
115
115
  const n3 = [];
116
116
  let o3 = e3;
117
117
  for (; o3 && o3 !== t3; ) o3.hasAttribute("data-block-id") && n3.unshift(g(o3.getAttribute("data-block-id") ?? "")), o3 = o3.parentElement;
118
118
  return n3;
119
- })(t2, o2), f2 = M(o2);
120
- if (e2 === o2 || e2 === f2) {
121
- const t3 = e2 === f2 ? f2 : o2;
119
+ })(t2, o2), l2 = M(o2);
120
+ if (e2 === o2 || e2 === l2) {
121
+ const t3 = e2 === l2 ? l2 : o2;
122
122
  let r3 = 0, c3 = 0;
123
123
  for (const e3 of Array.from(t3.childNodes)) {
124
124
  if (c3 >= n2) break;
125
125
  r3 += O(e3), c3++;
126
126
  }
127
- return s(i2, r3, l2.length > 1 ? l2 : void 0);
127
+ return s(i2, r3, f2.length > 1 ? f2 : void 0);
128
128
  }
129
- if (f2 !== o2 && !f2.contains(e2)) return s(i2, 0, l2.length > 1 ? l2 : void 0);
129
+ if (l2 !== o2 && !l2.contains(e2)) return s(i2, 0, f2.length > 1 ? f2 : void 0);
130
130
  let c2 = 0;
131
- const u2 = y(f2);
131
+ const u2 = y(l2);
132
132
  let a2 = u2.nextNode();
133
133
  for (; a2; ) {
134
- if (a2 === e2) return a2.nodeType === Node.TEXT_NODE ? s(i2, c2 + n2, l2.length > 1 ? l2 : void 0) : s(i2, c2, l2.length > 1 ? l2 : void 0);
134
+ if (a2 === e2) return a2.nodeType === Node.TEXT_NODE ? s(i2, c2 + n2, f2.length > 1 ? f2 : void 0) : s(i2, c2, f2.length > 1 ? f2 : void 0);
135
135
  a2.nodeType === Node.TEXT_NODE ? c2 += a2.textContent?.length ?? 0 : a2 instanceof HTMLElement && "false" === a2.getAttribute("contenteditable") && (c2 += 1), a2 = u2.nextNode();
136
136
  }
137
137
  if (e2.nodeType === Node.ELEMENT_NODE) {
@@ -140,9 +140,9 @@ function I(t2, e2, n2) {
140
140
  if (o3 >= n2) break;
141
141
  t3 += O(r3), o3++;
142
142
  }
143
- return s(i2, t3, l2.length > 1 ? l2 : void 0);
143
+ return s(i2, t3, f2.length > 1 ? f2 : void 0);
144
144
  }
145
- return s(i2, 0, l2.length > 1 ? l2 : void 0);
145
+ return s(i2, 0, f2.length > 1 ? f2 : void 0);
146
146
  }
147
147
  function y(t2) {
148
148
  return document.createTreeWalker(t2, NodeFilter.SHOW_ALL, { acceptNode: (e2) => e2 instanceof HTMLElement && e2.hasAttribute("data-cursor-wrapper") || (function(t3, e3) {
@@ -174,26 +174,26 @@ function O(t2) {
174
174
  }
175
175
  function S(e2, n2, o2, r2) {
176
176
  const i2 = n2.selection;
177
- if (c(i2) || f(i2)) return false;
177
+ if (c(i2) || l(i2)) return false;
178
178
  if (!u(i2)) return false;
179
- const l2 = n2.getBlock(i2.anchor.blockId);
180
- if (!l2) return false;
181
- const s2 = i2.anchor.offset, a2 = t(l2), g2 = e2.querySelector(`[data-block-id="${i2.anchor.blockId}"]`), h2 = g2 instanceof HTMLElement && "rtl" === d(g2);
179
+ const f2 = n2.getBlock(i2.anchor.blockId);
180
+ if (!f2) return false;
181
+ const s2 = i2.anchor.offset, a2 = t(f2), g2 = e2.querySelector(`[data-block-id="${i2.anchor.blockId}"]`), h2 = g2 instanceof HTMLElement && "rtl" === d(g2);
182
182
  if ("left" === o2) return h2 ? s2 === a2 : 0 === s2;
183
183
  if ("right" === o2) return h2 ? 0 === s2 : s2 === a2;
184
184
  if ("up" === o2 && 0 === s2) return true;
185
185
  if ("down" === o2 && s2 === a2) return true;
186
186
  const b2 = k(e2);
187
187
  return b2 && b2.modify ? (function(t2, e3, n3, o3) {
188
- const r3 = e3.anchorNode, i3 = e3.anchorOffset, l3 = e3.focusNode, f2 = e3.focusOffset;
189
- if (!r3 || !l3) return false;
188
+ const r3 = e3.anchorNode, i3 = e3.anchorOffset, f3 = e3.focusNode, l2 = e3.focusOffset;
189
+ if (!r3 || !f3) return false;
190
190
  const c2 = o3 ?? B(e3), s3 = "up" === n3 ? "backward" : "forward";
191
191
  try {
192
192
  e3.modify("move", s3, "line");
193
193
  const n4 = e3.focusNode;
194
194
  if (!n4) return false;
195
- if (F(t2, l3) !== F(t2, n4)) return true;
196
- if (e3.anchorNode === r3 && e3.anchorOffset === i3 && e3.focusNode === l3 && e3.focusOffset === f2) return true;
195
+ if (F(t2, f3) !== F(t2, n4)) return true;
196
+ if (e3.anchorNode === r3 && e3.anchorOffset === i3 && e3.focusNode === f3 && e3.focusOffset === l2) return true;
197
197
  if (c2) {
198
198
  const t3 = B(e3);
199
199
  if (t3 && Math.abs(t3.top - c2.top) < 1) return true;
@@ -201,7 +201,7 @@ function S(e2, n2, o2, r2) {
201
201
  return false;
202
202
  } finally {
203
203
  try {
204
- e3.setBaseAndExtent(r3, i3, l3, f2);
204
+ e3.setBaseAndExtent(r3, i3, f3, l2);
205
205
  } catch {
206
206
  }
207
207
  }
@@ -220,14 +220,14 @@ function C(e2, n2, o2, r2) {
220
220
  const i2 = w(n2, o2);
221
221
  if (!i2) return null;
222
222
  if (i2.isVoid) return b(n2, i2.targetId);
223
- const l2 = n2.getBlock(i2.targetId);
224
- if (!l2) return null;
225
- const f2 = t(l2);
223
+ const f2 = n2.getBlock(i2.targetId);
224
+ if (!f2) return null;
225
+ const l2 = t(f2);
226
226
  if (null !== r2) {
227
227
  const t2 = (function(t3, e3, n3, o3) {
228
228
  const r3 = t3.querySelector(`[data-block-id="${e3}"]`);
229
229
  if (!r3) return null;
230
- const i3 = r3.getBoundingClientRect(), l3 = "down" === n3 ? i3.top + 2 : i3.bottom - 2, f3 = Math.min(Math.max(o3, i3.left), i3.right - 1), c3 = N(t3.getRootNode(), f3, l3, t3.ownerDocument);
230
+ const i3 = r3.getBoundingClientRect(), f3 = "down" === n3 ? i3.top + 2 : i3.bottom - 2, l3 = Math.min(Math.max(o3, i3.left), i3.right - 1), c3 = N(t3.getRootNode(), l3, f3, t3.ownerDocument);
231
231
  if (!c3) return null;
232
232
  if (!r3.contains(c3.node)) return null;
233
233
  const s2 = I(t3, c3.node, c3.offset);
@@ -235,33 +235,33 @@ function C(e2, n2, o2, r2) {
235
235
  })(e2, i2.targetId, o2, r2);
236
236
  if (t2) return h(n2, t2.blockId, t2.offset);
237
237
  }
238
- const c2 = "down" === o2 ? 0 : f2;
238
+ const c2 = "down" === o2 ? 0 : l2;
239
239
  return h(n2, i2.targetId, c2);
240
240
  }
241
241
  function L(t2, n2) {
242
242
  if ("up" === n2 || "down" === n2) return null;
243
243
  const o2 = t2.selection;
244
- if (c(o2) || f(o2)) return null;
244
+ if (c(o2) || l(o2)) return null;
245
245
  if (!u(o2)) return null;
246
246
  const r2 = o2.anchor.blockId, i2 = t2.getBlock(r2);
247
247
  if (!i2) return null;
248
- const l2 = o2.anchor.offset;
248
+ const f2 = o2.anchor.offset;
249
249
  return "right" === n2 ? (function(t3, n3, o3, r3) {
250
250
  const i3 = e(o3, r3);
251
251
  return i3 && "inline" === i3.kind ? h(t3, n3, r3 + 1) : null;
252
- })(t2, r2, i2, l2) : (function(t3, n3, o3, r3) {
252
+ })(t2, r2, i2, f2) : (function(t3, n3, o3, r3) {
253
253
  if (0 === r3) return null;
254
254
  const i3 = e(o3, r3 - 1);
255
255
  return i3 && "inline" === i3.kind ? h(t3, n3, r3 - 1) : null;
256
- })(t2, r2, i2, l2);
256
+ })(t2, r2, i2, f2);
257
257
  }
258
258
  function x(e2, o2) {
259
259
  const r2 = e2.selection;
260
- if (!f(r2)) return null;
261
- const i2 = e2.getBlockOrder(), l2 = i2.indexOf(r2.blockId);
262
- if (l2 < 0) return null;
260
+ if (!l(r2)) return null;
261
+ const i2 = e2.getBlockOrder(), f2 = i2.indexOf(r2.blockId);
262
+ if (f2 < 0) return null;
263
263
  if ("before" === r2.side && ("right" === o2 || "down" === o2) || "after" === r2.side && ("left" === o2 || "up" === o2)) return b(e2, r2.blockId);
264
- const c2 = "before" === r2.side ? l2 - 1 : l2 + 1;
264
+ const c2 = "before" === r2.side ? f2 - 1 : f2 + 1;
265
265
  if (c2 < 0 || c2 >= i2.length) return null;
266
266
  const s2 = i2[c2];
267
267
  if (!s2) return null;
@@ -276,10 +276,10 @@ function B(t2) {
276
276
  }
277
277
  function w(t2, e2) {
278
278
  const r2 = t2.getBlockOrder(), i2 = t2.selection;
279
- if (c(i2) || f(i2)) return null;
280
- const l2 = r2.indexOf(i2.anchor.blockId);
281
- if (l2 < 0) return null;
282
- const s2 = "left" === e2 || "up" === e2 ? l2 - 1 : l2 + 1;
279
+ if (c(i2) || l(i2)) return null;
280
+ const f2 = r2.indexOf(i2.anchor.blockId);
281
+ if (f2 < 0) return null;
282
+ const s2 = "left" === e2 || "up" === e2 ? f2 - 1 : f2 + 1;
283
283
  if (s2 < 0 || s2 >= r2.length) return null;
284
284
  const u2 = r2[s2];
285
285
  return u2 && o(t2, i2.anchor.blockId, u2) ? { targetId: u2, isVoid: n(t2, u2) } : null;
@@ -308,4 +308,4 @@ export {
308
308
  T as r,
309
309
  p as s
310
310
  };
311
- //# sourceMappingURL=CaretNavigation-CAJXoaae.mjs.map
311
+ //# sourceMappingURL=CaretNavigation-qLfeL0So.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"CaretNavigation-CAJXoaae.mjs","sources":["../../src/state/SelectionTransactions.ts","../../src/view/DomPointUtils.ts","../../src/view/SelectionSync.ts","../../src/view/CaretNavigation.ts"],"sourcesContent":["/**\n * Shared transaction builders for cursor and selection movement.\n *\n * These helpers encapsulate the common pattern of building a transaction\n * that sets a new selection and clears storedMarks.\n */\n\nimport { findNodePath } from '../model/NodeResolver.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tcreateSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from './EditorState.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Builds a collapsed-cursor transaction that clears storedMarks. */\nexport function moveTx(state: EditorState, blockId: BlockId, offset: number): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createCollapsedSelection(blockId, offset))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a NodeSelection transaction that clears storedMarks. */\nexport function nodeSelTx(state: EditorState, targetId: BlockId): Transaction {\n\tconst path: BlockId[] = (findNodePath(state.doc, targetId) ?? []) as BlockId[];\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createNodeSelection(targetId, path))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a range-selection transaction that clears storedMarks. */\nexport function extendTx(\n\tstate: EditorState,\n\tanchorBlockId: BlockId,\n\tanchorOffset: number,\n\theadBlockId: BlockId,\n\theadOffset: number,\n): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(\n\t\t\tcreateSelection(\n\t\t\t\t{ blockId: anchorBlockId, offset: anchorOffset },\n\t\t\t\t{ blockId: headBlockId, offset: headOffset },\n\t\t\t),\n\t\t)\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n","/**\n * Utilities for resolving a DOM position from screen coordinates.\n *\n * Provides a fallback chain: `caretPositionFromPoint` → `caretRangeFromPoint`\n * → document fallback (when root is a ShadowRoot).\n */\n\n/** Result of resolving a DOM position from screen coordinates. */\nexport interface DomPoint {\n\treadonly node: Node;\n\treadonly offset: number;\n}\n\n/**\n * Resolves a DOM position from screen coordinates using the best available API.\n *\n * Tries `caretPositionFromPoint` first (standard), then `caretRangeFromPoint`\n * (WebKit fallback), and finally tries the ownerDocument when the root is\n * a ShadowRoot and the first two returned nothing.\n */\nexport function domPositionFromPoint(\n\troot: Document | ShadowRoot,\n\tx: number,\n\ty: number,\n\townerDoc?: Document,\n): DomPoint | null {\n\tlet domNode: Node | null = null;\n\tlet domOffset = 0;\n\n\t// Standard API\n\tif ('caretPositionFromPoint' in root) {\n\t\tconst cp = (root as Document).caretPositionFromPoint(x, y);\n\t\tif (cp) {\n\t\t\tdomNode = cp.offsetNode;\n\t\t\tdomOffset = cp.offset;\n\t\t}\n\t}\n\n\t// Fallback\n\tif (!domNode && 'caretRangeFromPoint' in root) {\n\t\tconst range = (root as Document).caretRangeFromPoint(x, y);\n\t\tif (range) {\n\t\t\tdomNode = range.startContainer;\n\t\t\tdomOffset = range.startOffset;\n\t\t}\n\t}\n\n\t// Also try on the document when the root is a ShadowRoot and returned nothing\n\tif (!domNode && ownerDoc && root !== ownerDoc) {\n\t\tif ('caretRangeFromPoint' in ownerDoc) {\n\t\t\tconst range = ownerDoc.caretRangeFromPoint(x, y);\n\t\t\tif (range) {\n\t\t\t\tdomNode = range.startContainer;\n\t\t\t\tdomOffset = range.startOffset;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!domNode) return null;\n\treturn { node: domNode, offset: domOffset };\n}\n","/**\n * Selection synchronization between editor state and DOM.\n */\n\nimport type { EditorSelection, Position, Selection } from '../model/Selection.js';\nimport {\n\tcreatePosition,\n\tcreateSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\n\ninterface DOMPosition {\n\tnode: Node;\n\toffset: number;\n}\n\n/** Gets the selection object, preferring shadow root's selection for Shadow DOM. */\nexport function getSelection(container: HTMLElement): globalThis.Selection | null {\n\tconst root = container.getRootNode();\n\tif (root instanceof ShadowRoot && 'getSelection' in root) {\n\t\treturn (root as ShadowRoot & { getSelection(): globalThis.Selection | null }).getSelection();\n\t}\n\treturn window.getSelection();\n}\n\n/** Syncs the editor state selection to the DOM. */\nexport function syncSelectionToDOM(container: HTMLElement, selection: EditorSelection): void {\n\tconst domSel = getSelection(container);\n\tif (!domSel) return;\n\n\t// GapCursor: clear browser selection (gap cursor has no DOM equivalent)\n\tif (isGapCursor(selection)) {\n\t\tdomSel.removeAllRanges();\n\t\treturn;\n\t}\n\n\t// NodeSelection: select the entire block element\n\tif (isNodeSelection(selection)) {\n\t\tconst blockEl = container.querySelector(`[data-block-id=\"${selection.nodeId}\"]`);\n\t\tif (!blockEl) return;\n\t\tconst parent = blockEl.parentNode;\n\t\tif (!parent) return;\n\t\tconst childIdx = childIndexOf(parent, blockEl);\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(parent, childIdx, parent, childIdx + 1);\n\t\t} catch {\n\t\t\t// Selection may fail if DOM is not yet rendered\n\t\t}\n\t\treturn;\n\t}\n\n\tconst anchorPos = statePositionToDOM(container, selection.anchor);\n\tconst headPos = statePositionToDOM(container, selection.head);\n\n\tif (!anchorPos || !headPos) return;\n\n\ttry {\n\t\tdomSel.setBaseAndExtent(anchorPos.node, anchorPos.offset, headPos.node, headPos.offset);\n\t} catch {\n\t\t// Selection may fail if DOM is not yet rendered\n\t}\n}\n\n/** Reads the current DOM selection and converts it to a state selection. */\nexport function readSelectionFromDOM(container: HTMLElement): Selection | null {\n\tconst domSel = getSelection(container);\n\tif (!domSel || domSel.rangeCount === 0) return null;\n\n\tconst anchorNode = domSel.anchorNode;\n\tconst focusNode = domSel.focusNode;\n\n\tif (!anchorNode || !focusNode) return null;\n\tif (!container.contains(anchorNode) || !container.contains(focusNode)) return null;\n\n\tconst anchor = domPositionToState(container, anchorNode, domSel.anchorOffset);\n\tconst head = domPositionToState(container, focusNode, domSel.focusOffset);\n\n\tif (!anchor || !head) return null;\n\n\treturn createSelection(anchor, head);\n}\n\n/** Converts a state position (blockId + offset) to a DOM position (node + offset). */\nfunction statePositionToDOM(container: HTMLElement, pos: Position): DOMPosition | null {\n\t// If path is available, navigate directly to the leaf block\n\tlet blockEl: Element | null = null;\n\tif (pos.path && pos.path.length > 0) {\n\t\tlet current: Element = container;\n\t\tfor (const id of pos.path) {\n\t\t\tconst found = current.querySelector(`:scope [data-block-id=\"${id}\"]`);\n\t\t\tif (!found) break;\n\t\t\tcurrent = found;\n\t\t}\n\t\tif (current !== container && current.getAttribute('data-block-id') === pos.blockId) {\n\t\t\tblockEl = current;\n\t\t}\n\t}\n\tblockEl ??= container.querySelector(`[data-block-id=\"${pos.blockId}\"]`);\n\tif (!blockEl) return null;\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle empty paragraphs with <br>\n\tif (contentEl.childNodes.length === 1 && contentEl.firstChild?.nodeName === 'BR') {\n\t\treturn { node: contentEl, offset: 0 };\n\t}\n\n\t// Walk through text nodes and inline elements to find the correct position\n\tlet remaining = pos.offset;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet current = walker.nextNode();\n\twhile (current) {\n\t\tif (current.nodeType === Node.TEXT_NODE) {\n\t\t\tconst len = current.textContent?.length ?? 0;\n\t\t\tif (remaining <= len) {\n\t\t\t\treturn { node: current, offset: remaining };\n\t\t\t}\n\t\t\tremaining -= len;\n\t\t} else if (\n\t\t\tcurrent instanceof HTMLElement &&\n\t\t\tcurrent.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\t// InlineNode element — width 1 in state offset space\n\t\t\tif (remaining === 0) {\n\t\t\t\t// Position before this inline element\n\t\t\t\tconst parent = current.parentNode;\n\t\t\t\tif (parent) {\n\t\t\t\t\tconst childIndex = childIndexOf(parent, current);\n\t\t\t\t\treturn { node: parent, offset: childIndex };\n\t\t\t\t}\n\t\t\t}\n\t\t\tremaining -= 1;\n\t\t}\n\t\tcurrent = walker.nextNode();\n\t}\n\n\t// Fallback: position at end of block\n\tconst lastChild = contentEl.lastChild;\n\tif (lastChild) {\n\t\tif (lastChild.nodeType === Node.TEXT_NODE) {\n\t\t\treturn { node: lastChild, offset: lastChild.textContent?.length ?? 0 };\n\t\t}\n\t\treturn { node: contentEl, offset: contentEl.childNodes.length };\n\t}\n\n\treturn { node: contentEl, offset: 0 };\n}\n\n/** Converts a DOM position to a state position, including nested path. */\nexport function domPositionToState(\n\tcontainer: HTMLElement,\n\tnode: Node,\n\tdomOffset: number,\n): Position | null {\n\t// Find the innermost block element\n\tconst blockEl = findBlockElement(container, node);\n\tif (!blockEl) return null;\n\n\tconst rawBlockId = blockEl.getAttribute('data-block-id');\n\tif (!rawBlockId) return null;\n\tconst bid = toBlockId(rawBlockId);\n\n\t// Build path by collecting all data-block-id ancestors from leaf to root\n\tconst path = buildBlockPath(container, blockEl);\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle clicks on the block or content element itself (e.g. empty paragraph with <br>)\n\tif (node === blockEl || node === contentEl) {\n\t\tconst targetEl: Element = node === contentEl ? contentEl : blockEl;\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\t\tfor (const child of Array.from(targetEl.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\t// If node is outside the contentDOM (e.g. in NodeView header), map to offset 0\n\tif (contentEl !== blockEl && !contentEl.contains(node)) {\n\t\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n\t}\n\n\t// Calculate offset by walking text nodes and inline elements\n\tlet offset = 0;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet walkerNode = walker.nextNode();\n\twhile (walkerNode) {\n\t\tif (walkerNode === node) {\n\t\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\treturn createPosition(bid, offset + domOffset, path.length > 1 ? path : undefined);\n\t\t\t}\n\t\t\t// Node is an inline element — return offset at its start\n\t\t\treturn createPosition(bid, offset, path.length > 1 ? path : undefined);\n\t\t}\n\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\toffset += walkerNode.textContent?.length ?? 0;\n\t\t} else if (\n\t\t\twalkerNode instanceof HTMLElement &&\n\t\t\twalkerNode.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\toffset += 1;\n\t\t}\n\t\twalkerNode = walker.nextNode();\n\t}\n\n\t// If the node is not a text node, try to find the offset from element context\n\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\n\t\tfor (const child of Array.from(node.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n}\n\n/** Builds an array of block IDs from root to leaf by walking up from a block element. */\nfunction buildBlockPath(container: HTMLElement, leafBlockEl: HTMLElement): BlockId[] {\n\tconst path: BlockId[] = [];\n\tlet current: HTMLElement | null = leafBlockEl;\n\n\twhile (current && current !== container) {\n\t\tif (current.hasAttribute('data-block-id')) {\n\t\t\tpath.unshift(toBlockId(current.getAttribute('data-block-id') ?? ''));\n\t\t}\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn path;\n}\n\n/** Finds the closest block element ancestor. */\nfunction findBlockElement(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n\n/** Checks if a node is inside a contentEditable=\"false\" inline element. */\nfunction isInsideInlineElement(node: Node, root: Element): boolean {\n\tlet parent: Node | null = node.parentNode;\n\twhile (parent && parent !== root) {\n\t\tif (parent instanceof HTMLElement && parent.getAttribute('contenteditable') === 'false') {\n\t\t\treturn true;\n\t\t}\n\t\tparent = parent.parentNode;\n\t}\n\treturn false;\n}\n\n/**\n * Creates a TreeWalker that visits text nodes and contentEditable=\"false\"\n * inline elements within a block, skipping mark wrappers and nested blocks.\n */\nfunction createInlineContentWalker(blockEl: Element): TreeWalker {\n\treturn document.createTreeWalker(blockEl, NodeFilter.SHOW_ALL, {\n\t\tacceptNode: (n: Node) => {\n\t\t\t// Skip cursor wrapper (ZWS for stored marks during IME composition)\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-cursor-wrapper')) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Skip anything inside an inline element (contentEditable=\"false\")\n\t\t\tif (isInsideInlineElement(n, blockEl)) return NodeFilter.FILTER_REJECT;\n\t\t\t// Skip nested block elements and their descendants\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-block-id') && n !== blockEl) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Accept text nodes\n\t\t\tif (n.nodeType === Node.TEXT_NODE) return NodeFilter.FILTER_ACCEPT;\n\t\t\t// Accept inline elements (contentEditable=\"false\")\n\t\t\tif (n instanceof HTMLElement && n.getAttribute('contenteditable') === 'false') {\n\t\t\t\treturn NodeFilter.FILTER_ACCEPT;\n\t\t\t}\n\t\t\t// Skip other elements (mark wrappers, decoration wrappers) — descend\n\t\t\treturn NodeFilter.FILTER_SKIP;\n\t\t},\n\t});\n}\n\n/** Returns the child index of a node within its parent. */\nfunction childIndexOf(parent: Node, child: Node): number {\n\tlet idx = 0;\n\tfor (const c of Array.from(parent.childNodes)) {\n\t\tif (c === child) return idx;\n\t\tidx++;\n\t}\n\treturn idx;\n}\n\n/**\n * Resolves the content root for inline content walking.\n * If the block element has a contentDOM (marked with data-content-dom),\n * returns it so the walker skips NodeView structural elements (headers, etc.).\n */\nfunction resolveContentRoot(blockEl: Element): Element {\n\tconst contentDOM: Element | null = blockEl.querySelector('[data-content-dom]');\n\treturn contentDOM ?? blockEl;\n}\n\n/** Counts the inline content width of a DOM node (text length + 1 per inline element). */\nfunction inlineContentWidth(node: Node): number {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent?.length ?? 0;\n\t}\n\tif (node instanceof HTMLElement && node.getAttribute('contenteditable') === 'false') {\n\t\treturn 1;\n\t}\n\t// Mark wrapper or other element — sum children\n\tlet width = 0;\n\tfor (const child of Array.from(node.childNodes)) {\n\t\twidth += inlineContentWidth(child);\n\t}\n\treturn width;\n}\n","/**\n * Caret navigation utilities for layout-aware cursor movement.\n *\n * Provides `endOfTextblock()` for detecting visual block boundaries,\n * `navigateAcrossBlocks()` for cross-block cursor transitions, and\n * `navigateVerticalWithGoalColumn()` for column-preserving vertical nav.\n */\n\nimport { type BlockNode, 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 { moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { domPositionFromPoint } from './DomPointUtils.js';\n\n/** Inset in pixels from block edge for vertical goal-column probing. */\nconst BLOCK_EDGE_INSET_PX = 2;\n\n/** Minimum vertical movement (pixels) for a Selection.modify probe to be considered a real move. */\nconst VERTICAL_MOVEMENT_THRESHOLD_PX = 1;\nimport { getTextDirection } from './Platform.js';\nimport { domPositionToState, getSelection } from './SelectionSync.js';\n\nexport type CaretDirection = 'left' | 'right' | 'up' | 'down';\n\n/** Result of resolving the adjacent block for vertical navigation. */\ninterface NavigationTarget {\n\treadonly targetId: BlockId;\n\treadonly isVoid: boolean;\n}\n\n/**\n * Checks whether the cursor is at the visual edge of its text block.\n *\n * For horizontal directions (left/right), uses a pure offset check.\n * For vertical directions (up/down), tries `Selection.modify` probing\n * to detect visual line boundaries, falling back to offset heuristic.\n *\n * An optional `caretRect` parameter avoids redundant `getBoundingClientRect`\n * calls when the caller already measured the caret position.\n */\nexport function endOfTextblock(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: CaretDirection,\n\tcaretRect?: DOMRect | null,\n): boolean {\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLength: number = getBlockLength(block);\n\n\t// Horizontal: direction-aware offset check\n\tconst blockEl: Element | null = container.querySelector(\n\t\t`[data-block-id=\"${sel.anchor.blockId}\"]`,\n\t);\n\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\n\tif (direction === 'left') return isRtl ? offset === blockLength : offset === 0;\n\tif (direction === 'right') return isRtl ? offset === 0 : offset === blockLength;\n\n\t// Vertical: quick offset check first\n\tif (direction === 'up' && offset === 0) return true;\n\tif (direction === 'down' && offset === blockLength) return true;\n\n\t// Vertical: try Selection.modify probing for multi-line blocks\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel || !domSel.modify) {\n\t\t// Fallback: offset heuristic (no Selection.modify in test environments)\n\t\treturn direction === 'up' ? offset === 0 : offset === blockLength;\n\t}\n\n\treturn probeVerticalBoundary(container, domSel, direction, caretRect);\n}\n\n/**\n * Creates a transaction to move the cursor to an adjacent block.\n *\n * Returns `null` if navigation is not possible (document boundary,\n * isolating boundary, or no adjacent block).\n */\nexport function navigateAcrossBlocks(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\t// Target is text → collapsed selection at start or end\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'right' || direction === 'down' ? 0 : getBlockLength(targetBlock);\n\n\treturn moveTx(state, target.targetId, targetOffset);\n}\n\n/**\n * Navigates vertically using a goal column for visual column preservation.\n *\n * Uses `caretPositionFromPoint` / `caretRangeFromPoint` to find the offset\n * in the target block that is closest to the given goalColumn X coordinate.\n * Falls back to start/end offset when goal column positioning is unavailable.\n */\nexport function navigateVerticalWithGoalColumn(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number | null,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\t// Void → NodeSelection (goalColumn irrelevant)\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\tconst blockLen: number = getBlockLength(targetBlock);\n\n\t// Try to resolve position using goalColumn\n\tif (goalColumn !== null) {\n\t\tconst position = resolveGoalColumnPosition(container, target.targetId, direction, goalColumn);\n\t\tif (position) return moveTx(state, position.blockId, position.offset);\n\t}\n\n\t// Fallback: start (down) or end (up)\n\tconst fallbackOffset: number = direction === 'down' ? 0 : blockLen;\n\treturn moveTx(state, target.targetId, fallbackOffset);\n}\n\n/**\n * Skips over an InlineNode atomically when arrowing left/right.\n *\n * Returns a transaction moving the cursor past the InlineNode,\n * or `null` if the adjacent content is not an InlineNode (or\n * the direction is vertical).\n */\nexport function skipInlineNode(state: EditorState, direction: CaretDirection): Transaction | null {\n\tif (direction === 'up' || direction === 'down') return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\tif (!isCollapsed(sel)) return null;\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\treturn direction === 'right'\n\t\t? skipInlineNodeRight(state, blockId, block, offset)\n\t\t: skipInlineNodeLeft(state, blockId, block, offset);\n}\n\nfunction skipInlineNodeRight(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tconst content = getContentAtOffset(block, offset);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset + 1);\n}\n\nfunction skipInlineNodeLeft(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tif (offset === 0) return null;\n\tconst content = getContentAtOffset(block, offset - 1);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset - 1);\n}\n\n/**\n * Navigates from a GapCursor position.\n *\n * - Arrow toward the adjacent void block → NodeSelection of that block.\n * - Arrow away from the void block → find next block: void → NodeSelection,\n * text → TextSelection, document boundary → no-op.\n */\nexport function navigateFromGapCursor(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (!isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst blockIdx: number = blockOrder.indexOf(sel.blockId);\n\tif (blockIdx < 0) return null;\n\n\tconst towardVoid: boolean =\n\t\t(sel.side === 'before' && (direction === 'right' || direction === 'down')) ||\n\t\t(sel.side === 'after' && (direction === 'left' || direction === 'up'));\n\n\tif (towardVoid) return nodeSelTx(state, sel.blockId);\n\n\t// Moving away from the void block — find the neighbor in the other direction\n\tconst awayIdx: number = sel.side === 'before' ? blockIdx - 1 : blockIdx + 1;\n\n\tif (awayIdx < 0 || awayIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[awayIdx];\n\tif (!targetId) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\t// Text block: place cursor at start or end\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'left' || direction === 'up' ? getBlockLength(targetBlock) : 0;\n\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n/** Returns the bounding rect of the current caret position, or null. */\nexport function getCaretRectFromSelection(domSel: globalThis.Selection): DOMRect | null {\n\tif (domSel.rangeCount === 0) return null;\n\tconst range: Range = domSel.getRangeAt(0);\n\tconst rects: DOMRectList = range.getClientRects();\n\tif (rects.length > 0) return rects[0] ?? null;\n\treturn range.getBoundingClientRect();\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves the adjacent block in the given direction for cross-block navigation.\n * Performs boundary checks, isolating checks, and void detection.\n */\nfunction resolveNavigationTarget(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): NavigationTarget | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst currentIdx: number = blockOrder.indexOf(sel.anchor.blockId);\n\tif (currentIdx < 0) return null;\n\n\tconst targetIdx: number =\n\t\tdirection === 'left' || direction === 'up' ? currentIdx - 1 : currentIdx + 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\n\tif (!canCrossBlockBoundary(state, sel.anchor.blockId, targetId)) return null;\n\n\treturn { targetId, isVoid: isVoidBlock(state, targetId) };\n}\n\n/**\n * Resolves an editor position in the target block by using the goalColumn\n * X coordinate and `caretPositionFromPoint` / `caretRangeFromPoint`.\n *\n * Returns `null` when the point-to-caret API is unavailable, when the\n * resolved DOM node falls outside the target block, or when the resulting\n * position doesn't belong to the target block — triggering the fallback\n * (block-end for up, block-start for down) in `navigateVerticalWithGoalColumn`.\n */\nfunction resolveGoalColumnPosition(\n\tcontainer: HTMLElement,\n\ttargetId: BlockId,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number,\n): { blockId: BlockId; offset: number } | null {\n\tconst blockEl: Element | null = container.querySelector(`[data-block-id=\"${targetId}\"]`);\n\tif (!blockEl) return null;\n\n\tconst rect: DOMRect = blockEl.getBoundingClientRect();\n\t// Small inset to ensure we hit inside the line, not on the border\n\tconst y: number =\n\t\tdirection === 'down' ? rect.top + BLOCK_EDGE_INSET_PX : rect.bottom - BLOCK_EDGE_INSET_PX;\n\t// Clamp goalColumn within the block's horizontal bounds to avoid\n\t// caretPositionFromPoint returning unexpected results for out-of-bounds X.\n\tconst clampedX: number = Math.min(Math.max(goalColumn, rect.left), rect.right - 1);\n\n\tconst root: Document | ShadowRoot = container.getRootNode() as Document | ShadowRoot;\n\tconst domPoint = domPositionFromPoint(root, clampedX, y, container.ownerDocument);\n\tif (!domPoint) return null;\n\n\t// Verify the DOM node is inside the target block\n\tif (!blockEl.contains(domPoint.node)) return null;\n\n\tconst position = domPositionToState(container, domPoint.node, domPoint.offset);\n\tif (!position) return null;\n\n\t// Ensure the resolved position is in the target block\n\tif (position.blockId !== targetId) return null;\n\n\treturn position;\n}\n\n/**\n * Probes whether the cursor is at the top/bottom visual line of a block.\n *\n * Strategy:\n * 1. `Selection.modify('move', direction, 'line')` — moves one visual line.\n * 2. If the cursor left the block or didn't move, it's at the boundary.\n * 3. Cross-check with `getBoundingClientRect`: compare the caret rect before\n * and after the probe. If the vertical position didn't change, the probe\n * hit the block edge even though `Selection.modify` reported movement\n * (can happen with certain inline element layouts).\n *\n * Original DOM selection is always restored before returning.\n */\nfunction probeVerticalBoundary(\n\tcontainer: HTMLElement,\n\tdomSel: globalThis.Selection,\n\tdirection: 'up' | 'down',\n\tpreReadRect?: DOMRect | null,\n): boolean {\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOffset: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOffset: number = domSel.focusOffset;\n\n\t// Cannot determine position → safe default: not at boundary (let browser handle)\n\tif (!origAnchor || !origFocus) return false;\n\n\tconst origRect: DOMRect | null = preReadRect ?? getCaretRectFromSelection(domSel);\n\tconst dirStr: string = direction === 'up' ? 'backward' : 'forward';\n\n\ttry {\n\t\tdomSel.modify('move', dirStr, 'line');\n\n\t\tconst newFocus: Node | null = domSel.focusNode;\n\t\tif (!newFocus) return false;\n\n\t\t// Check if the new position is still within the same block\n\t\tconst origBlock: HTMLElement | null = findBlockAncestor(container, origFocus);\n\t\tconst newBlock: HTMLElement | null = findBlockAncestor(container, newFocus);\n\n\t\t// Moved to a different block → definitely at boundary\n\t\tif (origBlock !== newBlock) return true;\n\n\t\t// Selection didn't move at all → at boundary\n\t\tconst didNotMove: boolean =\n\t\t\tdomSel.anchorNode === origAnchor &&\n\t\t\tdomSel.anchorOffset === origAnchorOffset &&\n\t\t\tdomSel.focusNode === origFocus &&\n\t\t\tdomSel.focusOffset === origFocusOffset;\n\t\tif (didNotMove) return true;\n\n\t\t// Cross-check with getBoundingClientRect: if vertical position unchanged,\n\t\t// the probe was a no-op visually (edge case with inline elements)\n\t\tif (origRect) {\n\t\t\tconst newRect: DOMRect | null = getCaretRectFromSelection(domSel);\n\t\t\tif (newRect) {\n\t\t\t\tconst verticalDelta: number = Math.abs(newRect.top - origRect.top);\n\t\t\t\tif (verticalDelta < VERTICAL_MOVEMENT_THRESHOLD_PX) return true;\n\t\t\t}\n\t\t}\n\n\t\t// Stayed in same block and moved vertically → not at boundary\n\t\treturn false;\n\t} finally {\n\t\t// Restore original selection\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOffset, origFocus, origFocusOffset);\n\t\t} catch {\n\t\t\t// Selection restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/** Finds the closest ancestor element with `data-block-id`. */\nfunction findBlockAncestor(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n"],"names":["moveTx","state","blockId","offset","transaction","setSelection","createCollapsedSelection","setStoredMarks","storedMarks","build","nodeSelTx","targetId","path","findNodePath","doc","createNodeSelection","extendTx","anchorBlockId","anchorOffset","headBlockId","headOffset","createSelection","domPositionFromPoint","root","x","y","ownerDoc","domNode","domOffset","cp","caretPositionFromPoint","offsetNode","range","caretRangeFromPoint","startContainer","startOffset","node","getSelection","container","getRootNode","ShadowRoot","window","syncSelectionToDOM","selection","domSel","isGapCursor","removeAllRanges","isNodeSelection","blockEl","querySelector","nodeId","parent","parentNode","childIdx","childIndexOf","setBaseAndExtent","anchorPos","statePositionToDOM","anchor","headPos","head","readSelectionFromDOM","rangeCount","anchorNode","focusNode","contains","domPositionToState","focusOffset","pos","length","current","id","found","getAttribute","contentEl","resolveContentRoot","childNodes","firstChild","nodeName","remaining","walker","createInlineContentWalker","nextNode","nodeType","Node","TEXT_NODE","len","textContent","HTMLElement","lastChild","hasAttribute","rawBlockId","bid","toBlockId","leafBlockEl","unshift","parentElement","targetEl","childOffset","child","Array","from","inlineContentWidth","createPosition","walkerNode","ELEMENT_NODE","document","createTreeWalker","NodeFilter","SHOW_ALL","acceptNode","n","FILTER_REJECT","FILTER_ACCEPT","FILTER_SKIP","idx","c","width","endOfTextblock","direction","caretRect","sel","isCollapsed","block","getBlock","blockLength","getBlockLength","isRtl","getTextDirection","modify","preReadRect","origAnchor","origAnchorOffset","origFocus","origFocusOffset","origRect","getCaretRectFromSelection","dirStr","newFocus","findBlockAncestor","newRect","Math","abs","top","navigateAcrossBlocks","target","resolveNavigationTarget","isVoid","targetBlock","targetOffset","navigateVerticalWithGoalColumn","goalColumn","blockLen","position","rect","getBoundingClientRect","bottom","clampedX","min","max","left","right","domPoint","ownerDocument","fallbackOffset","skipInlineNode","content","getContentAtOffset","kind","navigateFromGapCursor","blockOrder","getBlockOrder","blockIdx","indexOf","side","awayIdx","isVoidBlock","getRangeAt","rects","getClientRects","currentIdx","targetIdx","canCrossBlockBoundary"],"mappings":"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"}
1
+ {"version":3,"file":"CaretNavigation-qLfeL0So.mjs","sources":["../../src/state/SelectionTransactions.ts","../../src/view/DomPointUtils.ts","../../src/view/SelectionSync.ts","../../src/view/CaretNavigation.ts"],"sourcesContent":["/**\n * Shared transaction builders for cursor and selection movement.\n *\n * These helpers encapsulate the common pattern of building a transaction\n * that sets a new selection and clears storedMarks.\n */\n\nimport { findNodePath } from '../model/NodeResolver.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tcreateSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from './EditorState.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Builds a collapsed-cursor transaction that clears storedMarks. */\nexport function moveTx(state: EditorState, blockId: BlockId, offset: number): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createCollapsedSelection(blockId, offset))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a NodeSelection transaction that clears storedMarks. */\nexport function nodeSelTx(state: EditorState, targetId: BlockId): Transaction {\n\tconst path: BlockId[] = (findNodePath(state.doc, targetId) ?? []) as BlockId[];\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createNodeSelection(targetId, path))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a range-selection transaction that clears storedMarks. */\nexport function extendTx(\n\tstate: EditorState,\n\tanchorBlockId: BlockId,\n\tanchorOffset: number,\n\theadBlockId: BlockId,\n\theadOffset: number,\n): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(\n\t\t\tcreateSelection(\n\t\t\t\t{ blockId: anchorBlockId, offset: anchorOffset },\n\t\t\t\t{ blockId: headBlockId, offset: headOffset },\n\t\t\t),\n\t\t)\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n","/**\n * Utilities for resolving a DOM position from screen coordinates.\n *\n * Provides a fallback chain: `caretPositionFromPoint` → `caretRangeFromPoint`\n * → document fallback (when root is a ShadowRoot).\n */\n\n/** Result of resolving a DOM position from screen coordinates. */\nexport interface DomPoint {\n\treadonly node: Node;\n\treadonly offset: number;\n}\n\n/**\n * Resolves a DOM position from screen coordinates using the best available API.\n *\n * Tries `caretPositionFromPoint` first (standard), then `caretRangeFromPoint`\n * (WebKit fallback), and finally tries the ownerDocument when the root is\n * a ShadowRoot and the first two returned nothing.\n */\nexport function domPositionFromPoint(\n\troot: Document | ShadowRoot,\n\tx: number,\n\ty: number,\n\townerDoc?: Document,\n): DomPoint | null {\n\tlet domNode: Node | null = null;\n\tlet domOffset = 0;\n\n\t// Standard API\n\tif ('caretPositionFromPoint' in root) {\n\t\tconst cp = (root as Document).caretPositionFromPoint(x, y);\n\t\tif (cp) {\n\t\t\tdomNode = cp.offsetNode;\n\t\t\tdomOffset = cp.offset;\n\t\t}\n\t}\n\n\t// Fallback\n\tif (!domNode && 'caretRangeFromPoint' in root) {\n\t\tconst range = (root as Document).caretRangeFromPoint(x, y);\n\t\tif (range) {\n\t\t\tdomNode = range.startContainer;\n\t\t\tdomOffset = range.startOffset;\n\t\t}\n\t}\n\n\t// Also try on the document when the root is a ShadowRoot and returned nothing\n\tif (!domNode && ownerDoc && root !== ownerDoc) {\n\t\tif ('caretRangeFromPoint' in ownerDoc) {\n\t\t\tconst range = ownerDoc.caretRangeFromPoint(x, y);\n\t\t\tif (range) {\n\t\t\t\tdomNode = range.startContainer;\n\t\t\t\tdomOffset = range.startOffset;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!domNode) return null;\n\treturn { node: domNode, offset: domOffset };\n}\n","/**\n * Selection synchronization between editor state and DOM.\n */\n\nimport type { EditorSelection, Position, Selection } from '../model/Selection.js';\nimport {\n\tcreatePosition,\n\tcreateSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\n\ninterface DOMPosition {\n\tnode: Node;\n\toffset: number;\n}\n\n/** Gets the selection object, preferring shadow root's selection for Shadow DOM. */\nexport function getSelection(container: HTMLElement): globalThis.Selection | null {\n\tconst root = container.getRootNode();\n\tif (root instanceof ShadowRoot && 'getSelection' in root) {\n\t\treturn (root as ShadowRoot & { getSelection(): globalThis.Selection | null }).getSelection();\n\t}\n\treturn window.getSelection();\n}\n\n/** Syncs the editor state selection to the DOM. */\nexport function syncSelectionToDOM(container: HTMLElement, selection: EditorSelection): void {\n\tconst domSel = getSelection(container);\n\tif (!domSel) return;\n\n\t// GapCursor: clear browser selection (gap cursor has no DOM equivalent)\n\tif (isGapCursor(selection)) {\n\t\tdomSel.removeAllRanges();\n\t\treturn;\n\t}\n\n\t// NodeSelection: select the entire block element\n\tif (isNodeSelection(selection)) {\n\t\tconst blockEl = container.querySelector(`[data-block-id=\"${selection.nodeId}\"]`);\n\t\tif (!blockEl) return;\n\t\tconst parent = blockEl.parentNode;\n\t\tif (!parent) return;\n\t\tconst childIdx = childIndexOf(parent, blockEl);\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(parent, childIdx, parent, childIdx + 1);\n\t\t} catch {\n\t\t\t// Selection may fail if DOM is not yet rendered\n\t\t}\n\t\treturn;\n\t}\n\n\tconst anchorPos = statePositionToDOM(container, selection.anchor);\n\tconst headPos = statePositionToDOM(container, selection.head);\n\n\tif (!anchorPos || !headPos) return;\n\n\ttry {\n\t\tdomSel.setBaseAndExtent(anchorPos.node, anchorPos.offset, headPos.node, headPos.offset);\n\t} catch {\n\t\t// Selection may fail if DOM is not yet rendered\n\t}\n}\n\n/** Reads the current DOM selection and converts it to a state selection. */\nexport function readSelectionFromDOM(container: HTMLElement): Selection | null {\n\tconst domSel = getSelection(container);\n\tif (!domSel || domSel.rangeCount === 0) return null;\n\n\tconst anchorNode = domSel.anchorNode;\n\tconst focusNode = domSel.focusNode;\n\n\tif (!anchorNode || !focusNode) return null;\n\tif (!container.contains(anchorNode) || !container.contains(focusNode)) return null;\n\n\tconst anchor = domPositionToState(container, anchorNode, domSel.anchorOffset);\n\tconst head = domPositionToState(container, focusNode, domSel.focusOffset);\n\n\tif (!anchor || !head) return null;\n\n\treturn createSelection(anchor, head);\n}\n\n/** Converts a state position (blockId + offset) to a DOM position (node + offset). */\nfunction statePositionToDOM(container: HTMLElement, pos: Position): DOMPosition | null {\n\t// If path is available, navigate directly to the leaf block\n\tlet blockEl: Element | null = null;\n\tif (pos.path && pos.path.length > 0) {\n\t\tlet current: Element = container;\n\t\tfor (const id of pos.path) {\n\t\t\tconst found = current.querySelector(`:scope [data-block-id=\"${id}\"]`);\n\t\t\tif (!found) break;\n\t\t\tcurrent = found;\n\t\t}\n\t\tif (current !== container && current.getAttribute('data-block-id') === pos.blockId) {\n\t\t\tblockEl = current;\n\t\t}\n\t}\n\tblockEl ??= container.querySelector(`[data-block-id=\"${pos.blockId}\"]`);\n\tif (!blockEl) return null;\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle empty paragraphs with <br>\n\tif (contentEl.childNodes.length === 1 && contentEl.firstChild?.nodeName === 'BR') {\n\t\treturn { node: contentEl, offset: 0 };\n\t}\n\n\t// Walk through text nodes and inline elements to find the correct position\n\tlet remaining = pos.offset;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet current = walker.nextNode();\n\twhile (current) {\n\t\tif (current.nodeType === Node.TEXT_NODE) {\n\t\t\tconst len = current.textContent?.length ?? 0;\n\t\t\tif (remaining <= len) {\n\t\t\t\treturn { node: current, offset: remaining };\n\t\t\t}\n\t\t\tremaining -= len;\n\t\t} else if (\n\t\t\tcurrent instanceof HTMLElement &&\n\t\t\tcurrent.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\t// InlineNode element — width 1 in state offset space\n\t\t\tif (remaining === 0) {\n\t\t\t\t// Position before this inline element\n\t\t\t\tconst parent = current.parentNode;\n\t\t\t\tif (parent) {\n\t\t\t\t\tconst childIndex = childIndexOf(parent, current);\n\t\t\t\t\treturn { node: parent, offset: childIndex };\n\t\t\t\t}\n\t\t\t}\n\t\t\tremaining -= 1;\n\t\t}\n\t\tcurrent = walker.nextNode();\n\t}\n\n\t// Fallback: position at end of block\n\tconst lastChild = contentEl.lastChild;\n\tif (lastChild) {\n\t\tif (lastChild.nodeType === Node.TEXT_NODE) {\n\t\t\treturn { node: lastChild, offset: lastChild.textContent?.length ?? 0 };\n\t\t}\n\t\treturn { node: contentEl, offset: contentEl.childNodes.length };\n\t}\n\n\treturn { node: contentEl, offset: 0 };\n}\n\n/** Converts a DOM position to a state position, including nested path. */\nexport function domPositionToState(\n\tcontainer: HTMLElement,\n\tnode: Node,\n\tdomOffset: number,\n): Position | null {\n\t// Find the innermost block element\n\tconst blockEl = findBlockElement(container, node);\n\tif (!blockEl) return null;\n\n\tconst rawBlockId = blockEl.getAttribute('data-block-id');\n\tif (!rawBlockId) return null;\n\tconst bid = toBlockId(rawBlockId);\n\n\t// Build path by collecting all data-block-id ancestors from leaf to root\n\tconst path = buildBlockPath(container, blockEl);\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle clicks on the block or content element itself (e.g. empty paragraph with <br>)\n\tif (node === blockEl || node === contentEl) {\n\t\tconst targetEl: Element = node === contentEl ? contentEl : blockEl;\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\t\tfor (const child of Array.from(targetEl.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\t// If node is outside the contentDOM (e.g. in NodeView header), map to offset 0\n\tif (contentEl !== blockEl && !contentEl.contains(node)) {\n\t\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n\t}\n\n\t// Calculate offset by walking text nodes and inline elements\n\tlet offset = 0;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet walkerNode = walker.nextNode();\n\twhile (walkerNode) {\n\t\tif (walkerNode === node) {\n\t\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\treturn createPosition(bid, offset + domOffset, path.length > 1 ? path : undefined);\n\t\t\t}\n\t\t\t// Node is an inline element — return offset at its start\n\t\t\treturn createPosition(bid, offset, path.length > 1 ? path : undefined);\n\t\t}\n\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\toffset += walkerNode.textContent?.length ?? 0;\n\t\t} else if (\n\t\t\twalkerNode instanceof HTMLElement &&\n\t\t\twalkerNode.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\toffset += 1;\n\t\t}\n\t\twalkerNode = walker.nextNode();\n\t}\n\n\t// If the node is not a text node, try to find the offset from element context\n\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\n\t\tfor (const child of Array.from(node.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n}\n\n/** Builds an array of block IDs from root to leaf by walking up from a block element. */\nfunction buildBlockPath(container: HTMLElement, leafBlockEl: HTMLElement): BlockId[] {\n\tconst path: BlockId[] = [];\n\tlet current: HTMLElement | null = leafBlockEl;\n\n\twhile (current && current !== container) {\n\t\tif (current.hasAttribute('data-block-id')) {\n\t\t\tpath.unshift(toBlockId(current.getAttribute('data-block-id') ?? ''));\n\t\t}\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn path;\n}\n\n/** Finds the closest block element ancestor. */\nfunction findBlockElement(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n\n/** Checks if a node is inside a contentEditable=\"false\" inline element. */\nfunction isInsideInlineElement(node: Node, root: Element): boolean {\n\tlet parent: Node | null = node.parentNode;\n\twhile (parent && parent !== root) {\n\t\tif (parent instanceof HTMLElement && parent.getAttribute('contenteditable') === 'false') {\n\t\t\treturn true;\n\t\t}\n\t\tparent = parent.parentNode;\n\t}\n\treturn false;\n}\n\n/**\n * Creates a TreeWalker that visits text nodes and contentEditable=\"false\"\n * inline elements within a block, skipping mark wrappers and nested blocks.\n */\nfunction createInlineContentWalker(blockEl: Element): TreeWalker {\n\treturn document.createTreeWalker(blockEl, NodeFilter.SHOW_ALL, {\n\t\tacceptNode: (n: Node) => {\n\t\t\t// Skip cursor wrapper (ZWS for stored marks during IME composition)\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-cursor-wrapper')) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Skip anything inside an inline element (contentEditable=\"false\")\n\t\t\tif (isInsideInlineElement(n, blockEl)) return NodeFilter.FILTER_REJECT;\n\t\t\t// Skip nested block elements and their descendants\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-block-id') && n !== blockEl) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Accept text nodes\n\t\t\tif (n.nodeType === Node.TEXT_NODE) return NodeFilter.FILTER_ACCEPT;\n\t\t\t// Accept inline elements (contentEditable=\"false\")\n\t\t\tif (n instanceof HTMLElement && n.getAttribute('contenteditable') === 'false') {\n\t\t\t\treturn NodeFilter.FILTER_ACCEPT;\n\t\t\t}\n\t\t\t// Skip other elements (mark wrappers, decoration wrappers) — descend\n\t\t\treturn NodeFilter.FILTER_SKIP;\n\t\t},\n\t});\n}\n\n/** Returns the child index of a node within its parent. */\nfunction childIndexOf(parent: Node, child: Node): number {\n\tlet idx = 0;\n\tfor (const c of Array.from(parent.childNodes)) {\n\t\tif (c === child) return idx;\n\t\tidx++;\n\t}\n\treturn idx;\n}\n\n/**\n * Resolves the content root for inline content walking.\n * If the block element has a contentDOM (marked with data-content-dom),\n * returns it so the walker skips NodeView structural elements (headers, etc.).\n */\nfunction resolveContentRoot(blockEl: Element): Element {\n\tconst contentDOM: Element | null = blockEl.querySelector('[data-content-dom]');\n\treturn contentDOM ?? blockEl;\n}\n\n/** Counts the inline content width of a DOM node (text length + 1 per inline element). */\nfunction inlineContentWidth(node: Node): number {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent?.length ?? 0;\n\t}\n\tif (node instanceof HTMLElement && node.getAttribute('contenteditable') === 'false') {\n\t\treturn 1;\n\t}\n\t// Mark wrapper or other element — sum children\n\tlet width = 0;\n\tfor (const child of Array.from(node.childNodes)) {\n\t\twidth += inlineContentWidth(child);\n\t}\n\treturn width;\n}\n","/**\n * Caret navigation utilities for layout-aware cursor movement.\n *\n * Provides `endOfTextblock()` for detecting visual block boundaries,\n * `navigateAcrossBlocks()` for cross-block cursor transitions, and\n * `navigateVerticalWithGoalColumn()` for column-preserving vertical nav.\n */\n\nimport { type BlockNode, 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 { moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { domPositionFromPoint } from './DomPointUtils.js';\n\n/** Inset in pixels from block edge for vertical goal-column probing. */\nconst BLOCK_EDGE_INSET_PX = 2;\n\n/** Minimum vertical movement (pixels) for a Selection.modify probe to be considered a real move. */\nconst VERTICAL_MOVEMENT_THRESHOLD_PX = 1;\nimport { getTextDirection } from './Platform.js';\nimport { domPositionToState, getSelection } from './SelectionSync.js';\n\nexport type CaretDirection = 'left' | 'right' | 'up' | 'down';\n\n/** Result of resolving the adjacent block for vertical navigation. */\ninterface NavigationTarget {\n\treadonly targetId: BlockId;\n\treadonly isVoid: boolean;\n}\n\n/**\n * Checks whether the cursor is at the visual edge of its text block.\n *\n * For horizontal directions (left/right), uses a pure offset check.\n * For vertical directions (up/down), tries `Selection.modify` probing\n * to detect visual line boundaries, falling back to offset heuristic.\n *\n * An optional `caretRect` parameter avoids redundant `getBoundingClientRect`\n * calls when the caller already measured the caret position.\n */\nexport function endOfTextblock(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: CaretDirection,\n\tcaretRect?: DOMRect | null,\n): boolean {\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLength: number = getBlockLength(block);\n\n\t// Horizontal: direction-aware offset check\n\tconst blockEl: Element | null = container.querySelector(\n\t\t`[data-block-id=\"${sel.anchor.blockId}\"]`,\n\t);\n\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\n\tif (direction === 'left') return isRtl ? offset === blockLength : offset === 0;\n\tif (direction === 'right') return isRtl ? offset === 0 : offset === blockLength;\n\n\t// Vertical: quick offset check first\n\tif (direction === 'up' && offset === 0) return true;\n\tif (direction === 'down' && offset === blockLength) return true;\n\n\t// Vertical: try Selection.modify probing for multi-line blocks\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel || !domSel.modify) {\n\t\t// Fallback: offset heuristic (no Selection.modify in test environments)\n\t\treturn direction === 'up' ? offset === 0 : offset === blockLength;\n\t}\n\n\treturn probeVerticalBoundary(container, domSel, direction, caretRect);\n}\n\n/**\n * Creates a transaction to move the cursor to an adjacent block.\n *\n * Returns `null` if navigation is not possible (document boundary,\n * isolating boundary, or no adjacent block).\n */\nexport function navigateAcrossBlocks(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\t// Target is text → collapsed selection at start or end\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'right' || direction === 'down' ? 0 : getBlockLength(targetBlock);\n\n\treturn moveTx(state, target.targetId, targetOffset);\n}\n\n/**\n * Navigates vertically using a goal column for visual column preservation.\n *\n * Uses `caretPositionFromPoint` / `caretRangeFromPoint` to find the offset\n * in the target block that is closest to the given goalColumn X coordinate.\n * Falls back to start/end offset when goal column positioning is unavailable.\n */\nexport function navigateVerticalWithGoalColumn(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number | null,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\t// Void → NodeSelection (goalColumn irrelevant)\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\tconst blockLen: number = getBlockLength(targetBlock);\n\n\t// Try to resolve position using goalColumn\n\tif (goalColumn !== null) {\n\t\tconst position = resolveGoalColumnPosition(container, target.targetId, direction, goalColumn);\n\t\tif (position) return moveTx(state, position.blockId, position.offset);\n\t}\n\n\t// Fallback: start (down) or end (up)\n\tconst fallbackOffset: number = direction === 'down' ? 0 : blockLen;\n\treturn moveTx(state, target.targetId, fallbackOffset);\n}\n\n/**\n * Skips over an InlineNode atomically when arrowing left/right.\n *\n * Returns a transaction moving the cursor past the InlineNode,\n * or `null` if the adjacent content is not an InlineNode (or\n * the direction is vertical).\n */\nexport function skipInlineNode(state: EditorState, direction: CaretDirection): Transaction | null {\n\tif (direction === 'up' || direction === 'down') return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\tif (!isCollapsed(sel)) return null;\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\treturn direction === 'right'\n\t\t? skipInlineNodeRight(state, blockId, block, offset)\n\t\t: skipInlineNodeLeft(state, blockId, block, offset);\n}\n\nfunction skipInlineNodeRight(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tconst content = getContentAtOffset(block, offset);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset + 1);\n}\n\nfunction skipInlineNodeLeft(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tif (offset === 0) return null;\n\tconst content = getContentAtOffset(block, offset - 1);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset - 1);\n}\n\n/**\n * Navigates from a GapCursor position.\n *\n * - Arrow toward the adjacent void block → NodeSelection of that block.\n * - Arrow away from the void block → find next block: void → NodeSelection,\n * text → TextSelection, document boundary → no-op.\n */\nexport function navigateFromGapCursor(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (!isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst blockIdx: number = blockOrder.indexOf(sel.blockId);\n\tif (blockIdx < 0) return null;\n\n\tconst towardVoid: boolean =\n\t\t(sel.side === 'before' && (direction === 'right' || direction === 'down')) ||\n\t\t(sel.side === 'after' && (direction === 'left' || direction === 'up'));\n\n\tif (towardVoid) return nodeSelTx(state, sel.blockId);\n\n\t// Moving away from the void block — find the neighbor in the other direction\n\tconst awayIdx: number = sel.side === 'before' ? blockIdx - 1 : blockIdx + 1;\n\n\tif (awayIdx < 0 || awayIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[awayIdx];\n\tif (!targetId) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\t// Text block: place cursor at start or end\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'left' || direction === 'up' ? getBlockLength(targetBlock) : 0;\n\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n/** Returns the bounding rect of the current caret position, or null. */\nexport function getCaretRectFromSelection(domSel: globalThis.Selection): DOMRect | null {\n\tif (domSel.rangeCount === 0) return null;\n\tconst range: Range = domSel.getRangeAt(0);\n\tconst rects: DOMRectList = range.getClientRects();\n\tif (rects.length > 0) return rects[0] ?? null;\n\treturn range.getBoundingClientRect();\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves the adjacent block in the given direction for cross-block navigation.\n * Performs boundary checks, isolating checks, and void detection.\n */\nfunction resolveNavigationTarget(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): NavigationTarget | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst currentIdx: number = blockOrder.indexOf(sel.anchor.blockId);\n\tif (currentIdx < 0) return null;\n\n\tconst targetIdx: number =\n\t\tdirection === 'left' || direction === 'up' ? currentIdx - 1 : currentIdx + 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\n\tif (!canCrossBlockBoundary(state, sel.anchor.blockId, targetId)) return null;\n\n\treturn { targetId, isVoid: isVoidBlock(state, targetId) };\n}\n\n/**\n * Resolves an editor position in the target block by using the goalColumn\n * X coordinate and `caretPositionFromPoint` / `caretRangeFromPoint`.\n *\n * Returns `null` when the point-to-caret API is unavailable, when the\n * resolved DOM node falls outside the target block, or when the resulting\n * position doesn't belong to the target block — triggering the fallback\n * (block-end for up, block-start for down) in `navigateVerticalWithGoalColumn`.\n */\nfunction resolveGoalColumnPosition(\n\tcontainer: HTMLElement,\n\ttargetId: BlockId,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number,\n): { blockId: BlockId; offset: number } | null {\n\tconst blockEl: Element | null = container.querySelector(`[data-block-id=\"${targetId}\"]`);\n\tif (!blockEl) return null;\n\n\tconst rect: DOMRect = blockEl.getBoundingClientRect();\n\t// Small inset to ensure we hit inside the line, not on the border\n\tconst y: number =\n\t\tdirection === 'down' ? rect.top + BLOCK_EDGE_INSET_PX : rect.bottom - BLOCK_EDGE_INSET_PX;\n\t// Clamp goalColumn within the block's horizontal bounds to avoid\n\t// caretPositionFromPoint returning unexpected results for out-of-bounds X.\n\tconst clampedX: number = Math.min(Math.max(goalColumn, rect.left), rect.right - 1);\n\n\tconst root: Document | ShadowRoot = container.getRootNode() as Document | ShadowRoot;\n\tconst domPoint = domPositionFromPoint(root, clampedX, y, container.ownerDocument);\n\tif (!domPoint) return null;\n\n\t// Verify the DOM node is inside the target block\n\tif (!blockEl.contains(domPoint.node)) return null;\n\n\tconst position = domPositionToState(container, domPoint.node, domPoint.offset);\n\tif (!position) return null;\n\n\t// Ensure the resolved position is in the target block\n\tif (position.blockId !== targetId) return null;\n\n\treturn position;\n}\n\n/**\n * Probes whether the cursor is at the top/bottom visual line of a block.\n *\n * Strategy:\n * 1. `Selection.modify('move', direction, 'line')` — moves one visual line.\n * 2. If the cursor left the block or didn't move, it's at the boundary.\n * 3. Cross-check with `getBoundingClientRect`: compare the caret rect before\n * and after the probe. If the vertical position didn't change, the probe\n * hit the block edge even though `Selection.modify` reported movement\n * (can happen with certain inline element layouts).\n *\n * Original DOM selection is always restored before returning.\n */\nfunction probeVerticalBoundary(\n\tcontainer: HTMLElement,\n\tdomSel: globalThis.Selection,\n\tdirection: 'up' | 'down',\n\tpreReadRect?: DOMRect | null,\n): boolean {\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOffset: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOffset: number = domSel.focusOffset;\n\n\t// Cannot determine position → safe default: not at boundary (let browser handle)\n\tif (!origAnchor || !origFocus) return false;\n\n\tconst origRect: DOMRect | null = preReadRect ?? getCaretRectFromSelection(domSel);\n\tconst dirStr: string = direction === 'up' ? 'backward' : 'forward';\n\n\ttry {\n\t\tdomSel.modify('move', dirStr, 'line');\n\n\t\tconst newFocus: Node | null = domSel.focusNode;\n\t\tif (!newFocus) return false;\n\n\t\t// Check if the new position is still within the same block\n\t\tconst origBlock: HTMLElement | null = findBlockAncestor(container, origFocus);\n\t\tconst newBlock: HTMLElement | null = findBlockAncestor(container, newFocus);\n\n\t\t// Moved to a different block → definitely at boundary\n\t\tif (origBlock !== newBlock) return true;\n\n\t\t// Selection didn't move at all → at boundary\n\t\tconst didNotMove: boolean =\n\t\t\tdomSel.anchorNode === origAnchor &&\n\t\t\tdomSel.anchorOffset === origAnchorOffset &&\n\t\t\tdomSel.focusNode === origFocus &&\n\t\t\tdomSel.focusOffset === origFocusOffset;\n\t\tif (didNotMove) return true;\n\n\t\t// Cross-check with getBoundingClientRect: if vertical position unchanged,\n\t\t// the probe was a no-op visually (edge case with inline elements)\n\t\tif (origRect) {\n\t\t\tconst newRect: DOMRect | null = getCaretRectFromSelection(domSel);\n\t\t\tif (newRect) {\n\t\t\t\tconst verticalDelta: number = Math.abs(newRect.top - origRect.top);\n\t\t\t\tif (verticalDelta < VERTICAL_MOVEMENT_THRESHOLD_PX) return true;\n\t\t\t}\n\t\t}\n\n\t\t// Stayed in same block and moved vertically → not at boundary\n\t\treturn false;\n\t} finally {\n\t\t// Restore original selection\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOffset, origFocus, origFocusOffset);\n\t\t} catch {\n\t\t\t// Selection restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/** Finds the closest ancestor element with `data-block-id`. */\nfunction findBlockAncestor(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n"],"names":["moveTx","state","blockId","offset","transaction","setSelection","createCollapsedSelection","setStoredMarks","storedMarks","build","nodeSelTx","targetId","path","findNodePath","doc","createNodeSelection","extendTx","anchorBlockId","anchorOffset","headBlockId","headOffset","createSelection","domPositionFromPoint","root","x","y","ownerDoc","domNode","domOffset","cp","caretPositionFromPoint","offsetNode","range","caretRangeFromPoint","startContainer","startOffset","node","getSelection","container","getRootNode","ShadowRoot","window","syncSelectionToDOM","selection","domSel","isGapCursor","removeAllRanges","isNodeSelection","blockEl","querySelector","nodeId","parent","parentNode","childIdx","childIndexOf","setBaseAndExtent","anchorPos","statePositionToDOM","anchor","headPos","head","readSelectionFromDOM","rangeCount","anchorNode","focusNode","contains","domPositionToState","focusOffset","pos","length","current","id","found","getAttribute","contentEl","resolveContentRoot","childNodes","firstChild","nodeName","remaining","walker","createInlineContentWalker","nextNode","nodeType","Node","TEXT_NODE","len","textContent","HTMLElement","lastChild","hasAttribute","rawBlockId","bid","toBlockId","leafBlockEl","unshift","parentElement","targetEl","childOffset","child","Array","from","inlineContentWidth","createPosition","walkerNode","ELEMENT_NODE","document","createTreeWalker","NodeFilter","SHOW_ALL","acceptNode","n","FILTER_REJECT","FILTER_ACCEPT","FILTER_SKIP","idx","c","width","endOfTextblock","direction","caretRect","sel","isCollapsed","block","getBlock","blockLength","getBlockLength","isRtl","getTextDirection","modify","preReadRect","origAnchor","origAnchorOffset","origFocus","origFocusOffset","origRect","getCaretRectFromSelection","dirStr","newFocus","findBlockAncestor","newRect","Math","abs","top","navigateAcrossBlocks","target","resolveNavigationTarget","isVoid","targetBlock","targetOffset","navigateVerticalWithGoalColumn","goalColumn","blockLen","position","rect","getBoundingClientRect","bottom","clampedX","min","max","left","right","domPoint","ownerDocument","fallbackOffset","skipInlineNode","content","getContentAtOffset","kind","navigateFromGapCursor","blockOrder","getBlockOrder","blockIdx","indexOf","side","awayIdx","isVoidBlock","getRangeAt","rects","getClientRects","currentIdx","targetIdx","canCrossBlockBoundary"],"mappings":"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"}