Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/browser/parts/auxiliaryBarPart.ts
13394 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import '../../../workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css';
7
import './media/auxiliaryBarPart.css';
8
import { localize } from '../../../nls.js';
9
import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
10
import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js';
11
import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
12
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
13
import { INotificationService } from '../../../platform/notification/common/notification.js';
14
import { IStorageService } from '../../../platform/storage/common/storage.js';
15
import { IThemeService } from '../../../platform/theme/common/themeService.js';
16
import { ActiveAuxiliaryContext, AuxiliaryBarFocusContext } from '../../../workbench/common/contextkeys.js';
17
import { ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND, SIDE_BAR_TITLE_BORDER } from '../../../workbench/common/theme.js';
18
import { agentsPanelBackground, agentsPanelBorder, agentsPanelForeground, agentsBadgeBackground, agentsBadgeForeground } from '../../common/theme.js';
19
import { IViewDescriptorService, ViewContainerLocation } from '../../../workbench/common/views.js';
20
import { IExtensionService } from '../../../workbench/services/extensions/common/extensions.js';
21
import { IWorkbenchLayoutService, Parts } from '../../../workbench/services/layout/browser/layoutService.js';
22
import { HoverPosition } from '../../../base/browser/ui/hover/hoverWidget.js';
23
import { IAction } from '../../../base/common/actions.js';
24
import { assertReturnsDefined } from '../../../base/common/types.js';
25
import { LayoutPriority } from '../../../base/browser/ui/splitview/splitview.js';
26
import { AbstractPaneCompositePart, CompositeBarPosition } from '../../../workbench/browser/parts/paneCompositePart.js';
27
import { Part } from '../../../workbench/browser/part.js';
28
import { ActionsOrientation, IActionViewItem } from '../../../base/browser/ui/actionbar/actionbar.js';
29
import { IPaneCompositeBarOptions } from '../../../workbench/browser/parts/paneCompositeBar.js';
30
import { IMenuService, IMenu, MenuId, MenuItemAction } from '../../../platform/actions/common/actions.js';
31
import { Menus } from '../menus.js';
32
import { IHoverService } from '../../../platform/hover/browser/hover.js';
33
import { DropdownWithPrimaryActionViewItem } from '../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js';
34
import { IBaseActionViewItemOptions } from '../../../base/browser/ui/actionbar/actionViewItems.js';
35
import { getFlatContextMenuActions } from '../../../platform/actions/browser/menuEntryActionViewItem.js';
36
import { IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';
37
import { Extensions } from '../../../workbench/browser/panecomposite.js';
38
import { mainWindow } from '../../../base/browser/window.js';
39
40
/**
41
* Auxiliary bar part specifically for agent sessions workbench.
42
* This is a simplified version of the AuxiliaryBarPart for agent session contexts.
43
*/
44
export class AuxiliaryBarPart extends AbstractPaneCompositePart {
45
46
static readonly activeViewSettingsKey = 'workbench.agentsession.auxiliarybar.activepanelid';
47
static readonly pinnedViewsKey = 'workbench.agentsession.auxiliarybar.pinnedPanels';
48
static readonly placeholderViewContainersKey = 'workbench.agentsession.auxiliarybar.placeholderPanels';
49
static readonly viewContainersWorkspaceStateKey = 'workbench.agentsession.auxiliarybar.viewContainersWorkspaceState';
50
51
/** Visual margin values for the card-like appearance */
52
static readonly MARGIN_TOP = 10;
53
static readonly MARGIN_BOTTOM = 0;
54
static readonly MARGIN_RIGHT = 10;
55
56
// Action ID for run script - defined here to avoid layering issues
57
private static readonly RUN_SCRIPT_ACTION_ID = 'workbench.action.agentSessions.runScript';
58
private static readonly RUN_SCRIPT_DROPDOWN_MENU_ID = MenuId.for('AgentSessionsRunScriptDropdown');
59
private static readonly DEFAULT_MINIMUM_WIDTH = 270;
60
61
// Run script dropdown management
62
private readonly _runScriptDropdown = this._register(new MutableDisposable<DropdownWithPrimaryActionViewItem>());
63
private readonly _runScriptMenu = this._register(new MutableDisposable<IMenu>());
64
private readonly _runScriptMenuListener = this._register(new MutableDisposable<IDisposable>());
65
66
// Sessions-specific auxiliary bar dimensions (intentionally not tied to the sessions SidebarPart values)
67
override get minimumWidth(): number {
68
return AuxiliaryBarPart.DEFAULT_MINIMUM_WIDTH;
69
}
70
override readonly maximumWidth: number = Number.POSITIVE_INFINITY;
71
override readonly minimumHeight: number = 0;
72
override readonly maximumHeight: number = Number.POSITIVE_INFINITY;
73
override get snap(): boolean {
74
return this.hasAttachedEditorRequiringSidebarSpace() ? false : super.snap;
75
}
76
77
get preferredHeight(): number | undefined {
78
return this.layoutService.mainContainerDimension.height * 0.4;
79
}
80
81
get preferredWidth(): number | undefined {
82
const activeComposite = this.getActivePaneComposite();
83
84
if (!activeComposite) {
85
return undefined;
86
}
87
88
const width = activeComposite.getOptimalWidth();
89
if (typeof width !== 'number') {
90
return undefined;
91
}
92
93
return Math.max(width, 340);
94
}
95
96
readonly priority = LayoutPriority.Low;
97
98
constructor(
99
@INotificationService notificationService: INotificationService,
100
@IStorageService storageService: IStorageService,
101
@IContextMenuService contextMenuService: IContextMenuService,
102
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
103
@IKeybindingService keybindingService: IKeybindingService,
104
@IHoverService hoverService: IHoverService,
105
@IInstantiationService instantiationService: IInstantiationService,
106
@IThemeService themeService: IThemeService,
107
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
108
@IContextKeyService contextKeyService: IContextKeyService,
109
@IExtensionService extensionService: IExtensionService,
110
@IMenuService menuService: IMenuService,
111
) {
112
super(
113
Parts.AUXILIARYBAR_PART,
114
{
115
hasTitle: true,
116
trailingSeparator: false,
117
borderWidth: () => 0,
118
},
119
AuxiliaryBarPart.activeViewSettingsKey,
120
ActiveAuxiliaryContext.bindTo(contextKeyService),
121
AuxiliaryBarFocusContext.bindTo(contextKeyService),
122
'auxiliarybar',
123
'auxiliarybar',
124
undefined,
125
SIDE_BAR_TITLE_BORDER,
126
ViewContainerLocation.AuxiliaryBar,
127
Extensions.Auxiliary,
128
Menus.AuxiliaryBarTitle,
129
notificationService,
130
storageService,
131
contextMenuService,
132
layoutService,
133
keybindingService,
134
hoverService,
135
instantiationService,
136
themeService,
137
viewDescriptorService,
138
contextKeyService,
139
extensionService,
140
menuService,
141
);
142
143
this._register(this.layoutService.onDidChangePartVisibility(e => {
144
if (e.partId === Parts.AUXILIARYBAR_PART || e.partId === Parts.EDITOR_PART) {
145
this._onDidChange.fire(undefined);
146
}
147
}));
148
}
149
150
override create(parent: HTMLElement): void {
151
super.create(parent);
152
parent.setAttribute('role', 'complementary');
153
parent.setAttribute('aria-label', localize('auxiliaryBarAriaLabel', "Session Details"));
154
}
155
156
override updateStyles(): void {
157
super.updateStyles();
158
159
const container = assertReturnsDefined(this.getContainer());
160
161
// Store background and border as CSS variables for the card styling on .part
162
container.style.setProperty('--part-background', this.getColor(agentsPanelBackground) || '');
163
container.style.setProperty('--part-border-color', this.getColor(agentsPanelBorder) || 'transparent');
164
container.style.setProperty('--part-foreground', this.getColor(agentsPanelForeground) || '');
165
container.style.backgroundColor = this.getColor(agentsPanelBackground) || '';
166
167
// Clear borders - the card appearance uses border-radius instead
168
container.style.borderLeftColor = '';
169
container.style.borderRightColor = '';
170
container.style.borderLeftStyle = '';
171
container.style.borderRightStyle = '';
172
container.style.borderLeftWidth = '';
173
container.style.borderRightWidth = '';
174
}
175
176
protected getCompositeBarOptions(): IPaneCompositeBarOptions {
177
const $this = this;
178
return {
179
partContainerClass: 'auxiliarybar',
180
pinnedViewContainersKey: AuxiliaryBarPart.pinnedViewsKey,
181
placeholderViewContainersKey: AuxiliaryBarPart.placeholderViewContainersKey,
182
viewContainersWorkspaceStateKey: AuxiliaryBarPart.viewContainersWorkspaceStateKey,
183
icon: false,
184
orientation: ActionsOrientation.HORIZONTAL,
185
recomputeSizes: true,
186
activityHoverOptions: {
187
position: () => this.getCompositeBarPosition() === CompositeBarPosition.BOTTOM ? HoverPosition.ABOVE : HoverPosition.BELOW,
188
},
189
fillExtraContextMenuActions: actions => this.fillExtraContextMenuActions(actions),
190
compositeSize: 0,
191
iconSize: 16,
192
get overflowActionSize() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? 40 : 30; },
193
colors: theme => ({
194
activeBackgroundColor: theme.getColor(agentsPanelBackground),
195
inactiveBackgroundColor: theme.getColor(agentsPanelBackground),
196
get activeBorderBottomColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER); },
197
get activeForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND); },
198
get inactiveForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND); },
199
badgeBackground: theme.getColor(agentsBadgeBackground),
200
badgeForeground: theme.getColor(agentsBadgeForeground),
201
get dragAndDropBorder() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_DRAG_AND_DROP_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER); }
202
}),
203
compact: true
204
};
205
}
206
207
protected override actionViewItemProvider(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined {
208
// Create a DropdownWithPrimaryActionViewItem for the run script action
209
if (action.id === AuxiliaryBarPart.RUN_SCRIPT_ACTION_ID && action instanceof MenuItemAction) {
210
// Create and store the menu so we can listen for changes
211
if (!this._runScriptMenu.value) {
212
this._runScriptMenu.value = this.menuService.createMenu(AuxiliaryBarPart.RUN_SCRIPT_DROPDOWN_MENU_ID, this.contextKeyService);
213
this._runScriptMenuListener.value = this._runScriptMenu.value.onDidChange(() => this._updateRunScriptDropdown());
214
}
215
216
const dropdownActions = this._getRunScriptDropdownActions();
217
218
const dropdownAction: IAction = {
219
id: 'runScriptDropdown',
220
label: '',
221
tooltip: '',
222
class: undefined,
223
enabled: true,
224
run: () => { }
225
};
226
227
this._runScriptDropdown.value = this.instantiationService.createInstance(
228
DropdownWithPrimaryActionViewItem,
229
action,
230
dropdownAction,
231
dropdownActions,
232
'',
233
{
234
hoverDelegate: options.hoverDelegate,
235
getKeyBinding: (action: IAction) => this.keybindingService.lookupKeybinding(action.id, this.contextKeyService)
236
}
237
);
238
239
return this._runScriptDropdown.value;
240
}
241
242
return super.actionViewItemProvider(action, options);
243
}
244
245
private _getRunScriptDropdownActions(): IAction[] {
246
if (!this._runScriptMenu.value) {
247
return [];
248
}
249
return getFlatContextMenuActions(this._runScriptMenu.value.getActions({ shouldForwardArgs: true }));
250
}
251
252
private _updateRunScriptDropdown(): void {
253
if (this._runScriptDropdown.value) {
254
const dropdownActions = this._getRunScriptDropdownActions();
255
const dropdownAction: IAction = {
256
id: 'runScriptDropdown',
257
label: '',
258
tooltip: '',
259
class: undefined,
260
enabled: true,
261
run: () => { }
262
};
263
this._runScriptDropdown.value.update(dropdownAction, dropdownActions);
264
}
265
}
266
267
private hasAttachedEditorRequiringSidebarSpace(): boolean {
268
return this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)
269
&& this.layoutService.isVisible(Parts.EDITOR_PART, mainWindow);
270
}
271
272
private fillExtraContextMenuActions(_actions: IAction[]): void { }
273
274
protected shouldShowCompositeBar(): boolean {
275
return true;
276
}
277
278
protected getCompositeBarPosition(): CompositeBarPosition {
279
return CompositeBarPosition.TITLE;
280
}
281
282
override layout(width: number, height: number, top: number, left: number): void {
283
if (!this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)) {
284
return;
285
}
286
287
// Layout content with reduced dimensions to account for visual margins and border
288
const borderTotal = 2; // 1px border on each side
289
super.layout(
290
width - AuxiliaryBarPart.MARGIN_RIGHT - borderTotal,
291
height - AuxiliaryBarPart.MARGIN_TOP - AuxiliaryBarPart.MARGIN_BOTTOM - borderTotal,
292
top, left
293
);
294
295
// Restore the full grid-allocated dimensions so that Part.relayout() works correctly.
296
// Part.layout() only stores _dimension and _contentPosition - no other side effects.
297
Part.prototype.layout.call(this, width, height, top, left);
298
}
299
300
override toJSON(): object {
301
return {
302
type: Parts.AUXILIARYBAR_PART
303
};
304
}
305
}
306
307