From 6bd906981bf4477cc3328ba091cb571b5c263073 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Mon, 28 Jul 2025 23:33:17 +0100 Subject: [PATCH] Fix new chat --- src/extension.ts | 297 +++++++++++++++++++++++++++-------------------- 1 file changed, 169 insertions(+), 128 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 5008954..c760616 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -56,7 +56,6 @@ interface ConversationData { }; messages: Array<{ timestamp: string, messageType: string, data: any }>; filename: string; - isProcessing?: boolean; } class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider { @@ -64,7 +63,7 @@ class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider { private readonly _extensionUri: vscode.Uri, private readonly _context: vscode.ExtensionContext, private readonly _chatProvider: ClaudeChatProvider - ) {} + ) { } public resolveWebviewView( webviewView: vscode.WebviewView, @@ -333,7 +332,7 @@ class ClaudeChatProvider { if (this._messageHandlerDisposable) { this._messageHandlerDisposable.dispose(); } - + // Set up new message handler this._messageHandlerDisposable = webview.onDidReceiveMessage( message => this._handleWebviewMessage(message), @@ -349,7 +348,7 @@ class ClaudeChatProvider { } } - public showInWebview(webview: vscode.Webview, webviewView?: vscode.WebviewView) { + public showInWebview(webview: vscode.Webview, webviewView?: vscode.WebviewView) { // Close main panel if it's open if (this._panel) { console.log('Closing main panel because sidebar is opening'); @@ -436,11 +435,11 @@ class ClaudeChatProvider { type: 'userInput', data: message }); - + // Set processing state to true this._postMessage({ type: 'setProcessing', - data: {isProcessing: true} + data: { isProcessing: true } }); // Create backup commit before Claude makes changes @@ -466,7 +465,7 @@ class ClaudeChatProvider { // Get configuration const config = vscode.workspace.getConfiguration('claudeCodeChat'); const yoloMode = config.get('permissions.yoloMode', false); - + if (yoloMode) { // Yolo mode: skip all permissions regardless of MCP config args.push('--dangerously-skip-permissions'); @@ -573,6 +572,10 @@ class ClaudeChatProvider { console.log('Claude process closed with code:', code); console.log('Claude stderr output:', errorOutput); + if (!this._currentClaudeProcess) { + return; + } + // Clear process reference this._currentClaudeProcess = undefined; @@ -580,10 +583,16 @@ class ClaudeChatProvider { this._postMessage({ type: 'clearLoading' }); - + // Reset processing state this._isProcessing = false; + // Clear processing state + this._postMessage({ + type: 'setProcessing', + data: { isProcessing: false } + }); + if (code !== 0 && errorOutput.trim()) { // Error with output this._sendAndSaveMessage({ @@ -595,14 +604,26 @@ class ClaudeChatProvider { claudeProcess.on('error', (error) => { console.log('Claude process error:', error.message); - + + if (!this._currentClaudeProcess) { + return; + } + // Clear process reference this._currentClaudeProcess = undefined; - + this._postMessage({ type: 'clearLoading' }); - + + this._isProcessing = false; + + // Clear processing state + this._postMessage({ + type: 'setProcessing', + data: { isProcessing: false } + }); + // Check if claude command is not installed if (error.message.includes('ENOENT') || error.message.includes('command not found')) { this._sendAndSaveMessage({ @@ -624,6 +645,18 @@ class ClaudeChatProvider { if (jsonData.subtype === 'init') { // System initialization message - session ID will be captured from final result console.log('System initialized'); + this._currentSessionId = jsonData.session_id; + //this._sendAndSaveMessage({ type: 'init', data: { sessionId: jsonData.session_id; } }) + + // Show session info in UI + this._sendAndSaveMessage({ + type: 'sessionInfo', + data: { + sessionId: jsonData.session_id, + tools: jsonData.tools || [], + mcpServers: jsonData.mcp_servers || [] + } + }); } break; @@ -703,16 +736,16 @@ class ClaudeChatProvider { for (const content of jsonData.message.content) { if (content.type === 'tool_result') { let resultContent = content.content || 'Tool executed successfully'; - + // Stringify if content is an object or array if (typeof resultContent === 'object' && resultContent !== null) { resultContent = JSON.stringify(resultContent, null, 2); } - + const isError = content.is_error || false; // Find the last tool use to get the tool name - const lastToolUse = this._currentConversation[this._currentConversation.length-1] + const lastToolUse = this._currentConversation[this._currentConversation.length - 1] const toolName = lastToolUse?.data?.toolName; @@ -784,7 +817,7 @@ class ClaudeChatProvider { // Clear processing state this._postMessage({ type: 'setProcessing', - data: {isProcessing: false} + data: { isProcessing: false } }); // Update cumulative tracking @@ -819,6 +852,22 @@ class ClaudeChatProvider { private _newSession() { + + this._isProcessing = false + + // Update UI state + this._postMessage({ + type: 'setProcessing', + data: { isProcessing: false } + }); + + // Try graceful termination first + if (this._currentClaudeProcess) { + const processToKill = this._currentClaudeProcess; + this._currentClaudeProcess = undefined; + processToKill.kill('SIGTERM'); + } + // Clear current session this._currentSessionId = undefined; @@ -842,10 +891,10 @@ class ClaudeChatProvider { public newSessionOnConfigChange() { // Reinitialize MCP config with new WSL paths this._initializeMCPConfig(); - + // Start a new session due to configuration change this._newSession(); - + // Show notification to user vscode.window.showInformationMessage( 'WSL configuration changed. Started a new Claude session.', @@ -866,7 +915,7 @@ class ClaudeChatProvider { // Clear processing state this._postMessage({ type: 'setProcessing', - data: {isProcessing: false} + data: { isProcessing: false } }); // Show login required message @@ -896,7 +945,7 @@ class ClaudeChatProvider { 'OK' ); - // Send message to UI about terminal + // Send message to UI about terminal this._postMessage({ type: 'terminalOpened', data: `Please login to Claude in the terminal, then come back to this chat to continue.`, @@ -906,7 +955,7 @@ class ClaudeChatProvider { private async _initializeBackupRepo(): Promise { try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) {return;} + if (!workspaceFolder) { return; } const storagePath = this._context.storageUri?.fsPath; if (!storagePath) { @@ -939,7 +988,7 @@ class ClaudeChatProvider { private async _createBackupCommit(userMessage: string): Promise { try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder || !this._backupRepoPath) {return;} + if (!workspaceFolder || !this._backupRepoPath) { return; } const workspacePath = workspaceFolder.uri.fsPath; const now = new Date(); @@ -1048,10 +1097,10 @@ class ClaudeChatProvider { private async _initializeConversations(): Promise { try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) {return;} + if (!workspaceFolder) { return; } const storagePath = this._context.storageUri?.fsPath; - if (!storagePath) {return;} + if (!storagePath) { return; } this._conversationsPath = path.join(storagePath, 'conversations'); @@ -1070,7 +1119,7 @@ class ClaudeChatProvider { private async _initializeMCPConfig(): Promise { try { const storagePath = this._context.storageUri?.fsPath; - if (!storagePath) {return;} + if (!storagePath) { return; } // Create MCP config directory const mcpConfigDir = path.join(storagePath, 'mcp'); @@ -1085,11 +1134,11 @@ class ClaudeChatProvider { const mcpConfigPath = path.join(mcpConfigDir, 'mcp-servers.json'); const mcpPermissionsPath = this.convertToWSLPath(path.join(this._extensionUri.fsPath, 'mcp-permissions.js')); const permissionRequestsPath = this.convertToWSLPath(path.join(storagePath, 'permission-requests')); - + // Load existing config or create new one let mcpConfig: any = { mcpServers: {} }; const mcpConfigUri = vscode.Uri.file(mcpConfigPath); - + try { const existingContent = await vscode.workspace.fs.readFile(mcpConfigUri); mcpConfig = JSON.parse(new TextDecoder().decode(existingContent)); @@ -1097,12 +1146,12 @@ class ClaudeChatProvider { } catch { console.log('No existing MCP config found, creating new one'); } - + // Ensure mcpServers exists if (!mcpConfig.mcpServers) { mcpConfig.mcpServers = {}; } - + // Add or update the permissions server entry mcpConfig.mcpServers['claude-code-chat-permissions'] = { command: 'node', @@ -1114,7 +1163,7 @@ class ClaudeChatProvider { const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2)); await vscode.workspace.fs.writeFile(mcpConfigUri, configContent); - + console.log(`Updated MCP config at: ${mcpConfigPath}`); } catch (error: any) { console.error('Failed to initialize MCP config:', error.message); @@ -1124,13 +1173,13 @@ class ClaudeChatProvider { private async _initializePermissions(): Promise { try { - if(this._permissionWatcher){ + if (this._permissionWatcher) { this._permissionWatcher.dispose(); this._permissionWatcher = undefined; } const storagePath = this._context.storageUri?.fsPath; - if (!storagePath) {return;} + if (!storagePath) { return; } // Create permission requests directory this._permissionRequestsPath = path.join(path.join(storagePath, 'permission-requests')); @@ -1193,13 +1242,13 @@ class ClaudeChatProvider { private async _showPermissionDialog(request: any): Promise { const toolName = request.tool || 'Unknown Tool'; - + // Generate pattern for Bash commands let pattern = undefined; if (toolName === 'Bash' && request.input?.command) { pattern = this.getCommandPattern(request.input.command); } - + // Send permission request to the UI this._postMessage({ type: 'permissionRequest', @@ -1225,7 +1274,7 @@ class ClaudeChatProvider { if (resolver) { resolver(approved); this._pendingPermissionResolvers.delete(id); - + // Handle always allow setting if (alwaysAllow && approved) { void this._saveAlwaysAllowPermission(id); @@ -1241,7 +1290,7 @@ class ClaudeChatProvider { if (!storagePath) return; const requestFileUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', `${requestId}.request`)); - + let requestContent: Uint8Array; try { requestContent = await vscode.workspace.fs.readFile(requestFileUri); @@ -1250,11 +1299,11 @@ class ClaudeChatProvider { } const request = JSON.parse(new TextDecoder().decode(requestContent)); - + // Load existing workspace permissions const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json')); let permissions: any = { alwaysAllow: {} }; - + try { const content = await vscode.workspace.fs.readFile(permissionsUri); permissions = JSON.parse(new TextDecoder().decode(content)); @@ -1292,7 +1341,7 @@ class ClaudeChatProvider { // Save the permissions const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2)); await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent); - + console.log(`Saved always-allow permission for ${toolName}`); } catch (error) { console.error('Error saving always-allow permission:', error); @@ -1302,10 +1351,10 @@ class ClaudeChatProvider { private getCommandPattern(command: string): string { const parts = command.trim().split(/\s+/); if (parts.length === 0) return command; - + const baseCmd = parts[0]; const subCmd = parts.length > 1 ? parts[1] : ''; - + // Common patterns that should use wildcards const patterns = [ // Package managers @@ -1322,7 +1371,7 @@ class ClaudeChatProvider { ['pnpm', 'install', 'pnpm install *'], ['pnpm', 'add', 'pnpm add *'], ['pnpm', 'remove', 'pnpm remove *'], - + // Git commands ['git', 'add', 'git add *'], ['git', 'commit', 'git commit *'], @@ -1335,7 +1384,7 @@ class ClaudeChatProvider { ['git', 'reset', 'git reset *'], ['git', 'rebase', 'git rebase *'], ['git', 'tag', 'git tag *'], - + // Docker commands ['docker', 'run', 'docker run *'], ['docker', 'build', 'docker build *'], @@ -1347,7 +1396,7 @@ class ClaudeChatProvider { ['docker', 'rmi', 'docker rmi *'], ['docker', 'pull', 'docker pull *'], ['docker', 'push', 'docker push *'], - + // Build tools ['make', '', 'make *'], ['cargo', 'build', 'cargo build *'], @@ -1359,7 +1408,7 @@ class ClaudeChatProvider { ['mvn', 'package', 'mvn package *'], ['gradle', 'build', 'gradle build *'], ['gradle', 'test', 'gradle test *'], - + // System commands ['curl', '', 'curl *'], ['wget', '', 'wget *'], @@ -1369,7 +1418,7 @@ class ClaudeChatProvider { ['tar', '', 'tar *'], ['zip', '', 'zip *'], ['unzip', '', 'unzip *'], - + // Development tools ['node', '', 'node *'], ['python', '', 'python *'], @@ -1381,14 +1430,14 @@ class ClaudeChatProvider { ['bundle', 'install', 'bundle install *'], ['gem', 'install', 'gem install *'], ]; - + // Find matching pattern for (const [cmd, sub, pattern] of patterns) { if (baseCmd === cmd && (sub === '' || subCmd === sub)) { return pattern; } } - + // Default: return exact command return command; } @@ -1406,7 +1455,7 @@ class ClaudeChatProvider { const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json')); let permissions: any = { alwaysAllow: {} }; - + try { const content = await vscode.workspace.fs.readFile(permissionsUri); permissions = JSON.parse(new TextDecoder().decode(content)); @@ -1434,7 +1483,7 @@ class ClaudeChatProvider { const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json')); let permissions: any = { alwaysAllow: {} }; - + try { const content = await vscode.workspace.fs.readFile(permissionsUri); permissions = JSON.parse(new TextDecoder().decode(content)); @@ -1463,10 +1512,10 @@ class ClaudeChatProvider { // Save updated permissions const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2)); await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent); - + // Send updated permissions to UI this._sendPermissions(); - + console.log(`Removed permission for ${toolName}${command ? ` command: ${command}` : ''}`); } catch (error) { console.error('Error removing permission:', error); @@ -1480,7 +1529,7 @@ class ClaudeChatProvider { const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json')); let permissions: any = { alwaysAllow: {} }; - + try { const content = await vscode.workspace.fs.readFile(permissionsUri); permissions = JSON.parse(new TextDecoder().decode(content)); @@ -1497,19 +1546,19 @@ class ClaudeChatProvider { if (!permissions.alwaysAllow[toolName]) { permissions.alwaysAllow[toolName] = []; } - + // Convert to array if it's currently set to true if (permissions.alwaysAllow[toolName] === true) { permissions.alwaysAllow[toolName] = []; } - + if (Array.isArray(permissions.alwaysAllow[toolName])) { // For Bash commands, convert to pattern using existing logic let commandToAdd = command; if (toolName === 'Bash') { commandToAdd = this.getCommandPattern(command); } - + // Add if not already present if (!permissions.alwaysAllow[toolName].includes(commandToAdd)) { permissions.alwaysAllow[toolName].push(commandToAdd); @@ -1528,10 +1577,10 @@ class ClaudeChatProvider { // Save updated permissions const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2)); await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent); - + // Send updated permissions to UI this._sendPermissions(); - + console.log(`Added permission for ${toolName}${command ? ` command: ${command}` : ' (all commands)'}`); } catch (error) { console.error('Error adding permission:', error); @@ -1558,10 +1607,10 @@ class ClaudeChatProvider { } // Filter out internal servers before sending to UI - const filteredServers = Object.fromEntries( - Object.entries(mcpConfig.mcpServers || {}).filter(([name]) => name !== 'claude-code-chat-permissions') - ); - this._postMessage({ type: 'mcpServers', data: filteredServers }); + const filteredServers = Object.fromEntries( + Object.entries(mcpConfig.mcpServers || {}).filter(([name]) => name !== 'claude-code-chat-permissions') + ); + this._postMessage({ type: 'mcpServers', data: filteredServers }); } catch (error) { console.error('Error loading MCP servers:', error); this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to load MCP servers' } }); @@ -1657,7 +1706,7 @@ class ClaudeChatProvider { private async _sendCustomSnippets(): Promise { try { - const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {}); + const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {}); this._postMessage({ type: 'customSnippetsData', data: customSnippets @@ -1673,16 +1722,16 @@ class ClaudeChatProvider { private async _saveCustomSnippet(snippet: any): Promise { try { - const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {}); + const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {}); customSnippets[snippet.id] = snippet; - + await this._context.globalState.update('customPromptSnippets', customSnippets); - + this._postMessage({ type: 'customSnippetSaved', data: { snippet } }); - + console.log('Saved custom snippet:', snippet.name); } catch (error) { console.error('Error saving custom snippet:', error); @@ -1695,17 +1744,17 @@ class ClaudeChatProvider { private async _deleteCustomSnippet(snippetId: string): Promise { try { - const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {}); - + const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {}); + if (customSnippets[snippetId]) { delete customSnippets[snippetId]; await this._context.globalState.update('customPromptSnippets', customSnippets); - + this._postMessage({ type: 'customSnippetDeleted', data: { snippetId } }); - + console.log('Deleted custom snippet:', snippetId); } else { this._postMessage({ @@ -1725,24 +1774,24 @@ class ClaudeChatProvider { private convertToWSLPath(windowsPath: string): string { const config = vscode.workspace.getConfiguration('claudeCodeChat'); const wslEnabled = config.get('wsl.enabled', false); - + if (wslEnabled && windowsPath.match(/^[a-zA-Z]:/)) { // Convert C:\Users\... to /mnt/c/Users/... return windowsPath.replace(/^([a-zA-Z]):/, '/mnt/$1').toLowerCase().replace(/\\/g, '/'); } - + return windowsPath; } public getMCPConfigPath(): string | undefined { const storagePath = this._context.storageUri?.fsPath; - if (!storagePath) {return undefined;} - + if (!storagePath) { return undefined; } + const configPath = path.join(storagePath, 'mcp', 'mcp-servers.json'); return path.join(configPath); } - private _sendAndSaveMessage(message: { type: string, data: any }, options?: { isProcessing?: boolean }): void { + private _sendAndSaveMessage(message: { type: string, data: any }): void { // Initialize conversation if this is the first message if (this._currentConversation.length === 0) { @@ -1764,10 +1813,8 @@ class ClaudeChatProvider { } private async _saveCurrentConversation(): Promise { - if (!this._conversationsPath || this._currentConversation.length === 0) {return;} - if(!this._currentSessionId) {return;} - - console.log("IS PROCESSING", this._isProcessing) + if (!this._conversationsPath || this._currentConversation.length === 0) { return; } + if (!this._currentSessionId) { return; } try { // Create filename from first user message and timestamp @@ -1786,7 +1833,7 @@ class ClaudeChatProvider { const datePrefix = startTime.substring(0, 16).replace('T', '_').replace(/:/g, '-'); const filename = `${datePrefix}_${cleanMessage}.json`; - const conversationData : ConversationData = { + const conversationData: ConversationData = { sessionId: sessionId, startTime: this._conversationStartTime, endTime: new Date().toISOString(), @@ -1800,10 +1847,6 @@ class ClaudeChatProvider { filename }; - if (this._isProcessing !== undefined){ - conversationData.isProcessing = this._isProcessing || false - } - const filePath = path.join(this._conversationsPath, filename); const content = new TextEncoder().encode(JSON.stringify(conversationData, null, 2)); await vscode.workspace.fs.writeFile(vscode.Uri.file(filePath), content); @@ -1854,11 +1897,11 @@ class ClaudeChatProvider { fileList = fileList.filter(file => { const fileName = file.name.toLowerCase(); const filePath = file.path.toLowerCase(); - + // Check if term matches filename or any part of the path - return fileName.includes(term) || - filePath.includes(term) || - filePath.split('/').some(segment => segment.includes(term)); + return fileName.includes(term) || + filePath.includes(term) || + filePath.split('/').some(segment => segment.includes(term)); }); } @@ -1892,7 +1935,7 @@ class ClaudeChatProvider { 'Images': ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp'] } }); - + if (result && result.length > 0) { // Send the selected file paths back to webview result.forEach(uri => { @@ -1902,7 +1945,7 @@ class ClaudeChatProvider { }); }); } - + } catch (error) { console.error('Error selecting image files:', error); } @@ -1912,19 +1955,19 @@ class ClaudeChatProvider { console.log('Stop request received'); this._isProcessing = false - + // Update UI state this._postMessage({ type: 'setProcessing', - data: {isProcessing: false} + data: { isProcessing: false } }); - + if (this._currentClaudeProcess) { console.log('Terminating Claude process...'); - + // Try graceful termination first this._currentClaudeProcess.kill('SIGTERM'); - + // Force kill after 2 seconds if still running setTimeout(() => { if (this._currentClaudeProcess && !this._currentClaudeProcess.killed) { @@ -1932,20 +1975,20 @@ class ClaudeChatProvider { this._currentClaudeProcess.kill('SIGKILL'); } }, 2000); - + // Clear process reference this._currentClaudeProcess = undefined; - + this._postMessage({ type: 'clearLoading' }); - + // Send stop confirmation message directly to UI and save this._sendAndSaveMessage({ type: 'error', data: '⏹️ Claude code was stopped.' }); - + console.log('Claude process termination initiated'); } else { console.log('No Claude process running to stop'); @@ -1991,12 +2034,12 @@ class ClaudeChatProvider { private async _loadConversationHistory(filename: string): Promise { console.log("_loadConversationHistory"); - if (!this._conversationsPath) {return;} + if (!this._conversationsPath) { return; } try { const filePath = path.join(this._conversationsPath, filename); console.log("filePath", filePath); - + let conversationData: ConversationData; try { const fileUri = vscode.Uri.file(filePath); @@ -2005,8 +2048,7 @@ class ClaudeChatProvider { } catch { return; } - - console.log("conversationData-----", conversationData); + // Load conversation into current state this._currentConversation = conversationData.messages || []; this._conversationStartTime = conversationData.startTime; @@ -2030,10 +2072,10 @@ class ClaudeChatProvider { type: message.messageType, data: message.data }); - if(message.messageType === 'userInput'){ - try{ + if (message.messageType === 'userInput') { + try { requestStartTime = new Date(message.timestamp).getTime() - }catch(e){ + } catch (e) { console.log(e) } } @@ -2051,11 +2093,10 @@ class ClaudeChatProvider { }); // Restore processing state if the conversation was saved while processing - if (conversationData.isProcessing) { - this._isProcessing = conversationData.isProcessing; + if (this._isProcessing) { this._postMessage({ type: 'setProcessing', - data: {isProcessing: conversationData.isProcessing, requestStartTime} + data: { isProcessing: this._isProcessing, requestStartTime } }); } // Send ready message after conversation is loaded @@ -2094,15 +2135,15 @@ class ClaudeChatProvider { try { // Update VS Code configuration to enable YOLO mode const config = vscode.workspace.getConfiguration('claudeCodeChat'); - + // Clear any global setting and set workspace setting await config.update('permissions.yoloMode', true, vscode.ConfigurationTarget.Workspace); - + console.log('YOLO Mode enabled - all future permissions will be skipped'); - + // Send updated settings to UI this._sendCurrentSettings(); - + } catch (error) { console.error('Error enabling YOLO mode:', error); } @@ -2110,7 +2151,7 @@ class ClaudeChatProvider { private async _updateSettings(settings: { [key: string]: any }): Promise { const config = vscode.workspace.getConfiguration('claudeCodeChat'); - + try { for (const [key, value] of Object.entries(settings)) { if (key === 'permissions.yoloMode') { @@ -2121,7 +2162,7 @@ class ClaudeChatProvider { await config.update(key, value, vscode.ConfigurationTarget.Global); } } - + console.log('Settings updated:', settings); } catch (error) { console.error('Failed to update settings:', error); @@ -2147,10 +2188,10 @@ class ClaudeChatProvider { if (validModels.includes(model)) { this._selectedModel = model; console.log('Model selected:', model); - + // Store the model preference in workspace state this._context.workspaceState.update('claude.selectedModel', model); - + // Show confirmation vscode.window.showInformationMessage(`Claude model switched to: ${model.charAt(0).toUpperCase() + model.slice(1)}`); } else { @@ -2168,7 +2209,7 @@ class ClaudeChatProvider { // Build command arguments const args = ['/model']; - + // Add session resume if we have a current session if (this._currentSessionId) { args.push('--resume', this._currentSessionId); @@ -2205,7 +2246,7 @@ class ClaudeChatProvider { // Build command arguments const args = [`/${command}`]; - + // Add session resume if we have a current session if (this._currentSessionId) { args.push('--resume', this._currentSessionId); @@ -2236,7 +2277,7 @@ class ClaudeChatProvider { private _sendPlatformInfo() { const platform = process.platform; const dismissed = this._context.globalState.get('wslAlertDismissed', false); - + // Get WSL configuration const config = vscode.workspace.getConfiguration('claudeCodeChat'); const wslEnabled = config.get('wsl.enabled', false); @@ -2270,23 +2311,23 @@ class ClaudeChatProvider { private async _createImageFile(imageData: string, imageType: string) { try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (!workspaceFolder) {return;} + if (!workspaceFolder) { return; } // Extract base64 data from data URL const base64Data = imageData.split(',')[1]; const buffer = Buffer.from(base64Data, 'base64'); - + // Get file extension from image type const extension = imageType.split('/')[1] || 'png'; - + // Create unique filename with timestamp const timestamp = Date.now(); const imageFileName = `image_${timestamp}.${extension}`; - + // Create images folder in workspace .claude directory const imagesDir = vscode.Uri.joinPath(workspaceFolder.uri, '.claude', 'claude-code-chat-images'); await vscode.workspace.fs.createDirectory(imagesDir); - + // Create .gitignore to ignore all images const gitignorePath = vscode.Uri.joinPath(imagesDir, '.gitignore'); try { @@ -2296,11 +2337,11 @@ class ClaudeChatProvider { const gitignoreContent = new TextEncoder().encode('*\n'); await vscode.workspace.fs.writeFile(gitignorePath, gitignoreContent); } - + // Create the image file const imagePath = vscode.Uri.joinPath(imagesDir, imageFileName); await vscode.workspace.fs.writeFile(imagePath, buffer); - + // Send the file path back to webview this._postMessage({ type: 'imagePath', @@ -2308,7 +2349,7 @@ class ClaudeChatProvider { filePath: imagePath.fsPath } }); - + } catch (error) { console.error('Error creating image file:', error); vscode.window.showErrorMessage('Failed to create image file');