feat: Add enhanced context editing features

- Enable Edit button for user messages
- Add 'Delete and Restore' button to deletion dialog
- Implement backend support for synchronized file state restoration
This commit is contained in:
Roo Code 2025-08-21 14:55:22 +00:00
parent 6fd261d3b6
commit a6be9c4f03
6 changed files with 68 additions and 6 deletions

View file

@ -96,21 +96,50 @@ export const webviewMessageHandler = async (
}
}
/**
* Removes messages and restores file state to checkpoint
*/
const removeMessagesAndRestoreFileState = async (
currentCline: any,
messageIndex: number,
apiConversationHistoryIndex: number,
messageTs: number,
) => {
// First, delete messages
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
// Find the first checkpoint after this message
const checkpointMessage = currentCline.clineMessages.find(
(msg: ClineMessage) => msg.say === "checkpoint_saved" && msg.ts && msg.ts > messageTs,
)
if (checkpointMessage && checkpointMessage.text) {
// Restore to the checkpoint
const { checkpointRestore } = await import("../checkpoints")
await checkpointRestore(currentCline, {
ts: checkpointMessage.ts,
commitHash: checkpointMessage.text,
mode: "restore",
})
}
}
/**
* Handles message deletion operations with user confirmation
*/
const handleDeleteOperation = async (messageTs: number): Promise<void> => {
const handleDeleteOperation = async (messageTs: number, withRestore: boolean = false): Promise<void> => {
// Send message to webview to show delete confirmation dialog
await provider.postMessageToWebview({
type: "showDeleteMessageDialog",
messageTs,
withRestore,
})
}
/**
* Handles confirmed message deletion from webview dialog
*/
const handleDeleteMessageConfirm = async (messageTs: number): Promise<void> => {
const handleDeleteMessageConfirm = async (messageTs: number, withRestore: boolean = false): Promise<void> => {
// Only proceed if we have a current task.
if (provider.getCurrentTask()) {
const currentCline = provider.getCurrentTask()!
@ -120,8 +149,18 @@ export const webviewMessageHandler = async (
try {
const { historyItem } = await provider.getTaskWithId(currentCline.taskId)
// Delete this message and all subsequent messages
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
if (withRestore) {
// Delete messages and restore file state
await removeMessagesAndRestoreFileState(
currentCline,
messageIndex,
apiConversationHistoryIndex,
messageTs,
)
} else {
// Delete this message and all subsequent messages
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
}
// Initialize with history item after deletion
await provider.createTaskWithHistoryItem(historyItem)

View file

@ -193,6 +193,7 @@ export interface ExtensionMessage {
messageTs?: number
context?: string
commands?: Command[]
withRestore?: boolean // For showDeleteMessageDialog
}
export type ExtensionState = Pick<

View file

@ -254,6 +254,7 @@ export interface WebviewMessage {
visibility?: ShareVisibility // For share visibility
hasContent?: boolean // For checkRulesDirectoryResult
checkOnly?: boolean // For deleteCustomMode check
withRestore?: boolean // For deleteMessageConfirm with file state restore
codeIndexSettings?: {
// Global state settings
codebaseIndexEnabled: boolean

View file

@ -275,6 +275,15 @@ const App = () => {
vscode.postMessage({
type: "deleteMessageConfirm",
messageTs: deleteMessageDialogState.messageTs,
withRestore: false,
})
setDeleteMessageDialogState((prev) => ({ ...prev, isOpen: false }))
}}
onConfirmWithRestore={() => {
vscode.postMessage({
type: "deleteMessageConfirm",
messageTs: deleteMessageDialogState.messageTs,
withRestore: true,
})
setDeleteMessageDialogState((prev) => ({ ...prev, isOpen: false }))
}}

View file

@ -1090,7 +1090,7 @@ export const ChatRowContent = ({
<Button
variant="ghost"
size="icon"
className="shrink-0 hidden"
className="shrink-0"
disabled={isStreaming}
onClick={(e) => {
e.stopPropagation()

View file

@ -15,6 +15,7 @@ interface MessageModificationConfirmationDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
onConfirm: () => void
onConfirmWithRestore?: () => void
type: "edit" | "delete"
}
@ -22,11 +23,13 @@ export const MessageModificationConfirmationDialog: React.FC<MessageModification
open,
onOpenChange,
onConfirm,
onConfirmWithRestore,
type,
}) => {
const { t } = useAppTranslation()
const isEdit = type === "edit"
const isDelete = type === "delete"
const title = isEdit ? t("common:confirmation.editMessage") : t("common:confirmation.deleteMessage")
const description = isEdit ? t("common:confirmation.editWarning") : t("common:confirmation.deleteWarning")
@ -44,8 +47,17 @@ export const MessageModificationConfirmationDialog: React.FC<MessageModification
<AlertDialogAction
onClick={onConfirm}
className="bg-vscode-button-background hover:bg-vscode-button-hoverBackground text-vscode-button-foreground border-vscode-button-border">
{t("common:confirmation.proceed")}
{isDelete
? t("common:confirmation.deleteOnly") || "Delete Messages Only"
: t("common:confirmation.proceed")}
</AlertDialogAction>
{isDelete && onConfirmWithRestore && (
<AlertDialogAction
onClick={onConfirmWithRestore}
className="bg-vscode-button-background hover:bg-vscode-button-hoverBackground text-vscode-button-foreground border-vscode-button-border">
{t("common:confirmation.deleteAndRestore") || "Delete Messages and Restore Files"}
</AlertDialogAction>
)}
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>