@softwarity/geojson-editor 1.0.19 → 1.0.21

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 CHANGED
@@ -446,6 +446,29 @@ editor.goToPrevError();
446
446
  | `PageUp` | Scroll up one page |
447
447
  | `PageDown` | Scroll down one page |
448
448
  | `Shift+Home/End/PageUp/PageDown` | Extend selection while navigating |
449
+ | `Ctrl+I` / `Cmd+I` | Add feature via prompt (requires `internal-add-shortcut` attribute) |
450
+
451
+ ### Overriding Shortcuts
452
+
453
+ 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:
454
+
455
+ ```javascript
456
+ editor.addEventListener('keydown', async (e) => {
457
+ // Override Ctrl+S to save to a remote server instead of downloading
458
+ if (e.ctrlKey && e.key === 's') {
459
+ e.preventDefault();
460
+ e.stopPropagation();
461
+ await saveToRemoteServer(editor.getAll());
462
+ }
463
+ // Override Ctrl+O to load from a remote API instead of local filesystem
464
+ if (e.ctrlKey && e.key === 'o') {
465
+ e.preventDefault();
466
+ e.stopPropagation();
467
+ const features = await openRemoteFeatureSelector();
468
+ if (features) editor.set(features);
469
+ }
470
+ }, { capture: true }); // capture: true is required to intercept before the editor
471
+ ```
449
472
 
450
473
  ## Events
451
474
 
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @license MIT
3
3
  * @name @softwarity/geojson-editor
