/**
 * OverviewTab Component
 * Displays basic information about the selected node.
 * Uses detail-panel layout with Mark not needed / Include again (ExcludedStore).
 */
Vue.component('overview-tab', {
    props: {
        selectedNode: {
            type: Object,
            default: null
        },
        platformType: {
            type: String,
        },
        connection: {
            type: Object,
            default: null
        }
    },
    data() {
        return {
            fields: [],
            renderings: [],
            finalRenderings: [],
            loading: false,
            loadingRenderings: false,
            error: null,
            renderingsError: null,
            isMediaItem: false,
            overviewSubTab: 'fields',
            expandedFieldKeys: {},
            customFolderModalOpen: false,
            customUploadModalOpen: false,
            customFolderName: '',
            customPhysicalOpBusy: false,
            customDeleteFileBusy: false,
            customUploadSelectedLabel: '',
            customArrayAsItemsMarked: false,
            customArrayAsItemsBusy: false,
            arrayConfigModalOpen: false,
            arrayConfigIdField: '',
            arrayConfigItemTemplateName: '',
            arrayConfigDisplayRows: [''],
            arrayConfigCandidates: [],
            arrayConfigHasObjects: false,
            arrayConfigSaving: false
        };
    },
    watch: {
        selectedNode: {
            immediate: true,
            handler(newVal) {
                if (newVal) {
                    this.customFolderName = '';
                    this.customFolderModalOpen = false;
                    this.customUploadModalOpen = false;
                    this.customUploadSelectedLabel = '';
                    this.customArrayAsItemsMarked = false;
                    this.arrayConfigModalOpen = false;
                    this.arrayConfigIdField = '';
                    this.arrayConfigItemTemplateName = '';
                    this.arrayConfigDisplayRows = [''];
                    this.arrayConfigCandidates = [];
                    this.arrayConfigHasObjects = false;
                    this.loadFields();
                    this.loadCustomArrayAsItemsMode();
                    this.expandedFieldKeys = {};
                } else {
                    this.fields = [];
                    this.expandedFieldKeys = {};
                    this.customArrayAsItemsMarked = false;
                    this.arrayConfigModalOpen = false;
                    this.arrayConfigItemTemplateName = '';
                }
            }
        }
    },
    mounted() {
        var self = this;
        this._onCustomTreeRefresh = function() {
            if (!self.isCustomPlatform || !self.selectedNode) return;
            self.loadFields();
            var n = self.selectedNode;
            var t = (n.templateName || n.TemplateName || n.template || n.Template || n.contentType || n.ContentType || '').toLowerCase();
            if (t === 'jsonarray') self.loadCustomArrayAsItemsMode();
        };
        if (typeof this.$root !== 'undefined' && this.$root && this.$root.$on) {
            this.$root.$on('chameleon-custom-tree-refresh', this._onCustomTreeRefresh);
        }
    },
    beforeDestroy() {
        if (this._onCustomTreeRefresh && typeof this.$root !== 'undefined' && this.$root && this.$root.$off) {
            this.$root.$off('chameleon-custom-tree-refresh', this._onCustomTreeRefresh);
        }
    },
    methods: {
        async loadFields() {
            if (!this.selectedNode || !this.connection) {
                return;
            }

            this.loading = true;
            this.error = null;

            try {
                // Use fields from node when available (Sitecore only - SharePoint always needs GetNodeFields for resolved field names)
                if (this.isSitecore && this.selectedNode.fields && this.selectedNode.fields.length > 0) {
                    this.fields = this.normalizeDetailFields(this.selectedNode.fields);
                    this.loadRenderings();
                } else if (this.isSitecore) {
                    // Fetch for Sitecore
                    const platformToUse = this.platformType || this.connection.platformType;
                    const nodeId = this.selectedNode.id || this.selectedNode.Id;
                    const connId = this.connection.id || this.connection.Id;
                    const res = await fetch(`/api/ContentTree/GetNodeFields?id=${encodeURIComponent(nodeId)}&platformType=${platformToUse}&connectionId=${connId}`);
                    const data = await res.json();
                    
                    if (data.success === false || data.Success === false) {
                        this.error = data.message || data.Message;
                    } else {
                        this.fields = this.normalizeDetailFields(data.fields || data.Fields || []);
                        this.isMediaItem = data.isMediaItem || data.IsMediaItem;
                        this.loadRenderings();
                    }
                } else {
                    // SharePoint, Custom, Sitefinity source, and other platforms - fetch via GetNodeFields
                    const platformToUse = this.platformType || this.connection.platformType;
                    const nodeId = this.selectedNode.id || this.selectedNode.Id;
                    const connId = this.connection.id || this.connection.Id;
                    const fieldsUrl = typeof Helpers !== 'undefined' && Helpers.buildGetNodeFieldsUrl
                        ? Helpers.buildGetNodeFieldsUrl(nodeId, platformToUse, connId, this.selectedNode)
                        : `/api/ContentTree/GetNodeFields?id=${encodeURIComponent(nodeId)}&platformType=${encodeURIComponent(platformToUse)}&connectionId=${encodeURIComponent(connId)}`;
                    const res = await fetch(fieldsUrl);
                    const data = await res.json();

                    if (!res.ok || data.success === false || data.Success === false) {
                        this.error = data.message || data.Message || ('Failed to load fields (' + res.status + ')');
                        this.fields = [];
                    } else {
                        this.fields = this.normalizeDetailFields(data.fields || data.Fields || []);
                        this.isMediaItem = data.isMediaItem || data.IsMediaItem;
                    }
                }
            } catch (error) {
                console.error('Error loading fields:', error);
                this.error = 'Failed to load fields. Please try again.';
            } finally {
                this.loading = false;
            }
        },
        normalizeDetailFields(fields) {
            var rows = [];
            var list = fields || [];
            for (var i = 0; i < list.length; i++) {
                var f = list[i] || {};
                var name = (f.name || f.Name || f.title || f.Title || '').toString().trim();
                if (!name) continue;
                rows.push({
                    name: name,
                    value: f.value != null ? f.value : f.Value,
                    language: (f.language || f.Language || '').toString()
                });
            }
            return rows;
        },
        
        async loadRenderings() {
            if (!this.isSitecore || !this.selectedNode || !this.connection) {
                return;
            }
            
            this.loadingRenderings = true;
            this.renderingsError = null;
            
            try {
                const platformToUse = this.platformType || this.connection.platformType;
                const nodeId = this.selectedNode.id || this.selectedNode.Id;
                const connId = this.connection.id || this.connection.Id;
                const res = await fetch(`/api/ContentTree/GetNodeRenderings?id=${encodeURIComponent(nodeId)}&platformType=${platformToUse}&connectionId=${connId}`);
                const data = await res.json();
                
                if (data.success === false) {
                    this.renderingsError = data.message;
                } else {
                    this.renderings = data.renderings || [];
                    this.finalRenderings = data.finalRenderings || [];
                }
            } catch (error) {
                console.error('Error loading renderings:', error);
                this.renderingsError = 'Failed to load renderings. Please try again.';
            } finally {
                this.loadingRenderings = false;
            }
        },
        formatValue(value) {
            if (value === null || value === undefined) {
                return '—';
            }
            
            // Convert to string
            const strValue = String(value);
            
            // Handle boolean values
            if (strValue === 'True' || strValue === 'true' || strValue === '1') {
                return 'Yes';
            }
            if (strValue === 'False' || strValue === 'false' || strValue === '0') {
                return 'No';
            }
            
            // Handle dates
            if (strValue.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
                return new Date(strValue).toLocaleString();
            }
            
            // Handle empty values
            if (strValue.trim() === '') {
                return '—';
            }
            
            // Handle Sitecore image field
            if (this.isSitecore && strValue.includes('<image mediaid="{')) {
                const mediaIdMatch = strValue.match(/<image mediaid="{([^}]+)}"[^>]*>/);
                if (mediaIdMatch && mediaIdMatch[1]) {
                    const mediaId = mediaIdMatch[1];
                    const platformToUse = this.platformType || this.connection.platformType;
                    const imageUrl = `/api/ContentTree/GetFile?connectionId=${this.connection.id}&itemId=${mediaId}`;
                    
                    return `<div class="field-code">${this.escapeHtml(strValue)}</div>
                            <div class="mt-2"><img src="${imageUrl}" alt="Field Image Preview" style="max-width:80%" /></div>`;
                }
            }

            // Umbraco rich text / HTML fields: unwrap markup and proxy inline images via shared resolver
            const markup = this.isUmbraco ? this.unpackUmbracoMarkup(strValue) : strValue;
            const hasHtml = markup.startsWith('<') && (markup.includes('</') || markup.includes('/>'));
            if (hasHtml && /<img\b/i.test(markup)) {
                const preview = this.rewriteInlineMediaImages(markup);
                return `<div class="field-code">${this.escapeHtml(strValue)}</div>
                        <div class="mt-2 field-html-preview">${preview}</div>`;
            }
            
            // Handle HTML content without inline images
            if (strValue.startsWith('<') && (strValue.includes('</') || strValue.includes('/>'))) {
                return `<div class="field-code">${this.escapeHtml(strValue)}</div>`;
            }
            
            // Handle JSON content
            if ((strValue.startsWith('{') && strValue.endsWith('}')) || 
                (strValue.startsWith('[') && strValue.endsWith(']'))) {
                try {
                    const parsed = JSON.parse(strValue);
                    return `<div class="field-code">${this.escapeHtml(JSON.stringify(parsed, null, 2))}</div>`;
                } catch (e) {
                    // Not valid JSON, continue
                }
            }
            
            // Handle long text
            if (strValue.length > 100 || strValue.includes('\n')) {
                return `<div class="field-long-text">${this.escapeHtml(strValue)}</div>`;
            }
            
            // For all other values, escape HTML entities
            return this.escapeHtml(strValue);
        },
        
        escapeHtml(text) {
            const map = {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#039;'
            };
            
            return text.replace(/[&<>"']/g, m => map[m]);
        },
        unpackUmbracoMarkup(value) {
            const s = String(value || '').trim();
            if (!s.startsWith('{')) return s;
            try {
                const parsed = JSON.parse(s);
                const markup = parsed.markup != null ? parsed.markup : parsed.Markup;
                if (typeof markup === 'string') return markup;
            } catch (e) {
                // not JSON
            }
            return s;
        },
        decodeHtmlEntities(text) {
            if (!text) return '';
            const el = document.createElement('textarea');
            el.innerHTML = text;
            return el.value;
        },
        rewriteInlineMediaImages(html) {
            if (!html || !this.connection) return html;
            const connId = this.connection.id || this.connection.Id;
            const itemId = this.selectedNode ? (this.selectedNode.id || this.selectedNode.Id || '') : '';
            const self = this;
            return html.replace(/<img\b([^>]*)>/gi, function(match, attrs) {
                const srcMatch = attrs.match(/\bsrc\s*=\s*(['"])(.*?)\1/i);
                const udiMatch = attrs.match(/\bdata-udi\s*=\s*(['"])(.*?)\1/i);
                const src = srcMatch ? self.decodeHtmlEntities((srcMatch[2] || '').trim()) : '';
                const dataUdi = udiMatch ? self.decodeHtmlEntities((udiMatch[2] || '').trim()) : '';
                if ((!src || /^data:/i.test(src)) && !dataUdi) return match;

                let proxy = '/api/ContentTree/GetInlineImage?connectionId='
                    + encodeURIComponent(connId);
                if (dataUdi) {
                    proxy += '&dataUdi=' + encodeURIComponent(dataUdi);
                } else {
                    proxy += '&src=' + encodeURIComponent(src);
                }
                if (itemId) {
                    proxy += '&contentItemId=' + encodeURIComponent(itemId);
                }
                const newAttrs = attrs.replace(/\bsrc\s*=\s*(['"])(.*?)\1/i, function(m, quote) {
                    return 'src=' + quote + proxy + quote;
                });
                return '<img' + newAttrs + '>';
            });
        },
        toggleExcluded() {
            if (!this.selectedNode || !this.connection || typeof ImportMarksStore === 'undefined') return;
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            ImportMarksStore.toggleExcluded(connId, this.platformType || 'Sitecore', nodeId);
            ImportMarksStore.emitUpdated(this.$root);
        },
        toggleIncluded() {
            if (!this.selectedNode || !this.connection || typeof ImportMarksStore === 'undefined') return;
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            ImportMarksStore.toggleIncluded(connId, this.platformType || 'Sitecore', nodeId);
            ImportMarksStore.emitUpdated(this.$root);
        },
        clearImportMark() {
            if (!this.selectedNode || !this.connection || typeof ImportMarksStore === 'undefined') return;
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            ImportMarksStore.clearMark(connId, this.platformType || 'Sitecore', nodeId);
            ImportMarksStore.emitUpdated(this.$root);
        },
        templateLinkUrl(templateId) {
            if (!templateId) return '#';
            var base = (typeof window !== 'undefined' && window.location) ? window.location.pathname : '/Sitecore';
            var sep = base.indexOf('?') >= 0 ? '&' : '?';
            return base + sep + 'id=' + encodeURIComponent(templateId);
        },
        toggleFieldExpand(key) {
            this.$set(this.expandedFieldKeys, key, !this.expandedFieldKeys[key]);
        },
        isFieldExpanded(key) {
            return !!this.expandedFieldKeys[key];
        },
        fieldKey(field) {
            return (field.name || field.Name || '') + '_' + (field.language || field.Language || '');
        },
        fieldDisplayName(field) {
            var name = field && (field.name || field.Name || '');
            if (name === '__UDI') return 'UDI';
            return name;
        },
        fieldDisplayValue(field) {
            if (!field) return null;
            return field.value != null ? field.value : field.Value;
        },
        emitCustomTreeRefresh() {
            if (typeof this.$root !== 'undefined' && this.$root && this.$root.$emit) {
                this.$root.$emit('chameleon-custom-tree-refresh');
            }
        },
        async loadCustomArrayAsItemsMode() {
            if (!this.isCustomJsonArrayFolder || !this.connection || !this.selectedNode) {
                this.customArrayAsItemsMarked = false;
                this.arrayConfigCandidates = [];
                this.arrayConfigHasObjects = false;
                this.arrayConfigIdField = '';
                this.arrayConfigItemTemplateName = '';
                this.arrayConfigDisplayRows = [''];
                return;
            }
            this.customArrayAsItemsBusy = true;
            try {
                var connId = this.connection.id || this.connection.Id;
                var nodeId = this.selectedNode.id || this.selectedNode.Id;
                var res = await fetch('/api/ContentTree/GetCustomArrayAsItemsMode?connectionId=' + encodeURIComponent(connId) + '&nodeId=' + encodeURIComponent(nodeId));
                var data = await res.json().catch(function() { return null; });
                if (!data || data.success === false) {
                    this.customArrayAsItemsMarked = false;
                    this.arrayConfigItemTemplateName = '';
                    return;
                }
                this.customArrayAsItemsMarked = !!(data.marked === true || data.Marked === true);
                this.arrayConfigHasObjects = !!(data.hasObjectElements === true || data.HasObjectElements === true);
                this.arrayConfigCandidates = Array.isArray(data.candidateFields) ? data.candidateFields : (Array.isArray(data.CandidateFields) ? data.CandidateFields : []);
                var idf = (data.idField != null ? data.idField : data.IdField) || '';
                this.arrayConfigIdField = idf;
                this.arrayConfigItemTemplateName = (data.itemTemplateName != null ? data.itemTemplateName : data.ItemTemplateName) || '';
                var dnf = Array.isArray(data.displayNameFields) ? data.displayNameFields : (Array.isArray(data.DisplayNameFields) ? data.DisplayNameFields : []);
                this.arrayConfigDisplayRows = dnf.length ? dnf.slice() : [''];
            } catch (e) {
                console.error(e);
                this.customArrayAsItemsMarked = false;
                this.arrayConfigItemTemplateName = '';
            } finally {
                this.customArrayAsItemsBusy = false;
            }
        },
        async openArrayAsItemsConfigModal() {
            if (!this.connection || !this.selectedNode) return;
            if (this.isCustomJsonArrayFolder) {
                try {
                    await this.loadCustomArrayAsItemsMode();
                } catch (e) {
                    console.error(e);
                }
            }
            if (!this.arrayConfigDisplayRows || !this.arrayConfigDisplayRows.length)
                this.arrayConfigDisplayRows = [''];
            this.arrayConfigModalOpen = true;
        },
        closeArrayAsItemsConfigModal() {
            this.arrayConfigModalOpen = false;
            this.arrayConfigSaving = false;
        },
        addArrayDisplayFieldRow() {
            this.arrayConfigDisplayRows.push('');
        },
        removeArrayDisplayFieldRow(idx) {
            if (this.arrayConfigDisplayRows.length <= 1) {
                this.$set(this.arrayConfigDisplayRows, 0, '');
                return;
            }
            this.arrayConfigDisplayRows.splice(idx, 1);
        },
        async setArrayAsItemsMarkedOnServer(mark, idField, displayNameFields, itemTemplateName) {
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            var res = await fetch('/api/ContentTree/SetCustomArrayAsItemsMode', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    connectionId: connId,
                    nodeId: nodeId,
                    markAsItems: mark,
                    idField: idField || '',
                    displayNameFields: displayNameFields || [],
                    itemTemplateName: (itemTemplateName != null && itemTemplateName !== undefined) ? String(itemTemplateName).trim() : ''
                })
            });
            var data = await res.json().catch(function() { return null; });
            if (!res.ok || !data || data.success === false) {
                var msg = (data && (data.message || data.Message)) || 'Could not update manifest';
                throw new Error(msg);
            }
        },
        async onCustomArrayAsItemsCheckbox(ev) {
            if (!this.connection || !this.selectedNode) return;
            if (this.customArrayAsItemsBusy || this.arrayConfigSaving) {
                if (ev.target) ev.target.checked = this.customArrayAsItemsMarked;
                return;
            }
            var want = !!(ev.target && ev.target.checked);
            if (!want) {
                this.customArrayAsItemsBusy = true;
                try {
                    await this.setArrayAsItemsMarkedOnServer(false, '', [], '');
                    this.customArrayAsItemsMarked = false;
                    this.arrayConfigIdField = '';
                    this.arrayConfigItemTemplateName = '';
                    this.arrayConfigDisplayRows = [''];
                    if (typeof Helpers !== 'undefined') Helpers.showNotification('Mark as items turned off', 'success');
                    this.emitCustomTreeRefresh();
                } catch (e) {
                    if (typeof Helpers !== 'undefined') Helpers.showNotification((e && e.message) ? e.message : 'Update failed', 'error');
                    if (ev.target) ev.target.checked = true;
                } finally {
                    this.customArrayAsItemsBusy = false;
                }
                return;
            }
            if (want) {
                if (ev.target) ev.target.checked = false;
                this.openArrayAsItemsConfigModal();
            }
        },
        async submitArrayAsItemsConfig() {
            if (!this.connection || !this.selectedNode || this.arrayConfigSaving) return;
            var displays = (this.arrayConfigDisplayRows || []).map(function(x) { return (x || '').trim(); }).filter(Boolean);
            if (this.arrayConfigHasObjects) {
                if (!(this.arrayConfigIdField || '').trim()) {
                    if (typeof Helpers !== 'undefined') Helpers.showNotification('Select an ID field', 'error');
                    return;
                }
                if (!displays.length) {
                    if (typeof Helpers !== 'undefined') Helpers.showNotification('Add at least one display name field', 'error');
                    return;
                }
            }
            this.arrayConfigSaving = true;
            try {
                await this.setArrayAsItemsMarkedOnServer(true, (this.arrayConfigIdField || '').trim(), displays, (this.arrayConfigItemTemplateName || '').trim());
                this.customArrayAsItemsMarked = true;
                this.closeArrayAsItemsConfigModal();
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Array marked as items', 'success');
                this.emitCustomTreeRefresh();
                await this.loadCustomArrayAsItemsMode();
            } catch (e) {
                if (typeof Helpers !== 'undefined') Helpers.showNotification((e && e.message) ? e.message : 'Could not save', 'error');
            } finally {
                this.arrayConfigSaving = false;
            }
        },
        closeCustomFolderModal() {
            this.customFolderModalOpen = false;
        },
        openCustomCreateFolderModal() {
            this.customFolderName = '';
            this.customFolderModalOpen = true;
        },
        async submitCustomCreateFolder() {
            if (!this.canAddCustomChild || !this.connection || !this.selectedNode || this.customPhysicalOpBusy) return;
            var name = (this.customFolderName || '').trim();
            if (!name) {
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Enter a folder name', 'error');
                return;
            }
            this.customPhysicalOpBusy = true;
            try {
                var connId = this.connection.id || this.connection.Id;
                var parentId = this.selectedNode.id || this.selectedNode.Id;
                var res = await fetch('/api/ContentTree/AddCustomSourceEntry', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        connectionId: connId,
                        parentNodeId: parentId,
                        name: name,
                        entryType: 'folder'
                    })
                });
                var data = await res.json().catch(function() { return null; });
                if (!res.ok || !data || data.success === false) {
                    var msg = (data && (data.message || data.Message)) || 'Could not create folder';
                    if (typeof Helpers !== 'undefined') Helpers.showNotification(msg, 'error');
                    return;
                }
                this.customFolderModalOpen = false;
                this.customFolderName = '';
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Folder created', 'success');
                this.emitCustomTreeRefresh();
            } catch (e) {
                console.error(e);
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Request failed', 'error');
            } finally {
                this.customPhysicalOpBusy = false;
            }
        },
        closeCustomUploadModal() {
            this.customUploadModalOpen = false;
            this.customUploadSelectedLabel = '';
            var inp = this.$refs.customJsonFileInput;
            if (inp) inp.value = '';
        },
        openCustomUploadModal() {
            this.customUploadSelectedLabel = '';
            this.customUploadModalOpen = true;
            this.$nextTick(function() {
                var inp = this.$refs.customJsonFileInput;
                if (inp) inp.value = '';
            }.bind(this));
        },
        onCustomUploadFileChange(e) {
            var files = e && e.target && e.target.files;
            if (!files || !files.length) {
                this.customUploadSelectedLabel = '';
                return;
            }
            this.customUploadSelectedLabel = files[0].name || '';
        },
        submitCustomUpload() {
            var self = this;
            if (!this.canAddCustomChild || !this.connection || !this.selectedNode || this.customPhysicalOpBusy) return;
            var inp = this.$refs.customJsonFileInput;
            if (!inp || !inp.files || !inp.files[0]) {
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Choose a JSON file', 'error');
                return;
            }
            var file = inp.files[0];
            var rawName = (file.name || 'upload.json').split(/[/\\]/).pop() || 'upload.json';
            if (!/\.json$/i.test(rawName)) {
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Only .json files are allowed', 'error');
                return;
            }
            this.customPhysicalOpBusy = true;
            var reader = new FileReader();
            reader.onload = async function(ev) {
                try {
                    var text = (ev.target && ev.target.result) ? String(ev.target.result) : '';
                    if (text.length && text.charCodeAt(0) === 0xfeff) text = text.slice(1);
                    try {
                        JSON.parse(text);
                    } catch (parseErr) {
                        if (typeof Helpers !== 'undefined') Helpers.showNotification('Invalid JSON in file', 'error');
                        self.customPhysicalOpBusy = false;
                        return;
                    }
                    var connId = self.connection.id || self.connection.Id;
                    var parentId = self.selectedNode.id || self.selectedNode.Id;
                    var res = await fetch('/api/ContentTree/UploadCustomJsonFile', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            connectionId: connId,
                            parentNodeId: parentId,
                            fileName: rawName,
                            jsonContent: text
                        })
                    });
                    var data = await res.json().catch(function() { return null; });
                    if (!res.ok || !data || data.success === false) {
                        var msg = (data && (data.message || data.Message)) || 'Could not save file';
                        if (typeof Helpers !== 'undefined') Helpers.showNotification(msg, 'error');
                        return;
                    }
                    self.closeCustomUploadModal();
                    if (typeof Helpers !== 'undefined') Helpers.showNotification('File added', 'success');
                    self.emitCustomTreeRefresh();
                } catch (e) {
                    console.error(e);
                    if (typeof Helpers !== 'undefined') Helpers.showNotification('Request failed', 'error');
                } finally {
                    self.customPhysicalOpBusy = false;
                }
            };
            reader.onerror = function() {
                if (typeof Helpers !== 'undefined') Helpers.showNotification('Could not read file', 'error');
                self.customPhysicalOpBusy = false;
            };
            reader.readAsText(file, 'UTF-8');
        },
        async deleteCustomPhysicalJsonFile() {
            if (!this.isCustomPhysicalJsonFile || !this.connection || !this.selectedNode || this.customDeleteFileBusy || this.customPhysicalOpBusy) return;
            var nodeId = this.selectedNode.id != null ? this.selectedNode.id : this.selectedNode.Id;
            var label = this.selectedNode.name || nodeId;
            if (!confirm('Delete "' + label + '" from disk? This cannot be undone. Array-as-items settings for this file in manifest.json will be removed, and exclusions for this file or nodes inside it will be cleared.')) return;
            this.customDeleteFileBusy = true;
            try {
                var connId = this.connection.id || this.connection.Id;
                var plat = (this.platformType || (this.connection.platformType || 'Custom'));
                var res = await fetch('/api/ContentTree/DeleteCustomSourceFile', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ connectionId: connId, nodeId: nodeId })
                });
                var data = await res.json().catch(function() { return null; });
                if (!res.ok || !data || data.success === false) {
                    var msg = (data && (data.message || data.Message)) || 'Could not delete file';
                    if (typeof Helpers !== 'undefined') Helpers.showNotification(msg, 'error');
                    return;
                }
                if (typeof ImportMarksStore !== 'undefined') ImportMarksStore.removeStaleAfterCustomJsonFileDeleted(connId, plat, nodeId);
                if (typeof this.$root !== 'undefined' && this.$root && this.$root.$emit) {
                    this.$root.$emit('chameleon-custom-source-file-deleted', { nodeId: nodeId });
                }
                if (typeof Helpers !== 'undefined') Helpers.showNotification('File deleted', 'success');
                this.emitCustomTreeRefresh();
                if (typeof this.$root !== 'undefined' && this.$root && this.$root.$emit) {
                    ImportMarksStore.emitUpdated(this.$root);
                }
            } catch (e) {
                console.error(e);
                if (typeof Helpers !== 'undefined') Helpers.showNotification((e && e.message) ? e.message : 'Delete failed', 'error');
            } finally {
                this.customDeleteFileBusy = false;
            }
        },
        isCodeLike(value) {
            if (value == null || value === '') return false;
            const s = String(value);
            return (s.startsWith('<') && s.includes('</')) ||
                (s.startsWith('{') && s.endsWith('}')) ||
                (s.startsWith('[') && s.endsWith(']')) ||
                s.length > 100 ||
                s.includes('\n');
        }
    },
    computed: {
        isSitecore() {
            return this.platformType === 'Sitecore' || (this.connection && this.connection.platformType === 'Sitecore');
        },
        isUmbraco() {
            const p = (this.platformType || (this.connection && this.connection.platformType) || '').toLowerCase();
            return p === 'umbraco';
        },
        isCustomPlatform() {
            return (this.platformType || '').toLowerCase() === 'custom';
        },
        isSitefinitySource() {
            var p = (this.platformType || (this.connection && this.connection.platformType) || '').toLowerCase();
            return p === 'sitefinitysource';
        },
        isCustomJsonArrayFolder() {
            if (!this.isCustomPlatform || !this.selectedNode) return false;
            var n = this.selectedNode;
            // Tree API uses ContentItem: TemplateName -> "template" / "contentType", not "templateName"
            var t = (n.templateName || n.TemplateName || n.template || n.Template || n.contentType || n.ContentType || '').toLowerCase();
            return t === 'jsonarray';
        },
        /** Physical .json file on disk (Custom tree <c>f|…</c>); not virtual JSON nodes under a file. */
        isCustomPhysicalJsonFile() {
            if (!this.isCustomPlatform || !this.selectedNode) return false;
            var id = this.selectedNode.id != null ? this.selectedNode.id : this.selectedNode.Id;
            if (id == null || id === '') return false;
            if (String(id).indexOf('f|') === 0) return true;
            var tmpl = (this.selectedNode.templateName || this.selectedNode.TemplateName || this.selectedNode.template || this.selectedNode.Template || this.selectedNode.contentType || this.selectedNode.ContentType || '').toLowerCase();
            return tmpl === 'jsonfile';
        },
        canAddCustomChild() {
            if (!this.isCustomPlatform || !this.selectedNode) return false;
            var id = this.selectedNode.id != null ? this.selectedNode.id : this.selectedNode.Id;
            if (id == null || id === '') return false;
            id = String(id);
            if (id === 'custom-root') return true;
            return id.indexOf('d|') === 0;
        },
        isExcluded() {
            if (!this.selectedNode || !this.connection || typeof ImportMarksStore === 'undefined') return false;
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            return ImportMarksStore.isExcluded(connId, this.platformType || 'Sitecore', nodeId);
        },
        isIncluded() {
            if (!this.selectedNode || !this.connection || typeof ImportMarksStore === 'undefined') return false;
            var connId = this.connection.id || this.connection.Id;
            var nodeId = this.selectedNode.id || this.selectedNode.Id;
            return ImportMarksStore.isIncluded(connId, this.platformType || 'Sitecore', nodeId);
        },
        regularFields() {
            return this.fields
                .filter(f => !((f.name || f.Name || '').startsWith('__')))
                .sort((a, b) => (a.name || a.Name || '').localeCompare(b.name || b.Name || ''));
        },
        systemFields() {
            return this.fields
                .filter(f => (f.name || f.Name || '').startsWith('__'))
                .sort((a, b) => (a.name || a.Name || '').localeCompare(b.name || b.Name || ''));
        },
        /** Custom JSON array items: <c>id</c> is the manifest ID field value when configured (same as import memory). */
        displayedDetailItemId() {
            if (!this.selectedNode) return '';
            var id = this.selectedNode.id != null ? this.selectedNode.id : this.selectedNode.Id;
            return id != null ? String(id) : '';
        },
        displayedDetailTechnicalNodeId() {
            if (!this.selectedNode) return '';
            var id = this.displayedDetailItemId;
            var fu = this.selectedNode.fullUrl != null ? this.selectedNode.fullUrl : this.selectedNode.FullUrl;
            fu = fu != null ? String(fu) : '';
            if (fu && fu !== id) return fu;
            return '';
        }
    },
    template: `
        <div class="overview-tab">
            <div v-if="selectedNode">
                <div class="detail-panel">
                    <div class="detail-header">
                        <div>
                            <div style="font-size: 1.1rem; font-weight: 600;">{{ selectedNode.name }}</div>
                            <div style="font-size: 0.78rem; color: var(--text-muted); margin-top: 0.15rem;" class="mono">{{ selectedNode.fullUrl || selectedNode.path || selectedNode.id }}</div>
                            <span v-if="selectedNode.type || selectedNode.contentType || selectedNode.templateName" class="chip chip-accent mt-1" style="display:inline-block;">{{ selectedNode.type || selectedNode.contentType || selectedNode.templateName || 'Unknown' }}</span>
                        </div>
                        <div style="display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; justify-content: flex-end;">
                            <template v-if="isExcluded">
                                <button class="btn-outline-subtle" @click="clearImportMark" style="color: var(--success); border-color: rgba(52,211,153,0.3);">
                                    <i class="bi bi-check-circle me-1"></i>Clear skip
                                </button>
                            </template>
                            <template v-else-if="isIncluded">
                                <button class="btn-outline-subtle" @click="clearImportMark" style="color: var(--text-secondary);">
                                    <i class="bi bi-x-circle me-1"></i>Remove from import
                                </button>
                            </template>
                            <template v-else>
                                <button class="btn-danger-subtle" @click="toggleExcluded">
                                    <i class="bi bi-x-circle me-1"></i>Mark Not Needed
                                </button>
                                <button class="btn-outline-subtle" @click="toggleIncluded" style="color: #34d399; border-color: rgba(52,211,153,0.35);">
                                    <i class="bi bi-check2-circle me-1"></i>Mark for Import
                                </button>
                            </template>
                            <template v-if="isCustomPlatform && canAddCustomChild">
                                <button type="button" class="btn-outline-subtle" :disabled="customPhysicalOpBusy" @click="openCustomCreateFolderModal">
                                    <i class="bi bi-folder-plus me-1"></i>Create folder
                                </button>
                                <button type="button" class="btn-outline-subtle" :disabled="customPhysicalOpBusy" @click="openCustomUploadModal">
                                    <i class="bi bi-file-earmark-arrow-up me-1"></i>Add file
                                </button>
                            </template>
                            <button v-if="isCustomPlatform && isCustomPhysicalJsonFile" type="button" class="btn-danger-subtle" :disabled="customDeleteFileBusy || customPhysicalOpBusy" @click="deleteCustomPhysicalJsonFile">
                                <i class="bi bi-trash me-1"></i>Delete file
                            </button>
                        </div>
                    </div>
                    <div class="detail-body">
                        <div v-if="isCustomJsonArrayFolder" class="mb-3 p-3" style="border:1px solid rgba(148,163,184,0.22); border-radius:8px; background:rgba(15,23,42,0.25);">
                            <div class="detail-field-label">JSON array</div>
                            <div style="display:flex; flex-wrap:wrap; align-items:center; gap:0.75rem; margin-top:0.4rem;">
                                <label style="display:flex; align-items:center; gap:0.5rem; cursor:pointer; font-size:0.9rem;">
                                    <input type="checkbox" :checked="customArrayAsItemsMarked" :disabled="customArrayAsItemsBusy || arrayConfigSaving" @change="onCustomArrayAsItemsCheckbox" />
                                    Mark as items
                                </label>
                                <button v-if="customArrayAsItemsMarked" type="button" class="btn-outline-subtle" style="padding:0.25rem 0.6rem; font-size:0.82rem;" @click.prevent="openArrayAsItemsConfigModal">Configure display…</button>
                            </div>
                            <p style="font-size:0.78rem; color:var(--text-muted); margin:0.4rem 0 0 0; max-width:42rem; line-height:1.45;">Turning this on opens a short setup: choose the property used as the item ID in the UI and one or more properties for the tree label (composed with ·). Settings are stored in <code style="font-size:0.75rem;">manifest.json</code> for this array. Nested JSON arrays on each item still expand as folders.</p>
                        </div>
                        <div v-if="loading" class="text-center p-3">
                            <div class="spinner-border text-primary" role="status">
                                <span class="sr-only">Loading...</span>
                            </div>
                        </div>
                        
                        <div v-else-if="error" class="alert alert-danger">
                            {{ error }}
                        </div>
                        <div v-else>
                            <!-- Item ID with Template and Type -->
                            <div class="detail-meta-row mb-4">
                                <div class="detail-field-label">Item ID</div>
                                <div class="detail-field-value mono" style="font-size:0.78rem;">{{ displayedDetailItemId }}</div>
                                <div v-if="displayedDetailTechnicalNodeId" class="detail-field-value mono" style="font-size:0.72rem; color:var(--text-muted); margin-top:0.25rem;">Tree node: {{ displayedDetailTechnicalNodeId }}</div>
                                <div class="detail-meta-sub mt-2" style="font-size:0.8rem; color: var(--text-muted);">
                                    <span><strong>Template:</strong> </span>
                                    <a v-if="selectedNode.templateId" :href="templateLinkUrl(selectedNode.templateId)" target="_blank" class="detail-link">{{ selectedNode.template || selectedNode.contentType || selectedNode.templateName || '—' }}</a>
                                    <span v-else>{{ selectedNode.template || selectedNode.contentType || selectedNode.templateName || '—' }}</span>
                                    <span class="mx-2">|</span>
                                    <span><strong>Type:</strong> </span>
                                    <a v-if="selectedNode.templateId" :href="templateLinkUrl(selectedNode.templateId)" target="_blank" class="detail-link">{{ selectedNode.type || '—' }}</a>
                                    <span v-else>{{ selectedNode.type || '—' }}</span>
                                </div>
                            </div>
                            <!-- Media preview -->
                            <div v-if="isMediaItem">
                                <img :src="'/api/ContentTree/GetFile?connectionId=' + connection.id + '&itemId=' + selectedNode.id" style="max-width:80%" />
                            </div>
                            <!-- Sub-tabs: Fields | System Fields | Renderings -->
                            <div v-if="fields.length > 0 || isSitecore || isCustomPlatform || isSitefinitySource || isUmbraco" class="mt-4">
                                <div class="overview-sub-tabs mb-3">
                                    <span class="overview-sub-tab" :class="{ active: overviewSubTab === 'fields' }" @click="overviewSubTab = 'fields'">
                                        Fields <span class="tab-count">{{ regularFields.length }}</span>
                                    </span>
                                    <span class="overview-sub-tab" :class="{ active: overviewSubTab === 'system' }" @click="overviewSubTab = 'system'">
                                        System Fields <span class="tab-count">{{ systemFields.length }}</span>
                                    </span>
                                    <span v-if="isSitecore" class="overview-sub-tab" :class="{ active: overviewSubTab === 'renderings' }" @click="overviewSubTab = 'renderings'">
                                        Renderings <span class="tab-count">{{ renderings.length + finalRenderings.length }}</span>
                                    </span>
                                </div>
                                
                                <!-- Fields tab -->
                                <div v-show="overviewSubTab === 'fields'">
                                    <div v-if="regularFields.length > 0" class="table-responsive">
                                        <table class="table table-sm table-hover">
                                            <thead><tr><th style="width: 30%">Name</th><th>Value</th></tr></thead>
                                            <tbody>
                                                <tr v-for="field in regularFields" :key="fieldKey(field)">
                                                    <th>{{ fieldDisplayName(field) }} {{ field.language || field.Language }}</th>
                                                    <td>
                                                        <div class="field-value-cell" :class="{ 'expandable': isCodeLike(fieldDisplayValue(field)) }"
                                                             @click="isCodeLike(fieldDisplayValue(field)) && toggleFieldExpand(fieldKey(field))">
                                                            <div class="field-display" :class="{ 'field-code-expanded': isFieldExpanded(fieldKey(field)) }" v-html="formatValue(fieldDisplayValue(field))"></div>
                                                        </div>
                                                    </td>
                                                </tr>
                                            </tbody>
                                        </table>
                                    </div>
                                    <div v-else class="alert alert-info">No regular fields.</div>
                                </div>
                                
                                <!-- System Fields tab -->
                                <div v-show="overviewSubTab === 'system'">
                                    <div v-if="systemFields.length > 0" class="table-responsive">
                                        <table class="table table-sm table-hover">
                                            <thead><tr><th style="width: 30%">Name</th><th>Value</th></tr></thead>
                                            <tbody>
                                                <tr v-for="field in systemFields" :key="fieldKey(field)" class="text-muted">
                                                    <th>{{ fieldDisplayName(field) }} {{ field.language || field.Language }}</th>
                                                    <td>
                                                        <div class="field-value-cell" :class="{ 'expandable': isCodeLike(fieldDisplayValue(field)) }"
                                                             @click="isCodeLike(fieldDisplayValue(field)) && toggleFieldExpand(fieldKey(field))">
                                                            <div class="field-display" :class="{ 'field-code-expanded': isFieldExpanded(fieldKey(field)) }" v-html="formatValue(fieldDisplayValue(field))"></div>
                                                        </div>
                                                    </td>
                                                </tr>
                                            </tbody>
                                        </table>
                                    </div>
                                    <div v-else class="alert alert-info">No system fields.</div>
                                </div>
                                
                                <!-- Renderings tab (Sitecore only) -->
                                <div v-show="overviewSubTab === 'renderings' && isSitecore">
                                <div v-if="loadingRenderings" class="text-center p-3">
                                    <div class="spinner-border spinner-border-sm text-primary" role="status">
                                        <span class="sr-only">Loading renderings...</span>
                                    </div>
                                    <span class="ml-2">Loading renderings...</span>
                                </div>
                                
                                <div v-else-if="renderingsError" class="alert alert-danger">
                                    {{ renderingsError }}
                                </div>
                                
                                <div v-else>
                                    <!-- Renderings (shared presentation) -->
                                    <div v-if="renderings.length > 0" class="mb-4">
                                        <h6>Renderings <span class="text-muted" style="font-weight:normal;font-size:0.85em">(shared)</span></h6>
                                        <div class="table-responsive">
                                            <table class="table table-sm table-hover">
                                                <thead>
                                                    <tr>
                                                        <th>Name</th>
                                                        <th>Placeholder</th>
                                                        <th>Datasource</th>
                                                        <th>Type</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    <template v-for="rendering in renderings">
                                                        <tr :key="rendering.id">
                                                            <td>{{ rendering.name }}</td>
                                                            <td>{{ rendering.placeholder || '—' }}</td>
                                                            <td>{{ rendering.dataSourceName || '—' }}</td>
                                                            <td>{{ rendering.renderingDetails?.type || '—' }}</td>
                                                        </tr>
                                                        <tr :key="rendering.id + '-details'" class="rendering-details-row">
                                                            <td colspan="4" class="text-muted" style="font-size:0.85em; padding-top:0; padding-left:1rem; border-top:none; word-break:break-all;">
                                                                <span v-if="rendering.renderingDetails && rendering.renderingDetails.type === 'Controller'">
                                                                    {{ rendering.renderingDetails.controller }}/{{ rendering.renderingDetails.action }}
                                                                </span>
                                                                <span v-else-if="rendering.renderingDetails && rendering.renderingDetails.type === 'View'">
                                                                    {{ rendering.renderingDetails.view }}
                                                                </span>
                                                                <span v-else>—</span>
                                                            </td>
                                                        </tr>
                                                    </template>
                                                </tbody>
                                            </table>
                                        </div>
                                    </div>
                                    
                                    <!-- Final Renderings (version-specific delta) -->
                                    <div v-if="finalRenderings.length > 0" class="mb-4">
                                        <h6>Final Renderings <span class="text-muted" style="font-weight:normal;font-size:0.85em">(version-specific)</span></h6>
                                        <div class="table-responsive">
                                            <table class="table table-sm table-hover">
                                                <thead>
                                                    <tr>
                                                        <th>Name</th>
                                                        <th>Placeholder</th>
                                                        <th>Datasource</th>
                                                        <th>Type</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    <template v-for="rendering in finalRenderings">
                                                        <tr :key="rendering.id">
                                                            <td>{{ rendering.name }}</td>
                                                            <td>{{ rendering.placeholder || '—' }}</td>
                                                            <td>{{ rendering.dataSourceName || '—' }}</td>
                                                            <td>{{ rendering.renderingDetails?.type || '—' }}</td>
                                                        </tr>
                                                        <tr :key="rendering.id + '-details'" class="rendering-details-row">
                                                            <td colspan="4" class="text-muted" style="font-size:0.85em; padding-top:0; padding-left:1rem; border-top:none; word-break:break-all;">
                                                                <span v-if="rendering.renderingDetails && rendering.renderingDetails.type === 'Controller'">
                                                                    {{ rendering.renderingDetails.controller }}/{{ rendering.renderingDetails.action }}
                                                                </span>
                                                                <span v-else-if="rendering.renderingDetails && rendering.renderingDetails.type === 'View'">
                                                                    {{ rendering.renderingDetails.view }}
                                                                </span>
                                                                <span v-else>—</span>
                                                            </td>
                                                        </tr>
                                                    </template>
                                                </tbody>
                                            </table>
                                        </div>
                                    </div>
                                    
                                    <div v-if="renderings.length === 0 && finalRenderings.length === 0" class="alert alert-info">
                                        No renderings found for this item.
                                    </div>
                                </div>
                                </div>
                            </div>
                            <div v-else class="alert alert-info mt-3">
                                No fields available for this item.
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div v-else style="text-align: center; padding: 4rem 2rem; color: var(--text-muted);">
                <i class="bi bi-cursor" style="font-size: 2rem; display: block; margin-bottom: 0.75rem;"></i>
                Select a node from the tree to view details
            </div>
            <div v-show="customFolderModalOpen" class="connection-overlay">
                <div class="connect-panel" style="max-width:480px;width:min(480px,calc(100vw - 2rem));padding:1.5rem 2rem;" @click.stop>
                    <div class="connection-dialog-header">
                        <h2>Create folder</h2>
                        <button type="button" class="connection-dialog-close" @click="closeCustomFolderModal" aria-label="Close">
                            <i class="bi bi-x-lg"></i>
                        </button>
                    </div>
                    <p style="margin-bottom:1rem;">New folder is created under the selected location on disk.</p>
                    <div class="detail-field-label">Folder name</div>
                    <input v-model="customFolderName" class="form-control-dark" type="text" placeholder="e.g. data" style="width:100%; margin-bottom:1.25rem;" autocomplete="off" @keyup.enter="submitCustomCreateFolder" />
                    <div style="display:flex; gap:0.5rem; justify-content:flex-end; flex-wrap:wrap;">
                        <button type="button" class="btn-outline-subtle" @click="closeCustomFolderModal">Cancel</button>
                        <button type="button" class="btn-accent" :disabled="customPhysicalOpBusy" @click="submitCustomCreateFolder">
                            <span v-if="customPhysicalOpBusy" class="spinner-ring-sm" style="margin:0 0.35rem 0 0;"></span>
                            Create
                        </button>
                    </div>
                </div>
            </div>
            <div v-show="customUploadModalOpen" class="connection-overlay">
                <div class="connect-panel" style="max-width:520px;width:min(520px,calc(100vw - 2rem));padding:1.5rem 2rem;" @click.stop>
                    <div class="connection-dialog-header">
                        <h2>Add JSON file</h2>
                        <button type="button" class="connection-dialog-close" @click="closeCustomUploadModal" aria-label="Close">
                            <i class="bi bi-x-lg"></i>
                        </button>
                    </div>
                    <p style="margin-bottom:1rem;">Upload a <code style="font-size:0.78rem;">.json</code> file. It is saved under the selected folder using the file name from your computer.</p>
                    <input ref="customJsonFileInput" type="file" accept=".json,application/json" class="form-control-dark" style="width:100%; margin-bottom:0.75rem;" @change="onCustomUploadFileChange" />
                    <p v-if="customUploadSelectedLabel" style="font-size:0.82rem; color:var(--text-muted); margin:0 0 1rem 0;" class="mono">{{ customUploadSelectedLabel }}</p>
                    <div style="display:flex; gap:0.5rem; justify-content:flex-end; flex-wrap:wrap;">
                        <button type="button" class="btn-outline-subtle" @click="closeCustomUploadModal">Cancel</button>
                        <button type="button" class="btn-accent" :disabled="customPhysicalOpBusy" @click="submitCustomUpload">
                            <span v-if="customPhysicalOpBusy" class="spinner-ring-sm" style="margin:0 0.35rem 0 0;"></span>
                            Upload
                        </button>
                    </div>
                </div>
            </div>
            <div v-show="arrayConfigModalOpen" class="connection-overlay">
                <div class="connect-panel" style="max-width:520px;width:min(520px,calc(100vw - 2rem));padding:1.5rem 2rem;" @click.stop>
                    <div class="connection-dialog-header">
                        <h2>Mark array as items</h2>
                        <button type="button" class="connection-dialog-close" @click="closeArrayAsItemsConfigModal" aria-label="Close">
                            <i class="bi bi-x-lg"></i>
                        </button>
                    </div>
                    <p style="margin-bottom:1rem; font-size:0.88rem; color:var(--text-secondary);">Choose how each object in this array appears in the tree and in the node header. Values are saved on this array’s entry in <code style="font-size:0.75rem;">manifest.json</code>.</p>
                    <template v-if="arrayConfigHasObjects">
                        <div class="detail-field-label">ID (property)</div>
                        <p style="font-size:0.76rem; color:var(--text-muted); margin:0.2rem 0 0.4rem 0;">Becomes the item <strong>Id</strong> in the tree, imports, and memory (also exposed as <code style="font-size:0.72rem;">__itemId</code> in fields). Internal JSON position is kept separately as <code style="font-size:0.72rem;">__chameleonTreeNodeId</code>.</p>
                        <select v-model="arrayConfigIdField" class="form-control-dark" style="width:100%; margin-bottom:1rem;">
                            <option value="">— Select property —</option>
                            <option v-for="c in arrayConfigCandidates" :key="'id-'+c" :value="c">{{ c }}</option>
                        </select>
                        <div class="detail-field-label">Display name (one or more properties)</div>
                        <p style="font-size:0.76rem; color:var(--text-muted); margin:0.2rem 0 0.4rem 0;">Tree labels use these values in order, joined with · instead of <code style="font-size:0.72rem;">[0]</code>, <code style="font-size:0.72rem;">[1]</code>, …</p>
                        <div v-for="(row, idx) in arrayConfigDisplayRows" :key="'dn-'+idx" style="display:flex; gap:0.5rem; align-items:center; margin-bottom:0.45rem;">
                            <select v-model="arrayConfigDisplayRows[idx]" class="form-control-dark" style="flex:1;">
                                <option value="">— Select property —</option>
                                <option v-for="c in arrayConfigCandidates" :key="'dn-'+idx+'-'+c" :value="c">{{ c }}</option>
                            </select>
                            <button type="button" class="btn-outline-subtle" v-if="arrayConfigDisplayRows.length > 1" @click="removeArrayDisplayFieldRow(idx)" title="Remove">✕</button>
                        </div>
                        <button type="button" class="btn-outline-subtle mb-3" style="font-size:0.82rem;" @click="addArrayDisplayFieldRow">+ Add display field</button>
                    </template>
                    <template v-else>
                        <p class="alert alert-info" style="font-size:0.85rem;">This array has no JSON objects (only primitives or nested arrays). You can still enable flat items; tree labels stay as <code style="font-size:0.72rem;">[0]</code>, <code style="font-size:0.72rem;">[1]</code>, …</p>
                    </template>
                    <div class="detail-field-label" style="margin-top:0.25rem;">Template name (optional)</div>
                    <p style="font-size:0.76rem; color:var(--text-muted); margin:0.2rem 0 0.4rem 0;">If set, replaces the default <code style="font-size:0.72rem;">JsonItem</code> template for each item in this array (overview and tree).</p>
                    <input type="text" v-model.trim="arrayConfigItemTemplateName" class="form-control-dark" style="width:100%; margin-bottom:0.25rem;" placeholder="JsonItem" maxlength="200" autocomplete="off" />
                    <div style="display:flex; gap:0.5rem; justify-content:flex-end; flex-wrap:wrap;">
                        <button type="button" class="btn-outline-subtle" @click="closeArrayAsItemsConfigModal" :disabled="arrayConfigSaving">Cancel</button>
                        <button type="button" class="btn-accent" :disabled="arrayConfigSaving" @click="submitArrayAsItemsConfig">
                            <span v-if="arrayConfigSaving" class="spinner-ring-sm" style="margin:0 0.35rem 0 0;"></span>
                            Save
                        </button>
                    </div>
                </div>
            </div>
        </div>
    `
}); 
