@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,672 @@
1
+ /**
2
+ * Code folding detection and management
3
+ *
4
+ * Supports multiple folding strategies:
5
+ * - Indentation-based (Python, YAML)
6
+ * - Bracket-based (JavaScript, TypeScript, JSON, etc.)
7
+ * - Region markers (#region / #endregion)
8
+ */
9
+ const DEFAULT_CONFIG = {
10
+ brackets: true,
11
+ indentation: true,
12
+ regions: true,
13
+ comments: true,
14
+ minLines: 2
15
+ };
16
+ /**
17
+ * Bracket pairs for different languages
18
+ * Note: HTML/XML use tag-based folding, not simple bracket matching
19
+ */
20
+ const BRACKET_PAIRS = {
21
+ default: [
22
+ ['{', '}'],
23
+ ['[', ']'],
24
+ ['(', ')']
25
+ ]
26
+ };
27
+ /**
28
+ * HTML5 void elements that don't need closing tags
29
+ */
30
+ const HTML_VOID_ELEMENTS = new Set([
31
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
32
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
33
+ ]);
34
+ /**
35
+ * Extract tag name from an opening or closing tag
36
+ * Returns null if not a valid tag
37
+ */
38
+ function extractTagName(text, startPos) {
39
+ if (text[startPos] !== '<')
40
+ return null;
41
+ let pos = startPos + 1;
42
+ const isClosing = text[pos] === '/';
43
+ if (isClosing)
44
+ pos++;
45
+ // Skip whitespace
46
+ while (pos < text.length && /\s/.test(text[pos]))
47
+ pos++;
48
+ // Extract tag name (letters, digits, hyphens, underscores, colons for namespaces)
49
+ let name = '';
50
+ while (pos < text.length && /[a-zA-Z0-9\-_:]/.test(text[pos])) {
51
+ name += text[pos].toLowerCase();
52
+ pos++;
53
+ }
54
+ if (!name)
55
+ return null;
56
+ // Find the end of the tag
57
+ let inAttrString = false;
58
+ let attrStringChar = '';
59
+ while (pos < text.length) {
60
+ const char = text[pos];
61
+ if (!inAttrString) {
62
+ if (char === '"' || char === "'") {
63
+ inAttrString = true;
64
+ attrStringChar = char;
65
+ }
66
+ else if (char === '>') {
67
+ const isSelfClosing = text[pos - 1] === '/' || HTML_VOID_ELEMENTS.has(name);
68
+ return { name, isClosing, endPos: pos, isSelfClosing };
69
+ }
70
+ }
71
+ else if (char === attrStringChar) {
72
+ // Check for escaped quotes
73
+ let backslashes = 0;
74
+ let checkPos = pos - 1;
75
+ while (checkPos >= 0 && text[checkPos] === '\\') {
76
+ backslashes++;
77
+ checkPos--;
78
+ }
79
+ if (backslashes % 2 === 0) {
80
+ inAttrString = false;
81
+ attrStringChar = '';
82
+ }
83
+ }
84
+ pos++;
85
+ }
86
+ return null; // Tag not properly closed on this line
87
+ }
88
+ /**
89
+ * Detect fold regions based on HTML/XML tag matching
90
+ * Properly pairs opening tags with their corresponding closing tags
91
+ */
92
+ function detectHtmlTagFolds(lines, minLines = 2) {
93
+ const regions = [];
94
+ const tagStack = [];
95
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
96
+ const lineText = lines[lineNum].text;
97
+ let inComment = false;
98
+ let pos = 0;
99
+ while (pos < lineText.length) {
100
+ // Skip HTML comments
101
+ if (lineText.slice(pos, pos + 4) === '<!--') {
102
+ const endComment = lineText.indexOf('-->', pos + 4);
103
+ if (endComment >= 0) {
104
+ pos = endComment + 3;
105
+ continue;
106
+ }
107
+ else {
108
+ inComment = true;
109
+ break;
110
+ }
111
+ }
112
+ if (lineText[pos] === '<') {
113
+ const tagInfo = extractTagName(lineText, pos);
114
+ if (tagInfo) {
115
+ if (tagInfo.isClosing) {
116
+ // Find matching opening tag (search from top of stack)
117
+ for (let i = tagStack.length - 1; i >= 0; i--) {
118
+ if (tagStack[i].name === tagInfo.name) {
119
+ const openTag = tagStack[i];
120
+ // Remove all tags from this point up (handles malformed HTML)
121
+ tagStack.splice(i);
122
+ // Create fold region if spans multiple lines
123
+ if (lineNum - openTag.line >= minLines) {
124
+ regions.push({
125
+ startLine: openTag.line,
126
+ endLine: lineNum,
127
+ level: openTag.level,
128
+ type: 'bracket',
129
+ collapsed: false
130
+ });
131
+ }
132
+ break;
133
+ }
134
+ }
135
+ }
136
+ else if (!tagInfo.isSelfClosing) {
137
+ // Opening tag - push to stack
138
+ tagStack.push({
139
+ name: tagInfo.name,
140
+ line: lineNum,
141
+ column: pos,
142
+ level: tagStack.length
143
+ });
144
+ }
145
+ pos = tagInfo.endPos + 1;
146
+ continue;
147
+ }
148
+ }
149
+ pos++;
150
+ }
151
+ }
152
+ return regions;
153
+ }
154
+ /**
155
+ * Region markers for different languages
156
+ */
157
+ const REGION_MARKERS = {
158
+ default: {
159
+ start: /^\s*(?:\/\/|#|--|\/\*)\s*#?region\b/i,
160
+ end: /^\s*(?:\/\/|#|--|\/\*)\s*#?endregion\b/i
161
+ },
162
+ python: {
163
+ start: /^\s*#\s*region\b/i,
164
+ end: /^\s*#\s*endregion\b/i
165
+ },
166
+ html: {
167
+ start: /^\s*<!--\s*#?region\b/i,
168
+ end: /^\s*<!--\s*#?endregion\b/i
169
+ }
170
+ };
171
+ /**
172
+ * Get the indentation level of a line (number of leading spaces/tabs)
173
+ */
174
+ function getIndentLevel(text, tabSize = 4) {
175
+ let level = 0;
176
+ for (const char of text) {
177
+ if (char === ' ') {
178
+ level++;
179
+ }
180
+ else if (char === '\t') {
181
+ level += tabSize;
182
+ }
183
+ else {
184
+ break;
185
+ }
186
+ }
187
+ return level;
188
+ }
189
+ /**
190
+ * Check if a line is effectively empty (whitespace only)
191
+ */
192
+ function isEmptyLine(text) {
193
+ return text.trim().length === 0;
194
+ }
195
+ /**
196
+ * Detect fold regions based on indentation
197
+ *
198
+ * Single pass: maintain a stack of "open" indentation blocks. When a non-empty
199
+ * line is less-or-equally indented than the block on top of the stack, that
200
+ * block closes at the last non-empty line seen before it. This replaces the
201
+ * previous O(n^2) approach (which re-scanned forward from every line).
202
+ */
203
+ function detectIndentationFolds(lines, tabSize = 4, minLines = 2) {
204
+ const regions = [];
205
+ const lineCount = lines.length;
206
+ const stack = [];
207
+ let lastNonEmptyLine = -1;
208
+ const closeBlock = (block) => {
209
+ // The block spans from its header down to the last non-empty line that
210
+ // belonged to it (lastNonEmptyLine, captured before the dedent).
211
+ if (lastNonEmptyLine - block.startLine >= minLines) {
212
+ regions.push({
213
+ startLine: block.startLine,
214
+ endLine: lastNonEmptyLine,
215
+ level: block.indent,
216
+ type: 'indentation',
217
+ collapsed: false
218
+ });
219
+ }
220
+ };
221
+ for (let i = 0; i < lineCount; i++) {
222
+ const line = lines[i];
223
+ // Empty lines do not change indentation structure; they are absorbed into
224
+ // the surrounding block (handled because we only close on dedent at a
225
+ // non-empty line, and endLine tracks lastNonEmptyLine).
226
+ if (isEmptyLine(line.text))
227
+ continue;
228
+ const indent = getIndentLevel(line.text, tabSize);
229
+ // Close any blocks whose header is at >= this indentation: this line is a
230
+ // sibling or an outdent, so those blocks ended at lastNonEmptyLine.
231
+ while (stack.length > 0 && indent <= stack[stack.length - 1].indent) {
232
+ closeBlock(stack.pop());
233
+ }
234
+ // This line becomes a potential block header for deeper-indented lines.
235
+ stack.push({ startLine: i, indent });
236
+ lastNonEmptyLine = i;
237
+ }
238
+ // Close any remaining open blocks at end of document.
239
+ while (stack.length > 0) {
240
+ closeBlock(stack.pop());
241
+ }
242
+ return regions;
243
+ }
244
+ /**
245
+ * Detect fold regions based on bracket matching
246
+ */
247
+ function detectBracketFolds(lines, language = 'default', minLines = 2) {
248
+ const regions = [];
249
+ const pairs = BRACKET_PAIRS[language] || BRACKET_PAIRS.default;
250
+ const stack = [];
251
+ // Block-comment state must persist across lines: a `/*` on one line keeps us
252
+ // "inside a comment" until a matching `*/` is found, even many lines later.
253
+ let inBlockComment = false;
254
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
255
+ const lineText = lines[lineNum].text;
256
+ let inString = false;
257
+ let stringChar = '';
258
+ let inLineComment = false;
259
+ for (let col = 0; col < lineText.length; col++) {
260
+ const char = lineText[col];
261
+ // Inside a block comment: only look for the terminating `*/`.
262
+ if (inBlockComment) {
263
+ if (char === '*' && lineText[col + 1] === '/') {
264
+ inBlockComment = false;
265
+ col++; // consume the '/'
266
+ }
267
+ continue;
268
+ }
269
+ // Handle string detection with proper escape handling
270
+ // Count preceding backslashes: odd = escaped, even = not escaped
271
+ if (char === '"' || char === "'" || char === '`') {
272
+ let backslashCount = 0;
273
+ let checkPos = col - 1;
274
+ while (checkPos >= 0 && lineText[checkPos] === '\\') {
275
+ backslashCount++;
276
+ checkPos--;
277
+ }
278
+ // Quote is escaped only if preceded by odd number of backslashes
279
+ const isEscaped = backslashCount % 2 === 1;
280
+ if (!isEscaped) {
281
+ if (!inString) {
282
+ inString = true;
283
+ stringChar = char;
284
+ }
285
+ else if (char === stringChar) {
286
+ inString = false;
287
+ stringChar = '';
288
+ }
289
+ }
290
+ continue;
291
+ }
292
+ if (inString)
293
+ continue;
294
+ // Handle block comment start (outside strings).
295
+ if (char === '/' && lineText[col + 1] === '*') {
296
+ inBlockComment = true;
297
+ col++; // consume the '*'
298
+ continue;
299
+ }
300
+ // Handle line comments
301
+ if (char === '/' && lineText[col + 1] === '/') {
302
+ inLineComment = true;
303
+ break;
304
+ }
305
+ if (inLineComment)
306
+ continue;
307
+ // Check for opening brackets
308
+ for (const [open, close] of pairs) {
309
+ if (char === open) {
310
+ stack.push({
311
+ char: open,
312
+ closingChar: close,
313
+ line: lineNum,
314
+ column: col
315
+ });
316
+ }
317
+ else if (char === close && stack.length > 0) {
318
+ // Only match if top of stack is the correct closing bracket
319
+ // This ensures proper nesting (e.g., `{ ( } )` won't match incorrectly)
320
+ const topBracket = stack[stack.length - 1];
321
+ if (topBracket.closingChar === close) {
322
+ stack.pop();
323
+ // Create fold region if spans multiple lines
324
+ if (lineNum - topBracket.line >= minLines) {
325
+ regions.push({
326
+ startLine: topBracket.line,
327
+ endLine: lineNum,
328
+ level: stack.length, // Use current stack depth for level
329
+ type: 'bracket',
330
+ collapsed: false
331
+ });
332
+ }
333
+ }
334
+ // If mismatch, ignore the closing bracket (unbalanced brackets)
335
+ }
336
+ }
337
+ }
338
+ }
339
+ return regions;
340
+ }
341
+ /**
342
+ * Detect fold regions based on region markers
343
+ */
344
+ function detectRegionFolds(lines, language = 'default') {
345
+ const regions = [];
346
+ const markers = REGION_MARKERS[language] || REGION_MARKERS.default;
347
+ const stack = [];
348
+ let level = 0;
349
+ for (let i = 0; i < lines.length; i++) {
350
+ const lineText = lines[i].text;
351
+ if (markers.start.test(lineText)) {
352
+ stack.push({ line: i, level: level++ });
353
+ }
354
+ else if (markers.end.test(lineText) && stack.length > 0) {
355
+ const start = stack.pop();
356
+ level--;
357
+ regions.push({
358
+ startLine: start.line,
359
+ endLine: i,
360
+ level: start.level,
361
+ type: 'region',
362
+ collapsed: false
363
+ });
364
+ }
365
+ }
366
+ return regions;
367
+ }
368
+ /**
369
+ * Detect fold regions for multi-line comments
370
+ */
371
+ function detectCommentFolds(lines, minLines = 2) {
372
+ const regions = [];
373
+ let commentStart = -1;
374
+ for (let i = 0; i < lines.length; i++) {
375
+ const lineText = lines[i].text.trim();
376
+ // Block comment start
377
+ if (lineText.startsWith('/*') && !lineText.includes('*/')) {
378
+ commentStart = i;
379
+ }
380
+ // Block comment end
381
+ else if (commentStart >= 0 && lineText.includes('*/')) {
382
+ if (i - commentStart >= minLines) {
383
+ regions.push({
384
+ startLine: commentStart,
385
+ endLine: i,
386
+ level: 0,
387
+ type: 'comment',
388
+ collapsed: false
389
+ });
390
+ }
391
+ commentStart = -1;
392
+ }
393
+ // HTML comment
394
+ else if (lineText.startsWith('<!--') && !lineText.includes('-->')) {
395
+ commentStart = i;
396
+ }
397
+ else if (commentStart >= 0 && lineText.includes('-->')) {
398
+ if (i - commentStart >= minLines) {
399
+ regions.push({
400
+ startLine: commentStart,
401
+ endLine: i,
402
+ level: 0,
403
+ type: 'comment',
404
+ collapsed: false
405
+ });
406
+ }
407
+ commentStart = -1;
408
+ }
409
+ }
410
+ return regions;
411
+ }
412
+ /**
413
+ * Detect all fold regions in a document
414
+ */
415
+ export function detectFoldRegions(lines, language = 'plaintext', config = {}) {
416
+ const cfg = { ...DEFAULT_CONFIG, ...config };
417
+ const allRegions = [];
418
+ // Detect different types of fold regions
419
+ if (cfg.brackets) {
420
+ const langLower = language.toLowerCase();
421
+ // Pure HTML/XML use tag-based folding only
422
+ const pureMarkupLanguages = ['html', 'xml', 'xhtml', 'svg'];
423
+ // Mixed languages use both tag and bracket folding
424
+ const mixedLanguages = ['vue', 'svelte', 'jsx', 'tsx'];
425
+ if (pureMarkupLanguages.includes(langLower)) {
426
+ allRegions.push(...detectHtmlTagFolds(lines, cfg.minLines));
427
+ }
428
+ else if (mixedLanguages.includes(langLower)) {
429
+ // Mixed languages: detect both HTML tags and JS/TS braces
430
+ allRegions.push(...detectHtmlTagFolds(lines, cfg.minLines));
431
+ allRegions.push(...detectBracketFolds(lines, 'default', cfg.minLines));
432
+ }
433
+ else {
434
+ allRegions.push(...detectBracketFolds(lines, langLower, cfg.minLines));
435
+ }
436
+ }
437
+ if (cfg.indentation) {
438
+ // For languages like Python, prioritize indentation
439
+ const indentLanguages = ['python', 'yaml', 'yml', 'haml', 'slim', 'pug', 'jade'];
440
+ if (indentLanguages.includes(language.toLowerCase())) {
441
+ allRegions.push(...detectIndentationFolds(lines, 4, cfg.minLines));
442
+ }
443
+ }
444
+ if (cfg.regions) {
445
+ allRegions.push(...detectRegionFolds(lines, language));
446
+ }
447
+ if (cfg.comments) {
448
+ allRegions.push(...detectCommentFolds(lines, cfg.minLines));
449
+ }
450
+ // Sort by start line, then by length (longer regions first for nesting)
451
+ allRegions.sort((a, b) => {
452
+ if (a.startLine !== b.startLine) {
453
+ return a.startLine - b.startLine;
454
+ }
455
+ return (b.endLine - b.startLine) - (a.endLine - a.startLine);
456
+ });
457
+ // Remove overlapping regions (keep the first/largest one)
458
+ const filteredRegions = [];
459
+ const coveredLines = new Set();
460
+ for (const region of allRegions) {
461
+ // Check if this region's start line is already covered
462
+ if (coveredLines.has(region.startLine)) {
463
+ continue;
464
+ }
465
+ filteredRegions.push(region);
466
+ // Mark start line as covered (we can still have nested folds with different start lines)
467
+ coveredLines.add(region.startLine);
468
+ }
469
+ return filteredRegions;
470
+ }
471
+ /**
472
+ * Fold state manager
473
+ */
474
+ export class FoldManager {
475
+ regions = [];
476
+ collapsedLines = new Set();
477
+ listeners = new Set();
478
+ /**
479
+ * Update fold regions based on document content
480
+ */
481
+ updateRegions(lines, language, config) {
482
+ // Preserve collapsed state for regions that still exist
483
+ const wasCollapsed = new Set();
484
+ for (const region of this.regions) {
485
+ if (region.collapsed) {
486
+ wasCollapsed.add(region.startLine);
487
+ }
488
+ }
489
+ this.regions = detectFoldRegions(lines, language, config);
490
+ // Restore collapsed state
491
+ for (const region of this.regions) {
492
+ if (wasCollapsed.has(region.startLine)) {
493
+ region.collapsed = true;
494
+ }
495
+ }
496
+ this.updateCollapsedLines();
497
+ }
498
+ /**
499
+ * Get all fold regions
500
+ */
501
+ getRegions() {
502
+ return this.regions;
503
+ }
504
+ /**
505
+ * Get fold region at a specific line (if it starts there)
506
+ */
507
+ getRegionAtLine(line) {
508
+ return this.regions.find(r => r.startLine === line);
509
+ }
510
+ /**
511
+ * Check if a line is hidden (inside a collapsed fold)
512
+ */
513
+ isLineHidden(line) {
514
+ return this.collapsedLines.has(line);
515
+ }
516
+ /**
517
+ * Check if a line has a fold indicator
518
+ */
519
+ hasFoldIndicator(line) {
520
+ return this.regions.some(r => r.startLine === line);
521
+ }
522
+ /**
523
+ * Check if the fold at this line is collapsed
524
+ */
525
+ isFoldCollapsed(line) {
526
+ const region = this.getRegionAtLine(line);
527
+ return region?.collapsed ?? false;
528
+ }
529
+ /**
530
+ * Toggle fold at a line
531
+ */
532
+ toggleFold(line) {
533
+ const region = this.getRegionAtLine(line);
534
+ if (!region)
535
+ return false;
536
+ region.collapsed = !region.collapsed;
537
+ this.updateCollapsedLines();
538
+ this.emit();
539
+ return true;
540
+ }
541
+ /**
542
+ * Collapse fold at a line
543
+ */
544
+ collapse(line) {
545
+ const region = this.getRegionAtLine(line);
546
+ if (!region || region.collapsed)
547
+ return false;
548
+ region.collapsed = true;
549
+ this.updateCollapsedLines();
550
+ this.emit();
551
+ return true;
552
+ }
553
+ /**
554
+ * Expand fold at a line
555
+ */
556
+ expand(line) {
557
+ const region = this.getRegionAtLine(line);
558
+ if (!region || !region.collapsed)
559
+ return false;
560
+ region.collapsed = false;
561
+ this.updateCollapsedLines();
562
+ this.emit();
563
+ return true;
564
+ }
565
+ /**
566
+ * Collapse all folds
567
+ */
568
+ collapseAll() {
569
+ for (const region of this.regions) {
570
+ region.collapsed = true;
571
+ }
572
+ this.updateCollapsedLines();
573
+ this.emit();
574
+ }
575
+ /**
576
+ * Expand all folds
577
+ */
578
+ expandAll() {
579
+ for (const region of this.regions) {
580
+ region.collapsed = false;
581
+ }
582
+ this.collapsedLines.clear();
583
+ this.emit();
584
+ }
585
+ /**
586
+ * Collapse folds at a specific nesting level
587
+ */
588
+ collapseLevel(level) {
589
+ for (const region of this.regions) {
590
+ if (region.level === level) {
591
+ region.collapsed = true;
592
+ }
593
+ }
594
+ this.updateCollapsedLines();
595
+ this.emit();
596
+ }
597
+ /**
598
+ * Get visible lines (line numbers that should be rendered)
599
+ */
600
+ getVisibleLines(totalLines) {
601
+ const visible = [];
602
+ for (let i = 0; i < totalLines; i++) {
603
+ if (!this.collapsedLines.has(i)) {
604
+ visible.push(i);
605
+ }
606
+ }
607
+ return visible;
608
+ }
609
+ /**
610
+ * Get the number of hidden lines after a fold start
611
+ */
612
+ getHiddenLineCount(startLine) {
613
+ const region = this.getRegionAtLine(startLine);
614
+ if (!region || !region.collapsed)
615
+ return 0;
616
+ return region.endLine - region.startLine;
617
+ }
618
+ /**
619
+ * Update the set of collapsed (hidden) lines
620
+ */
621
+ updateCollapsedLines() {
622
+ this.collapsedLines.clear();
623
+ for (const region of this.regions) {
624
+ if (region.collapsed) {
625
+ // Hide all lines except the first one
626
+ for (let i = region.startLine + 1; i <= region.endLine; i++) {
627
+ this.collapsedLines.add(i);
628
+ }
629
+ }
630
+ }
631
+ }
632
+ /** Maximum listeners to prevent memory leaks */
633
+ static MAX_LISTENERS = 100;
634
+ /**
635
+ * Subscribe to fold changes
636
+ * In development mode, throws an error to catch memory leaks early.
637
+ * In production, warns and evicts the oldest listener.
638
+ */
639
+ onChange(callback) {
640
+ if (this.listeners.size >= FoldManager.MAX_LISTENERS) {
641
+ const msg = `[FoldManager] Maximum listener count (${FoldManager.MAX_LISTENERS}) exceeded. Possible memory leak - ensure listeners are unsubscribed.`;
642
+ // Fail fast in development to catch bugs early
643
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {
644
+ throw new Error(msg);
645
+ }
646
+ // In production, warn and auto-cleanup oldest listener
647
+ console.warn(msg);
648
+ const firstListener = this.listeners.values().next().value;
649
+ if (firstListener) {
650
+ this.listeners.delete(firstListener);
651
+ }
652
+ }
653
+ this.listeners.add(callback);
654
+ return () => this.listeners.delete(callback);
655
+ }
656
+ emit() {
657
+ for (const listener of this.listeners) {
658
+ try {
659
+ listener();
660
+ }
661
+ catch (error) {
662
+ console.error('[FoldManager] Listener callback failed:', error);
663
+ }
664
+ }
665
+ }
666
+ }
667
+ /**
668
+ * Create a new fold manager
669
+ */
670
+ export function createFoldManager() {
671
+ return new FoldManager();
672
+ }