add actions to switch workbench mode (#290595)

* add actions to switch workbench mode

* feedback
This commit is contained in:
Sandeep Somavarapu
2026-01-27 00:47:01 +01:00
committed by GitHub
parent 4644123d42
commit 9fffd2bd58
7 changed files with 91 additions and 21 deletions

View File

@@ -6,7 +6,7 @@
import { Disposable } from '../../base/common/lifecycle.js';
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js';
import { IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from '../../platform/contextkey/common/contextkeys.js';
import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsAgentSessionsWorkspaceContext } from '../common/contextkeys.js';
import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext, IsAgentSessionsWorkspaceContext, WorkbenchModeContext } from '../common/contextkeys.js';
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js';
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js';
@@ -23,6 +23,7 @@ import { getTitleBarStyle } from '../../platform/window/common/window.js';
import { mainWindow } from '../../base/browser/window.js';
import { isFullscreen, onDidChangeFullscreen } from '../../base/browser/browser.js';
import { IEditorService } from '../services/editor/common/editorService.js';
import { IWorkbenchModeService } from '../services/layout/common/workbenchModeService.js';
export class WorkbenchContextKeysHandler extends Disposable {
@@ -48,6 +49,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
private virtualWorkspaceContext: IContextKey<string>;
private temporaryWorkspaceContext: IContextKey<boolean>;
private isAgentSessionsWorkspaceContext: IContextKey<boolean>;
private workbenchModeContext: IContextKey<string>;
private inAutomationContext: IContextKey<boolean>;
private inZenModeContext: IContextKey<boolean>;
@@ -77,6 +79,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService,
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
@IWorkbenchModeService private readonly workbenchModeService: IWorkbenchModeService,
) {
super();
@@ -96,6 +99,8 @@ export class WorkbenchContextKeysHandler extends Disposable {
this.temporaryWorkspaceContext = TemporaryWorkspaceContext.bindTo(this.contextKeyService);
this.isAgentSessionsWorkspaceContext = IsAgentSessionsWorkspaceContext.bindTo(this.contextKeyService);
this.isAgentSessionsWorkspaceContext.set(!!this.contextService.getWorkspace().isAgentSessionsWorkspace);
this.workbenchModeContext = WorkbenchModeContext.bindTo(this.contextKeyService);
this.workbenchModeContext.set(this.workbenchModeService.workbenchMode ?? '');
this.updateWorkspaceContextKeys();
// Capabilities
@@ -227,6 +232,8 @@ export class WorkbenchContextKeysHandler extends Disposable {
this.updateWorkspaceContextKeys();
}));
this._register(this.workbenchModeService.onDidChangeWorkbenchMode(mode => this.workbenchModeContext.set(mode ?? '')));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {
this.updateSplitEditorsVerticallyContext();

View File

@@ -390,7 +390,7 @@ export class BrowserMain extends Disposable {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Layout Mode
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService));
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService, storageService));
serviceCollection.set(IWorkbenchModeService, workbenchModeService);
try {
await workbenchModeService.initialize();

View File

@@ -35,6 +35,8 @@ export const TemporaryWorkspaceContext = new RawContextKey<boolean>('temporaryWo
export const IsAgentSessionsWorkspaceContext = new RawContextKey<boolean>('isAgentSessionsWorkspace', false, localize('isAgentSessionsWorkspace', "Whether the current workspace is the agent sessions workspace."));
export const WorkbenchModeContext = new RawContextKey<string>('workbenchMode', '', localize('workbenchMode', "The current workbench mode."));
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));

View File

