// ============ 文件浏览器模块 ============
/* global Drawer, showToast, refreshFileList, escapeHtml, performRename, performMove, performDelete, copyFilePath, copyDownloadUrl, downloadFile, addToChat, openTerminal */
// 注意：currentItemPath, currentItemName, currentItemIsDir 由 globals.js 定义

/** 文件打开方式配置缓存 */
window.__fileOpenConfig = null;

/** 获取文件打开方式配置 */
function getFileOpenConfig(callback) {
    if (window.__fileOpenConfig) {
        if (callback) callback(window.__fileOpenConfig);
        return;
    }
    fetch('/api/file-open-config', { credentials: 'same-origin' })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            if (d && d.success && d.data) {
                window.__fileOpenConfig = d.data;
            } else {
                window.__fileOpenConfig = {};
            }
            if (callback) callback(window.__fileOpenConfig);
        })
        .catch(function() {
            window.__fileOpenConfig = {};
            if (callback) callback(window.__fileOpenConfig);
        });
}

/** 获取文件类型的打开方式 */
function getOpenMethod(ext, config) {
    var extToGroup = {
        '.jpg': 'image', '.jpeg': 'image', '.png': 'image', '.gif': 'image',
        '.bmp': 'image', '.webp': 'image', '.avif': 'image',
        '.xlsx': 'excel', '.xls': 'excel', '.xlsm': 'excel', '.xlsb': 'excel',
        '.docx': 'word', '.doc': 'word',
        '.pptx': 'ppt', '.ppt': 'ppt',
        '.pdf': 'pdf',
        '.zip': 'archive', '.rar': 'archive', '.7z': 'archive',
        '.tar': 'archive', '.gz': 'archive', '.bz2': 'archive',
        '.tar.gz': 'archive', '.tar.bz2': 'archive', '.tar.xz': 'archive',
        '.py': 'code', '.js': 'code', '.ts': 'code', '.jsx': 'code', '.tsx': 'code',
        '.vue': 'code', '.go': 'code', '.rs': 'code', '.java': 'code',
        '.c': 'code', '.cpp': 'code', '.h': 'code', '.css': 'code',
        '.scss': 'code', '.less': 'code', '.xml': 'code',
        '.json': 'json', '.jsonc': 'json',
        '.yaml': 'yaml', '.yml': 'yaml', '.toml': 'yaml', '.ini': 'yaml',
        '.conf': 'yaml', '.env': 'yaml',
        '.md': 'markdown', '.markdown': 'markdown',
        '.mp4': 'video', '.webm': 'video', '.mov': 'video', '.avi': 'video',
        '.mkv': 'video', '.wmv': 'video', '.flv': 'video',
        '.mp3': 'audio', '.wav': 'audio', '.flac': 'audio', '.aac': 'audio',
        '.ogg': 'audio', '.m4a': 'audio', '.wma': 'audio',
        '.txt': 'text', '.log': 'text', '.sh': 'text', '.bat': 'text', '.ps1': 'text',
        '.html': 'web', '.htm': 'web', '.css': 'web', '.svg': 'web',
    };
    var group = extToGroup[ext] || 'text';
    return config[group] || 'browser';
}

/** 根据打开方式处理文件 */
function handleFileByMethod(path, name, method) {
    if (method === 'preview-image') {
        if (typeof window.openPreview === 'function') window.openPreview(path, name);
    } else if (method === 'preview-excel') {
        if (typeof window.openPreview === 'function') window.openPreview(path, name);
    } else if (method === 'preview-archive') {
        if (typeof window.openPreview === 'function') window.openPreview(path, name);
    } else if (method === 'preview-json') {
        window.open('/json/editor?path=' + encodeURIComponent(path), '_blank', 'noopener');
    } else if (method === 'edit-code') {
        window.open('/edit/' + encodeURIComponent(path), '_blank', 'noopener');
    } else if (method === 'edit-yaml') {
        window.open('/yaml/editor?path=' + encodeURIComponent(path), '_blank', 'noopener');
    } else if (method === 'edit-md') {
        window.open('/view/' + encodePathForUrl(path), '_blank', 'noopener');
    } else if (method === 'download') {
        downloadFile(path, { name: name, openInNewTab: true });
    } else if (method === 'preview-url') {
        // URL 文件：读取内容中的 URL 并新窗口打开
        fetch('/api/file/read?path=' + encodeURIComponent(path))
            .then(function(r) { return r.json(); })
            .then(function(d) {
                if (d?.success && d?.data?.content) {
                    var match = d.data.content.match(/URL=(.+)/);
                    if (match && match[1]) {
                        window.open(match[1].trim(), '_blank');
                    } else {
                        // 直接打开文件内容作为 URL
                        window.open(d.data.content.trim(), '_blank');
                    }
                }
            })
            .catch(function() {});
    } else {
        window.open('/serve/' + encodePathForUrl(path), '_blank', 'noopener');
    }
}

function openCurrentFolderMenu(ev) {
    if (ev && typeof ev.preventDefault === 'function') ev.preventDefault();
    if (ev && typeof ev.stopPropagation === 'function') ev.stopPropagation();
    var pathEl = document.getElementById('currentBrowsePath');
    var nameEl = document.getElementById('currentBrowseName');
    var path = pathEl ? String(pathEl.value || '') : '';
    var name = nameEl ? String(nameEl.value || '') : '';
    if (!name) {
        var p = (path || '').replace(/\\/g, '/').replace(/\/+$/, '');
        name = p ? p.split('/').pop() : (typeof I18n !== 'undefined' ? I18n.t('fileBrowser.rootDirectory') : 'Root Directory');
    }

    // 使用 SmallMenu 显示文件夹菜单
    var menuId = 'folder-menu';
    window.currentItemPath = path;
    window.currentItemName = name;
    window.currentItemIsDir = true;

    var menuItems = [
        { label: typeof I18n !== 'undefined' ? I18n.t('folder_menu.copy') : 'Copy', icon: '📋', action: function() { handleMenuAction('copyPath'); }, actionParams: path },
        { label: typeof I18n !== 'undefined' ? I18n.t('folder_menu.link') : 'Create symlink', icon: '🔗', action: function() { handleMenuAction('link'); }, actionParams: path },
        { label: typeof I18n !== 'undefined' ? I18n.t('folder_menu.terminal') : 'Open in terminal', icon: '📺', action: function() { handleMenuAction('terminal'); }, actionParams: path },
        { label: typeof I18n !== 'undefined' ? I18n.t('folder_menu.compress') : 'Compress', icon: '🗜️', action: function() { handleMenuAction('newArchive'); }, actionParams: path }
    ];

    var menuHtml = SmallMenu.render({
        menuId: menuId,
        triggerText: '⋮',
        items: menuItems,
        center: true,
        hideTrigger: true,
        closeOnClickOutside: true
    });

    // 添加到 body
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = menuHtml;
    document.body.appendChild(tempDiv.firstChild);

    // 打开菜单
    setTimeout(function() {
        SmallMenu.open(menuId);
    }, 10);
}

// 拖拽相关变量和函数（使用 window 对象）
function startDrag(e) {
    window.isDragging = true;
    window.startY = e.clientY || e.touches[0].clientY;
    const sheet = e.target.closest('.bottom-sheet');
    window.startHeight = sheet ? sheet.offsetHeight : 300;
    document.addEventListener('mousemove', onDrag);
    document.addEventListener('mouseup', endDrag);
    document.addEventListener('touchmove', onDrag);
    document.addEventListener('touchend', endDrag);
}

function onDrag(e) {
    if (!window.isDragging) { return; }
    const currentY = e.clientY || e.touches[0].clientY;
    const delta = window.startY - currentY;
    const sheet = document.querySelector('.bottom-sheet[style*="display: block"]') || document.querySelector('.bottom-sheet');
    if (sheet) {
        const newHeight = Math.max(200, Math.min(window.innerHeight * 0.9, window.startHeight + delta));
        sheet.style.height = newHeight + 'px';
    }
}

function endDrag() {
    window.isDragging = false;
    document.removeEventListener('mousemove', onDrag);
    document.removeEventListener('mouseup', endDrag);
    document.removeEventListener('touchmove', onDrag);
    document.removeEventListener('touchend', endDrag);
}

// 文件操作
function handleMenuAction(action) {
    setTimeout(() => {
        switch(action) {
            case 'download': downloadFile(window.currentItemPath, { name: window.currentItemName }); break;
            case 'edit': openEditor(window.currentItemPath); break;
            case 'copyUrl': copyDownloadUrl(window.currentItemPath); break;
            case 'copyPath': copyFilePath(window.currentItemPath); break;
            case 'rename': showRenameModal(); break;
            case 'move': showMoveModal(); break;
            case 'clone': cloneItem(); break;
            case 'pin': 
                __togglePin(getCurrentBrowsePath(), window.currentItemName);
                break;
            case 'unpin':
                __togglePin(getCurrentBrowsePath(), window.currentItemName);
                break;
            case 'cut':
                if (window.Clipboard && typeof window.Clipboard.set === 'function') {
                    window.Clipboard.set('cut', { path: window.currentItemPath, name: window.currentItemName, isDir: window.currentItemIsDir });
                    showToast('已剪切', 'success');
                }
                break;
            case 'copy':
                if (window.Clipboard && typeof window.Clipboard.set === 'function') {
                    window.Clipboard.set('copy', { path: window.currentItemPath, name: window.currentItemName, isDir: window.currentItemIsDir });
                    showToast('已复制', 'success');
                }
                break;
            case 'link':
                if (window.Clipboard && typeof window.Clipboard.set === 'function') {
                    window.Clipboard.set('link', { path: window.currentItemPath, name: window.currentItemName, isDir: window.currentItemIsDir });
                    showToast('已选择创建链接: ' + window.currentItemName, 'success');
                }
                break;
            case 'email':
                sendFileByEmail(window.currentItemPath, window.currentItemName);
                break;
            case 'chat': addToChat(window.currentItemPath); break;
            case 'terminal': openTerminal(window.currentItemPath, window.currentItemIsDir); break;
            case 'delete': confirmDelete(window.currentItemPath, window.currentItemName); break;
            case 'details': showDetails(window.currentItemPath, window.currentItemName); break;
            case 'extract': extractArchiveHere(window.currentItemPath, window.currentItemName); break;
            case 'newArchive':
                openArchiveCreateDialog([window.currentItemPath], getParentDir(window.currentItemPath));
                break;
        }
    }, 50);
}

function normalizeRelPath(path) {
    return (path || '').toString().replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+$/, '');
}

