// ==UserScript== // @name Claude Chat Exporter // @namespace lugia19.com // @match https://claude.ai/* // @version 1.1.0 // @author lugia19 // @license GPLv3 // @description Allows exporting chat conversations from claude.ai. // @grant none // @downloadURL none // ==/UserScript== (function () { 'use strict'; const SELECTORS = { USER_MESSAGE: '[data-testid="user-message"]', AI_MESSAGE: '.font-claude-message', }; function getConversationId() { const match = window.location.pathname.match(/\/chat\/([^/?]+)/); return match ? match[1] : null; } function createExportCard() { const container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 80px; left: calc(100% - 200px); background: #2D2D2D; border: 1px solid #3B3B3B; border-radius: 8px; z-index: 9999; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); cursor: move; user-select: none; width: 180px; `; // Header (always visible) const header = document.createElement('div'); header.style.cssText = ` display: flex; align-items: center; padding: 8px 10px; color: white; font-size: 12px; gap: 8px; `; const arrow = document.createElement('div'); arrow.innerHTML = '▼'; arrow.style.cssText = ` cursor: pointer; transition: transform 0.2s; `; header.appendChild(arrow); header.appendChild(document.createTextNode('Export Chat')); // Content container (collapsible) const content = document.createElement('div'); content.style.cssText = ` padding: 10px; display: flex; flex-direction: column; gap: 10px; `; const exportButton = document.createElement('button'); exportButton.textContent = 'Export'; exportButton.style.cssText = ` background: #3b82f6; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; width: 100%; `; const formatDropdown = document.createElement('select'); formatDropdown.innerHTML = ` `; formatDropdown.style.cssText = ` background: #3B3B3B; color: white; border: none; padding: 6px 12px; border-radius: 4px; width: 100%; appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat; background-position: right 8px center; background-size: 16px; `; content.appendChild(exportButton); content.appendChild(formatDropdown); container.appendChild(header); container.appendChild(content); document.body.appendChild(container); // Toggle collapse/expand let isCollapsed = false; arrow.addEventListener('click', (e) => { e.stopPropagation(); isCollapsed = !isCollapsed; content.style.display = isCollapsed ? 'none' : 'block'; arrow.style.transform = isCollapsed ? 'rotate(-90deg)' : ''; }); // Export functionality exportButton.addEventListener('click', async () => { const conversationId = getConversationId(); if (!conversationId) { alert('Not in a conversation.'); return; } const messages = await getMessages(); const format = formatDropdown.value; const filename = `Claude_export_${conversationId}.${format}`; const content = formatExport(messages, format); downloadFile(filename, content); }); // Dragging functionality let isDragging = false; let currentX; let currentY; let initialX; let initialY; header.addEventListener('mousedown', (e) => { if (e.target === arrow) return; isDragging = true; initialX = e.clientX - container.offsetLeft; initialY = e.clientY - container.offsetTop; header.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; container.style.left = `${currentX}px`; container.style.top = `${currentY}px`; }); document.addEventListener('mouseup', () => { isDragging = false; header.style.cursor = 'move'; }); } async function getMessages() { const userMessages = document.querySelectorAll(SELECTORS.USER_MESSAGE); const aiMessages = document.querySelectorAll(SELECTORS.AI_MESSAGE); const messages = []; for (let i = 0; i < Math.max(userMessages.length, aiMessages.length); i++) { if (i < userMessages.length) { messages.push({ role: 'user', content: userMessages[i].textContent }); } if (i < aiMessages.length) { messages.push({ role: 'assistant', content: aiMessages[i].textContent }); } } return messages; } function formatExport(messages, format) { switch (format) { case 'txt': return messages.map(msg => { const role = msg.role === 'user' ? 'User' : 'Assistant'; return `[${role}]\n${msg.content}\n`; }).join('\n'); case 'jsonl': return messages.map(JSON.stringify).join('\n'); default: throw new Error(`Unsupported format: ${format}`); } } function downloadFile(filename, content) { const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); } function initialize() { createExportCard(); } initialize(); })();