npm package: @softwarity/geojson-editor
Description: A feature-rich GeoJSON editor Web Component with syntax highlighting, collapsible nodes, and color picker
Author: Softwarity
Latest version: 1.0.34

<p align="center">
  <a href="https://www.softwarity.io/">
    <img src="https://www.softwarity.io/img/softwarity.svg" alt="Softwarity" height="60">
  </a>
</p>

# @softwarity/geojson-editor

<p align="center">
  <a href="https://www.npmjs.com/package/@softwarity/geojson-editor">
    <img src="https://img.shields.io/npm/v/@softwarity/geojson-editor?color=blue&label=npm" alt="npm version">
  </a>
  <a href="https://bundlephobia.com/package/@softwarity/geojson-editor">
    <img src="https://img.shields.io/bundlephobia/minzip/@softwarity/geojson-editor?label=size" alt="bundle size">
  </a>
  <a href="https://github.com/softwarity/geojson-editor/blob/main/LICENSE">
    <img src="https://img.shields.io/badge/license-MIT-blue" alt="license">
  </a>
  <a href="https://codecov.io/gh/softwarity/geojson-editor">
    <img src="https://codecov.io/gh/softwarity/geojson-editor/graph/badge.svg" alt="codecov">
  </a>
</p>

A feature-rich, framework-agnostic **Web Component** for editing GeoJSON features with syntax highlighting, collapsible nodes, and integrated color picker.

