Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,35 +79,32 @@
<KeyboardShortcutInfo keys={['cmd', 'k']} />
</Button>

<!-- Export All Conversations -->
<Button
class="w-full justify-start text-sm"
onclick={() => {
exportAllConversations();
importConversations().catch((err) => {
console.error('Import failed:', err);
// Optional: show toast or dialog
});
}}
variant="ghost"
>
<div class="flex items-center gap-2">
<Download class="h-4 w-4" />
Export all
<Upload class="h-4 w-4" />
Import conversations
</div>
</Button>

<!-- Import Conversations -->
<Button
class="w-full justify-start text-sm"
onclick={() => {
importConversations().catch(err => {
console.error('Import failed:', err);
// Optional: show toast or dialog
});
exportAllConversations();
}}
variant="ghost"
>

<div class="flex items-center gap-2">
<Upload class="h-4 w-4" />
Import all
<Download class="h-4 w-4" />
Export all conversations
</div>
</Button>
{/if}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
},
{
icon: Download,
label: 'Download',
label: 'Export',
onclick: (e) => {
e.stopPropagation();
downloadConversation(conversation.id);
Expand Down
61 changes: 36 additions & 25 deletions tools/server/webui/src/lib/stores/chat.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -958,27 +958,27 @@ class ChatStore {
* @param convId - The conversation ID to download
*/
async downloadConversation(convId: string): Promise<void> {
if (!this.activeConversation || this.activeConversation.id !== convId) {
// Load the conversation if not currently active
const conversation = await DatabaseStore.getConversation(convId);
if (!conversation) return;

const messages = await DatabaseStore.getConversationMessages(convId);
const conversationData = {
conv: conversation,
messages
};
if (!this.activeConversation || this.activeConversation.id !== convId) {
// Load the conversation if not currently active
const conversation = await DatabaseStore.getConversation(convId);
if (!conversation) return;

this.triggerDownload(conversationData);
} else {
// Use current active conversation data
const conversationData: ExportedConversations = {
conv: this.activeConversation!,
messages: this.activeMessages
};
const messages = await DatabaseStore.getConversationMessages(convId);
const conversationData = {
conv: conversation,
messages
};

this.triggerDownload(conversationData);
}
this.triggerDownload(conversationData);
} else {
// Use current active conversation data
const conversationData: ExportedConversations = {
conv: this.activeConversation!,
messages: this.activeMessages
};

this.triggerDownload(conversationData);
}
}

/**
Expand All @@ -987,20 +987,24 @@ class ChatStore {
* @param filename - Optional filename
*/
private triggerDownload(data: ExportedConversations, filename?: string): void {
const conversation = 'conv' in data ? data.conv : (Array.isArray(data) ? data[0]?.conv : undefined);
const conversation =
'conv' in data ? data.conv : Array.isArray(data) ? data[0]?.conv : undefined;
if (!conversation) {
console.error('Invalid data: missing conversation');
return;
}
const conversationName = conversation.name ? conversation.name.trim() : '';
const convId = conversation.id || 'unknown';
const truncatedSuffix = conversationName.toLowerCase()
.replace(/[^a-z0-9]/gi, '_').replace(/_+/g, '_').substring(0, 20);
const truncatedSuffix = conversationName
.toLowerCase()
.replace(/[^a-z0-9]/gi, '_')
.replace(/_+/g, '_')
.substring(0, 20);
const downloadFilename = filename || `conversation_${convId}_${truncatedSuffix}.json`;

const conversationJson = JSON.stringify(data, null, 2);
const blob = new Blob([conversationJson], {
type: 'application/json',
type: 'application/json'
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
Expand Down Expand Up @@ -1073,11 +1077,18 @@ class ChatStore {

if (Array.isArray(parsedData)) {
importedData = parsedData;
} else if (parsedData && typeof parsedData === 'object' && 'conv' in parsedData && 'messages' in parsedData) {
} else if (
parsedData &&
typeof parsedData === 'object' &&
'conv' in parsedData &&
'messages' in parsedData
) {
// Single conversation object
importedData = [parsedData];
} else {
throw new Error('Invalid file format: expected array of conversations or single conversation object');
throw new Error(
'Invalid file format: expected array of conversations or single conversation object'
);
}

const result = await DatabaseStore.importConversations(importedData);
Expand Down
1 change: 0 additions & 1 deletion tools/server/webui/src/lib/stores/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,5 +381,4 @@ export class DatabaseStore {
return { imported: importedCount, skipped: skippedCount };
});
}

}