Chat GPT Code Export Button

Adds Export button to code blocks in ChatGPT responses, prompts user to save code as file with predefined filename based on coding language detected from the code block's class name.

Versión del día 4/7/2024. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Chat GPT Code Export Button
// @namespace    http://tampermonkey.net/
// @version      2024/07/03
// @license      MIT
// @description  Adds Export button to code blocks in ChatGPT responses, prompts user to save code as file with predefined filename based on coding language detected from the code block's class name.
// @author       Muffin & Arcadie
// @match        https://chatgpt.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Function to add "Export" button to existing code headers
    function addExportButtonToHeaders() {
        const codeHeaders = document.querySelectorAll('.flex.items-center.relative.text-token-text-secondary.bg-token-main-surface-secondary.px-4.py-2.text-xs.font-sans.justify-between.rounded-t-md');

        codeHeaders.forEach(header => {
            // Check if "Export" button is already added
            if (header.querySelector('.export-button')) {
                return; // Skip if already added
            }

            // Find the language label
            const languageLabel = header.querySelector('span');

            // Create the "Export" button
            const exportButton = document.createElement('button');
            exportButton.innerText = 'Export';
            exportButton.classList.add('export-button'); // Add a class for styling
            exportButton.style.padding = '8px 16px';
            exportButton.style.marginRight = '8px'; // Adjust spacing if necessary
            exportButton.style.border = 'none';
            exportButton.style.borderRadius = '4px';
            exportButton.style.backgroundColor = '#e3e3e3';
            exportButton.style.color = '#333';
            exportButton.style.cursor = 'pointer';
            exportButton.style.transition = 'background-color 0.3s';

            // Insert "Export" button after the language label
            languageLabel.parentNode.insertBefore(exportButton, languageLabel.nextSibling);

            // Add click event listener for the "Export" button
            exportButton.addEventListener('click', () => {
                const codeBlock = header.parentElement.querySelector('pre code'); // Assuming structure, adjust as needed
                const language = languageLabel.textContent.trim().toLowerCase(); // Extract language
                exportCode(codeBlock, language); // Call export function
            });
        });
    }

    // Function to open File Explorer for saving the code as file
    async function exportCode(codeBlock, language) {
        let fileName;
        let fileExtension;
        let mimeType;

        // Determine filename, extension, and MIME type based on language
        switch (language) {
            case 'javascript':
            case 'js':
                fileName = 'script';
                fileExtension = '.js';
                mimeType = 'application/javascript';
                break;
            case 'html':
                fileName = 'index';
                fileExtension = '.html';
                mimeType = 'text/html';
                break;
            case 'css':
                fileName = 'styles';
                fileExtension = '.css';
                mimeType = 'text/css';
                break;
            case 'python':
            case 'py':
                fileName = 'main';
                fileExtension = '.py';
                mimeType = 'text/x-python';
                break;
            default:
                // If language cannot be determined from <span>, fallback to provided language
                switch (language.toLowerCase()) {
                    case 'javascript':
                    case 'js':
                        fileName = 'script';
                        fileExtension = '.js';
                        mimeType = 'application/javascript';
                        break;
                    case 'html':
                        fileName = 'index';
                        fileExtension = '.html';
                        mimeType = 'text/html';
                        break;
                    case 'css':
                        fileName = 'styles';
                        fileExtension = '.css';
                        mimeType = 'text/css';
                        break;
                    case 'python':
                    case 'py':
                        fileName = 'main';
                        fileExtension = '.py';
                        mimeType = 'text/x-python';
                        break;
                    case 'java':
                        fileName = 'Main';
                        fileExtension = '.java';
                        mimeType = 'text/x-java-source';
                        break;
                    case 'kotlin':
                        fileName = 'Main';
                        fileExtension = '.kt';
                        mimeType = 'text/x-kotlin';
                        break;
                    case 'c++':
                    case 'cpp':
                        fileName = 'main';
                        fileExtension = '.cpp';
                        mimeType = 'text/x-c++src';
                        break;
                    case 'c#':
                    case 'csharp':
                        fileName = 'Program';
                        fileExtension = '.cs';
                        mimeType = 'text/x-csharp';
                        break;
                    case 'c':
                        fileName = 'main';
                        fileExtension = '.c';
                        mimeType = 'text/x-csrc';
                        break;
                    case 'ruby':
                        fileName = 'script';
                        fileExtension = '.rb';
                        mimeType = 'text/x-ruby';
                        break;
                    case 'rust':
                        fileName = 'main';
                        fileExtension = '.rs';
                        mimeType = 'text/x-rustsrc';
                        break;
                    case 'php':
                        fileName = 'script';
                        fileExtension = '.php';
                        mimeType = 'text/x-php';
                        break;
                    case 'swift':
                        fileName = 'main';
                        fileExtension = '.swift';
                        mimeType = 'text/x-swift';
                        break;
                    case 'typescript':
                    case 'ts':
                        fileName = 'script';
                        fileExtension = '.ts';
                        mimeType = 'application/typescript';
                        break;
                    case 'go':
                        fileName = 'main';
                        fileExtension = '.go';
                        mimeType = 'text/x-go';
                        break;
                    case 'perl':
                        fileName = 'script';
                        fileExtension = '.pl';
                        mimeType = 'text/x-perl';
                        break;
                    case 'lua':
                        fileName = 'script';
                        fileExtension = '.lua';
                        mimeType = 'text/x-lua';
                        break;
                    default:
                        fileName = 'code';
                        fileExtension = '.txt';
                        mimeType = 'text/plain';
                        break;
                }
                break;
        }

        // Create a Blob object with the code content
        const blob = new Blob([codeBlock.innerText], { type: mimeType });

        try {
            if (window.showSaveFilePicker) {
                // Use File System Access API if available
                const fileHandle = await window.showSaveFilePicker({
                    suggestedName: fileName + fileExtension,
                    types: [
                        {
                            description: language,
                            accept: {
                                [mimeType]: [fileExtension],
                            },
                        },
                    ],
                });

                const writable = await fileHandle.createWritable();
                await writable.write(blob);
                await writable.close();
            } else {
                // Fallback for browsers that do not support showSaveFilePicker
                const downloadLink = document.createElement('a');
                downloadLink.href = URL.createObjectURL(blob);
                downloadLink.download = fileName + fileExtension;
                downloadLink.style.display = 'none';
                document.body.appendChild(downloadLink);
                downloadLink.click();
                URL.revokeObjectURL(downloadLink.href);
                document.body.removeChild(downloadLink);
            }
        } catch (error) {
            console.error('Save file dialog was canceled or failed', error);
        }
    }

    // Observe the document for changes and add "Export" button to new code headers
    const observer = new MutationObserver(addExportButtonToHeaders);
    observer.observe(document.body, { childList: true, subtree: true });

    // Initial processing of existing code headers
    addExportButtonToHeaders();

    // Add custom CSS styles if needed
})();