Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9519571
Skip flaky test " setTextDocumentLanguage for notebook cells" (#293072)
alexr00 Feb 5, 2026
e940fed
agent sessions - remove border when sessions show stacked (#293074)
bpasero Feb 5, 2026
bf79fb6
editors - introduce `MODAL_GROUP` for a modal editor part (#293020)
bpasero Feb 5, 2026
e80df14
theme - update color values for command center and editor widgets in …
mrleemurray Feb 5, 2026
f45c1a8
theme - update command center and widget background colors in 2026 li…
mrleemurray Feb 5, 2026
da60cfc
Bump distro (#293094)
alexr00 Feb 5, 2026
833ea64
Skip `install and enable vscode-smoketest-check extension` (#293111)
alexr00 Feb 5, 2026
a3b2a31
fix: launching application after install on windows (#292864)
deepak1556 Feb 5, 2026
dcde087
Fixes: #293115
EmrecanKaracayir Feb 5, 2026
45934c7
Enable copilotTrackingId as assignment unit in vscode core (#292997)
vijayupadya Feb 5, 2026
36d5f82
theme - add widget border and refine shadow styles in 2026 light theme
mrleemurray Feb 5, 2026
f26a1d1
toasts - fix leaks (#293119)
bpasero Feb 5, 2026
93c6f60
fix: update selection and highlight background colors in 2026 Dark theme
mrleemurray Feb 5, 2026
d7c0597
Merge pull request #293116 from EmrecanKaracayir/emrecankaracayir/inl…
Tyriar Feb 5, 2026
db2ddd5
Merge pull request #293133 from microsoft/mrleemurray/yelling-crimson…
mrleemurray Feb 5, 2026
dae9c94
chat: wire up yieldrequested for steering messages (#293026)
connor4312 Feb 5, 2026
4b4cae3
Mark these as ExP controllable (#293135)
alexdima Feb 5, 2026
2fd6a33
Surface power API
chrmarti Feb 5, 2026
21bea13
Merge pull request #293140 from microsoft/mrleemurray/complex-gold-ma…
mrleemurray Feb 5, 2026
3acdd6f
Bump distro
chrmarti Feb 5, 2026
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
213 changes: 213 additions & 0 deletions .github/instructions/modal-editor-part.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
description: Architecture documentation for VS Code modal editor part. Use when working with modal editor functionality in `src/vs/workbench/browser/parts/editor/modalEditorPart.ts`
applyTo: src/vs/workbench/**/modal*.ts
---

# Modal Editor Part Design Document

This document describes the conceptual design of the Modal Editor Part feature in VS Code. Use this as a reference when working with modal editor functionality.

## Overview

The Modal Editor Part is a new editor part concept that displays editors in a modal overlay on top of the workbench. It follows the same architectural pattern as `AUX_WINDOW_GROUP` (auxiliary window editor parts) but renders within the main window as an overlay instead of a separate window.

## Architecture

### Constants and Types

Location: `src/vs/workbench/services/editor/common/editorService.ts`

```typescript
export const MODAL_GROUP = -4;
export type MODAL_GROUP_TYPE = typeof MODAL_GROUP;
```

The `MODAL_GROUP` constant follows the pattern of other special group identifiers:
- `ACTIVE_GROUP = -1`
- `SIDE_GROUP = -2`
- `AUX_WINDOW_GROUP = -3`
- `MODAL_GROUP = -4`

### Interfaces

Location: `src/vs/workbench/services/editor/common/editorGroupsService.ts`

```typescript
export interface IModalEditorPart extends IEditorPart {
readonly onWillClose: Event<void>;
close(): boolean;
}
```

The `IModalEditorPart` interface extends `IEditorPart` and adds:
- `onWillClose`: Event fired before the modal closes
- `close()`: Closes the modal, merging confirming editors back to the main part

### Service Method

The `IEditorGroupsService` interface includes:

```typescript
createModalEditorPart(): Promise<IModalEditorPart>;
```

## Implementation

### ModalEditorPart Class

Location: `src/vs/workbench/browser/parts/editor/modalEditorPart.ts`

The implementation consists of two classes:

1. **`ModalEditorPart`**: Factory class that creates the modal UI
- Creates modal backdrop with dimmed overlay
- Creates shadow container for the modal window
- Handles layout relative to main container dimensions
- Registers escape key and click-outside handlers for closing

2. **`ModalEditorPartImpl`**: The actual editor part extending `EditorPart`
- Enforces `showTabs: 'single'` and `closeEmptyGroups: true`
- Overrides `removeGroup` to close modal when last group is removed
- Does not persist state (modal is transient)
- Merges editors back to main part on close

### Key Behaviors

1. **Single Tab Mode**: Modal enforces `showTabs: 'single'` for a focused experience
2. **Auto-close on Empty**: When all editors are closed, the modal closes automatically
3. **Merge on Close**: Confirming editors (dirty, etc.) are merged back to main part
4. **Escape to Close**: Pressing Escape closes the modal
5. **Click Outside to Close**: Clicking the dimmed backdrop closes the modal

### CSS Styling

Location: `src/vs/workbench/browser/parts/editor/media/modalEditorPart.css`

```css
.monaco-modal-editor-block {
/* Full-screen overlay with flexbox centering */
}

.monaco-modal-editor-block.dimmed {
/* Semi-transparent dark background */
}

.modal-editor-shadow {
/* Shadow and border-radius for the modal window */
}
```

## Integration Points

### EditorParts Service

Location: `src/vs/workbench/browser/parts/editor/editorParts.ts`

The `EditorParts` class implements `createModalEditorPart()`:

```typescript
async createModalEditorPart(): Promise<IModalEditorPart> {
const { part, disposables } = await this.instantiationService
.createInstance(ModalEditorPart, this).create();

this._onDidAddGroup.fire(part.activeGroup);

disposables.add(toDisposable(() => {
this._onDidRemoveGroup.fire(part.activeGroup);
}));

return part;
}
```

### Active Part Detection

Location: `src/vs/workbench/browser/parts/editor/editorParts.ts`

Override of `getPartByDocument` to detect when focus is in a modal:

```typescript
protected override getPartByDocument(document: Document): EditorPart {
if (this._parts.size > 1) {
const activeElement = getActiveElement();

for (const part of this._parts) {
if (part !== this.mainPart && part.element?.ownerDocument === document) {
const container = part.getContainer();
if (container && isAncestor(activeElement, container)) {
return part;
}
}
}
}
return super.getPartByDocument(document);
}
```

This ensures that when focus is in the modal, it is considered the active part for editor opening via quick open, etc.

### Editor Group Finder

Location: `src/vs/workbench/services/editor/common/editorGroupFinder.ts`

The `findGroup` function handles `MODAL_GROUP`:

```typescript
else if (preferredGroup === MODAL_GROUP) {
group = editorGroupService.createModalEditorPart()
.then(part => part.activeGroup);
}
```

## Usage Examples

### Opening an Editor in Modal

```typescript
// Using the editor service
await editorService.openEditor(input, options, MODAL_GROUP);

// Using a flag pattern (e.g., settings)
interface IOpenSettingsOptions {
openInModal?: boolean;
}

// Implementation checks the flag
if (options.openInModal) {
group = await findGroup(accessor, {}, MODAL_GROUP);
}
```

### Current Integrations

1. **Settings Editor**: Opens in modal via `openInModal: true` option
2. **Keyboard Shortcuts Editor**: Opens in modal via `openInModal: true` option
3. **Extensions Editor**: Uses `openInModal: true` in `IExtensionEditorOptions`
4. **Profiles Editor**: Opens directly with `MODAL_GROUP`

## Testing

Location: `src/vs/workbench/services/editor/test/browser/modalEditorGroup.test.ts`

Test categories:
- Constants and types verification
- Creation and initial state
- Editor operations (open, split)
- Closing behavior and events
- Options enforcement
- Integration with EditorParts service

## Design Decisions

1. **Why extend EditorPart?**: Reuses all editor group functionality without duplication
2. **Why single tab mode?**: Modal is for focused, single-editor experiences
3. **Why merge on close?**: Prevents data loss for dirty editors
4. **Why same window?**: Avoids complexity of auxiliary windows while providing overlay UX
5. **Why transient state?**: Modal is meant for temporary focused editing, not persistence

## Future Considerations

- Consider adding animation for open/close transitions
- Consider size/position customization
- Consider multiple modal stacking (though likely not needed)
- Consider keyboard navigation between modal and main editor areas
13 changes: 12 additions & 1 deletion build/win32/code.iss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ OutputBaseFilename=VSCodeSetup
Compression=lzma
SolidCompression=yes
AppMutex={code:GetAppMutex}
SetupMutex={#AppMutex}setup
SetupMutex={code:GetSetupMutex}
WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp"
WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp"
SetupIconFile={#RepoDir}\resources\win32\code.ico
Expand Down Expand Up @@ -1469,6 +1469,17 @@ begin
Result := '{#AppMutex}';
end;

function GetSetupMutex(Value: string): string;
begin
// Always create the base setup mutex to prevent multiple installers running.
// During background updates, also create a -updating mutex that VS Code checks
// to avoid launching while an update is in progress.
if IsBackgroundUpdate() then
Result := '{#AppMutex}setup,{#AppMutex}-updating'
else
Result := '{#AppMutex}setup';
end;

function GetDestDir(Value: string): string;
begin
if IsBackgroundUpdate() and not IsVersionedUpdate() then
Expand Down
5 changes: 5 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,11 @@ export default tseslint.config(
'collapse',
'create',
'delete',
'lock',
'resume',
'shutdown',
'suspend',
'unlock',
'discover',
'dispose',
'drop',
Expand Down
14 changes: 7 additions & 7 deletions extensions/theme-2026/themes/2026-dark.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,18 @@
"commandCenter.foreground": "#bfbfbf",
"commandCenter.activeForeground": "#bfbfbf",
"commandCenter.background": "#191A1B",
"commandCenter.activeBackground": "#5a5d5e2f",
"commandCenter.border": "#333536BB",
"commandCenter.activeBackground": "#252627",
"commandCenter.border": "#2E3031",
"editor.background": "#121314",
"editor.foreground": "#BBBEBF",
"editorLineNumber.foreground": "#858889",
"editorLineNumber.activeForeground": "#BBBEBF",
"editorCursor.foreground": "#BBBEBF",
"editor.selectionBackground": "#3994BC33",
"editor.selectionBackground": "#3994BC50",
"editor.inactiveSelectionBackground": "#3994BC80",
"editor.selectionHighlightBackground": "#3994BC1A",
"editor.wordHighlightBackground": "#3994BC33",
"editor.wordHighlightStrongBackground": "#3994BC33",
"editor.wordHighlightBackground": "#3994BC50",
"editor.wordHighlightStrongBackground": "#3994BC50",
"editor.findMatchBackground": "#3994BC4D",
"editor.findMatchHighlightBackground": "#3994BC26",
"editor.findRangeHighlightBackground": "#242526",
Expand All @@ -122,15 +122,15 @@
"editorCodeLens.foreground": "#888888",
"editorBracketMatch.background": "#3994BC55",
"editorBracketMatch.border": "#2A2B2CFF",
"editorWidget.background": "#202122AA",
"editorWidget.background": "#202122",
"editorWidget.border": "#2A2B2CFF",
"editorWidget.foreground": "#bfbfbf",
"editorSuggestWidget.background": "#202122",
"editorSuggestWidget.border": "#2A2B2CFF",
"editorSuggestWidget.foreground": "#bfbfbf",
"editorSuggestWidget.highlightForeground": "#bfbfbf",
"editorSuggestWidget.selectedBackground": "#3994BC26",
"editorHoverWidget.background": "#20212266",
"editorHoverWidget.background": "#202122",
"editorHoverWidget.border": "#2A2B2CFF",
"peekView.border": "#2A2B2CFF",
"peekViewEditor.background": "#191A1B",
Expand Down
Loading
Loading