diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index d71856d..b6f8b7d 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -16,7 +16,7 @@ import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; import { ChatOllama } from '@langchain/ollama'; import { ChatOpenAI } from '@langchain/openai'; import crypto from 'crypto'; -import { and, eq, gte } from 'drizzle-orm'; +import { and, eq, gt } from 'drizzle-orm'; import { EventEmitter } from 'stream'; export const runtime = 'nodejs'; @@ -210,11 +210,21 @@ const handleHistorySave = async ( }) .execute(); } else { + await db + .update(messagesSchema) + .set({ + content: message.content, + metadata: JSON.stringify({ + createdAt: new Date(), + }), + }) + .where(eq(messagesSchema.messageId, humanMessageId)) + .execute(); await db .delete(messagesSchema) .where( and( - gte(messagesSchema.id, messageExists.id), + gt(messagesSchema.id, messageExists.id), eq(messagesSchema.chatId, message.chatId), ), ) diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index 59de5d9..e8f3a05 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -20,6 +20,7 @@ const Chat = ({ setOptimizationMode, focusMode, setFocusMode, + handleEditMessage, }: { messages: Message[]; sendMessage: ( @@ -41,6 +42,7 @@ const Chat = ({ setOptimizationMode: (mode: string) => void; focusMode: string; setFocusMode: (mode: string) => void; + handleEditMessage: (messageId: string, content: string) => void; }) => { const [isAtBottom, setIsAtBottom] = useState(true); const [manuallyScrolledUp, setManuallyScrolledUp] = useState(false); @@ -180,6 +182,7 @@ const Chat = ({ isLast={isLast} rewrite={rewrite} sendMessage={sendMessage} + handleEditMessage={handleEditMessage} /> {!isLast && msg.role === 'assistant' && (
diff --git a/src/components/ChatWindow.tsx b/src/components/ChatWindow.tsx index 9c8e8ba..2485252 100644 --- a/src/components/ChatWindow.tsx +++ b/src/components/ChatWindow.tsx @@ -338,8 +338,8 @@ const ChatWindow = ({ id }: { id?: string }) => { message: string, options?: { messageId?: string; - rewriteIndex?: number; suggestions?: string[]; + editMode?: boolean; }, ) => { setScrollTrigger((x) => (x === 0 ? -1 : 0)); @@ -369,16 +369,16 @@ const ChatWindow = ({ id }: { id?: string }) => { let added = false; let messageChatHistory = chatHistory; - if (options?.rewriteIndex !== undefined) { - const rewriteIndex = options.rewriteIndex; + // If the user is editing or rewriting a message, we need to remove the messages after it + const rewriteIndex = messages.findIndex( + (msg) => msg.messageId === options?.messageId, + ); + if (rewriteIndex !== -1) { setMessages((prev) => { - return [...prev.slice(0, messages.length > 2 ? rewriteIndex - 1 : 0)]; + return [...prev.slice(0, rewriteIndex)]; }); - messageChatHistory = chatHistory.slice( - 0, - messages.length > 2 ? rewriteIndex - 1 : 0, - ); + messageChatHistory = chatHistory.slice(0, rewriteIndex); setChatHistory(messageChatHistory); setScrollTrigger((prev) => prev + 1); @@ -587,11 +587,28 @@ const ChatWindow = ({ id }: { id?: string }) => { ); if (messageIndex == -1) return; sendMessage(messages[messageIndex - 1].content, { - messageId: messageId, - rewriteIndex: messageIndex, + messageId: messages[messageIndex - 1].messageId, }); }; + const handleEditMessage = async (messageId: string, newContent: string) => { + // Get the index of the message being edited + const messageIndex = messages.findIndex( + (msg) => msg.messageId === messageId, + ); + if (messageIndex === -1) return; + + try { + sendMessage(newContent, { + messageId, + editMode: true, + }); + } catch (error) { + console.error('Error updating message:', error); + toast.error('Failed to update message'); + } + }; + useEffect(() => { if (isReady && initialMessage && isConfigReady) { sendMessage(initialMessage); @@ -638,6 +655,7 @@ const ChatWindow = ({ id }: { id?: string }) => { setOptimizationMode={setOptimizationMode} focusMode={focusMode} setFocusMode={setFocusMode} + handleEditMessage={handleEditMessage} /> ) : ( diff --git a/src/components/MessageBox.tsx b/src/components/MessageBox.tsx index e347952..312a4c3 100644 --- a/src/components/MessageBox.tsx +++ b/src/components/MessageBox.tsx @@ -1,81 +1,8 @@ import { cn } from '@/lib/utils'; -import { CheckCheck, CopyIcon } from 'lucide-react'; +import { Check, Pencil, X } from 'lucide-react'; import { useState } from 'react'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import { Message } from './ChatWindow'; import MessageTabs from './MessageTabs'; -import ThinkBox from './ThinkBox'; - -const ThinkTagProcessor = ({ children }: { children: React.ReactNode }) => { - return ; -}; - -const CodeBlock = ({ - className, - children, -}: { - className?: string; - children: React.ReactNode; -}) => { - // Extract language from className (format could be "language-javascript" or "lang-javascript") - let language = ''; - if (className) { - if (className.startsWith('language-')) { - language = className.replace('language-', ''); - } else if (className.startsWith('lang-')) { - language = className.replace('lang-', ''); - } - } - - const content = children as string; - - const [isCopied, setIsCopied] = useState(false); - - const handleCopyCode = () => { - navigator.clipboard.writeText(content); - setIsCopied(true); - setTimeout(() => setIsCopied(false), 2000); - }; - - console.log('Code block language:', language, 'Class name:', className); // For debugging - - return ( -
-
- {language} - -
- 1} - useInlineStyles={true} - PreTag="div" - > - {content} - -
- ); -}; const MessageBox = ({ message, @@ -85,6 +12,7 @@ const MessageBox = ({ isLast, rewrite, sendMessage, + handleEditMessage, }: { message: Message; messageIndex: number; @@ -100,7 +28,29 @@ const MessageBox = ({ suggestions?: string[]; }, ) => void; + handleEditMessage: (messageId: string, content: string) => void; }) => { + // Local state for editing functionality + const [isEditing, setIsEditing] = useState(false); + const [editedContent, setEditedContent] = useState(''); + + // Initialize editing + const startEditMessage = () => { + setIsEditing(true); + setEditedContent(message.content); + }; + + // Cancel editing + const cancelEditMessage = () => { + setIsEditing(false); + setEditedContent(''); + }; + + // Save edits + const saveEditMessage = () => { + handleEditMessage(message.messageId, editedContent); + setIsEditing(false); + }; return (
{message.role === 'user' && ( @@ -111,9 +61,51 @@ const MessageBox = ({ 'break-words', )} > -

- {message.content} -

+ {isEditing ? ( +
+