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