function getParentDir(path) {
    var p = normalizeRelPath(path);
    var idx = p.lastIndexOf('/');
    return idx >= 0 ? p.slice(0, idx) : '';
}

function getCurrentBrowsePath() {
    var el = document.getElementById('currentBrowsePath');
    return el ? normalizeRelPath(el.value || '') : '';
}

var __pinState = { dir: '', byName: {} };

function __pinHeaders() {
    var h = {};
    if (typeof window.authHeaders === 'function') {
        try {
            Object.assign(h, window.authHeaders() || {});
        } catch (e) {}
    }
    return h;
}

function __togglePin(dirRel, name) {
    var dir = normalizeRelPath(dirRel);
    return fetch('/api/pin/toggle', {
        method: 'POST',
        headers: Object.assign({ 'Content-Type': 'application/json' }, __pinHeaders()),
        body: JSON.stringify({ dir: dir, name: name })
    })
        .then(function(r) { return r.json().catch(function() { return null; }); });
}

function __rebuildPinMap(pins) {
    var m = {};
    (Array.isArray(pins) ? pins : []).forEach(function(p) {
        if (!p || !p.name) return;
        var t = typeof p.pinned_at === 'number' ? p.pinned_at : parseInt(p.pinned_at, 10) || 0;
        m[String(p.name)] = t;
    });
    __pinState.byName = m;
}

function __isPinnedName(name) {
    return Object.prototype.hasOwnProperty.call(__pinState.byName, String(name));
}

function __removeInlinePinButtons() {
    document.querySelectorAll('.file-list .file-name button.pin-btn').forEach(function(btn) {
        if (btn && btn.parentNode) btn.parentNode.removeChild(btn);
    });
}

function __applyPinnedClasses() {
    document.querySelectorAll('.file-list .file-item[data-name]').forEach(function(el) {
        var name = String(el.dataset && el.dataset.name ? el.dataset.name : '');
        if (!name) return;
        if (__isPinnedName(name)) el.classList.add('is-pinned');
        else el.classList.remove('is-pinned');
    });
}

function __applyPinOrder() {
    var list = document.querySelector('.file-list');
    if (!list) return;

    var pinnedEls = [];
    document.querySelectorAll('.file-list .file-item[data-name]').forEach(function(el) {
        var name = String(el.dataset && el.dataset.name ? el.dataset.name : '');
        if (!name) return;
        if (!__isPinnedName(name)) return;
        pinnedEls.push({ el: el, name: name, t: __pinState.byName[name] || 0 });
    });

    if (!pinnedEls.length) return;

    pinnedEls.sort(function(a, b) { return (b.t || 0) - (a.t || 0); });
    var first = list.firstChild;
    pinnedEls.forEach(function(item) {
        if (item.el && item.el.parentNode === list) {
            list.insertBefore(item.el, first);
            first = item.el.nextSibling;
        }
    });
}

function __applyPinsToDom(dirRel) {
    __removeInlinePinButtons();
    __applyPinnedClasses();
    __applyPinOrder();
}

function initPinsForCurrentDir() {
    var dir = getCurrentBrowsePath();
    __pinState.dir = dir;
    var pins = Array.isArray(window.__INITIAL_PINS__) ? window.__INITIAL_PINS__ : [];
    var pinDir = typeof window.__INITIAL_PIN_DIR__ === 'string' ? normalizeRelPath(window.__INITIAL_PIN_DIR__) : '';
    if (pinDir !== dir) pins = [];
    __rebuildPinMap(pins);
    __applyPinsToDom(dir);
}

function formatTimestampYYYYMMDDHHMMSS() {
    var d = new Date();
    var pad = function(n) { return String(n).padStart(2, '0'); };
    return String(d.getFullYear())
        + pad(d.getMonth() + 1)
        + pad(d.getDate())
        + pad(d.getHours())
        + pad(d.getMinutes())
        + pad(d.getSeconds());
}

function openArchiveProgressDrawer() {
    var fill = document.getElementById('archiveProgressFill');
    var text = document.getElementById('archiveProgressText');
    if (fill) fill.style.width = '0%';
    if (text) text.textContent = (typeof I18n !== 'undefined' ? I18n.t('archive.preparing') : 'Preparing…');
    Drawer.open('archiveProgressDrawer');
}

function closeArchiveProgressDrawer() {
    if (window.__archiveProgressPoller && typeof window.__archiveProgressPoller.cancel === 'function') {
        window.__archiveProgressPoller.cancel();
    }
    window.__archiveProgressPoller = null;
    Drawer.close('archiveProgressDrawer');
}

function startArchiveProgressPoll(taskId) {
    if (!taskId || !window.TaskPoller || typeof window.TaskPoller.start !== 'function') return;
    openArchiveProgressDrawer();
    window.__archiveProgressPoller = window.TaskPoller.start(taskId, {
        intervalMs: 600,
        timeoutMs: 30 * 60 * 1000,
        onUpdate: function(evt) {
            var payload = evt && evt.payload ? evt.payload : null;
            var p = payload && typeof payload.progress === 'number' ? payload.progress : null;
            var msg = payload && typeof payload.message === 'string' ? payload.message : '';
            var fill = document.getElementById('archiveProgressFill');
            var text = document.getElementById('archiveProgressText');
            if (text && msg) text.textContent = msg;
            if (fill && p !== null && !Number.isNaN(p)) fill.style.width = String(Math.max(0, Math.min(100, p))) + '%';
        }
    });
    window.__archiveProgressPoller.promise.then(function(res) {
        if (res && res.ok) {
            showToast(typeof I18n !== 'undefined' ? I18n.t('archive.compress_success') : 'Compression completed', 'success');
            closeArchiveProgressDrawer();
            if (typeof refreshFileList === 'function') doRefreshFileList();
            return;
        }
        var payload = res && res.payload ? res.payload : null;
        var msg = payload && (payload.message || (payload.error && payload.error.message)) ? (payload.message || (payload.error && payload.error.message)) : '';
        showToast(msg || (typeof I18n !== 'undefined' ? I18n.t('archive.compress_failed') : 'Compression failed'), 'error');
        closeArchiveProgressDrawer();
    });
}

function openArchiveCreateDialog(paths, outputDir) {
    var items = Array.isArray(paths) ? paths.filter(function(p) { return typeof p === 'string' && p.trim(); }) : [];
    if (!items.length) {
        showToast(typeof I18n !== 'undefined' ? I18n.t('archive.no_selection') : 'No items selected for compression', 'warning');
        return;
    }
    var outDir = typeof outputDir === 'string' ? normalizeRelPath(outputDir) : '';
    var defaultName = (typeof I18n !== 'undefined' ? I18n.t('archive.default_name') : 'New Archive') + '.zip';

    if (typeof window.openDialogDrawer === 'function') {
        window.openDialogDrawer({
            title: typeof I18n !== 'undefined' ? I18n.t('archive.create_title') : 'Create Archive',
            message: typeof I18n !== 'undefined' ? I18n.t('archive.create_message') : 'Enter archive name and select format (created in same directory)',
            input: true,
            placeholder: typeof I18n !== 'undefined' ? I18n.t('archive.name_placeholder') : 'e.g., backup.zip',
            defaultValue: defaultName,
            confirmText: typeof I18n !== 'undefined' ? I18n.t('archive.start') : 'Start Compression',
            select: {
                options: [
                    { value: 'zip', label: 'zip' },
                    { value: 'tar.gz', label: 'tar.gz' }
                ],
                defaultValue: 'zip'
            },
            onConfirm: function(result) {
                var name = result && typeof result.value === 'string' ? result.value.trim() : '';
                var fmt = result && typeof result.select === 'string' ? result.select.trim() : 'zip';
                var archiveName = typeof I18n !== 'undefined' ? I18n.t('archive.default_name') : 'New Archive';
                if (!name) name = fmt === 'zip' ? archiveName + '.zip' : archiveName + '.tar.gz';
                if (fmt === 'zip' && !name.toLowerCase().endsWith('.zip')) name = name + '.zip';
                if (fmt === 'tar.gz' && !name.toLowerCase().endsWith('.tar.gz')) {
                    if (name.toLowerCase().endsWith('.tar')) name = name + '.gz';
                    else name = name + '.tar.gz';
                }
                fetch('/api/archive/create', {
                    method: 'POST',
                    headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function' ? authHeaders() : {})),
                    body: JSON.stringify({
                        paths: items,
                        output_dir: outDir,
                        name: name,
                        format: fmt
                    })
                })
                    .then(function(r) { return r.json(); })
                    .then(function(d) {
                        if (d && d.success && d.data && d.data.taskId) {
                            startArchiveProgressPoll(d.data.taskId);
                        } else {
                            showToast((d && d.error && d.error.message) || (typeof I18n !== 'undefined' ? I18n.t('task.create_failed') : 'Failed to create task'), 'error');
                        }
                    })
                    .catch(function() { showToast(typeof I18n !== 'undefined' ? I18n.t('task.create_failed') : 'Failed to create task', 'error'); });
            }
        });
        return;
    }

    SwalPrompt('压缩包名称', '请输入压缩包名称', defaultName, function(value) { if (value) { startCompression(value); } }); picked = value;
    if (picked === null) return;
    fetch('/api/archive/create', {
        method: 'POST',
        headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function' ? authHeaders() : {})),
        body: JSON.stringify({ paths: items, output_dir: outDir, name: picked, format: 'zip' })
    })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            if (d && d.success && d.data && d.data.taskId) startArchiveProgressPoll(d.data.taskId);
            else showToast((d && d.error && d.error.message) || '创建任务失败', 'error');
        })
        .catch(function() { showToast('创建任务失败', 'error'); });
}

function shareText(text) {
    var t = typeof text === 'string' ? text : '';
    if (!t) return;
    if (navigator.share && navigator.canShare && navigator.canShare({ text: t })) {
        // 优先使用原生分享（仅移动设备）
        navigator.share({ text: t }).catch(function() {});
        return;
    }
    // 不支持原生分享时，自动复制到剪贴板
    copyToClipboard(t);
    showToast(typeof I18n !== 'undefined' ? I18n.t('share.link_copied') : 'Link copied to clipboard', 'success');
}

function shareMenuDownloadUrl() {
    var path = window.currentItemPath || '';
    var url = window.location.origin + '/download/' + encodeURIComponent(path);
    shareText(url);
}

function shareMenuPath() {
    shareText(window.currentItemPath || '');
}

