Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,32 @@ export class ModernitySettingsWidget extends Disposable {
actionsRow.style.display = 'flex';
actionsRow.style.gap = '6px';

// Developer mode toggle (T278837441): swaps between the simple locked
// chat panel and developer mode (code viewer, file tree, debugging,
// search, source control). The left panel and terminal stay locked.
if (this.mode === 'settings') {
const developerModeButton = this._register(new Button(actionsRow, {
...defaultButtonStyles,
title: localize('modernity.developerMode.tooltip', "Swap between simple mode (locked chat panel) and developer mode (code viewer, file tree, debugging, search, source control). The left panel and the terminal stay locked in both modes."),
}));
const updateDeveloperModeButtonLabel = (): void => {
const developerMode = this.configurationService.getValue<boolean>('modernity.developerMode') ?? false;
developerModeButton.label = developerMode
? localize('modernity.developerMode.on', "Developer Mode: On")
: localize('modernity.developerMode.off', "Developer Mode: Off");
};
updateDeveloperModeButtonLabel();
this._register(developerModeButton.onDidClick(async () => {
const developerMode = this.configurationService.getValue<boolean>('modernity.developerMode') ?? false;
await this.configurationService.updateValue('modernity.developerMode', !developerMode, ConfigurationTarget.USER);
}));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('modernity.developerMode')) {
updateDeveloperModeButtonLabel();
}
}));
}

