@nocturnium/svelte-ide 1.0.0-rc.1 → 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 (120) 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 +16 -6
  5. package/dist/components/agents/AgentPresenceBar.svelte +7 -3
  6. package/dist/components/ai/AIConversationList.svelte +40 -34
  7. package/dist/components/ai/AIInlineEdit.svelte +7 -4
  8. package/dist/components/ai/AIMessage.svelte +6 -6
  9. package/dist/components/ai/AIMessageActions.svelte +18 -4
  10. package/dist/components/ai/AIMessageContent.svelte +23 -21
  11. package/dist/components/ai/AIPanel.svelte +24 -17
  12. package/dist/components/ai/AISuggestionWidget.svelte +1 -3
  13. package/dist/components/ai/AIToolCallDisplay.svelte +10 -14
  14. package/dist/components/core/Avatar.svelte +1 -1
  15. package/dist/components/core/Badge.svelte +9 -1
  16. package/dist/components/core/ConnectionStatus.svelte +73 -68
  17. package/dist/components/core/ContextMenu.svelte +1 -1
  18. package/dist/components/core/ErrorBoundary.svelte +57 -57
  19. package/dist/components/core/ErrorBoundary.svelte.d.ts +5 -5
  20. package/dist/components/core/Icon.svelte +22 -11
  21. package/dist/components/core/ResizeHandle.svelte +1 -1
  22. package/dist/components/core/Tooltip.svelte +1 -7
  23. package/dist/components/editor/AIFocusLayer.svelte +15 -7
  24. package/dist/components/editor/Breadcrumbs.svelte +18 -6
  25. package/dist/components/editor/BreakpointLayer.svelte +54 -63
  26. package/dist/components/editor/CognitiveLoadMeter.svelte +5 -3
  27. package/dist/components/editor/CollaborativeEditor.svelte +3 -7
  28. package/dist/components/editor/CommandPalette.svelte +8 -10
  29. package/dist/components/editor/ComplexityLayer.svelte +9 -6
  30. package/dist/components/editor/ComplexityLayer.svelte.d.ts +0 -7
  31. package/dist/components/editor/ConflictZoneLayer.svelte +4 -10
  32. package/dist/components/editor/ContextLens.svelte +2 -5
  33. package/dist/components/editor/CustomEditor.svelte +87 -45
  34. package/dist/components/editor/DebugConsole.svelte +13 -27
  35. package/dist/components/editor/EchoCursorLayer.svelte +7 -7
  36. package/dist/components/editor/EditorGutter.svelte +10 -4
  37. package/dist/components/editor/EditorLines.svelte +6 -3
  38. package/dist/components/editor/EditorPane.svelte +2 -7
  39. package/dist/components/editor/EditorSelections.svelte +31 -13
  40. package/dist/components/editor/FileExplorer.svelte +32 -22
  41. package/dist/components/editor/FileIcon.svelte +3 -1
  42. package/dist/components/editor/FindReplace.svelte +18 -9
  43. package/dist/components/editor/GhostBracketLayer.svelte +3 -4
  44. package/dist/components/editor/GitBlameLayer.svelte +2 -1
  45. package/dist/components/editor/InlineDiagnosticsLayer.svelte +8 -16
  46. package/dist/components/editor/InlineDiffLayer.svelte +18 -9
  47. package/dist/components/editor/MinimalEditor.svelte +1 -1
  48. package/dist/components/editor/MinimalEditor2.svelte +1 -1
  49. package/dist/components/editor/Minimap.svelte +16 -9
  50. package/dist/components/editor/PluginPreviewSandbox.svelte +5 -5
  51. package/dist/components/editor/PluginPreviewSandbox.svelte.d.ts +7 -0
  52. package/dist/components/editor/ProblemsPanel.svelte +16 -41
  53. package/dist/components/editor/QuickActionsMenu.svelte +5 -16
  54. package/dist/components/editor/SnippetPalette.svelte +15 -15
  55. package/dist/components/editor/SnippetPalette.svelte.d.ts +6 -0
  56. package/dist/components/editor/StructureMap.svelte +10 -4
  57. package/dist/components/editor/SymbolOutline.svelte +27 -44
  58. package/dist/components/editor/TimelineScrubber.svelte +9 -9
  59. package/dist/components/editor/TokenRenderer.svelte +1 -1
  60. package/dist/components/editor/core/bracket-healer.js +1 -1
  61. package/dist/components/editor/core/complexity-analyzer.js +42 -12
  62. package/dist/components/editor/core/conflict-predictor.js +2 -4
  63. package/dist/components/editor/core/crdt-binding.js +1 -1
  64. package/dist/components/editor/core/folding.d.ts +9 -0
  65. package/dist/components/editor/core/folding.js +42 -7
  66. package/dist/components/editor/core/git-blame.js +1 -1
  67. package/dist/components/editor/core/keybindings.js +0 -4
  68. package/dist/components/editor/core/multi-cursor.d.ts +1 -1
  69. package/dist/components/editor/core/multi-cursor.js +5 -9
  70. package/dist/components/editor/core/navigation.js +2 -6
  71. package/dist/components/editor/core/quick-actions.js +22 -17
  72. package/dist/components/editor/core/search.js +2 -6
  73. package/dist/components/editor/core/semantic-analyzer.js +1 -3
  74. package/dist/components/editor/core/snippet-manager.js +5 -4
  75. package/dist/components/editor/core/state.js +4 -4
  76. package/dist/components/editor/core/timeline.js +1 -3
  77. package/dist/components/editor/editor-input.js +13 -10
  78. package/dist/components/editor/editor-multicursor.js +2 -2
  79. package/dist/components/editor/languages.js +1 -1
  80. package/dist/components/editor/tokenizer/languages/css.js +146 -29
  81. package/dist/components/editor/tokenizer/languages/go.js +76 -13
  82. package/dist/components/editor/tokenizer/languages/html.js +1 -1
  83. package/dist/components/editor/tokenizer/languages/javascript.js +211 -32
  84. package/dist/components/editor/tokenizer/languages/markdown.js +2 -2
  85. package/dist/components/editor/tokenizer/languages/python.js +116 -19
  86. package/dist/components/editor/tokenizer/languages/svelte.js +21 -8
  87. package/dist/components/layout/IDELayout.svelte +34 -9
  88. package/dist/components/layout/IDELayout.svelte.d.ts +26 -24
  89. package/dist/components/layout/StatusBar.svelte +32 -21
  90. package/dist/components/lsp/AutocompleteWidget.svelte +26 -24
  91. package/dist/components/lsp/DiagnosticMarker.svelte +62 -52
  92. package/dist/components/lsp/DiagnosticsPanel.svelte +51 -39
  93. package/dist/components/lsp/HoverTooltip.svelte +59 -63
  94. package/dist/components/lsp/LSPEditor.svelte +15 -27
  95. package/dist/components/lsp/SignatureHelpWidget.svelte +12 -9
  96. package/dist/components/plugins/PluginCard.svelte +4 -13
  97. package/dist/components/plugins/PluginProposalForm.svelte +21 -34
  98. package/dist/components/vfs/LockConflictDialog.svelte +114 -47
  99. package/dist/components/vfs/LockIndicator.svelte +0 -7
  100. package/dist/components/vfs/LockOverlay.svelte +53 -53
  101. package/dist/components/vfs/LockOverlay.svelte.d.ts +5 -5
  102. package/dist/components/vfs/VersionConflictDialog.svelte +107 -74
  103. package/dist/services/error-handling.js +1 -7
  104. package/dist/services/lsp-client.js +2 -2
  105. package/dist/services/mock-ai.js +9 -7
  106. package/dist/services/optimistic.d.ts +1 -1
  107. package/dist/services/vfs-client.js +1 -1
  108. package/dist/stores/agents.svelte.js +69 -12
  109. package/dist/stores/ai-persistence.svelte.d.ts +3 -1
  110. package/dist/stores/ai-persistence.svelte.js +19 -6
  111. package/dist/stores/ai.svelte.js +72 -16
  112. package/dist/stores/collaboration.svelte.js +81 -17
  113. package/dist/stores/editor.svelte.js +51 -11
  114. package/dist/stores/layout.svelte.js +1 -1
  115. package/dist/stores/plugin.svelte.js +64 -14
  116. package/dist/stores/vfs.svelte.d.ts +1 -1
  117. package/dist/stores/vfs.svelte.js +89 -25
  118. package/dist/styles/theme.css +16 -7
  119. package/dist/types/plugin.d.ts +0 -12
  120. package/package.json +184 -176
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 {
@@ -9,7 +9,7 @@
9
9
  * - Typing indicator animation
10
10
  */
11
11
 
12
- import type { Agent, AgentCursor as AgentCursorType, CursorPosition, CursorSelection } from '../../types';
12
+ import type { Agent, CursorPosition, CursorSelection } from '../../types';
13
13
 
14
14
  interface Props {
15
15
  /** The agent this cursor belongs to */
@@ -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) {
@@ -45,7 +45,6 @@
45
45
  let overflowCount = $derived(Math.max(0, activeAgents.length - maxVisible));
46
46
 
47
47
  // Group by status for summary
48
- let onlineCount = $derived(agents.filter((a) => a.status === 'online').length);
49
48
  let busyCount = $derived(agents.filter((a) => a.status === 'busy').length);
50
49
  let stalledCount = $derived(agents.filter((a) => a.status === 'stalled').length);
51
50
  </script>
@@ -58,7 +57,10 @@
58
57
  {#if activeAgents.length > 0}
59
58
  <div class="presence-bar__avatars">
60
59
  {#each visibleAgents as agent, i (agent.id)}
61
- <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
+ >
62
64
  <button
63
65
  class="presence-bar__avatar"
64
66
  style="--index: {i}; --total: {visibleAgents.length}"
@@ -147,7 +149,9 @@
147
149
  background: transparent;
148
150
  padding: 0;
149
151
  cursor: pointer;
150
- 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);
151
155
  }
152
156
 
153
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');
@@ -78,6 +70,7 @@
78
70
  if (filter === 'starred') {
79
71
  result = result.filter((c) => c.starred);
80
72
  } else if (filter === 'today') {
73
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- temporary comparison value, not reactive UI state
81
74
  const today = new Date();
82
75
  today.setHours(0, 0, 0, 0);
83
76
  result = result.filter((c) => new Date(c.updatedAt) >= today);
@@ -137,21 +130,12 @@
137
130
 
138
131
  <!-- Search -->
139
132
  <div class="list-search">
140
- <Input
141
- type="search"
142
- placeholder="Search conversations..."
143
- bind:value={searchQuery}
144
- size="sm"
145
- />
133
+ <Input type="search" placeholder="Search conversations..." bind:value={searchQuery} size="sm" />
146
134
  </div>
147
135
 
148
136
  <!-- Filters -->
149
137
  <div class="list-filters">
150
- <button
151
- class="filter-btn"
152
- class:active={filter === 'all'}
153
- onclick={() => (filter = 'all')}
154
- >
138
+ <button class="filter-btn" class:active={filter === 'all'} onclick={() => (filter = 'all')}>
155
139
  All
156
140
  </button>
157
141
  <button
@@ -162,11 +146,7 @@
162
146
  <Icon name="star" size={12} />
163
147
  Starred
164
148
  </button>
165
- <button
166
- class="filter-btn"
167
- class:active={filter === 'today'}
168
- onclick={() => (filter = 'today')}
169
- >
149
+ <button class="filter-btn" class:active={filter === 'today'} onclick={() => (filter = 'today')}>
170
150
  Today
171
151
  </button>
172
152
  </div>
@@ -208,7 +188,10 @@
208
188
  <div class="item-actions" bind:this={menuRef}>
209
189
  <button
210
190
  class="action-trigger"
211
- onclick={(e) => { e.stopPropagation(); toggleMenu(conv.id); }}
191
+ onclick={(e) => {
192
+ e.stopPropagation();
193
+ toggleMenu(conv.id);
194
+ }}
212
195
  aria-label="Conversation options"
213
196
  aria-expanded={menuOpenId === conv.id}
214
197
  aria-haspopup="menu"
@@ -219,7 +202,13 @@
219
202
  {#if menuOpenId === conv.id}
220
203
  <div class="action-menu" role="menu" aria-label="Conversation actions">
221
204
  {#if onStar}
222
- <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
+ >
223
212
  <Icon name={conv.starred ? 'star-off' : 'star'} size={14} />
224
213
  {conv.starred ? 'Unstar' : 'Star'}
225
214
  </button>
@@ -231,17 +220,36 @@
231
220
  </button>
232
221
  {/if}
233
222
  {#if onExport}
234
- <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
+ >
235
230
  <Icon name="download" size={14} />
236
231
  Export Markdown
237
232
  </button>
238
- <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
+ >
239
240
  <Icon name="code" size={14} />
240
241
  Export JSON
241
242
  </button>
242
243
  {/if}
243
244
  {#if onDelete}
244
- <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
+ >
245
253
  <Icon name="trash" size={14} />
246
254
  Delete
247
255
  </button>
@@ -261,9 +269,7 @@
261
269
  <p>No conversations today</p>
262
270
  {:else}
263
271
  <p>No conversations yet</p>
264
- <Button variant="primary" size="sm" onclick={onNew}>
265
- Start a conversation
266
- </Button>
272
+ <Button variant="primary" size="sm" onclick={onNew}>Start a conversation</Button>
267
273
  {/if}
268
274
  </div>
269
275
  {/each}
@@ -17,6 +17,8 @@
17
17
  class: className = ''
18
18
  }: Props = $props();
19
19
 
20
+ // Seeded once from the prop: this is the editable input buffer, mounted fresh per edit.
21
+ // svelte-ignore state_referenced_locally
20
22
  let prompt = $state(initialPrompt);
21
23
  let isSubmitting = $state(false);
22
24
 
@@ -40,6 +42,9 @@
40
42
  }
41
43
  </script>
42
44
 
45
+ <!-- Wrapper-level keydown only captures Escape / Cmd+Enter shortcuts; the real controls
46
+ (textarea, buttons) inside are the focusable interactive elements. -->
47
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
43
48
  <div class="ai-inline-edit {className}" onkeydown={handleKeydown}>
44
49
  <div class="ai-inline-edit__header">
45
50
  <Icon name="sparkles" size={14} />
@@ -63,9 +68,7 @@
63
68
  </div>
64
69
 
65
70
  <div class="ai-inline-edit__actions">
66
- <Button variant="ghost" size="sm" onclick={onCancel} disabled={isSubmitting}>
67
- Cancel
68
- </Button>
71
+ <Button variant="ghost" size="sm" onclick={onCancel} disabled={isSubmitting}>Cancel</Button>
69
72
  <Button
70
73
  variant="primary"
71
74
  size="sm"
@@ -75,7 +78,7 @@
75
78
  {#if isSubmitting}
76
79
  <Spinner size="xs" />
77
80
  {:else}
78
- {#snippet icon()}<Icon name="wand" size={14} />{/snippet}
81
+ {#snippet _icon()}<Icon name="wand" size={14} />{/snippet}
79
82
  {/if}
80
83
  Apply
81
84
  </Button>