function batchArchive() {
    var paths = [];
    document.querySelectorAll('.file-checkbox:checked').forEach(function(c) {
        if (c && c.dataset && c.dataset.path) paths.push(c.dataset.path);
    });
    if (paths.length === 0) {
        showToast(typeof I18n !== 'undefined' ? I18n.t('archive.select_files') : 'Please select files to compress', 'warning');
        return;
    }
    openArchiveCreateDialog(paths, getCurrentBrowsePath());
}

function showDragUploadOverlay(show) {
    var overlay = document.getElementById('dragUploadOverlay');
    if (!overlay) return;
    overlay.style.display = show ? 'flex' : 'none';
}

function openDragUploadDrawer(files) {
    var listEl = document.getElementById('dragUploadFileList');
    var targetEl = document.getElementById('dragUploadTargetPath');
    var fillEl = document.getElementById('dragUploadProgressFill');
    var textEl = document.getElementById('dragUploadProgressText');

    var arr = Array.from(files || []).filter(function(f) { return f && typeof f.name === 'string'; });
    if (!arr.length) return;

    var baseTs = formatTimestampYYYYMMDDHHMMSS();
    var renamed = arr.map(function(f, idx) {
        var name = f.name || '';
        var dot = name.lastIndexOf('.');
        var ext = dot >= 0 ? name.slice(dot) : '';
        if (arr.length === 1) return baseTs + ext;
        return baseTs + '_' + String(idx + 1) + ext;
    });

    window.__dragUploadState = { files: arr, renamed: renamed, uploading: false };

    if (targetEl) targetEl.value = getCurrentBrowsePath();
    if (fillEl) fillEl.style.width = '0%';
    if (textEl) textEl.textContent = '';
    if (listEl) {
        listEl.innerHTML = arr.map(function(f, i) {
            var size = typeof f.size === 'number' ? formatSize(f.size) : '';
            var type = f.type || 'application/octet-stream';
            return '<div style="display:flex;justify-content:space-between;gap:12px;padding:8px 6px;border-bottom:1px solid #f1f2f4;">'
                + '<div style="min-width:0;">'
                + '<div style="font-weight:600;word-break:break-all;">' + escapeHtml(f.name) + '</div>'
                + '<div style="font-size:12px;color:#6e7781;">' + escapeHtml(size) + (size ? ' · ' : '') + escapeHtml(type) + '</div>'
                + '</div>'
                + '<div style="font-family:monospace;font-size:12px;color:#57606a;white-space:nowrap;flex-shrink:0;">→ ' + escapeHtml(renamed[i]) + '</div>'
                + '</div>';
        }).join('');
    }
    Drawer.open('dragUploadDrawer');
}

function closeDragUploadDrawer() {
    window.__dragUploadState = null;
    showDragUploadOverlay(false);
    Drawer.close('dragUploadDrawer');
}

async function startDragUpload() {
    var state = window.__dragUploadState;
    if (!state || !Array.isArray(state.files) || !state.files.length) return;
    if (state.uploading) return;
    state.uploading = true;

    var targetEl = document.getElementById('dragUploadTargetPath');
    var targetDir = targetEl ? normalizeRelPath(targetEl.value || '') : '';
    var total = state.files.length;
    var fillEl = document.getElementById('dragUploadProgressFill');
    var textEl = document.getElementById('dragUploadProgressText');

    for (var i = 0; i < total; i++) {
        if (!window.__dragUploadState || !window.__dragUploadState.uploading) return;
        var f = state.files[i];
        var desired = state.renamed[i] || (formatTimestampYYYYMMDDHHMMSS() + (getFileExt(f.name || '') || ''));
        var uploadingText = typeof I18n !== 'undefined' ? I18n.t('upload.uploading') : 'Uploading...';
        if (textEl) textEl.textContent = uploadingText + ' ' + String(i + 1) + '/' + String(total);

        var fd = new FormData();
        fd.append('file', f, desired);
        fd.append('target_dir', targetDir);
        fd.append('filename', desired);

        try {
            var resp = await fetch('/api/upload', { method: 'POST', body: fd });
            var json = null;
            try { json = await resp.json(); } catch (e) { json = null; }
            if (!json || !json.success) {
                var msg = json && json.error && json.error.message ? json.error.message : (typeof I18n !== 'undefined' ? I18n.t('upload.failed') : 'Upload failed');
                showToast(msg, 'error');
                state.uploading = false;
                return;
            }
        } catch (e) {
            showToast(typeof I18n !== 'undefined' ? I18n.t('upload.failed') : 'Upload failed', 'error');
            state.uploading = false;
            return;
        }

        var pct = Math.round(((i + 1) * 1000) / total) / 10;
        if (fillEl) fillEl.style.width = String(pct) + '%';
    }

    state.uploading = false;
    showToast(typeof I18n !== 'undefined' ? I18n.t('common.upload_success') : 'Upload successful', 'success');
    closeDragUploadDrawer();
    if (typeof refreshFileList === 'function') doRefreshFileList();
}

function openPasteImageDrawer(blob) {
    if (!blob) return;
    var targetEl = document.getElementById('pasteImageTargetPath');
    var nameEl = document.getElementById('pasteImageFilename');
    var imgEl = document.getElementById('pasteImagePreview');

    var type = String(blob.type || '').toLowerCase();
    var ext = '.png';
    if (type === 'image/jpeg' || type === 'image/jpg') ext = '.jpg';
    if (type === 'image/png') ext = '.png';

    var ts = formatTimestampYYYYMMDDHHMMSS();
    var filename = 'paste_' + ts + ext;

    if (targetEl) targetEl.value = getCurrentBrowsePath();
    if (nameEl) nameEl.value = filename;

    var url = URL.createObjectURL(blob);
    window.__pasteImageState = { blob: blob, url: url };
    if (imgEl) {
        imgEl.src = url;
        imgEl.style.display = 'block';
    }
    Drawer.open('pasteImageDrawer');
}

function closePasteImageDrawer() {
    var st = window.__pasteImageState;
    if (st && st.url) {
        try { URL.revokeObjectURL(st.url); } catch (e) {}
    }
    window.__pasteImageState = null;
    var imgEl = document.getElementById('pasteImagePreview');
    if (imgEl) {
        imgEl.src = '';
        imgEl.style.display = 'none';
    }
    Drawer.close('pasteImageDrawer');
}

async function savePasteImage() {
    var st = window.__pasteImageState;
    if (!st || !st.blob) return;
    var targetEl = document.getElementById('pasteImageTargetPath');
    var nameEl = document.getElementById('pasteImageFilename');
    var targetDir = targetEl ? normalizeRelPath(targetEl.value || '') : '';
    var filename = nameEl ? (nameEl.value || '').trim() : '';
    if (!filename) {
        showToast(typeof I18n !== 'undefined' ? I18n.t('validation.name_required') : 'Name is required', 'warning');
        return;
    }

    var file = null;
    try {
        file = new File([st.blob], filename, { type: st.blob.type || 'image/png' });
    } catch (e) {
        file = st.blob;
    }

    var fd = new FormData();
    fd.append('file', file, filename);
    fd.append('target_dir', targetDir);
    fd.append('filename', filename);

    try {
        var resp = await fetch('/api/upload', { method: 'POST', body: fd });
        var json = null;
        try { json = await resp.json(); } catch (e) { json = null; }
        if (!json || !json.success) {
            var msg = json && json.error && json.error.message ? json.error.message : '保存失败';
            showToast(msg, 'error');
            return;
        }
    } catch (e) {
        showToast(typeof I18n !== 'undefined' ? I18n.t('common.save_failed') : 'Save failed', 'error');
        return;
    }

    showToast(typeof I18n !== 'undefined' ? I18n.t('common.save_success') : 'Save successful', 'success');
    closePasteImageDrawer();
    if (typeof refreshFileList === 'function') doRefreshFileList();
}

function initDragUploadAndPaste() {
    if (window.__dragUploadInited) return;
    window.__dragUploadInited = true;

    var dragCounter = 0;
    window.addEventListener('dragenter', function(e) {
        if (!e || !e.dataTransfer) return;
        if (e.dataTransfer.types && Array.from(e.dataTransfer.types).indexOf('Files') < 0) return;
        dragCounter += 1;
        showDragUploadOverlay(true);
    });
    window.addEventListener('dragleave', function(e) {
        dragCounter = Math.max(0, dragCounter - 1);
        if (dragCounter === 0) showDragUploadOverlay(false);
    });
    window.addEventListener('dragover', function(e) {
        if (!e || !e.dataTransfer) return;
        if (e.dataTransfer.types && Array.from(e.dataTransfer.types).indexOf('Files') < 0) return;
        e.preventDefault();
        showDragUploadOverlay(true);
    });
    window.addEventListener('drop', function(e) {
        if (!e || !e.dataTransfer) return;
        var files = e.dataTransfer.files;
        if (!files || files.length === 0) return;
        e.preventDefault();
        dragCounter = 0;
        showDragUploadOverlay(false);
        openDragUploadDrawer(files);
    });

    document.addEventListener('paste', function(e) {
        var ev = e || window.event;
        var cd = ev && ev.clipboardData ? ev.clipboardData : null;
        if (!cd || !cd.items) return;
        var imgItem = null;
        for (var i = 0; i < cd.items.length; i++) {
            var it = cd.items[i];
            if (it && it.kind === 'file' && String(it.type || '').toLowerCase().startsWith('image/')) {
                imgItem = it;
                break;
            }
        }
        if (!imgItem) return;
        var blob = imgItem.getAsFile ? imgItem.getAsFile() : null;
        if (!blob) return;
        if (ev.preventDefault) ev.preventDefault();
        openPasteImageDrawer(blob);
    });
}

function openEditor(path) {
    if (!path) return;
    window.location.href = '/edit/' + encodePathForUrl(path);
}

function isArchiveName(name) {
    var n = (name || '').toLowerCase();
    if (!n) return false;
    if (n.endsWith('.tar.gz')) return true;
    if (n.endsWith('.tar.bz2')) return true;
    if (n.endsWith('.tar.xz')) return true;
    var parts = n.split('.');
    if (parts.length < 2) return false;
    var ext = parts.pop();
    return ['zip', 'rar', 'tar', 'tgz', '7z'].indexOf(ext) >= 0;
}

