@nocturnium/svelte-ide 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) 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.js +210 -29
  69. package/dist/components/editor/tokenizer/languages/python.js +116 -19
  70. package/dist/components/editor/tokenizer/languages/svelte.js +20 -7
  71. package/dist/components/layout/IDELayout.svelte +6 -2
  72. package/dist/components/layout/StatusBar.svelte +32 -20
  73. package/dist/components/lsp/AutocompleteWidget.svelte +19 -19
  74. package/dist/components/lsp/DiagnosticMarker.svelte +61 -52
  75. package/dist/components/lsp/DiagnosticsPanel.svelte +45 -27
  76. package/dist/components/lsp/HoverTooltip.svelte +56 -61
  77. package/dist/components/lsp/LSPEditor.svelte +7 -18
  78. package/dist/components/lsp/SignatureHelpWidget.svelte +12 -9
  79. package/dist/components/plugins/PluginCard.svelte +3 -13
  80. package/dist/components/plugins/PluginProposalForm.svelte +19 -31
  81. package/dist/components/vfs/LockConflictDialog.svelte +112 -45
  82. package/dist/components/vfs/LockIndicator.svelte +0 -1
  83. package/dist/components/vfs/LockOverlay.svelte +53 -53
  84. package/dist/components/vfs/LockOverlay.svelte.d.ts +5 -5
  85. package/dist/components/vfs/VersionConflictDialog.svelte +107 -77
  86. package/dist/services/error-handling.js +1 -7
  87. package/dist/services/mock-ai.js +9 -7
  88. package/dist/stores/agents.svelte.js +50 -10
  89. package/dist/stores/ai.svelte.js +66 -18
  90. package/dist/stores/collaboration.svelte.js +70 -14
  91. package/dist/stores/editor.svelte.js +50 -10
  92. package/dist/stores/plugin.svelte.js +60 -12
  93. package/dist/stores/vfs.svelte.js +77 -19
  94. package/dist/styles/theme.css +16 -7
  95. package/package.json +186 -1
package/README.md CHANGED
@@ -50,17 +50,13 @@ without the design tokens):
50
50
 
51
51
  ```svelte
52
52
  <script>
53
- import { CustomEditor } from '@nocturnium/svelte-ide';
54
- import '@nocturnium/svelte-ide/theme.css';
53
+ import { CustomEditor } from '@nocturnium/svelte-ide';
54
+ import '@nocturnium/svelte-ide/theme.css';
55
55
 
56
- let code = $state('function hello() {\n console.log("world");\n}');
56
+ let code = $state('function hello() {\n console.log("world");\n}');
57
57
  </script>
58
58
 
59
- <CustomEditor
60
- content={code}
61
- language="javascript"
62
- onChange={(value) => (code = value)}
63
- />
59
+ <CustomEditor content={code} language="javascript" onChange={(value) => (code = value)} />
64
60
  ```
65
61
 
66
62
  `<CustomEditor>` also accepts `readonly`, `folding`, `multiCursor`, `maxCursors`,
@@ -83,24 +79,24 @@ to enumerate them at runtime.
83
79
 
84
80
  ```svelte
85
81
  <script>
86
- import { LSPEditor, createLSPClient } from '@nocturnium/svelte-ide';
87
- import '@nocturnium/svelte-ide/theme.css';
82
+ import { LSPEditor, createLSPClient } from '@nocturnium/svelte-ide';
83
+ import '@nocturnium/svelte-ide/theme.css';
88
84
 
89
- const client = createLSPClient({
90
- serverUrl: 'ws://localhost:8765/lsp?language=typescript',
91
- rootUri: 'file:///workspace',
92
- });
85
+ const client = createLSPClient({
86
+ serverUrl: 'ws://localhost:8765/lsp?language=typescript',
87
+ rootUri: 'file:///workspace'
88
+ });
93
89
 
94
- let code = $state('const greeting: string = "hi";');
90
+ let code = $state('const greeting: string = "hi";');
95
91
  </script>
96
92
 
97
93
  <LSPEditor
98
- content={code}
99
- uri="file:///workspace/main.ts"
100
- language="typescript"
101
- lspClient={client}
102
- onChange={(value) => (code = value)}
103
- onDiagnostics={(diagnostics) => console.log(diagnostics)}
94
+ content={code}
95
+ uri="file:///workspace/main.ts"
96
+ language="typescript"
97
+ lspClient={client}
98
+ onChange={(value) => (code = value)}
99
+ onDiagnostics={(diagnostics) => console.log(diagnostics)}
104
100
  />
105
101
  ```
