diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index a550b4702bf..9dfc4565f3b 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -560,6 +560,21 @@ ] }, "commands": [ + { + "command": "aws.amazonq.stopCmdExecution", + "title": "Stop Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.runCmdExecution", + "title": "Run Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "title": "Reject Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, { "command": "_aws.amazonq.notifications.dismiss", "title": "%AWS.generic.dismiss%", @@ -850,6 +865,24 @@ } ], "keybindings": [ + { + "command": "aws.amazonq.stopCmdExecution", + "key": "ctrl+shift+backspace", + "mac": "cmd+shift+backspace", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.runCmdExecution", + "key": "ctrl+shift+enter", + "mac": "cmd+shift+enter", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "key": "ctrl+shift+r", + "mac": "cmd+shift+r", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, { "command": "_aws.amazonq.focusChat.keybinding", "win": "win+alt+i", diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 8998144e7e9..83e70b7bae3 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -59,7 +59,10 @@ export function registerCommands(provider: AmazonQChatViewProvider) { params: {}, }) }) - }) + }), + registerShellCommandShortCut('aws.amazonq.runCmdExecution', 'run-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.rejectCmdExecution', 'reject-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.stopCmdExecution', 'stop-shell-command', provider) ) } @@ -156,3 +159,14 @@ export async function focusAmazonQPanel() { await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus') await Commands.tryExecute('aws.amazonq.AmazonCommonAuth.focus') } + +function registerShellCommandShortCut(commandName: string, buttonId: string, provider: AmazonQChatViewProvider) { + return Commands.register(commandName, async () => { + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'aws/chat/executeShellCommandShortCut', + params: { id: buttonId }, + }) + }) + }) +} diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 57253abc2ba..71de85f90a7 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -88,6 +88,7 @@ import { openUrl, isTextEditor, globals, + setContext, } from 'aws-core-vscode/shared' import { DefaultAmazonQAppInitContext, @@ -490,6 +491,11 @@ export function registerMessageListeners( } default: if (isServerEvent(message.command)) { + if (enterFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', true) + } else if (exitFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', false) + } languageClient.sendNotification(message.command, message.params) } break @@ -699,6 +705,14 @@ function isServerEvent(command: string) { return command.startsWith('aws/chat/') || command === 'telemetry/event' } +function enterFocus(params: any) { + return params.name === 'enterFocus' +} + +function exitFocus(params: any) { + return params.name === 'exitFocus' +} + /** * Decodes partial chat responses from the language server before sending them to mynah UI */ diff --git a/packages/amazonq/src/lsp/chat/webviewProvider.ts b/packages/amazonq/src/lsp/chat/webviewProvider.ts index 7d51648398d..109a6afd10f 100644 --- a/packages/amazonq/src/lsp/chat/webviewProvider.ts +++ b/packages/amazonq/src/lsp/chat/webviewProvider.ts @@ -13,6 +13,7 @@ import { Webview, } from 'vscode' import * as path from 'path' +import * as os from 'os' import { globals, isSageMaker, @@ -149,7 +150,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider { const vscodeApi = acquireVsCodeApi() const hybridChatConnector = new HybridChatAdapter(${(await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected'},${featureConfigData},${welcomeCount},${disclaimerAcknowledged},${regionProfileString},${disabledCommands},${isSMUS},${isSM},vscodeApi.postMessage) const commands = [hybridChatConnector.initialQuickActions[0]] - qChat = amazonQChat.createChat(vscodeApi, {disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, agenticMode: true, quickActionCommands: commands, modelSelectionEnabled: ${modelSelectionEnabled}}, hybridChatConnector, ${JSON.stringify(featureConfigData)}); + qChat = amazonQChat.createChat(vscodeApi, {os: "${os.platform()}", disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, agenticMode: true, quickActionCommands: commands, modelSelectionEnabled: ${modelSelectionEnabled}}, hybridChatConnector, ${JSON.stringify(featureConfigData)}); } window.addEventListener('message', (event) => { /** diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index ab1d0665325..4d052912c8e 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -165,6 +165,7 @@ export async function startLanguageServer( pinnedContextEnabled: true, imageContextEnabled: true, mcp: true, + shortcut: true, reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, diff --git a/packages/core/src/shared/vscode/setContext.ts b/packages/core/src/shared/vscode/setContext.ts index 08d651578dc..7cfaf4092f8 100644 --- a/packages/core/src/shared/vscode/setContext.ts +++ b/packages/core/src/shared/vscode/setContext.ts @@ -40,6 +40,7 @@ export type contextKey = | 'gumby.wasQCodeTransformationUsed' | 'amazonq.inline.codelensShortcutEnabled' | 'aws.toolkit.lambda.walkthroughSelected' + | 'aws.amazonq.amazonqChatLSP.isFocus' const contextMap: Partial> = {}