(() => {
    const API_BASE = '/api/gui/files';
    const EDITOR_PAGE = '/file-manager/editor';

    const state = {
        currentPath: '',
        items: [],
        selected: new Set(),
        lastSelectedIndex: null,
        clipboard: null, // {mode: 'copy'|'cut', items: []}
        treeCache: new Map(),
        treeExpanded: new Set(['']),
        isDraggingSelection: false,
        dragStart: null,
        selectionRect: null,
        selectionJustFinished: false,
        selectionDisabled: false,
    };

    const icons = {
        directory: '📁',
        default: '📄',
        editable: '📝',
        code: '💻',
        markdown: '🧾',
        image: '🖼️',
        archive: '🗃️',
    };

    const fileGrid = document.getElementById('fileGrid');
    const directoryTree = document.getElementById('directoryTree');
    const breadcrumbEl = document.getElementById('breadcrumb');
    const selectionInfo = document.getElementById('selectionInfo');
    const statusBar = document.getElementById('statusBar');
    const contextMenu = document.getElementById('contextMenu');
    const dialogBackdrop = document.getElementById('dialogBackdrop');
    const dialogTitle = document.getElementById('dialogTitle');
    const dialogContent = document.getElementById('dialogContent');
    const dialogCancel = document.getElementById('dialogCancel');
    const dialogConfirm = document.getElementById('dialogConfirm');
    const hiddenUploader = document.getElementById('hiddenUploader');
    const pasteBtn = document.getElementById('btnPaste');

    const newFolderBtn = document.getElementById('btnNewFolder');
    const newFileBtn = document.getElementById('btnNewFile');
    const refreshBtn = document.getElementById('btnRefresh');
    const uploadBtn = document.getElementById('btnUpload');
    const backBtn = document.getElementById('btnBack');
    const returnChatBtn = document.getElementById('btnReturnChat');
    const downloadBtn = document.getElementById('btnDownload');
    const renameBtn = document.getElementById('btnRename');
    const copyBtn = document.getElementById('btnCopy');
    const cutBtn = document.getElementById('btnCut');
    const deleteBtn = document.getElementById('btnDelete');
    const toggleSelectionBtn = document.getElementById('btnToggleSelection');

    const clamp = (value, min, max) => Math.max(min, Math.min(value, max));
    const urlParams = new URLSearchParams(window.location.search);
    const initialPathParam = (urlParams.get('path') || '').replace(/^\//, '').replace(/\/$/, '');

    dialogBackdrop.hidden = true;

    let dialogHandlers = { confirm: null, cancel: null };

    function clearDialogHandlers() {
        dialogHandlers.confirm = null;
        dialogHandlers.cancel = null;
    }

    function registerDialogHandlers(confirmHandler, cancelHandler) {
        dialogHandlers.confirm = confirmHandler || null;
        dialogHandlers.cancel = cancelHandler || null;
    }

    function closeDialog() {
        dialogBackdrop.hidden = true;
        clearDialogHandlers();
    }

    dialogCancel.addEventListener('click', () => {
        if (dialogHandlers.cancel) {
            const handler = dialogHandlers.cancel;
            clearDialogHandlers();
            handler();
        } else {
            closeDialog();
        }
    });

    dialogConfirm.addEventListener('click', () => {
        if (dialogHandlers.confirm) {
            const handler = dialogHandlers.confirm;
            clearDialogHandlers();
            handler();
        } else {
            closeDialog();
        }
    });

    dialogBackdrop.addEventListener('click', (event) => {
        if (event.target === dialogBackdrop) {
            if (dialogHandlers.cancel) {
                const handler = dialogHandlers.cancel;
                clearDialogHandlers();
                handler();
            } else {
                closeDialog();
            }
        }
    });

    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape' && !dialogBackdrop.hidden) {
            if (dialogHandlers.cancel) {
                const handler = dialogHandlers.cancel;
                clearDialogHandlers();
                handler();
            } else {
                closeDialog();
            }
        }
    });
    closeDialog();

    function showStatus(message) {
        statusBar.textContent = message;
    }

    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 formatTime(ts) {
        const d = new Date(ts * 1000);
        return d.toLocaleString();
    }

    function joinPath(base, name) {
        if (!base) return name;
        return `${base.replace(/\/$/, '')}/${name}`;
    }

    function getIcon(entry) {
        if (entry.type === 'directory') return icons.directory;
        if (entry.is_editable) return icons.editable;
        const ext = entry.extension || '';
        if (['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp'].includes(ext)) {
            return icons.image;
        }
        if (['.zip', '.rar', '.7z', '.tar', '.gz'].includes(ext)) {
            return icons.archive;
        }
        if (['.js', '.ts', '.py', '.rb', '.php', '.java', '.kt', '.go', '.rs', '.c', '.cpp', '.h', '.hpp'].includes(ext)) {
            return icons.code;
        }
        if (['.md', '.markdown'].includes(ext)) {
            return icons.markdown;
        }
        return icons.default;
    }

    async function request(url, options = {}) {
        const response = await fetch(url, options);
        const data = await response.json().catch(() => ({}));
        if (!response.ok || data.success === false) {
            const message = data.error || data.message || `请求失败 (${response.status})`;
            throw new Error(message);
        }
        return data;
    }

    function updateUrl(path) {
        const url = new URL(window.location.href);
        if (path) {
            url.searchParams.set('path', path);
        } else {
            url.searchParams.delete('path');
        }
        window.history.replaceState({}, '', url.pathname + url.search);
    }

    async function ensureAncestors(path) {
        await ensureTreeNode('', false);
        state.treeExpanded.add('');
        if (!path) {
            renderTree();
            return;
        }
        const segments = path.split('/').filter(Boolean);
        let current = '';
        for (let index = 0; index < segments.length; index += 1) {
            const segment = segments[index];
            current = current ? `${current}/${segment}` : segment;
            if (index < segments.length - 1) {
                state.treeExpanded.add(current);
            }
            await ensureTreeNode(current, false);
        }
        renderTree();
    }

    async function loadDirectory(path = '', { updateHistory = true } = {}) {
        hideContextMenu();
        showStatus('加载中...');
        try {
            const result = await request(`${API_BASE}/entries?path=${encodeURIComponent(path)}`);
            const resolvedPath = result.data.path || '';
            state.currentPath = resolvedPath;
            state.items = result.data.items || [];
            const directoryEntries = state.items.filter((item) => item.type === 'directory');
            state.treeCache.set(resolvedPath, directoryEntries);
            if (updateHistory) {
                updateUrl(resolvedPath);
            }
            await ensureAncestors(resolvedPath);
            renderBreadcrumb(result.data.breadcrumb || []);
            renderGrid();
            updateSelection([]);
            state.lastSelectedIndex = null;
            showStatus(`已加载 ${state.items.length} 项`);
        } catch (err) {
            showStatus(err.message);
        }
    }

    function renderBreadcrumb(crumbs) {
        breadcrumbEl.innerHTML = '';
        crumbs.forEach((crumb, index) => {
            const span = document.createElement('span');
            span.textContent = crumb.name;
            span.dataset.path = crumb.path;
            span.addEventListener('click', () => {
                loadDirectory(crumb.path);
            });
            breadcrumbEl.appendChild(span);
            if (index < crumbs.length - 1) {
                const sep = document.createElement('span');
                sep.textContent = '›';
                sep.classList.add('fm-breadcrumb-sep');
                breadcrumbEl.appendChild(sep);
            }
        });
    }

    function renderGrid() {
        fileGrid.innerHTML = '';
        state.items.forEach((entry, index) => {
            const card = document.createElement('div');
            card.className = 'fm-card';
            card.tabIndex = 0;
            card.dataset.path = entry.path;
            card.dataset.index = index;
            if (state.selected.has(entry.path)) {
                card.classList.add('selected');
            }

            const icon = document.createElement('div');
            icon.className = 'fm-card-icon';
            icon.textContent = getIcon(entry);

            const name = document.createElement('div');
            name.className = 'fm-card-name';
            name.textContent = entry.name;

            const meta = document.createElement('div');
            meta.className = 'fm-card-meta';
            const lines = [];
            if (entry.type === 'file') {
                lines.push(formatSize(entry.size));
            } else {
                lines.push('目录');
            }
            lines.push(formatTime(entry.modified_at));
            meta.innerHTML = lines.join('<br>');

            card.appendChild(icon);
            card.appendChild(name);
            card.appendChild(meta);

            card.addEventListener('click', (event) => handleItemClick(event, entry, index));
            card.addEventListener('dblclick', () => handleItemDoubleClick(entry));
            card.addEventListener('contextmenu', (event) => handleItemContextMenu(event, entry));

            fileGrid.appendChild(card);
        });

        const overlay = document.createElement('div');
        overlay.className = 'fm-drop-overlay';
        overlay.textContent = '释放即可上传到此目录';
        fileGrid.appendChild(overlay);
    }

    function updateSelection(paths, options = { append: false, range: false }) {
        if (!options.append && !options.range) {
            state.selected.clear();
            if (paths.length === 0) {
                state.lastSelectedIndex = null;
            }
        }
        paths.forEach((path) => {
            if (state.selected.has(path) && options.append) {
                state.selected.delete(path);
            } else {
                state.selected.add(path);
            }
        });
        syncSelectionUI();
    }

    function syncSelectionUI() {
        const cards = fileGrid.querySelectorAll('.fm-card');
        cards.forEach((card) => {
            if (state.selected.has(card.dataset.path)) {
                card.classList.add('selected');
            } else {
                card.classList.remove('selected');
            }
        });
        selectionInfo.textContent = `已选中 ${state.selected.size} 项`;
        pasteBtn.disabled = !state.clipboard || !state.clipboard.items.length;
    }

    function handleItemClick(event, entry, index) {
        const isMetaKey = event.metaKey || event.ctrlKey;
        const isShiftKey = event.shiftKey;
        if (isShiftKey && state.lastSelectedIndex !== null) {
            const start = Math.min(state.lastSelectedIndex, index);
            const end = Math.max(state.lastSelectedIndex, index);
            const paths = state.items.slice(start, end + 1).map((item) => item.path);
            updateSelection(paths, { range: true });
        } else if (isMetaKey) {
            updateSelection([entry.path], { append: true });
            state.lastSelectedIndex = index;
        } else {
            updateSelection([entry.path], { append: false });
            state.lastSelectedIndex = index;
        }
    }

    function handleItemDoubleClick(entry) {
        if (entry.type === 'directory') {
            loadDirectory(entry.path);
            return;
        }
        if (entry.is_editable) {
            window.location.href = `${EDITOR_PAGE}?path=${encodeURIComponent(entry.path)}`;
            return;
        }
        window.open(`${API_BASE}/download?path=${encodeURIComponent(entry.path)}`, '_blank');
    }

    function handleItemContextMenu(event, entry) {
        event.preventDefault();
        if (!state.selected.has(entry.path)) {
            updateSelection([entry.path], { append: false });
        }
        showContextMenu(event.clientX, event.clientY);
    }

    function showContextMenu(x, y) {
        const single = state.selected.size === 1;
        const singleEntry = single ? getSingleSelected() : null;
        contextMenu.innerHTML = '';

        const entries = [];

        if (singleEntry) {
            if (singleEntry.type === 'directory') {
                entries.push({ label: '打开', action: openSelected, disabled: false });
            } else if (singleEntry.is_editable) {
                entries.push({ label: '在编辑器中打开', action: openEditor, disabled: false });
                if (singleEntry.extension === '.html' || singleEntry.extension === '.htm') {
                    entries.push({ label: '预览', action: previewSelected, disabled: false });
                }
                entries.push({ label: '下载', action: downloadSelected, disabled: false });
            } else {
                const isHtml = singleEntry.extension === '.html' || singleEntry.extension === '.htm';
                if (isHtml) {
                    entries.push({ label: '预览', action: previewSelected, disabled: false });
                }
                entries.push({ label: '下载', action: downloadSelected, disabled: false });
            }
        } else if (state.selected.size > 0) {
            entries.push({ label: '下载', action: downloadSelected, disabled: false });
        }

        entries.push(
            { label: '重命名', action: renameSelected, disabled: !single },
            { label: '复制', action: copySelected, disabled: state.selected.size === 0 },
            { label: '剪切', action: cutSelected, disabled: state.selected.size === 0 },
            { label: '粘贴', action: pasteClipboard, disabled: !state.clipboard || !state.clipboard.items.length },
            { label: '删除', action: deleteSelected, disabled: state.selected.size === 0 },
        );

        entries.forEach((item) => {
            const btn = document.createElement('button');
            btn.textContent = item.label;
            btn.disabled = item.disabled;
            btn.addEventListener('click', () => {
                hideContextMenu();
                item.action();
            });
            contextMenu.appendChild(btn);
        });
        contextMenu.style.display = 'block';
        const { innerWidth, innerHeight } = window;
        const menuRect = contextMenu.getBoundingClientRect();
        const left = clamp(x, 0, innerWidth - menuRect.width);
        const top = clamp(y, 0, innerHeight - menuRect.height);
        contextMenu.style.left = `${left}px`;
        contextMenu.style.top = `${top}px`;
    }

    function hideContextMenu() {
        contextMenu.style.display = 'none';
    }

    function getSingleSelected() {
        if (state.selected.size !== 1) return null;
        const path = Array.from(state.selected)[0];
        return state.items.find((item) => item.path === path) || null;
    }

    function openSelected() {
        const entry = getSingleSelected();
        if (!entry) return;
        handleItemDoubleClick(entry);
    }

    function previewSelected() {
        const entry = getSingleSelected();
        if (!entry) return;
        if (entry.extension !== '.html' && entry.extension !== '.htm') {
            showStatus('仅支持预览 HTML 文件');
            return;
        }
        window.open(`/file-preview/${encodeURIComponent(entry.path)}`, '_blank');
    }

    function openEditor() {
        const entry = getSingleSelected();
        if (!entry || !entry.is_editable) return;
        window.location.href = `${EDITOR_PAGE}?path=${encodeURIComponent(entry.path)}`;
    }

    function triggerBlobDownload(filename, blob) {
        const url = URL.createObjectURL(blob);
        const anchor = document.createElement('a');
        anchor.href = url;
        anchor.download = filename;
        document.body.appendChild(anchor);
        anchor.click();
        anchor.remove();
        URL.revokeObjectURL(url);
    }

    async function downloadSelected() {
        if (!state.selected.size) return;
        if (state.selected.size === 1) {
            const path = Array.from(state.selected)[0];
            window.open(`${API_BASE}/download?path=${encodeURIComponent(path)}`, '_blank');
            return;
        }
        try {
            const resp = await fetch(`${API_BASE}/download/batch`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ paths: Array.from(state.selected) })
            });
            if (!resp.ok) {
                let message = '批量下载失败';
                try {
                    const data = await resp.json();
                    message = data.error || data.message || message;
                } catch (_) {
                    // ignore
                }
                throw new Error(message);
            }
            const blob = await resp.blob();
            triggerBlobDownload(`selected_${Date.now()}.zip`, blob);
            showStatus(`已开始下载 ${state.selected.size} 个项目`);
        } catch (err) {
            showStatus(err.message);
        }
    }

    async function renameSelected() {
        const entry = getSingleSelected();
        if (!entry) return;
        const newName = await promptDialog('重命名', entry.name);
        if (!newName) return;
        try {
            await request(`${API_BASE}/rename`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ path: entry.path, new_name: newName }),
            });
            await loadDirectory(state.currentPath);
        } catch (err) {
            showStatus(err.message);
        }
    }

    function copySelected() {
        if (!state.selected.size) return;
        state.clipboard = { mode: 'copy', items: Array.from(state.selected) };
        showStatus(`已复制 ${state.clipboard.items.length} 项`);
        syncSelectionUI();
    }

    function cutSelected() {
        if (!state.selected.size) return;
        state.clipboard = { mode: 'cut', items: Array.from(state.selected) };
        showStatus(`已剪切 ${state.clipboard.items.length} 项`);
        syncSelectionUI();
    }

    async function pasteClipboard() {
        if (!state.clipboard || !state.clipboard.items.length) return;
        const endpoint = state.clipboard.mode === 'copy' ? 'copy' : 'move';
        try {
            await request(`${API_BASE}/${endpoint}`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    paths: state.clipboard.items,
                    target_dir: state.currentPath,
                }),
            });
            if (state.clipboard.mode === 'cut') {
                state.clipboard = null;
            }
            await loadDirectory(state.currentPath);
        } catch (err) {
            showStatus(err.message);
        }
    }

    async function deleteSelected() {
        if (!state.selected.size) return;
        const confirm = await confirmDialog(`确认删除选中的 ${state.selected.size} 项吗？该操作不可撤销。`);
        if (!confirm) return;
        try {
            await request(`${API_BASE}/delete`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ paths: Array.from(state.selected) }),
            });
            await loadDirectory(state.currentPath);
        } catch (err) {
            showStatus(err.message);
        }
    }

    function promptDialog(title, defaultValue = '') {
        return new Promise((resolve) => {
            dialogTitle.textContent = title;
            dialogContent.innerHTML = '';
            const input = document.createElement('input');
            input.value = defaultValue;
            input.autofocus = true;
            dialogContent.appendChild(input);
            const finish = (value) => {
                closeDialog();
                resolve(value);
            };
            registerDialogHandlers(() => finish(input.value.trim()), () => finish(null));
            dialogBackdrop.hidden = false;
            input.addEventListener('keydown', (evt) => {
                if (evt.key === 'Enter') {
                    evt.preventDefault();
                    finish(input.value.trim());
                } else if (evt.key === 'Escape') {
                    evt.preventDefault();
                    finish(null);
                }
            });
            setTimeout(() => input.select(), 50);
        });
    }

    function confirmDialog(message) {
        return new Promise((resolve) => {
            dialogTitle.textContent = '确认操作';
            dialogContent.innerHTML = `<p>${message}</p>`;
            const finish = (value) => {
                closeDialog();
                resolve(value);
            };
            registerDialogHandlers(() => finish(true), () => finish(false));
            dialogBackdrop.hidden = false;
        });
    }

    function handleGlobalClick(event) {
        if (!contextMenu.contains(event.target)) {
            hideContextMenu();
        }
    }

    function handleGridBackgroundClick(event) {
        if (event.target === fileGrid) {
            if (state.selectionJustFinished) {
                return;
            }
            updateSelection([]);
        }
    }

    function handleDragEnter(event) {
        event.preventDefault();
        fileGrid.classList.add('drop-target');
    }

    function handleDragOver(event) {
        event.preventDefault();
    }

    function handleDragLeave(event) {
        if (event.target === fileGrid) {
            fileGrid.classList.remove('drop-target');
        }
    }

    async function handleDrop(event) {
        event.preventDefault();
        fileGrid.classList.remove('drop-target');
        if (!event.dataTransfer || !event.dataTransfer.files.length) return;
        const files = event.dataTransfer.files;
        await uploadFiles(files, state.currentPath);
    }

    async function uploadFiles(fileList, targetPath) {
        for (const file of fileList) {
            const form = new FormData();
            form.append('file', file, file.name);
            form.append('filename', file.name);
            form.append('path', targetPath);
            try {
                await request(`${API_BASE}/upload`, {
                    method: 'POST',
                    body: form,
                });
                showStatus(`已上传 ${file.name}`);
            } catch (err) {
                showStatus(`上传失败：${err.message}`);
            }
        }
        await loadDirectory(state.currentPath);
    }

    function initSelectionRectangle() {
        fileGrid.addEventListener('pointerdown', (event) => {
            if (state.selectionDisabled) return;
            if (event.target !== fileGrid) return;
            state.isDraggingSelection = true;
            state.dragStart = { x: event.clientX, y: event.clientY };
            state.selectionRect = document.createElement('div');
            state.selectionRect.className = 'fm-selection-rect';
            fileGrid.appendChild(state.selectionRect);
            updateSelection([]);
            state.selectionJustFinished = false;
            fileGrid.setPointerCapture(event.pointerId);
        });

        fileGrid.addEventListener('pointermove', (event) => {
            if (!state.isDraggingSelection || !state.selectionRect) return;
            const rect = fileGrid.getBoundingClientRect();
            const current = { x: event.clientX, y: event.clientY };
            const x = Math.min(state.dragStart.x, current.x) - rect.left + fileGrid.scrollLeft;
            const y = Math.min(state.dragStart.y, current.y) - rect.top + fileGrid.scrollTop;
            const width = Math.abs(state.dragStart.x - current.x);
            const height = Math.abs(state.dragStart.y - current.y);
            Object.assign(state.selectionRect.style, {
                left: `${x}px`,
                top: `${y}px`,
                width: `${width}px`,
                height: `${height}px`,
            });

            const selectionBox = {
                left: Math.min(state.dragStart.x, current.x),
                right: Math.max(state.dragStart.x, current.x),
                top: Math.min(state.dragStart.y, current.y),
                bottom: Math.max(state.dragStart.y, current.y),
            };

            const selected = [];
            const cards = fileGrid.querySelectorAll('.fm-card');
            cards.forEach((card) => {
                const bounds = card.getBoundingClientRect();
                const intersects = !(selectionBox.right < bounds.left ||
                    selectionBox.left > bounds.right ||
                    selectionBox.bottom < bounds.top ||
                    selectionBox.top > bounds.bottom);
                if (intersects) {
                    selected.push(card.dataset.path);
                }
            });
            updateSelection(selected);
        });

        fileGrid.addEventListener('pointerup', (event) => {
            if (!state.isDraggingSelection) return;
            state.isDraggingSelection = false;
            if (state.selectionRect) {
                state.selectionRect.remove();
                state.selectionRect = null;
            }
            state.selectionJustFinished = true;
            requestAnimationFrame(() => {
                state.selectionJustFinished = false;
            });
            fileGrid.releasePointerCapture(event.pointerId);
        });
    }

    async function ensureTreeNode(path, shouldRender = true) {
        let changed = false;
        if (!state.treeCache.has(path)) {
            try {
                const result = await request(`${API_BASE}/entries?path=${encodeURIComponent(path)}`);
                const directories = result.data.items.filter((item) => item.type === 'directory');
                state.treeCache.set(path, directories);
                changed = true;
            } catch (err) {
                showStatus(err.message);
            }
        }
        if (shouldRender && changed) {
            renderTree();
        }
        return changed;
    }

    function renderTree() {
        directoryTree.innerHTML = '';
        const rootNode = createTreeNode('', '根目录');
        directoryTree.appendChild(rootNode);
    }

    function createTreeNode(path, name) {
        const li = document.createElement('li');
        const header = document.createElement('div');
        header.className = 'fm-tree-item';
        if (path === state.currentPath) {
            header.classList.add('active');
        }
        const toggle = document.createElement('span');
        toggle.className = 'fm-tree-toggle';
        toggle.textContent = state.treeExpanded.has(path) ? '▾' : '▸';
        toggle.addEventListener('click', async (event) => {
            event.stopPropagation();
            if (state.treeExpanded.has(path)) {
                state.treeExpanded.delete(path);
            } else {
                state.treeExpanded.add(path);
                await ensureTreeNode(path, false);
            }
            renderTree();
        });

        const label = document.createElement('span');
        label.textContent = name;
        label.addEventListener('click', () => loadDirectory(path));

        header.addEventListener('click', () => loadDirectory(path));

        header.appendChild(toggle);
        header.appendChild(label);
        li.appendChild(header);

        if (state.treeExpanded.has(path)) {
            const children = document.createElement('ul');
            children.className = 'fm-tree-children';
            const dirs = state.treeCache.get(path) || [];
            dirs.forEach((dir) => {
                const child = createTreeNode(dir.path, dir.name);
                children.appendChild(child);
            });
            li.appendChild(children);
        }
        return li;
    }

    function bindToolbar() {
        newFolderBtn.addEventListener('click', async () => {
            const name = await promptDialog('新建文件夹', '新建文件夹');
            if (!name) return;
            try {
                await request(`${API_BASE}/create`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        path: state.currentPath,
                        name,
                        type: 'directory',
                    }),
                });
                await loadDirectory(state.currentPath);
            } catch (err) {
                showStatus(err.message);
            }
        });

        newFileBtn.addEventListener('click', async () => {
            const name = await promptDialog('新建文件', '新建文件.txt');
            if (!name) return;
            try {
                await request(`${API_BASE}/create`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        path: state.currentPath,
                        name,
                        type: 'file',
                    }),
                });
                await loadDirectory(state.currentPath);
            } catch (err) {
                showStatus(err.message);
            }
        });

        refreshBtn.addEventListener('click', () => loadDirectory(state.currentPath));
        uploadBtn.addEventListener('click', () => hiddenUploader.click());
        backBtn.addEventListener('click', () => {
            if (!state.currentPath) {
                loadDirectory('');
                return;
            }
            const segments = state.currentPath.split('/').filter(Boolean);
            if (segments.length === 0) {
                loadDirectory('');
                return;
            }
            segments.pop();
            const parentPath = segments.join('/');
            loadDirectory(parentPath);
        });

        returnChatBtn.addEventListener('click', () => {
            window.location.href = '/new';
        });
        downloadBtn.addEventListener('click', downloadSelected);
        renameBtn.addEventListener('click', renameSelected);
        copyBtn.addEventListener('click', copySelected);
        cutBtn.addEventListener('click', cutSelected);
        pasteBtn.addEventListener('click', pasteClipboard);
        deleteBtn.addEventListener('click', deleteSelected);
        toggleSelectionBtn.addEventListener('click', () => {
            state.selectionDisabled = !state.selectionDisabled;
            toggleSelectionBtn.textContent = state.selectionDisabled ? '启用框选' : '禁用框选';
            const msg = state.selectionDisabled ? '已禁用框选' : '已启用框选';
            showStatus(msg);
        });

        hiddenUploader.addEventListener('change', async (event) => {
            const files = event.target.files;
            if (files && files.length) {
                await uploadFiles(files, state.currentPath);
            }
            hiddenUploader.value = '';
        });
    }

    function bindGlobalEvents() {
        document.addEventListener('click', handleGlobalClick);
        fileGrid.addEventListener('click', handleGridBackgroundClick);
        fileGrid.addEventListener('contextmenu', (event) => {
            if (event.target === fileGrid) {
                event.preventDefault();
                if (state.selected.size) {
                    showContextMenu(event.clientX, event.clientY);
                }
            }
        });
        fileGrid.addEventListener('dragenter', handleDragEnter);
        fileGrid.addEventListener('dragover', handleDragOver);
        fileGrid.addEventListener('dragleave', handleDragLeave);
        fileGrid.addEventListener('drop', handleDrop);
        initSelectionRectangle();
    }

    async function bootstrap() {
        bindToolbar();
        bindGlobalEvents();
        await loadDirectory(initialPathParam, { updateHistory: false });
    }

    bootstrap().catch((err) => {
        console.error(err);
        showStatus(err.message);
    });
})();
