feat: enhance chat import with multi-format support (#936)
Some checks failed
Docker Publish / docker-build-publish (push) Has been cancelled
Update Stable Branch / prepare-release (push) Has been cancelled

* feat: enhance chat import with multi-format support

- Add support for importing chats from different formats:
  - Standard Bolt format
  - Chrome extension format
  - History array format
  - Bolt export format
- Add Import Chats button to Data Management
- Add proper error handling and logging
- Update README with backup/restore feature

* refactor: simplify chat import formats

- Remove multi-format support from DataTab
- Keep only standard Bolt export formats
- Simplify ImportButtons to handle standard format only
This commit is contained in:
Siddharth V 2025-01-07 06:46:42 -08:00 committed by GitHub
parent 7004c897f7
commit 4fd5040355
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 91 additions and 6 deletions

View File

@ -49,6 +49,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed
- ✅ Bolt terminal to see the output of LLM run commands (@thecodacus)
- ✅ Streaming of code output (@thecodacus)
- ✅ Ability to revert code to earlier version (@wonderwhy-er)
- ✅ Chat history backup and restore functionality (@sidbetatester)
- ✅ Cohere Integration (@hasanraiyan)
- ✅ Dynamic model max token length (@hasanraiyan)
- ✅ Better prompt enhancing (@SujalXplores)

View File

@ -2,6 +2,11 @@ import type { Message } from 'ai';
import { toast } from 'react-toastify';
import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
type ChatData = {
messages?: Message[]; // Standard Bolt format
description?: string; // Optional description
};
export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
return (
<div className="flex flex-col items-center justify-center w-auto">
@ -20,14 +25,16 @@ export function ImportButtons(importChat: ((description: string, messages: Messa
reader.onload = async (e) => {
try {
const content = e.target?.result as string;
const data = JSON.parse(content);
const data = JSON.parse(content) as ChatData;
if (!Array.isArray(data.messages)) {
toast.error('Invalid chat file format');
// Standard format
if (Array.isArray(data.messages)) {
await importChat(data.description || 'Imported Chat', data.messages);
toast.success('Chat imported successfully');
return;
}
await importChat(data.description, data.messages);
toast.success('Chat imported successfully');
toast.error('Invalid chat file format');
} catch (error: unknown) {
if (error instanceof Error) {
toast.error('Failed to parse chat file: ' + error.message);

View File

@ -2,9 +2,10 @@ import React, { useState } from 'react';
import { useNavigate } from '@remix-run/react';
import Cookies from 'js-cookie';
import { toast } from 'react-toastify';
import { db, deleteById, getAll } from '~/lib/persistence';
import { db, deleteById, getAll, setMessages } from '~/lib/persistence';
import { logStore } from '~/lib/stores/logs';
import { classNames } from '~/utils/classNames';
import type { Message } from 'ai';
// List of supported providers that can have API keys
const API_KEY_PROVIDERS = [
@ -232,6 +233,76 @@ export default function DataTab() {
event.target.value = '';
};
const processChatData = (data: any): Array<{
id: string;
messages: Message[];
description: string;
urlId?: string;
}> => {
// Handle Bolt standard format (single chat)
if (data.messages && Array.isArray(data.messages)) {
const chatId = crypto.randomUUID();
return [{
id: chatId,
messages: data.messages,
description: data.description || 'Imported Chat',
urlId: chatId
}];
}
// Handle Bolt export format (multiple chats)
if (data.chats && Array.isArray(data.chats)) {
return data.chats.map((chat: { id?: string; messages: Message[]; description?: string; urlId?: string; }) => ({
id: chat.id || crypto.randomUUID(),
messages: chat.messages,
description: chat.description || 'Imported Chat',
urlId: chat.urlId,
}));
}
console.error('No matching format found for:', data);
throw new Error('Unsupported chat format');
};
const handleImportChats = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (!file || !db) {
toast.error('Something went wrong');
return;
}
try {
const content = await file.text();
const data = JSON.parse(content);
const chatsToImport = processChatData(data);
for (const chat of chatsToImport) {
await setMessages(db, chat.id, chat.messages, chat.urlId, chat.description);
}
logStore.logSystem('Chats imported successfully', { count: chatsToImport.length });
toast.success(`Successfully imported ${chatsToImport.length} chat${chatsToImport.length > 1 ? 's' : ''}`);
window.location.reload();
} catch (error) {
if (error instanceof Error) {
logStore.logError('Failed to import chats:', error);
toast.error('Failed to import chats: ' + error.message);
} else {
toast.error('Failed to import chats');
}
console.error(error);
}
};
input.click();
};
return (
<div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
<div className="mb-6">
@ -248,6 +319,12 @@ export default function DataTab() {
>
Export All Chats
</button>
<button
onClick={handleImportChats}
className="px-4 py-2 bg-bolt-elements-button-primary-background hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-textPrimary rounded-lg transition-colors"
>
Import Chats
</button>
<button
onClick={handleDeleteAllChats}
disabled={isDeleting}