@notectl/core 1.7.0 → 1.7.2

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 (218) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs → AttrRegistry-D9Jx_RVb.mjs} +2 -2
  3. package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs.map → AttrRegistry-D9Jx_RVb.mjs.map} +1 -1
  4. package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs +80 -0
  5. package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs.map +1 -0
  6. package/dist/chunks/{CaretNavigation-CAJXoaae.mjs → CaretNavigation-7JYpIKbs.mjs} +56 -56
  7. package/dist/chunks/{CaretNavigation-CAJXoaae.mjs.map → CaretNavigation-7JYpIKbs.mjs.map} +1 -1
  8. package/dist/chunks/ColorGrid-Dneqb8if.mjs +81 -0
  9. package/dist/chunks/ColorGrid-Dneqb8if.mjs.map +1 -0
  10. package/dist/chunks/ColorPickerPopup-CiCLBbe9.mjs +37 -0
  11. package/dist/chunks/ColorPickerPopup-CiCLBbe9.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-Brtw2jL1.mjs} +18 -18
  15. package/dist/chunks/{Document-CuwE73FF.mjs.map → Document-Brtw2jL1.mjs.map} +1 -1
  16. package/dist/chunks/MarkCommands-6RUJ4L3r.mjs +94 -0
  17. package/dist/chunks/MarkCommands-6RUJ4L3r.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-C2XYkeOi.mjs} +2 -2
  21. package/dist/chunks/{NodeResolver-B-wzthAQ.mjs.map → NodeResolver-C2XYkeOi.mjs.map} +1 -1
  22. package/dist/chunks/PopupPositioning-BTT-_pQ3.mjs +32 -0
  23. package/dist/chunks/PopupPositioning-BTT-_pQ3.mjs.map +1 -0
  24. package/dist/chunks/RangeIterator-Bh4Vl_6D.mjs +16 -0
  25. package/dist/chunks/RangeIterator-Bh4Vl_6D.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/StyleRuntime-ChRjVQwL.mjs +239 -0
  29. package/dist/chunks/StyleRuntime-ChRjVQwL.mjs.map +1 -0
  30. package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
  31. package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs → ToolbarKeyboardNav-DhpL8zNV.mjs} +3 -3
  32. package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs.map → ToolbarKeyboardNav-DhpL8zNV.mjs.map} +1 -1
  33. package/dist/chunks/{ToolbarPlugin-B6JETWaw.mjs → ToolbarPlugin-DpLVJ71w.mjs} +348 -135
  34. package/dist/chunks/ToolbarPlugin-DpLVJ71w.mjs.map +1 -0
  35. package/dist/chunks/index--n1RGpq-.mjs +171 -0
  36. package/dist/chunks/index--n1RGpq-.mjs.map +1 -0
  37. package/dist/chunks/index-4or5UzNO.mjs +168 -0
  38. package/dist/chunks/index-4or5UzNO.mjs.map +1 -0
  39. package/dist/chunks/{index-pxbbDHVH.mjs → index-ABlTDvEX.mjs} +20 -19
  40. package/dist/chunks/index-ABlTDvEX.mjs.map +1 -0
  41. package/dist/chunks/{index--z_Txzej.mjs → index-BAke7kNL.mjs} +45 -45
  42. package/dist/chunks/{index--z_Txzej.mjs.map → index-BAke7kNL.mjs.map} +1 -1
  43. package/dist/chunks/{index-DMt3ojrY.mjs → index-BXtw5PNN.mjs} +2 -2
  44. package/dist/chunks/{index-DMt3ojrY.mjs.map → index-BXtw5PNN.mjs.map} +1 -1
  45. package/dist/chunks/{index-FlTg80Pk.mjs → index-BhDXjueu.mjs} +285 -325
  46. package/dist/chunks/index-BhDXjueu.mjs.map +1 -0
  47. package/dist/chunks/{index-D0s0Q8Wc.mjs → index-BqNUfq-y.mjs} +43 -35
  48. package/dist/chunks/index-BqNUfq-y.mjs.map +1 -0
  49. package/dist/chunks/{index-CflvcXSd.mjs → index-BvqkuEY1.mjs} +21 -17
  50. package/dist/chunks/index-BvqkuEY1.mjs.map +1 -0
  51. package/dist/chunks/{index-C9gc3Bws.mjs → index-C1opJocO.mjs} +32 -32
  52. package/dist/chunks/{index-C9gc3Bws.mjs.map → index-C1opJocO.mjs.map} +1 -1
  53. package/dist/chunks/index-CWltg1wK.mjs +124 -0
  54. package/dist/chunks/index-CWltg1wK.mjs.map +1 -0
  55. package/dist/chunks/{index-D8tbbW97.mjs → index-Cd3uJLP3.mjs} +2 -2
  56. package/dist/chunks/{index-D8tbbW97.mjs.map → index-Cd3uJLP3.mjs.map} +1 -1
  57. package/dist/chunks/{index-C0cEs7Jc.mjs → index-CluzpB2b.mjs} +10 -10
  58. package/dist/chunks/{index-C0cEs7Jc.mjs.map → index-CluzpB2b.mjs.map} +1 -1
  59. package/dist/chunks/{index-CoeD4T9-.mjs → index-D3NlpmT1.mjs} +15 -11
  60. package/dist/chunks/index-D3NlpmT1.mjs.map +1 -0
  61. package/dist/chunks/{index-CAV2N-fF.mjs → index-D515iymW.mjs} +75 -74
  62. package/dist/chunks/{index-CAV2N-fF.mjs.map → index-D515iymW.mjs.map} +1 -1
  63. package/dist/chunks/{index-B-NSeaM4.mjs → index-Dk49P9N4.mjs} +2 -2
  64. package/dist/chunks/{index-B-NSeaM4.mjs.map → index-Dk49P9N4.mjs.map} +1 -1
  65. package/dist/chunks/index-DyzigvbZ.mjs +410 -0
  66. package/dist/chunks/index-DyzigvbZ.mjs.map +1 -0
  67. package/dist/chunks/index-ErZ5_a1o.mjs +296 -0
  68. package/dist/chunks/index-ErZ5_a1o.mjs.map +1 -0
  69. package/dist/chunks/{index-CNs4QQ8l.mjs → index-fsVJLutn.mjs} +52 -64
  70. package/dist/chunks/index-fsVJLutn.mjs.map +1 -0
  71. package/dist/commands/AttributedMarkCommands.d.ts +46 -0
  72. package/dist/commands/AttributedMarkCommands.d.ts.map +1 -0
  73. package/dist/commands/CommandHelpers.d.ts +21 -0
  74. package/dist/commands/CommandHelpers.d.ts.map +1 -0
  75. package/dist/commands/Commands.d.ts +2 -8
  76. package/dist/commands/Commands.d.ts.map +1 -1
  77. package/dist/commands/DeleteCommands.d.ts.map +1 -1
  78. package/dist/commands/GapCursorCommands.d.ts.map +1 -1
  79. package/dist/commands/NodeSelectionCommands.d.ts.map +1 -1
  80. package/dist/commands/PasteCommand.d.ts.map +1 -1
  81. package/dist/commands/RangeIterator.d.ts +10 -0
  82. package/dist/commands/RangeIterator.d.ts.map +1 -0
  83. package/dist/editor/DocumentParser.d.ts.map +1 -1
  84. package/dist/editor/DocumentSerializer.d.ts +2 -0
  85. package/dist/editor/DocumentSerializer.d.ts.map +1 -1
  86. package/dist/editor/EditorThemeController.d.ts +3 -0
  87. package/dist/editor/EditorThemeController.d.ts.map +1 -1
  88. package/dist/editor/MarkSerializer.d.ts +11 -0
  89. package/dist/editor/MarkSerializer.d.ts.map +1 -0
  90. package/dist/editor/NotectlEditor.d.ts +19 -3
  91. package/dist/editor/NotectlEditor.d.ts.map +1 -1
  92. package/dist/editor/PaperLayoutController.d.ts +2 -0
  93. package/dist/editor/PaperLayoutController.d.ts.map +1 -1
  94. package/dist/editor/styles/font-select.d.ts +1 -1
  95. package/dist/editor/styles/font-select.d.ts.map +1 -1
  96. package/dist/editor/styles/font-size-select.d.ts +1 -1
  97. package/dist/editor/styles/font-size-select.d.ts.map +1 -1
  98. package/dist/editor/styles/heading-select.d.ts +1 -1
  99. package/dist/editor/styles/heading-select.d.ts.map +1 -1
  100. package/dist/editor/styles/toolbar.d.ts +1 -1
  101. package/dist/editor/styles/toolbar.d.ts.map +1 -1
  102. package/dist/index.d.ts +9 -2
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/input/ClipboardHandler.d.ts +0 -2
  105. package/dist/input/ClipboardHandler.d.ts.map +1 -1
  106. package/dist/input/LegacyHTMLNormalizer.d.ts.map +1 -1
  107. package/dist/model/MarkSpec.d.ts +6 -0
  108. package/dist/model/MarkSpec.d.ts.map +1 -1
  109. package/dist/notectl-core.mjs +2244 -2095
  110. package/dist/notectl-core.mjs.map +1 -1
  111. package/dist/notectl-core.umd.js +2862 -2415
  112. package/dist/notectl-core.umd.js.map +1 -1
  113. package/dist/plugins/PluginManager.d.ts +2 -0
  114. package/dist/plugins/PluginManager.d.ts.map +1 -1
  115. package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -1
  116. package/dist/plugins/alignment.mjs +1 -1
  117. package/dist/plugins/blockquote.mjs +1 -1
  118. package/dist/plugins/caret-navigation.mjs +1 -1
  119. package/dist/plugins/code-block/CodeBlockNodeView.d.ts.map +1 -1
  120. package/dist/plugins/code-block.mjs +1 -1
  121. package/dist/plugins/font/FontPlugin.d.ts +0 -6
  122. package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
  123. package/dist/plugins/font-size/FontSizeOperations.d.ts.map +1 -1
  124. package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -5
  125. package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
  126. package/dist/plugins/font-size.mjs +1 -1
  127. package/dist/plugins/font.mjs +1 -1
  128. package/dist/plugins/gap-cursor.mjs +2 -2
  129. package/dist/plugins/hard-break.mjs +2 -25
  130. package/dist/plugins/hard-break.mjs.map +1 -1
  131. package/dist/plugins/heading/HeadingBlockTypePicker.d.ts +1 -7
  132. package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
  133. package/dist/plugins/heading/HeadingPlugin.d.ts +0 -6
  134. package/dist/plugins/heading/HeadingPlugin.d.ts.map +1 -1
  135. package/dist/plugins/heading.mjs +1 -1
  136. package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
  137. package/dist/plugins/highlight.mjs +1 -1
  138. package/dist/plugins/horizontal-rule.mjs +1 -1
  139. package/dist/plugins/image/ImageNodeView.d.ts.map +1 -1
  140. package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
  141. package/dist/plugins/image.mjs +1 -1
  142. package/dist/plugins/link/LinkPlugin.d.ts.map +1 -1
  143. package/dist/plugins/link.mjs +1 -1
  144. package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
  145. package/dist/plugins/list.mjs +1 -1
  146. package/dist/plugins/print/PrintContentPreparer.d.ts.map +1 -1
  147. package/dist/plugins/shared/ColorGrid.d.ts +25 -0
  148. package/dist/plugins/shared/ColorGrid.d.ts.map +1 -0
  149. package/dist/plugins/shared/ColorMarkOperations.d.ts.map +1 -1
  150. package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
  151. package/dist/plugins/shared/ColorValidation.d.ts +13 -2
  152. package/dist/plugins/shared/ColorValidation.d.ts.map +1 -1
  153. package/dist/plugins/shared/PopupKeyboardPatterns.d.ts +43 -0
  154. package/dist/plugins/shared/PopupKeyboardPatterns.d.ts.map +1 -0
  155. package/dist/plugins/shared/PopupManager.d.ts +45 -0
  156. package/dist/plugins/shared/PopupManager.d.ts.map +1 -0
  157. package/dist/plugins/shared/PopupPositioning.d.ts +21 -0
  158. package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -0
  159. package/dist/plugins/strikethrough.mjs +1 -1
  160. package/dist/plugins/super-sub.mjs +1 -1
  161. package/dist/plugins/table/TableBorderColor.d.ts.map +1 -1
  162. package/dist/plugins/table/TableContextMenu.d.ts +2 -1
  163. package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
  164. package/dist/plugins/table/TableControls.d.ts.map +1 -1
  165. package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -1
  166. package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -1
  167. package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
  168. package/dist/plugins/table.mjs +1 -1
  169. package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
  170. package/dist/plugins/text-color.mjs +1 -1
  171. package/dist/plugins/text-formatting.mjs +1 -1
  172. package/dist/plugins/toolbar/ToolbarItem.d.ts +11 -2
  173. package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
  174. package/dist/plugins/toolbar/ToolbarOverflowController.d.ts +5 -1
  175. package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
  176. package/dist/plugins/toolbar/ToolbarPlugin.d.ts +1 -0
  177. package/dist/plugins/toolbar/ToolbarPlugin.d.ts.map +1 -1
  178. package/dist/plugins/toolbar/ToolbarPopupController.d.ts +9 -1
  179. package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
  180. package/dist/plugins/toolbar/ToolbarRenderers.d.ts.map +1 -1
  181. package/dist/plugins/toolbar/ToolbarTooltip.d.ts.map +1 -1
  182. package/dist/plugins/toolbar/index.d.ts +1 -1
  183. package/dist/plugins/toolbar/index.d.ts.map +1 -1
  184. package/dist/plugins/toolbar.mjs +1 -1
  185. package/dist/state/BlockTreeOps.d.ts +21 -0
  186. package/dist/state/BlockTreeOps.d.ts.map +1 -0
  187. package/dist/state/InlineContentOps.d.ts +37 -0
  188. package/dist/state/InlineContentOps.d.ts.map +1 -0
  189. package/dist/state/StepApplication.d.ts.map +1 -1
  190. package/dist/style/StyleRuntime.d.ts +38 -0
  191. package/dist/style/StyleRuntime.d.ts.map +1 -0
  192. package/dist/test/PluginTestUtils.d.ts +4 -0
  193. package/dist/test/PluginTestUtils.d.ts.map +1 -1
  194. package/dist/view/Reconciler.d.ts.map +1 -1
  195. package/package.json +2 -2
  196. package/dist/chunks/ColorNames-BdsHwA2f.mjs +0 -59
  197. package/dist/chunks/ColorNames-BdsHwA2f.mjs.map +0 -1
  198. package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs +0 -125
  199. package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs.map +0 -1
  200. package/dist/chunks/Commands-CicwYoTQ.mjs +0 -286
  201. package/dist/chunks/Commands-CicwYoTQ.mjs.map +0 -1
  202. package/dist/chunks/MarkCommands-CC37WA7Z.mjs +0 -94
  203. package/dist/chunks/MarkCommands-CC37WA7Z.mjs.map +0 -1
  204. package/dist/chunks/ToolbarPlugin-B6JETWaw.mjs.map +0 -1
  205. package/dist/chunks/index-B9GkDVsx.mjs +0 -187
  206. package/dist/chunks/index-B9GkDVsx.mjs.map +0 -1
  207. package/dist/chunks/index-CKPrAOr6.mjs +0 -209
  208. package/dist/chunks/index-CKPrAOr6.mjs.map +0 -1
  209. package/dist/chunks/index-CNs4QQ8l.mjs.map +0 -1
  210. package/dist/chunks/index-CflvcXSd.mjs.map +0 -1
  211. package/dist/chunks/index-CoeD4T9-.mjs.map +0 -1
  212. package/dist/chunks/index-D0s0Q8Wc.mjs.map +0 -1
  213. package/dist/chunks/index-DfzCof6H.mjs +0 -191
  214. package/dist/chunks/index-DfzCof6H.mjs.map +0 -1
  215. package/dist/chunks/index-DyIwN_67.mjs +0 -404
  216. package/dist/chunks/index-DyIwN_67.mjs.map +0 -1
  217. package/dist/chunks/index-FlTg80Pk.mjs.map +0 -1
  218. package/dist/chunks/index-pxbbDHVH.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Document-CuwE73FF.mjs","sources":["../../src/model/Document.ts"],"sourcesContent":["/**\n * Core document model types for the Notectl editor.\n * All types are deeply readonly — mutations create new instances.\n */\n\nimport type { BlockId, InlineTypeName, MarkTypeName, NodeTypeName } from './TypeBrands.js';\n\n// --- Mark Types ---\n\n/** @deprecated Use {@link MarkTypeName} for new code. */\nexport type MarkType = MarkTypeName;\n\nexport interface Mark {\n\treadonly type: MarkTypeName;\n\treadonly attrs?: Readonly<Record<string, string | number | boolean>>;\n}\n\nexport interface BoldMark extends Mark {\n\treadonly type: MarkTypeName & 'bold';\n}\n\nexport interface ItalicMark extends Mark {\n\treadonly type: MarkTypeName & 'italic';\n}\n\nexport interface UnderlineMark extends Mark {\n\treadonly type: MarkTypeName & 'underline';\n}\n\n// --- Node Types ---\n\n/** @deprecated Use {@link NodeTypeName} for new code. */\nexport type NodeType = NodeTypeName;\n\nexport interface BlockAttrs {\n\treadonly [key: string]: string | number | boolean;\n}\n\nexport interface TextNode {\n\treadonly type: 'text';\n\treadonly text: string;\n\treadonly marks: readonly Mark[];\n}\n\n/** Atomic inline element (width 1 in offset space). */\nexport interface InlineNode {\n\treadonly type: 'inline';\n\treadonly inlineType: InlineTypeName;\n\treadonly attrs: Readonly<Record<string, string | number | boolean>>;\n}\n\n/** A child of a BlockNode: text, inline element, or nested block. */\nexport type ChildNode = TextNode | InlineNode | BlockNode;\n\nexport interface BlockNode {\n\treadonly id: BlockId;\n\treadonly type: NodeTypeName;\n\treadonly attrs?: BlockAttrs;\n\treadonly children: readonly ChildNode[];\n}\n\nexport interface Document {\n\treadonly children: readonly BlockNode[];\n}\n\n// --- Text Segment (for mark-preserving undo) ---\n\nexport interface TextSegment {\n\treadonly text: string;\n\treadonly marks: readonly Mark[];\n}\n\n/** A content segment: either text (with marks) or an inline node. */\nexport type ContentSegment =\n\t| { readonly kind: 'text'; readonly text: string; readonly marks: readonly Mark[] }\n\t| { readonly kind: 'inline'; readonly node: InlineNode };\n\n/** Extracts TextNode segments within a block for the given offset range. */\nexport function getBlockSegmentsInRange(\n\tblock: BlockNode,\n\tfrom: number,\n\tto: number,\n): readonly TextSegment[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tconst segments: TextSegment[] = [];\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tconst childWidth: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tconst childEnd: number = pos + childWidth;\n\n\t\tif (childEnd <= from || pos >= to) {\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isInlineNode(child)) {\n\t\t\t// InlineNodes are skipped for TextSegment extraction\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst sliceFrom: number = Math.max(0, from - pos);\n\t\tconst sliceTo: number = Math.min(child.text.length, to - pos);\n\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\n\t\tif (text.length > 0) {\n\t\t\tsegments.push({ text, marks: child.marks });\n\t\t}\n\n\t\tpos = childEnd;\n\t}\n\n\treturn segments;\n}\n\n/** Returns content segments (text and inline) for a block range. */\nexport function getBlockContentSegmentsInRange(\n\tblock: BlockNode,\n\tfrom: number,\n\tto: number,\n): readonly ContentSegment[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tconst segments: ContentSegment[] = [];\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tconst childWidth: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tconst childEnd: number = pos + childWidth;\n\n\t\tif (childEnd <= from || pos >= to) {\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isInlineNode(child)) {\n\t\t\tsegments.push({ kind: 'inline', node: child });\n\t\t} else {\n\t\t\tconst sliceFrom: number = Math.max(0, from - pos);\n\t\t\tconst sliceTo: number = Math.min(child.text.length, to - pos);\n\t\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\t\t\tif (text.length > 0) {\n\t\t\t\tsegments.push({ kind: 'text', text, marks: child.marks });\n\t\t\t}\n\t\t}\n\n\t\tpos = childEnd;\n\t}\n\n\treturn segments;\n}\n\n// --- Type Guards ---\n\n/** Checks whether a value is a {@link TextNode}. */\nexport function isTextNode(node: unknown): node is TextNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\t(node as TextNode).type === 'text' &&\n\t\ttypeof (node as TextNode).text === 'string'\n\t);\n}\n\n/** Checks whether a value is an {@link InlineNode}. */\nexport function isInlineNode(node: unknown): node is InlineNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\t(node as InlineNode).type === 'inline' &&\n\t\ttypeof (node as InlineNode).inlineType === 'string'\n\t);\n}\n\n/** Checks whether a value is a {@link BlockNode}. */\nexport function isBlockNode(node: unknown): node is BlockNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\ttypeof (node as BlockNode).id === 'string' &&\n\t\ttypeof (node as BlockNode).type === 'string' &&\n\t\t(node as BlockNode).type !== ('text' as string) &&\n\t\t(node as BlockNode).type !== ('inline' as string) &&\n\t\tArray.isArray((node as BlockNode).children)\n\t);\n}\n\n// --- Child Node Helpers ---\n\n/** Returns true if a block has only inline content (TextNodes and InlineNodes). */\nexport function isLeafBlock(node: BlockNode): boolean {\n\treturn node.children.every((c) => isTextNode(c) || isInlineNode(c));\n}\n\n/** Returns only the TextNode children of a block. */\nexport function getTextChildren(node: BlockNode): readonly TextNode[] {\n\treturn node.children.filter((c): c is TextNode => isTextNode(c));\n}\n\n/** Returns the inline content children (TextNode | InlineNode) of a block. */\nexport function getInlineChildren(node: BlockNode): readonly (TextNode | InlineNode)[] {\n\treturn node.children.filter((c): c is TextNode | InlineNode => isTextNode(c) || isInlineNode(c));\n}\n\n/** Returns only the BlockNode children of a block. */\nexport function getBlockChildren(node: BlockNode): readonly BlockNode[] {\n\treturn node.children.filter((c): c is BlockNode => isBlockNode(c));\n}\n\n// --- Factory Functions ---\n\n/** Generates a unique block ID using crypto.randomUUID(). */\nexport function generateBlockId(): BlockId {\n\treturn `block-${crypto.randomUUID()}` as BlockId;\n}\n\n/** Creates a new empty {@link Document} with a single empty paragraph. */\nexport function createDocument(children?: readonly BlockNode[]): Document {\n\treturn {\n\t\tchildren: children ?? [createBlockNode('paragraph' as NodeTypeName)],\n\t};\n}\n\n/** Creates a new {@link BlockNode}. */\nexport function createBlockNode(\n\ttype: NodeTypeName,\n\tchildren?: readonly ChildNode[],\n\tid?: BlockId,\n\tattrs?: BlockAttrs,\n): BlockNode {\n\treturn {\n\t\tid: id ?? generateBlockId(),\n\t\ttype,\n\t\t...(attrs ? { attrs } : {}),\n\t\tchildren: children ?? [createTextNode('')],\n\t};\n}\n\n/** Creates a new {@link TextNode}. */\nexport function createTextNode(text: string, marks?: readonly Mark[]): TextNode {\n\treturn {\n\t\ttype: 'text',\n\t\ttext,\n\t\tmarks: marks ?? [],\n\t};\n}\n\n/** Creates a new {@link InlineNode}. */\nexport function createInlineNode(\n\tinlineType: InlineTypeName,\n\tattrs?: Readonly<Record<string, string | number | boolean>>,\n): InlineNode {\n\treturn {\n\t\ttype: 'inline',\n\t\tinlineType,\n\t\tattrs: attrs ?? {},\n\t};\n}\n\n// --- Utility Functions ---\n\n/** Extracts plain text from a block (InlineNodes are skipped). */\nexport function getBlockText(block: BlockNode): string {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet text = '';\n\tfor (const child of inlineChildren) {\n\t\tif (isTextNode(child)) {\n\t\t\ttext += child.text;\n\t\t}\n\t}\n\treturn text;\n}\n\n/** Returns the length of a block's inline content (InlineNodes count as 1). */\nexport function getBlockLength(block: BlockNode): number {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet len = 0;\n\tfor (const child of inlineChildren) {\n\t\tlen += isInlineNode(child) ? 1 : child.text.length;\n\t}\n\treturn len;\n}\n\n/** Returns the marks active at the given offset (empty for InlineNode offsets). */\nexport function getBlockMarksAtOffset(block: BlockNode, offset: number): readonly Mark[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (offset === pos) return [];\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst end: number = pos + child.text.length;\n\t\tif (offset >= pos && offset < end) {\n\t\t\treturn child.marks;\n\t\t}\n\t\tif (offset === pos && child.text.length === 0) {\n\t\t\treturn child.marks;\n\t\t}\n\t\tpos = end;\n\t}\n\n\t// Fall back to last text child's marks\n\tfor (let i: number = inlineChildren.length - 1; i >= 0; i--) {\n\t\tconst child: TextNode | InlineNode | undefined = inlineChildren[i];\n\t\tif (child && isTextNode(child)) {\n\t\t\treturn child.marks;\n\t\t}\n\t}\n\treturn [];\n}\n\n/** Checks whether two marks are equal by type and attrs. */\nexport function marksEqual(a: Mark, b: Mark): boolean {\n\tif (a.type !== b.type) return false;\n\tconst aAttrs = a.attrs;\n\tconst bAttrs = b.attrs;\n\tif (!aAttrs && !bAttrs) return true;\n\tif (!aAttrs || !bAttrs) return false;\n\tconst aKeys = Object.keys(aAttrs);\n\tconst bKeys = Object.keys(bAttrs);\n\tif (aKeys.length !== bKeys.length) return false;\n\treturn aKeys.every((key) => aAttrs[key] === bAttrs[key]);\n}\n\n/** Checks whether two mark arrays contain the same marks (order-independent). */\nexport function markSetsEqual(a: readonly Mark[], b: readonly Mark[]): boolean {\n\tif (a.length !== b.length) return false;\n\treturn a.every((markA) => b.some((markB) => marksEqual(markA, markB)));\n}\n\n/** Returns true if the mark set contains a mark of the given type. */\nexport function hasMark(marks: readonly Mark[], markType: MarkTypeName): boolean {\n\treturn marks.some((m) => m.type === markType);\n}\n\n/** Adds a mark to a mark set (no duplicates). */\nexport function addMarkToSet(marks: readonly Mark[], mark: Mark): readonly Mark[] {\n\tif (hasMark(marks, mark.type)) return marks;\n\treturn [...marks, mark];\n}\n\n/** Removes a mark type from a mark set. */\nexport function removeMarkFromSet(marks: readonly Mark[], markType: MarkTypeName): readonly Mark[] {\n\treturn marks.filter((m) => m.type !== markType);\n}\n\n/**\n * Normalizes text nodes within a block: merges adjacent nodes with identical marks,\n * removes empty text nodes (keeping at least one).\n */\nexport function normalizeTextNodes(nodes: readonly TextNode[]): readonly TextNode[] {\n\tif (nodes.length === 0) return [createTextNode('')];\n\n\tconst result: TextNode[] = [];\n\tfor (const node of nodes) {\n\t\tconst prev: TextNode | undefined = result[result.length - 1];\n\t\tif (prev && markSetsEqual(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = createTextNode(prev.text + node.text, prev.marks);\n\t\t} else if (node.text.length > 0 || result.length === 0) {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\n\treturn result.length === 0 ? [createTextNode('')] : result;\n}\n\n/**\n * Normalizes mixed inline content: merges adjacent TextNodes with same marks,\n * removes empty TextNodes adjacent to InlineNodes, preserves InlineNodes as-is.\n * Guarantees at least one TextNode exists if all text is empty.\n */\nexport function normalizeInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n): readonly (TextNode | InlineNode)[] {\n\tif (nodes.length === 0) return [createTextNode('')];\n\n\t// Fast path: no InlineNodes, delegate to normalizeTextNodes\n\tif (nodes.every((n): n is TextNode => isTextNode(n))) {\n\t\treturn normalizeTextNodes(nodes);\n\t}\n\n\tconst result: (TextNode | InlineNode)[] = [];\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tresult.push(node);\n\t\t\tcontinue;\n\t\t}\n\t\t// TextNode: try to merge with previous TextNode\n\t\tconst prev: TextNode | InlineNode | undefined = result[result.length - 1];\n\t\tif (prev && isTextNode(prev) && markSetsEqual(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = createTextNode(prev.text + node.text, prev.marks);\n\t\t} else if (node.text.length > 0 || result.length === 0) {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\n\t// Remove empty TextNodes that are adjacent to InlineNodes\n\tconst cleaned: (TextNode | InlineNode)[] = result.filter((node, i) => {\n\t\tif (isInlineNode(node)) return true;\n\t\tif (node.text.length > 0) return true;\n\t\t// Keep if it's the only node\n\t\tif (result.length === 1) return true;\n\t\t// Remove empty TextNode if adjacent to InlineNode\n\t\tconst prev: TextNode | InlineNode | undefined = result[i - 1];\n\t\tconst next: TextNode | InlineNode | undefined = result[i + 1];\n\t\tif ((prev && isInlineNode(prev)) || (next && isInlineNode(next))) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\t// Ensure at least one TextNode exists\n\tif (cleaned.length === 0) return [createTextNode('')];\n\tif (!cleaned.some((n) => isTextNode(n))) {\n\t\treturn [createTextNode(''), ...cleaned];\n\t}\n\n\treturn cleaned;\n}\n\n/** Yields each inline child with its offset range. InlineNodes have width 1. */\nexport function* walkInlineContent(children: readonly (TextNode | InlineNode)[]): Generator<{\n\treadonly child: TextNode | InlineNode;\n\treadonly from: number;\n\treadonly to: number;\n}> {\n\tlet pos = 0;\n\tfor (const child of children) {\n\t\tconst width: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tyield { child, from: pos, to: pos + width };\n\t\tpos += width;\n\t}\n}\n\n/**\n * Converts a block-space offset to a text-space offset.\n *\n * Block-space counts InlineNodes as width 1, but `getBlockText()` skips them.\n * This function maps a block offset to the equivalent position in the\n * text-only string returned by `getBlockText()`.\n */\nexport function blockOffsetToTextOffset(block: BlockNode, blockOffset: number): number {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet blockPos = 0;\n\tlet textPos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (blockPos >= blockOffset) return textPos;\n\t\t\tblockPos += 1;\n\t\t} else {\n\t\t\tif (blockPos + child.text.length >= blockOffset) {\n\t\t\t\treturn textPos + (blockOffset - blockPos);\n\t\t\t}\n\t\t\tblockPos += child.text.length;\n\t\t\ttextPos += child.text.length;\n\t\t}\n\t}\n\n\treturn textPos;\n}\n\n/** Returns the content at a specific offset: text char, inline node, or null. */\nexport function getContentAtOffset(\n\tblock: BlockNode,\n\toffset: number,\n):\n\t| { readonly kind: 'text'; readonly char: string; readonly marks: readonly Mark[] }\n\t| { readonly kind: 'inline'; readonly node: InlineNode }\n\t| null {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\n\tfor (const { child, from } of walkInlineContent(inlineChildren)) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (offset === from) return { kind: 'inline', node: child };\n\t\t\tcontinue;\n\t\t}\n\t\tconst localOffset: number = offset - from;\n\t\tif (localOffset >= 0 && localOffset < child.text.length) {\n\t\t\treturn {\n\t\t\t\tkind: 'text',\n\t\t\t\tchar: child.text[localOffset] ?? '',\n\t\t\t\tmarks: child.marks,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn null;\n}\n"],"names":["getBlockSegmentsInRange","block","from","to","inlineChildren","getInlineChildren","segments","pos","child","childEnd","isInlineNode","text","length","sliceFrom","Math","max","sliceTo","min","slice","push","marks","isTextNode","node","type","inlineType","isBlockNode","id","Array","isArray","children","isLeafBlock","every","c","getTextChildren","filter","getBlockChildren","generateBlockId","crypto","randomUUID","createDocument","createBlockNode","attrs","createTextNode","createInlineNode","getBlockText","getBlockLength","len","getBlockMarksAtOffset","offset","end","i","markSetsEqual","a","b","markA","some","markB","aAttrs","bAttrs","aKeys","Object","keys","bKeys","key","hasMark","markType","m","addMarkToSet","mark","removeMarkFromSet","normalizeInlineContent","nodes","n","result","prev","cleaned","next","blockOffsetToTextOffset","blockOffset","blockPos","textPos","getContentAtOffset","width","kind","localOffset","char"],"mappings":"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"}