function extractArchiveHere(path, name) {
    if (!path || !name) {
        return;
    }
    if (!isArchiveName(name)) {
        showToast(typeof I18n !== 'undefined' ? I18n.t('archive.not_archive') : 'Not an archive', 'error');
        return;
    }
    var normPath = (path || '').replace(/\\/g, '/');
    var lastSlash = normPath.lastIndexOf('/');
    var archiveDir = lastSlash >= 0 ? normPath.slice(0, lastSlash) : '';
    var filename = lastSlash >= 0 ? normPath.slice(lastSlash + 1) : normPath;
    var baseName = filename;
    if (filename.toLowerCase().endsWith('.zip')) {
        baseName = filename.slice(0, -4);
    }
    var defaultTarget = baseName;
    var rootEl = document.getElementById('rootDir');
    var rootVal = rootEl ? (rootEl.value || '') : '';
    var rootNorm = rootVal.replace(/\\/g, '/').replace(/\/+$/, '');
    var rootName = rootNorm ? rootNorm.split('/').pop() : '';
    var displayDir = (rootName ? ('/' + rootName) : '') + (archiveDir ? ('/' + archiveDir) : '');
    if (!displayDir) displayDir = '/';
    var extractMsg = typeof I18n !== 'undefined' ? I18n.t('archive.extract_message') : 'Enter extraction target directory (relative to current folder)';
    var msg = extractMsg + ': ' + displayDir;

    var runExtract = function(targetDir) {
        var t = (targetDir || '').trim();
        if (!t) t = baseName;
        t = (t || '').replace(/\\/g, '/').replace(/^\/+/, '');
        if (archiveDir) t = archiveDir + '/' + t;
        var url = '/api/archive/extract/' + encodePathForUrl(path);
        fetch(url, {
            method: 'POST',
            headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function' ? authHeaders() : {})),
            body: JSON.stringify({ target_dir: t })
        })
            .then(function(r) {
                return r.json().catch(function() {
                    return { success: false, error: { message: '解压失败' } };
                });
            })
            .then(function(data) {
                if (data && data.success) {
                    showToast(typeof I18n !== 'undefined' ? I18n.t('archive.extract_success') : 'Extraction completed', 'success');
                    if (typeof refreshFileList === 'function') doRefreshFileList();
                } else {
                    showToast((data && data.error && data.error.message) || (data && data.message) || (typeof I18n !== 'undefined' ? I18n.t('archive.extract_failed') : 'Extraction failed'), 'error');
                }
            })
            .catch(function(err) {
                showToast(typeof I18n !== 'undefined' ? I18n.t('archive.extract_failed') : 'Extraction failed', 'error');
            });
    };

    if (typeof window.showPromptDrawer === 'function') {
        window.showPromptDrawer(typeof I18n !== 'undefined' ? I18n.t('archive.extract_here') : 'Extract Here', msg, typeof I18n !== 'undefined' ? I18n.t('archive.extract_placeholder') : 'e.g., downloads/unpacked', defaultTarget, typeof I18n !== 'undefined' ? I18n.t('archive.extract') : 'Extract', runExtract, true);
        return;
    }

    SwalPrompt('移动到', msg, defaultTarget, function(value) { if (value) { target = value; doMoveOrCopy(target); } });
    if (target === null) return;
    runExtract(target);
}

function encodePathForUrl(path) {
    var p = (path || '').replace(/\\/g, '/');
    return encodeURIComponent(p).replace(/%2F/g, '/');
}

function getFileExt(name) {
    var n = (name || '').toLowerCase();
    if (!n) return '';
    if (n.endsWith('.tar.gz')) return '.tar.gz';
    if (n.endsWith('.tar.bz2')) return '.tar.bz2';
    if (n.endsWith('.tar.xz')) return '.tar.xz';
    if (n.startsWith('.') && n.indexOf('.', 1) === -1) return n;
    var idx = n.lastIndexOf('.');
    if (idx < 0) return '';
    return n.slice(idx);
}

function attachFileItemDefaultHandlers() {
    document.querySelectorAll('.file-item[data-path][data-name]').forEach(function(el) {
        if (el.dataset && el.dataset.defaultClickBound === 'true') return;
        if (el.dataset) el.dataset.defaultClickBound = 'true';
        el.addEventListener('click', function(ev) {
            var target = ev.target;
            if (target && (target.closest('.menu-btn') || target.closest('input[type="checkbox"]') || target.closest('a') || target.closest('button'))) {
                return;
            }
            var path = el.dataset.path || '';
            var name = el.dataset.name || '';
            var isDir = (el.dataset.isDir || '').toLowerCase() === 'true';
            if (!path && !name) return;

            if (isDir) {
                var nav = window.BrowseNavigator;
                if (nav && typeof nav.navigateTo === 'function') {
                    nav.navigateTo(path || '', { replace: false });
                } else {
                    var dirUrl = path ? ('/browse/' + encodePathForUrl(path)) : '/browse/';
                    window.location.assign(dirUrl);
                }
                return;
            }

            var ext = getFileExt(name);
            
            // 如果没有扩展名，直接下载
            if (!ext) {
                downloadFile(path, { name: name, openInNewTab: true });
                return;
            }
            
            // 使用服务端返回的 open_method
            var method = el.dataset.openMethod || el.dataset.openmethod || 'browser';
            console.log('[open] method:', method, 'path:', path);
            handleFileByMethod(path, name, method);
            return;
            // JSON 文件
            if (ext === '.json') {
                window.open('/json/editor?path=' + encodeURIComponent(path), '_blank', 'noopener');
                return;
            }
            // 配置文件
            if (ext === '.yaml' || ext === '.yml' || ext === '.toml' || ext === '.ini' || ext === '.conf') {
                window.open('/yaml/editor?path=' + encodeURIComponent(path), '_blank', 'noopener');
                return;
            }
            // URL 文件：读取内容中的 URL 并新窗口打开
            if (ext === '.url') {
                fetch('/api/file/read?path=' + encodeURIComponent(path))
                    .then(function(r) { return r.json(); })
                    .then(function(d) {
                        if (d?.success && d?.data?.content) {
                            var match = d.data.content.match(/URL=(.+)/);
                            if (match && match[1]) {
                                window.open(match[1].trim(), '_blank');
                            }
                        }
                    })
                    .catch(function() {});
                return;
            }
            // 所有其他文件都用 /serve/ 让浏览器原生预览
            window.open('/serve/' + encodePathForUrl(path), '_blank', 'noopener');
        });
    });
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        attachFileItemDefaultHandlers();
        initPinsForCurrentDir();
        initDragUploadAndPaste();
    });
} else {
    attachFileItemDefaultHandlers();
    initPinsForCurrentDir();
    initDragUploadAndPaste();
}

// 删除
function confirmDelete(path, name) {
    if (window.showConfirmDrawer) {
        window.showConfirmDrawer(
            typeof I18n !== 'undefined' ? I18n.t('common.delete') : 'Delete',
            (typeof I18n !== 'undefined' ? I18n.t('confirm.delete_message') : 'This action cannot be undone. Continue?') + ' "' + (name || '') + '" ？',
            typeof I18n !== 'undefined' ? I18n.t('common.delete') : 'Delete',
            function() { performDelete(path); },
            true
        );
        return;
    }
    document.getElementById('itemNameToDelete').textContent = name;
    Drawer.open('confirmModal');
}

function closeLegacyConfirmModal() {
    Drawer.close('confirmModal');
}

function performDelete(path) {
    var p = typeof path === 'string' && path ? path : window.currentItemPath;
    fetch(`/delete/${encodeURIComponent(p)}`, { method: 'DELETE' })
        .then(r => r.json())
        .then(data => {
            if (data.success) {
                showToast(typeof I18n !== 'undefined' ? I18n.t('delete.moved_to_trash') : 'Moved to trash', 'success');
                doRefreshFileList();
            } else {
                showToast(data.message || (typeof I18n !== 'undefined' ? I18n.t('common.delete_failed') : 'Delete failed'), 'error');
            }
        })
        .catch(() => showToast(typeof I18n !== 'undefined' ? I18n.t('common.delete_failed') : 'Delete failed', 'error'));
    if (document.getElementById('confirmModal') && document.getElementById('confirmModal').classList.contains('open')) {
        closeLegacyConfirmModal();
    }
}

// 重命名
function showRenameModal() {
    Drawer.open('renameModal');
    document.getElementById('renameInput').value = window.currentItemName;
    setTimeout(() => document.getElementById('renameInput').focus(), 100);
}

function closeRenameModal() {
    Drawer.close('renameModal');
}

function closeRenameOnBackdrop(event) {
    if (event.target.id === 'renameModal') {
        closeRenameModal();
    }
}

function performRename() {
    const newName = document.getElementById('renameInput').value.trim();
    if (!newName) { return; }
    
    fetch(`/rename/${encodeURIComponent(window.currentItemPath)}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ new_name: newName })
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            showToast(typeof I18n !== 'undefined' ? I18n.t('rename.success') : 'Rename successful', 'success');
            doRefreshFileList();
        } else {
            showToast(data.message || (typeof I18n !== 'undefined' ? I18n.t('rename.failed') : 'Rename failed'), 'error');
        }
    })
    .catch(() => showToast(typeof I18n !== 'undefined' ? I18n.t('rename.failed') : 'Rename failed', 'error'));
    
    closeRenameModal();
}

// 移动
function showMoveModal() {
    Drawer.open('moveModal');
    document.getElementById('targetPathInput').value = '';
}

function closeMoveModal() {
    Drawer.close('moveModal');
}

function performMove() {
    const targetPath = document.getElementById('targetPathInput').value.trim();
    if (!targetPath) { return; }
    
    fetch(`/move/${encodeURIComponent(window.currentItemPath)}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ target_path: targetPath })
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            showToast(typeof I18n !== 'undefined' ? I18n.t('move.success') : 'Move successful', 'success');
            doRefreshFileList();
        } else {
            showToast(data.message || (typeof I18n !== 'undefined' ? I18n.t('move.failed') : 'Move failed'), 'error');
        }
    })
    .catch(() => showToast(typeof I18n !== 'undefined' ? I18n.t('move.failed') : 'Move failed', 'error'));
    
    closeMoveModal();
}

// 克隆
function cloneItem() {
    fetch(`/clone/${encodeURIComponent(window.currentItemPath)}`, { method: 'POST' })
        .then(r => r.json())
        .then(data => {
            if (data.success) {
                showToast(data.message, 'success');
                doRefreshFileList();
            } else {
                showToast(data.message || (typeof I18n !== 'undefined' ? I18n.t('clone.failed') : 'Clone failed'), 'error');
            }
        })
        .catch(() => showToast(typeof I18n !== 'undefined' ? I18n.t('clone.failed') : 'Clone failed', 'error'));
}

