@@ -3,6 +3,7 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
33import  {  oneDark ,  oneLight  }  from  'react-syntax-highlighter/dist/esm/styles/prism' ; 
44
55import  CopyIcon  from  "./icons/CopyIcon" ; 
6+ import  PencilIcon  from  "./icons/PencilIcon" ; 
67import  ThinkBlock  from  "./ThinkBlock" ; 
78import  {  AVAILABLE_MODELS  }  from  "./ModelSelector" ; 
89
@@ -196,9 +197,11 @@ function MarkdownWithSyntaxHighlighting({ content, isDark, isGenerating = false,
196197  ) ; 
197198} 
198199
199- export  default  function  Chat ( {  messages,  isRunning,  loading =  false ,  selectedModel } )  { 
200+ export  default  function  Chat ( {  messages,  isRunning,  loading =  false ,  selectedModel,  onEditMessage  } )  { 
200201  const  empty  =  messages . length  ===  0 ; 
201202  const  [ copiedMessageIndex ,  setCopiedMessageIndex ]  =  useState ( null ) ; 
203+   const  [ editingMessageIndex ,  setEditingMessageIndex ]  =  useState ( null ) ; 
204+   const  [ editingMessageText ,  setEditingMessageText ]  =  useState ( "" ) ; 
202205
203206  // Detect if dark theme is active 
204207  const  isDark  =  useMemo ( ( )  =>  { 
@@ -221,6 +224,25 @@ export default function Chat({ messages, isRunning, loading = false, selectedMod
221224    } 
222225  } ; 
223226
227+   // Edit message functions 
228+   const  startEditing  =  ( messageIndex ,  messageText )  =>  { 
229+     setEditingMessageIndex ( messageIndex ) ; 
230+     setEditingMessageText ( messageText ) ; 
231+   } ; 
232+ 
233+   const  cancelEditing  =  ( )  =>  { 
234+     setEditingMessageIndex ( null ) ; 
235+     setEditingMessageText ( "" ) ; 
236+   } ; 
237+ 
238+   const  submitEdit  =  ( )  =>  { 
239+     if  ( editingMessageText . trim ( )  &&  onEditMessage )  { 
240+       onEditMessage ( editingMessageIndex ,  editingMessageText . trim ( ) ) ; 
241+       setEditingMessageIndex ( null ) ; 
242+       setEditingMessageText ( "" ) ; 
243+     } 
244+   } ; 
245+ 
224246  useEffect ( ( )  =>  { 
225247    // Handle MathJax 
226248    if  ( window . MathJax )  { 
@@ -235,7 +257,7 @@ export default function Chat({ messages, isRunning, loading = false, selectedMod
235257      }   ${ empty  ? "flex flex-col items-center justify-end"  : "space-y-4" } } 
236258    > 
237259      { messages . map ( ( msg ,  i )  =>  ( 
238-           < div  key = { `message-${ i }  }  className = { `flex ${ msg . role  ===  "user"  ? "justify-end"  : "justify-start" }  } > 
260+           < div  key = { `message-${ i }  }  className = { `flex ${ msg . role  ===  "user"  ? ( editingMessageIndex   ===   i  ?  "justify-start"   :  "justify- end")  : "justify-start" }  } > 
239261            { msg . role  ===  "assistant"  ? ( 
240262              < div  className = "relative group w-full max-w-none" > 
241263                < div  className = "min-h-6 text-gray-800 dark:text-gray-200 overflow-wrap-anywhere w-full" > 
@@ -268,10 +290,62 @@ export default function Chat({ messages, isRunning, loading = false, selectedMod
268290                ) } 
269291              </ div > 
270292            )  : ( 
271-               < div  className = "bg-gray-100 dark:bg-gray-600 text-gray-800 dark:text-gray-200 rounded-2xl px-4 py-2 max-w-xs sm:max-w-sm md:max-w-lg lg:max-w-xl" > 
272-                 < div  className = "min-h-6 break-words" > 
273-                   { msg . content } 
274-                 </ div > 
293+               < div  className = { `relative group ${ editingMessageIndex  ===  i  ? 'w-full'  : '' }  } > 
294+                 { editingMessageIndex  ===  i  ? ( 
295+                   // Editing mode 
296+                   < div  className = "bg-gray-100 dark:bg-gray-600 rounded-2xl px-4 py-2 w-full" > 
297+                     < textarea 
298+                       value = { editingMessageText } 
299+                       onChange = { ( e )  =>  setEditingMessageText ( e . target . value ) } 
300+                       className = "w-full min-h-[60px] bg-transparent border-none outline-none text-gray-800 dark:text-gray-200 resize-none" 
301+                       autoFocus 
302+                       onKeyDown = { ( e )  =>  { 
303+                         if  ( e . key  ===  'Enter'  &&  ! e . shiftKey )  { 
304+                           e . preventDefault ( ) ; 
305+                           submitEdit ( ) ; 
306+                         }  else  if  ( e . key  ===  'Escape' )  { 
307+                           cancelEditing ( ) ; 
308+                         } 
309+                       } } 
310+                     /> 
311+                     < div  className = "flex gap-2 mt-2" > 
312+                       < button 
313+                         onClick = { cancelEditing } 
314+                         className = "px-2 py-1 text-xs bg-gray-200 dark:bg-gray-500 text-gray-700 dark:text-gray-200 rounded hover:bg-gray-300 dark:hover:bg-gray-400 transition-colors" 
315+                       > 
316+                         Cancel
317+                       </ button > 
318+                       < button 
319+                         onClick = { submitEdit } 
320+                         className = "px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors" 
321+                         disabled = { ! editingMessageText . trim ( ) } 
322+                       > 
323+                         Edit
324+                       </ button > 
325+                     </ div > 
326+                   </ div > 
327+                 )  : ( 
328+                   // Normal display mode 
329+                   < div > 
330+                     < div  className = "bg-gray-100 dark:bg-gray-600 text-gray-800 dark:text-gray-200 rounded-2xl px-4 py-2 max-w-xs sm:max-w-sm md:max-w-lg lg:max-w-xl" > 
331+                       < div  className = "min-h-6 break-words" > 
332+                         { msg . content } 
333+                       </ div > 
334+                     </ div > 
335+                     { ! isRunning  &&  ( 
336+                       < div  className = "flex justify-end mt-1 opacity-0 group-hover:opacity-100 transition-opacity" > 
337+                         < button 
338+                           onClick = { ( )  =>  startEditing ( i ,  msg . content ) } 
339+                           className = "flex items-center gap-1 px-2 py-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors" 
340+                           title = "Edit message" 
341+                         > 
342+                           < PencilIcon  className = "h-3 w-3"  /> 
343+                           Edit
344+                         </ button > 
345+                       </ div > 
346+                     ) } 
347+                   </ div > 
348+                 ) } 
275349              </ div > 
276350            ) } 
277351          </ div > 
0 commit comments