From 3fe0b46353126c481aaa682bead6bf9c62561472 Mon Sep 17 00:00:00 2001 From: Will Li Date: Wed, 2 Jul 2025 13:46:00 -0700 Subject: [PATCH 1/7] working functionality --- src/core/webview/webviewMessageHandler.ts | 48 +++++++++++ src/i18n/locales/en/common.json | 5 ++ src/shared/WebviewMessage.ts | 2 + webview-ui/src/components/chat/ChatRow.tsx | 92 ++++++++++++++++++---- 4 files changed, 131 insertions(+), 16 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 296f23bc5fd..32471eb0e12 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1087,6 +1087,54 @@ export const webviewMessageHandler = async ( } break } + case "submitEditedMessage": { + if ( + provider.getCurrentCline() && + typeof message.value === "number" && + message.value && + message.editedMessageContent + ) { + const timeCutoff = message.value - 1000 // 1 second buffer before the message to edit + + const messageIndex = provider + .getCurrentCline()! + .clineMessages.findIndex((msg) => msg.ts && msg.ts >= timeCutoff) + + const apiConversationHistoryIndex = provider + .getCurrentCline() + ?.apiConversationHistory.findIndex((msg) => msg.ts && msg.ts >= timeCutoff) + + if (messageIndex !== -1) { + try { + const currentCline = provider.getCurrentCline()! + + // Delete the original message and all subsequent messages + await currentCline.overwriteClineMessages(currentCline.clineMessages.slice(0, messageIndex)) + + // Delete the original message and all subsequent messages in API history + if (apiConversationHistoryIndex !== undefined && apiConversationHistoryIndex !== -1) { + await currentCline.overwriteApiConversationHistory( + currentCline.apiConversationHistory.slice(0, apiConversationHistoryIndex), + ) + } + + // Process the edited message as a regular user message + // This will add it to the conversation and trigger an AI response + webviewMessageHandler(provider, { + type: "askResponse", + askResponse: "messageResponse", + text: message.editedMessageContent, + }) + } catch (error) { + console.error("Error in submitEditedMessage:", error) + vscode.window.showErrorMessage( + `Error editing message: ${error instanceof Error ? error.message : String(error)}`, + ) + } + } + } + break + } case "screenshotQuality": await updateGlobalState("screenshotQuality", message.value) await provider.postStateToWebview() diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b0779cdd892..ea432436edb 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -107,6 +107,11 @@ "remove": "Remove", "keep": "Keep" }, + "buttons": { + "save": "Save", + "cancel": "Cancel", + "edit": "Edit" + }, "tasks": { "canceled": "Task error: It was stopped and canceled by the user.", "deleted": "Task failure: It was stopped and deleted by the user.", diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 0f1d22c3c73..2621980fd00 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -103,6 +103,7 @@ export interface WebviewMessage { | "enhancedPrompt" | "draggedImages" | "deleteMessage" + | "submitEditedMessage" | "terminalOutputLineLimit" | "terminalShellIntegrationTimeout" | "terminalShellIntegrationDisabled" @@ -184,6 +185,7 @@ export interface WebviewMessage { | "checkRulesDirectory" | "checkRulesDirectoryResult" text?: string + editedMessageContent?: string tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account" disabled?: boolean dataUri?: string diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index f61eb8c5e02..f2803b6b54d 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -108,6 +108,8 @@ export const ChatRowContent = ({ const [reasoningCollapsed, setReasoningCollapsed] = useState(true) const [isDiffErrorExpanded, setIsDiffErrorExpanded] = useState(false) const [showCopySuccess, setShowCopySuccess] = useState(false) + const [isEditing, setIsEditing] = useState(false) + const [editedContent, setEditedContent] = useState("") const { copyWithFeedback } = useCopyToClipboard() // Memoized callback to prevent re-renders caused by inline arrow functions @@ -115,6 +117,31 @@ export const ChatRowContent = ({ onToggleExpand(message.ts) }, [onToggleExpand, message.ts]) + // Handle edit button click + const handleEditClick = useCallback(() => { + setIsEditing(true) + setEditedContent(message.text || "") + // Edit mode is now handled entirely in the frontend + // No need to notify the backend + }, [message.text]) + + // Handle cancel edit + const handleCancelEdit = useCallback(() => { + setIsEditing(false) + setEditedContent(message.text || "") + }, [message.text]) + + // Handle save edit + const handleSaveEdit = useCallback(() => { + setIsEditing(false) + // Send edited message to backend + vscode.postMessage({ + type: "submitEditedMessage", + value: message.ts, + editedMessageContent: editedContent, + }) + }, [message.ts, editedContent]) + const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => { if (message.text !== null && message.text !== undefined && message.say === "api_req_started") { const info = safeJsonParse(message.text) @@ -983,23 +1010,56 @@ export const ChatRowContent = ({ case "user_feedback": return (
-
-
- + {isEditing ? ( +
+