@nocturnium/svelte-ide 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -47
- package/dist/components/agents/AgentActivityPanel.svelte +3 -1
- package/dist/components/agents/AgentAvatar.svelte +127 -35
- package/dist/components/agents/AgentCursor.svelte +15 -5
- package/dist/components/agents/AgentPresenceBar.svelte +7 -2
- package/dist/components/ai/AIConversationList.svelte +39 -34
- package/dist/components/ai/AIInlineEdit.svelte +1 -3
- package/dist/components/ai/AIMessage.svelte +5 -5
- package/dist/components/ai/AIMessageActions.svelte +18 -3
- package/dist/components/ai/AIMessageContent.svelte +22 -20
- package/dist/components/ai/AIPanel.svelte +17 -9
- package/dist/components/ai/AISuggestionWidget.svelte +1 -3
- package/dist/components/ai/AIToolCallDisplay.svelte +10 -14
- package/dist/components/core/Badge.svelte +9 -1
- package/dist/components/core/ConnectionStatus.svelte +73 -68
- package/dist/components/core/ErrorBoundary.svelte +56 -56
- package/dist/components/core/ErrorBoundary.svelte.d.ts +5 -5
- package/dist/components/core/Icon.svelte +22 -11
- package/dist/components/core/ResizeHandle.svelte +1 -1
- package/dist/components/core/Tooltip.svelte +1 -7
- package/dist/components/editor/AIFocusLayer.svelte +15 -7
- package/dist/components/editor/Breadcrumbs.svelte +18 -6
- package/dist/components/editor/BreakpointLayer.svelte +51 -60
- package/dist/components/editor/CognitiveLoadMeter.svelte +4 -2
- package/dist/components/editor/CollaborativeEditor.svelte +1 -5
- package/dist/components/editor/CommandPalette.svelte +1 -4
- package/dist/components/editor/ComplexityLayer.svelte +8 -6
- package/dist/components/editor/ConflictZoneLayer.svelte +2 -8
- package/dist/components/editor/ContextLens.svelte +1 -4
- package/dist/components/editor/CustomEditor.svelte +85 -41
- package/dist/components/editor/DebugConsole.svelte +8 -17
- package/dist/components/editor/EchoCursorLayer.svelte +3 -1
- package/dist/components/editor/EditorGutter.svelte +8 -2
- package/dist/components/editor/EditorLines.svelte +6 -3
- package/dist/components/editor/EditorPane.svelte +1 -6
- package/dist/components/editor/EditorSelections.svelte +29 -11
- package/dist/components/editor/FileExplorer.svelte +26 -4
- package/dist/components/editor/FileIcon.svelte +3 -1
- package/dist/components/editor/FindReplace.svelte +16 -4
- package/dist/components/editor/GhostBracketLayer.svelte +2 -2
- package/dist/components/editor/GitBlameLayer.svelte +2 -1
- package/dist/components/editor/InlineDiagnosticsLayer.svelte +4 -13
- package/dist/components/editor/InlineDiffLayer.svelte +18 -9
- package/dist/components/editor/Minimap.svelte +16 -9
- package/dist/components/editor/PluginPreviewSandbox.svelte +3 -2
- package/dist/components/editor/ProblemsPanel.svelte +11 -35
- package/dist/components/editor/QuickActionsMenu.svelte +5 -14
- package/dist/components/editor/SnippetPalette.svelte +11 -12
- package/dist/components/editor/StructureMap.svelte +2 -1
- package/dist/components/editor/SymbolOutline.svelte +14 -19
- package/dist/components/editor/TimelineScrubber.svelte +7 -6
- package/dist/components/editor/core/complexity-analyzer.js +42 -12
- package/dist/components/editor/core/conflict-predictor.js +2 -4
- package/dist/components/editor/core/folding.d.ts +9 -0
- package/dist/components/editor/core/folding.js +40 -5
- package/dist/components/editor/core/multi-cursor.js +4 -8
- package/dist/components/editor/core/navigation.js +2 -6
- package/dist/components/editor/core/quick-actions.js +22 -17
- package/dist/components/editor/core/search.js +2 -6
- package/dist/components/editor/core/semantic-analyzer.js +1 -3
- package/dist/components/editor/core/snippet-manager.js +4 -3
- package/dist/components/editor/core/state.js +2 -2
- package/dist/components/editor/core/timeline.js +1 -3
- package/dist/components/editor/editor-input.js +9 -6
- package/dist/components/editor/editor-multicursor.js +2 -2
- package/dist/components/editor/tokenizer/languages/css.js +146 -24
- package/dist/components/editor/tokenizer/languages/go.js +76 -13
- package/dist/components/editor/tokenizer/languages/javascript.d.ts +13 -2
- package/dist/components/editor/tokenizer/languages/javascript.js +278 -84
- package/dist/components/editor/tokenizer/languages/python.js +116 -19
- package/dist/components/editor/tokenizer/languages/svelte.js +20 -7
- package/dist/components/layout/IDELayout.svelte +6 -2
- package/dist/components/layout/StatusBar.svelte +32 -20
- package/dist/components/lsp/AutocompleteWidget.svelte +19 -19
- package/dist/components/lsp/DiagnosticMarker.svelte +61 -52
- package/dist/components/lsp/DiagnosticsPanel.svelte +45 -27
- package/dist/components/lsp/HoverTooltip.svelte +56 -61
- package/dist/components/lsp/LSPEditor.svelte +7 -18
- package/dist/components/lsp/SignatureHelpWidget.svelte +12 -9
- package/dist/components/plugins/PluginCard.svelte +3 -13
- package/dist/components/plugins/PluginProposalForm.svelte +19 -31
- package/dist/components/vfs/LockConflictDialog.svelte +112 -45
- package/dist/components/vfs/LockIndicator.svelte +0 -1
- package/dist/components/vfs/LockOverlay.svelte +53 -53
- package/dist/components/vfs/LockOverlay.svelte.d.ts +5 -5
- package/dist/components/vfs/VersionConflictDialog.svelte +107 -77
- package/dist/services/error-handling.js +1 -7
- package/dist/services/mock-ai.js +9 -7
- package/dist/stores/agents.svelte.js +50 -10
- package/dist/stores/ai.svelte.js +66 -18
- package/dist/stores/collaboration.svelte.js +70 -14
- package/dist/stores/editor.svelte.js +50 -10
- package/dist/stores/plugin.svelte.js +60 -12
- package/dist/stores/vfs.svelte.js +77 -19
- package/dist/styles/theme.css +16 -7
- package/package.json +186 -1
|
@@ -87,9 +87,7 @@
|
|
|
87
87
|
let featuredAgent = $derived.by(() => {
|
|
88
88
|
// Prefer agent working on current file
|
|
89
89
|
if (currentFilePath) {
|
|
90
|
-
const agentOnFile = busyAgents.find(a =>
|
|
91
|
-
a.currentTask?.files?.includes(currentFilePath)
|
|
92
|
-
);
|
|
90
|
+
const agentOnFile = busyAgents.find((a) => a.currentTask?.files?.includes(currentFilePath));
|
|
93
91
|
if (agentOnFile) return agentOnFile;
|
|
94
92
|
}
|
|
95
93
|
// Otherwise show first busy agent
|
|
@@ -107,10 +105,18 @@
|
|
|
107
105
|
const phase = featuredAgent.currentTask?.progress?.phase;
|
|
108
106
|
let action = 'working';
|
|
109
107
|
switch (phase) {
|
|
110
|
-
case 'planning':
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
case '
|
|
108
|
+
case 'planning':
|
|
109
|
+
action = 'planning';
|
|
110
|
+
break;
|
|
111
|
+
case 'implementing':
|
|
112
|
+
action = 'coding';
|
|
113
|
+
break;
|
|
114
|
+
case 'testing':
|
|
115
|
+
action = 'testing';
|
|
116
|
+
break;
|
|
117
|
+
case 'complete':
|
|
118
|
+
action = 'done';
|
|
119
|
+
break;
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
const otherBusy = busyAgents.length - 1;
|
|
@@ -120,14 +126,10 @@
|
|
|
120
126
|
|
|
121
127
|
// Lock context for current file
|
|
122
128
|
let currentFileLock = $derived(
|
|
123
|
-
currentFilePath ? locks.find(l => l.path === currentFilePath) : undefined
|
|
124
|
-
);
|
|
125
|
-
let isMyLock = $derived(
|
|
126
|
-
currentFileLock && userId ? currentFileLock.holder === userId : false
|
|
127
|
-
);
|
|
128
|
-
let otherLocks = $derived(
|
|
129
|
-
locks.filter(l => l.path !== currentFilePath)
|
|
129
|
+
currentFilePath ? locks.find((l) => l.path === currentFilePath) : undefined
|
|
130
130
|
);
|
|
131
|
+
let isMyLock = $derived(currentFileLock && userId ? currentFileLock.holder === userId : false);
|
|
132
|
+
let otherLocks = $derived(locks.filter((l) => l.path !== currentFilePath));
|
|
131
133
|
|
|
132
134
|
// Lock expiry countdown
|
|
133
135
|
let lockExpiryText = $state<string | null>(null);
|
|
@@ -196,7 +198,7 @@
|
|
|
196
198
|
return `${currentFileLock.holder} is editing this file.`;
|
|
197
199
|
}
|
|
198
200
|
if (locks.length === 0) return 'No files locked';
|
|
199
|
-
const holders = [...new Set(locks.map(l => l.holder))];
|
|
201
|
+
const holders = [...new Set(locks.map((l) => l.holder))];
|
|
200
202
|
return `Locked by: ${holders.join(', ')}`;
|
|
201
203
|
});
|
|
202
204
|
</script>
|
|
@@ -434,8 +436,13 @@
|
|
|
434
436
|
}
|
|
435
437
|
|
|
436
438
|
@keyframes ai-dot-shimmer {
|
|
437
|
-
0%,
|
|
438
|
-
|
|
439
|
+
0%,
|
|
440
|
+
100% {
|
|
441
|
+
background-position: 0% 50%;
|
|
442
|
+
}
|
|
443
|
+
50% {
|
|
444
|
+
background-position: 100% 50%;
|
|
445
|
+
}
|
|
439
446
|
}
|
|
440
447
|
|
|
441
448
|
/* Lock Status */
|
|
@@ -468,8 +475,13 @@
|
|
|
468
475
|
}
|
|
469
476
|
|
|
470
477
|
@keyframes expiry-pulse {
|
|
471
|
-
0%,
|
|
472
|
-
|
|
478
|
+
0%,
|
|
479
|
+
100% {
|
|
480
|
+
opacity: 1;
|
|
481
|
+
}
|
|
482
|
+
50% {
|
|
483
|
+
opacity: 0.6;
|
|
484
|
+
}
|
|
473
485
|
}
|
|
474
486
|
|
|
475
487
|
.status-bar__separator {
|
|
@@ -493,7 +505,7 @@
|
|
|
493
505
|
|
|
494
506
|
/* Responsive: hide less important info on narrow screens */
|
|
495
507
|
@media (max-width: 600px) {
|
|
496
|
-
.status-bar__right > :nth-child(n+3):nth-child(-n+5) {
|
|
508
|
+
.status-bar__right > :nth-child(n + 3):nth-child(-n + 5) {
|
|
497
509
|
display: none;
|
|
498
510
|
}
|
|
499
511
|
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
// SVG icons for completion item kinds
|
|
3
|
+
function getKindSVG(kind?: number): string {
|
|
4
|
+
const icons: Record<number, string> = {
|
|
5
|
+
2: '<path d="M4 4h4v2H4v4H2V4h2zm10 0h2v6h-4V8h2V4zm-6 8h4v4H8v-4zm8 0h2v4h-4v-2h2v-2z" fill="currentColor"/>',
|
|
6
|
+
3: '<path d="M4 4h8v2H4v8H2V4h2zm6 4h4v8h-2v-6H8V8h2z" fill="currentColor"/>',
|
|
7
|
+
6: '<path d="M4 4h8v4H8V6H6v4H4V4zm0 8h2v4H4v-4zm4 0h8v4H8v-4z" fill="currentColor"/>',
|
|
8
|
+
7: '<path d="M4 4h8v2H4v8H2V4h2zm4 4h6v8H6V8h2zm2 2v4h2v-4h-2z" fill="currentColor"/>',
|
|
9
|
+
8: '<path d="M6 4h8v2H6v8H4V4h2zm6 4h2v8H8v-2h4V8z" fill="currentColor"/>',
|
|
10
|
+
9: '<path d="M2 4h4v2H2v8h8v2H2V4zm6 2h8v10H8V6zm2 2v6h4V8h-4z" fill="currentColor"/>',
|
|
11
|
+
13: '<path d="M4 4h8v2H4v8H2V4h2zm4 4h6v8H6V8h2zm2 2v2h2v-2h-2z" fill="currentColor"/>',
|
|
12
|
+
14: '<path d="M4 6h10v2H4v6H2V6h2zm4 4h6v6H6v-6h2zm2 2v2h2v-2h-2z" fill="currentColor"/>',
|
|
13
|
+
15: '<path d="M4 4v12h12V4H4zm2 2h8v8H6V6z" fill="currentColor"/>',
|
|
14
|
+
21: '<path d="M8 2l6 6-6 6-6-6 6-6zm0 2.83L4.83 8 8 11.17 11.17 8 8 4.83z" fill="currentColor"/>'
|
|
15
|
+
};
|
|
16
|
+
return `<svg viewBox="0 0 16 16" width="16" height="16">${icons[kind ?? 1] ?? '<circle cx="8" cy="8" r="4" fill="currentColor"/>'}</svg>`;
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
1
20
|
<script lang="ts">
|
|
2
21
|
/**
|
|
3
22
|
* AutocompleteWidget - LSP completion suggestions dropdown
|
|
@@ -206,25 +225,6 @@
|
|
|
206
225
|
{/if}
|
|
207
226
|
</div>
|
|
208
227
|
|
|
209
|
-
<script module lang="ts">
|
|
210
|
-
// SVG icons for completion item kinds
|
|
211
|
-
function getKindSVG(kind?: number): string {
|
|
212
|
-
const icons: Record<number, string> = {
|
|
213
|
-
2: '<path d="M4 4h4v2H4v4H2V4h2zm10 0h2v6h-4V8h2V4zm-6 8h4v4H8v-4zm8 0h2v4h-4v-2h2v-2z" fill="currentColor"/>',
|
|
214
|
-
3: '<path d="M4 4h8v2H4v8H2V4h2zm6 4h4v8h-2v-6H8V8h2z" fill="currentColor"/>',
|
|
215
|
-
6: '<path d="M4 4h8v4H8V6H6v4H4V4zm0 8h2v4H4v-4zm4 0h8v4H8v-4z" fill="currentColor"/>',
|
|
216
|
-
7: '<path d="M4 4h8v2H4v8H2V4h2zm4 4h6v8H6V8h2zm2 2v4h2v-4h-2z" fill="currentColor"/>',
|
|
217
|
-
8: '<path d="M6 4h8v2H6v8H4V4h2zm6 4h2v8H8v-2h4V8z" fill="currentColor"/>',
|
|
218
|
-
9: '<path d="M2 4h4v2H2v8h8v2H2V4zm6 2h8v10H8V6zm2 2v6h4V8h-4z" fill="currentColor"/>',
|
|
219
|
-
13: '<path d="M4 4h8v2H4v8H2V4h2zm4 4h6v8H6V8h2zm2 2v2h2v-2h-2z" fill="currentColor"/>',
|
|
220
|
-
14: '<path d="M4 6h10v2H4v6H2V6h2zm4 4h6v6H6v-6h2zm2 2v2h2v-2h-2z" fill="currentColor"/>',
|
|
221
|
-
15: '<path d="M4 4v12h12V4H4zm2 2h8v8H6V6z" fill="currentColor"/>',
|
|
222
|
-
21: '<path d="M8 2l6 6-6 6-6-6 6-6zm0 2.83L4.83 8 8 11.17 11.17 8 8 4.83z" fill="currentColor"/>'
|
|
223
|
-
};
|
|
224
|
-
return `<svg viewBox="0 0 16 16" width="16" height="16">${icons[kind ?? 1] ?? '<circle cx="8" cy="8" r="4" fill="currentColor"/>'}</svg>`;
|
|
225
|
-
}
|
|
226
|
-
</script>
|
|
227
|
-
|
|
228
228
|
<style>
|
|
229
229
|
.autocomplete-widget {
|
|
230
230
|
position: fixed;
|
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
function getGutterIcon(severity?: number): string {
|
|
3
|
+
const color =
|
|
4
|
+
severity === 1
|
|
5
|
+
? 'var(--ide-error)'
|
|
6
|
+
: severity === 2
|
|
7
|
+
? 'var(--ide-warning)'
|
|
8
|
+
: severity === 3
|
|
9
|
+
? 'var(--ide-info)'
|
|
10
|
+
: 'var(--ide-text-muted)';
|
|
11
|
+
|
|
12
|
+
switch (severity) {
|
|
13
|
+
case 1: // Error - filled circle with X
|
|
14
|
+
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
15
|
+
<circle cx="8" cy="8" r="6" fill="${color}"/>
|
|
16
|
+
<path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
17
|
+
</svg>`;
|
|
18
|
+
case 2: // Warning - triangle
|
|
19
|
+
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
20
|
+
<path d="M8 2L1 14h14L8 2z" fill="${color}"/>
|
|
21
|
+
<path d="M8 6v4M8 12v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
22
|
+
</svg>`;
|
|
23
|
+
case 3: // Info - circle with i
|
|
24
|
+
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
25
|
+
<circle cx="8" cy="8" r="6" fill="${color}"/>
|
|
26
|
+
<path d="M8 5v1M8 8v4" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
27
|
+
</svg>`;
|
|
28
|
+
case 4: // Hint - lightbulb outline
|
|
29
|
+
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
30
|
+
<path d="M8 1a5 5 0 013 9v2a1 1 0 01-1 1H6a1 1 0 01-1-1v-2a5 5 0 013-9z" fill="none" stroke="${color}" stroke-width="1.2"/>
|
|
31
|
+
<path d="M6 14h4" stroke="${color}" stroke-width="1.2" stroke-linecap="round"/>
|
|
32
|
+
</svg>`;
|
|
33
|
+
default:
|
|
34
|
+
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
35
|
+
<circle cx="8" cy="8" r="4" fill="${color}"/>
|
|
36
|
+
</svg>`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
1
41
|
<script lang="ts">
|
|
2
42
|
/**
|
|
3
43
|
* DiagnosticMarker - Inline diagnostic indicator
|
|
@@ -18,30 +58,35 @@
|
|
|
18
58
|
class?: string;
|
|
19
59
|
}
|
|
20
60
|
|
|
21
|
-
let {
|
|
22
|
-
diagnostic,
|
|
23
|
-
type = 'inline',
|
|
24
|
-
onClick,
|
|
25
|
-
class: className = ''
|
|
26
|
-
}: Props = $props();
|
|
61
|
+
let { diagnostic, type = 'inline', onClick, class: className = '' }: Props = $props();
|
|
27
62
|
|
|
28
63
|
function getSeverityClass(severity?: DiagnosticSeverity): string {
|
|
29
64
|
switch (severity) {
|
|
30
|
-
case 1:
|
|
31
|
-
|
|
32
|
-
case
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
case 1:
|
|
66
|
+
return 'error';
|
|
67
|
+
case 2:
|
|
68
|
+
return 'warning';
|
|
69
|
+
case 3:
|
|
70
|
+
return 'info';
|
|
71
|
+
case 4:
|
|
72
|
+
return 'hint';
|
|
73
|
+
default:
|
|
74
|
+
return 'info';
|
|
35
75
|
}
|
|
36
76
|
}
|
|
37
77
|
|
|
38
78
|
function getSeverityLabel(severity?: DiagnosticSeverity): string {
|
|
39
79
|
switch (severity) {
|
|
40
|
-
case 1:
|
|
41
|
-
|
|
42
|
-
case
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
case 1:
|
|
81
|
+
return 'Error';
|
|
82
|
+
case 2:
|
|
83
|
+
return 'Warning';
|
|
84
|
+
case 3:
|
|
85
|
+
return 'Info';
|
|
86
|
+
case 4:
|
|
87
|
+
return 'Hint';
|
|
88
|
+
default:
|
|
89
|
+
return 'Diagnostic';
|
|
45
90
|
}
|
|
46
91
|
}
|
|
47
92
|
</script>
|
|
@@ -67,42 +112,6 @@
|
|
|
67
112
|
></span>
|
|
68
113
|
{/if}
|
|
69
114
|
|
|
70
|
-
<script module lang="ts">
|
|
71
|
-
function getGutterIcon(severity?: number): string {
|
|
72
|
-
const color = severity === 1 ? 'var(--ide-error)' :
|
|
73
|
-
severity === 2 ? 'var(--ide-warning)' :
|
|
74
|
-
severity === 3 ? 'var(--ide-info)' :
|
|
75
|
-
'var(--ide-text-muted)';
|
|
76
|
-
|
|
77
|
-
switch (severity) {
|
|
78
|
-
case 1: // Error - filled circle with X
|
|
79
|
-
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
80
|
-
<circle cx="8" cy="8" r="6" fill="${color}"/>
|
|
81
|
-
<path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
82
|
-
</svg>`;
|
|
83
|
-
case 2: // Warning - triangle
|
|
84
|
-
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
85
|
-
<path d="M8 2L1 14h14L8 2z" fill="${color}"/>
|
|
86
|
-
<path d="M8 6v4M8 12v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
87
|
-
</svg>`;
|
|
88
|
-
case 3: // Info - circle with i
|
|
89
|
-
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
90
|
-
<circle cx="8" cy="8" r="6" fill="${color}"/>
|
|
91
|
-
<path d="M8 5v1M8 8v4" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/>
|
|
92
|
-
</svg>`;
|
|
93
|
-
case 4: // Hint - lightbulb outline
|
|
94
|
-
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
95
|
-
<path d="M8 1a5 5 0 013 9v2a1 1 0 01-1 1H6a1 1 0 01-1-1v-2a5 5 0 013-9z" fill="none" stroke="${color}" stroke-width="1.2"/>
|
|
96
|
-
<path d="M6 14h4" stroke="${color}" stroke-width="1.2" stroke-linecap="round"/>
|
|
97
|
-
</svg>`;
|
|
98
|
-
default:
|
|
99
|
-
return `<svg viewBox="0 0 16 16" width="12" height="12">
|
|
100
|
-
<circle cx="8" cy="8" r="4" fill="${color}"/>
|
|
101
|
-
</svg>`;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
</script>
|
|
105
|
-
|
|
106
115
|
<style>
|
|
107
116
|
.diagnostic-gutter {
|
|
108
117
|
display: flex;
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
const errorIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="var(--ide-bg-primary)" stroke-width="1.5"/></svg>`;
|
|
3
|
+
const warningIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><path d="M8 1L1 15h14L8 1z" fill="currentColor"/><path d="M8 6v4M8 12v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
4
|
+
const infoIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M8 5v1M8 8v4" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
5
|
+
const checkIcon = `<svg viewBox="0 0 16 16" width="24" height="24"><circle cx="8" cy="8" r="7" fill="var(--ide-success)" opacity="0.2"/><path d="M5 8l2 2 4-4" stroke="var(--ide-success)" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
6
|
+
|
|
7
|
+
function getSeveritySVG(severity?: number): string {
|
|
8
|
+
switch (severity) {
|
|
9
|
+
case 1:
|
|
10
|
+
return errorIcon;
|
|
11
|
+
case 2:
|
|
12
|
+
return warningIcon;
|
|
13
|
+
case 3:
|
|
14
|
+
return infoIcon;
|
|
15
|
+
case 4:
|
|
16
|
+
return `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M8 4v5M8 11v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
17
|
+
default:
|
|
18
|
+
return infoIcon;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
1
23
|
<script lang="ts">
|
|
2
24
|
/**
|
|
3
25
|
* DiagnosticsPanel - LSP diagnostics list display
|
|
@@ -62,10 +84,18 @@
|
|
|
62
84
|
diagnostics.forEach((diags) => {
|
|
63
85
|
for (const diag of diags) {
|
|
64
86
|
switch (diag.severity) {
|
|
65
|
-
case 1:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
case
|
|
87
|
+
case 1:
|
|
88
|
+
result.errors++;
|
|
89
|
+
break;
|
|
90
|
+
case 2:
|
|
91
|
+
result.warnings++;
|
|
92
|
+
break;
|
|
93
|
+
case 3:
|
|
94
|
+
result.info++;
|
|
95
|
+
break;
|
|
96
|
+
case 4:
|
|
97
|
+
result.hints++;
|
|
98
|
+
break;
|
|
69
99
|
}
|
|
70
100
|
}
|
|
71
101
|
});
|
|
@@ -74,11 +104,16 @@
|
|
|
74
104
|
|
|
75
105
|
function getSeverityIcon(severity?: DiagnosticSeverity): string {
|
|
76
106
|
switch (severity) {
|
|
77
|
-
case 1:
|
|
78
|
-
|
|
79
|
-
case
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
case 1:
|
|
108
|
+
return 'error';
|
|
109
|
+
case 2:
|
|
110
|
+
return 'warning';
|
|
111
|
+
case 3:
|
|
112
|
+
return 'info';
|
|
113
|
+
case 4:
|
|
114
|
+
return 'hint';
|
|
115
|
+
default:
|
|
116
|
+
return 'info';
|
|
82
117
|
}
|
|
83
118
|
}
|
|
84
119
|
|
|
@@ -149,7 +184,7 @@
|
|
|
149
184
|
{#if allDiagnostics().length === 0}
|
|
150
185
|
<div class="diagnostics-panel__empty">
|
|
151
186
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -- SVG icons are static string literals defined in this file, not user content -->
|
|
152
|
-
|
|
187
|
+
<span class="diagnostics-panel__empty-icon">{@html checkIcon}</span>
|
|
153
188
|
<span>No problems detected</span>
|
|
154
189
|
</div>
|
|
155
190
|
{:else}
|
|
@@ -180,23 +215,6 @@
|
|
|
180
215
|
</div>
|
|
181
216
|
</div>
|
|
182
217
|
|
|
183
|
-
<script module lang="ts">
|
|
184
|
-
const errorIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="var(--ide-bg-primary)" stroke-width="1.5"/></svg>`;
|
|
185
|
-
const warningIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><path d="M8 1L1 15h14L8 1z" fill="currentColor"/><path d="M8 6v4M8 12v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
186
|
-
const infoIcon = `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M8 5v1M8 8v4" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
187
|
-
const checkIcon = `<svg viewBox="0 0 16 16" width="24" height="24"><circle cx="8" cy="8" r="7" fill="var(--ide-success)" opacity="0.2"/><path d="M5 8l2 2 4-4" stroke="var(--ide-success)" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
188
|
-
|
|
189
|
-
function getSeveritySVG(severity?: number): string {
|
|
190
|
-
switch (severity) {
|
|
191
|
-
case 1: return errorIcon;
|
|
192
|
-
case 2: return warningIcon;
|
|
193
|
-
case 3: return infoIcon;
|
|
194
|
-
case 4: return `<svg viewBox="0 0 16 16" width="14" height="14"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M8 4v5M8 11v1" stroke="var(--ide-bg-primary)" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
195
|
-
default: return infoIcon;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
</script>
|
|
199
|
-
|
|
200
218
|
<style>
|
|
201
219
|
.diagnostics-panel {
|
|
202
220
|
display: flex;
|
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
// Simple markdown renderer for hover content
|
|
3
|
+
// Handles code blocks, inline code, bold, italic, and links
|
|
4
|
+
function renderMarkdown(text: string): string {
|
|
5
|
+
let html = escapeHtml(text);
|
|
6
|
+
|
|
7
|
+
// Code blocks with language
|
|
8
|
+
html = html.replace(
|
|
9
|
+
/```(\w+)?\n([\s\S]*?)```/g,
|
|
10
|
+
(_, lang, code) =>
|
|
11
|
+
`<pre class="hover-tooltip__code-block" data-lang="${lang || ''}">${code.trim()}</pre>`
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
// Inline code
|
|
15
|
+
html = html.replace(/`([^`]+)`/g, '<code class="hover-tooltip__inline-code">$1</code>');
|
|
16
|
+
|
|
17
|
+
// Bold
|
|
18
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
19
|
+
|
|
20
|
+
// Italic
|
|
21
|
+
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
22
|
+
|
|
23
|
+
// Links (href is validated; unsafe schemes degrade to plain text)
|
|
24
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label: string, href: string) => {
|
|
25
|
+
const url = safeUrl(href);
|
|
26
|
+
return url ? `<a href="${url}" target="_blank" rel="noopener">${label}</a>` : label;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Line breaks
|
|
30
|
+
html = html.replace(/\n/g, '<br>');
|
|
31
|
+
|
|
32
|
+
return html;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function escapeHtml(text: string): string {
|
|
36
|
+
const map: Record<string, string> = {
|
|
37
|
+
'&': '&',
|
|
38
|
+
'<': '<',
|
|
39
|
+
'>': '>',
|
|
40
|
+
'"': '"',
|
|
41
|
+
"'": '''
|
|
42
|
+
};
|
|
43
|
+
return text.replace(/[&<>"']/g, (char) => map[char]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Whitelist link schemes. Relative/anchor URLs and http(s)/mailto are allowed;
|
|
47
|
+
// anything else (e.g. javascript:, data:, vbscript:) is rejected.
|
|
48
|
+
function safeUrl(url: string): string | null {
|
|
49
|
+
const trimmed = url.trim();
|
|
50
|
+
if (/^(\/|#|\.\/|\.\.\/)/.test(trimmed)) return trimmed;
|
|
51
|
+
if (/^(https?:|mailto:)/i.test(trimmed)) return trimmed;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
1
56
|
<script lang="ts">
|
|
2
57
|
/**
|
|
3
58
|
* HoverTooltip - LSP hover information display
|
|
@@ -20,13 +75,7 @@
|
|
|
20
75
|
class?: string;
|
|
21
76
|
}
|
|
22
77
|
|
|
23
|
-
let {
|
|
24
|
-
hover,
|
|
25
|
-
position,
|
|
26
|
-
maxWidth = 500,
|
|
27
|
-
onDismiss,
|
|
28
|
-
class: className = ''
|
|
29
|
-
}: Props = $props();
|
|
78
|
+
let { hover, position, maxWidth = 500, onDismiss, class: className = '' }: Props = $props();
|
|
30
79
|
|
|
31
80
|
let tooltipRef: HTMLElement;
|
|
32
81
|
|
|
@@ -124,60 +173,6 @@
|
|
|
124
173
|
</div>
|
|
125
174
|
</div>
|
|
126
175
|
|
|
127
|
-
<script module lang="ts">
|
|
128
|
-
// Simple markdown renderer for hover content
|
|
129
|
-
// Handles code blocks, inline code, bold, italic, and links
|
|
130
|
-
function renderMarkdown(text: string): string {
|
|
131
|
-
let html = escapeHtml(text);
|
|
132
|
-
|
|
133
|
-
// Code blocks with language
|
|
134
|
-
html = html.replace(
|
|
135
|
-
/```(\w+)?\n([\s\S]*?)```/g,
|
|
136
|
-
(_, lang, code) => `<pre class="hover-tooltip__code-block" data-lang="${lang || ''}">${code.trim()}</pre>`
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
// Inline code
|
|
140
|
-
html = html.replace(/`([^`]+)`/g, '<code class="hover-tooltip__inline-code">$1</code>');
|
|
141
|
-
|
|
142
|
-
// Bold
|
|
143
|
-
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
144
|
-
|
|
145
|
-
// Italic
|
|
146
|
-
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
147
|
-
|
|
148
|
-
// Links (href is validated; unsafe schemes degrade to plain text)
|
|
149
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label: string, href: string) => {
|
|
150
|
-
const url = safeUrl(href);
|
|
151
|
-
return url ? `<a href="${url}" target="_blank" rel="noopener">${label}</a>` : label;
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Line breaks
|
|
155
|
-
html = html.replace(/\n/g, '<br>');
|
|
156
|
-
|
|
157
|
-
return html;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function escapeHtml(text: string): string {
|
|
161
|
-
const map: Record<string, string> = {
|
|
162
|
-
'&': '&',
|
|
163
|
-
'<': '<',
|
|
164
|
-
'>': '>',
|
|
165
|
-
'"': '"',
|
|
166
|
-
"'": '''
|
|
167
|
-
};
|
|
168
|
-
return text.replace(/[&<>"']/g, (char) => map[char]);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Whitelist link schemes. Relative/anchor URLs and http(s)/mailto are allowed;
|
|
172
|
-
// anything else (e.g. javascript:, data:, vbscript:) is rejected.
|
|
173
|
-
function safeUrl(url: string): string | null {
|
|
174
|
-
const trimmed = url.trim();
|
|
175
|
-
if (/^(\/|#|\.\/|\.\.\/)/.test(trimmed)) return trimmed;
|
|
176
|
-
if (/^(https?:|mailto:)/i.test(trimmed)) return trimmed;
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
</script>
|
|
180
|
-
|
|
181
176
|
<style>
|
|
182
177
|
.hover-tooltip {
|
|
183
178
|
position: fixed;
|
|
@@ -12,12 +12,7 @@
|
|
|
12
12
|
import { onMount } from 'svelte';
|
|
13
13
|
import type { EditorPreferences } from '../../types';
|
|
14
14
|
import type { LSPClient } from '../../services/lsp-client';
|
|
15
|
-
import type {
|
|
16
|
-
CompletionItem,
|
|
17
|
-
Hover,
|
|
18
|
-
SignatureHelp,
|
|
19
|
-
Diagnostic
|
|
20
|
-
} from '../../types/lsp';
|
|
15
|
+
import type { CompletionItem, Hover, SignatureHelp, Diagnostic } from '../../types/lsp';
|
|
21
16
|
import type { Cursor } from '../editor/core/multi-cursor';
|
|
22
17
|
import CustomEditor from '../editor/CustomEditor.svelte';
|
|
23
18
|
import AutocompleteWidget from './AutocompleteWidget.svelte';
|
|
@@ -262,15 +257,13 @@
|
|
|
262
257
|
switch (e.key) {
|
|
263
258
|
case 'ArrowUp':
|
|
264
259
|
e.preventDefault();
|
|
265
|
-
completionSelectedIndex =
|
|
266
|
-
? completionSelectedIndex - 1
|
|
267
|
-
: completionItems.length - 1;
|
|
260
|
+
completionSelectedIndex =
|
|
261
|
+
completionSelectedIndex > 0 ? completionSelectedIndex - 1 : completionItems.length - 1;
|
|
268
262
|
return;
|
|
269
263
|
case 'ArrowDown':
|
|
270
264
|
e.preventDefault();
|
|
271
|
-
completionSelectedIndex =
|
|
272
|
-
? completionSelectedIndex + 1
|
|
273
|
-
: 0;
|
|
265
|
+
completionSelectedIndex =
|
|
266
|
+
completionSelectedIndex < completionItems.length - 1 ? completionSelectedIndex + 1 : 0;
|
|
274
267
|
return;
|
|
275
268
|
case 'Enter':
|
|
276
269
|
case 'Tab':
|
|
@@ -369,7 +362,7 @@
|
|
|
369
362
|
const gutterWidth = 50; // Approximate
|
|
370
363
|
|
|
371
364
|
const x = rect.left + gutterWidth + (cursorColumn - 1) * charWidth;
|
|
372
|
-
const y = rect.top +
|
|
365
|
+
const y = rect.top + cursorLine * lineHeight;
|
|
373
366
|
|
|
374
367
|
return { x, y };
|
|
375
368
|
}
|
|
@@ -459,11 +452,7 @@
|
|
|
459
452
|
|
|
460
453
|
<!-- Hover Tooltip -->
|
|
461
454
|
{#if showHover && hoverData}
|
|
462
|
-
<HoverTooltip
|
|
463
|
-
hover={hoverData}
|
|
464
|
-
position={hoverPosition}
|
|
465
|
-
onDismiss={hideHover}
|
|
466
|
-
/>
|
|
455
|
+
<HoverTooltip hover={hoverData} position={hoverPosition} onDismiss={hideHover} />
|
|
467
456
|
{/if}
|
|
468
457
|
|
|
469
458
|
<!-- Signature Help -->
|
|
@@ -18,12 +18,7 @@
|
|
|
18
18
|
class?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
let {
|
|
22
|
-
signatureHelp,
|
|
23
|
-
position,
|
|
24
|
-
onDismiss,
|
|
25
|
-
class: className = ''
|
|
26
|
-
}: Props = $props();
|
|
21
|
+
let { signatureHelp, position, onDismiss, class: className = '' }: Props = $props();
|
|
27
22
|
|
|
28
23
|
let activeSignature = $derived(
|
|
29
24
|
signatureHelp.signatures[signatureHelp.activeSignature ?? 0] as SignatureInformation | undefined
|
|
@@ -52,7 +47,11 @@
|
|
|
52
47
|
return '';
|
|
53
48
|
}
|
|
54
49
|
|
|
55
|
-
function parseSignatureLabel(sig: SignatureInformation): {
|
|
50
|
+
function parseSignatureLabel(sig: SignatureInformation): {
|
|
51
|
+
before: string;
|
|
52
|
+
param: string;
|
|
53
|
+
after: string;
|
|
54
|
+
} {
|
|
56
55
|
const params = sig.parameters;
|
|
57
56
|
if (!params || params.length === 0 || activeParameterIndex >= params.length) {
|
|
58
57
|
return { before: sig.label, param: '', after: '' };
|
|
@@ -116,12 +115,16 @@
|
|
|
116
115
|
{@const parsed = parseSignatureLabel(activeSignature)}
|
|
117
116
|
<div class="signature-help__signature">
|
|
118
117
|
<code class="signature-help__label">
|
|
119
|
-
<span class="signature-help__label-before">{parsed.before}</span><span
|
|
118
|
+
<span class="signature-help__label-before">{parsed.before}</span><span
|
|
119
|
+
class="signature-help__label-param">{parsed.param}</span
|
|
120
|
+
><span class="signature-help__label-after">{parsed.after}</span>
|
|
120
121
|
</code>
|
|
121
122
|
</div>
|
|
122
123
|
|
|
123
124
|
{#if activeSignature.parameters && activeSignature.parameters[activeParameterIndex]}
|
|
124
|
-
{@const paramDoc = getParameterDocumentation(
|
|
125
|
+
{@const paramDoc = getParameterDocumentation(
|
|
126
|
+
activeSignature.parameters[activeParameterIndex]
|
|
127
|
+
)}
|
|
125
128
|
{#if paramDoc}
|
|
126
129
|
<div class="signature-help__param-doc">
|
|
127
130
|
<span class="signature-help__param-name">
|
|
@@ -19,13 +19,7 @@
|
|
|
19
19
|
onUninstall?: () => void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
let {
|
|
23
|
-
plugin,
|
|
24
|
-
installed = false,
|
|
25
|
-
showStatus = false,
|
|
26
|
-
onInstall,
|
|
27
|
-
onUninstall
|
|
28
|
-
}: Props = $props();
|
|
22
|
+
let { plugin, installed = false, showStatus = false, onInstall, onUninstall }: Props = $props();
|
|
29
23
|
|
|
30
24
|
function getStatusVariant(
|
|
31
25
|
status: PluginStatus
|
|
@@ -72,13 +66,9 @@
|
|
|
72
66
|
</div>
|
|
73
67
|
<div class="plugin-card__actions">
|
|
74
68
|
{#if installed}
|
|
75
|
-
<Button variant="danger" size="xs" onclick={onUninstall}>
|
|
76
|
-
Uninstall
|
|
77
|
-
</Button>
|
|
69
|
+
<Button variant="danger" size="xs" onclick={onUninstall}>Uninstall</Button>
|
|
78
70
|
{:else if plugin.status === 'deployed'}
|
|
79
|
-
<Button variant="primary" size="xs" onclick={onInstall}>
|
|
80
|
-
Install
|
|
81
|
-
</Button>
|
|
71
|
+
<Button variant="primary" size="xs" onclick={onInstall}>Install</Button>
|
|
82
72
|
{/if}
|
|
83
73
|
</div>
|
|
84
74
|
</div>
|