@nocturnium/svelte-ide 1.0.0 → 1.0.2
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.d.ts +13 -2
- package/dist/components/editor/tokenizer/languages/javascript.js +278 -84
- 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
|
@@ -5,7 +5,19 @@
|
|
|
5
5
|
/** Display name */
|
|
6
6
|
name: string;
|
|
7
7
|
/** Symbol type */
|
|
8
|
-
type:
|
|
8
|
+
type:
|
|
9
|
+
| 'file'
|
|
10
|
+
| 'module'
|
|
11
|
+
| 'namespace'
|
|
12
|
+
| 'class'
|
|
13
|
+
| 'interface'
|
|
14
|
+
| 'function'
|
|
15
|
+
| 'method'
|
|
16
|
+
| 'property'
|
|
17
|
+
| 'variable'
|
|
18
|
+
| 'constant'
|
|
19
|
+
| 'enum'
|
|
20
|
+
| 'block';
|
|
9
21
|
/** Line number (0-based) */
|
|
10
22
|
line: number;
|
|
11
23
|
/** Column number */
|
|
@@ -211,7 +223,10 @@
|
|
|
211
223
|
</span>
|
|
212
224
|
<span class="breadcrumb-label">{symbol.name}</span>
|
|
213
225
|
{#if symbol.siblings && symbol.siblings.length > 0}
|
|
214
|
-
<span
|
|
226
|
+
<span
|
|
227
|
+
class="breadcrumb-chevron"
|
|
228
|
+
class:breadcrumb-chevron--open={activeDropdown === symbol.id}
|
|
229
|
+
>
|
|
215
230
|
▾
|
|
216
231
|
</span>
|
|
217
232
|
{/if}
|
|
@@ -233,10 +248,7 @@
|
|
|
233
248
|
class:dropdown-item--current={sibling.id === symbol.id}
|
|
234
249
|
onclick={() => handleSiblingSelect(sibling)}
|
|
235
250
|
>
|
|
236
|
-
<span
|
|
237
|
-
class="dropdown-icon"
|
|
238
|
-
style="color: {typeColors[sibling.type]};"
|
|
239
|
-
>
|
|
251
|
+
<span class="dropdown-icon" style="color: {typeColors[sibling.type]};">
|
|
240
252
|
{typeIcons[sibling.type]}
|
|
241
253
|
</span>
|
|
242
254
|
<span class="dropdown-label">{sibling.name}</span>
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
function getBreakpointTitle(bp: Breakpoint): string {
|
|
3
|
+
let title = `Breakpoint at line ${bp.line + 1}`;
|
|
4
|
+
if (bp.condition) {
|
|
5
|
+
title += `\nCondition: ${bp.condition}`;
|
|
6
|
+
}
|
|
7
|
+
if (bp.logMessage) {
|
|
8
|
+
title += `\nLog: ${bp.logMessage}`;
|
|
9
|
+
}
|
|
10
|
+
if (bp.hitCondition) {
|
|
11
|
+
title += `\nHit condition: ${bp.hitCondition}`;
|
|
12
|
+
}
|
|
13
|
+
if (bp.hitCount > 0) {
|
|
14
|
+
title += `\nHit count: ${bp.hitCount}`;
|
|
15
|
+
}
|
|
16
|
+
if (bp.state === 'disabled') {
|
|
17
|
+
title += '\n(Disabled)';
|
|
18
|
+
}
|
|
19
|
+
if (bp.errorMessage) {
|
|
20
|
+
title += `\nError: ${bp.errorMessage}`;
|
|
21
|
+
}
|
|
22
|
+
return title;
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
1
26
|
<script lang="ts">
|
|
2
27
|
/**
|
|
3
28
|
* Breakpoint Layer
|
|
@@ -41,7 +66,10 @@
|
|
|
41
66
|
}: Props = $props();
|
|
42
67
|
|
|
43
68
|
let breakpoints = $state<Breakpoint[]>([]);
|
|
44
|
-
let contextMenu = $state<{
|
|
69
|
+
let contextMenu = $state<{
|
|
70
|
+
breakpoint: Breakpoint;
|
|
71
|
+
position: { top: number; left: number };
|
|
72
|
+
} | null>(null);
|
|
45
73
|
let editingBreakpoint = $state<Breakpoint | null>(null);
|
|
46
74
|
let conditionInput = $state('');
|
|
47
75
|
let logMessageInput = $state('');
|
|
@@ -264,35 +292,20 @@
|
|
|
264
292
|
class="breakpoint-context-menu"
|
|
265
293
|
style="top: {contextMenu.position.top}px; left: {contextMenu.position.left}px;"
|
|
266
294
|
>
|
|
267
|
-
<button
|
|
268
|
-
class="menu-item"
|
|
269
|
-
onclick={() => handleContextAction('toggle')}
|
|
270
|
-
>
|
|
295
|
+
<button class="menu-item" onclick={() => handleContextAction('toggle')}>
|
|
271
296
|
{contextMenu.breakpoint.state === 'disabled' ? 'Enable' : 'Disable'} Breakpoint
|
|
272
297
|
</button>
|
|
273
|
-
<button
|
|
274
|
-
class="menu-item"
|
|
275
|
-
onclick={() => handleContextAction('remove')}
|
|
276
|
-
>
|
|
298
|
+
<button class="menu-item" onclick={() => handleContextAction('remove')}>
|
|
277
299
|
Remove Breakpoint
|
|
278
300
|
</button>
|
|
279
301
|
<div class="menu-divider"></div>
|
|
280
|
-
<button
|
|
281
|
-
class="menu-item"
|
|
282
|
-
onclick={() => handleContextAction('condition')}
|
|
283
|
-
>
|
|
302
|
+
<button class="menu-item" onclick={() => handleContextAction('condition')}>
|
|
284
303
|
Edit Condition...
|
|
285
304
|
</button>
|
|
286
|
-
<button
|
|
287
|
-
class="menu-item"
|
|
288
|
-
onclick={() => handleContextAction('logpoint')}
|
|
289
|
-
>
|
|
305
|
+
<button class="menu-item" onclick={() => handleContextAction('logpoint')}>
|
|
290
306
|
Edit Log Message...
|
|
291
307
|
</button>
|
|
292
|
-
<button
|
|
293
|
-
class="menu-item"
|
|
294
|
-
onclick={() => handleContextAction('hitCondition')}
|
|
295
|
-
>
|
|
308
|
+
<button class="menu-item" onclick={() => handleContextAction('hitCondition')}>
|
|
296
309
|
Edit Hit Count...
|
|
297
310
|
</button>
|
|
298
311
|
</div>
|
|
@@ -300,8 +313,19 @@
|
|
|
300
313
|
|
|
301
314
|
<!-- Edit dialog -->
|
|
302
315
|
{#if editingBreakpoint}
|
|
303
|
-
<div
|
|
304
|
-
|
|
316
|
+
<div
|
|
317
|
+
class="breakpoint-edit-overlay"
|
|
318
|
+
role="presentation"
|
|
319
|
+
onclick={cancelEdit}
|
|
320
|
+
onkeydown={(e) => e.key === 'Escape' && cancelEdit()}
|
|
321
|
+
>
|
|
322
|
+
<div
|
|
323
|
+
class="breakpoint-edit-dialog"
|
|
324
|
+
role="dialog"
|
|
325
|
+
tabindex={-1}
|
|
326
|
+
onclick={(e) => e.stopPropagation()}
|
|
327
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
328
|
+
>
|
|
305
329
|
<div class="dialog-header">
|
|
306
330
|
{#if editMode === 'condition'}
|
|
307
331
|
Edit Condition
|
|
@@ -325,9 +349,7 @@
|
|
|
325
349
|
autofocus
|
|
326
350
|
/>
|
|
327
351
|
</label>
|
|
328
|
-
<p class="dialog-hint">
|
|
329
|
-
Expression will be evaluated in the current scope.
|
|
330
|
-
</p>
|
|
352
|
+
<p class="dialog-hint">Expression will be evaluated in the current scope.</p>
|
|
331
353
|
{:else if editMode === 'logpoint'}
|
|
332
354
|
<label class="dialog-label">
|
|
333
355
|
Log message (use {'{expression}'} for values):
|
|
@@ -341,9 +363,7 @@
|
|
|
341
363
|
autofocus
|
|
342
364
|
/>
|
|
343
365
|
</label>
|
|
344
|
-
<p class="dialog-hint">
|
|
345
|
-
Logpoints log a message without stopping execution.
|
|
346
|
-
</p>
|
|
366
|
+
<p class="dialog-hint">Logpoints log a message without stopping execution.</p>
|
|
347
367
|
{:else}
|
|
348
368
|
<label class="dialog-label">
|
|
349
369
|
Break when hit count:
|
|
@@ -363,43 +383,14 @@
|
|
|
363
383
|
{/if}
|
|
364
384
|
</div>
|
|
365
385
|
<div class="dialog-actions">
|
|
366
|
-
<button class="dialog-btn dialog-btn--cancel" onclick={cancelEdit}>
|
|
367
|
-
|
|
368
|
-
</button>
|
|
369
|
-
<button class="dialog-btn dialog-btn--save" onclick={saveEdit}>
|
|
370
|
-
Save
|
|
371
|
-
</button>
|
|
386
|
+
<button class="dialog-btn dialog-btn--cancel" onclick={cancelEdit}> Cancel </button>
|
|
387
|
+
<button class="dialog-btn dialog-btn--save" onclick={saveEdit}> Save </button>
|
|
372
388
|
</div>
|
|
373
389
|
</div>
|
|
374
390
|
</div>
|
|
375
391
|
{/if}
|
|
376
392
|
{/if}
|
|
377
393
|
|
|
378
|
-
<script module lang="ts">
|
|
379
|
-
function getBreakpointTitle(bp: Breakpoint): string {
|
|
380
|
-
let title = `Breakpoint at line ${bp.line + 1}`;
|
|
381
|
-
if (bp.condition) {
|
|
382
|
-
title += `\nCondition: ${bp.condition}`;
|
|
383
|
-
}
|
|
384
|
-
if (bp.logMessage) {
|
|
385
|
-
title += `\nLog: ${bp.logMessage}`;
|
|
386
|
-
}
|
|
387
|
-
if (bp.hitCondition) {
|
|
388
|
-
title += `\nHit condition: ${bp.hitCondition}`;
|
|
389
|
-
}
|
|
390
|
-
if (bp.hitCount > 0) {
|
|
391
|
-
title += `\nHit count: ${bp.hitCount}`;
|
|
392
|
-
}
|
|
393
|
-
if (bp.state === 'disabled') {
|
|
394
|
-
title += '\n(Disabled)';
|
|
395
|
-
}
|
|
396
|
-
if (bp.errorMessage) {
|
|
397
|
-
title += `\nError: ${bp.errorMessage}`;
|
|
398
|
-
}
|
|
399
|
-
return title;
|
|
400
|
-
}
|
|
401
|
-
</script>
|
|
402
|
-
|
|
403
394
|
<style>
|
|
404
395
|
.breakpoint-layer {
|
|
405
396
|
position: absolute;
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
aria-label="Code complexity: {score} out of 100, {levelLabel} complexity"
|
|
69
69
|
onmouseenter={handleMouseEnter}
|
|
70
70
|
onmouseleave={handleMouseLeave}
|
|
71
|
-
|
|
71
|
+
{onclick}
|
|
72
72
|
onkeydown={(e) => e.key === 'Enter' && onclick?.()}
|
|
73
73
|
tabindex={onclick ? 0 : -1}
|
|
74
74
|
>
|
|
@@ -143,7 +143,9 @@
|
|
|
143
143
|
{#if metrics.regions.some((r) => r.suggestion)}
|
|
144
144
|
<div class="cognitive-meter__tooltip-section">
|
|
145
145
|
<span class="cognitive-meter__tooltip-label">Suggestions:</span>
|
|
146
|
-
{#each metrics.regions
|
|
146
|
+
{#each metrics.regions
|
|
147
|
+
.filter((r) => r.suggestion)
|
|
148
|
+
.slice(0, 2) as region (region.startLine)}
|
|
147
149
|
<p class="cognitive-meter__tooltip-suggestion">
|
|
148
150
|
{region.suggestion}
|
|
149
151
|
</p>
|
|
@@ -9,11 +9,7 @@
|
|
|
9
9
|
import { onMount, onDestroy } from 'svelte';
|
|
10
10
|
import * as Y from 'yjs';
|
|
11
11
|
import CustomEditor from './CustomEditor.svelte';
|
|
12
|
-
import {
|
|
13
|
-
createEditorState,
|
|
14
|
-
createCRDTBinding,
|
|
15
|
-
type CRDTBinding
|
|
16
|
-
} from './core';
|
|
12
|
+
import { createEditorState, createCRDTBinding, type CRDTBinding } from './core';
|
|
17
13
|
import type { EditorPreferences } from '../../types';
|
|
18
14
|
import type { CollaborationUser } from '../../types/crdt';
|
|
19
15
|
|
|
@@ -134,10 +134,7 @@
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function escapeHtml(text: string): string {
|
|
137
|
-
return text
|
|
138
|
-
.replace(/&/g, '&')
|
|
139
|
-
.replace(/</g, '<')
|
|
140
|
-
.replace(/>/g, '>');
|
|
137
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
function escapeRegex(text: string): string {
|
|
@@ -59,7 +59,10 @@
|
|
|
59
59
|
|
|
60
60
|
// Group by line (in case of overlapping regions, take highest score)
|
|
61
61
|
let lineScores = $derived.by(() => {
|
|
62
|
-
const scores = new SvelteMap<
|
|
62
|
+
const scores = new SvelteMap<
|
|
63
|
+
number,
|
|
64
|
+
{ score: number; region: ComplexityRegion; isStart: boolean; isEnd: boolean }
|
|
65
|
+
>();
|
|
63
66
|
|
|
64
67
|
for (const indicator of lineIndicators) {
|
|
65
68
|
const existing = scores.get(indicator.line);
|
|
@@ -142,10 +145,7 @@
|
|
|
142
145
|
style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;"
|
|
143
146
|
>
|
|
144
147
|
<div class="complexity-tooltip__header">
|
|
145
|
-
<span
|
|
146
|
-
class="complexity-tooltip__badge"
|
|
147
|
-
style="background: {getColor(hoveredRegion.score)}"
|
|
148
|
-
>
|
|
148
|
+
<span class="complexity-tooltip__badge" style="background: {getColor(hoveredRegion.score)}">
|
|
149
149
|
{hoveredRegion.score}
|
|
150
150
|
</span>
|
|
151
151
|
<span class="complexity-tooltip__title">
|
|
@@ -188,7 +188,9 @@
|
|
|
188
188
|
cursor: help;
|
|
189
189
|
background: var(--indicator-color);
|
|
190
190
|
opacity: var(--indicator-opacity);
|
|
191
|
-
transition:
|
|
191
|
+
transition:
|
|
192
|
+
opacity 0.15s ease,
|
|
193
|
+
width 0.15s ease;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
.complexity-gutter__indicator:hover {
|
|
@@ -139,10 +139,7 @@
|
|
|
139
139
|
tabindex={-1}
|
|
140
140
|
>
|
|
141
141
|
<!-- Gutter indicator -->
|
|
142
|
-
<div
|
|
143
|
-
class="conflict-zone__gutter"
|
|
144
|
-
style="left: -{gutterWidth}px; width: {gutterWidth}px;"
|
|
145
|
-
>
|
|
142
|
+
<div class="conflict-zone__gutter" style="left: -{gutterWidth}px; width: {gutterWidth}px;">
|
|
146
143
|
<span class="conflict-zone__icon">{getSeverityIcon(zone.severity)}</span>
|
|
147
144
|
</div>
|
|
148
145
|
|
|
@@ -211,10 +208,7 @@
|
|
|
211
208
|
<div class="conflict-tooltip__participants">
|
|
212
209
|
{#each hoveredZone.participants as participant (participant.userId)}
|
|
213
210
|
<div class="conflict-tooltip__participant">
|
|
214
|
-
<span
|
|
215
|
-
class="conflict-tooltip__dot"
|
|
216
|
-
style="background: {participant.color}"
|
|
217
|
-
></span>
|
|
211
|
+
<span class="conflict-tooltip__dot" style="background: {participant.color}"></span>
|
|
218
212
|
<span class="conflict-tooltip__name">
|
|
219
213
|
{participant.userName}
|
|
220
214
|
{#if participant.isAI}
|
|
@@ -174,10 +174,7 @@
|
|
|
174
174
|
{#if enabled && visibleItems.length > 0}
|
|
175
175
|
<div class="context-lens" aria-hidden="true">
|
|
176
176
|
{#each visibleItems as item (`${item.line}-${item.type}`)}
|
|
177
|
-
<div
|
|
178
|
-
class="context-lens__item context-lens__item--{item.type}"
|
|
179
|
-
style={getItemStyle(item)}
|
|
180
|
-
>
|
|
177
|
+
<div class="context-lens__item context-lens__item--{item.type}" style={getItemStyle(item)}>
|
|
181
178
|
{#if item.icon}
|
|
182
179
|
<span class="context-lens__icon">{item.icon}</span>
|
|
183
180
|
{/if}
|
|
@@ -192,7 +192,10 @@
|
|
|
192
192
|
|
|
193
193
|
// Reactive state from editor
|
|
194
194
|
let lines = $state<readonly ReturnType<typeof editorState.getLine>[]>([]);
|
|
195
|
-
let selection = $state<Selection>({
|
|
195
|
+
let selection = $state<Selection>({
|
|
196
|
+
anchor: { line: 0, column: 0 },
|
|
197
|
+
head: { line: 0, column: 0 }
|
|
198
|
+
});
|
|
196
199
|
let hasSelection = $state(false);
|
|
197
200
|
let cursors = $state<readonly Cursor[]>([]);
|
|
198
201
|
let hasMultipleCursors = $state(false);
|
|
@@ -386,7 +389,9 @@
|
|
|
386
389
|
onUnfoldAll: () => foldManager.expandAll(),
|
|
387
390
|
onSelectNextOccurrence: () => selectNextOccurrence(editorState),
|
|
388
391
|
onSelectAllOccurrences: () => selectAllOccurrences(editorState),
|
|
389
|
-
onShowCommandPalette: () => {
|
|
392
|
+
onShowCommandPalette: () => {
|
|
393
|
+
showCommandPalette = true;
|
|
394
|
+
},
|
|
390
395
|
onSave,
|
|
391
396
|
announce,
|
|
392
397
|
resetCursorBlink,
|
|
@@ -396,15 +401,25 @@
|
|
|
396
401
|
}
|
|
397
402
|
},
|
|
398
403
|
|
|
399
|
-
setCharWidth: (w) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
+
setCharWidth: (w) => {
|
|
405
|
+
charWidth = w;
|
|
406
|
+
},
|
|
407
|
+
setLineHeight: (h) => {
|
|
408
|
+
lineHeight = h;
|
|
409
|
+
},
|
|
410
|
+
setScrollTop: (v) => {
|
|
411
|
+
scrollTop = v;
|
|
412
|
+
},
|
|
413
|
+
setScrollLeft: (v) => {
|
|
414
|
+
scrollLeft = v;
|
|
415
|
+
},
|
|
416
|
+
setCursorVisible: (v) => {
|
|
417
|
+
cursorVisible = v;
|
|
418
|
+
},
|
|
404
419
|
|
|
405
420
|
isShowFindReplace: () => showFindReplace,
|
|
406
421
|
getSearchQuery: () => searchQuery,
|
|
407
|
-
getSearchMatchCount: () => searchMatches.length
|
|
422
|
+
getSearchMatchCount: () => searchMatches.length
|
|
408
423
|
});
|
|
409
424
|
}
|
|
410
425
|
|
|
@@ -453,17 +468,23 @@
|
|
|
453
468
|
function handleFoldCurrent() {
|
|
454
469
|
if (!folding || !editorState) return;
|
|
455
470
|
const currentLine = selection.head.line;
|
|
456
|
-
//
|
|
457
|
-
|
|
471
|
+
// Fold the innermost block the cursor is *inside*, not only when the cursor
|
|
472
|
+
// sits on the fold header. Skip already-collapsed regions so repeated
|
|
473
|
+
// presses fold progressively outward (VS Code behaviour).
|
|
474
|
+
const region = foldManager.getInnermostRegionContaining(currentLine, 'uncollapsed');
|
|
458
475
|
if (region) {
|
|
459
|
-
foldManager.collapse(
|
|
476
|
+
foldManager.collapse(region.startLine);
|
|
460
477
|
}
|
|
461
478
|
}
|
|
462
479
|
|
|
463
480
|
function handleUnfoldCurrent() {
|
|
464
481
|
if (!folding || !editorState) return;
|
|
465
482
|
const currentLine = selection.head.line;
|
|
466
|
-
|
|
483
|
+
// Expand the innermost collapsed block containing the cursor (mirrors fold).
|
|
484
|
+
const region = foldManager.getInnermostRegionContaining(currentLine, 'collapsed');
|
|
485
|
+
if (region) {
|
|
486
|
+
foldManager.expand(region.startLine);
|
|
487
|
+
}
|
|
467
488
|
}
|
|
468
489
|
|
|
469
490
|
function handleFoldIndicatorClick(lineNumber: number, e: MouseEvent) {
|
|
@@ -486,7 +507,7 @@
|
|
|
486
507
|
if (!folding || visibleLineIndices.length === 0) {
|
|
487
508
|
return lines.map((line, index) => ({ line, index }));
|
|
488
509
|
}
|
|
489
|
-
return visibleLineIndices.map(index => ({
|
|
510
|
+
return visibleLineIndices.map((index) => ({
|
|
490
511
|
line: lines[index],
|
|
491
512
|
index
|
|
492
513
|
}));
|
|
@@ -512,7 +533,11 @@
|
|
|
512
533
|
const firstRow = Math.max(0, Math.floor(scrollTop / lineHeight) - ROW_OVERSCAN);
|
|
513
534
|
const lastRow = Math.min(rowCount - 1, firstRow + rowsPerViewport + ROW_OVERSCAN * 2);
|
|
514
535
|
|
|
515
|
-
const slice: Array<{
|
|
536
|
+
const slice: Array<{
|
|
537
|
+
line: (typeof visibleLines)[number]['line'];
|
|
538
|
+
index: number;
|
|
539
|
+
visualRow: number;
|
|
540
|
+
}> = [];
|
|
516
541
|
for (let visualRow = firstRow; visualRow <= lastRow; visualRow++) {
|
|
517
542
|
const entry = visibleLines[visualRow];
|
|
518
543
|
slice.push({ line: entry.line, index: entry.index, visualRow });
|
|
@@ -629,7 +654,6 @@
|
|
|
629
654
|
);
|
|
630
655
|
});
|
|
631
656
|
|
|
632
|
-
|
|
633
657
|
// Watch for content prop changes (external changes only, not user typing)
|
|
634
658
|
$effect(() => {
|
|
635
659
|
// Only react to a GENUINE external change to the `content` prop (e.g. a file
|
|
@@ -662,8 +686,14 @@
|
|
|
662
686
|
// Use requestAnimationFrame to ensure DOM has updated
|
|
663
687
|
const rafId = requestAnimationFrame(() => {
|
|
664
688
|
if (contentEl) {
|
|
665
|
-
contentEl.scrollTop = Math.min(
|
|
666
|
-
|
|
689
|
+
contentEl.scrollTop = Math.min(
|
|
690
|
+
savedScrollTop,
|
|
691
|
+
contentEl.scrollHeight - contentEl.clientHeight
|
|
692
|
+
);
|
|
693
|
+
contentEl.scrollLeft = Math.min(
|
|
694
|
+
savedScrollLeft,
|
|
695
|
+
contentEl.scrollWidth - contentEl.clientWidth
|
|
696
|
+
);
|
|
667
697
|
}
|
|
668
698
|
});
|
|
669
699
|
// Cleanup: cancel pending RAF if effect re-runs
|
|
@@ -687,15 +717,16 @@
|
|
|
687
717
|
}
|
|
688
718
|
});
|
|
689
719
|
|
|
690
|
-
|
|
691
720
|
// Ensure cursor is on a visible line after fold changes
|
|
692
721
|
$effect(() => {
|
|
693
722
|
if (folding && editorState && foldManager.isLineHidden(selection.head.line)) {
|
|
694
723
|
// Find the fold region containing the cursor and move to its start
|
|
695
724
|
for (const region of foldRegions) {
|
|
696
|
-
if (
|
|
725
|
+
if (
|
|
726
|
+
region.collapsed &&
|
|
697
727
|
selection.head.line > region.startLine &&
|
|
698
|
-
selection.head.line <= region.endLine
|
|
728
|
+
selection.head.line <= region.endLine
|
|
729
|
+
) {
|
|
699
730
|
editorState.setCursor({ line: region.startLine, column: 0 });
|
|
700
731
|
break;
|
|
701
732
|
}
|
|
@@ -838,9 +869,18 @@
|
|
|
838
869
|
matchCount={searchMatches.length}
|
|
839
870
|
currentMatch={currentMatchIndex >= 0 ? currentMatchIndex + 1 : 0}
|
|
840
871
|
onQueryChange={handleQueryChange}
|
|
841
|
-
onReplaceTextChange={(text) => {
|
|
842
|
-
|
|
843
|
-
|
|
872
|
+
onReplaceTextChange={(text) => {
|
|
873
|
+
editorFind.setReplaceText(text);
|
|
874
|
+
syncFindState();
|
|
875
|
+
}}
|
|
876
|
+
onCaseSensitiveChange={(value) => {
|
|
877
|
+
editorFind.setCaseSensitive(value);
|
|
878
|
+
syncFindState();
|
|
879
|
+
}}
|
|
880
|
+
onUseRegexChange={(value) => {
|
|
881
|
+
editorFind.setUseRegex(value);
|
|
882
|
+
syncFindState();
|
|
883
|
+
}}
|
|
844
884
|
onFindNext={handleFindNext}
|
|
845
885
|
onFindPrev={handleFindPrev}
|
|
846
886
|
onReplace={handleReplace}
|
|
@@ -872,7 +912,12 @@
|
|
|
872
912
|
|
|
873
913
|
<!-- Screen reader status (announces cursor position changes) -->
|
|
874
914
|
<div class="custom-editor__sr-status" aria-live="polite" aria-atomic="true">
|
|
875
|
-
{readonly ? 'Read-only. ' : ''}Line {selection.head.line + 1}, Column {selection.head.column +
|
|
915
|
+
{readonly ? 'Read-only. ' : ''}Line {selection.head.line + 1}, Column {selection.head.column +
|
|
916
|
+
1}{hasSelection
|
|
917
|
+
? `, ${Math.abs(selection.head.line - selection.anchor.line) + 1} lines selected`
|
|
918
|
+
: ''}{hasMultipleCursors ? `, ${cursors.length} cursors active` : ''}{searchMatches.length > 0
|
|
919
|
+
? `, ${searchMatches.length} search matches, match ${currentMatchIndex + 1} of ${searchMatches.length}`
|
|
920
|
+
: ''}
|
|
876
921
|
</div>
|
|
877
922
|
|
|
878
923
|
<!-- Dynamic screen reader announcements for actions -->
|
|
@@ -882,7 +927,10 @@
|
|
|
882
927
|
|
|
883
928
|
<!-- Hidden keyboard shortcuts help for screen readers -->
|
|
884
929
|
<div id="editor-help" class="custom-editor__sr-status">
|
|
885
|
-
Keyboard shortcuts: Ctrl+F to find, Ctrl+H to replace, Ctrl+D to add cursor at next occurrence,
|
|
930
|
+
Keyboard shortcuts: Ctrl+F to find, Ctrl+H to replace, Ctrl+D to add cursor at next occurrence,
|
|
931
|
+
Ctrl+Shift+L to select all occurrences, Escape to clear.{folding
|
|
932
|
+
? ' Ctrl+Shift+[ to fold, Ctrl+Shift+] to unfold.'
|
|
933
|
+
: ''}
|
|
886
934
|
</div>
|
|
887
935
|
|
|
888
936
|
<!-- Main editor content -->
|
|
@@ -903,8 +951,8 @@
|
|
|
903
951
|
{#if complexityHighlighting && complexityMetrics}
|
|
904
952
|
<ComplexityLayer
|
|
905
953
|
metrics={complexityMetrics}
|
|
906
|
-
|
|
907
|
-
|
|
954
|
+
{lineHeight}
|
|
955
|
+
{gutterWidth}
|
|
908
956
|
minScore={complexityThreshold}
|
|
909
957
|
enabled={complexityHighlighting}
|
|
910
958
|
/>
|
|
@@ -914,9 +962,9 @@
|
|
|
914
962
|
{#if aiAgents.length > 0}
|
|
915
963
|
<AIFocusLayer
|
|
916
964
|
agents={aiAgents}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
965
|
+
{lineHeight}
|
|
966
|
+
{charWidth}
|
|
967
|
+
{gutterWidth}
|
|
920
968
|
contentPadding={CONTENT_PADDING}
|
|
921
969
|
showLabels={showAILabels}
|
|
922
970
|
showFocusRegions={showAIFocusRegions}
|
|
@@ -926,7 +974,10 @@
|
|
|
926
974
|
|
|
927
975
|
<!-- Gutter background (extends full height of content, matching the virtualized spacer) -->
|
|
928
976
|
{#if mergedPrefs.lineNumbers !== 'off'}
|
|
929
|
-
<div
|
|
977
|
+
<div
|
|
978
|
+
class="custom-editor__gutter-bg"
|
|
979
|
+
style="width: {gutterWidth}px; height: {totalContentHeight}px;"
|
|
980
|
+
></div>
|
|
930
981
|
{/if}
|
|
931
982
|
|
|
932
983
|
<!-- Search match highlights (below selection) -->
|
|
@@ -946,7 +997,7 @@
|
|
|
946
997
|
<EditorSelections
|
|
947
998
|
{cursors}
|
|
948
999
|
{cursorVisible}
|
|
949
|
-
|
|
1000
|
+
{readonly}
|
|
950
1001
|
{lineHeight}
|
|
951
1002
|
{charWidth}
|
|
952
1003
|
{gutterWidth}
|
|
@@ -959,7 +1010,7 @@
|
|
|
959
1010
|
|
|
960
1011
|
<!-- Lines (virtualized: only the windowed slice is rendered) -->
|
|
961
1012
|
<EditorLines
|
|
962
|
-
|
|
1013
|
+
{windowedLines}
|
|
963
1014
|
totalHeight={totalContentHeight}
|
|
964
1015
|
{lineHeight}
|
|
965
1016
|
activeLine={selection.head.line}
|
|
@@ -974,15 +1025,11 @@
|
|
|
974
1025
|
onFoldIndicatorClick={handleFoldIndicatorClick}
|
|
975
1026
|
onExpandFold={(index) => foldManager.expand(index)}
|
|
976
1027
|
/>
|
|
977
|
-
|
|
978
1028
|
</div>
|
|
979
1029
|
</div>
|
|
980
1030
|
|
|
981
1031
|
<!-- Command Palette -->
|
|
982
|
-
<CommandPalette
|
|
983
|
-
bind:open={showCommandPalette}
|
|
984
|
-
onClose={() => editorContent?.focus()}
|
|
985
|
-
/>
|
|
1032
|
+
<CommandPalette bind:open={showCommandPalette} onClose={() => editorContent?.focus()} />
|
|
986
1033
|
|
|
987
1034
|
<style>
|
|
988
1035
|
.custom-editor {
|
|
@@ -1070,8 +1117,6 @@
|
|
|
1070
1117
|
border-color: rgba(255, 213, 0, 0.8);
|
|
1071
1118
|
}
|
|
1072
1119
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
1120
|
/* Gutter background - extends full height of content */
|
|
1076
1121
|
.custom-editor__gutter-bg {
|
|
1077
1122
|
position: absolute;
|
|
@@ -1084,7 +1129,6 @@
|
|
|
1084
1129
|
pointer-events: none;
|
|
1085
1130
|
}
|
|
1086
1131
|
|
|
1087
|
-
|
|
1088
1132
|
/* Token styles */
|
|
1089
1133
|
:global(.token-comment),
|
|
1090
1134
|
:global(.token-comment-line),
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
</div>
|
|
261
261
|
|
|
262
262
|
<div class="header-filters">
|
|
263
|
-
{#each
|
|
263
|
+
{#each ['all', 'error', 'warning', 'info', 'log'] as const as filterType (filterType)}
|
|
264
264
|
{@const count = entryCounts()[filterType]}
|
|
265
265
|
<button
|
|
266
266
|
class="filter-btn"
|
|
@@ -273,7 +273,9 @@
|
|
|
273
273
|
<span class="filter-icon">{getTypeIcon(filterType)}</span>
|
|
274
274
|
{/if}
|
|
275
275
|
<span class="filter-label">
|
|
276
|
-
{filterType === 'all'
|
|
276
|
+
{filterType === 'all'
|
|
277
|
+
? 'All'
|
|
278
|
+
: filterType.charAt(0).toUpperCase() + filterType.slice(1)}
|
|
277
279
|
</span>
|
|
278
280
|
{#if count > 0}
|
|
279
281
|
<span class="filter-count">{count}</span>
|
|
@@ -283,13 +285,9 @@
|
|
|
283
285
|
</div>
|
|
284
286
|
|
|
285
287
|
<div class="header-actions">
|
|
286
|
-
<button class="action-btn" onclick={onClear} title="Clear console (Ctrl+L)">
|
|
287
|
-
🗑
|
|
288
|
-
</button>
|
|
288
|
+
<button class="action-btn" onclick={onClear} title="Clear console (Ctrl+L)"> 🗑 </button>
|
|
289
289
|
{#if onClose}
|
|
290
|
-
<button class="action-btn" onclick={onClose} title="Close">
|
|
291
|
-
×
|
|
292
|
-
</button>
|
|
290
|
+
<button class="action-btn" onclick={onClose} title="Close"> × </button>
|
|
293
291
|
{/if}
|
|
294
292
|
</div>
|
|
295
293
|
</div>
|
|
@@ -317,10 +315,7 @@
|
|
|
317
315
|
</span>
|
|
318
316
|
|
|
319
317
|
{#if entry.expandable}
|
|
320
|
-
<button
|
|
321
|
-
class="entry-expand"
|
|
322
|
-
onclick={() => toggleExpand(entry.id)}
|
|
323
|
-
>
|
|
318
|
+
<button class="entry-expand" onclick={() => toggleExpand(entry.id)}>
|
|
324
319
|
{isExpanded ? '▾' : '▸'}
|
|
325
320
|
</button>
|
|
326
321
|
{/if}
|
|
@@ -362,11 +357,7 @@
|
|
|
362
357
|
onkeydown={handleKeyDown}
|
|
363
358
|
placeholder={inputPlaceholder}
|
|
364
359
|
/>
|
|
365
|
-
<button
|
|
366
|
-
class="input-submit"
|
|
367
|
-
onclick={handleSubmit}
|
|
368
|
-
disabled={!inputValue.trim()}
|
|
369
|
-
>
|
|
360
|
+
<button class="input-submit" onclick={handleSubmit} disabled={!inputValue.trim()}>
|
|
370
361
|
⏎
|
|
371
362
|
</button>
|
|
372
363
|
</div>
|