diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 8c5a140ae0a5..8795238f3924 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -166,6 +166,7 @@ function ChatRenderer( const [userMessage, threadRunOptions] = generateRequestPayload( qaPair.user ) + return regenerate({ threadId, userMessageId: qaPair.user.id, @@ -176,6 +177,35 @@ function ChatRenderer( } } + const onEditMessage = async (userMessageId: string) => { + if (!threadId) return + + const qaPair = qaPairs.find(o => o.user.id === userMessageId) + if (!qaPair?.user || !qaPair.assistant) return + + const userMessage = qaPair.user + let nextClientContext: Context[] = [] + + // restore client context + if (userMessage.relevantContext?.length) { + nextClientContext = nextClientContext.concat(userMessage.relevantContext) + } + + setRelevantContext(uniqWith(nextClientContext, isEqual)) + + // delete message pair + const nextQaPairs = qaPairs.filter(o => o.user.id !== userMessageId) + setQaPairs(nextQaPairs) + setInput(userMessage.message) + if (userMessage.activeContext) { + onNavigateToContext(userMessage.activeContext, { + openInEditor: true + }) + } + + deleteThreadMessagePair(threadId, qaPair?.user.id, qaPair?.assistant?.id) + } + // Reload the last AI chat response const onReload = async () => { if (!qaPairs?.length) return @@ -195,7 +225,7 @@ function ChatRenderer( const handleMessageAction = ( userMessageId: string, - actionType: 'delete' | 'regenerate' + actionType: MessageActionType ) => { switch (actionType) { case 'delete': @@ -204,6 +234,9 @@ function ChatRenderer( case 'regenerate': onRegenerateResponse(userMessageId) break + case 'edit': + onEditMessage(userMessageId) + break default: break } @@ -291,8 +324,9 @@ function ChatRenderer( userMessage: UserMessage ): [CreateMessageInput, ThreadRunOptionsInput] => { // use selectContext or activeContext for code query - const contextForCodeQuery = - userMessage?.selectContext || userMessage?.activeContext + const contextForCodeQuery: FileContext | undefined = + userMessage.selectContext || userMessage.activeContext + const codeQuery: InputMaybe = contextForCodeQuery ? { content: contextForCodeQuery.content ?? '', @@ -350,13 +384,14 @@ function ChatRenderer( }\n${'```'}\n` } - const newUserMessage = { + const newUserMessage: UserMessage = { ...userMessage, message: userMessage.message + selectCodeSnippet, - // For forward compatibility - activeSelection: activeSelection || userMessage.activeContext, // If no id is provided, set a fallback id. - id: userMessage.id ?? nanoid() + id: userMessage.id ?? nanoid(), + selectContext: userMessage.selectContext, + // For forward compatibility + activeContext: activeSelection || userMessage.activeContext } const nextQaPairs = [ @@ -374,7 +409,7 @@ function ChatRenderer( setQaPairs(nextQaPairs) - return sendUserMessage(...generateRequestPayload(newUserMessage)) + sendUserMessage(...generateRequestPayload(newUserMessage)) } ) diff --git a/ee/tabby-ui/components/chat/question-answer.tsx b/ee/tabby-ui/components/chat/question-answer.tsx index 50c514e92bec..cc1644e1fa54 100644 --- a/ee/tabby-ui/components/chat/question-answer.tsx +++ b/ee/tabby-ui/components/chat/question-answer.tsx @@ -25,7 +25,13 @@ import { import { CopyButton } from '../copy-button' import { ErrorMessageBlock, MessageMarkdown } from '../message-markdown' import { Button } from '../ui/button' -import { IconFile, IconRefresh, IconTrash, IconUser } from '../ui/icons' +import { + IconEdit, + IconFile, + IconRefresh, + IconTrash, + IconUser +} from '../ui/icons' import { Separator } from '../ui/separator' import { Skeleton } from '../ui/skeleton' import { MyAvatar } from '../user-avatar' @@ -202,7 +208,17 @@ function UserMessageCardActions(props: { message: UserMessage }) { + )} + {!isLoading && ( +