diff --git a/src/vs/sessions/common/sessionCommands.ts b/src/vs/sessions/common/sessionCommands.ts index 9a76c0a9b93c4..c3c47d95b3028 100644 --- a/src/vs/sessions/common/sessionCommands.ts +++ b/src/vs/sessions/common/sessionCommands.ts @@ -19,6 +19,9 @@ export const RENAME_SESSION_COMMAND_ID = 'sessionsViewPane.renameSession'; /** Closes a chat tab. Registered in `sessionsActions.ts`. */ export const CLOSE_CHAT_COMMAND_ID = 'sessions.chatCompositeBar.closeChat'; +/** Focuses the active session. Registered in `sessionsActions.ts`. */ +export const FOCUS_ACTIVE_SESSION_COMMAND_ID = 'sessions.focusActiveSession'; + export const FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID = 'sessions.focusPreviousChatGroup'; export const FOCUS_NEXT_CHAT_GROUP_COMMAND_ID = 'sessions.focusNextChatGroup'; diff --git a/src/vs/sessions/contrib/chat/browser/sessionsChatAccessibilityHelp.ts b/src/vs/sessions/contrib/chat/browser/sessionsChatAccessibilityHelp.ts index 5ad65102faec8..5dc55fba66a06 100644 --- a/src/vs/sessions/contrib/chat/browser/sessionsChatAccessibilityHelp.ts +++ b/src/vs/sessions/contrib/chat/browser/sessionsChatAccessibilityHelp.ts @@ -16,7 +16,7 @@ import { FOCUS_AI_CUSTOMIZATION_VIEW_ID } from '../../aiCustomizationTreeView/br import { ISessionsPartService } from '../../../services/sessions/browser/sessionsPartService.js'; import { ISessionsService } from '../../../services/sessions/browser/sessionsService.js'; import { REPLACE_PROMPT_TEMPLATE_PLACEHOLDER_COMMAND_ID } from './promptTemplatePlaceholder.js'; -import { FOCUS_NEXT_CHAT_GROUP_COMMAND_ID, FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID, MOVE_CHAT_TO_NEXT_GROUP_COMMAND_ID, MOVE_CHAT_TO_PREVIOUS_GROUP_COMMAND_ID, SPLIT_CHAT_GROUP_DOWN_COMMAND_ID, SPLIT_CHAT_GROUP_RIGHT_COMMAND_ID } from '../../../common/sessionCommands.js'; +import { FOCUS_ACTIVE_SESSION_COMMAND_ID, FOCUS_NEXT_CHAT_GROUP_COMMAND_ID, FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID, MOVE_CHAT_TO_NEXT_GROUP_COMMAND_ID, MOVE_CHAT_TO_PREVIOUS_GROUP_COMMAND_ID, SPLIT_CHAT_GROUP_DOWN_COMMAND_ID, SPLIT_CHAT_GROUP_RIGHT_COMMAND_ID } from '../../../common/sessionCommands.js'; export class SessionsChatAccessibilityHelp implements IAccessibleViewImplementation { readonly priority = 120; readonly name = 'sessionsChat'; @@ -66,6 +66,7 @@ export class SessionsChatAccessibilityHelp implements IAccessibleViewImplementat content.push(localize('sessionsChat.deleteChat', "To permanently delete a chat, open the chat tab's context menu and choose Delete Chat. This is destructive and cannot be undone.")); content.push(localize('sessionsChat.promptTimeline', "When the prompt timeline is enabled, a handle on the left edge of the transcript lists your prompts. Activate it to expand the list, use the up and down arrows (or Home and End) to move between prompts, Enter or Space to jump to a prompt, and Escape to dismiss the list and return focus to the handle. When a prompt title is pinned above the transcript, activate its title to jump to that prompt.")); content.push(localize('sessionsChat.find', "To search the chat transcript, invoke Find in Chat{0}. Find Next{1} and Find Previous{2} move between results, scrolling each one into view.", '', '', '')); + content.push(localize('sessionsChat.focusActiveSession', "Focus the active session{0}. The default Open Chat (Agent) shortcut also focuses the active session in the Agents window.", ``)); content.push(localize('sessionsChat.goBack', "Go back through visited sessions{0}.", '')); content.push(localize('sessionsChat.goForward', "Go forward through visited sessions{0}.", '')); content.push(localize('sessionsChat.navigatePreviousSession', "Navigate to the previous session in the list{0}.", '')); diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts index 20fb404aad63e..4a88943e25b7c 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts @@ -28,7 +28,7 @@ import { Menus } from '../../../browser/menus.js'; import { SessionsCategories } from '../../../common/categories.js'; import { CanGoBackContext, CanGoForwardContext, SessionProviderIdContext, MultipleSessionsVisibleContext, SessionIsArchivedContext, SessionIsCreatedContext, SessionIsMaximizedContext, SessionIsStickyContext, SessionsFocusContext, SessionSupportsMultipleChatsContext, SessionsWelcomeVisibleContext, SessionIdContext, SessionHasMultipleCommittedChatsContext, SessionHasMultipleOpenChatsContext, SessionsPickerVisibleContext, SessionActiveChatIsClosableContext, SessionActiveChatIsDeletableContext, SessionChatsPickerVisibleContext, SessionActiveChatHasSubagentsContext, SessionsTitleBarNewSessionEnabledContext, SessionsEditorScopeContext, SessionsHasClosedItemContext, IsQuickChatSessionContext } from '../../../common/contextkeys.js'; import { ANY_AGENT_HOST_PROVIDER_RE } from '../../../common/agentHostSessionsProvider.js'; -import { CLOSE_CHAT_COMMAND_ID, FOCUS_NEXT_CHAT_GROUP_COMMAND_ID, FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID, MOVE_CHAT_TO_NEXT_GROUP_COMMAND_ID, MOVE_CHAT_TO_PREVIOUS_GROUP_COMMAND_ID, SPLIT_CHAT_GROUP_DOWN_COMMAND_ID, SPLIT_CHAT_GROUP_RIGHT_COMMAND_ID } from '../../../common/sessionCommands.js'; +import { CLOSE_CHAT_COMMAND_ID, FOCUS_ACTIVE_SESSION_COMMAND_ID, FOCUS_NEXT_CHAT_GROUP_COMMAND_ID, FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID, MOVE_CHAT_TO_NEXT_GROUP_COMMAND_ID, MOVE_CHAT_TO_PREVIOUS_GROUP_COMMAND_ID, SPLIT_CHAT_GROUP_DOWN_COMMAND_ID, SPLIT_CHAT_GROUP_RIGHT_COMMAND_ID } from '../../../common/sessionCommands.js'; import { IActiveSession, ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js'; import { ISessionsService } from '../../../services/sessions/browser/sessionsService.js'; import { ChatOriginKind, getChatCapabilities, getUntitledSessionTitle, IChat, ISession, SessionStatus } from '../../../services/sessions/common/session.js'; @@ -328,7 +328,7 @@ registerAction2(class GoForwardAction extends Action2 { registerAction2(class FocusActiveSessionAction extends Action2 { constructor() { super({ - id: 'sessions.focusActiveSession', + id: FOCUS_ACTIVE_SESSION_COMMAND_ID, title: localize2('focusActiveSession', "Focus Active Session"), f1: true, category: SessionsCategories.Sessions, @@ -338,7 +338,15 @@ registerAction2(class FocusActiveSessionAction extends Action2 { // focuses the active session. Using the normal open chat action will not work for new session views. weight: KeybindingWeight.SessionsContrib, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI }, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI], + mac: { + primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI] + }, + linux: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI, + secondary: [KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI] + }, }, }); } diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsFocusActiveSessionKeybinding.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsFocusActiveSessionKeybinding.test.ts new file mode 100644 index 0000000000000..af9cead1e1780 --- /dev/null +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsFocusActiveSessionKeybinding.test.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { decodeKeybinding } from '../../../../../base/common/keybindings.js'; +import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; +import { OperatingSystem } from '../../../../../base/common/platform.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import '../../../../../editor/contrib/format/browser/formatActions.js'; +import { FOCUS_ACTIVE_SESSION_COMMAND_ID } from '../../../../common/sessionCommands.js'; +import '../../browser/sessionsActions.js'; + +suite('Sessions - Focus Active Session keybinding', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + + function getRule(os: OperatingSystem, keybinding: number) { + const hash = decodeKeybinding(keybinding, os)!.getHashCode(); + return KeybindingsRegistry.getDefaultKeybindingsForOS(os) + .find(item => item.command === FOCUS_ACTIVE_SESSION_COMMAND_ID && item.keybinding?.getHashCode() === hash); + } + + test('aliases the platform Open Chat (Agent) keybinding', () => { + const windowsRule = getRule(OperatingSystem.Windows, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI); + const macRule = getRule(OperatingSystem.Macintosh, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI); + const linuxRule = getRule(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI); + + assert.deepStrictEqual({ + windows: { weight: windowsRule?.weight1, secondary: windowsRule?.weight2 }, + mac: { weight: macRule?.weight1, secondary: macRule?.weight2 }, + linux: { weight: linuxRule?.weight1, secondary: linuxRule?.weight2 }, + }, { + windows: { weight: KeybindingWeight.SessionsContrib, secondary: -1 }, + mac: { weight: KeybindingWeight.SessionsContrib, secondary: -1 }, + linux: { weight: KeybindingWeight.SessionsContrib, secondary: -1 }, + }); + }); + + test('preserves the Linux Format Document keybinding', () => { + const hash = decodeKeybinding(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI, OperatingSystem.Linux)!.getHashCode(); + const rules = KeybindingsRegistry.getDefaultKeybindingsForOS(OperatingSystem.Linux) + .filter(item => item.keybinding?.getHashCode() === hash); + + assert.deepStrictEqual({ + formatDocument: rules.some(item => item.command === 'editor.action.formatDocument'), + focusActiveSession: rules.some(item => item.command === FOCUS_ACTIVE_SESSION_COMMAND_ID), + }, { + formatDocument: true, + focusActiveSession: false, + }); + }); +});