From a0bbc5764ed500b63e6b07990c8b01f3ad08d4e3 Mon Sep 17 00:00:00 2001 From: erwinh22 Date: Thu, 19 Jun 2025 15:10:04 -0400 Subject: [PATCH] Add settings UI with WSL configuration support - Add settings button to webview UI - Implement settings panel with WSL bridge configuration - Add real-time settings updates via VS Code configuration API - Support for WSL distro, node path, and claude path configuration - Improve user experience by eliminating manual config file editing --- CHANGELOG.md | 17 +++++- README.md | 21 ++++++++ TEST_SETTINGS.md | 55 +++++++++++++++++++ package-lock.json | 4 +- package.json | 30 ++++++++++- src/extension.ts | 118 +++++++++++++++++++++++++++++++++-------- src/ui.ts | 131 +++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 349 insertions(+), 27 deletions(-) create mode 100644 TEST_SETTINGS.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2416a93..2fe902f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to the "claude-code-chat" extension will be documented in th Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. -## [Unreleased] +## [0.0.8] - 2025-06-19 + +### Added +- WSL (Windows Subsystem for Linux) configuration support + - New setting: `claudeCodeChat.wsl.enabled` to enable WSL integration + - New setting: `claudeCodeChat.wsl.distro` to specify WSL distribution + - New setting: `claudeCodeChat.wsl.nodePath` to configure Node.js path in WSL + - New setting: `claudeCodeChat.wsl.claudePath` to configure Claude path in WSL +- Automatic detection of execution environment (native vs WSL) +- WSL support for Claude login terminal command + +### Changed +- Claude execution now supports both native and WSL environments based on configuration +- Terminal login command adapts to WSL settings when enabled + +## [0.0.7] - Previous Release - Initial release \ No newline at end of file diff --git a/README.md b/README.md index 8fc3506..51d0969 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,27 @@ If you want to revert these changes, just click "Restore Checkpoint" to go back | `Enter` | Send message | | `@` | Open file picker | +### WSL Configuration (Windows Users) +If you're using Claude Code through WSL (Windows Subsystem for Linux), you can configure the extension to use WSL: + +1. Open VS Code Settings (`Ctrl+,` or `Cmd+,`) +2. Search for "Claude Code Chat" +3. Configure these settings: + - **Claude Code Chat: WSL Enabled** - Enable WSL integration + - **Claude Code Chat: WSL Distro** - Your WSL distribution name (e.g., `Ubuntu`, `Debian`) + - **Claude Code Chat: WSL Node Path** - Path to Node.js in WSL (default: `/usr/bin/node`) + - **Claude Code Chat: WSL Claude Path** - Path to Claude in WSL (default: `/usr/local/bin/claude`) + +Example configuration in `settings.json`: +```json +{ + "claudeCodeChat.wsl.enabled": true, + "claudeCodeChat.wsl.distro": "Ubuntu", + "claudeCodeChat.wsl.nodePath": "/usr/bin/node", + "claudeCodeChat.wsl.claudePath": "/usr/local/bin/claude" +} +``` + --- ## 🎯 **Pro Tips & Tricks** diff --git a/TEST_SETTINGS.md b/TEST_SETTINGS.md new file mode 100644 index 0000000..2b633ce --- /dev/null +++ b/TEST_SETTINGS.md @@ -0,0 +1,55 @@ +# Settings Interface Test Plan + +## Overview +Added a settings interface to the Claude Code Chat webview that allows users to configure WSL settings. + +## Changes Made + +### 1. UI Changes (ui.ts) +- Added a settings button (⚙️) in the header that's always visible +- Created a settings modal with WSL configuration options: + - Enable WSL Integration checkbox + - WSL Distribution input field + - Node.js Path in WSL input field + - Claude Path in WSL input field +- Added JavaScript functions: + - `toggleSettings()` - Shows/hides the settings modal + - `hideSettingsModal()` - Hides the settings modal + - `updateSettings()` - Sends settings changes to VS Code + - Event listeners for modal interaction + +### 2. Extension Changes (extension.ts) +- Added message handlers: + - `getSettings` - Retrieves current settings from VS Code configuration + - `updateSettings` - Updates VS Code configuration with new settings +- Added methods: + - `_sendCurrentSettings()` - Sends current settings to webview + - `_updateSettings()` - Updates VS Code configuration + +### 3. Configuration (package.json) +- Already has WSL configuration properties defined: + - `claudeCodeChat.wsl.enabled` + - `claudeCodeChat.wsl.distro` + - `claudeCodeChat.wsl.nodePath` + - `claudeCodeChat.wsl.claudePath` + +## Testing Steps + +1. Open VS Code with the extension +2. Open Claude Code Chat (Ctrl+Shift+C) +3. Click the settings button (⚙️) in the header +4. Verify the settings modal appears +5. Check that current WSL settings are loaded +6. Toggle "Enable WSL Integration" and verify: + - WSL options show/hide accordingly + - Settings are saved when changed +7. Modify WSL settings and verify they persist +8. Close and reopen the settings to confirm values are saved + +## Features + +- Settings button is always visible in the header +- Modal design matches the existing tools modal +- Real-time show/hide of WSL options based on enabled state +- Settings persist across sessions +- Success/error notifications when saving settings \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 392a601..c2a060d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-chat", - "version": "0.0.6", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-chat", - "version": "0.0.6", + "version": "0.0.7", "license": "SEE LICENSE IN LICENSE", "devDependencies": { "@types/mocha": "^10.0.10", diff --git a/package.json b/package.json index 101b74f..4a3f2e3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-code-chat", "displayName": "Claude Code Chat", "description": "Beautiful Claude Code Chat Interface for VS Code", - "version": "0.0.7", + "version": "0.0.8", "publisher": "AndrePimenta", "author": "Andre Pimenta", "repository": { @@ -47,6 +47,9 @@ ], "icon": "icon.png", "main": "./out/extension.js", + "activationEvents": [ + "onStartupFinished" + ], "contributes": { "commands": [ { @@ -142,6 +145,31 @@ "icon": "icon.png" } ] + }, + "configuration": { + "title": "Claude Code Chat", + "properties": { + "claudeCodeChat.wsl.enabled": { + "type": "boolean", + "default": false, + "description": "Enable WSL integration for running Claude" + }, + "claudeCodeChat.wsl.distro": { + "type": "string", + "default": "Ubuntu", + "description": "WSL distribution name (e.g., Ubuntu, Debian)" + }, + "claudeCodeChat.wsl.nodePath": { + "type": "string", + "default": "/usr/bin/node", + "description": "Path to Node.js in the WSL distribution" + }, + "claudeCodeChat.wsl.claudePath": { + "type": "string", + "default": "/usr/local/bin/claude", + "description": "Path to Claude executable in the WSL distribution" + } + } } }, "scripts": { diff --git a/src/extension.ts b/src/extension.ts index 638e3b6..60e7c90 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,9 +7,11 @@ import html from './ui'; const exec = util.promisify(cp.exec); export function activate(context: vscode.ExtensionContext) { + console.log('Claude Code Chat extension is being activated!'); const provider = new ClaudeChatProvider(context.extensionUri, context); const disposable = vscode.commands.registerCommand('claude-code-chat.openChat', () => { + console.log('Claude Code Chat command executed!'); provider.show(); }); @@ -33,6 +35,7 @@ export function activate(context: vscode.ExtensionContext) { statusBarItem.show(); context.subscriptions.push(disposable, loadConversationDisposable, statusBarItem); + console.log('Claude Code Chat extension activation completed successfully!'); } export function deactivate() { } @@ -197,6 +200,12 @@ class ClaudeChatProvider { case 'stopRequest': this._stopClaudeProcess(); return; + case 'getSettings': + this._sendCurrentSettings(); + return; + case 'updateSettings': + this._updateSettings(message.settings); + return; } }, null, @@ -252,7 +261,7 @@ class ClaudeChatProvider { await this._createBackupCommit(message); } catch (e) { - console.log("error", e) + console.log("error", e); } // Show loading indicator @@ -281,15 +290,40 @@ class ClaudeChatProvider { console.log('Claude command args:', args); - const claudeProcess = cp.spawn('claude', args, { - cwd: cwd, - stdio: ['pipe', 'pipe', 'pipe'], - env: { - ...process.env, - FORCE_COLOR: '0', - NO_COLOR: '1' - } - }); + // Get configuration + const config = vscode.workspace.getConfiguration('claudeCodeChat'); + const wslEnabled = config.get('wsl.enabled', false); + const wslDistro = config.get('wsl.distro', 'Ubuntu'); + const nodePath = config.get('wsl.nodePath', '/usr/bin/node'); + const claudePath = config.get('wsl.claudePath', '/usr/local/bin/claude'); + + let claudeProcess: cp.ChildProcess; + + if (wslEnabled) { + // Use WSL + console.log('Using WSL configuration:', { wslDistro, nodePath, claudePath }); + claudeProcess = cp.spawn('wsl', ['-d', wslDistro, nodePath, '--no-warnings', '--enable-source-maps', claudePath, ...args], { + cwd: cwd, + stdio: ['pipe', 'pipe', 'pipe'], + env: { + ...process.env, + FORCE_COLOR: '0', + NO_COLOR: '1' + } + }); + } else { + // Use native claude command + console.log('Using native Claude command'); + claudeProcess = cp.spawn('claude', args, { + cwd: cwd, + stdio: ['pipe', 'pipe', 'pipe'], + env: { + ...process.env, + FORCE_COLOR: '0', + NO_COLOR: '1' + } + }); + } // Store process reference for potential termination this._currentClaudeProcess = claudeProcess; @@ -582,9 +616,19 @@ class ClaudeChatProvider { type: 'loginRequired' }); + // Get configuration to check if WSL is enabled + const config = vscode.workspace.getConfiguration('claudeCodeChat'); + const wslEnabled = config.get('wsl.enabled', false); + const wslDistro = config.get('wsl.distro', 'Ubuntu'); + const claudePath = config.get('wsl.claudePath', '/usr/local/bin/claude'); + // Open terminal and run claude login const terminal = vscode.window.createTerminal('Claude Login'); - terminal.sendText('claude'); + if (wslEnabled) { + terminal.sendText(`wsl -d ${wslDistro} ${claudePath}`); + } else { + terminal.sendText('claude'); + } terminal.show(); // Show info message @@ -597,7 +641,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) { @@ -630,7 +674,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(); @@ -739,10 +783,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'); @@ -765,7 +809,7 @@ class ClaudeChatProvider { } if (message.type === 'sessionInfo') { - message.data.sessionId + message.data.sessionId; } // Send to UI @@ -783,7 +827,7 @@ class ClaudeChatProvider { } private async _saveCurrentConversation(): Promise { - if (!this._conversationsPath || this._currentConversation.length === 0) return; + if (!this._conversationsPath || this._currentConversation.length === 0) {return;} try { // Create filename from first user message and timestamp @@ -1009,12 +1053,12 @@ class ClaudeChatProvider { } private async _loadConversationHistory(filename: string): Promise { - console.log("_loadConversationHistory") - if (!this._conversationsPath) return; + console.log("_loadConversationHistory"); + if (!this._conversationsPath) {return;} try { const filePath = path.join(this._conversationsPath, filename); - console.log("filePath", filePath) + console.log("filePath", filePath); let conversationData; try { @@ -1025,7 +1069,7 @@ class ClaudeChatProvider { return; } - console.log("conversationData", conversationData) + console.log("conversationData", conversationData); // Load conversation into current state this._currentConversation = conversationData.messages || []; this._conversationStartTime = conversationData.startTime; @@ -1069,7 +1113,37 @@ class ClaudeChatProvider { } private _getHtmlForWebview(): string { - return html + return html; + } + + private _sendCurrentSettings(): void { + const config = vscode.workspace.getConfiguration('claudeCodeChat'); + const settings = { + 'wsl.enabled': config.get('wsl.enabled', false), + 'wsl.distro': config.get('wsl.distro', 'Ubuntu'), + 'wsl.nodePath': config.get('wsl.nodePath', '/usr/bin/node'), + 'wsl.claudePath': config.get('wsl.claudePath', '/usr/local/bin/claude') + }; + + this._panel?.webview.postMessage({ + type: 'settingsData', + data: settings + }); + } + + private async _updateSettings(settings: { [key: string]: any }): Promise { + const config = vscode.workspace.getConfiguration('claudeCodeChat'); + + try { + for (const [key, value] of Object.entries(settings)) { + await config.update(key, value, vscode.ConfigurationTarget.Global); + } + + vscode.window.showInformationMessage('Settings updated successfully'); + } catch (error) { + console.error('Failed to update settings:', error); + vscode.window.showErrorMessage('Failed to update settings'); + } } public dispose() { diff --git a/src/ui.ts b/src/ui.ts index 517af54..2a2250e 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -727,7 +727,7 @@ const html = ` background-color: var(--vscode-editor-background); border: 1px solid var(--vscode-panel-border); border-radius: 4px; - width: 450px; + width: 500px; max-height: 600px; display: flex; flex-direction: column; @@ -797,6 +797,17 @@ const html = ` opacity: 0.7; } + .settings-group { + margin-bottom: 20px; + } + + .settings-group h3 { + margin: 0 0 12px 0; + font-size: 13px; + font-weight: 600; + color: var(--vscode-foreground); + } + .status { padding: 8px 12px; background: linear-gradient(135deg, #1e1e1e 0%, #2d2d2d 100%); @@ -1071,6 +1082,7 @@ const html = `
+
@@ -1216,6 +1228,57 @@ const html = ` + + + `;