// Export / Import JSON buttons
if (this.mode === 'settings') {
const exportButton = this._register(new Button(actionsRow, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,12 @@ configurationRegistry.registerConfiguration({
default: false,
},
// Modernity Dev Settings - extensible, workspace-scoped for paths
'modernity.developerMode': {
type: 'boolean',
description: nls.localize('modernity.developerMode', "Developer mode swaps the locked chat panel (simple mode) for a code viewer, a file tree panel, and debugging, search and source control. The left panel and the terminal stay locked in both modes."),
default: false,
scope: ConfigurationScope.APPLICATION,
},
'modernity.projects.uncompiledCodePath': {
type: 'string',
description: nls.localize('modernity.projects.uncompiledCodePath', "Directory where uncompiled / intermediate mod code is placed. Workspace setting."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,35 @@
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import { ModernityDaemonStatusBarEntry } from './modernityDaemonStatus.js';
import { ModernityInferenceStatusBarEntry } from './modernityInferenceStatus.js';
import { registerAction2 } from '../../../../platform/actions/common/actions.js';
import { Action2 } from '../../../../platform/actions/common/actions.js';
import { MODERNITY_DEVELOPER_MODE_SETTING, ModernityDevModeContribution } from './modernityDevMode.js';
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
import { URI } from '../../../../base/common/uri.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';

registerWorkbenchContribution2(ModernityDaemonStatusBarEntry.ID, ModernityDaemonStatusBarEntry, WorkbenchPhase.AfterRestored);
registerWorkbenchContribution2(ModernityInferenceStatusBarEntry.ID, ModernityInferenceStatusBarEntry, WorkbenchPhase.AfterRestored);
registerWorkbenchContribution2(ModernityDevModeContribution.ID, ModernityDevModeContribution, WorkbenchPhase.AfterRestored);

class ToggleModernityDeveloperModeAction extends Action2 {
constructor() {
super({
id: 'modernity.toggleDeveloperMode',
title: { value: 'Modernity: Toggle Developer Mode', original: 'Modernity: Toggle Developer Mode' },
f1: true,
});
}

async run(accessor: ServicesAccessor): Promise<void> {
const configurationService = accessor.get(IConfigurationService);
const developerMode = configurationService.getValue<boolean>(MODERNITY_DEVELOPER_MODE_SETTING) ?? false;
await configurationService.updateValue(MODERNITY_DEVELOPER_MODE_SETTING, !developerMode, ConfigurationTarget.USER);
}
}

registerAction2(ToggleModernityDeveloperModeAction);

class OpenModernityDaemonStatusAction extends Action2 {
constructor() {
Expand Down
126 changes: 126 additions & 0 deletions src/vs/workbench/contrib/modernity/browser/modernityDevMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Modernity Contributors. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable } from '../../../../base/common/lifecycle.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
import { IWorkbenchContribution } from '../../../common/contributions.js';
import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js';
import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js';
import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js';

export const MODERNITY_DEVELOPER_MODE_SETTING = 'modernity.developerMode';

export const MODERNITY_DEVELOPER_MODE_CONTEXT_KEY = new RawContextKey<boolean>('modernityDeveloperMode', false);

/**
* Built-in view containers unlocked in developer mode. They are hosted in the
* auxiliary bar next to the chat because the primary side bar is never
* restored: the left panel exposes custom extensions which can break the
* product (T278837441).
*/
const DEVELOPER_MODE_VIEW_CONTAINERS = [
'workbench.view.explorer', // file tree panel
'workbench.view.search',
'workbench.view.scm',
'workbench.view.debug',
] as const;

/**
* Swaps the workbench between the simple mode (locked chat panel) and the
* developer mode (code viewer, file tree, debugging, search, source control).
*
* Enforced in both modes, per T278837441:
* - the primary side bar and activity bar are never restored, so custom
* extensions cannot surface and break the product;
* - the panel part is never shown, so the terminal stays unreachable.
*/
export class ModernityDevModeContribution extends Disposable implements IWorkbenchContribution {

static readonly ID = 'workbench.contrib.modernityDevMode';

private readonly developerModeContextKey: IContextKey<boolean>;

constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IPaneCompositePartService private readonly paneCompositePartService: IPaneCompositePartService,
@IContextKeyService contextKeyService: IContextKeyService,
) {
super();

this.developerModeContextKey = MODERNITY_DEVELOPER_MODE_CONTEXT_KEY.bindTo(contextKeyService);

this.applyMode();

this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(MODERNITY_DEVELOPER_MODE_SETTING)) {
this.applyMode();
}
}));

// The left panel and the terminal stay locked regardless of mode, so
// re-hide those parts whenever something tries to surface them.
this._register(this.layoutService.onDidChangePartVisibility(e => {
if (!e.visible) {
return;
}
if (e.partId === Parts.PANEL_PART || e.partId === Parts.SIDEBAR_PART || e.partId === Parts.ACTIVITYBAR_PART) {
this.layoutService.setPartHidden(true, e.partId as Parts);
}
}));
}

private applyMode(): void {
const developerMode = this.configurationService.getValue<boolean>(MODERNITY_DEVELOPER_MODE_SETTING) ?? false;
this.developerModeContextKey.set(developerMode);
if (developerMode) {
this.applyDeveloperMode();
} else {
this.applySimpleMode();
}
}

/**
* Simple mode: the locked chat panel covers the entire screen.
*/
private applySimpleMode(): void {
// Park the developer view containers back at their default home. They
// stay unreachable because the side bar is never shown.
this.moveDeveloperViewContainers(ViewContainerLocation.Sidebar);

this.layoutService.setPartHidden(true, Parts.ACTIVITYBAR_PART);
this.layoutService.setPartHidden(true, Parts.SIDEBAR_PART);
this.layoutService.setPartHidden(true, Parts.PANEL_PART);
this.layoutService.setAuxiliaryBarMaximized(true);
}

/**
* Developer mode: code viewer plus file tree, debugging, search and source
* control next to the chat. The left panel and the terminal stay locked.
*/
private applyDeveloperMode(): void {
this.layoutService.setPartHidden(true, Parts.ACTIVITYBAR_PART);
this.layoutService.setPartHidden(true, Parts.SIDEBAR_PART);
this.layoutService.setPartHidden(true, Parts.PANEL_PART);

this.layoutService.setAuxiliaryBarMaximized(false);
this.layoutService.setPartHidden(false, Parts.EDITOR_PART);

this.moveDeveloperViewContainers(ViewContainerLocation.AuxiliaryBar);
this.layoutService.setPartHidden(false, Parts.AUXILIARYBAR_PART);
void this.paneCompositePartService.openPaneComposite(DEVELOPER_MODE_VIEW_CONTAINERS[0], ViewContainerLocation.AuxiliaryBar, false);
}

private moveDeveloperViewContainers(location: ViewContainerLocation): void {
for (const id of DEVELOPER_MODE_VIEW_CONTAINERS) {
const container = this.viewDescriptorService.getViewContainerById(id);
if (container && this.viewDescriptorService.getViewContainerLocation(container) !== location) {
this.viewDescriptorService.moveViewContainerToLocation(container, location, undefined, MODERNITY_DEVELOPER_MODE_SETTING);
}
}
}
}