@nocturnium/svelte-ide 1.0.1 → 1.0.3
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 +5 -3
- package/dist/components/ai/AIMessageContent.svelte +24 -14
- package/dist/components/ai/AIPanel.svelte +22 -0
- package/dist/components/editor/CollaborativeEditor.svelte +68 -5
- package/dist/components/editor/CollaborativeEditor.svelte.d.ts +14 -0
- package/dist/components/editor/CustomEditor.svelte +52 -33
- package/dist/components/editor/CustomEditor.svelte.d.ts +2 -2
- package/dist/components/editor/Editor.svelte +17 -0
- package/dist/components/editor/Editor.svelte.d.ts +9 -0
- package/dist/components/editor/EditorPane.svelte +18 -1
- package/dist/components/editor/EditorPane.svelte.d.ts +5 -0
- package/dist/components/editor/EditorSelections.svelte +27 -11
- package/dist/components/editor/EditorSelections.svelte.d.ts +1 -0
- package/dist/components/editor/core/folding.d.ts +11 -0
- package/dist/components/editor/core/folding.js +41 -0
- package/dist/components/editor/core/index.d.ts +0 -5
- package/dist/components/editor/core/index.js +4 -5
- package/dist/components/editor/core/state.d.ts +5 -0
- package/dist/components/editor/core/state.js +131 -12
- package/dist/components/editor/editor-find.d.ts +1 -0
- package/dist/components/editor/editor-find.js +6 -5
- package/dist/components/editor/editor-input.d.ts +1 -0
- package/dist/components/editor/editor-input.js +4 -1
- package/dist/components/editor/editor-scroll.d.ts +1 -0
- package/dist/components/editor/editor-scroll.js +2 -1
- package/dist/components/editor/index.d.ts +19 -3
- package/dist/components/editor/index.js +18 -4
- package/dist/components/editor/tokenizer/base.d.ts +1 -25
- package/dist/components/editor/tokenizer/base.js +0 -172
- package/dist/components/editor/tokenizer/index.d.ts +4 -0
- package/dist/components/editor/tokenizer/index.js +1 -1
- package/dist/components/editor/tokenizer/languages/html.d.ts +3 -2
- package/dist/components/editor/tokenizer/languages/html.js +64 -6
- package/dist/components/editor/tokenizer/languages/javascript.d.ts +13 -5
- package/dist/components/editor/tokenizer/languages/javascript.js +69 -57
- package/dist/components/editor/tokenizer/languages/svelte.d.ts +1 -1
- package/dist/components/editor/tokenizer/languages/svelte.js +6 -1
- package/dist/components/editor/tokenizer/types.d.ts +0 -28
- package/dist/crdt/awareness.d.ts +8 -2
- package/dist/crdt/awareness.js +11 -4
- package/dist/crdt/document.d.ts +10 -1
- package/dist/crdt/document.js +15 -7
- package/dist/crdt/index.d.ts +8 -2
- package/dist/crdt/index.js +5 -2
- package/dist/crdt/undo.d.ts +2 -7
- package/dist/crdt/undo.js +1 -8
- package/dist/index.d.ts +7 -9
- package/dist/index.js +7 -9
- package/dist/services/error-handling.d.ts +2 -11
- package/dist/services/error-handling.js +15 -4
- package/dist/services/lsp-client.d.ts +3 -0
- package/dist/services/lsp-client.js +55 -10
- package/dist/services/optimistic.d.ts +8 -5
- package/dist/services/optimistic.js +36 -10
- package/dist/services/vfs-client.js +11 -3
- package/dist/stores/agents.svelte.js +3 -2
- package/dist/stores/ai-persistence.svelte.js +7 -2
- package/dist/stores/ai.svelte.js +2 -1
- package/dist/stores/collaboration.svelte.d.ts +1 -1
- package/dist/stores/collaboration.svelte.js +3 -2
- package/dist/stores/editor.svelte.js +29 -5
- package/dist/stores/layout.svelte.js +3 -0
- package/dist/stores/plugin.svelte.js +9 -3
- package/dist/stores/vfs.svelte.js +26 -9
- package/dist/styles/theme.css +43 -0
- package/dist/types/vfs.d.ts +15 -1
- package/dist/types/vfs.js +9 -0
- package/dist/utils/language.d.ts +4 -3
- package/dist/utils/language.js +8 -18
- package/package.json +1 -1
- package/dist/components/editor/MinimalEditor.svelte +0 -75
- package/dist/components/editor/MinimalEditor.svelte.d.ts +0 -6
- package/dist/components/editor/MinimalEditor2.svelte +0 -84
- package/dist/components/editor/MinimalEditor2.svelte.d.ts +0 -6
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
8
9
|
const state = $state({
|
|
9
|
-
agents: new
|
|
10
|
+
agents: new SvelteMap(),
|
|
10
11
|
events: [],
|
|
11
12
|
maxEvents: 200,
|
|
12
13
|
activities: [],
|
|
13
14
|
maxActivities: 500,
|
|
14
|
-
cursors: new
|
|
15
|
+
cursors: new SvelteMap(),
|
|
15
16
|
selectedAgentId: null,
|
|
16
17
|
filter: 'all',
|
|
17
18
|
connected: false,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles saving and loading AI conversations to IndexedDB/localStorage
|
|
5
5
|
*/
|
|
6
|
+
import { browser } from '$app/environment';
|
|
6
7
|
const DEFAULT_CONFIG = {
|
|
7
8
|
dbName: 'svelte-ide-ai',
|
|
8
9
|
storeName: 'conversations',
|
|
@@ -31,7 +32,7 @@ export async function initPersistence(options) {
|
|
|
31
32
|
config = { ...DEFAULT_CONFIG, ...options };
|
|
32
33
|
try {
|
|
33
34
|
// Check if IndexedDB is available
|
|
34
|
-
if (!('indexedDB' in globalThis)) {
|
|
35
|
+
if (!browser || !('indexedDB' in globalThis)) {
|
|
35
36
|
console.warn('IndexedDB not available, using localStorage fallback');
|
|
36
37
|
initialized = true;
|
|
37
38
|
return true;
|
|
@@ -52,7 +53,7 @@ export async function initPersistence(options) {
|
|
|
52
53
|
*/
|
|
53
54
|
function openDatabase() {
|
|
54
55
|
return new Promise((resolve, reject) => {
|
|
55
|
-
const request = indexedDB.open(config.dbName, config.version);
|
|
56
|
+
const request = globalThis.indexedDB.open(config.dbName, config.version);
|
|
56
57
|
request.onerror = () => reject(request.error);
|
|
57
58
|
request.onsuccess = () => resolve(request.result);
|
|
58
59
|
request.onupgradeneeded = (event) => {
|
|
@@ -318,6 +319,8 @@ export async function pruneOldConversations() {
|
|
|
318
319
|
}
|
|
319
320
|
// localStorage helpers
|
|
320
321
|
function getLocalStorageConversations() {
|
|
322
|
+
if (typeof localStorage === 'undefined')
|
|
323
|
+
return [];
|
|
321
324
|
try {
|
|
322
325
|
const data = localStorage.getItem('ai-conversations');
|
|
323
326
|
return data ? JSON.parse(data) : [];
|
|
@@ -327,6 +330,8 @@ function getLocalStorageConversations() {
|
|
|
327
330
|
}
|
|
328
331
|
}
|
|
329
332
|
function setLocalStorageConversations(conversations) {
|
|
333
|
+
if (typeof localStorage === 'undefined')
|
|
334
|
+
return;
|
|
330
335
|
try {
|
|
331
336
|
localStorage.setItem('ai-conversations', JSON.stringify(conversations));
|
|
332
337
|
}
|
package/dist/stores/ai.svelte.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
8
9
|
// Default configuration
|
|
9
10
|
const defaultConfig = {
|
|
10
11
|
endpoint: '/api/chat',
|
|
@@ -19,7 +20,7 @@ const defaultConfig = {
|
|
|
19
20
|
const state = $state({
|
|
20
21
|
conversations: [],
|
|
21
22
|
activeConversationId: null,
|
|
22
|
-
tools: new
|
|
23
|
+
tools: new SvelteMap(),
|
|
23
24
|
config: { ...defaultConfig },
|
|
24
25
|
editSessions: [],
|
|
25
26
|
suggestions: [],
|
|
@@ -24,7 +24,7 @@ export declare const config: {
|
|
|
24
24
|
readonly current: CollaborationConfig | null;
|
|
25
25
|
};
|
|
26
26
|
export declare const status: {
|
|
27
|
-
readonly current: "error" | "
|
|
27
|
+
readonly current: "error" | "disconnected" | "connecting" | "connected";
|
|
28
28
|
};
|
|
29
29
|
export declare const error: {
|
|
30
30
|
readonly current: string | null;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
8
9
|
// Reactive state
|
|
9
10
|
const state = $state({
|
|
10
11
|
config: null,
|
|
@@ -12,8 +13,8 @@ const state = $state({
|
|
|
12
13
|
error: null,
|
|
13
14
|
synced: false,
|
|
14
15
|
users: [],
|
|
15
|
-
cursors: new
|
|
16
|
-
awareness: new
|
|
16
|
+
cursors: new SvelteMap(),
|
|
17
|
+
awareness: new SvelteMap(),
|
|
17
18
|
aiSessions: [],
|
|
18
19
|
pendingChanges: [],
|
|
19
20
|
snapshots: [],
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
8
9
|
import { DEFAULT_EDITOR_PREFERENCES } from '../types';
|
|
9
10
|
import { detectLanguage } from '../utils/language';
|
|
10
11
|
// Reactive state using $state rune
|
|
@@ -17,6 +18,12 @@ const state = $state({
|
|
|
17
18
|
loading: false,
|
|
18
19
|
error: null
|
|
19
20
|
});
|
|
21
|
+
// Private saved-content baseline used to compute dirty state without changing
|
|
22
|
+
// the exported EditorTab shape.
|
|
23
|
+
const savedContents = new SvelteMap();
|
|
24
|
+
function isTabDirty(tab) {
|
|
25
|
+
return tab.content !== (savedContents.get(tab.id) ?? tab.content);
|
|
26
|
+
}
|
|
20
27
|
// Getter functions for derived values (Svelte 5 module-safe)
|
|
21
28
|
export function getTabs() {
|
|
22
29
|
return state.tabs;
|
|
@@ -122,6 +129,7 @@ export function openFile(path, content, options) {
|
|
|
122
129
|
isDirty: false,
|
|
123
130
|
cursorPosition: { line: 1, column: 1 }
|
|
124
131
|
};
|
|
132
|
+
savedContents.set(id, content);
|
|
125
133
|
state.tabs = [...state.tabs, tab];
|
|
126
134
|
if (options?.focus !== false) {
|
|
127
135
|
state.activeTabId = id;
|
|
@@ -138,10 +146,11 @@ export function closeTab(tabId) {
|
|
|
138
146
|
if (index === -1)
|
|
139
147
|
return false;
|
|
140
148
|
const tab = state.tabs[index];
|
|
141
|
-
if (tab
|
|
149
|
+
if (isTabDirty(tab)) {
|
|
142
150
|
// Could prompt for save here - returning false indicates unsaved changes
|
|
143
151
|
return false;
|
|
144
152
|
}
|
|
153
|
+
savedContents.delete(tabId);
|
|
145
154
|
state.tabs = state.tabs.filter((t) => t.id !== tabId);
|
|
146
155
|
// Update active tab if we closed the active one
|
|
147
156
|
if (state.activeTabId === tabId) {
|
|
@@ -157,6 +166,7 @@ export function forceCloseTab(tabId) {
|
|
|
157
166
|
const index = state.tabs.findIndex((t) => t.id === tabId);
|
|
158
167
|
if (index === -1)
|
|
159
168
|
return;
|
|
169
|
+
savedContents.delete(tabId);
|
|
160
170
|
state.tabs = state.tabs.filter((t) => t.id !== tabId);
|
|
161
171
|
if (state.activeTabId === tabId) {
|
|
162
172
|
const newIndex = Math.min(index, state.tabs.length - 1);
|
|
@@ -167,9 +177,10 @@ export function forceCloseTab(tabId) {
|
|
|
167
177
|
* Close all tabs
|
|
168
178
|
*/
|
|
169
179
|
export function closeAllTabs(force = false) {
|
|
170
|
-
if (!force && state.tabs.some(
|
|
180
|
+
if (!force && state.tabs.some(isTabDirty)) {
|
|
171
181
|
return false;
|
|
172
182
|
}
|
|
183
|
+
savedContents.clear();
|
|
173
184
|
state.tabs = [];
|
|
174
185
|
state.activeTabId = null;
|
|
175
186
|
return true;
|
|
@@ -178,9 +189,14 @@ export function closeAllTabs(force = false) {
|
|
|
178
189
|
* Close other tabs (keep the specified one)
|
|
179
190
|
*/
|
|
180
191
|
export function closeOtherTabs(keepTabId, force = false) {
|
|
181
|
-
if (!force && state.tabs.some((t) => t.id !== keepTabId && t
|
|
192
|
+
if (!force && state.tabs.some((t) => t.id !== keepTabId && isTabDirty(t))) {
|
|
182
193
|
return false;
|
|
183
194
|
}
|
|
195
|
+
for (const tab of state.tabs) {
|
|
196
|
+
if (tab.id !== keepTabId) {
|
|
197
|
+
savedContents.delete(tab.id);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
184
200
|
state.tabs = state.tabs.filter((t) => t.id === keepTabId);
|
|
185
201
|
state.activeTabId = keepTabId;
|
|
186
202
|
return true;
|
|
@@ -197,13 +213,21 @@ export function setActiveTab(tabId) {
|
|
|
197
213
|
* Update content for a tab
|
|
198
214
|
*/
|
|
199
215
|
export function updateContent(tabId, content) {
|
|
200
|
-
state.tabs = state.tabs.map((t) => t.id === tabId
|
|
216
|
+
state.tabs = state.tabs.map((t) => t.id === tabId
|
|
217
|
+
? { ...t, content, isDirty: content !== (savedContents.get(tabId) ?? t.content) }
|
|
218
|
+
: t);
|
|
201
219
|
}
|
|
202
220
|
/**
|
|
203
221
|
* Mark a tab as saved
|
|
204
222
|
*/
|
|
205
223
|
export function markSaved(tabId, newContent) {
|
|
206
|
-
state.tabs = state.tabs.map((t) => t.id === tabId
|
|
224
|
+
state.tabs = state.tabs.map((t) => t.id === tabId
|
|
225
|
+
? (() => {
|
|
226
|
+
const content = newContent ?? t.content;
|
|
227
|
+
savedContents.set(tabId, content);
|
|
228
|
+
return { ...t, isDirty: false, content };
|
|
229
|
+
})()
|
|
230
|
+
: t);
|
|
207
231
|
}
|
|
208
232
|
/**
|
|
209
233
|
* Update cursor position for a tab
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
|
+
import { browser } from '$app/environment';
|
|
8
9
|
// Default sizes
|
|
9
10
|
const SIDEBAR_DEFAULT_WIDTH = 260;
|
|
10
11
|
const SIDEBAR_MIN_WIDTH = 180;
|
|
@@ -219,6 +220,8 @@ export function toggleStatusBar() {
|
|
|
219
220
|
*/
|
|
220
221
|
export function toggleFullScreen() {
|
|
221
222
|
state.isFullScreen = !state.isFullScreen;
|
|
223
|
+
if (!browser || typeof document === 'undefined')
|
|
224
|
+
return;
|
|
222
225
|
if (state.isFullScreen) {
|
|
223
226
|
document.documentElement.requestFullscreen?.();
|
|
224
227
|
}
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
* Note: Svelte 5 modules cannot directly export $derived values.
|
|
11
11
|
* We use getter functions to expose reactive derived state.
|
|
12
12
|
*/
|
|
13
|
+
import { browser } from '$app/environment';
|
|
14
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
13
15
|
// Reactive state
|
|
14
16
|
const state = $state({
|
|
15
17
|
proposals: [],
|
|
16
|
-
instances: new
|
|
17
|
-
commands: new
|
|
18
|
-
panels: new
|
|
18
|
+
instances: new SvelteMap(),
|
|
19
|
+
commands: new SvelteMap(),
|
|
20
|
+
panels: new SvelteMap(),
|
|
19
21
|
eventSource: null,
|
|
20
22
|
connected: false,
|
|
21
23
|
loadingProposals: false,
|
|
@@ -151,6 +153,10 @@ export function connect(endpoint = '/api/plugins/stream') {
|
|
|
151
153
|
if (state.eventSource) {
|
|
152
154
|
state.eventSource.close();
|
|
153
155
|
}
|
|
156
|
+
if (!browser || typeof EventSource === 'undefined') {
|
|
157
|
+
state.connected = false;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
154
160
|
const eventSource = new EventSource(endpoint);
|
|
155
161
|
eventSource.onopen = () => {
|
|
156
162
|
state.connected = true;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* We use getter functions to expose reactive derived state.
|
|
7
7
|
*/
|
|
8
8
|
import * as vfsClient from '../services/vfs-client';
|
|
9
|
-
import {
|
|
9
|
+
import { browser } from '$app/environment';
|
|
10
|
+
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
10
11
|
import { parseError, isConflictError, logError } from '../services/error-handling';
|
|
11
12
|
import { optimisticUpdate } from '../services/optimistic';
|
|
12
13
|
// Reactive state using $state rune
|
|
@@ -14,12 +15,12 @@ const state = $state({
|
|
|
14
15
|
workspace: null,
|
|
15
16
|
workspaceLoading: false,
|
|
16
17
|
files: [],
|
|
17
|
-
fileMap: new
|
|
18
|
-
dirtyFiles: new
|
|
19
|
-
locks: new
|
|
20
|
-
lockStatuses: new
|
|
21
|
-
pendingLocks: new
|
|
22
|
-
activeTransactions: new
|
|
18
|
+
fileMap: new SvelteMap(),
|
|
19
|
+
dirtyFiles: new SvelteSet(),
|
|
20
|
+
locks: new SvelteMap(),
|
|
21
|
+
lockStatuses: new SvelteMap(),
|
|
22
|
+
pendingLocks: new SvelteSet(),
|
|
23
|
+
activeTransactions: new SvelteMap(),
|
|
23
24
|
transactionHistory: [],
|
|
24
25
|
eventSource: null,
|
|
25
26
|
connected: false,
|
|
@@ -30,7 +31,7 @@ const state = $state({
|
|
|
30
31
|
errorCode: null,
|
|
31
32
|
structuredError: null,
|
|
32
33
|
version: 0,
|
|
33
|
-
pendingRetries: new
|
|
34
|
+
pendingRetries: new SvelteMap(),
|
|
34
35
|
conflictQueue: []
|
|
35
36
|
});
|
|
36
37
|
// Event handlers for SSE
|
|
@@ -39,6 +40,7 @@ const eventHandlers = new Map();
|
|
|
39
40
|
// Lock TTL refresh intervals
|
|
40
41
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- non-reactive internal timer registry, not UI state
|
|
41
42
|
const lockRefreshIntervals = new Map(); // path -> intervalId
|
|
43
|
+
let reconnectTimer = null;
|
|
42
44
|
// Current user ID (set during initialization)
|
|
43
45
|
let currentUserId = null;
|
|
44
46
|
// ============================================================================
|
|
@@ -496,6 +498,8 @@ export function setLocks(locks) {
|
|
|
496
498
|
}
|
|
497
499
|
}
|
|
498
500
|
function startLockRefresh(lock) {
|
|
501
|
+
if (!browser || typeof window === 'undefined')
|
|
502
|
+
return;
|
|
499
503
|
// Refresh at 80% of TTL to ensure we don't lose the lock
|
|
500
504
|
const refreshInterval = lock.ttl * 0.8;
|
|
501
505
|
const intervalId = window.setInterval(async () => {
|
|
@@ -581,9 +585,17 @@ export function completeTransaction(transactionId, status) {
|
|
|
581
585
|
const BASE_RECONNECT_DELAY = 1000;
|
|
582
586
|
const MAX_RECONNECT_ATTEMPTS = 10;
|
|
583
587
|
export function connect(workspaceId) {
|
|
588
|
+
if (reconnectTimer) {
|
|
589
|
+
clearTimeout(reconnectTimer);
|
|
590
|
+
reconnectTimer = null;
|
|
591
|
+
}
|
|
584
592
|
if (state.eventSource) {
|
|
585
593
|
state.eventSource.close();
|
|
586
594
|
}
|
|
595
|
+
if (!browser || typeof EventSource === 'undefined') {
|
|
596
|
+
state.connected = false;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
587
599
|
const endpoint = `/api/vfs/workspaces/${workspaceId}/stream`;
|
|
588
600
|
const eventSource = new EventSource(endpoint);
|
|
589
601
|
eventSource.onopen = () => {
|
|
@@ -597,7 +609,8 @@ export function connect(workspaceId) {
|
|
|
597
609
|
if (state.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
598
610
|
const delay = BASE_RECONNECT_DELAY * Math.pow(2, state.reconnectAttempts);
|
|
599
611
|
state.reconnectAttempts++;
|
|
600
|
-
setTimeout(() => {
|
|
612
|
+
reconnectTimer = setTimeout(() => {
|
|
613
|
+
reconnectTimer = null;
|
|
601
614
|
if (!state.connected) {
|
|
602
615
|
connect(workspaceId);
|
|
603
616
|
}
|
|
@@ -619,6 +632,10 @@ export function connect(workspaceId) {
|
|
|
619
632
|
state.eventSource = eventSource;
|
|
620
633
|
}
|
|
621
634
|
export function disconnect() {
|
|
635
|
+
if (reconnectTimer) {
|
|
636
|
+
clearTimeout(reconnectTimer);
|
|
637
|
+
reconnectTimer = null;
|
|
638
|
+
}
|
|
622
639
|
if (state.eventSource) {
|
|
623
640
|
state.eventSource.close();
|
|
624
641
|
state.eventSource = null;
|
package/dist/styles/theme.css
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
--ide-bg-elevated: color-mix(in srgb, var(--ide-bg-secondary) 90%, white 10%);
|
|
44
44
|
--ide-bg-hover: color-mix(in srgb, var(--ide-bg-tertiary) 50%, transparent);
|
|
45
45
|
--ide-bg-active: var(--ide-bg-tertiary);
|
|
46
|
+
--ide-bg-overlay: color-mix(in srgb, black 75%, transparent);
|
|
46
47
|
--ide-bg-selection-secondary: color-mix(
|
|
47
48
|
in srgb,
|
|
48
49
|
var(--color-nocturnium-aurora-purple) 22%,
|
|
@@ -62,9 +63,11 @@
|
|
|
62
63
|
--ide-interactive-active: var(--color-nocturnium-ember);
|
|
63
64
|
--ide-interactive-focus: var(--color-nocturnium-aurora-blue);
|
|
64
65
|
--ide-interactive-muted: color-mix(in srgb, var(--ide-interactive) 70%, transparent);
|
|
66
|
+
--ide-interactive-rgb: 74, 141, 183;
|
|
65
67
|
|
|
66
68
|
/* IDE Accent Colors */
|
|
67
69
|
--ide-accent: var(--color-nocturnium-wave);
|
|
70
|
+
--ide-accent-hover: var(--color-nocturnium-flame);
|
|
68
71
|
--ide-accent-strong: var(--color-nocturnium-flame);
|
|
69
72
|
|
|
70
73
|
/* IDE Semantic Colors */
|
|
@@ -98,8 +101,10 @@
|
|
|
98
101
|
|
|
99
102
|
/* IDE Borders */
|
|
100
103
|
--ide-border: color-mix(in srgb, var(--ide-text-secondary) 20%, transparent);
|
|
104
|
+
--ide-border-light: color-mix(in srgb, var(--ide-text-secondary) 12%, transparent);
|
|
101
105
|
--ide-border-focus: var(--ide-interactive);
|
|
102
106
|
--ide-border-error: var(--ide-error);
|
|
107
|
+
--ide-focus-ring: var(--ide-interactive-focus);
|
|
103
108
|
|
|
104
109
|
/* IDE Shadows */
|
|
105
110
|
--ide-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
@@ -113,6 +118,7 @@
|
|
|
113
118
|
'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, Monaco, 'Courier New', monospace;
|
|
114
119
|
--ide-font-size-xs: 0.75rem;
|
|
115
120
|
--ide-font-size-sm: 0.875rem;
|
|
121
|
+
--ide-font-size-md: 1rem;
|
|
116
122
|
--ide-font-size-base: 1rem;
|
|
117
123
|
--ide-font-size-lg: 1.125rem;
|
|
118
124
|
--ide-font-size-xl: 1.25rem;
|
|
@@ -130,8 +136,10 @@
|
|
|
130
136
|
--ide-spacing-lg: 1.5rem;
|
|
131
137
|
--ide-spacing-xl: 2rem;
|
|
132
138
|
--ide-spacing-2xl: 3rem;
|
|
139
|
+
--ide-spacing-3xl: 4rem;
|
|
133
140
|
|
|
134
141
|
/* IDE Radii */
|
|
142
|
+
--ide-radius-xs: 0.125rem;
|
|
135
143
|
--ide-radius-sm: 0.25rem;
|
|
136
144
|
--ide-radius-md: 0.375rem;
|
|
137
145
|
--ide-radius-lg: 0.5rem;
|
|
@@ -165,6 +173,21 @@
|
|
|
165
173
|
--ide-status-bar-height: 24px;
|
|
166
174
|
--ide-tab-height: 36px;
|
|
167
175
|
--ide-header-height: 40px;
|
|
176
|
+
--ide-scrollbar-thumb: var(--ide-bg-tertiary);
|
|
177
|
+
|
|
178
|
+
/* IDE Syntax Colors */
|
|
179
|
+
--ide-syntax-keyword: var(--color-nocturnium-aurora-purple);
|
|
180
|
+
--ide-syntax-string: var(--color-nocturnium-aurora-green);
|
|
181
|
+
--ide-syntax-function: var(--color-nocturnium-aurora-blue);
|
|
182
|
+
--ide-syntax-number: var(--color-nocturnium-aurora-yellow);
|
|
183
|
+
--ide-syntax-comment: var(--ide-text-muted);
|
|
184
|
+
--ide-syntax-type: var(--color-nocturnium-teal);
|
|
185
|
+
--ide-syntax-variable: var(--ide-text-primary);
|
|
186
|
+
--ide-syntax-operator: var(--ide-text-primary);
|
|
187
|
+
--ide-syntax-punctuation: var(--ide-text-secondary);
|
|
188
|
+
--ide-syntax-constant: var(--color-nocturnium-aurora-yellow);
|
|
189
|
+
--ide-syntax-tag: var(--color-nocturnium-aurora-pink);
|
|
190
|
+
--ide-syntax-attribute: var(--color-nocturnium-aurora-yellow);
|
|
168
191
|
|
|
169
192
|
/* Agent Status Colors */
|
|
170
193
|
--ide-agent-online: var(--color-nocturnium-aurora-green);
|
|
@@ -175,6 +198,7 @@
|
|
|
175
198
|
|
|
176
199
|
/* Agent Type Colors */
|
|
177
200
|
--ide-agent-human: var(--color-nocturnium-aurora-blue);
|
|
201
|
+
--ide-agent-ai: var(--color-nocturnium-ember);
|
|
178
202
|
--ide-agent-ai-primary: var(--color-nocturnium-ember);
|
|
179
203
|
--ide-agent-ai-secondary: var(--color-nocturnium-aurora-purple);
|
|
180
204
|
--ide-agent-system: var(--color-nocturnium-ocean);
|
|
@@ -192,6 +216,12 @@
|
|
|
192
216
|
--ide-transaction-committed: var(--color-nocturnium-aurora-green);
|
|
193
217
|
--ide-transaction-failed: var(--ide-error);
|
|
194
218
|
|
|
219
|
+
/* File Status Colors */
|
|
220
|
+
--ide-status-created: var(--color-nocturnium-aurora-green);
|
|
221
|
+
--ide-status-modified: var(--color-nocturnium-aurora-yellow);
|
|
222
|
+
--ide-status-deleted: var(--ide-error);
|
|
223
|
+
--ide-status-renamed: var(--color-nocturnium-aurora-purple);
|
|
224
|
+
|
|
195
225
|
/* Progress Indicators */
|
|
196
226
|
--ide-progress-track: var(--ide-bg-tertiary);
|
|
197
227
|
--ide-progress-fill: var(--color-nocturnium-wave);
|
|
@@ -623,6 +653,19 @@
|
|
|
623
653
|
background: var(--ide-agent-ai-primary);
|
|
624
654
|
}
|
|
625
655
|
|
|
656
|
+
@media (prefers-reduced-motion: reduce) {
|
|
657
|
+
.ide-agent-status-ring,
|
|
658
|
+
.ide-agent-status-ring--busy,
|
|
659
|
+
.ide-agent-status-ring--stalled,
|
|
660
|
+
.ide-lock-badge--pending,
|
|
661
|
+
.ide-lock-badge--conflict,
|
|
662
|
+
.ide-transaction-indicator--pending,
|
|
663
|
+
.ide-agent-cursor,
|
|
664
|
+
.ide-agent-cursor--typing::after {
|
|
665
|
+
animation: none;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
626
669
|
/* Force default cursor on editor gutter/line numbers */
|
|
627
670
|
[class*='gutter'],
|
|
628
671
|
[class*='line-number'],
|
package/dist/types/vfs.d.ts
CHANGED
|
@@ -112,9 +112,23 @@ export interface VFSLockAcquisitionOptions {
|
|
|
112
112
|
export declare class VFSError extends Error {
|
|
113
113
|
code: VFSErrorCode;
|
|
114
114
|
details?: unknown | undefined;
|
|
115
|
+
statusCode?: number;
|
|
116
|
+
path?: string;
|
|
117
|
+
workspaceId?: string;
|
|
118
|
+
retryable: boolean;
|
|
119
|
+
userMessage: string;
|
|
120
|
+
technicalDetails?: string;
|
|
121
|
+
recoveryOptions: {
|
|
122
|
+
id: string;
|
|
123
|
+
label: string;
|
|
124
|
+
description: string;
|
|
125
|
+
action: 'retry' | 'force' | 'merge' | 'discard' | 'refresh' | 'wait' | 'cancel';
|
|
126
|
+
recommended?: boolean;
|
|
127
|
+
dangerous?: boolean;
|
|
128
|
+
}[];
|
|
115
129
|
constructor(message: string, code: VFSErrorCode, details?: unknown | undefined);
|
|
116
130
|
}
|
|
117
|
-
export type VFSErrorCode = 'FILE_LOCKED' | 'VERSION_CONFLICT' | 'FILE_NOT_FOUND' | 'PERMISSION_DENIED' | '
|
|
131
|
+
export type VFSErrorCode = 'NETWORK_ERROR' | 'CONNECTION_LOST' | 'TIMEOUT' | 'FILE_LOCKED' | 'LOCK_EXPIRED' | 'LOCK_CONFLICT' | 'VERSION_CONFLICT' | 'FILE_NOT_FOUND' | 'PERMISSION_DENIED' | 'WORKSPACE_NOT_FOUND' | 'INVALID_OPERATION' | 'SERVER_ERROR' | 'RATE_LIMITED' | 'TRANSACTION_FAILED' | 'INVALID_PATH' | 'UNKNOWN';
|
|
118
132
|
export type VFSEvent = VFSSnapshotEvent | VFSUpdateEvent | VFSPingEvent | VFSCompleteEvent | VFSErrorEvent | VFSIterationCompletedEvent | VFSChangeClassifiedEvent | VFSGateProgressEvent | VFSLockAcquiredEvent | VFSLockReleasedEvent;
|
|
119
133
|
export interface VFSBaseEvent {
|
|
120
134
|
timestamp: string;
|
package/dist/types/vfs.js
CHANGED
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
export class VFSError extends Error {
|
|
10
10
|
code;
|
|
11
11
|
details;
|
|
12
|
+
statusCode;
|
|
13
|
+
path;
|
|
14
|
+
workspaceId;
|
|
15
|
+
retryable = false;
|
|
16
|
+
userMessage;
|
|
17
|
+
technicalDetails;
|
|
18
|
+
recoveryOptions = [];
|
|
12
19
|
constructor(message, code, details) {
|
|
13
20
|
super(message);
|
|
14
21
|
this.code = code;
|
|
15
22
|
this.details = details;
|
|
16
23
|
this.name = 'VFSError';
|
|
24
|
+
this.userMessage = message;
|
|
25
|
+
this.technicalDetails = message;
|
|
17
26
|
}
|
|
18
27
|
}
|
package/dist/utils/language.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Language detection and utilities
|
|
3
|
-
*/
|
|
4
1
|
/**
|
|
5
2
|
* Detect language from filename
|
|
6
3
|
*/
|
|
@@ -17,6 +14,10 @@ export declare function getLanguageMimeType(language: string): string;
|
|
|
17
14
|
* Check if a language is supported for syntax highlighting
|
|
18
15
|
*/
|
|
19
16
|
export declare function isLanguageSupported(language: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get languages with real syntax tokenizers.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getSupportedLanguages(): string[];
|
|
20
21
|
/**
|
|
21
22
|
* Get display name for a language
|
|
22
23
|
*/
|
package/dist/utils/language.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSupportedLanguages as getTokenizerSupportedLanguages, resolveLanguage } from '../components/editor/tokenizer';
|
|
1
2
|
/**
|
|
2
3
|
* Language detection and utilities
|
|
3
4
|
*/
|
|
@@ -144,24 +145,13 @@ export function getLanguageMimeType(language) {
|
|
|
144
145
|
* Check if a language is supported for syntax highlighting
|
|
145
146
|
*/
|
|
146
147
|
export function isLanguageSupported(language) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
'python',
|
|
155
|
-
'go',
|
|
156
|
-
'rust',
|
|
157
|
-
'java',
|
|
158
|
-
'cpp',
|
|
159
|
-
'sql',
|
|
160
|
-
'xml',
|
|
161
|
-
'yaml',
|
|
162
|
-
'php'
|
|
163
|
-
];
|
|
164
|
-
return supported.includes(language);
|
|
148
|
+
return getTokenizerSupportedLanguages().includes(resolveLanguage(language));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get languages with real syntax tokenizers.
|
|
152
|
+
*/
|
|
153
|
+
export function getSupportedLanguages() {
|
|
154
|
+
return getTokenizerSupportedLanguages();
|
|
165
155
|
}
|
|
166
156
|
/**
|
|
167
157
|
* Get display name for a language
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocturnium/svelte-ide",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Svelte 5 code editor and IDE building blocks — custom editor, syntax highlighting, code folding, multi-cursor, LSP client, and optional realtime collaboration.",
|
|
5
5
|
"author": "Nocturnium & Jordan Dziat <hello@nocturnium.ai> (https://nocturnium.ai)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
// Minimal editor to debug cursor issue
|
|
3
|
-
interface Props {
|
|
4
|
-
content: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
let { content }: Props = $props();
|
|
8
|
-
const lines = $derived(content.split('\n'));
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<div class="editor">
|
|
12
|
-
<div class="editor__content">
|
|
13
|
-
<div class="editor__lines">
|
|
14
|
-
{#each lines as line, i (i)}
|
|
15
|
-
<div class="editor__line">
|
|
16
|
-
<div class="editor__gutter">
|
|
17
|
-
<span class="editor__line-number">{i + 1}</span>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="editor__line-content">{line || '\u00A0'}</div>
|
|
20
|
-
</div>
|
|
21
|
-
{/each}
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<style>
|
|
27
|
-
.editor {
|
|
28
|
-
position: relative;
|
|
29
|
-
width: 100%;
|
|
30
|
-
height: 100%;
|
|
31
|
-
background: #0d1421;
|
|
32
|
-
font-family: monospace;
|
|
33
|
-
font-size: 14px;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.editor__content {
|
|
37
|
-
position: relative;
|
|
38
|
-
width: 100%;
|
|
39
|
-
height: 100%;
|
|
40
|
-
overflow: auto;
|
|
41
|
-
cursor: default;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.editor__lines {
|
|
45
|
-
position: relative;
|
|
46
|
-
min-height: 100%;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.editor__line {
|
|
50
|
-
display: flex;
|
|
51
|
-
line-height: 20px;
|
|
52
|
-
cursor: default;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.editor__gutter {
|
|
56
|
-
width: 50px;
|
|
57
|
-
background: #1a2744;
|
|
58
|
-
text-align: right;
|
|
59
|
-
padding-right: 8px;
|
|
60
|
-
user-select: none;
|
|
61
|
-
cursor: default;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.editor__line-number {
|
|
65
|
-
color: #666;
|
|
66
|
-
cursor: default;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.editor__line-content {
|
|
70
|
-
flex: 1;
|
|
71
|
-
padding-left: 8px;
|
|
72
|
-
color: #e8e8f0;
|
|
73
|
-
cursor: text;
|
|
74
|
-
}
|
|
75
|
-
</style>
|