**[🚀 Try the Live Demo](https://softwarity.github.io/geojson-editor/)** | **[📋 Release Notes](RELEASE_NOTES.md)**

<p align="center">
  <a href="https://softwarity.github.io/geojson-editor/">
    <img src="preview.png" alt="GeoJSON Editor Preview" width="800">
  </a>
</p>

## Why not Monaco, CodeMirror, or Prism?

| | @softwarity/geojson-editor | Monaco Editor | CodeMirror 6 | Prism.js |
|---|:---:|:---:|:---:|:---:|
| **Size (gzip)** | <img src="https://img.shields.io/bundlephobia/minzip/@softwarity/geojson-editor?label="> | ~2.5 MB* | ~150 KB* | ~20 KB* |
| **Editable** | ✅ Full editor | ✅ Full editor | ✅ Full editor | ❌ Read-only |
| **GeoJSON validation** | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ None |
| **Type highlighting** | ✅ Contextual | ⚠️ Generic JSON | ⚠️ Generic JSON | ⚠️ Generic JSON |
| **Invalid type detection** | ✅ Visual feedback | ❌ | ❌ | ❌ |
| **Collapsible nodes** | ✅ Native | ✅ | ✅ Plugin | ❌ |
| **Undo/Redo** | ✅ With grouping | ✅ | ✅ | ❌ |
| **Color picker** | ✅ Integrated | ❌ | ❌ | ❌ |
| **Boolean checkbox** | ✅ Integrated | ❌ | ❌ | ❌ |
| **Feature visibility toggle** | ✅ | ❌ | ❌ | ❌ |
| **Auto-collapse coordinates** | ✅ | ❌ | ❌ | ❌ |
| **FeatureCollection output** | ✅ Always | ❌ | ❌ | ❌ |
| **Clear button** | ✅ | ❌ | ❌ | ❌ |
| **Save to file (Ctrl+S)** | ✅ | ❌ | ❌ | ❌ |
| **Open from file (Ctrl+O)** | ✅ | ❌ | ❌ | ❌ |
| **Error navigation** | ✅ | ✅ | ✅ | ❌ |
| **Dark mode detection** | ✅ Auto | ⚠️ Manual | ⚠️ Manual | ⚠️ Manual |
| **Dependencies** | 0 | Many | Few | 0 |
| **Setup complexity** | 1 line | Complex | Moderate | Simple |

<sub>* Estimated total size: Monaco includes web workers loaded dynamically; CodeMirror/Prism require plugins for equivalent functionality (line numbers, folding, language support).</sub>

**TL;DR**: If you're building a GeoJSON-focused application and need a lightweight, specialized editor with built-in validation and GeoJSON-aware features, this component does exactly that — without the overhead of a general-purpose code editor.

## Features

- **Full Editor** - Complete editing capabilities with cursor navigation, selection, copy/paste, and keyboard shortcuts — unlike read-only syntax highlighters
- **GeoJSON-Aware Highlighting** - Distinct colors for GeoJSON keywords (`type`, `coordinates`, `geometry`, etc.)
- **GeoJSON Type Validation** - Valid types (`Point`, `LineString`, `Polygon`, etc.) highlighted distinctly; invalid types (`LinearRing`, unknown types) shown with error styling (colors configurable via theme)
- **Syntax Highlighting** - JSON syntax highlighting with customizable color schemes
- **Collapsible Nodes** - Collapse/expand JSON objects and arrays with visual indicators (`{...}` / `[...]`); use Enter to expand and Shift+Enter to collapse; `coordinates` auto-collapsed on load
- **Attribute Navigation** - Tab/Shift+Tab to navigate between JSON attributes (keys and values) for quick editing
- **Virtualized Rendering** - Monaco-like architecture: only visible lines are rendered to DOM for optimal performance with large GeoJSON files
- **Feature Visibility Toggle** - Hide/show individual Features via eye icon in gutter; hidden features are grayed out and excluded from `change` events (useful for temporary filtering without deleting data)
- **Color Picker** - Built-in color swatch for hex color properties (`#rrggbb`) displayed inline next to the value; click to open native color picker
- **Boolean Checkbox** - Inline checkbox for boolean properties displayed next to the value; toggle to switch between `true`/`false` and emit changes (e.g., `marker: true` to show vertices)
- **Dark/Light Color Schemes** - Automatic color scheme based on system preference via CSS `light-dark()` function
- **Auto-format** - Automatic JSON formatting in real-time (always enabled)
- **Readonly Mode** - Visual indicator with diagonal stripes when editing is disabled
- **Block Editing in Collapsed Areas** - Prevents accidental edits in collapsed sections
- **Smart Copy/Paste** - Copy includes expanded content even from collapsed nodes
- **FeatureCollection Output** - Emits valid FeatureCollection with all edited features
- **Clear Button** - Discreet ✕ button in suffix area to clear all editor content (hidden in readonly mode)
- **Undo/Redo** - Full undo/redo support with Ctrl+Z / Ctrl+Y / Ctrl+Shift+Z; rapid keystrokes grouped as single undo step
- **Save to File** - Ctrl+S to download GeoJSON as `.geojson` file; programmatic `save(filename)` method available
- **Open from File** - Ctrl+O to open a `.geojson` or `.json` file from the client filesystem; programmatic `open()` method available
- **Error Navigation** - Visual error indicators in gutter with navigation buttons (◀ ▶) to jump between errors; error count displayed in suffix area
- **Current Features Event** - `current-features` event emitted as a FeatureCollection when cursor/selection changes; includes all features overlapping with selection; useful for synchronizing with maps

## Installation

### Option 1: CDN (No build step required)

Simply add a script tag to your HTML file:

```html
<!-- Using unpkg -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>

<!-- Or using jsDelivr -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@softwarity/geojson-editor"></script>
```

You can also specify a version:

```html
<!-- Specific version -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor@1.0.0"></script>

<!-- Latest minor/patch of v1 -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor@1"></script>
```

### Option 2: NPM (With bundler)

If you're using a bundler (Vite, Webpack, Rollup, etc.):

```bash
npm install @softwarity/geojson-editor
```

Then import in your JavaScript/TypeScript:

```javascript
import '@softwarity/geojson-editor';
```

### TypeScript Support

This package includes TypeScript type definitions out of the box. No additional `@types/*` package required.

```typescript
import '@softwarity/geojson-editor';
import type { SetOptions, CursorPosition, GeometryType } from '@softwarity/geojson-editor';

// Type-safe editor access
const editor = document.querySelector('geojson-editor') as GeoJsonEditor;

// Full autocompletion and type checking
editor.set(features, { collapsed: ['coordinates'] });
```

**Exported types:**
- `GeoJsonEditor` - The Web Component class
- `SetOptions` - Options for `set()`, `add()`, `insertAt()`, `open()`
- `CursorPosition` - Cursor position `{ line, column }`
- `GeometryType` - GeoJSON geometry types union

## Usage

### Basic Usage

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
</head>
<body>
  <!-- User edits features, component wraps in FeatureCollection -->
  <geojson-editor placeholder="Enter GeoJSON features here..."></geojson-editor>
</body>
</html>
```

Users edit features directly (comma-separated), and the component automatically wraps them in a `{"type": "FeatureCollection", "features": [...]}` structure for validation and events.

### With Theme Control

The component automatically adapts to system color scheme. Override with CSS:

```html
<!-- Force dark mode -->
<geojson-editor style="color-scheme: dark;"></geojson-editor>

<!-- Force light mode -->
<geojson-editor style="color-scheme: light;"></geojson-editor>
```

### Listen to Changes

```javascript
const editor = document.querySelector('geojson-editor');

// Valid GeoJSON emits change event with parsed object directly
editor.addEventListener('change', (e) => {
  console.log('GeoJSON:', e.detail); // Parsed GeoJSON object
});

// Invalid JSON or GeoJSON validation error emits error event
editor.addEventListener('error', (e) => {
  console.error('Error:', e.detail.error);
  console.log('Errors:', e.detail.errors); // Array of validation errors (if GeoJSON validation)
});
```

## Attributes

| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| `value` | `string` | `""` | Initial editor content (features array content) |
| `placeholder` | `string` | `""` | Placeholder text |
| `readonly` | `boolean` | `false` | Make editor read-only |

**Note:** `coordinates` nodes are automatically collapsed when content is loaded to improve readability. Use Enter to expand and Shift+Enter to collapse nodes, or click the gutter toggle. Use Tab/Shift+Tab to navigate between attributes.

### Themes

The component uses CSS `light-dark()` function for automatic dark/light mode switching based on the system's `color-scheme` preference.

#### Pre-built Themes

Ready-to-use theme CSS files are available:

| Theme | Description | CDN Link |
|-------|-------------|----------|
| **Default (IntelliJ)** | Built-in, no extra CSS needed | - |
| **VS Code** | Visual Studio Code inspired colors | [vscode.css](https://unpkg.com/@softwarity/geojson-editor/themes/vscode.css) |
| **GitHub** | GitHub's code styling | [github.css](https://unpkg.com/@softwarity/geojson-editor/themes/github.css) |
| **Monokai** | Classic dark Monokai palette | [monokai.css](https://unpkg.com/@softwarity/geojson-editor/themes/monokai.css) |
| **Solarized** | Ethan Schoonover's precision colors | [solarized.css](https://unpkg.com/@softwarity/geojson-editor/themes/solarized.css) |
| **Catppuccin** | Soothing pastel theme (Latte/Mocha) | [catppuccin.css](https://unpkg.com/@softwarity/geojson-editor/themes/catppuccin.css) |

**Using via CDN:**
```html
<!-- Include theme CSS before the component -->
<link rel="stylesheet" href="https://unpkg.com/@softwarity/geojson-editor/themes/monokai.css">

<!-- Then use the component -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
```

**Using via npm:**
```javascript
// Import theme CSS in your build
import '@softwarity/geojson-editor/themes/github.css';
import '@softwarity/geojson-editor';
```

#### Custom Themes

Create your own theme by overriding CSS custom properties on `:root` with the `light-dark()` function:

```css
:root {
  /* Editor background and text */
  --geojson-editor-bg-color: light-dark(#ffffff, #1e1e1e);
  --geojson-editor-text-color: light-dark(#000000, #d4d4d4);
  --geojson-editor-caret-color: light-dark(#000000, #aeafad);

  /* Gutter (line numbers area) */
  --geojson-editor-gutter-bg: light-dark(#f3f3f3, #1e1e1e);
  --geojson-editor-gutter-border: light-dark(#e0e0e0, #333333);
  --geojson-editor-gutter-text: light-dark(#237893, #858585);

  /* JSON syntax highlighting */
  --geojson-editor-json-key: light-dark(#0451a5, #9cdcfe);
  --geojson-editor-json-string: light-dark(#a31515, #ce9178);
  --geojson-editor-json-number: light-dark(#098658, #b5cea8);
  --geojson-editor-json-boolean: light-dark(#0000ff, #569cd6);
  --geojson-editor-json-punct: light-dark(#000000, #d4d4d4);
  --geojson-editor-json-error: light-dark(#cd3131, #f44747);

  /* GeoJSON-specific */
  --geojson-editor-geojson-key: light-dark(#800000, #9cdcfe);
  --geojson-editor-geojson-type: light-dark(#008000, #4ec9b0);
  --geojson-editor-geojson-type-invalid: light-dark(#cd3131, #f44747);

  /* Controls (checkboxes, color swatches) */
  --geojson-editor-control-color: light-dark(#0000ff, #569cd6);
  --geojson-editor-control-bg: light-dark(#f3f3f3, #3c3c3c);
  --geojson-editor-control-border: light-dark(#c0c0c0, #6b6b6b);

  /* Selection and errors */
  --geojson-editor-selection-color: light-dark(rgba(173, 214, 255, 0.5), rgba(38, 79, 120, 0.5));
  --geojson-editor-error-color: light-dark(#cd3131, #f44747);
}
```

#### Forcing Light or Dark Mode

```css
/* Force dark mode on specific editor */
geojson-editor.dark-editor {
  color-scheme: dark;
}

/* Force light mode */
geojson-editor.light-editor {
  color-scheme: light;
}
```

#### Integration with CSS Frameworks

The editor inherits `color-scheme` from parent elements. To integrate with frameworks that use class-based dark mode:

**Tailwind CSS** (uses `html.dark`):
```css
html.dark { color-scheme: dark; }
html:not(.dark) { color-scheme: light; }
```

**Bootstrap 5** (uses `[data-bs-theme]`):
```css
[data-bs-theme="dark"] { color-scheme: dark; }
[data-bs-theme="light"] { color-scheme: light; }
```

**DaisyUI / Other** (any class-based theme):
```css
.dark-theme { color-scheme: dark; }
.light-theme { color-scheme: light; }
```

#### CSS Custom Properties Reference

| Variable | Description |
|----------|-------------|
| `--geojson-editor-bg-color` | Editor background |
| `--geojson-editor-text-color` | Default text color |
| `--geojson-editor-caret-color` | Cursor color |
| `--geojson-editor-gutter-bg` | Line numbers background |
| `--geojson-editor-gutter-border` | Gutter border color |
| `--geojson-editor-gutter-text` | Line numbers color |
| `--geojson-editor-json-key` | JSON property keys |
| `--geojson-editor-json-string` | String values |
| `--geojson-editor-json-number` | Number values |
| `--geojson-editor-json-boolean` | Boolean/null values |
| `--geojson-editor-json-punct` | Punctuation (brackets, colons) |
| `--geojson-editor-json-error` | Invalid JSON highlighting |
| `--geojson-editor-geojson-key` | GeoJSON keywords (type, geometry, etc.) |
| `--geojson-editor-geojson-type` | Valid geometry types (Point, Polygon, etc.) |
| `--geojson-editor-geojson-type-invalid` | Invalid geometry types |
| `--geojson-editor-control-color` | Inline controls accent color |
| `--geojson-editor-control-bg` | Inline controls background |
| `--geojson-editor-control-border` | Inline controls border |
| `--geojson-editor-selection-color` | Text selection background |
| `--geojson-editor-error-color` | Error indicators |

## API Methods

```javascript
const editor = document.querySelector('geojson-editor');
```

### Features API

Programmatic manipulation of features:

| Method | Description |
|--------|-------------|
| `set(input, options?)` | Replace all features (throws if invalid) |
| `add(input, options?)` | Add features at the end (throws if invalid) |
| `insertAt(input, index, options?)` | Insert at index (negative = from end: -1 = before last) (throws if invalid) |
| `removeAt(index)` | Remove feature at index (negative = from end), returns removed feature |
| `removeAll()` | Remove all features, returns array of removed features |
| `get(index)` | Get feature at index (negative = from end) |
| `getAll()` | Get all features as an array |
| `emit()` | Emit the current document on the change event |

**Flexible Input:** `set()`, `add()`, and `insertAt()` accept multiple input formats:
- **FeatureCollection** → extracts the `features` array
- **Feature[]** → uses the array directly
- **Feature** → wraps in an array

**Options Parameter:** `set()`, `add()`, `insertAt()`, and `open()` accept an optional `options` object:

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `collapsed` | `string[]` \| `function` | `['coordinates']` | Attributes to collapse after loading |

The `collapsed` option controls which JSON nodes are automatically collapsed:
- **`string[]`**: Static list of attribute names to collapse (e.g., `['coordinates', 'properties']`)
- **`function(feature, index) => string[]`**: Dynamic function returning attributes per feature
- **`'$root'`**: Special keyword to collapse entire features
- **Empty array `[]`**: Disables auto-collapse (nothing collapsed)

```javascript
// Default: only coordinates collapsed
editor.set(features);

// Collapse coordinates and properties
editor.set(features, { collapsed: ['coordinates', 'properties'] });

// Collapse entire features
editor.set(features, { collapsed: ['$root'] });

// No auto-collapse
editor.set(features, { collapsed: [] });

// Dynamic: collapse geometry only for Points
editor.set(features, {
  collapsed: (feature, index) => {
    if (feature.geometry?.type === 'Point') {
      return ['geometry'];
    }
    return ['coordinates'];
  }
});
```

**Validation:** All input features are validated before adding. Invalid features throw an `Error` with a descriptive message. A valid Feature must have:
- `type: "Feature"`
- `geometry`: object with valid type (`Point`, `LineString`, `Polygon`, etc.) and `coordinates`, or `null`
- `properties`: object or `null`

**Smart Paste:** When pasting GeoJSON content (Ctrl+V), the editor automatically detects and normalizes the format (FeatureCollection, Feature[], or single Feature). Invalid GeoJSON falls back to raw text insertion.

```javascript
// Set features from array
editor.set([
  { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} },
  { type: 'Feature', geometry: { type: 'Point', coordinates: [1, 1] }, properties: {} }
]);

// Set from FeatureCollection
editor.set({
  type: 'FeatureCollection',
  features: [{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }]
});

// Set a single feature
editor.set({ type: 'Feature', geometry: { type: 'Point', coordinates: [2, 2] }, properties: {} });

// Add multiple features at once
editor.add([feature1, feature2]);

// Insert FeatureCollection at position 1
editor.insertAt(featureCollection, 1);

// Remove last feature
const removed = editor.removeAt(-1);

// Get all features
const features = editor.getAll();

// Manually emit change event
editor.emit();
```

### Undo/Redo API

Full undo/redo support with action grouping:

| Method | Description |
|--------|-------------|
| `undo()` | Undo last action, returns `true` if successful |
| `redo()` | Redo previously undone action, returns `true` if successful |
| `canUndo()` | Check if undo is available |
| `canRedo()` | Check if redo is available |
| `clearHistory()` | Clear undo/redo history |

**Keyboard shortcuts:**
- `Ctrl+Z` / `Cmd+Z` - Undo
- `Ctrl+Y` / `Cmd+Y` - Redo
- `Ctrl+Shift+Z` / `Cmd+Shift+Z` - Redo (alternative)

**Action grouping:** Rapid keystrokes (< 500ms apart, same action type) are automatically grouped as a single undo step. This means typing "hello" quickly creates one undo entry, not five.

```javascript
// Programmatic undo/redo
if (editor.canUndo()) {
  editor.undo();
}

// Clear history after saving
editor.clearHistory();
```

### Save API

Save the current GeoJSON content to a file:

| Method | Description |
|--------|-------------|
| `save(filename?)` | Download GeoJSON as file, returns `true` if successful |

**Keyboard shortcut:**
- `Ctrl+S` / `Cmd+S` - Save with default filename (`features.geojson`)

```javascript
// Save with default filename
editor.save();  // Downloads "features.geojson"

// Save with custom filename
editor.save('my-map-data.geojson');
```

### Open API

Open a GeoJSON file from the client filesystem:

| Method | Description |
|--------|-------------|
| `open(options?)` | Open file dialog, returns `Promise<boolean>` (true if loaded successfully) |

**Keyboard shortcut:**
- `Ctrl+O` / `Cmd+O` - Open file dialog

**Supported formats:**
- FeatureCollection (extracts features array)
- Single Feature (wraps in array)
- Array of Features

**Note:** The `Ctrl+O` shortcut is disabled in readonly mode, but `open()` remains available via API for programmatic loading.

```javascript
// Open file dialog
const success = await editor.open();
if (success) {
  console.log('File loaded:', editor.getAll());
}

// Open with custom collapse options
const success = await editor.open({ collapsed: ['$root'] });
```

### Error Navigation API

Navigate between syntax and structural errors:

| Method | Description |
|--------|-------------|
| `goToNextError()` | Navigate to next error, returns `true` if found |
| `goToPrevError()` | Navigate to previous error, returns `true` if found |

Error lines are indicated with a red bar in the gutter. The error count is displayed in the suffix area between navigation buttons (◀ ▶).

```javascript
// Navigate to errors programmatically
editor.goToNextError();
editor.goToPrevError();
```

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Ctrl+S` / `Cmd+S` | Save to file |
| `Ctrl+O` / `Cmd+O` | Open file (disabled in readonly) |
| `Ctrl+Z` / `Cmd+Z` | Undo |
| `Ctrl+Y` / `Cmd+Y` | Redo |
| `Ctrl+Shift+Z` / `Cmd+Shift+Z` | Redo (alternative) |
| `Ctrl+A` / `Cmd+A` | Select all |
| `Ctrl+C` / `Cmd+C` | Copy |
| `Ctrl+X` / `Cmd+X` | Cut |
| `Ctrl+V` / `Cmd+V` | Paste |
| `Ctrl+←` / `Cmd+←` | Move cursor to previous word |
| `Ctrl+→` / `Cmd+→` | Move cursor to next word |
| `Ctrl+Shift+←` / `Cmd+Shift+←` | Select to previous word |
| `Ctrl+Shift+→` / `Cmd+Shift+→` | Select to next word |
| `Enter` | Expand collapsed node at cursor |
| `Shift+Enter` | Collapse innermost node containing cursor |
| `Tab` | Navigate to next attribute (key or value) |
| `Shift+Tab` | Navigate to previous attribute |
| `Home` | Go to start of line |
| `End` | Go to end of line |
| `Ctrl+Home` / `Cmd+Home` | Go to start of document |
| `Ctrl+End` / `Cmd+End` | Go to end of document |
| `PageUp` | Scroll up one page |
| `PageDown` | Scroll down one page |
| `Shift+Home/End/PageUp/PageDown` | Extend selection while navigating |
| `Ctrl+I` / `Cmd+I` | Add feature via prompt (requires `internal-add-shortcut` attribute) |

### Overriding Shortcuts

All keyboard shortcuts handled by the editor call `stopPropagation()` to prevent them from bubbling up. To override a default shortcut behavior, use a capture-phase event listener:

```javascript
editor.addEventListener('keydown', async (e) => {
  // Override Ctrl+S to save to a remote server instead of downloading
  if (e.ctrlKey && e.key === 's') {
    e.preventDefault();
    e.stopPropagation();
    await saveToRemoteServer(editor.getAll());
  }
  // Override Ctrl+O to load from a remote API instead of local filesystem
  if (e.ctrlKey && e.key === 'o') {
    e.preventDefault();
    e.stopPropagation();
    const features = await openRemoteFeatureSelector();
    if (features) editor.set(features);
  }
}, { capture: true });  // capture: true is required to intercept before the editor
```

## Events

### `change`

Fired when content changes and GeoJSON is valid (debounced 150ms).

```javascript
editor.addEventListener('change', (e) => {
  console.log(e.detail);  // Parsed GeoJSON object directly
});
```

**Event detail:** The parsed GeoJSON object (always a FeatureCollection).

**Note:** Hidden features (toggled via the eye icon) are automatically excluded from the emitted GeoJSON. This allows temporary filtering without modifying the actual JSON content.

**Example:**

```javascript
// User edits features only, but change event includes the FeatureCollection wrapper
editor.addEventListener('change', (e) => {
  console.log(e.detail);
  // → { type: "FeatureCollection", features: [{ type: "Feature", ... }] }
});
```

### `error`

Fired when content changes but JSON is invalid or GeoJSON validation fails (debounced 150ms).

```javascript
editor.addEventListener('error', (e) => {
  console.error(e.detail.error);   // Error message
  console.log(e.detail.errors);    // Array of validation errors (GeoJSON validation only)
  console.log(e.detail.content);   // Raw content for debugging
});
```

**Event detail properties:**

| Property | Type | Description |
|----------|------|-------------|
| `error` | `string` | Error message (JSON parse error or GeoJSON validation summary) |
| `errors` | `string[]` | Array of validation errors with paths (GeoJSON validation only) |
| `content` | `string` | Raw editor content (for debugging) |

**GeoJSON validation errors include:**
- Invalid types (e.g., `"LinearRing"`)
- Unknown types (any `type` value not in the GeoJSON specification)

### `current-features`

Fired when the current feature(s) change. Useful for synchronizing the editor with a map display. Emits a FeatureCollection containing all features that overlap with the cursor position or selection.

**Triggers:**
- Editor gains focus → emits FeatureCollection with feature at cursor
- Cursor moves to a different feature → emits FeatureCollection with new feature(s)
- Selection spans multiple features → emits FeatureCollection with all overlapping features
- Cursor moves outside any feature → emits empty FeatureCollection
- Editor loses focus → emits empty FeatureCollection

```javascript
editor.addEventListener('current-features', (e) => {
  const featureCollection = e.detail;  // Always a FeatureCollection
  if (featureCollection.features.length > 0) {
    // Highlight these features on your map
    highlightLayer.setData(featureCollection);
  } else {
    // No current features
    highlightLayer.setData({ type: 'FeatureCollection', features: [] });
  }
});
```

**Event detail:** A FeatureCollection object containing:
- All features overlapping with the current selection (if selection exists)
- The single feature at cursor position (if no selection)
- Empty features array if cursor is outside any feature or editor loses focus

**Note:** The event is only fired when the set of features changes, not on every cursor movement within the same feature. This prevents excessive event firing during normal editing.

## Styling

The component uses Shadow DOM with CSS variables for theming. Customize colors via CSS custom properties (see [Theme Control](#theme-control)).

## Browser Support

Works in all modern browsers supporting:
- Web Components
- Shadow DOM
- ES6 Modules

## Development

See [DEVELOPMENT.md](DEVELOPMENT.md) for detailed development guide.

```bash
# Install dependencies
npm install

# Start dev server with live demo
npm run dev

# Build for production
npm run build
```

## License

MIT