Add import, fix export

This commit is contained in:
eduardruzga
2024-11-23 00:23:45 +02:00
parent 9f49c25f96
commit fb34a4cb8f
9 changed files with 93 additions and 86 deletions

View File

@@ -12,7 +12,6 @@ import { Messages } from './Messages.client';
import { SendButton } from './SendButton.client';
import { APIKeyManager } from './APIKeyManager';
import Cookies from 'js-cookie';
import { exportChat, importChat } from '~/utils/chatExport';
import { toast } from 'react-toastify';
import * as Tooltip from '@radix-ui/react-tooltip';
@@ -90,6 +89,8 @@ interface BaseChatProps {
sendMessage?: (event: React.UIEvent, messageInput?: string) => void;
handleInputChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
enhancePrompt?: () => void;
importChat?: (description: string, messages: Message[]) => Promise<void>;
exportChat?: () => void;
}
export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
@@ -113,7 +114,9 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
sendMessage,
handleInputChange,
enhancePrompt,
handleStop
handleStop,
importChat,
exportChat
},
ref
) => {
@@ -292,7 +295,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
</>
)}
</IconButton>
<ClientOnly>{() => <ExportChatButton description={description} messages={messages}/>}</ClientOnly>
<ClientOnly>{() => <ExportChatButton exportChat={exportChat}/>}</ClientOnly>
</div>
{input.length > 3 ? (
<div className="text-xs text-bolt-elements-textTertiary">
@@ -317,18 +320,31 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
accept=".json"
onChange={async (e) => {
const file = e.target.files?.[0];
if (file) {
if (file && importChat) {
try {
const { messages: importedMessages } = await importChat(file);
// Import each message
for (const msg of importedMessages) {
await sendMessage(new Event('import') as unknown as React.UIEvent, msg.content);
}
toast.success('Chat imported successfully');
const reader = new FileReader();
reader.onload = async (e) => {
try {
const content = e.target?.result as string;
const data = JSON.parse(content);
if (!Array.isArray(data.messages)) {
toast.error('Invalid chat file format');
}
await importChat(data.description, data.messages);
toast.success('Chat imported successfully');
} catch (error) {
toast.error('Failed to parse chat file');
}
};
reader.onerror = () => toast.error('Failed to read chat file');
reader.readAsText(file);
} catch (error) {
toast.error(error instanceof Error ? error.message : 'Failed to import chat');
}
e.target.value = ''; // Reset file input
} else {
toast.error('Something went wrong');
}
}}
/>

View File

@@ -28,12 +28,12 @@ const logger = createScopedLogger('Chat');
export function Chat() {
renderLogger.trace('Chat');
const { ready, initialMessages, storeMessageHistory } = useChatHistory();
const { ready, initialMessages, storeMessageHistory, importChat, exportChat } = useChatHistory();
const title = useStore(description);
return (
<>
{ready && <ChatImpl description={title} initialMessages={initialMessages} storeMessageHistory={storeMessageHistory} />}
{ready && <ChatImpl description={title} initialMessages={initialMessages} exportChat={exportChat} storeMessageHistory={storeMessageHistory} importChat={importChat} />}
<ToastContainer
closeButton={({ closeToast }) => {
return (
@@ -68,9 +68,11 @@ export function Chat() {
interface ChatProps {
initialMessages: Message[];
storeMessageHistory: (messages: Message[]) => Promise<void>;
importChat: (description: string, messages: Message[]) => Promise<void>;
exportChat: () => void;
}
export const ChatImpl = memo(({ description, initialMessages, storeMessageHistory }: ChatProps) => {
export const ChatImpl = memo(({ description, initialMessages, storeMessageHistory, importChat, exportChat }: ChatProps) => {
useShortcuts();
const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -254,6 +256,8 @@ export const ChatImpl = memo(({ description, initialMessages, storeMessageHistor
handleInputChange={handleInputChange}
handleStop={abort}
description={description}
importChat={importChat}
exportChat={exportChat}
messages={messages.map((message, i) => {
if (message.role === 'user') {
return message;

View File

@@ -1,14 +1,12 @@
import WithTooltip from '~/components/ui/Tooltip';
import { IconButton } from '~/components/ui/IconButton';
import { exportChat } from '~/utils/chatExport';
import React from 'react';
import type { Message } from 'ai';
export const ExportChatButton = ({description, messages}: {description: string, messages: Message[]}) => {
export const ExportChatButton = ({exportChat}: {exportChat: () => void}) => {
return (<WithTooltip tooltip="Export Chat">
<IconButton
title="Export Chat"
onClick={() => exportChat(messages || [], description)}
onClick={exportChat}
>
<div className="i-ph:download-simple text-xl"></div>
</IconButton>

View File

@@ -33,7 +33,6 @@ export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props:
toast.error('Chat persistence is not available');
return;
}
const urlId = await forkChat(db, chatId.get()!, messageId);
window.location.href = `/chat/${urlId}`;
} catch (error) {

View File

@@ -1,16 +1,16 @@
import * as Dialog from '@radix-ui/react-dialog';
import { useEffect, useRef, useState } from 'react';
import { type ChatHistoryItem } from '~/lib/persistence';
import { exportChat } from '~/utils/chatExport';
import WithTooltip from '~/components/ui/Tooltip';
interface HistoryItemProps {
item: ChatHistoryItem;
onDelete?: (event: React.UIEvent) => void;
onDuplicate?: (id: string) => void;
exportChat: (id?: string) => void;
}
export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) {
export function HistoryItem({ item, onDelete, onDuplicate, exportChat }: HistoryItemProps) {
const [hovering, setHovering] = useState(false);
const hoverRef = useRef<HTMLDivElement>(null);
@@ -53,7 +53,8 @@ export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) {
className="i-ph:download-simple scale-110 mr-2"
onClick={(event) => {
event.preventDefault();
exportChat(item.messages, item.description);
exportChat(item.id);
//exportChat(item.messages, item.description);
}}
title="Export chat"
/>

View File

@@ -34,7 +34,7 @@ const menuVariants = {
type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
export function Menu() {
const { duplicateCurrentChat } = useChatHistory();
const { duplicateCurrentChat, exportChat } = useChatHistory();
const menuRef = useRef<HTMLDivElement>(null);
const [list, setList] = useState<ChatHistoryItem[]>([]);
const [open, setOpen] = useState(false);
@@ -102,7 +102,6 @@ export function Menu() {
const handleDeleteClick = (event: React.UIEvent, item: ChatHistoryItem) => {
event.preventDefault();
setDialogContent({ type: 'delete', item });
};
@@ -144,6 +143,7 @@ export function Menu() {
<HistoryItem
key={item.id}
item={item}
exportChat={exportChat}
onDelete={(event) => handleDeleteClick(event, item)}
onDuplicate={() => handleDuplicate(item.id)}
/>