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.

Versão de: 04/07/2024. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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
})();