Greasy Fork is available in English.

Upload To Gitlab Button for Claude.ai Chat

Adds an "Upload To Gitlab" button before the chat controls on Claude.ai chat pages

// ==UserScript==
// @name         Upload To Gitlab Button for Claude.ai Chat
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Adds an "Upload To Gitlab" button before the chat controls on Claude.ai chat pages
// @match        https://claude.ai/chat/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to create the "Upload To Gitlab" button
    function createUploadToGitlabButton() {
        const uploadButton = document.createElement('button');
        uploadButton.textContent = 'Upload To Gitlab';
        uploadButton.id = 'upload-to-gitlab-btn';
        uploadButton.style.backgroundColor = 'blue';
        uploadButton.style.color = 'white';
        uploadButton.style.padding = '5px 10px';
        uploadButton.style.borderRadius = '5px';
        uploadButton.style.marginRight = '10px';
        uploadButton.addEventListener('click', openModal);
        return uploadButton;
    }

    // Function to open the modal
    function openModal() {
        const modal = document.createElement('div');
        modal.id = 'gitlab-modal';
        modal.style.position = 'fixed';
        modal.style.top = '50%';
        modal.style.left = '50%';
        modal.style.transform = 'translate(-50%, -50%)';
        modal.style.backgroundColor = 'hsl(var(--bg-200)/var(--tw-bg-opacity))';
        modal.style.padding = '20px';
        modal.style.borderRadius = '5px';
        modal.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.3)';
        modal.style.zIndex = '9999';
        modal.innerHTML = `
            <h2>Upload To Gitlab</h2>
            <form id="gitlab-form">
                <label for="gitlab-domain">Gitlab Domain:</label>
                <input type="text" id="gitlab-domain" name="gitlab-domain" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="gitlab-token">Gitlab Token:</label>
                <input type="text" id="gitlab-token" name="gitlab-token" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="gitlab-project-id">Gitlab Project ID:</label>
                <input type="text" id="gitlab-project-id" name="gitlab-project-id" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="project-branch">Project Branch:</label>
                <input type="text" id="project-branch" name="project-branch" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="file-path">Path:</label>
                <input type="text" id="file-path" name="file-path" style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <div style="text-align: right;">
                    <button type="button" id="close-modal-btn" style="margin-right: 10px;">X</button>
                    <button type="submit" style="background-color: green; color: white;">Upload</button>
                </div>
            </form>
        `;
        document.body.appendChild(modal);

        // Populate form fields with saved data
        const gitlabDomain = localStorage.getItem('gitlab-domain');
        const gitlabToken = localStorage.getItem('gitlab-token');
        const gitlabProjectId = localStorage.getItem('gitlab-project-id');
        const projectBranch = localStorage.getItem('project-branch');
        const filePath = localStorage.getItem('file-path');
        if (gitlabDomain) {
            document.getElementById('gitlab-domain').value = gitlabDomain;
        }
        if (gitlabToken) {
            document.getElementById('gitlab-token').value = gitlabToken;
        }
        if (gitlabProjectId) {
            document.getElementById('gitlab-project-id').value = gitlabProjectId;
        }
        if (projectBranch) {
            document.getElementById('project-branch').value = projectBranch;
        }
        if (filePath) {
            document.getElementById('file-path').value = filePath;
        }

        // Handle form submission
        document.getElementById('gitlab-form').addEventListener('submit', function(e) {
            e.preventDefault();
            const gitlabDomain = document.getElementById('gitlab-domain').value.replace(/^https?:\/\//, '').replace(/\/$/, '');
            const gitlabToken = document.getElementById('gitlab-token').value;
            const gitlabProjectId = document.getElementById('gitlab-project-id').value;
            const projectBranch = document.getElementById('project-branch').value;
            const filePath = document.getElementById('file-path').value.trim();

            // Save form data in localStorage
            localStorage.setItem('gitlab-domain', gitlabDomain);
            localStorage.setItem('gitlab-token', gitlabToken);
            localStorage.setItem('gitlab-project-id', gitlabProjectId);
            localStorage.setItem('project-branch', projectBranch);
            localStorage.setItem('file-path', filePath);

            // Close the modal
            document.body.removeChild(modal);

            // Fetch data from the API and upload to Gitlab
            uploadToGitlab(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath);
        });

        // Handle modal close button click
        document.getElementById('close-modal-btn').addEventListener('click', function() {
            document.body.removeChild(modal);
        });
    }

    // Function to check if the file exists on Gitlab
    function checkFileExists(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath) {
        const checkFileUrl = `https://${gitlabDomain}/api/v4/projects/${gitlabProjectId}/repository/files/${encodeURIComponent(filePath)}?ref=${projectBranch}`;
        return fetch(checkFileUrl, {
            method: 'GET',
            headers: {
                'PRIVATE-TOKEN': gitlabToken
            }
        }).then(response => {
            if (response.status === 200) {
                return true;
            } else if (response.status === 404) {
                return false;
            } else {
                throw new Error(`Unexpected response status: ${response.status}`);
            }
        });
    }

    // Function to upload data to Gitlab
    function uploadToGitlab(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, userFilePath) {
        const cacheName = 'apis';
        caches.open(cacheName).then(function(cache) {
            cache.keys().then(function(requests) {
                let organizationId = null;
                requests.forEach(function(request) {
                    if (request.url.includes('https://claude.ai/api/organizations/')) {
                        const parts = request.url.split('/');
                        const index = parts.indexOf('organizations');
                        if (index !== -1 && parts.length > index + 1) {
                            organizationId = parts[index + 1];
                        }
                    }
                });
                console.log('Organization ID (from cache):', organizationId);

                // Get the current chat ID from the browser URL
                const currentUrl = window.location.href;
                const chatId = currentUrl.split('/').pop();
                console.log('Current Chat ID (from URL):', chatId);

                // Fetch JSON data from the API URL
                const apiUrl = `https://claude.ai/api/organizations/${organizationId}/chat_conversations/${chatId}`;
                fetch(apiUrl)
                    .then(response => response.json())
                    .then(data => {
                        console.log('API Response:', data);

                        const filePath = userFilePath ? `${userFilePath.replace(/\/$/, '')}/${chatId}.json` : `${chatId}.json`;
                        checkFileExists(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath)
                            .then(fileExists => {
                                const action = fileExists ? 'update' : 'create';

                                // Prepare the payload for Gitlab API request
                                const payload = {
                                    branch: projectBranch,
                                    commit_message: `${action === 'create' ? 'Add' : 'Update'} chat: ${data.name}`,
                                    actions: [
                                        {
                                            action: action,
                                            file_path: filePath,
                                            content: JSON.stringify(data, null, 2)
                                        }
                                    ]
                                };

                                // Make the Gitlab API request to create or update the file
                                const gitlabUrl = `https://${gitlabDomain}/api/v4/projects/${gitlabProjectId}/repository/commits`;
                                fetch(gitlabUrl, {
                                    method: 'POST',
                                    headers: {
                                        'PRIVATE-TOKEN': gitlabToken,
                                        'Content-Type': 'application/json'
                                    },
                                    body: JSON.stringify(payload)
                                })
                                    .then(response => {
                                        if (response.status === 401) {
                                            alert('Unauthorized: Invalid Gitlab token');
                                        } else if (response.status !== 201) {
                                            return response.json().then(data => {
                                                throw new Error(JSON.stringify(data));
                                            });
                                        } else {
                                            return response.json();
                                        }
                                    })
                                    .then(data => {
                                        console.log('Gitlab API Response:', data);
                                        window.open(data.web_url, '_blank');
                                    })
                                    .catch(error => {
                                        console.error('Error uploading to Gitlab:', error);
                                        alert(`Failed to upload chat to Gitlab. Error: ${error.message}`);
                                    });
                            })
                            .catch(error => {
                                console.error('Error checking file existence:', error);
                                alert('Failed to check file existence on Gitlab.');
                            });
                    })
                    .catch(error => {
                        console.error('Error fetching API data:', error);
                        alert('Failed to fetch chat data from the API.');
                    });
            });
        });
    }

    // Function to insert the "Upload To Gitlab" button before the chat controls
    function insertUploadToGitlabButton() {
        const chatControls = document.querySelector('[data-testid="chat-controls"]');
        const existingButton = document.getElementById('upload-to-gitlab-btn');
        if (chatControls && !existingButton) {
            const uploadButton = createUploadToGitlabButton();
            chatControls.parentNode.insertBefore(uploadButton, chatControls);
        }
    }

    // Observe changes in the DOM and insert the "Upload To Gitlab" button when the chat controls appear
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                insertUploadToGitlabButton();
            }
        });
    });

    // Configure the observer to watch for changes in the entire document
    const config = { childList: true, subtree: true };
    observer.observe(document.body, config);

    // Insert the "Upload To Gitlab" button initially if the chat controls are already present
    insertUploadToGitlabButton();
})();