mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2025-12-10 23:09:50 +00:00
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
This commit is contained in:
118
src/extension.ts
118
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<boolean>('wsl.enabled', false);
|
||||
const wslDistro = config.get<string>('wsl.distro', 'Ubuntu');
|
||||
const nodePath = config.get<string>('wsl.nodePath', '/usr/bin/node');
|
||||
const claudePath = config.get<string>('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<boolean>('wsl.enabled', false);
|
||||
const wslDistro = config.get<string>('wsl.distro', 'Ubuntu');
|
||||
const claudePath = config.get<string>('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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<boolean>('wsl.enabled', false),
|
||||
'wsl.distro': config.get<string>('wsl.distro', 'Ubuntu'),
|
||||
'wsl.nodePath': config.get<string>('wsl.nodePath', '/usr/bin/node'),
|
||||
'wsl.claudePath': config.get<string>('wsl.claudePath', '/usr/local/bin/claude')
|
||||
};
|
||||
|
||||
this._panel?.webview.postMessage({
|
||||
type: 'settingsData',
|
||||
data: settings
|
||||
});
|
||||
}
|
||||
|
||||
private async _updateSettings(settings: { [key: string]: any }): Promise<void> {
|
||||
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() {
|
||||
|
||||
131
src/ui.ts
131
src/ui.ts
@@ -727,7 +727,7 @@ const html = `<!DOCTYPE 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 = `<!DOCTYPE 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 = `<!DOCTYPE html>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<div id="sessionStatus" class="session-status" style="display: none;">No session</div>
|
||||
<button class="btn outlined" id="settingsBtn" onclick="toggleSettings()" title="Settings">⚙️</button>
|
||||
<button class="btn outlined" id="historyBtn" onclick="toggleConversationHistory()" style="display: none;">📚 History</button>
|
||||
<button class="btn primary" id="newSessionBtn" onclick="newSession()" style="display: none;">New Chat</button>
|
||||
</div>
|
||||
@@ -1216,6 +1228,57 @@ const html = `<!DOCTYPE html>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings modal -->
|
||||
<div id="settingsModal" class="tools-modal" style="display: none;">
|
||||
<div class="tools-modal-content">
|
||||
<div class="tools-modal-header">
|
||||
<span>Claude Code Chat Settings</span>
|
||||
<button class="tools-close-btn" onclick="hideSettingsModal()">✕</button>
|
||||
</div>
|
||||
<div class="tools-list">
|
||||
<h3 style="margin-top: 0; margin-bottom: 16px; font-size: 14px; font-weight: 600;">WSL Configuration</h3>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="tool-item">
|
||||
<input type="checkbox" id="wsl-enabled" onchange="updateSettings()">
|
||||
<label for="wsl-enabled">Enable WSL Integration</label>
|
||||
</div>
|
||||
|
||||
<div id="wslOptions" style="margin-left: 24px; margin-top: 12px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--vscode-descriptionForeground);">WSL Distribution</label>
|
||||
<input type="text" id="wsl-distro" class="file-search-input" style="width: 100%;" placeholder="Ubuntu" onchange="updateSettings()">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--vscode-descriptionForeground);">Node.js Path in WSL</label>
|
||||
<input type="text" id="wsl-node-path" class="file-search-input" style="width: 100%;" placeholder="/usr/bin/node" onchange="updateSettings()">
|
||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 4px 0 0 0;">
|
||||
Find your node installation path in WSL by running: <code style="background: var(--vscode-textCodeBlock-background); padding: 2px 4px; border-radius: 3px;">which node</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-size: 12px; color: var(--vscode-descriptionForeground);">Claude Path in WSL</label>
|
||||
<input type="text" id="wsl-claude-path" class="file-search-input" style="width: 100%;" placeholder="/usr/local/bin/claude" onchange="updateSettings()">
|
||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 4px 0 0 0;">
|
||||
Find your claude installation path in WSL by running: <code style="background: var(--vscode-textCodeBlock-background); padding: 2px 4px; border-radius: 3px;">which claude</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--vscode-panel-border);">
|
||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
||||
WSL integration allows you to run Claude Code from within Windows Subsystem for Linux.
|
||||
This is useful if you have Claude installed in WSL instead of Windows.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
const messagesDiv = document.getElementById('messages');
|
||||
@@ -2281,6 +2344,72 @@ const html = `<!DOCTYPE html>
|
||||
listDiv.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
// Settings functions
|
||||
|
||||
function toggleSettings() {
|
||||
const settingsModal = document.getElementById('settingsModal');
|
||||
if (settingsModal.style.display === 'none') {
|
||||
// Request current settings from VS Code
|
||||
vscode.postMessage({
|
||||
type: 'getSettings'
|
||||
});
|
||||
settingsModal.style.display = 'flex';
|
||||
} else {
|
||||
hideSettingsModal();
|
||||
}
|
||||
}
|
||||
|
||||
function hideSettingsModal() {
|
||||
document.getElementById('settingsModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
const wslEnabled = document.getElementById('wsl-enabled').checked;
|
||||
const wslDistro = document.getElementById('wsl-distro').value;
|
||||
const wslNodePath = document.getElementById('wsl-node-path').value;
|
||||
const wslClaudePath = document.getElementById('wsl-claude-path').value;
|
||||
|
||||
// Update WSL options visibility
|
||||
document.getElementById('wslOptions').style.display = wslEnabled ? 'block' : 'none';
|
||||
|
||||
// Send settings to VS Code immediately
|
||||
vscode.postMessage({
|
||||
type: 'updateSettings',
|
||||
settings: {
|
||||
'wsl.enabled': wslEnabled,
|
||||
'wsl.distro': wslDistro || 'Ubuntu',
|
||||
'wsl.nodePath': wslNodePath || '/usr/bin/node',
|
||||
'wsl.claudePath': wslClaudePath || '/usr/local/bin/claude'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Close settings modal when clicking outside
|
||||
document.getElementById('settingsModal').addEventListener('click', (e) => {
|
||||
if (e.target === document.getElementById('settingsModal')) {
|
||||
hideSettingsModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Add settings message handler to window message event
|
||||
const originalMessageHandler = window.onmessage;
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
|
||||
if (message.type === 'settingsData') {
|
||||
// Update UI with current settings
|
||||
document.getElementById('wsl-enabled').checked = message.data['wsl.enabled'] || false;
|
||||
document.getElementById('wsl-distro').value = message.data['wsl.distro'] || 'Ubuntu';
|
||||
document.getElementById('wsl-node-path').value = message.data['wsl.nodePath'] || '/usr/bin/node';
|
||||
document.getElementById('wsl-claude-path').value = message.data['wsl.claudePath'] || '/usr/local/bin/claude';
|
||||
|
||||
// Show/hide WSL options
|
||||
document.getElementById('wslOptions').style.display = message.data['wsl.enabled'] ? 'block' : 'none';
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
Reference in New Issue
Block a user