feat(vscode-ide-companion): improve message handling and diff auto-opening

- Ignore messages during checkpoint saves in WebViewProvider
- Move diff auto-opening logic from useEffect to useWebViewMessages hook
- Remove unused imports and variables in EditToolCall component
- Enhance tool call handling for edit operations with diff content
This commit is contained in:
yiliang114
2025-12-09 00:15:30 +08:00
parent 58b9e477bc
commit ef3d7b92d0
3 changed files with 42 additions and 39 deletions

View File

@@ -57,6 +57,20 @@ export class WebViewProvider {
}); });
// Setup agent callbacks // Setup agent callbacks
this.agentManager.onMessage((message) => {
// Ignore history replay while background /chat save is running
if (this.messageHandler.getIsSavingCheckpoint()) {
console.log(
'[WebViewProvider] Ignoring message during checkpoint save',
);
return;
}
this.sendMessageToWebView({
type: 'message',
data: message,
});
});
this.agentManager.onStreamChunk((chunk: string) => { this.agentManager.onStreamChunk((chunk: string) => {
// Ignore stream chunks from background /chat save commands // Ignore stream chunks from background /chat save commands
if (this.messageHandler.getIsSavingCheckpoint()) { if (this.messageHandler.getIsSavingCheckpoint()) {

View File

@@ -6,7 +6,7 @@
* Edit tool call component - specialized for file editing operations * Edit tool call component - specialized for file editing operations
*/ */
import { useEffect, useCallback, useMemo } from 'react'; import { useMemo } from 'react';
import type { BaseToolCallProps } from '../shared/types.js'; import type { BaseToolCallProps } from '../shared/types.js';
import { import {
groupContent, groupContent,
@@ -14,8 +14,6 @@ import {
} from '../shared/utils.js'; } from '../shared/utils.js';
import { FileLink } from '../../../layout/FileLink.js'; import { FileLink } from '../../../layout/FileLink.js';
import type { ToolCallContainerProps } from '../shared/LayoutComponents.js'; import type { ToolCallContainerProps } from '../shared/LayoutComponents.js';
import { useVSCode } from '../../../../hooks/useVSCode.js';
import { handleOpenDiff } from '../../../../utils/diffUtils.js';
export const ToolCallContainer: React.FC<ToolCallContainerProps> = ({ export const ToolCallContainer: React.FC<ToolCallContainerProps> = ({
label, label,
@@ -70,44 +68,9 @@ const getDiffSummary = (
*/ */
export const EditToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => { export const EditToolCall: React.FC<BaseToolCallProps> = ({ toolCall }) => {
const { content, locations, toolCallId } = toolCall; const { content, locations, toolCallId } = toolCall;
const vscode = useVSCode();
// Group content by type; memoize to avoid new array identities on every render // Group content by type; memoize to avoid new array identities on every render
const { errors, diffs } = useMemo(() => groupContent(content), [content]); const { errors, diffs } = useMemo(() => groupContent(content), [content]);
const handleOpenDiffInternal = useCallback(
(
path: string | undefined,
oldText: string | null | undefined,
newText: string | undefined,
) => {
handleOpenDiff(vscode, path, oldText, newText);
},
[vscode],
);
// Automatically trigger openDiff when diff content is detected
// Only trigger once per tool call by checking toolCallId
useEffect(() => {
// Only auto-open if there are diffs and we have the required data
if (diffs.length > 0) {
const firstDiff = diffs[0];
const path = firstDiff.path || (locations && locations[0]?.path) || '';
if (
path &&
firstDiff.oldText !== undefined &&
firstDiff.newText !== undefined
) {
// Add a small delay to ensure the component is fully rendered
const timer = setTimeout(() => {
handleOpenDiffInternal(path, firstDiff.oldText, firstDiff.newText);
}, 100);
// Proper cleanup function
return () => timer && clearTimeout(timer);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [toolCallId]);
// Failed case: show explicit failed message and render inline diffs // Failed case: show explicit failed message and render inline diffs
if (toolCall.status === 'failed') { if (toolCall.status === 'failed') {

View File

@@ -386,7 +386,7 @@ export const useWebViewMessages = ({
if (permToolCall?.toolCallId) { if (permToolCall?.toolCallId) {
// Infer kind more robustly for permission preview: // Infer kind more robustly for permission preview:
// - If content contains a diff entry, force 'edit' so the EditToolCall auto-opens VS Code diff // - If content contains a diff entry, force 'edit' so the EditToolCall can handle it properly
// - Else try title-based hints; fall back to provided kind or 'execute' // - Else try title-based hints; fall back to provided kind or 'execute'
let kind = permToolCall.kind || 'execute'; let kind = permToolCall.kind || 'execute';
const contentArr = (permToolCall.content as unknown[]) || []; const contentArr = (permToolCall.content as unknown[]) || [];
@@ -400,6 +400,32 @@ export const useWebViewMessages = ({
: false; : false;
if (hasDiff) { if (hasDiff) {
kind = 'edit'; kind = 'edit';
// Auto-open diff view for edit operations with diff content
// This replaces the useEffect auto-trigger in EditToolCall component
const diffContent = contentArr.find(
(c: unknown) =>
!!c &&
typeof c === 'object' &&
(c as { type?: string }).type === 'diff',
) as
| { path?: string; oldText?: string; newText?: string }
| undefined;
if (
diffContent?.path &&
diffContent?.oldText !== undefined &&
diffContent?.newText !== undefined
) {
vscode.postMessage({
type: 'openDiff',
data: {
path: diffContent.path,
oldText: diffContent.oldText,
newText: diffContent.newText,
},
});
}
} else if (permToolCall.title) { } else if (permToolCall.title) {
const title = permToolCall.title.toLowerCase(); const title = permToolCall.title.toLowerCase();
if (title.includes('touch') || title.includes('echo')) { if (title.includes('touch') || title.includes('echo')) {