@softwarity/geojson-editor 1.0.23 → 1.0.25

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.
@@ -11,10 +11,15 @@
11
11
  }
12
12
 
13
13
  :host {
14
+ /* Color scheme - inherits from parent, falls back to system preference if not set */
15
+ color-scheme: inherit;
16
+
17
+ /* Layout variables (internal only) */
14
18
  --line-height: 19.5px;
15
19
  --gutter-width: 50px;
16
20
  --editor-padding-y: 8px;
17
21
  --editor-padding-x: 12px;
22
+
18
23
  display: flex;
19
24
  flex-direction: column;
20
25
  position: relative;
@@ -39,7 +44,7 @@
39
44
  position: relative;
40
45
  width: 100%;
41
46
  flex: 1;
42
- background: var(--bg-color, #fff);
47
+ background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
43
48
  display: flex;
44
49
  overflow: hidden;
45
50
  }
@@ -47,8 +52,8 @@
47
52
  /* ========== Gutter ========== */
48
53
  .gutter {
49
54
  width: var(--gutter-width);
50
- background: var(--gutter-bg, #f0f0f0);
51
- border-right: 1px solid var(--gutter-border, #e0e0e0);
55
+ background: var(--geojson-editor-gutter-bg, light-dark(#f0f0f0, #313335));
56
+ border-right: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
52
57
  overflow: hidden;
53
58
  flex-shrink: 0;
54
59
  position: relative;
@@ -90,12 +95,12 @@
90
95
  top: 0;
91
96
  bottom: 0;
92
97
  width: 3px;
93
- background: var(--error-color, #dc3545);
98
+ background: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
94
99
  }
95
100
 
96
101
  .line-number {
97
102
  font-size: 11px;
98
- color: var(--gutter-text, #999);
103
+ color: var(--geojson-editor-gutter-text, light-dark(#999, #606366));
99
104
  user-select: none;
100
105
  min-width: 20px;
101
106
  text-align: right;
@@ -118,7 +123,7 @@
118
123
  flex-shrink: 0;
119
124
  background: transparent;
120
125
  border: none;
121
- color: var(--json-punct, #a9b7c6);
126
+ color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
122
127
  font-size: 10px;
123
128
  display: flex;
124
129
  align-items: center;
@@ -178,7 +183,7 @@
178
183
  padding: var(--editor-padding-y) var(--editor-padding-x);
179
184
  overscroll-behavior: contain; /* Prevent scroll chaining to parent */
180
185
  scrollbar-width: thin;
181
- scrollbar-color: var(--control-border, #c0c0c0) var(--control-bg, #e8e8e8);
186
+ scrollbar-color: var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a)) var(--geojson-editor-control-bg, light-dark(#e8e8e8, #3c3f41));
182
187
  }
183
188
 
184
189
  /* Scroll content - defines total scrollable height */
@@ -209,7 +214,7 @@
209
214
  position: absolute;
210
215
  width: 2px;
211
216
  height: 1em;
212
- background: var(--caret-color, #000);
217
+ background: var(--geojson-editor-caret-color, light-dark(#000, #bbb));
213
218
  top: 0.15em;
214
219
  pointer-events: none;
215
220
  animation: cursor-blink 1s step-end infinite;
@@ -237,7 +242,7 @@
237
242
  position: absolute;
238
243
  height: 100%;
239
244
  top: 0;
240
- background: var(--selection-color, rgba(51, 153, 255, 0.3));
245
+ background: var(--geojson-editor-selection-color, light-dark(rgba(51, 153, 255, 0.3), rgba(51, 153, 255, 0.4)));
241
246
  pointer-events: none;
242
247
  z-index: 0;
243
248
  }
@@ -259,47 +264,47 @@
259
264
  }
260
265
 
261
266
  /* ========== Syntax Highlighting ========== */
262
- .json-key { color: var(--json-key, #660e7a); }
263
- .json-string { color: var(--json-string, #008000); }
264
- .json-number { color: var(--json-number, #00f); }
265
- .json-boolean, .json-null { color: var(--json-boolean, #000080); }
266
- .json-punctuation { color: var(--json-punct, #000); }
267
- .json-error { color: var(--json-error, #f00); }
267
+ .json-key { color: var(--geojson-editor-json-key, light-dark(#660e7a, #9876aa)); }
268
+ .json-string { color: var(--geojson-editor-json-string, light-dark(#008000, #6a8759)); }
269
+ .json-number { color: var(--geojson-editor-json-number, light-dark(#00f, #6897bb)); }
270
+ .json-boolean, .json-null { color: var(--geojson-editor-json-boolean, light-dark(#000080, #cc7832)); }
271
+ .json-punctuation { color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6)); }
272
+ .json-error { color: var(--geojson-editor-json-error, light-dark(#f00, #ff6b68)); }
268
273
 
269
- .geojson-key { color: var(--geojson-key, #660e7a); font-weight: 600; }
270
- .geojson-type { color: var(--geojson-type, #008000); font-weight: 600; }
271
- .geojson-type-invalid { color: var(--geojson-type-invalid, #f00); font-weight: 600; }
274
+ .geojson-key { color: var(--geojson-editor-geojson-key, light-dark(#660e7a, #9876aa)); font-weight: 600; }
275
+ .geojson-type { color: var(--geojson-editor-geojson-type, light-dark(#008000, #6a8759)); font-weight: 600; }
276
+ .geojson-type-invalid { color: var(--geojson-editor-geojson-type-invalid, light-dark(#f00, #ff6b68)); font-weight: 600; }
272
277
 
273
278
  /* Collapsed node styling */
274
279
  .collapsed-bracket-array,
275
280
  .collapsed-bracket-object {
276
- color: var(--json-punct, #000);
281
+ color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
277
282
  }
278
283
  .collapsed-bracket-array::after,
279
284
  .collapsed-bracket-object::after {
280
285
  content: '…';
281
- color: var(--json-punct, #888);
286
+ color: var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
282
287
  }
283
288
 
284
289
  /* ========== Prefix/Suffix ========== */
285
290
  .prefix-wrapper, .suffix-wrapper {
286
291
  display: flex;
287
292
  flex-shrink: 0;
288
- background: var(--bg-color, #fff);
293
+ background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
289
294
  }
290
295
 
291
296
  .prefix-gutter, .suffix-gutter {
292
297
  width: var(--gutter-width);
293
- background: var(--gutter-bg, #f0f0f0);
294
- border-right: 1px solid var(--gutter-border, #e0e0e0);
298
+ background: var(--geojson-editor-gutter-bg, light-dark(#f0f0f0, #313335));
299
+ border-right: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
295
300
  flex-shrink: 0;
296
301
  }
297
302
 
298
303
  .editor-prefix, .editor-suffix {
299
304
  flex: 1;
300
305
  padding: 4px 12px;
301
- color: var(--text-color, #000);
302
- background: var(--bg-color, #fff);
306
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
307
+ background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
303
308
  user-select: none;
304
309
  white-space: pre-wrap;
305
310
  word-wrap: break-word;
@@ -316,7 +321,7 @@
316
321
  transform: translateY(-50%);
317
322
  background: transparent;
318
323
  border: none;
319
- color: var(--text-color, #000);
324
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
320
325
  opacity: 0.15;
321
326
  cursor: pointer;
322
327
  font-size: 0.7rem;
@@ -339,8 +344,8 @@
339
344
  top: 2rem;
340
345
  right: 0.5rem;
341
346
  z-index: 1000;
342
- background: var(--bg-color, #ffffff);
343
- border: 1px solid var(--gutter-border, #e0e0e0);
347
+ background: var(--geojson-editor-bg-color, light-dark(#fff, #2b2b2b));
348
+ border: 1px solid var(--geojson-editor-gutter-border, light-dark(#e0e0e0, #3c3f41));
344
349
  border-radius: 6px;
345
350
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
346
351
  padding: 12px 16px;
@@ -356,13 +361,13 @@
356
361
  .info-popup-title {
357
362
  font-weight: bold;
358
363
  font-size: 13px;
359
- color: var(--text-color, #000000);
364
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
360
365
  margin-bottom: 4px;
361
366
  }
362
367
  .info-popup-version {
363
368
  display: block;
364
369
  font-size: 11px;
365
- color: var(--text-color, #000000);
370
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
366
371
  opacity: 0.6;
367
372
  margin-bottom: 8px;
368
373
  text-decoration: none;
@@ -373,7 +378,7 @@
373
378
  }
374
379
  .info-popup-copyright {
375
380
  font-size: 10px;
376
- color: var(--text-color, #000000);
381
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
377
382
  opacity: 0.5;
378
383
  }
379
384
 
@@ -384,7 +389,7 @@
384
389
  transform: translateY(-50%);
385
390
  background: transparent;
386
391
  border: none;
387
- color: var(--text-color, #000);
392
+ color: var(--geojson-editor-text-color, light-dark(#000, #a9b7c6));
388
393
  opacity: 0.3;
389
394
  cursor: pointer;
390
395
  font-size: 0.65rem;
@@ -413,7 +418,7 @@
413
418
  .error-nav-btn {
414
419
  background: transparent;
415
420
  border: none;
416
- color: var(--error-color, #dc3545);
421
+ color: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
417
422
  cursor: pointer;
418
423
  font-size: 8px;
419
424
  width: 16px;
@@ -429,7 +434,7 @@
429
434
  opacity: 1;
430
435
  }
431
436
  .error-count {
432
- color: var(--error-color, #dc3545);
437
+ color: var(--geojson-editor-error-color, light-dark(#dc3545, #ff6b68));
433
438
  font-size: 11px;
434
439
  min-width: 20px;
435
440
  text-align: center;
@@ -437,9 +442,9 @@
437
442
 
438
443
  /* ========== Scrollbar ========== */
439
444
  .viewport::-webkit-scrollbar { width: 10px; height: 10px; }
440
- .viewport::-webkit-scrollbar-track { background: var(--control-bg, #e8e8e8); }
441
- .viewport::-webkit-scrollbar-thumb { background: var(--control-border, #c0c0c0); border-radius: 5px; }
442
- .viewport::-webkit-scrollbar-thumb:hover { background: var(--control-color, #000080); }
445
+ .viewport::-webkit-scrollbar-track { background: var(--geojson-editor-control-bg, light-dark(#e8e8e8, #3c3f41)); }
446
+ .viewport::-webkit-scrollbar-thumb { background: var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a)); border-radius: 5px; }
447
+ .viewport::-webkit-scrollbar-thumb:hover { background: var(--geojson-editor-control-color, light-dark(#000080, #cc7832)); }
443
448
 
444
449
  /* ========== Inline Controls (using ::before pseudo-elements) ========== */
445
450
 
@@ -464,24 +469,24 @@
464
469
  .json-color:hover::before,
465
470
  .json-boolean:hover::before {
466
471
  transform: translateY(-50%) scale(1.2);
467
- border-color: var(--control-color, #000080);
472
+ border-color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
468
473
  }
469
474
 
470
475
  /* Color swatch specific styles */
471
476
  .json-color::before {
472
477
  background-color: var(--swatch-color);
473
- border: 1px solid var(--json-punct, #a9b7c6);
478
+ border: 1px solid var(--geojson-editor-json-punct, light-dark(#000, #a9b7c6));
474
479
  }
475
480
 
476
481
  /* Boolean checkbox specific styles */
477
482
  .json-boolean::before {
478
- border: 1.5px solid var(--control-border, #c0c0c0);
483
+ border: 1.5px solid var(--geojson-editor-control-border, light-dark(#c0c0c0, #5a5a5a));
479
484
  background: transparent;
480
485
  }
481
486
  .json-bool-true::before {
482
487
  content: '✔';
483
- border-color: var(--control-color, #000080);
484
- color: var(--control-color, #000080);
488
+ border-color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
489
+ color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
485
490
  font-size: 8px;
486
491
  display: flex;
487
492
  align-items: center;
@@ -496,7 +501,7 @@
496
501
  left: 0;
497
502
  top: 3px;
498
503
  font-size: 10px;
499
- color: var(--control-color, #000080);
504
+ color: var(--geojson-editor-control-color, light-dark(#000080, #cc7832));
500
505
  opacity: 0.6;
501
506
  cursor: pointer;
502
507
  z-index: 1;
@@ -4,8 +4,7 @@ import type { Feature } from 'geojson';
4
4
 
5
5
  // ========== Imports from extracted modules ==========
6
6
  import type {
7
- SetOptions,
8
- ThemeSettings
7
+ SetOptions
9
8
  } from './types.js';
10
9
 
11
10
  import type {
@@ -35,19 +34,18 @@ import {
35
34
  RE_BRACKET_POS,
36
35
  RE_IS_WORD_CHAR,
37
36
  RE_ATTR_AND_BOOL_VALUE,
38
- RE_TO_KEBAB,
39
37
  RE_OPEN_BRACES,
40
38
  RE_CLOSE_BRACES,
41
39
  RE_OPEN_BRACKETS,
42
40
  RE_CLOSE_BRACKET
43
41
  } from './constants.js';
44
42
 
45
- import { createElement, countBrackets, parseSelectorToHostRule } from './utils.js';
43
+ import { createElement, countBrackets } from './utils.js';
46
44
  import { validateGeoJSON, normalizeToFeatures } from './validation.js';
47
45
  import { highlightSyntax, namedColorToHex, isNamedColor } from './syntax-highlighter.js';
48
46
 
49
47
  // Re-export public types
50
- export type { SetOptions, ThemeConfig, ThemeSettings } from './types.js';
48
+ export type { SetOptions } from './types.js';
51
49
 
52
50
  // Alias for minification
53
51
  const _ce = createElement;
@@ -94,8 +92,6 @@ class GeoJsonEditor extends HTMLElement {
94
92
  private renderTimer: ReturnType<typeof setTimeout> | undefined = undefined;
95
93
  private inputTimer: ReturnType<typeof setTimeout> | undefined = undefined;
96
94
 
97
- // ========== Theme ==========
98
- themes: ThemeSettings = { dark: {}, light: {} };
99
95
 
100
96
  // ========== Undo/Redo History ==========
101
97
  private _undoStack: EditorSnapshot[] = [];
@@ -116,6 +112,7 @@ class GeoJsonEditor extends HTMLElement {
116
112
  private _contextMapFirstLine: string | undefined = undefined;
117
113
  private _contextMapLastLine: string | undefined = undefined;
118
114
  private _errorLinesCache: Set<number> | null = null;
115
+ private _lastCursorFeatureIndex: number | null = null; // For current-feature event deduplication
119
116
 
120
117
  // ========== Cached DOM Elements ==========
121
118
  private _viewport: HTMLElement | null = null;
@@ -440,7 +437,7 @@ class GeoJsonEditor extends HTMLElement {
440
437
 
441
438
  // ========== Observed Attributes ==========
442
439
  static get observedAttributes() {
443
- return ['readonly', 'value', 'placeholder', 'dark-selector', 'internal-add-shortcut'];
440
+ return ['readonly', 'value', 'placeholder', 'internal-add-shortcut'];
444
441
  }
445
442
 
446
443
  // ========== Lifecycle ==========
@@ -449,7 +446,6 @@ class GeoJsonEditor extends HTMLElement {
449
446
  this._cacheElements();
450
447
  this.setupEventListeners();
451
448
  this.updatePrefixSuffix();
452
- this.updateThemeCSS();
453
449
 
454
450
  if (this.value) {
455
451
  this.setValue(this.value);
@@ -484,9 +480,6 @@ class GeoJsonEditor extends HTMLElement {
484
480
  case 'placeholder':
485
481
  this.updatePlaceholderContent();
486
482
  break;
487
- case 'dark-selector':
488
- this.updateThemeCSS();
489
- break;
490
483
  }
491
484
  }
492
485
 
@@ -697,12 +690,16 @@ class GeoJsonEditor extends HTMLElement {
697
690
  editorWrapper.classList.add('focused');
698
691
  this._invalidateRenderCache(); // Force re-render to show cursor
699
692
  this.scheduleRender();
693
+ // Emit current feature on focus (force to always emit on focus gain)
694
+ this._emitCurrentFeature(true);
700
695
  });
701
696
 
702
697
  hiddenTextarea.addEventListener('blur', () => {
703
698
  editorWrapper.classList.remove('focused');
704
699
  this._invalidateRenderCache(); // Force re-render to hide cursor
705
700
  this.scheduleRender();
701
+ // Emit null on blur
702
+ this._emitCurrentFeatureNull();
706
703
  });
707
704
 
708
705
  // Scroll handling
@@ -1324,11 +1321,16 @@ class GeoJsonEditor extends HTMLElement {
1324
1321
 
1325
1322
  linesContainer.innerHTML = '';
1326
1323
  linesContainer.appendChild(fragment);
1327
-
1324
+
1328
1325
  // Render gutter with same range
1329
1326
  this.renderGutter(startIndex, endIndex);
1327
+
1328
+ // Emit current-feature event if feature changed (only when editor is focused)
1329
+ if (this._editorWrapper?.classList.contains('focused')) {
1330
+ this._emitCurrentFeature();
1331
+ }
1330
1332
  }
1331
-
1333
+
1332
1334
  /**
1333
1335
  * Insert cursor element at the specified column position
1334
1336
  * Uses absolute positioning to avoid affecting text layout
@@ -2757,7 +2759,14 @@ class GeoJsonEditor extends HTMLElement {
2757
2759
  // Try to parse as GeoJSON and normalize
2758
2760
  let pastedFeatureCount = 0;
2759
2761
  try {
2760
- const parsed = JSON.parse(text);
2762
+ // First try direct parse (single Feature, Feature[], or FeatureCollection)
2763
+ let parsed;
2764
+ try {
2765
+ parsed = JSON.parse(text);
2766
+ } catch {
2767
+ // If direct parse fails, try wrapping with [] (for "feature, feature" format from editor copy)
2768
+ parsed = JSON.parse('[' + text + ']');
2769
+ }
2761
2770
  const features = normalizeToFeatures(parsed);
2762
2771
  pastedFeatureCount = features.length;
2763
2772
  // Valid GeoJSON - insert formatted features
@@ -2780,13 +2789,11 @@ class GeoJsonEditor extends HTMLElement {
2780
2789
  if (pastedFeatureCount > 0) {
2781
2790
  // Restore collapsed state for existing features and collapse new features' coordinates
2782
2791
  const ranges = this._findCollapsibleRanges();
2783
- const featureRanges = ranges.filter(r => r.isRootFeature);
2784
2792
 
2785
2793
  for (const range of ranges) {
2786
- // Find which feature this range belongs to
2787
- const featureIndex = featureRanges.findIndex(fr =>
2788
- range.startLine >= fr.startLine && range.endLine <= fr.endLine
2789
- );
2794
+ // Find which feature this range belongs to using the correct featureRanges map
2795
+ const featureIndex = this._getFeatureIndexForLine(range.startLine);
2796
+ if (featureIndex === -1) continue;
2790
2797
 
2791
2798
  if (featureIndex < existingFeatureCount) {
2792
2799
  // Existing feature - restore collapsed state
@@ -3078,18 +3085,15 @@ class GeoJsonEditor extends HTMLElement {
3078
3085
  private _applyCollapsedOption(collapsed: string[] | ((feature: Feature | null, index: number) => string[]), features: Feature[] | null = null): void {
3079
3086
  const ranges = this._findCollapsibleRanges();
3080
3087
 
3081
- // Group ranges by feature (root nodes)
3082
- const featureRanges = ranges.filter(r => r.isRootFeature);
3083
-
3084
3088
  // Determine which attributes to collapse per feature
3085
3089
  for (const range of ranges) {
3086
3090
  let shouldCollapse = false;
3087
3091
 
3088
3092
  if (typeof collapsed === 'function') {
3089
- // Find which feature this range belongs to
3090
- const featureIndex = featureRanges.findIndex(fr =>
3091
- range.startLine >= fr.startLine && range.endLine <= fr.endLine
3092
- );
3093
+ // Find which feature this range belongs to using the correct featureRanges map
3094
+ const featureIndex = this._getFeatureIndexForLine(range.startLine);
3095
+ if (featureIndex === -1) continue;
3096
+
3093
3097
  const feature = features?.[featureIndex] || null;
3094
3098
  const collapsedAttrs = collapsed(feature, featureIndex);
3095
3099
 
@@ -3537,6 +3541,51 @@ class GeoJsonEditor extends HTMLElement {
3537
3541
  }
3538
3542
  }
3539
3543
 
3544
+ /**
3545
+ * Emit current-feature event when cursor moves to a different feature
3546
+ * Only emits when the feature changes (not on every cursor move)
3547
+ * @param force - If true, emit even if feature hasn't changed (used on focus)
3548
+ */
3549
+ private _emitCurrentFeature(force: boolean = false): void {
3550
+ const featureIndex = this._getFeatureIndexForLine(this.cursorLine);
3551
+
3552
+ // Only emit if feature changed (unless forced)
3553
+ if (!force && featureIndex === this._lastCursorFeatureIndex) return;
3554
+ this._lastCursorFeatureIndex = featureIndex;
3555
+
3556
+ if (featureIndex === -1) {
3557
+ // Cursor is not in a feature
3558
+ this.dispatchEvent(new CustomEvent('current-feature', {
3559
+ detail: null,
3560
+ bubbles: true,
3561
+ composed: true
3562
+ }));
3563
+ } else {
3564
+ // Get the feature at cursor
3565
+ const features = this._parseFeatures();
3566
+ const feature = features[featureIndex] || null;
3567
+
3568
+ this.dispatchEvent(new CustomEvent('current-feature', {
3569
+ detail: feature,
3570
+ bubbles: true,
3571
+ composed: true
3572
+ }));
3573
+ }
3574
+ }
3575
+
3576
+ /**
3577
+ * Emit current-feature with null (used on blur)
3578
+ * Always emits to ensure map is cleared when editor loses focus
3579
+ */
3580
+ private _emitCurrentFeatureNull(): void {
3581
+ this._lastCursorFeatureIndex = null;
3582
+ this.dispatchEvent(new CustomEvent('current-feature', {
3583
+ detail: null,
3584
+ bubbles: true,
3585
+ composed: true
3586
+ }));
3587
+ }
3588
+
3540
3589
  // ========== UI Updates ==========
3541
3590
 
3542
3591
  updateReadonly() {
@@ -3578,74 +3627,6 @@ class GeoJsonEditor extends HTMLElement {
3578
3627
  if (this._editorSuffix) this._editorSuffix.textContent = this.suffix;
3579
3628
  }
3580
3629
 
3581
- // ========== Theme ==========
3582
-
3583
- updateThemeCSS() {
3584
- const darkSelector = this.getAttribute('dark-selector') || '.dark';
3585
- const darkRule = parseSelectorToHostRule(darkSelector);
3586
-
3587
- let themeStyle = this._id('theme-styles') as HTMLStyleElement;
3588
- if (!themeStyle) {
3589
- themeStyle = _ce('style') as HTMLStyleElement;
3590
- themeStyle.id = 'theme-styles';
3591
- this.shadowRoot!.insertBefore(themeStyle, this.shadowRoot!.firstChild);
3592
- }
3593
-
3594
- const darkDefaults = {
3595
- bgColor: '#2b2b2b',
3596
- textColor: '#a9b7c6',
3597
- caretColor: '#bbb',
3598
- gutterBg: '#313335',
3599
- gutterBorder: '#3c3f41',
3600
- gutterText: '#606366',
3601
- jsonKey: '#9876aa',
3602
- jsonString: '#6a8759',
3603
- jsonNumber: '#6897bb',
3604
- jsonBoolean: '#cc7832',
3605
- jsonNull: '#cc7832',
3606
- jsonPunct: '#a9b7c6',
3607
- jsonError: '#ff6b68',
3608
- controlColor: '#cc7832',
3609
- controlBg: '#3c3f41',
3610
- controlBorder: '#5a5a5a',
3611
- geojsonKey: '#9876aa',
3612
- geojsonType: '#6a8759',
3613
- geojsonTypeInvalid: '#ff6b68',
3614
- jsonKeyInvalid: '#ff6b68'
3615
- };
3616
-
3617
- RE_TO_KEBAB.lastIndex = 0;
3618
- const toKebab = (str: string) => str.replace(RE_TO_KEBAB, '-$1').toLowerCase();
3619
- const generateVars = (obj: Record<string, string | undefined>) => Object.entries(obj)
3620
- .filter((entry): entry is [string, string] => entry[1] !== undefined)
3621
- .map(([k, v]) => `--${toKebab(k)}: ${v};`)
3622
- .join('\n ');
3623
-
3624
- const lightVars = generateVars(this.themes.light as Record<string, string | undefined> || {});
3625
- const darkTheme = { ...darkDefaults, ...this.themes.dark };
3626
- const darkVars = generateVars(darkTheme as Record<string, string | undefined>);
3627
-
3628
- let css = lightVars ? `:host {\n ${lightVars}\n }\n` : '';
3629
- css += `${darkRule} {\n ${darkVars}\n }`;
3630
-
3631
- themeStyle.textContent = css;
3632
- }
3633
-
3634
- setTheme(theme: ThemeSettings): void {
3635
- if (theme.dark) this.themes.dark = { ...this.themes.dark, ...theme.dark };
3636
- if (theme.light) this.themes.light = { ...this.themes.light, ...theme.light };
3637
- this.updateThemeCSS();
3638
- }
3639
-
3640
- resetTheme(): void {
3641
- this.themes = { dark: {}, light: {} };
3642
- this.updateThemeCSS();
3643
- }
3644
-
3645
- getTheme(): ThemeSettings {
3646
- return { ...this.themes };
3647
- }
3648
-
3649
3630
  /**
3650
3631
  * Find all collapsible ranges using the mappings built by _rebuildNodeIdMappings
3651
3632
  * This method only READS the existing mappings, it doesn't create new IDs
@@ -3863,19 +3844,17 @@ class GeoJsonEditor extends HTMLElement {
3863
3844
 
3864
3845
  // Restore collapsed state for existing features
3865
3846
  const ranges = this._findCollapsibleRanges();
3866
- const featureRanges = ranges.filter(r => r.isRootFeature);
3867
3847
  const actualNewCount = newCount !== undefined ? newCount : features.length - newStartIndex;
3868
3848
 
3869
3849
  for (const range of ranges) {
3870
- // Find which feature this range belongs to
3871
- const featureIndex = featureRanges.findIndex(fr =>
3872
- range.startLine >= fr.startLine && range.endLine <= fr.endLine
3873
- );
3850
+ // Find which feature this range belongs to using the correct featureRanges map
3851
+ const featureIndex = this._getFeatureIndexForLine(range.startLine);
3852
+ if (featureIndex === -1) continue;
3874
3853
 
3875
3854
  // Determine if this is an existing feature (adjust index for insertAt case)
3876
3855
  let originalFeatureIndex = featureIndex;
3877
3856
  if (featureIndex >= newStartIndex && featureIndex < newStartIndex + actualNewCount) {
3878
- // This is a new feature - apply options
3857
+ // This is a new feature - apply options later
3879
3858
  continue;
3880
3859
  } else if (featureIndex >= newStartIndex + actualNewCount) {
3881
3860
  // Feature was shifted by insertion - adjust index
@@ -3911,13 +3890,11 @@ class GeoJsonEditor extends HTMLElement {
3911
3890
  if (!collapsed || (Array.isArray(collapsed) && collapsed.length === 0)) return;
3912
3891
 
3913
3892
  const ranges = this._findCollapsibleRanges();
3914
- const featureRanges = ranges.filter(r => r.isRootFeature);
3915
3893
 
3916
3894
  for (const range of ranges) {
3917
- // Find which feature this range belongs to
3918
- const featureIndex = featureRanges.findIndex(fr =>
3919
- range.startLine >= fr.startLine && range.endLine <= fr.endLine
3920
- );
3895
+ // Find which feature this range belongs to using the correct featureRanges map
3896
+ const featureIndex = this._getFeatureIndexForLine(range.startLine);
3897
+ if (featureIndex === -1) continue;
3921
3898
 
3922
3899
  // Only process new features
3923
3900
  if (featureIndex < startIndex || featureIndex >= startIndex + count) continue;
@@ -4046,12 +4023,12 @@ class GeoJsonEditor extends HTMLElement {
4046
4023
 
4047
4024
  // Restore collapsed state for remaining features
4048
4025
  const ranges = this._findCollapsibleRanges();
4049
- const featureRanges = ranges.filter(r => r.isRootFeature);
4050
4026
 
4051
4027
  for (const range of ranges) {
4052
- const featureIndex = featureRanges.findIndex(fr =>
4053
- range.startLine >= fr.startLine && range.endLine <= fr.endLine
4054
- );
4028
+ // Find which feature this range belongs to using the correct featureRanges map
4029
+ const featureIndex = this._getFeatureIndexForLine(range.startLine);
4030
+ if (featureIndex === -1) continue;
4031
+
4055
4032
  const key = `${featureIndex}:${range.nodeKey}`;
4056
4033
  if (collapsedKeys.has(key)) {
4057
4034
  this.collapsedNodes.add(range.nodeId);
@@ -4075,6 +4052,10 @@ class GeoJsonEditor extends HTMLElement {
4075
4052
  this.lines = [];
4076
4053
  this.collapsedNodes.clear();
4077
4054
  this.hiddenFeatures.clear();
4055
+ this.cursorLine = 0;
4056
+ this.cursorColumn = 0;
4057
+ this.selectionStart = null;
4058
+ this.selectionEnd = null;
4078
4059
  this.updateModel();
4079
4060
  this.scheduleRender();
4080
4061
  this.updatePlaceholderVisibility();
package/src/types.ts CHANGED
@@ -16,33 +16,3 @@ export interface SetOptions {
16
16
  */
17
17
  collapsed?: string[] | ((feature: Feature | null, index: number) => string[]);
18
18
  }
19
-
20
- /** Theme configuration */
21
- export interface ThemeConfig {
22
- bgColor?: string;
23
- textColor?: string;
24
- caretColor?: string;
25
- gutterBg?: string;
26
- gutterBorder?: string;
27
- gutterText?: string;
28
- jsonKey?: string;
29
- jsonString?: string;
30
- jsonNumber?: string;
31
- jsonBoolean?: string;
32
- jsonNull?: string;
33
- jsonPunct?: string;
34
- jsonError?: string;
35
- controlColor?: string;
36
- controlBg?: string;
37
- controlBorder?: string;
38
- geojsonKey?: string;
39
- geojsonType?: string;
40
- geojsonTypeInvalid?: string;
41
- jsonKeyInvalid?: string;
42
- }
43
-
44
- /** Theme settings for dark and light modes */
45
- export interface ThemeSettings {
46
- dark?: ThemeConfig;
47
- light?: ThemeConfig;
48
- }