@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
|
@@ -160,7 +160,9 @@
|
|
|
160
160
|
{#if echoCursors.length > 0}
|
|
161
161
|
<div class="echo-mode-indicator">
|
|
162
162
|
<span class="echo-mode-indicator__icon">~</span>
|
|
163
|
-
<span class="echo-mode-indicator__count"
|
|
163
|
+
<span class="echo-mode-indicator__count"
|
|
164
|
+
>{echoCursors.length} echo{echoCursors.length !== 1 ? 's' : ''}</span
|
|
165
|
+
>
|
|
164
166
|
</div>
|
|
165
167
|
{/if}
|
|
166
168
|
</div>
|
|
@@ -51,7 +51,11 @@
|
|
|
51
51
|
<!-- Line number -->
|
|
52
52
|
{#if lineNumbers !== 'off'}
|
|
53
53
|
<div class="custom-editor__gutter" data-cursor-type="default" style="cursor: default !important;">
|
|
54
|
-
<span
|
|
54
|
+
<span
|
|
55
|
+
class="custom-editor__line-number"
|
|
56
|
+
data-cursor-type="default"
|
|
57
|
+
style="cursor: default !important;">{lineIndex + 1}</span
|
|
58
|
+
>
|
|
55
59
|
</div>
|
|
56
60
|
{/if}
|
|
57
61
|
|
|
@@ -73,7 +77,9 @@
|
|
|
73
77
|
color: var(--ide-text-muted);
|
|
74
78
|
font-size: 8px;
|
|
75
79
|
opacity: 0;
|
|
76
|
-
transition:
|
|
80
|
+
transition:
|
|
81
|
+
opacity 0.15s ease,
|
|
82
|
+
color 0.15s ease;
|
|
77
83
|
z-index: 5;
|
|
78
84
|
}
|
|
79
85
|
|
|
@@ -84,9 +84,12 @@
|
|
|
84
84
|
<button
|
|
85
85
|
class="custom-editor__fold-placeholder"
|
|
86
86
|
title="{getHiddenLineCount(index)} lines hidden"
|
|
87
|
-
onclick={(e) => {
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
onclick={(e) => {
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
onExpandFold(index);
|
|
90
|
+
}}
|
|
91
|
+
aria-label="Expand {getHiddenLineCount(index)} hidden lines">...</button
|
|
92
|
+
>
|
|
90
93
|
{/if}
|
|
91
94
|
</div>
|
|
92
95
|
</div>
|
|
@@ -61,12 +61,7 @@
|
|
|
61
61
|
|
|
62
62
|
<div class="editor-pane {className}">
|
|
63
63
|
{#if tabs.length > 0}
|
|
64
|
-
<EditorTabs
|
|
65
|
-
{tabs}
|
|
66
|
-
{activeTabId}
|
|
67
|
-
onSelect={setActiveTab}
|
|
68
|
-
onClose={handleCloseTab}
|
|
69
|
-
/>
|
|
64
|
+
<EditorTabs {tabs} {activeTabId} onSelect={setActiveTab} onClose={handleCloseTab} />
|
|
70
65
|
|
|
71
66
|
{#if activeTab}
|
|
72
67
|
<div class="editor-pane__content">
|
|
@@ -43,13 +43,25 @@
|
|
|
43
43
|
}: Props = $props();
|
|
44
44
|
|
|
45
45
|
// Selection rects memoization
|
|
46
|
-
let cachedSelectionRects: Array<{
|
|
46
|
+
let cachedSelectionRects: Array<{
|
|
47
|
+
top: number;
|
|
48
|
+
left: number;
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
isPrimary: boolean;
|
|
52
|
+
}> = [];
|
|
47
53
|
let cachedSelectionKey = '';
|
|
48
54
|
|
|
49
55
|
// Get selection rectangles for all cursors (memoized and virtualized to viewport)
|
|
50
|
-
function getSelectionRects(): Array<{
|
|
56
|
+
function getSelectionRects(): Array<{
|
|
57
|
+
top: number;
|
|
58
|
+
left: number;
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
isPrimary: boolean;
|
|
62
|
+
}> {
|
|
51
63
|
// Check if any cursor has a selection
|
|
52
|
-
const anySelection = cursors.some(c => !isSelectionEmpty(c.selection));
|
|
64
|
+
const anySelection = cursors.some((c) => !isSelectionEmpty(c.selection));
|
|
53
65
|
if (!anySelection) {
|
|
54
66
|
cachedSelectionRects = [];
|
|
55
67
|
cachedSelectionKey = '';
|
|
@@ -59,21 +71,27 @@
|
|
|
59
71
|
// Calculate visible line range (with 1-line buffer for smooth scrolling)
|
|
60
72
|
const vh = viewportHeight || FALLBACK_VIEWPORT_HEIGHT;
|
|
61
73
|
const firstVisibleLine = Math.max(0, Math.floor(scrollTop / lineHeight) - 1);
|
|
62
|
-
const lastVisibleLine = Math.min(
|
|
63
|
-
lineCount - 1,
|
|
64
|
-
Math.ceil((scrollTop + vh) / lineHeight) + 1
|
|
65
|
-
);
|
|
74
|
+
const lastVisibleLine = Math.min(lineCount - 1, Math.ceil((scrollTop + vh) / lineHeight) + 1);
|
|
66
75
|
|
|
67
76
|
// Create cache key from all cursors, scroll position, and measurement values
|
|
68
|
-
const cursorKeys = cursors
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
const cursorKeys = cursors
|
|
78
|
+
.map(
|
|
79
|
+
(c) =>
|
|
80
|
+
`${c.id}:${c.selection.anchor.line}:${c.selection.anchor.column}-${c.selection.head.line}:${c.selection.head.column}`
|
|
81
|
+
)
|
|
82
|
+
.join('|');
|
|
71
83
|
const key = `${cursorKeys}@${firstVisibleLine}-${lastVisibleLine}:${charWidth}:${lineHeight}:${gutterWidth}`;
|
|
72
84
|
if (key === cachedSelectionKey) {
|
|
73
85
|
return cachedSelectionRects;
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
const rects: Array<{
|
|
88
|
+
const rects: Array<{
|
|
89
|
+
top: number;
|
|
90
|
+
left: number;
|
|
91
|
+
width: number;
|
|
92
|
+
height: number;
|
|
93
|
+
isPrimary: boolean;
|
|
94
|
+
}> = [];
|
|
77
95
|
|
|
78
96
|
// Process each cursor's selection
|
|
79
97
|
for (const cursor of cursors) {
|
|
@@ -399,10 +399,20 @@
|
|
|
399
399
|
<div class="file-explorer__header">
|
|
400
400
|
<span class="file-explorer__title">Explorer</span>
|
|
401
401
|
<div class="file-explorer__actions">
|
|
402
|
-
<Button
|
|
402
|
+
<Button
|
|
403
|
+
variant="ghost"
|
|
404
|
+
size="xs"
|
|
405
|
+
onclick={() => startInlineCreation('', 'file')}
|
|
406
|
+
title="New File"
|
|
407
|
+
>
|
|
403
408
|
<Icon name="file-plus" size={14} />
|
|
404
409
|
</Button>
|
|
405
|
-
<Button
|
|
410
|
+
<Button
|
|
411
|
+
variant="ghost"
|
|
412
|
+
size="xs"
|
|
413
|
+
onclick={() => startInlineCreation('', 'folder')}
|
|
414
|
+
title="New Folder"
|
|
415
|
+
>
|
|
406
416
|
<Icon name="folder-plus" size={14} />
|
|
407
417
|
</Button>
|
|
408
418
|
</div>
|
|
@@ -462,7 +472,13 @@
|
|
|
462
472
|
class:file-node__status-dot--modified={changeStatus === 'modified'}
|
|
463
473
|
class:file-node__status-dot--deleted={changeStatus === 'deleted'}
|
|
464
474
|
class:file-node__status-dot--renamed={changeStatus === 'renamed'}
|
|
465
|
-
title={changeStatus === 'created'
|
|
475
|
+
title={changeStatus === 'created'
|
|
476
|
+
? 'New'
|
|
477
|
+
: changeStatus === 'modified'
|
|
478
|
+
? 'Modified'
|
|
479
|
+
: changeStatus === 'deleted'
|
|
480
|
+
? 'Deleted'
|
|
481
|
+
: 'Renamed'}
|
|
466
482
|
></span>
|
|
467
483
|
{/if}
|
|
468
484
|
{#if node.type === 'folder'}
|
|
@@ -493,7 +509,13 @@
|
|
|
493
509
|
<div class="file-node__agents">
|
|
494
510
|
{#each fileAgents.slice(0, 3) as agent (agent.id)}
|
|
495
511
|
<Tooltip content="{agent.name} is editing">
|
|
496
|
-
<AgentAvatar
|
|
512
|
+
<AgentAvatar
|
|
513
|
+
{agent}
|
|
514
|
+
size="xs"
|
|
515
|
+
showStatus={false}
|
|
516
|
+
showBadge={false}
|
|
517
|
+
showProgress={false}
|
|
518
|
+
/>
|
|
497
519
|
</Tooltip>
|
|
498
520
|
{/each}
|
|
499
521
|
{#if fileAgents.length > 3}
|
|
@@ -83,7 +83,9 @@
|
|
|
83
83
|
file: 'M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm-1 1.5L18.5 9H14a1 1 0 01-1-1V3.5z'
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
const iconPath = $derived(
|
|
86
|
+
const iconPath = $derived(
|
|
87
|
+
isDirectory ? (expanded ? icons.folderOpen : icons.folder) : icons.file
|
|
88
|
+
);
|
|
87
89
|
const iconColor = $derived(isDirectory ? 'var(--ide-interactive)' : color);
|
|
88
90
|
</script>
|
|
89
91
|
|
|
@@ -278,7 +278,12 @@
|
|
|
278
278
|
title="Replace all (Shift+Enter)"
|
|
279
279
|
>
|
|
280
280
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
|
|
281
|
-
<path
|
|
281
|
+
<path
|
|
282
|
+
d="M1 4h6M1 7h8M1 10h6M10 4l2 2-2 2M10 8l2 2-2 2"
|
|
283
|
+
stroke="currentColor"
|
|
284
|
+
stroke-width="1.2"
|
|
285
|
+
fill="none"
|
|
286
|
+
/>
|
|
282
287
|
</svg>
|
|
283
288
|
</button>
|
|
284
289
|
</div>
|
|
@@ -316,7 +321,9 @@
|
|
|
316
321
|
border-radius: 3px;
|
|
317
322
|
color: var(--ide-text-secondary);
|
|
318
323
|
cursor: pointer;
|
|
319
|
-
transition:
|
|
324
|
+
transition:
|
|
325
|
+
background 0.1s,
|
|
326
|
+
color 0.1s;
|
|
320
327
|
}
|
|
321
328
|
|
|
322
329
|
.find-replace__toggle:hover {
|
|
@@ -386,7 +393,10 @@
|
|
|
386
393
|
font-family: var(--ide-font-mono);
|
|
387
394
|
font-size: 11px;
|
|
388
395
|
cursor: pointer;
|
|
389
|
-
transition:
|
|
396
|
+
transition:
|
|
397
|
+
background 0.1s,
|
|
398
|
+
border-color 0.1s,
|
|
399
|
+
color 0.1s;
|
|
390
400
|
}
|
|
391
401
|
|
|
392
402
|
.find-replace__option:hover {
|
|
@@ -425,7 +435,9 @@
|
|
|
425
435
|
border-radius: 3px;
|
|
426
436
|
color: var(--ide-text-secondary);
|
|
427
437
|
cursor: pointer;
|
|
428
|
-
transition:
|
|
438
|
+
transition:
|
|
439
|
+
background 0.1s,
|
|
440
|
+
color 0.1s;
|
|
429
441
|
}
|
|
430
442
|
|
|
431
443
|
.find-replace__action:hover:not(:disabled) {
|
|
@@ -191,11 +191,11 @@
|
|
|
191
191
|
<div
|
|
192
192
|
class="bracket-mismatch bracket-mismatch--{mismatch.issue}"
|
|
193
193
|
style="{getMismatchStyle(mismatch)} --mismatch-color: {color};"
|
|
194
|
-
title=
|
|
194
|
+
title={mismatch.issue === 'unclosed'
|
|
195
195
|
? `Unclosed '${mismatch.character}'`
|
|
196
196
|
: mismatch.issue === 'unexpected'
|
|
197
197
|
? `Unexpected '${mismatch.character}'`
|
|
198
|
-
: `Expected '${mismatch.expected}' but found '${mismatch.character}'`}
|
|
198
|
+
: `Expected '${mismatch.expected}' but found '${mismatch.character}'`}
|
|
199
199
|
>
|
|
200
200
|
<span class="bracket-mismatch__underline"></span>
|
|
201
201
|
{#if mismatch.issue === 'mismatched' && mismatch.expected}
|
|
@@ -167,7 +167,8 @@
|
|
|
167
167
|
class:git-blame-row--hovered={hoveredLine !== null &&
|
|
168
168
|
hoveredLine >= group.startLine &&
|
|
169
169
|
hoveredLine <= group.endLine}
|
|
170
|
-
style="top: {group.startLine *
|
|
170
|
+
style="top: {group.startLine *
|
|
171
|
+
lineHeight}px; height: {height}px; --blame-color: {info.color};"
|
|
171
172
|
onmouseenter={(e) => handleMouseEnter(info, e)}
|
|
172
173
|
onmouseleave={handleMouseLeave}
|
|
173
174
|
onclick={() => handleClick(info)}
|
|
@@ -215,18 +215,8 @@
|
|
|
215
215
|
role="button"
|
|
216
216
|
tabindex={-1}
|
|
217
217
|
>
|
|
218
|
-
<svg
|
|
219
|
-
|
|
220
|
-
width={width}
|
|
221
|
-
height="4"
|
|
222
|
-
viewBox="0 0 {width} 4"
|
|
223
|
-
>
|
|
224
|
-
<path
|
|
225
|
-
d={generateSquigglePath(width)}
|
|
226
|
-
stroke={color}
|
|
227
|
-
stroke-width="1"
|
|
228
|
-
fill="none"
|
|
229
|
-
/>
|
|
218
|
+
<svg class="squiggle-svg" {width} height="4" viewBox="0 0 {width} 4">
|
|
219
|
+
<path d={generateSquigglePath(width)} stroke={color} stroke-width="1" fill="none" />
|
|
230
220
|
</svg>
|
|
231
221
|
</div>
|
|
232
222
|
{/each}
|
|
@@ -297,7 +287,8 @@
|
|
|
297
287
|
{/if}
|
|
298
288
|
|
|
299
289
|
<div class="tooltip-location">
|
|
300
|
-
Line {hoveredDiagnostic.range.start.line + 1}, Column {hoveredDiagnostic.range.start
|
|
290
|
+
Line {hoveredDiagnostic.range.start.line + 1}, Column {hoveredDiagnostic.range.start
|
|
291
|
+
.column + 1}
|
|
301
292
|
</div>
|
|
302
293
|
</div>
|
|
303
294
|
{/if}
|
|
@@ -77,11 +77,21 @@
|
|
|
77
77
|
/**
|
|
78
78
|
* Group consecutive changes of the same type
|
|
79
79
|
*/
|
|
80
|
-
function getGroupedChanges(): Array<{
|
|
80
|
+
function getGroupedChanges(): Array<{
|
|
81
|
+
startLine: number;
|
|
82
|
+
endLine: number;
|
|
83
|
+
type: DiffLine['type'];
|
|
84
|
+
changes: DiffLine[];
|
|
85
|
+
}> {
|
|
81
86
|
if (changes.length === 0) return [];
|
|
82
87
|
|
|
83
|
-
const groups: Array<{
|
|
84
|
-
|
|
88
|
+
const groups: Array<{
|
|
89
|
+
startLine: number;
|
|
90
|
+
endLine: number;
|
|
91
|
+
type: DiffLine['type'];
|
|
92
|
+
changes: DiffLine[];
|
|
93
|
+
}> = [];
|
|
94
|
+
let currentGroup: (typeof groups)[0] | null = null;
|
|
85
95
|
|
|
86
96
|
// Sort by line number
|
|
87
97
|
const sortedChanges = [...changes].sort((a, b) => a.line - b.line);
|
|
@@ -166,7 +176,9 @@
|
|
|
166
176
|
onkeydown={(e) => e.key === 'Enter' && handleClick(group.changes[0])}
|
|
167
177
|
role="button"
|
|
168
178
|
tabindex={-1}
|
|
169
|
-
title="{getLabel(group.type)}: {group.changes.length} line{group.changes.length !== 1
|
|
179
|
+
title="{getLabel(group.type)}: {group.changes.length} line{group.changes.length !== 1
|
|
180
|
+
? 's'
|
|
181
|
+
: ''}"
|
|
170
182
|
></div>
|
|
171
183
|
|
|
172
184
|
<!-- Optional full-line highlight -->
|
|
@@ -184,7 +196,7 @@
|
|
|
184
196
|
{/each}
|
|
185
197
|
|
|
186
198
|
<!-- Removed line markers (triangles in gutter) -->
|
|
187
|
-
{#each changes.filter(c => c.type === 'removed') as removed (removed.line)}
|
|
199
|
+
{#each changes.filter((c) => c.type === 'removed') as removed (removed.line)}
|
|
188
200
|
<div
|
|
189
201
|
class="diff-removed-marker"
|
|
190
202
|
style="top: {removed.line * lineHeight + lineHeight / 2 - 4}px;"
|
|
@@ -195,10 +207,7 @@
|
|
|
195
207
|
|
|
196
208
|
<!-- Tooltip -->
|
|
197
209
|
{#if hoveredChange}
|
|
198
|
-
<div
|
|
199
|
-
class="diff-tooltip"
|
|
200
|
-
style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;"
|
|
201
|
-
>
|
|
210
|
+
<div class="diff-tooltip" style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;">
|
|
202
211
|
<div class="tooltip-header" style="color: {getColor(hoveredChange.type)};">
|
|
203
212
|
{getLabel(hoveredChange.type)} line {hoveredChange.line + 1}
|
|
204
213
|
</div>
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
ctx.fillRect(0, 0, width, editorHeight);
|
|
119
119
|
|
|
120
120
|
// Calculate effective line height based on scaling
|
|
121
|
-
const effectiveLineHeight = needsScaling ?
|
|
121
|
+
const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
|
|
122
122
|
const effectiveCharWidth = CHAR_WIDTH * (needsScaling ? scaleFactor : 1);
|
|
123
123
|
|
|
124
124
|
// Draw highlights first (behind text)
|
|
@@ -150,11 +150,18 @@
|
|
|
150
150
|
|
|
151
151
|
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
152
152
|
color = colors.comment;
|
|
153
|
-
} else if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
} else if (
|
|
154
|
+
trimmed.startsWith('import') ||
|
|
155
|
+
trimmed.startsWith('export') ||
|
|
156
|
+
trimmed.startsWith('const') ||
|
|
157
|
+
trimmed.startsWith('let') ||
|
|
158
|
+
trimmed.startsWith('function') ||
|
|
159
|
+
trimmed.startsWith('class') ||
|
|
160
|
+
trimmed.startsWith('if') ||
|
|
161
|
+
trimmed.startsWith('for') ||
|
|
162
|
+
trimmed.startsWith('return') ||
|
|
163
|
+
trimmed.startsWith('async')
|
|
164
|
+
) {
|
|
158
165
|
color = colors.keyword;
|
|
159
166
|
} else if (trimmed.includes('"') || trimmed.includes("'") || trimmed.includes('`')) {
|
|
160
167
|
color = colors.string;
|
|
@@ -208,7 +215,7 @@
|
|
|
208
215
|
function handleClick(e: MouseEvent) {
|
|
209
216
|
const rect = container.getBoundingClientRect();
|
|
210
217
|
const y = e.clientY - rect.top;
|
|
211
|
-
const effectiveLineHeight = needsScaling ?
|
|
218
|
+
const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
|
|
212
219
|
const line = Math.floor(y / effectiveLineHeight);
|
|
213
220
|
onNavigate?.(Math.max(0, Math.min(line, lines.length - 1)));
|
|
214
221
|
}
|
|
@@ -219,7 +226,7 @@
|
|
|
219
226
|
function handleMouseMove(e: MouseEvent) {
|
|
220
227
|
const rect = container.getBoundingClientRect();
|
|
221
228
|
const y = e.clientY - rect.top;
|
|
222
|
-
const effectiveLineHeight = needsScaling ?
|
|
229
|
+
const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
|
|
223
230
|
hoverLine = Math.floor(y / effectiveLineHeight);
|
|
224
231
|
|
|
225
232
|
if (isDragging) {
|
|
@@ -244,7 +251,7 @@
|
|
|
244
251
|
if (isDragging) {
|
|
245
252
|
const rect = container.getBoundingClientRect();
|
|
246
253
|
const y = Math.max(0, Math.min(e.clientY - rect.top, editorHeight));
|
|
247
|
-
const effectiveLineHeight = needsScaling ?
|
|
254
|
+
const effectiveLineHeight = needsScaling ? editorHeight / lines.length : LINE_HEIGHT;
|
|
248
255
|
const line = Math.floor(y / effectiveLineHeight);
|
|
249
256
|
onNavigate?.(Math.max(0, Math.min(line, lines.length - 1)));
|
|
250
257
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* code transformation preview with rollback capability.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
interface PluginTransform {
|
|
12
11
|
/** Transform ID */
|
|
13
12
|
id: string;
|
|
@@ -387,7 +386,9 @@
|
|
|
387
386
|
{:else}
|
|
388
387
|
<div class="preview-empty">
|
|
389
388
|
<p>Select a plugin to preview its transformation</p>
|
|
390
|
-
<p class="preview-hint">
|
|
389
|
+
<p class="preview-hint">
|
|
390
|
+
Your code will be transformed in this sandbox before applying
|
|
391
|
+
</p>
|
|
391
392
|
</div>
|
|
392
393
|
{/if}
|
|
393
394
|
</div>
|
|
@@ -9,11 +9,7 @@
|
|
|
9
9
|
* - Batch actions
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type {
|
|
13
|
-
DiagnosticsManager,
|
|
14
|
-
Diagnostic,
|
|
15
|
-
DiagnosticSeverity
|
|
16
|
-
} from './core/diagnostics';
|
|
12
|
+
import type { DiagnosticsManager, Diagnostic, DiagnosticSeverity } from './core/diagnostics';
|
|
17
13
|
import {
|
|
18
14
|
getSeverityIcon,
|
|
19
15
|
getSeverityColor,
|
|
@@ -34,13 +30,7 @@
|
|
|
34
30
|
onClose?: () => void;
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
let {
|
|
38
|
-
manager,
|
|
39
|
-
open = true,
|
|
40
|
-
height = 200,
|
|
41
|
-
onNavigate,
|
|
42
|
-
onClose
|
|
43
|
-
}: Props = $props();
|
|
33
|
+
let { manager, open = true, height = 200, onNavigate, onClose }: Props = $props();
|
|
44
34
|
|
|
45
35
|
let allDiagnostics = $state<Map<string, Diagnostic[]>>(new Map());
|
|
46
36
|
let searchQuery = $state('');
|
|
@@ -195,7 +185,7 @@
|
|
|
195
185
|
|
|
196
186
|
<div class="header-filters">
|
|
197
187
|
<!-- Severity filters -->
|
|
198
|
-
{#each
|
|
188
|
+
{#each ['error', 'warning', 'info', 'hint'] as const as severity (severity)}
|
|
199
189
|
{@const count = counts()[severity]}
|
|
200
190
|
<button
|
|
201
191
|
class="severity-filter"
|
|
@@ -211,11 +201,7 @@
|
|
|
211
201
|
{/each}
|
|
212
202
|
|
|
213
203
|
<!-- Source filter -->
|
|
214
|
-
<select
|
|
215
|
-
class="source-filter"
|
|
216
|
-
bind:value={sourceFilter}
|
|
217
|
-
title="Filter by source"
|
|
218
|
-
>
|
|
204
|
+
<select class="source-filter" bind:value={sourceFilter} title="Filter by source">
|
|
219
205
|
<option value={null}>All sources</option>
|
|
220
206
|
{#each allSources() as source (source)}
|
|
221
207
|
<option value={source}>{source}</option>
|
|
@@ -224,12 +210,8 @@
|
|
|
224
210
|
</div>
|
|
225
211
|
|
|
226
212
|
<div class="header-actions">
|
|
227
|
-
<button class="action-btn" onclick={expandAll} title="Expand all">
|
|
228
|
-
|
|
229
|
-
</button>
|
|
230
|
-
<button class="action-btn" onclick={collapseAll} title="Collapse all">
|
|
231
|
-
⊟
|
|
232
|
-
</button>
|
|
213
|
+
<button class="action-btn" onclick={expandAll} title="Expand all"> ⊞ </button>
|
|
214
|
+
<button class="action-btn" onclick={collapseAll} title="Collapse all"> ⊟ </button>
|
|
233
215
|
<button
|
|
234
216
|
class="action-btn"
|
|
235
217
|
class:active={groupByFile}
|
|
@@ -239,9 +221,7 @@
|
|
|
239
221
|
📁
|
|
240
222
|
</button>
|
|
241
223
|
{#if onClose}
|
|
242
|
-
<button class="action-btn close-btn" onclick={onClose} title="Close panel">
|
|
243
|
-
×
|
|
244
|
-
</button>
|
|
224
|
+
<button class="action-btn close-btn" onclick={onClose} title="Close panel"> × </button>
|
|
245
225
|
{/if}
|
|
246
226
|
</div>
|
|
247
227
|
</div>
|
|
@@ -255,9 +235,7 @@
|
|
|
255
235
|
bind:value={searchQuery}
|
|
256
236
|
/>
|
|
257
237
|
{#if searchQuery}
|
|
258
|
-
<button class="search-clear" onclick={() => (searchQuery = '')}>
|
|
259
|
-
×
|
|
260
|
-
</button>
|
|
238
|
+
<button class="search-clear" onclick={() => (searchQuery = '')}> × </button>
|
|
261
239
|
{/if}
|
|
262
240
|
</div>
|
|
263
241
|
|
|
@@ -303,7 +281,8 @@
|
|
|
303
281
|
<span class="diagnostic-code">{diagnostic.code}</span>
|
|
304
282
|
{/if}
|
|
305
283
|
<span class="diagnostic-location">
|
|
306
|
-
[Ln {diagnostic.range.start.line + 1}, Col {diagnostic.range.start.column +
|
|
284
|
+
[Ln {diagnostic.range.start.line + 1}, Col {diagnostic.range.start.column +
|
|
285
|
+
1}]
|
|
307
286
|
</span>
|
|
308
287
|
</button>
|
|
309
288
|
{/each}
|
|
@@ -319,10 +298,7 @@
|
|
|
319
298
|
class="diagnostic-item diagnostic-item--flat"
|
|
320
299
|
onclick={() => handleDiagnosticClick(filePath, diagnostic)}
|
|
321
300
|
>
|
|
322
|
-
<span
|
|
323
|
-
class="diagnostic-icon"
|
|
324
|
-
style="color: {getSeverityColor(diagnostic.severity)};"
|
|
325
|
-
>
|
|
301
|
+
<span class="diagnostic-icon" style="color: {getSeverityColor(diagnostic.severity)};">
|
|
326
302
|
{getSeverityIcon(diagnostic.severity)}
|
|
327
303
|
</span>
|
|
328
304
|
<span class="diagnostic-message">{diagnostic.message}</span>
|
|
@@ -7,14 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { onMount } from 'svelte';
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
CodeAction
|
|
13
|
-
} from './core/quick-actions';
|
|
14
|
-
import {
|
|
15
|
-
groupActionsByKind,
|
|
16
|
-
getKindIcon
|
|
17
|
-
} from './core/quick-actions';
|
|
10
|
+
import type { QuickActionsManager, CodeAction } from './core/quick-actions';
|
|
11
|
+
import { groupActionsByKind, getKindIcon } from './core/quick-actions';
|
|
18
12
|
|
|
19
13
|
interface Props {
|
|
20
14
|
/** Quick actions manager instance */
|
|
@@ -184,11 +178,7 @@
|
|
|
184
178
|
|
|
185
179
|
<!-- Actions menu -->
|
|
186
180
|
{#if isOpen}
|
|
187
|
-
<div
|
|
188
|
-
class="actions-menu"
|
|
189
|
-
role="menu"
|
|
190
|
-
aria-label="Code actions"
|
|
191
|
-
>
|
|
181
|
+
<div class="actions-menu" role="menu" aria-label="Code actions">
|
|
192
182
|
{#each [...groupedActions.entries()] as [groupLabel, groupActions] (groupLabel)}
|
|
193
183
|
<div class="action-group">
|
|
194
184
|
<div class="group-header">
|
|
@@ -265,7 +255,8 @@
|
|
|
265
255
|
}
|
|
266
256
|
|
|
267
257
|
@keyframes pulse {
|
|
268
|
-
0%,
|
|
258
|
+
0%,
|
|
259
|
+
100% {
|
|
269
260
|
box-shadow: 0 0 0 0 rgba(249, 230, 79, 0.4);
|
|
270
261
|
}
|
|
271
262
|
50% {
|
|
@@ -21,13 +21,7 @@
|
|
|
21
21
|
onClose?: () => void;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
let {
|
|
25
|
-
manager,
|
|
26
|
-
language = 'javascript',
|
|
27
|
-
open = false,
|
|
28
|
-
onSelect,
|
|
29
|
-
onClose
|
|
30
|
-
}: Props = $props();
|
|
24
|
+
let { manager, language = 'javascript', open = false, onSelect, onClose }: Props = $props();
|
|
31
25
|
|
|
32
26
|
let searchQuery = $state('');
|
|
33
27
|
let selectedCategory = $state<string | null>(null);
|
|
@@ -163,7 +157,14 @@
|
|
|
163
157
|
<div class="snippet-backdrop" onclick={() => onClose?.()} role="presentation"></div>
|
|
164
158
|
|
|
165
159
|
<!-- Palette -->
|
|
166
|
-
<div
|
|
160
|
+
<div
|
|
161
|
+
class="snippet-palette"
|
|
162
|
+
onkeydown={handleKeyDown}
|
|
163
|
+
role="dialog"
|
|
164
|
+
tabindex={-1}
|
|
165
|
+
aria-modal="true"
|
|
166
|
+
aria-label="Snippet palette"
|
|
167
|
+
>
|
|
167
168
|
<div class="snippet-header">
|
|
168
169
|
<input
|
|
169
170
|
bind:this={searchInput}
|
|
@@ -232,7 +233,7 @@
|
|
|
232
233
|
|
|
233
234
|
<div class="preview-body">
|
|
234
235
|
<!-- eslint-disable-next-line svelte/no-at-html-tags — content is escaped by escapeHtml() before placeholder spans are added -->
|
|
235
|
-
|
|
236
|
+
<pre>{@html getPreview(snippet.body)}</pre>
|
|
236
237
|
</div>
|
|
237
238
|
|
|
238
239
|
<div class="preview-footer">
|
|
@@ -248,9 +249,7 @@
|
|
|
248
249
|
</div>
|
|
249
250
|
{/if}
|
|
250
251
|
{:else}
|
|
251
|
-
<div class="preview-empty">
|
|
252
|
-
Select a snippet to preview
|
|
253
|
-
</div>
|
|
252
|
+
<div class="preview-empty">Select a snippet to preview</div>
|
|
254
253
|
{/if}
|
|
255
254
|
</div>
|
|
256
255
|
</div>
|
|
@@ -243,7 +243,8 @@
|
|
|
243
243
|
<button
|
|
244
244
|
class="structure-map__node"
|
|
245
245
|
class:structure-map__node--active={isCursorInNode(node)}
|
|
246
|
-
class:structure-map__node--complex={node.metrics.complexity &&
|
|
246
|
+
class:structure-map__node--complex={node.metrics.complexity &&
|
|
247
|
+
node.metrics.complexity >= 50}
|
|
247
248
|
style="
|
|
248
249
|
top: {getNodeTop(node)}%;
|
|
249
250
|
height: {Math.max(1, getNodeHeight(node))}%;
|