@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.
Files changed (96) hide show
  1. package/README.md +43 -47
  2. package/dist/components/agents/AgentActivityPanel.svelte +3 -1
  3. package/dist/components/agents/AgentAvatar.svelte +127 -35
  4. package/dist/components/agents/AgentCursor.svelte +15 -5
  5. package/dist/components/agents/AgentPresenceBar.svelte +7 -2
  6. package/dist/components/ai/AIConversationList.svelte +39 -34
  7. package/dist/components/ai/AIInlineEdit.svelte +1 -3
  8. package/dist/components/ai/AIMessage.svelte +5 -5
  9. package/dist/components/ai/AIMessageActions.svelte +18 -3
  10. package/dist/components/ai/AIMessageContent.svelte +22 -20
  11. package/dist/components/ai/AIPanel.svelte +17 -9
  12. package/dist/components/ai/AISuggestionWidget.svelte +1 -3
  13. package/dist/components/ai/AIToolCallDisplay.svelte +10 -14
  14. package/dist/components/core/Badge.svelte +9 -1
  15. package/dist/components/core/ConnectionStatus.svelte +73 -68
  16. package/dist/components/core/ErrorBoundary.svelte +56 -56
  17. package/dist/components/core/ErrorBoundary.svelte.d.ts +5 -5
  18. package/dist/components/core/Icon.svelte +22 -11
  19. package/dist/components/core/ResizeHandle.svelte +1 -1
  20. package/dist/components/core/Tooltip.svelte +1 -7
  21. package/dist/components/editor/AIFocusLayer.svelte +15 -7
  22. package/dist/components/editor/Breadcrumbs.svelte +18 -6
  23. package/dist/components/editor/BreakpointLayer.svelte +51 -60
  24. package/dist/components/editor/CognitiveLoadMeter.svelte +4 -2
  25. package/dist/components/editor/CollaborativeEditor.svelte +1 -5
  26. package/dist/components/editor/CommandPalette.svelte +1 -4
  27. package/dist/components/editor/ComplexityLayer.svelte +8 -6
  28. package/dist/components/editor/ConflictZoneLayer.svelte +2 -8
  29. package/dist/components/editor/ContextLens.svelte +1 -4
  30. package/dist/components/editor/CustomEditor.svelte +85 -41
  31. package/dist/components/editor/DebugConsole.svelte +8 -17
  32. package/dist/components/editor/EchoCursorLayer.svelte +3 -1
  33. package/dist/components/editor/EditorGutter.svelte +8 -2
  34. package/dist/components/editor/EditorLines.svelte +6 -3
  35. package/dist/components/editor/EditorPane.svelte +1 -6
  36. package/dist/components/editor/EditorSelections.svelte +29 -11
  37. package/dist/components/editor/FileExplorer.svelte +26 -4
  38. package/dist/components/editor/FileIcon.svelte +3 -1
  39. package/dist/components/editor/FindReplace.svelte +16 -4
  40. package/dist/components/editor/GhostBracketLayer.svelte +2 -2
  41. package/dist/components/editor/GitBlameLayer.svelte +2 -1
  42. package/dist/components/editor/InlineDiagnosticsLayer.svelte +4 -13
  43. package/dist/components/editor/InlineDiffLayer.svelte +18 -9
  44. package/dist/components/editor/Minimap.svelte +16 -9
  45. package/dist/components/editor/PluginPreviewSandbox.svelte +3 -2
  46. package/dist/components/editor/ProblemsPanel.svelte +11 -35
  47. package/dist/components/editor/QuickActionsMenu.svelte +5 -14
  48. package/dist/components/editor/SnippetPalette.svelte +11 -12
  49. package/dist/components/editor/StructureMap.svelte +2 -1
  50. package/dist/components/editor/SymbolOutline.svelte +14 -19
  51. package/dist/components/editor/TimelineScrubber.svelte +7 -6
  52. package/dist/components/editor/core/complexity-analyzer.js +42 -12
  53. package/dist/components/editor/core/conflict-predictor.js +2 -4
  54. package/dist/components/editor/core/folding.d.ts +9 -0
  55. package/dist/components/editor/core/folding.js +40 -5
  56. package/dist/components/editor/core/multi-cursor.js +4 -8
  57. package/dist/components/editor/core/navigation.js +2 -6
  58. package/dist/components/editor/core/quick-actions.js +22 -17
  59. package/dist/components/editor/core/search.js +2 -6
  60. package/dist/components/editor/core/semantic-analyzer.js +1 -3
  61. package/dist/components/editor/core/snippet-manager.js +4 -3
  62. package/dist/components/editor/core/state.js +2 -2
  63. package/dist/components/editor/core/timeline.js +1 -3
  64. package/dist/components/editor/editor-input.js +9 -6
  65. package/dist/components/editor/editor-multicursor.js +2 -2
  66. package/dist/components/editor/tokenizer/languages/css.js +146 -24
  67. package/dist/components/editor/tokenizer/languages/go.js +76 -13
  68. package/dist/components/editor/tokenizer/languages/javascript.d.ts +13 -2
  69. package/dist/components/editor/tokenizer/languages/javascript.js +278 -84
  70. package/dist/components/editor/tokenizer/languages/python.js +116 -19
  71. package/dist/components/editor/tokenizer/languages/svelte.js +20 -7
  72. package/dist/components/layout/IDELayout.svelte +6 -2
  73. package/dist/components/layout/StatusBar.svelte +32 -20
  74. package/dist/components/lsp/AutocompleteWidget.svelte +19 -19
  75. package/dist/components/lsp/DiagnosticMarker.svelte +61 -52
  76. package/dist/components/lsp/DiagnosticsPanel.svelte +45 -27
  77. package/dist/components/lsp/HoverTooltip.svelte +56 -61
  78. package/dist/components/lsp/LSPEditor.svelte +7 -18
  79. package/dist/components/lsp/SignatureHelpWidget.svelte +12 -9
  80. package/dist/components/plugins/PluginCard.svelte +3 -13
  81. package/dist/components/plugins/PluginProposalForm.svelte +19 -31
  82. package/dist/components/vfs/LockConflictDialog.svelte +112 -45
  83. package/dist/components/vfs/LockIndicator.svelte +0 -1
  84. package/dist/components/vfs/LockOverlay.svelte +53 -53
  85. package/dist/components/vfs/LockOverlay.svelte.d.ts +5 -5
  86. package/dist/components/vfs/VersionConflictDialog.svelte +107 -77
  87. package/dist/services/error-handling.js +1 -7
  88. package/dist/services/mock-ai.js +9 -7
  89. package/dist/stores/agents.svelte.js +50 -10
  90. package/dist/stores/ai.svelte.js +66 -18
  91. package/dist/stores/collaboration.svelte.js +70 -14
  92. package/dist/stores/editor.svelte.js +50 -10
  93. package/dist/stores/plugin.svelte.js +60 -12
  94. package/dist/stores/vfs.svelte.js +77 -19
  95. package/dist/styles/theme.css +16 -7
  96. 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': action = 'planning'; break;