4
- * @version 1.0.19
4
+ * @version 1.0.21
5
5
  * @author Softwarity (https://www.softwarity.io/)
6
6
  * @copyright 2025 Softwarity
7
7
  * @see https://github.com/softwarity/geojson-editor
8
8
  */
9
- const e=["type","geometry","properties","coordinates","id","features"],t=["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon"],s=/"geometry"\s*:/,i=/"properties"\s*:/,n=/"features"\s*:/,r=/^(\s*"[^"]+"\s*:\s*)([{\[])/,o=/^(\s*)([{\[]),?\s*$/,l=/&/g,a=/</g,h=/>/g,c=/([{}[\],:])/g,d=/"([^"]+)"(<span class="json-punctuation">:<\/span>)/g,u=/<span class="geojson-key">"type"<\/span><span class="json-punctuation">:<\/span>(\s*)"([^"]*)"/g,p=/(<span class="json-punctuation">:<\/span>)(\s*)"([^"]*)"/g,g=/(<span class="json-punctuation">:<\/span>)(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gi,f=/(<span class="json-punctuation">[\[,]<\/span>)(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gi,m=/^(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gim,_=/(<span class="json-punctuation">:<\/span>)(\s*)(true|false)/g,C=/(<span class="json-punctuation">:<\/span>)(\s*)(null)/g,b=/(<\/span>|^)([^<]+)(<span|$)/g,v=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,L=/^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/,y=/^\s*$/,x=/(\s+)/,w=/"([\w-]+)"\s*:\s*(?:"([^"]*)"|(\btrue\b|\bfalse\b))/g,k=/"([\w-]+)"\s*:\s*(?:"([^"]*)"|(\btrue\b|\bfalse\b))/,S=/"([\w-]+)"\s*:\s*(true|false)/,T=/"type"\s*:\s*"Feature"/,E=/^\s*"([^"]+)"\s*:\s*([{\[])/,j=/^\s*([{\[]),?\s*$/,N=/[{\[]/,R=/[\w-]/,I=/([A-Z])/g,F=/\{/g,$=/\}/g,A=/\[/g,B=/\]/g;function M(e){if(!e)return null;if(void 0!==e.id)return`id:${e.id}`;if(void 0!==e.properties?.id)return`prop:${e.properties.id}`;const t=e.geometry?.type||"null",s=e.geometry,i=JSON.stringify(s?.coordinates||[]);let n=0;for(let r=0;r<i.length;r++)n=(n<<5)-n+i.charCodeAt(r),n&=n;return`hash:${t}:${n.toString(36)}`}function K(e,t){const s="{"===t?"}":"]";let i=0,n=0,r=!1,o=!1;for(const l of e)o?o=!1:"\\"===l&&r?o=!0:'"'!==l?r||(l===t&&i++,l===s&&n++):r=!r;return{open:i,close:n}}function P(e){if(!e||"object"!=typeof e)throw new Error("Feature must be an object");if("Feature"!==e.type)throw new Error('Feature type must be "Feature"');if(!("geometry"in e))throw new Error("Feature must have a geometry property");if(!("properties"in e))throw new Error("Feature must have a properties property");if(null!==e.geometry){if("object"!=typeof e.geometry)throw new Error("Feature geometry must be an object or null");if(!e.geometry.type)throw new Error("Feature geometry must have a type");if(!t.includes(e.geometry.type))throw new Error(`Invalid geometry type: "${e.geometry.type}"`);if(!("coordinates"in e.geometry))throw new Error("Feature geometry must have coordinates")}if(null!==e.properties&&"object"!=typeof e.properties)throw new Error("Feature properties must be an object or null")}function H(e){let t=[];if(Array.isArray(e))t=e;else{if(!e||"object"!=typeof e)throw new Error("Input must be a Feature, array of Features, or FeatureCollection");if("FeatureCollection"===e.type&&"features"in e&&Array.isArray(e.features))t=e.features;else{if("Feature"!==e.type)throw new Error("Input must be a Feature, array of Features, or FeatureCollection");t=[e]}}for(const s of t)P(s);return t}let D=null;function O(e){return",aliceblue,antiquewhite,aqua,aquamarine,azure,beige,bisque,black,blanchedalmond,blue,blueviolet,brown,burlywood,cadetblue,chartreuse,chocolate,coral,cornflowerblue,cornsilk,crimson,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkgrey,darkkhaki,darkmagenta,darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray,darkslategrey,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dimgrey,dodgerblue,firebrick,floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,green,greenyellow,grey,honeydew,hotpink,indianred,indigo,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon,lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgray,lightgreen,lightgrey,lightpink,lightsalmon,lightseagreen,lightskyblue,lightslategray,lightslategrey,lightsteelblue,lightyellow,lime,limegreen,linen,magenta,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple,mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,midnightblue,mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid,palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue,purple,rebeccapurple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,seagreen,seashell,sienna,silver,skyblue,slateblue,slategray,slategrey,snow,springgreen,steelblue,tan,teal,thistle,tomato,turquoise,violet,wheat,white,whitesmoke,yellow,yellowgreen,".includes(","+e.toLowerCase()+",")}function V(s,i,n){if(!s)return"";let L=s,w=null;if(n?.collapseButton?.isCollapsed){const e=s.match(r),t=!e&&s.match(o);e?(L=e[1]+e[2],w=e[2]):t&&(L=t[1]+t[2],w=t[2])}let k=L.replace(l,"&amp;").replace(a,"&lt;").replace(h,"&gt;");if(k=k.replace(c,'<span class="json-punctuation">$1</span>'),d.lastIndex=0,k=k.replace(d,(t,s,n)=>"properties"!==i&&e.includes(s)?`<span class="geojson-key">"${s}"</span>${n}`:`<span class="json-key">"${s}"</span>${n}`),"properties"!==i&&(u.lastIndex=0,k=k.replace(u,(e,s,i)=>`<span class="geojson-key">"type"</span><span class="json-punctuation">:</span>${s}<span class="${"Feature"===i||"FeatureCollection"===i||t.includes(i)?"geojson-type":"geojson-type-invalid"}">"${i}"</span>`)),p.lastIndex=0,k=k.replace(p,(e,t,s,i)=>e.includes("geojson-type")||e.includes("json-string")?e:v.test(i)||O(i)?`${t}${s}<span class="json-string json-color" data-color="${i}" style="--swatch-color: ${i}">"${i}"</span>`:`${t}${s}<span class="json-string">"${i}"</span>`),g.lastIndex=0,k=k.replace(g,'$1$2<span class="json-number">$3</span>'),f.lastIndex=0,k=k.replace(f,'$1$2<span class="json-number">$3</span>'),m.lastIndex=0,k=k.replace(m,'$1<span class="json-number">$2</span>'),_.lastIndex=0,k=k.replace(_,(e,t,s,i)=>`${t}${s}<span class="json-boolean${"true"===i?" json-bool-true":" json-bool-false"}">${i}</span>`),C.lastIndex=0,k=k.replace(C,'$1$2<span class="json-null">$3</span>'),w){const e="["===w?"collapsed-bracket-array":"collapsed-bracket-object";k=k.replace(new RegExp(`<span class="json-punctuation">\\${w}<\\/span>$`),`<span class="${e}">${w}</span>`)}return b.lastIndex=0,k=k.replace(b,(e,t,s,i)=>{if(!s||y.test(s))return e;const n=s.split(x);let r=!1;const o=n.map(e=>y.test(e)?e:(r=!0,`<span class="json-error">${e}</span>`)).join("");return r?t+o+i:e}),k}const z=e=>document.createElement(e);class q extends HTMLElement{constructor(){super(),this.lines=[],this.collapsedNodes=/* @__PURE__ */new Set,this.hiddenFeatures=/* @__PURE__ */new Set,this._nodeIdCounter=0,this._lineToNodeId=/* @__PURE__ */new Map,this._nodeIdToLines=/* @__PURE__ */new Map,this._openedNodeKeys=/* @__PURE__ */new Set,this.visibleLines=[],this.lineMetadata=/* @__PURE__ */new Map,this.featureRanges=/* @__PURE__ */new Map,this.viewportHeight=0,this.lineHeight=19.5,this.bufferLines=5,this._lastStartIndex=-1,this._lastEndIndex=-1,this._lastTotalLines=-1,this._scrollRaf=null,this.cursorLine=0,this.cursorColumn=0,this.selectionStart=null,this.selectionEnd=null,this.renderTimer=void 0,this.inputTimer=void 0,this.themes={dark:{},light:{}},this._undoStack=[],this._redoStack=[],this._maxHistorySize=100,this._lastActionTime=0,this._lastActionType=null,this._groupingDelay=500,this._isSelecting=!1,this._isComposing=!1,this._blockRender=!1,this._insertMode=!0,this._charWidth=null,this._contextMapCache=null,this._contextMapLinesLength=0,this._contextMapFirstLine=void 0,this._contextMapLastLine=void 0,this._errorLinesCache=null,this._viewport=null,this._linesContainer=null,this._scrollContent=null,this._hiddenTextarea=null,this._gutterContent=null,this._gutterScrollContent=null,this._gutterScroll=null,this._gutter=null,this._clearBtn=null,this._editorWrapper=null,this._placeholderLayer=null,this._editorPrefix=null,this._editorSuffix=null,this._errorNav=null,this._errorCount=null,this._prevErrorBtn=null,this._nextErrorBtn=null,this.attachShadow({mode:"open"})}_id(e){return this.shadowRoot.getElementById(e)}_invalidateRenderCache(){this._lastStartIndex=-1,this._lastEndIndex=-1,this._lastTotalLines=-1}_createSnapshot(){return{lines:[...this.lines],cursorLine:this.cursorLine,cursorColumn:this.cursorColumn,timestamp:Date.now()}}_restoreSnapshot(e){this.lines=[...e.lines],this.cursorLine=e.cursorLine,this.cursorColumn=e.cursorColumn,this.updateModel(),this._invalidateRenderCache(),this.scheduleRender(),this.updatePlaceholderVisibility(),this.emitChange()}_saveToHistory(e="edit"){const t=Date.now();if(!(e===this._lastActionType&&t-this._lastActionTime<this._groupingDelay)){const e=this._createSnapshot();this._undoStack.push(e),this._undoStack.length>this._maxHistorySize&&this._undoStack.shift(),this._redoStack=[]}this._lastActionTime=t,this._lastActionType=e}undo(){if(0===this._undoStack.length)return!1;this._redoStack.push(this._createSnapshot());const e=this._undoStack.pop();return e&&this._restoreSnapshot(e),this._lastActionType=null,this._lastActionTime=0,!0}redo(){if(0===this._redoStack.length)return!1;this._undoStack.push(this._createSnapshot());const e=this._redoStack.pop();return e&&this._restoreSnapshot(e),this._lastActionType=null,this._lastActionTime=0,!0}clearHistory(){this._undoStack=[],this._redoStack=[],this._lastActionType=null,this._lastActionTime=0}canUndo(){return this._undoStack.length>0}canRedo(){return this._redoStack.length>0}_generateNodeId(){return"node_"+ ++this._nodeIdCounter}_getCollapsedRangeForLine(e){for(const[t,s]of this._nodeIdToLines)if(this.collapsedNodes.has(t)&&e>s.startLine&&e<s.endLine)return{nodeId:t,...s};return null}_getCollapsedClosingLine(e){for(const[t,s]of this._nodeIdToLines)if(this.collapsedNodes.has(t)&&e===s.endLine)return{nodeId:t,...s};return null}_getClosingBracketPos(e){return Math.max(e.lastIndexOf("]"),e.lastIndexOf("}"))}_getCollapsedNodeAtLine(e){const t=this._lineToNodeId.get(e);if(t&&this.collapsedNodes.has(t)){const e=this._nodeIdToLines.get(t);if(e)return{nodeId:t,...e}}return null}_getContainingExpandedNode(e){let t=null;for(const[s,i]of this._nodeIdToLines)this.collapsedNodes.has(s)||e>=i.startLine&&e<=i.endLine&&(!t||i.endLine-i.startLine<t.endLine-t.startLine)&&(t={nodeId:s,...i});return t}_deleteCollapsedNode(e){this._saveToHistory("delete");const t=e.endLine-e.startLine+1;this.lines.splice(e.startLine,t),this.cursorLine=Math.min(e.startLine,this.lines.length-1),this.cursorColumn=0,this.formatAndUpdate()}_rebuildNodeIdMappings(){const e=/* @__PURE__ */new Set;for(const s of this.collapsedNodes){const t=this._nodeIdToLines.get(s);t?.uniqueKey&&e.add(t.uniqueKey)}this._nodeIdCounter=0,this._lineToNodeId.clear(),this._nodeIdToLines.clear(),this.collapsedNodes.clear();const t=/* @__PURE__ */new Map;for(let s=0;s<this.lines.length;s++){const i=this.lines[s],n=i.match(E),r=!n&&i.match(j);if(!n&&!r)continue;let o,l;if(n)o=n[1],l=n[2];else{if(!r)continue;l=r[1],o=`__root_${l}_${s}`}const a=K(i.substring(i.indexOf(l)+1),l);if(a.close>a.open)continue;const h=this._findClosingLine(s,l);if(-1===h||h===s)continue;const c=this._generateNodeId(),d=t.get(o)||0;t.set(o,d+1);const u=`${o}:${d}`;this._lineToNodeId.set(s,c),this._nodeIdToLines.set(c,{startLine:s,endLine:h,nodeKey:o,uniqueKey:u,isRootFeature:!!r}),e.has(u)&&!this._openedNodeKeys.has(u)&&this.collapsedNodes.add(c)}}static get observedAttributes(){return["readonly","value","placeholder","dark-selector"]}connectedCallback(){this.render(),this._cacheElements(),this.setupEventListeners(),this.updatePrefixSuffix(),this.updateThemeCSS(),this.value&&this.setValue(this.value),this.updatePlaceholderVisibility()}disconnectedCallback(){this.renderTimer&&clearTimeout(this.renderTimer),this.inputTimer&&clearTimeout(this.inputTimer);const e=document.querySelector(".geojson-color-picker-input");e&&(e._closeListener&&document.removeEventListener("click",e._closeListener,!0),e.remove())}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"value":this.setValue(s);break;case"readonly":this.updateReadonly();break;case"placeholder":this.updatePlaceholderContent();break;case"dark-selector":this.updateThemeCSS()}}get readonly(){return this.hasAttribute("readonly")}get value(){return this.getAttribute("value")||""}get placeholder(){return this.getAttribute("placeholder")||""}get prefix(){return'{"type": "FeatureCollection", "features": ['}get suffix(){return"]}"}render(){const e=this.shadowRoot,t=z("style");t.textContent=':host *,:host *:before,:host *:after{box-sizing:border-box;font: 13px/1.5 Courier New,Courier,monospace;font-variant:normal;letter-spacing:0;word-spacing:0;text-transform:none;text-decoration:none;text-indent:0}:host{--line-height: 19.5px;--gutter-width: 50px;--editor-padding-y: 8px;--editor-padding-x: 12px;display:flex;flex-direction:column;position:relative;width:100%;height:400px;border-radius:4px;overflow:hidden}:host([readonly]) .editor-wrapper:after{content:"";position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(-45deg,rgba(128,128,128,.08),rgba(128,128,128,.08) 3px,transparent 3px,transparent 12px);z-index:1}:host([readonly]) .hidden-textarea{cursor:text}.editor-wrapper{position:relative;width:100%;flex:1;background:var(--bg-color, #fff);display:flex;overflow:hidden}.gutter{width:var(--gutter-width);background:var(--gutter-bg, #f0f0f0);border-right:1px solid var(--gutter-border, #e0e0e0);overflow:hidden;flex-shrink:0;position:relative}.gutter-scroll{position:absolute;inset:0;overflow:hidden;padding:8px 0}.gutter-scroll-content{position:relative;width:100%}.gutter-content{position:absolute;top:0;left:0;right:0;will-change:transform}.gutter-line{height:var(--line-height);display:flex;align-items:center;justify-content:flex-end;padding-right:4px;position:relative}.gutter-line.has-error:before{content:"";position:absolute;right:0;top:0;bottom:0;width:3px;background:var(--error-color, #dc3545)}.line-number{font-size:11px;color:var(--gutter-text, #999);-webkit-user-select:none;user-select:none;min-width:20px;text-align:right}.collapse-column{width:16px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.collapse-button{width:12px;height:12px;border-radius:2px;cursor:pointer;flex-shrink:0;background:transparent;border:none;color:var(--json-punct, #a9b7c6);font-size:10px;display:flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none;opacity:0;transition:transform .1s,opacity .15s}.collapse-button.collapsed,.gutter:hover .collapse-button{opacity:1}.collapse-button:hover{transform:scale(1.2)}@media(hover:none),(pointer:coarse){.collapse-button{opacity:1}}.editor-content{position:relative;flex:1;overflow:hidden}.hidden-textarea{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;padding:0;margin:0;border:none;outline:none;resize:none;overflow:hidden;z-index:-1;pointer-events:none;caret-color:transparent}.viewport{position:absolute;inset:0;overflow:auto;padding:var(--editor-padding-y) var(--editor-padding-x);overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--control-border, #c0c0c0) var(--control-bg, #e8e8e8)}.scroll-content{position:relative;width:100%}.lines-container{position:absolute;top:0;left:0;right:0;will-change:transform}.line{height:var(--line-height);white-space:pre;display:block;position:relative}.cursor{position:absolute;width:2px;height:1em;background:var(--caret-color, #000);top:.15em;pointer-events:none;animation:cursor-blink 1s step-end infinite}.cursor.cursor-block{width:.6em;opacity:.5;animation:cursor-blink-block 1s step-end infinite}@keyframes cursor-blink-block{0%,to{opacity:.5}50%{opacity:.2}}@keyframes cursor-blink{0%,to{opacity:1}50%{opacity:0}}.selection{position:absolute;height:100%;top:0;background:var(--selection-color, rgba(51, 153, 255, .3));pointer-events:none;z-index:0}.line-hidden>span{opacity:.35;filter:grayscale(50%)}.placeholder-layer{position:absolute;top:var(--editor-padding-y);left:var(--editor-padding-x);color:#6a6a6a;pointer-events:none;z-index:0;white-space:pre}.json-key{color:var(--json-key, #660e7a)}.json-string{color:var(--json-string, #008000)}.json-number{color:var(--json-number, #00f)}.json-boolean,.json-null{color:var(--json-boolean, #000080)}.json-punctuation{color:var(--json-punct, #000)}.json-error{color:var(--json-error, #f00)}.geojson-key{color:var(--geojson-key, #660e7a);font-weight:600}.geojson-type{color:var(--geojson-type, #008000);font-weight:600}.geojson-type-invalid{color:var(--geojson-type-invalid, #f00);font-weight:600}.collapsed-bracket-array,.collapsed-bracket-object{color:var(--json-punct, #000)}.collapsed-bracket-array:after,.collapsed-bracket-object:after{content:"…";color:var(--json-punct, #888)}.prefix-wrapper,.suffix-wrapper{display:flex;flex-shrink:0;background:var(--bg-color, #fff)}.prefix-gutter,.suffix-gutter{width:var(--gutter-width);background:var(--gutter-bg, #f0f0f0);border-right:1px solid var(--gutter-border, #e0e0e0);flex-shrink:0}.editor-prefix,.editor-suffix{flex:1;padding:4px 12px;color:var(--text-color, #000);background:var(--bg-color, #fff);-webkit-user-select:none;user-select:none;white-space:pre-wrap;word-wrap:break-word;opacity:.6}.prefix-wrapper{border-bottom:1px solid rgba(255,255,255,.1);position:relative}.suffix-wrapper{border-top:1px solid rgba(255,255,255,.1);position:relative}.info-btn{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:transparent;border:none;color:var(--text-color, #000);opacity:.15;cursor:pointer;font-size:.7rem;width:1rem;height:1rem;padding:0;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:opacity .2s;font-family:sans-serif}.info-btn:hover{opacity:.5}.clear-btn{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:transparent;border:none;color:var(--text-color, #000);opacity:.3;cursor:pointer;font-size:.65rem;width:1rem;height:1rem;padding:.15rem 0 0;border-radius:3px;display:flex;align-items:center;justify-content:center;transition:opacity .2s,background .2s}.clear-btn:hover{opacity:.7;background:#ffffff1a}.clear-btn[hidden]{display:none}.error-nav{display:none;align-items:center;gap:4px;margin-right:24px}.error-nav.visible{display:flex}.error-nav-btn{background:transparent;border:none;color:var(--error-color, #dc3545);cursor:pointer;font-size:8px;width:16px;height:16px;padding:0;display:flex;align-items:center;justify-content:center;opacity:.7;transition:opacity .2s}.error-nav-btn:hover{opacity:1}.error-count{color:var(--error-color, #dc3545);font-size:11px;min-width:20px;text-align:center}.viewport::-webkit-scrollbar{width:10px;height:10px}.viewport::-webkit-scrollbar-track{background:var(--control-bg, #e8e8e8)}.viewport::-webkit-scrollbar-thumb{background:var(--control-border, #c0c0c0);border-radius:5px}.viewport::-webkit-scrollbar-thumb:hover{background:var(--control-color, #000080)}.json-color,.json-boolean{position:relative}.json-color:before,.json-boolean:before{content:"";position:absolute;left:-8px;top:50%;transform:translateY(-50%);margin-top:-1px;width:8px;height:8px;border-radius:2px;cursor:pointer}.json-color:hover:before,.json-boolean:hover:before{transform:translateY(-50%) scale(1.2);border-color:var(--control-color, #000080)}.json-color:before{background-color:var(--swatch-color);border:1px solid var(--json-punct, #a9b7c6)}.json-boolean:before{border:1.5px solid var(--control-border, #c0c0c0);background:transparent}.json-bool-true:before{content:"✔";border-color:var(--control-color, #000080);color:var(--control-color, #000080);font-size:8px;display:flex;align-items:center;justify-content:center;line-height:8px}.line.has-visibility:before{content:"👁";position:absolute;left:0;top:3px;font-size:10px;color:var(--control-color, #000080);opacity:.6;cursor:pointer;z-index:1}.line.has-visibility:hover:before{opacity:1;transform:scale(1.15)}.line.has-visibility.feature-hidden:before{opacity:.5}';const s=z("div");for(s.innerHTML=function(e="",t=""){return`\n <div class="prefix-wrapper">\n <div class="prefix-gutter"></div>\n <div class="editor-prefix" id="editorPrefix"></div>\n <button class="info-btn" id="infoBtn" title="@softwarity/geojson-editor v${t}" aria-label="About">ⓘ</button>\n </div>\n <div class="editor-wrapper">\n <div class="gutter">\n <div class="gutter-scroll" id="gutterScroll">\n <div class="gutter-scroll-content" id="gutterScrollContent">\n <div class="gutter-content" id="gutterContent"></div>\n </div>\n </div>\n </div>\n <div class="editor-content">\n <div class="placeholder-layer" id="placeholderLayer">${s=e,s?s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""}</div>\n <textarea\n class="hidden-textarea"\n id="hiddenTextarea"\n spellcheck="false"\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n tabindex="0"\n ></textarea>\n <div class="viewport" id="viewport">\n <div class="scroll-content" id="scrollContent">\n <div class="lines-container" id="linesContainer"></div>\n </div>\n </div>\n </div>\n </div>\n <div class="suffix-wrapper">\n <div class="suffix-gutter"></div>\n <div class="editor-suffix" id="editorSuffix"></div>\n <div class="error-nav" id="errorNav">\n <button class="error-nav-btn" id="prevErrorBtn" title="Previous error">◀</button>\n <span class="error-count" id="errorCount"></span>\n <button class="error-nav-btn" id="nextErrorBtn" title="Next error">▶</button>\n </div>\n <button class="clear-btn" id="clearBtn" title="Clear editor">✕</button>\n </div>\n `;var s}(this.placeholder,"1.0.19"),e.innerHTML="",e.appendChild(t);s.firstChild;)e.appendChild(s.firstChild)}_cacheElements(){this._viewport=this._id("viewport"),this._linesContainer=this._id("linesContainer"),this._scrollContent=this._id("scrollContent"),this._hiddenTextarea=this._id("hiddenTextarea"),this._gutterContent=this._id("gutterContent"),this._gutterScrollContent=this._id("gutterScrollContent"),this._gutterScroll=this._id("gutterScroll"),this._gutter=this.shadowRoot.querySelector(".gutter"),this._clearBtn=this._id("clearBtn"),this._editorWrapper=this.shadowRoot.querySelector(".editor-wrapper"),this._placeholderLayer=this._id("placeholderLayer"),this._editorPrefix=this._id("editorPrefix"),this._editorSuffix=this._id("editorSuffix"),this._errorNav=this._id("errorNav"),this._errorCount=this._id("errorCount"),this._prevErrorBtn=this._id("prevErrorBtn"),this._nextErrorBtn=this._id("nextErrorBtn")}setupEventListeners(){const e=this._hiddenTextarea,t=this._viewport,s=this._gutterContent,i=this._gutter,n=this._clearBtn,r=this._editorWrapper;if(!(e&&t&&s&&i&&n&&r))return;this._isSelecting=!1,t.addEventListener("click",e=>{this.handleEditorClick(e)},!0),t.addEventListener("mousedown",t=>{const s=t.target,i=s.closest(".line.has-visibility");if(i){const e=i.getBoundingClientRect();if(t.clientX-e.left<14)return void(this._blockRender=!0)}if(s.classList.contains("json-color")||s.classList.contains("json-boolean")){const e=s.getBoundingClientRect(),i=t.clientX-e.left;if(i<0&&i>=-8)return void(this._blockRender=!0)}t.preventDefault();const n=this._getPositionFromClick(t);t.shiftKey&&this.selectionStart?(this.selectionEnd=n,this.cursorLine=n.line,this.cursorColumn=n.column):(this.cursorLine=n.line,this.cursorColumn=n.column,this.selectionStart={line:n.line,column:n.column},this.selectionEnd=null,this._isSelecting=!0),e.focus(),this._invalidateRenderCache(),this.scheduleRender()}),t.addEventListener("mousemove",e=>{if(!this._isSelecting)return;const s=this._getPositionFromClick(e);this.selectionEnd=s,this.cursorLine=s.line,this.cursorColumn=s.column;const i=t.getBoundingClientRect();e.clientY<i.top+30?t.scrollTop-=20:e.clientY>i.bottom-30&&(t.scrollTop+=20),this._invalidateRenderCache(),this.scheduleRender()}),document.addEventListener("mouseup",()=>{this._isSelecting=!1}),e.addEventListener("focus",()=>{r.classList.add("focused"),this._invalidateRenderCache(),this.scheduleRender()}),e.addEventListener("blur",()=>{r.classList.remove("focused"),this._invalidateRenderCache(),this.scheduleRender()});let o=!1;t.addEventListener("scroll",()=>{o||(this.syncGutterScroll(),this._scrollRaf||(this._scrollRaf=requestAnimationFrame(()=>{this._scrollRaf=null,o=!0,this.renderViewport(),o=!1})))}),e.addEventListener("compositionstart",()=>{this._isComposing=!0}),e.addEventListener("compositionend",()=>{this._isComposing=!1,this.handleInput()}),e.addEventListener("input",()=>{this._isComposing||this.handleInput()}),e.addEventListener("keydown",e=>{this.handleKeydown(e)}),e.addEventListener("paste",e=>{this.handlePaste(e)}),e.addEventListener("copy",e=>{this.handleCopy(e)}),e.addEventListener("cut",e=>{this.handleCut(e)}),s.addEventListener("click",e=>{this.handleGutterClick(e)}),i.addEventListener("mousedown",e=>{e.preventDefault()}),i.addEventListener("wheel",e=>{e.preventDefault(),t.scrollTop+=e.deltaY}),n.addEventListener("click",()=>{this.removeAll()}),this._prevErrorBtn?.addEventListener("click",()=>{this.goToPrevError()}),this._nextErrorBtn?.addEventListener("click",()=>{this.goToNextError()}),this.updateReadonly()}setValue(e,t=!0){if(this.lines.length>0&&this._saveToHistory("setValue"),e&&e.trim())try{const t="["+e+"]",s=JSON.parse(t),i=JSON.stringify(s,null,2).split("\n");this.lines=i.slice(1,-1)}catch(s){this.lines=e.split("\n")}else this.lines=[];this.collapsedNodes.clear(),this.hiddenFeatures.clear(),this._openedNodeKeys.clear(),this._lineToNodeId.clear(),this._nodeIdToLines.clear(),this.cursorLine=0,this.cursorColumn=0,this.updateModel(),this.scheduleRender(),this.updatePlaceholderVisibility(),t&&this.lines.length>0&&requestAnimationFrame(()=>{this.autoCollapseCoordinates()}),this.emitChange()}getContent(){return this.lines.join("\n")}updateModel(){this._contextMapCache=null,this._errorLinesCache=null,this._rebuildNodeIdMappings(),this.computeFeatureRanges(),this.computeLineMetadata(),this.computeVisibleLines()}updateView(){this.computeLineMetadata(),this.computeVisibleLines()}computeFeatureRanges(){this.featureRanges.clear();try{const e=this.lines.join("\n"),t=this.prefix+e+this.suffix,s=JSON.parse(t);if(!s.features)return;let i=0,n=0,r=!1,o=-1,l=null;for(let a=0;a<this.lines.length;a++){const e=this.lines[a];if(!r&&T.test(e)){let e=a;for(let t=a;t>=0;t--){const s=this.lines[t].trim();if("{"===s||"{,"===s){e=t;break}}o=e,r=!0,n=1;for(let t=e;t<=a;t++){const s=K(this.lines[t],"{");n+=t===e?s.open-1-s.close:s.open-s.close}i<s.features.length&&(l=M(s.features[i]))}else if(r){const t=K(e,"{");n+=t.open-t.close,n<=0&&(l&&this.featureRanges.set(l,{startLine:o,endLine:a,featureIndex:i}),i++,r=!1,l=null)}}}catch(e){}}computeLineMetadata(){this.lineMetadata.clear();const e=this._findCollapsibleRanges(),t=this._computeErrorLines();for(let s=0;s<this.lines.length;s++){const i=this.lines[s],n={colors:[],booleans:[],collapseButton:null,visibilityButton:null,isHidden:!1,isCollapsed:!1,featureKey:null,hasError:t.has(s)};let r;for(w.lastIndex=0;null!==(r=w.exec(i));){const[,e,t,s]=r;s?n.booleans.push({attributeName:e,value:"true"===s}):t&&(v.test(t)||O(t))&&n.colors.push({attributeName:e,color:t})}const o=e.find(e=>e.startLine===s);o&&(n.collapseButton={nodeKey:o.nodeKey,nodeId:o.nodeId,isCollapsed:this.collapsedNodes.has(o.nodeId)}),e.find(e=>this.collapsedNodes.has(e.nodeId)&&s>e.startLine&&s<e.endLine)&&(n.isCollapsed=!0);for(const[e,t]of this.featureRanges)if(s>=t.startLine&&s<=t.endLine){n.featureKey=e,this.hiddenFeatures.has(e)&&(n.isHidden=!0),s===t.startLine&&(n.visibilityButton={featureKey:e,isHidden:this.hiddenFeatures.has(e)});break}this.lineMetadata.set(s,n)}}_computeErrorLines(){if(null!==this._errorLinesCache)return this._errorLinesCache;const e=/* @__PURE__ */new Set;for(let s=0;s<this.lines.length;s++)V(this.lines[s],"",void 0).includes("json-error")&&e.add(s);try{const e="["+this.lines.join("\n")+"]";JSON.parse(e)}catch(t){if(t instanceof Error){const s=t.message.match(/line (\d+)/);if(s){const t=Math.max(0,parseInt(s[1],10)-1);e.add(t)}}}return this._errorLinesCache=e,e}_getErrorLines(){const e=[];for(const[t,s]of this.lineMetadata)s.hasError&&e.push(t);return e.sort((e,t)=>e-t)}goToNextError(){const e=this._getErrorLines();if(0===e.length)return!1;const t=e.find(e=>e>this.cursorLine),s=void 0!==t?t:e[0];return this._goToErrorLine(s)}goToPrevError(){const e=this._getErrorLines();if(0===e.length)return!1;const t=e.filter(e=>e<this.cursorLine),s=t.length>0?t[t.length-1]:e[e.length-1];return this._goToErrorLine(s)}_expandNodesContainingLine(e){let t=!1;for(const[s,i]of this._nodeIdToLines)this.collapsedNodes.has(s)&&e>i.startLine&&e<=i.endLine&&(this.collapsedNodes.delete(s),i.uniqueKey&&this._openedNodeKeys.add(i.uniqueKey),t=!0);return t}_goToErrorLine(e){return this._expandNodesContainingLine(e)&&this.updateView(),this.cursorLine=e,this.cursorColumn=0,this._invalidateRenderCache(),this._scrollToCursor(!0),this.renderViewport(),this._updateErrorDisplay(),this._hiddenTextarea?.focus(),!0}_expandErrorNodes(){const e=this._getErrorLines();if(0===e.length)return;let t=!1;for(const s of e)this._expandNodesContainingLine(s)&&(t=!0);t&&this.updateView()}computeVisibleLines(){this.visibleLines=[];for(let e=0;e<this.lines.length;e++){const t=this.lineMetadata.get(e);t&&t.isCollapsed||this.visibleLines.push({index:e,content:this.lines[e],meta:t})}this._invalidateRenderCache()}scheduleRender(){this.renderTimer||(this.renderTimer=requestAnimationFrame(()=>{this.renderTimer=void 0,this.renderViewport()}))}renderViewport(){if(this._blockRender)return;const e=this._viewport,t=this._linesContainer,s=this._scrollContent;if(!e||!t)return;this.viewportHeight=e.clientHeight;const i=this.visibleLines.length,n=i*this.lineHeight;if(s){s.style.height=`${n}px`;const e=this._getCharWidth(),t=this.lines.reduce((e,t)=>Math.max(e,t.length),0)*e+20;s.style.minWidth=`${t}px`}const r=e.scrollTop,o=Math.floor(r/this.lineHeight),l=Math.ceil(this.viewportHeight/this.lineHeight),a=Math.max(0,o-this.bufferLines),h=Math.min(i,o+l+this.bufferLines);if(i>0&&this._lastStartIndex===a&&this._lastEndIndex===h&&this._lastTotalLines===i)return;this._lastStartIndex=a,this._lastEndIndex=h,this._lastTotalLines=i;const c=a*this.lineHeight;t.style.transform=`translateY(${c}px)`;const d=this._buildContextMap(),u=this._editorWrapper?.classList.contains("focused"),p=document.createDocumentFragment();if(0===i){const e=z("div");return e.className="line empty-line",e.dataset.lineIndex="0",u&&(e.innerHTML=this._insertCursor(0)),p.appendChild(e),t.innerHTML="",t.appendChild(p),void this.renderGutter(0,0)}for(let g=a;g<h;g++){const e=this.visibleLines[g];if(!e)continue;const t=z("div");t.className="line",t.dataset.lineIndex=String(e.index),e.meta?.visibilityButton&&(t.classList.add("has-visibility"),t.dataset.featureKey=e.meta.visibilityButton.featureKey,e.meta.visibilityButton.isHidden&&t.classList.add("feature-hidden")),e.meta?.isHidden&&t.classList.add("line-hidden");const s=d.get(e.index)||"Feature";let i=V(e.content,s,e.meta);u&&this._hasSelection()&&(i=this._addSelectionHighlight(i,e.index,e.content)),u&&e.index===this.cursorLine&&(i+=this._insertCursor(this.cursorColumn)),t.innerHTML=i,p.appendChild(t)}t.innerHTML="",t.appendChild(p),this.renderGutter(a,h)}_insertCursor(e){const t=this._getCharWidth(),s=e*t;return this._insertMode?`<span class="cursor" style="left: ${s}px"></span>`:`<span class="cursor cursor-block" style="left: ${s}px; width: ${t}px"></span>`}_addSelectionHighlight(e,t,s){const i=this._normalizeSelection();if(!i)return e;const{start:n,end:r}=i;if(t<n.line||t>r.line)return e;const o=this._getCharWidth();let l,a;return t===n.line&&t===r.line?(l=n.column,a=r.column):t===n.line?(l=n.column,a=s.length):t===r.line?(l=0,a=r.column):(l=0,a=s.length),`<span class="selection" style="left: ${l*o}px; width: ${(a-l)*o}px"></span>`+e}_getCharWidth(){if(!this._charWidth){const e=z("canvas").getContext("2d");e?(e.font="13px 'Courier New', Courier, monospace",this._charWidth=e.measureText("M").width):this._charWidth=7.8}return this._charWidth}renderGutter(e,t){const s=this._gutterContent,i=this._gutterScrollContent;if(!s)return;const n=this.visibleLines.length*this.lineHeight;i&&(i.style.height=`${n}px`);const r=e*this.lineHeight;s.style.transform=`translateY(${r}px)`;const o=document.createDocumentFragment();for(let l=e;l<t;l++){const e=this.visibleLines[l];if(!e)continue;const t=z("div");t.className="gutter-line";const s=e.meta;s?.hasError&&t.classList.add("has-error");const i=z("span");i.className="line-number",i.textContent=String(e.index+1),t.appendChild(i);const n=z("div");if(n.className="collapse-column",s?.collapseButton){const t=z("div");t.className="collapse-button"+(s.collapseButton.isCollapsed?" collapsed":""),t.textContent=s.collapseButton.isCollapsed?"›":"⌄",t.dataset.line=String(e.index),t.dataset.nodeId=s.collapseButton.nodeId,t.title=s.collapseButton.isCollapsed?"Expand":"Collapse",n.appendChild(t)}t.appendChild(n),o.appendChild(t)}s.innerHTML="",s.appendChild(o)}syncGutterScroll(){this._gutterScroll&&this._viewport&&(this._gutterScroll.scrollTop=this._viewport.scrollTop)}handleInput(){const e=this._hiddenTextarea,t=e?.value;if(t)if(this._hasSelection()&&this._deleteSelection(),this._getCollapsedRangeForLine(this.cursorLine))e.value="";else{if(this._getCollapsedClosingLine(this.cursorLine)){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);if(this.cursorColumn<=s)return void(e.value="")}if(this._getCollapsedNodeAtLine(this.cursorLine)){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t)return void(e.value="")}if(this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],s=e.substring(0,this.cursorColumn),i=t.split("\n");if(1===i.length){if(this._insertMode){const i=e.substring(this.cursorColumn);this.lines[this.cursorLine]=s+t+i}else{const i=e.substring(this.cursorColumn+t.length);this.lines[this.cursorLine]=s+t+i}this.cursorColumn+=t.length}else{const t=e.substring(this.cursorColumn);this.lines[this.cursorLine]=s+i[0];for(let e=1;e<i.length-1;e++)this.lines.splice(this.cursorLine+e,0,i[e]);const n=i[i.length-1]+t;this.lines.splice(this.cursorLine+i.length-1,0,n),this.cursorLine+=i.length-1,this.cursorColumn=i[i.length-1].length}}else{const e=t.split("\n");this.lines.push(...e),this.cursorLine=this.lines.length-1,this.cursorColumn=this.lines[this.cursorLine].length}e.value="",clearTimeout(this.inputTimer),this.inputTimer=setTimeout(()=>{this.formatAndUpdate()},150)}}handleKeydown(e){const t={inCollapsedZone:this._getCollapsedRangeForLine(this.cursorLine),onCollapsedNode:this._getCollapsedNodeAtLine(this.cursorLine),onClosingLine:this._getCollapsedClosingLine(this.cursorLine)},s={Enter:()=>this._handleEnter(e.shiftKey,t),Backspace:()=>this._handleBackspace(t),Delete:()=>this._handleDelete(t),ArrowUp:()=>this._handleArrowKey(-1,0,e.shiftKey,e.ctrlKey||e.metaKey),ArrowDown:()=>this._handleArrowKey(1,0,e.shiftKey,e.ctrlKey||e.metaKey),ArrowLeft:()=>this._handleArrowKey(0,-1,e.shiftKey,e.ctrlKey||e.metaKey),ArrowRight:()=>this._handleArrowKey(0,1,e.shiftKey,e.ctrlKey||e.metaKey),Home:()=>this._handleHomeEnd("home",e.shiftKey,t.onClosingLine),End:()=>this._handleHomeEnd("end",e.shiftKey,t.onClosingLine),PageUp:()=>this._handlePageUpDown("up",e.shiftKey),PageDown:()=>this._handlePageUpDown("down",e.shiftKey),Tab:()=>this._handleTab(e.shiftKey,t),Insert:()=>{this._insertMode=!this._insertMode,this.scheduleRender()}},i={a:()=>this._selectAll(),z:()=>e.shiftKey?this.redo():this.undo(),y:()=>this.redo(),s:()=>this.save(),o:()=>!this.hasAttribute("readonly")&&this.open()};if(s[e.key])return e.preventDefault(),void s[e.key]();(e.ctrlKey||e.metaKey)&&i[e.key]&&(e.preventDefault(),i[e.key]())}_handleEnter(e,t){if(e){const e=this._getContainingExpandedNode(this.cursorLine);if(e){const t=this.lines[e.startLine],s=t.search(N);this.toggleCollapse(e.nodeId),this.cursorLine=e.startLine,this.cursorColumn=s>=0?s+1:t.length,this._clearSelection(),this._scrollToCursor()}return}if(t.onCollapsedNode)this.toggleCollapse(t.onCollapsedNode.nodeId);else{if(t.onClosingLine){const e=this.lines[this.cursorLine],s=this._getClosingBracketPos(e);if(s>=0&&this.cursorColumn<=s)return void this.toggleCollapse(t.onClosingLine.nodeId)}t.inCollapsedZone}}_handleBackspace(e){if(this._hasSelection())return this._deleteSelection(),void this.formatAndUpdate();if(e.onClosingLine){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);return s>=0&&this.cursorColumn>s+1?void this.deleteBackward():void this._deleteCollapsedNode(e.onClosingLine)}if(e.onCollapsedNode&&0===this.cursorColumn)this._deleteCollapsedNode(e.onCollapsedNode);else if(!e.inCollapsedZone){if(e.onCollapsedNode){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t+1)return void this._deleteCollapsedNode(e.onCollapsedNode)}this.deleteBackward()}}_handleDelete(e){if(this._hasSelection())return this._deleteSelection(),void this.formatAndUpdate();if(e.onClosingLine){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);return s>=0&&this.cursorColumn>s?void this.deleteForward():void this._deleteCollapsedNode(e.onClosingLine)}if(e.onCollapsedNode){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t)return void this._deleteCollapsedNode(e.onCollapsedNode)}e.inCollapsedZone||this.deleteForward()}_handleTab(e,t){e?this._navigateToPrevAttribute():this._navigateToNextAttribute()}_navigateToNextAttribute(){const e=this.visibleLines.length;let t=this.visibleLines.findIndex(e=>e.index===this.cursorLine);t<0&&(t=0);for(let s=t;s<e;s++){const e=this.visibleLines[s],i=this.lines[e.index],n=s===t?this.cursorColumn:0,r=this._findNextAttributeOrBracket(i,n,e.index);if(null!==r)return this.cursorLine=e.index,this.cursorColumn=r.start,r.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:r.start},this.selectionEnd={line:e.index,column:r.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}for(let s=0;s<t;s++){const e=this.visibleLines[s],t=this.lines[e.index],i=this._findNextAttributeOrBracket(t,0,e.index);if(null!==i)return this.cursorLine=e.index,this.cursorColumn=i.start,i.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:i.start},this.selectionEnd={line:e.index,column:i.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}}_navigateToPrevAttribute(){const e=this.visibleLines.length;let t=this.visibleLines.findIndex(e=>e.index===this.cursorLine);t<0&&(t=e-1);for(let s=t;s>=0;s--){const e=this.visibleLines[s],i=this.lines[e.index],n=s===t?this.cursorColumn:i.length,r=this._findPrevAttributeOrBracket(i,n,e.index);if(null!==r)return this.cursorLine=e.index,this.cursorColumn=r.start,r.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:r.start},this.selectionEnd={line:e.index,column:r.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}for(let s=e-1;s>t;s--){const e=this.visibleLines[s],t=this.lines[e.index],i=this._findPrevAttributeOrBracket(t,t.length,e.index);if(null!==i)return this.cursorLine=e.index,this.cursorColumn=i.start,i.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:i.start},this.selectionEnd={line:e.index,column:i.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}}_findNextAttributeInLine(e,t){const s=[],i=/"([^"]+)"(?:\s*:\s*(?:"([^"]*)"|(-?\d+\.?\d*(?:e[+-]?\d+)?)|true|false|null))?/gi;let n;for(;null!==(n=i.exec(e));){const t=n.index+1,i=t+n[1].length;if(s.push({start:t,end:i}),void 0!==n[2]){const t=e.substring(n.index).match(/:\s*"([^"]*)"/);if(t){const e=n.index+(t.index||0)+t[0].indexOf('"')+1,i=e+n[2].length;s.push({start:e,end:i})}}else if(void 0!==n[3]){const t=e.substring(n.index).match(/:\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)/i);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}else{const t=e.substring(n.index).match(/:\s*(true|false|null)/);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}}const r=/(?:^|[\[,\s])(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*(?:[,\]]|$)/gi;for(;null!==(n=r.exec(e));){const e=n[1],t=n.index+n[0].indexOf(e),i=t+e.length;s.some(e=>e.start===t&&e.end===i)||s.push({start:t,end:i})}s.sort((e,t)=>e.start-t.start);for(const o of s)if(o.start>t)return o;return null}_findPrevAttributeInLine(e,t){const s=[],i=/"([^"]+)"(?:\s*:\s*(?:"([^"]*)"|(-?\d+\.?\d*(?:e[+-]?\d+)?)|true|false|null))?/gi;let n;for(;null!==(n=i.exec(e));){const t=n.index+1,i=t+n[1].length;if(s.push({start:t,end:i}),void 0!==n[2]){const t=e.substring(n.index).match(/:\s*"([^"]*)"/);if(t){const e=n.index+(t.index||0)+t[0].indexOf('"')+1,i=e+n[2].length;s.push({start:e,end:i})}}else if(void 0!==n[3]){const t=e.substring(n.index).match(/:\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)/i);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}else{const t=e.substring(n.index).match(/:\s*(true|false|null)/);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}}const r=/(?:^|[\[,\s])(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*(?:[,\]]|$)/gi;for(;null!==(n=r.exec(e));){const e=n[1],t=n.index+n[0].indexOf(e),i=t+e.length;s.some(e=>e.start===t&&e.end===i)||s.push({start:t,end:i})}s.sort((e,t)=>e.start-t.start);for(let o=s.length-1;o>=0;o--)if(s[o].end<t)return s[o];return null}_findBracketInLine(e){const t=e.match(/[\[{]\s*$/);return t&&void 0!==t.index?t.index+1:null}_findNextAttributeOrBracket(e,t,s){const i=this._findNextAttributeInLine(e,t),n=this._findBracketInLine(e);return null!==i&&null!==n?n>t&&n<i.start?{start:n,end:n,isBracket:!0}:{...i,isBracket:!1}:null!==i?{...i,isBracket:!1}:null!==n&&n>t?{start:n,end:n,isBracket:!0}:null}_findPrevAttributeOrBracket(e,t,s){const i=this._findPrevAttributeInLine(e,t),n=this._findBracketInLine(e);return null!==i&&null!==n?n<t&&n>i.end?{start:n,end:n,isBracket:!0}:{...i,isBracket:!1}:null!==i?{...i,isBracket:!1}:null!==n&&n<t?{start:n,end:n,isBracket:!0}:null}insertNewline(){if(this._saveToHistory("newline"),this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],t=e.substring(0,this.cursorColumn),s=e.substring(this.cursorColumn);this.lines[this.cursorLine]=t,this.lines.splice(this.cursorLine+1,0,s),this.cursorLine++,this.cursorColumn=0}else this.lines.push(""),this.cursorLine=this.lines.length-1,this.cursorColumn=0;this.formatAndUpdate()}deleteBackward(){if(this._saveToHistory("delete"),this.cursorColumn>0){const e=this.lines[this.cursorLine];this.lines[this.cursorLine]=e.substring(0,this.cursorColumn-1)+e.substring(this.cursorColumn),this.cursorColumn--}else if(this.cursorLine>0){const e=this.lines[this.cursorLine],t=this.lines[this.cursorLine-1];this.cursorColumn=t.length,this.lines[this.cursorLine-1]=t+e,this.lines.splice(this.cursorLine,1),this.cursorLine--}this.formatAndUpdate()}deleteForward(){if(this._saveToHistory("delete"),this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine];this.cursorColumn<e.length?this.lines[this.cursorLine]=e.substring(0,this.cursorColumn)+e.substring(this.cursorColumn+1):this.cursorLine<this.lines.length-1&&(this.lines[this.cursorLine]=e+this.lines[this.cursorLine+1],this.lines.splice(this.cursorLine+1,1))}this.formatAndUpdate()}moveCursorSkipCollapsed(e){let t=this.cursorLine+e;for(;t>=0&&t<this.lines.length;){const s=this._getCollapsedRangeForLine(t);if(!s)break;t=e>0?s.endLine:s.startLine}this.cursorLine=Math.max(0,Math.min(this.lines.length-1,t));const s=this.lines[this.cursorLine]?.length||0;this.cursorColumn=Math.min(this.cursorColumn,s),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}moveCursorHorizontal(e){e>0?this._moveCursorRight():this._moveCursorLeft(),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_moveCursorRight(){const e=this.lines[this.cursorLine],t=this._getCollapsedNodeAtLine(this.cursorLine);if(this._getCollapsedClosingLine(this.cursorLine)){const t=this._getClosingBracketPos(e);this.cursorColumn<t?this.cursorColumn=t:this.cursorColumn>=e.length?this.cursorLine<this.lines.length-1&&(this.cursorLine++,this.cursorColumn=0):this.cursorColumn++}else if(t){const s=e.search(N);this.cursorColumn<s?this.cursorColumn++:this.cursorColumn===s?this.cursorColumn=s+1:(this.cursorLine=t.endLine,this.cursorColumn=this._getClosingBracketPos(this.lines[this.cursorLine]))}else if(this.cursorColumn>=e.length){if(this.cursorLine<this.lines.length-1){this.cursorLine++,this.cursorColumn=0;const e=this._getCollapsedRangeForLine(this.cursorLine);e&&(this.cursorLine=e.endLine,this.cursorColumn=0)}}else this.cursorColumn++}_moveCursorLeft(){const e=this.lines[this.cursorLine],t=this._getCollapsedNodeAtLine(this.cursorLine),s=this._getCollapsedClosingLine(this.cursorLine);if(s){const t=this._getClosingBracketPos(e);if(this.cursorColumn>t+1)this.cursorColumn--;else{this.cursorLine=s.startLine;const e=this.lines[this.cursorLine];this.cursorColumn=e.search(N)+1}}else if(t){const t=e.search(N);this.cursorColumn>t+1?this.cursorColumn=t+1:this.cursorColumn===t+1?this.cursorColumn=t:this.cursorColumn>0?this.cursorColumn--:this.cursorLine>0&&(this.cursorLine--,this.cursorColumn=this.lines[this.cursorLine]?.length||0)}else if(this.cursorColumn>0)this.cursorColumn--;else if(this.cursorLine>0)if(this.cursorLine--,this._getCollapsedClosingLine(this.cursorLine))this.cursorColumn=this.lines[this.cursorLine]?.length||0;else{const e=this._getCollapsedRangeForLine(this.cursorLine);if(e){this.cursorLine=e.startLine;const t=this.lines[this.cursorLine];this.cursorColumn=t.search(N)+1}else this.cursorColumn=this.lines[this.cursorLine]?.length||0}}_scrollToCursor(e=!1){const t=this._viewport;if(!t)return;const s=this.visibleLines.findIndex(e=>e.index===this.cursorLine);if(-1===s)return;const i=s*this.lineHeight,n=t.clientHeight;if(e)t.scrollTop=Math.max(0,i-n/2+this.lineHeight/2);else{const e=t.scrollTop,s=e+n;i<e?t.scrollTop=i:i+this.lineHeight>s&&(t.scrollTop=i+this.lineHeight-n)}}_handleArrowKey(e,t,s,i=!1){s&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn}),0!==e?this.moveCursorSkipCollapsed(e):0!==t&&(i?this._moveCursorByWord(t):this.moveCursorHorizontal(t)),s?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null)}_moveCursorByWord(e){const t=this.lines[this.cursorLine]||"",s=e=>R.test(e),i=this._getCollapsedNodeAtLine(this.cursorLine);if(e>0){let e=this.cursorColumn;if(i){const s=t.search(N);if(s>=0&&e>=s)return this.cursorLine=i.endLine,this.cursorColumn=(this.lines[this.cursorLine]||"").length,this._invalidateRenderCache(),this._scrollToCursor(),void this.scheduleRender()}if(e>=t.length){if(this.cursorLine<this.lines.length-1){let e=this.cursorLine+1;const t=this._getCollapsedRangeForLine(e);t&&(e=t.endLine),this.cursorLine=Math.min(e,this.lines.length-1),this.cursorColumn=0}}else if(s(t[e])){for(;e<t.length&&s(t[e]);)e++;this.cursorColumn=e}else{for(;e<t.length&&!s(t[e]);)e++;this.cursorColumn=e}}else{let e=this.cursorColumn;const i=this._getCollapsedClosingLine(this.cursorLine);if(i){const s=this._getClosingBracketPos(t);if(s>=0&&e<=s+1){this.cursorLine=i.startLine;const e=(this.lines[this.cursorLine]||"").search(N);return this.cursorColumn=e>=0?e:0,this._invalidateRenderCache(),this._scrollToCursor(),void this.scheduleRender()}}if(0===e){if(this.cursorLine>0){let e=this.cursorLine-1;const t=this._getCollapsedRangeForLine(e);t&&(e=t.startLine),this.cursorLine=Math.max(e,0),this.cursorColumn=this.lines[this.cursorLine].length}}else if(e>0&&s(t[e-1])){for(;e>0&&s(t[e-1]);)e--;this.cursorColumn=e}else{for(;e>0&&!s(t[e-1]);)e--;for(;e>0&&s(t[e-1]);)e--;this.cursorColumn=e}}this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_handleHomeEnd(e,t,s){if(t&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn}),"home"===e)s?(this.cursorLine=s.startLine,this.cursorColumn=0):0===this.cursorColumn?(this.cursorLine=0,this.cursorColumn=0):this.cursorColumn=0;else{const e=this.lines[this.cursorLine]?.length||0;this.cursorColumn===e?(this.cursorLine=this.lines.length-1,this.cursorColumn=this.lines[this.cursorLine]?.length||0):this.cursorColumn=e}t?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_handlePageUpDown(e,t){t&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn});const s=this._viewport;if(!s)return;const i=Math.floor(s.clientHeight/this.lineHeight);if("up"===e){const e=this.visibleLines.findIndex(e=>e.index===this.cursorLine),t=Math.max(0,e-i);this.cursorLine=this.visibleLines[t]?.index||0}else{const e=this.visibleLines.findIndex(e=>e.index===this.cursorLine),t=Math.min(this.visibleLines.length-1,e+i);this.cursorLine=this.visibleLines[t]?.index||this.lines.length-1}this.cursorColumn=Math.min(this.cursorColumn,this.lines[this.cursorLine]?.length||0),t?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_selectAll(){this.selectionStart={line:0,column:0};const e=this.lines.length-1;this.selectionEnd={line:e,column:this.lines[e]?.length||0},this.cursorLine=e,this.cursorColumn=this.lines[e]?.length||0,this._invalidateRenderCache(),this.scheduleRender()}_getSelectedText(){const e=this._normalizeSelection();if(!e)return"";const{start:t,end:s}=e;if(t.line===s.line)return this.lines[t.line].substring(t.column,s.column);let i=this.lines[t.line].substring(t.column)+"\n";for(let n=t.line+1;n<s.line;n++)i+=this.lines[n]+"\n";return i+=this.lines[s.line].substring(0,s.column),i}_normalizeSelection(){if(!this.selectionStart||!this.selectionEnd)return null;const e=this.selectionStart,t=this.selectionEnd;return e.line<t.line||e.line===t.line&&e.column<=t.column?{start:e,end:t}:{start:t,end:e}}_hasSelection(){return!(!this.selectionStart||!this.selectionEnd||this.selectionStart.line===this.selectionEnd.line&&this.selectionStart.column===this.selectionEnd.column)}_clearSelection(){this.selectionStart=null,this.selectionEnd=null}_deleteSelection(){const e=this._normalizeSelection();if(!e)return!1;const{start:t,end:s}=e;if(this._saveToHistory("delete"),t.line===s.line){const e=this.lines[t.line];this.lines[t.line]=e.substring(0,t.column)+e.substring(s.column)}else{const e=this.lines[t.line].substring(0,t.column),i=this.lines[s.line].substring(s.column);this.lines[t.line]=e+i,this.lines.splice(t.line+1,s.line-t.line)}return this.cursorLine=t.line,this.cursorColumn=t.column,this.selectionStart=null,this.selectionEnd=null,!0}insertText(e){if(this._hasSelection()&&this._deleteSelection(),!this._getCollapsedRangeForLine(this.cursorLine)){if(this._getCollapsedClosingLine(this.cursorLine)){const e=this.lines[this.cursorLine],t=this._getClosingBracketPos(e);if(this.cursorColumn<=t)return}if(this._getCollapsedNodeAtLine(this.cursorLine)){const e=this.lines[this.cursorLine].search(N);if(this.cursorColumn>e)return}if(this._saveToHistory("insert"),0===this.lines.length){const t=e.split("\n");this.lines=t,this.cursorLine=t.length-1,this.cursorColumn=t[t.length-1].length}else if(this.cursorLine<this.lines.length){const t=this.lines[this.cursorLine];this.lines[this.cursorLine]=t.substring(0,this.cursorColumn)+e+t.substring(this.cursorColumn),this.cursorColumn+=e.length}this.formatAndUpdate()}}handlePaste(e){e.preventDefault();const t=e.clipboardData?.getData("text/plain");if(!t)return;const s=0===this.lines.length;try{const e=H(JSON.parse(t)).map(e=>JSON.stringify(e,null,2)).join(",\n");this.insertText(e)}catch{this.insertText(t)}this.renderTimer&&(cancelAnimationFrame(this.renderTimer),this.renderTimer=void 0),s&&this.lines.length>0&&this.autoCollapseCoordinates(),this._expandErrorNodes(),this.renderViewport()}handleCopy(e){e.preventDefault(),e.clipboardData&&(this._hasSelection()?e.clipboardData.setData("text/plain",this._getSelectedText()):e.clipboardData.setData("text/plain",this.getContent()))}handleCut(e){e.preventDefault(),e.clipboardData&&(this._hasSelection()?(e.clipboardData.setData("text/plain",this._getSelectedText()),this._saveToHistory("cut"),this._deleteSelection(),this.formatAndUpdate()):(e.clipboardData.setData("text/plain",this.getContent()),this._saveToHistory("cut"),this.lines=[],this.cursorLine=0,this.cursorColumn=0,this.formatAndUpdate()))}_getPositionFromClick(e){const t=this._viewport,s=this._linesContainer;if(!t)return{line:0,column:0};const i=t.getBoundingClientRect(),n=e.clientY-i.top+t.scrollTop-8,r=Math.floor(n/this.lineHeight);let o=0,l=0;if(r>=0&&r<this.visibleLines.length){const n=this.visibleLines[r];o=n.index;const a=s?.querySelector(`.line[data-line-index="${n.index}"]`),h=this._getCharWidth();if(a){const t=a.getBoundingClientRect(),s=e.clientX-t.left,i=Math.round(s/h),r=n.content?.length||0;l=Math.max(0,Math.min(i,r))}else{const s=12,r=e.clientX-i.left+t.scrollLeft-s,o=Math.round(r/h),a=n.content?.length||0;l=Math.max(0,Math.min(o,a))}}return{line:o,column:l}}handleGutterClick(e){const t=e.target;if(!t)return;const s=t.closest(".visibility-button");if(s)this.toggleFeatureVisibility(s.dataset.featureKey);else if(t.classList.contains("collapse-button")){const e=t.dataset.nodeId;return void(e&&this.toggleCollapse(e))}}handleEditorClick(e){const t=e.target;if(!t)return;this._blockRender=!1;const s=t.closest(".line.has-visibility");if(s){const t=s.getBoundingClientRect();if(e.clientX-t.left<14){e.preventDefault(),e.stopPropagation();const t=s.dataset.featureKey;return void(t&&this.toggleFeatureVisibility(t))}}if(t.classList.contains("json-color")){const s=t.getBoundingClientRect(),i=e.clientX-s.left;if(i<0&&i>=-8){e.preventDefault(),e.stopPropagation();const s=t.dataset.color,i=t.closest(".line");if(i){const e=parseInt(i.dataset.lineIndex||"0"),n=this.lines[e].match(k);n&&n[1]&&s&&this.showColorPicker(t,e,s,n[1])}return}}if(t.classList.contains("json-boolean")){const s=t.getBoundingClientRect(),i=e.clientX-s.left;if(i<0&&i>=-8){e.preventDefault(),e.stopPropagation();const s=t.closest(".line");if(s){const e=parseInt(s.dataset.lineIndex||"0"),t=this.lines[e].match(S);if(t){const s="true"===t[2];this.updateBooleanValue(e,!s,t[1])}}return}}}toggleCollapse(e){const t=this._nodeIdToLines.get(e);this.collapsedNodes.has(e)?(this.collapsedNodes.delete(e),t?.uniqueKey&&this._openedNodeKeys.add(t.uniqueKey)):(this.collapsedNodes.add(e),t?.uniqueKey&&this._openedNodeKeys.delete(t.uniqueKey)),this.updateView(),this._invalidateRenderCache(),this.scheduleRender()}autoCollapseCoordinates(){this._hasErrors()||this._applyCollapsedOption(["coordinates"])}_hasErrors(){try{const e="["+this.lines.join("\n")+"]";JSON.parse(e)}catch{return!0}for(const e of this.lines)if(V(e,"",void 0).includes("json-error"))return!0;return!1}_applyCollapsedFromOptions(e,t){if(this._hasErrors())return;const s=void 0!==e.collapsed?e.collapsed:["coordinates"];s&&(!Array.isArray(s)||s.length>0)&&this._applyCollapsedOption(s,t)}_applyCollapsedOption(e,t=null){const s=this._findCollapsibleRanges(),i=s.filter(e=>e.isRootFeature);for(const n of s){let s=!1;if("function"==typeof e){const r=i.findIndex(e=>n.startLine>=e.startLine&&n.endLine<=e.endLine),o=e(t?.[r]||null,r);s=n.isRootFeature?o.includes("$root"):o.includes(n.nodeKey)}else Array.isArray(e)&&(s=n.isRootFeature?e.includes("$root"):e.includes(n.nodeKey));s&&this.collapsedNodes.add(n.nodeId)}this.updateModel(),this.scheduleRender()}toggleFeatureVisibility(e){e&&(this.hiddenFeatures.has(e)?this.hiddenFeatures.delete(e):this.hiddenFeatures.add(e),this.updateView(),this.scheduleRender(),this.emitChange())}showColorPicker(e,t,s,i){const n=document.querySelector(".geojson-color-picker-anchor");n&&n.remove();const r=z("div");r.className="geojson-color-picker-anchor";const o=e.getBoundingClientRect();r.style.cssText=`\n position: fixed;\n left: ${o.left-8}px;\n top: ${o.top+o.height}px;\n width: 10px;\n height: 10px;\n z-index: 9998;\n `,document.body.appendChild(r);const l=z("input");l.type="color";let a=s;a=s.startsWith("#")?s.replace(L,"#$1$1$2$2$3$3"):function(e){const t=(D||(D=document.createElement("div"),D.style.display="none",document.body.appendChild(D)),D);t.style.color=e;const s=getComputedStyle(t).color;if(!s)return null;const i=s.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(!i)return null;const[,n,r,o]=i;return"#"+parseInt(n,10).toString(16).padStart(2,"0")+parseInt(r,10).toString(16).padStart(2,"0")+parseInt(o,10).toString(16).padStart(2,"0")}(s)||"#000000",l.value=a,l.className="geojson-color-picker-input",l.style.cssText="\n position: absolute;\n left: 0;\n top: 0;\n width: 10px;\n height: 10px;\n opacity: 0;\n border: none;\n padding: 0;\n cursor: pointer;\n ",r.appendChild(l),l.addEventListener("input",e=>{this.updateColorValue(t,e.target.value,i)});const h=e=>{e.target!==l&&(document.removeEventListener("click",h,!0),r.remove())};l._closeListener=h,setTimeout(()=>{document.addEventListener("click",h,!0)},100),l.focus(),l.click()}updateColorValue(e,t,s){const i=new RegExp(`"${s}"\\s*:\\s*"(?:#[0-9a-fA-F]{3,6}|[a-zA-Z]+)"`);this.lines[e]=this.lines[e].replace(i,`"${s}": "${t}"`),this.updateView(),this.scheduleRender(),this.emitChange()}updateBooleanValue(e,t,s){const i=new RegExp(`"${s}"\\s*:\\s*(true|false)`);this.lines[e]=this.lines[e].replace(i,`"${s}": ${t}`),this.updateView(),this.scheduleRender(),this.emitChange()}_bestEffortFormat(e,t){const s=e.split("\n");if(void 0!==t&&t>=0&&t<s.length){const e=s[t],i=s.slice(0,t).join("\n"),n=i.trim()?this._formatChunk(i):[],r=this._computeDepthAtEnd(n)+this._computeBracketDelta(e),o=s.slice(t+1).join("\n"),l=o.trim()?this._formatChunk(o,r):[];return[...n,e,...l]}return this._formatChunk(e)}_computeBracketDelta(e){let t=0,s=!1,i=!1;for(const n of e)i?i=!1:"\\"===n&&s?i=!0:'"'!==n?s||("{"===n||"["===n?t++:"}"!==n&&"]"!==n||t--):s=!s;return t}_computeDepthAtEnd(e){let t=1;for(const s of e)for(const e of s)"{"===e||"["===e?t++:"}"!==e&&"]"!==e||(t=Math.max(0,t-1));return t}_formatChunk(e,t=1){const s=[];let i="",n=t,r=!1,o=!1;for(let l=0;l<e.length;l++){const t=e[l];if(o)i+=t,o=!1;else if("\\"===t&&r)i+=t,o=!0;else if('"'!==t)if(r)i+=t;else if("{"===t||"["===t)i+=t,s.push(" ".repeat(n)+i.trim()),n++,i="";else if("}"===t||"]"===t)i.trim()&&s.push(" ".repeat(n)+i.trim()),n=Math.max(0,n-1),i=t;else if(","===t)i+=t,s.push(" ".repeat(n)+i.trim()),i="";else if(":"===t){if(i+=": ",l++," "===e[l])continue;l--}else{if("\n"===t||"\r"===t)continue;i+=t}else r=!r,i+=t}return i.trim()&&s.push(" ".repeat(n)+i.trim()),s}formatAndUpdate(){const e=this.cursorLine,t=this.cursorColumn,s=this.lines.join("\n");try{const e="["+s+"]",t=JSON.parse(e),i=JSON.stringify(t,null,2).split("\n");this.lines=i.slice(1,-1)}catch{if(s.trim()){const t=(this.lines[e]||"").length<80?e:void 0;this.lines=this._bestEffortFormat(s,t)}}if(this.lines.join("\n")===s)this.cursorLine=e,this.cursorColumn=t;else if(0===this.cursorColumn);else{const i=s.split("\n")[e]||"",n=i.length-i.trimStart().length,r=Math.max(0,t-n);if(this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],t=e.length-e.trimStart().length;this.cursorColumn=t+r}}this.cursorLine=Math.min(this.cursorLine,Math.max(0,this.lines.length-1)),this.cursorColumn=Math.min(this.cursorColumn,this.lines[this.cursorLine]?.length||0),this.updateModel(),this._expandErrorNodes(),this.scheduleRender(),this.updatePlaceholderVisibility(),this._updateErrorDisplay(),this.emitChange()}emitChange(){const e=this.getContent(),s=this.prefix+e+this.suffix;try{let i=JSON.parse(s);this.hiddenFeatures.size>0&&(i.features=i.features.filter(e=>{const t=M(e);return!t||!this.hiddenFeatures.has(t)}));const n=function(e){const s=[];return e.features?(e.features.forEach((e,i)=>{"Feature"!==e.type&&s.push(`features[${i}]: type must be "Feature"`),e.geometry&&e.geometry.type&&(t.includes(e.geometry.type)||s.push(`features[${i}].geometry: invalid type "${e.geometry.type}"`))}),s):s}(i);n.length>0?this.dispatchEvent(new CustomEvent("error",{detail:{error:n.join("; "),errors:n,content:e},bubbles:!0,composed:!0})):this.dispatchEvent(new CustomEvent("change",{detail:i,bubbles:!0,composed:!0}))}catch(i){this.dispatchEvent(new CustomEvent("error",{detail:{error:i instanceof Error?i.message:"Unknown error",content:e},bubbles:!0,composed:!0}))}}updateReadonly(){this._hiddenTextarea&&(this._hiddenTextarea.readOnly=this.readonly),this._clearBtn&&(this._clearBtn.hidden=this.readonly)}updatePlaceholderVisibility(){this._placeholderLayer&&(this._placeholderLayer.style.display=this.lines.length>0?"none":"block")}_updateErrorDisplay(){const e=this._getErrorLines().length;this._errorNav&&this._errorNav.classList.toggle("visible",e>0),this._errorCount&&(this._errorCount.textContent=e>0?String(e):"")}updatePlaceholderContent(){this._placeholderLayer&&(this._placeholderLayer.textContent=this.placeholder),this.updatePlaceholderVisibility()}updatePrefixSuffix(){this._editorPrefix&&(this._editorPrefix.textContent=this.prefix),this._editorSuffix&&(this._editorSuffix.textContent=this.suffix)}updateThemeCSS(){const e=(t=this.getAttribute("dark-selector")||".dark").startsWith(".")&&!t.includes(" ")?`:host(${t})`:`:host-context(${t})`;var t;let s=this._id("theme-styles");s||(s=z("style"),s.id="theme-styles",this.shadowRoot.insertBefore(s,this.shadowRoot.firstChild)),I.lastIndex=0;const i=e=>Object.entries(e).filter(e=>void 0!==e[1]).map(([e,t])=>{return`--${s=e,s.replace(I,"-$1").toLowerCase()}: ${t};`;var s}).join("\n "),n=i(this.themes.light||{});let r=n?`:host {\n ${n}\n }\n`:"";r+=`${e} {\n ${i({bgColor:"#2b2b2b",textColor:"#a9b7c6",caretColor:"#bbb",gutterBg:"#313335",gutterBorder:"#3c3f41",gutterText:"#606366",jsonKey:"#9876aa",jsonString:"#6a8759",jsonNumber:"#6897bb",jsonBoolean:"#cc7832",jsonNull:"#cc7832",jsonPunct:"#a9b7c6",jsonError:"#ff6b68",controlColor:"#cc7832",controlBg:"#3c3f41",controlBorder:"#5a5a5a",geojsonKey:"#9876aa",geojsonType:"#6a8759",geojsonTypeInvalid:"#ff6b68",jsonKeyInvalid:"#ff6b68",...this.themes.dark})}\n }`,s.textContent=r}setTheme(e){e.dark&&(this.themes.dark={...this.themes.dark,...e.dark}),e.light&&(this.themes.light={...this.themes.light,...e.light}),this.updateThemeCSS()}resetTheme(){this.themes={dark:{},light:{}},this.updateThemeCSS()}getTheme(){return{...this.themes}}_findCollapsibleRanges(){const e=[];for(const[t,s]of this._lineToNodeId){const i=this._nodeIdToLines.get(s);if(!i)continue;const n=this.lines[t];if(!n)continue;const r=n.match(E),o=!r&&n.match(j);if(!r&&!o)continue;const l=r?r[2]:o?o[1]:"{";e.push({startLine:i.startLine,endLine:i.endLine,nodeKey:i.nodeKey||(r?r[1]:`__root_${t}`),nodeId:s,openBracket:l,isRootFeature:!!o})}return e.sort((e,t)=>e.startLine-t.startLine),e}_findClosingLine(e,t){let s=1;const i=this.lines[e],n=i.indexOf(t);if(-1!==n){const r=K(i.substring(n+1),t);if(s+=r.open-r.close,0===s)return e}for(let r=e+1;r<this.lines.length;r++){const e=K(this.lines[r],t);if(s+=e.open-e.close,0===s)return r}return-1}_buildContextMap(){const e=this.lines.length;if(this._contextMapCache&&this._contextMapLinesLength===e&&this._contextMapFirstLine===this.lines[0]&&this._contextMapLastLine===this.lines[e-1])return this._contextMapCache;const t=/* @__PURE__ */new Map,r=[];let o=null;for(let l=0;l<e;l++){const e=this.lines[l],a=r[r.length-1]?.context||"Feature";t.set(l,a),s.test(e)?o="geometry":i.test(e)?o="properties":n.test(e)&&(o="Feature"),F.lastIndex=0,$.lastIndex=0,A.lastIndex=0,B.lastIndex=0;const h=(e.match(F)||[]).length,c=(e.match($)||[]).length,d=(e.match(A)||[]).length,u=(e.match(B)||[]).length;for(let t=0;t<h+d;t++)r.push({context:o||a,isArray:t>=h}),o=null;for(let t=0;t<c+u&&r.length>0;t++)r.pop()}return this._contextMapCache=t,this._contextMapLinesLength=e,this._contextMapFirstLine=this.lines[0],this._contextMapLastLine=this.lines[e-1],t}set(e,t={}){const s=H(e);this._setFeaturesInternal(s,t)}add(e,t={}){const s=H(e),i=[...this._parseFeatures(),...s];this._setFeaturesInternal(i,t)}insertAt(e,t,s={}){const i=H(e),n=this._parseFeatures(),r=t<0?n.length+t:t;n.splice(Math.max(0,Math.min(r,n.length)),0,...i),this._setFeaturesInternal(n,s)}_setFeaturesInternal(e,t){const s=e.map(e=>JSON.stringify(e,null,2)).join(",\n");this.setValue(s,!1),this._applyCollapsedFromOptions(t,e)}removeAt(e){const t=this._parseFeatures(),s=e<0?t.length+e:e;if(s>=0&&s<t.length){const e=t.splice(s,1)[0];return this.set(t),e}}removeAll(){this.lines.length>0&&this._saveToHistory("removeAll");const e=this._parseFeatures();return this.lines=[],this.collapsedNodes.clear(),this.hiddenFeatures.clear(),this.updateModel(),this.scheduleRender(),this.updatePlaceholderVisibility(),this.emitChange(),e}get(e){const t=this._parseFeatures();return t[e<0?t.length+e:e]}getAll(){return this._parseFeatures()}emit(){this.emitChange()}save(e="features.geojson"){try{const t={type:"FeatureCollection",features:this._parseFeatures()},s=JSON.stringify(t,null,2),i=new Blob([s],{type:"application/geo+json"}),n=URL.createObjectURL(i),r=z("a");return r.href=n,r.download=e,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n),!0}catch(t){return!1}}open(e={}){return new Promise(t=>{const s=z("input");s.type="file",s.accept=".geojson,.json,application/geo+json,application/json",s.style.display="none",s.addEventListener("change",i=>{const n=i.target.files?.[0];if(!n)return document.body.removeChild(s),void t(!1);const r=new FileReader;r.onload=i=>{try{const n=i.target?.result,r=H(JSON.parse(n));this._saveToHistory("open"),this.set(r,e),this.clearHistory(),document.body.removeChild(s),t(!0)}catch(n){document.body.removeChild(s),t(!1)}},r.onerror=()=>{document.body.removeChild(s),t(!1)},r.readAsText(n)}),s.addEventListener("cancel",()=>{document.body.removeChild(s),t(!1)}),document.body.appendChild(s),s.click()})}_parseFeatures(){try{const e=this.lines.join("\n");return e.trim()?JSON.parse("["+e+"]"):[]}catch(e){return[]}}}customElements.get("geojson-editor")||customElements.define("geojson-editor",q);export{q as default};
9
+ const e=["type","geometry","properties","coordinates","id","features"],t=["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon"],s=/"geometry"\s*:/,i=/"properties"\s*:/,n=/"features"\s*:/,r=/^(\s*"[^"]+"\s*:\s*)([{\[])/,o=/^(\s*)([{\[]),?\s*$/,l=/&/g,a=/</g,h=/>/g,c=/([{}[\],:])/g,d=/"([^"]+)"(<span class="json-punctuation">:<\/span>)/g,u=/<span class="geojson-key">"type"<\/span><span class="json-punctuation">:<\/span>(\s*)"([^"]*)"/g,p=/(<span class="json-punctuation">:<\/span>)(\s*)"([^"]*)"/g,g=/(<span class="json-punctuation">:<\/span>)(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gi,f=/(<span class="json-punctuation">[\[,]<\/span>)(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gi,m=/^(\s*)(-?\d+\.?\d*(?:e[+-]?\d+)?)/gim,_=/(<span class="json-punctuation">:<\/span>)(\s*)(true|false)/g,C=/(<span class="json-punctuation">:<\/span>)(\s*)(null)/g,v=/(<\/span>|^)([^<]+)(<span|$)/g,b=/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,L=/^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/,y=/^\s*$/,x=/(\s+)/,w=/"([\w-]+)"\s*:\s*(?:"([^"]*)"|(\btrue\b|\bfalse\b))/g,k=/"([\w-]+)"\s*:\s*(?:"([^"]*)"|(\btrue\b|\bfalse\b))/,S=/"([\w-]+)"\s*:\s*(true|false)/,T=/"type"\s*:\s*"Feature"/,E=/^\s*"([^"]+)"\s*:\s*([{\[])/,j=/^\s*([{\[]),?\s*$/,N=/[{\[]/,R=/[\w-]/,I=/([A-Z])/g,F=/\{/g,A=/\}/g,$=/\[/g,B=/\]/g;function M(e){if(!e)return null;if(void 0!==e.id)return`id:${e.id}`;if(void 0!==e.properties?.id)return`prop:${e.properties.id}`;const t=e.geometry?.type||"null",s=e.geometry,i=JSON.stringify(s?.coordinates||[]);let n=0;for(let r=0;r<i.length;r++)n=(n<<5)-n+i.charCodeAt(r),n&=n;return`hash:${t}:${n.toString(36)}`}function P(e,t){const s="{"===t?"}":"]";let i=0,n=0,r=!1,o=!1;for(const l of e)o?o=!1:"\\"===l&&r?o=!0:'"'!==l?r||(l===t&&i++,l===s&&n++):r=!r;return{open:i,close:n}}function K(e){if(!e||"object"!=typeof e)throw new Error("Feature must be an object");if("Feature"!==e.type)throw new Error('Feature type must be "Feature"');if(!("geometry"in e))throw new Error("Feature must have a geometry property");if(!("properties"in e))throw new Error("Feature must have a properties property");if(null!==e.geometry){if("object"!=typeof e.geometry)throw new Error("Feature geometry must be an object or null");if(!e.geometry.type)throw new Error("Feature geometry must have a type");if(!t.includes(e.geometry.type))throw new Error(`Invalid geometry type: "${e.geometry.type}"`);if(!("coordinates"in e.geometry))throw new Error("Feature geometry must have coordinates")}if(null!==e.properties&&"object"!=typeof e.properties)throw new Error("Feature properties must be an object or null")}function H(e){let t=[];if(Array.isArray(e))t=e;else{if(!e||"object"!=typeof e)throw new Error("Input must be a Feature, array of Features, or FeatureCollection");if("FeatureCollection"===e.type&&"features"in e&&Array.isArray(e.features))t=e.features;else{if("Feature"!==e.type)throw new Error("Input must be a Feature, array of Features, or FeatureCollection");t=[e]}}for(const s of t)K(s);return t}let D=null;function O(e){return",aliceblue,antiquewhite,aqua,aquamarine,azure,beige,bisque,black,blanchedalmond,blue,blueviolet,brown,burlywood,cadetblue,chartreuse,chocolate,coral,cornflowerblue,cornsilk,crimson,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkgrey,darkkhaki,darkmagenta,darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray,darkslategrey,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dimgrey,dodgerblue,firebrick,floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,green,greenyellow,grey,honeydew,hotpink,indianred,indigo,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon,lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgray,lightgreen,lightgrey,lightpink,lightsalmon,lightseagreen,lightskyblue,lightslategray,lightslategrey,lightsteelblue,lightyellow,lime,limegreen,linen,magenta,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple,mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,midnightblue,mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid,palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue,purple,rebeccapurple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,seagreen,seashell,sienna,silver,skyblue,slateblue,slategray,slategrey,snow,springgreen,steelblue,tan,teal,thistle,tomato,turquoise,violet,wheat,white,whitesmoke,yellow,yellowgreen,".includes(","+e.toLowerCase()+",")}function V(s,i,n){if(!s)return"";let L=s,w=null;if(n?.collapseButton?.isCollapsed){const e=s.match(r),t=!e&&s.match(o);e?(L=e[1]+e[2],w=e[2]):t&&(L=t[1]+t[2],w=t[2])}let k=L.replace(l,"&amp;").replace(a,"&lt;").replace(h,"&gt;");if(k=k.replace(c,'<span class="json-punctuation">$1</span>'),d.lastIndex=0,k=k.replace(d,(t,s,n)=>"properties"!==i&&e.includes(s)?`<span class="geojson-key">"${s}"</span>${n}`:`<span class="json-key">"${s}"</span>${n}`),"properties"!==i&&(u.lastIndex=0,k=k.replace(u,(e,s,i)=>`<span class="geojson-key">"type"</span><span class="json-punctuation">:</span>${s}<span class="${"Feature"===i||"FeatureCollection"===i||t.includes(i)?"geojson-type":"geojson-type-invalid"}">"${i}"</span>`)),p.lastIndex=0,k=k.replace(p,(e,t,s,i)=>e.includes("geojson-type")||e.includes("json-string")?e:b.test(i)||O(i)?`${t}${s}<span class="json-string json-color" data-color="${i}" style="--swatch-color: ${i}">"${i}"</span>`:`${t}${s}<span class="json-string">"${i}"</span>`),g.lastIndex=0,k=k.replace(g,'$1$2<span class="json-number">$3</span>'),f.lastIndex=0,k=k.replace(f,'$1$2<span class="json-number">$3</span>'),m.lastIndex=0,k=k.replace(m,'$1<span class="json-number">$2</span>'),_.lastIndex=0,k=k.replace(_,(e,t,s,i)=>`${t}${s}<span class="json-boolean${"true"===i?" json-bool-true":" json-bool-false"}">${i}</span>`),C.lastIndex=0,k=k.replace(C,'$1$2<span class="json-null">$3</span>'),w){const e="["===w?"collapsed-bracket-array":"collapsed-bracket-object";k=k.replace(new RegExp(`<span class="json-punctuation">\\${w}<\\/span>$`),`<span class="${e}">${w}</span>`)}return v.lastIndex=0,k=k.replace(v,(e,t,s,i)=>{if(!s||y.test(s))return e;const n=s.split(x);let r=!1;const o=n.map(e=>y.test(e)?e:(r=!0,`<span class="json-error">${e}</span>`)).join("");return r?t+o+i:e}),k}const z=e=>document.createElement(e);class q extends HTMLElement{constructor(){super(),this.lines=[],this.collapsedNodes=/* @__PURE__ */new Set,this.hiddenFeatures=/* @__PURE__ */new Set,this._nodeIdCounter=0,this._lineToNodeId=/* @__PURE__ */new Map,this._nodeIdToLines=/* @__PURE__ */new Map,this._openedNodeKeys=/* @__PURE__ */new Set,this.visibleLines=[],this.lineMetadata=/* @__PURE__ */new Map,this.featureRanges=/* @__PURE__ */new Map,this.viewportHeight=0,this.lineHeight=19.5,this.bufferLines=5,this._lastStartIndex=-1,this._lastEndIndex=-1,this._lastTotalLines=-1,this._scrollRaf=null,this.cursorLine=0,this.cursorColumn=0,this.selectionStart=null,this.selectionEnd=null,this.renderTimer=void 0,this.inputTimer=void 0,this.themes={dark:{},light:{}},this._undoStack=[],this._redoStack=[],this._maxHistorySize=100,this._lastActionTime=0,this._lastActionType=null,this._groupingDelay=500,this._isSelecting=!1,this._isComposing=!1,this._blockRender=!1,this._insertMode=!0,this._charWidth=null,this._contextMapCache=null,this._contextMapLinesLength=0,this._contextMapFirstLine=void 0,this._contextMapLastLine=void 0,this._errorLinesCache=null,this._viewport=null,this._linesContainer=null,this._scrollContent=null,this._hiddenTextarea=null,this._gutterContent=null,this._gutterScrollContent=null,this._gutterScroll=null,this._gutter=null,this._clearBtn=null,this._editorWrapper=null,this._placeholderLayer=null,this._editorPrefix=null,this._editorSuffix=null,this._errorNav=null,this._errorCount=null,this._prevErrorBtn=null,this._nextErrorBtn=null,this.attachShadow({mode:"open"})}_id(e){return this.shadowRoot.getElementById(e)}_invalidateRenderCache(){this._lastStartIndex=-1,this._lastEndIndex=-1,this._lastTotalLines=-1}_createSnapshot(){return{lines:[...this.lines],cursorLine:this.cursorLine,cursorColumn:this.cursorColumn,timestamp:Date.now()}}_restoreSnapshot(e){this.lines=[...e.lines],this.cursorLine=e.cursorLine,this.cursorColumn=e.cursorColumn,this.updateModel(),this._invalidateRenderCache(),this.scheduleRender(),this.updatePlaceholderVisibility(),this.emitChange()}_saveToHistory(e="edit"){const t=Date.now();if(!(e===this._lastActionType&&t-this._lastActionTime<this._groupingDelay)){const e=this._createSnapshot();this._undoStack.push(e),this._undoStack.length>this._maxHistorySize&&this._undoStack.shift(),this._redoStack=[]}this._lastActionTime=t,this._lastActionType=e}undo(){if(0===this._undoStack.length)return!1;this._redoStack.push(this._createSnapshot());const e=this._undoStack.pop();return e&&this._restoreSnapshot(e),this._lastActionType=null,this._lastActionTime=0,!0}redo(){if(0===this._redoStack.length)return!1;this._undoStack.push(this._createSnapshot());const e=this._redoStack.pop();return e&&this._restoreSnapshot(e),this._lastActionType=null,this._lastActionTime=0,!0}clearHistory(){this._undoStack=[],this._redoStack=[],this._lastActionType=null,this._lastActionTime=0}canUndo(){return this._undoStack.length>0}canRedo(){return this._redoStack.length>0}_generateNodeId(){return"node_"+ ++this._nodeIdCounter}_getCollapsedRangeForLine(e){for(const[t,s]of this._nodeIdToLines)if(this.collapsedNodes.has(t)&&e>s.startLine&&e<s.endLine)return{nodeId:t,...s};return null}_getCollapsedClosingLine(e){for(const[t,s]of this._nodeIdToLines)if(this.collapsedNodes.has(t)&&e===s.endLine)return{nodeId:t,...s};return null}_getClosingBracketPos(e){return Math.max(e.lastIndexOf("]"),e.lastIndexOf("}"))}_getCollapsedNodeAtLine(e){const t=this._lineToNodeId.get(e);if(t&&this.collapsedNodes.has(t)){const e=this._nodeIdToLines.get(t);if(e)return{nodeId:t,...e}}return null}_getContainingExpandedNode(e){let t=null;for(const[s,i]of this._nodeIdToLines)this.collapsedNodes.has(s)||e>=i.startLine&&e<=i.endLine&&(!t||i.endLine-i.startLine<t.endLine-t.startLine)&&(t={nodeId:s,...i});return t}_deleteCollapsedNode(e){this._saveToHistory("delete");const t=e.endLine-e.startLine+1;this.lines.splice(e.startLine,t),this.cursorLine=Math.min(e.startLine,this.lines.length-1),this.cursorColumn=0,this.formatAndUpdate()}_rebuildNodeIdMappings(){const e=/* @__PURE__ */new Set;for(const s of this.collapsedNodes){const t=this._nodeIdToLines.get(s);t?.uniqueKey&&e.add(t.uniqueKey)}this._nodeIdCounter=0,this._lineToNodeId.clear(),this._nodeIdToLines.clear(),this.collapsedNodes.clear();const t=/* @__PURE__ */new Map;for(let s=0;s<this.lines.length;s++){const i=this.lines[s],n=i.match(E),r=!n&&i.match(j);if(!n&&!r)continue;let o,l;if(n)o=n[1],l=n[2];else{if(!r)continue;l=r[1],o=`__root_${l}_${s}`}const a=P(i.substring(i.indexOf(l)+1),l);if(a.close>a.open)continue;const h=this._findClosingLine(s,l);if(-1===h||h===s)continue;const c=this._generateNodeId(),d=t.get(o)||0;t.set(o,d+1);const u=`${o}:${d}`;this._lineToNodeId.set(s,c),this._nodeIdToLines.set(c,{startLine:s,endLine:h,nodeKey:o,uniqueKey:u,isRootFeature:!!r}),e.has(u)&&!this._openedNodeKeys.has(u)&&this.collapsedNodes.add(c)}}static get observedAttributes(){return["readonly","value","placeholder","dark-selector","internal-add-shortcut"]}connectedCallback(){this.render(),this._cacheElements(),this.setupEventListeners(),this.updatePrefixSuffix(),this.updateThemeCSS(),this.value&&this.setValue(this.value),this.updatePlaceholderVisibility()}disconnectedCallback(){this.renderTimer&&clearTimeout(this.renderTimer),this.inputTimer&&clearTimeout(this.inputTimer);const e=document.querySelector(".geojson-color-picker-input");e&&(e._closeListener&&document.removeEventListener("click",e._closeListener,!0),e.remove())}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"value":this.setValue(s);break;case"readonly":this.updateReadonly();break;case"placeholder":this.updatePlaceholderContent();break;case"dark-selector":this.updateThemeCSS()}}get readonly(){return this.hasAttribute("readonly")}get value(){return this.getAttribute("value")||""}get placeholder(){return this.getAttribute("placeholder")||""}get internalAddShortcut(){return this.hasAttribute("internal-add-shortcut")}get prefix(){return'{"type": "FeatureCollection", "features": ['}get suffix(){return"]}"}render(){const e=this.shadowRoot,t=z("style");t.textContent=':host *,:host *:before,:host *:after{box-sizing:border-box;font: 13px/1.5 Courier New,Courier,monospace;font-variant:normal;letter-spacing:0;word-spacing:0;text-transform:none;text-decoration:none;text-indent:0}:host{--line-height: 19.5px;--gutter-width: 50px;--editor-padding-y: 8px;--editor-padding-x: 12px;display:flex;flex-direction:column;position:relative;width:100%;height:400px;border-radius:4px;overflow:hidden}:host([readonly]) .editor-wrapper:after{content:"";position:absolute;inset:0;pointer-events:none;background:repeating-linear-gradient(-45deg,rgba(128,128,128,.08),rgba(128,128,128,.08) 3px,transparent 3px,transparent 12px);z-index:1}:host([readonly]) .hidden-textarea{cursor:text}.editor-wrapper{position:relative;width:100%;flex:1;background:var(--bg-color, #fff);display:flex;overflow:hidden}.gutter{width:var(--gutter-width);background:var(--gutter-bg, #f0f0f0);border-right:1px solid var(--gutter-border, #e0e0e0);overflow:hidden;flex-shrink:0;position:relative}.gutter-scroll{position:absolute;inset:0;overflow:hidden;padding:8px 0}.gutter-scroll-content{position:relative;width:100%}.gutter-content{position:absolute;top:0;left:0;right:0;will-change:transform}.gutter-line{height:var(--line-height);display:flex;align-items:center;justify-content:flex-end;padding-right:4px;position:relative}.gutter-line.has-error:before{content:"";position:absolute;right:0;top:0;bottom:0;width:3px;background:var(--error-color, #dc3545)}.line-number{font-size:11px;color:var(--gutter-text, #999);-webkit-user-select:none;user-select:none;min-width:20px;text-align:right}.collapse-column{width:16px;display:flex;align-items:center;justify-content:center;flex-shrink:0}.collapse-button{width:12px;height:12px;border-radius:2px;cursor:pointer;flex-shrink:0;background:transparent;border:none;color:var(--json-punct, #a9b7c6);font-size:10px;display:flex;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none;opacity:0;transition:transform .1s,opacity .15s}.collapse-button.collapsed,.gutter:hover .collapse-button{opacity:1}.collapse-button:hover{transform:scale(1.2)}@media(hover:none),(pointer:coarse){.collapse-button{opacity:1}}.editor-content{position:relative;flex:1;overflow:hidden}.hidden-textarea{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;padding:0;margin:0;border:none;outline:none;resize:none;overflow:hidden;z-index:-1;pointer-events:none;caret-color:transparent}.viewport{position:absolute;inset:0;overflow:auto;padding:var(--editor-padding-y) var(--editor-padding-x);overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--control-border, #c0c0c0) var(--control-bg, #e8e8e8)}.scroll-content{position:relative;width:100%}.lines-container{position:absolute;top:0;left:0;right:0;will-change:transform}.line{height:var(--line-height);white-space:pre;display:block;position:relative}.cursor{position:absolute;width:2px;height:1em;background:var(--caret-color, #000);top:.15em;pointer-events:none;animation:cursor-blink 1s step-end infinite}.cursor.cursor-block{width:.6em;opacity:.5;animation:cursor-blink-block 1s step-end infinite}@keyframes cursor-blink-block{0%,to{opacity:.5}50%{opacity:.2}}@keyframes cursor-blink{0%,to{opacity:1}50%{opacity:0}}.selection{position:absolute;height:100%;top:0;background:var(--selection-color, rgba(51, 153, 255, .3));pointer-events:none;z-index:0}.line-hidden>span{opacity:.35;filter:grayscale(50%)}.placeholder-layer{position:absolute;top:var(--editor-padding-y);left:var(--editor-padding-x);color:#6a6a6a;pointer-events:none;z-index:0;white-space:pre}.json-key{color:var(--json-key, #660e7a)}.json-string{color:var(--json-string, #008000)}.json-number{color:var(--json-number, #00f)}.json-boolean,.json-null{color:var(--json-boolean, #000080)}.json-punctuation{color:var(--json-punct, #000)}.json-error{color:var(--json-error, #f00)}.geojson-key{color:var(--geojson-key, #660e7a);font-weight:600}.geojson-type{color:var(--geojson-type, #008000);font-weight:600}.geojson-type-invalid{color:var(--geojson-type-invalid, #f00);font-weight:600}.collapsed-bracket-array,.collapsed-bracket-object{color:var(--json-punct, #000)}.collapsed-bracket-array:after,.collapsed-bracket-object:after{content:"…";color:var(--json-punct, #888)}.prefix-wrapper,.suffix-wrapper{display:flex;flex-shrink:0;background:var(--bg-color, #fff)}.prefix-gutter,.suffix-gutter{width:var(--gutter-width);background:var(--gutter-bg, #f0f0f0);border-right:1px solid var(--gutter-border, #e0e0e0);flex-shrink:0}.editor-prefix,.editor-suffix{flex:1;padding:4px 12px;color:var(--text-color, #000);background:var(--bg-color, #fff);-webkit-user-select:none;user-select:none;white-space:pre-wrap;word-wrap:break-word;opacity:.6}.prefix-wrapper{border-bottom:1px solid rgba(255,255,255,.1);position:relative}.suffix-wrapper{border-top:1px solid rgba(255,255,255,.1);position:relative}.info-btn{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:transparent;border:none;color:var(--text-color, #000);opacity:.15;cursor:pointer;font-size:.7rem;width:1rem;height:1rem;padding:0;border-radius:50%;display:flex;align-items:center;justify-content:center;transition:opacity .2s;font-family:sans-serif}.info-btn:hover{opacity:.5}.clear-btn{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:transparent;border:none;color:var(--text-color, #000);opacity:.3;cursor:pointer;font-size:.65rem;width:1rem;height:1rem;padding:.15rem 0 0;border-radius:3px;display:flex;align-items:center;justify-content:center;transition:opacity .2s,background .2s}.clear-btn:hover{opacity:.7;background:#ffffff1a}.clear-btn[hidden]{display:none}.error-nav{display:none;align-items:center;gap:4px;margin-right:24px}.error-nav.visible{display:flex}.error-nav-btn{background:transparent;border:none;color:var(--error-color, #dc3545);cursor:pointer;font-size:8px;width:16px;height:16px;padding:0;display:flex;align-items:center;justify-content:center;opacity:.7;transition:opacity .2s}.error-nav-btn:hover{opacity:1}.error-count{color:var(--error-color, #dc3545);font-size:11px;min-width:20px;text-align:center}.viewport::-webkit-scrollbar{width:10px;height:10px}.viewport::-webkit-scrollbar-track{background:var(--control-bg, #e8e8e8)}.viewport::-webkit-scrollbar-thumb{background:var(--control-border, #c0c0c0);border-radius:5px}.viewport::-webkit-scrollbar-thumb:hover{background:var(--control-color, #000080)}.json-color,.json-boolean{position:relative}.json-color:before,.json-boolean:before{content:"";position:absolute;left:-8px;top:50%;transform:translateY(-50%);margin-top:-1px;width:8px;height:8px;border-radius:2px;cursor:pointer}.json-color:hover:before,.json-boolean:hover:before{transform:translateY(-50%) scale(1.2);border-color:var(--control-color, #000080)}.json-color:before{background-color:var(--swatch-color);border:1px solid var(--json-punct, #a9b7c6)}.json-boolean:before{border:1.5px solid var(--control-border, #c0c0c0);background:transparent}.json-bool-true:before{content:"✔";border-color:var(--control-color, #000080);color:var(--control-color, #000080);font-size:8px;display:flex;align-items:center;justify-content:center;line-height:8px}.line.has-visibility:before{content:"👁";position:absolute;left:0;top:3px;font-size:10px;color:var(--control-color, #000080);opacity:.6;cursor:pointer;z-index:1}.line.has-visibility:hover:before{opacity:1;transform:scale(1.15)}.line.has-visibility.feature-hidden:before{opacity:.5}';const s=z("div");for(s.innerHTML=function(e="",t=""){return`\n <div class="prefix-wrapper">\n <div class="prefix-gutter"></div>\n <div class="editor-prefix" id="editorPrefix"></div>\n <button class="info-btn" id="infoBtn" title="@softwarity/geojson-editor v${t}" aria-label="About">ⓘ</button>\n </div>\n <div class="editor-wrapper">\n <div class="gutter">\n <div class="gutter-scroll" id="gutterScroll">\n <div class="gutter-scroll-content" id="gutterScrollContent">\n <div class="gutter-content" id="gutterContent"></div>\n </div>\n </div>\n </div>\n <div class="editor-content">\n <div class="placeholder-layer" id="placeholderLayer">${s=e,s?s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""}</div>\n <textarea\n class="hidden-textarea"\n id="hiddenTextarea"\n spellcheck="false"\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n tabindex="0"\n ></textarea>\n <div class="viewport" id="viewport">\n <div class="scroll-content" id="scrollContent">\n <div class="lines-container" id="linesContainer"></div>\n </div>\n </div>\n </div>\n </div>\n <div class="suffix-wrapper">\n <div class="suffix-gutter"></div>\n <div class="editor-suffix" id="editorSuffix"></div>\n <div class="error-nav" id="errorNav">\n <button class="error-nav-btn" id="prevErrorBtn" title="Previous error">◀</button>\n <span class="error-count" id="errorCount"></span>\n <button class="error-nav-btn" id="nextErrorBtn" title="Next error">▶</button>\n </div>\n <button class="clear-btn" id="clearBtn" title="Clear editor">✕</button>\n </div>\n `;var s}(this.placeholder,"1.0.21"),e.innerHTML="",e.appendChild(t);s.firstChild;)e.appendChild(s.firstChild)}_cacheElements(){this._viewport=this._id("viewport"),this._linesContainer=this._id("linesContainer"),this._scrollContent=this._id("scrollContent"),this._hiddenTextarea=this._id("hiddenTextarea"),this._gutterContent=this._id("gutterContent"),this._gutterScrollContent=this._id("gutterScrollContent"),this._gutterScroll=this._id("gutterScroll"),this._gutter=this.shadowRoot.querySelector(".gutter"),this._clearBtn=this._id("clearBtn"),this._editorWrapper=this.shadowRoot.querySelector(".editor-wrapper"),this._placeholderLayer=this._id("placeholderLayer"),this._editorPrefix=this._id("editorPrefix"),this._editorSuffix=this._id("editorSuffix"),this._errorNav=this._id("errorNav"),this._errorCount=this._id("errorCount"),this._prevErrorBtn=this._id("prevErrorBtn"),this._nextErrorBtn=this._id("nextErrorBtn")}setupEventListeners(){const e=this._hiddenTextarea,t=this._viewport,s=this._gutterContent,i=this._gutter,n=this._clearBtn,r=this._editorWrapper;if(!(e&&t&&s&&i&&n&&r))return;this._isSelecting=!1,t.addEventListener("click",e=>{this.handleEditorClick(e)},!0),t.addEventListener("mousedown",t=>{const s=t.target,i=s.closest(".line.has-visibility");if(i){const e=i.getBoundingClientRect();if(t.clientX-e.left<14)return void(this._blockRender=!0)}if(s.classList.contains("json-color")||s.classList.contains("json-boolean")){const e=s.getBoundingClientRect(),i=t.clientX-e.left;if(i<0&&i>=-8)return void(this._blockRender=!0)}if(t.preventDefault(),2===t.detail){const s=this._getPositionFromClick(t);return this._selectWordAt(s.line,s.column),this._isSelecting=!1,e.focus(),this._invalidateRenderCache(),void this.scheduleRender()}const n=this._getPositionFromClick(t);t.shiftKey&&this.selectionStart?(this.selectionEnd=n,this.cursorLine=n.line,this.cursorColumn=n.column):(this.cursorLine=n.line,this.cursorColumn=n.column,this.selectionStart={line:n.line,column:n.column},this.selectionEnd=null,this._isSelecting=!0),e.focus(),this._invalidateRenderCache(),this.scheduleRender()}),t.addEventListener("mousemove",e=>{if(!this._isSelecting)return;const s=this._getPositionFromClick(e);this.selectionEnd=s,this.cursorLine=s.line,this.cursorColumn=s.column;const i=t.getBoundingClientRect();e.clientY<i.top+30?t.scrollTop-=20:e.clientY>i.bottom-30&&(t.scrollTop+=20),this._invalidateRenderCache(),this.scheduleRender()}),document.addEventListener("mouseup",()=>{this._isSelecting=!1}),e.addEventListener("focus",()=>{r.classList.add("focused"),this._invalidateRenderCache(),this.scheduleRender()}),e.addEventListener("blur",()=>{r.classList.remove("focused"),this._invalidateRenderCache(),this.scheduleRender()});let o=!1;t.addEventListener("scroll",()=>{o||(this.syncGutterScroll(),this._scrollRaf||(this._scrollRaf=requestAnimationFrame(()=>{this._scrollRaf=null,o=!0,this.renderViewport(),o=!1})))}),e.addEventListener("compositionstart",()=>{this._isComposing=!0}),e.addEventListener("compositionend",()=>{this._isComposing=!1,this.handleInput()}),e.addEventListener("input",()=>{this._isComposing||this.handleInput()}),e.addEventListener("keydown",e=>{this.handleKeydown(e)}),e.addEventListener("paste",e=>{this.handlePaste(e)}),e.addEventListener("copy",e=>{this.handleCopy(e)}),e.addEventListener("cut",e=>{this.handleCut(e)}),s.addEventListener("click",e=>{this.handleGutterClick(e)}),i.addEventListener("mousedown",e=>{e.preventDefault()}),i.addEventListener("wheel",e=>{e.preventDefault(),t.scrollTop+=e.deltaY}),n.addEventListener("click",()=>{this.removeAll()}),this._prevErrorBtn?.addEventListener("click",()=>{this.goToPrevError()}),this._nextErrorBtn?.addEventListener("click",()=>{this.goToNextError()}),this.updateReadonly()}setValue(e,t=!0){if(this.lines.length>0&&this._saveToHistory("setValue"),e&&e.trim())try{const t="["+e+"]",s=JSON.parse(t),i=JSON.stringify(s,null,2).split("\n");this.lines=i.slice(1,-1)}catch(s){this.lines=e.split("\n")}else this.lines=[];this.collapsedNodes.clear(),this.hiddenFeatures.clear(),this._openedNodeKeys.clear(),this._lineToNodeId.clear(),this._nodeIdToLines.clear(),this.cursorLine=0,this.cursorColumn=0,this.updateModel(),this.scheduleRender(),this.updatePlaceholderVisibility(),t&&this.lines.length>0&&requestAnimationFrame(()=>{this.autoCollapseCoordinates()}),this.emitChange()}getContent(){return this.lines.join("\n")}updateModel(){this._contextMapCache=null,this._errorLinesCache=null,this._rebuildNodeIdMappings(),this.computeFeatureRanges(),this.computeLineMetadata(),this.computeVisibleLines()}updateView(){this.computeLineMetadata(),this.computeVisibleLines()}computeFeatureRanges(){this.featureRanges.clear();try{const e=this.lines.join("\n"),t=this.prefix+e+this.suffix,s=JSON.parse(t);if(!s.features)return;let i=0,n=0,r=!1,o=-1,l=null;for(let a=0;a<this.lines.length;a++){const e=this.lines[a];if(!r&&T.test(e)){let e=a;for(let t=a;t>=0;t--){const s=this.lines[t].trim();if("{"===s||"{,"===s){e=t;break}}o=e,r=!0,n=1;for(let t=e;t<=a;t++){const s=P(this.lines[t],"{");n+=t===e?s.open-1-s.close:s.open-s.close}i<s.features.length&&(l=M(s.features[i]))}else if(r){const t=P(e,"{");n+=t.open-t.close,n<=0&&(l&&this.featureRanges.set(l,{startLine:o,endLine:a,featureIndex:i}),i++,r=!1,l=null)}}}catch(e){}}computeLineMetadata(){this.lineMetadata.clear();const e=this._findCollapsibleRanges(),t=this._computeErrorLines();for(let s=0;s<this.lines.length;s++){const i=this.lines[s],n={colors:[],booleans:[],collapseButton:null,visibilityButton:null,isHidden:!1,isCollapsed:!1,featureKey:null,hasError:t.has(s)};let r;for(w.lastIndex=0;null!==(r=w.exec(i));){const[,e,t,s]=r;s?n.booleans.push({attributeName:e,value:"true"===s}):t&&(b.test(t)||O(t))&&n.colors.push({attributeName:e,color:t})}const o=e.find(e=>e.startLine===s);o&&(n.collapseButton={nodeKey:o.nodeKey,nodeId:o.nodeId,isCollapsed:this.collapsedNodes.has(o.nodeId)}),e.find(e=>this.collapsedNodes.has(e.nodeId)&&s>e.startLine&&s<e.endLine)&&(n.isCollapsed=!0);for(const[e,t]of this.featureRanges)if(s>=t.startLine&&s<=t.endLine){n.featureKey=e,this.hiddenFeatures.has(e)&&(n.isHidden=!0),s===t.startLine&&(n.visibilityButton={featureKey:e,isHidden:this.hiddenFeatures.has(e)});break}this.lineMetadata.set(s,n)}}_computeErrorLines(){if(null!==this._errorLinesCache)return this._errorLinesCache;const e=/* @__PURE__ */new Set;for(let s=0;s<this.lines.length;s++)V(this.lines[s],"",void 0).includes("json-error")&&e.add(s);try{const e="["+this.lines.join("\n")+"]";JSON.parse(e)}catch(t){if(t instanceof Error){const s=t.message.match(/line (\d+)/);if(s){const t=Math.max(0,parseInt(s[1],10)-1);e.add(t)}}}return this._errorLinesCache=e,e}_getErrorLines(){const e=[];for(const[t,s]of this.lineMetadata)s.hasError&&e.push(t);return e.sort((e,t)=>e-t)}goToNextError(){const e=this._getErrorLines();if(0===e.length)return!1;const t=e.find(e=>e>this.cursorLine),s=void 0!==t?t:e[0];return this._goToErrorLine(s)}goToPrevError(){const e=this._getErrorLines();if(0===e.length)return!1;const t=e.filter(e=>e<this.cursorLine),s=t.length>0?t[t.length-1]:e[e.length-1];return this._goToErrorLine(s)}_expandNodesContainingLine(e){let t=!1;for(const[s,i]of this._nodeIdToLines)this.collapsedNodes.has(s)&&e>i.startLine&&e<=i.endLine&&(this.collapsedNodes.delete(s),i.uniqueKey&&this._openedNodeKeys.add(i.uniqueKey),t=!0);return t}_goToErrorLine(e){return this._expandNodesContainingLine(e)&&this.updateView(),this.cursorLine=e,this.cursorColumn=0,this._invalidateRenderCache(),this._scrollToCursor(!0),this.renderViewport(),this._updateErrorDisplay(),this._hiddenTextarea?.focus(),!0}_expandErrorNodes(){const e=this._getErrorLines();if(0===e.length)return;let t=!1;for(const s of e)this._expandNodesContainingLine(s)&&(t=!0);t&&this.updateView()}computeVisibleLines(){this.visibleLines=[];for(let e=0;e<this.lines.length;e++){const t=this.lineMetadata.get(e);t&&t.isCollapsed||this.visibleLines.push({index:e,content:this.lines[e],meta:t})}this._invalidateRenderCache()}scheduleRender(){this.renderTimer||(this.renderTimer=requestAnimationFrame(()=>{this.renderTimer=void 0,this.renderViewport()}))}renderViewport(){if(this._blockRender)return;const e=this._viewport,t=this._linesContainer,s=this._scrollContent;if(!e||!t)return;this.viewportHeight=e.clientHeight;const i=this.visibleLines.length,n=i*this.lineHeight;if(s){s.style.height=`${n}px`;const e=this._getCharWidth(),t=this.lines.reduce((e,t)=>Math.max(e,t.length),0)*e+20;s.style.minWidth=`${t}px`}const r=e.scrollTop,o=Math.floor(r/this.lineHeight),l=Math.ceil(this.viewportHeight/this.lineHeight),a=Math.max(0,o-this.bufferLines),h=Math.min(i,o+l+this.bufferLines);if(i>0&&this._lastStartIndex===a&&this._lastEndIndex===h&&this._lastTotalLines===i)return;this._lastStartIndex=a,this._lastEndIndex=h,this._lastTotalLines=i;const c=a*this.lineHeight;t.style.transform=`translateY(${c}px)`;const d=this._buildContextMap(),u=this._editorWrapper?.classList.contains("focused"),p=document.createDocumentFragment();if(0===i){const e=z("div");return e.className="line empty-line",e.dataset.lineIndex="0",u&&(e.innerHTML=this._insertCursor(0)),p.appendChild(e),t.innerHTML="",t.appendChild(p),void this.renderGutter(0,0)}for(let g=a;g<h;g++){const e=this.visibleLines[g];if(!e)continue;const t=z("div");t.className="line",t.dataset.lineIndex=String(e.index),e.meta?.visibilityButton&&(t.classList.add("has-visibility"),t.dataset.featureKey=e.meta.visibilityButton.featureKey,e.meta.visibilityButton.isHidden&&t.classList.add("feature-hidden")),e.meta?.isHidden&&t.classList.add("line-hidden");const s=d.get(e.index)||"Feature";let i=V(e.content,s,e.meta);u&&this._hasSelection()&&(i=this._addSelectionHighlight(i,e.index,e.content)),u&&e.index===this.cursorLine&&(i+=this._insertCursor(this.cursorColumn)),t.innerHTML=i,p.appendChild(t)}t.innerHTML="",t.appendChild(p),this.renderGutter(a,h)}_insertCursor(e){const t=this._getCharWidth(),s=e*t;return this._insertMode?`<span class="cursor" style="left: ${s}px"></span>`:`<span class="cursor cursor-block" style="left: ${s}px; width: ${t}px"></span>`}_addSelectionHighlight(e,t,s){const i=this._normalizeSelection();if(!i)return e;const{start:n,end:r}=i;if(t<n.line||t>r.line)return e;const o=this._getCharWidth();let l,a;return t===n.line&&t===r.line?(l=n.column,a=r.column):t===n.line?(l=n.column,a=s.length):t===r.line?(l=0,a=r.column):(l=0,a=s.length),`<span class="selection" style="left: ${l*o}px; width: ${(a-l)*o}px"></span>`+e}_getCharWidth(){if(!this._charWidth){const e=z("canvas").getContext("2d");e?(e.font="13px 'Courier New', Courier, monospace",this._charWidth=e.measureText("M").width):this._charWidth=7.8}return this._charWidth}renderGutter(e,t){const s=this._gutterContent,i=this._gutterScrollContent;if(!s)return;const n=this.visibleLines.length*this.lineHeight;i&&(i.style.height=`${n}px`);const r=e*this.lineHeight;s.style.transform=`translateY(${r}px)`;const o=document.createDocumentFragment();for(let l=e;l<t;l++){const e=this.visibleLines[l];if(!e)continue;const t=z("div");t.className="gutter-line";const s=e.meta;s?.hasError&&t.classList.add("has-error");const i=z("span");i.className="line-number",i.textContent=String(e.index+1),t.appendChild(i);const n=z("div");if(n.className="collapse-column",s?.collapseButton){const t=z("div");t.className="collapse-button"+(s.collapseButton.isCollapsed?" collapsed":""),t.textContent=s.collapseButton.isCollapsed?"›":"⌄",t.dataset.line=String(e.index),t.dataset.nodeId=s.collapseButton.nodeId,t.title=s.collapseButton.isCollapsed?"Expand":"Collapse",n.appendChild(t)}t.appendChild(n),o.appendChild(t)}s.innerHTML="",s.appendChild(o)}syncGutterScroll(){this._gutterScroll&&this._viewport&&(this._gutterScroll.scrollTop=this._viewport.scrollTop)}handleInput(){const e=this._hiddenTextarea,t=e?.value;if(t)if(this._hasSelection()&&this._deleteSelection(),this._getCollapsedRangeForLine(this.cursorLine))e.value="";else{if(this._getCollapsedClosingLine(this.cursorLine)){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);if(this.cursorColumn<=s)return void(e.value="")}if(this._getCollapsedNodeAtLine(this.cursorLine)){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t)return void(e.value="")}if(this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],s=e.substring(0,this.cursorColumn),i=t.split("\n");if(1===i.length){if(this._insertMode){const i=e.substring(this.cursorColumn);this.lines[this.cursorLine]=s+t+i}else{const i=e.substring(this.cursorColumn+t.length);this.lines[this.cursorLine]=s+t+i}this.cursorColumn+=t.length}else{const t=e.substring(this.cursorColumn);this.lines[this.cursorLine]=s+i[0];for(let e=1;e<i.length-1;e++)this.lines.splice(this.cursorLine+e,0,i[e]);const n=i[i.length-1]+t;this.lines.splice(this.cursorLine+i.length-1,0,n),this.cursorLine+=i.length-1,this.cursorColumn=i[i.length-1].length}}else{const e=t.split("\n");this.lines.push(...e),this.cursorLine=this.lines.length-1,this.cursorColumn=this.lines[this.cursorLine].length}e.value="",clearTimeout(this.inputTimer),this.inputTimer=setTimeout(()=>{this.formatAndUpdate()},150)}}handleKeydown(e){const t={inCollapsedZone:this._getCollapsedRangeForLine(this.cursorLine),onCollapsedNode:this._getCollapsedNodeAtLine(this.cursorLine),onClosingLine:this._getCollapsedClosingLine(this.cursorLine)},s={Enter:()=>this._handleEnter(e.shiftKey,t),Backspace:()=>this._handleBackspace(t),Delete:()=>this._handleDelete(t),ArrowUp:()=>this._handleArrowKey(-1,0,e.shiftKey,e.ctrlKey||e.metaKey),ArrowDown:()=>this._handleArrowKey(1,0,e.shiftKey,e.ctrlKey||e.metaKey),ArrowLeft:()=>this._handleArrowKey(0,-1,e.shiftKey,e.ctrlKey||e.metaKey),ArrowRight:()=>this._handleArrowKey(0,1,e.shiftKey,e.ctrlKey||e.metaKey),Home:()=>this._handleHomeEnd("home",e.shiftKey,t.onClosingLine),End:()=>this._handleHomeEnd("end",e.shiftKey,t.onClosingLine),PageUp:()=>this._handlePageUpDown("up",e.shiftKey),PageDown:()=>this._handlePageUpDown("down",e.shiftKey),Tab:()=>this._handleTab(e.shiftKey,t),Insert:()=>{this._insertMode=!this._insertMode,this.scheduleRender()}},i={a:()=>this._selectAll(),z:()=>e.shiftKey?this.redo():this.undo(),y:()=>this.redo(),s:()=>this.save(),o:()=>!this.hasAttribute("readonly")&&this.open(),i:()=>this.internalAddShortcut&&!this.readonly&&this._handleAddFeaturePrompt()};if(s[e.key])return e.preventDefault(),e.stopPropagation(),void s[e.key]();(e.ctrlKey||e.metaKey)&&i[e.key]&&(e.preventDefault(),e.stopPropagation(),i[e.key]())}_handleEnter(e,t){if(e){const e=this._getContainingExpandedNode(this.cursorLine);if(e){const t=this.lines[e.startLine],s=t.search(N);this.toggleCollapse(e.nodeId),this.cursorLine=e.startLine,this.cursorColumn=s>=0?s+1:t.length,this._clearSelection(),this._scrollToCursor()}return}if(t.onCollapsedNode)this.toggleCollapse(t.onCollapsedNode.nodeId);else{if(t.onClosingLine){const e=this.lines[this.cursorLine],s=this._getClosingBracketPos(e);if(s>=0&&this.cursorColumn<=s)return void this.toggleCollapse(t.onClosingLine.nodeId)}t.inCollapsedZone}}_handleBackspace(e){if(this._hasSelection())return this._deleteSelection(),void this.formatAndUpdate();if(e.onClosingLine){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);return s>=0&&this.cursorColumn>s+1?void this.deleteBackward():void this._deleteCollapsedNode(e.onClosingLine)}if(e.onCollapsedNode&&0===this.cursorColumn)this._deleteCollapsedNode(e.onCollapsedNode);else if(!e.inCollapsedZone){if(e.onCollapsedNode){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t+1)return void this._deleteCollapsedNode(e.onCollapsedNode)}this.deleteBackward()}}_handleDelete(e){if(this._hasSelection())return this._deleteSelection(),void this.formatAndUpdate();if(e.onClosingLine){const t=this.lines[this.cursorLine],s=this._getClosingBracketPos(t);return s>=0&&this.cursorColumn>s?void this.deleteForward():void this._deleteCollapsedNode(e.onClosingLine)}if(e.onCollapsedNode){const t=this.lines[this.cursorLine].search(N);if(this.cursorColumn>t)return void this._deleteCollapsedNode(e.onCollapsedNode)}e.inCollapsedZone||this.deleteForward()}_handleTab(e,t){e?this._navigateToPrevAttribute():this._navigateToNextAttribute()}_navigateToNextAttribute(){const e=this.visibleLines.length;let t=this.visibleLines.findIndex(e=>e.index===this.cursorLine);t<0&&(t=0);for(let s=t;s<e;s++){const e=this.visibleLines[s],i=this.lines[e.index],n=s===t?this.cursorColumn:0,r=this._findNextAttributeOrBracket(i,n,e.index);if(null!==r)return this.cursorLine=e.index,this.cursorColumn=r.start,r.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:r.start},this.selectionEnd={line:e.index,column:r.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}for(let s=0;s<t;s++){const e=this.visibleLines[s],t=this.lines[e.index],i=this._findNextAttributeOrBracket(t,0,e.index);if(null!==i)return this.cursorLine=e.index,this.cursorColumn=i.start,i.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:i.start},this.selectionEnd={line:e.index,column:i.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}}_navigateToPrevAttribute(){const e=this.visibleLines.length;let t=this.visibleLines.findIndex(e=>e.index===this.cursorLine);t<0&&(t=e-1);for(let s=t;s>=0;s--){const e=this.visibleLines[s],i=this.lines[e.index],n=s===t?this.cursorColumn:i.length,r=this._findPrevAttributeOrBracket(i,n,e.index);if(null!==r)return this.cursorLine=e.index,this.cursorColumn=r.start,r.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:r.start},this.selectionEnd={line:e.index,column:r.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}for(let s=e-1;s>t;s--){const e=this.visibleLines[s],t=this.lines[e.index],i=this._findPrevAttributeOrBracket(t,t.length,e.index);if(null!==i)return this.cursorLine=e.index,this.cursorColumn=i.start,i.isBracket?this._clearSelection():(this.selectionStart={line:e.index,column:i.start},this.selectionEnd={line:e.index,column:i.end}),this._scrollToCursor(),this._invalidateRenderCache(),void this.scheduleRender()}}_findNextAttributeInLine(e,t){const s=[],i=/"([^"]+)"(?:\s*:\s*(?:"([^"]*)"|(-?\d+\.?\d*(?:e[+-]?\d+)?)|true|false|null))?/gi;let n;for(;null!==(n=i.exec(e));){const t=n.index+1,i=t+n[1].length;if(s.push({start:t,end:i}),void 0!==n[2]){const t=e.substring(n.index).match(/:\s*"([^"]*)"/);if(t){const e=n.index+(t.index||0)+t[0].indexOf('"')+1,i=e+n[2].length;s.push({start:e,end:i})}}else if(void 0!==n[3]){const t=e.substring(n.index).match(/:\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)/i);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}else{const t=e.substring(n.index).match(/:\s*(true|false|null)/);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}}const r=/(?:^|[\[,\s])(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*(?:[,\]]|$)/gi;for(;null!==(n=r.exec(e));){const e=n[1],t=n.index+n[0].indexOf(e),i=t+e.length;s.some(e=>e.start===t&&e.end===i)||s.push({start:t,end:i})}s.sort((e,t)=>e.start-t.start);for(const o of s)if(o.start>t)return o;return null}_findPrevAttributeInLine(e,t){const s=[],i=/"([^"]+)"(?:\s*:\s*(?:"([^"]*)"|(-?\d+\.?\d*(?:e[+-]?\d+)?)|true|false|null))?/gi;let n;for(;null!==(n=i.exec(e));){const t=n.index+1,i=t+n[1].length;if(s.push({start:t,end:i}),void 0!==n[2]){const t=e.substring(n.index).match(/:\s*"([^"]*)"/);if(t){const e=n.index+(t.index||0)+t[0].indexOf('"')+1,i=e+n[2].length;s.push({start:e,end:i})}}else if(void 0!==n[3]){const t=e.substring(n.index).match(/:\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)/i);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}else{const t=e.substring(n.index).match(/:\s*(true|false|null)/);if(t){const e=n.index+(t.index||0)+t[0].indexOf(t[1]),i=e+t[1].length;s.push({start:e,end:i})}}}const r=/(?:^|[\[,\s])(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*(?:[,\]]|$)/gi;for(;null!==(n=r.exec(e));){const e=n[1],t=n.index+n[0].indexOf(e),i=t+e.length;s.some(e=>e.start===t&&e.end===i)||s.push({start:t,end:i})}s.sort((e,t)=>e.start-t.start);for(let o=s.length-1;o>=0;o--)if(s[o].end<t)return s[o];return null}_findBracketInLine(e){const t=e.match(/[\[{]\s*$/);return t&&void 0!==t.index?t.index+1:null}_findNextAttributeOrBracket(e,t,s){const i=this._findNextAttributeInLine(e,t),n=this._findBracketInLine(e);return null!==i&&null!==n?n>t&&n<i.start?{start:n,end:n,isBracket:!0}:{...i,isBracket:!1}:null!==i?{...i,isBracket:!1}:null!==n&&n>t?{start:n,end:n,isBracket:!0}:null}_findPrevAttributeOrBracket(e,t,s){const i=this._findPrevAttributeInLine(e,t),n=this._findBracketInLine(e);return null!==i&&null!==n?n<t&&n>i.end?{start:n,end:n,isBracket:!0}:{...i,isBracket:!1}:null!==i?{...i,isBracket:!1}:null!==n&&n<t?{start:n,end:n,isBracket:!0}:null}insertNewline(){if(this._saveToHistory("newline"),this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],t=e.substring(0,this.cursorColumn),s=e.substring(this.cursorColumn);this.lines[this.cursorLine]=t,this.lines.splice(this.cursorLine+1,0,s),this.cursorLine++,this.cursorColumn=0}else this.lines.push(""),this.cursorLine=this.lines.length-1,this.cursorColumn=0;this.formatAndUpdate()}deleteBackward(){if(this._saveToHistory("delete"),this.cursorColumn>0){const e=this.lines[this.cursorLine];this.lines[this.cursorLine]=e.substring(0,this.cursorColumn-1)+e.substring(this.cursorColumn),this.cursorColumn--}else if(this.cursorLine>0){const e=this.lines[this.cursorLine],t=this.lines[this.cursorLine-1];this.cursorColumn=t.length,this.lines[this.cursorLine-1]=t+e,this.lines.splice(this.cursorLine,1),this.cursorLine--}this.formatAndUpdate()}deleteForward(){if(this._saveToHistory("delete"),this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine];this.cursorColumn<e.length?this.lines[this.cursorLine]=e.substring(0,this.cursorColumn)+e.substring(this.cursorColumn+1):this.cursorLine<this.lines.length-1&&(this.lines[this.cursorLine]=e+this.lines[this.cursorLine+1],this.lines.splice(this.cursorLine+1,1))}this.formatAndUpdate()}moveCursorSkipCollapsed(e){let t=this.cursorLine+e;for(;t>=0&&t<this.lines.length;){const s=this._getCollapsedRangeForLine(t);if(!s)break;t=e>0?s.endLine:s.startLine}this.cursorLine=Math.max(0,Math.min(this.lines.length-1,t));const s=this.lines[this.cursorLine]?.length||0;this.cursorColumn=Math.min(this.cursorColumn,s),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}moveCursorHorizontal(e){e>0?this._moveCursorRight():this._moveCursorLeft(),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_moveCursorRight(){const e=this.lines[this.cursorLine],t=this._getCollapsedNodeAtLine(this.cursorLine);if(this._getCollapsedClosingLine(this.cursorLine)){const t=this._getClosingBracketPos(e);this.cursorColumn<t?this.cursorColumn=t:this.cursorColumn>=e.length?this.cursorLine<this.lines.length-1&&(this.cursorLine++,this.cursorColumn=0):this.cursorColumn++}else if(t){const s=e.search(N);this.cursorColumn<s?this.cursorColumn++:this.cursorColumn===s?this.cursorColumn=s+1:(this.cursorLine=t.endLine,this.cursorColumn=this._getClosingBracketPos(this.lines[this.cursorLine]))}else if(this.cursorColumn>=e.length){if(this.cursorLine<this.lines.length-1){this.cursorLine++,this.cursorColumn=0;const e=this._getCollapsedRangeForLine(this.cursorLine);e&&(this.cursorLine=e.endLine,this.cursorColumn=0)}}else this.cursorColumn++}_moveCursorLeft(){const e=this.lines[this.cursorLine],t=this._getCollapsedNodeAtLine(this.cursorLine),s=this._getCollapsedClosingLine(this.cursorLine);if(s){const t=this._getClosingBracketPos(e);if(this.cursorColumn>t+1)this.cursorColumn--;else{this.cursorLine=s.startLine;const e=this.lines[this.cursorLine];this.cursorColumn=e.search(N)+1}}else if(t){const t=e.search(N);this.cursorColumn>t+1?this.cursorColumn=t+1:this.cursorColumn===t+1?this.cursorColumn=t:this.cursorColumn>0?this.cursorColumn--:this.cursorLine>0&&(this.cursorLine--,this.cursorColumn=this.lines[this.cursorLine]?.length||0)}else if(this.cursorColumn>0)this.cursorColumn--;else if(this.cursorLine>0)if(this.cursorLine--,this._getCollapsedClosingLine(this.cursorLine))this.cursorColumn=this.lines[this.cursorLine]?.length||0;else{const e=this._getCollapsedRangeForLine(this.cursorLine);if(e){this.cursorLine=e.startLine;const t=this.lines[this.cursorLine];this.cursorColumn=t.search(N)+1}else this.cursorColumn=this.lines[this.cursorLine]?.length||0}}_scrollToCursor(e=!1){const t=this._viewport;if(!t)return;const s=this.visibleLines.findIndex(e=>e.index===this.cursorLine);if(-1===s)return;const i=s*this.lineHeight,n=t.clientHeight;if(e)t.scrollTop=Math.max(0,i-n/2+this.lineHeight/2);else{const e=t.scrollTop,s=e+n;i<e?t.scrollTop=i:i+this.lineHeight>s&&(t.scrollTop=i+this.lineHeight-n)}}_handleArrowKey(e,t,s,i=!1){s&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn}),0!==e?this.moveCursorSkipCollapsed(e):0!==t&&(i?this._moveCursorByWord(t):this.moveCursorHorizontal(t)),s?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null)}_moveCursorByWord(e){const t=this.lines[this.cursorLine]||"",s=e=>R.test(e),i=this._getCollapsedNodeAtLine(this.cursorLine);if(e>0){let e=this.cursorColumn;if(i){const s=t.search(N);if(s>=0&&e>=s)return this.cursorLine=i.endLine,this.cursorColumn=(this.lines[this.cursorLine]||"").length,this._invalidateRenderCache(),this._scrollToCursor(),void this.scheduleRender()}if(e>=t.length){if(this.cursorLine<this.lines.length-1){let e=this.cursorLine+1;const t=this._getCollapsedRangeForLine(e);t&&(e=t.endLine),this.cursorLine=Math.min(e,this.lines.length-1),this.cursorColumn=0}}else if(s(t[e])){for(;e<t.length&&s(t[e]);)e++;this.cursorColumn=e}else{for(;e<t.length&&!s(t[e]);)e++;this.cursorColumn=e}}else{let e=this.cursorColumn;const i=this._getCollapsedClosingLine(this.cursorLine);if(i){const s=this._getClosingBracketPos(t);if(s>=0&&e<=s+1){this.cursorLine=i.startLine;const e=(this.lines[this.cursorLine]||"").search(N);return this.cursorColumn=e>=0?e:0,this._invalidateRenderCache(),this._scrollToCursor(),void this.scheduleRender()}}if(0===e){if(this.cursorLine>0){let e=this.cursorLine-1;const t=this._getCollapsedRangeForLine(e);t&&(e=t.startLine),this.cursorLine=Math.max(e,0),this.cursorColumn=this.lines[this.cursorLine].length}}else if(e>0&&s(t[e-1])){for(;e>0&&s(t[e-1]);)e--;this.cursorColumn=e}else{for(;e>0&&!s(t[e-1]);)e--;for(;e>0&&s(t[e-1]);)e--;this.cursorColumn=e}}this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_handleHomeEnd(e,t,s){if(t&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn}),"home"===e)s?(this.cursorLine=s.startLine,this.cursorColumn=0):0===this.cursorColumn?(this.cursorLine=0,this.cursorColumn=0):this.cursorColumn=0;else{const e=this.lines[this.cursorLine]?.length||0;this.cursorColumn===e?(this.cursorLine=this.lines.length-1,this.cursorColumn=this.lines[this.cursorLine]?.length||0):this.cursorColumn=e}t?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_handlePageUpDown(e,t){t&&!this.selectionStart&&(this.selectionStart={line:this.cursorLine,column:this.cursorColumn});const s=this._viewport;if(!s)return;const i=Math.floor(s.clientHeight/this.lineHeight);if("up"===e){const e=this.visibleLines.findIndex(e=>e.index===this.cursorLine),t=Math.max(0,e-i);this.cursorLine=this.visibleLines[t]?.index||0}else{const e=this.visibleLines.findIndex(e=>e.index===this.cursorLine),t=Math.min(this.visibleLines.length-1,e+i);this.cursorLine=this.visibleLines[t]?.index||this.lines.length-1}this.cursorColumn=Math.min(this.cursorColumn,this.lines[this.cursorLine]?.length||0),t?this.selectionEnd={line:this.cursorLine,column:this.cursorColumn}:(this.selectionStart=null,this.selectionEnd=null),this._invalidateRenderCache(),this._scrollToCursor(),this.scheduleRender()}_selectAll(){this.selectionStart={line:0,column:0};const e=this.lines.length-1;this.selectionEnd={line:e,column:this.lines[e]?.length||0},this.cursorLine=e,this.cursorColumn=this.lines[e]?.length||0,this._invalidateRenderCache(),this.scheduleRender()}_getSelectedText(){const e=this._normalizeSelection();if(!e)return"";const{start:t,end:s}=e;if(t.line===s.line)return this.lines[t.line].substring(t.column,s.column);let i=this.lines[t.line].substring(t.column)+"\n";for(let n=t.line+1;n<s.line;n++)i+=this.lines[n]+"\n";return i+=this.lines[s.line].substring(0,s.column),i}_normalizeSelection(){if(!this.selectionStart||!this.selectionEnd)return null;const e=this.selectionStart,t=this.selectionEnd;return e.line<t.line||e.line===t.line&&e.column<=t.column?{start:e,end:t}:{start:t,end:e}}_hasSelection(){return!(!this.selectionStart||!this.selectionEnd||this.selectionStart.line===this.selectionEnd.line&&this.selectionStart.column===this.selectionEnd.column)}_clearSelection(){this.selectionStart=null,this.selectionEnd=null}_selectWordAt(e,t){if(e<0||e>=this.lines.length)return;const s=this.lines[e];if(!s||t>s.length)return;const i=e=>/[\w\-]/.test(e);let n=!1,r=-1,o=-1,l=!1;for(let c=0;c<s.length;c++){const i=s[c];if(l)l=!1;else if("\\"!==i){if('"'===i)if(n){if(o=c,t>=r&&t<=o)return this.selectionStart={line:e,column:r+1},this.selectionEnd={line:e,column:o},this.cursorLine=e,void(this.cursorColumn=o);n=!1,r=-1,o=-1}else n=!0,r=c}else l=!0}let a=t,h=t;for(;a>0&&i(s[a-1]);)a--;for(;h<s.length&&i(s[h]);)h++;a<h&&(this.selectionStart={line:e,column:a},this.selectionEnd={line:e,column:h},this.cursorLine=e,this.cursorColumn=h)}_deleteSelection(){const e=this._normalizeSelection();if(!e)return!1;const{start:t,end:s}=e;if(this._saveToHistory("delete"),t.line===s.line){const e=this.lines[t.line];this.lines[t.line]=e.substring(0,t.column)+e.substring(s.column)}else{const e=this.lines[t.line].substring(0,t.column),i=this.lines[s.line].substring(s.column);this.lines[t.line]=e+i,this.lines.splice(t.line+1,s.line-t.line)}return this.cursorLine=t.line,this.cursorColumn=t.column,this.selectionStart=null,this.selectionEnd=null,!0}insertText(e){if(this._hasSelection()&&this._deleteSelection(),!this._getCollapsedRangeForLine(this.cursorLine)){if(this._getCollapsedClosingLine(this.cursorLine)){const e=this.lines[this.cursorLine],t=this._getClosingBracketPos(e);if(this.cursorColumn<=t)return}if(this._getCollapsedNodeAtLine(this.cursorLine)){const e=this.lines[this.cursorLine].search(N);if(this.cursorColumn>e)return}if(this._saveToHistory("insert"),0===this.lines.length){const t=e.split("\n");this.lines=t,this.cursorLine=t.length-1,this.cursorColumn=t[t.length-1].length}else if(this.cursorLine<this.lines.length){const t=this.lines[this.cursorLine];this.lines[this.cursorLine]=t.substring(0,this.cursorColumn)+e+t.substring(this.cursorColumn),this.cursorColumn+=e.length}this.formatAndUpdate()}}handlePaste(e){e.preventDefault();const t=e.clipboardData?.getData("text/plain");if(!t)return;const s=0===this.lines.length;try{const e=H(JSON.parse(t)).map(e=>JSON.stringify(e,null,2)).join(",\n");this.insertText(e)}catch{this.insertText(t)}this.renderTimer&&(cancelAnimationFrame(this.renderTimer),this.renderTimer=void 0),s&&this.lines.length>0&&this.autoCollapseCoordinates(),this._expandErrorNodes(),this.renderViewport()}handleCopy(e){e.preventDefault(),e.clipboardData&&(this._hasSelection()?e.clipboardData.setData("text/plain",this._getSelectedText()):e.clipboardData.setData("text/plain",this.getContent()))}handleCut(e){e.preventDefault(),e.clipboardData&&(this._hasSelection()?(e.clipboardData.setData("text/plain",this._getSelectedText()),this._saveToHistory("cut"),this._deleteSelection(),this.formatAndUpdate()):(e.clipboardData.setData("text/plain",this.getContent()),this._saveToHistory("cut"),this.lines=[],this.cursorLine=0,this.cursorColumn=0,this.formatAndUpdate()))}_getPositionFromClick(e){const t=this._viewport,s=this._linesContainer;if(!t)return{line:0,column:0};const i=t.getBoundingClientRect(),n=e.clientY-i.top+t.scrollTop-8,r=Math.floor(n/this.lineHeight);let o=0,l=0;if(r>=0&&r<this.visibleLines.length){const n=this.visibleLines[r];o=n.index;const a=s?.querySelector(`.line[data-line-index="${n.index}"]`),h=this._getCharWidth();if(a){const t=a.getBoundingClientRect(),s=e.clientX-t.left,i=Math.round(s/h),r=n.content?.length||0;l=Math.max(0,Math.min(i,r))}else{const s=12,r=e.clientX-i.left+t.scrollLeft-s,o=Math.round(r/h),a=n.content?.length||0;l=Math.max(0,Math.min(o,a))}}return{line:o,column:l}}handleGutterClick(e){const t=e.target;if(!t)return;const s=t.closest(".visibility-button");if(s)this.toggleFeatureVisibility(s.dataset.featureKey);else if(t.classList.contains("collapse-button")){const e=t.dataset.nodeId;return void(e&&this.toggleCollapse(e))}}handleEditorClick(e){const t=e.target;if(!t)return;this._blockRender=!1;const s=t.closest(".line.has-visibility");if(s){const t=s.getBoundingClientRect();if(e.clientX-t.left<14){e.preventDefault(),e.stopPropagation();const t=s.dataset.featureKey;return void(t&&this.toggleFeatureVisibility(t))}}if(t.classList.contains("json-color")){const s=t.getBoundingClientRect(),i=e.clientX-s.left;if(i<0&&i>=-8){e.preventDefault(),e.stopPropagation();const s=t.dataset.color,i=t.closest(".line");if(i){const e=parseInt(i.dataset.lineIndex||"0"),n=this.lines[e].match(k);n&&n[1]&&s&&this.showColorPicker(t,e,s,n[1])}return}}if(t.classList.contains("json-boolean")){const s=t.getBoundingClientRect(),i=e.clientX-s.left;if(i<0&&i>=-8){e.preventDefault(),e.stopPropagation();const s=t.closest(".line");if(s){const e=parseInt(s.dataset.lineIndex||"0"),t=this.lines[e].match(S);if(t){const s="true"===t[2];this.updateBooleanValue(e,!s,t[1])}}return}}}toggleCollapse(e){const t=this._nodeIdToLines.get(e);this.collapsedNodes.has(e)?(this.collapsedNodes.delete(e),t?.uniqueKey&&this._openedNodeKeys.add(t.uniqueKey)):(this.collapsedNodes.add(e),t?.uniqueKey&&this._openedNodeKeys.delete(t.uniqueKey)),this.updateView(),this._invalidateRenderCache(),this.scheduleRender()}autoCollapseCoordinates(){this._hasErrors()||this._applyCollapsedOption(["coordinates"])}_hasErrors(){try{const e="["+this.lines.join("\n")+"]";JSON.parse(e)}catch{return!0}for(const e of this.lines)if(V(e,"",void 0).includes("json-error"))return!0;return!1}_applyCollapsedFromOptions(e,t){if(this._hasErrors())return;const s=void 0!==e.collapsed?e.collapsed:["coordinates"];s&&(!Array.isArray(s)||s.length>0)&&this._applyCollapsedOption(s,t)}_applyCollapsedOption(e,t=null){const s=this._findCollapsibleRanges(),i=s.filter(e=>e.isRootFeature);for(const n of s){let s=!1;if("function"==typeof e){const r=i.findIndex(e=>n.startLine>=e.startLine&&n.endLine<=e.endLine),o=e(t?.[r]||null,r);s=n.isRootFeature?o.includes("$root"):o.includes(n.nodeKey)}else Array.isArray(e)&&(s=n.isRootFeature?e.includes("$root"):e.includes(n.nodeKey));s&&this.collapsedNodes.add(n.nodeId)}this.updateModel(),this.scheduleRender()}toggleFeatureVisibility(e){e&&(this.hiddenFeatures.has(e)?this.hiddenFeatures.delete(e):this.hiddenFeatures.add(e),this.updateView(),this.scheduleRender(),this.emitChange())}showColorPicker(e,t,s,i){const n=document.querySelector(".geojson-color-picker-anchor");n&&n.remove();const r=z("div");r.className="geojson-color-picker-anchor";const o=e.getBoundingClientRect();r.style.cssText=`\n position: fixed;\n left: ${o.left-8}px;\n top: ${o.top+o.height}px;\n width: 10px;\n height: 10px;\n z-index: 9998;\n `,document.body.appendChild(r);const l=z("input");l.type="color";let a=s;a=s.startsWith("#")?s.replace(L,"#$1$1$2$2$3$3"):function(e){const t=(D||(D=document.createElement("div"),D.style.display="none",document.body.appendChild(D)),D);t.style.color=e;const s=getComputedStyle(t).color;if(!s)return null;const i=s.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(!i)return null;const[,n,r,o]=i;return"#"+parseInt(n,10).toString(16).padStart(2,"0")+parseInt(r,10).toString(16).padStart(2,"0")+parseInt(o,10).toString(16).padStart(2,"0")}(s)||"#000000",l.value=a,l.className="geojson-color-picker-input",l.style.cssText="\n position: absolute;\n left: 0;\n top: 0;\n width: 10px;\n height: 10px;\n opacity: 0;\n border: none;\n padding: 0;\n cursor: pointer;\n ",r.appendChild(l),l.addEventListener("input",e=>{this.updateColorValue(t,e.target.value,i)});const h=e=>{e.target!==l&&(document.removeEventListener("click",h,!0),r.remove())};l._closeListener=h,setTimeout(()=>{document.addEventListener("click",h,!0)},100),l.focus(),l.click()}updateColorValue(e,t,s){const i=new RegExp(`"${s}"\\s*:\\s*"(?:#[0-9a-fA-F]{3,6}|[a-zA-Z]+)"`);this.lines[e]=this.lines[e].replace(i,`"${s}": "${t}"`),this.updateView(),this.scheduleRender(),this.emitChange()}updateBooleanValue(e,t,s){const i=new RegExp(`"${s}"\\s*:\\s*(true|false)`);this.lines[e]=this.lines[e].replace(i,`"${s}": ${t}`),this.updateView(),this.scheduleRender(),this.emitChange()}_bestEffortFormat(e,t){const s=e.split("\n");if(void 0!==t&&t>=0&&t<s.length){const e=s[t],i=s.slice(0,t).join("\n"),n=i.trim()?this._formatChunk(i):[],r=this._computeDepthAtEnd(n)+this._computeBracketDelta(e),o=s.slice(t+1).join("\n"),l=o.trim()?this._formatChunk(o,r):[];return[...n,e,...l]}return this._formatChunk(e)}_computeBracketDelta(e){let t=0,s=!1,i=!1;for(const n of e)i?i=!1:"\\"===n&&s?i=!0:'"'!==n?s||("{"===n||"["===n?t++:"}"!==n&&"]"!==n||t--):s=!s;return t}_computeDepthAtEnd(e){let t=1;for(const s of e)for(const e of s)"{"===e||"["===e?t++:"}"!==e&&"]"!==e||(t=Math.max(0,t-1));return t}_formatChunk(e,t=1){const s=[];let i="",n=t,r=!1,o=!1;for(let l=0;l<e.length;l++){const t=e[l];if(o)i+=t,o=!1;else if("\\"===t&&r)i+=t,o=!0;else if('"'!==t)if(r)i+=t;else if("{"===t||"["===t)i+=t,s.push(" ".repeat(n)+i.trim()),n++,i="";else if("}"===t||"]"===t)i.trim()&&s.push(" ".repeat(n)+i.trim()),n=Math.max(0,n-1),i=t;else if(","===t)i+=t,s.push(" ".repeat(n)+i.trim()),i="";else if(":"===t){if(i+=": ",l++," "===e[l])continue;l--}else{if("\n"===t||"\r"===t)continue;i+=t}else r=!r,i+=t}return i.trim()&&s.push(" ".repeat(n)+i.trim()),s}formatAndUpdate(){const e=this.cursorLine,t=this.cursorColumn,s=this.lines.join("\n");try{const e="["+s+"]",t=JSON.parse(e),i=JSON.stringify(t,null,2).split("\n");this.lines=i.slice(1,-1)}catch{if(s.trim()){const t=(this.lines[e]||"").length<80?e:void 0,i=s.split("\n"),n=void 0!==t?this._formatChunk(i.slice(0,t).join("\n")).length:0;this.lines=this._bestEffortFormat(s,t),void 0!==t&&n>=0&&(this.cursorLine=n)}}if(this.lines.join("\n")===s)this.cursorLine=e,this.cursorColumn=t;else if(0===this.cursorColumn);else{const i=s.split("\n")[e]||"",n=i.length-i.trimStart().length,r=Math.max(0,t-n);if(this.cursorLine<this.lines.length){const e=this.lines[this.cursorLine],t=e.length-e.trimStart().length;this.cursorColumn=t+r}}this.cursorLine=Math.min(this.cursorLine,Math.max(0,this.lines.length-1)),this.cursorColumn=Math.min(this.cursorColumn,this.lines[this.cursorLine]?.length||0),this.updateModel(),this._expandErrorNodes(),this.scheduleRender(),this.updatePlaceholderVisibility(),this._updateErrorDisplay(),this.emitChange()}emitChange(){const e=this.getContent(),s=this.prefix+e+this.suffix;try{let i=JSON.parse(s);this.hiddenFeatures.size>0&&(i.features=i.features.filter(e=>{const t=M(e);return!t||!this.hiddenFeatures.has(t)}));const n=function(e){const s=[];return e.features?(e.features.forEach((e,i)=>{"Feature"!==e.type&&s.push(`features[${i}]: type must be "Feature"`),e.geometry&&e.geometry.type&&(t.includes(e.geometry.type)||s.push(`features[${i}].geometry: invalid type "${e.geometry.type}"`))}),s):s}(i);n.length>0?this.dispatchEvent(new CustomEvent("error",{detail:{error:n.join("; "),errors:n,content:e},bubbles:!0,composed:!0})):this.dispatchEvent(new CustomEvent("change",{detail:i,bubbles:!0,composed:!0}))}catch(i){this.dispatchEvent(new CustomEvent("error",{detail:{error:i instanceof Error?i.message:"Unknown error",content:e},bubbles:!0,composed:!0}))}}updateReadonly(){this._hiddenTextarea&&(this._hiddenTextarea.readOnly=this.readonly),this._clearBtn&&(this._clearBtn.hidden=this.readonly)}updatePlaceholderVisibility(){this._placeholderLayer&&(this._placeholderLayer.style.display=this.lines.length>0?"none":"block")}_updateErrorDisplay(){const e=this._getErrorLines().length;this._errorNav&&this._errorNav.classList.toggle("visible",e>0),this._errorCount&&(this._errorCount.textContent=e>0?String(e):"")}updatePlaceholderContent(){this._placeholderLayer&&(this._placeholderLayer.textContent=this.placeholder),this.updatePlaceholderVisibility()}updatePrefixSuffix(){this._editorPrefix&&(this._editorPrefix.textContent=this.prefix),this._editorSuffix&&(this._editorSuffix.textContent=this.suffix)}updateThemeCSS(){const e=(t=this.getAttribute("dark-selector")||".dark").startsWith(".")&&!t.includes(" ")?`:host(${t})`:`:host-context(${t})`;var t;let s=this._id("theme-styles");s||(s=z("style"),s.id="theme-styles",this.shadowRoot.insertBefore(s,this.shadowRoot.firstChild)),I.lastIndex=0;const i=e=>Object.entries(e).filter(e=>void 0!==e[1]).map(([e,t])=>{return`--${s=e,s.replace(I,"-$1").toLowerCase()}: ${t};`;var s}).join("\n "),n=i(this.themes.light||{});let r=n?`:host {\n ${n}\n }\n`:"";r+=`${e} {\n ${i({bgColor:"#2b2b2b",textColor:"#a9b7c6",caretColor:"#bbb",gutterBg:"#313335",gutterBorder:"#3c3f41",gutterText:"#606366",jsonKey:"#9876aa",jsonString:"#6a8759",jsonNumber:"#6897bb",jsonBoolean:"#cc7832",jsonNull:"#cc7832",jsonPunct:"#a9b7c6",jsonError:"#ff6b68",controlColor:"#cc7832",controlBg:"#3c3f41",controlBorder:"#5a5a5a",geojsonKey:"#9876aa",geojsonType:"#6a8759",geojsonTypeInvalid:"#ff6b68",jsonKeyInvalid:"#ff6b68",...this.themes.dark})}\n }`,s.textContent=r}setTheme(e){e.dark&&(this.themes.dark={...this.themes.dark,...e.dark}),e.light&&(this.themes.light={...this.themes.light,...e.light}),this.updateThemeCSS()}resetTheme(){this.themes={dark:{},light:{}},this.updateThemeCSS()}getTheme(){return{...this.themes}}_findCollapsibleRanges(){const e=[];for(const[t,s]of this._lineToNodeId){const i=this._nodeIdToLines.get(s);if(!i)continue;const n=this.lines[t];if(!n)continue;const r=n.match(E),o=!r&&n.match(j);if(!r&&!o)continue;const l=r?r[2]:o?o[1]:"{";e.push({startLine:i.startLine,endLine:i.endLine,nodeKey:i.nodeKey||(r?r[1]:`__root_${t}`),nodeId:s,openBracket:l,isRootFeature:!!o})}return e.sort((e,t)=>e.startLine-t.startLine),e}_findClosingLine(e,t){let s=1;const i=this.lines[e],n=i.indexOf(t);if(-1!==n){const r=P(i.substring(n+1),t);if(s+=r.open-r.close,0===s)return e}for(let r=e+1;r<this.lines.length;r++){const e=P(this.lines[r],t);if(s+=e.open-e.close,0===s)return r}return-1}_buildContextMap(){const e=this.lines.length;if(this._contextMapCache&&this._contextMapLinesLength===e&&this._contextMapFirstLine===this.lines[0]&&this._contextMapLastLine===this.lines[e-1])return this._contextMapCache;const t=/* @__PURE__ */new Map,r=[];let o=null;for(let l=0;l<e;l++){const e=this.lines[l],a=r[r.length-1]?.context||"Feature";t.set(l,a),s.test(e)?o="geometry":i.test(e)?o="properties":n.test(e)&&(o="Feature"),F.lastIndex=0,A.lastIndex=0,$.lastIndex=0,B.lastIndex=0;const h=(e.match(F)||[]).length,c=(e.match(A)||[]).length,d=(e.match($)||[]).length,u=(e.match(B)||[]).length;for(let t=0;t<h+d;t++)r.push({context:o||a,isArray:t>=h}),o=null;for(let t=0;t<c+u&&r.length>0;t++)r.pop()}return this._contextMapCache=t,this._contextMapLinesLength=e,this._contextMapFirstLine=this.lines[0],this._contextMapLastLine=this.lines[e-1],t}set(e,t={}){const s=H(e);this._setFeaturesInternal(s,t)}add(e,t={}){const s=H(e),i=[...this._parseFeatures(),...s];this._setFeaturesInternal(i,t)}insertAt(e,t,s={}){const i=H(e),n=this._parseFeatures(),r=t<0?n.length+t:t;n.splice(Math.max(0,Math.min(r,n.length)),0,...i),this._setFeaturesInternal(n,s)}_setFeaturesInternal(e,t){const s=e.map(e=>JSON.stringify(e,null,2)).join(",\n");this.setValue(s,!1),this._applyCollapsedFromOptions(t,e)}removeAt(e){const t=this._parseFeatures(),s=e<0?t.length+e:e;if(s>=0&&s<t.length){const e=t.splice(s,1)[0];return this.set(t),e}}removeAll(){this.lines.length>0&&this._saveToHistory("removeAll");const e=this._parseFeatures();return this.lines=[],this.collapsedNodes.clear(),this.hiddenFeatures.clear(),this.updateModel(),this.scheduleRender(),this.updatePlaceholderVisibility(),this.emitChange(),e}get(e){const t=this._parseFeatures();return t[e<0?t.length+e:e]}getAll(){return this._parseFeatures()}emit(){this.emitChange()}save(e="features.geojson"){try{const t={type:"FeatureCollection",features:this._parseFeatures()},s=JSON.stringify(t,null,2),i=new Blob([s],{type:"application/geo+json"}),n=URL.createObjectURL(i),r=z("a");return r.href=n,r.download=e,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n),!0}catch(t){return!1}}open(e={}){return new Promise(t=>{const s=z("input");s.type="file",s.accept=".geojson,.json,application/geo+json,application/json",s.style.display="none",s.addEventListener("change",i=>{const n=i.target.files?.[0];if(!n)return document.body.removeChild(s),void t(!1);const r=new FileReader;r.onload=i=>{try{const n=i.target?.result,r=H(JSON.parse(n));this._saveToHistory("open"),this.set(r,e),this.clearHistory(),document.body.removeChild(s),t(!0)}catch(n){document.body.removeChild(s),t(!1)}},r.onerror=()=>{document.body.removeChild(s),t(!1)},r.readAsText(n)}),s.addEventListener("cancel",()=>{document.body.removeChild(s),t(!1)}),document.body.appendChild(s),s.click()})}_handleAddFeaturePrompt(){const e=prompt("Enter GeoJSON (Feature, Feature[], or FeatureCollection):");if(e&&e.trim())try{const t=H(JSON.parse(e));t.length>0&&this.add(t)}catch{}}_parseFeatures(){try{const e=this.lines.join("\n");return e.trim()?JSON.parse("["+e+"]"):[]}catch(e){return[]}}}customElements.get("geojson-editor")||customElements.define("geojson-editor",q);export{q as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwarity/geojson-editor",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "A feature-rich GeoJSON editor Web Component with syntax highlighting, collapsible nodes, and color picker",
5
5
  "type": "module",
6
6
  "main": "./dist/geojson-editor.js",
@@ -440,7 +440,7 @@ class GeoJsonEditor extends HTMLElement {
440
440
 
441
441
  // ========== Observed Attributes ==========
442
442
  static get observedAttributes() {
443
- return ['readonly', 'value', 'placeholder', 'dark-selector'];
443
+ return ['readonly', 'value', 'placeholder', 'dark-selector', 'internal-add-shortcut'];
444
444
  }
445
445
 
446
446
  // ========== Lifecycle ==========
@@ -494,6 +494,7 @@ class GeoJsonEditor extends HTMLElement {
494
494
  get readonly() { return this.hasAttribute('readonly'); }
495
495
  get value() { return this.getAttribute('value') || ''; }
496
496
  get placeholder() { return this.getAttribute('placeholder') || ''; }
497
+ get internalAddShortcut() { return this.hasAttribute('internal-add-shortcut'); }
497
498
  get prefix() { return '{"type": "FeatureCollection", "features": ['; }
498
499
  get suffix() { return ']}'; }
499
500
 
@@ -586,6 +587,17 @@ class GeoJsonEditor extends HTMLElement {
586
587
  // Prevent default to avoid losing focus after click
587
588
  e.preventDefault();
588
589
 
590
+ // Double-click: select word (e.detail === 2)
591
+ if (e.detail === 2) {
592
+ const pos = this._getPositionFromClick(e);
593
+ this._selectWordAt(pos.line, pos.column);
594
+ this._isSelecting = false;
595
+ hiddenTextarea.focus();
596
+ this._invalidateRenderCache();
597
+ this.scheduleRender();
598
+ return;
599
+ }
600
+
589
601
  // Calculate click position
590
602
  const pos = this._getPositionFromClick(e);
591
603
 
@@ -1531,12 +1543,14 @@ class GeoJsonEditor extends HTMLElement {
1531
1543
  'z': () => e.shiftKey ? this.redo() : this.undo(),
1532
1544
  'y': () => this.redo(),
1533
1545
  's': () => this.save(),
1534
- 'o': () => !this.hasAttribute('readonly') && this.open()
1546
+ 'o': () => !this.hasAttribute('readonly') && this.open(),
1547
+ 'i': () => this.internalAddShortcut && !this.readonly && this._handleAddFeaturePrompt()
1535
1548
  };
1536
1549
 
1537
1550
  // Check for direct key match
1538
1551
  if (keyHandlers[e.key]) {
1539
1552
  e.preventDefault();
1553
+ e.stopPropagation();
1540
1554
  keyHandlers[e.key]();
1541
1555
  return;
1542
1556
  }
@@ -1544,6 +1558,7 @@ class GeoJsonEditor extends HTMLElement {
1544
1558
  // Check for modifier key combinations
1545
1559
  if ((e.ctrlKey || e.metaKey) && modifierHandlers[e.key]) {
1546
1560
  e.preventDefault();
1561
+ e.stopPropagation();
1547
1562
  modifierHandlers[e.key]();
1548
1563
  }
1549
1564
  }
@@ -2510,6 +2525,78 @@ class GeoJsonEditor extends HTMLElement {
2510
2525
  this.selectionEnd = null;
2511
2526
  }
2512
2527
 
2528
+ /**
2529
+ * Select word/token at given position (for double-click)
2530
+ */
2531
+ private _selectWordAt(line: number, column: number): void {
2532
+ if (line < 0 || line >= this.lines.length) return;
2533
+ const lineContent = this.lines[line];
2534
+ if (!lineContent || column > lineContent.length) return;
2535
+
2536
+ // Define word characters (letters, digits, underscore, hyphen for JSON keys)
2537
+ const isWordChar = (ch: string) => /[\w\-]/.test(ch);
2538
+
2539
+ // Check if we're inside a string (find surrounding quotes)
2540
+ let inString = false;
2541
+ let stringStart = -1;
2542
+ let stringEnd = -1;
2543
+ let escaped = false;
2544
+
2545
+ for (let i = 0; i < lineContent.length; i++) {
2546
+ const ch = lineContent[i];
2547
+ if (escaped) {
2548
+ escaped = false;
2549
+ continue;
2550
+ }
2551
+ if (ch === '\\') {
2552
+ escaped = true;
2553
+ continue;
2554
+ }
2555
+ if (ch === '"') {
2556
+ if (!inString) {
2557
+ inString = true;
2558
+ stringStart = i;
2559
+ } else {
2560
+ stringEnd = i;
2561
+ // Check if column is within this string (including quotes)
2562
+ if (column >= stringStart && column <= stringEnd) {
2563
+ // Select the string content (without quotes)
2564
+ this.selectionStart = { line, column: stringStart + 1 };
2565
+ this.selectionEnd = { line, column: stringEnd };
2566
+ this.cursorLine = line;
2567
+ this.cursorColumn = stringEnd;
2568
+ return;
2569
+ }
2570
+ inString = false;
2571
+ stringStart = -1;
2572
+ stringEnd = -1;
2573
+ }
2574
+ }
2575
+ }
2576
+
2577
+ // Not in a string - select word characters
2578
+ let start = column;
2579
+ let end = column;
2580
+
2581
+ // Find start of word
2582
+ while (start > 0 && isWordChar(lineContent[start - 1])) {
2583
+ start--;
2584
+ }
2585
+
2586
+ // Find end of word
2587
+ while (end < lineContent.length && isWordChar(lineContent[end])) {
2588
+ end++;
2589
+ }
2590
+
2591
+ // If we found a word, select it
2592
+ if (start < end) {
2593
+ this.selectionStart = { line, column: start };
2594
+ this.selectionEnd = { line, column: end };
2595
+ this.cursorLine = line;
2596
+ this.cursorColumn = end;
2597
+ }
2598
+ }
2599
+
2513
2600
  /**
2514
2601
  * Delete selected text
2515
2602
  */
@@ -3184,7 +3271,19 @@ class GeoJsonEditor extends HTMLElement {
3184
3271
  // If cursor line is short, likely typing. Long lines = paste
3185
3272
  const isSmallEdit = cursorLineContent.length < 80;
3186
3273
  const skipLine = isSmallEdit ? oldCursorLine : undefined;
3274
+
3275
+ // Format and track where the skipped line ends up
3276
+ const sourceLines = oldContent.split('\n');
3277
+ const beforeLinesCount = skipLine !== undefined
3278
+ ? this._formatChunk(sourceLines.slice(0, skipLine).join('\n')).length
3279
+ : 0;
3280
+
3187
3281
  this.lines = this._bestEffortFormat(oldContent, skipLine);
3282
+
3283
+ // Update cursor line to point to the skipped line's new position
3284
+ if (skipLine !== undefined && beforeLinesCount >= 0) {
3285
+ this.cursorLine = beforeLinesCount;
3286
+ }
3188
3287
  }
3189
3288
  }
3190
3289
 
@@ -3688,6 +3787,24 @@ class GeoJsonEditor extends HTMLElement {
3688
3787
  });
3689
3788
  }
3690
3789
 
3790
+ /**
3791
+ * Handle Ctrl+I shortcut - prompt user to add Feature/Features/FeatureCollection
3792
+ */
3793
+ private _handleAddFeaturePrompt(): void {
3794
+ const input = prompt('Enter GeoJSON (Feature, Feature[], or FeatureCollection):');
3795
+ if (!input || !input.trim()) return;
3796
+
3797
+ try {
3798
+ const parsed = JSON.parse(input);
3799
+ const features = normalizeToFeatures(parsed);
3800
+ if (features.length > 0) {
3801
+ this.add(features);
3802
+ }
3803
+ } catch {
3804
+ // Invalid JSON - ignore silently
3805
+ }
3806
+ }
3807
+
3691
3808
  private _parseFeatures() {
3692
3809
  try {
3693
3810
  const content = this.lines.join('\n');