@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.
- package/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/components/agents/AgentActivityPanel.svelte +565 -0
- package/dist/components/agents/AgentActivityPanel.svelte.d.ts +24 -0
- package/dist/components/agents/AgentAvatar.svelte +417 -0
- package/dist/components/agents/AgentAvatar.svelte.d.ts +23 -0
- package/dist/components/agents/AgentCursor.svelte +224 -0
- package/dist/components/agents/AgentCursor.svelte.d.ts +35 -0
- package/dist/components/agents/AgentPresenceBar.svelte +261 -0
- package/dist/components/agents/AgentPresenceBar.svelte.d.ts +20 -0
- package/dist/components/agents/index.d.ts +4 -0
- package/dist/components/agents/index.js +5 -0
- package/dist/components/ai/AIConversationList.svelte +524 -0
- package/dist/components/ai/AIConversationList.svelte.d.ts +17 -0
- package/dist/components/ai/AIEditPreview.svelte +132 -0
- package/dist/components/ai/AIEditPreview.svelte.d.ts +8 -0
- package/dist/components/ai/AIInlineEdit.svelte +155 -0
- package/dist/components/ai/AIInlineEdit.svelte.d.ts +10 -0
- package/dist/components/ai/AIMessage.svelte +239 -0
- package/dist/components/ai/AIMessage.svelte.d.ts +13 -0
- package/dist/components/ai/AIMessageActions.svelte +176 -0
- package/dist/components/ai/AIMessageActions.svelte.d.ts +12 -0
- package/dist/components/ai/AIMessageContent.svelte +355 -0
- package/dist/components/ai/AIMessageContent.svelte.d.ts +7 -0
- package/dist/components/ai/AIPanel.svelte +561 -0
- package/dist/components/ai/AIPanel.svelte.d.ts +7 -0
- package/dist/components/ai/AISuggestionWidget.svelte +132 -0
- package/dist/components/ai/AISuggestionWidget.svelte.d.ts +10 -0
- package/dist/components/ai/AIToolCallDisplay.svelte +317 -0
- package/dist/components/ai/AIToolCallDisplay.svelte.d.ts +12 -0
- package/dist/components/ai/index.d.ts +9 -0
- package/dist/components/ai/index.js +10 -0
- package/dist/components/core/Avatar.svelte +110 -0
- package/dist/components/core/Avatar.svelte.d.ts +12 -0
- package/dist/components/core/Badge.svelte +98 -0
- package/dist/components/core/Badge.svelte.d.ts +11 -0
- package/dist/components/core/Button.svelte +175 -0
- package/dist/components/core/Button.svelte.d.ts +18 -0
- package/dist/components/core/ConnectionStatus.svelte +294 -0
- package/dist/components/core/ConnectionStatus.svelte.d.ts +20 -0
- package/dist/components/core/ContextMenu.svelte +176 -0
- package/dist/components/core/ContextMenu.svelte.d.ts +19 -0
- package/dist/components/core/ErrorBoundary.svelte +277 -0
- package/dist/components/core/ErrorBoundary.svelte.d.ts +23 -0
- package/dist/components/core/Icon.svelte +107 -0
- package/dist/components/core/Icon.svelte.d.ts +8 -0
- package/dist/components/core/Input.svelte +138 -0
- package/dist/components/core/Input.svelte.d.ts +20 -0
- package/dist/components/core/Kbd.svelte +34 -0
- package/dist/components/core/Kbd.svelte.d.ts +7 -0
- package/dist/components/core/ResizeHandle.svelte +200 -0
- package/dist/components/core/ResizeHandle.svelte.d.ts +23 -0
- package/dist/components/core/Spinner.svelte +35 -0
- package/dist/components/core/Spinner.svelte.d.ts +7 -0
- package/dist/components/core/Textarea.svelte +112 -0
- package/dist/components/core/Textarea.svelte.d.ts +18 -0
- package/dist/components/core/Tooltip.svelte +103 -0
- package/dist/components/core/Tooltip.svelte.d.ts +11 -0
- package/dist/components/core/index.d.ts +13 -0
- package/dist/components/core/index.js +14 -0
- package/dist/components/editor/AIFocusLayer.svelte +430 -0
- package/dist/components/editor/AIFocusLayer.svelte.d.ts +32 -0
- package/dist/components/editor/Breadcrumbs.svelte +435 -0
- package/dist/components/editor/Breadcrumbs.svelte.d.ts +33 -0
- package/dist/components/editor/BreakpointLayer.svelte +642 -0
- package/dist/components/editor/BreakpointLayer.svelte.d.ts +20 -0
- package/dist/components/editor/CognitiveLoadMeter.svelte +324 -0
- package/dist/components/editor/CognitiveLoadMeter.svelte.d.ts +18 -0
- package/dist/components/editor/CollaborativeEditor.svelte +218 -0
- package/dist/components/editor/CollaborativeEditor.svelte.d.ts +32 -0
- package/dist/components/editor/CommandPalette.svelte +434 -0
- package/dist/components/editor/CommandPalette.svelte.d.ts +11 -0
- package/dist/components/editor/ComplexityLayer.svelte +293 -0
- package/dist/components/editor/ComplexityLayer.svelte.d.ts +23 -0
- package/dist/components/editor/ConflictZoneLayer.svelte +441 -0
- package/dist/components/editor/ConflictZoneLayer.svelte.d.ts +25 -0
- package/dist/components/editor/ContextLens.svelte +262 -0
- package/dist/components/editor/ContextLens.svelte.d.ts +27 -0
- package/dist/components/editor/CustomEditor.svelte +1242 -0
- package/dist/components/editor/CustomEditor.svelte.d.ts +37 -0
- package/dist/components/editor/DebugConsole.svelte +646 -0
- package/dist/components/editor/DebugConsole.svelte.d.ts +41 -0
- package/dist/components/editor/EchoCursorLayer.svelte +363 -0
- package/dist/components/editor/EchoCursorLayer.svelte.d.ts +24 -0
- package/dist/components/editor/Editor.svelte +61 -0
- package/dist/components/editor/Editor.svelte.d.ts +22 -0
- package/dist/components/editor/EditorGutter.svelte +119 -0
- package/dist/components/editor/EditorGutter.svelte.d.ts +19 -0
- package/dist/components/editor/EditorLines.svelte +182 -0
- package/dist/components/editor/EditorLines.svelte.d.ts +43 -0
- package/dist/components/editor/EditorPane.svelte +134 -0
- package/dist/components/editor/EditorPane.svelte.d.ts +9 -0
- package/dist/components/editor/EditorSelections.svelte +186 -0
- package/dist/components/editor/EditorSelections.svelte.d.ts +25 -0
- package/dist/components/editor/EditorTabs.svelte +170 -0
- package/dist/components/editor/EditorTabs.svelte.d.ts +12 -0
- package/dist/components/editor/FileExplorer.svelte +811 -0
- package/dist/components/editor/FileExplorer.svelte.d.ts +67 -0
- package/dist/components/editor/FileIcon.svelte +110 -0
- package/dist/components/editor/FileIcon.svelte.d.ts +10 -0
- package/dist/components/editor/FindReplace.svelte +448 -0
- package/dist/components/editor/FindReplace.svelte.d.ts +40 -0
- package/dist/components/editor/GhostBracketLayer.svelte +391 -0
- package/dist/components/editor/GhostBracketLayer.svelte.d.ts +24 -0
- package/dist/components/editor/GitBlameLayer.svelte +436 -0
- package/dist/components/editor/GitBlameLayer.svelte.d.ts +18 -0
- package/dist/components/editor/InlineDiagnosticsLayer.svelte +540 -0
- package/dist/components/editor/InlineDiagnosticsLayer.svelte.d.ts +35 -0
- package/dist/components/editor/InlineDiffLayer.svelte +337 -0
- package/dist/components/editor/InlineDiffLayer.svelte.d.ts +31 -0
- package/dist/components/editor/MinimalEditor.svelte +75 -0
- package/dist/components/editor/MinimalEditor.svelte.d.ts +6 -0
- package/dist/components/editor/MinimalEditor2.svelte +84 -0
- package/dist/components/editor/MinimalEditor2.svelte.d.ts +6 -0
- package/dist/components/editor/Minimap.svelte +327 -0
- package/dist/components/editor/Minimap.svelte.d.ts +34 -0
- package/dist/components/editor/PluginPreviewSandbox.svelte +793 -0
- package/dist/components/editor/PluginPreviewSandbox.svelte.d.ts +49 -0
- package/dist/components/editor/ProblemsPanel.svelte +628 -0
- package/dist/components/editor/ProblemsPanel.svelte.d.ts +25 -0
- package/dist/components/editor/QuickActionsMenu.svelte +403 -0
- package/dist/components/editor/QuickActionsMenu.svelte.d.ts +18 -0
- package/dist/components/editor/SnippetPalette.svelte +530 -0
- package/dist/components/editor/SnippetPalette.svelte.d.ts +16 -0
- package/dist/components/editor/StructureMap.svelte +431 -0
- package/dist/components/editor/StructureMap.svelte.d.ts +37 -0
- package/dist/components/editor/SymbolOutline.svelte +722 -0
- package/dist/components/editor/SymbolOutline.svelte.d.ts +44 -0
- package/dist/components/editor/TimelineScrubber.svelte +470 -0
- package/dist/components/editor/TimelineScrubber.svelte.d.ts +40 -0
- package/dist/components/editor/TokenRenderer.svelte +69 -0
- package/dist/components/editor/TokenRenderer.svelte.d.ts +15 -0
- package/dist/components/editor/constants.d.ts +32 -0
- package/dist/components/editor/constants.js +36 -0
- package/dist/components/editor/core/ai-awareness.d.ts +176 -0
- package/dist/components/editor/core/ai-awareness.js +210 -0
- package/dist/components/editor/core/bracket-healer.d.ts +189 -0
- package/dist/components/editor/core/bracket-healer.js +406 -0
- package/dist/components/editor/core/breakpoints.d.ts +203 -0
- package/dist/components/editor/core/breakpoints.js +414 -0
- package/dist/components/editor/core/commands.d.ts +108 -0
- package/dist/components/editor/core/commands.js +246 -0
- package/dist/components/editor/core/complexity-analyzer.d.ts +123 -0
- package/dist/components/editor/core/complexity-analyzer.js +376 -0
- package/dist/components/editor/core/conflict-predictor.d.ts +135 -0
- package/dist/components/editor/core/conflict-predictor.js +316 -0
- package/dist/components/editor/core/crdt-binding.d.ts +118 -0
- package/dist/components/editor/core/crdt-binding.js +286 -0
- package/dist/components/editor/core/diagnostics.d.ts +210 -0
- package/dist/components/editor/core/diagnostics.js +335 -0
- package/dist/components/editor/core/echo-cursor.d.ts +201 -0
- package/dist/components/editor/core/echo-cursor.js +267 -0
- package/dist/components/editor/core/folding.d.ts +124 -0
- package/dist/components/editor/core/folding.js +672 -0
- package/dist/components/editor/core/ghost-pair.d.ts +122 -0
- package/dist/components/editor/core/ghost-pair.js +221 -0
- package/dist/components/editor/core/git-blame.d.ts +170 -0
- package/dist/components/editor/core/git-blame.js +324 -0
- package/dist/components/editor/core/index.d.ts +26 -0
- package/dist/components/editor/core/index.js +24 -0
- package/dist/components/editor/core/keybindings.d.ts +79 -0
- package/dist/components/editor/core/keybindings.js +357 -0
- package/dist/components/editor/core/multi-cursor.d.ts +196 -0
- package/dist/components/editor/core/multi-cursor.js +521 -0
- package/dist/components/editor/core/navigation.d.ts +107 -0
- package/dist/components/editor/core/navigation.js +408 -0
- package/dist/components/editor/core/quick-actions.d.ts +189 -0
- package/dist/components/editor/core/quick-actions.js +427 -0
- package/dist/components/editor/core/search.d.ts +88 -0
- package/dist/components/editor/core/search.js +192 -0
- package/dist/components/editor/core/semantic-analyzer.d.ts +77 -0
- package/dist/components/editor/core/semantic-analyzer.js +424 -0
- package/dist/components/editor/core/snippet-manager.d.ts +202 -0
- package/dist/components/editor/core/snippet-manager.js +565 -0
- package/dist/components/editor/core/state.d.ts +367 -0
- package/dist/components/editor/core/state.js +900 -0
- package/dist/components/editor/core/timeline.d.ts +204 -0
- package/dist/components/editor/core/timeline.js +349 -0
- package/dist/components/editor/editor-find.d.ts +56 -0
- package/dist/components/editor/editor-find.js +148 -0
- package/dist/components/editor/editor-input.d.ts +77 -0
- package/dist/components/editor/editor-input.js +445 -0
- package/dist/components/editor/editor-multicursor.d.ts +21 -0
- package/dist/components/editor/editor-multicursor.js +196 -0
- package/dist/components/editor/editor-scroll.d.ts +14 -0
- package/dist/components/editor/editor-scroll.js +34 -0
- package/dist/components/editor/index.d.ts +15 -0
- package/dist/components/editor/index.js +21 -0
- package/dist/components/editor/languages.d.ts +62 -0
- package/dist/components/editor/languages.js +285 -0
- package/dist/components/editor/theme.d.ts +88 -0
- package/dist/components/editor/theme.js +139 -0
- package/dist/components/editor/tokenizer/base.d.ts +40 -0
- package/dist/components/editor/tokenizer/base.js +203 -0
- package/dist/components/editor/tokenizer/index.d.ts +56 -0
- package/dist/components/editor/tokenizer/index.js +215 -0
- package/dist/components/editor/tokenizer/languages/css.d.ts +17 -0
- package/dist/components/editor/tokenizer/languages/css.js +194 -0
- package/dist/components/editor/tokenizer/languages/go.d.ts +17 -0
- package/dist/components/editor/tokenizer/languages/go.js +220 -0
- package/dist/components/editor/tokenizer/languages/html.d.ts +24 -0
- package/dist/components/editor/tokenizer/languages/html.js +145 -0
- package/dist/components/editor/tokenizer/languages/javascript.d.ts +56 -0
- package/dist/components/editor/tokenizer/languages/javascript.js +452 -0
- package/dist/components/editor/tokenizer/languages/json.d.ts +12 -0
- package/dist/components/editor/tokenizer/languages/json.js +91 -0
- package/dist/components/editor/tokenizer/languages/markdown.d.ts +16 -0
- package/dist/components/editor/tokenizer/languages/markdown.js +156 -0
- package/dist/components/editor/tokenizer/languages/python.d.ts +20 -0
- package/dist/components/editor/tokenizer/languages/python.js +227 -0
- package/dist/components/editor/tokenizer/languages/svelte.d.ts +40 -0
- package/dist/components/editor/tokenizer/languages/svelte.js +326 -0
- package/dist/components/editor/tokenizer/types.d.ts +86 -0
- package/dist/components/editor/tokenizer/types.js +4 -0
- package/dist/components/layout/IDELayout.svelte +274 -0
- package/dist/components/layout/IDELayout.svelte.d.ts +29 -0
- package/dist/components/layout/StatusBar.svelte +511 -0
- package/dist/components/layout/StatusBar.svelte.d.ts +47 -0
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.js +3 -0
- package/dist/components/lsp/AutocompleteWidget.svelte +364 -0
- package/dist/components/lsp/AutocompleteWidget.svelte.d.ts +33 -0
- package/dist/components/lsp/DiagnosticMarker.svelte +166 -0
- package/dist/components/lsp/DiagnosticMarker.svelte.d.ts +19 -0
- package/dist/components/lsp/DiagnosticsPanel.svelte +388 -0
- package/dist/components/lsp/DiagnosticsPanel.svelte.d.ts +21 -0
- package/dist/components/lsp/HoverTooltip.svelte +274 -0
- package/dist/components/lsp/HoverTooltip.svelte.d.ts +24 -0
- package/dist/components/lsp/LSPEditor.svelte +486 -0
- package/dist/components/lsp/LSPEditor.svelte.d.ts +39 -0
- package/dist/components/lsp/SignatureHelpWidget.svelte +216 -0
- package/dist/components/lsp/SignatureHelpWidget.svelte.d.ts +22 -0
- package/dist/components/lsp/index.d.ts +6 -0
- package/dist/components/lsp/index.js +7 -0
- package/dist/components/plugins/PluginCard.svelte +153 -0
- package/dist/components/plugins/PluginCard.svelte.d.ts +19 -0
- package/dist/components/plugins/PluginPanel.svelte +280 -0
- package/dist/components/plugins/PluginPanel.svelte.d.ts +8 -0
- package/dist/components/plugins/PluginProposalForm.svelte +250 -0
- package/dist/components/plugins/PluginProposalForm.svelte.d.ts +6 -0
- package/dist/components/plugins/PluginStatusBadge.svelte +14 -0
- package/dist/components/plugins/PluginStatusBadge.svelte.d.ts +8 -0
- package/dist/components/plugins/index.d.ts +4 -0
- package/dist/components/plugins/index.js +5 -0
- package/dist/components/vfs/LockConflictDialog.svelte +705 -0
- package/dist/components/vfs/LockConflictDialog.svelte.d.ts +21 -0
- package/dist/components/vfs/LockIndicator.svelte +194 -0
- package/dist/components/vfs/LockIndicator.svelte.d.ts +29 -0
- package/dist/components/vfs/LockOverlay.svelte +344 -0
- package/dist/components/vfs/LockOverlay.svelte.d.ts +17 -0
- package/dist/components/vfs/VersionConflictDialog.svelte +549 -0
- package/dist/components/vfs/VersionConflictDialog.svelte.d.ts +24 -0
- package/dist/components/vfs/index.d.ts +4 -0
- package/dist/components/vfs/index.js +5 -0
- package/dist/crdt/awareness.d.ts +42 -0
- package/dist/crdt/awareness.js +109 -0
- package/dist/crdt/document.d.ts +101 -0
- package/dist/crdt/document.js +187 -0
- package/dist/crdt/index.d.ts +9 -0
- package/dist/crdt/index.js +8 -0
- package/dist/crdt/provider.d.ts +85 -0
- package/dist/crdt/provider.js +150 -0
- package/dist/crdt/types.d.ts +61 -0
- package/dist/crdt/types.js +4 -0
- package/dist/crdt/undo.d.ts +34 -0
- package/dist/crdt/undo.js +70 -0
- package/dist/index.d.ts +277 -0
- package/dist/index.js +280 -0
- package/dist/plugins/index.d.ts +103 -0
- package/dist/plugins/index.js +153 -0
- package/dist/services/error-handling.d.ts +95 -0
- package/dist/services/error-handling.js +413 -0
- package/dist/services/ide-integration.d.ts +83 -0
- package/dist/services/ide-integration.js +367 -0
- package/dist/services/lsp-client.d.ts +69 -0
- package/dist/services/lsp-client.js +667 -0
- package/dist/services/mock-ai.d.ts +37 -0
- package/dist/services/mock-ai.js +318 -0
- package/dist/services/optimistic.d.ts +141 -0
- package/dist/services/optimistic.js +367 -0
- package/dist/services/vfs-client.d.ts +81 -0
- package/dist/services/vfs-client.js +348 -0
- package/dist/stores/agents.svelte.d.ts +85 -0
- package/dist/stores/agents.svelte.js +459 -0
- package/dist/stores/ai-persistence.svelte.d.ts +76 -0
- package/dist/stores/ai-persistence.svelte.js +334 -0
- package/dist/stores/ai.svelte.d.ts +140 -0
- package/dist/stores/ai.svelte.js +383 -0
- package/dist/stores/collaboration.svelte.d.ts +164 -0
- package/dist/stores/collaboration.svelte.js +334 -0
- package/dist/stores/editor.svelte.d.ts +131 -0
- package/dist/stores/editor.svelte.js +250 -0
- package/dist/stores/index.d.ts +10 -0
- package/dist/stores/index.js +29 -0
- package/dist/stores/layout.svelte.d.ts +171 -0
- package/dist/stores/layout.svelte.js +351 -0
- package/dist/stores/plugin.svelte.d.ts +121 -0
- package/dist/stores/plugin.svelte.js +410 -0
- package/dist/stores/vfs.svelte.d.ts +123 -0
- package/dist/stores/vfs.svelte.js +680 -0
- package/dist/styles/theme.css +623 -0
- package/dist/types/agents.d.ts +127 -0
- package/dist/types/agents.js +5 -0
- package/dist/types/ai.d.ts +137 -0
- package/dist/types/ai.js +4 -0
- package/dist/types/crdt.d.ts +222 -0
- package/dist/types/crdt.js +5 -0
- package/dist/types/editor.d.ts +52 -0
- package/dist/types/editor.js +18 -0
- package/dist/types/events.d.ts +133 -0
- package/dist/types/events.js +4 -0
- package/dist/types/filesystem.d.ts +77 -0
- package/dist/types/filesystem.js +4 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +12 -0
- package/dist/types/lsp.d.ts +691 -0
- package/dist/types/lsp.js +108 -0
- package/dist/types/plugin.d.ts +239 -0
- package/dist/types/plugin.js +5 -0
- package/dist/types/vfs.d.ts +191 -0
- package/dist/types/vfs.js +18 -0
- package/dist/utils/format.d.ts +55 -0
- package/dist/utils/format.js +152 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/keybindings.d.ts +33 -0
- package/dist/utils/keybindings.js +171 -0
- package/dist/utils/language.d.ts +27 -0
- package/dist/utils/language.js +222 -0
- package/package.json +178 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflict Predictor
|
|
3
|
+
*
|
|
4
|
+
* Real-time conflict prediction for collaborative editing.
|
|
5
|
+
* Detects when multiple users are editing the same semantic region
|
|
6
|
+
* and predicts potential merge conflicts before they happen.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
recentEditWindow: 30000,
|
|
10
|
+
proximityThreshold: 10,
|
|
11
|
+
warningThreshold: 0.3,
|
|
12
|
+
includeAI: true
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Conflict Predictor class
|
|
16
|
+
*/
|
|
17
|
+
export class ConflictPredictor {
|
|
18
|
+
config;
|
|
19
|
+
listeners = new Set();
|
|
20
|
+
lastZones = [];
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Predict conflict zones based on user awareness and semantic regions
|
|
26
|
+
*/
|
|
27
|
+
predict(users, semanticRegions) {
|
|
28
|
+
const zones = [];
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
// Filter to active users (edited recently)
|
|
31
|
+
const activeUsers = users.filter((user) => {
|
|
32
|
+
if (!this.config.includeAI && user.isAI)
|
|
33
|
+
return false;
|
|
34
|
+
return now - user.lastEditTime < this.config.recentEditWindow;
|
|
35
|
+
});
|
|
36
|
+
// Need at least 2 active users for conflict
|
|
37
|
+
if (activeUsers.length < 2) {
|
|
38
|
+
this.lastZones = [];
|
|
39
|
+
this.notifyListeners([]);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
// Check each semantic region for multiple users
|
|
43
|
+
for (const region of semanticRegions) {
|
|
44
|
+
const usersInRegion = activeUsers.filter((user) => user.cursorLine >= region.startLine &&
|
|
45
|
+
user.cursorLine <= region.endLine);
|
|
46
|
+
// Also check for users near the region
|
|
47
|
+
const usersNearRegion = activeUsers.filter((user) => {
|
|
48
|
+
const distanceToRegion = Math.min(Math.abs(user.cursorLine - region.startLine), Math.abs(user.cursorLine - region.endLine));
|
|
49
|
+
return (distanceToRegion <= this.config.proximityThreshold &&
|
|
50
|
+
!usersInRegion.includes(user));
|
|
51
|
+
});
|
|
52
|
+
const allRelevantUsers = [...usersInRegion, ...usersNearRegion];
|
|
53
|
+
if (allRelevantUsers.length >= 2) {
|
|
54
|
+
const probability = this.calculateProbability(allRelevantUsers, region, now);
|
|
55
|
+
if (probability >= this.config.warningThreshold) {
|
|
56
|
+
zones.push({
|
|
57
|
+
id: `conflict-${region.startLine}-${region.endLine}`,
|
|
58
|
+
startLine: region.startLine,
|
|
59
|
+
endLine: region.endLine,
|
|
60
|
+
probability,
|
|
61
|
+
severity: this.getSeverity(probability),
|
|
62
|
+
participants: allRelevantUsers.map((user) => ({
|
|
63
|
+
userId: user.id,
|
|
64
|
+
userName: user.name,
|
|
65
|
+
color: user.color,
|
|
66
|
+
cursorLine: user.cursorLine,
|
|
67
|
+
lastEditTime: user.lastEditTime,
|
|
68
|
+
isAI: user.isAI
|
|
69
|
+
})),
|
|
70
|
+
semanticUnit: region.label || `Lines ${region.startLine + 1}-${region.endLine + 1}`,
|
|
71
|
+
suggestion: this.getSuggestion(allRelevantUsers, probability)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Also check for proximity-based conflicts outside semantic regions
|
|
77
|
+
const proximityZones = this.detectProximityConflicts(activeUsers, semanticRegions);
|
|
78
|
+
zones.push(...proximityZones);
|
|
79
|
+
// Merge overlapping zones
|
|
80
|
+
const mergedZones = this.mergeOverlappingZones(zones);
|
|
81
|
+
this.lastZones = mergedZones;
|
|
82
|
+
this.notifyListeners(mergedZones);
|
|
83
|
+
return mergedZones;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the last predicted zones
|
|
87
|
+
*/
|
|
88
|
+
getLastZones() {
|
|
89
|
+
return this.lastZones;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Subscribe to conflict zone changes
|
|
93
|
+
*/
|
|
94
|
+
subscribe(listener) {
|
|
95
|
+
this.listeners.add(listener);
|
|
96
|
+
return () => this.listeners.delete(listener);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Calculate conflict probability
|
|
100
|
+
*/
|
|
101
|
+
calculateProbability(users, region, now) {
|
|
102
|
+
// Factors:
|
|
103
|
+
// 1. Number of users (more users = higher probability)
|
|
104
|
+
// 2. Cursor proximity (closer = higher probability)
|
|
105
|
+
// 3. Edit recency (more recent = higher probability)
|
|
106
|
+
// 4. Region size (smaller = higher probability due to less space)
|
|
107
|
+
const userCount = users.length;
|
|
108
|
+
const userCountFactor = Math.min(1, (userCount - 1) / 3); // 2 users = 0.33, 3 = 0.66, 4+ = 1
|
|
109
|
+
// Calculate average cursor proximity
|
|
110
|
+
const cursorLines = users.map((u) => u.cursorLine);
|
|
111
|
+
const avgProximity = this.calculateAverageProximity(cursorLines);
|
|
112
|
+
const proximityFactor = Math.max(0, 1 - avgProximity / this.config.proximityThreshold);
|
|
113
|
+
// Calculate edit recency factor
|
|
114
|
+
const recencyFactors = users.map((u) => {
|
|
115
|
+
const timeSinceEdit = now - u.lastEditTime;
|
|
116
|
+
return Math.max(0, 1 - timeSinceEdit / this.config.recentEditWindow);
|
|
117
|
+
});
|
|
118
|
+
const avgRecency = recencyFactors.reduce((a, b) => a + b, 0) / recencyFactors.length;
|
|
119
|
+
// Region size factor (smaller regions are more conflict-prone)
|
|
120
|
+
const regionSize = region.endLine - region.startLine + 1;
|
|
121
|
+
const sizeFactor = Math.min(1, 10 / regionSize);
|
|
122
|
+
// Weighted combination
|
|
123
|
+
const probability = userCountFactor * 0.3 + proximityFactor * 0.35 + avgRecency * 0.25 + sizeFactor * 0.1;
|
|
124
|
+
return Math.min(1, Math.max(0, probability));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Calculate average pairwise proximity between cursor positions
|
|
128
|
+
*/
|
|
129
|
+
calculateAverageProximity(lines) {
|
|
130
|
+
if (lines.length < 2)
|
|
131
|
+
return Infinity;
|
|
132
|
+
let totalDistance = 0;
|
|
133
|
+
let pairs = 0;
|
|
134
|
+
for (let i = 0; i < lines.length; i++) {
|
|
135
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
136
|
+
totalDistance += Math.abs(lines[i] - lines[j]);
|
|
137
|
+
pairs++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return pairs > 0 ? totalDistance / pairs : Infinity;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get severity level from probability
|
|
144
|
+
*/
|
|
145
|
+
getSeverity(probability) {
|
|
146
|
+
if (probability >= 0.8)
|
|
147
|
+
return 'critical';
|
|
148
|
+
if (probability >= 0.6)
|
|
149
|
+
return 'high';
|
|
150
|
+
if (probability >= 0.4)
|
|
151
|
+
return 'medium';
|
|
152
|
+
return 'low';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get suggestion based on conflict state
|
|
156
|
+
*/
|
|
157
|
+
getSuggestion(users, probability) {
|
|
158
|
+
const userNames = users.map((u) => u.name);
|
|
159
|
+
const aiUsers = users.filter((u) => u.isAI);
|
|
160
|
+
const humanUsers = users.filter((u) => !u.isAI);
|
|
161
|
+
if (probability >= 0.8) {
|
|
162
|
+
if (aiUsers.length > 0 && humanUsers.length > 0) {
|
|
163
|
+
return `AI is editing nearby. Consider pausing AI changes.`;
|
|
164
|
+
}
|
|
165
|
+
return `High conflict risk with ${userNames.join(', ')}. Consider coordinating.`;
|
|
166
|
+
}
|
|
167
|
+
if (probability >= 0.6) {
|
|
168
|
+
return `${userNames.join(' and ')} are editing nearby.`;
|
|
169
|
+
}
|
|
170
|
+
return `Multiple editors in this area.`;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Detect proximity-based conflicts outside semantic regions
|
|
174
|
+
*/
|
|
175
|
+
detectProximityConflicts(users, semanticRegions) {
|
|
176
|
+
const zones = [];
|
|
177
|
+
// Group users by proximity
|
|
178
|
+
const visited = new Set();
|
|
179
|
+
for (let i = 0; i < users.length; i++) {
|
|
180
|
+
if (visited.has(users[i].id))
|
|
181
|
+
continue;
|
|
182
|
+
const cluster = [users[i]];
|
|
183
|
+
visited.add(users[i].id);
|
|
184
|
+
for (let j = i + 1; j < users.length; j++) {
|
|
185
|
+
if (visited.has(users[j].id))
|
|
186
|
+
continue;
|
|
187
|
+
const distance = Math.abs(users[i].cursorLine - users[j].cursorLine);
|
|
188
|
+
if (distance <= this.config.proximityThreshold) {
|
|
189
|
+
cluster.push(users[j]);
|
|
190
|
+
visited.add(users[j].id);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (cluster.length >= 2) {
|
|
194
|
+
// Check if this cluster is already covered by a semantic region
|
|
195
|
+
const minLine = Math.min(...cluster.map((u) => u.cursorLine));
|
|
196
|
+
const maxLine = Math.max(...cluster.map((u) => u.cursorLine));
|
|
197
|
+
const isInSemanticRegion = semanticRegions.some((r) => minLine >= r.startLine && maxLine <= r.endLine);
|
|
198
|
+
if (!isInSemanticRegion) {
|
|
199
|
+
const probability = this.calculateProximityProbability(cluster);
|
|
200
|
+
if (probability >= this.config.warningThreshold) {
|
|
201
|
+
zones.push({
|
|
202
|
+
id: `proximity-${minLine}-${maxLine}`,
|
|
203
|
+
startLine: Math.max(0, minLine - 2),
|
|
204
|
+
endLine: maxLine + 2,
|
|
205
|
+
probability,
|
|
206
|
+
severity: this.getSeverity(probability),
|
|
207
|
+
participants: cluster.map((user) => ({
|
|
208
|
+
userId: user.id,
|
|
209
|
+
userName: user.name,
|
|
210
|
+
color: user.color,
|
|
211
|
+
cursorLine: user.cursorLine,
|
|
212
|
+
lastEditTime: user.lastEditTime,
|
|
213
|
+
isAI: user.isAI
|
|
214
|
+
})),
|
|
215
|
+
semanticUnit: `Lines ${minLine + 1}-${maxLine + 1}`,
|
|
216
|
+
suggestion: this.getSuggestion(cluster, probability)
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return zones;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Calculate probability for proximity-based conflict
|
|
226
|
+
*/
|
|
227
|
+
calculateProximityProbability(users) {
|
|
228
|
+
const now = Date.now();
|
|
229
|
+
// Simpler calculation for proximity conflicts
|
|
230
|
+
const cursorLines = users.map((u) => u.cursorLine);
|
|
231
|
+
const avgProximity = this.calculateAverageProximity(cursorLines);
|
|
232
|
+
const proximityFactor = Math.max(0, 1 - avgProximity / this.config.proximityThreshold);
|
|
233
|
+
const recencyFactors = users.map((u) => {
|
|
234
|
+
const timeSinceEdit = now - u.lastEditTime;
|
|
235
|
+
return Math.max(0, 1 - timeSinceEdit / this.config.recentEditWindow);
|
|
236
|
+
});
|
|
237
|
+
const avgRecency = recencyFactors.reduce((a, b) => a + b, 0) / recencyFactors.length;
|
|
238
|
+
return proximityFactor * 0.6 + avgRecency * 0.4;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Merge overlapping conflict zones
|
|
242
|
+
*/
|
|
243
|
+
mergeOverlappingZones(zones) {
|
|
244
|
+
if (zones.length <= 1)
|
|
245
|
+
return zones;
|
|
246
|
+
// Sort by start line
|
|
247
|
+
const sorted = [...zones].sort((a, b) => a.startLine - b.startLine);
|
|
248
|
+
const merged = [];
|
|
249
|
+
let current = sorted[0];
|
|
250
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
251
|
+
const next = sorted[i];
|
|
252
|
+
if (next.startLine <= current.endLine + 1) {
|
|
253
|
+
// Merge overlapping zones
|
|
254
|
+
current = {
|
|
255
|
+
...current,
|
|
256
|
+
id: `${current.id}-${next.id}`,
|
|
257
|
+
endLine: Math.max(current.endLine, next.endLine),
|
|
258
|
+
probability: Math.max(current.probability, next.probability),
|
|
259
|
+
severity: this.getSeverity(Math.max(current.probability, next.probability)),
|
|
260
|
+
participants: this.mergeParticipants(current.participants, next.participants),
|
|
261
|
+
semanticUnit: current.semanticUnit
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
merged.push(current);
|
|
266
|
+
current = next;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
merged.push(current);
|
|
270
|
+
return merged;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Merge participant lists, removing duplicates
|
|
274
|
+
*/
|
|
275
|
+
mergeParticipants(a, b) {
|
|
276
|
+
const seen = new Set();
|
|
277
|
+
const result = [];
|
|
278
|
+
for (const p of [...a, ...b]) {
|
|
279
|
+
if (!seen.has(p.userId)) {
|
|
280
|
+
seen.add(p.userId);
|
|
281
|
+
result.push(p);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Notify listeners of zone changes
|
|
288
|
+
*/
|
|
289
|
+
notifyListeners(zones) {
|
|
290
|
+
for (const listener of this.listeners) {
|
|
291
|
+
try {
|
|
292
|
+
listener(zones);
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
console.error('[ConflictPredictor] Listener error:', e);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Create a conflict predictor instance
|
|
302
|
+
*/
|
|
303
|
+
export function createConflictPredictor(config) {
|
|
304
|
+
return new ConflictPredictor(config);
|
|
305
|
+
}
|
|
306
|
+
// Singleton instance
|
|
307
|
+
let globalPredictor = null;
|
|
308
|
+
/**
|
|
309
|
+
* Get the global conflict predictor
|
|
310
|
+
*/
|
|
311
|
+
export function getConflictPredictor() {
|
|
312
|
+
if (!globalPredictor) {
|
|
313
|
+
globalPredictor = createConflictPredictor();
|
|
314
|
+
}
|
|
315
|
+
return globalPredictor;
|
|
316
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRDT binding for the custom editor
|
|
3
|
+
*
|
|
4
|
+
* This module provides integration between the custom editor state
|
|
5
|
+
* and Yjs CRDT for real-time collaborative editing.
|
|
6
|
+
*/
|
|
7
|
+
import * as Y from 'yjs';
|
|
8
|
+
import type { EditorState, Position } from './state';
|
|
9
|
+
/**
|
|
10
|
+
* CRDT binding configuration
|
|
11
|
+
*/
|
|
12
|
+
export interface CRDTBindingConfig {
|
|
13
|
+
/** Yjs document */
|
|
14
|
+
doc: Y.Doc;
|
|
15
|
+
/** Text type name in the document */
|
|
16
|
+
textName?: string;
|
|
17
|
+
/** Editor state to bind */
|
|
18
|
+
editorState: EditorState;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Position in both local and CRDT coordinates
|
|
22
|
+
*/
|
|
23
|
+
export interface CRDTPosition {
|
|
24
|
+
/** Local position (line/column) */
|
|
25
|
+
local: Position;
|
|
26
|
+
/** CRDT index position */
|
|
27
|
+
index: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* CRDT binding class that syncs editor state with Yjs
|
|
31
|
+
*/
|
|
32
|
+
export declare class CRDTBinding {
|
|
33
|
+
private doc;
|
|
34
|
+
private text;
|
|
35
|
+
private editorState;
|
|
36
|
+
private isUpdating;
|
|
37
|
+
private isDestroyed;
|
|
38
|
+
private cleanupFns;
|
|
39
|
+
private textObserver;
|
|
40
|
+
constructor(config: CRDTBindingConfig);
|
|
41
|
+
/**
|
|
42
|
+
* Set up bidirectional bindings
|
|
43
|
+
*/
|
|
44
|
+
private setupBindings;
|
|
45
|
+
/**
|
|
46
|
+
* Sync content from CRDT to editor
|
|
47
|
+
*/
|
|
48
|
+
private syncFromCRDT;
|
|
49
|
+
/**
|
|
50
|
+
* Handle changes from CRDT
|
|
51
|
+
* Uses Yjs relative positions for accurate cursor preservation during concurrent edits
|
|
52
|
+
*/
|
|
53
|
+
private handleCRDTChange;
|
|
54
|
+
/**
|
|
55
|
+
* Convert position to index using provided lines (for atomic operations)
|
|
56
|
+
*/
|
|
57
|
+
private positionToIndexWithLines;
|
|
58
|
+
/**
|
|
59
|
+
* Handle local editor changes
|
|
60
|
+
*/
|
|
61
|
+
private handleLocalChange;
|
|
62
|
+
/**
|
|
63
|
+
* Convert editor position to CRDT index
|
|
64
|
+
*/
|
|
65
|
+
positionToIndex(position: Position): number;
|
|
66
|
+
/**
|
|
67
|
+
* Convert CRDT index to editor position
|
|
68
|
+
*/
|
|
69
|
+
indexToPosition(index: number): Position;
|
|
70
|
+
/**
|
|
71
|
+
* Apply a full local content replacement into the bound editor state,
|
|
72
|
+
* which propagates to the CRDT / Yjs text via the local-change listener.
|
|
73
|
+
*
|
|
74
|
+
* Use this for edits that originate outside the bound EditorState (e.g. an
|
|
75
|
+
* inner editor component that maintains its own state) so that local typing
|
|
76
|
+
* reaches collaborators. No-op while a remote update is being applied, which
|
|
77
|
+
* also prevents a remote -> local -> remote echo loop.
|
|
78
|
+
*
|
|
79
|
+
* @returns `true` if the content changed and was propagated, `false` otherwise.
|
|
80
|
+
*/
|
|
81
|
+
setContent(content: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Whether a remote (CRDT-originated) update is currently being applied.
|
|
84
|
+
* Callers can use this to suppress re-sending an echoed local change.
|
|
85
|
+
*/
|
|
86
|
+
get isApplyingRemoteChange(): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Get the Yjs text type
|
|
89
|
+
*/
|
|
90
|
+
getText(): Y.Text;
|
|
91
|
+
/**
|
|
92
|
+
* Get the Yjs document
|
|
93
|
+
*/
|
|
94
|
+
getDoc(): Y.Doc;
|
|
95
|
+
/**
|
|
96
|
+
* Clean up bindings
|
|
97
|
+
*/
|
|
98
|
+
destroy(): void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create a CRDT binding
|
|
102
|
+
*/
|
|
103
|
+
export declare function createCRDTBinding(config: CRDTBindingConfig): CRDTBinding;
|
|
104
|
+
/**
|
|
105
|
+
* Relative position for cursor synchronization
|
|
106
|
+
*/
|
|
107
|
+
export interface RelativePosition {
|
|
108
|
+
/** Yjs relative position */
|
|
109
|
+
yRelPos: Y.RelativePosition;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a relative position from an editor position
|
|
113
|
+
*/
|
|
114
|
+
export declare function createRelativePosition(text: Y.Text, binding: CRDTBinding, position: Position): RelativePosition;
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a relative position to an editor position
|
|
117
|
+
*/
|
|
118
|
+
export declare function resolveRelativePosition(doc: Y.Doc, binding: CRDTBinding, relPos: RelativePosition): Position | null;
|