Skip to content

Commit 4fd5040

Browse files
feat: enhance chat import with multi-format support (stackblitz-labs#936)
* 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
1 parent 7004c89 commit 4fd5040

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

README.md

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

app/components/chat/chatExportAndImport/ImportButtons.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import type { Message } from 'ai';
22
import { toast } from 'react-toastify';
33
import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
44

5+
type ChatData = {
6+
messages?: Message[]; // Standard Bolt format
7+
description?: string; // Optional description
8+
};
9+
510
export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
611
return (
712
<div className="flex flex-col items-center justify-center w-auto">
@@ -20,14 +25,16 @@ export function ImportButtons(importChat: ((description: string, messages: Messa
2025
reader.onload = async (e) => {
2126
try {
2227
const content = e.target?.result as string;
23-
const data = JSON.parse(content);
28+
const data = JSON.parse(content) as ChatData;
2429

25-
if (!Array.isArray(data.messages)) {
26-
toast.error('Invalid chat file format');
30+
// Standard format
31+
if (Array.isArray(data.messages)) {
32+
await importChat(data.description || 'Imported Chat', data.messages);
33+
toast.success('Chat imported successfully');
34+
return;
2735
}
2836

29-
await importChat(data.description, data.messages);
30-
toast.success('Chat imported successfully');
37+
toast.error('Invalid chat file format');
3138
} catch (error: unknown) {
3239
if (error instanceof Error) {
3340
toast.error('Failed to parse chat file: ' + error.message);

app/components/settings/data/DataTab.tsx

+78-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import React, { useState } from 'react';
22
import { useNavigate } from '@remix-run/react';
33
import Cookies from 'js-cookie';
44
import { toast } from 'react-toastify';
5-
import { db, deleteById, getAll } from '~/lib/persistence';
5+
import { db, deleteById, getAll, setMessages } from '~/lib/persistence';
66
import { logStore } from '~/lib/stores/logs';
77
import { classNames } from '~/utils/classNames';
8+
import type { Message } from 'ai';
89

910
// List of supported providers that can have API keys
1011
const API_KEY_PROVIDERS = [
@@ -232,6 +233,76 @@ export default function DataTab() {
232233
event.target.value = '';
233234
};
234235

236+
const processChatData = (data: any): Array<{
237+
id: string;
238+
messages: Message[];
239+
description: string;
240+
urlId?: string;
241+
}> => {
242+
// Handle Bolt standard format (single chat)
243+
if (data.messages && Array.isArray(data.messages)) {
244+
const chatId = crypto.randomUUID();
245+
return [{
246+
id: chatId,
247+
messages: data.messages,
248+
description: data.description || 'Imported Chat',
249+
urlId: chatId
250+
}];
251+
}
252+
253+
// Handle Bolt export format (multiple chats)
254+
if (data.chats && Array.isArray(data.chats)) {
255+
return data.chats.map((chat: { id?: string; messages: Message[]; description?: string; urlId?: string; }) => ({
256+
id: chat.id || crypto.randomUUID(),
257+
messages: chat.messages,
258+
description: chat.description || 'Imported Chat',
259+
urlId: chat.urlId,
260+
}));
261+
}
262+
263+
console.error('No matching format found for:', data);
264+
throw new Error('Unsupported chat format');
265+
};
266+
267+
const handleImportChats = () => {
268+
const input = document.createElement('input');
269+
input.type = 'file';
270+
input.accept = '.json';
271+
272+
input.onchange = async (e) => {
273+
const file = (e.target as HTMLInputElement).files?.[0];
274+
275+
if (!file || !db) {
276+
toast.error('Something went wrong');
277+
return;
278+
}
279+
280+
try {
281+
const content = await file.text();
282+
const data = JSON.parse(content);
283+
const chatsToImport = processChatData(data);
284+
285+
for (const chat of chatsToImport) {
286+
await setMessages(db, chat.id, chat.messages, chat.urlId, chat.description);
287+
}
288+
289+
logStore.logSystem('Chats imported successfully', { count: chatsToImport.length });
290+
toast.success(`Successfully imported ${chatsToImport.length} chat${chatsToImport.length > 1 ? 's' : ''}`);
291+
window.location.reload();
292+
} catch (error) {
293+
if (error instanceof Error) {
294+
logStore.logError('Failed to import chats:', error);
295+
toast.error('Failed to import chats: ' + error.message);
296+
} else {
297+
toast.error('Failed to import chats');
298+
}
299+
console.error(error);
300+
}
301+
};
302+
303+
input.click();
304+
};
305+
235306
return (
236307
<div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
237308
<div className="mb-6">
@@ -248,6 +319,12 @@ export default function DataTab() {
248319
>
249320
Export All Chats
250321
</button>
322+
<button
323+
onClick={handleImportChats}
324+
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"
325+
>
326+
Import Chats
327+
</button>
251328
<button
252329
onClick={handleDeleteAllChats}
253330
disabled={isDeleting}

0 commit comments

Comments
 (0)