working functionality

This commit is contained in:
Will Li 2025-07-02 13:46:00 -07:00
parent 05040414c2
commit 3fe0b46353
4 changed files with 131 additions and 16 deletions

View file

@ -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()

View file

@ -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.",

View file

@ -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

View file

@ -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<ClineApiReqInfo>(message.text)
@ -983,23 +1010,56 @@ export const ChatRowContent = ({
case "user_feedback":
return (
<div className="bg-vscode-editor-background border rounded-xs p-1 overflow-hidden whitespace-pre-wrap">
<div className="flex justify-between">
<div className="flex-grow px-2 py-1 wrap-anywhere">
<Mention text={message.text} withShadow />
{isEditing ? (
<div className="flex flex-col gap-2 p-2">
<textarea
className="w-full p-2 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded-xs"
value={editedContent}
onChange={(e) => setEditedContent(e.target.value)}
rows={5}
autoFocus
/>
<div className="flex justify-end gap-2">
<Button variant="secondary" size="sm" onClick={handleCancelEdit}>
{t("chat:cancel.title")}
</Button>
<Button variant="default" size="sm" onClick={handleSaveEdit}>
{t("chat:save.title")}
</Button>
</div>
</div>
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
vscode.postMessage({ type: "deleteMessage", value: message.ts })
}}>
<span className="codicon codicon-trash" />
</Button>
</div>
{message.images && message.images.length > 0 && (
) : (
<div className="flex justify-between">
<div className="flex-grow px-2 py-1 wrap-anywhere">
<Mention text={message.text} withShadow />
</div>
<div className="flex">
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
handleEditClick()
}}>
<span className="codicon codicon-edit" />
</Button>
<Button
variant="ghost"
size="icon"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()
vscode.postMessage({ type: "deleteMessage", value: message.ts })
}}>
<span className="codicon codicon-trash" />
</Button>
</div>
</div>
)}
{!isEditing && message.images && message.images.length > 0 && (
<Thumbnails images={message.images} style={{ marginTop: "8px" }} />
)}
</div>