@nocturnium/svelte-ide 1.0.6 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,13 +17,27 @@
17
17
  lineHeight: number;
18
18
  /** Gutter width in pixels */
19
19
  gutterWidth?: number;
20
+ /**
21
+ * Full height of the scrollable content in pixels. The layer must span the
22
+ * whole document (not just one viewport) or indicators below the first
23
+ * screenful get clipped — which previously hid every high-complexity region
24
+ * that sat past the initial view.
25
+ */
26
+ totalHeight?: number;
20
27
  /** Minimum score to show indicators (default: 50) */
21
28
  minScore?: number;
22
29
  /** Whether indicators are enabled */
23
30
  enabled?: boolean;
24
31
  }
25
32
 
26
- let { metrics, lineHeight, gutterWidth = 50, minScore = 50, enabled = true }: Props = $props();
33
+ let {
34
+ metrics,
35
+ lineHeight,
36
+ gutterWidth = 50,
37
+ totalHeight = 0,
38
+ minScore = 50,
39
+ enabled = true
40
+ }: Props = $props();
27
41
 
28
42
  // Filter regions that exceed the threshold
29
43
  let highlightedRegions = $derived(
@@ -116,7 +130,11 @@
116
130
  </script>
117
131
 
118
132
  {#if enabled && lineScores.size > 0}
119
- <div class="complexity-gutter" aria-hidden="true" style="width: {gutterWidth}px;">
133
+ <div
134
+ class="complexity-gutter"
135
+ aria-hidden="true"
136
+ style="width: {gutterWidth}px;{totalHeight ? ` height: ${totalHeight}px;` : ''}"
137
+ >
120
138
  {#each [...lineScores.entries()] as [line, data] (line)}
121
139
  <div
122
140
  class="complexity-gutter__indicator"
@@ -176,10 +194,11 @@
176
194
  position: absolute;
177
195
  top: 0;
178
196
  left: 0;
197
+ /* Height is set inline to the full content height so indicators below the
198
+ first viewport are not clipped. Fall back to the viewport when unknown. */
179
199
  bottom: 0;
180
200
  pointer-events: none;
181
201
  z-index: 5;
182
- overflow: hidden;
183
202
  }
184
203
 
185
204
  .complexity-gutter__indicator {
@@ -6,6 +6,13 @@ interface Props {
6
6
  lineHeight: number;
7
7
  /** Gutter width in pixels */
8
8
  gutterWidth?: number;
9
+ /**
10
+ * Full height of the scrollable content in pixels. The layer must span the
11
+ * whole document (not just one viewport) or indicators below the first
12
+ * screenful get clipped — which previously hid every high-complexity region
13
+ * that sat past the initial view.
14
+ */
15
+ totalHeight?: number;
9
16
  /** Minimum score to show indicators (default: 50) */
10
17
  minScore?: number;
11
18
  /** Whether indicators are enabled */
@@ -510,10 +510,22 @@
510
510
  if (!folding || !editorState) return;
511
511
  foldManager.expandAll();
512
512
  const analyzer = getSemanticAnalyzer();
513
- const regions = analyzer.analyze(editorState.lines, language);
514
- for (const category of preset.hide) {
515
- for (const region of analyzer.getByCategory(regions, category)) {
516
- foldManager.collapse(region.startLine);
513
+ const semanticRegions = analyzer.analyze(editorState.lines, language);
514
+ const foldRegions = foldManager.getRegions();
515
+
516
+ // Semantic region boundaries don't always line up exactly with bracket-fold
517
+ // headers — a function's foldable `{` can sit a line below its doc-commented
518
+ // header, and a hidden category (e.g. `private`, `error-handling`) may cover
519
+ // several nested blocks. So collapse every foldable block whose header falls
520
+ // within a hidden semantic region's range, rather than requiring an exact
521
+ // header-line match (which silently folded almost nothing). collapse() is
522
+ // idempotent, so collapsing the same header twice is harmless.
523
+ for (const region of semanticRegions) {
524
+ if (!preset.hide.includes(region.category)) continue;
525
+ for (const fold of foldRegions) {
526
+ if (fold.startLine >= region.startLine && fold.startLine <= region.endLine) {
527
+ foldManager.collapse(fold.startLine);
528
+ }
517
529
  }
518
530
  }
519
531
  }
@@ -993,6 +1005,7 @@
993
1005
  metrics={complexityMetrics}
994
1006
  {lineHeight}
995
1007
  {gutterWidth}
1008
+ totalHeight={totalContentHeight}
996
1009
  minScore={complexityThreshold}
997
1010
  enabled={complexityHighlighting}
998
1011
  />
@@ -18,7 +18,7 @@ export interface FoldRegion {
18
18
  /** Indentation level (for nested folds) */
19
19
  level: number;
20
20
  /** Type of fold region */
21
- type: 'bracket' | 'indentation' | 'region' | 'comment';
21
+ type: 'bracket' | 'indentation' | 'region' | 'comment' | 'import';
22
22
  /** Whether this region is currently collapsed */
23
23
  collapsed: boolean;
24
24
  }
@@ -34,12 +34,11 @@ export interface FoldingConfig {
34
34
  regions: boolean;
35
35
  /** Enable multi-line comment folding */
36
36
  comments: boolean;
37
+ /** Enable folding a run of consecutive import statements */
38
+ imports: boolean;
37
39
  /** Minimum lines for a foldable region */
38
40
  minLines: number;
39
41
  }
40
- /**
41
- * Detect all fold regions in a document
42
- */
43
42
  export declare function detectFoldRegions(lines: readonly Line[], language?: string, config?: Partial<FoldingConfig>): FoldRegion[];
44
43
  /**
45
44
  * Fold state manager
@@ -11,6 +11,7 @@ const DEFAULT_CONFIG = {
11
11
  indentation: true,
12
12
  regions: true,
13
13
  comments: true,
14
+ imports: true,
14
15
  minLines: 2
15
16
  };
16
17
  /**
@@ -424,6 +425,38 @@ function detectCommentFolds(lines, minLines = 2) {
424
425
  /**
425
426
  * Detect all fold regions in a document
426
427
  */
428
+ /**
429
+ * Detect a fold region for each run of consecutive import statements. Import
430
+ * groups have no braces of their own, so without this an editor (and the
431
+ * "fold imports" semantic preset) can't collapse them.
432
+ */
433
+ function detectImportFolds(lines, minLines = 2) {
434
+ const regions = [];
435
+ // ES `import ... from`, side-effect `import '...'`, and Python `from x import`.
436
+ const isImportLine = (text) => /^\s*import\b/.test(text) || /^\s*from\s+\S+\s+import\b/.test(text);
437
+ let start = -1;
438
+ for (let i = 0; i <= lines.length; i++) {
439
+ const isImport = i < lines.length && isImportLine(lines[i].text);
440
+ if (isImport) {
441
+ if (start === -1)
442
+ start = i;
443
+ }
444
+ else if (start !== -1) {
445
+ const end = i - 1;
446
+ if (end - start + 1 >= minLines) {
447
+ regions.push({
448
+ startLine: start,
449
+ endLine: end,
450
+ level: 0,
451
+ type: 'import',
452
+ collapsed: false
453
+ });
454
+ }
455
+ start = -1;
456
+ }
457
+ }
458
+ return regions;
459
+ }
427
460
  export function detectFoldRegions(lines, language = 'plaintext', config = {}) {
428
461
  const cfg = { ...DEFAULT_CONFIG, ...config };
429
462
  const allRegions = [];
@@ -459,6 +492,9 @@ export function detectFoldRegions(lines, language = 'plaintext', config = {}) {
459
492
  if (cfg.comments) {
460
493
  allRegions.push(...detectCommentFolds(lines, cfg.minLines));
461
494
  }
495
+ if (cfg.imports) {
496
+ allRegions.push(...detectImportFolds(lines, cfg.minLines));
497
+ }
462
498
  // Sort by start line, then by length (longer regions first for nesting)
463
499
  allRegions.sort((a, b) => {
464
500
  if (a.startLine !== b.startLine) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocturnium/svelte-ide",
3
- "version": "1.0.6",
3
+ "version": "1.1.1",
4
4
  "description": "Svelte 5 code editor and IDE building blocks — custom editor, syntax highlighting, code folding, multi-cursor, LSP client, and optional realtime collaboration.",
5
5
  "author": "Nocturnium & Jordan Dziat <hello@nocturnium.ai> (https://nocturnium.ai)",
6
6
  "license": "MIT",