From b9dc2cc0f9a88dae1494cb308f40d59c8cd8007c Mon Sep 17 00:00:00 2001 From: Will Chen Date: Fri, 2 May 2025 15:35:39 -0700 Subject: [PATCH] Undo button (#76) --- src/components/chat/MessagesList.tsx | 177 +++++++++++++++++++-------- src/ipc/handlers/app_handlers.ts | 1 - src/ipc/ipc_client.ts | 14 ++- 3 files changed, 136 insertions(+), 56 deletions(-) diff --git a/src/components/chat/MessagesList.tsx b/src/components/chat/MessagesList.tsx index 7836a34..e44edda 100644 --- a/src/components/chat/MessagesList.tsx +++ b/src/components/chat/MessagesList.tsx @@ -1,17 +1,19 @@ import type React from "react"; import type { Message } from "@/ipc/ipc_types"; -import { forwardRef } from "react"; +import { forwardRef, useState } from "react"; import ChatMessage from "./ChatMessage"; import { SetupBanner } from "../SetupBanner"; import { useSettings } from "@/hooks/useSettings"; import { useStreamChat } from "@/hooks/useStreamChat"; import { selectedChatIdAtom } from "@/atoms/chatAtoms"; -import { useAtom, useAtomValue } from "jotai"; -import { RefreshCw } from "lucide-react"; +import { useAtom, useAtomValue, useSetAtom } from "jotai"; +import { Loader2, RefreshCw, Undo } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useVersions } from "@/hooks/useVersions"; import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { showError } from "@/lib/toast"; +import { IpcClient } from "@/ipc/ipc_client"; +import { chatMessagesAtom } from "@/atoms/chatAtoms"; interface MessagesListProps { messages: Message[]; @@ -25,6 +27,10 @@ export const MessagesList = forwardRef( const { streamMessage, isStreaming, error, setError } = useStreamChat(); const { isAnyProviderSetup } = useSettings(); const selectedChatId = useAtomValue(selectedChatIdAtom); + const setMessages = useSetAtom(chatMessagesAtom); + const [isUndoLoading, setIsUndoLoading] = useState(false); + const [isRetryLoading, setIsRetryLoading] = useState(false); + return (
{messages.length > 0 ? ( @@ -40,67 +46,136 @@ export const MessagesList = forwardRef(
)} {messages.length > 0 && !isStreaming && ( -
+
+ {messages[messages.length - 1].role === "assistant" && ( + + )}
diff --git a/src/ipc/handlers/app_handlers.ts b/src/ipc/handlers/app_handlers.ts index 8d22326..5249795 100644 --- a/src/ipc/handlers/app_handlers.ts +++ b/src/ipc/handlers/app_handlers.ts @@ -494,7 +494,6 @@ export function registerAppHandlers() { _, { appId, previousVersionId }: { appId: number; previousVersionId: string } ) => { - logger.log(`Reverting to version ${previousVersionId} for app ${appId}`); return withLock(appId, async () => { const app = await db.query.apps.findFirst({ where: eq(apps.id, appId), diff --git a/src/ipc/ipc_client.ts b/src/ipc/ipc_client.ts index ab55a47..a11932a 100644 --- a/src/ipc/ipc_client.ts +++ b/src/ipc/ipc_client.ts @@ -787,25 +787,31 @@ export class IpcClient { public async listLocalOllamaModels(): Promise { try { - const response = await this.ipcRenderer.invoke("local-models:list-ollama"); + const response = await this.ipcRenderer.invoke( + "local-models:list-ollama" + ); return response?.models || []; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to fetch Ollama models: ${error.message}`); } - throw new Error('Failed to fetch Ollama models: Unknown error occurred'); + throw new Error("Failed to fetch Ollama models: Unknown error occurred"); } } public async listLocalLMStudioModels(): Promise { try { - const response = await this.ipcRenderer.invoke("local-models:list-lmstudio"); + const response = await this.ipcRenderer.invoke( + "local-models:list-lmstudio" + ); return response?.models || []; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to fetch LM Studio models: ${error.message}`); } - throw new Error('Failed to fetch LM Studio models: Unknown error occurred'); + throw new Error( + "Failed to fetch LM Studio models: Unknown error occurred" + ); } }