// 下载
function getDownloadDisplayName(path, preferredName) {
    if (preferredName) return preferredName;
    if (window.currentItemName) return window.currentItemName;
    var p = (path || '').replace(/\\/g, '/').replace(/\/+$/, '');
    if (!p) return '';
    return p.split('/').pop() || '';
}

function getFileSizeTextFromDom(path) {
    var p = String(path || '');
    var sizeText = '';
    document.querySelectorAll('.file-item[data-path]').forEach(function(el) {
        if (!el || !el.dataset) return;
        if (String(el.dataset.path || '') !== p) return;
        var sizeEl = el.querySelector('.file-size');
        if (!sizeEl) return;
        var t = String(sizeEl.textContent || '').trim();
        if (t && t !== '-') sizeText = t;
    });
    return sizeText;
}

function downloadFile(path, opts) {
    var p = typeof path === 'string' ? path : '';
    if (!p) return;
    var o = opts && typeof opts === 'object' ? opts : {};
    var name = getDownloadDisplayName(p, o.name || '');
    var openInNewTab = !!o.openInNewTab;
    var url = '/download/' + encodePathForUrl(p);

    var headers = (typeof window.authHeaders === 'function') ? window.authHeaders() : null;
    var fetchOpts = headers ? { headers: headers } : undefined;
    fetch('/api/file/info?path=' + encodeURIComponent(p), fetchOpts)
        .then(function(r) { return r.json(); })
        .then(function(data) {
            var info = data && data.success ? data.data : null;
            if (info && info.is_dir) {
                showToast(typeof I18n !== 'undefined' ? I18n.t('download.no_folder') : 'Cannot download folders', 'warning');
                return;
            }

            var sizeText = '';
            if (info && typeof info.size_human === 'string' && info.size_human) sizeText = info.size_human;
            if (!sizeText && info && typeof info.size === 'number' && isFinite(info.size)) sizeText = formatSize(info.size);
            if (!sizeText) sizeText = getFileSizeTextFromDom(p);

            var lines = [];
            lines.push(typeof I18n !== 'undefined' ? I18n.t('download.confirm_title') : 'Confirm Download?');
            if (name) lines.push((typeof I18n !== 'undefined' ? I18n.t('common.name') : 'Name') + '：' + name);
            if (sizeText) lines.push((typeof I18n !== 'undefined' ? I18n.t('common.size') : 'Size') + '：' + sizeText);
            if (info && (info.mtime || info.modified)) lines.push((typeof I18n !== 'undefined' ? I18n.t('common.modified') : 'Modified') + '：' + String(info.mtime || info.modified));
            lines.push((typeof I18n !== 'undefined' ? I18n.t('common.path') : 'Path') + '：' + p);

            SwalConfirm('批量操作确认', lines.join('\n'), function() { performBatchOp(); }, 'warning');
            if (!ok) return;

            if (openInNewTab) window.open(url, '_blank', 'noopener');
            else window.location.href = url;
        })
        .catch(function() {
            var sizeText = getFileSizeTextFromDom(p);
            var lines = [];
            lines.push('确认下载？');
            if (name) lines.push('名称：' + name);
            if (sizeText) lines.push('大小：' + sizeText);
            lines.push('路径：' + p);
            SwalConfirm('批量操作确认', lines.join('\n'), function() { performBatchOp(); }, 'warning');
            if (!ok) return;
            if (openInNewTab) window.open(url, '_blank', 'noopener');
            else window.location.href = url;
        });
}

function copyDownloadUrl(path) {
    const url = window.location.origin + '/download/' + encodeURIComponent(path);
    copyToClipboard(url);
}

function copyFilePath(path) {
    // 获取绝对路径
    var rootPath = document.getElementById('rootDir')?.value || '';
    var currentPath = document.getElementById('currentBrowsePath')?.value || '';
    
    if (!path) {
        copyToClipboard(path);
        return;
    }
    
    // 如果 path 已经是绝对路径，直接复制
    if (path.startsWith('/')) {
        copyToClipboard(path);
        return;
    }
    
    var absolutePath = path;
    
    // 如果有 rootPath，优先使用 rootPath
    if (rootPath) {
        // 检查当前路径相对于 rootPath 的部分
        if (currentPath && currentPath.startsWith(rootPath)) {
            var relativeFromRoot = currentPath.slice(rootPath.length);
            if (relativeFromRoot) {
                absolutePath = rootPath + relativeFromRoot + '/' + path;
            } else {
                absolutePath = rootPath + '/' + path;
            }
        } else {
            absolutePath = rootPath + '/' + path;
        }
    } else if (currentPath) {
        absolutePath = currentPath + '/' + path;
    }
    
    copyToClipboard(absolutePath);
}

// 通用复制函数
function copyToClipboard(text) {
    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed';
    textarea.style.opacity = '0';
    textarea.style.left = '0';
    textarea.style.top = '0';
    document.body.appendChild(textarea);
    textarea.select();
    textarea.setSelectionRange(0, text.length);
    try {
        const successful = document.execCommand('copy');
        document.body.removeChild(textarea);
        showToast(successful ? (typeof I18n !== 'undefined' ? I18n.t('copy.success') : 'Copy successful') : (typeof I18n !== 'undefined' ? I18n.t('copy.failed') : 'Copy failed'), successful ? 'success' : 'error');
    } catch (err) {
        document.body.removeChild(textarea);
        showToast(typeof I18n !== 'undefined' ? I18n.t('copy.failed') : 'Copy failed', 'error');
    }
}

function editFile(path) {
    window.location.href = '/edit/' + encodePathForUrl(path);
}

// 添加到对话
function addToChat(path) {
    // 获取绝对路径
    var rootPath = document.getElementById('rootDir')?.value || '';
    var currentPath = document.getElementById('currentBrowsePath')?.value || '';
    
    if (!path) {
        var absolutePath = '';
    } else if (path.startsWith('/')) {
        var absolutePath = path;
    } else {
        var absolutePath = path;
        // 如果有 rootPath，优先使用 rootPath
        if (rootPath) {
            if (currentPath && currentPath.startsWith(rootPath)) {
                var relativeFromRoot = currentPath.slice(rootPath.length);
                if (relativeFromRoot) {
                    absolutePath = rootPath + relativeFromRoot + '/' + path;
                } else {
                    absolutePath = rootPath + '/' + path;
                }
            } else {
                absolutePath = rootPath + '/' + path;
            }
        } else if (currentPath) {
            absolutePath = currentPath + '/' + path;
        }
    }
    
    // 隐藏菜单
    if (typeof closeMenuModal === 'function') {
        closeMenuModal();
    }
    
    if (typeof window.openBotModal === 'function') {
        window.openBotModal();
    }
    
    const input = document.getElementById('botInput');
    if (input) {
        const prefix = absolutePath ? (absolutePath + '： ') : '';
        input.value = (input.value || '') + prefix;
        input.focus();
        try { input.setSelectionRange(input.value.length, input.value.length); } catch (e) {}
    }
}

// 详情
window.showDetails = function(path, name) {
    // 确保 path 有效（可能是 undefined、空字符串或字符串 'undefined'）
    if (!path || path === 'undefined' || path === 'null') {
        console.error('showDetails: invalid path', path);
        if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('error.invalid_path') : 'Invalid file path', 'error');
        return;
    }
    const modal = document.getElementById('detailsModal');
    const content = document.getElementById('detailsContent');
    if (modal) { Drawer.open('detailsModal'); }
    var loadingText = typeof I18n !== 'undefined' ? I18n.t('common.loading') : 'Loading...';
    if (content) { content.innerHTML = '<div style="text-align:center;padding:40px;color:#666;">🔄 ' + loadingText + '</div>'; }
    
    fetch('/api/file/info?path=' + encodeURIComponent(path))
        .then(r => r.json())
        .then(data => {
            if (data && data.success && content) {
                const info = data.data;
                
                // 构建类型显示
                var folderText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.folder') : 'Folder';
                var fileText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.file') : 'File';
                let typeHtml = info.is_dir ? '📁 ' + folderText : '📄 ' + fileText;
                if (info.is_symlink) {
                    const isBroken = !info.target_exists;
                    var symlinkText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.symlink') : 'Symlink';
                    var brokenText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.broken') : 'Broken';
                    typeHtml += ' → <span style="color:' + (isBroken ? '#ff4444' : '#58a6ff') + ';">' + symlinkText + (isBroken ? ' (' + brokenText + ')' : '') + '</span>';
                }
                
                // 构建软链接目标行
                let linkRowHtml = '';
                if (info.is_symlink && info.link_target) {
                    var symlinkTargetText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.symlink_target') : 'Symlink Target';
                    linkRowHtml = '<tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">' + symlinkTargetText + '</td><td style="padding:10px;font-family:monospace;word-break:break-all;color:#58a6ff;">' + escapeHtml(info.link_target) + '</td></tr>';
                }
                
                var pathText = typeof I18n !== 'undefined' ? I18n.t('common.path') : 'Path';
                var typeText = typeof I18n !== 'undefined' ? I18n.t('common.type') : 'Type';
                var sizeText = typeof I18n !== 'undefined' ? I18n.t('common.size') : 'Size';
                var permissionsText = typeof I18n !== 'undefined' ? I18n.t('common.permissions') : 'Permissions';
                var ownerText = typeof I18n !== 'undefined' ? I18n.t('common.owner') : 'Owner';
                var ctimeText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.ctime') : 'Created';
                var mtimeText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.mtime') : 'Modified';
                var atimeText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.atime') : 'Accessed';
                var unknownText = typeof I18n !== 'undefined' ? I18n.t('common.unknown') : 'Unknown';
                
                content.innerHTML = `
                    <div style="padding: 16px 0;">
                        <div style="font-size: 18px; font-weight: bold; margin-bottom: 16px; word-break: break-all;">${escapeHtml(name)}</div>
                        <table style="width:100%;border-collapse:collapse;font-size:14px;">
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${pathText}</td><td style="padding:10px;font-family:monospace;word-break:break-all;">${escapeHtml(path)}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${typeText}</td><td style="padding:10px;">${typeHtml}</td></tr>
                            ${linkRowHtml}
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${sizeText}</td><td style="padding:10px;">${info.size_human || formatSize(info.size)}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${permissionsText}</td><td style="padding:10px;font-family:monospace;">${info.permissions || unknownText}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${ownerText}</td><td style="padding:10px;">${info.owner || unknownText}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${ctimeText}</td><td style="padding:10px;">${info.ctime || unknownText}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${mtimeText}</td><td style="padding:10px;">${info.mtime || unknownText}</td></tr>
                            <tr style="border-bottom:1px solid #eee;"><td style="padding:10px;color:#666;">${atimeText}</td><td style="padding:10px;">${info.atime || unknownText}</td></tr>
                        </table>
                    </div>
                `;
            } else if (content) {
                const msg = data && data.error && data.error.message ? data.error.message : '❌ ' + (typeof I18n !== 'undefined' ? I18n.t('fileBrowser.get_details_failed') : 'Failed to get details');
                content.innerHTML = '<div style="text-align:center;padding:40px;color:#cf222e;">' + escapeHtml(msg) + '</div>';
            }
        })
        .catch(() => {
            var detailsFailedText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.get_details_failed') : 'Failed to get details';
            if (content) { content.innerHTML = '<div style="text-align:center;padding:40px;color:#cf222e;">❌ ' + detailsFailedText + '</div>'; }
        });
};

