From 37e8aec9207a96946f02f293951c9a10755dc7ed Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 3 Mar 2026 23:14:11 +0000 Subject: [PATCH] feat(chat): add conversation import from JSON (M8.8) Import AIUI JSON exports and Claude.ai export format via file picker. Merges imported conversations into existing data without overwriting. Shows import summary with count and format detected. Co-Authored-By: Claude Opus 4.6 --- .../app/src/components/chat/ChatHeader.vue | 62 ++++++++++++ packages/app/src/utils/conversation-import.ts | 98 +++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 packages/app/src/utils/conversation-import.ts diff --git a/packages/app/src/components/chat/ChatHeader.vue b/packages/app/src/components/chat/ChatHeader.vue index 91324649..a3c211ef 100644 --- a/packages/app/src/components/chat/ChatHeader.vue +++ b/packages/app/src/components/chat/ChatHeader.vue @@ -191,6 +191,12 @@ Plain text (.txt)
+
@@ -210,6 +233,7 @@ import { useChatStore } from '@/stores/chat' import { useAI } from '@/composables/useAI' import { useContentPanel } from '@/composables/useContentPanel' import { downloadConversation, type ExportFormat } from '@/utils/conversation-export' +import { parseImportFile } from '@/utils/conversation-import' defineProps<{ title: string @@ -330,6 +354,44 @@ function handleDelete() { chatStore.deleteConversation(id) showMenu.value = false } + +const importInputRef = ref(null) +const importStatus = ref(null) + +function triggerImport() { + showMenu.value = false + importInputRef.value?.click() +} + +async function handleImportFile(e: Event) { + const input = e.target as HTMLInputElement + const file = input.files?.[0] + if (!file) return + + try { + const text = await file.text() + const result = parseImportFile(text) + + if (result.error || result.conversations.length === 0) { + importStatus.value = `Error: ${result.error ?? 'No conversations found'}` + } else { + for (const conv of result.conversations) { + chatStore.conversations.set(conv.id, conv) + } + const count = result.conversations.length + importStatus.value = `Imported ${count} conversation${count > 1 ? 's' : ''} (${result.format})` + // Switch to first imported conversation + chatStore.setActiveConversation(result.conversations[0].id) + } + } catch { + importStatus.value = 'Error: Failed to read file' + } + + // Clear file input for re-use + input.value = '' + // Auto-hide status after 3 seconds + setTimeout(() => { importStatus.value = null }, 3000) +}