111
- case 'implementing': action = 'coding'; break;
112
- case 'testing': action = 'testing'; break;
113
- case 'complete': action = 'done'; break;
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%, 100% { background-position: 0% 50%; }
438
- 50% { background-position: 100% 50%; }
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%, 100% { opacity: 1; }
472
- 50% { opacity: 0.6; }
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: return 'error';
31
- case 2: return 'warning';
32
- case 3: return 'info';
33
- case 4: return 'hint';
34
- default: return 'info';
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: return 'Error';
41
- case 2: return 'Warning';
42
- case 3: return 'Info';
43
- case 4: return 'Hint';
44
- default: return 'Diagnostic';
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: result.errors++; break;
66
- case 2: result.warnings++; break;
67
- case 3: result.info++; break;
68
- case 4: result.hints++; break;
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: return 'error';
78
- case 2: return 'warning';
79
- case 3: return 'info';
80
- case 4: return 'hint';
81
- default: return 'info';
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
- <span class="diagnostics-panel__empty-icon">{@html checkIcon}</span>
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
+ '&': '&amp;',
38
+ '<': '&lt;',
39
+ '>': '&gt;',
40
+ '"': '&quot;',
41
+ "'": '&#039;'
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
- '&': '&amp;',
163
- '<': '&lt;',
164
- '>': '&gt;',
165
- '"': '&quot;',
166
- "'": '&#039;'
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 = completionSelectedIndex > 0
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 = completionSelectedIndex < completionItems.length - 1
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 + (cursorLine) * lineHeight;
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): { before: string; param: string; after: string } {
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 class="signature-help__label-param">{parsed.param}</span><span class="signature-help__label-after">{parsed.after}</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(activeSignature.parameters[activeParameterIndex])}
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>