@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.
- package/README.md +43 -47
- package/dist/components/agents/AgentActivityPanel.svelte +3 -1
- package/dist/components/agents/AgentAvatar.svelte +127 -35
- package/dist/components/agents/AgentCursor.svelte +15 -5
- package/dist/components/agents/AgentPresenceBar.svelte +7 -2
- package/dist/components/ai/AIConversationList.svelte +39 -34
- package/dist/components/ai/AIInlineEdit.svelte +1 -3
- package/dist/components/ai/AIMessage.svelte +5 -5
- package/dist/components/ai/AIMessageActions.svelte +18 -3
- package/dist/components/ai/AIMessageContent.svelte +22 -20
- package/dist/components/ai/AIPanel.svelte +17 -9
- package/dist/components/ai/AISuggestionWidget.svelte +1 -3
- package/dist/components/ai/AIToolCallDisplay.svelte +10 -14
- package/dist/components/core/Badge.svelte +9 -1
- package/dist/components/core/ConnectionStatus.svelte +73 -68
- package/dist/components/core/ErrorBoundary.svelte +56 -56
- package/dist/components/core/ErrorBoundary.svelte.d.ts +5 -5
- package/dist/components/core/Icon.svelte +22 -11
- package/dist/components/core/ResizeHandle.svelte +1 -1
- package/dist/components/core/Tooltip.svelte +1 -7
- package/dist/components/editor/AIFocusLayer.svelte +15 -7
- package/dist/components/editor/Breadcrumbs.svelte +18 -6
- package/dist/components/editor/BreakpointLayer.svelte +51 -60
- package/dist/components/editor/CognitiveLoadMeter.svelte +4 -2
- package/dist/components/editor/CollaborativeEditor.svelte +1 -5
- package/dist/components/editor/CommandPalette.svelte +1 -4
- package/dist/components/editor/ComplexityLayer.svelte +8 -6
- package/dist/components/editor/ConflictZoneLayer.svelte +2 -8
- package/dist/components/editor/ContextLens.svelte +1 -4
- package/dist/components/editor/CustomEditor.svelte +85 -41
- package/dist/components/editor/DebugConsole.svelte +8 -17
- package/dist/components/editor/EchoCursorLayer.svelte +3 -1
- package/dist/components/editor/EditorGutter.svelte +8 -2
- package/dist/components/editor/EditorLines.svelte +6 -3
- package/dist/components/editor/EditorPane.svelte +1 -6
- package/dist/components/editor/EditorSelections.svelte +29 -11
- package/dist/components/editor/FileExplorer.svelte +26 -4
- package/dist/components/editor/FileIcon.svelte +3 -1
- package/dist/components/editor/FindReplace.svelte +16 -4
- package/dist/components/editor/GhostBracketLayer.svelte +2 -2
- package/dist/components/editor/GitBlameLayer.svelte +2 -1
- package/dist/components/editor/InlineDiagnosticsLayer.svelte +4 -13
- package/dist/components/editor/InlineDiffLayer.svelte +18 -9
- package/dist/components/editor/Minimap.svelte +16 -9
- package/dist/components/editor/PluginPreviewSandbox.svelte +3 -2
- package/dist/components/editor/ProblemsPanel.svelte +11 -35
- package/dist/components/editor/QuickActionsMenu.svelte +5 -14
- package/dist/components/editor/SnippetPalette.svelte +11 -12
- package/dist/components/editor/StructureMap.svelte +2 -1
- package/dist/components/editor/SymbolOutline.svelte +14 -19
- package/dist/components/editor/TimelineScrubber.svelte +7 -6
- package/dist/components/editor/core/complexity-analyzer.js +42 -12
- package/dist/components/editor/core/conflict-predictor.js +2 -4
- package/dist/components/editor/core/folding.d.ts +9 -0
- package/dist/components/editor/core/folding.js +40 -5
- package/dist/components/editor/core/multi-cursor.js +4 -8
- package/dist/components/editor/core/navigation.js +2 -6
- package/dist/components/editor/core/quick-actions.js +22 -17
- package/dist/components/editor/core/search.js +2 -6
- package/dist/components/editor/core/semantic-analyzer.js +1 -3
- package/dist/components/editor/core/snippet-manager.js +4 -3
- package/dist/components/editor/core/state.js +2 -2
- package/dist/components/editor/core/timeline.js +1 -3
- package/dist/components/editor/editor-input.js +9 -6
- package/dist/components/editor/editor-multicursor.js +2 -2
- package/dist/components/editor/tokenizer/languages/css.js +146 -24
- package/dist/components/editor/tokenizer/languages/go.js +76 -13
- package/dist/components/editor/tokenizer/languages/javascript.js +210 -29
- package/dist/components/editor/tokenizer/languages/python.js +116 -19
- package/dist/components/editor/tokenizer/languages/svelte.js +20 -7
- package/dist/components/layout/IDELayout.svelte +6 -2
- package/dist/components/layout/StatusBar.svelte +32 -20
- package/dist/components/lsp/AutocompleteWidget.svelte +19 -19
- package/dist/components/lsp/DiagnosticMarker.svelte +61 -52
- package/dist/components/lsp/DiagnosticsPanel.svelte +45 -27
- package/dist/components/lsp/HoverTooltip.svelte +56 -61
- package/dist/components/lsp/LSPEditor.svelte +7 -18
- package/dist/components/lsp/SignatureHelpWidget.svelte +12 -9
- package/dist/components/plugins/PluginCard.svelte +3 -13
- package/dist/components/plugins/PluginProposalForm.svelte +19 -31
- package/dist/components/vfs/LockConflictDialog.svelte +112 -45
- package/dist/components/vfs/LockIndicator.svelte +0 -1
- package/dist/components/vfs/LockOverlay.svelte +53 -53
- package/dist/components/vfs/LockOverlay.svelte.d.ts +5 -5
- package/dist/components/vfs/VersionConflictDialog.svelte +107 -77
- package/dist/services/error-handling.js +1 -7
- package/dist/services/mock-ai.js +9 -7
- package/dist/stores/agents.svelte.js +50 -10
- package/dist/stores/ai.svelte.js +66 -18
- package/dist/stores/collaboration.svelte.js +70 -14
- package/dist/stores/editor.svelte.js +50 -10
- package/dist/stores/plugin.svelte.js +60 -12
- package/dist/stores/vfs.svelte.js +77 -19
- package/dist/styles/theme.css +16 -7
- package/package.json +186 -1
|
@@ -121,20 +121,22 @@
|
|
|
121
121
|
// Format text with basic markdown. Input is escaped FIRST, then a small set of
|
|
122
122
|
// safe inline elements is re-introduced — so the only HTML emitted is ours.
|
|
123
123
|
function formatText(text: string): string {
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
return (
|
|
125
|
+
escapeHtml(text)
|
|
126
|
+
// Bold
|
|
127
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
128
|
+
// Italic
|
|
129
|
+
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
130
|
+
// Links (href is validated; unsafe schemes degrade to plain text)
|
|
131
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label: string, href: string) => {
|
|
132
|
+
const url = safeUrl(href);
|
|
133
|
+
return url
|
|
134
|
+
? `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`
|
|
135
|
+
: label;
|
|
136
|
+
})
|
|
137
|
+
// Line breaks
|
|
138
|
+
.replace(/\n/g, '<br>')
|
|
139
|
+
);
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
// Copy code to clipboard
|
|
@@ -159,11 +161,7 @@
|
|
|
159
161
|
<div class="code-block">
|
|
160
162
|
<div class="code-header">
|
|
161
163
|
<span class="code-language">{block.language}</span>
|
|
162
|
-
<button
|
|
163
|
-
class="code-copy"
|
|
164
|
-
onclick={() => copyCode(block.content, i)}
|
|
165
|
-
title="Copy code"
|
|
166
|
-
>
|
|
164
|
+
<button class="code-copy" onclick={() => copyCode(block.content, i)} title="Copy code">
|
|
167
165
|
{#if copiedIndex === i}
|
|
168
166
|
<Icon name="check" size={14} />
|
|
169
167
|
<span>Copied!</span>
|
|
@@ -173,7 +171,11 @@
|
|
|
173
171
|
{/if}
|
|
174
172
|
</button>
|
|
175
173
|
</div>
|
|
176
|
-
<pre class="code-content"><code
|
|
174
|
+
<pre class="code-content"><code
|
|
175
|
+
>{#each tokenizeCode(block.content, block.language || 'plaintext') as token, ti (ti)}<span
|
|
176
|
+
class="token-{token.type}">{token.text}</span
|
|
177
|
+
>{/each}</code
|
|
178
|
+
></pre>
|
|
177
179
|
</div>
|
|
178
180
|
{:else if block.type === 'inline-code'}
|
|
179
181
|
<code class="inline-code">{block.content}</code>
|
|
@@ -133,8 +133,11 @@
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
function handleExportConversation(conv: AIConversation, format: 'json' | 'md') {
|
|
136
|
-
const content =
|
|
137
|
-
|
|
136
|
+
const content =
|
|
137
|
+
format === 'md' ? exportConversationMarkdown(conv) : exportConversationJSON(conv);
|
|
138
|
+
const blob = new Blob([content], {
|
|
139
|
+
type: format === 'md' ? 'text/markdown' : 'application/json'
|
|
140
|
+
});
|
|
138
141
|
const url = URL.createObjectURL(blob);
|
|
139
142
|
const a = document.createElement('a');
|
|
140
143
|
a.href = url;
|
|
@@ -214,7 +217,13 @@
|
|
|
214
217
|
</div>
|
|
215
218
|
|
|
216
219
|
<!-- Messages -->
|
|
217
|
-
<div
|
|
220
|
+
<div
|
|
221
|
+
class="ai-panel__messages"
|
|
222
|
+
bind:this={messagesContainer}
|
|
223
|
+
role="log"
|
|
224
|
+
aria-live="polite"
|
|
225
|
+
aria-label="Conversation messages"
|
|
226
|
+
>
|
|
218
227
|
{#if getMessages().length === 0}
|
|
219
228
|
<div class="ai-panel__empty">
|
|
220
229
|
<div class="ai-panel__empty-icon">
|
|
@@ -229,18 +238,17 @@
|
|
|
229
238
|
<button class="suggestion-chip" onclick={() => (inputValue = 'Find bugs in this file')}>
|
|
230
239
|
Find bugs
|
|
231
240
|
</button>
|
|
232
|
-
<button
|
|
241
|
+
<button
|
|
242
|
+
class="suggestion-chip"
|
|
243
|
+
onclick={() => (inputValue = 'Write tests for this function')}
|
|
244
|
+
>
|
|
233
245
|
Write tests
|
|
234
246
|
</button>
|
|
235
247
|
</div>
|
|
236
248
|
</div>
|
|
237
249
|
{:else}
|
|
238
250
|
{#each getMessages() as message (message.id)}
|
|
239
|
-
<AIMessage
|
|
240
|
-
{message}
|
|
241
|
-
onCopy={handleCopyMessage}
|
|
242
|
-
onRetry={handleRetryMessage}
|
|
243
|
-
/>
|
|
251
|
+
<AIMessage {message} onCopy={handleCopyMessage} onRetry={handleRetryMessage} />
|
|
244
252
|
{/each}
|
|
245
253
|
{#if getIsStreaming()}
|
|
246
254
|
<div class="ai-panel__typing">
|
|
@@ -62,9 +62,7 @@
|
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
64
|
<div class="ai-suggestion__actions">
|
|
65
|
-
<Button variant="ghost" size="xs" onclick={onDismiss}>
|
|
66
|
-
Dismiss
|
|
67
|
-
</Button>
|
|
65
|
+
<Button variant="ghost" size="xs" onclick={onDismiss}>Dismiss</Button>
|
|
68
66
|
<Button variant="primary" size="xs" onclick={onAccept}>
|
|
69
67
|
{#snippet icon()}<Icon name="check" size={12} />{/snippet}
|
|
70
68
|
Accept
|
|
@@ -11,14 +11,7 @@
|
|
|
11
11
|
startedAt?: Date;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
let {
|
|
15
|
-
toolCall,
|
|
16
|
-
status = 'completed',
|
|
17
|
-
result,
|
|
18
|
-
error,
|
|
19
|
-
duration,
|
|
20
|
-
startedAt
|
|
21
|
-
}: Props = $props();
|
|
14
|
+
let { toolCall, status = 'completed', result, error, duration, startedAt }: Props = $props();
|
|
22
15
|
|
|
23
16
|
let expanded = $state(false);
|
|
24
17
|
let showResult = $state(false);
|
|
@@ -74,7 +67,10 @@
|
|
|
74
67
|
}
|
|
75
68
|
|
|
76
69
|
// Truncate long results
|
|
77
|
-
function truncateResult(
|
|
70
|
+
function truncateResult(
|
|
71
|
+
r: string,
|
|
72
|
+
maxLength: number = 500
|
|
73
|
+
): { text: string; truncated: boolean } {
|
|
78
74
|
if (r.length <= maxLength) return { text: r, truncated: false };
|
|
79
75
|
return { text: r.slice(0, maxLength), truncated: true };
|
|
80
76
|
}
|
|
@@ -139,15 +135,15 @@
|
|
|
139
135
|
<Icon name="arrow-left" size={12} />
|
|
140
136
|
<span>Result</span>
|
|
141
137
|
{#if truncatedResult?.truncated}
|
|
142
|
-
<button
|
|
143
|
-
class="tool-call__toggle"
|
|
144
|
-
onclick={() => (showResult = !showResult)}
|
|
145
|
-
>
|
|
138
|
+
<button class="tool-call__toggle" onclick={() => (showResult = !showResult)}>
|
|
146
139
|
{showResult ? 'Show less' : 'Show full'}
|
|
147
140
|
</button>
|
|
148
141
|
{/if}
|
|
149
142
|
</div>
|
|
150
|
-
<pre class="tool-call__code tool-call__code--result">{showResult ||
|
|
143
|
+
<pre class="tool-call__code tool-call__code--result">{showResult ||
|
|
144
|
+
!truncatedResult?.truncated
|
|
145
|
+
? formattedResult
|
|
146
|
+
: truncatedResult?.text + '...'}</pre>
|
|
151
147
|
</div>
|
|
152
148
|
{/if}
|
|
153
149
|
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
|
-
variant?:
|
|
5
|
+
variant?:
|
|
6
|
+
| 'default'
|
|
7
|
+
| 'primary'
|
|
8
|
+
| 'secondary'
|
|
9
|
+
| 'success'
|
|
10
|
+
| 'warning'
|
|
11
|
+
| 'error'
|
|
12
|
+
| 'danger'
|
|
13
|
+
| 'info';
|
|
6
14
|
size?: 'sm' | 'md';
|
|
7
15
|
dot?: boolean;
|
|
8
16
|
class?: string;
|
|
@@ -1,79 +1,84 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
/**
|
|
3
|
+
* ConnectionStatus Component
|
|
4
|
+
*
|
|
5
|
+
* Displays the current connection status with the VFS server.
|
|
6
|
+
* Shows connected, disconnected, and reconnecting states.
|
|
7
|
+
*/
|
|
8
8
|
|
|
9
|
-
export type ConnectionState =
|
|
9
|
+
export type ConnectionState =
|
|
10
|
+
| 'connected'
|
|
11
|
+
| 'disconnected'
|
|
12
|
+
| 'connecting'
|
|
13
|
+
| 'reconnecting'
|
|
14
|
+
| 'error';
|
|
10
15
|
|
|
11
|
-
interface Props {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
16
|
+
interface Props {
|
|
17
|
+
state: ConnectionState;
|
|
18
|
+
reconnectAttempts?: number;
|
|
19
|
+
maxReconnectAttempts?: number;
|
|
20
|
+
lastEventTime?: string | null;
|
|
21
|
+
errorMessage?: string | null;
|
|
22
|
+
onRetry?: () => void;
|
|
23
|
+
compact?: boolean;
|
|
24
|
+
showLabel?: boolean;
|
|
25
|
+
}
|
|
21
26
|
|
|
22
|
-
let {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}: Props = $props();
|
|
27
|
+
let {
|
|
28
|
+
state,
|
|
29
|
+
reconnectAttempts = 0,
|
|
30
|
+
maxReconnectAttempts = 10,
|
|
31
|
+
lastEventTime = null,
|
|
32
|
+
errorMessage = null,
|
|
33
|
+
onRetry,
|
|
34
|
+
compact = false,
|
|
35
|
+
showLabel = true
|
|
36
|
+
}: Props = $props();
|
|
32
37
|
|
|
33
|
-
// Computed display values
|
|
34
|
-
let statusLabel = $derived.by(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
38
|
+
// Computed display values
|
|
39
|
+
let statusLabel = $derived.by(() => {
|
|
40
|
+
switch (state) {
|
|
41
|
+
case 'connected':
|
|
42
|
+
return 'Connected';
|
|
43
|
+
case 'disconnected':
|
|
44
|
+
return 'Disconnected';
|
|
45
|
+
case 'connecting':
|
|
46
|
+
return 'Connecting...';
|
|
47
|
+
case 'reconnecting':
|
|
48
|
+
return `Reconnecting (${reconnectAttempts}/${maxReconnectAttempts})`;
|
|
49
|
+
case 'error':
|
|
50
|
+
return 'Connection Error';
|
|
51
|
+
default:
|
|
52
|
+
return 'Unknown';
|
|
53
|
+
}
|
|
54
|
+
});
|
|
50
55
|
|
|
51
|
-
let statusIcon = $derived.by(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
56
|
+
let statusIcon = $derived.by(() => {
|
|
57
|
+
switch (state) {
|
|
58
|
+
case 'connected':
|
|
59
|
+
return 'check';
|
|
60
|
+
case 'disconnected':
|
|
61
|
+
case 'error':
|
|
62
|
+
return 'x';
|
|
63
|
+
case 'connecting':
|
|
64
|
+
case 'reconnecting':
|
|
65
|
+
return 'loader';
|
|
66
|
+
default:
|
|
67
|
+
return 'circle';
|
|
68
|
+
}
|
|
69
|
+
});
|
|
65
70
|
|
|
66
|
-
let lastSyncDisplay = $derived.by(() => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
let lastSyncDisplay = $derived.by(() => {
|
|
72
|
+
if (!lastEventTime) return null;
|
|
73
|
+
const date = new Date(lastEventTime);
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const diff = now.getTime() - date.getTime();
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
+
if (diff < 60000) return 'Just now';
|
|
78
|
+
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
|
|
79
|
+
if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
|
|
80
|
+
return date.toLocaleDateString();
|
|
81
|
+
});
|
|
77
82
|
</script>
|
|
78
83
|
|
|
79
84
|
<div
|
|
@@ -1,60 +1,62 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import type { Snippet } from 'svelte';
|
|
10
|
-
import type { VFSError, RecoveryOption } from '../../services/error-handling';
|
|
11
|
-
import { isVFSError } from '../../services/error-handling';
|
|
12
|
-
|
|
13
|
-
interface Props {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let { children, fallback, onError, onReset, showDetails = false }: Props = $props();
|
|
22
|
-
|
|
23
|
-
let error = $state<Error | null>(null);
|
|
24
|
-
let showTechnicalDetails = $state(false);
|
|
25
|
-
|
|
26
|
-
// Derived error info
|
|
27
|
-
let vfsError = $derived(error && isVFSError(error) ? (error as VFSError) : null);
|
|
28
|
-
let errorTitle = $derived(vfsError?.code ?? 'Error');
|
|
29
|
-
let errorMessage = $derived(vfsError?.userMessage ?? error?.message ?? 'An unexpected error occurred');
|
|
30
|
-
let recoveryOptions = $derived(vfsError?.recoveryOptions ?? []);
|
|
31
|
-
|
|
32
|
-
function handleError(e: Error) {
|
|
33
|
-
error = e;
|
|
34
|
-
onError?.(e);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function reset() {
|
|
38
|
-
error = null;
|
|
39
|
-
showTechnicalDetails = false;
|
|
40
|
-
onReset?.();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function handleRecovery(option: RecoveryOption) {
|
|
44
|
-
if (option.action === 'retry' || option.action === 'refresh') {
|
|
45
|
-
reset();
|
|
2
|
+
/**
|
|
3
|
+
* ErrorBoundary Component
|
|
4
|
+
*
|
|
5
|
+
* Catches errors in child components and displays a fallback UI.
|
|
6
|
+
* Provides retry and recovery options.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Snippet } from 'svelte';
|
|
10
|
+
import type { VFSError, RecoveryOption } from '../../services/error-handling';
|
|
11
|
+
import { isVFSError } from '../../services/error-handling';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
children: Snippet;
|
|
15
|
+
fallback?: Snippet<[{ error: Error; reset: () => void }]>;
|
|
16
|
+
onError?: (error: Error) => void;
|
|
17
|
+
onReset?: () => void;
|
|
18
|
+
showDetails?: boolean;
|
|
46
19
|
}
|
|
47
|
-
// Other actions would be handled by parent via onError
|
|
48
|
-
}
|
|
49
20
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
21
|
+
let { children, fallback, onError, onReset, showDetails = false }: Props = $props();
|
|
22
|
+
|
|
23
|
+
let error = $state<Error | null>(null);
|
|
24
|
+
let showTechnicalDetails = $state(false);
|
|
25
|
+
|
|
26
|
+
// Derived error info
|
|
27
|
+
let vfsError = $derived(error && isVFSError(error) ? (error as VFSError) : null);
|
|
28
|
+
let errorTitle = $derived(vfsError?.code ?? 'Error');
|
|
29
|
+
let errorMessage = $derived(
|
|
30
|
+
vfsError?.userMessage ?? error?.message ?? 'An unexpected error occurred'
|
|
31
|
+
);
|
|
32
|
+
let recoveryOptions = $derived(vfsError?.recoveryOptions ?? []);
|
|
33
|
+
|
|
34
|
+
function handleError(e: Error) {
|
|
35
|
+
error = e;
|
|
36
|
+
onError?.(e);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function reset() {
|
|
40
|
+
error = null;
|
|
41
|
+
showTechnicalDetails = false;
|
|
42
|
+
onReset?.();
|
|
43
|
+
}
|
|
54
44
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
function handleRecovery(option: RecoveryOption) {
|
|
46
|
+
if (option.action === 'retry' || option.action === 'refresh') {
|
|
47
|
+
reset();
|
|
48
|
+
}
|
|
49
|
+
// Other actions would be handled by parent via onError
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Expose error handler for programmatic use
|
|
53
|
+
export function setError(e: Error) {
|
|
54
|
+
handleError(e);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function clearError() {
|
|
58
|
+
reset();
|
|
59
|
+
}
|
|
58
60
|
</script>
|
|
59
61
|
|
|
60
62
|
{#if error}
|
|
@@ -99,9 +101,7 @@ export function clearError() {
|
|
|
99
101
|
</div>
|
|
100
102
|
{:else}
|
|
101
103
|
<div class="recovery-options">
|
|
102
|
-
<button class="recovery-btn recommended" onclick={reset}>
|
|
103
|
-
Try Again
|
|
104
|
-
</button>
|
|
104
|
+
<button class="recovery-btn recommended" onclick={reset}> Try Again </button>
|
|
105
105
|
</div>
|
|
106
106
|
{/if}
|
|
107
107
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
* ErrorBoundary Component
|
|
3
|
+
*
|
|
4
|
+
* Catches errors in child components and displays a fallback UI.
|
|
5
|
+
* Provides retry and recovery options.
|
|
6
|
+
*/
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
interface Props {
|
|
9
9
|
children: Snippet;
|
|
@@ -22,8 +22,10 @@
|
|
|
22
22
|
check: 'M20 6L9 17l-5-5',
|
|
23
23
|
x: 'M18 6L6 18M6 6l12 12',
|
|
24
24
|
search: 'M21 21l-6-6m2-5a7 7 0 1 1-14 0 7 7 0 0 1 14 0z',
|
|
25
|
-
settings:
|
|
26
|
-
|
|
25
|
+
settings:
|
|
26
|
+
'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z',
|
|
27
|
+
refresh:
|
|
28
|
+
'M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15',
|
|
27
29
|
save: 'M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2zM17 21v-8H7v8M7 3v5h8',
|
|
28
30
|
copy: 'M20 9h-9a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2zM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1',
|
|
29
31
|
trash: 'M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2',
|
|
@@ -43,8 +45,10 @@
|
|
|
43
45
|
terminal: 'M4 17l6-6-6-6M12 19h8',
|
|
44
46
|
code: 'M16 18l6-6-6-6M8 6l-6 6 6 6',
|
|
45
47
|
split: 'M12 3v18M3 3h18v18H3z',
|
|
46
|
-
maximize:
|
|
47
|
-
|
|
48
|
+
maximize:
|
|
49
|
+
'M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3',
|
|
50
|
+
minimize:
|
|
51
|
+
'M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3',
|
|
48
52
|
|
|
49
53
|
// AI
|
|
50
54
|
sparkles: 'M12 2l2.4 7.2L22 12l-7.6 2.8L12 22l-2.4-7.2L2 12l7.6-2.8L12 2z',
|
|
@@ -54,19 +58,25 @@
|
|
|
54
58
|
|
|
55
59
|
// Status
|
|
56
60
|
info: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 16v-4M12 8h.01',
|
|
57
|
-
warning:
|
|
58
|
-
|
|
61
|
+
warning:
|
|
62
|
+
'M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z',
|
|
63
|
+
error:
|
|
64
|
+
'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM15 9l-6 6M9 9l6 6',
|
|
59
65
|
success: 'M22 11.08V12a10 10 0 1 1-5.93-9.14M22 4L12 14.01l-3-3',
|
|
60
|
-
loading:
|
|
66
|
+
loading:
|
|
67
|
+
'M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83',
|
|
61
68
|
|
|
62
69
|
// Users
|
|
63
70
|
user: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
|
|
64
|
-
users:
|
|
71
|
+
users:
|
|
72
|
+
'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75M9 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
|
|
65
73
|
|
|
66
74
|
// Git
|
|
67
|
-
'git-branch':
|
|
75
|
+
'git-branch':
|
|
76
|
+
'M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 9a9 9 0 0 1-9 9',
|
|
68
77
|
'git-commit': 'M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM1.05 12H8M16 12h6.95',
|
|
69
|
-
'git-merge':
|
|
78
|
+
'git-merge':
|
|
79
|
+
'M18 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21V9a9 9 0 0 0 9 9M6 21h12',
|
|
70
80
|
|
|
71
81
|
// Misc
|
|
72
82
|
play: 'M5 3l14 9-14 9V3z',
|
|
@@ -76,7 +86,8 @@
|
|
|
76
86
|
external: 'M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3',
|
|
77
87
|
clock: 'M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 6v6l4 2',
|
|
78
88
|
zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z',
|
|
79
|
-
plugin:
|
|
89
|
+
plugin:
|
|
90
|
+
'M12 2v6M12 18v4M4.93 4.93l4.24 4.24M14.83 14.83l4.24 4.24M2 12h6M18 12h4M4.93 19.07l4.24-4.24M14.83 9.17l4.24-4.24'
|
|
80
91
|
};
|
|
81
92
|
|
|
82
93
|
const path = $derived(icons[name] ?? icons.file);
|
|
@@ -9,13 +9,7 @@
|
|
|
9
9
|
children: Snippet;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
let {
|
|
13
|
-
content,
|
|
14
|
-
position = 'top',
|
|
15
|
-
delay = 300,
|
|
16
|
-
class: className = '',
|
|
17
|
-
children
|
|
18
|
-
}: Props = $props();
|
|
12
|
+
let { content, position = 'top', delay = 300, class: className = '', children }: Props = $props();
|
|
19
13
|
|
|
20
14
|
let visible = $state(false);
|
|
21
15
|
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
|
|
45
45
|
// Filter to active agents with cursors
|
|
46
46
|
let visibleAgents = $derived(
|
|
47
|
-
enabled
|
|
47
|
+
enabled
|
|
48
|
+
? agents.filter((a) => a.isActive && (a.cursor?.visible || a.focusRegions.length > 0))
|
|
49
|
+
: []
|
|
48
50
|
);
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -218,7 +220,8 @@
|
|
|
218
220
|
rgba(var(--ai-color-rgb, 59, 130, 246), calc(var(--ai-intensity) * 0.05)) 50%,
|
|
219
221
|
transparent 100%
|
|
220
222
|
);
|
|
221
|
-
box-shadow: inset 0 0 20px
|
|
223
|
+
box-shadow: inset 0 0 20px
|
|
224
|
+
rgba(var(--ai-color-rgb, 59, 130, 246), calc(var(--ai-intensity) * 0.1));
|
|
222
225
|
}
|
|
223
226
|
|
|
224
227
|
/* Thinking - pulsing purple glow */
|
|
@@ -255,7 +258,8 @@
|
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
@keyframes region-think-pulse {
|
|
258
|
-
0%,
|
|
261
|
+
0%,
|
|
262
|
+
100% {
|
|
259
263
|
opacity: 0.7;
|
|
260
264
|
}
|
|
261
265
|
50% {
|
|
@@ -333,7 +337,8 @@
|
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
@keyframes cursor-idle-pulse {
|
|
336
|
-
0%,
|
|
340
|
+
0%,
|
|
341
|
+
100% {
|
|
337
342
|
opacity: 0.8;
|
|
338
343
|
}
|
|
339
344
|
50% {
|
|
@@ -353,7 +358,8 @@
|
|
|
353
358
|
}
|
|
354
359
|
|
|
355
360
|
@keyframes cursor-type-flash {
|
|
356
|
-
0%,
|
|
361
|
+
0%,
|
|
362
|
+
100% {
|
|
357
363
|
opacity: 1;
|
|
358
364
|
}
|
|
359
365
|
50% {
|
|
@@ -362,7 +368,8 @@
|
|
|
362
368
|
}
|
|
363
369
|
|
|
364
370
|
@keyframes cursor-think-glow {
|
|
365
|
-
0%,
|
|
371
|
+
0%,
|
|
372
|
+
100% {
|
|
366
373
|
filter: brightness(1);
|
|
367
374
|
}
|
|
368
375
|
50% {
|
|
@@ -375,7 +382,8 @@
|
|
|
375
382
|
}
|
|
376
383
|
|
|
377
384
|
@keyframes glow-expand {
|
|
378
|
-
0%,
|
|
385
|
+
0%,
|
|
386
|
+
100% {
|
|
379
387
|
transform: scale(1);
|
|
380
388
|
opacity: 0.4;
|
|
381
389
|
}
|