@nocturnium/svelte-ide 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/components/agents/AgentActivityPanel.svelte +565 -0
  4. package/dist/components/agents/AgentActivityPanel.svelte.d.ts +24 -0
  5. package/dist/components/agents/AgentAvatar.svelte +417 -0
  6. package/dist/components/agents/AgentAvatar.svelte.d.ts +23 -0
  7. package/dist/components/agents/AgentCursor.svelte +224 -0
  8. package/dist/components/agents/AgentCursor.svelte.d.ts +35 -0
  9. package/dist/components/agents/AgentPresenceBar.svelte +261 -0
  10. package/dist/components/agents/AgentPresenceBar.svelte.d.ts +20 -0
  11. package/dist/components/agents/index.d.ts +4 -0
  12. package/dist/components/agents/index.js +5 -0
  13. package/dist/components/ai/AIConversationList.svelte +524 -0
  14. package/dist/components/ai/AIConversationList.svelte.d.ts +17 -0
  15. package/dist/components/ai/AIEditPreview.svelte +132 -0
  16. package/dist/components/ai/AIEditPreview.svelte.d.ts +8 -0
  17. package/dist/components/ai/AIInlineEdit.svelte +155 -0
  18. package/dist/components/ai/AIInlineEdit.svelte.d.ts +10 -0
  19. package/dist/components/ai/AIMessage.svelte +239 -0
  20. package/dist/components/ai/AIMessage.svelte.d.ts +13 -0
  21. package/dist/components/ai/AIMessageActions.svelte +176 -0
  22. package/dist/components/ai/AIMessageActions.svelte.d.ts +12 -0
  23. package/dist/components/ai/AIMessageContent.svelte +355 -0
  24. package/dist/components/ai/AIMessageContent.svelte.d.ts +7 -0
  25. package/dist/components/ai/AIPanel.svelte +561 -0
  26. package/dist/components/ai/AIPanel.svelte.d.ts +7 -0
  27. package/dist/components/ai/AISuggestionWidget.svelte +132 -0
  28. package/dist/components/ai/AISuggestionWidget.svelte.d.ts +10 -0
  29. package/dist/components/ai/AIToolCallDisplay.svelte +317 -0
  30. package/dist/components/ai/AIToolCallDisplay.svelte.d.ts +12 -0
  31. package/dist/components/ai/index.d.ts +9 -0
  32. package/dist/components/ai/index.js +10 -0
  33. package/dist/components/core/Avatar.svelte +110 -0
  34. package/dist/components/core/Avatar.svelte.d.ts +12 -0
  35. package/dist/components/core/Badge.svelte +98 -0
  36. package/dist/components/core/Badge.svelte.d.ts +11 -0
  37. package/dist/components/core/Button.svelte +175 -0
  38. package/dist/components/core/Button.svelte.d.ts +18 -0
  39. package/dist/components/core/ConnectionStatus.svelte +294 -0
  40. package/dist/components/core/ConnectionStatus.svelte.d.ts +20 -0
  41. package/dist/components/core/ContextMenu.svelte +176 -0
  42. package/dist/components/core/ContextMenu.svelte.d.ts +19 -0
  43. package/dist/components/core/ErrorBoundary.svelte +277 -0
  44. package/dist/components/core/ErrorBoundary.svelte.d.ts +23 -0
  45. package/dist/components/core/Icon.svelte +107 -0
  46. package/dist/components/core/Icon.svelte.d.ts +8 -0
  47. package/dist/components/core/Input.svelte +138 -0
  48. package/dist/components/core/Input.svelte.d.ts +20 -0
  49. package/dist/components/core/Kbd.svelte +34 -0
  50. package/dist/components/core/Kbd.svelte.d.ts +7 -0
  51. package/dist/components/core/ResizeHandle.svelte +200 -0
  52. package/dist/components/core/ResizeHandle.svelte.d.ts +23 -0
  53. package/dist/components/core/Spinner.svelte +35 -0
  54. package/dist/components/core/Spinner.svelte.d.ts +7 -0
  55. package/dist/components/core/Textarea.svelte +112 -0
  56. package/dist/components/core/Textarea.svelte.d.ts +18 -0
  57. package/dist/components/core/Tooltip.svelte +103 -0
  58. package/dist/components/core/Tooltip.svelte.d.ts +11 -0
  59. package/dist/components/core/index.d.ts +13 -0
  60. package/dist/components/core/index.js +14 -0
  61. package/dist/components/editor/AIFocusLayer.svelte +430 -0
  62. package/dist/components/editor/AIFocusLayer.svelte.d.ts +32 -0
  63. package/dist/components/editor/Breadcrumbs.svelte +435 -0
  64. package/dist/components/editor/Breadcrumbs.svelte.d.ts +33 -0
  65. package/dist/components/editor/BreakpointLayer.svelte +642 -0
  66. package/dist/components/editor/BreakpointLayer.svelte.d.ts +20 -0
  67. package/dist/components/editor/CognitiveLoadMeter.svelte +324 -0
  68. package/dist/components/editor/CognitiveLoadMeter.svelte.d.ts +18 -0
  69. package/dist/components/editor/CollaborativeEditor.svelte +218 -0
  70. package/dist/components/editor/CollaborativeEditor.svelte.d.ts +32 -0
  71. package/dist/components/editor/CommandPalette.svelte +434 -0
  72. package/dist/components/editor/CommandPalette.svelte.d.ts +11 -0
  73. package/dist/components/editor/ComplexityLayer.svelte +293 -0
  74. package/dist/components/editor/ComplexityLayer.svelte.d.ts +23 -0
  75. package/dist/components/editor/ConflictZoneLayer.svelte +441 -0
  76. package/dist/components/editor/ConflictZoneLayer.svelte.d.ts +25 -0
  77. package/dist/components/editor/ContextLens.svelte +262 -0
  78. package/dist/components/editor/ContextLens.svelte.d.ts +27 -0
  79. package/dist/components/editor/CustomEditor.svelte +1242 -0
  80. package/dist/components/editor/CustomEditor.svelte.d.ts +37 -0
  81. package/dist/components/editor/DebugConsole.svelte +646 -0
  82. package/dist/components/editor/DebugConsole.svelte.d.ts +41 -0
  83. package/dist/components/editor/EchoCursorLayer.svelte +363 -0
  84. package/dist/components/editor/EchoCursorLayer.svelte.d.ts +24 -0
  85. package/dist/components/editor/Editor.svelte +61 -0
  86. package/dist/components/editor/Editor.svelte.d.ts +22 -0
  87. package/dist/components/editor/EditorGutter.svelte +119 -0
  88. package/dist/components/editor/EditorGutter.svelte.d.ts +19 -0
  89. package/dist/components/editor/EditorLines.svelte +182 -0
  90. package/dist/components/editor/EditorLines.svelte.d.ts +43 -0
  91. package/dist/components/editor/EditorPane.svelte +134 -0
  92. package/dist/components/editor/EditorPane.svelte.d.ts +9 -0
  93. package/dist/components/editor/EditorSelections.svelte +186 -0
  94. package/dist/components/editor/EditorSelections.svelte.d.ts +25 -0
  95. package/dist/components/editor/EditorTabs.svelte +170 -0
  96. package/dist/components/editor/EditorTabs.svelte.d.ts +12 -0
  97. package/dist/components/editor/FileExplorer.svelte +811 -0
  98. package/dist/components/editor/FileExplorer.svelte.d.ts +67 -0
  99. package/dist/components/editor/FileIcon.svelte +110 -0
  100. package/dist/components/editor/FileIcon.svelte.d.ts +10 -0
  101. package/dist/components/editor/FindReplace.svelte +448 -0
  102. package/dist/components/editor/FindReplace.svelte.d.ts +40 -0
  103. package/dist/components/editor/GhostBracketLayer.svelte +391 -0
  104. package/dist/components/editor/GhostBracketLayer.svelte.d.ts +24 -0
  105. package/dist/components/editor/GitBlameLayer.svelte +436 -0
  106. package/dist/components/editor/GitBlameLayer.svelte.d.ts +18 -0
  107. package/dist/components/editor/InlineDiagnosticsLayer.svelte +540 -0
  108. package/dist/components/editor/InlineDiagnosticsLayer.svelte.d.ts +35 -0
  109. package/dist/components/editor/InlineDiffLayer.svelte +337 -0
  110. package/dist/components/editor/InlineDiffLayer.svelte.d.ts +31 -0
  111. package/dist/components/editor/MinimalEditor.svelte +75 -0
  112. package/dist/components/editor/MinimalEditor.svelte.d.ts +6 -0
  113. package/dist/components/editor/MinimalEditor2.svelte +84 -0
  114. package/dist/components/editor/MinimalEditor2.svelte.d.ts +6 -0
  115. package/dist/components/editor/Minimap.svelte +327 -0
  116. package/dist/components/editor/Minimap.svelte.d.ts +34 -0
  117. package/dist/components/editor/PluginPreviewSandbox.svelte +793 -0
  118. package/dist/components/editor/PluginPreviewSandbox.svelte.d.ts +49 -0
  119. package/dist/components/editor/ProblemsPanel.svelte +628 -0
  120. package/dist/components/editor/ProblemsPanel.svelte.d.ts +25 -0
  121. package/dist/components/editor/QuickActionsMenu.svelte +403 -0
  122. package/dist/components/editor/QuickActionsMenu.svelte.d.ts +18 -0
  123. package/dist/components/editor/SnippetPalette.svelte +530 -0
  124. package/dist/components/editor/SnippetPalette.svelte.d.ts +16 -0
  125. package/dist/components/editor/StructureMap.svelte +431 -0
  126. package/dist/components/editor/StructureMap.svelte.d.ts +37 -0
  127. package/dist/components/editor/SymbolOutline.svelte +722 -0
  128. package/dist/components/editor/SymbolOutline.svelte.d.ts +44 -0
  129. package/dist/components/editor/TimelineScrubber.svelte +470 -0
  130. package/dist/components/editor/TimelineScrubber.svelte.d.ts +40 -0
  131. package/dist/components/editor/TokenRenderer.svelte +69 -0
  132. package/dist/components/editor/TokenRenderer.svelte.d.ts +15 -0
  133. package/dist/components/editor/constants.d.ts +32 -0
  134. package/dist/components/editor/constants.js +36 -0
  135. package/dist/components/editor/core/ai-awareness.d.ts +176 -0
  136. package/dist/components/editor/core/ai-awareness.js +210 -0
  137. package/dist/components/editor/core/bracket-healer.d.ts +189 -0
  138. package/dist/components/editor/core/bracket-healer.js +406 -0
  139. package/dist/components/editor/core/breakpoints.d.ts +203 -0
  140. package/dist/components/editor/core/breakpoints.js +414 -0
  141. package/dist/components/editor/core/commands.d.ts +108 -0
  142. package/dist/components/editor/core/commands.js +246 -0
  143. package/dist/components/editor/core/complexity-analyzer.d.ts +123 -0
  144. package/dist/components/editor/core/complexity-analyzer.js +376 -0
  145. package/dist/components/editor/core/conflict-predictor.d.ts +135 -0
  146. package/dist/components/editor/core/conflict-predictor.js +316 -0
  147. package/dist/components/editor/core/crdt-binding.d.ts +118 -0
  148. package/dist/components/editor/core/crdt-binding.js +286 -0
  149. package/dist/components/editor/core/diagnostics.d.ts +210 -0
  150. package/dist/components/editor/core/diagnostics.js +335 -0
  151. package/dist/components/editor/core/echo-cursor.d.ts +201 -0
  152. package/dist/components/editor/core/echo-cursor.js +267 -0
  153. package/dist/components/editor/core/folding.d.ts +124 -0
  154. package/dist/components/editor/core/folding.js +672 -0
  155. package/dist/components/editor/core/ghost-pair.d.ts +122 -0
  156. package/dist/components/editor/core/ghost-pair.js +221 -0
  157. package/dist/components/editor/core/git-blame.d.ts +170 -0
  158. package/dist/components/editor/core/git-blame.js +324 -0
  159. package/dist/components/editor/core/index.d.ts +26 -0
  160. package/dist/components/editor/core/index.js +24 -0
  161. package/dist/components/editor/core/keybindings.d.ts +79 -0
  162. package/dist/components/editor/core/keybindings.js +357 -0
  163. package/dist/components/editor/core/multi-cursor.d.ts +196 -0
  164. package/dist/components/editor/core/multi-cursor.js +521 -0
  165. package/dist/components/editor/core/navigation.d.ts +107 -0
  166. package/dist/components/editor/core/navigation.js +408 -0
  167. package/dist/components/editor/core/quick-actions.d.ts +189 -0
  168. package/dist/components/editor/core/quick-actions.js +427 -0
  169. package/dist/components/editor/core/search.d.ts +88 -0
  170. package/dist/components/editor/core/search.js +192 -0
  171. package/dist/components/editor/core/semantic-analyzer.d.ts +77 -0
  172. package/dist/components/editor/core/semantic-analyzer.js +424 -0
  173. package/dist/components/editor/core/snippet-manager.d.ts +202 -0
  174. package/dist/components/editor/core/snippet-manager.js +565 -0
  175. package/dist/components/editor/core/state.d.ts +367 -0
  176. package/dist/components/editor/core/state.js +900 -0
  177. package/dist/components/editor/core/timeline.d.ts +204 -0
  178. package/dist/components/editor/core/timeline.js +349 -0
  179. package/dist/components/editor/editor-find.d.ts +56 -0
  180. package/dist/components/editor/editor-find.js +148 -0
  181. package/dist/components/editor/editor-input.d.ts +77 -0
  182. package/dist/components/editor/editor-input.js +445 -0
  183. package/dist/components/editor/editor-multicursor.d.ts +21 -0
  184. package/dist/components/editor/editor-multicursor.js +196 -0
  185. package/dist/components/editor/editor-scroll.d.ts +14 -0
  186. package/dist/components/editor/editor-scroll.js +34 -0
  187. package/dist/components/editor/index.d.ts +15 -0
  188. package/dist/components/editor/index.js +21 -0
  189. package/dist/components/editor/languages.d.ts +62 -0
  190. package/dist/components/editor/languages.js +285 -0
  191. package/dist/components/editor/theme.d.ts +88 -0
  192. package/dist/components/editor/theme.js +139 -0
  193. package/dist/components/editor/tokenizer/base.d.ts +40 -0
  194. package/dist/components/editor/tokenizer/base.js +203 -0
  195. package/dist/components/editor/tokenizer/index.d.ts +56 -0
  196. package/dist/components/editor/tokenizer/index.js +215 -0
  197. package/dist/components/editor/tokenizer/languages/css.d.ts +17 -0
  198. package/dist/components/editor/tokenizer/languages/css.js +194 -0
  199. package/dist/components/editor/tokenizer/languages/go.d.ts +17 -0
  200. package/dist/components/editor/tokenizer/languages/go.js +220 -0
  201. package/dist/components/editor/tokenizer/languages/html.d.ts +24 -0
  202. package/dist/components/editor/tokenizer/languages/html.js +145 -0
  203. package/dist/components/editor/tokenizer/languages/javascript.d.ts +56 -0
  204. package/dist/components/editor/tokenizer/languages/javascript.js +452 -0
  205. package/dist/components/editor/tokenizer/languages/json.d.ts +12 -0
  206. package/dist/components/editor/tokenizer/languages/json.js +91 -0
  207. package/dist/components/editor/tokenizer/languages/markdown.d.ts +16 -0
  208. package/dist/components/editor/tokenizer/languages/markdown.js +156 -0
  209. package/dist/components/editor/tokenizer/languages/python.d.ts +20 -0
  210. package/dist/components/editor/tokenizer/languages/python.js +227 -0
  211. package/dist/components/editor/tokenizer/languages/svelte.d.ts +40 -0
  212. package/dist/components/editor/tokenizer/languages/svelte.js +326 -0
  213. package/dist/components/editor/tokenizer/types.d.ts +86 -0
  214. package/dist/components/editor/tokenizer/types.js +4 -0
  215. package/dist/components/layout/IDELayout.svelte +274 -0
  216. package/dist/components/layout/IDELayout.svelte.d.ts +29 -0
  217. package/dist/components/layout/StatusBar.svelte +511 -0
  218. package/dist/components/layout/StatusBar.svelte.d.ts +47 -0
  219. package/dist/components/layout/index.d.ts +2 -0
  220. package/dist/components/layout/index.js +3 -0
  221. package/dist/components/lsp/AutocompleteWidget.svelte +364 -0
  222. package/dist/components/lsp/AutocompleteWidget.svelte.d.ts +33 -0
  223. package/dist/components/lsp/DiagnosticMarker.svelte +166 -0
  224. package/dist/components/lsp/DiagnosticMarker.svelte.d.ts +19 -0
  225. package/dist/components/lsp/DiagnosticsPanel.svelte +388 -0
  226. package/dist/components/lsp/DiagnosticsPanel.svelte.d.ts +21 -0
  227. package/dist/components/lsp/HoverTooltip.svelte +274 -0
  228. package/dist/components/lsp/HoverTooltip.svelte.d.ts +24 -0
  229. package/dist/components/lsp/LSPEditor.svelte +486 -0
  230. package/dist/components/lsp/LSPEditor.svelte.d.ts +39 -0
  231. package/dist/components/lsp/SignatureHelpWidget.svelte +216 -0
  232. package/dist/components/lsp/SignatureHelpWidget.svelte.d.ts +22 -0
  233. package/dist/components/lsp/index.d.ts +6 -0
  234. package/dist/components/lsp/index.js +7 -0
  235. package/dist/components/plugins/PluginCard.svelte +153 -0
  236. package/dist/components/plugins/PluginCard.svelte.d.ts +19 -0
  237. package/dist/components/plugins/PluginPanel.svelte +280 -0
  238. package/dist/components/plugins/PluginPanel.svelte.d.ts +8 -0
  239. package/dist/components/plugins/PluginProposalForm.svelte +250 -0
  240. package/dist/components/plugins/PluginProposalForm.svelte.d.ts +6 -0
  241. package/dist/components/plugins/PluginStatusBadge.svelte +14 -0
  242. package/dist/components/plugins/PluginStatusBadge.svelte.d.ts +8 -0
  243. package/dist/components/plugins/index.d.ts +4 -0
  244. package/dist/components/plugins/index.js +5 -0
  245. package/dist/components/vfs/LockConflictDialog.svelte +705 -0
  246. package/dist/components/vfs/LockConflictDialog.svelte.d.ts +21 -0
  247. package/dist/components/vfs/LockIndicator.svelte +194 -0
  248. package/dist/components/vfs/LockIndicator.svelte.d.ts +29 -0
  249. package/dist/components/vfs/LockOverlay.svelte +344 -0
  250. package/dist/components/vfs/LockOverlay.svelte.d.ts +17 -0
  251. package/dist/components/vfs/VersionConflictDialog.svelte +549 -0
  252. package/dist/components/vfs/VersionConflictDialog.svelte.d.ts +24 -0
  253. package/dist/components/vfs/index.d.ts +4 -0
  254. package/dist/components/vfs/index.js +5 -0
  255. package/dist/crdt/awareness.d.ts +42 -0
  256. package/dist/crdt/awareness.js +109 -0
  257. package/dist/crdt/document.d.ts +101 -0
  258. package/dist/crdt/document.js +187 -0
  259. package/dist/crdt/index.d.ts +9 -0
  260. package/dist/crdt/index.js +8 -0
  261. package/dist/crdt/provider.d.ts +85 -0
  262. package/dist/crdt/provider.js +150 -0
  263. package/dist/crdt/types.d.ts +61 -0
  264. package/dist/crdt/types.js +4 -0
  265. package/dist/crdt/undo.d.ts +34 -0
  266. package/dist/crdt/undo.js +70 -0
  267. package/dist/index.d.ts +277 -0
  268. package/dist/index.js +280 -0
  269. package/dist/plugins/index.d.ts +103 -0
  270. package/dist/plugins/index.js +153 -0
  271. package/dist/services/error-handling.d.ts +95 -0
  272. package/dist/services/error-handling.js +413 -0
  273. package/dist/services/ide-integration.d.ts +83 -0
  274. package/dist/services/ide-integration.js +367 -0
  275. package/dist/services/lsp-client.d.ts +69 -0
  276. package/dist/services/lsp-client.js +667 -0
  277. package/dist/services/mock-ai.d.ts +37 -0
  278. package/dist/services/mock-ai.js +318 -0
  279. package/dist/services/optimistic.d.ts +141 -0
  280. package/dist/services/optimistic.js +367 -0
  281. package/dist/services/vfs-client.d.ts +81 -0
  282. package/dist/services/vfs-client.js +348 -0
  283. package/dist/stores/agents.svelte.d.ts +85 -0
  284. package/dist/stores/agents.svelte.js +459 -0
  285. package/dist/stores/ai-persistence.svelte.d.ts +76 -0
  286. package/dist/stores/ai-persistence.svelte.js +334 -0
  287. package/dist/stores/ai.svelte.d.ts +140 -0
  288. package/dist/stores/ai.svelte.js +383 -0
  289. package/dist/stores/collaboration.svelte.d.ts +164 -0
  290. package/dist/stores/collaboration.svelte.js +334 -0
  291. package/dist/stores/editor.svelte.d.ts +131 -0
  292. package/dist/stores/editor.svelte.js +250 -0
  293. package/dist/stores/index.d.ts +10 -0
  294. package/dist/stores/index.js +29 -0
  295. package/dist/stores/layout.svelte.d.ts +171 -0
  296. package/dist/stores/layout.svelte.js +351 -0
  297. package/dist/stores/plugin.svelte.d.ts +121 -0
  298. package/dist/stores/plugin.svelte.js +410 -0
  299. package/dist/stores/vfs.svelte.d.ts +123 -0
  300. package/dist/stores/vfs.svelte.js +680 -0
  301. package/dist/styles/theme.css +623 -0
  302. package/dist/types/agents.d.ts +127 -0
  303. package/dist/types/agents.js +5 -0
  304. package/dist/types/ai.d.ts +137 -0
  305. package/dist/types/ai.js +4 -0
  306. package/dist/types/crdt.d.ts +222 -0
  307. package/dist/types/crdt.js +5 -0
  308. package/dist/types/editor.d.ts +52 -0
  309. package/dist/types/editor.js +18 -0
  310. package/dist/types/events.d.ts +133 -0
  311. package/dist/types/events.js +4 -0
  312. package/dist/types/filesystem.d.ts +77 -0
  313. package/dist/types/filesystem.js +4 -0
  314. package/dist/types/index.d.ts +9 -0
  315. package/dist/types/index.js +12 -0
  316. package/dist/types/lsp.d.ts +691 -0
  317. package/dist/types/lsp.js +108 -0
  318. package/dist/types/plugin.d.ts +239 -0
  319. package/dist/types/plugin.js +5 -0
  320. package/dist/types/vfs.d.ts +191 -0
  321. package/dist/types/vfs.js +18 -0
  322. package/dist/utils/format.d.ts +55 -0
  323. package/dist/utils/format.js +152 -0
  324. package/dist/utils/index.d.ts +3 -0
  325. package/dist/utils/index.js +4 -0
  326. package/dist/utils/keybindings.d.ts +33 -0
  327. package/dist/utils/keybindings.js +171 -0
  328. package/dist/utils/language.d.ts +27 -0
  329. package/dist/utils/language.js +222 -0
  330. package/package.json +178 -0
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Multi-cursor management for the custom editor
3
+ *
4
+ * Provides support for creating, managing, and editing with multiple cursors.
5
+ * Cursors can have selections and are automatically merged when they touch or overlap.
6
+ */
7
+ /** Default maximum cursors */
8
+ const DEFAULT_MAX_CURSORS = 100;
9
+ /**
10
+ * Compare two positions
11
+ * Returns negative if a < b, positive if a > b, 0 if equal
12
+ */
13
+ export function comparePositions(a, b) {
14
+ if (a.line !== b.line)
15
+ return a.line - b.line;
16
+ return a.column - b.column;
17
+ }
18
+ /**
19
+ * Check if position a is before position b
20
+ */
21
+ export function isPositionBefore(a, b) {
22
+ return comparePositions(a, b) < 0;
23
+ }
24
+ /**
25
+ * Check if position a is before or equal to position b
26
+ */
27
+ export function isPositionBeforeOrEqual(a, b) {
28
+ return comparePositions(a, b) <= 0;
29
+ }
30
+ /**
31
+ * Check if two positions are equal
32
+ */
33
+ export function positionsEqual(a, b) {
34
+ return a.line === b.line && a.column === b.column;
35
+ }
36
+ /**
37
+ * Get the start position of a selection (min of anchor and head)
38
+ */
39
+ export function getSelectionStart(selection) {
40
+ return isPositionBefore(selection.anchor, selection.head)
41
+ ? selection.anchor
42
+ : selection.head;
43
+ }
44
+ /**
45
+ * Get the end position of a selection (max of anchor and head)
46
+ */
47
+ export function getSelectionEnd(selection) {
48
+ return isPositionBefore(selection.anchor, selection.head)
49
+ ? selection.head
50
+ : selection.anchor;
51
+ }
52
+ /**
53
+ * Check if a selection is empty (anchor equals head)
54
+ */
55
+ export function isSelectionEmpty(selection) {
56
+ return positionsEqual(selection.anchor, selection.head);
57
+ }
58
+ /**
59
+ * Check if two selections overlap or touch
60
+ */
61
+ export function selectionsOverlap(a, b) {
62
+ const aStart = getSelectionStart(a);
63
+ const aEnd = getSelectionEnd(a);
64
+ const bStart = getSelectionStart(b);
65
+ const bEnd = getSelectionEnd(b);
66
+ // They overlap if neither is completely before the other
67
+ return isPositionBeforeOrEqual(aStart, bEnd) && isPositionBeforeOrEqual(bStart, aEnd);
68
+ }
69
+ /**
70
+ * Merge two selections into one (union)
71
+ */
72
+ export function mergeSelections(a, b) {
73
+ const aStart = getSelectionStart(a);
74
+ const aEnd = getSelectionEnd(a);
75
+ const bStart = getSelectionStart(b);
76
+ const bEnd = getSelectionEnd(b);
77
+ const start = isPositionBefore(aStart, bStart) ? aStart : bStart;
78
+ const end = isPositionBefore(aEnd, bEnd) ? bEnd : aEnd;
79
+ return { anchor: start, head: end };
80
+ }
81
+ /**
82
+ * Manages multiple cursors in the editor
83
+ */
84
+ /** Maximum number of listeners allowed per manager */
85
+ const MAX_LISTENERS = 100;
86
+ export class CursorManager {
87
+ cursors = new Map();
88
+ primaryId = '';
89
+ nextId = 0;
90
+ maxCursors;
91
+ listeners = new Set();
92
+ constructor(config = {}) {
93
+ this.maxCursors = config.maxCursors ?? DEFAULT_MAX_CURSORS;
94
+ // Initialize with a single primary cursor at start
95
+ this.addCursor({ line: 0, column: 0 }, true);
96
+ }
97
+ /**
98
+ * Get all cursors
99
+ */
100
+ getCursors() {
101
+ return [...this.cursors.values()];
102
+ }
103
+ /**
104
+ * Get all cursors sorted by position (top to bottom, left to right)
105
+ */
106
+ getSortedCursors() {
107
+ return [...this.cursors.values()].sort((a, b) => {
108
+ const posA = getSelectionStart(a.selection);
109
+ const posB = getSelectionStart(b.selection);
110
+ return comparePositions(posA, posB);
111
+ });
112
+ }
113
+ /**
114
+ * Get cursors sorted in reverse order (bottom to top)
115
+ * Useful for editing operations to maintain position validity
116
+ */
117
+ getSortedCursorsReverse() {
118
+ return this.getSortedCursors().reverse();
119
+ }
120
+ /**
121
+ * Get the primary cursor
122
+ */
123
+ getPrimary() {
124
+ const primary = this.cursors.get(this.primaryId);
125
+ if (!primary) {
126
+ // Fallback: return first cursor
127
+ const first = this.cursors.values().next().value;
128
+ if (first) {
129
+ this.setPrimary(first.id);
130
+ return first;
131
+ }
132
+ // Should never happen, but create a default cursor
133
+ return this.addCursor({ line: 0, column: 0 }, true);
134
+ }
135
+ return primary;
136
+ }
137
+ /**
138
+ * Get cursor count
139
+ */
140
+ get count() {
141
+ return this.cursors.size;
142
+ }
143
+ /**
144
+ * Check if there are multiple cursors
145
+ */
146
+ get hasMultiple() {
147
+ return this.cursors.size > 1;
148
+ }
149
+ /**
150
+ * Add a new cursor at a position
151
+ * @param position - Position for the new cursor
152
+ * @param makePrimary - Make this the primary cursor
153
+ * @returns The new cursor, or null if at max capacity
154
+ */
155
+ addCursor(position, makePrimary = false) {
156
+ // Check if we're at capacity
157
+ if (this.cursors.size >= this.maxCursors && !makePrimary) {
158
+ // Return the closest existing cursor instead
159
+ return this.getClosestCursor(position);
160
+ }
161
+ const id = `cursor-${this.nextId++}`;
162
+ const cursor = {
163
+ id,
164
+ selection: { anchor: { ...position }, head: { ...position } },
165
+ isPrimary: makePrimary || this.cursors.size === 0
166
+ };
167
+ if (cursor.isPrimary) {
168
+ // Clear previous primary
169
+ const oldPrimary = this.cursors.get(this.primaryId);
170
+ if (oldPrimary) {
171
+ oldPrimary.isPrimary = false;
172
+ }
173
+ this.primaryId = id;
174
+ }
175
+ this.cursors.set(id, cursor);
176
+ this.mergeTouchingCursors();
177
+ this.emit();
178
+ return cursor;
179
+ }
180
+ /**
181
+ * Add a cursor with a selection
182
+ */
183
+ addCursorWithSelection(anchor, head, makePrimary = false) {
184
+ const cursor = this.addCursor(anchor, makePrimary);
185
+ cursor.selection = {
186
+ anchor: { ...anchor },
187
+ head: { ...head }
188
+ };
189
+ this.mergeTouchingCursors();
190
+ this.emit();
191
+ return cursor;
192
+ }
193
+ /**
194
+ * Remove a cursor by ID
195
+ * @returns true if removed, false if it was the last cursor
196
+ */
197
+ removeCursor(id) {
198
+ if (this.cursors.size <= 1) {
199
+ return false; // Must keep at least one cursor
200
+ }
201
+ const wasPrimary = this.primaryId === id;
202
+ this.cursors.delete(id);
203
+ if (wasPrimary) {
204
+ // Promote first remaining cursor to primary
205
+ const first = this.cursors.values().next().value;
206
+ if (first) {
207
+ this.setPrimary(first.id);
208
+ }
209
+ }
210
+ this.emit();
211
+ return true;
212
+ }
213
+ /**
214
+ * Remove the last added secondary cursor (for Ctrl+U)
215
+ * @returns true if a cursor was removed
216
+ */
217
+ removeLastSecondary() {
218
+ if (this.cursors.size <= 1) {
219
+ return false;
220
+ }
221
+ // Get all non-primary cursors sorted by ID (most recent last)
222
+ const secondaries = [...this.cursors.values()]
223
+ .filter(c => !c.isPrimary)
224
+ .sort((a, b) => {
225
+ // Extract numeric part of ID for comparison
226
+ const idA = parseInt(a.id.replace('cursor-', ''), 10);
227
+ const idB = parseInt(b.id.replace('cursor-', ''), 10);
228
+ return idB - idA; // Descending (newest first)
229
+ });
230
+ if (secondaries.length > 0) {
231
+ this.cursors.delete(secondaries[0].id);
232
+ this.emit();
233
+ return true;
234
+ }
235
+ return false;
236
+ }
237
+ /**
238
+ * Clear all secondary cursors, keeping only the primary
239
+ */
240
+ clearSecondary() {
241
+ if (this.cursors.size <= 1) {
242
+ return;
243
+ }
244
+ const primary = this.getPrimary();
245
+ this.cursors.clear();
246
+ this.cursors.set(primary.id, primary);
247
+ this.emit();
248
+ }
249
+ /**
250
+ * Set the primary cursor
251
+ */
252
+ setPrimary(id) {
253
+ const newPrimary = this.cursors.get(id);
254
+ if (!newPrimary)
255
+ return;
256
+ // Clear old primary
257
+ const oldPrimary = this.cursors.get(this.primaryId);
258
+ if (oldPrimary) {
259
+ oldPrimary.isPrimary = false;
260
+ }
261
+ newPrimary.isPrimary = true;
262
+ this.primaryId = id;
263
+ }
264
+ /**
265
+ * Set cursor position (clears selection)
266
+ */
267
+ setCursor(id, position) {
268
+ const cursor = this.cursors.get(id);
269
+ if (!cursor)
270
+ return;
271
+ cursor.selection = {
272
+ anchor: { ...position },
273
+ head: { ...position }
274
+ };
275
+ this.mergeTouchingCursors();
276
+ this.emit();
277
+ }
278
+ /**
279
+ * Batch update cursor positions without triggering merge/emit on each update
280
+ * More efficient for updating multiple cursors at once
281
+ */
282
+ batchUpdateCursors(updates) {
283
+ for (const update of updates) {
284
+ const cursor = this.cursors.get(update.id);
285
+ if (cursor) {
286
+ cursor.selection = {
287
+ anchor: { ...update.position },
288
+ head: { ...update.position }
289
+ };
290
+ }
291
+ }
292
+ this.mergeTouchingCursors();
293
+ this.emit();
294
+ }
295
+ /**
296
+ * Set cursor selection
297
+ */
298
+ setSelection(id, anchor, head) {
299
+ const cursor = this.cursors.get(id);
300
+ if (!cursor)
301
+ return;
302
+ cursor.selection = {
303
+ anchor: { ...anchor },
304
+ head: { ...head }
305
+ };
306
+ this.mergeTouchingCursors();
307
+ this.emit();
308
+ }
309
+ /**
310
+ * Extend cursor selection (move head, keep anchor)
311
+ */
312
+ extendSelection(id, head) {
313
+ const cursor = this.cursors.get(id);
314
+ if (!cursor)
315
+ return;
316
+ cursor.selection.head = { ...head };
317
+ this.mergeTouchingCursors();
318
+ this.emit();
319
+ }
320
+ /**
321
+ * Set all cursors to new positions (for single cursor operations)
322
+ */
323
+ setSingleCursor(position) {
324
+ this.clearSecondary();
325
+ const primary = this.getPrimary();
326
+ primary.selection = {
327
+ anchor: { ...position },
328
+ head: { ...position }
329
+ };
330
+ this.emit();
331
+ }
332
+ /**
333
+ * Set single cursor with selection
334
+ */
335
+ setSingleSelection(anchor, head) {
336
+ this.clearSecondary();
337
+ const primary = this.getPrimary();
338
+ primary.selection = {
339
+ anchor: { ...anchor },
340
+ head: { ...head }
341
+ };
342
+ this.emit();
343
+ }
344
+ /**
345
+ * Check if any cursor contains a position
346
+ */
347
+ isPositionInAnyCursor(position) {
348
+ for (const cursor of this.cursors.values()) {
349
+ const start = getSelectionStart(cursor.selection);
350
+ const end = getSelectionEnd(cursor.selection);
351
+ if ((isPositionBeforeOrEqual(start, position) && isPositionBeforeOrEqual(position, end)) ||
352
+ positionsEqual(cursor.selection.head, position)) {
353
+ return true;
354
+ }
355
+ }
356
+ return false;
357
+ }
358
+ /**
359
+ * Get the closest cursor to a position
360
+ */
361
+ getClosestCursor(position) {
362
+ let closest = this.getPrimary();
363
+ let closestDistance = Infinity;
364
+ for (const cursor of this.cursors.values()) {
365
+ const cursorPos = cursor.selection.head;
366
+ const distance = Math.abs(cursorPos.line - position.line) * 10000 +
367
+ Math.abs(cursorPos.column - position.column);
368
+ if (distance < closestDistance) {
369
+ closestDistance = distance;
370
+ closest = cursor;
371
+ }
372
+ }
373
+ return closest;
374
+ }
375
+ /**
376
+ * Merge overlapping or touching cursors
377
+ */
378
+ mergeTouchingCursors() {
379
+ if (this.cursors.size <= 1)
380
+ return;
381
+ const sorted = this.getSortedCursors();
382
+ const merged = [];
383
+ let primaryWasMerged = false;
384
+ let mergedPrimaryInto = null;
385
+ for (const cursor of sorted) {
386
+ const last = merged[merged.length - 1];
387
+ if (last && selectionsOverlap(last.selection, cursor.selection)) {
388
+ // Merge into last cursor
389
+ last.selection = mergeSelections(last.selection, cursor.selection);
390
+ // Track if primary was merged
391
+ if (cursor.isPrimary) {
392
+ primaryWasMerged = true;
393
+ mergedPrimaryInto = last;
394
+ }
395
+ }
396
+ else {
397
+ merged.push(cursor);
398
+ }
399
+ }
400
+ // Rebuild the map
401
+ this.cursors.clear();
402
+ for (const cursor of merged) {
403
+ this.cursors.set(cursor.id, cursor);
404
+ }
405
+ // Ensure we have a valid primary
406
+ if (primaryWasMerged && mergedPrimaryInto) {
407
+ this.setPrimary(mergedPrimaryInto.id);
408
+ }
409
+ else if (!this.cursors.has(this.primaryId)) {
410
+ // Primary was removed in merge, set new primary
411
+ const first = this.cursors.values().next().value;
412
+ if (first) {
413
+ this.setPrimary(first.id);
414
+ }
415
+ }
416
+ }
417
+ /**
418
+ * Add cursor above the current primary cursor
419
+ * @param lineCount - Number of lines in the document
420
+ */
421
+ addCursorAbove(lineCount) {
422
+ const primary = this.getPrimary();
423
+ const pos = primary.selection.head;
424
+ if (pos.line <= 0) {
425
+ return false;
426
+ }
427
+ // Add cursor on line above, same column (will be clamped by caller)
428
+ this.addCursor({ line: pos.line - 1, column: pos.column });
429
+ return true;
430
+ }
431
+ /**
432
+ * Add cursor below the current primary cursor
433
+ * @param lineCount - Number of lines in the document
434
+ */
435
+ addCursorBelow(lineCount) {
436
+ const primary = this.getPrimary();
437
+ const pos = primary.selection.head;
438
+ if (pos.line >= lineCount - 1) {
439
+ return false;
440
+ }
441
+ // Add cursor on line below, same column (will be clamped by caller)
442
+ this.addCursor({ line: pos.line + 1, column: pos.column });
443
+ return true;
444
+ }
445
+ /**
446
+ * Subscribe to cursor changes
447
+ * In development mode, throws an error to catch memory leaks early.
448
+ * In production, warns and evicts the oldest listener.
449
+ */
450
+ onChange(callback) {
451
+ if (this.listeners.size >= MAX_LISTENERS) {
452
+ const msg = `[CursorManager] Maximum listener count (${MAX_LISTENERS}) exceeded. Possible memory leak - ensure listeners are unsubscribed.`;
453
+ // Fail fast in development to catch bugs early
454
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {
455
+ throw new Error(msg);
456
+ }
457
+ // In production, warn and auto-cleanup oldest listener
458
+ console.warn(msg);
459
+ const firstListener = this.listeners.values().next().value;
460
+ if (firstListener) {
461
+ this.listeners.delete(firstListener);
462
+ }
463
+ }
464
+ this.listeners.add(callback);
465
+ return () => this.listeners.delete(callback);
466
+ }
467
+ emit() {
468
+ for (const listener of this.listeners) {
469
+ try {
470
+ listener();
471
+ }
472
+ catch (error) {
473
+ console.error('[CursorManager] Listener callback failed:', error);
474
+ }
475
+ }
476
+ }
477
+ /**
478
+ * Restore cursors from a saved state (for undo/redo)
479
+ */
480
+ restore(cursors, primaryId) {
481
+ this.cursors.clear();
482
+ for (const cursor of cursors) {
483
+ this.cursors.set(cursor.id, {
484
+ id: cursor.id,
485
+ selection: {
486
+ anchor: { ...cursor.selection.anchor },
487
+ head: { ...cursor.selection.head }
488
+ },
489
+ isPrimary: cursor.isPrimary
490
+ });
491
+ }
492
+ this.primaryId = primaryId;
493
+ // Ensure we have at least one cursor
494
+ if (this.cursors.size === 0) {
495
+ this.addCursor({ line: 0, column: 0 }, true);
496
+ }
497
+ this.emit();
498
+ }
499
+ /**
500
+ * Clone the current state (for undo/redo)
501
+ */
502
+ clone() {
503
+ return {
504
+ cursors: [...this.cursors.values()].map(c => ({
505
+ id: c.id,
506
+ selection: {
507
+ anchor: { ...c.selection.anchor },
508
+ head: { ...c.selection.head }
509
+ },
510
+ isPrimary: c.isPrimary
511
+ })),
512
+ primaryId: this.primaryId
513
+ };
514
+ }
515
+ }
516
+ /**
517
+ * Create a new CursorManager
518
+ */
519
+ export function createCursorManager(config) {
520
+ return new CursorManager(config);
521
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Cursor navigation utilities
3
+ *
4
+ * This module provides cursor movement operations for the custom editor.
5
+ * Properly handles Unicode including emoji and characters outside the BMP.
6
+ */
7
+ import type { EditorState, Position } from './state';
8
+ /**
9
+ * Check if character is a word character
10
+ * Supports Unicode identifiers (e.g., café, π, 日本語)
11
+ */
12
+ export declare function isWordChar(char: string): boolean;
13
+ /**
14
+ * Navigation operations for the editor
15
+ */
16
+ export declare class Navigation {
17
+ private state;
18
+ /**
19
+ * Sticky column (preferred column) for vertical navigation.
20
+ * Remembers the column position when moving through lines of varying lengths.
21
+ */
22
+ private stickyColumn;
23
+ constructor(state: EditorState);
24
+ /**
25
+ * Reset sticky column - call this when cursor moves horizontally
26
+ */
27
+ private resetStickyColumn;
28
+ /**
29
+ * Get the target column for vertical movement, using sticky column if set
30
+ */
31
+ private getTargetColumn;
32
+ /**
33
+ * Move cursor left
34
+ * Properly handles Unicode surrogate pairs (emoji, etc.)
35
+ */
36
+ moveLeft(extend?: boolean): void;
37
+ /**
38
+ * Move cursor right
39
+ * Properly handles Unicode surrogate pairs (emoji, etc.)
40
+ */
41
+ moveRight(extend?: boolean): void;
42
+ /**
43
+ * Move cursor up
44
+ * Uses sticky column to preserve horizontal position through lines of varying lengths
45
+ */
46
+ moveUp(extend?: boolean): void;
47
+ /**
48
+ * Move cursor down
49
+ * Uses sticky column to preserve horizontal position through lines of varying lengths
50
+ */
51
+ moveDown(extend?: boolean): void;
52
+ /**
53
+ * Move to start of line
54
+ */
55
+ moveToLineStart(extend?: boolean): void;
56
+ /**
57
+ * Move to end of line
58
+ */
59
+ moveToLineEnd(extend?: boolean): void;
60
+ /**
61
+ * Move to start of previous word
62
+ * Properly handles Unicode surrogate pairs (emoji, etc.)
63
+ */
64
+ moveWordLeft(extend?: boolean): void;
65
+ /**
66
+ * Move to start of next word
67
+ * Properly handles Unicode surrogate pairs (emoji, etc.)
68
+ */
69
+ moveWordRight(extend?: boolean): void;
70
+ /**
71
+ * Move to start of document
72
+ */
73
+ moveToDocumentStart(extend?: boolean): void;
74
+ /**
75
+ * Move to end of document
76
+ */
77
+ moveToDocumentEnd(extend?: boolean): void;
78
+ /**
79
+ * Move up by a page
80
+ */
81
+ movePageUp(pageSize: number, extend?: boolean): void;
82
+ /**
83
+ * Move down by a page
84
+ */
85
+ movePageDown(pageSize: number, extend?: boolean): void;
86
+ /**
87
+ * Select current word
88
+ * Properly handles Unicode surrogate pairs (emoji, etc.)
89
+ */
90
+ selectWord(): void;
91
+ /**
92
+ * Select current line
93
+ */
94
+ selectLine(): void;
95
+ /**
96
+ * Move to position, optionally extending selection
97
+ */
98
+ private moveTo;
99
+ /**
100
+ * Get position from mouse coordinates
101
+ */
102
+ positionFromPoint(x: number, y: number, lineHeight: number, charWidth: number, scrollTop: number, scrollLeft: number, paddingLeft: number, gutterWidth: number): Position;
103
+ }
104
+ /**
105
+ * Create navigation helper
106
+ */
107
+ export declare function createNavigation(state: EditorState): Navigation;