From 8e7919765a695adf0796ff190db176003d98a4b2 Mon Sep 17 00:00:00 2001 From: Siddharth Venkumahanti Date: Thu, 14 Nov 2024 04:22:07 -0800 Subject: [PATCH] feat: Add chat history backup and restore functionality with Chrome extension support --- app/components/chat/ChatBackup.tsx | 0 app/components/sidebar/Menu.client.tsx | 58 ++++++++++++++++++++++---- app/utils/backup.ts | 0 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 app/components/chat/ChatBackup.tsx create mode 100644 app/utils/backup.ts diff --git a/app/components/chat/ChatBackup.tsx b/app/components/chat/ChatBackup.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index 7960f7820..b5c30ffc6 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -154,25 +154,69 @@ export function Menu() { version: backupData.version, hasHistory: !!backupData.history, historyIsArray: Array.isArray(backupData.history), - historyLength: backupData.history?.length + historyLength: backupData.history?.length, + rawKeys: Object.keys(backupData) }); - - if (!backupData.version || !backupData.history || !Array.isArray(backupData.history)) { - throw new Error('Invalid backup file format'); - } if (!db) { throw new Error('Database not initialized'); } + let chatHistory; + + // Handle different backup formats + if (backupData.version && backupData.history) { + // Our standard format + chatHistory = backupData.history; + } else if (backupData.boltHistory) { + // Chrome extension IndexedDB backup format + chatHistory = Object.values(backupData.boltHistory.chats || {}); + logger.info('Detected Chrome extension backup format', { + itemCount: chatHistory.length, + sampleItem: chatHistory[0] + }); + } else if (Array.isArray(backupData)) { + // Direct array format + chatHistory = backupData; + } else { + // Try to find any object with chat-like properties + const possibleChats = Object.values(backupData).find(value => + Array.isArray(value) || + (typeof value === 'object' && value !== null && 'messages' in value) + ); + + if (possibleChats) { + chatHistory = Array.isArray(possibleChats) ? possibleChats : [possibleChats]; + logger.info('Found possible chat data in alternate format', { + itemCount: chatHistory.length, + sampleItem: chatHistory[0] + }); + } else { + throw new Error('Unrecognized backup file format'); + } + } + + // Validate and normalize chat items + const normalizedHistory = chatHistory.map(item => { + if (!item.id || !Array.isArray(item.messages)) { + throw new Error('Invalid chat item format'); + } + return { + id: item.id, + messages: item.messages, + urlId: item.urlId || item.id, + description: item.description || `Imported chat ${item.id}` + }; + }); + // Store each chat history item logger.info('Starting import of chat history items'); - for (const item of backupData.history) { + for (const item of normalizedHistory) { logger.info('Importing chat item:', { id: item.id, description: item.description }); await setMessages(db, item.id, item.messages, item.urlId, item.description); } - toast.success('Chat history imported successfully'); + toast.success(`Successfully imported ${normalizedHistory.length} chats`); // Reload the page to show imported chats window.location.reload(); } catch (error) { diff --git a/app/utils/backup.ts b/app/utils/backup.ts new file mode 100644 index 000000000..e69de29bb