feat(vscode-ide-companion): add cancel streaming functionality

- Add handleCancel callback to App component
- Implement cancelStreaming message posting to VS Code
- Add onCancel prop to InputForm component
- Replace send button with stop button during streaming
This commit is contained in:
yiliang114
2025-12-04 01:53:19 +08:00
parent 35f98723ca
commit e3c456a430
27 changed files with 730 additions and 286 deletions

View File

@@ -5,6 +5,9 @@
*/
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { BaseMessageHandler } from './BaseMessageHandler.js';
import { getFileName } from '../utils/webviewUtils.js';
@@ -20,6 +23,7 @@ export class FileMessageHandler extends BaseMessageHandler {
'getWorkspaceFiles',
'openFile',
'openDiff',
'createAndOpenTempFile',
].includes(messageType);
}
@@ -47,6 +51,10 @@ export class FileMessageHandler extends BaseMessageHandler {
await this.handleOpenDiff(data);
break;
case 'createAndOpenTempFile':
await this.handleCreateAndOpenTempFile(data);
break;
default:
console.warn(
'[FileMessageHandler] Unknown message type:',
@@ -347,4 +355,52 @@ export class FileMessageHandler extends BaseMessageHandler {
vscode.window.showErrorMessage(`Failed to open diff: ${error}`);
}
}
/**
* Create and open temporary file
*/
private async handleCreateAndOpenTempFile(
data: Record<string, unknown> | undefined,
): Promise<void> {
if (!data) {
console.warn(
'[FileMessageHandler] No data provided for createAndOpenTempFile',
);
return;
}
try {
const content = (data.content as string) || '';
const fileName = (data.fileName as string) || 'temp';
const fileExtension = (data.fileExtension as string) || '.txt';
// Create temporary file path
const tempDir = os.tmpdir();
const tempFileName = `${fileName}-${Date.now()}${fileExtension}`;
const tempFilePath = path.join(tempDir, tempFileName);
// Write content to temporary file
await fs.promises.writeFile(tempFilePath, content, 'utf8');
// Open the temporary file in VS Code
const uri = vscode.Uri.file(tempFilePath);
await vscode.window.showTextDocument(uri, {
preview: false,
preserveFocus: false,
});
console.log(
'[FileMessageHandler] Created and opened temporary file:',
tempFilePath,
);
} catch (error) {
console.error(
'[FileMessageHandler] Failed to create and open temporary file:',
error,
);
vscode.window.showErrorMessage(
`Failed to create and open temporary file: ${error}`,
);
}
}
}

View File

@@ -25,6 +25,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
'getQwenSessions',
'saveSession',
'resumeSession',
'cancelStreaming',
// UI action: open a new chat tab (new WebviewPanel)
'openNewChatTab',
].includes(messageType);
@@ -102,6 +103,11 @@ export class SessionMessageHandler extends BaseMessageHandler {
}
break;
case 'cancelStreaming':
// Handle cancel streaming request from webview
await this.handleCancelStreaming();
break;
default:
console.warn(
'[SessionMessageHandler] Unknown message type:',
@@ -910,6 +916,34 @@ export class SessionMessageHandler extends BaseMessageHandler {
}
}
/**
* Handle cancel streaming request
*/
private async handleCancelStreaming(): Promise<void> {
try {
console.log('[SessionMessageHandler] Canceling streaming...');
// Cancel the current streaming operation in the agent manager
await this.agentManager.cancelCurrentPrompt();
// Send streamEnd message to WebView to update UI
this.sendToWebView({
type: 'streamEnd',
data: { timestamp: Date.now(), reason: 'user_cancelled' },
});
console.log('[SessionMessageHandler] Streaming cancelled successfully');
} catch (_error) {
console.log('[SessionMessageHandler] Streaming cancelled (interrupted)');
// Always send streamEnd to update UI, regardless of errors
this.sendToWebView({
type: 'streamEnd',
data: { timestamp: Date.now(), reason: 'user_cancelled' },
});
}
}
/**
* Handle resume session request
*/