@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
@@ -160,7 +160,9 @@
160
160
  {#if echoCursors.length > 0}
161
161
  <div class="echo-mode-indicator">
162
162
  <span class="echo-mode-indicator__icon">~</span>
163
- <span class="echo-mode-indicator__count">{echoCursors.length} echo{echoCursors.length !== 1 ? 's' : ''}</span>
163
+ <span class="echo-mode-indicator__count"
164
+ >{echoCursors.length} echo{echoCursors.length !== 1 ? 's' : ''}</span
165
+ >
164
166
  </div>
165
167
  {/if}
166
168
  </div>
@@ -51,7 +51,11 @@
51
51
  <!-- Line number -->
52
52
  {#if lineNumbers !== 'off'}
53
53
  <div class="custom-editor__gutter" data-cursor-type="default" style="cursor: default !important;">
54
- <span class="custom-editor__line-number" data-cursor-type="default" style="cursor: default !important;">{lineIndex + 1}</span>
54
+ <span
55
+ class="custom-editor__line-number"
56
+ data-cursor-type="default"
57
+ style="cursor: default !important;">{lineIndex + 1}</span
58
+ >
55
59
  </div>
56
60
  {/if}
57
61
 
@@ -73,7 +77,9 @@
73
77
  color: var(--ide-text-muted);
74
78
  font-size: 8px;
75
79
  opacity: 0;
76
- transition: opacity 0.15s ease, color 0.15s ease;
80
+ transition:
81
+ opacity 0.15s ease,
82
+ color 0.15s ease;
77
83
  z-index: 5;
78
84
  }
79
85
 
@@ -84,9 +84,12 @@
84
84
  <button
85
85
  class="custom-editor__fold-placeholder"
86
86
  title="{getHiddenLineCount(index)} lines hidden"
87
- onclick={(e) => { e.stopPropagation(); onExpandFold(index); }}
88
- aria-label="Expand {getHiddenLineCount(index)} hidden lines"
89
- >...</button>
87
+ onclick={(e) => {
88
+ e.stopPropagation();
89
+ onExpandFold(index);
90
+ }}
91
+ aria-label="Expand {getHiddenLineCount(index)} hidden lines">...</button
92
+ >
90
93
  {/if}
91
94
  </div>
92
95
  </div>
@@ -61,12 +61,7 @@
61
61
 
62
62
  <div class="editor-pane {className}">
63
63
  {#if tabs.length > 0}
64
- <EditorTabs
65
- {tabs}
66
- {activeTabId}
67
- onSelect={setActiveTab}
68
- onClose={handleCloseTab}
69
- />
64
+ <EditorTabs {tabs} {activeTabId} onSelect={setActiveTab} onClose={handleCloseTab} />
70
65
 
71
66
  {#if activeTab}
72
67
  <div class="editor-pane__content">
@@ -43,13 +43,25 @@
43
43
  }: Props = $props();
44
44
 
45
45
  // Selection rects memoization
46
- let cachedSelectionRects: Array<{ top: number; left: number; width: number; height: number; isPrimary: boolean }> = [];
46
+ let cachedSelectionRects: Array<{
47
+ top: number;
48
+ left: number;
49
+ width: number;
50
+ height: number;
51
+ isPrimary: boolean;
52
+ }> = [];
47
53
  let cachedSelectionKey = '';
48
54
 
49
55
  // Get selection rectangles for all cursors (memoized and virtualized to viewport)
50
- function getSelectionRects(): Array<{ top: number; left: number; width: number; height: number; isPrimary: boolean }> {
56
+ function getSelectionRects(): Array<{
57
+ top: number;
58
+ left: number;
59
+ width: number;
60
+ height: number;
61
+ isPrimary: boolean;
62
+ }> {
51
63
  // Check if any cursor has a selection
52
- const anySelection = cursors.some(c => !isSelectionEmpty(c.selection));
64
+ const anySelection = cursors.some((c) => !isSelectionEmpty(c.selection));
53
65
  if (!anySelection) {
54
66
  cachedSelectionRects = [];
55
67
  cachedSelectionKey = '';
@@ -59,21 +71,27 @@
59
71
  // Calculate visible line range (with 1-line buffer for smooth scrolling)
60
72
  const vh = viewportHeight || FALLBACK_VIEWPORT_HEIGHT;
61
73
  const firstVisibleLine = Math.max(0, Math.floor(scrollTop / lineHeight) - 1);
62
- const lastVisibleLine = Math.min(
63
- lineCount - 1,
64
- Math.ceil((scrollTop + vh) / lineHeight) + 1
65
- );
74
+ const lastVisibleLine = Math.min(lineCount - 1, Math.ceil((scrollTop + vh) / lineHeight) + 1);
66
75
 
67
76
  // Create cache key from all cursors, scroll position, and measurement values
68
- const cursorKeys = cursors.map(c =>
69
- `${c.id}:${c.selection.anchor.line}:${c.selection.anchor.column}-${c.selection.head.line}:${c.selection.head.column}`
70
- ).join('|');
77
+ const cursorKeys = cursors
78
+ .map(
79
+ (c) =>
80
+ `${c.id}:${c.selection.anchor.line}:${c.selection.anchor.column}-${c.selection.head.line}:${c.selection.head.column}`
81
+ )
82
+ .join('|');
71
83
  const key = `${cursorKeys}@${firstVisibleLine}-${lastVisibleLine}:${charWidth}:${lineHeight}:${gutterWidth}`;
72
84
  if (key === cachedSelectionKey) {
73
85
  return cachedSelectionRects;
74
86
  }
75
87
 
76
- const rects: Array<{ top: number; left: number; width: number; height: number; isPrimary: boolean }> = [];
88
+ const rects: Array<{
89
+ top: number;
90
+ left: number;
91
+ width: number;
92
+ height: number;
93
+ isPrimary: boolean;
94
+ }> = [];
77
95
 
78
96
  // Process each cursor's selection
79
97
  for (const cursor of cursors) {
@@ -399,10 +399,20 @@
399
399
  <div class="file-explorer__header">
400
400
  <span class="file-explorer__title">Explorer</span>
401
401
  <div class="file-explorer__actions">
402
- <Button variant="ghost" size="xs" onclick={() => startInlineCreation('', 'file')} title="New File">
402
+ <Button
403
+ variant="ghost"
404
+ size="xs"
405
+ onclick={() => startInlineCreation('', 'file')}
406
+ title="New File"
407
+ >
403
408
  <Icon name="file-plus" size={14} />
404
409
  </Button>
405
- <Button variant="ghost" size="xs" onclick={() => startInlineCreation('', 'folder')} title="New Folder">
410
+ <Button
411
+ variant="ghost"
412
+ size="xs"
413
+ onclick={() => startInlineCreation('', 'folder')}
414
+ title="New Folder"
415
+ >
406
416
  <Icon name="folder-plus" size={14} />
407
417
  </Button>
408
418
  </div>
@@ -462,7 +472,13 @@
462
472
  class:file-node__status-dot--modified={changeStatus === 'modified'}
463
473
  class:file-node__status-dot--deleted={changeStatus === 'deleted'}
464
474
  class:file-node__status-dot--renamed={changeStatus === 'renamed'}
465
- title={changeStatus === 'created' ? 'New' : changeStatus === 'modified' ? 'Modified' : changeStatus === 'deleted' ? 'Deleted' : 'Renamed'}
475
+ title={changeStatus === 'created'
476
+ ? 'New'
477
+ : changeStatus === 'modified'
478
+ ? 'Modified'
479
+ : changeStatus === 'deleted'
480
+ ? 'Deleted'
481
+ : 'Renamed'}
466
482
  ></span>
467
483
  {/if}
468
484
  {#if node.type === 'folder'}
@@ -493,7 +509,13 @@
493
509
  <div class="file-node__agents">
494
510
  {#each fileAgents.slice(0, 3) as agent (agent.id)}
495
511
  <Tooltip content="{agent.name} is editing">
496
- <AgentAvatar {agent} size="xs" showStatus={false} showBadge={false} showProgress={false} />
512
+ <AgentAvatar
513
+ {agent}
514
+ size="xs"
515
+ showStatus={false}
516
+ showBadge={false}
517
+ showProgress={false}
518
+ />
497
519
  </Tooltip>
498
520
  {/each}
499
521
  {#if fileAgents.length > 3}
@@ -83,7 +83,9 @@
83
83
  file: 'M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm-1 1.5L18.5 9H14a1 1 0 01-1-1V3.5z'
84
84
  };
85
85
 
86
- const iconPath = $derived(isDirectory ? (expanded ? icons.folderOpen : icons.folder) : icons.file);
86
+ const iconPath = $derived(
87
+ isDirectory ? (expanded ? icons.folderOpen : icons.folder) : icons.file
88
+ );
87
89
  const iconColor = $derived(isDirectory ? 'var(--ide-interactive)' : color);
88
90
  </script>
89
91
 
@@ -278,7 +278,12 @@
278
278
  title="Replace all (Shift+Enter)"
279
279
  >
280
280
  <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
281
- <path d="M1 4h6M1 7h8M1 10h6M10 4l2 2-2 2M10 8l2 2-2 2" stroke="currentColor" stroke-width="1.2" fill="none" />
281
+ <path
282
+ d="M1 4h6M1 7h8M1 10h6M10 4l2 2-2 2M10 8l2 2-2 2"
283
+ stroke="currentColor"
284
+ stroke-width="1.2"
285
+ fill="none"
286
+ />
282
287
  </svg>
283
288
  </button>
284
289
  </div>
@@ -316,7 +321,9 @@
316
321
  border-radius: 3px;
317
322
  color: var(--ide-text-secondary);
318
323
  cursor: pointer;
319
- transition: background 0.1s, color 0.1s;
324
+ transition:
325
+ background 0.1s,
326
+ color 0.1s;
320
327
  }
321
328
 
322
329
  .find-replace__toggle:hover {
@@ -386,7 +393,10 @@
386
393
  font-family: var(--ide-font-mono);
387
394
  font-size: 11px;
388
395
  cursor: pointer;
389
- transition: background 0.1s, border-color 0.1s, color 0.1s;
396
+ transition:
397
+ background 0.1s,
398
+ border-color 0.1s,
399
+ color 0.1s;
390
400
  }
391
401
 
392
402
  .find-replace__option:hover {
@@ -425,7 +435,9 @@
425
435
  border-radius: 3px;
426
436
  color: var(--ide-text-secondary);
427
437
  cursor: pointer;
428
- transition: background 0.1s, color 0.1s;
438
+ transition:
439
+ background 0.1s,
440
+ color 0.1s;
429
441
  }
430
442
 
431
443
  .find-replace__action:hover:not(:disabled) {
@@ -191,11 +191,11 @@
191
191
  <div
192
192
  class="bracket-mismatch bracket-mismatch--{mismatch.issue}"
193
193
  style="{getMismatchStyle(mismatch)} --mismatch-color: {color};"
194
- title="{mismatch.issue === 'unclosed'
194
+ title={mismatch.issue === 'unclosed'
195
195
  ? `Unclosed '${mismatch.character}'`
196
196
  : mismatch.issue === 'unexpected'
197
197
  ? `Unexpected '${mismatch.character}'`
198
- : `Expected '${mismatch.expected}' but found '${mismatch.character}'`}"
198
+ : `Expected '${mismatch.expected}' but found '${mismatch.character}'`}
199
199
  >
200
200
  <span class="bracket-mismatch__underline"></span>
201
201
  {#if mismatch.issue === 'mismatched' && mismatch.expected}
@@ -167,7 +167,8 @@
167
167
  class:git-blame-row--hovered={hoveredLine !== null &&
168
168
  hoveredLine >= group.startLine &&
169
169
  hoveredLine <= group.endLine}
170
- style="top: {group.startLine * lineHeight}px; height: {height}px; --blame-color: {info.color};"
170
+ style="top: {group.startLine *
171
+ lineHeight}px; height: {height}px; --blame-color: {info.color};"
171
172
  onmouseenter={(e) => handleMouseEnter(info, e)}
172
173
  onmouseleave={handleMouseLeave}
173
174
  onclick={() => handleClick(info)}
@@ -215,18 +215,8 @@
215
215
  role="button"
216
216
  tabindex={-1}
217
217
  >
218
- <svg
219
- class="squiggle-svg"
220
- width={width}
221
- height="4"
222
- viewBox="0 0 {width} 4"
223
- >
224
- <path
225
- d={generateSquigglePath(width)}
226
- stroke={color}
227
- stroke-width="1"
228
- fill="none"
229
- />
218
+ <svg class="squiggle-svg" {width} height="4" viewBox="0 0 {width} 4">
219
+ <path d={generateSquigglePath(width)} stroke={color} stroke-width="1" fill="none" />
230
220
  </svg>
231
221
  </div>
232
222
  {/each}
@@ -297,7 +287,8 @@
297
287
  {/if}
298
288
 
299
289
  <div class="tooltip-location">
300
- Line {hoveredDiagnostic.range.start.line + 1}, Column {hoveredDiagnostic.range.start.column + 1}
290
+ Line {hoveredDiagnostic.range.start.line + 1}, Column {hoveredDiagnostic.range.start
291
+ .column + 1}
301
292
  </div>
302
293
  </div>
303
294
  {/if}
@@ -77,11 +77,21 @@
77
77
  /**
78
78
  * Group consecutive changes of the same type
79
79
  */
80
- function getGroupedChanges(): Array<{ startLine: number; endLine: number; type: DiffLine['type']; changes: DiffLine[] }> {
80
+ function getGroupedChanges(): Array<{
81
+ startLine: number;
82
+ endLine: number;
83
+ type: DiffLine['type'];
84
+ changes: DiffLine[];
85
+ }> {
81
86
  if (changes.length === 0) return [];
82
87
 
83
- const groups: Array<{ startLine: number; endLine: number; type: DiffLine['type']; changes: DiffLine[] }> = [];
84
- let currentGroup: typeof groups[0] | null = null;
88
+ const groups: Array<{
89
+ startLine: number;
90
+ endLine: number;
91
+ type: DiffLine['type'];
92
+ changes: DiffLine[];
93
+ }> = [];
94
+ let currentGroup: (typeof groups)[0] | null = null;
85
95
 
86
96
  // Sort by line number
87
97
  const sortedChanges = [...changes].sort((a, b) => a.line - b.line);
@@ -166,7 +176,9 @@
166
176
  onkeydown={(e) => e.key === 'Enter' && handleClick(group.changes[0])}
167
177
  role="button"
168
178
  tabindex={-1}
169
- title="{getLabel(group.type)}: {group.changes.length} line{group.changes.length !== 1 ? 's' : ''}"
179
+ title="{getLabel(group.type)}: {group.changes.length} line{group.changes.length !== 1
180
+ ? 's'
181
+ : ''}"
170
182
  ></div>
171
183
 
172
184
  <!-- Optional full-line highlight -->
@@ -184,7 +196,7 @@
184
196
  {/each}
185
197
 
186
198
  <!-- Removed line markers (triangles in gutter) -->
187
- {#each changes.filter(c => c.type === 'removed') as removed (removed.line)}
199
+ {#each changes.filter((c) => c.type === 'removed') as removed (removed.line)}
188
200
  <div
189
201
  class="diff-removed-marker"
190
202
  style="top: {removed.line * lineHeight + lineHeight / 2 - 4}px;"
@@ -195,10 +207,7 @@
195
207
 
196
208
  <!-- Tooltip -->
197
209
  {#if hoveredChange}
198
- <div
199
- class="diff-tooltip"
200
- style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;"
201
- >
210
+ <div class="diff-tooltip" style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;">
202
211
  <div class="tooltip-header" style="color: {getColor(hoveredChange.type)};">
203
212
  {getLabel(hoveredChange.type)} line {hoveredChange.line + 1}
204
213
  </div>
@@ -118,7 +118,7 @@
118
118
  ctx.fillRect(0, 0, width, editorHeight);
119
119
 
120
120
  // Calculate effective line height based on scaling
121
- const effectiveLineHeight = needsScaling ? (editorHeight / lines.length) : LINE_HEIGHT;
121
+ const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
122
122
  const effectiveCharWidth = CHAR_WIDTH * (needsScaling ? scaleFactor : 1);
123
123
 
124
124
  // Draw highlights first (behind text)
@@ -150,11 +150,18 @@
150
150
 
151
151
  if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
152
152
  color = colors.comment;
153
- } else if (trimmed.startsWith('import') || trimmed.startsWith('export') ||
154
- trimmed.startsWith('const') || trimmed.startsWith('let') ||
155
- trimmed.startsWith('function') || trimmed.startsWith('class') ||
156
- trimmed.startsWith('if') || trimmed.startsWith('for') ||
157
- trimmed.startsWith('return') || trimmed.startsWith('async')) {
153
+ } else if (
154
+ trimmed.startsWith('import') ||
155
+ trimmed.startsWith('export') ||
156
+ trimmed.startsWith('const') ||
157
+ trimmed.startsWith('let') ||
158
+ trimmed.startsWith('function') ||
159
+ trimmed.startsWith('class') ||
160
+ trimmed.startsWith('if') ||
161
+ trimmed.startsWith('for') ||
162
+ trimmed.startsWith('return') ||
163
+ trimmed.startsWith('async')
164
+ ) {
158
165
  color = colors.keyword;
159
166
  } else if (trimmed.includes('"') || trimmed.includes("'") || trimmed.includes('`')) {
160
167
  color = colors.string;
@@ -208,7 +215,7 @@
208
215
  function handleClick(e: MouseEvent) {
209
216
  const rect = container.getBoundingClientRect();
210
217
  const y = e.clientY - rect.top;
211
- const effectiveLineHeight = needsScaling ? (editorHeight / lines.length) : LINE_HEIGHT;
218
+ const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
212
219
  const line = Math.floor(y / effectiveLineHeight);
213
220
  onNavigate?.(Math.max(0, Math.min(line, lines.length - 1)));
214
221
  }
@@ -219,7 +226,7 @@
219
226
  function handleMouseMove(e: MouseEvent) {
220
227
  const rect = container.getBoundingClientRect();
221
228
  const y = e.clientY - rect.top;
222
- const effectiveLineHeight = needsScaling ? (editorHeight / lines.length) : LINE_HEIGHT;
229
+ const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
223
230
  hoverLine = Math.floor(y / effectiveLineHeight);
224
231
 
225
232
  if (isDragging) {
@@ -244,7 +251,7 @@
244
251
  if (isDragging) {
245
252
  const rect = container.getBoundingClientRect();
246
253
  const y = Math.max(0, Math.min(e.clientY - rect.top, editorHeight));
247
- const effectiveLineHeight = needsScaling ? (editorHeight / lines.length) : LINE_HEIGHT;
254
+ const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
248
255
  const line = Math.floor(y / effectiveLineHeight);
249
256
  onNavigate?.(Math.max(0, Math.min(line, lines.length - 1)));
250
257
  }
@@ -7,7 +7,6 @@
7
7
  * code transformation preview with rollback capability.
8
8
  */
9
9
 
10
-
11
10
  interface PluginTransform {
12
11
  /** Transform ID */
13
12
  id: string;
@@ -387,7 +386,9 @@
387
386
  {:else}
388
387
  <div class="preview-empty">
389
388
  <p>Select a plugin to preview its transformation</p>
390
- <p class="preview-hint">Your code will be transformed in this sandbox before applying</p>
389
+ <p class="preview-hint">
390
+ Your code will be transformed in this sandbox before applying
391
+ </p>
391
392
  </div>
392
393
  {/if}
393
394
  </div>
@@ -9,11 +9,7 @@
9
9
  * - Batch actions
10
10
  */
11
11
 
12
- import type {
13
- DiagnosticsManager,
14
- Diagnostic,
15
- DiagnosticSeverity
16
- } from './core/diagnostics';
12
+ import type { DiagnosticsManager, Diagnostic, DiagnosticSeverity } from './core/diagnostics';
17
13
  import {
18
14
  getSeverityIcon,
19
15
  getSeverityColor,
@@ -34,13 +30,7 @@
34
30
  onClose?: () => void;
35
31
  }
36
32
 
37
- let {
38
- manager,
39
- open = true,
40
- height = 200,
41
- onNavigate,
42
- onClose
43
- }: Props = $props();
33
+ let { manager, open = true, height = 200, onNavigate, onClose }: Props = $props();
44
34
 
45
35
  let allDiagnostics = $state<Map<string, Diagnostic[]>>(new Map());
46
36
  let searchQuery = $state('');
@@ -195,7 +185,7 @@
195
185
 
196
186
  <div class="header-filters">
197
187
  <!-- Severity filters -->
198
- {#each (['error', 'warning', 'info', 'hint'] as const) as severity (severity)}
188
+ {#each ['error', 'warning', 'info', 'hint'] as const as severity (severity)}
199
189
  {@const count = counts()[severity]}
200
190
  <button
201
191
  class="severity-filter"
@@ -211,11 +201,7 @@
211
201
  {/each}
212
202
 
213
203
  <!-- Source filter -->
214
- <select
215
- class="source-filter"
216
- bind:value={sourceFilter}
217
- title="Filter by source"
218
- >
204
+ <select class="source-filter" bind:value={sourceFilter} title="Filter by source">
219
205
  <option value={null}>All sources</option>
220
206
  {#each allSources() as source (source)}
221
207
  <option value={source}>{source}</option>
@@ -224,12 +210,8 @@
224
210
  </div>
225
211
 
226
212
  <div class="header-actions">
227
- <button class="action-btn" onclick={expandAll} title="Expand all">
228
-
229
- </button>
230
- <button class="action-btn" onclick={collapseAll} title="Collapse all">
231
-
232
- </button>
213
+ <button class="action-btn" onclick={expandAll} title="Expand all"> ⊞ </button>
214
+ <button class="action-btn" onclick={collapseAll} title="Collapse all"> ⊟ </button>
233
215
  <button
234
216
  class="action-btn"
235
217
  class:active={groupByFile}
@@ -239,9 +221,7 @@
239
221
  📁
240
222
  </button>
241
223
  {#if onClose}
242
- <button class="action-btn close-btn" onclick={onClose} title="Close panel">
243
- ×
244
- </button>
224
+ <button class="action-btn close-btn" onclick={onClose} title="Close panel"> × </button>
245
225
  {/if}
246
226
  </div>
247
227
  </div>
@@ -255,9 +235,7 @@
255
235
  bind:value={searchQuery}
256
236
  />
257
237
  {#if searchQuery}
258
- <button class="search-clear" onclick={() => (searchQuery = '')}>
259
- ×
260
- </button>
238
+ <button class="search-clear" onclick={() => (searchQuery = '')}> × </button>
261
239
  {/if}
262
240
  </div>
263
241
 
@@ -303,7 +281,8 @@
303
281
  <span class="diagnostic-code">{diagnostic.code}</span>
304
282
  {/if}
305
283
  <span class="diagnostic-location">
306
- [Ln {diagnostic.range.start.line + 1}, Col {diagnostic.range.start.column + 1}]
284
+ [Ln {diagnostic.range.start.line + 1}, Col {diagnostic.range.start.column +
285
+ 1}]
307
286
  </span>
308
287
  </button>
309
288
  {/each}
@@ -319,10 +298,7 @@
319
298
  class="diagnostic-item diagnostic-item--flat"
320
299
  onclick={() => handleDiagnosticClick(filePath, diagnostic)}
321
300
  >
322
- <span
323
- class="diagnostic-icon"
324
- style="color: {getSeverityColor(diagnostic.severity)};"
325
- >
301
+ <span class="diagnostic-icon" style="color: {getSeverityColor(diagnostic.severity)};">
326
302
  {getSeverityIcon(diagnostic.severity)}
327
303
  </span>
328
304
  <span class="diagnostic-message">{diagnostic.message}</span>
@@ -7,14 +7,8 @@
7
7
  */
8
8
 
9
9
  import { onMount } from 'svelte';
10
- import type {
11
- QuickActionsManager,
12
- CodeAction
13
- } from './core/quick-actions';
14
- import {
15
- groupActionsByKind,
16
- getKindIcon
17
- } from './core/quick-actions';
10
+ import type { QuickActionsManager, CodeAction } from './core/quick-actions';
11
+ import { groupActionsByKind, getKindIcon } from './core/quick-actions';
18
12
 
19
13
  interface Props {
20
14
  /** Quick actions manager instance */
@@ -184,11 +178,7 @@
184
178
 
185
179
  <!-- Actions menu -->
186
180
  {#if isOpen}
187
- <div
188
- class="actions-menu"
189
- role="menu"
190
- aria-label="Code actions"
191
- >
181
+ <div class="actions-menu" role="menu" aria-label="Code actions">
192
182
  {#each [...groupedActions.entries()] as [groupLabel, groupActions] (groupLabel)}
193
183
  <div class="action-group">
194
184
  <div class="group-header">
@@ -265,7 +255,8 @@
265
255
  }
266
256
 
267
257
  @keyframes pulse {
268
- 0%, 100% {
258
+ 0%,
259
+ 100% {
269
260
  box-shadow: 0 0 0 0 rgba(249, 230, 79, 0.4);
270
261
  }
271
262
  50% {
@@ -21,13 +21,7 @@
21
21
  onClose?: () => void;
22
22
  }
23
23
 
24
- let {
25
- manager,
26
- language = 'javascript',
27
- open = false,
28
- onSelect,
29
- onClose
30
- }: Props = $props();
24
+ let { manager, language = 'javascript', open = false, onSelect, onClose }: Props = $props();
31
25
 
32
26
  let searchQuery = $state('');
33
27
  let selectedCategory = $state<string | null>(null);
@@ -163,7 +157,14 @@
163
157
  <div class="snippet-backdrop" onclick={() => onClose?.()} role="presentation"></div>
164
158
 
165
159
  <!-- Palette -->
166
- <div class="snippet-palette" onkeydown={handleKeyDown} role="dialog" tabindex={-1} aria-modal="true" aria-label="Snippet palette">
160
+ <div
161
+ class="snippet-palette"
162
+ onkeydown={handleKeyDown}
163
+ role="dialog"
164
+ tabindex={-1}
165
+ aria-modal="true"
166
+ aria-label="Snippet palette"
167
+ >
167
168
  <div class="snippet-header">
168
169
  <input
169
170
  bind:this={searchInput}
@@ -232,7 +233,7 @@
232
233
 
233
234
  <div class="preview-body">
234
235
  <!-- eslint-disable-next-line svelte/no-at-html-tags — content is escaped by escapeHtml() before placeholder spans are added -->
235
- <pre>{@html getPreview(snippet.body)}</pre>
236
+ <pre>{@html getPreview(snippet.body)}</pre>
236
237
  </div>
237
238
 
238
239
  <div class="preview-footer">
@@ -248,9 +249,7 @@
248
249
  </div>
249
250
  {/if}
250
251
  {:else}
251
- <div class="preview-empty">
252
- Select a snippet to preview
253
- </div>
252
+ <div class="preview-empty">Select a snippet to preview</div>
254
253
  {/if}
255
254
  </div>
256
255
  </div>
@@ -243,7 +243,8 @@
243
243
  <button
244
244
  class="structure-map__node"
245
245
  class:structure-map__node--active={isCursorInNode(node)}
246
- class:structure-map__node--complex={node.metrics.complexity && node.metrics.complexity >= 50}
246
+ class:structure-map__node--complex={node.metrics.complexity &&
247
+ node.metrics.complexity >= 50}
247
248
  style="
248
249
  top: {getNodeTop(node)}%;
249
250
  height: {Math.max(1, getNodeHeight(node))}%;