Add Claude Conversation Exporter Chrome extension
Features: - Export individual conversations or bulk export all conversations - Multiple formats: JSON (with full data), Markdown, Plain Text - Browse page with search, filter by model, and sort capabilities - Automatic model inference for conversations with null models (default model) - Branch-aware exports (only exports current conversation branch) - ZIP archive creation for bulk exports with progress tracking - Organization ID configuration via options page Key capabilities: - Preserves complete model information (unlike official Claude.ai export) - Infers historical models based on conversation date - Plain text format uses Human:/Assistant: prefixes (abbreviated to H:/A:) - Handles conversation branches correctly - Beautiful UI with gradient-themed icons and header graphics Technical implementation: - Manifest V3 Chrome extension - Content script injection for Claude.ai pages - Background service worker for tab management - JSZip integration for creating ZIP archives - Shared utilities for code reuse Created by Claude Opus 4.1 in collaboration with human developer
This commit is contained in:
@@ -303,6 +303,9 @@ async function exportConversation(conversationId, conversationName) {
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Infer model if null
|
||||
data.model = inferModel(data);
|
||||
|
||||
let content, filename, type;
|
||||
switch (format) {
|
||||
case 'markdown':
|
||||
@@ -387,6 +390,9 @@ async function exportAllFiltered() {
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Infer model if null
|
||||
data.model = inferModel(data);
|
||||
|
||||
// Generate filename and content based on format
|
||||
let content, filename;
|
||||
const safeName = conv.name.replace(/[<>:"/\\|?*]/g, '_'); // Remove invalid filename characters
|
||||
|
||||
+38
@@ -1,5 +1,35 @@
|
||||
// Note: Organization ID is now stored in extension settings
|
||||
// Users need to configure it in the extension options page
|
||||
|
||||
// Default model timeline for null models
|
||||
const DEFAULT_MODEL_TIMELINE = [
|
||||
{ date: new Date('2024-01-01'), model: 'claude-3-sonnet-20240229' }, // Before June 20, 2024
|
||||
{ date: new Date('2024-06-20'), model: 'claude-3-5-sonnet-20240620' }, // Starting June 20, 2024
|
||||
{ date: new Date('2024-10-22'), model: 'claude-3-5-sonnet-20241022' }, // Starting October 22, 2024
|
||||
{ date: new Date('2025-02-29'), model: 'claude-3-7-sonnet-20250219' }, // Starting February 29, 2025
|
||||
{ date: new Date('2025-05-14'), model: 'claude-sonnet-4-20250514' } // Starting May 14, 2025
|
||||
];
|
||||
|
||||
// Infer model for conversations with null model based on date
|
||||
function inferModel(conversation) {
|
||||
if (conversation.model) {
|
||||
return conversation.model;
|
||||
}
|
||||
|
||||
// Use created_at date to determine which default model was active
|
||||
const conversationDate = new Date(conversation.created_at);
|
||||
|
||||
// Find the appropriate model based on the conversation date
|
||||
// Start from the end and work backwards to find the right period
|
||||
for (let i = DEFAULT_MODEL_TIMELINE.length - 1; i >= 0; i--) {
|
||||
if (conversationDate >= DEFAULT_MODEL_TIMELINE[i].date) {
|
||||
return DEFAULT_MODEL_TIMELINE[i].model;
|
||||
}
|
||||
}
|
||||
|
||||
// If date is before all known dates, use the first model
|
||||
return DEFAULT_MODEL_TIMELINE[0].model;
|
||||
}
|
||||
|
||||
// Fetch conversation data
|
||||
async function fetchConversation(orgId, conversationId) {
|
||||
@@ -175,6 +205,10 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
fetchConversation(request.orgId, request.conversationId)
|
||||
.then(data => {
|
||||
console.log('Conversation data fetched successfully:', data);
|
||||
|
||||
// Infer model if null
|
||||
data.model = inferModel(data);
|
||||
|
||||
let content, filename, type;
|
||||
|
||||
switch (request.format) {
|
||||
@@ -232,6 +266,10 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
try {
|
||||
console.log(`Fetching full conversation ${count + 1}/${conversations.length}: ${conv.uuid}`);
|
||||
const fullConv = await fetchConversation(request.orgId, conv.uuid);
|
||||
|
||||
// Infer model if null
|
||||
fullConv.model = inferModel(fullConv);
|
||||
|
||||
let content, filename, type;
|
||||
|
||||
if (request.format === 'markdown') {
|
||||
|
||||
Reference in New Issue
Block a user