window.closeDetailsModal = function() {
    Drawer.close('detailsModal');
};

window.closeDetailsOnBackdrop = function(event) {
    if (event.target.id === 'detailsModal') { closeDetailsModal(); } };

function doRefreshFileList() {
    // 调用全局的 refreshFileList（会尝试 Vue AJAX 刷新，回退到页面刷新）
    if (typeof window.refreshFileList === 'function') {
        window.refreshFileList();
    } else {
        window.location.reload();
    }
}

function formatSize(size) {
    if (size < 1024) { return size + ' B'; }
    if (size < 1024 * 1024) { return (size / 1024).toFixed(1) + ' KB'; }
    if (size < 1024 * 1024 * 1024) { return (size / 1024 / 1024).toFixed(1) + ' MB'; }
    return (size / 1024 / 1024 / 1024).toFixed(1) + ' GB';
}

function openSearchResultFolder(path, isDir) {
    var p = (path || '').toString().replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+$/, '');
    var dir = '';
    if (isDir) {
        var idx = p.lastIndexOf('/');
        dir = idx >= 0 ? p.slice(0, idx) : '';
    } else {
        var idx2 = p.lastIndexOf('/');
        dir = idx2 >= 0 ? p.slice(0, idx2) : '';
    }
    var enc = (typeof encodePathForUrl === 'function') ? encodePathForUrl(dir) : encodeURIComponent(dir).replace(/%2F/g, '/');
    window.location.href = dir ? ('/browse/' + enc) : '/browse/';
    return false;
}

function openSearchResultMenu(ev, el) {
    if (ev && typeof ev.stopPropagation === 'function') ev.stopPropagation();
    if (!el || !el.dataset) return false;
    var path = decodeURIComponent(el.dataset.path || '');
    var name = decodeURIComponent(el.dataset.name || '');
    var isDir = (el.dataset.isDir || '').toLowerCase() === 'true';
    if (typeof window.showFileSmallMenu === 'function') {
        window.showFileSmallMenu(path, name, isDir, el);
    }
    return false;
}

function doSearch() {
    var input = document.getElementById('searchInput');
    var keyword = input ? String(input.value || '').trim() : '';
    if (!keyword) return;
    var resultsContainer = document.getElementById('searchResults');
    var searchingText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.searching') : 'Searching...';
    if (resultsContainer) resultsContainer.innerHTML = '<div style="text-align:center;padding:20px;color:#666;">' + searchingText + '</div>';
    fetch('/api/search?q=' + encodeURIComponent(keyword))
        .then(function(r) { return r.json(); })
        .then(function(data) {
            var results = data && data.success && data.data ? data.data.results : null;
            if (!resultsContainer) return;
            if (results && results.length > 0) {
                resultsContainer.innerHTML = results.map(function(item) {
                    var rawPath = (item && item.path ? String(item.path) : '').replace(/\\/g, '/').replace(/^\/+/, '');
                    var rawName = item && item.name ? String(item.name) : '';
                    var isDir = !!(item && item.is_dir);
                    var icon = item && item.icon ? String(item.icon) : (isDir ? '📁' : '📄');
                    var safeName = (typeof escapeHtml === 'function') ? escapeHtml(rawName) : rawName;
                    var safePath = (typeof escapeHtml === 'function') ? escapeHtml(rawPath) : rawPath;
                    var encPath = encodeURIComponent(rawPath);
                    var encName = encodeURIComponent(rawName);
                    return (
                        '<div class="file-item search-result-item" data-path="' + safePath + '" data-name="' + safeName + '" data-is-dir="' + (isDir ? 'true' : 'false') + '">' +
                            '<div class="file-col-icon">' +
                                '<span class="file-icon">' + (typeof escapeHtml === 'function' ? escapeHtml(icon) : icon) + '</span>' +
                            '</div>' +
                            '<div class="file-col-info">' +
                                '<div class="file-name"><span>' + safeName + '</span></div>' +
                                '<div class="file-details-inline"><span>' + safePath + '</span></div>' +
                            '</div>' +
                            '<div class="file-col-actions" style="gap:8px;">' +
                                '<a href="#" class="preview-btn" title="' + (typeof I18n !== 'undefined' ? I18n.t('fileBrowser.open_folder') : 'Containing folder') + '" onclick="return openSearchResultFolder(decodeURIComponent(\'' + encPath + '\'), ' + (isDir ? 'true' : 'false') + ');">📁</a>' +
                                '<div class="menu-btn" data-path="' + encPath + '" data-name="' + encName + '" data-is-dir="' + (isDir ? 'true' : 'false') + '" onclick="return openSearchResultMenu(event, this);">' +
                                    '<span>⋮</span>' +
                                '</div>' +
                            '</div>' +
                        '</div>'
                    );
                }).join('');
                attachFileItemDefaultHandlers();
            } else {
                var noResultsText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.search_no_results') : 'No results found';
                resultsContainer.innerHTML = '<div style="text-align:center;padding:40px;color:#666;">' + noResultsText + '</div>';
            }
        })
        .catch(function() {
            var searchFailedText = typeof I18n !== 'undefined' ? I18n.t('fileBrowser.search_failed') : 'Search failed';
            if (resultsContainer) resultsContainer.innerHTML = '<div style="text-align:center;padding:40px;color:#cf222e;">' + searchFailedText + '</div>';
        });
}

function openTrashDrawer(callbacks) {
    Drawer.open('trashDrawer', callbacks);
    loadTrashList();
}

function closeTrashDrawer(callbacks) {
    Drawer.close('trashDrawer', Object.assign({}, callbacks || {}, {
        afterClose: function() {
            var container = document.getElementById('trashListContainer');
            if (container) container.innerHTML = '';
            if (callbacks && typeof callbacks.afterClose === 'function') callbacks.afterClose();
        }
    }));
}

function loadTrashList() {
    var container = document.getElementById('trashListContainer');
    var loadingText = typeof I18n !== 'undefined' ? I18n.t('common.loading') : 'Loading...';
    if (container) container.innerHTML = '<div style="text-align:center;padding:40px;color:#666;">🔄 ' + loadingText + '</div>';
    fetch('/api/trash/list', { headers: (typeof authHeaders === 'function') ? authHeaders() : {} })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            var items = data && data.success && data.data ? data.data.items : null;
            if (!container) return;
            if (!items || items.length === 0) {
                var emptyTrashText = typeof I18n !== 'undefined' ? I18n.t('trash.empty') : 'Trash is empty';
                container.innerHTML = '<div style="text-align:center;padding:40px;color:#666;">' + emptyTrashText + '</div>';
                return;
            }
            container.innerHTML = items.map(function(item) {
                var rawName = item.name || '';
                var rawDisplayName = item.display_name || item.name || '';
                var displayName = (typeof escapeHtml === 'function') ? escapeHtml(rawDisplayName) : rawDisplayName;
                var deletedAt = (typeof escapeHtml === 'function') ? escapeHtml(item.deleted_at || '') : (item.deleted_at || '');
                var typeIcon = item.is_dir ? '📁' : '📄';
                return (
                    '<div style="padding:12px;border:1px solid #eee;border-radius:10px;margin-bottom:10px;background:#fff;display:flex;gap:12px;align-items:flex-start;">' +
                        '<div style="font-size:18px;line-height:1;">' + typeIcon + '</div>' +
                        '<div style="flex:1;min-width:0;">' +
                            '<div style="font-weight:600;word-break:break-word;white-space:pre-wrap;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">' +
                                '<span>' + displayName + '</span>' +
                                '<button class="modal-btn modal-btn-confirm" style="padding:4px 8px;border-radius:999px;font-size:12px;" data-trash-name="' + encodeURIComponent(rawName) + '" data-trash-default="' + encodeURIComponent(rawDisplayName) + '" onclick="restoreTrashItemFromButton(this)">' + (typeof I18n !== 'undefined' ? I18n.t('trash.restore') : 'Restore') + '</button>' +
                            '</div>' +
                            '<div style="margin-top:4px;color:#666;font-size:12px;">' + (typeof I18n !== 'undefined' ? I18n.t('trash.deleted_at') : 'Deleted At') + ': ' + deletedAt + '</div>' +
                        '</div>' +
                    '</div>'
                );
            }).join('');
        })
        .catch(function() {
            var loadFailedText = typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed';
            if (container) container.innerHTML = '<div style="text-align:center;padding:40px;color:#cf222e;">' + loadFailedText + '</div>';
        });
}

function restoreTrashItemFromButton(btn) {
    if (!btn || !btn.dataset) return;
    var rawName = decodeURIComponent(btn.dataset.trashName || '');
    var suggested = decodeURIComponent(btn.dataset.trashDefault || '');
    showPromptDrawer(
        typeof I18n !== 'undefined' ? I18n.t('trash.restore') : 'Restore',
        typeof I18n !== 'undefined' ? I18n.t('trash.restore_dialog.path_placeholder') : 'Enter restore path',
        typeof I18n !== 'undefined' ? I18n.t('trash.restore_example') : 'e.g., docs/a.txt',
        suggested,
        typeof I18n !== 'undefined' ? I18n.t('trash.restore') : 'Restore',
        function(targetPath) {
            if (!targetPath) return;
            fetch('/api/trash/restore/' + encodeURIComponent(rawName), {
                method: 'POST',
                headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function') ? authHeaders() : {}),
                body: JSON.stringify({ target_path: targetPath })
            })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data && data.success) {
                        if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('trash.restore_success') : 'Restore successful', 'success');
                        loadTrashList();
                    } else {
                        if (typeof showToast === 'function') showToast((data && data.error && data.error.message) || (typeof I18n !== 'undefined' ? I18n.t('trash.restore_failed') : 'Restore failed'), 'error');
                    }
                })
                .catch(function() { if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('trash.restore_failed') : 'Restore failed', 'error'); });
        }
    );
}

