From 09692629cdfd9d46b0eee5f59205ffb42160ede2 Mon Sep 17 00:00:00 2001 From: Ian Jennings Date: Sun, 17 Aug 2025 17:40:39 -0500 Subject: [PATCH 1/2] fix for colors --- src/utils/sidebarProvider.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/utils/sidebarProvider.ts b/src/utils/sidebarProvider.ts index 617e8e4..d63112d 100644 --- a/src/utils/sidebarProvider.ts +++ b/src/utils/sidebarProvider.ts @@ -714,6 +714,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { overflow-y: hidden; white-space: pre; max-width: 100%; + color: #b3d334 !important; word-wrap: normal; /* Don't break words in code */ } @@ -729,7 +730,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { padding: 0 !important; margin: 0 !important; font-family: inherit !important; - color: var(--vscode-editor-foreground) !important; + color: #b3d334 !important; white-space: pre; overflow-wrap: normal; } @@ -745,7 +746,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { .code-block code[class*="language-"] { background: none !important; - color: var(--vscode-editor-foreground) !important; + color: #b3d334 !important; } /* Override Prism.js tokens to use VS Code colors */ @@ -763,7 +764,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { .token.constant, .token.symbol, .token.deleted { - color: var( #b3d334) !important; + color: #b3d334 !important; } .token.selector, @@ -772,7 +773,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { .token.char, .token.builtin, .token.inserted { - color: var(#34d3d3) !important; + color: #b3d334 !important; } .token.operator, @@ -786,18 +787,18 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { .token.atrule, .token.attr-value, .token.keyword { - color: var(#34d3d3) !important; + color: #34d3d3 !important; } .token.function, .token.class-name { - color: var(#DCDCAA) !important; + color: #DCDCAA !important; } .token.regex, .token.important, .token.variable { - color: var(#9CDCFE) !important; + color: #9CDCFE !important; } /* VS Code icon styling */ From 6cacc0898f9a1dc3a53c6f760d977a24b1b34aa4 Mon Sep 17 00:00:00 2001 From: Ian Jennings Date: Thu, 21 Aug 2025 10:39:27 -0500 Subject: [PATCH 2/2] usability patches --- media/webview.js | 17 +++++++++++ package.json | 8 ++--- src/commands/chat.ts | 8 +++++ src/commands/runTest.ts | 8 +++++ src/extension.ts | 57 ++++++++++++++++++++++++++++++++++-- src/utils/sidebarProvider.ts | 40 ++++++++++++++++++++++--- 6 files changed, 128 insertions(+), 10 deletions(-) diff --git a/media/webview.js b/media/webview.js index 8825041..5a2e339 100644 --- a/media/webview.js +++ b/media/webview.js @@ -227,6 +227,9 @@ class TestDriverWebview { case 'showSuggestedPromptsAfterExample': this.showSuggestedPromptsAfterExample(); break; + case 'showNoWorkspaceMessage': + this.showNoWorkspaceMessage(); + break; case 'error': this.addMessage(message.data, 'error', '❌'); this.isRunning = false; @@ -684,6 +687,20 @@ class TestDriverWebview { } } + showNoWorkspaceMessage() { + // Clear any existing messages + this.clearChat(); + + // Show a message indicating no workspace is available + this.addMessage('📁 No workspace folder is open', 'system', '📁'); + this.addMessage('Please open a folder to get started with TestDriver. Go to File → Open Folder... to select a project folder.', 'system', 'â„šī¸'); + + // Ensure empty state is hidden since we're showing messages + if (this.emptyState) { + this.emptyState.style.display = 'none'; + } + } + showSuggestedPromptsAfterExample() { // Set flag to show suggested prompts and keep them visible until first user message this.showSuggestedAfterExample = true; diff --git a/package.json b/package.json index bf52b49..7f4dfa1 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ { "id": "signup", "title": "Get a TestDriver API Key", - "description": "Because TestDriver is powered by agentic users, a free account is required to access:\n\n - Dedicated Virtual Machines (VMs) for running agents\n- TestDriver AI + Vision models\n- Community support\n[Get API Key](https://app.testdriver.ai/)", + "description": "An account is required to access TestDriver's AI Model as well as sandbox environments for running tests..\n[Get API Key](https://app.testdriver.ai/)", "media": { "image": "media/main.png", "altText": "TestDriver main interface" @@ -64,7 +64,7 @@ "altText": "TestDriver API key setup" }, "completionEvents": [ - "command:testdriver.setApiKey" + "onContext:testdriver.hasApiKey" ] }, { @@ -76,7 +76,7 @@ "altText": "TestDriver chat interface" }, "completionEvents": [ - "command:testdriver.openChat" + "onContext:testdriver.chatOpened" ] }, { @@ -88,7 +88,7 @@ "altText": "TestDriver run test interface" }, "completionEvents": [ - "command:testdriver.runTest" + "onContext:testdriver.testPanelOpened" ] }, { diff --git a/src/commands/chat.ts b/src/commands/chat.ts index b0d360c..9def9c0 100644 --- a/src/commands/chat.ts +++ b/src/commands/chat.ts @@ -313,6 +313,14 @@ export function registerChatCommand(context: vscode.ExtensionContext) { // Focus on the TestDriver sidebar view instead of creating a separate webview panel await vscode.commands.executeCommand('testdriver-sidebar.focus'); + + // Set context to indicate chat has been opened for walkthrough completion + await vscode.commands.executeCommand('setContext', 'testdriver.chatOpened', true); + + // Persist this state for future sessions + await context.globalState.update('testdriver.chatOpenedBefore', true); + + return true; // Return success for walkthrough completion }); context.subscriptions.push(disposable); diff --git a/src/commands/runTest.ts b/src/commands/runTest.ts index dd82609..efa1d64 100644 --- a/src/commands/runTest.ts +++ b/src/commands/runTest.ts @@ -10,6 +10,14 @@ export function registerRunTestCommand(context: vscode.ExtensionContext) { const disposable = vscode.commands.registerCommand('testdriver.runTest', async (_uri?: vscode.Uri) => { // Open the VS Code Test Explorer panel await vscode.commands.executeCommand('workbench.view.testing.focus'); + + // Set context to indicate test panel has been opened for walkthrough completion + await vscode.commands.executeCommand('setContext', 'testdriver.testPanelOpened', true); + + // Persist this state for future sessions + await context.globalState.update('testdriver.testPanelOpenedBefore', true); + + return true; // Return success for walkthrough completion }); context.subscriptions.push(disposable); } diff --git a/src/extension.ts b/src/extension.ts index 9a8f647..0edeb0a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -68,6 +68,33 @@ export async function activate(context: vscode.ExtensionContext) { registerTestdriverRunTest(context); registerTestdriverChat(context); + // Check if API key is already set and mark walkthrough step as complete if needed + const existingApiKey = await context.secrets.get('TD_API_KEY'); + if (existingApiKey) { + logger.info('API key already exists, walkthrough step should be complete'); + vscode.commands.executeCommand('setContext', 'testdriver.hasApiKey', true); + } else { + vscode.commands.executeCommand('setContext', 'testdriver.hasApiKey', false); + } + + // Check if chat has been opened before (for returning users) + const chatOpenedBefore = context.globalState.get('testdriver.chatOpenedBefore', false); + if (chatOpenedBefore || !isFirstInstall) { + // If not first install, assume chat has been opened before + vscode.commands.executeCommand('setContext', 'testdriver.chatOpened', true); + } else { + vscode.commands.executeCommand('setContext', 'testdriver.chatOpened', false); + } + + // Check if test panel has been opened before (for returning users) + const testPanelOpenedBefore = context.globalState.get('testdriver.testPanelOpenedBefore', false); + if (testPanelOpenedBefore || !isFirstInstall) { + // If not first install, assume test panel has been opened before + vscode.commands.executeCommand('setContext', 'testdriver.testPanelOpened', true); + } else { + vscode.commands.executeCommand('setContext', 'testdriver.testPanelOpened', false); + } + // Register the sidebar provider const sidebarProvider = new TestDriverSidebarProvider(context.extensionUri, context); context.subscriptions.push( @@ -110,16 +137,42 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(disposable); const apiKeyDisposable = vscode.commands.registerCommand('testdriver.setApiKey', async () => { + logger.info('API key command started'); + + // Check if API key is already set + const existingKey = await context.secrets.get('TD_API_KEY'); + if (existingKey) { + const overwrite = await vscode.window.showQuickPick( + ['Yes, replace it', 'No, keep existing'], + { + placeHolder: 'API key is already set. Do you want to replace it?' + } + ); + if (overwrite !== 'Yes, replace it') { + logger.info('API key command cancelled - keeping existing key'); + return true; // Consider this a successful completion since key exists + } + } + const apiKey = await vscode.window.showInputBox({ prompt: 'Enter your TestDriver API key (from app.testdriver.ai/team)', ignoreFocusOut: true, password: true }); - if (apiKey) { - await context.secrets.store('TD_API_KEY', apiKey); + if (apiKey && apiKey.trim()) { + await context.secrets.store('TD_API_KEY', apiKey.trim()); vscode.window.showInformationMessage('TestDriver API key saved securely.'); + logger.info('API key saved successfully'); + track({ event: 'api_key.set' }); + + // Set context to indicate API key is now available + await vscode.commands.executeCommand('setContext', 'testdriver.hasApiKey', true); + + return true; // Return success for walkthrough completion } else { vscode.window.showWarningMessage('No API key entered.'); + logger.info('API key command cancelled'); + return false; // Return failure } }); context.subscriptions.push(apiKeyDisposable); diff --git a/src/utils/sidebarProvider.ts b/src/utils/sidebarProvider.ts index 029c4af..bdcf621 100644 --- a/src/utils/sidebarProvider.ts +++ b/src/utils/sidebarProvider.ts @@ -71,10 +71,24 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { if (workspaceFolder) { const fileName = editor.document.uri.fsPath.split('/').pop() || 'test file'; this._updateFileIndicator(workspaceFolder.name, fileName); + } else { + // Handle case where workspace folder is not defined + const fileName = editor.document.uri.fsPath.split('/').pop() || 'test file'; + this._updateFileIndicator('Unknown Workspace', fileName); } } }) ); + + // Listen for workspace folder changes to refresh the UI + this._context.subscriptions.push( + vscode.workspace.onDidChangeWorkspaceFolders(async () => { + console.log('Workspace folders changed, refreshing sidebar'); + if (this._view) { + await this._checkAndShowExamples(this._view); + } + }) + ); } public postMessage(message: { command: string; data: string }) { @@ -89,10 +103,14 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { private _updateFileIndicator(workspaceName: string, fileName: string) { if (this._view) { + // Provide fallback values for safety + const safeworkspaceName = workspaceName || 'Unknown Workspace'; + const safeFileName = fileName || 'No file selected'; + this._view.webview.postMessage({ command: 'updateFileIndicator', - workspaceName: workspaceName, - fileName: fileName + workspaceName: safeworkspaceName, + fileName: safeFileName }); } } @@ -398,7 +416,21 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { const targetWorkspaceFolder = await this._getTargetWorkspaceFolder(); if (!targetWorkspaceFolder) { - console.log('No workspace folders found'); + console.log('No workspace folders found, showing default state'); + + // Handle case when no workspace is available + this._updateFileIndicator('No Workspace', 'Open a folder to get started'); + + // Show a message to the user about opening a workspace + webviewView.webview.postMessage({ + command: 'showNoWorkspaceMessage' + }); + + // Hide both the input area and run button when no workspace + webviewView.webview.postMessage({ + command: 'hideInputAndRunButton' + }); + return; } @@ -423,7 +455,7 @@ export class TestDriverSidebarProvider implements vscode.WebviewViewProvider { const workspaceFolders = vscode.workspace.workspaceFolders; const isExtensionDev = workspaceFolders?.some(folder => folder.uri.fsPath.includes('testdriver-vscode-extension') - ); + ) ?? false; if (isExtensionDev && !showExamples) { console.log('Extension development mode detected, showing examples for testing');