@@ -7,11 +7,12 @@ import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions
import { localize2 } from '../../../../../nls.js';
import { Action2 } from '../../../../../platform/actions/common/actions.js';
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
import { ProductQualityContext } from '../../../../../platform/contextkey/common/contextkeys.js';
import { INativeEnvironmentService } from '../../../../../platform/environment/common/environment.js';
import { IFileService } from '../../../../../platform/files/common/files.js';
import { INativeHostService } from '../../../../../platform/native/common/native.js';
import { ChatEntitlementContextKeys } from '../../../../services/chat/common/chatEntitlementService.js';
import { IWorkbenchModeService } from '../../../../services/layout/common/workbenchModeService.js';
import { IsAgentSessionsWorkspaceContext, WorkbenchModeContext } from '../../../../common/contextkeys.js';
import { CHAT_CATEGORY } from '../../browser/actions/chatActions.js';
export class OpenAgentSessionsWindowAction extends Action2 {
@@ -20,7 +21,7 @@ export class OpenAgentSessionsWindowAction extends Action2 {
id: 'workbench.action.openAgentSessionsWindow',
title: localize2('openAgentSessionsWindow', "Open Agent Sessions Window"),
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(ChatEntitlementContextKeys.Setup.hidden.negate(), ProductQualityContext.notEqualsTo('stable')),
precondition: ChatEntitlementContextKeys.Setup.hidden.negate(),
f1: true,
});
}
@@ -45,3 +46,45 @@ export class OpenAgentSessionsWindowAction extends Action2 {
await nativeHostService.openWindow([{ workspaceUri }], { forceNewWindow: true });
}
}
export class SwitchToAgentSessionsModeAction extends Action2 {
constructor() {
super({
id: 'workbench.action.switchToAgentSessionsMode',
title: localize2('switchToAgentSessionsMode', "Switch to Agent Sessions Mode"),
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(
ChatEntitlementContextKeys.Setup.hidden.negate(),
IsAgentSessionsWorkspaceContext.toNegated(),
WorkbenchModeContext.notEqualsTo('agent-sessions')
),
f1: true,
});
}
async run(accessor: ServicesAccessor) {
const workbenchModeService = accessor.get(IWorkbenchModeService);
await workbenchModeService.setWorkbenchMode('agent-sessions');
}
}
export class SwitchToNormalModeAction extends Action2 {
constructor() {
super({
id: 'workbench.action.switchToNormalMode',
title: localize2('switchToNormalMode', "Switch to Default Mode"),
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(
ChatEntitlementContextKeys.Setup.hidden.negate(),
IsAgentSessionsWorkspaceContext.toNegated(),
WorkbenchModeContext.notEqualsTo('')
),
f1: true,
});
}
async run(accessor: ServicesAccessor) {
const workbenchModeService = accessor.get(IWorkbenchModeService);
await workbenchModeService.setWorkbenchMode(undefined);
}
}

View File