function clearTrash() {
    showConfirmDrawer(
        typeof I18n !== 'undefined' ? I18n.t('trash.clear_dialog.title') : 'Confirm Empty',
        typeof I18n !== 'undefined' ? I18n.t('trash.clear_dialog.message') : 'Are you sure you want to empty the trash?',
        typeof I18n !== 'undefined' ? I18n.t('trash.clear') : 'Clear',
        function() {
            fetch('/api/trash/clear', { method: 'POST', headers: (typeof authHeaders === 'function') ? authHeaders() : {} })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data && data.success) {
                        if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('trash.clear_success') : 'Trash emptied', 'success');
                        loadTrashList();
                    } else {
                        if (typeof showToast === 'function') showToast((data && data.error && data.error.message) || (typeof I18n !== 'undefined' ? I18n.t('trash.clear_failed') : 'Empty failed'), 'error');
                    }
                })
                .catch(function() { if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('trash.clear_failed') : 'Empty failed', 'error'); });
        },
        true
    );
}

function openCreateMenuDrawer(callbacks) {
    // 使用 SmallMenu 显示创建菜单
    var menuId = 'create-menu';

    var menuItems = [
        { label: typeof I18n !== 'undefined' ? I18n.t('fileBrowser.upload') : 'Upload', icon: '📤', action: function() { createMenuUpload(); } },
        { label: typeof I18n !== 'undefined' ? I18n.t('create_menu.new_folder') : 'New Folder', icon: '📁', action: function() { createMenuNewFolder(); } },
        { label: typeof I18n !== 'undefined' ? I18n.t('create_menu.new_file') : 'New File', icon: '📄', action: function() { createMenuNewFile(); } },
        { label: typeof I18n !== 'undefined' ? I18n.t('create_menu.url_shortcut') : 'URL Shortcut', icon: '🔗', action: function() { createMenuNewUrl(); } }
    ];

    var menuHtml = SmallMenu.render({
        menuId: menuId,
        triggerText: '+',
        items: menuItems,
        center: true,
        hideTrigger: true,
        closeOnClickOutside: true
    });

    // 添加到 body
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = menuHtml;
    document.body.appendChild(tempDiv.firstChild);

    // 打开菜单
    setTimeout(function() {
        SmallMenu.open(menuId);
    }, 10);
}

function closeCreateMenuDrawer(callbacks) {
    Drawer.close('createMenuDrawer', callbacks);
}

function createMenuUpload() {
    closeCreateMenuDrawer();
    var input = document.getElementById('fileInputInline');
    if (input) input.click();
}

function createMenuNewFolder() {
    closeCreateMenuDrawer();
    showPromptDrawer(
        typeof I18n !== 'undefined' ? I18n.t('create_menu.new_folder') : 'New Folder',
        typeof I18n !== 'undefined' ? I18n.t('create_menu.folder_name_placeholder') : 'Enter folder name',
        typeof I18n !== 'undefined' ? I18n.t('create_menu.folder_example') : 'e.g., assets',
        'new_folder',
        typeof I18n !== 'undefined' ? I18n.t('common.create') : 'Create',
        function(name) {
            var pickedName = (typeof name === 'string') ? name.trim() : '';
            if (!pickedName) {
                if (typeof showToast === 'function') showToast('名称不能为空', 'warning');
                return;
            }
            var currentPathEl = document.getElementById('currentBrowsePath');
            var currentPath = currentPathEl ? String(currentPathEl.value || '') : '';
            var url = currentPath ? '/mkdir/' + encodeURIComponent(currentPath) : '/mkdir';
            fetch(url, {
                method: 'POST',
                headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function') ? authHeaders() : {}),
                body: JSON.stringify({ name: pickedName })
            })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data && data.success) {
                        if (typeof showToast === 'function') showToast('创建成功', 'success');
                        doRefreshFileList();
                    } else {
                        if (typeof showToast === 'function') showToast((data && (data.message || (data.error && data.error.message))) || '创建失败', 'error');
                    }
                })
                .catch(function() { if (typeof showToast === 'function') showToast('创建失败', 'error'); });
        }
    );
}

function createMenuNewFile() {
    closeCreateMenuDrawer();
    showPromptDrawer(
        typeof I18n !== 'undefined' ? I18n.t('create_menu.new_file') : 'New File',
        typeof I18n !== 'undefined' ? I18n.t('create_menu.file_name_placeholder') : 'Enter file name',
        typeof I18n !== 'undefined' ? I18n.t('create_menu.file_example') : 'e.g., README.md',
        'new_file.txt',
        typeof I18n !== 'undefined' ? I18n.t('common.create') : 'Create',
        function(name) {
            var pickedName = (typeof name === 'string') ? name.trim() : '';
            if (!pickedName) {
                if (typeof showToast === 'function') showToast('名称不能为空', 'warning');
                return;
            }
            var currentPathEl = document.getElementById('currentBrowsePath');
            var currentPath = currentPathEl ? String(currentPathEl.value || '') : '';
            var url = currentPath ? '/touch/' + encodeURIComponent(currentPath) : '/touch';
            fetch(url, {
                method: 'POST',
                headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function') ? authHeaders() : {}),
                body: JSON.stringify({ name: pickedName })
            })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data && data.success) {
                        if (typeof showToast === 'function') showToast('创建成功', 'success');
                        doRefreshFileList();
                    } else {
                        if (typeof showToast === 'function') showToast((data && (data.message || (data.error && data.error.message))) || '创建失败', 'error');
                    }
                })
                .catch(function() { if (typeof showToast === 'function') showToast('创建失败', 'error'); });
        }
    );
}

// 网页快捷方式创建
function createMenuNewUrl() {
    closeCreateMenuDrawer();
    document.getElementById('urlShortcutName').value = '';
    document.getElementById('urlShortcutUrl').value = '';
    document.getElementById('urlShortcutDrawer').classList.add('open');
    document.getElementById('urlShortcutBackdrop').classList.add('open');
    setTimeout(function() { document.getElementById('urlShortcutName').focus(); }, 100);
}

function closeUrlShortcutDrawer() {
    document.getElementById('urlShortcutDrawer').classList.remove('open');
    document.getElementById('urlShortcutBackdrop').classList.remove('open');
}

function confirmUrlShortcut() {
    var name = document.getElementById('urlShortcutName').value.trim();
    var url = document.getElementById('urlShortcutUrl').value.trim();
    
    if (!name) {
        if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('validation.name_required') : 'Name is required', 'warning');
        return;
    }
    if (!url) {
        if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('validation.url_required') : 'Please enter URL', 'warning');
        return;
    }
    
    // 添加 .url 后缀
    var filename = name.endsWith('.url') ? name : name + '.url';
    var content = '[InternetShortcut]\nURL=' + url;
    
    var currentPathEl = document.getElementById('currentBrowsePath');
    var currentPath = currentPathEl ? String(currentPathEl.value || '') : '';
    var filePath = currentPath ? currentPath + '/' + filename : filename;
    
    fetch('/api/file/write', {
        method: 'POST',
        headers: Object.assign({ 'Content-Type': 'application/json' }, (typeof authHeaders === 'function') ? authHeaders() : {}),
        body: JSON.stringify({ path: filePath, content: content })
    })
        .then(function(r) { return r.json(); })
        .then(function(data) {
                if (data && data.success) {
                    if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('common.create_success') : 'Create successful', 'success');
                    doRefreshFileList();
                    closeUrlShortcutDrawer();
                } else {
                    if (typeof showToast === 'function') showToast((data && (data.message || (data.error && data.error.message))) || (typeof I18n !== 'undefined' ? I18n.t('common.create_failed') : 'Create failed'), 'error');
                }
            })
            .catch(function() { if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('common.create_failed') : 'Create failed', 'error'); });
}

// 使用 SmallMenu 显示文件操作菜单
function showFileSmallMenu(path, name, isDir, triggerElement) {
    var menuId = 'file-menu';
    var menuItems = [];
    var currentPath = path;
    var currentName = name;

    // 保存当前文件信息到全局，供 handleMenuAction 使用
    window.currentItemPath = currentPath;
    window.currentItemName = currentName;
    window.currentItemIsDir = isDir;

    // 通用操作 - 传递 path 参数
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.details') : 'Details', icon: '📋', action: function() { handleMenuAction('details'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.edit') : 'Edit', icon: '✏️', action: function() { handleMenuAction('edit'); }, actionParams: currentPath });
    
    // 置顶操作
    var isPinned = __isPinnedName(name);
    menuItems.push({ 
        label: isPinned ? '取消置顶' : '置顶', 
        icon: isPinned ? '📌' : '📍', 
        action: function() { handleMenuAction(isPinned ? 'unpin' : 'pin'); }, 
        actionParams: currentPath 
    });

    if (!isDir) {
        menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.download') : 'Download', icon: '📥', action: function() { handleMenuAction('download'); }, actionParams: currentPath });
    }

    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.copy_url') : 'Copy download URL', icon: '📋', action: function() { handleMenuAction('copyUrl'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.copy_path') : 'Copy absolute path', icon: '📋', action: function() { handleMenuAction('copyPath'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.rename') : 'Rename', icon: '✏️', action: function() { handleMenuAction('rename'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.cut') : 'Cut', icon: '✂️', action: function() { handleMenuAction('cut'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.copy') : 'Copy', icon: '📋', action: function() { handleMenuAction('copy'); }, actionParams: currentPath });

    // 解压/压缩
    if (!isDir && isArchiveName(name)) {
        menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.extract') : 'Extract', icon: '📦', action: function() { handleMenuAction('extract'); }, actionParams: currentPath });
    }
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.compress') : 'Compress', icon: '🗜️', action: function() { handleMenuAction('newArchive'); }, actionParams: currentPath });

    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.link') : 'Create symlink', icon: '🔗', action: function() { handleMenuAction('link'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.terminal') : 'Open in terminal', icon: '📺', action: function() { handleMenuAction('terminal'); }, actionParams: currentPath });
    menuItems.push({ label: typeof I18n !== 'undefined' ? I18n.t('file_menu.delete') : 'Delete', icon: '🗑️', action: function() { handleMenuAction('delete'); }, actionParams: currentPath, danger: true });

    // 使用 SmallMenu 渲染（居中显示，带遮罩）
    var menuHtml = SmallMenu.render({
        menuId: menuId,
        triggerText: '⋮',
        items: menuItems,
        center: true,
        hideTrigger: true,
        closeOnClickOutside: true
    });

    // 将菜单添加到 body，避免破坏 Vue 的事件监听
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = menuHtml;
    var menuWrapper = tempDiv.firstChild;
    document.body.appendChild(menuWrapper);

    // 延迟打开菜单
    setTimeout(function() {
        SmallMenu.open(menuId);
    }, 10);
}

