diff --git a/app/components/sidebar/HistoryItem.tsx b/app/components/sidebar/HistoryItem.tsx index 8022e4d2..df270c8c 100644 --- a/app/components/sidebar/HistoryItem.tsx +++ b/app/components/sidebar/HistoryItem.tsx @@ -5,9 +5,10 @@ import { type ChatHistoryItem } from '~/lib/persistence'; interface HistoryItemProps { item: ChatHistoryItem; onDelete?: (event: React.UIEvent) => void; + onDuplicate?: (id: string) => void; } -export function HistoryItem({ item, onDelete }: HistoryItemProps) { +export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) { const [hovering, setHovering] = useState(false); const hoverRef = useRef(null); @@ -44,7 +45,14 @@ export function HistoryItem({ item, onDelete }: HistoryItemProps) { {item.description}
{hovering && ( -
+
+ {onDuplicate && ( +
))} diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts index 7a952e34..5b96f009 100644 --- a/app/lib/persistence/db.ts +++ b/app/lib/persistence/db.ts @@ -158,3 +158,23 @@ async function getUrlIds(db: IDBDatabase): Promise { }; }); } + +export async function duplicateChat(db: IDBDatabase, id: string): Promise { + const chat = await getMessages(db, id); + if (!chat) { + throw new Error('Chat not found'); + } + + const newId = await getNextId(db); + const newUrlId = await getUrlId(db, newId); // Get a new urlId for the duplicated chat + + await setMessages( + db, + newId, + chat.messages, + newUrlId, // Use the new urlId + `${chat.description || 'Chat'} (copy)` + ); + + return newUrlId; // Return the urlId instead of id for navigation +} diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index 4b416031..2790a8b7 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -4,7 +4,7 @@ import { atom } from 'nanostores'; import type { Message } from 'ai'; import { toast } from 'react-toastify'; import { workbenchStore } from '~/lib/stores/workbench'; -import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db'; +import { getMessages, getNextId, getUrlId, openDatabase, setMessages, duplicateChat } from './db'; export interface ChatHistoryItem { id: string; @@ -46,11 +46,11 @@ export function useChatHistory() { .then((storedMessages) => { if (storedMessages && storedMessages.messages.length > 0) { const rewindId = searchParams.get('rewindId'); - const filteredMessages = rewindId - ? storedMessages.messages.slice(0, + const filteredMessages = rewindId + ? storedMessages.messages.slice(0, storedMessages.messages.findIndex(m => m.id === rewindId) + 1) : storedMessages.messages; - + setInitialMessages(filteredMessages); setUrlId(storedMessages.urlId); description.set(storedMessages.description); @@ -100,6 +100,19 @@ export function useChatHistory() { await setMessages(db, chatId.get() as string, messages, urlId, description.get()); }, + duplicateCurrentChat: async (listItemId:string) => { + if (!db || (!mixedId && !listItemId)) { + return; + } + + try { + const newId = await duplicateChat(db, mixedId || listItemId); + navigate(`/chat/${newId}`); + toast.success('Chat duplicated successfully'); + } catch (error) { + toast.error('Failed to duplicate chat'); + } + } }; }