// ==UserScript==
// @name         ChatGPT File Uploader + GitHub
// @namespace    https://github.com/Clad3815/chatgpt-file-uploader
// @version      4.0.0
// @updateURL    https://github.com/Clad3815/chatgpt-file-uploader/raw/refs/heads/main/src/chatgpt-upload-files-plugin.user.js
// @downloadURL  https://github.com/Clad3815/chatgpt-file-uploader/raw/refs/heads/main/src/chatgpt-upload-files-plugin.user.js
// @description  Adds true file upload capabilities to ChatGPT with preview, syntax highlighting, and proper file handling. Upload local files/folders or from GitHub using a stepper-based modal flow - features not available in the standard interface.
// @match        https://chatgpt.com/*
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      github.com
// @connect      api.github.com
// @connect      raw.githubusercontent.com
// @require      https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markdown.min.js
// @resource     PRISM_CSS https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css
// ==/UserScript==

(function () {
    'use strict';

    //------------------------------------------------------------------
    // 0) Inject necessary CSS (Prism + jsTree + minimal custom)
    //------------------------------------------------------------------
    const prismCss = GM_getResourceText('PRISM_CSS');
    if (prismCss) GM_addStyle(prismCss);

    const jstreeCss = GM_getResourceText('JSTREE_CSS');
    if (jstreeCss) {
        GM_addStyle(jstreeCss);
    }

    // Minimal required custom CSS (only what can't be done with Tailwind)
    GM_addStyle(`
        /* Minimal spinner animation */
        @keyframes spin { 0% { transform:rotate(0deg); } 100% { transform:rotate(360deg); } }
        .my-spinner { animation: spin 1s linear infinite; }

        /* Custom Tree View */
        .tree-view {
            font-size: 0.875rem;
            user-select: none;
        }
        
        .tree-item {
            display: flex;
            align-items: center;
            padding: 4px 0;
        }
        
        .tree-item:hover {
            background: rgba(0, 0, 0, 0.05);
        }
        
        .tree-indent {
            width: 24px;
            height: 100%;
            display: inline-block;
        }
        
        .tree-toggle {
            width: 20px;
            height: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            color: #666;
        }
        
        .tree-toggle:hover {
            color: #000;
        }
        
        .tree-icon {
            width: 20px;
            height: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 6px;
        }
        
        .tree-checkbox {
            margin-right: 6px;
        }
        
        .tree-label {
            flex: 1;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* Tree View Animations */
        .tree-children {
            overflow: hidden;
            transition: height 0.2s ease-in-out;
        }

        .tree-children.collapsed {
            height: 0 !important;
        }

        .tree-toggle svg {
            transition: transform 0.2s ease-in-out;
        }

        .tree-toggle.expanded svg {
            transform: rotate(90deg);
        }
    `);

    //------------------------------------------------------------------
    // 1) Globals & utility
    //------------------------------------------------------------------
    let uploadedFiles = [];          // current batch of chosen files
    const processedMessages = {};    // to store which messages we've already parsed for <user_attachments>

    function createEl(tag, {
        className = '',
        text = '',
        html = '',
        attrs = {},
        children = [],
        style = ''
    } = {}) {
        const el = document.createElement(tag);
        if (className) el.className = className;
        if (text) el.textContent = text;
        if (html) el.innerHTML = html;
        if (style) el.style = style;
        for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
        for (const c of children) el.appendChild(c);
        return el;
    }

    function formatFileSize(bytes) {
        if (!bytes || typeof bytes !== 'number' || isNaN(bytes)) return '';
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    function formatFileDetails(fileInfo) {
        const parts = [];
        if (fileInfo.modifyTime) parts.push(`Modified on ${fileInfo.modifyTime}`);
        if (fileInfo.size) parts.push(fileInfo.size);
        return parts.join(' • ');
    }

    // Reads a File object from the browser into text
    async function readFileContent(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (e) => {
                resolve({
                    content: e.target.result,
                    modifyTime: new Date(file.lastModified).toLocaleString(),
                    size: formatFileSize(file.size)
                });
            };
            reader.onerror = (err) => reject(err);
            reader.readAsText(file);
        });
    }

    // For syntax highlight with Prism
    function getLanguageFromExtension(ext) {
        const map = { js: 'javascript', py: 'python', html: 'html', css: 'css', json: 'json', md: 'markdown' };
        return map[ext] || null;
    }

    //------------------------------------------------------------------
    // [NEW] 1b) Build ASCII representation of the repo structure
    //------------------------------------------------------------------
    function buildRepoStructureASCII(githubTree) {
        // 1) Convert { path, type='tree'|'blob' }[] into a nested object
        const root = {};

        for (const item of githubTree) {
            const { path } = item;
            const parts = path.split('/');
            let cur = root;
            for (let i = 0; i < parts.length; i++) {
                const segment = parts[i];
                if (!cur[segment]) {
                    cur[segment] = {};
                }
                cur = cur[segment];
            }
        }

        // 2) DFS to build ASCII lines
        function buildLines(node, prefix = '', isLast = true) {
            const keys = Object.keys(node).sort();
            let lines = [];
            keys.forEach((key, idx) => {
                const lastEntry = (idx === keys.length - 1);
                const branchChar = lastEntry ? '└── ' : '├── ';
                const childPrefix = prefix + (isLast ? '    ' : '│   ');
                lines.push(prefix + branchChar + key);

                // Recurse if sub-nodes
                if (Object.keys(node[key]).length > 0) {
                    lines = lines.concat(buildLines(node[key], childPrefix, lastEntry));
                }
            });
            return lines;
        }

        // 3) If you want a single top-level label, you can do so:
        // For example, 'repo/'
        const topKeys = Object.keys(root).sort();
        let resultLines = ['repo/'];
        topKeys.forEach((k, idx) => {
            const isLast = (idx === topKeys.length - 1);
            const branchChar = isLast ? '└── ' : '├── ';
            const childPrefix = '    ';
            resultLines.push(branchChar + k);

            if (Object.keys(root[k]).length > 0) {
                resultLines.push(...buildLines(root[k], childPrefix, isLast));
            }
        });

        return resultLines.join('\n');
    }

    //------------------------------------------------------------------
    // 2) Show file "View content" modal
    //------------------------------------------------------------------
    function showModal(fileName, fileContent, fileInfo) {
        // remove old
        let overlay = document.getElementById('files-modal-overlay');
        if (overlay) overlay.remove();

        overlay = createEl('div', {
            attrs: { id: 'files-modal-overlay' },
            className: `
                fixed inset-0 bg-black bg-opacity-50 z-[9999]
                flex items-center justify-center
                opacity-0 transition-opacity duration-200
            `
        });

        const modal = createEl('div', {
            className: `
                w-[600px] max-w-[90vw] max-h-[80vh]
                bg-white dark:bg-gray-800
                text-gray-900 dark:text-gray-100
                border border-gray-200 dark:border-gray-700
                rounded-xl shadow-lg
                flex flex-col
            `
        });

        // Header
        const header = createEl('div', {
            className: 'px-4 py-3 text-lg font-semibold border-b border-gray-200 dark:border-gray-700'
        });
        const titleBox = createEl('div', { className: 'flex-1' });
        const title = createEl('div', { className: 'font-semibold', text: fileName });
        const details = createEl('div', { className: 'text-xs text-token-text-secondary mt-1', text: formatFileDetails(fileInfo) });
        titleBox.appendChild(title);
        titleBox.appendChild(details);

        const closeBtn = createEl('button', {
            className: `
                text-sm px-3 py-1
                bg-token-main-surface-secondary dark:bg-token-main-surface-primary
                border border-token-border-light dark:border-token-border-dark
                rounded hover:bg-[#f0f0f0] cursor-pointer
            `,
            text: 'Close'
        });
        closeBtn.addEventListener('click', () => overlay.remove());
        header.append(titleBox, closeBtn);

        // Content
        const content = createEl('div', {
            className: 'p-4 flex-1 overflow-y-auto transition-all duration-300'
        });
        const pre = createEl('pre', {
            className: `
                whitespace-pre-wrap break-words
                text-token-text-primary bg-token-main-surface-secondary
                p-2 rounded border border-token-border-light
                overflow-auto
            `
        });
        const ext = fileName.split('.').pop().toLowerCase();
        const lang = getLanguageFromExtension(ext);
        if (lang) {
            pre.innerHTML = Prism.highlight(fileContent, Prism.languages[lang], lang);
        } else {
            pre.textContent = fileContent;
        }
        content.appendChild(pre);

        modal.append(header, content);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        // animate
        requestAnimationFrame(() => {
            overlay.style.opacity = '1';
            modal.style.transform = 'scale(1)';
        });
    }

    //------------------------------------------------------------------
    // 3) Preview panel + add / remove
    //------------------------------------------------------------------
    function removeFile(fileIndex) {
        uploadedFiles.splice(fileIndex, 1);
        updatePreview();
    }

    function updatePreview() {
        const ta = document.querySelector('#prompt-textarea');
        if (!ta) return;

        let preview = document.getElementById('files-preview');
        if (!preview) {
            const parent = ta.closest('.relative.flex');
            if (!parent) return;
            preview = createEl('div', {
                attrs: { id: 'files-preview' },
                className: `
                    mt-3 p-4 rounded-xl
                    border border-token-border-light dark:border-token-border-dark
                    bg-token-main-surface-primary dark:bg-token-main-surface-secondary
                    text-token-text-primary
                    max-h-[400px] overflow-y-auto
                `
            });
            parent.parentNode.insertBefore(preview, parent.nextSibling);
        }
        preview.innerHTML = '';

        if (uploadedFiles.length === 0) {
            preview.remove();
            return;
        }

        // Header avec compteur et bouton toggle
        const header = createEl('div', {
            className: 'flex items-center justify-between mb-3 pb-2 border-b border-token-border-light'
        });

        const headerLeft = createEl('div', {
            className: 'text-sm font-medium flex items-center gap-2',
            html: `
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
                    <polyline points="13 2 13 9 20 9"/>
                </svg>
                Files attached (${uploadedFiles.length})
            `
        });

        const toggleBtn = createEl('button', {
            className: 'text-sm text-token-text-secondary hover:text-token-text-primary transition-colors',
            html: `
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
                     stroke="currentColor" stroke-width="2" class="transform transition-transform">
                    <path d="M19 9l-7 7-7-7"/>
                </svg>
            `
        });

        header.append(headerLeft, toggleBtn);
        preview.appendChild(header);

        // Container pour la liste des fichiers
        const filesContainer = createEl('div', {
            className: `
                grid gap-2 grid-cols-1
                overflow-hidden transition-all duration-300
            `
        });
        preview.appendChild(filesContainer);

        // État replié/déplié
        let isExpanded = true;
        toggleBtn.addEventListener('click', () => {
            isExpanded = !isExpanded;
            filesContainer.style.maxHeight = isExpanded ? '500px' : '0px';
            toggleBtn.querySelector('svg').style.transform = isExpanded ? 'rotate(0deg)' : 'rotate(-90deg)';
        });

        // Grouper les fichiers par dossier
        const filesByFolder = {};
        uploadedFiles.forEach((fileObj, idx) => {
            const parts = fileObj.name.split('/');
            const folder = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
            if (!filesByFolder[folder]) filesByFolder[folder] = [];
            filesByFolder[folder].push({ ...fileObj, index: idx });
        });

        // Créer les groupes de fichiers
        Object.entries(filesByFolder).forEach(([folder, files]) => {
            if (folder) {
                // Créer un groupe pour les fichiers dans des dossiers
                const folderGroup = createEl('div', {
                    className: 'border border-token-border-light rounded-lg mb-2'
                });

                const folderHeader = createEl('div', {
                    className: `
                        flex items-center justify-between p-2
                        bg-token-main-surface-secondary cursor-pointer
                        hover:bg-opacity-70 rounded-t-lg
                    `,
                    html: `
                        <div class="flex items-center gap-2">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
                                 stroke="currentColor" stroke-width="2" class="transform transition-transform">
                                <path d="M19 9l-7 7-7-7"/>
                            </svg>
                            <span class="font-medium text-sm">${folder} (${files.length})</span>
                        </div>
                    `
                });

                const folderContent = createEl('div', {
                    className: 'p-2 border-t border-token-border-light'
                });

                let isFolderExpanded = true;
                folderHeader.addEventListener('click', () => {
                    isFolderExpanded = !isFolderExpanded;
                    folderContent.style.display = isFolderExpanded ? 'block' : 'none';
                    folderHeader.querySelector('svg').style.transform =
                        isFolderExpanded ? 'rotate(0deg)' : 'rotate(-90deg)';
                });

                files.forEach(fileObj => {
                    folderContent.appendChild(createFileBlock(fileObj, true)); // true pour afficher le bouton delete
                });

                folderGroup.append(folderHeader, folderContent);
                filesContainer.appendChild(folderGroup);
            } else {
                // Fichiers à la racine
                files.forEach(fileObj => {
                    filesContainer.appendChild(createFileBlock(fileObj, true)); // true pour afficher le bouton delete
                });
            }
        });
    }

    // Nouvelle fonction pour créer un bloc de fichier
    function createFileBlock(fileObj, showDelete = false) {
        const block = createEl('div', {
            className: `
                relative bg-token-main-surface-secondary
                rounded-lg p-3 group
                hover:bg-opacity-70 transition-all
            `
        });

        const row = createEl('div', { className: 'flex items-start gap-3' });

        const icon = createEl('div', {
            className: `
                flex-shrink-0 w-8 h-8
                flex items-center justify-center
                rounded-lg bg-token-main-surface-primary
            `,
            html: `
                <svg width="16" height="16" viewBox="0 0 24 24"
                     fill="none" stroke="currentColor" stroke-width="2"
                     class="text-token-text-secondary">
                    <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
                    <polyline points="13 2 13 9 20 9"/>
                </svg>
            `
        });

        const infoBox = createEl('div', { className: 'flex-1 min-w-0' });
        const fName = createEl('div', {
            className: 'font-medium text-sm truncate',
            text: fileObj.name.split('/').pop()
        });
        const fDetails = createEl('div', {
            className: 'text-xs text-token-text-secondary mt-0.5',
            text: fileObj.size
        });

        // Actions
        const actions = createEl('div', {
            className: `
                absolute top-2 right-2
                flex gap-1
                opacity-0 group-hover:opacity-100
                transition-opacity duration-200
            `
        });

        const viewBtn = createEl('button', {
            className: `
                p-1.5 rounded-md
                hover:bg-token-main-surface-primary
                text-token-text-secondary hover:text-token-text-primary
            `,
            html: `
                <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
                    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
                    <circle cx="12" cy="12" r="3"/>
                </svg>
            `
        });
        viewBtn.addEventListener('click', (ev) => {
            ev.stopPropagation();
            showModal(fileObj.name, fileObj.content, fileObj);
        });

        const delBtn = createEl('button', {
            className: `
                p-1.5 rounded-md
                hover:bg-red-100 dark:hover:bg-red-900/30
                text-token-text-secondary hover:text-red-500
            `,
            html: `
                <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
                    <path d="M18 6L6 18M6 6l12 12"/>
                </svg>
            `,
            attrs: {
                title: 'Remove file'
            }
        });
        delBtn.addEventListener('click', (ev) => {
            ev.stopPropagation();
            removeFile(fileObj.index);
        });

        if (showDelete) {
            actions.append(viewBtn, delBtn);
        } else {
            actions.append(viewBtn);
        }

        infoBox.append(fName, fDetails);
        row.append(icon, infoBox);
        block.append(row, actions);
        block.addEventListener('click', () => showModal(fileObj.name, fileObj.content, fileObj));

        return block;
    }

    //------------------------------------------------------------------
    // 4) Insert attachments on send
    //------------------------------------------------------------------
    async function insertFilesIntoTextarea() {
        if (uploadedFiles.length === 0) return;
        const ta = document.querySelector('#prompt-textarea');
        if (!ta) return;

        let attachXML = '\n<user_attachments>';
        for (const f of uploadedFiles) {
            attachXML += `\n  <attachment name="${f.name}" last_edit="${f.modifyTime}" size="${f.size}">\n${f.content}\n  </attachment>`;
        }
        attachXML += '\n</user_attachments>\n\n';

        const currentText = ta.innerText;
        ta.innerHTML = '';
        const p = document.createElement('p');
        p.appendChild(document.createTextNode(attachXML + currentText));
        ta.appendChild(p);
        ta.dispatchEvent(new Event('input', { bubbles: true }));

        // clear the batch
        uploadedFiles = [];
        updatePreview();
    }

    function interceptSend() {
        const sendBtn = document.querySelector('button[data-testid="send-button"]');
        if (!sendBtn) return;

        if (!sendBtn.dataset.tampermonkeyInjected) {
            sendBtn.dataset.tampermonkeyInjected = 'true';
            sendBtn.addEventListener('click', async () => {
                if (uploadedFiles.length > 0) {
                    await insertFilesIntoTextarea();
                }
            }, { capture: true });
        }

        const ta = document.querySelector('#prompt-textarea');
        if (ta && !ta.dataset.tampermonkeyEnterHooked) {
            ta.dataset.tampermonkeyEnterHooked = 'true';
            ta.addEventListener('keydown', async (ev) => {
                if (ev.key === 'Enter' && !ev.shiftKey) {
                    if (uploadedFiles.length > 0) {
                        await insertFilesIntoTextarea();
                    }
                }
            }, { capture: true });
        }
    }

    //------------------------------------------------------------------
    // 5) Parsing <user_attachments> in user messages
    //------------------------------------------------------------------
    function processUserMessage(msgEl) {
        const msgId = msgEl.getAttribute('data-message-id');
        if (!msgId) return;

        const textEl = msgEl.querySelector('.whitespace-pre-wrap');
        if (!textEl) return;

        const origText = textEl.textContent || '';
        if (processedMessages[msgId] && processedMessages[msgId].originalText === origText) return;

        const reOuter = /<user_attachments>([\s\S]*?)<\/user_attachments>/g;
        const reAttach = /<attachment name="([^"]+)" last_edit="([^"]+)" size="([^"]+)">\s*([\s\S]*?)\s*<\/attachment>/g;

        let newText = origText;
        const foundFiles = [];

        let outerMatch;
        while ((outerMatch = reOuter.exec(origText)) !== null) {
            const content = outerMatch[1];
            let attachMatch;
            while ((attachMatch = reAttach.exec(content)) !== null) {
                const fileName = attachMatch[1];
                const modifyTime = attachMatch[2];
                const size = attachMatch[3];
                const fileContent = attachMatch[4];
                foundFiles.push({ name: fileName, modifyTime, size, content: fileContent });
            }
            newText = newText.replace(outerMatch[0], '');
        }
        textEl.textContent = newText.trim();

        if (foundFiles.length > 0) {
            // show a block below the message
            let container = msgEl.querySelector('.parsed-files-container');
            if (container) container.remove();
            container = createEl('div', { className: 'parsed-files-container mt-3 mb-4' });

            // Header avec compteur et toggle
            const header = createEl('div', {
                className: 'flex items-center justify-between mb-2',
                html: `
                    <div class="text-xs text-token-text-secondary font-medium flex items-center gap-2">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
                            <polyline points="13 2 13 9 20 9"/>
                        </svg>
                        Files shared (${foundFiles.length})
                    </div>
                    <button class="text-xs text-token-text-secondary hover:text-token-text-primary transition-colors">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                             stroke="currentColor" stroke-width="2" class="transform transition-transform rotate(-90deg)">
                            <path d="M19 9l-7 7-7-7"/>
                        </svg>
                    </button>
                `
            });
            container.appendChild(header);

            // Wrapper pour les fichiers
            const wrapper = createEl('div', {
                className: 'overflow-hidden transition-all duration-300',
                style: 'max-height: 0px;'
            });

            // Grouper par dossier
            const filesByFolder = {};
            foundFiles.forEach(file => {
                const parts = file.name.split('/');
                const folder = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
                if (!filesByFolder[folder]) filesByFolder[folder] = [];
                filesByFolder[folder].push(file);
            });

            // Créer les groupes
            Object.entries(filesByFolder).forEach(([folder, files]) => {
                if (folder) {
                    const folderGroup = createEl('div', {
                        className: 'border border-token-border-light rounded-lg mb-2'
                    });

                    const folderHeader = createEl('div', {
                        className: `
                            flex items-center justify-between p-2
                            bg-token-main-surface-secondary cursor-pointer
                            hover:bg-opacity-70 rounded-t-lg
                        `,
                        html: `
                            <div class="flex items-center gap-2">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                                     stroke="currentColor" stroke-width="2" class="transform transition-transform">
                                    <path d="M19 9l-7 7-7-7"/>
                                </svg>
                                <span class="text-xs font-medium">${folder} (${files.length})</span>
                            </div>
                        `
                    });

                    const folderContent = createEl('div', {
                        className: 'p-2 border-t border-token-border-light'
                    });

                    let isFolderExpanded = true;
                    folderHeader.addEventListener('click', () => {
                        isFolderExpanded = !isFolderExpanded;
                        folderContent.style.display = isFolderExpanded ? 'block' : 'none';
                        folderHeader.querySelector('svg').style.transform =
                            isFolderExpanded ? 'rotate(0deg)' : 'rotate(-90deg)';
                    });

                    files.forEach(file => {
                        folderContent.appendChild(createFileBlock(file));
                    });

                    folderGroup.append(folderHeader, folderContent);
                    wrapper.appendChild(folderGroup);
                } else {
                    files.forEach(file => {
                        wrapper.appendChild(createFileBlock(file));
                    });
                }
            });

            container.appendChild(wrapper);

            // Toggle global
            const toggleBtn = header.querySelector('button');
            let isExpanded = false;
            toggleBtn.addEventListener('click', () => {
                isExpanded = !isExpanded;
                wrapper.style.maxHeight = isExpanded ? wrapper.scrollHeight + 'px' : '0px';
                toggleBtn.querySelector('svg').style.transform =
                    isExpanded ? 'rotate(0deg)' : 'rotate(-90deg)';
            });

            textEl.parentElement.insertBefore(container, textEl.nextSibling);
        }

        processedMessages[msgId] = { originalText: origText, filesFound: foundFiles };
    }

    // Helper pour créer un bloc de fichier
    function createFileBlock(file, showDelete = false) {
        const block = createEl('div', {
            className: `
                group relative flex items-center gap-3
                rounded-lg border border-token-border-light
                bg-token-main-surface-secondary p-2
                hover:bg-token-main-surface-tertiary transition-colors duration-200
            `
        });

        const icon = createEl('div', {
            className: `
                flex items-center justify-center w-6 h-6
                rounded-lg bg-token-main-surface-primary
                text-token-text-secondary
            `,
            html: `
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                     stroke="currentColor" stroke-width="2">
                    <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
                    <polyline points="13 2 13 9 20 9"/>
                </svg>
            `
        });

        const fileInfo = createEl('div', { className: 'flex-1 min-w-0' });
        const name = createEl('div', {
            className: 'text-xs font-medium truncate',
            text: file.name.split('/').pop()
        });
        const details = createEl('div', {
            className: 'text-xs text-token-text-secondary mt-0.5',
            text: formatFileDetails(file)
        });
        fileInfo.append(name, details);

        const viewBtn = createEl('button', {
            className: `
                absolute right-2 opacity-0 group-hover:opacity-100
                transition-opacity duration-200 text-xs py-1 px-2
                rounded-md bg-token-main-surface-primary
                hover:bg-token-main-surface-tertiary
                text-token-text-primary border border-token-border-light
            `,
            text: 'View content'
        });
        viewBtn.addEventListener('click', () => showModal(file.name, file.content, file));

        const delBtn = createEl('button', {
            className: `
                absolute right-2 opacity-0 group-hover:opacity-100
                transition-opacity duration-200 text-xs py-1 px-2
                rounded-md bg-token-main-surface-primary
                hover:bg-token-main-surface-tertiary
                text-token-text-primary border border-token-border-light
            `,
            text: 'Delete'
        });
        delBtn.addEventListener('click', (ev) => {
            ev.stopPropagation();
            removeFile(file.index);
        });

        const actions = createEl('div', {
            className: 'absolute right-0 top-0 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200'
        });

        if (showDelete) {
            actions.append(viewBtn, delBtn);
        } else {
            actions.append(viewBtn);
        }

        block.append(icon, fileInfo, actions);
        block.addEventListener('click', () => showModal(file.name, file.content, file));

        return block;
    }

    function observeUserMessages() {
        const chatContainer = document.body;
        const mo = new MutationObserver(() => {
            const userMsgs = document.querySelectorAll('[data-message-author-role="user"]');
            userMsgs.forEach(m => processUserMessage(m));
        });
        mo.observe(chatContainer, { childList: true, subtree: true });
    }

    //------------------------------------------------------------------
    // 6) GitHub import - Stepper-based flow
    //------------------------------------------------------------------
    function parseGithubUrl(url) {
        const out = { owner: null, repo: null, branch: null };
        try {
            const u = new URL(url);
            const seg = u.pathname.split('/').filter(Boolean);
            if (seg.length >= 2) {
                out.owner = seg[0];
                out.repo = seg[1];
            }
            if (seg.length >= 4 && seg[2] === 'tree') {
                out.branch = seg[3];
            }
        } catch (e) { /* ignore*/ }
        return out;
    }

    function getDefaultBranch(owner, repo) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.github.com/repos/${owner}/${repo}`,
                onload: (res) => {
                    if (res.status !== 200) return reject(new Error('GitHub API error: ' + res.status));
                    const data = JSON.parse(res.responseText || '{}');
                    if (!data.default_branch) return reject(new Error('No default_branch in response.'));
                    resolve(data.default_branch);
                },
                onerror: (err) => reject(err)
            });
        });
    }

    function fetchRepoTree(owner, repo, branch) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`,
                onload: (res) => {
                    if (res.status !== 200) return reject(new Error('Tree fetch error: ' + res.status));
                    const data = JSON.parse(res.responseText || '{}');
                    resolve(data.tree || []);
                },
                onerror: (err) => reject(err)
            });
        });
    }

    function fetchFileContentRaw(owner, repo, branch, path) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`,
                onload: (res) => {
                    if (res.status === 200) resolve(res.responseText);
                    else reject(new Error(`Error fetching ${path}: ${res.status}`));
                },
                onerror: (err) => reject(err)
            });
        });
    }

    const IGNORED_NAMES = ['.env', '.git', '.gitignore', 'node_modules', 'package-lock.json'];
    function buildJsTreeData(githubTree) {
        const nodeIndex = {};
        const data = [];

        function ensureFolder(folderPath) {
            if (nodeIndex[folderPath]) return;
            const fId = 'folder:' + folderPath;
            const i = folderPath.lastIndexOf('/');
            let parent = null, name = folderPath;
            if (i >= 0) {
                parent = folderPath.slice(0, i);
                name = folderPath.slice(i + 1);
            }
            nodeIndex[folderPath] = {
                id: fId,
                parent: parent ? 'folder:' + parent : '#',
                text: name,
                type: 'folder',
                state: { opened: false, checked: false }
            };
            if (parent) ensureFolder(parent);
        }

        for (const item of githubTree) {
            const { path, type } = item;
            const parts = path.split('/');
            if (type === 'tree') {
                ensureFolder(path);
            } else if (type === 'blob') {
                if (parts.length > 1) {
                    ensureFolder(parts.slice(0, -1).join('/'));
                }
                const fileName = parts[parts.length - 1];
                const parent = (parts.length > 1) ? 'folder:' + parts.slice(0, -1).join('/') : '#';
                const fileId = 'file:' + path;
                const isIgnored = IGNORED_NAMES.some(ign => fileName === ign);
                nodeIndex[fileId] = {
                    id: fileId,
                    parent,
                    text: fileName,
                    type: 'file',
                    li_attr: { 'data-file-ref': path },
                    state: { opened: false, checked: !isIgnored }
                };
            }
        }

        for (const k in nodeIndex) data.push(nodeIndex[k]);
        return data;
    }

    // The stepper modal
    /******************************************************
     * Nouveau showGitHubStepperModal() avec design épuré
     ******************************************************/
    async function showGitHubStepperModal() {
        return new Promise((resolve) => {
            // 1) Supprime l'overlay si elle existe déjà
            let oldOverlay = document.getElementById('github-flow-overlay');
            if (oldOverlay) oldOverlay.remove();

            // 2) Crée une overlay pleine page, légèrement grisée
            //    (pour le fond derrière la popup).
            const overlay = createEl('div', {
                attrs: { id: 'github-flow-overlay' },
                className: `
          fixed inset-0
          z-[9999]
          bg-black/50
          flex items-center justify-center
          opacity-0
          transition-opacity duration-300
        `
            });

            // 3) Popup principale « à la ChatGPT »,
            //    centrée et de taille max 680px (comme test_popup.html),
            //    mais pas trop haute (max-h-[80vh]).
            const modal = createEl('div', {
                attrs: { role: 'dialog', 'data-state': 'open', tabindex: '-1', 'aria-modal': 'true' },
                className: `
          popover relative
          w-full
          max-w-[680px]
          max-h-[80vh]
          bg-token-main-surface-primary
          text-start
          rounded-2xl
          shadow-xl
          flex flex-col
          overflow-hidden
          focus:outline-none
          transform scale-95
        `,
                style: `
          pointer-events: auto;
        `
            });

            // === HEADER ===
            const header = createEl('div', {
                className: `
          flex items-center justify-between
          border-b border-black/10 dark:border-white/10
          px-4 pb-4 pt-5 sm:p-6
        `
            });

            // Titre
            const headerLeft = createEl('div', { className: 'flex items-center' });
            const titleBox = createEl('div', { className: 'flex grow flex-col gap-1' });
            const headerTitle = createEl('h2', {
                className: 'text-lg font-semibold leading-6 text-token-text-primary',
                text: 'Import from GitHub'
            });
            titleBox.appendChild(headerTitle);
            headerLeft.appendChild(titleBox);
            header.appendChild(headerLeft);

            // Bouton Close (croix)
            const closeBtn = createEl('button', {
                attrs: { 'data-testid': 'close-button', 'aria-label': 'Close' },
                className: `
          flex h-8 w-8 items-center justify-center
          rounded-full bg-transparent
          hover:bg-token-main-surface-secondary
          focus-visible:outline-none focus-visible:ring-2
          focus-visible:ring-token-text-quaternary focus-visible:ring-offset-1
          dark:hover:bg-token-main-surface-tertiary
        `,
                html: `
          <svg width="24" height="24" viewBox="0 0 24 24"
               fill="none" xmlns="http://www.w3.org/2000/svg"
               class="icon-md">
            <path fill-rule="evenodd" clip-rule="evenodd"
              d="M5.63603 5.63604C6.02656 5.24552 6.65972 5.24552 
                 7.05025 5.63604L12 10.5858L16.9497 5.63604C17.3403 
                 5.24552 17.9734 5.24552 18.364 5.63604C18.7545 
                 6.02657 18.7545 6.65973 18.364 7.05025L13.4142 
                 12L18.364 16.9497C18.7545 17.3403 18.7545 17.9734 
                 18.364 18.364C17.9734 18.7545 17.3403 18.7545 
                 16.9497 18.364L12 13.4142L7.05025 18.364C6.65972 
                 18.7545 6.02656 18.7545 5.63603 18.364C5.24551 
                 17.9734 5.24551 17.3403 5.63603 16.9497L10.5858 
                 12L5.63603 7.05025C5.24551 6.65973 5.24551 6.02657 
                 5.63603 5.63604Z"
              fill="currentColor"></path>
          </svg>
        `
            });
            closeBtn.addEventListener('click', () => {
                overlay.style.opacity = '0';
                setTimeout(() => overlay.remove(), 300);
                resolve(false);
            });
            header.appendChild(closeBtn);

            // === CONTENU PRINCIPAL SCROLLABLE ===
            const mainContainer = createEl('div', {
                className: `
          flex-grow
          overflow-y-auto
          relative
          text-sm text-token-text-primary
        `
            });

            // === FOOTER (facultatif) ===
            const footer = createEl('div', {
                className: `
          flex flex-col gap-3
          border-t border-black/10 dark:border-white/10
          px-4 py-4 sm:p-6
        `
            });
            // Pour l'instant, pas d'actions globales, on le laisse vide
            footer.style.display = 'none';

            // On assemble tout
            modal.appendChild(header);
            modal.appendChild(mainContainer);
            modal.appendChild(footer);
            overlay.appendChild(modal);
            document.body.appendChild(overlay);

            // Animation d'apparition
            requestAnimationFrame(() => {
                overlay.style.opacity = '1';
                modal.style.transform = 'scale(1)';
            });

            //------------------------------------------------
            //             LOGIQUE DU STEPPER
            //------------------------------------------------

            // Variables internes pour stocker les données entre étapes
            let githubTreeGlobal = null;
            let ownerGlobal = null;
            let repoGlobal = null;
            let branchGlobal = null;

            // Lance l'étape 1
            showStep1();

            /** Étape 1 : saisir l'URL du repo */
            function showStep1() {
                mainContainer.innerHTML = '';
                const contentWrap = createEl('div', {
                    className: 'px-4 pb-6 pt-4 sm:px-6'
                });

                const label = createEl('label', {
                    className: 'block text-sm font-semibold mb-2',
                    text: 'GitHub repo URL (ex: https://github.com/owner/repo[/tree/branch])'
                });

                const inputUrl = createEl('input', {
                    className: `
            w-full p-3 rounded-md
            border border-gray-300 dark:border-gray-600
            bg-token-main-surface-secondary
            text-token-text-primary
            placeholder-gray-500 dark:placeholder-gray-400
            focus:ring-2 focus:ring-green-500 focus:border-transparent
            transition-colors duration-200
          `,
                    attrs: {
                        type: 'text',
                        placeholder: 'https://github.com/owner/repo'
                    }
                });

                const loadBtn = createEl('button', {
                    className: `
            mt-4 px-4 py-2
            bg-green-600 hover:bg-green-700
            text-white font-medium text-sm
            rounded-md transition-colors duration-200
            focus:ring-2 focus:ring-green-500 focus:ring-offset-2
          `,
                    text: 'Load repository'
                });

                loadBtn.addEventListener('click', async () => {
                    const val = inputUrl.value.trim();
                    if (!val) return;

                    // On affiche un spinner pendant le chargement
                    mainContainer.innerHTML = '';
                    mainContainer.appendChild(spinnerSection('Loading repository data...'));

                    try {
                        const { owner, repo, branch } = parseGithubUrl(val);
                        if (!owner || !repo) {
                            mainContainer.innerHTML = '';
                            mainContainer.appendChild(errorSection('Invalid GitHub URL.'));
                            return;
                        }
                        const finalBranch = branch || await getDefaultBranch(owner, repo);
                        const tree = await fetchRepoTree(owner, repo, finalBranch);

                        // On stocke pour l'étape suivante
                        githubTreeGlobal = tree;
                        ownerGlobal = owner;
                        repoGlobal = repo;
                        branchGlobal = finalBranch;

                        // Étape 2
                        showStep2();
                    } catch (e) {
                        mainContainer.innerHTML = '';
                        mainContainer.appendChild(errorSection(e.message || 'Error while fetching repo.'));
                    }
                });

                contentWrap.append(label, inputUrl, loadBtn);
                mainContainer.appendChild(contentWrap);
            }

            /** Étape 2 : sélection des fichiers dans l'arborescence */
            function showStep2() {
                mainContainer.innerHTML = '';
                const wrapper = createEl('div', {
                    className: 'px-4 pb-6 pt-4 sm:px-6'
                });

                const note = createEl('p', {
                    className: 'mb-3 text-sm',
                    text: 'Select the files/folders to import:'
                });

                const treeContainer = createEl('div', {
                    className: `
            tree-view
            border border-gray-300 dark:border-gray-600
            rounded-md p-2
            max-h-[250px] overflow-auto
            bg-token-main-surface-secondary
          `
                });

                // Convertit githubTreeGlobal en un objet hiérarchique
                const treeData = {};
                githubTreeGlobal.forEach(item => {
                    if (!item || !item.path) return;
                    let current = treeData;
                    const parts = item.path.split('/');
                    parts.forEach((part, i) => {
                        if (!current[part]) {
                            current[part] = {
                                name: part,
                                path: parts.slice(0, i + 1).join('/'),
                                isFolder: (i < parts.length - 1) || (item.type === 'tree'),
                                children: {},
                                checked: true,
                                expanded: false
                            };
                        }
                        current = current[part].children;
                    });
                });

                // Rendu du tree
                renderTreeView(treeContainer, treeData);

                // Bouton confirm
                const confirmBtn = createEl('button', {
                    className: `
            mt-4 px-4 py-2
            bg-blue-600 hover:bg-blue-700
            text-white font-medium text-sm
            rounded-md transition-colors duration-200
            focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
          `,
                    text: 'Confirm'
                });

                confirmBtn.addEventListener('click', () => {
                    const selectedFiles = [];
                    function collectSelectedFiles(node) {
                        if (!node.isFolder && node.checked) selectedFiles.push(node.path);
                        Object.values(node.children).forEach(collectSelectedFiles);
                    }
                    Object.values(treeData).forEach(collectSelectedFiles);

                    if (!selectedFiles.length) {
                        alert('No files selected!');
                        return;
                    }
                    showStep3(selectedFiles);
                });

                wrapper.append(note, treeContainer, confirmBtn);
                mainContainer.appendChild(wrapper);
            }

            /** Étape 3 : import effectif des fichiers + message final */
            function showStep3(filePaths) {
                mainContainer.innerHTML = '';
                const info = createEl('div', {
                    className: `
            flex flex-col items-center justify-center gap-4
            p-6
          `
                });

                const title = createEl('div', {
                    className: 'text-lg font-medium',
                    text: 'Importing files...'
                });
                const progress = createEl('div', {
                    className: 'text-sm text-gray-500 dark:text-gray-400'
                });
                info.append(title, progress);
                mainContainer.appendChild(info);

                // Processus d'import
                (async () => {
                    let fetchCount = 0;
                    for (let i = 0; i < filePaths.length; i++) {
                        const path = filePaths[i];
                        progress.textContent = `(${i + 1}/${filePaths.length}) ${path}`;
                        try {
                            const raw = await fetchFileContentRaw(ownerGlobal, repoGlobal, branchGlobal, path);
                            uploadedFiles.push({
                                name: path,
                                content: raw,
                                modifyTime: new Date().toLocaleString(),
                                size: formatFileSize(raw.length)
                            });
                            fetchCount++;
                        } catch (err) {
                            console.error('Error fetching', path, err);
                        }
                    }

                    // On ajoute un attachment_info.xml avec la structure
                    const asciiTree = buildRepoStructureASCII(githubTreeGlobal);
                    const infoContent = [
                        '<attachment_info>',
                        `  Repository: https://github.com/${ownerGlobal}/${repoGlobal}`,
                        '',
                        `  Branch: ${branchGlobal}`,
                        '',
                        '  <structure>',
                        asciiTree,
                        '  </structure>',
                        '</attachment_info>'
                    ].join('\n');

                    uploadedFiles.push({
                        name: 'attachment_info.xml',
                        content: infoContent,
                        modifyTime: new Date().toLocaleString(),
                        size: formatFileSize(infoContent.length)
                    });

                    // Message de succès
                    info.innerHTML = `
            <div
              class="flex items-center justify-center w-16 h-16 
                     rounded-full bg-green-100 dark:bg-green-900/30">
              <svg class="w-8 h-8 text-green-500" fill="none"
                   stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round"
                      stroke-linejoin="round"
                      stroke-width="2"
                      d="M5 13l4 4L19 7"/>
              </svg>
            </div>
            <div class="text-lg font-medium">Import Complete!</div>
            <div class="text-sm text-gray-500 dark:text-gray-400">
              Successfully imported ${fetchCount} file(s).
            </div>
          `;

                    // Ferme la modal au bout de 1.5s
                    setTimeout(() => {
                        overlay.style.opacity = '0';
                        setTimeout(() => overlay.remove(), 300);
                        resolve(true);
                    }, 1500);
                })();
            }

            //------------------------------------------------
            //      Petites fonctions pour spinner, erreur
            //------------------------------------------------

            function spinnerSection(label) {
                const container = createEl('div', {
                    className: 'p-6 flex items-center gap-3'
                });
                container.innerHTML = `
          <svg class="my-spinner" width="24" height="24" fill="none"
               stroke="currentColor" stroke-width="2">
            <circle cx="12" cy="12" r="10" stroke-opacity="0.25"/>
            <path d="M12 2 C6.48 2 2 6.48 2 12"
                  stroke-linecap="round" stroke-opacity="0.75"/>
          </svg>
          <span>${label || 'Loading...'}</span>
        `;
                return container;
            }

            function errorSection(message) {
                const div = createEl('div', {
                    className: 'p-6 text-red-600'
                });
                div.textContent = message;
                return div;
            }
        });
    }


    async function onClickGitHubImport() {
        closeMenuIfNeeded();
        const result = await showGitHubStepperModal();
        if (result) updatePreview();
    }

    function closeMenuIfNeeded() {
        const triggerBtn = document.querySelector('#radix-\\:rkd\\:');
        if (triggerBtn) {
            triggerBtn.click();
        } else {
            const escEvent = new KeyboardEvent('keydown', { key: 'Escape' });
            document.dispatchEvent(escEvent);
        }
    }

    //------------------------------------------------------------------
    // 7) Local file / folder upload
    //------------------------------------------------------------------
    function addLocalFileButton(menu) {
        if (menu.querySelector('.upload-texte-btn')) return;
        // Upload Files button
        const uploadFilesBtn = createEl('div', {
            className: `
                flex items-center m-1.5 p-2.5 text-sm cursor-pointer
                focus-visible:outline-0 group relative hover:bg-[#f5f5f5]
                dark:hover:bg-token-main-surface-secondary
                rounded-md gap-2.5 upload-texte-btn
            `,
            attrs: { role: 'menuitem', tabIndex: '-1' },
            html: `
                <div class="flex items-center justify-center text-token-text-secondary h-5 w-5">
                    <svg width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M4 14.5V12a8 8 0 0 1 16 0v2.5"/>
                        <path d="M12 12v9"/>
                        <path d="M8 17l4-4 4 4"/>
                    </svg>
                </div>
                <div class="flex flex-col text-token-text-primary">Upload Files</div>
            `
        });
        const fileInput = createEl('input', { attrs: { type: 'file' }, style: 'display:none;' });
        fileInput.multiple = true;
        uploadFilesBtn.addEventListener('click', () => {
            closeMenuIfNeeded();
            fileInput.click();
        });
        fileInput.addEventListener('change', async () => {
            const arr = Array.from(fileInput.files || []);
            for (const f of arr) {
                const info = await readFileContent(f);
                uploadedFiles.push({ name: f.name, content: info.content, modifyTime: info.modifyTime, size: info.size });
            }
            updatePreview();
        });
        uploadFilesBtn.appendChild(fileInput);
        menu.appendChild(uploadFilesBtn);

        // Upload Folder
        const uploadFolderBtn = createEl('div', {
            className: `
                flex items-center m-1.5 p-2.5 text-sm cursor-pointer
                focus-visible:outline-0 group relative hover:bg-[#f5f5f5]
                dark:hover:bg-token-main-surface-secondary
                rounded-md gap-2.5
            `,
            attrs: { role: 'menuitem', tabIndex: '-1' },
            html: `
                <div class="flex items-center justify-center text-token-text-secondary h-5 w-5">
                    <svg width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M3 7H9L10 9H21V19C21 20.1046 20.1046 21
                                 19 21H5
                                 C3.8954 21 3 20.1046 3 19V7Z"/>
                        <path d="M3 7C3 5.89543 3.89543 5 5 5H8L9 7"/>
                    </svg>
                </div>
                <div class="flex flex-col text-token-text-primary">Upload Folder</div>
            `
        });
        const folderInput = createEl('input', { attrs: { type: 'file' }, style: 'display:none;' });
        folderInput.multiple = true;
        folderInput.setAttribute('webkitdirectory', '');
        folderInput.setAttribute('directory', '');
        uploadFolderBtn.addEventListener('click', () => {
            closeMenuIfNeeded();
            folderInput.click();
        });
        folderInput.addEventListener('change', async () => {
            const arr = Array.from(folderInput.files || []);
            if (!arr.length) return;

            // Construire la structure de données pour notre tree view
            const paths = arr.map(file => ({
                path: (file.webkitRelativePath || file.name).replace(/^\.?\//, ''),
                type: 'file'
            }));

            const chosen = await showFolderTreeModal(paths);
            if (!chosen) return;

            // Traiter les fichiers sélectionnés
            for (const p of chosen) {
                const fr = arr.find(f => (f.webkitRelativePath || f.name).replace(/^\.?\//, '') === p);
                if (fr) {
                    const info = await readFileContent(fr);
                    uploadedFiles.push({
                        name: p,
                        content: info.content,
                        modifyTime: info.modifyTime,
                        size: info.size
                    });
                }
            }
            updatePreview();
        });
        uploadFolderBtn.appendChild(folderInput);
        menu.appendChild(uploadFolderBtn);
    }

    //------------------------------------------------------------------
    // 8) Minimal folder-tree modal for local folder
    //------------------------------------------------------------------
    function showFolderTreeModal(paths) {
        return new Promise((resolve) => {
            let overlay = document.getElementById('folder-modal-overlay');
            if (overlay) overlay.remove();

            overlay = createEl('div', {
                attrs: { id: 'folder-modal-overlay' },
                className: `
                    fixed inset-0 bg-black bg-opacity-50 z-[9999]
                    flex items-center justify-center
                    opacity-0 transition-opacity duration-200
                `
            });

            const modal = createEl('div', {
                className: `
                    rounded-xl border border-token-border-light
                    bg-token-main-surface-primary text-token-text-primary p-4
                    w-[600px] max-w-[90vw] max-h-[80vh]
                    flex flex-col gap-4 transform scale-95
                    transition-transform duration-200
                `
            });

            // Header
            const header = createEl('div', {
                className: 'flex justify-between items-center'
            });
            const title = createEl('div', {
                className: 'font-semibold',
                text: 'Select files to import'
            });
            const buttonBox = createEl('div', {
                className: 'flex gap-2'
            });

            // Buttons
            const cancelBtn = createEl('button', {
                className: `
                    text-sm px-3 py-1 bg-token-main-surface-secondary
                    border border-token-border-light rounded
                    hover:bg-[#f0f0f0]
                `,
                text: 'Cancel'
            });

            const confirmBtn = createEl('button', {
                className: `
                    text-sm px-3 py-1 bg-token-main-surface-secondary
                    border border-token-border-light rounded
                    hover:bg-[#f0f0f0]
                `,
                text: 'Confirm'
            });

            buttonBox.append(cancelBtn, confirmBtn);
            header.append(title, buttonBox);

            // Tree container
            const treeContainer = createEl('div', {
                className: 'tree-view overflow-auto flex-1 border border-token-border-light p-3 rounded'
            });

            modal.append(header, treeContainer);
            overlay.appendChild(modal);
            document.body.appendChild(overlay);

            // Animation
            requestAnimationFrame(() => {
                overlay.style.opacity = '1';
                modal.style.transform = 'scale(1)';
            });

            // Initialize tree (plus besoin de .map())
            const treeData = buildTreeData(paths);
            renderTreeView(treeContainer, treeData);

            // Events
            cancelBtn.onclick = () => {
                overlay.remove();
                resolve(null);
            };

            confirmBtn.onclick = () => {
                const selected = getSelectedFiles(treeData);
                overlay.remove();
                resolve(selected);
            };
        });
    }

    function buildTreeData(paths) {
        const tree = {};

        // Construire l'arbre
        paths.forEach(item => {
            if (!item || !item.path) return; // Skip invalid items

            let current = tree;
            const parts = item.path.split('/');

            parts.forEach((part, index) => {
                if (!current[part]) {
                    current[part] = {
                        name: part,
                        path: parts.slice(0, index + 1).join('/'),
                        isFolder: index < parts.length - 1,
                        children: {},
                        checked: false,
                        expanded: true
                    };
                }
                current = current[part].children;
            });
        });

        return tree;
    }

    function renderTreeView(container, treeData, level = 0, parentPath = '') {
        if (level === 0) {
            container.innerHTML = '';
        }

        Object.values(treeData).forEach(node => {
            const itemWrapper = document.createElement('div');
            itemWrapper.className = 'tree-node';

            // Créer l'élément principal
            const item = document.createElement('div');
            item.className = 'tree-item';
            item.style.paddingLeft = `${level * 24}px`;

            // Toggle pour les dossiers
            const toggle = document.createElement('div');
            toggle.className = `tree-toggle ${node.expanded ? 'expanded' : ''}`;
            if (node.isFolder) {
                toggle.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>';
            }

            // Icône
            const icon = document.createElement('div');
            icon.className = 'tree-icon';
            icon.innerHTML = node.isFolder ?
                '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7h9l1 2h8v11a2 2 0 01-2 2H5a2 2 0 01-2-2V7z"/></svg>' :
                '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 00-2 2v16c0 1.1.9 2 2 2h12a2 2 0 002-2V9l-7-7z"/><path d="M13 2v7h7"/></svg>';

            // Checkbox
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.className = 'tree-checkbox';
            checkbox.checked = node.checked;

            // Label
            const label = document.createElement('div');
            label.className = 'tree-label';
            label.textContent = node.name;

            item.append(toggle, icon, checkbox, label);
            itemWrapper.appendChild(item);

            // Container pour les enfants avec animation
            if (node.isFolder) {
                const childrenContainer = document.createElement('div');
                childrenContainer.className = `tree-children ${node.expanded ? '' : 'collapsed'}`;

                // Render children
                renderTreeView(childrenContainer, node.children, level + 1, node.path);

                // Event listeners
                toggle.onclick = (e) => {
                    e.stopPropagation();
                    node.expanded = !node.expanded;
                    toggle.classList.toggle('expanded');

                    if (node.expanded) {
                        childrenContainer.classList.remove('collapsed');
                        // Set height for animation
                        const height = Array.from(childrenContainer.children)
                            .reduce((acc, child) => acc + child.offsetHeight, 0);
                        childrenContainer.style.height = height + 'px';
                    } else {
                        // Get current height
                        const height = childrenContainer.offsetHeight;
                        childrenContainer.style.height = height + 'px';
                        // Force reflow
                        childrenContainer.offsetHeight;
                        // Collapse
                        childrenContainer.classList.add('collapsed');
                    }
                };

                itemWrapper.appendChild(childrenContainer);
            }

            // Checkbox event
            checkbox.onchange = () => {
                node.checked = checkbox.checked;
                if (node.isFolder) {
                    updateChildrenChecked(node, checkbox.checked);
                    // Update children checkboxes in DOM
                    const childCheckboxes = itemWrapper.querySelectorAll('.tree-checkbox');
                    childCheckboxes.forEach(cb => {
                        cb.checked = checkbox.checked;
                    });
                }
                updateParentChecked(treeData, node.path);
            };

            container.appendChild(itemWrapper);
        });
    }

    function updateChildrenChecked(node, checked) {
        node.checked = checked;
        Object.values(node.children).forEach(child => {
            updateChildrenChecked(child, checked);
        });
    }

    function updateParentChecked(tree, path) {
        const parts = path.split('/');
        let current = tree;

        // Pour chaque niveau de profondeur sauf le dernier
        for (let i = 0; i < parts.length - 1; i++) {
            const parentPath = parts.slice(0, i + 1).join('/');
            const parent = getNodeByPath(tree, parentPath);

            if (!parent || !current[parts[i]]) continue;

            // Vérifier si tous les enfants sont cochés
            const children = Object.values(current[parts[i]].children);
            if (children.length > 0) {
                parent.checked = children.every(child => child.checked);
            }

            // Avancer dans l'arbre
            current = current[parts[i]].children;
        }
    }

    function getNodeByPath(tree, path) {
        let current = tree;
        const parts = path.split('/');

        for (const part of parts) {
            if (!current[part]) return null;
            current = current[part];
        }

        return current;
    }

    function getSelectedFiles(tree) {
        const selected = [];

        function traverse(node) {
            if (!node.isFolder && node.checked) {
                selected.push(node.path);
            }
            Object.values(node.children).forEach(traverse);
        }

        Object.values(tree).forEach(traverse);
        return selected;
    }

    //------------------------------------------------------------------
    // 9) Add "Upload from GitHub" button + hooking
    //------------------------------------------------------------------
    function addGithubButton(menu) {
        if (menu.querySelector('.upload-github-btn')) return;

        const ghBtn = createEl('div', {
            className: `
                flex items-center m-1.5 p-2.5 text-sm cursor-pointer
                focus-visible:outline-0 group relative hover:bg-[#f5f5f5]
                dark:hover:bg-token-main-surface-secondary
                rounded-md gap-2.5 upload-github-btn
            `,
            attrs: { role: 'menuitem', tabIndex: '-1' },
            children: [
                createEl('div', {
                    className: 'flex items-center justify-center text-token-text-secondary h-5 w-5', html: `
                    <svg width="24" height="24" viewBox="0 0 24 24"
                         fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M9 19c-4.97 1-5-2.16-7-2m14 2v-3.48A3.37
                                 3.37 0 0 0 17.64 13c.75-.4
                                 1.36-1.17 1.36-2.64
                                 0-2-1.5-3-3.5-3
                                 A3.75 3.75 0 0 0 12 9.77
                                 A3.75 3.75 0 0 0
                                 8.5 7c-2 0-3.5 1-3.5 3
                                 0 1.47.61 2.24
                                 1.36 2.64A3.37 3.37 0 0
                                 0 6.36 16.52V20"/>
                    </svg>
                `}),
                createEl('div', {
                    className: 'flex flex-col text-token-text-primary dark:text-token-text-primary',
                    text: 'Upload from GitHub'
                })
            ]
        });
        ghBtn.addEventListener('click', onClickGitHubImport);
        menu.appendChild(ghBtn);
    }

    // The function that tries to add our 3 new buttons into the ChatGPT menu
    function addUploadButtons() {
        const menu = document.querySelector('div[role="menu"]');
        if (!menu) return;
        addLocalFileButton(menu);
        addGithubButton(menu);
    }

    //------------------------------------------------------------------
    // 10) Observers / main entry
    //------------------------------------------------------------------
    function observeMenuAndSend() {
        const obs = new MutationObserver(() => {
            addUploadButtons();
            interceptSend();
        });
        obs.observe(document.documentElement, { childList: true, subtree: true });
    }

    //------------------------------------------------------------------
    // MAIN
    //------------------------------------------------------------------
    observeMenuAndSend();
    observeUserMessages();
})();