// 判断是否为压缩文件
function isArchiveName(name) {
    if (!name) return false;
    var lower = name.toLowerCase();
    return lower.endsWith('.zip') || lower.endsWith('.rar') || lower.endsWith('.7z') ||
           lower.endsWith('.tar') || lower.endsWith('.gz') || lower.endsWith('.tar.gz') ||
           lower.endsWith('.tar.bz2') || lower.endsWith('.tar.xz');
}

// 导出到 window
window.showFileSmallMenu = showFileSmallMenu;
window.openCurrentFolderMenu = openCurrentFolderMenu;
window.startDrag = startDrag;
window.handleMenuAction = handleMenuAction;
window.confirmDelete = confirmDelete;
window.closeLegacyConfirmModal = closeLegacyConfirmModal;
window.performDelete = performDelete;
window.showRenameModal = showRenameModal;
window.closeRenameModal = closeRenameModal;
window.closeRenameOnBackdrop = closeRenameOnBackdrop;
window.performRename = performRename;
window.showMoveModal = showMoveModal;
window.closeMoveModal = closeMoveModal;
window.performMove = performMove;
window.cloneItem = cloneItem;
window.downloadFile = downloadFile;
window.copyDownloadUrl = copyDownloadUrl;
window.copyFilePath = copyFilePath;
window.shareMenuDownloadUrl = shareMenuDownloadUrl;
window.shareMenuPath = shareMenuPath;
window.editFile = editFile;
window.openEditor = openEditor;
window.addToChat = addToChat;
// refreshFileList 在 globals.js 中定义，这里不覆盖
// window.refreshFileList = doRefreshFileList;
window.openArchiveCreateDialog = openArchiveCreateDialog;
window.closeArchiveProgressDrawer = closeArchiveProgressDrawer;
window.batchArchive = batchArchive;
window.closeDragUploadDrawer = closeDragUploadDrawer;
window.startDragUpload = startDragUpload;
window.closePasteImageDrawer = closePasteImageDrawer;
window.savePasteImage = savePasteImage;
window.openSearchResultFolder = openSearchResultFolder;
window.openSearchResultMenu = openSearchResultMenu;
window.doSearch = doSearch;
window.openTrashDrawer = openTrashDrawer;
window.closeTrashDrawer = closeTrashDrawer;
window.loadTrashList = loadTrashList;
window.restoreTrashItemFromButton = restoreTrashItemFromButton;
window.clearTrash = clearTrash;
window.openCreateMenuDrawer = openCreateMenuDrawer;
window.closeCreateMenuDrawer = closeCreateMenuDrawer;
window.createMenuUpload = createMenuUpload;
window.createMenuNewFolder = createMenuNewFolder;
window.createMenuNewFile = createMenuNewFile;
window.createMenuNewUrl = createMenuNewUrl;
window.closeUrlShortcutDrawer = closeUrlShortcutDrawer;
window.confirmUrlShortcut = confirmUrlShortcut;

// ============ Git状态栏 ============

// 获取当前路径（从URL或面包屑）
function getCurrentPathFromUrl() {
    var pathMatch = window.location.pathname.match(/\/browse\/(.*)/);
    if (pathMatch && pathMatch[1]) {
        return decodeURIComponent(pathMatch[1]);
    }
    return '';
}

// 加载Git状态
function loadGitStatusBar() {
    var bar = document.getElementById('gitStatusBar');
    if (!bar) return;

    var status = window.__INITIAL_GIT_STATUS__ || null;
    if (status && status.is_repo) {
        showGitStatusBar(status);
        return;
    }
    bar.style.display = 'none';
}

function showGitStatusBar(status) {
    var bar = document.getElementById('gitStatusBar');
    if (!bar) return;
    
    // 分支名
    var branchEl = document.getElementById('gitStatusBranch');
    if (branchEl) {
        branchEl.textContent = status.branch || 'unknown';
    }
    
    // 状态指示器
    var indicatorEl = document.getElementById('gitStatusIndicator');
    if (indicatorEl) {
        indicatorEl.textContent = status.has_changes ? '✗' : '●';
        indicatorEl.className = 'git-status-indicator ' + (status.has_changes ? 'dirty' : 'clean');
    }
    
    // 统计信息
    var statsEl = document.getElementById('gitStatusStats');
    if (statsEl && status.has_changes) {
        var stats = [];
        if (status.untracked > 0) stats.push('<span class="git-stat git-stat-untracked">[+' + status.untracked + ']</span>');
        if (status.added > 0) stats.push('<span class="git-stat git-stat-added">[A:' + status.added + ']</span>');
        if (status.modified > 0) stats.push('<span class="git-stat git-stat-modified">[~' + status.modified + ']</span>');
        if (status.deleted > 0) stats.push('<span class="git-stat git-stat-deleted">[-' + status.deleted + ']</span>');
        statsEl.innerHTML = stats.join('');
    } else if (statsEl) {
        statsEl.innerHTML = '<span style="color:#2da44e;">Clean</span>';
    }
    
    // 显示状态栏
    bar.style.display = 'flex';
}

// 从状态栏打开Git管理抽屉
function openGitModalFromBar() {
    if (typeof window.openGitModal === 'function') {
        // 传递当前路径，找到对应的仓库
        var path = getCurrentPathFromUrl();
        var rootDirEl = document.getElementById('rootDir');
        var rootDir = rootDirEl ? rootDirEl.value : '';
        
        // 构建完整路径
        var fullPath = rootDir;
        if (path) {
            // 确保路径格式正确
            fullPath = (rootDir ? rootDir + '/' + path : path).replace(/\/+/g, '/');
        }
        
        // 调用Git管理，传入当前路径
        if (typeof window.loadGitList === 'function') {
            Drawer.open('gitModal');
            window.loadGitList(fullPath);
        }
    }
}

// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
    setTimeout(loadGitStatusBar, 100);
});

// 页面可见性变化时刷新（从其他标签页切回）
document.addEventListener('visibilitychange', function() {
    if (!document.hidden && document.getElementById('gitStatusBar')) {
        loadGitStatusBar();
    }
});



function sendFileByEmail(path, name) {
    openEmailDrawer(function(email) {
        fetch('/api/file/email', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ path: path, name: name, email: email })
        })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            if (d.success) {
                showToast('邮件发送成功', 'success');
            } else {
                showToast('发送失败: ' + (d.error || d.message), 'error');
            }
        })
        .catch(function(e) { showToast('发送失败', 'error'); });
    });
}

window.sendFileByEmail = sendFileByEmail;
// Email input drawer functions
window.openEmailDrawer = function(callback) {
    window.__emailCallback = callback;
    var el = document.getElementById('emailDrawer');
    if (el) el.style.display = 'block';
    var backdrop = document.getElementById('emailBackdrop');
    if (backdrop) backdrop.style.display = 'block';
    var input = document.getElementById('emailInput');
    if (input) {
        input.value = '';
        input.focus();
    }
    // Load history
    fetch('/api/email/history', {credentials: 'same-origin'}).then(function(r) {return r.json();}).then(function(d) {
        var list = document.getElementById('emailHistoryList');
        if (list) {
            var history = d.data || [];
            if (history.length === 0) {
                list.innerHTML = '<div style="color:#8b949e;text-align:center;padding:20px;">暂无历史</div>';
            } else {
                list.innerHTML = history.map(function(email) {
                    return '<div style="display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #eee;"><span>'+email+'</span><span onclick="deleteEmailHistory(\''+email+'\')" style="cursor:pointer;color:#cf222e;padding:4px;">×</span></div>';
                }).join('');
            }
        }
    });
};

window.closeEmailDrawer = function() {
    var el = document.getElementById('emailDrawer');
    if (el) el.style.display = 'none';
    var backdrop = document.getElementById('emailBackdrop');
    if (backdrop) backdrop.style.display = 'none';
};

window.deleteEmailHistory = function(email) {
    fetch('/api/email/history', {
        method: 'DELETE',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({email: email}),
        credentials: 'same-origin'
    }).then(function(r) {return r.json();}).then(function() {
        window.openEmailDrawer(window.__emailCallback);
    });
};

window.confirmEmail = function() {
    var input = document.getElementById('emailInput');
    var email = input.value.trim();
    var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!re.test(email)) {
        SwalAlert('格式错误', '请输入正确的邮箱格式', 'error');
        return;
    }
    fetch('/api/email/history', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({email: email}),
        credentials: 'same-origin'
    });
    window.closeEmailDrawer();
    if (window.__emailCallback) window.__emailCallback(email);
};
// Email drawer functions
window.openEmailDrawer = function(callback) {
    window.__emailCallback = callback;
    document.getElementById('emailDrawer').style.display = 'block';
    document.getElementById('emailBackdrop').style.display = 'block';
    // Load history
    fetch('/api/email/history', {credentials: 'same-origin'})
    .then(function(r) {return r.json();})
    .then(function(d) {
        var list = document.getElementById('emailHistoryList');
        var history = d.data || [];
        if (history.length === 0) {
            list.innerHTML = '<div style="color:#8b949e;text-align:center;padding:20px;">暂无历史</div>';
        } else {
            list.innerHTML = history.map(function(email) {
                return '<div style="display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #eee;">' +
                    '<span>' + email + '</span>' +
                    '<span onclick="deleteEmailHistory(\'' + email + '\')" style="cursor:pointer;color:#cf222e;padding:4px;">×</span></div>';
            }).join('');
        }
    });
};

window.closeEmailDrawer = function() {
    document.getElementById('emailDrawer').style.display = 'none';
    document.getElementById('emailBackdrop').style.display = 'none';
};

window.deleteEmailHistory = function(email) {
    fetch('/api/email/history', {
        method: 'DELETE',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({email: email}),
        credentials: 'same-origin'
    }).then(function(r) {return r.json();}).then(function() {
        window.openEmailDrawer(window.__emailCallback);
    });
};

window.confirmEmail = function() {
    var input = document.getElementById('emailInput');
    var email = input.value.trim();
    var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!re.test(email)) {
        SwalAlert('格式错误', '请输入正确的邮箱格式', 'error');
        return;
    }
    // Save to history
    fetch('/api/email/history', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({email: email}),
        credentials: 'same-origin'
    });
    window.closeEmailDrawer();
    if (window.__emailCallback) window.__emailCallback(email);
};