1
+ {"version":3,"file":"Document-Brtw2jL1.mjs","sources":["../../src/model/Document.ts"],"sourcesContent":["/**\n * Core document model types for the Notectl editor.\n * All types are deeply readonly — mutations create new instances.\n */\n\nimport type { BlockId, InlineTypeName, MarkTypeName, NodeTypeName } from './TypeBrands.js';\n\n// --- Mark Types ---\n\n/** @deprecated Use {@link MarkTypeName} for new code. */\nexport type MarkType = MarkTypeName;\n\nexport interface Mark {\n\treadonly type: MarkTypeName;\n\treadonly attrs?: Readonly<Record<string, string | number | boolean>>;\n}\n\nexport interface BoldMark extends Mark {\n\treadonly type: MarkTypeName & 'bold';\n}\n\nexport interface ItalicMark extends Mark {\n\treadonly type: MarkTypeName & 'italic';\n}\n\nexport interface UnderlineMark extends Mark {\n\treadonly type: MarkTypeName & 'underline';\n}\n\n// --- Node Types ---\n\n/** @deprecated Use {@link NodeTypeName} for new code. */\nexport type NodeType = NodeTypeName;\n\nexport interface BlockAttrs {\n\treadonly [key: string]: string | number | boolean;\n}\n\nexport interface TextNode {\n\treadonly type: 'text';\n\treadonly text: string;\n\treadonly marks: readonly Mark[];\n}\n\n/** Atomic inline element (width 1 in offset space). */\nexport interface InlineNode {\n\treadonly type: 'inline';\n\treadonly inlineType: InlineTypeName;\n\treadonly attrs: Readonly<Record<string, string | number | boolean>>;\n}\n\n/** A child of a BlockNode: text, inline element, or nested block. */\nexport type ChildNode = TextNode | InlineNode | BlockNode;\n\nexport interface BlockNode {\n\treadonly id: BlockId;\n\treadonly type: NodeTypeName;\n\treadonly attrs?: BlockAttrs;\n\treadonly children: readonly ChildNode[];\n}\n\nexport interface Document {\n\treadonly children: readonly BlockNode[];\n}\n\n// --- Text Segment (for mark-preserving undo) ---\n\nexport interface TextSegment {\n\treadonly text: string;\n\treadonly marks: readonly Mark[];\n}\n\n/** A content segment: either text (with marks) or an inline node. */\nexport type ContentSegment =\n\t| { readonly kind: 'text'; readonly text: string; readonly marks: readonly Mark[] }\n\t| { readonly kind: 'inline'; readonly node: InlineNode };\n\n/** Extracts TextNode segments within a block for the given offset range. */\nexport function getBlockSegmentsInRange(\n\tblock: BlockNode,\n\tfrom: number,\n\tto: number,\n): readonly TextSegment[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tconst segments: TextSegment[] = [];\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tconst childWidth: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tconst childEnd: number = pos + childWidth;\n\n\t\tif (childEnd <= from || pos >= to) {\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isInlineNode(child)) {\n\t\t\t// InlineNodes are skipped for TextSegment extraction\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst sliceFrom: number = Math.max(0, from - pos);\n\t\tconst sliceTo: number = Math.min(child.text.length, to - pos);\n\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\n\t\tif (text.length > 0) {\n\t\t\tsegments.push({ text, marks: child.marks });\n\t\t}\n\n\t\tpos = childEnd;\n\t}\n\n\treturn segments;\n}\n\n/** Returns content segments (text and inline) for a block range. */\nexport function getBlockContentSegmentsInRange(\n\tblock: BlockNode,\n\tfrom: number,\n\tto: number,\n): readonly ContentSegment[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tconst segments: ContentSegment[] = [];\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tconst childWidth: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tconst childEnd: number = pos + childWidth;\n\n\t\tif (childEnd <= from || pos >= to) {\n\t\t\tpos = childEnd;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isInlineNode(child)) {\n\t\t\tsegments.push({ kind: 'inline', node: child });\n\t\t} else {\n\t\t\tconst sliceFrom: number = Math.max(0, from - pos);\n\t\t\tconst sliceTo: number = Math.min(child.text.length, to - pos);\n\t\t\tconst text: string = child.text.slice(sliceFrom, sliceTo);\n\t\t\tif (text.length > 0) {\n\t\t\t\tsegments.push({ kind: 'text', text, marks: child.marks });\n\t\t\t}\n\t\t}\n\n\t\tpos = childEnd;\n\t}\n\n\treturn segments;\n}\n\n// --- Type Guards ---\n\n/** Checks whether a value is a {@link TextNode}. */\nexport function isTextNode(node: unknown): node is TextNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\t(node as TextNode).type === 'text' &&\n\t\ttypeof (node as TextNode).text === 'string'\n\t);\n}\n\n/** Checks whether a value is an {@link InlineNode}. */\nexport function isInlineNode(node: unknown): node is InlineNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\t(node as InlineNode).type === 'inline' &&\n\t\ttypeof (node as InlineNode).inlineType === 'string'\n\t);\n}\n\n/** Checks whether a value is a {@link BlockNode}. */\nexport function isBlockNode(node: unknown): node is BlockNode {\n\treturn (\n\t\ttypeof node === 'object' &&\n\t\tnode !== null &&\n\t\ttypeof (node as BlockNode).id === 'string' &&\n\t\ttypeof (node as BlockNode).type === 'string' &&\n\t\t(node as BlockNode).type !== ('text' as string) &&\n\t\t(node as BlockNode).type !== ('inline' as string) &&\n\t\tArray.isArray((node as BlockNode).children)\n\t);\n}\n\n// --- Child Node Helpers ---\n\n/** Returns true if a block has only inline content (TextNodes and InlineNodes). */\nexport function isLeafBlock(node: BlockNode): boolean {\n\treturn node.children.every((c) => isTextNode(c) || isInlineNode(c));\n}\n\n/** Returns only the TextNode children of a block. */\nexport function getTextChildren(node: BlockNode): readonly TextNode[] {\n\treturn node.children.filter((c): c is TextNode => isTextNode(c));\n}\n\n/** Returns the inline content children (TextNode | InlineNode) of a block. */\nexport function getInlineChildren(node: BlockNode): readonly (TextNode | InlineNode)[] {\n\treturn node.children.filter((c): c is TextNode | InlineNode => isTextNode(c) || isInlineNode(c));\n}\n\n/** Returns only the BlockNode children of a block. */\nexport function getBlockChildren(node: BlockNode): readonly BlockNode[] {\n\treturn node.children.filter((c): c is BlockNode => isBlockNode(c));\n}\n\n// --- Factory Functions ---\n\n/** Generates a unique block ID using crypto.randomUUID(). */\nexport function generateBlockId(): BlockId {\n\treturn `block-${crypto.randomUUID()}` as BlockId;\n}\n\n/** Creates a new empty {@link Document} with a single empty paragraph. */\nexport function createDocument(children?: readonly BlockNode[]): Document {\n\treturn {\n\t\tchildren: children ?? [createBlockNode('paragraph' as NodeTypeName)],\n\t};\n}\n\n/** Creates a new {@link BlockNode}. */\nexport function createBlockNode(\n\ttype: NodeTypeName,\n\tchildren?: readonly ChildNode[],\n\tid?: BlockId,\n\tattrs?: BlockAttrs,\n): BlockNode {\n\treturn {\n\t\tid: id ?? generateBlockId(),\n\t\ttype,\n\t\t...(attrs ? { attrs } : {}),\n\t\tchildren: children ?? [createTextNode('')],\n\t};\n}\n\n/** Creates a new {@link TextNode}. */\nexport function createTextNode(text: string, marks?: readonly Mark[]): TextNode {\n\treturn {\n\t\ttype: 'text',\n\t\ttext,\n\t\tmarks: marks ?? [],\n\t};\n}\n\n/** Creates a new {@link InlineNode}. */\nexport function createInlineNode(\n\tinlineType: InlineTypeName,\n\tattrs?: Readonly<Record<string, string | number | boolean>>,\n): InlineNode {\n\treturn {\n\t\ttype: 'inline',\n\t\tinlineType,\n\t\tattrs: attrs ?? {},\n\t};\n}\n\n// --- Utility Functions ---\n\n/** Extracts plain text from a block (InlineNodes are skipped). */\nexport function getBlockText(block: BlockNode): string {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet text = '';\n\tfor (const child of inlineChildren) {\n\t\tif (isTextNode(child)) {\n\t\t\ttext += child.text;\n\t\t}\n\t}\n\treturn text;\n}\n\n/** Returns the length of a block's inline content (InlineNodes count as 1). */\nexport function getBlockLength(block: BlockNode): number {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet len = 0;\n\tfor (const child of inlineChildren) {\n\t\tlen += isInlineNode(child) ? 1 : child.text.length;\n\t}\n\treturn len;\n}\n\n/** Returns the marks active at the given offset (empty for InlineNode offsets). */\nexport function getBlockMarksAtOffset(block: BlockNode, offset: number): readonly Mark[] {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet pos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (offset === pos) return [];\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst end: number = pos + child.text.length;\n\t\tif (offset >= pos && offset < end) {\n\t\t\treturn child.marks;\n\t\t}\n\t\tif (offset === pos && child.text.length === 0) {\n\t\t\treturn child.marks;\n\t\t}\n\t\tpos = end;\n\t}\n\n\t// Fall back to last text child's marks\n\tfor (let i: number = inlineChildren.length - 1; i >= 0; i--) {\n\t\tconst child: TextNode | InlineNode | undefined = inlineChildren[i];\n\t\tif (child && isTextNode(child)) {\n\t\t\treturn child.marks;\n\t\t}\n\t}\n\treturn [];\n}\n\n/** Checks whether two marks are equal by type and attrs. */\nexport function marksEqual(a: Mark, b: Mark): boolean {\n\tif (a.type !== b.type) return false;\n\tconst aAttrs = a.attrs;\n\tconst bAttrs = b.attrs;\n\tif (!aAttrs && !bAttrs) return true;\n\tif (!aAttrs || !bAttrs) return false;\n\tconst aKeys = Object.keys(aAttrs);\n\tconst bKeys = Object.keys(bAttrs);\n\tif (aKeys.length !== bKeys.length) return false;\n\treturn aKeys.every((key) => aAttrs[key] === bAttrs[key]);\n}\n\n/** Checks whether two mark arrays contain the same marks (order-independent). */\nexport function markSetsEqual(a: readonly Mark[], b: readonly Mark[]): boolean {\n\tif (a.length !== b.length) return false;\n\treturn a.every((markA) => b.some((markB) => marksEqual(markA, markB)));\n}\n\n/** Returns true if the mark set contains a mark of the given type. */\nexport function hasMark(marks: readonly Mark[], markType: MarkTypeName): boolean {\n\treturn marks.some((m) => m.type === markType);\n}\n\n/** Adds a mark to a mark set (no duplicates). */\nexport function addMarkToSet(marks: readonly Mark[], mark: Mark): readonly Mark[] {\n\tif (hasMark(marks, mark.type)) return marks;\n\treturn [...marks, mark];\n}\n\n/** Removes a mark type from a mark set. */\nexport function removeMarkFromSet(marks: readonly Mark[], markType: MarkTypeName): readonly Mark[] {\n\treturn marks.filter((m) => m.type !== markType);\n}\n\n/**\n * Normalizes text nodes within a block: merges adjacent nodes with identical marks,\n * removes empty text nodes (keeping at least one).\n */\nexport function normalizeTextNodes(nodes: readonly TextNode[]): readonly TextNode[] {\n\tif (nodes.length === 0) return [createTextNode('')];\n\n\tconst result: TextNode[] = [];\n\tfor (const node of nodes) {\n\t\tconst prev: TextNode | undefined = result[result.length - 1];\n\t\tif (prev && markSetsEqual(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = createTextNode(prev.text + node.text, prev.marks);\n\t\t} else if (node.text.length > 0 || result.length === 0) {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\n\treturn result.length === 0 ? [createTextNode('')] : result;\n}\n\n/**\n * Normalizes mixed inline content: merges adjacent TextNodes with same marks,\n * removes empty TextNodes adjacent to InlineNodes, preserves InlineNodes as-is.\n * Guarantees at least one TextNode exists if all text is empty.\n */\nexport function normalizeInlineContent(\n\tnodes: readonly (TextNode | InlineNode)[],\n): readonly (TextNode | InlineNode)[] {\n\tif (nodes.length === 0) return [createTextNode('')];\n\n\t// Fast path: no InlineNodes, delegate to normalizeTextNodes\n\tif (nodes.every((n): n is TextNode => isTextNode(n))) {\n\t\treturn normalizeTextNodes(nodes);\n\t}\n\n\tconst result: (TextNode | InlineNode)[] = [];\n\n\tfor (const node of nodes) {\n\t\tif (isInlineNode(node)) {\n\t\t\tresult.push(node);\n\t\t\tcontinue;\n\t\t}\n\t\t// TextNode: try to merge with previous TextNode\n\t\tconst prev: TextNode | InlineNode | undefined = result[result.length - 1];\n\t\tif (prev && isTextNode(prev) && markSetsEqual(prev.marks, node.marks)) {\n\t\t\tresult[result.length - 1] = createTextNode(prev.text + node.text, prev.marks);\n\t\t} else if (node.text.length > 0 || result.length === 0) {\n\t\t\tresult.push(node);\n\t\t}\n\t}\n\n\t// Remove empty TextNodes that are adjacent to InlineNodes\n\tconst cleaned: (TextNode | InlineNode)[] = result.filter((node, i) => {\n\t\tif (isInlineNode(node)) return true;\n\t\tif (node.text.length > 0) return true;\n\t\t// Keep if it's the only node\n\t\tif (result.length === 1) return true;\n\t\t// Remove empty TextNode if adjacent to InlineNode\n\t\tconst prev: TextNode | InlineNode | undefined = result[i - 1];\n\t\tconst next: TextNode | InlineNode | undefined = result[i + 1];\n\t\tif ((prev && isInlineNode(prev)) || (next && isInlineNode(next))) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t});\n\n\t// Ensure at least one TextNode exists\n\tif (cleaned.length === 0) return [createTextNode('')];\n\tif (!cleaned.some((n) => isTextNode(n))) {\n\t\treturn [createTextNode(''), ...cleaned];\n\t}\n\n\treturn cleaned;\n}\n\n/** Yields each inline child with its offset range. InlineNodes have width 1. */\nexport function* walkInlineContent(children: readonly (TextNode | InlineNode)[]): Generator<{\n\treadonly child: TextNode | InlineNode;\n\treadonly from: number;\n\treadonly to: number;\n}> {\n\tlet pos = 0;\n\tfor (const child of children) {\n\t\tconst width: number = isInlineNode(child) ? 1 : child.text.length;\n\t\tyield { child, from: pos, to: pos + width };\n\t\tpos += width;\n\t}\n}\n\n/**\n * Converts a block-space offset to a text-space offset.\n *\n * Block-space counts InlineNodes as width 1, but `getBlockText()` skips them.\n * This function maps a block offset to the equivalent position in the\n * text-only string returned by `getBlockText()`.\n */\nexport function blockOffsetToTextOffset(block: BlockNode, blockOffset: number): number {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet blockPos = 0;\n\tlet textPos = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (blockPos >= blockOffset) return textPos;\n\t\t\tblockPos += 1;\n\t\t} else {\n\t\t\tif (blockPos + child.text.length >= blockOffset) {\n\t\t\t\treturn textPos + (blockOffset - blockPos);\n\t\t\t}\n\t\t\tblockPos += child.text.length;\n\t\t\ttextPos += child.text.length;\n\t\t}\n\t}\n\n\treturn textPos;\n}\n\n/** Returns the content at a specific offset: text char, inline node, or null. */\nexport function getContentAtOffset(\n\tblock: BlockNode,\n\toffset: number,\n):\n\t| { readonly kind: 'text'; readonly char: string; readonly marks: readonly Mark[] }\n\t| { readonly kind: 'inline'; readonly node: InlineNode }\n\t| null {\n\tconst inlineChildren: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\n\tfor (const { child, from } of walkInlineContent(inlineChildren)) {\n\t\tif (isInlineNode(child)) {\n\t\t\tif (offset === from) return { kind: 'inline', node: child };\n\t\t\tcontinue;\n\t\t}\n\t\tconst localOffset: number = offset - from;\n\t\tif (localOffset >= 0 && localOffset < child.text.length) {\n\t\t\treturn {\n\t\t\t\tkind: 'text',\n\t\t\t\tchar: child.text[localOffset] ?? '',\n\t\t\t\tmarks: child.marks,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn null;\n}\n"],"names":["getBlockSegmentsInRange","block","from","to","inlineChildren","getInlineChildren","segments","pos","child","childEnd","isInlineNode","text","length","sliceFrom","Math","max","sliceTo","min","slice","push","marks","isTextNode","node","type","inlineType","isBlockNode","id","Array","isArray","children","isLeafBlock","every","c","getTextChildren","filter","getBlockChildren","generateBlockId","crypto","randomUUID","createDocument","createBlockNode","attrs","createTextNode","createInlineNode","getBlockText","getBlockLength","len","getBlockMarksAtOffset","offset","end","i","markSetsEqual","a","b","markA","some","markB","aAttrs","bAttrs","aKeys","Object","keys","bKeys","key","hasMark","markType","m","addMarkToSet","mark","removeMarkFromSet","normalizeInlineContent","nodes","n","result","prev","cleaned","next","blockOffsetToTextOffset","blockOffset","blockPos","textPos","getContentAtOffset","width","kind","localOffset","char"],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import { h as e, g as t, c as n, a as r, i as o } from "./Document-Brtw2jL1.mjs";
2
+ import { i as s, a as c, b as i, s as a } from "./Selection-BRTab5hO.mjs";
3
+ import { m as u } from "./TypeBrands-C09nE2qO.mjs";
4
+ import { f } from "./RangeIterator-Bh4Vl_6D.mjs";
5
+ function l() {
6
+ return { nodeTypes: ["paragraph"], markTypes: ["bold", "italic", "underline"], getNodeSpec: () => {
7
+ } };
8
+ }
9
+ function d(e2) {
10
+ return { nodeTypes: e2.getNodeTypes(), markTypes: e2.getMarkTypes(), getNodeSpec: (t2) => e2.getNodeSpec(t2) };
11
+ }
12
+ function m(e2, t2) {
13
+ return e2.markTypes.includes(t2);
14
+ }
15
+ function k(e2, t2) {
16
+ return e2.nodeTypes.includes(t2);
17
+ }
18
+ const p = { bold: true, italic: true, underline: true };
19
+ function b(n2, r2, o2 = p) {
20
+ if ((function(e2, t2) {
21
+ const n3 = e2;
22
+ return "bold" === n3 ? !t2.bold : "italic" === n3 ? !t2.italic : "underline" === n3 && !t2.underline;
23
+ })(r2, o2)) return null;
24
+ if (!m(n2.schema, r2)) return null;
25
+ if (s(n2.selection) || c(n2.selection)) return null;
26
+ const u2 = { type: r2 }, l2 = n2.selection;
27
+ if (i(l2)) {
28
+ const o3 = n2.getBlock(l2.anchor.blockId);
29
+ if (!o3) return null;
30
+ const s2 = n2.storedMarks ?? t(o3, l2.anchor.offset), c2 = e(s2, r2) ? s2.filter((e2) => e2.type !== r2) : [...s2, u2];
31
+ return n2.transaction("command").setStoredMarks(c2, n2.storedMarks).setSelection(l2).build();
32
+ }
33
+ const d2 = a(l2, n2.getBlockOrder()), k2 = n2.transaction("command"), b2 = M(n2, r2);
34
+ return f(n2, d2, (e2, t2, n3) => {
35
+ b2 ? k2.removeMark(e2, t2, n3, u2) : k2.addMark(e2, t2, n3, u2);
36
+ }), k2.setSelection(l2), k2.build();
37
+ }
38
+ function g(e2, t2) {
39
+ return b(e2, u("bold"), t2);
40
+ }
41
+ function y(e2, t2) {
42
+ return b(e2, u("italic"), t2);
43
+ }
44
+ function T(e2, t2) {
45
+ return b(e2, u("underline"), t2);
46
+ }
47
+ function h(n2, r2) {
48
+ const o2 = n2.selection;
49
+ if (s(o2) || c(o2)) return false;
50
+ if (i(o2)) {
51
+ if (n2.storedMarks) return e(n2.storedMarks, r2);
52
+ const s2 = n2.getBlock(o2.anchor.blockId);
53
+ if (!s2) return false;
54
+ const c2 = t(s2, o2.anchor.offset);
55
+ return e(c2, r2);
56
+ }
57
+ return M(n2, r2);
58
+ }
59
+ function M(e2, t2) {
60
+ const r2 = e2.selection;
61
+ if (s(r2) || c(r2)) return false;
62
+ const o2 = e2.getBlockOrder(), i2 = a(r2, o2), u2 = o2.indexOf(i2.from.blockId), f2 = o2.indexOf(i2.to.blockId);
63
+ for (let r3 = u2; r3 <= f2; r3++) {
64
+ const s2 = o2[r3];
65
+ if (!s2) continue;
66
+ const c2 = e2.getBlock(s2);
67
+ if (!c2) continue;
68
+ const a2 = n(c2);
69
+ if (!S(c2, r3 === u2 ? i2.from.offset : 0, r3 === f2 ? i2.to.offset : a2, t2)) return false;
70
+ }
71
+ return true;
72
+ }
73
+ function S(t2, n2, s2, c2) {
74
+ if (n2 === s2) return false;
75
+ let i2 = 0;
76
+ for (const a2 of r(t2)) if (o(a2)) {
77
+ const t3 = i2 + a2.text.length;
78
+ if (t3 > n2 && i2 < s2 && !e(a2.marks, c2)) return false;
79
+ i2 = t3;
80
+ } else i2 += 1;
81
+ return true;
82
+ }
83
+ export {
84
+ k as a,
85
+ h as b,
86
+ y as c,
87
+ l as d,
88
+ b as e,
89
+ T as f,
90
+ m as i,
91
+ d as s,
92
+ g as t
93
+ };
94
+ //# sourceMappingURL=MarkCommands-6RUJ4L3r.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkCommands-6RUJ4L3r.mjs","sources":["../../src/model/Schema.ts","../../src/commands/MarkCommands.ts"],"sourcesContent":["/**\n * Schema definition for the Notectl editor.\n * Defines which node types and marks are allowed.\n */\n\nimport type { NodeSpec } from './NodeSpec.js';\nimport type { SchemaRegistry } from './SchemaRegistry.js';\n\nexport interface Schema {\n\treadonly nodeTypes: readonly string[];\n\treadonly markTypes: readonly string[];\n\t/** Looks up the full NodeSpec for a given type. Available when created via schemaFromRegistry. */\n\treadonly getNodeSpec?: (type: string) => NodeSpec | undefined;\n}\n\n/** Creates the default schema with paragraph nodes and bold/italic/underline marks. */\nexport function defaultSchema(): Schema {\n\treturn {\n\t\tnodeTypes: ['paragraph'],\n\t\tmarkTypes: ['bold', 'italic', 'underline'],\n\t\tgetNodeSpec: () => undefined,\n\t};\n}\n\n/** Derives a Schema from a SchemaRegistry's registered specs. */\nexport function schemaFromRegistry(registry: SchemaRegistry): Schema {\n\treturn {\n\t\tnodeTypes: registry.getNodeTypes(),\n\t\tmarkTypes: registry.getMarkTypes(),\n\t\tgetNodeSpec: (type: string) => registry.getNodeSpec(type),\n\t};\n}\n\n/** Checks whether the given mark type is allowed by the schema. */\nexport function isMarkAllowed(schema: Schema, markType: string): boolean {\n\treturn schema.markTypes.includes(markType);\n}\n\n/** Checks whether the given node type is allowed by the schema. */\nexport function isNodeTypeAllowed(schema: Schema, nodeType: string): boolean {\n\treturn schema.nodeTypes.includes(nodeType);\n}\n","/**\n * Mark-related commands: toggling bold/italic/underline and checking\n * whether a mark is active.\n */\n\nimport {\n\ttype BlockNode,\n\ttype Mark,\n\ttype MarkType,\n\tgetBlockLength,\n\tgetBlockMarksAtOffset,\n\tgetInlineChildren,\n\thasMark,\n\tisTextNode,\n} from '../model/Document.js';\nimport { isMarkAllowed } from '../model/Schema.js';\nimport { isCollapsed, isGapCursor, isNodeSelection, selectionRange } from '../model/Selection.js';\nimport { markType as mkType } 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// --- Feature Configuration ---\n\nexport interface FeatureConfig {\n\treadonly bold: boolean;\n\treadonly italic: boolean;\n\treadonly underline: boolean;\n}\n\nconst defaultFeatures: FeatureConfig = { bold: true, italic: true, underline: true };\n\n/**\n * Toggles a mark on the current selection.\n * If collapsed, toggles stored marks. If range, applies/removes from text.\n */\nexport function toggleMark(\n\tstate: EditorState,\n\tmarkType: MarkType,\n\tfeatures: FeatureConfig = defaultFeatures,\n): Transaction | null {\n\tif (isFeatureGated(markType, features)) return null;\n\tif (!isMarkAllowed(state.schema, markType)) return null;\n\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return null;\n\n\tconst mark: Mark = { type: markType };\n\tconst sel = state.selection;\n\n\tif (isCollapsed(sel)) {\n\t\t// Toggle stored marks\n\t\tconst anchorBlock = state.getBlock(sel.anchor.blockId);\n\t\tif (!anchorBlock) return null;\n\t\tconst currentMarks = state.storedMarks ?? getBlockMarksAtOffset(anchorBlock, sel.anchor.offset);\n\t\tconst hasIt = hasMark(currentMarks, markType);\n\t\tconst newMarks = hasIt\n\t\t\t? currentMarks.filter((m) => m.type !== markType)\n\t\t\t: [...currentMarks, 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\t// Range selection — apply/remove mark to all blocks in range\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\n\t// Determine if we should add or remove\n\tconst shouldRemove = isMarkActiveInRange(state, markType);\n\n\tforEachBlockInRange(state, range, (blockId, from, to) => {\n\t\tif (shouldRemove) {\n\t\t\tbuilder.removeMark(blockId, from, to, mark);\n\t\t} else {\n\t\t\tbuilder.addMark(blockId, from, to, mark);\n\t\t}\n\t});\n\n\tbuilder.setSelection(sel);\n\treturn builder.build();\n}\n\nexport function toggleBold(state: EditorState, features?: FeatureConfig): Transaction | null {\n\treturn toggleMark(state, mkType('bold'), features);\n}\n\nexport function toggleItalic(state: EditorState, features?: FeatureConfig): Transaction | null {\n\treturn toggleMark(state, mkType('italic'), features);\n}\n\nexport function toggleUnderline(state: EditorState, features?: FeatureConfig): Transaction | null {\n\treturn toggleMark(state, mkType('underline'), features);\n}\n\n/** Checks if a mark is active at the current selection. */\nexport function isMarkActive(state: EditorState, markType: MarkType): boolean {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\n\tif (isCollapsed(sel)) {\n\t\tif (state.storedMarks) {\n\t\t\treturn hasMark(state.storedMarks, markType);\n\t\t}\n\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\tif (!block) return false;\n\t\tconst marks = getBlockMarksAtOffset(block, sel.anchor.offset);\n\t\treturn hasMark(marks, markType);\n\t}\n\n\treturn isMarkActiveInRange(state, markType);\n}\n\n/** Checks if a mark is active across the entire selection range. */\nfunction isMarkActiveInRange(state: EditorState, markType: MarkType): boolean {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\tconst blockOrder = state.getBlockOrder();\n\tconst range = selectionRange(sel, blockOrder);\n\n\tconst fromIdx = blockOrder.indexOf(range.from.blockId);\n\tconst toIdx = blockOrder.indexOf(range.to.blockId);\n\n\tfor (let i = fromIdx; i <= toIdx; i++) {\n\t\tconst blockId = blockOrder[i];\n\t\tif (!blockId) continue;\n\t\tconst block = state.getBlock(blockId);\n\t\tif (!block) continue;\n\t\tconst blockLen = getBlockLength(block);\n\t\tconst from = i === fromIdx ? range.from.offset : 0;\n\t\tconst to = i === toIdx ? range.to.offset : blockLen;\n\n\t\tif (!isMarkActiveInBlock(block, from, to, markType)) return false;\n\t}\n\n\treturn true;\n}\n\nfunction isMarkActiveInBlock(\n\tblock: BlockNode,\n\tfrom: number,\n\tto: number,\n\tmarkType: MarkType,\n): boolean {\n\tif (from === to) return false;\n\tlet pos = 0;\n\tfor (const child of getInlineChildren(block)) {\n\t\tif (isTextNode(child)) {\n\t\t\tconst childEnd = pos + child.text.length;\n\t\t\tif (childEnd > from && pos < to) {\n\t\t\t\tif (!hasMark(child.marks, markType)) return false;\n\t\t\t}\n\t\t\tpos = childEnd;\n\t\t} else {\n\t\t\t// InlineNode: skip (width 1, no marks)\n\t\t\tpos += 1;\n\t\t}\n\t}\n\treturn true;\n}\n\nfunction isFeatureGated(type: MarkType, features: FeatureConfig): boolean {\n\tconst key = type as string;\n\tif (key === 'bold') return !features.bold;\n\tif (key === 'italic') return !features.italic;\n\tif (key === 'underline') return !features.underline;\n\treturn false;\n}\n"],"names":["defaultSchema","nodeTypes","markTypes","getNodeSpec","schemaFromRegistry","registry","getNodeTypes","getMarkTypes","type","isMarkAllowed","schema","markType","includes","isNodeTypeAllowed","nodeType","defaultFeatures","bold","italic","underline","toggleMark","state","features","key","isNodeSelection","selection","isGapCursor","mark","sel","isCollapsed","anchorBlock","getBlock","anchor","blockId","currentMarks","storedMarks","getBlockMarksAtOffset","offset","newMarks","hasMark","filter","m","transaction","setStoredMarks","setSelection","build","range","selectionRange","getBlockOrder","builder","shouldRemove","isMarkActiveInRange","forEachBlockInRange","from","to","removeMark","addMark","toggleBold","mkType","toggleItalic","toggleUnderline","isMarkActive","block","marks","blockOrder","fromIdx","indexOf","toIdx","i","blockLen","getBlockLength","isMarkActiveInBlock","pos","child","getInlineChildren","isTextNode","childEnd","text","length"],"mappings":"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"}
@@ -38,10 +38,10 @@ function r(t2, r2, c) {
38
38
  return i === s && (!i || !s || e(t2, r2, c));
39
39
  }
40
40
  export {
41
- o as a,
42
- n as b,
41
+ n as a,
42
+ o as b,
43
43
  r as c,
44
44
  t as i,
45
45
  e as s
46
46
  };
47
- //# sourceMappingURL=NavigationUtils-CyW0Vo8A.mjs.map
47
+ //# sourceMappingURL=NavigationUtils-Cspt2ZrK.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationUtils-CyW0Vo8A.mjs","sources":["../../src/model/NavigationUtils.ts"],"sourcesContent":["/**\n * Pure navigation query functions for block boundary and isolating checks.\n *\n * These utilities inspect the document structure and schema to answer\n * questions about block traversal — whether a block is void, isolating,\n * or whether navigation between two blocks is allowed.\n */\n\nimport type { EditorState } from '../state/EditorState.js';\nimport type { BlockId } from './TypeBrands.js';\n\n/** Returns true if the block with the given ID is a void block (e.g. image, HR). */\nexport function isVoidBlock(state: EditorState, bid: BlockId): boolean {\n\tconst block = state.getBlock(bid);\n\tif (!block) return false;\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\treturn getNodeSpec(block.type)?.isVoid === true;\n}\n\n/** Checks whether two blocks share the same parent in the document tree. */\nexport function sharesParent(state: EditorState, blockIdA: BlockId, blockIdB: BlockId): boolean {\n\tconst pathA = state.getNodePath(blockIdA);\n\tconst pathB = state.getNodePath(blockIdB);\n\tif (!pathA || !pathB) return false;\n\tif (pathA.length !== pathB.length) return false;\n\t// Compare parent paths (all but last element)\n\tfor (let i = 0; i < pathA.length - 1; i++) {\n\t\tif (pathA[i] !== pathB[i]) return false;\n\t}\n\treturn true;\n}\n\n/** Checks whether a block is inside an isolating node (e.g. table_cell). */\nexport function isInsideIsolating(state: EditorState, blockId: BlockId): boolean {\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\tconst path = state.getNodePath(blockId);\n\tif (!path || path.length <= 1) return false;\n\n\t// Check ancestors (not the block itself)\n\tfor (let i = 0; i < path.length - 1; i++) {\n\t\tconst ancestorId = path[i];\n\t\tif (!ancestorId) continue;\n\t\tconst ancestor = state.getBlock(ancestorId);\n\t\tif (!ancestor) continue;\n\t\tconst spec = getNodeSpec(ancestor.type);\n\t\tif (spec?.isolating) return true;\n\t}\n\treturn false;\n}\n\n/** Returns true if the block itself has the isolating property. */\nexport function isIsolatingBlock(state: EditorState, blockId: BlockId): boolean {\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return false;\n\treturn getNodeSpec(block.type)?.isolating === true;\n}\n\n/**\n * Checks whether navigation between two blocks is allowed.\n * Prevents crossing isolating boundaries (e.g. table cells).\n */\nexport function canCrossBlockBoundary(state: EditorState, fromId: BlockId, toId: BlockId): boolean {\n\t// A block that is itself isolating cannot be crossed into via arrow keys\n\tif (isIsolatingBlock(state, fromId) || isIsolatingBlock(state, toId)) return false;\n\n\tconst fromInside: boolean = isInsideIsolating(state, fromId);\n\tconst toInside: boolean = isInsideIsolating(state, toId);\n\n\t// One inside isolating, the other not → disallow\n\tif (fromInside !== toInside) return false;\n\n\t// Both inside isolating → must share the same parent\n\tif (fromInside && toInside) return sharesParent(state, fromId, toId);\n\n\treturn true;\n}\n"],"names":["isVoidBlock","state","bid","block","getBlock","getNodeSpec","schema","type","isVoid","sharesParent","blockIdA","blockIdB","pathA","getNodePath","pathB","length","i","isInsideIsolating","blockId","path","ancestorId","ancestor","spec","isolating","isIsolatingBlock","canCrossBlockBoundary","fromId","toId","fromInside","toInside"],"mappings":"AAYO,SAASA,EAAYC,IAAoBC,IAAAA;AAC/C,QAAMC,KAAQF,GAAMG,SAASF;AAC7B,MAAA,CAAKC,GAAO;AACZ,QAAME,KAAcJ,GAAMK,OAAOD;AACjC,SAAA,CAAA,CAAKA,MAAAA,SACEA,GAAYF,GAAMI,IAAAA,GAAOC;AACjC;AAGO,SAASC,EAAaR,IAAoBS,IAAmBC;AACnE,QAAMC,KAAQX,GAAMY,YAAYH,EAAAA,GAC1BI,KAAQb,GAAMY,YAAYF;AAChC,MAAA,CAAKC,MAAAA,CAAUE,GAAO,QAAA;AACtB,MAAIF,GAAMG,WAAWD,GAAMC,OAAQ,QAAA;AAEnC,WAASC,KAAI,GAAGA,KAAIJ,GAAMG,SAAS,GAAGC,KACrC,KAAIJ,GAAMI,EAAAA,MAAOF,GAAME,EAAAA,EAAI,QAAA;AAE5B,SAAA;AACD;AAGO,SAASC,EAAkBhB,IAAoBiB;AACrD,QAAMb,KAAcJ,GAAMK,OAAOD;AACjC,OAAKA,GAAa,QAAA;AAClB,QAAMc,KAAOlB,GAAMY,YAAYK,EAAAA;AAC/B,OAAKC,MAAQA,GAAKJ,UAAU,EAAG,QAAA;AAG/B,WAASC,KAAI,GAAGA,KAAIG,GAAKJ,SAAS,GAAGC,MAAK;AACzC,UAAMI,KAAaD,GAAKH,EAAAA;AACxB,QAAA,CAAKI,GAAY;AACjB,UAAMC,IAAWpB,GAAMG,SAASgB;AAChC,QAAA,CAAKC,EAAU;AACf,UAAMC,IAAOjB,GAAYgB,EAASd,IAAAA;AAClC,QAAIe,GAAMC,UAAW,QAAA;AAAA,EACtB;AACA,SAAA;AACD;AAGO,SAASC,EAAiBvB,IAAoBiB,IAAAA;AACpD,QAAMb,KAAcJ,GAAMK,OAAOD;AACjC,MAAA,CAAKA,GAAa,QAAA;AAClB,QAAMF,KAAQF,GAAMG,SAASc;AAC7B,SAAA,CAAA,CAAKf,MAAAA,SACEE,GAAYF,GAAMI,OAAOgB;AACjC;AAMO,SAASE,EAAsBxB,IAAoByB,IAAiBC,GAAAA;AAE1E,MAAIH,EAAiBvB,IAAOyB,EAAAA,KAAWF,EAAiBvB,IAAO0B,CAAAA,EAAO,QAAA;AAEtE,QAAMC,IAAsBX,EAAkBhB,IAAOyB,EAAAA,GAC/CG,IAAoBZ,EAAkBhB,IAAO0B;AAGnD,SAAIC,MAAeC,OAGfD,KAAAA,CAAcC,KAAiBpB,EAAaR,IAAOyB,IAAQC;AAGhE;"}
1
+ {"version":3,"file":"NavigationUtils-Cspt2ZrK.mjs","sources":["../../src/model/NavigationUtils.ts"],"sourcesContent":["/**\n * Pure navigation query functions for block boundary and isolating checks.\n *\n * These utilities inspect the document structure and schema to answer\n * questions about block traversal — whether a block is void, isolating,\n * or whether navigation between two blocks is allowed.\n */\n\nimport type { EditorState } from '../state/EditorState.js';\nimport type { BlockId } from './TypeBrands.js';\n\n/** Returns true if the block with the given ID is a void block (e.g. image, HR). */\nexport function isVoidBlock(state: EditorState, bid: BlockId): boolean {\n\tconst block = state.getBlock(bid);\n\tif (!block) return false;\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\treturn getNodeSpec(block.type)?.isVoid === true;\n}\n\n/** Checks whether two blocks share the same parent in the document tree. */\nexport function sharesParent(state: EditorState, blockIdA: BlockId, blockIdB: BlockId): boolean {\n\tconst pathA = state.getNodePath(blockIdA);\n\tconst pathB = state.getNodePath(blockIdB);\n\tif (!pathA || !pathB) return false;\n\tif (pathA.length !== pathB.length) return false;\n\t// Compare parent paths (all but last element)\n\tfor (let i = 0; i < pathA.length - 1; i++) {\n\t\tif (pathA[i] !== pathB[i]) return false;\n\t}\n\treturn true;\n}\n\n/** Checks whether a block is inside an isolating node (e.g. table_cell). */\nexport function isInsideIsolating(state: EditorState, blockId: BlockId): boolean {\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\tconst path = state.getNodePath(blockId);\n\tif (!path || path.length <= 1) return false;\n\n\t// Check ancestors (not the block itself)\n\tfor (let i = 0; i < path.length - 1; i++) {\n\t\tconst ancestorId = path[i];\n\t\tif (!ancestorId) continue;\n\t\tconst ancestor = state.getBlock(ancestorId);\n\t\tif (!ancestor) continue;\n\t\tconst spec = getNodeSpec(ancestor.type);\n\t\tif (spec?.isolating) return true;\n\t}\n\treturn false;\n}\n\n/** Returns true if the block itself has the isolating property. */\nexport function isIsolatingBlock(state: EditorState, blockId: BlockId): boolean {\n\tconst getNodeSpec = state.schema.getNodeSpec;\n\tif (!getNodeSpec) return false;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return false;\n\treturn getNodeSpec(block.type)?.isolating === true;\n}\n\n/**\n * Checks whether navigation between two blocks is allowed.\n * Prevents crossing isolating boundaries (e.g. table cells).\n */\nexport function canCrossBlockBoundary(state: EditorState, fromId: BlockId, toId: BlockId): boolean {\n\t// A block that is itself isolating cannot be crossed into via arrow keys\n\tif (isIsolatingBlock(state, fromId) || isIsolatingBlock(state, toId)) return false;\n\n\tconst fromInside: boolean = isInsideIsolating(state, fromId);\n\tconst toInside: boolean = isInsideIsolating(state, toId);\n\n\t// One inside isolating, the other not → disallow\n\tif (fromInside !== toInside) return false;\n\n\t// Both inside isolating → must share the same parent\n\tif (fromInside && toInside) return sharesParent(state, fromId, toId);\n\n\treturn true;\n}\n"],"names":["isVoidBlock","state","bid","block","getBlock","getNodeSpec","schema","type","isVoid","sharesParent","blockIdA","blockIdB","pathA","getNodePath","pathB","length","i","isInsideIsolating","blockId","path","ancestorId","ancestor","spec","isolating","isIsolatingBlock","canCrossBlockBoundary","fromId","toId","fromInside","toInside"],"mappings":"AAYO,SAASA,EAAYC,IAAoBC,IAAAA;AAC/C,QAAMC,KAAQF,GAAMG,SAASF;AAC7B,MAAA,CAAKC,GAAO;AACZ,QAAME,KAAcJ,GAAMK,OAAOD;AACjC,SAAA,CAAA,CAAKA,MAAAA,SACEA,GAAYF,GAAMI,IAAAA,GAAOC;AACjC;AAGO,SAASC,EAAaR,IAAoBS,IAAmBC;AACnE,QAAMC,KAAQX,GAAMY,YAAYH,EAAAA,GAC1BI,KAAQb,GAAMY,YAAYF;AAChC,MAAA,CAAKC,MAAAA,CAAUE,GAAO,QAAA;AACtB,MAAIF,GAAMG,WAAWD,GAAMC,OAAQ,QAAA;AAEnC,WAASC,KAAI,GAAGA,KAAIJ,GAAMG,SAAS,GAAGC,KACrC,KAAIJ,GAAMI,EAAAA,MAAOF,GAAME,EAAAA,EAAI,QAAA;AAE5B,SAAA;AACD;AAGO,SAASC,EAAkBhB,IAAoBiB;AACrD,QAAMb,KAAcJ,GAAMK,OAAOD;AACjC,OAAKA,GAAa,QAAA;AAClB,QAAMc,KAAOlB,GAAMY,YAAYK,EAAAA;AAC/B,OAAKC,MAAQA,GAAKJ,UAAU,EAAG,QAAA;AAG/B,WAASC,KAAI,GAAGA,KAAIG,GAAKJ,SAAS,GAAGC,MAAK;AACzC,UAAMI,KAAaD,GAAKH,EAAAA;AACxB,QAAA,CAAKI,GAAY;AACjB,UAAMC,IAAWpB,GAAMG,SAASgB;AAChC,QAAA,CAAKC,EAAU;AACf,UAAMC,IAAOjB,GAAYgB,EAASd,IAAAA;AAClC,QAAIe,GAAMC,UAAW,QAAA;AAAA,EACtB;AACA,SAAA;AACD;AAGO,SAASC,EAAiBvB,IAAoBiB,IAAAA;AACpD,QAAMb,KAAcJ,GAAMK,OAAOD;AACjC,MAAA,CAAKA,GAAa,QAAA;AAClB,QAAMF,KAAQF,GAAMG,SAASc;AAC7B,SAAA,CAAA,CAAKf,MAAAA,SACEE,GAAYF,GAAMI,OAAOgB;AACjC;AAMO,SAASE,EAAsBxB,IAAoByB,IAAiBC,GAAAA;AAE1E,MAAIH,EAAiBvB,IAAOyB,EAAAA,KAAWF,EAAiBvB,IAAO0B,CAAAA,EAAO,QAAA;AAEtE,QAAMC,IAAsBX,EAAkBhB,IAAOyB,EAAAA,GAC/CG,IAAoBZ,EAAkBhB,IAAO0B;AAGnD,SAAIC,MAAeC,OAGfD,KAAAA,CAAcC,KAAiBpB,EAAaR,IAAOyB,IAAQC;AAGhE;"}
@@ -1,4 +1,4 @@
1
- import { a as n } from "./Document-CuwE73FF.mjs";
1
+ import { d as n } from "./Document-Brtw2jL1.mjs";
2
2
  function i(i2, r2) {
3
3
  if (0 === r2.length) return;
4
4
  let t2 = i2.children.find((n2) => n2.id === r2[0]);
@@ -78,4 +78,4 @@ export {
78
78
  i as r,
79
79
  o as w
80
80
  };
81
- //# sourceMappingURL=NodeResolver-B-wzthAQ.mjs.map
81
+ //# sourceMappingURL=NodeResolver-C2XYkeOi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeResolver-B-wzthAQ.mjs","sources":["../../src/model/NodeResolver.ts"],"sourcesContent":["/**\n * Utilities for resolving nodes by path within the document tree.\n * Supports both flat and recursive (nested) document structures.\n */\n\nimport type { BlockNode, Document } from './Document.js';\nimport { isBlockNode } from './Document.js';\n\n/**\n * Resolves a node in the document tree by its path (array of block IDs).\n * The path traces the lineage from the root block down to the target.\n */\nexport function resolveNodeByPath(doc: Document, path: readonly string[]): BlockNode | undefined {\n\tif (path.length === 0) return undefined;\n\n\tlet current: BlockNode | undefined = doc.children.find((b) => b.id === path[0]);\n\tif (!current) return undefined;\n\n\tfor (let i = 1; i < path.length; i++) {\n\t\tconst childId = path[i];\n\t\tif (!childId) return undefined;\n\t\tconst next: BlockNode | undefined = current?.children.find(\n\t\t\t(c): c is BlockNode => isBlockNode(c) && c.id === childId,\n\t\t);\n\t\tif (!next) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Resolves the parent of a node at the given path.\n * Returns the parent container (Document or BlockNode) and the child's index within it.\n */\nexport function resolveParentByPath(\n\tdoc: Document,\n\tpath: readonly string[],\n): { parent: Document | BlockNode; index: number } | undefined {\n\tif (path.length === 0) return undefined;\n\n\tif (path.length === 1) {\n\t\tconst index = doc.children.findIndex((b) => b.id === path[0]);\n\t\tif (index === -1) return undefined;\n\t\treturn { parent: doc, index };\n\t}\n\n\tconst parentPath = path.slice(0, -1);\n\tconst parent = resolveNodeByPath(doc, parentPath);\n\tif (!parent) return undefined;\n\n\tconst childId = path[path.length - 1];\n\tif (!childId) return undefined;\n\tconst index = parent.children.findIndex(\n\t\t(c): c is BlockNode => isBlockNode(c) && c.id === childId,\n\t);\n\tif (index === -1) return undefined;\n\n\treturn { parent, index };\n}\n\n/**\n * Finds the path (array of block IDs) to a node by its ID.\n * Performs recursive DFS through the document tree.\n */\nexport function findNodePath(doc: Document, nodeId: string): string[] | undefined {\n\tfor (const block of doc.children) {\n\t\tif (block.id === nodeId) return [nodeId];\n\n\t\tconst subPath = findNodePathInBlock(block, nodeId);\n\t\tif (subPath) return [block.id, ...subPath];\n\t}\n\treturn undefined;\n}\n\nfunction findNodePathInBlock(block: BlockNode, nodeId: string): string[] | undefined {\n\tfor (const child of block.children) {\n\t\tif (!isBlockNode(child)) continue;\n\t\tif (child.id === nodeId) return [nodeId];\n\n\t\tconst subPath = findNodePathInBlock(child, nodeId);\n\t\tif (subPath) return [child.id, ...subPath];\n\t}\n\treturn undefined;\n}\n\n/**\n * Walks all block nodes in the document tree in depth-first order.\n * Visits every BlockNode, including nested ones, with their full path.\n */\nexport function walkNodes(\n\tdoc: Document,\n\tcallback: (node: BlockNode, path: string[]) => void,\n): void {\n\tfor (const block of doc.children) {\n\t\twalkNodeRecursive(block, [block.id], callback);\n\t}\n}\n\nfunction walkNodeRecursive(\n\tnode: BlockNode,\n\tpath: string[],\n\tcallback: (node: BlockNode, path: string[]) => void,\n): void {\n\tcallback(node, path);\n\tfor (const child of node.children) {\n\t\tif (isBlockNode(child)) {\n\t\t\twalkNodeRecursive(child, [...path, child.id], callback);\n\t\t}\n\t}\n}\n\n/**\n * Finds a block node by ID anywhere in the document tree (recursive DFS).\n */\nexport function findNode(doc: Document, nodeId: string): BlockNode | undefined {\n\tfor (const block of doc.children) {\n\t\tif (block.id === nodeId) return block;\n\t\tconst found = findNodeInBlock(block, nodeId);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\nfunction findNodeInBlock(block: BlockNode, nodeId: string): BlockNode | undefined {\n\tfor (const child of block.children) {\n\t\tif (!isBlockNode(child)) continue;\n\t\tif (child.id === nodeId) return child;\n\t\tconst found = findNodeInBlock(child, nodeId);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\n/**\n * Finds a block node and returns it together with its path.\n */\nexport function findNodeWithPath(\n\tdoc: Document,\n\tnodeId: string,\n): { node: BlockNode; path: string[] } | undefined {\n\tconst path = findNodePath(doc, nodeId);\n\tif (!path) return undefined;\n\tconst node = resolveNodeByPath(doc, path);\n\tif (!node) return undefined;\n\treturn { node, path };\n}\n"],"names":["resolveNodeByPath","doc","path","length","current","children","find","b","id","i","childId","next","c","isBlockNode","resolveParentByPath","index","findIndex","parent","slice","findNodePath","nodeId","block","subPath","findNodePathInBlock","child","walkNodes","callback","walkNodeRecursive","node","findNode","found","findNodeInBlock","findNodeWithPath"],"mappings":"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"}
1
+ {"version":3,"file":"NodeResolver-C2XYkeOi.mjs","sources":["../../src/model/NodeResolver.ts"],"sourcesContent":["/**\n * Utilities for resolving nodes by path within the document tree.\n * Supports both flat and recursive (nested) document structures.\n */\n\nimport type { BlockNode, Document } from './Document.js';\nimport { isBlockNode } from './Document.js';\n\n/**\n * Resolves a node in the document tree by its path (array of block IDs).\n * The path traces the lineage from the root block down to the target.\n */\nexport function resolveNodeByPath(doc: Document, path: readonly string[]): BlockNode | undefined {\n\tif (path.length === 0) return undefined;\n\n\tlet current: BlockNode | undefined = doc.children.find((b) => b.id === path[0]);\n\tif (!current) return undefined;\n\n\tfor (let i = 1; i < path.length; i++) {\n\t\tconst childId = path[i];\n\t\tif (!childId) return undefined;\n\t\tconst next: BlockNode | undefined = current?.children.find(\n\t\t\t(c): c is BlockNode => isBlockNode(c) && c.id === childId,\n\t\t);\n\t\tif (!next) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Resolves the parent of a node at the given path.\n * Returns the parent container (Document or BlockNode) and the child's index within it.\n */\nexport function resolveParentByPath(\n\tdoc: Document,\n\tpath: readonly string[],\n): { parent: Document | BlockNode; index: number } | undefined {\n\tif (path.length === 0) return undefined;\n\n\tif (path.length === 1) {\n\t\tconst index = doc.children.findIndex((b) => b.id === path[0]);\n\t\tif (index === -1) return undefined;\n\t\treturn { parent: doc, index };\n\t}\n\n\tconst parentPath = path.slice(0, -1);\n\tconst parent = resolveNodeByPath(doc, parentPath);\n\tif (!parent) return undefined;\n\n\tconst childId = path[path.length - 1];\n\tif (!childId) return undefined;\n\tconst index = parent.children.findIndex(\n\t\t(c): c is BlockNode => isBlockNode(c) && c.id === childId,\n\t);\n\tif (index === -1) return undefined;\n\n\treturn { parent, index };\n}\n\n/**\n * Finds the path (array of block IDs) to a node by its ID.\n * Performs recursive DFS through the document tree.\n */\nexport function findNodePath(doc: Document, nodeId: string): string[] | undefined {\n\tfor (const block of doc.children) {\n\t\tif (block.id === nodeId) return [nodeId];\n\n\t\tconst subPath = findNodePathInBlock(block, nodeId);\n\t\tif (subPath) return [block.id, ...subPath];\n\t}\n\treturn undefined;\n}\n\nfunction findNodePathInBlock(block: BlockNode, nodeId: string): string[] | undefined {\n\tfor (const child of block.children) {\n\t\tif (!isBlockNode(child)) continue;\n\t\tif (child.id === nodeId) return [nodeId];\n\n\t\tconst subPath = findNodePathInBlock(child, nodeId);\n\t\tif (subPath) return [child.id, ...subPath];\n\t}\n\treturn undefined;\n}\n\n/**\n * Walks all block nodes in the document tree in depth-first order.\n * Visits every BlockNode, including nested ones, with their full path.\n */\nexport function walkNodes(\n\tdoc: Document,\n\tcallback: (node: BlockNode, path: string[]) => void,\n): void {\n\tfor (const block of doc.children) {\n\t\twalkNodeRecursive(block, [block.id], callback);\n\t}\n}\n\nfunction walkNodeRecursive(\n\tnode: BlockNode,\n\tpath: string[],\n\tcallback: (node: BlockNode, path: string[]) => void,\n): void {\n\tcallback(node, path);\n\tfor (const child of node.children) {\n\t\tif (isBlockNode(child)) {\n\t\t\twalkNodeRecursive(child, [...path, child.id], callback);\n\t\t}\n\t}\n}\n\n/**\n * Finds a block node by ID anywhere in the document tree (recursive DFS).\n */\nexport function findNode(doc: Document, nodeId: string): BlockNode | undefined {\n\tfor (const block of doc.children) {\n\t\tif (block.id === nodeId) return block;\n\t\tconst found = findNodeInBlock(block, nodeId);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\nfunction findNodeInBlock(block: BlockNode, nodeId: string): BlockNode | undefined {\n\tfor (const child of block.children) {\n\t\tif (!isBlockNode(child)) continue;\n\t\tif (child.id === nodeId) return child;\n\t\tconst found = findNodeInBlock(child, nodeId);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\n/**\n * Finds a block node and returns it together with its path.\n */\nexport function findNodeWithPath(\n\tdoc: Document,\n\tnodeId: string,\n): { node: BlockNode; path: string[] } | undefined {\n\tconst path = findNodePath(doc, nodeId);\n\tif (!path) return undefined;\n\tconst node = resolveNodeByPath(doc, path);\n\tif (!node) return undefined;\n\treturn { node, path };\n}\n"],"names":["resolveNodeByPath","doc","path","length","current","children","find","b","id","i","childId","next","c","isBlockNode","resolveParentByPath","index","findIndex","parent","slice","findNodePath","nodeId","block","subPath","findNodePathInBlock","child","walkNodes","callback","walkNodeRecursive","node","findNode","found","findNodeInBlock","findNodeWithPath"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { s as t } from "./StyleRuntime-ChRjVQwL.mjs";
2
+ function o(o2, e2, i) {
3
+ const f = i.offset ?? 2, s = window.innerWidth, n = window.innerHeight, h = { position: "fixed", zIndex: "10000" };
4
+ switch (i.position) {
5
+ case "below-start": {
6
+ let t2 = e2.bottom + f, i2 = e2.left;
7
+ i2 + o2.offsetWidth > s - 4 && (i2 = s - o2.offsetWidth - 4), t2 + o2.offsetHeight > n - 4 && (t2 = e2.top - o2.offsetHeight - f), i2 < 4 && (i2 = 4), t2 < 4 && (t2 = 4), h.top = `${t2}px`, h.left = `${i2}px`, h.right = "auto";
8
+ break;
9
+ }
10
+ case "below-end": {
11
+ let t2 = e2.bottom + f;
12
+ const i2 = s - e2.right;
13
+ t2 + o2.offsetHeight > n - 4 && (t2 = e2.top - o2.offsetHeight - f), t2 < 4 && (t2 = 4), h.top = `${t2}px`, h.right = `${i2}px`, h.left = "auto";
14
+ break;
15
+ }
16
+ case "right": {
17
+ let t2 = e2.right + f, i2 = e2.top;
18
+ t2 + o2.offsetWidth > s - 4 && (t2 = e2.left - o2.offsetWidth - f), i2 + o2.offsetHeight > n - 4 && (i2 = n - o2.offsetHeight - 4), t2 < 4 && (t2 = 4), i2 < 4 && (i2 = 4), h.top = `${i2}px`, h.left = `${t2}px`, h.right = "auto";
19
+ break;
20
+ }
21
+ }
22
+ t(o2, h);
23
+ }
24
+ function e(t2, o2) {
25
+ const e2 = o2.getRootNode();
26
+ e2 instanceof ShadowRoot ? e2.appendChild(t2) : document.body.appendChild(t2);
27
+ }
28
+ export {
29
+ e as a,
30
+ o as p
31
+ };
32
+ //# sourceMappingURL=PopupPositioning-BTT-_pQ3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PopupPositioning-BTT-_pQ3.mjs","sources":["../../src/plugins/shared/PopupPositioning.ts"],"sourcesContent":["/**\n * Shared popup positioning logic. Positions a popup element relative to an\n * anchor rectangle, clamping to the viewport. Also provides a shadow DOM-aware\n * append helper.\n */\n\nimport { setStyleProperties } from '../../style/StyleRuntime.js';\n\n// --- Types ---\n\nexport type PopupPosition = 'below-start' | 'below-end' | 'right';\n\nexport interface PositionOptions {\n\treadonly position: PopupPosition;\n\treadonly offset?: number;\n}\n\n// --- Constants ---\n\nconst DEFAULT_OFFSET = 2;\nconst Z_INDEX = '10000';\nconst POPUP_MIN_MARGIN = 4;\n\n// --- Positioning ---\n\n/**\n * Positions a popup element relative to the given anchor rectangle.\n * Uses fixed positioning and clamps to the viewport edges.\n */\nexport function positionPopup(popup: HTMLElement, anchor: DOMRect, options: PositionOptions): void {\n\tconst offset: number = options.offset ?? DEFAULT_OFFSET;\n\tconst vpWidth: number = window.innerWidth;\n\tconst vpHeight: number = window.innerHeight;\n\tconst styles: Record<string, string> = {\n\t\tposition: 'fixed',\n\t\tzIndex: Z_INDEX,\n\t};\n\n\tswitch (options.position) {\n\t\tcase 'below-start': {\n\t\t\tlet top: number = anchor.bottom + offset;\n\t\t\tlet left: number = anchor.left;\n\n\t\t\tif (left + popup.offsetWidth > vpWidth - POPUP_MIN_MARGIN) {\n\t\t\t\tleft = vpWidth - popup.offsetWidth - POPUP_MIN_MARGIN;\n\t\t\t}\n\t\t\tif (top + popup.offsetHeight > vpHeight - POPUP_MIN_MARGIN) {\n\t\t\t\ttop = anchor.top - popup.offsetHeight - offset;\n\t\t\t}\n\t\t\tif (left < POPUP_MIN_MARGIN) left = POPUP_MIN_MARGIN;\n\t\t\tif (top < POPUP_MIN_MARGIN) top = POPUP_MIN_MARGIN;\n\t\t\tstyles.top = `${top}px`;\n\t\t\tstyles.left = `${left}px`;\n\t\t\tstyles.right = 'auto';\n\t\t\tbreak;\n\t\t}\n\t\tcase 'below-end': {\n\t\t\tlet top: number = anchor.bottom + offset;\n\t\t\tconst rightEdge: number = vpWidth - anchor.right;\n\n\t\t\tif (top + popup.offsetHeight > vpHeight - POPUP_MIN_MARGIN) {\n\t\t\t\ttop = anchor.top - popup.offsetHeight - offset;\n\t\t\t}\n\t\t\tif (top < POPUP_MIN_MARGIN) top = POPUP_MIN_MARGIN;\n\t\t\tstyles.top = `${top}px`;\n\t\t\tstyles.right = `${rightEdge}px`;\n\t\t\tstyles.left = 'auto';\n\t\t\tbreak;\n\t\t}\n\t\tcase 'right': {\n\t\t\tlet left: number = anchor.right + offset;\n\t\t\tlet top: number = anchor.top;\n\n\t\t\tif (left + popup.offsetWidth > vpWidth - POPUP_MIN_MARGIN) {\n\t\t\t\tleft = anchor.left - popup.offsetWidth - offset;\n\t\t\t}\n\t\t\tif (top + popup.offsetHeight > vpHeight - POPUP_MIN_MARGIN) {\n\t\t\t\ttop = vpHeight - popup.offsetHeight - POPUP_MIN_MARGIN;\n\t\t\t}\n\t\t\tif (left < POPUP_MIN_MARGIN) left = POPUP_MIN_MARGIN;\n\t\t\tif (top < POPUP_MIN_MARGIN) top = POPUP_MIN_MARGIN;\n\t\t\tstyles.top = `${top}px`;\n\t\t\tstyles.left = `${left}px`;\n\t\t\tstyles.right = 'auto';\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tsetStyleProperties(popup, styles);\n}\n\n/**\n * Appends an element to the appropriate root: the shadow root if the\n * reference node lives inside one, otherwise `document.body`.\n */\nexport function appendToRoot(element: HTMLElement, referenceNode: Node): void {\n\tconst root: Node = referenceNode.getRootNode();\n\tif (root instanceof ShadowRoot) {\n\t\troot.appendChild(element);\n\t} else {\n\t\tdocument.body.appendChild(element);\n\t}\n}\n"],"names":["positionPopup","popup","anchor","options","offset","vpWidth","window","innerWidth","vpHeight","innerHeight","styles","position","zIndex","top","bottom","left","offsetWidth","offsetHeight","right","rightEdge","setStyleProperties","appendToRoot","element","referenceNode","root","getRootNode","ShadowRoot","appendChild","document","body"],"mappings":";AA6BO,SAASA,EAAcC,IAAoBC,IAAiBC,GAAAA;AAClE,QAAMC,IAAiBD,EAAQC,UAXT,GAYhBC,IAAkBC,OAAOC,YACzBC,IAAmBF,OAAOG,aAC1BC,IAAiC,EACtCC,UAAU,SACVC,QAfc;AAkBf,UAAQT,EAAQQ;IACf,KAAK,eAAe;AACnB,UAAIE,KAAcX,GAAOY,SAASV,GAC9BW,KAAeb,GAAOa;AAEtBA,MAAAA,KAAOd,GAAMe,cAAcX,IAtBT,MAuBrBU,KAAOV,IAAUJ,GAAMe,cAvBF,IAyBlBH,KAAMZ,GAAMgB,eAAeT,IAzBT,MA0BrBK,KAAMX,GAAOW,MAAMZ,GAAMgB,eAAeb,IAErCW,KA5BkB,MA4BOA,KA5BP,IA6BlBF,KA7BkB,MA6BMA,KA7BN,IA8BtBH,EAAOG,MAAM,GAAGA,EAAAA,MAChBH,EAAOK,OAAO,GAAGA,EAAAA,MACjBL,EAAOQ,QAAQ;AACf;AAAA,IACD;AAAA,IACA,KAAK,aAAa;AACjB,UAAIL,KAAcX,GAAOY,SAASV;AAClC,YAAMe,KAAoBd,IAAUH,GAAOgB;AAEvCL,MAAAA,KAAMZ,GAAMgB,eAAeT,IAvCT,MAwCrBK,KAAMX,GAAOW,MAAMZ,GAAMgB,eAAeb,IAErCS,KA1CkB,MA0CMA,KA1CN,IA2CtBH,EAAOG,MAAM,GAAGA,EAAAA,MAChBH,EAAOQ,QAAQ,GAAGC,EAAAA,MAClBT,EAAOK,OAAO;AACd;AAAA,IACD;AAAA,IACA,KAAK,SAAS;AACb,UAAIA,KAAeb,GAAOgB,QAAQd,GAC9BS,KAAcX,GAAOW;AAErBE,MAAAA,KAAOd,GAAMe,cAAcX,IApDT,MAqDrBU,KAAOb,GAAOa,OAAOd,GAAMe,cAAcZ,IAEtCS,KAAMZ,GAAMgB,eAAeT,IAvDT,MAwDrBK,KAAML,IAAWP,GAAMgB,eAxDF,IA0DlBF,KA1DkB,MA0DOA,KA1DP,IA2DlBF,KA3DkB,MA2DMA,KA3DN,IA4DtBH,EAAOG,MAAM,GAAGA,EAAAA,MAChBH,EAAOK,OAAO,GAAGA,EAAAA,MACjBL,EAAOQ,QAAQ;AACf;AAAA,IACD;AAAA;AAGDE,IAAmBnB,IAAOS;AAC3B;AAMO,SAASW,EAAaC,IAAsBC,IAAAA;AAClD,QAAMC,KAAaD,GAAcE;AAC7BD,EAAAA,cAAgBE,aACnBF,GAAKG,YAAYL,EAAAA,IAEjBM,SAASC,KAAKF,YAAYL,EAAAA;AAE5B;"}
@@ -0,0 +1,16 @@
1
+ import { c as o } from "./Document-Brtw2jL1.mjs";
2
+ function t(t2, f, c) {
3
+ const n = t2.getBlockOrder(), e = n.indexOf(f.from.blockId), s = n.indexOf(f.to.blockId);
4
+ for (let i = e; i <= s; i++) {
5
+ const r = n[i];
6
+ if (!r) continue;
7
+ const m = t2.getBlock(r);
8
+ if (!m) continue;
9
+ const d = i === e ? f.from.offset : 0, l = i === s ? f.to.offset : o(m);
10
+ d !== l && c(r, d, l);
11
+ }
12
+ }
13
+ export {
14
+ t as f
15
+ };
16
+ //# sourceMappingURL=RangeIterator-Bh4Vl_6D.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RangeIterator-Bh4Vl_6D.mjs","sources":["../../src/commands/RangeIterator.ts"],"sourcesContent":["/**\n * Utility for iterating over blocks within a selection range.\n *\n * Extracted from Commands.ts to break circular dependencies:\n * Commands.ts re-exports from MarkCommands.ts / AttributedMarkCommands.ts,\n * which in turn import forEachBlockInRange from Commands.ts.\n */\n\nimport { getBlockLength } from '../model/Document.js';\nimport type { SelectionRange } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\n\n/**\n * Iterates over each block in the given selection range, invoking the callback\n * with the block ID, per-block start offset, and per-block end offset.\n * Blocks where `from === to` are skipped automatically.\n */\nexport function forEachBlockInRange(\n\tstate: EditorState,\n\trange: SelectionRange,\n\tcallback: (blockId: BlockId, from: number, to: number) => void,\n): void {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst fromIdx: number = blockOrder.indexOf(range.from.blockId);\n\tconst toIdx: number = blockOrder.indexOf(range.to.blockId);\n\n\tfor (let i: number = fromIdx; i <= toIdx; i++) {\n\t\tconst blockId: BlockId | undefined = blockOrder[i];\n\t\tif (!blockId) continue;\n\t\tconst block = state.getBlock(blockId);\n\t\tif (!block) continue;\n\n\t\tconst from: number = i === fromIdx ? range.from.offset : 0;\n\t\tconst to: number = i === toIdx ? range.to.offset : getBlockLength(block);\n\n\t\tif (from === to) continue;\n\t\tcallback(blockId, from, to);\n\t}\n}\n"],"names":["forEachBlockInRange","state","range","callback","blockOrder","getBlockOrder","fromIdx","indexOf","from","blockId","toIdx","to","i","block","getBlock","offset","getBlockLength"],"mappings":";AAkBO,SAASA,EACfC,IACAC,GACAC,GAAAA;AAEA,QAAMC,IAAiCH,GAAMI,cAAAA,GACvCC,IAAkBF,EAAWG,QAAQL,EAAMM,KAAKC,OAAAA,GAChDC,IAAgBN,EAAWG,QAAQL,EAAMS,GAAGF,OAAAA;AAElD,WAASG,IAAYN,GAASM,KAAKF,GAAOE,KAAK;AAC9C,UAAMH,IAA+BL,EAAWQ,CAAAA;AAChD,QAAA,CAAKH,EAAS;AACd,UAAMI,IAAQZ,GAAMa,SAASL,CAAAA;AAC7B,QAAA,CAAKI,EAAO;AAEZ,UAAML,IAAeI,MAAMN,IAAUJ,EAAMM,KAAKO,SAAS,GACnDJ,IAAaC,MAAMF,IAAQR,EAAMS,GAAGI,SAASC,EAAeH,CAAAA;AAE9DL,UAASG,KACbR,EAASM,GAASD,GAAMG,CAAAA;AAAAA,EACzB;AACD;"}
@@ -38,15 +38,15 @@ function u(o2, n2) {
38
38
  export {
39
39
  t as a,
40
40
  h as b,
41
- f as c,
42
- n as d,
41
+ r as c,
42
+ f as d,
43
43
  o as e,
44
- r as f,
45
- d as g,
46
- c as h,
44
+ d as f,
45
+ c as g,
46
+ a as h,
47
47
  e as i,
48
- a as j,
48
+ n as j,
49
49
  s as k,
50
50
  u as s
51
51
  };
52
- //# sourceMappingURL=Selection-DmFDwiPJ.mjs.map
52
+ //# sourceMappingURL=Selection-BRTab5hO.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Selection-DmFDwiPJ.mjs","sources":["../../src/model/Selection.ts"],"sourcesContent":["/**\n * Selection model for the Notectl editor.\n * A selection is defined by an anchor and head position within the document.\n */\n\nimport type { BlockId } from './TypeBrands.js';\n\nexport interface Position {\n\treadonly blockId: BlockId;\n\treadonly offset: number;\n\t/** Path from root block to leaf block (optional, for nested structures). */\n\treadonly path?: readonly BlockId[];\n}\n\nexport interface Selection {\n\treadonly anchor: Position;\n\treadonly head: Position;\n}\n\nexport interface SelectionRange {\n\treadonly from: Position;\n\treadonly to: Position;\n}\n\n/** A selection that selects an entire node (e.g. void blocks, table selection). */\nexport interface NodeSelection {\n\treadonly type: 'node';\n\treadonly nodeId: BlockId;\n\treadonly path: readonly BlockId[];\n}\n\n/** A virtual cursor at the boundary of a void block where no native caret can exist. */\nexport interface GapCursorSelection {\n\treadonly type: 'gap';\n\treadonly side: 'before' | 'after';\n\treadonly blockId: BlockId;\n\treadonly path: readonly BlockId[];\n}\n\n/** Union type representing a text selection, node selection, or gap cursor. */\nexport type EditorSelection = Selection | NodeSelection | GapCursorSelection;\n\n/** Creates a NodeSelection for the given block. */\nexport function createNodeSelection(nodeId: BlockId, path: readonly BlockId[]): NodeSelection {\n\treturn { type: 'node', nodeId, path };\n}\n\n/** Creates a GapCursorSelection at the boundary of a void block. */\nexport function createGapCursor(\n\tblockId: BlockId,\n\tside: 'before' | 'after',\n\tpath: readonly BlockId[],\n): GapCursorSelection {\n\treturn { type: 'gap', side, blockId, path };\n}\n\n/** Type guard: returns true if the selection is a NodeSelection. */\nexport function isNodeSelection(sel: EditorSelection): sel is NodeSelection {\n\treturn 'type' in sel && sel.type === 'node';\n}\n\n/** Type guard: returns true if the selection is a GapCursorSelection. */\nexport function isGapCursor(sel: EditorSelection): sel is GapCursorSelection {\n\treturn 'type' in sel && sel.type === 'gap';\n}\n\n/** Type guard: returns true if the selection is a text Selection. */\nexport function isTextSelection(sel: EditorSelection): sel is Selection {\n\treturn !('type' in sel);\n}\n\n/** Compares two EditorSelections for equality. */\nexport function selectionsEqual(a: EditorSelection, b: EditorSelection): boolean {\n\tif (isNodeSelection(a) && isNodeSelection(b)) {\n\t\treturn a.nodeId === b.nodeId;\n\t}\n\tif (isGapCursor(a) && isGapCursor(b)) {\n\t\treturn a.blockId === b.blockId && a.side === b.side;\n\t}\n\tif (isTextSelection(a) && isTextSelection(b)) {\n\t\treturn (\n\t\t\ta.anchor.blockId === b.anchor.blockId &&\n\t\t\ta.anchor.offset === b.anchor.offset &&\n\t\t\ta.head.blockId === b.head.blockId &&\n\t\t\ta.head.offset === b.head.offset\n\t\t);\n\t}\n\treturn false;\n}\n\n/** Creates a Position, optionally with a path. */\nexport function createPosition(\n\tblockId: BlockId,\n\toffset: number,\n\tpath?: readonly BlockId[],\n): Position {\n\treturn path ? { blockId, offset, path } : { blockId, offset };\n}\n\n/** Creates a selection with distinct anchor and head. */\nexport function createSelection(anchor: Position, head: Position): Selection {\n\treturn { anchor, head };\n}\n\n/** Creates a collapsed selection (cursor) at the given position. */\nexport function createCollapsedSelection(blockId: BlockId, offset: number): Selection {\n\tconst pos: Position = { blockId, offset };\n\treturn { anchor: pos, head: pos };\n}\n\n/** Returns true if the selection is collapsed (cursor with no range). NodeSelection and GapCursor are never collapsed. */\nexport function isCollapsed(sel: EditorSelection): boolean {\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\treturn sel.anchor.blockId === sel.head.blockId && sel.anchor.offset === sel.head.offset;\n}\n\n/**\n * Returns true if the selection direction is forward (anchor before head).\n * When anchor and head are in the same block, compares offsets.\n * Cross-block ordering uses document order (not determinable here — caller provides block order).\n * For NodeSelection, always returns true.\n */\nexport function isForward(sel: EditorSelection, blockOrder?: readonly BlockId[]): boolean {\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return true;\n\tif (sel.anchor.blockId === sel.head.blockId) {\n\t\treturn sel.anchor.offset <= sel.head.offset;\n\t}\n\tif (blockOrder) {\n\t\tconst anchorIdx = blockOrder.indexOf(sel.anchor.blockId);\n\t\tconst headIdx = blockOrder.indexOf(sel.head.blockId);\n\t\treturn anchorIdx <= headIdx;\n\t}\n\treturn true;\n}\n\n/**\n * Returns a normalized range where `from` is always before `to`.\n * Throws for NodeSelection — use isNodeSelection() guard first.\n */\nexport function selectionRange(sel: Selection, blockOrder?: readonly BlockId[]): SelectionRange {\n\tif (isForward(sel, blockOrder)) {\n\t\treturn { from: sel.anchor, to: sel.head };\n\t}\n\treturn { from: sel.head, to: sel.anchor };\n}\n"],"names":["createNodeSelection","nodeId","path","type","createGapCursor","blockId","side","isNodeSelection","sel","isGapCursor","isTextSelection","selectionsEqual","a","b","anchor","offset","head","createPosition","createSelection","createCollapsedSelection","pos","isCollapsed","isForward","blockOrder","indexOf","selectionRange","from","to"],"mappings":"AA2CO,SAASA,EAAoBC,IAAiBC,IAAAA;AACpD,SAAO,EAAEC,MAAM,QAAQF,QAAAA,IAAQC,MAAAA,GAAAA;AAChC;AAGO,SAASE,EACfC,IACAC,IACAJ,IAAAA;AAEA,SAAO,EAAEC,MAAM,OAAOG,MAAAA,IAAMD,SAAAA,IAASH,MAAAA,GAAAA;AACtC;AAGO,SAASK,EAAgBC,IAAAA;AAC/B,SAAO,UAAUA,MAAoB,WAAbA,GAAIL;AAC7B;AAGO,SAASM,EAAYD,IAAAA;AAC3B,SAAO,UAAUA,MAAoB,UAAbA,GAAIL;AAC7B;AAGO,SAASO,EAAgBF,IAAAA;AAC/B,SAAA,EAAS,UAAUA;AACpB;AAGO,SAASG,EAAgBC,IAAoBC,IAAAA;AACnD,SAAIN,EAAgBK,OAAML,EAAgBM,EAAAA,IAClCD,GAAEX,WAAWY,GAAEZ,SAEnBQ,EAAYG,EAAAA,KAAMH,EAAYI,EAAAA,IAC1BD,GAAEP,YAAYQ,GAAER,WAAWO,GAAEN,SAASO,GAAEP,OAAAA,EAAAA,CAE5CI,EAAgBE,EAAAA,KAAAA,CAAMF,EAAgBG,EAAAA,MAExCD,GAAEE,OAAOT,YAAYQ,GAAEC,OAAOT,WAC9BO,GAAEE,OAAOC,WAAWF,GAAEC,OAAOC,UAC7BH,GAAEI,KAAKX,YAAYQ,GAAEG,KAAKX,WAC1BO,GAAEI,KAAKD,WAAWF,GAAEG,KAAKD;AAI5B;AAGO,SAASE,EACfZ,IACAU,IACAb,IAAAA;AAEA,SAAOA,KAAO,EAAEG,SAAAA,IAASU,QAAAA,IAAQb,MAAAA,GAAAA,IAAS,EAAEG,SAAAA,IAASU,QAAAA,GAAAA;AACtD;AAGO,SAASG,EAAgBJ,IAAkBE;AACjD,SAAO,EAAEF,QAAAA,IAAQE,MAAAA,GAAAA;AAClB;AAGO,SAASG,EAAyBd,IAAkBU,IAAAA;AAC1D,QAAMK,KAAgB,EAAEf,SAAAA,IAASU,QAAAA,GAAAA;AACjC,SAAO,EAAED,QAAQM,IAAKJ,MAAMI,GAAAA;AAC7B;AAGO,SAASC,EAAYb,IAAAA;AAC3B,SAAA,CAAID,EAAgBC,EAAAA,KAAAA,CAAQC,EAAYD,EAAAA,KACjCA,GAAIM,OAAOT,YAAYG,GAAIQ,KAAKX,WAAWG,GAAIM,OAAOC,WAAWP,GAAIQ,KAAKD;AAClF;AAQO,SAASO,EAAUd,IAAsBe,IAAAA;AAC/C,SAAA,EAAA,CAAIhB,EAAgBC,EAAAA,KAAAA,CAAQC,EAAYD,EAAAA,OACpCA,GAAIM,OAAOT,YAAYG,GAAIQ,KAAKX,UAC5BG,GAAIM,OAAOC,UAAUP,GAAIQ,KAAKD,SAAAA,CAElCQ,MACeA,GAAWC,QAAQhB,GAAIM,OAAOT,OAAAA,KAChCkB,GAAWC,QAAQhB,GAAIQ,KAAKX,OAAAA;AAI9C;AAMO,SAASoB,EAAejB,IAAgBe,IAAAA;AAC9C,SAAID,EAAUd,IAAKe,EAAAA,IACX,EAAEG,MAAMlB,GAAIM,QAAQa,IAAInB,GAAIQ,KAAAA,IAE7B,EAAEU,MAAMlB,GAAIQ,MAAMW,IAAInB,GAAIM,OAAAA;AAClC;"}
1
+ {"version":3,"file":"Selection-BRTab5hO.mjs","sources":["../../src/model/Selection.ts"],"sourcesContent":["/**\n * Selection model for the Notectl editor.\n * A selection is defined by an anchor and head position within the document.\n */\n\nimport type { BlockId } from './TypeBrands.js';\n\nexport interface Position {\n\treadonly blockId: BlockId;\n\treadonly offset: number;\n\t/** Path from root block to leaf block (optional, for nested structures). */\n\treadonly path?: readonly BlockId[];\n}\n\nexport interface Selection {\n\treadonly anchor: Position;\n\treadonly head: Position;\n}\n\nexport interface SelectionRange {\n\treadonly from: Position;\n\treadonly to: Position;\n}\n\n/** A selection that selects an entire node (e.g. void blocks, table selection). */\nexport interface NodeSelection {\n\treadonly type: 'node';\n\treadonly nodeId: BlockId;\n\treadonly path: readonly BlockId[];\n}\n\n/** A virtual cursor at the boundary of a void block where no native caret can exist. */\nexport interface GapCursorSelection {\n\treadonly type: 'gap';\n\treadonly side: 'before' | 'after';\n\treadonly blockId: BlockId;\n\treadonly path: readonly BlockId[];\n}\n\n/** Union type representing a text selection, node selection, or gap cursor. */\nexport type EditorSelection = Selection | NodeSelection | GapCursorSelection;\n\n/** Creates a NodeSelection for the given block. */\nexport function createNodeSelection(nodeId: BlockId, path: readonly BlockId[]): NodeSelection {\n\treturn { type: 'node', nodeId, path };\n}\n\n/** Creates a GapCursorSelection at the boundary of a void block. */\nexport function createGapCursor(\n\tblockId: BlockId,\n\tside: 'before' | 'after',\n\tpath: readonly BlockId[],\n): GapCursorSelection {\n\treturn { type: 'gap', side, blockId, path };\n}\n\n/** Type guard: returns true if the selection is a NodeSelection. */\nexport function isNodeSelection(sel: EditorSelection): sel is NodeSelection {\n\treturn 'type' in sel && sel.type === 'node';\n}\n\n/** Type guard: returns true if the selection is a GapCursorSelection. */\nexport function isGapCursor(sel: EditorSelection): sel is GapCursorSelection {\n\treturn 'type' in sel && sel.type === 'gap';\n}\n\n/** Type guard: returns true if the selection is a text Selection. */\nexport function isTextSelection(sel: EditorSelection): sel is Selection {\n\treturn !('type' in sel);\n}\n\n/** Compares two EditorSelections for equality. */\nexport function selectionsEqual(a: EditorSelection, b: EditorSelection): boolean {\n\tif (isNodeSelection(a) && isNodeSelection(b)) {\n\t\treturn a.nodeId === b.nodeId;\n\t}\n\tif (isGapCursor(a) && isGapCursor(b)) {\n\t\treturn a.blockId === b.blockId && a.side === b.side;\n\t}\n\tif (isTextSelection(a) && isTextSelection(b)) {\n\t\treturn (\n\t\t\ta.anchor.blockId === b.anchor.blockId &&\n\t\t\ta.anchor.offset === b.anchor.offset &&\n\t\t\ta.head.blockId === b.head.blockId &&\n\t\t\ta.head.offset === b.head.offset\n\t\t);\n\t}\n\treturn false;\n}\n\n/** Creates a Position, optionally with a path. */\nexport function createPosition(\n\tblockId: BlockId,\n\toffset: number,\n\tpath?: readonly BlockId[],\n): Position {\n\treturn path ? { blockId, offset, path } : { blockId, offset };\n}\n\n/** Creates a selection with distinct anchor and head. */\nexport function createSelection(anchor: Position, head: Position): Selection {\n\treturn { anchor, head };\n}\n\n/** Creates a collapsed selection (cursor) at the given position. */\nexport function createCollapsedSelection(blockId: BlockId, offset: number): Selection {\n\tconst pos: Position = { blockId, offset };\n\treturn { anchor: pos, head: pos };\n}\n\n/** Returns true if the selection is collapsed (cursor with no range). NodeSelection and GapCursor are never collapsed. */\nexport function isCollapsed(sel: EditorSelection): boolean {\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\treturn sel.anchor.blockId === sel.head.blockId && sel.anchor.offset === sel.head.offset;\n}\n\n/**\n * Returns true if the selection direction is forward (anchor before head).\n * When anchor and head are in the same block, compares offsets.\n * Cross-block ordering uses document order (not determinable here — caller provides block order).\n * For NodeSelection, always returns true.\n */\nexport function isForward(sel: EditorSelection, blockOrder?: readonly BlockId[]): boolean {\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return true;\n\tif (sel.anchor.blockId === sel.head.blockId) {\n\t\treturn sel.anchor.offset <= sel.head.offset;\n\t}\n\tif (blockOrder) {\n\t\tconst anchorIdx = blockOrder.indexOf(sel.anchor.blockId);\n\t\tconst headIdx = blockOrder.indexOf(sel.head.blockId);\n\t\treturn anchorIdx <= headIdx;\n\t}\n\treturn true;\n}\n\n/**\n * Returns a normalized range where `from` is always before `to`.\n * Throws for NodeSelection — use isNodeSelection() guard first.\n */\nexport function selectionRange(sel: Selection, blockOrder?: readonly BlockId[]): SelectionRange {\n\tif (isForward(sel, blockOrder)) {\n\t\treturn { from: sel.anchor, to: sel.head };\n\t}\n\treturn { from: sel.head, to: sel.anchor };\n}\n"],"names":["createNodeSelection","nodeId","path","type","createGapCursor","blockId","side","isNodeSelection","sel","isGapCursor","isTextSelection","selectionsEqual","a","b","anchor","offset","head","createPosition","createSelection","createCollapsedSelection","pos","isCollapsed","isForward","blockOrder","indexOf","selectionRange","from","to"],"mappings":"AA2CO,SAASA,EAAoBC,IAAiBC,IAAAA;AACpD,SAAO,EAAEC,MAAM,QAAQF,QAAAA,IAAQC,MAAAA,GAAAA;AAChC;AAGO,SAASE,EACfC,IACAC,IACAJ,IAAAA;AAEA,SAAO,EAAEC,MAAM,OAAOG,MAAAA,IAAMD,SAAAA,IAASH,MAAAA,GAAAA;AACtC;AAGO,SAASK,EAAgBC,IAAAA;AAC/B,SAAO,UAAUA,MAAoB,WAAbA,GAAIL;AAC7B;AAGO,SAASM,EAAYD,IAAAA;AAC3B,SAAO,UAAUA,MAAoB,UAAbA,GAAIL;AAC7B;AAGO,SAASO,EAAgBF,IAAAA;AAC/B,SAAA,EAAS,UAAUA;AACpB;AAGO,SAASG,EAAgBC,IAAoBC,IAAAA;AACnD,SAAIN,EAAgBK,OAAML,EAAgBM,EAAAA,IAClCD,GAAEX,WAAWY,GAAEZ,SAEnBQ,EAAYG,EAAAA,KAAMH,EAAYI,EAAAA,IAC1BD,GAAEP,YAAYQ,GAAER,WAAWO,GAAEN,SAASO,GAAEP,OAAAA,EAAAA,CAE5CI,EAAgBE,EAAAA,KAAAA,CAAMF,EAAgBG,EAAAA,MAExCD,GAAEE,OAAOT,YAAYQ,GAAEC,OAAOT,WAC9BO,GAAEE,OAAOC,WAAWF,GAAEC,OAAOC,UAC7BH,GAAEI,KAAKX,YAAYQ,GAAEG,KAAKX,WAC1BO,GAAEI,KAAKD,WAAWF,GAAEG,KAAKD;AAI5B;AAGO,SAASE,EACfZ,IACAU,IACAb,IAAAA;AAEA,SAAOA,KAAO,EAAEG,SAAAA,IAASU,QAAAA,IAAQb,MAAAA,GAAAA,IAAS,EAAEG,SAAAA,IAASU,QAAAA,GAAAA;AACtD;AAGO,SAASG,EAAgBJ,IAAkBE;AACjD,SAAO,EAAEF,QAAAA,IAAQE,MAAAA,GAAAA;AAClB;AAGO,SAASG,EAAyBd,IAAkBU,IAAAA;AAC1D,QAAMK,KAAgB,EAAEf,SAAAA,IAASU,QAAAA,GAAAA;AACjC,SAAO,EAAED,QAAQM,IAAKJ,MAAMI,GAAAA;AAC7B;AAGO,SAASC,EAAYb,IAAAA;AAC3B,SAAA,CAAID,EAAgBC,EAAAA,KAAAA,CAAQC,EAAYD,EAAAA,KACjCA,GAAIM,OAAOT,YAAYG,GAAIQ,KAAKX,WAAWG,GAAIM,OAAOC,WAAWP,GAAIQ,KAAKD;AAClF;AAQO,SAASO,EAAUd,IAAsBe,IAAAA;AAC/C,SAAA,EAAA,CAAIhB,EAAgBC,EAAAA,KAAAA,CAAQC,EAAYD,EAAAA,OACpCA,GAAIM,OAAOT,YAAYG,GAAIQ,KAAKX,UAC5BG,GAAIM,OAAOC,UAAUP,GAAIQ,KAAKD,SAAAA,CAElCQ,MACeA,GAAWC,QAAQhB,GAAIM,OAAOT,OAAAA,KAChCkB,GAAWC,QAAQhB,GAAIQ,KAAKX,OAAAA;AAI9C;AAMO,SAASoB,EAAejB,IAAgBe,IAAAA;AAC9C,SAAID,EAAUd,IAAKe,EAAAA,IACX,EAAEG,MAAMlB,GAAIM,QAAQa,IAAInB,GAAIQ,KAAAA,IAE7B,EAAEU,MAAMlB,GAAIQ,MAAMW,IAAInB,GAAIM,OAAAA;AAClC;"}