@nocturnium/svelte-ide 1.0.2 → 1.0.4
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/ContextLens.svelte +16 -10
- package/dist/components/editor/CustomEditor.svelte +52 -33
- package/dist/components/editor/CustomEditor.svelte.d.ts +2 -2
- package/dist/components/editor/EchoCursorLayer.svelte +43 -11
- 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/GhostBracketLayer.svelte +38 -25
- 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 +0 -3
- package/dist/components/editor/tokenizer/languages/javascript.js +1 -2
- 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/mock-ai.js +1 -1
- 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 +3 -2
- 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
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ LSP, collaboration, AI, and plugin pieces into a full IDE experience.
|
|
|
23
23
|
- **Find & replace** with regex support.
|
|
24
24
|
- **LSP client** — autocomplete, hover, signatures, diagnostics over WebSocket.
|
|
25
25
|
- **Realtime collaboration** (optional) — CRDT/Yjs, tree-shakeable.
|
|
26
|
-
- **AI panel & agent presence** layers for assistant
|
|
26
|
+
- **AI panel & agent presence** layers for assistant UI and presence patterns.
|
|
27
27
|
- **Plugin system** with a proposal-based lifecycle (bring your own backend).
|
|
28
28
|
- **Themeable** — every color/size is a CSS custom property you can override.
|
|
29
29
|
- **Zero external UI dependencies**; collaboration deps are optional peers.
|
|
@@ -138,8 +138,10 @@ See the [Collaboration guide](https://github.com/nocturnium/svelte-ide/blob/main
|
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
`<AIPanel>` talks to **your own** chat endpoint (configurable; defaults to
|
|
141
|
-
`/api/chat`) via the AI store.
|
|
142
|
-
|
|
141
|
+
`/api/chat`) via the AI store. The repository's demo route returns canned mock
|
|
142
|
+
responses only; it does not connect to a real model provider. Consumers should
|
|
143
|
+
bring their own backend for inference. Model output is HTML-escaped with
|
|
144
|
+
link-scheme whitelisting before rendering. See the
|
|
143
145
|
[AI & agents guide](https://github.com/nocturnium/svelte-ide/blob/main/docs/guides/ai-and-agents.md).
|
|
144
146
|
|
|
145
147
|
## Entry points
|
|
@@ -286,56 +286,60 @@
|
|
|
286
286
|
|
|
287
287
|
/* Syntax highlighting tokens */
|
|
288
288
|
.code-content :global(.token-keyword) {
|
|
289
|
-
color: var(--syntax-keyword
|
|
289
|
+
color: var(--ide-syntax-keyword);
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
.code-content :global(.token-string) {
|
|
293
|
-
color: var(--syntax-string
|
|
293
|
+
color: var(--ide-syntax-string);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
.code-content :global(.token-number) {
|
|
297
|
-
color: var(--syntax-number
|
|
297
|
+
color: var(--ide-syntax-number);
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
.code-content :global(.token-comment) {
|
|
301
|
-
color: var(--syntax-comment
|
|
301
|
+
color: var(--ide-syntax-comment);
|
|
302
302
|
font-style: italic;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
.code-content :global(.token-function) {
|
|
306
|
-
color: var(--syntax-function
|
|
306
|
+
color: var(--ide-syntax-function);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
.code-content :global(.token-class) {
|
|
310
|
-
color: var(--syntax-
|
|
310
|
+
color: var(--ide-syntax-type);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
.code-content :global(.token-operator) {
|
|
314
|
-
color: var(--syntax-operator
|
|
314
|
+
color: var(--ide-syntax-operator);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
.code-content :global(.token-punctuation) {
|
|
318
|
-
color: var(--syntax-punctuation
|
|
318
|
+
color: var(--ide-syntax-punctuation);
|
|
319
319
|
}
|
|
320
320
|
|
|
321
321
|
.code-content :global(.token-variable) {
|
|
322
|
-
color: var(--syntax-variable
|
|
322
|
+
color: var(--ide-syntax-variable);
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
.code-content :global(.token-property) {
|
|
326
|
-
color: var(--
|
|
326
|
+
color: var(--ide-accent);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
.code-content :global(.token-type) {
|
|
330
|
-
color: var(--syntax-type
|
|
330
|
+
color: var(--ide-syntax-type);
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
.code-content :global(.token-tag) {
|
|
334
|
-
color: var(--syntax-tag
|
|
334
|
+
color: var(--ide-syntax-tag);
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
.code-content :global(.token-attribute) {
|
|
338
|
-
color: var(--syntax-attribute
|
|
338
|
+
color: var(--ide-syntax-attribute);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.code-content :global(.token-constant) {
|
|
342
|
+
color: var(--ide-syntax-constant);
|
|
339
343
|
}
|
|
340
344
|
|
|
341
345
|
.code-content :global(.token-text),
|
|
@@ -346,7 +350,7 @@
|
|
|
346
350
|
/* Streaming cursor */
|
|
347
351
|
.cursor {
|
|
348
352
|
animation: blink 1s step-end infinite;
|
|
349
|
-
color: var(--
|
|
353
|
+
color: var(--ide-accent);
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
@keyframes blink {
|
|
@@ -354,4 +358,10 @@
|
|
|
354
358
|
opacity: 0;
|
|
355
359
|
}
|
|
356
360
|
}
|
|
361
|
+
|
|
362
|
+
@media (prefers-reduced-motion: reduce) {
|
|
363
|
+
.cursor {
|
|
364
|
+
animation: none;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
357
367
|
</style>
|
|
@@ -208,6 +208,7 @@
|
|
|
208
208
|
<Icon name="sparkles" size={16} />
|
|
209
209
|
<span>AI Assistant</span>
|
|
210
210
|
</div>
|
|
211
|
+
<span class="ai-panel__mock-badge">Demo mock - no real model</span>
|
|
211
212
|
</div>
|
|
212
213
|
<div class="ai-panel__actions">
|
|
213
214
|
<Button variant="ghost" size="xs" onclick={handleNewConversation} title="New conversation">
|
|
@@ -337,6 +338,7 @@
|
|
|
337
338
|
display: flex;
|
|
338
339
|
align-items: center;
|
|
339
340
|
gap: var(--ide-spacing-sm);
|
|
341
|
+
min-width: 0;
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
.ai-panel__title {
|
|
@@ -346,6 +348,22 @@
|
|
|
346
348
|
font-size: var(--ide-font-size-sm);
|
|
347
349
|
font-weight: 600;
|
|
348
350
|
color: var(--ide-text-primary);
|
|
351
|
+
flex-shrink: 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.ai-panel__mock-badge {
|
|
355
|
+
display: inline-flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
min-width: 0;
|
|
358
|
+
padding: 0.125rem var(--ide-spacing-sm);
|
|
359
|
+
border: 1px solid color-mix(in srgb, var(--ide-accent) 40%, var(--ide-border));
|
|
360
|
+
border-radius: var(--ide-radius-full);
|
|
361
|
+
background: color-mix(in srgb, var(--ide-accent) 10%, var(--ide-bg-secondary));
|
|
362
|
+
color: var(--ide-text-secondary);
|
|
363
|
+
font-size: var(--ide-font-size-xs);
|
|
364
|
+
font-weight: 500;
|
|
365
|
+
line-height: var(--ide-line-height-normal);
|
|
366
|
+
white-space: nowrap;
|
|
349
367
|
}
|
|
350
368
|
|
|
351
369
|
.ai-panel__actions {
|
|
@@ -541,6 +559,10 @@
|
|
|
541
559
|
.ai-panel__input {
|
|
542
560
|
padding: var(--ide-spacing-sm);
|
|
543
561
|
}
|
|
562
|
+
|
|
563
|
+
.ai-panel__mock-badge {
|
|
564
|
+
white-space: normal;
|
|
565
|
+
}
|
|
544
566
|
}
|
|
545
567
|
|
|
546
568
|
@media (max-width: 480px) {
|
|
@@ -8,14 +8,30 @@
|
|
|
8
8
|
|
|
9
9
|
import { onMount, onDestroy } from 'svelte';
|
|
10
10
|
import * as Y from 'yjs';
|
|
11
|
+
import type { Awareness } from 'y-protocols/awareness';
|
|
11
12
|
import CustomEditor from './CustomEditor.svelte';
|
|
12
|
-
import { createEditorState
|
|
13
|
+
import { createEditorState } from './core/state';
|
|
14
|
+
import { createCRDTBinding, type CRDTBinding } from './core/crdt-binding';
|
|
15
|
+
import { createAwarenessProtocol, type AwarenessProtocol } from '../../crdt/awareness';
|
|
16
|
+
import {
|
|
17
|
+
CollaborativeProvider,
|
|
18
|
+
createProvider,
|
|
19
|
+
type CollaborativeProvider as CollaborativeProviderType
|
|
20
|
+
} from '../../crdt/provider';
|
|
13
21
|
import type { EditorPreferences } from '../../types';
|
|
14
22
|
import type { CollaborationUser } from '../../types/crdt';
|
|
15
23
|
|
|
16
24
|
interface Props {
|
|
17
25
|
/** Yjs document for collaboration (optional - will create one if not provided) */
|
|
18
26
|
doc?: Y.Doc;
|
|
27
|
+
/** Existing provider; its awareness is used for transmitted presence */
|
|
28
|
+
provider?: CollaborativeProviderType;
|
|
29
|
+
/** Existing provider-attached awareness for transmitted presence */
|
|
30
|
+
awareness?: Awareness;
|
|
31
|
+
/** WebSocket server URL when the component should create a provider */
|
|
32
|
+
serverUrl?: string;
|
|
33
|
+
/** WebSocket room ID when the component should create a provider */
|
|
34
|
+
roomId?: string;
|
|
19
35
|
/** Document ID for standalone mode */
|
|
20
36
|
documentId?: string;
|
|
21
37
|
/** Initial content when creating a new document */
|
|
@@ -32,6 +48,10 @@
|
|
|
32
48
|
class?: string;
|
|
33
49
|
/** Current user info for cursor display */
|
|
34
50
|
currentUser?: CollaborationUser;
|
|
51
|
+
/** File path this user is viewing */
|
|
52
|
+
viewingFile?: string;
|
|
53
|
+
/** File path this user is editing */
|
|
54
|
+
editingFile?: string;
|
|
35
55
|
/** Called when content changes */
|
|
36
56
|
onChange?: (content: string) => void;
|
|
37
57
|
/** Called when cursor position changes */
|
|
@@ -42,14 +62,20 @@
|
|
|
42
62
|
|
|
43
63
|
let {
|
|
44
64
|
doc: externalDoc,
|
|
45
|
-
|
|
65
|
+
provider: externalProvider,
|
|
66
|
+
awareness: externalAwareness,
|
|
67
|
+
serverUrl,
|
|
68
|
+
roomId,
|
|
69
|
+
documentId = 'document',
|
|
46
70
|
initialContent = '',
|
|
47
71
|
textName = 'content',
|
|
48
72
|
language = 'plaintext',
|
|
49
73
|
readonly = false,
|
|
50
74
|
preferences = {},
|
|
51
75
|
class: className = '',
|
|
52
|
-
currentUser
|
|
76
|
+
currentUser,
|
|
77
|
+
viewingFile,
|
|
78
|
+
editingFile,
|
|
53
79
|
onChange,
|
|
54
80
|
onCursorChange,
|
|
55
81
|
onSave
|
|
@@ -57,6 +83,8 @@
|
|
|
57
83
|
|
|
58
84
|
// Create internal doc if none provided
|
|
59
85
|
let internalDoc: Y.Doc | null = null;
|
|
86
|
+
let ownedProvider: CollaborativeProvider | null = null;
|
|
87
|
+
let awarenessProtocol: AwarenessProtocol | null = null;
|
|
60
88
|
|
|
61
89
|
// Editor state and CRDT binding
|
|
62
90
|
let editorState = $state<ReturnType<typeof createEditorState> | null>(null);
|
|
@@ -82,6 +110,27 @@
|
|
|
82
110
|
}
|
|
83
111
|
}
|
|
84
112
|
|
|
113
|
+
const provider =
|
|
114
|
+
externalProvider ??
|
|
115
|
+
(serverUrl && roomId
|
|
116
|
+
? createProvider(doc, serverUrl, roomId, currentUser ?? createAnonymousUser(documentId))
|
|
117
|
+
: null);
|
|
118
|
+
if (provider && !externalProvider) {
|
|
119
|
+
ownedProvider = provider;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const awareness = provider?.awareness ?? externalAwareness;
|
|
123
|
+
awarenessProtocol = createAwarenessProtocol(awareness ?? doc);
|
|
124
|
+
if (currentUser) {
|
|
125
|
+
awarenessProtocol.setUser(currentUser);
|
|
126
|
+
}
|
|
127
|
+
if (viewingFile !== undefined) {
|
|
128
|
+
awarenessProtocol.setViewingFile(viewingFile);
|
|
129
|
+
}
|
|
130
|
+
if (editingFile !== undefined) {
|
|
131
|
+
awarenessProtocol.setEditingFile(editingFile);
|
|
132
|
+
}
|
|
133
|
+
|
|
85
134
|
// Create editor state
|
|
86
135
|
editorState = createEditorState({
|
|
87
136
|
content: initialContent,
|
|
@@ -131,9 +180,21 @@
|
|
|
131
180
|
yTextObserver?.();
|
|
132
181
|
yTextObserver = null;
|
|
133
182
|
crdtBinding?.destroy();
|
|
183
|
+
awarenessProtocol?.destroy();
|
|
184
|
+
awarenessProtocol = null;
|
|
185
|
+
ownedProvider?.destroy();
|
|
186
|
+
ownedProvider = null;
|
|
134
187
|
internalDoc?.destroy();
|
|
135
188
|
});
|
|
136
189
|
|
|
190
|
+
function createAnonymousUser(id: string): CollaborationUser {
|
|
191
|
+
return {
|
|
192
|
+
id,
|
|
193
|
+
name: 'Anonymous',
|
|
194
|
+
color: '#60a5fa'
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
137
198
|
// Handle content changes from the inner editor (local typing).
|
|
138
199
|
//
|
|
139
200
|
// The inner CustomEditor maintains its OWN EditorState, so local edits never
|
|
@@ -157,8 +218,10 @@
|
|
|
157
218
|
function handleCursorChange(line: number, column: number) {
|
|
158
219
|
onCursorChange?.(line, column);
|
|
159
220
|
|
|
160
|
-
|
|
161
|
-
|
|
221
|
+
if (!crdtBinding || !awarenessProtocol) return;
|
|
222
|
+
const index = crdtBinding.positionToIndex({ line, column });
|
|
223
|
+
awarenessProtocol.setCursor(index, index);
|
|
224
|
+
awarenessProtocol.setSelection(index, index);
|
|
162
225
|
}
|
|
163
226
|
</script>
|
|
164
227
|
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
+
import type { Awareness } from 'y-protocols/awareness';
|
|
3
|
+
import { type CollaborativeProvider as CollaborativeProviderType } from '../../crdt/provider';
|
|
2
4
|
import type { EditorPreferences } from '../../types';
|
|
3
5
|
import type { CollaborationUser } from '../../types/crdt';
|
|
4
6
|
interface Props {
|
|
5
7
|
/** Yjs document for collaboration (optional - will create one if not provided) */
|
|
6
8
|
doc?: Y.Doc;
|
|
9
|
+
/** Existing provider; its awareness is used for transmitted presence */
|
|
10
|
+
provider?: CollaborativeProviderType;
|
|
11
|
+
/** Existing provider-attached awareness for transmitted presence */
|
|
12
|
+
awareness?: Awareness;
|
|
13
|
+
/** WebSocket server URL when the component should create a provider */
|
|
14
|
+
serverUrl?: string;
|
|
15
|
+
/** WebSocket room ID when the component should create a provider */
|
|
16
|
+
roomId?: string;
|
|
7
17
|
/** Document ID for standalone mode */
|
|
8
18
|
documentId?: string;
|
|
9
19
|
/** Initial content when creating a new document */
|
|
@@ -20,6 +30,10 @@ interface Props {
|
|
|
20
30
|
class?: string;
|
|
21
31
|
/** Current user info for cursor display */
|
|
22
32
|
currentUser?: CollaborationUser;
|
|
33
|
+
/** File path this user is viewing */
|
|
34
|
+
viewingFile?: string;
|
|
35
|
+
/** File path this user is editing */
|
|
36
|
+
editingFile?: string;
|
|
23
37
|
/** Called when content changes */
|
|
24
38
|
onChange?: (content: string) => void;
|
|
25
39
|
/** Called when cursor position changes */
|
|
@@ -205,13 +205,13 @@
|
|
|
205
205
|
align-items: center;
|
|
206
206
|
gap: 6px;
|
|
207
207
|
padding: 2px 8px;
|
|
208
|
-
background:
|
|
209
|
-
border: 1px solid var(--
|
|
208
|
+
background: color-mix(in srgb, var(--ide-bg-elevated) 95%, transparent);
|
|
209
|
+
border: 1px solid var(--ide-border);
|
|
210
210
|
border-radius: 4px;
|
|
211
211
|
font-size: 11px;
|
|
212
|
-
font-family: var(--font-mono
|
|
212
|
+
font-family: var(--ide-font-mono);
|
|
213
213
|
white-space: nowrap;
|
|
214
|
-
box-shadow:
|
|
214
|
+
box-shadow: var(--ide-shadow-md);
|
|
215
215
|
animation: lens-fade-in 0.15s ease-out;
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -227,19 +227,19 @@
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
.context-lens__item--function-header {
|
|
230
|
-
border-left: 3px solid
|
|
230
|
+
border-left: 3px solid var(--ide-syntax-function);
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
.context-lens__item--variable-type {
|
|
234
|
-
border-left: 3px solid
|
|
234
|
+
border-left: 3px solid var(--ide-syntax-keyword);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
.context-lens__item--parameter-info {
|
|
238
|
-
border-left: 3px solid
|
|
238
|
+
border-left: 3px solid var(--ide-syntax-string);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
.context-lens__item--return-type {
|
|
242
|
-
border-left: 3px solid
|
|
242
|
+
border-left: 3px solid var(--ide-syntax-number);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
.context-lens__icon {
|
|
@@ -248,12 +248,18 @@
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
.context-lens__label {
|
|
251
|
-
color:
|
|
251
|
+
color: var(--ide-text-primary);
|
|
252
252
|
font-weight: 600;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
.context-lens__detail {
|
|
256
|
-
color:
|
|
256
|
+
color: var(--ide-text-muted);
|
|
257
257
|
font-weight: 400;
|
|
258
258
|
}
|
|
259
|
+
|
|
260
|
+
@media (prefers-reduced-motion: reduce) {
|
|
261
|
+
.context-lens__item {
|
|
262
|
+
animation: none;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
259
265
|
</style>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
multiCursor?: boolean;
|
|
54
54
|
/** Maximum number of cursors (default: 100) */
|
|
55
55
|
maxCursors?: number;
|
|
56
|
-
/** Enable complexity highlighting (default:
|
|
56
|
+
/** Enable complexity highlighting (default: false) */
|
|
57
57
|
complexityHighlighting?: boolean;
|
|
58
58
|
/** Minimum complexity score to show highlighting (default: 50) */
|
|
59
59
|
complexityThreshold?: number;
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
aiAgents?: AIAwareness[];
|
|
62
62
|
/** Show AI cursor labels (default: true) */
|
|
63
63
|
showAILabels?: boolean;
|
|
64
|
-
/** Show AI focus regions (default:
|
|
64
|
+
/** Show AI focus regions (default: false) */
|
|
65
65
|
showAIFocusRegions?: boolean;
|
|
66
66
|
onChange?: (content: string) => void;
|
|
67
67
|
onCursorChange?: (line: number, column: number) => void;
|
|
@@ -81,11 +81,11 @@
|
|
|
81
81
|
folding = true,
|
|
82
82
|
multiCursor = true,
|
|
83
83
|
maxCursors = 100,
|
|
84
|
-
complexityHighlighting =
|
|
84
|
+
complexityHighlighting = false,
|
|
85
85
|
complexityThreshold = 50,
|
|
86
86
|
aiAgents = [],
|
|
87
87
|
showAILabels = true,
|
|
88
|
-
showAIFocusRegions =
|
|
88
|
+
showAIFocusRegions = false,
|
|
89
89
|
onChange,
|
|
90
90
|
onCursorChange,
|
|
91
91
|
onCursorsChange,
|
|
@@ -171,10 +171,23 @@
|
|
|
171
171
|
// Input handler module (created in initEditor)
|
|
172
172
|
let inputHandlers = $state(null as unknown as ReturnType<typeof createEditorInput>);
|
|
173
173
|
|
|
174
|
+
function lineToVisualRow(line: number): number {
|
|
175
|
+
const lineCount = editorState?.lineCount ?? lines.length;
|
|
176
|
+
if (!folding || lineCount <= 0) return Math.max(0, Math.floor(line));
|
|
177
|
+
return foldManager.lineToVisualRow(line, lineCount);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function visualRowToLine(visualRow: number): number {
|
|
181
|
+
const lineCount = editorState?.lineCount ?? lines.length;
|
|
182
|
+
if (!folding || lineCount <= 0) return Math.max(0, Math.floor(visualRow));
|
|
183
|
+
return foldManager.visualRowToLine(visualRow, lineCount);
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
// Scroll management module
|
|
175
187
|
const editorScroll = createEditorScroll({
|
|
176
188
|
getEditorContent: () => editorContent,
|
|
177
189
|
getSelection: () => selection,
|
|
190
|
+
lineToVisualRow: (line) => lineToVisualRow(line),
|
|
178
191
|
getMeasurements: () => ({ lineHeight, charWidth, gutterWidth, contentPadding: CONTENT_PADDING })
|
|
179
192
|
});
|
|
180
193
|
const { scrollCursorIntoView } = editorScroll;
|
|
@@ -378,6 +391,7 @@
|
|
|
378
391
|
getCursors: () => cursors,
|
|
379
392
|
getScrollPosition: () => ({ scrollTop, scrollLeft }),
|
|
380
393
|
getMeasurements: () => ({ charWidth, lineHeight, gutterWidth }),
|
|
394
|
+
visualRowToLine: (visualRow) => visualRowToLine(visualRow),
|
|
381
395
|
|
|
382
396
|
onOpenFind: (withReplace) => openFind(withReplace),
|
|
383
397
|
onCloseFind: () => closeFind(),
|
|
@@ -507,10 +521,12 @@
|
|
|
507
521
|
if (!folding || visibleLineIndices.length === 0) {
|
|
508
522
|
return lines.map((line, index) => ({ line, index }));
|
|
509
523
|
}
|
|
510
|
-
return visibleLineIndices
|
|
511
|
-
|
|
512
|
-
index
|
|
513
|
-
|
|
524
|
+
return visibleLineIndices
|
|
525
|
+
.filter((index) => index >= 0 && index < lines.length)
|
|
526
|
+
.map((index) => ({
|
|
527
|
+
line: lines[index],
|
|
528
|
+
index
|
|
529
|
+
}));
|
|
514
530
|
});
|
|
515
531
|
|
|
516
532
|
/** Extra rows rendered above/below the viewport to avoid blank edges while scrolling. */
|
|
@@ -540,6 +556,7 @@
|
|
|
540
556
|
}> = [];
|
|
541
557
|
for (let visualRow = firstRow; visualRow <= lastRow; visualRow++) {
|
|
542
558
|
const entry = visibleLines[visualRow];
|
|
559
|
+
if (!entry) continue;
|
|
543
560
|
slice.push({ line: entry.line, index: entry.index, visualRow });
|
|
544
561
|
}
|
|
545
562
|
return slice;
|
|
@@ -590,7 +607,8 @@
|
|
|
590
607
|
setContent: (content) => editorState.setContent(content),
|
|
591
608
|
scrollCursorIntoView: () => scrollCursorIntoView(),
|
|
592
609
|
focusEditor: () => hiddenInput?.focus(),
|
|
593
|
-
isReadonly: () => readonly
|
|
610
|
+
isReadonly: () => readonly,
|
|
611
|
+
lineToVisualRow: (line) => lineToVisualRow(line)
|
|
594
612
|
});
|
|
595
613
|
}
|
|
596
614
|
|
|
@@ -1006,6 +1024,7 @@
|
|
|
1006
1024
|
{viewportHeight}
|
|
1007
1025
|
getLine={(n) => editorState.getLine(n)}
|
|
1008
1026
|
lineCount={editorState?.lineCount ?? 0}
|
|
1027
|
+
lineToVisualRow={(line) => lineToVisualRow(line)}
|
|
1009
1028
|
/>
|
|
1010
1029
|
|
|
1011
1030
|
<!-- Lines (virtualized: only the windowed slice is rendered) -->
|
|
@@ -1134,21 +1153,21 @@
|
|
|
1134
1153
|
:global(.token-comment-line),
|
|
1135
1154
|
:global(.token-comment-block),
|
|
1136
1155
|
:global(.token-comment-doc) {
|
|
1137
|
-
color: var(--ide-
|
|
1156
|
+
color: var(--ide-syntax-comment);
|
|
1138
1157
|
font-style: italic;
|
|
1139
1158
|
}
|
|
1140
1159
|
|
|
1141
1160
|
:global(.token-string),
|
|
1142
1161
|
:global(.token-string-template) {
|
|
1143
|
-
color: var(--
|
|
1162
|
+
color: var(--ide-syntax-string);
|
|
1144
1163
|
}
|
|
1145
1164
|
|
|
1146
1165
|
:global(.token-string-regex) {
|
|
1147
|
-
color: var(--
|
|
1166
|
+
color: var(--ide-syntax-tag);
|
|
1148
1167
|
}
|
|
1149
1168
|
|
|
1150
1169
|
:global(.token-string-escape) {
|
|
1151
|
-
color: var(--
|
|
1170
|
+
color: var(--ide-syntax-constant);
|
|
1152
1171
|
}
|
|
1153
1172
|
|
|
1154
1173
|
:global(.token-number),
|
|
@@ -1156,7 +1175,7 @@
|
|
|
1156
1175
|
:global(.token-number-float),
|
|
1157
1176
|
:global(.token-number-hex),
|
|
1158
1177
|
:global(.token-number-binary) {
|
|
1159
|
-
color: var(--
|
|
1178
|
+
color: var(--ide-syntax-number);
|
|
1160
1179
|
}
|
|
1161
1180
|
|
|
1162
1181
|
:global(.token-keyword),
|
|
@@ -1165,7 +1184,7 @@
|
|
|
1165
1184
|
:global(.token-keyword-definition),
|
|
1166
1185
|
:global(.token-keyword-module),
|
|
1167
1186
|
:global(.token-keyword-storage) {
|
|
1168
|
-
color: var(--
|
|
1187
|
+
color: var(--ide-syntax-keyword);
|
|
1169
1188
|
}
|
|
1170
1189
|
|
|
1171
1190
|
:global(.token-operator),
|
|
@@ -1173,30 +1192,30 @@
|
|
|
1173
1192
|
:global(.token-operator-comparison),
|
|
1174
1193
|
:global(.token-operator-logical),
|
|
1175
1194
|
:global(.token-operator-assignment) {
|
|
1176
|
-
color: var(--ide-
|
|
1195
|
+
color: var(--ide-syntax-operator);
|
|
1177
1196
|
}
|
|
1178
1197
|
|
|
1179
1198
|
:global(.token-variable) {
|
|
1180
|
-
color: var(--ide-
|
|
1199
|
+
color: var(--ide-syntax-variable);
|
|
1181
1200
|
}
|
|
1182
1201
|
|
|
1183
1202
|
:global(.token-variable-definition) {
|
|
1184
|
-
color: var(--
|
|
1203
|
+
color: var(--ide-accent);
|
|
1185
1204
|
}
|
|
1186
1205
|
|
|
1187
1206
|
:global(.token-variable-parameter) {
|
|
1188
|
-
color: color-mix(in srgb, var(--
|
|
1207
|
+
color: color-mix(in srgb, var(--ide-accent) 80%, var(--ide-text-muted));
|
|
1189
1208
|
}
|
|
1190
1209
|
|
|
1191
1210
|
:global(.token-function),
|
|
1192
1211
|
:global(.token-function-definition),
|
|
1193
1212
|
:global(.token-function-call) {
|
|
1194
|
-
color: var(--
|
|
1213
|
+
color: var(--ide-syntax-function);
|
|
1195
1214
|
}
|
|
1196
1215
|
|
|
1197
1216
|
:global(.token-property),
|
|
1198
1217
|
:global(.token-property-definition) {
|
|
1199
|
-
color: var(--
|
|
1218
|
+
color: var(--ide-accent);
|
|
1200
1219
|
}
|
|
1201
1220
|
|
|
1202
1221
|
:global(.token-type),
|
|
@@ -1204,14 +1223,14 @@
|
|
|
1204
1223
|
:global(.token-type-interface),
|
|
1205
1224
|
:global(.token-type-namespace),
|
|
1206
1225
|
:global(.token-type-builtin) {
|
|
1207
|
-
color: var(--
|
|
1226
|
+
color: var(--ide-syntax-type);
|
|
1208
1227
|
}
|
|
1209
1228
|
|
|
1210
1229
|
:global(.token-constant),
|
|
1211
1230
|
:global(.token-constant-boolean),
|
|
1212
1231
|
:global(.token-constant-null),
|
|
1213
1232
|
:global(.token-constant-builtin) {
|
|
1214
|
-
color: var(--
|
|
1233
|
+
color: var(--ide-syntax-constant);
|
|
1215
1234
|
}
|
|
1216
1235
|
|
|
1217
1236
|
:global(.token-punctuation),
|
|
@@ -1220,34 +1239,34 @@
|
|
|
1220
1239
|
:global(.token-punctuation-paren),
|
|
1221
1240
|
:global(.token-punctuation-separator),
|
|
1222
1241
|
:global(.token-punctuation-accessor) {
|
|
1223
|
-
color: var(--ide-
|
|
1242
|
+
color: var(--ide-syntax-punctuation);
|
|
1224
1243
|
}
|
|
1225
1244
|
|
|
1226
1245
|
/* Svelte template braces - make them stand out */
|
|
1227
1246
|
:global(.token-punctuation-brace) {
|
|
1228
|
-
color: var(--
|
|
1247
|
+
color: var(--ide-syntax-constant);
|
|
1229
1248
|
font-weight: 600;
|
|
1230
1249
|
}
|
|
1231
1250
|
|
|
1232
1251
|
:global(.token-tag),
|
|
1233
1252
|
:global(.token-tag-name) {
|
|
1234
|
-
color: var(--
|
|
1253
|
+
color: var(--ide-syntax-tag);
|
|
1235
1254
|
}
|
|
1236
1255
|
|
|
1237
1256
|
:global(.token-tag-attribute) {
|
|
1238
|
-
color: var(--
|
|
1257
|
+
color: var(--ide-syntax-attribute);
|
|
1239
1258
|
}
|
|
1240
1259
|
|
|
1241
1260
|
:global(.token-tag-attribute-value) {
|
|
1242
|
-
color: var(--
|
|
1261
|
+
color: var(--ide-syntax-string);
|
|
1243
1262
|
}
|
|
1244
1263
|
|
|
1245
1264
|
:global(.token-tag-punctuation) {
|
|
1246
|
-
color: var(--ide-
|
|
1265
|
+
color: var(--ide-syntax-punctuation);
|
|
1247
1266
|
}
|
|
1248
1267
|
|
|
1249
1268
|
:global(.token-markup-heading) {
|
|
1250
|
-
color: var(--
|
|
1269
|
+
color: var(--ide-syntax-function);
|
|
1251
1270
|
font-weight: bold;
|
|
1252
1271
|
}
|
|
1253
1272
|
|
|
@@ -1260,12 +1279,12 @@
|
|
|
1260
1279
|
}
|
|
1261
1280
|
|
|
1262
1281
|
:global(.token-markup-link) {
|
|
1263
|
-
color: var(--
|
|
1282
|
+
color: var(--ide-accent);
|
|
1264
1283
|
text-decoration: underline;
|
|
1265
1284
|
}
|
|
1266
1285
|
|
|
1267
1286
|
:global(.token-markup-code) {
|
|
1268
|
-
color: var(--
|
|
1287
|
+
color: var(--ide-syntax-string);
|
|
1269
1288
|
background: var(--ide-bg-tertiary);
|
|
1270
1289
|
}
|
|
1271
1290
|
|
|
@@ -1275,7 +1294,7 @@
|
|
|
1275
1294
|
}
|
|
1276
1295
|
|
|
1277
1296
|
:global(.token-markup-list) {
|
|
1278
|
-
color: var(--
|
|
1297
|
+
color: var(--ide-text-accent);
|
|
1279
1298
|
}
|
|
1280
1299
|
|
|
1281
1300
|
:global(.token-invalid) {
|
|
@@ -14,7 +14,7 @@ interface Props {
|
|
|
14
14
|
multiCursor?: boolean;
|
|
15
15
|
/** Maximum number of cursors (default: 100) */
|
|
16
16
|
maxCursors?: number;
|
|
17
|
-
/** Enable complexity highlighting (default:
|
|
17
|
+
/** Enable complexity highlighting (default: false) */
|
|
18
18
|
complexityHighlighting?: boolean;
|
|
19
19
|
/** Minimum complexity score to show highlighting (default: 50) */
|
|
20
20
|
complexityThreshold?: number;
|
|
@@ -22,7 +22,7 @@ interface Props {
|
|
|
22
22
|
aiAgents?: AIAwareness[];
|
|
23
23
|
/** Show AI cursor labels (default: true) */
|
|
24
24
|
showAILabels?: boolean;
|
|
25
|
-
/** Show AI focus regions (default:
|
|
25
|
+
/** Show AI focus regions (default: false) */
|
|
26
26
|
showAIFocusRegions?: boolean;
|
|
27
27
|
onChange?: (content: string) => void;
|
|
28
28
|
onCursorChange?: (line: number, column: number) => void;
|