@@ -35,7 +35,7 @@ import { registerChatDeveloperActions } from './actions/chatDeveloperActions.js'
import { registerChatExportZipAction } from './actions/chatExportZip.js';
import { HoldToVoiceChatInChatViewAction, InlineVoiceChatAction, KeywordActivationContribution, QuickVoiceChatAction, ReadChatResponseAloud, StartVoiceChatAction, StopListeningAction, StopListeningAndSubmitAction, StopReadAloud, StopReadChatItemAloud, VoiceChatInChatViewAction } from './actions/voiceChatActions.js';
import { NativeBuiltinToolsContribution } from './builtInTools/tools.js';
import { OpenAgentSessionsWindowAction } from './agentSessions/agentSessionsActions.js';
import { OpenAgentSessionsWindowAction, SwitchToAgentSessionsModeAction, SwitchToNormalModeAction } from './agentSessions/agentSessionsActions.js';
class ChatCommandLineHandler extends Disposable {
@@ -190,6 +190,8 @@ class ChatLifecycleHandler extends Disposable {
}
registerAction2(OpenAgentSessionsWindowAction);
registerAction2(SwitchToAgentSessionsModeAction);
registerAction2(SwitchToNormalModeAction);
registerAction2(StartVoiceChatAction);
registerAction2(VoiceChatInChatViewAction);

View File

@@ -325,7 +325,7 @@ export class DesktopMain extends Disposable {
]);
// Workbench Mode
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService));
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService, storageService));
serviceCollection.set(IWorkbenchModeService, workbenchModeService);
try {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
import { IWorkbenchModeConfiguration, IWorkbenchModeService } from '../common/workbenchModeService.js';
@@ -16,30 +16,36 @@ import { IWorkspaceContextService } from '../../../../platform/workspace/common/
import { Registry } from '../../../../platform/registry/common/platform.js';
import { Extensions, IConfigurationDefaults, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
import { IStringDictionary } from '../../../../base/common/collections.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
export class WorkbenchModeService extends Disposable implements IWorkbenchModeService {
declare readonly _serviceBrand: undefined;
private static readonly WORKBENCH_MODE_STORAGE_KEY = 'workbench.mode';
private _workbenchMode: string | undefined;
get workbenchMode(): string | undefined { return this._workbenchMode; }
private readonly _onDidChangeWorkbenchMode = this._register(new Emitter<string | undefined>());
readonly onDidChangeWorkbenchMode: Event<string | undefined> = this._onDidChangeWorkbenchMode.event;
private readonly workbenchModeFileWatcher = this._register(new MutableDisposable());
private readonly workbenchModeFileWatcherDiposables = this._register(new DisposableStore());
private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
private configurationDefaults: IConfigurationDefaults | undefined;
constructor(
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IFileService private readonly fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ILogService private readonly logService: ILogService
@ILogService private readonly logService: ILogService,
@IStorageService private readonly storageService: IStorageService
) {
super();
this._workbenchMode = workspaceContextService.getWorkspace().isAgentSessionsWorkspace ? 'agent-sessions' : undefined;
this._workbenchMode = this.workspaceContextService.getWorkspace().isAgentSessionsWorkspace
? 'agent-sessions'
: this.storageService.get(WorkbenchModeService.WORKBENCH_MODE_STORAGE_KEY, StorageScope.WORKSPACE);
this.watchCurrentModeFile();
}
@@ -48,10 +54,7 @@ export class WorkbenchModeService extends Disposable implements IWorkbenchModeSe
}
private async updateWorkbenchModeConfiguration(): Promise<void> {
if (!this._workbenchMode) {
return;
}
const workbenchModeConfiguration = await this.getWorkbenchModeConfiguration(this._workbenchMode);
const workbenchModeConfiguration = this._workbenchMode ? await this.getWorkbenchModeConfiguration(this._workbenchMode) : undefined;
this.updateConfigurationDefaults(workbenchModeConfiguration?.settings);
}
@@ -69,18 +72,18 @@ export class WorkbenchModeService extends Disposable implements IWorkbenchModeSe
private watchCurrentModeFile(): void {
if (!this._workbenchMode) {
this.workbenchModeFileWatcher.clear();
this.workbenchModeFileWatcherDiposables.clear();
return;
}
const workbenchModeFileUri = this.getWorkbenchModeFileUri(this._workbenchMode);
if (!workbenchModeFileUri) {
this.workbenchModeFileWatcher.clear();
this.workbenchModeFileWatcherDiposables.clear();
return;
}
this.workbenchModeFileWatcher.value = this.fileService.watch(workbenchModeFileUri);
this._register(this.fileService.onDidFilesChange(e => {
this.workbenchModeFileWatcherDiposables.add(this.fileService.watch(workbenchModeFileUri));
this.workbenchModeFileWatcherDiposables.add(this.fileService.onDidFilesChange(e => {
if (e.affects(workbenchModeFileUri)) {
this.updateWorkbenchModeConfiguration();
this._onDidChangeWorkbenchMode.fire(this._workbenchMode);
@@ -139,13 +142,26 @@ export class WorkbenchModeService extends Disposable implements IWorkbenchModeSe
}
async setWorkbenchMode(modeId: string | undefined): Promise<void> {
if (this.workspaceContextService.getWorkspace().isAgentSessionsWorkspace) {
throw new Error('Cannot set workbench mode in an agent sessions workspace');
}
if (this._workbenchMode === modeId) {
return;
}
this._workbenchMode = modeId;
this.updateWorkbenchModeConfiguration();
this.updateWorkbenchMode(modeId);
await this.updateWorkbenchModeConfiguration();
this.watchCurrentModeFile();
this._onDidChangeWorkbenchMode.fire(modeId);
}
private updateWorkbenchMode(modeId: string | undefined): void {
this._workbenchMode = modeId;
if (modeId === undefined) {
this.storageService.remove(WorkbenchModeService.WORKBENCH_MODE_STORAGE_KEY, StorageScope.WORKSPACE);
} else {
this.storageService.store(WorkbenchModeService.WORKBENCH_MODE_STORAGE_KEY, modeId, StorageScope.WORKSPACE, StorageTarget.MACHINE);
}
}
}