/**
 * Personalización del explorador (única fuente para Config + Explorador).
 * Depende de: window.BASE_URL (opcional), fetch a index.php?action=...
 */
(function (global) {
    'use strict';

    var cache = null;
    var listeners = [];
    var inFlight = null;

    function apiPath() {
        try {
            var u = new URL(global.location.href);
            u.search = '';
            return u.pathname;
        } catch (e) {
            return '/index.php';
        }
    }

    function baseUrl() {
        return (typeof global.BASE_URL === 'string' && global.BASE_URL) ? global.BASE_URL : '';
    }

    function buildMediaSrc(relPath) {
        if (!relPath) return '';
        var rel = String(relPath).replace(/\\/g, '/');
        if (rel.indexOf('..') !== -1) return '';
        var low = rel.toLowerCase();
        if (low.indexOf('media/explorer/') !== 0 && low.indexOf('media/') !== 0) return '';
        try {
            return baseUrl() + 'file.php?f=' + encodeURIComponent(global.btoa(unescape(encodeURIComponent(rel))));
        } catch (e) {
            return baseUrl() + 'file.php?f=' + encodeURIComponent(rel);
        }
    }

    function normalizeRel(p) {
        if (p == null || p === '') return '';
        return String(p)
            .replace(/\\/g, '/')
            .replace(/^\/+/, '')
            .split('/')
            .filter(function (x) {
                return x && x !== '.' && x !== '..';
            })
            .join('/');
    }

    function normBucket(b) {
        return b === 'cache' ? 'cache' : 'files';
    }

    function getCache() {
        return cache || { global: {}, folders: { files: {}, cache: {} }, files: { files: {}, cache: {} } };
    }

    function fetchCustomization(force) {
        if (!force && inFlight) return inFlight;
        if (!force && cache) return Promise.resolve(cache);
        inFlight = fetch(apiPath() + '?action=explorerCustomizationGet', {
            method: 'GET',
            credentials: 'same-origin',
            cache: 'no-store'
        })
            .then(function (r) {
                return r.json();
            })
            .then(function (j) {
                inFlight = null;
                if (!j || !j.success) throw new Error((j && j.error) || 'explorerCustomizationGet');
                cache = j.data;
                listeners.forEach(function (fn) {
                    try {
                        fn(cache);
                    } catch (e) {}
                });
                return cache;
            })
            .catch(function (e) {
                inFlight = null;
                throw e;
            });
        return inFlight;
    }

    function savePatch(patch) {
        return fetch(apiPath() + '?action=explorerCustomizationSave', {
            method: 'POST',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ patch: patch })
        })
            .then(function (r) {
                return r.json();
            })
            .then(function (j) {
                if (!j || !j.success) throw new Error((j && j.error) || 'explorerCustomizationSave');
                cache = j.data;
                listeners.forEach(function (fn) {
                    try {
                        fn(cache);
                    } catch (e) {}
                });
                return cache;
            });
    }

    function uploadCustomizationFile(category, file) {
        var fd = new FormData();
        fd.append('category', category);
        fd.append('file', file);
        return fetch(apiPath() + '?action=explorerCustomizationUpload', {
            method: 'POST',
            credentials: 'same-origin',
            body: fd
        })
            .then(function (r) {
                return r.json();
            })
            .then(function (j) {
                if (!j || !j.success) throw new Error((j && j.error) || 'upload');
                return j.relPath || '';
            });
    }

    function onChange(fn) {
        if (typeof fn !== 'function') return function () {};
        listeners.push(fn);
        return function () {
            listeners = listeners.filter(function (x) {
                return x !== fn;
            });
        };
    }

    function resolveExplorerChrome() {
        var g = getCache().global || {};
        var ch = g.explorer_chrome || {};
        return {
            type: ch.type || 'none',
            color: ch.color || '',
            src: ch.src || ''
        };
    }

    function resolveFolderStyle(bucket, relFolderPath) {
        bucket = normBucket(bucket);
        var rel = normalizeRel(relFolderPath);
        var d = getCache();
        var g = d.global || {};
        var fb = (d.folders && d.folders[bucket]) || {};
        var row = fb[rel] || {};
        return {
            icon: row.icon || g.default_folder_icon || '',
            color: row.color || '',
            background: row.background || '',
            preview_type: row.preview_type || g.default_preview_type || 'none',
            preview_src: row.preview_src || '',
            view_mode: row.view_mode || g.folder_view_mode || '',
            card_style: row.card_style || '',
            allow_gif: g.allow_gif_preview !== false,
            allow_video: g.allow_video_preview !== false
        };
    }

    function fileExt(name) {
        var n = String(name || '');
        var i = n.lastIndexOf('.');
        if (i < 0) return '';
        return n.slice(i + 1).toLowerCase().replace(/[^a-z0-9]/g, '');
    }

    function resolveFileStyle(bucket, relParent, fileName, item) {
        bucket = normBucket(bucket);
        var relFile = fileRelKey(bucket, relParent, fileName, item);
        var d = getCache();
        var g = d.global || {};
        var ext = fileExt(fileName);
        var extMap = g.extensions;
        if (!extMap || typeof extMap !== 'object' || Array.isArray(extMap)) {
            extMap = {};
        }
        var extRow = ext && extMap[ext] ? extMap[ext] : {};
        var fb = (d.files && d.files[bucket]) || {};
        var row = fb[relFile] || {};
        return {
            icon: row.icon || extRow.icon || '',
            color: row.color || extRow.color || '',
            card_style: row.card_style || extRow.card_style || ''
        };
    }

    function sanitizeFaClass(icon) {
        var s = String(icon || '').trim();
        if (!s) return '';
        if (s.length > 200) return '';
        if (/<|javascript:/i.test(s)) return '';
        if (/^https?:/i.test(s)) return '';
        if (/^media\//i.test(s)) return '';
        if (!/^fa[srlb]?\s+fa-[\w\-]+$/i.test(s) && !/^(fas|far|fal|fab)\s+fa-[\w\-]+$/i.test(s)) return '';
        return s.replace(/\s+/g, ' ');
    }

    function iconInnerHtml(icon, fallbackClass) {
        var ic = String(icon || '').trim();
        if (!ic) {
            return '<i class="' + (fallbackClass || 'fas fa-folder') + '"></i>';
        }
        if (/^media\//i.test(ic)) {
            var src = buildMediaSrc(ic);
            if (!src) return '<i class="' + (fallbackClass || 'fas fa-folder') + '"></i>';
            return '<img class="fe-cust-icon-img" src="' + escAttr(src) + '" alt="" draggable="false">';
        }
        var fa = sanitizeFaClass(ic);
        if (fa) return '<i class="' + escAttr(fa) + '"></i>';
        return '<i class="' + (fallbackClass || 'fas fa-folder') + '"></i>';
    }

    function escAttr(s) {
        return String(s)
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;');
    }

    function folderThumbHtml(style, wrapCls, isTile) {
        var w = wrapCls || 'explorer-item-thumb';
        var pt = (style.preview_type || 'none').toLowerCase();
        var src = style.preview_src || '';
        if (pt === 'video' && src && style.allow_video !== false) {
            var vsrc = buildMediaSrc(src);
            if (vsrc) {
                return (
                    '<div class="' +
                    escAttr(w) +
                    ' fe-cust-folder-thumb fe-cust-preview-video"><video src="' +
                    escAttr(vsrc) +
                    '" muted loop playsinline autoplay preload="metadata" draggable="false"></video></div>'
                );
            }
        }
        if ((pt === 'image' || pt === 'gif') && src && (pt !== 'gif' || style.allow_gif !== false)) {
            var isrc = buildMediaSrc(src);
            if (isrc) {
                return (
                    '<div class="' +
                    escAttr(w) +
                    ' fe-cust-folder-thumb fe-cust-preview-image"><img src="' +
                    escAttr(isrc) +
                    '" alt="" loading="lazy" draggable="false"></div>'
                );
            }
        }
        var fc = style.icon ? '' : 'fas fa-folder fe-thumb-folder-ico';
        var inner = style.icon ? iconInnerHtml(style.icon, 'fas fa-folder fe-thumb-folder-ico') : '<i class="fas fa-folder fe-thumb-folder-ico"></i>';
        var colorAttr = style.color ? ' style="--fe-cust-accent:' + escAttr(style.color) + '"' : '';
        return '<div class="' + escAttr(w) + ' fe-cust-folder-thumb fe-cust-folder-icononly"' + colorAttr + '>' + inner + '</div>';
    }

    function fileThumbOverlay(style, fallbackInner) {
        var parts = '';
        if (style.icon) {
            parts += '<span class="fe-cust-file-icon-overlay">' + iconInnerHtml(style.icon, '') + '</span>';
        }
        return parts + fallbackInner;
    }

    function applyExplorerChromeToHost(host) {
        if (!host || !host.querySelector) return;
        var feMain = host.querySelector('.fe-main');
        if (!feMain) return;
        var pos = global.getComputedStyle(feMain).position;
        if (pos === 'static') feMain.style.position = 'relative';
        var layer = feMain.querySelector('.fe-explorer-chrome-bg');
        if (!layer) {
            layer = document.createElement('div');
            layer.className = 'fe-explorer-chrome-bg';
            layer.setAttribute('aria-hidden', 'true');
            feMain.insertBefore(layer, feMain.firstChild);
        }
        var chrome = resolveExplorerChrome();
        layer.className = 'fe-explorer-chrome-bg fe-chrome-type-' + String(chrome.type || 'none');
        layer.innerHTML = '';
        layer.style.background = '';
        var t = (chrome.type || 'none').toLowerCase();
        if (t === 'solid' && chrome.color) {
            layer.style.background = chrome.color;
        } else if ((t === 'image' || t === 'gif') && chrome.src) {
            var img = document.createElement('img');
            img.src = buildMediaSrc(chrome.src);
            img.alt = '';
            img.draggable = false;
            layer.appendChild(img);
        } else if (t === 'video' && chrome.src) {
            var v = document.createElement('video');
            v.src = buildMediaSrc(chrome.src);
            v.muted = true;
            v.loop = true;
            v.playsInline = true;
            v.autoplay = true;
            v.setAttribute('playsinline', '');
            v.draggable = false;
            layer.appendChild(v);
        }
    }

    function folderRelKey(bucket, parentRel, folderName, item) {
        bucket = normBucket(bucket);
        if (item && item.__fromGlobal) {
            return normalizeRel(item.__relPath || (normalizeRel(item.__parentPath || '') + '/' + (item.name || '')).replace(/^\//, ''));
        }
        var base = normalizeRel(parentRel || '');
        return normalizeRel(base ? base + '/' + folderName : folderName);
    }

    function fileRelKey(bucket, parentRel, fileName, item) {
        bucket = normBucket(bucket);
        if (item && item.__fromGlobal) {
            return normalizeRel(normalizeRel(item.__parentPath || '') + '/' + (item.name || ''));
        }
        var base = normalizeRel(parentRel || '');
        return normalizeRel(base ? base + '/' + fileName : fileName);
    }

    function patchFolder(bucket, relFolderPath, dataOrNull) {
        bucket = normBucket(bucket);
        var rel = normalizeRel(relFolderPath);
        var patch = { folders: {} };
        patch.folders[bucket] = {};
        patch.folders[bucket][rel] = dataOrNull;
        return savePatch(patch);
    }

    function patchFile(bucket, relFilePath, dataOrNull) {
        bucket = normBucket(bucket);
        var rel = normalizeRel(relFilePath);
        var patch = { files: {} };
        patch.files[bucket] = {};
        patch.files[bucket][rel] = dataOrNull;
        return savePatch(patch);
    }

    function patchGlobal(partial) {
        return savePatch({ global: partial || {} });
    }

    /** Modal rápido (explorador / atajos): requiere nodos #explorerCustModal en index.php */
    function bindExplorerCustomizationModal() {
        var modal = document.getElementById('explorerCustModal');
        if (!modal || modal.dataset.bound === '1') return;
        modal.dataset.bound = '1';
        var closeBtn = document.getElementById('explorerCustModalClose');
        var cancelBtn = document.getElementById('explorerCustModalCancel');
        var saveBtn = document.getElementById('explorerCustModalSave');
        var resetBtn = document.getElementById('explorerCustModalReset');
        if (closeBtn)
            closeBtn.addEventListener('click', function () {
                closeModal();
            });
        if (cancelBtn)
            cancelBtn.addEventListener('click', function () {
                closeModal();
            });
        modal.addEventListener('click', function (e) {
            if (e.target === modal) closeModal();
        });
        var upPrev = document.getElementById('explorerCustUploadPreviewBtn');
        if (upPrev) {
            upPrev.addEventListener('click', function () {
                var inp = document.getElementById('explorerCustPreviewFile');
                var pt = (document.getElementById('explorerCustPreviewType') || {}).value || 'image';
                if (!inp || !inp.files || !inp.files[0]) return;
                var cat = pt === 'video' ? 'videos' : pt === 'gif' ? 'gifs' : 'previews';
                uploadCustomizationFile(cat, inp.files[0])
                    .then(function (rel) {
                        var ps = document.getElementById('explorerCustPreviewSrc');
                        if (ps) ps.value = rel;
                    })
                    .catch(function (err) {
                        void (global.appDialog && global.appDialog.alert(String(err && err.message ? err.message : err)));
                    });
            });
        }
        if (saveBtn)
            saveBtn.addEventListener('click', function () {
                var ctx = modal._feCustCtx;
                if (!ctx) return;
                var data = readForm(modal);
                var p = Promise.resolve();
                if (ctx.mode === 'folder') {
                    p = patchFolder(ctx.bucket, ctx.relKey, data);
                } else if (ctx.mode === 'file') {
                    p = patchFile(ctx.bucket, ctx.relKey, data);
                }
                p.then(function () {
                    closeModal();
                    if (typeof ctx.onSaved === 'function') ctx.onSaved();
                }).catch(function (err) {
                        void (global.appDialog && global.appDialog.alert(String(err && err.message ? err.message : err)));
                });
            });
        if (resetBtn)
            resetBtn.addEventListener('click', function () {
                var ctx = modal._feCustCtx;
                if (!ctx) return;
                var p = Promise.resolve();
                if (ctx.mode === 'folder') p = patchFolder(ctx.bucket, ctx.relKey, null);
                else if (ctx.mode === 'file') p = patchFile(ctx.bucket, ctx.relKey, null);
                p.then(function () {
                    closeModal();
                    if (typeof ctx.onSaved === 'function') ctx.onSaved();
                }).catch(function (err) {
                        void (global.appDialog && global.appDialog.alert(String(err && err.message ? err.message : err)));
                });
            });
    }

    function readForm(modal) {
        var icon = (document.getElementById('explorerCustIcon') || {}).value || '';
        var color = (document.getElementById('explorerCustColor') || {}).value || '';
        var pt = (document.getElementById('explorerCustPreviewType') || {}).value || 'none';
        var ps = (document.getElementById('explorerCustPreviewSrc') || {}).value || '';
        var vm = (document.getElementById('explorerCustViewMode') || {}).value || '';
        var bg = (document.getElementById('explorerCustBg') || {}).value || '';
        var card = (document.getElementById('explorerCustCardStyle') || {}).value || '';
        var out = {};
        out.icon = icon.trim();
        if (color.trim()) out.color = color.trim();
        if (pt) out.preview_type = pt;
        if (ps.trim()) out.preview_src = ps.trim();
        if (vm.trim()) out.view_mode = vm.trim();
        if (bg.trim()) out.background = bg.trim();
        if (card.trim()) out.card_style = card.trim();
        return out;
    }

    function fillForm(modal, style, mode) {
        var icon = document.getElementById('explorerCustIcon');
        var color = document.getElementById('explorerCustColor');
        var pt = document.getElementById('explorerCustPreviewType');
        var ps = document.getElementById('explorerCustPreviewSrc');
        var vm = document.getElementById('explorerCustViewMode');
        var bg = document.getElementById('explorerCustBg');
        var rowPreview = document.getElementById('explorerCustRowPreview');
        var rowBg = document.getElementById('explorerCustRowBg');
        var rowVm = document.getElementById('explorerCustRowViewMode');
        var rowCard = document.getElementById('explorerCustRowCard');
        var card = document.getElementById('explorerCustCardStyle');
        if (icon) icon.value = style.icon || '';
        if (color) color.value = style.color || '#000000';
        if (pt) pt.value = style.preview_type || 'none';
        if (ps) ps.value = style.preview_src || '';
        if (vm) vm.value = style.view_mode || '';
        if (bg) bg.value = style.background || '';
        if (card) card.value = style.card_style || '';
        if (rowPreview) rowPreview.style.display = mode === 'folder' ? '' : 'none';
        if (rowBg) rowBg.style.display = mode === 'folder' ? '' : 'none';
        if (rowVm) rowVm.style.display = mode === 'folder' ? '' : 'none';
        if (rowCard) rowCard.style.display = mode === 'file' ? '' : 'none';
    }

    function openModal(ctx) {
        bindExplorerCustomizationModal();
        var modal = document.getElementById('explorerCustModal');
        if (!modal) return;
        modal._feCustCtx = ctx;
        var title = document.getElementById('explorerCustModalTitle');
        if (title) title.textContent = ctx.title || 'Personalizar';
        var st =
            ctx.mode === 'folder'
                ? resolveFolderStyle(ctx.bucket, ctx.relKey)
                : resolveFileStyle(ctx.bucket, ctx.relParent || '', ctx.fileName || '', ctx.item || null);
        fillForm(modal, st, ctx.mode);
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        document.body.classList.add('modal-open');
    }

    function closeModal() {
        var modal = document.getElementById('explorerCustModal');
        if (!modal) return;
        modal.style.display = 'none';
        modal.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('modal-open');
    }

    function postProcessFileThumbHtml(html, item, thumbCtx) {
        if (!html || !item || item.type === 'dir') return html;
        if (!thumbCtx || !thumbCtx.ecBucket || !global.GridExplorerCustomization) return html;
        var fs = resolveFileStyle(thumbCtx.ecBucket, thumbCtx.ecParentRel || '', item.name, item);
        if (!fs.icon && !fs.color && !fs.card_style) return html;
        var extra = '';
        if (fs.icon) {
            extra += '<span class="fe-cust-file-badge">' + iconInnerHtml(fs.icon, '') + '</span>';
        }
        var cs = fs.card_style ? String(fs.card_style).replace(/[^a-z0-9_-]/gi, '') : '';
        var cls = 'fe-cust-file-wrap' + (cs ? ' fe-card-' + cs : '');
        var st = fs.color ? ' style="--fe-cust-file:' + escAttr(fs.color) + '"' : '';
        return '<div class="' + escAttr(cls) + '"' + st + '>' + extra + html + '</div>';
    }

    function bindToolbarCustModal() {
        var modal = document.getElementById('explorerToolbarCustModal');
        if (!modal || modal.dataset.bound === '1') return;
        modal.dataset.bound = '1';
        var closeBtn = document.getElementById('explorerToolbarCustModalClose');
        var cancelBtn = document.getElementById('explorerToolbarCustModalCancel');
        var saveBtn = document.getElementById('explorerToolbarCustModalSave');
        var paletteBtn = document.getElementById('explorerToolbarCustOpenPaletteBtn');
        var uploadBtn = document.getElementById('explorerToolbarCustUploadBtn');
        function closeM() {
            modal.style.display = 'none';
            modal.setAttribute('aria-hidden', 'true');
            document.body.classList.remove('modal-open');
        }
        if (closeBtn) closeBtn.addEventListener('click', closeM);
        if (cancelBtn) cancelBtn.addEventListener('click', closeM);
        modal.addEventListener('click', function (e) {
            if (e.target === modal) closeM();
        });
        if (paletteBtn) {
            paletteBtn.addEventListener('click', function () {
                if (typeof global.initExplorerThemeModalOnce === 'function') global.initExplorerThemeModalOnce();
                if (typeof global.openExplorerThemeModal === 'function') global.openExplorerThemeModal();
            });
        }
        if (uploadBtn) {
            uploadBtn.addEventListener('click', function () {
                var inp = document.getElementById('explorerToolbarCustFile');
                var ct = (document.getElementById('explorerToolbarCustChromeType') || {}).value || 'image';
                if (!inp || !inp.files || !inp.files[0]) return;
                var cat = ct === 'video' ? 'videos' : ct === 'gif' ? 'gifs' : 'backgrounds';
                uploadCustomizationFile(cat, inp.files[0])
                    .then(function (rel) {
                        var src = document.getElementById('explorerToolbarCustChromeSrc');
                        if (src) src.value = rel;
                    })
                    .catch(function (e) {
                        void (global.appDialog && global.appDialog.alert(String(e && e.message ? e.message : e)));
                    });
            });
        }
        if (saveBtn) {
            saveBtn.addEventListener('click', function () {
                var t = (document.getElementById('explorerToolbarCustChromeType') || {}).value || 'none';
                var c = (document.getElementById('explorerToolbarCustChromeColor') || {}).value || '';
                var s = (document.getElementById('explorerToolbarCustChromeSrc') || {}).value || '';
                savePatch({
                    global: {
                        explorer_chrome: {
                            type: t,
                            color: t === 'solid' ? c : '',
                            src: t !== 'solid' && t !== 'none' ? String(s).trim() : ''
                        }
                    }
                })
                    .then(function () {
                        closeM();
                        document.querySelectorAll('.file-explorer').forEach(function (host) {
                            applyExplorerChromeToHost(host);
                        });
                    })
                    .catch(function (e) {
                        void (global.appDialog && global.appDialog.alert(String(e && e.message ? e.message : e)));
                    });
            });
        }
    }

    function openToolbarCustModal() {
        bindToolbarCustModal();
        var modal = document.getElementById('explorerToolbarCustModal');
        if (!modal) return;
        var ch = resolveExplorerChrome();
        var t = document.getElementById('explorerToolbarCustChromeType');
        var c = document.getElementById('explorerToolbarCustChromeColor');
        var s = document.getElementById('explorerToolbarCustChromeSrc');
        if (t) t.value = ch.type || 'none';
        if (c) c.value = ch.color || '#000000';
        if (s) s.value = ch.src || '';
        modal.style.display = 'block';
        modal.setAttribute('aria-hidden', 'false');
        document.body.classList.add('modal-open');
    }

    function initConfigTab() {
        var tabBtn = document.getElementById('explorerCustomizationTabBtn');
        var panel = document.getElementById('explorerCustomizationTab');
        if (!tabBtn || !panel || tabBtn.dataset.bound === '1') return;
        tabBtn.dataset.bound = '1';
        tabBtn.addEventListener('click', function () {
            fetchCustomization(true).then(function () {
                fillConfigPanelFromCache();
            }).catch(function () {});
        });
        var saveGlobal = document.getElementById('explorerCustConfigSaveBtn');
        if (saveGlobal) {
            saveGlobal.addEventListener('click', function () {
                var chromeType = (document.getElementById('ecfgChromeType') || {}).value || 'none';
                var chromeColor = (document.getElementById('ecfgChromeColor') || {}).value || '';
                var chromeSrc = (document.getElementById('ecfgChromeSrc') || {}).value || '';
                var defIcon = (document.getElementById('ecfgDefaultFolderIcon') || {}).value || '';
                var defPreview = (document.getElementById('ecfgDefaultPreview') || {}).value || 'none';
                var vm = (document.getElementById('ecfgViewMode') || {}).value || '';
                var isz = (document.getElementById('ecfgIconSize') || {}).value || 'medium';
                var agif = !!(document.getElementById('ecfgAllowGif') || {}).checked;
                var avideo = !!(document.getElementById('ecfgAllowVideo') || {}).checked;
                var theme = (document.getElementById('ecfgExplorerTheme') || {}).value || 'default';
                var g = {
                    explorer_theme: theme,
                    folder_view_mode: vm,
                    icon_size: isz,
                    default_folder_icon: defIcon.trim(),
                    default_preview_type: defPreview,
                    allow_gif_preview: agif,
                    allow_video_preview: avideo,
                    explorer_chrome: {
                        type: chromeType,
                        color: chromeType === 'solid' ? chromeColor : '',
                        src: chromeType !== 'solid' && chromeType !== 'none' ? chromeSrc.trim() : ''
                    }
                };
                savePatch({ global: g })
                    .then(async function () {
                        if (global.appDialog && global.appDialog.alert) await global.appDialog.alert('Guardado');
                        document.querySelectorAll('.file-explorer').forEach(function (host) {
                            applyExplorerChromeToHost(host);
                        });
                    })
                    .catch(function (e) {
                        void (global.appDialog && global.appDialog.alert(String(e && e.message ? e.message : e)));
                    });
            });
        }
        var uploadChrome = document.getElementById('ecfgChromeUploadBtn');
        if (uploadChrome) {
            uploadChrome.addEventListener('click', function () {
                var inp = document.getElementById('ecfgChromeFile');
                if (!inp || !inp.files || !inp.files[0]) return;
                var ct = (document.getElementById('ecfgChromeType') || {}).value || 'image';
                var cat = ct === 'video' ? 'videos' : ct === 'gif' ? 'gifs' : 'backgrounds';
                uploadCustomizationFile(cat, inp.files[0])
                    .then(function (rel) {
                        var src = document.getElementById('ecfgChromeSrc');
                        if (src) src.value = rel;
                    })
                    .catch(function (e) {
                        void (global.appDialog && global.appDialog.alert(String(e && e.message ? e.message : e)));
                    });
            });
        }
    }

    function fillConfigPanelFromCache() {
        var d = getCache();
        var g = d.global || {};
        var ch = g.explorer_chrome || {};
        var set = function (id, v) {
            var el = document.getElementById(id);
            if (el) el.value = v != null ? String(v) : '';
        };
        set('ecfgExplorerTheme', g.explorer_theme || 'default');
        set('ecfgViewMode', g.folder_view_mode || 'icons_md');
        set('ecfgIconSize', g.icon_size || 'medium');
        set('ecfgDefaultFolderIcon', g.default_folder_icon || '');
        set('ecfgDefaultPreview', g.default_preview_type || 'none');
        set('ecfgChromeType', ch.type || 'none');
        set('ecfgChromeColor', ch.color || '#000000');
        set('ecfgChromeSrc', ch.src || '');
        var gif = document.getElementById('ecfgAllowGif');
        var vid = document.getElementById('ecfgAllowVideo');
        if (gif) gif.checked = g.allow_gif_preview !== false;
        if (vid) vid.checked = g.allow_video_preview !== false;
    }

    var api = {
        fetchCustomization: fetchCustomization,
        savePatch: savePatch,
        uploadCustomizationFile: uploadCustomizationFile,
        onChange: onChange,
        getCache: getCache,
        resolveExplorerChrome: resolveExplorerChrome,
        resolveFolderStyle: resolveFolderStyle,
        resolveFileStyle: resolveFileStyle,
        applyExplorerChromeToHost: applyExplorerChromeToHost,
        folderThumbHtml: folderThumbHtml,
        fileThumbOverlay: fileThumbOverlay,
        fileRelKey: fileRelKey,
        folderRelKey: folderRelKey,
        patchFolder: patchFolder,
        patchFile: patchFile,
        patchGlobal: patchGlobal,
        buildMediaSrc: buildMediaSrc,
        iconInnerHtml: iconInnerHtml,
        openModal: openModal,
        closeModal: closeModal,
        initConfigTab: initConfigTab,
        refreshConfigPanel: fillConfigPanelFromCache,
        postProcessFileThumbHtml: postProcessFileThumbHtml,
        openToolbarCustModal: openToolbarCustModal,
        normalizeRel: normalizeRel,
        normBucket: normBucket
    };

    global.GridExplorerCustomization = api;

    if (typeof document !== 'undefined') {
        document.addEventListener('DOMContentLoaded', function () {
            try {
                initConfigTab();
                bindExplorerCustomizationModal();
                bindToolbarCustModal();
            } catch (e) {}
        });
    }
})(typeof window !== 'undefined' ? window : this);
