Skip to content

Commit bde7c2a

Browse files
committed
fix: show edit warning dialog only once per session
- Added hasShownEditWarning flag to ClineProvider to track if warning has been shown - Modified handleEditOperation to check flag before showing dialog - Updated tests to reflect new behavior where dialog only shows on first edit - Fixes #6058 where edit warning was shown on every message edit
1 parent df6c57d commit bde7c2a

File tree

4 files changed

+93
-12
lines changed

4 files changed

+93
-12
lines changed

src/core/webview/ClineProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export class ClineProvider
115115
public readonly latestAnnouncementId = "jul-09-2025-3-23-0" // Update for v3.23.0 announcement
116116
public readonly providerSettingsManager: ProviderSettingsManager
117117
public readonly customModesManager: CustomModesManager
118+
public hasShownEditWarning = false // Session-based flag for edit warning dialog
118119

119120
constructor(
120121
readonly context: vscode.ExtensionContext,

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2909,21 +2909,24 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
29092909

29102910
await Promise.all([edit1Promise, edit2Promise])
29112911

2912-
// Verify dialogs were shown for both edits
2912+
// With the new implementation, only the first edit shows the dialog
2913+
// The second edit happens after hasShownEditWarning is already true
29132914
expect(mockPostMessage).toHaveBeenCalledWith({
29142915
type: "showEditMessageDialog",
29152916
messageTs: 2000,
29162917
text: "Edited message 1",
2918+
images: undefined,
29172919
})
2918-
expect(mockPostMessage).toHaveBeenCalledWith({
2920+
2921+
// The second edit should not show a dialog
2922+
expect(mockPostMessage).not.toHaveBeenCalledWith({
29192923
type: "showEditMessageDialog",
29202924
messageTs: 4000,
29212925
text: "Edited message 2",
29222926
})
29232927

2924-
// Simulate user confirming both edits
2928+
// Simulate user confirming the first edit
29252929
await messageHandler({ type: "editMessageConfirm", messageTs: 2000, text: "Edited message 1" })
2926-
await messageHandler({ type: "editMessageConfirm", messageTs: 4000, text: "Edited message 2" })
29272930

29282931
// Both operations should complete without throwing
29292932
expect(mockCline.overwriteClineMessages).toHaveBeenCalled()

src/core/webview/__tests__/webviewMessageHandler.spec.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,9 @@ describe("webviewMessageHandler - message dialog preferences", () => {
517517
})
518518

519519
describe("submitEditedMessage", () => {
520-
it("should always show dialog for edit confirmation", async () => {
520+
it("should show dialog for edit confirmation on first edit in session", async () => {
521521
vi.mocked(mockClineProvider.getCurrentCline).mockReturnValue({} as any) // Mock current cline exists
522+
mockClineProvider.hasShownEditWarning = false // Reset the flag
522523

523524
await webviewMessageHandler(mockClineProvider, {
524525
type: "submitEditedMessage",
@@ -531,6 +532,75 @@ describe("webviewMessageHandler - message dialog preferences", () => {
531532
messageTs: 123456789,
532533
text: "edited content",
533534
})
535+
expect(mockClineProvider.hasShownEditWarning).toBe(true)
536+
})
537+
538+
it("should not show dialog for subsequent edits in the same session", async () => {
539+
const editMessageTs = 1234567890000 // Message to edit
540+
const mockCline = {
541+
taskId: "test-task-id",
542+
apiConversationHistory: [
543+
{ role: "user", content: "Previous message", ts: editMessageTs - 5000 },
544+
{ role: "assistant", content: "Response", ts: editMessageTs - 4000 },
545+
{ role: "user", content: "Message to edit", ts: editMessageTs },
546+
{ role: "assistant", content: "Later response", ts: editMessageTs + 2000 },
547+
],
548+
clineMessages: [
549+
{ ts: editMessageTs - 5000, type: "say", say: "text", text: "Much earlier message" },
550+
{ ts: editMessageTs - 2000, type: "say", say: "text", text: "Earlier message" },
551+
{ ts: editMessageTs, type: "say", say: "user_feedback", text: "Original message to edit" },
552+
{ ts: editMessageTs + 2000, type: "say", say: "text", text: "Later message" },
553+
],
554+
overwriteClineMessages: vi.fn().mockResolvedValue(undefined),
555+
overwriteApiConversationHistory: vi.fn().mockResolvedValue(undefined),
556+
handleWebviewAskResponse: vi.fn(),
557+
}
558+
559+
// Mock getCurrentCline to always return our mockCline
560+
vi.mocked(mockClineProvider.getCurrentCline).mockReturnValue(mockCline as any)
561+
562+
vi.mocked(mockClineProvider.getTaskWithId).mockResolvedValue({
563+
historyItem: { id: "test-task-id" } as any,
564+
taskDirPath: "/test/path",
565+
apiConversationHistoryFilePath: "/test/path/api.json",
566+
uiMessagesFilePath: "/test/path/ui.json",
567+
apiConversationHistory: [],
568+
})
569+
vi.mocked(mockClineProvider.initClineWithHistoryItem).mockResolvedValue({} as any)
570+
571+
mockClineProvider.hasShownEditWarning = true // Flag already set from previous edit
572+
573+
await webviewMessageHandler(mockClineProvider, {
574+
type: "submitEditedMessage",
575+
value: editMessageTs,
576+
editedMessageContent: "edited content",
577+
images: undefined,
578+
})
579+
580+
// Should not show dialog
581+
expect(mockClineProvider.postMessageToWebview).not.toHaveBeenCalledWith({
582+
type: "showEditMessageDialog",
583+
messageTs: editMessageTs,
584+
text: "edited content",
585+
})
586+
587+
// Should have called overwrite methods to remove messages from the timestamp onwards
588+
// The cutoff is editMessageTs - 1000, so messages before that should remain
589+
expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([
590+
{ ts: editMessageTs - 5000, type: "say", say: "text", text: "Much earlier message" },
591+
{ ts: editMessageTs - 2000, type: "say", say: "text", text: "Earlier message" },
592+
])
593+
expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([
594+
{ role: "user", content: "Previous message", ts: editMessageTs - 5000 },
595+
{ role: "assistant", content: "Response", ts: editMessageTs - 4000 },
596+
])
597+
598+
// Should have called handleWebviewAskResponse
599+
expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith(
600+
"messageResponse",
601+
"edited content",
602+
undefined,
603+
)
534604
})
535605
})
536606
})

src/core/webview/webviewMessageHandler.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,20 @@ export const webviewMessageHandler = async (
138138
* Handles message editing operations with user confirmation
139139
*/
140140
const handleEditOperation = async (messageTs: number, editedContent: string, images?: string[]): Promise<void> => {
141-
// Send message to webview to show edit confirmation dialog
142-
await provider.postMessageToWebview({
143-
type: "showEditMessageDialog",
144-
messageTs,
145-
text: editedContent,
146-
images,
147-
})
141+
// Check if we've already shown the edit warning in this session
142+
if (!provider.hasShownEditWarning) {
143+
// First time editing in this session - show the warning dialog
144+
provider.hasShownEditWarning = true
145+
await provider.postMessageToWebview({
146+
type: "showEditMessageDialog",
147+
messageTs,
148+
text: editedContent,
149+
images,
150+
})
151+
} else {
152+
// Already shown the warning in this session - proceed directly with the edit
153+
await handleEditMessageConfirm(messageTs, editedContent, images)
154+
}
148155
}
149156

150157
/**

0 commit comments

Comments
 (0)