diff --git a/src/browser/App.stories.tsx b/src/browser/App.stories.tsx index b04b16cf25..fba7a7f5b3 100644 --- a/src/browser/App.stories.tsx +++ b/src/browser/App.stories.tsx @@ -67,6 +67,7 @@ function setupMockAPI(options: { sendMessage: () => Promise.resolve({ success: true, data: undefined }), resumeStream: () => Promise.resolve({ success: true, data: undefined }), interruptStream: () => Promise.resolve({ success: true, data: undefined }), + clearQueue: () => Promise.resolve({ success: true, data: undefined }), truncateHistory: () => Promise.resolve({ success: true, data: undefined }), replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }), getInfo: () => Promise.resolve(null), @@ -1118,6 +1119,7 @@ export const ActiveWorkspaceWithChat: Story = { sendMessage: () => Promise.resolve({ success: true, data: undefined }), resumeStream: () => Promise.resolve({ success: true, data: undefined }), interruptStream: () => Promise.resolve({ success: true, data: undefined }), + clearQueue: () => Promise.resolve({ success: true, data: undefined }), truncateHistory: () => Promise.resolve({ success: true, data: undefined }), replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }), getInfo: () => Promise.resolve(null), @@ -1408,6 +1410,7 @@ These tables should render cleanly without any disruptive copy or download actio sendMessage: () => Promise.resolve({ success: true, data: undefined }), resumeStream: () => Promise.resolve({ success: true, data: undefined }), interruptStream: () => Promise.resolve({ success: true, data: undefined }), + clearQueue: () => Promise.resolve({ success: true, data: undefined }), truncateHistory: () => Promise.resolve({ success: true, data: undefined }), replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }), getInfo: () => Promise.resolve(null), diff --git a/src/browser/api.ts b/src/browser/api.ts index 449e57e238..4314b5c908 100644 --- a/src/browser/api.ts +++ b/src/browser/api.ts @@ -225,6 +225,7 @@ const webApi: IPCApi = { invokeIPC(IPC_CHANNELS.WORKSPACE_RESUME_STREAM, workspaceId, options), interruptStream: (workspaceId, options) => invokeIPC(IPC_CHANNELS.WORKSPACE_INTERRUPT_STREAM, workspaceId, options), + clearQueue: (workspaceId) => invokeIPC(IPC_CHANNELS.WORKSPACE_QUEUE_CLEAR, workspaceId), truncateHistory: (workspaceId, percentage) => invokeIPC(IPC_CHANNELS.WORKSPACE_TRUNCATE_HISTORY, workspaceId, percentage), replaceChatHistory: (workspaceId, summaryMessage) => diff --git a/src/browser/components/AIView.tsx b/src/browser/components/AIView.tsx index 556d6260cc..cb43414f31 100644 --- a/src/browser/components/AIView.tsx +++ b/src/browser/components/AIView.tsx @@ -27,6 +27,7 @@ import type { DisplayedMessage } from "@/common/types/message"; import type { RuntimeConfig } from "@/common/types/runtime"; import { useAIViewKeybinds } from "@/browser/hooks/useAIViewKeybinds"; import { evictModelFromLRU } from "@/browser/hooks/useModelLRU"; +import { QueuedMessage } from "./Messages/QueuedMessage"; interface AIViewProps { workspaceId: string; @@ -141,8 +142,28 @@ const AIViewInner: React.FC = ({ setEditingMessage({ id: messageId, content }); }, []); - const handleEditLastUserMessage = useCallback(() => { + const handleEditQueuedMessage = useCallback(async () => { + const queuedMessage = workspaceState?.queuedMessage; + if (!queuedMessage) return; + + await window.api.workspace.clearQueue(workspaceId); + chatInputAPI.current?.restoreText(queuedMessage.content); + + // Restore images if present + if (queuedMessage.imageParts && queuedMessage.imageParts.length > 0) { + chatInputAPI.current?.restoreImages(queuedMessage.imageParts); + } + }, [workspaceId, workspaceState?.queuedMessage, chatInputAPI]); + + const handleEditLastUserMessage = useCallback(async () => { if (!workspaceState) return; + + if (workspaceState.queuedMessage) { + await handleEditQueuedMessage(); + return; + } + + // Otherwise, edit last user message const mergedMessages = mergeConsecutiveStreamErrors(workspaceState.messages); const lastUserMessage = [...mergedMessages] .reverse() @@ -159,7 +180,7 @@ const AIViewInner: React.FC = ({ element?.scrollIntoView({ behavior: "smooth", block: "center" }); }); } - }, [workspaceState, contentRef, setAutoScroll]); + }, [workspaceState, contentRef, setAutoScroll, handleEditQueuedMessage]); const handleCancelEdit = useCallback(() => { setEditingMessage(undefined); @@ -458,6 +479,12 @@ const AIViewInner: React.FC = ({ /> )} + {workspaceState?.queuedMessage && ( + void handleEditQueuedMessage()} + /> + )} {!autoScroll && (