@softwarity/geojson-editor 1.0.3 → 1.0.4

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.
@@ -2,9 +2,9 @@ var e=Object.defineProperty,t=(t,n,o)=>n in t?e(t,n,{enumerable:!0,configurable:
2
2
  /**
3
3
  * @license MIT
4
4
  * @name @softwarity/geojson-editor
5
- * @version 1.0.3
5
+ * @version 1.0.4
6
6
  * @author Softwarity (https://www.softwarity.io/)
7
7
  * @copyright 2024 Softwarity
8
8
  * @see https://github.com/softwarity/geojson-editor
9
9
  */
10
- const o=class e extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.collapsedData=new Map,this.colorPositions=[],this.nodeTogglePositions=[],this.highlightTimer=null,this._cachedLineHeight=null,this._cachedPaddingTop=null,this.themes={dark:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}}}static get observedAttributes(){return["readonly","value","placeholder","auto-format","dark-selector","feature-collection"]}connectedCallback(){this.render(),this.setupEventListeners(),this.updatePrefixSuffix(),this.updateThemeCSS(),this.value&&this.updateValue(this.value),this.updatePlaceholderContent()}attributeChangedCallback(e,t,n){var o;if(t!==n)if("value"===e)this.updateValue(n);else if("readonly"===e)this.updateReadonly();else if("placeholder"===e)this.updatePlaceholderContent();else if("dark-selector"===e)this.updateThemeCSS();else if("feature-collection"===e)this.updatePrefixSuffix();else if("auto-format"===e){const e=null==(o=this.shadowRoot)?void 0:o.getElementById("textarea");e&&e.value&&this.autoFormat&&(this.autoFormatContent(),this.updateHighlight())}}get readonly(){return this.hasAttribute("readonly")}get value(){return this.getAttribute("value")||""}get placeholder(){return this.getAttribute("placeholder")||""}get autoFormat(){return this.hasAttribute("auto-format")}get featureCollection(){return this.hasAttribute("feature-collection")}get prefix(){return this.featureCollection?e.FEATURE_COLLECTION_PREFIX:""}get suffix(){return this.featureCollection?e.FEATURE_COLLECTION_SUFFIX:""}render(){const e=`\n <div class="editor-prefix" id="editorPrefix"></div>\n <div class="editor-wrapper">\n <div class="gutter">\n <div class="gutter-content" id="gutterContent"></div>\n </div>\n <div class="editor-content">\n <div class="placeholder-layer" id="placeholderLayer">${this.escapeHtml(this.placeholder)}</div>\n <div class="highlight-layer" id="highlightLayer"></div>\n <textarea\n id="textarea"\n spellcheck="false"\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n ></textarea>\n </div>\n </div>\n <div class="editor-suffix" id="editorSuffix"></div>\n `;this.shadowRoot.innerHTML="\n <style>\n /* Global reset with exact values to prevent external CSS interference */\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: border-box;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n font-variant: normal;\n line-height: 1.5;\n letter-spacing: 0;\n text-transform: none;\n text-decoration: none;\n text-indent: 0;\n word-spacing: 0;\n }\n\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n height: 400px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n border-radius: 4px;\n overflow: hidden;\n }\n\n :host([readonly]) .editor-wrapper::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n background: repeating-linear-gradient(\n -45deg,\n rgba(128, 128, 128, 0.08),\n rgba(128, 128, 128, 0.08) 3px,\n transparent 3px,\n transparent 12px\n );\n z-index: 1;\n }\n\n :host([readonly]) textarea {\n cursor: text;\n }\n\n .editor-wrapper {\n position: relative;\n width: 100%;\n flex: 1;\n background: var(--bg-color);\n display: flex;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n }\n\n .gutter {\n width: 24px;\n height: 100%;\n background: var(--gutter-bg);\n border-right: 1px solid var(--gutter-border);\n overflow: hidden;\n flex-shrink: 0;\n position: relative;\n }\n\n .gutter-content {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n padding: 8px 4px;\n }\n\n .gutter-line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5em;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .color-indicator {\n width: 12px;\n height: 12px;\n border-radius: 2px;\n border: 1px solid #555;\n cursor: pointer;\n transition: transform 0.1s;\n flex-shrink: 0;\n }\n\n .color-indicator:hover {\n transform: scale(1.2);\n border-color: #fff;\n }\n\n .collapse-button {\n width: 12px;\n height: 12px;\n background: var(--control-bg);\n border: 1px solid var(--control-border);\n border-radius: 2px;\n color: var(--control-color);\n font-size: 8px;\n font-weight: bold;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.1s;\n flex-shrink: 0;\n user-select: none;\n }\n\n .collapse-button:hover {\n background: var(--control-bg);\n border-color: var(--control-color);\n transform: scale(1.1);\n }\n\n .color-picker-popup {\n position: absolute;\n background: #2d2d30;\n border: 1px solid #555;\n border-radius: 4px;\n padding: 8px;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.5);\n }\n\n .color-picker-popup input[type=\"color\"] {\n width: 150px;\n height: 30px;\n border: none;\n cursor: pointer;\n }\n\n .editor-content {\n position: relative;\n flex: 1;\n overflow: hidden;\n }\n\n .highlight-layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow: auto;\n pointer-events: none;\n z-index: 1;\n color: var(--text-color);\n }\n\n .highlight-layer::-webkit-scrollbar {\n display: none;\n }\n\n textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n margin: 0;\n border: none;\n outline: none;\n background: transparent;\n color: transparent;\n caret-color: var(--caret-color);\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n resize: none;\n overflow: auto;\n z-index: 2;\n box-sizing: border-box;\n }\n\n textarea::selection {\n background: rgba(51, 153, 255, 0.3);\n }\n\n textarea::placeholder {\n color: transparent;\n }\n\n .placeholder-layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n color: #6a6a6a;\n pointer-events: none;\n z-index: 0;\n overflow: hidden;\n }\n\n textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n /* Syntax highlighting colors */\n .json-key {\n color: var(--json-key);\n }\n\n .json-string {\n color: var(--json-string);\n }\n\n .json-number {\n color: var(--json-number);\n }\n\n .json-boolean {\n color: var(--json-boolean);\n }\n\n .json-null {\n color: var(--json-null);\n }\n\n .json-punctuation {\n color: var(--json-punct);\n }\n\n /* GeoJSON-specific highlighting */\n .geojson-key {\n color: var(--geojson-key);\n font-weight: 600;\n }\n\n .geojson-type {\n color: var(--geojson-type);\n font-weight: 600;\n }\n\n .geojson-type-invalid {\n color: var(--geojson-type-invalid);\n font-weight: 600;\n }\n\n .json-key-invalid {\n color: var(--json-key-invalid);\n }\n\n /* Prefix and suffix styling */\n .editor-prefix,\n .editor-suffix {\n padding: 4px 12px;\n color: var(--text-color);\n background: var(--bg-color);\n user-select: none;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex-shrink: 0;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n opacity: 0.6;\n border-left: 3px solid rgba(102, 126, 234, 0.5);\n }\n\n .editor-prefix {\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .editor-suffix {\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* Scrollbar styling - WebKit (Chrome, Safari, Edge) */\n textarea::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n\n textarea::-webkit-scrollbar-track {\n background: var(--control-bg);\n }\n\n textarea::-webkit-scrollbar-thumb {\n background: var(--control-border);\n border-radius: 5px;\n }\n\n textarea::-webkit-scrollbar-thumb:hover {\n background: var(--control-color);\n }\n\n /* Scrollbar styling - Firefox */\n textarea {\n scrollbar-width: thin;\n scrollbar-color: var(--control-border) var(--control-bg);\n }\n </style>\n "+e}setupEventListeners(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");e.addEventListener("scroll",()=>{t.scrollTop=e.scrollTop,t.scrollLeft=e.scrollLeft,this.syncGutterScroll(e.scrollTop)}),e.addEventListener("input",()=>{this.updatePlaceholderVisibility(),clearTimeout(this.highlightTimer),this.highlightTimer=setTimeout(()=>{this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange()},150)}),e.addEventListener("paste",()=>{clearTimeout(this.highlightTimer),setTimeout(()=>{this.updatePlaceholderVisibility(),this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange()},10)}),this.shadowRoot.getElementById("gutterContent").addEventListener("click",e=>{if(e.target.classList.contains("color-indicator")){const t=parseInt(e.target.dataset.line),n=e.target.dataset.color,o=e.target.dataset.attributeName;this.showColorPicker(e.target,t,n,o)}else if(e.target.classList.contains("collapse-button")){const t=e.target.dataset.nodeKey,n=parseInt(e.target.dataset.line);this.toggleCollapse(t,n)}}),this.shadowRoot.querySelector(".gutter").addEventListener("wheel",t=>{t.preventDefault(),e.scrollTop+=t.deltaY}),e.addEventListener("keydown",e=>{this.handleKeydownInCollapsedArea(e)}),e.addEventListener("copy",e=>{this.handleCopyWithCollapsedContent(e)}),e.addEventListener("cut",e=>{this.handleCutWithCollapsedContent(e)}),this.updateReadonly()}syncGutterScroll(e){this.shadowRoot.getElementById("gutterContent").style.transform=`translateY(-${e}px)`}updateReadonly(){const e=this.shadowRoot.getElementById("textarea");e&&(e.disabled=this.readonly)}escapeHtml(e){return e?e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""}updatePlaceholderVisibility(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("placeholderLayer");e&&t&&(t.style.display=e.value?"none":"block")}updatePlaceholderContent(){const e=this.shadowRoot.getElementById("placeholderLayer");e&&(e.textContent=this.placeholder),this.updatePlaceholderVisibility()}updateValue(e){const t=this.shadowRoot.getElementById("textarea");if(t&&t.value!==e){if(t.value=e||"",this.autoFormat&&e)try{const n=this.prefix,o=this.suffix,s=n.trimEnd().endsWith("["),i=o.trimStart().startsWith("]");if(s&&i){const n="["+e+"]",o=JSON.parse(n),s=JSON.stringify(o,null,2).split("\n");s.length>2?t.value=s.slice(1,-1).join("\n"):t.value=""}else if(!n&&!o){const n=JSON.parse(e);t.value=JSON.stringify(n,null,2)}}catch{}this.updateHighlight(),this.updatePlaceholderVisibility(),t.value&&requestAnimationFrame(()=>{this.applyAutoCollapsed()}),this.emitChange()}}updatePrefixSuffix(){const e=this.shadowRoot.getElementById("editorPrefix"),t=this.shadowRoot.getElementById("editorSuffix");e&&(this.prefix?(e.textContent=this.prefix,e.style.display="block"):(e.textContent="",e.style.display="none")),t&&(this.suffix?(t.textContent=this.suffix,t.style.display="block"):(t.textContent="",t.style.display="none"))}updateHighlight(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");if(!e||!t)return;const n=e.value,{highlighted:o,colors:s,toggles:i}=this.highlightJSON(n);t.innerHTML=o,this.colorPositions=s,this.nodeTogglePositions=i,this.updateGutter()}highlightJSON(t){if(!t.trim())return{highlighted:"",colors:[],toggles:[]};const n=t.split("\n"),o=[],s=[];let i=[];const r=this.buildContextMap(t);return n.forEach((t,n)=>{const a=e.REGEX;let l;for(a.colorInLine.lastIndex=0;null!==(l=a.colorInLine.exec(t));)o.push({line:n,color:l[2],attributeName:l[1]});const h=t.match(a.collapsibleNode);if(h){const e=h[2];t.includes("{...}")||t.includes("[...]")?s.push({line:n,nodeKey:e,isCollapsed:!0}):this.bracketClosesOnSameLine(t,h[3])||s.push({line:n,nodeKey:e,isCollapsed:!1})}const d=r.get(n);i.push(this.highlightSyntax(t,d))}),{highlighted:i.join("\n"),colors:o,toggles:s}}buildContextMap(t){var n;const o=t.split("\n"),s=new Map,i=[];let r=null;const a=this.featureCollection?"Feature":null;for(let t=0;t<o.length;t++){const l=o[t],h=i.length>0?null==(n=i[i.length-1])?void 0:n.context:a;s.set(t,h);for(let t=0;t<l.length;t++){const n=l[t];if('"'===n){const n=l.substring(t).match(/^"([^"]+)"\s*:/);if(n){const o=n[1];e.CONTEXT_CHANGING_KEYS[o]&&(r=e.CONTEXT_CHANGING_KEYS[o]),t+=n[0].length-1;continue}}if('"'===n&&i.length>0&&l.substring(0,t).match(/"type"\s*:\s*$/)){const n=l.substring(t).match(/^"([^"]+)"/);if(n&&e.GEOJSON_TYPES_ALL.includes(n[1])){const e=i[i.length-1];e&&(e.context=n[1])}}if("{"===n||"["===n){let e;if(r)e=r,r=null;else if(0===i.length)e=a;else{const t=i[i.length-1];e=t&&t.isArray?t.context:null}i.push({context:e,isArray:"["===n})}("}"===n||"]"===n)&&i.length>0&&i.pop()}}return s}highlightSyntax(t,n){if(!t.trim())return"";const o=n?e.VALID_KEYS_BY_CONTEXT[n]:null,s=e.REGEX;return t.replace(s.ampersand,"&amp;").replace(s.lessThan,"&lt;").replace(s.greaterThan,"&gt;").replace(s.jsonKey,(t,s)=>"properties"===n?`<span class="json-key">"${s}"</span>:`:e.GEOJSON_STRUCTURAL_KEYS.includes(s)?`<span class="geojson-key">"${s}"</span>:`:(t=>!(!e.GEOJSON_STRUCTURAL_KEYS.includes(t)&&n&&null!=o)||o.includes(t))(s)?`<span class="json-key">"${s}"</span>:`:`<span class="json-key-invalid">"${s}"</span>:`).replace(s.typeValue,(t,o)=>(t=>!n||"properties"===n||("geometry"===n||e.GEOJSON_TYPES_GEOMETRY.includes(n)?e.GEOJSON_TYPES_GEOMETRY.includes(t):"Feature"!==n&&"FeatureCollection"!==n||e.GEOJSON_TYPES_ALL.includes(t)))(o)?`<span class="geojson-key">"type"</span>: <span class="geojson-type">"${o}"</span>`:`<span class="geojson-key">"type"</span>: <span class="geojson-type-invalid">"${o}"</span>`).replace(s.stringValue,(e,t)=>e.includes("<span")?e:`: <span class="json-string">"${t}"</span>`).replace(s.numberAfterColon,': <span class="json-number">$1</span>').replace(s.boolean,': <span class="json-boolean">$1</span>').replace(s.nullValue,': <span class="json-null">$1</span>').replace(s.allNumbers,'<span class="json-number">$1</span>').replace(s.punctuation,'<span class="json-punctuation">$1</span>')}toggleCollapse(e,t){const n=this.shadowRoot.getElementById("textarea"),o=n.value.split("\n"),s=o[t];if(s.includes("{...}")||s.includes("[...]")){let n=null,i=null;const r=`${t}-${e}`;if(this.collapsedData.has(r))n=r,i=this.collapsedData.get(r);else for(const[t,o]of this.collapsedData.entries())if(o.nodeKey===e){const e=s.match(/^(\s*)/)[1].length;if(o.indent===e){n=t,i=o;break}}if(!n||!i)return;const{originalLine:a,content:l}=i;o[t]=a,o.splice(t+1,0,...l),this.collapsedData.delete(n)}else{const n=s.match(/^(\s*)"([^"]+)"\s*:\s*([{\[])/);if(!n)return;const i=n[1],r=n[3],a="{"===r?"}":"]";if(this.bracketClosesOnSameLine(s,r))return;let l=1,h=t;const d=[];for(let e=t+1;e<o.length;e++){const t=o[e];for(const e of t)e===r&&l++,e===a&&l--;if(d.push(t),0===l){h=e;break}}const c=`${t}-${e}`;this.collapsedData.set(c,{originalLine:s,content:d,indent:i.length,nodeKey:e});const p=s.substring(0,s.indexOf(r)),u=o[h]&&o[h].trim().endsWith(",");o[t]=`${p}${r}...${a}${u?",":""}`,o.splice(t+1,h-t)}n.value=o.join("\n"),this.updateHighlight()}applyAutoCollapsed(){const e=this.shadowRoot.getElementById("textarea");if(!e||!e.value)return;const t=e.value.split("\n");for(let e=t.length-1;e>=0;e--){const n=t[e],o=n.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(o){const s=o[2];if("coordinates"===s){const i=o[1],r=o[3],a="{"===r?"}":"]";if(this.bracketClosesOnSameLine(n,r))continue;let l=1,h=e;const d=[];for(let n=e+1;n<t.length;n++){const e=t[n];for(const t of e)t===r&&l++,t===a&&l--;if(d.push(e),0===l){h=n;break}}const c=`${e}-${s}`;this.collapsedData.set(c,{originalLine:n,content:d,indent:i.length,nodeKey:s});const p=n.substring(0,n.indexOf(r)),u=t[h]&&t[h].trim().endsWith(",");t[e]=`${p}${r}...${a}${u?",":""}`,t.splice(e+1,h-e)}}}e.value=t.join("\n"),this.updateHighlight()}updateGutter(){const e=this.shadowRoot.getElementById("gutterContent"),t=this.shadowRoot.getElementById("textarea");if(!t)return;if(null===this._cachedLineHeight){const e=getComputedStyle(t);this._cachedLineHeight=parseFloat(e.lineHeight),this._cachedPaddingTop=parseFloat(e.paddingTop)}const n=this._cachedLineHeight,o=this._cachedPaddingTop;e.textContent="";const s=new Map;this.colorPositions.forEach(({line:e,color:t,attributeName:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).colors.push({color:t,attributeName:n})}),this.nodeTogglePositions.forEach(({line:e,nodeKey:t,isCollapsed:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).buttons.push({nodeKey:t,isCollapsed:n})});const i=document.createDocumentFragment();s.forEach((e,t)=>{const s=document.createElement("div");s.className="gutter-line",s.style.top=`${o+t*n}px`,e.colors.forEach(({color:e,attributeName:n})=>{const o=document.createElement("div");o.className="color-indicator",o.style.backgroundColor=e,o.dataset.line=t,o.dataset.color=e,o.dataset.attributeName=n,o.title=`${n}: ${e}`,s.appendChild(o)}),e.buttons.forEach(({nodeKey:e,isCollapsed:n})=>{const o=document.createElement("div");o.className="collapse-button",o.textContent=n?"+":"-",o.dataset.line=t,o.dataset.nodeKey=e,o.title=n?"Expand":"Collapse",s.appendChild(o)}),i.appendChild(s)}),e.appendChild(i)}showColorPicker(e,t,n,o){const s=document.querySelector(".geojson-color-picker-input");s&&s.remove();const i=document.createElement("input");i.type="color",i.value=n,i.className="geojson-color-picker-input";const r=e.getBoundingClientRect();i.style.position="fixed",i.style.left=`${r.left}px`,i.style.top=`${r.top}px`,i.style.width="12px",i.style.height="12px",i.style.opacity="0.01",i.style.border="none",i.style.padding="0",i.style.zIndex="9999",i.addEventListener("input",e=>{this.updateColorValue(t,e.target.value,o)}),i.addEventListener("change",e=>{this.updateColorValue(t,e.target.value,o)});const a=e=>{e.target!==i&&!i.contains(e.target)&&(i.remove(),document.removeEventListener("click",a,!0))};document.body.appendChild(i),setTimeout(()=>{document.addEventListener("click",a,!0)},100),i.focus(),i.click()}updateColorValue(e,t,n){const o=this.shadowRoot.getElementById("textarea"),s=o.value.split("\n"),i=new RegExp(`"${n}"\\s*:\\s*"#[0-9a-fA-F]{6}"`);s[e]=s[e].replace(i,`"${n}": "${t}"`),o.value=s.join("\n"),this.updateHighlight(),this.emitChange()}handleKeydownInCollapsedArea(e){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","PageUp","PageDown","Tab"].includes(e.key)||e.ctrlKey||e.metaKey)return;const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.value.substring(0,n).split("\n").length-1,s=t.value.split("\n")[o];s&&(s.includes("{...}")||s.includes("[...]"))&&e.preventDefault()}handleCopyWithCollapsedContent(e){const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n===o)return;const s=t.value.substring(n,o);if(!s.includes("{...}")&&!s.includes("[...]"))return;const i=this.expandCollapsedMarkersInText(s,n);e.preventDefault(),e.clipboardData.setData("text/plain",i)}expandCollapsedMarkersInText(e,t){const n=this.shadowRoot.getElementById("textarea").value.substring(0,t).split("\n").length-1,o=e.split("\n"),s=[];return o.forEach((e,t)=>{const o=n+t;if(e.includes("{...}")||e.includes("[...]")){let t=!1;this.collapsedData.forEach((e,n)=>{parseInt(n.split("-")[0])===o&&(s.push(e.originalLine),s.push(...e.content),t=!0)}),t||s.push(e)}else s.push(e)}),s.join("\n")}handleCutWithCollapsedContent(e){this.handleCopyWithCollapsedContent(e);const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n!==o){const e=t.value;t.value=e.substring(0,n)+e.substring(o),t.selectionStart=t.selectionEnd=n,this.updateHighlight(),this.updatePlaceholderVisibility(),this.emitChange()}}emitChange(){const e=this.shadowRoot.getElementById("textarea"),t=this.expandAllCollapsed(e.value),n=this.prefix+t+this.suffix;try{const e=JSON.parse(n),o=this.validateGeoJSON(e);o.length>0?this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:`GeoJSON validation: ${o.join("; ")}`,errors:o,content:t},bubbles:!0,composed:!0})):this.dispatchEvent(new CustomEvent("change",{detail:e,bubbles:!0,composed:!0}))}catch(e){this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:e.message,content:t},bubbles:!0,composed:!0}))}}validateGeoJSON(t,n="",o="root"){const s=[];if(!t||"object"!=typeof t)return s;if("properties"!==o&&void 0!==t.type){const i=t.type;"string"==typeof i&&("geometry"===o?e.GEOJSON_TYPES_GEOMETRY.includes(i)||s.push(`Invalid geometry type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_GEOMETRY.join(", ")})`):e.GEOJSON_TYPES_FEATURE.includes(i)||s.push(`Invalid type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_FEATURE.join(", ")})`))}if(Array.isArray(t))t.forEach((e,t)=>{s.push(...this.validateGeoJSON(e,`${n}[${t}]`,o))});else for(const[e,i]of Object.entries(t))if("object"==typeof i&&null!==i){const t=n?`${n}.${e}`:e;let r=o;"properties"===e?r="properties":"geometry"===e||"geometries"===e?r="geometry":"features"===e&&(r="root"),s.push(...this.validateGeoJSON(i,t,r))}return s}bracketClosesOnSameLine(e,t){const n="{"===t?"}":"]",o=e.indexOf(t);if(-1===o)return!1;const s=e.substring(o+1);let i=1;for(const e of s)if(e===t&&i++,e===n&&i--,0===i)return!0;return!1}expandAllCollapsed(t){const n=e.REGEX;for(;t.includes("{...}")||t.includes("[...]");){const e=t.split("\n");let o=!1;for(let t=0;t<e.length;t++){const s=e[t];if(!s.includes("{...}")&&!s.includes("[...]"))continue;const i=s.match(n.collapsedMarker);if(!i)continue;const r=i[2],a=i[1].length,l=`${t}-${r}`;let h=this.collapsedData.has(l)?l:null;if(!h)for(const[e,t]of this.collapsedData.entries())if(t.nodeKey===r&&t.indent===a){h=e;break}if(h){const{originalLine:n,content:s}=this.collapsedData.get(h);e[t]=n,e.splice(t+1,0,...s),o=!0;break}}if(!o)break;t=e.join("\n")}return t}formatJSONContent(e){const t=this.prefix,n=this.suffix,o=t.trimEnd().endsWith("["),s=n.trimStart().startsWith("]");if(o&&s){const t="["+e+"]",n=JSON.parse(t),o=JSON.stringify(n,null,2).split("\n");return o.length>2?o.slice(1,-1).join("\n"):""}if(t||n){const o=t+e+n;return JSON.parse(o),e}{const t=JSON.parse(e);return JSON.stringify(t,null,2)}}autoFormatContentWithCursor(){const e=this.shadowRoot.getElementById("textarea"),t=e.selectionStart,n=e.value.substring(0,t).split("\n"),o=n.length-1,s=n[n.length-1].length,i=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),r=this.expandAllCollapsed(e.value);try{const t=this.formatJSONContent(r);if(t!==r){this.collapsedData.clear(),e.value=t,i.length>0&&this.reapplyCollapsed(i);const n=e.value.split("\n");if(o<n.length){const t=Math.min(s,n[o].length);let i=0;for(let e=0;e<o;e++)i+=n[e].length+1;i+=t,e.setSelectionRange(i,i)}}}catch{}}autoFormatContent(){const e=this.shadowRoot.getElementById("textarea"),t=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),n=this.expandAllCollapsed(e.value);try{const o=this.formatJSONContent(n);o!==n&&(this.collapsedData.clear(),e.value=o,t.length>0&&this.reapplyCollapsed(t))}catch{}}reapplyCollapsed(e){const t=this.shadowRoot.getElementById("textarea"),n=t.value.split("\n"),o=new Map;e.forEach(({nodeKey:e,indent:t})=>{const n=`${e}-${t}`;o.set(n,(o.get(n)||0)+1)});const s=new Map;for(let e=n.length-1;e>=0;e--){const t=n[e],i=t.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(i){const r=i[2],a=`${r}-${i[1].length}`;if(o.has(a)&&(s.set(a,(s.get(a)||0)+1),s.get(a)<=o.get(a))){const o=i[1],s=i[3],a="{"===s?"}":"]";if(this.bracketClosesOnSameLine(t,s))continue;let l=1,h=e;const d=[];for(let t=e+1;t<n.length;t++){const e=n[t];for(const t of e)t===s&&l++,t===a&&l--;if(d.push(e),0===l){h=t;break}}const c=`${e}-${r}`;this.collapsedData.set(c,{originalLine:t,content:d,indent:o.length,nodeKey:r});const p=t.substring(0,t.indexOf(s)),u=n[h]&&n[h].trim().endsWith(",");n[e]=`${p}${s}...${a}${u?",":""}`,n.splice(e+1,h-e)}}}t.value=n.join("\n")}parseSelectorToHostRule(e){return e&&""!==e?e.startsWith(".")&&!e.includes(" ")?`:host(${e})`:`:host-context(${e})`:':host([data-color-scheme="dark"])'}updateThemeCSS(){const e=this.getAttribute("dark-selector")||".dark",t=this.parseSelectorToHostRule(e);let n=this.shadowRoot.getElementById("theme-styles");n||(n=document.createElement("style"),n.id="theme-styles",this.shadowRoot.insertBefore(n,this.shadowRoot.firstChild));const o=`\n :host {\n --bg-color: ${this.themes.light.background};\n --text-color: ${this.themes.light.textColor};\n --caret-color: ${this.themes.light.caretColor};\n --gutter-bg: ${this.themes.light.gutterBackground};\n --gutter-border: ${this.themes.light.gutterBorder};\n --json-key: ${this.themes.light.jsonKey};\n --json-string: ${this.themes.light.jsonString};\n --json-number: ${this.themes.light.jsonNumber};\n --json-boolean: ${this.themes.light.jsonBoolean};\n --json-null: ${this.themes.light.jsonNull};\n --json-punct: ${this.themes.light.jsonPunctuation};\n --control-color: ${this.themes.light.controlColor};\n --control-bg: ${this.themes.light.controlBg};\n --control-border: ${this.themes.light.controlBorder};\n --geojson-key: ${this.themes.light.geojsonKey};\n --geojson-type: ${this.themes.light.geojsonType};\n --geojson-type-invalid: ${this.themes.light.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.light.jsonKeyInvalid};\n }\n\n ${t} {\n --bg-color: ${this.themes.dark.background};\n --text-color: ${this.themes.dark.textColor};\n --caret-color: ${this.themes.dark.caretColor};\n --gutter-bg: ${this.themes.dark.gutterBackground};\n --gutter-border: ${this.themes.dark.gutterBorder};\n --json-key: ${this.themes.dark.jsonKey};\n --json-string: ${this.themes.dark.jsonString};\n --json-number: ${this.themes.dark.jsonNumber};\n --json-boolean: ${this.themes.dark.jsonBoolean};\n --json-null: ${this.themes.dark.jsonNull};\n --json-punct: ${this.themes.dark.jsonPunctuation};\n --control-color: ${this.themes.dark.controlColor};\n --control-bg: ${this.themes.dark.controlBg};\n --control-border: ${this.themes.dark.controlBorder};\n --geojson-key: ${this.themes.dark.geojsonKey};\n --geojson-type: ${this.themes.dark.geojsonType};\n --geojson-type-invalid: ${this.themes.dark.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.dark.jsonKeyInvalid};\n }\n `;n.textContent=o}getTheme(){return{dark:{...this.themes.dark},light:{...this.themes.light}}}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:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}},this.updateThemeCSS()}};n(o,"DEFAULT_THEMES",{dark:{background:"#1e1e1e",textColor:"#d4d4d4",caretColor:"#fff",gutterBackground:"#252526",gutterBorder:"#3e3e42",jsonKey:"#9cdcfe",jsonString:"#ce9178",jsonNumber:"#b5cea8",jsonBoolean:"#569cd6",jsonNull:"#569cd6",jsonPunctuation:"#d4d4d4",controlColor:"#c586c0",controlBg:"#3e3e42",controlBorder:"#555",geojsonKey:"#c586c0",geojsonType:"#4ec9b0",geojsonTypeInvalid:"#f44747",jsonKeyInvalid:"#f44747"},light:{background:"#ffffff",textColor:"#333333",caretColor:"#000",gutterBackground:"#f5f5f5",gutterBorder:"#ddd",jsonKey:"#0000ff",jsonString:"#a31515",jsonNumber:"#098658",jsonBoolean:"#0000ff",jsonNull:"#0000ff",jsonPunctuation:"#333333",controlColor:"#a31515",controlBg:"#e0e0e0",controlBorder:"#999",geojsonKey:"#af00db",geojsonType:"#267f99",geojsonTypeInvalid:"#d32f2f",jsonKeyInvalid:"#d32f2f"}}),n(o,"FEATURE_COLLECTION_PREFIX",'{"type": "FeatureCollection", "features": ['),n(o,"FEATURE_COLLECTION_SUFFIX","]}"),n(o,"REGEX",{ampersand:/&/g,lessThan:/</g,greaterThan:/>/g,jsonKey:/"([^"]+)"\s*:/g,typeValue:/<span class="geojson-key">"type"<\/span>:\s*"([^"]*)"/g,stringValue:/:\s*"([^"]*)"/g,numberAfterColon:/:\s*(-?\d+\.?\d*)/g,boolean:/:\s*(true|false)/g,nullValue:/:\s*(null)/g,allNumbers:/\b(-?\d+\.?\d*)\b/g,punctuation:/([{}[\],])/g,colorInLine:/"(\w+)"\s*:\s*"(#[0-9a-fA-F]{6})"/g,collapsibleNode:/^(\s*)"(\w+)"\s*:\s*([{\[])/,collapsedMarker:/^(\s*)"(\w+)"\s*:\s*([{\[])\.\.\.([\]\}])/}),n(o,"GEOJSON_TYPES_FEATURE",["Feature","FeatureCollection"]),n(o,"GEOJSON_TYPES_GEOMETRY",["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","GeometryCollection"]),n(o,"GEOJSON_TYPES_ALL",[...o.GEOJSON_TYPES_FEATURE,...o.GEOJSON_TYPES_GEOMETRY]),n(o,"VALID_KEYS_BY_CONTEXT",{Feature:["type","geometry","properties","id","bbox"],FeatureCollection:["type","features","bbox","properties"],Point:["type","coordinates","bbox"],MultiPoint:["type","coordinates","bbox"],LineString:["type","coordinates","bbox"],MultiLineString:["type","coordinates","bbox"],Polygon:["type","coordinates","bbox"],MultiPolygon:["type","coordinates","bbox"],GeometryCollection:["type","geometries","bbox"],properties:null,geometry:["type","coordinates","geometries","bbox"]}),n(o,"CONTEXT_CHANGING_KEYS",{geometry:"geometry",properties:"properties",features:"Feature",geometries:"geometry"}),n(o,"GEOJSON_STRUCTURAL_KEYS",["type","geometry","properties","features","geometries","coordinates","bbox","id","crs"]);let s=o;customElements.define("geojson-editor",s);
10
+ const o=class e extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.collapsedData=new Map,this.colorPositions=[],this.nodeTogglePositions=[],this.highlightTimer=null,this._cachedLineHeight=null,this._cachedPaddingTop=null,this.themes={dark:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}}}static get observedAttributes(){return["readonly","value","placeholder","auto-format","dark-selector","feature-collection"]}connectedCallback(){this.render(),this.setupEventListeners(),this.updatePrefixSuffix(),this.updateThemeCSS(),this.value&&this.updateValue(this.value),this.updatePlaceholderContent()}attributeChangedCallback(e,t,n){var o;if(t!==n)if("value"===e)this.updateValue(n);else if("readonly"===e)this.updateReadonly();else if("placeholder"===e)this.updatePlaceholderContent();else if("dark-selector"===e)this.updateThemeCSS();else if("feature-collection"===e)this.updatePrefixSuffix();else if("auto-format"===e){const e=null==(o=this.shadowRoot)?void 0:o.getElementById("textarea");e&&e.value&&this.autoFormat&&(this.autoFormatContent(),this.updateHighlight())}}get readonly(){return this.hasAttribute("readonly")}get value(){return this.getAttribute("value")||""}get placeholder(){return this.getAttribute("placeholder")||""}get autoFormat(){return this.hasAttribute("auto-format")}get featureCollection(){return this.hasAttribute("feature-collection")}get prefix(){return this.featureCollection?e.FEATURE_COLLECTION_PREFIX:""}get suffix(){return this.featureCollection?e.FEATURE_COLLECTION_SUFFIX:""}render(){const e=`\n <div class="editor-prefix" id="editorPrefix"></div>\n <div class="editor-wrapper">\n <div class="gutter">\n <div class="gutter-content" id="gutterContent"></div>\n </div>\n <div class="editor-content">\n <div class="placeholder-layer" id="placeholderLayer">${this.escapeHtml(this.placeholder)}</div>\n <div class="highlight-layer" id="highlightLayer"></div>\n <textarea\n id="textarea"\n spellcheck="false"\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n ></textarea>\n </div>\n </div>\n <div class="editor-suffix" id="editorSuffix"></div>\n `;this.shadowRoot.innerHTML="\n <style>\n /* Global reset with exact values to prevent external CSS interference */\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: border-box;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n font-variant: normal;\n line-height: 1.5;\n letter-spacing: 0;\n text-transform: none;\n text-decoration: none;\n text-indent: 0;\n word-spacing: 0;\n }\n\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n height: 400px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n border-radius: 4px;\n overflow: hidden;\n }\n\n :host([readonly]) .editor-wrapper::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n background: repeating-linear-gradient(\n -45deg,\n rgba(128, 128, 128, 0.08),\n rgba(128, 128, 128, 0.08) 3px,\n transparent 3px,\n transparent 12px\n );\n z-index: 1;\n }\n\n :host([readonly]) textarea {\n cursor: text;\n }\n\n .editor-wrapper {\n position: relative;\n width: 100%;\n flex: 1;\n background: var(--bg-color);\n display: flex;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n }\n\n .gutter {\n width: 24px;\n height: 100%;\n background: var(--gutter-bg);\n border-right: 1px solid var(--gutter-border);\n overflow: hidden;\n flex-shrink: 0;\n position: relative;\n }\n\n .gutter-content {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n padding: 8px 4px;\n }\n\n .gutter-line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5em;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .color-indicator {\n width: 12px;\n height: 12px;\n border-radius: 2px;\n border: 1px solid #555;\n cursor: pointer;\n transition: transform 0.1s;\n flex-shrink: 0;\n }\n\n .color-indicator:hover {\n transform: scale(1.2);\n border-color: #fff;\n }\n\n .collapse-button {\n width: 12px;\n height: 12px;\n background: var(--control-bg);\n border: 1px solid var(--control-border);\n border-radius: 2px;\n color: var(--control-color);\n font-size: 8px;\n font-weight: bold;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.1s;\n flex-shrink: 0;\n user-select: none;\n }\n\n .collapse-button:hover {\n background: var(--control-bg);\n border-color: var(--control-color);\n transform: scale(1.1);\n }\n\n .color-picker-popup {\n position: absolute;\n background: #2d2d30;\n border: 1px solid #555;\n border-radius: 4px;\n padding: 8px;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.5);\n }\n\n .color-picker-popup input[type=\"color\"] {\n width: 150px;\n height: 30px;\n border: none;\n cursor: pointer;\n }\n\n .editor-content {\n position: relative;\n flex: 1;\n overflow: hidden;\n }\n\n .highlight-layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow: auto;\n pointer-events: none;\n z-index: 1;\n color: var(--text-color);\n }\n\n .highlight-layer::-webkit-scrollbar {\n display: none;\n }\n\n textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n margin: 0;\n border: none;\n outline: none;\n background: transparent;\n color: transparent;\n caret-color: var(--caret-color);\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n resize: none;\n overflow: auto;\n z-index: 2;\n box-sizing: border-box;\n }\n\n textarea::selection {\n background: rgba(51, 153, 255, 0.3);\n }\n\n textarea::placeholder {\n color: transparent;\n }\n\n .placeholder-layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n color: #6a6a6a;\n pointer-events: none;\n z-index: 0;\n overflow: hidden;\n }\n\n textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n /* Syntax highlighting colors */\n .json-key {\n color: var(--json-key);\n }\n\n .json-string {\n color: var(--json-string);\n }\n\n .json-number {\n color: var(--json-number);\n }\n\n .json-boolean {\n color: var(--json-boolean);\n }\n\n .json-null {\n color: var(--json-null);\n }\n\n .json-punctuation {\n color: var(--json-punct);\n }\n\n /* GeoJSON-specific highlighting */\n .geojson-key {\n color: var(--geojson-key);\n font-weight: 600;\n }\n\n .geojson-type {\n color: var(--geojson-type);\n font-weight: 600;\n }\n\n .geojson-type-invalid {\n color: var(--geojson-type-invalid);\n font-weight: 600;\n }\n\n .json-key-invalid {\n color: var(--json-key-invalid);\n }\n\n /* Prefix and suffix styling */\n .editor-prefix,\n .editor-suffix {\n padding: 4px 12px;\n color: var(--text-color);\n background: var(--bg-color);\n user-select: none;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex-shrink: 0;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n opacity: 0.6;\n border-left: 3px solid rgba(102, 126, 234, 0.5);\n }\n\n .editor-prefix {\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .editor-suffix {\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* Scrollbar styling - WebKit (Chrome, Safari, Edge) */\n textarea::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n\n textarea::-webkit-scrollbar-track {\n background: var(--control-bg);\n }\n\n textarea::-webkit-scrollbar-thumb {\n background: var(--control-border);\n border-radius: 5px;\n }\n\n textarea::-webkit-scrollbar-thumb:hover {\n background: var(--control-color);\n }\n\n /* Scrollbar styling - Firefox */\n textarea {\n scrollbar-width: thin;\n scrollbar-color: var(--control-border) var(--control-bg);\n }\n </style>\n "+e}setupEventListeners(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");e.addEventListener("scroll",()=>{t.scrollTop=e.scrollTop,t.scrollLeft=e.scrollLeft,this.syncGutterScroll(e.scrollTop)}),e.addEventListener("input",()=>{this.updatePlaceholderVisibility(),clearTimeout(this.highlightTimer),this.highlightTimer=setTimeout(()=>{this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange()},150)}),e.addEventListener("paste",()=>{clearTimeout(this.highlightTimer),setTimeout(()=>{this.updatePlaceholderVisibility(),this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange(),this.applyAutoCollapsed()},10)}),this.shadowRoot.getElementById("gutterContent").addEventListener("click",e=>{if(e.target.classList.contains("color-indicator")){const t=parseInt(e.target.dataset.line),n=e.target.dataset.color,o=e.target.dataset.attributeName;this.showColorPicker(e.target,t,n,o)}else if(e.target.classList.contains("collapse-button")){const t=e.target.dataset.nodeKey,n=parseInt(e.target.dataset.line);this.toggleCollapse(t,n)}}),this.shadowRoot.querySelector(".gutter").addEventListener("wheel",t=>{t.preventDefault(),e.scrollTop+=t.deltaY}),e.addEventListener("keydown",e=>{this.handleKeydownInCollapsedArea(e)}),e.addEventListener("copy",e=>{this.handleCopyWithCollapsedContent(e)}),e.addEventListener("cut",e=>{this.handleCutWithCollapsedContent(e)}),this.updateReadonly()}syncGutterScroll(e){this.shadowRoot.getElementById("gutterContent").style.transform=`translateY(-${e}px)`}updateReadonly(){const e=this.shadowRoot.getElementById("textarea");e&&(e.disabled=this.readonly)}escapeHtml(e){return e?e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""}updatePlaceholderVisibility(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("placeholderLayer");e&&t&&(t.style.display=e.value?"none":"block")}updatePlaceholderContent(){const e=this.shadowRoot.getElementById("placeholderLayer");e&&(e.textContent=this.placeholder),this.updatePlaceholderVisibility()}updateValue(e){const t=this.shadowRoot.getElementById("textarea");if(t&&t.value!==e){if(t.value=e||"",this.autoFormat&&e)try{const n=this.prefix,o=this.suffix,s=n.trimEnd().endsWith("["),i=o.trimStart().startsWith("]");if(s&&i){const n="["+e+"]",o=JSON.parse(n),s=JSON.stringify(o,null,2).split("\n");s.length>2?t.value=s.slice(1,-1).join("\n"):t.value=""}else if(!n&&!o){const n=JSON.parse(e);t.value=JSON.stringify(n,null,2)}}catch{}this.updateHighlight(),this.updatePlaceholderVisibility(),t.value&&requestAnimationFrame(()=>{this.applyAutoCollapsed()}),this.emitChange()}}updatePrefixSuffix(){const e=this.shadowRoot.getElementById("editorPrefix"),t=this.shadowRoot.getElementById("editorSuffix");e&&(this.prefix?(e.textContent=this.prefix,e.style.display="block"):(e.textContent="",e.style.display="none")),t&&(this.suffix?(t.textContent=this.suffix,t.style.display="block"):(t.textContent="",t.style.display="none"))}updateHighlight(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");if(!e||!t)return;const n=e.value,{highlighted:o,colors:s,toggles:i}=this.highlightJSON(n);t.innerHTML=o,this.colorPositions=s,this.nodeTogglePositions=i,this.updateGutter()}highlightJSON(t){if(!t.trim())return{highlighted:"",colors:[],toggles:[]};const n=t.split("\n"),o=[],s=[];let i=[];const r=this.buildContextMap(t);return n.forEach((t,n)=>{const l=e.REGEX;let a;for(l.colorInLine.lastIndex=0;null!==(a=l.colorInLine.exec(t));)o.push({line:n,color:a[2],attributeName:a[1]});const h=t.match(l.collapsibleNode);if(h){const e=h[2];t.includes("{...}")||t.includes("[...]")?s.push({line:n,nodeKey:e,isCollapsed:!0}):this.bracketClosesOnSameLine(t,h[3])||s.push({line:n,nodeKey:e,isCollapsed:!1})}const d=r.get(n);i.push(this.highlightSyntax(t,d))}),{highlighted:i.join("\n"),colors:o,toggles:s}}buildContextMap(t){var n;const o=t.split("\n"),s=new Map,i=[];let r=null;const l=this.featureCollection?"Feature":null;for(let t=0;t<o.length;t++){const a=o[t],h=i.length>0?null==(n=i[i.length-1])?void 0:n.context:l;s.set(t,h);for(let t=0;t<a.length;t++){const n=a[t];if('"'===n){const n=a.substring(t).match(/^"([^"]+)"\s*:/);if(n){const o=n[1];e.CONTEXT_CHANGING_KEYS[o]&&(r=e.CONTEXT_CHANGING_KEYS[o]),t+=n[0].length-1;continue}}if('"'===n&&i.length>0&&a.substring(0,t).match(/"type"\s*:\s*$/)){const n=a.substring(t).match(/^"([^"]+)"/);if(n&&e.GEOJSON_TYPES_ALL.includes(n[1])){const e=i[i.length-1];e&&(e.context=n[1])}}if("{"===n||"["===n){let e;if(r)e=r,r=null;else if(0===i.length)e=l;else{const t=i[i.length-1];e=t&&t.isArray?t.context:null}i.push({context:e,isArray:"["===n})}("}"===n||"]"===n)&&i.length>0&&i.pop()}}return s}highlightSyntax(t,n){if(!t.trim())return"";const o=n?e.VALID_KEYS_BY_CONTEXT[n]:null,s=e.REGEX;return t.replace(s.ampersand,"&amp;").replace(s.lessThan,"&lt;").replace(s.greaterThan,"&gt;").replace(s.jsonKey,(t,s)=>"properties"===n?`<span class="json-key">"${s}"</span>:`:e.GEOJSON_STRUCTURAL_KEYS.includes(s)?`<span class="geojson-key">"${s}"</span>:`:(t=>!(!e.GEOJSON_STRUCTURAL_KEYS.includes(t)&&n&&null!=o)||o.includes(t))(s)?`<span class="json-key">"${s}"</span>:`:`<span class="json-key-invalid">"${s}"</span>:`).replace(s.typeValue,(t,o)=>(t=>!n||"properties"===n||("geometry"===n||e.GEOJSON_TYPES_GEOMETRY.includes(n)?e.GEOJSON_TYPES_GEOMETRY.includes(t):"Feature"!==n&&"FeatureCollection"!==n||e.GEOJSON_TYPES_ALL.includes(t)))(o)?`<span class="geojson-key">"type"</span>: <span class="geojson-type">"${o}"</span>`:`<span class="geojson-key">"type"</span>: <span class="geojson-type-invalid">"${o}"</span>`).replace(s.stringValue,(e,t)=>e.includes("<span")?e:`: <span class="json-string">"${t}"</span>`).replace(s.numberAfterColon,': <span class="json-number">$1</span>').replace(s.boolean,': <span class="json-boolean">$1</span>').replace(s.nullValue,': <span class="json-null">$1</span>').replace(s.allNumbers,'<span class="json-number">$1</span>').replace(s.punctuation,'<span class="json-punctuation">$1</span>')}toggleCollapse(e,t){const n=this.shadowRoot.getElementById("textarea"),o=n.value.split("\n"),s=o[t];if(s.includes("{...}")||s.includes("[...]")){let n=null,i=null;const r=`${t}-${e}`;if(this.collapsedData.has(r))n=r,i=this.collapsedData.get(r);else for(const[t,o]of this.collapsedData.entries())if(o.nodeKey===e){const e=s.match(/^(\s*)/)[1].length;if(o.indent===e){n=t,i=o;break}}if(!n||!i)return;const{originalLine:l,content:a}=i;o[t]=l,o.splice(t+1,0,...a),this.collapsedData.delete(n)}else{const n=s.match(/^(\s*)"([^"]+)"\s*:\s*([{\[])/);if(!n)return;const i=n[1],r=n[3],l="{"===r?"}":"]";if(this.bracketClosesOnSameLine(s,r))return;let a=1,h=t;const d=[];for(let e=t+1;e<o.length;e++){const t=o[e];for(const e of t)e===r&&a++,e===l&&a--;if(d.push(t),0===a){h=e;break}}const c=`${t}-${e}`;this.collapsedData.set(c,{originalLine:s,content:d,indent:i.length,nodeKey:e});const p=s.substring(0,s.indexOf(r)),u=o[h]&&o[h].trim().endsWith(",");o[t]=`${p}${r}...${l}${u?",":""}`,o.splice(t+1,h-t)}n.value=o.join("\n"),this.updateHighlight()}applyAutoCollapsed(){const e=this.shadowRoot.getElementById("textarea");if(!e||!e.value)return;const t=e.value.split("\n");for(let e=t.length-1;e>=0;e--){const n=t[e],o=n.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(o){const s=o[2];if("coordinates"===s){const i=o[1],r=o[3],l="{"===r?"}":"]";if(this.bracketClosesOnSameLine(n,r))continue;let a=1,h=e;const d=[];for(let n=e+1;n<t.length;n++){const e=t[n];for(const t of e)t===r&&a++,t===l&&a--;if(d.push(e),0===a){h=n;break}}const c=`${e}-${s}`;this.collapsedData.set(c,{originalLine:n,content:d,indent:i.length,nodeKey:s});const p=n.substring(0,n.indexOf(r)),u=t[h]&&t[h].trim().endsWith(",");t[e]=`${p}${r}...${l}${u?",":""}`,t.splice(e+1,h-e)}}}e.value=t.join("\n"),this.updateHighlight()}updateGutter(){const e=this.shadowRoot.getElementById("gutterContent"),t=this.shadowRoot.getElementById("textarea");if(!t)return;if(null===this._cachedLineHeight){const e=getComputedStyle(t);this._cachedLineHeight=parseFloat(e.lineHeight),this._cachedPaddingTop=parseFloat(e.paddingTop)}const n=this._cachedLineHeight,o=this._cachedPaddingTop;e.textContent="";const s=new Map;this.colorPositions.forEach(({line:e,color:t,attributeName:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).colors.push({color:t,attributeName:n})}),this.nodeTogglePositions.forEach(({line:e,nodeKey:t,isCollapsed:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).buttons.push({nodeKey:t,isCollapsed:n})});const i=document.createDocumentFragment();s.forEach((e,t)=>{const s=document.createElement("div");s.className="gutter-line",s.style.top=`${o+t*n}px`,e.colors.forEach(({color:e,attributeName:n})=>{const o=document.createElement("div");o.className="color-indicator",o.style.backgroundColor=e,o.dataset.line=t,o.dataset.color=e,o.dataset.attributeName=n,o.title=`${n}: ${e}`,s.appendChild(o)}),e.buttons.forEach(({nodeKey:e,isCollapsed:n})=>{const o=document.createElement("div");o.className="collapse-button",o.textContent=n?"+":"-",o.dataset.line=t,o.dataset.nodeKey=e,o.title=n?"Expand":"Collapse",s.appendChild(o)}),i.appendChild(s)}),e.appendChild(i)}showColorPicker(e,t,n,o){const s=document.querySelector(".geojson-color-picker-input");s&&s.remove();const i=document.createElement("input");i.type="color",i.value=n,i.className="geojson-color-picker-input";const r=e.getBoundingClientRect();i.style.position="fixed",i.style.left=`${r.left}px`,i.style.top=`${r.top}px`,i.style.width="12px",i.style.height="12px",i.style.opacity="0.01",i.style.border="none",i.style.padding="0",i.style.zIndex="9999",i.addEventListener("input",e=>{this.updateColorValue(t,e.target.value,o)}),i.addEventListener("change",e=>{this.updateColorValue(t,e.target.value,o)});const l=e=>{e.target!==i&&!i.contains(e.target)&&(i.remove(),document.removeEventListener("click",l,!0))};document.body.appendChild(i),setTimeout(()=>{document.addEventListener("click",l,!0)},100),i.focus(),i.click()}updateColorValue(e,t,n){const o=this.shadowRoot.getElementById("textarea"),s=o.value.split("\n"),i=new RegExp(`"${n}"\\s*:\\s*"#[0-9a-fA-F]{6}"`);s[e]=s[e].replace(i,`"${n}": "${t}"`),o.value=s.join("\n"),this.updateHighlight(),this.emitChange()}handleKeydownInCollapsedArea(e){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","PageUp","PageDown","Tab"].includes(e.key)||e.ctrlKey||e.metaKey)return;const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.value.substring(0,n).split("\n").length-1,s=t.value.split("\n")[o];s&&(s.includes("{...}")||s.includes("[...]"))&&e.preventDefault()}handleCopyWithCollapsedContent(e){const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n===o)return;const s=t.value.substring(n,o);if(!s.includes("{...}")&&!s.includes("[...]"))return;const i=this.expandCollapsedMarkersInText(s,n);e.preventDefault(),e.clipboardData.setData("text/plain",i)}expandCollapsedMarkersInText(e,t){const n=this.shadowRoot.getElementById("textarea").value.substring(0,t).split("\n").length-1,o=e.split("\n"),s=[];return o.forEach((e,t)=>{const o=n+t;if(e.includes("{...}")||e.includes("[...]")){let t=!1;this.collapsedData.forEach((e,n)=>{parseInt(n.split("-")[0])===o&&(s.push(e.originalLine),s.push(...e.content),t=!0)}),t||s.push(e)}else s.push(e)}),s.join("\n")}handleCutWithCollapsedContent(e){this.handleCopyWithCollapsedContent(e);const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n!==o){const e=t.value;t.value=e.substring(0,n)+e.substring(o),t.selectionStart=t.selectionEnd=n,this.updateHighlight(),this.updatePlaceholderVisibility(),this.emitChange()}}emitChange(){const e=this.shadowRoot.getElementById("textarea"),t=this.expandAllCollapsed(e.value),n=this.prefix+t+this.suffix;try{const e=JSON.parse(n),o=this.validateGeoJSON(e);o.length>0?this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:`GeoJSON validation: ${o.join("; ")}`,errors:o,content:t},bubbles:!0,composed:!0})):this.dispatchEvent(new CustomEvent("change",{detail:e,bubbles:!0,composed:!0}))}catch(e){this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:e.message,content:t},bubbles:!0,composed:!0}))}}validateGeoJSON(t,n="",o="root"){const s=[];if(!t||"object"!=typeof t)return s;if("properties"!==o&&void 0!==t.type){const i=t.type;"string"==typeof i&&("geometry"===o?e.GEOJSON_TYPES_GEOMETRY.includes(i)||s.push(`Invalid geometry type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_GEOMETRY.join(", ")})`):e.GEOJSON_TYPES_FEATURE.includes(i)||s.push(`Invalid type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_FEATURE.join(", ")})`))}if(Array.isArray(t))t.forEach((e,t)=>{s.push(...this.validateGeoJSON(e,`${n}[${t}]`,o))});else for(const[e,i]of Object.entries(t))if("object"==typeof i&&null!==i){const t=n?`${n}.${e}`:e;let r=o;"properties"===e?r="properties":"geometry"===e||"geometries"===e?r="geometry":"features"===e&&(r="root"),s.push(...this.validateGeoJSON(i,t,r))}return s}bracketClosesOnSameLine(e,t){const n="{"===t?"}":"]",o=e.indexOf(t);if(-1===o)return!1;const s=e.substring(o+1);let i=1;for(const e of s)if(e===t&&i++,e===n&&i--,0===i)return!0;return!1}expandAllCollapsed(t){const n=e.REGEX;for(;t.includes("{...}")||t.includes("[...]");){const e=t.split("\n");let o=!1;for(let t=0;t<e.length;t++){const s=e[t];if(!s.includes("{...}")&&!s.includes("[...]"))continue;const i=s.match(n.collapsedMarker);if(!i)continue;const r=i[2],l=i[1].length,a=`${t}-${r}`;let h=this.collapsedData.has(a)?a:null;if(!h)for(const[e,t]of this.collapsedData.entries())if(t.nodeKey===r&&t.indent===l){h=e;break}if(h){const{originalLine:n,content:s}=this.collapsedData.get(h);e[t]=n,e.splice(t+1,0,...s),o=!0;break}}if(!o)break;t=e.join("\n")}return t}formatJSONContent(e){const t=this.prefix,n=this.suffix,o=t.trimEnd().endsWith("["),s=n.trimStart().startsWith("]");if(o&&s){const t="["+e+"]",n=JSON.parse(t),o=JSON.stringify(n,null,2).split("\n");return o.length>2?o.slice(1,-1).join("\n"):""}if(t||n){const o=t+e+n;return JSON.parse(o),e}{const t=JSON.parse(e);return JSON.stringify(t,null,2)}}autoFormatContentWithCursor(){const e=this.shadowRoot.getElementById("textarea"),t=e.selectionStart,n=e.value.substring(0,t).split("\n"),o=n.length-1,s=n[n.length-1].length,i=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),r=this.expandAllCollapsed(e.value);try{const t=this.formatJSONContent(r);if(t!==r){this.collapsedData.clear(),e.value=t,i.length>0&&this.reapplyCollapsed(i);const n=e.value.split("\n");if(o<n.length){const t=Math.min(s,n[o].length);let i=0;for(let e=0;e<o;e++)i+=n[e].length+1;i+=t,e.setSelectionRange(i,i)}}}catch{}}autoFormatContent(){const e=this.shadowRoot.getElementById("textarea"),t=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),n=this.expandAllCollapsed(e.value);try{const o=this.formatJSONContent(n);o!==n&&(this.collapsedData.clear(),e.value=o,t.length>0&&this.reapplyCollapsed(t))}catch{}}reapplyCollapsed(e){const t=this.shadowRoot.getElementById("textarea"),n=t.value.split("\n"),o=new Map;e.forEach(({nodeKey:e,indent:t})=>{const n=`${e}-${t}`;o.set(n,(o.get(n)||0)+1)});const s=new Map;for(let e=n.length-1;e>=0;e--){const t=n[e],i=t.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(i){const r=i[2],l=`${r}-${i[1].length}`;if(o.has(l)&&(s.set(l,(s.get(l)||0)+1),s.get(l)<=o.get(l))){const o=i[1],s=i[3],l="{"===s?"}":"]";if(this.bracketClosesOnSameLine(t,s))continue;let a=1,h=e;const d=[];for(let t=e+1;t<n.length;t++){const e=n[t];for(const t of e)t===s&&a++,t===l&&a--;if(d.push(e),0===a){h=t;break}}const c=`${e}-${r}`;this.collapsedData.set(c,{originalLine:t,content:d,indent:o.length,nodeKey:r});const p=t.substring(0,t.indexOf(s)),u=n[h]&&n[h].trim().endsWith(",");n[e]=`${p}${s}...${l}${u?",":""}`,n.splice(e+1,h-e)}}}t.value=n.join("\n")}parseSelectorToHostRule(e){return e&&""!==e?e.startsWith(".")&&!e.includes(" ")?`:host(${e})`:`:host-context(${e})`:':host([data-color-scheme="dark"])'}updateThemeCSS(){const e=this.getAttribute("dark-selector")||".dark",t=this.parseSelectorToHostRule(e);let n=this.shadowRoot.getElementById("theme-styles");n||(n=document.createElement("style"),n.id="theme-styles",this.shadowRoot.insertBefore(n,this.shadowRoot.firstChild));const o=`\n :host {\n --bg-color: ${this.themes.light.background};\n --text-color: ${this.themes.light.textColor};\n --caret-color: ${this.themes.light.caretColor};\n --gutter-bg: ${this.themes.light.gutterBackground};\n --gutter-border: ${this.themes.light.gutterBorder};\n --json-key: ${this.themes.light.jsonKey};\n --json-string: ${this.themes.light.jsonString};\n --json-number: ${this.themes.light.jsonNumber};\n --json-boolean: ${this.themes.light.jsonBoolean};\n --json-null: ${this.themes.light.jsonNull};\n --json-punct: ${this.themes.light.jsonPunctuation};\n --control-color: ${this.themes.light.controlColor};\n --control-bg: ${this.themes.light.controlBg};\n --control-border: ${this.themes.light.controlBorder};\n --geojson-key: ${this.themes.light.geojsonKey};\n --geojson-type: ${this.themes.light.geojsonType};\n --geojson-type-invalid: ${this.themes.light.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.light.jsonKeyInvalid};\n }\n\n ${t} {\n --bg-color: ${this.themes.dark.background};\n --text-color: ${this.themes.dark.textColor};\n --caret-color: ${this.themes.dark.caretColor};\n --gutter-bg: ${this.themes.dark.gutterBackground};\n --gutter-border: ${this.themes.dark.gutterBorder};\n --json-key: ${this.themes.dark.jsonKey};\n --json-string: ${this.themes.dark.jsonString};\n --json-number: ${this.themes.dark.jsonNumber};\n --json-boolean: ${this.themes.dark.jsonBoolean};\n --json-null: ${this.themes.dark.jsonNull};\n --json-punct: ${this.themes.dark.jsonPunctuation};\n --control-color: ${this.themes.dark.controlColor};\n --control-bg: ${this.themes.dark.controlBg};\n --control-border: ${this.themes.dark.controlBorder};\n --geojson-key: ${this.themes.dark.geojsonKey};\n --geojson-type: ${this.themes.dark.geojsonType};\n --geojson-type-invalid: ${this.themes.dark.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.dark.jsonKeyInvalid};\n }\n `;n.textContent=o}getTheme(){return{dark:{...this.themes.dark},light:{...this.themes.light}}}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:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}},this.updateThemeCSS()}};n(o,"DEFAULT_THEMES",{dark:{background:"#1e1e1e",textColor:"#d4d4d4",caretColor:"#fff",gutterBackground:"#252526",gutterBorder:"#3e3e42",jsonKey:"#9cdcfe",jsonString:"#ce9178",jsonNumber:"#b5cea8",jsonBoolean:"#569cd6",jsonNull:"#569cd6",jsonPunctuation:"#d4d4d4",controlColor:"#c586c0",controlBg:"#3e3e42",controlBorder:"#555",geojsonKey:"#c586c0",geojsonType:"#4ec9b0",geojsonTypeInvalid:"#f44747",jsonKeyInvalid:"#f44747"},light:{background:"#ffffff",textColor:"#333333",caretColor:"#000",gutterBackground:"#f5f5f5",gutterBorder:"#ddd",jsonKey:"#0000ff",jsonString:"#a31515",jsonNumber:"#098658",jsonBoolean:"#0000ff",jsonNull:"#0000ff",jsonPunctuation:"#333333",controlColor:"#a31515",controlBg:"#e0e0e0",controlBorder:"#999",geojsonKey:"#af00db",geojsonType:"#267f99",geojsonTypeInvalid:"#d32f2f",jsonKeyInvalid:"#d32f2f"}}),n(o,"FEATURE_COLLECTION_PREFIX",'{"type": "FeatureCollection", "features": ['),n(o,"FEATURE_COLLECTION_SUFFIX","]}"),n(o,"REGEX",{ampersand:/&/g,lessThan:/</g,greaterThan:/>/g,jsonKey:/"([^"]+)"\s*:/g,typeValue:/<span class="geojson-key">"type"<\/span>:\s*"([^"]*)"/g,stringValue:/:\s*"([^"]*)"/g,numberAfterColon:/:\s*(-?\d+\.?\d*)/g,boolean:/:\s*(true|false)/g,nullValue:/:\s*(null)/g,allNumbers:/\b(-?\d+\.?\d*)\b/g,punctuation:/([{}[\],])/g,colorInLine:/"(\w+)"\s*:\s*"(#[0-9a-fA-F]{6})"/g,collapsibleNode:/^(\s*)"(\w+)"\s*:\s*([{\[])/,collapsedMarker:/^(\s*)"(\w+)"\s*:\s*([{\[])\.\.\.([\]\}])/}),n(o,"GEOJSON_TYPES_FEATURE",["Feature","FeatureCollection"]),n(o,"GEOJSON_TYPES_GEOMETRY",["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","GeometryCollection"]),n(o,"GEOJSON_TYPES_ALL",[...o.GEOJSON_TYPES_FEATURE,...o.GEOJSON_TYPES_GEOMETRY]),n(o,"VALID_KEYS_BY_CONTEXT",{Feature:["type","geometry","properties","id","bbox"],FeatureCollection:["type","features","bbox","properties"],Point:["type","coordinates","bbox"],MultiPoint:["type","coordinates","bbox"],LineString:["type","coordinates","bbox"],MultiLineString:["type","coordinates","bbox"],Polygon:["type","coordinates","bbox"],MultiPolygon:["type","coordinates","bbox"],GeometryCollection:["type","geometries","bbox"],properties:null,geometry:["type","coordinates","geometries","bbox"]}),n(o,"CONTEXT_CHANGING_KEYS",{geometry:"geometry",properties:"properties",features:"Feature",geometries:"geometry"}),n(o,"GEOJSON_STRUCTURAL_KEYS",["type","geometry","properties","features","geometries","coordinates","bbox","id","crs"]);let s=o;customElements.define("geojson-editor",s);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwarity/geojson-editor",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
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",
@@ -569,6 +569,8 @@ class GeoJsonEditor extends HTMLElement {
569
569
  }
570
570
  this.updateHighlight();
571
571
  this.emitChange();
572
+ // Auto-collapse coordinates after paste
573
+ this.applyAutoCollapsed();
572
574
  }, 10);
573
575
  });
574
576