106
102
 
@@ -112,18 +108,18 @@ You supply the LSP bridge. A ready-to-run Go WebSocket bridge lives in
112
108
 
113
109
  ```svelte
114
110
  <script>
115
- import { CollaborativeEditor } from '@nocturnium/svelte-ide';
116
- import '@nocturnium/svelte-ide/theme.css';
117
- // requires: npm install yjs y-websocket y-protocols
111
+ import { CollaborativeEditor } from '@nocturnium/svelte-ide';
112
+ import '@nocturnium/svelte-ide/theme.css';
113
+ // requires: npm install yjs y-websocket y-protocols
118
114
 
119
- let content = $state('');
115
+ let content = $state('');
120
116
  </script>
121
117
 
122
118
  <CollaborativeEditor
123
- documentId="room-1"
124
- initialContent="// edit together"
125
- language="javascript"
126
- onChange={(value) => (content = value)}
119
+ documentId="room-1"
120
+ initialContent="// edit together"
121
+ language="javascript"
122
+ onChange={(value) => (content = value)}
127
123
  />
128
124
  ```
129
125
 
@@ -134,8 +130,8 @@ See the [Collaboration guide](https://github.com/nocturnium/svelte-ide/blob/main
134
130
 
135
131
  ```svelte
136
132
  <script>
137
- import { AIPanel } from '@nocturnium/svelte-ide';
138
- import '@nocturnium/svelte-ide/theme.css';
133
+ import { AIPanel } from '@nocturnium/svelte-ide';
134
+ import '@nocturnium/svelte-ide/theme.css';
139
135
  </script>
140
136
 
141
137
  <AIPanel />
@@ -152,22 +148,22 @@ The package root exposes the **stable core**. Backend-dependent and
152
148
  experimental subsystems live behind dedicated subpaths — this keeps intent
153
149
  explicit and tree-shaking clean.
154
150
 
155
- | Import | Contents |
156
- | --- | --- |
157
- | `@nocturnium/svelte-ide` | Stable core: editors, layout shell, file explorer, core UI primitives, editor/language/tokenizer/theme utilities, LSP client, layout-store functions, public types |
158
- | `@nocturnium/svelte-ide/theme.css` | Default theme (design tokens + component styles) |
159
- | `.../components/editor` | `CustomEditor`, `Editor`, `EditorPane`, `EditorTabs`, … |
160
- | `.../components/core` | `Button`, `Icon`, `Input`, `Tooltip`, `ResizeHandle`, … |
161
- | `.../components/ai` | `AIPanel`, `AIMessage`, `AIInlineEdit`, … |
162
- | `.../components/lsp` | `LSPEditor`, `AutocompleteWidget`, `HoverTooltip`, … |
163
- | `.../components/agents` | `AgentAvatar`, `AgentActivityPanel`, `AgentCursor`, … |
164
- | `.../components/vfs` | `LockIndicator`, `LockConflictDialog`, … |
165
- | `.../components/layout` | `IDELayout`, `StatusBar` |
166
- | `.../components/plugins` | `PluginPanel`, `PluginCard`, … |
167
- | `.../stores` | Full Svelte 5 runes store surface (layout, editor, ai, plugin, …) |
168
- | `.../plugins` | Plugin runtime (`createPluginLoader`, `definePlugin`, `defineCommand`, `definePanel`, `pluginRegistry`) |
169
- | `.../crdt` | CRDT collaboration primitives (requires Yjs) |
170
- | `.../types`, `.../utils` | Full type and helper-function surface |
151
+ | Import | Contents |
152
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
153
+ | `@nocturnium/svelte-ide` | Stable core: editors, layout shell, file explorer, core UI primitives, editor/language/tokenizer/theme utilities, LSP client, layout-store functions, public types |
154
+ | `@nocturnium/svelte-ide/theme.css` | Default theme (design tokens + component styles) |
155
+ | `.../components/editor` | `CustomEditor`, `Editor`, `EditorPane`, `EditorTabs`, … |
156
+ | `.../components/core` | `Button`, `Icon`, `Input`, `Tooltip`, `ResizeHandle`, … |
157
+ | `.../components/ai` | `AIPanel`, `AIMessage`, `AIInlineEdit`, … |
158
+ | `.../components/lsp` | `LSPEditor`, `AutocompleteWidget`, `HoverTooltip`, … |
159
+ | `.../components/agents` | `AgentAvatar`, `AgentActivityPanel`, `AgentCursor`, … |
160
+ | `.../components/vfs` | `LockIndicator`, `LockConflictDialog`, … |
161
+ | `.../components/layout` | `IDELayout`, `StatusBar` |
162
+ | `.../components/plugins` | `PluginPanel`, `PluginCard`, … |
163
+ | `.../stores` | Full Svelte 5 runes store surface (layout, editor, ai, plugin, …) |
164
+ | `.../plugins` | Plugin runtime (`createPluginLoader`, `definePlugin`, `defineCommand`, `definePanel`, `pluginRegistry`) |
165
+ | `.../crdt` | CRDT collaboration primitives (requires Yjs) |
166
+ | `.../types`, `.../utils` | Full type and helper-function surface |
171
167
 
172
168
  ## API Stability
173
169
 
@@ -57,7 +57,9 @@
57
57
  selectedAgentId ? activities.filter((a) => a.agentId === selectedAgentId) : activities
58
58
  );
59
59
 
60
- let onlineCount = $derived(agents.filter((a) => a.status === 'online' || a.status === 'busy').length);
60
+ let onlineCount = $derived(
61
+ agents.filter((a) => a.status === 'online' || a.status === 'busy').length
62
+ );
61
63
  let busyCount = $derived(agents.filter((a) => a.status === 'busy').length);
62
64
 
63
65
  function formatTime(timestamp: string): string {
@@ -52,20 +52,25 @@
52
52
  if (!agent.currentTask?.progress.phase) return null;
53
53
  const phase = agent.currentTask.progress.phase;
54
54
  switch (phase) {
55
- case 'planning': return 'Planning...';
56
- case 'implementing': return 'Coding...';
57
- case 'testing': return 'Testing...';
58
- case 'complete': return 'Done';
59
- default: return phase;
55
+ case 'planning':
56
+ return 'Planning...';
57
+ case 'implementing':
58
+ return 'Coding...';
59
+ case 'testing':
60
+ return 'Testing...';
61
+ case 'complete':
62
+ return 'Done';
63
+ default:
64
+ return phase;
60
65
  }
61
66
  });
62
67
 
63
68
  // Is this an AI agent?
64
69
  let isAI = $derived(
65
70
  agent.type === 'coder' ||
66
- agent.type === 'reviewer' ||
67
- agent.type === 'tester' ||
68
- agent.type === 'architect'
71
+ agent.type === 'reviewer' ||
72
+ agent.type === 'tester' ||
73
+ agent.type === 'architect'
69
74
  );
70
75
 
71
76
  // Unique ID for SVG gradients
@@ -93,10 +98,10 @@
93
98
  </linearGradient>
94
99
  <!-- Glow filter -->
95
100
  <filter id="glow-{agent.id.slice(0, 8)}" x="-50%" y="-50%" width="200%" height="200%">
96
- <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
101
+ <feGaussianBlur stdDeviation="2" result="coloredBlur" />
97
102
  <feMerge>
98
- <feMergeNode in="coloredBlur"/>
99
- <feMergeNode in="SourceGraphic"/>
103
+ <feMergeNode in="coloredBlur" />
104
+ <feMergeNode in="SourceGraphic" />
100
105
  </feMerge>
101
106
  </filter>
102
107
  </defs>
@@ -159,30 +164,65 @@
159
164
  <div class="agent-avatar__badge-inner">
160
165
  {#if agent.type === 'coder'}
161
166
  <!-- Code brackets icon -->
162
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
167
+ <svg
168
+ viewBox="0 0 24 24"
169
+ fill="none"
170
+ stroke="currentColor"
171
+ stroke-width="2.5"
172
+ stroke-linecap="round"
173
+ stroke-linejoin="round"
174
+ >
163
175
  <polyline points="16 18 22 12 16 6" />
164
176
  <polyline points="8 6 2 12 8 18" />
165
177
  </svg>
166
178
  {:else if agent.type === 'reviewer'}
167
179
  <!-- Checkmark icon -->
168
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
180
+ <svg
181
+ viewBox="0 0 24 24"
182
+ fill="none"
183
+ stroke="currentColor"
184
+ stroke-width="2.5"
185
+ stroke-linecap="round"
186
+ stroke-linejoin="round"
187
+ >
169
188
  <polyline points="20 6 9 17 4 12" />
170
189
  </svg>
171
190
  {:else if agent.type === 'tester'}
172
191
  <!-- Flask icon -->
173
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
192
+ <svg
193
+ viewBox="0 0 24 24"
194
+ fill="none"
195
+ stroke="currentColor"
196
+ stroke-width="2.5"
197
+ stroke-linecap="round"
198
+ stroke-linejoin="round"
199
+ >
174
200
  <path d="M9 3h6v6l4 9H5l4-9V3z" />
175
201
  <path d="M9 3h6" />
176
202
  </svg>
177
203
  {:else if agent.type === 'architect'}
178
204
  <!-- Blueprint/compass icon -->
179
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
205
+ <svg
206
+ viewBox="0 0 24 24"
207
+ fill="none"
208
+ stroke="currentColor"
209
+ stroke-width="2.5"
210
+ stroke-linecap="round"
211
+ stroke-linejoin="round"
212
+ >
180
213
  <circle cx="12" cy="12" r="10" />
181
214
  <polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" />
182
215
  </svg>
183
216
  {:else}
184
217
  <!-- User icon -->
185
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
218
+ <svg
219
+ viewBox="0 0 24 24"
220
+ fill="none"
221
+ stroke="currentColor"
222
+ stroke-width="2.5"
223
+ stroke-linecap="round"
224
+ stroke-linejoin="round"
225
+ >
186
226
  <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
187
227
  <circle cx="12" cy="7" r="4" />
188
228
  </svg>
@@ -230,8 +270,13 @@
230
270
  }
231
271
 
232
272
  @keyframes avatar-breathe {
233
- 0%, 100% { transform: scale(1); }
234
- 50% { transform: scale(1.02); }
273
+ 0%,
274
+ 100% {
275
+ transform: scale(1);
276
+ }
277
+ 50% {
278
+ transform: scale(1.02);
279
+ }
235
280
  }
236
281
 
237
282
  /* Status Ring */
@@ -274,7 +319,8 @@
274
319
  }
275
320
 
276
321
  @keyframes ring-pulse {
277
- 0%, 100% {
322
+ 0%,
323
+ 100% {
278
324
  opacity: 1;
279
325
  box-shadow: 0 0 8px color-mix(in srgb, var(--ide-agent-online) 40%, transparent);
280
326
  }
@@ -285,18 +331,34 @@
285
331
  }
286
332
 
287
333
  @keyframes ring-spin {
288
- from { transform: rotate(0deg); }
289
- to { transform: rotate(360deg); }
334
+ from {
335
+ transform: rotate(0deg);
336
+ }
337
+ to {
338
+ transform: rotate(360deg);
339
+ }
290
340
  }
291
341
 
292
342
  @keyframes ring-error {
293
- 0%, 100% { opacity: 1; transform: scale(1); }
294
- 50% { opacity: 0.6; transform: scale(1.05); }
343
+ 0%,
344
+ 100% {
345
+ opacity: 1;
346
+ transform: scale(1);
347
+ }
348
+ 50% {
349
+ opacity: 0.6;
350
+ transform: scale(1.05);
351
+ }
295
352
  }
296
353
 
297
354
  @keyframes ring-stalled {
298
- 0%, 100% { opacity: 0.8; }
299
- 50% { opacity: 0.3; }
355
+ 0%,
356
+ 100% {
357
+ opacity: 0.8;
358
+ }
359
+ 50% {
360
+ opacity: 0.3;
361
+ }
300
362
  }
301
363
 
302
364
  /* Progress Ring */
@@ -361,8 +423,13 @@
361
423
  }
362
424
 
363
425
  @keyframes badge-shimmer {
364
- 0%, 100% { background-position: 0% 50%; }
365
- 50% { background-position: 100% 50%; }
426
+ 0%,
427
+ 100% {
428
+ background-position: 0% 50%;
429
+ }
430
+ 50% {
431
+ background-position: 100% 50%;
432
+ }
366
433
  }
367
434
 
368
435
  /* Badge Glow */
@@ -380,8 +447,15 @@
380
447
  }
381
448
 
382
449
  @keyframes glow-pulse {
383
- 0%, 100% { opacity: 0.5; transform: scale(1); }
384
- 50% { opacity: 1; transform: scale(1.3); }
450
+ 0%,
451
+ 100% {
452
+ opacity: 0.5;
453
+ transform: scale(1);
454
+ }
455
+ 50% {
456
+ opacity: 1;
457
+ transform: scale(1.3);
458
+ }
385
459
  }
386
460
 
387
461
  /* Phase Label */
@@ -394,15 +468,33 @@
394
468
  }
395
469
 
396
470
  @keyframes phase-fade-in {
397
- from { opacity: 0; transform: translateY(-4px); }
398
- to { opacity: 1; transform: translateY(0); }
471
+ from {
472
+ opacity: 0;
473
+ transform: translateY(-4px);
474
+ }
475
+ to {
476
+ opacity: 1;
477
+ transform: translateY(0);
478
+ }
399
479
  }
400
480
 
401
481
  /* Size-specific adjustments */
402
- .agent-avatar--xs .agent-avatar__ring { inset: -2px; border-width: 1.5px; }
403
- .agent-avatar--sm .agent-avatar__ring { inset: -2px; border-width: 1.5px; }
404
- .agent-avatar--lg .agent-avatar__ring { inset: -4px; border-width: 2.5px; }
405
- .agent-avatar--xl .agent-avatar__ring { inset: -4px; border-width: 3px; }
482
+ .agent-avatar--xs .agent-avatar__ring {
483
+ inset: -2px;
484
+ border-width: 1.5px;
485
+ }
486
+ .agent-avatar--sm .agent-avatar__ring {
487
+ inset: -2px;
488
+ border-width: 1.5px;
489
+ }
490
+ .agent-avatar--lg .agent-avatar__ring {
491
+ inset: -4px;
492
+ border-width: 2.5px;
493
+ }
494
+ .agent-avatar--xl .agent-avatar__ring {
495
+ inset: -4px;
496
+ border-width: 3px;
497
+ }
406
498
 
407
499
  /* Hover state */
408
500
  .agent-avatar:hover .agent-avatar__inner {
@@ -56,7 +56,9 @@
56
56
 
57
57
  // Calculate selection dimensions if present
58
58
  let hasSelection = $derived(
59
- selection && (selection.start.line !== selection.end.line || selection.start.column !== selection.end.column)
59
+ selection &&
60
+ (selection.start.line !== selection.end.line ||
61
+ selection.start.column !== selection.end.column)
60
62
  );
61
63
 
62
64
  function getSelectionStyle() {
@@ -64,10 +66,18 @@
64
66
 
65
67
  const startLine = Math.min(selection.start.line, selection.end.line);
66
68
  const endLine = Math.max(selection.start.line, selection.end.line);
67
- const startCol = selection.start.line < selection.end.line ? selection.start.column :
68
- (selection.start.line === selection.end.line ? Math.min(selection.start.column, selection.end.column) : selection.end.column);
69
- const endCol = selection.start.line < selection.end.line ? selection.end.column :
70
- (selection.start.line === selection.end.line ? Math.max(selection.start.column, selection.end.column) : selection.start.column);
69
+ const startCol =
70
+ selection.start.line < selection.end.line
71
+ ? selection.start.column
72
+ : selection.start.line === selection.end.line
73
+ ? Math.min(selection.start.column, selection.end.column)
74
+ : selection.end.column;
75
+ const endCol =
76
+ selection.start.line < selection.end.line
77
+ ? selection.end.column
78
+ : selection.start.line === selection.end.line
79
+ ? Math.max(selection.start.column, selection.end.column)
80
+ : selection.start.column;
71
81
 
72
82
  // For single line selection
73
83
  if (startLine === endLine) {
@@ -57,7 +57,10 @@
57
57
  {#if activeAgents.length > 0}
58
58
  <div class="presence-bar__avatars">
59
59
  {#each visibleAgents as agent, i (agent.id)}
60
- <Tooltip content="{agent.name} ({agent.status})" position={orientation === 'vertical' ? 'right' : 'bottom'}>
60
+ <Tooltip
61
+ content="{agent.name} ({agent.status})"
62
+ position={orientation === 'vertical' ? 'right' : 'bottom'}
63
+ >
61
64
  <button
62
65
  class="presence-bar__avatar"
63
66
  style="--index: {i}; --total: {visibleAgents.length}"
@@ -146,7 +149,9 @@
146
149
  background: transparent;
147
150
  padding: 0;
148
151
  cursor: pointer;
149
- transition: transform var(--ide-transition-fast), z-index var(--ide-transition-fast);
152
+ transition:
153
+ transform var(--ide-transition-fast),
154
+ z-index var(--ide-transition-fast);
150
155
  }
151
156
 
152
157
  /* Stacked effect for horizontal - overlap avatars */
@@ -19,16 +19,8 @@
19
19
  onExport?: (conversation: AIConversation, format: 'json' | 'md') => void;
20
20
  }
21
21
 
22
- let {
23
- conversations,
24
- activeId,
25
- onSelect,
26
- onNew,
27
- onDelete,
28
- onRename,
29
- onStar,
30
- onExport
31
- }: Props = $props();
22
+ let { conversations, activeId, onSelect, onNew, onDelete, onRename, onStar, onExport }: Props =
23
+ $props();
32
24
 
33
25
  let searchQuery = $state('');
34
26
  let filter = $state<'all' | 'starred' | 'today'>('all');
@@ -138,21 +130,12 @@
138
130
 
139
131
  <!-- Search -->
140
132
  <div class="list-search">
141
- <Input
142
- type="search"
143
- placeholder="Search conversations..."
144
- bind:value={searchQuery}
145
- size="sm"
146
- />
133
+ <Input type="search" placeholder="Search conversations..." bind:value={searchQuery} size="sm" />
147
134
  </div>
148
135
 
149
136
  <!-- Filters -->
150
137
  <div class="list-filters">
151
- <button
152
- class="filter-btn"
153
- class:active={filter === 'all'}
154
- onclick={() => (filter = 'all')}
155
- >
138
+ <button class="filter-btn" class:active={filter === 'all'} onclick={() => (filter = 'all')}>
156
139
  All
157
140
  </button>
158
141
  <button
@@ -163,11 +146,7 @@
163
146
  <Icon name="star" size={12} />
164
147
  Starred
165
148
  </button>
166
- <button
167
- class="filter-btn"
168
- class:active={filter === 'today'}
169
- onclick={() => (filter = 'today')}
170
- >
149
+ <button class="filter-btn" class:active={filter === 'today'} onclick={() => (filter = 'today')}>
171
150
  Today
172
151
  </button>
173
152
  </div>
@@ -209,7 +188,10 @@
209
188
  <div class="item-actions" bind:this={menuRef}>
210
189
  <button
211
190
  class="action-trigger"
212
- onclick={(e) => { e.stopPropagation(); toggleMenu(conv.id); }}
191
+ onclick={(e) => {
192
+ e.stopPropagation();
193
+ toggleMenu(conv.id);
194
+ }}
213
195
  aria-label="Conversation options"
214
196
  aria-expanded={menuOpenId === conv.id}
215
197
  aria-haspopup="menu"
@@ -220,7 +202,13 @@
220
202
  {#if menuOpenId === conv.id}
221
203
  <div class="action-menu" role="menu" aria-label="Conversation actions">
222
204
  {#if onStar}
223
- <button role="menuitem" onclick={() => { onStar(conv, !conv.starred); menuOpenId = null; }}>
205
+ <button
206
+ role="menuitem"
207
+ onclick={() => {
208
+ onStar(conv, !conv.starred);
209
+ menuOpenId = null;
210
+ }}
211
+ >
224
212
  <Icon name={conv.starred ? 'star-off' : 'star'} size={14} />
225
213
  {conv.starred ? 'Unstar' : 'Star'}
226
214
  </button>
@@ -232,17 +220,36 @@
232
220
  </button>
233
221
  {/if}
234
222
  {#if onExport}
235
- <button role="menuitem" onclick={() => { onExport(conv, 'md'); menuOpenId = null; }}>
223
+ <button
224
+ role="menuitem"
225
+ onclick={() => {
226
+ onExport(conv, 'md');
227
+ menuOpenId = null;
228
+ }}
229
+ >
236
230
  <Icon name="download" size={14} />
237
231
  Export Markdown
238
232
  </button>
239
- <button role="menuitem" onclick={() => { onExport(conv, 'json'); menuOpenId = null; }}>
233
+ <button
234
+ role="menuitem"
235
+ onclick={() => {
236
+ onExport(conv, 'json');
237
+ menuOpenId = null;
238
+ }}
239
+ >
240
240
  <Icon name="code" size={14} />
241
241
  Export JSON
242
242
  </button>
243
243
  {/if}
244
244
  {#if onDelete}
245
- <button role="menuitem" class="danger" onclick={() => { onDelete(conv); menuOpenId = null; }}>
245
+ <button
246
+ role="menuitem"
247
+ class="danger"
248
+ onclick={() => {
249
+ onDelete(conv);
250
+ menuOpenId = null;
251
+ }}
252
+ >
246
253
  <Icon name="trash" size={14} />
247
254
  Delete
248
255
  </button>
@@ -262,9 +269,7 @@
262
269
  <p>No conversations today</p>
263
270
  {:else}
264
271
  <p>No conversations yet</p>
265
- <Button variant="primary" size="sm" onclick={onNew}>
266
- Start a conversation
267
- </Button>
272
+ <Button variant="primary" size="sm" onclick={onNew}>Start a conversation</Button>
268
273
  {/if}
269
274
  </div>
270
275
  {/each}
@@ -68,9 +68,7 @@
68
68
  </div>
69
69
 
70
70
  <div class="ai-inline-edit__actions">
71
- <Button variant="ghost" size="sm" onclick={onCancel} disabled={isSubmitting}>
72
- Cancel
73
- </Button>
71
+ <Button variant="ghost" size="sm" onclick={onCancel} disabled={isSubmitting}>Cancel</Button>
74
72
  <Button
75
73
  variant="primary"
76
74
  size="sm"
@@ -86,10 +86,7 @@
86
86
  {#if message.toolCalls && message.toolCalls.length > 0}
87
87
  <div class="ai-message__tool-calls">
88
88
  {#each message.toolCalls as toolCall (toolCall.id)}
89
- <AIToolCallDisplay
90
- {toolCall}
91
- status="completed"
92
- />
89
+ <AIToolCallDisplay {toolCall} status="completed" />
93
90
  {/each}
94
91
  </div>
95
92
  {/if}
@@ -185,7 +182,10 @@
185
182
  }
186
183
 
187
184
  .ai-message--user .ai-message__text {
188
- background: var(--ide-ai-user, color-mix(in srgb, var(--color-nocturnium-wave) 25%, var(--ide-bg-secondary)));
185
+ background: var(
186
+ --ide-ai-user,
187
+ color-mix(in srgb, var(--color-nocturnium-wave) 25%, var(--ide-bg-secondary))
188
+ );
189
189
  color: var(--ide-text-primary);
190
190
  border-bottom-right-radius: var(--ide-radius-sm);
191
191
  border: 1px solid color-mix(in srgb, var(--color-nocturnium-wave) 40%, transparent);
@@ -53,7 +53,12 @@
53
53
  }
54
54
  </script>
55
55
 
56
- <div class="message-actions" class:message-actions--user={isUser} role="group" aria-label="Message actions">
56
+ <div
57
+ class="message-actions"
58
+ class:message-actions--user={isUser}
59
+ role="group"
60
+ aria-label="Message actions"
61
+ >
57
62
  <!-- Copy -->
58
63
  <button
59
64
  class="action-btn"
@@ -78,14 +83,24 @@
78
83
 
79
84
  <!-- Retry (user messages to resend, assistant to regenerate) -->
80
85
  {#if onRetry}
81
- <button class="action-btn" onclick={handleRetry} title={isUser ? 'Resend' : 'Regenerate'} aria-label={isUser ? 'Resend message' : 'Regenerate response'}>
86
+ <button
87
+ class="action-btn"
88
+ onclick={handleRetry}
89
+ title={isUser ? 'Resend' : 'Regenerate'}
90
+ aria-label={isUser ? 'Resend message' : 'Regenerate response'}
91
+ >
82
92
  <Icon name="refresh" size={16} />
83
93
  </button>
84
94
  {/if}
85
95
 
86
96
  <!-- Branch conversation from this point -->
87
97
  {#if onBranch}
88
- <button class="action-btn" onclick={handleBranch} title="Branch from here" aria-label="Create branch from this message">
98
+ <button
99
+ class="action-btn"
100
+ onclick={handleBranch}
101
+ title="Branch from here"
102
+ aria-label="Create branch from this message"
103
+ >
89
104
  <Icon name="git-branch" size={16} />
90
105
  </button>
91
106
  {/if}