Path: blob/main/src/vs/sessions/browser/parts/chatBarPart.ts
13395 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 './media/chatBarPart.css';6import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';7import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js';8import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';9import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';10import { INotificationService } from '../../../platform/notification/common/notification.js';11import { IStorageService } from '../../../platform/storage/common/storage.js';12import { IThemeService } from '../../../platform/theme/common/themeService.js';13import { 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';14import { agentsPanelBackground, agentsPanelBorder, agentsPanelForeground, agentsBadgeBackground, agentsBadgeForeground } from '../../common/theme.js';15import { IViewDescriptorService, ViewContainerLocation } from '../../../workbench/common/views.js';16import { IExtensionService } from '../../../workbench/services/extensions/common/extensions.js';17import { IWorkbenchLayoutService, Parts } from '../../../workbench/services/layout/browser/layoutService.js';18import { HoverPosition } from '../../../base/browser/ui/hover/hoverWidget.js';19import { assertReturnsDefined } from '../../../base/common/types.js';20import { LayoutPriority } from '../../../base/browser/ui/splitview/splitview.js';21import { AbstractPaneCompositePart, CompositeBarPosition } from '../../../workbench/browser/parts/paneCompositePart.js';22import { Part } from '../../../workbench/browser/part.js';23import { ActionsOrientation } from '../../../base/browser/ui/actionbar/actionbar.js';24import { IPaneCompositeBarOptions } from '../../../workbench/browser/parts/paneCompositeBar.js';25import { IMenuService } from '../../../platform/actions/common/actions.js';26import { IHoverService } from '../../../platform/hover/browser/hover.js';27import { Extensions } from '../../../workbench/browser/panecomposite.js';28import { Menus } from '../menus.js';29import { ActiveChatBarContext, ChatBarFocusContext } from '../../common/contextkeys.js';30import { ChatCompositeBar } from './chatCompositeBar.js';31import { prepend } from '../../../base/browser/dom.js';3233export class ChatBarPart extends AbstractPaneCompositePart { // TODO: should not be a AbstractPaneCompositePart but instead a custom Part with a CompositeBar3435static readonly activeViewSettingsKey = 'workbench.chatbar.activepanelid';36static readonly pinnedViewsKey = 'workbench.chatbar.pinnedPanels';37static readonly placeholderViewContainersKey = 'workbench.chatbar.placeholderPanels';38static readonly viewContainersWorkspaceStateKey = 'workbench.chatbar.viewContainersWorkspaceState';3940override readonly minimumWidth: number = 300;41override readonly maximumWidth: number = Number.POSITIVE_INFINITY;42override readonly minimumHeight: number = 0;43override readonly maximumHeight: number = Number.POSITIVE_INFINITY;44override get snap(): boolean { return false; }4546/** Visual margin values for the card-like appearance */47static readonly MARGIN_TOP = 10;48static readonly MARGIN_LEFT = 10;49static readonly MARGIN_RIGHT = 10;50static readonly MARGIN_BOTTOM = 0;5152/** Border width on the card (1px each side) */53static readonly BORDER_WIDTH = 1;5455/** Height of the session composite bar when visible */56private static readonly SESSION_BAR_HEIGHT = 35;5758private _sessionCompositeBar: ChatCompositeBar | undefined;5960protected _lastLayout: { readonly width: number; readonly height: number; readonly top: number; readonly left: number } | undefined;6162get preferredHeight(): number | undefined {63return this.layoutService.mainContainerDimension.height * 0.4;64}6566readonly priority = LayoutPriority.High;6768constructor(69@INotificationService notificationService: INotificationService,70@IStorageService storageService: IStorageService,71@IContextMenuService contextMenuService: IContextMenuService,72@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,73@IKeybindingService keybindingService: IKeybindingService,74@IHoverService hoverService: IHoverService,75@IInstantiationService instantiationService: IInstantiationService,76@IThemeService themeService: IThemeService,77@IViewDescriptorService viewDescriptorService: IViewDescriptorService,78@IContextKeyService contextKeyService: IContextKeyService,79@IExtensionService extensionService: IExtensionService,80@IMenuService menuService: IMenuService81) {82super(83Parts.CHATBAR_PART,84{85hasTitle: false,86trailingSeparator: true,87borderWidth: () => 0,88},89ChatBarPart.activeViewSettingsKey,90ActiveChatBarContext.bindTo(contextKeyService),91ChatBarFocusContext.bindTo(contextKeyService),92'chatbar',93'chatbar',94undefined,95SIDE_BAR_TITLE_BORDER,96ViewContainerLocation.ChatBar,97Extensions.ChatBar,98Menus.ChatBarTitle,99notificationService,100storageService,101contextMenuService,102layoutService,103keybindingService,104hoverService,105instantiationService,106themeService,107viewDescriptorService,108contextKeyService,109extensionService,110menuService,111);112}113114override create(parent: HTMLElement): void {115super.create(parent);116117// Create the session composite bar and prepend it before the content area118this._sessionCompositeBar = this._register(this.instantiationService.createInstance(ChatCompositeBar));119prepend(parent, this._sessionCompositeBar.element);120121// Relayout when session bar visibility changes122this._register(this._sessionCompositeBar.onDidChangeVisibility(() => {123if (this._lastLayout) {124this.layout(this._lastLayout.width, this._lastLayout.height, this._lastLayout.top, this._lastLayout.left);125}126}));127}128129override updateStyles(): void {130super.updateStyles();131132const container = assertReturnsDefined(this.getContainer());133134// Store background and border as CSS variables for the card styling on .part135container.style.setProperty('--part-background', this.getColor(agentsPanelBackground) || '');136container.style.setProperty('--part-border-color', this.getColor(agentsPanelBorder) || 'transparent');137container.style.setProperty('--part-foreground', this.getColor(agentsPanelForeground) || '');138container.style.backgroundColor = this.getColor(agentsPanelBackground) || '';139}140141override layout(width: number, height: number, top: number, left: number): void {142if (!this.layoutService.isVisible(Parts.CHATBAR_PART)) {143return;144}145146this._lastLayout = { width, height, top, left };147148// Account for the session composite bar height when visible149const sessionBarHeight = this._sessionCompositeBar?.visible ? ChatBarPart.SESSION_BAR_HEIGHT : 0;150151// Layout content with reduced dimensions to account for visual margins and border152const borderTotal = ChatBarPart.BORDER_WIDTH * 2;153const marginLeft = this.layoutService.isVisible(Parts.SIDEBAR_PART) ? 0 : ChatBarPart.MARGIN_LEFT;154super.layout(155width - marginLeft - ChatBarPart.MARGIN_RIGHT - borderTotal,156height - ChatBarPart.MARGIN_TOP - ChatBarPart.MARGIN_BOTTOM - borderTotal - sessionBarHeight,157top, left158);159160// Restore the full grid-allocated dimensions so that Part.relayout() works correctly.161Part.prototype.layout.call(this, width, height, top, left);162}163164protected getCompositeBarOptions(): IPaneCompositeBarOptions {165return {166partContainerClass: 'chatbar',167pinnedViewContainersKey: ChatBarPart.pinnedViewsKey,168placeholderViewContainersKey: ChatBarPart.placeholderViewContainersKey,169viewContainersWorkspaceStateKey: ChatBarPart.viewContainersWorkspaceStateKey,170icon: false,171orientation: ActionsOrientation.HORIZONTAL,172recomputeSizes: true,173activityHoverOptions: {174position: () => HoverPosition.BELOW,175},176fillExtraContextMenuActions: () => { },177compositeSize: 0,178iconSize: 16,179overflowActionSize: 30,180colors: theme => ({181activeBackgroundColor: theme.getColor(agentsPanelBackground),182inactiveBackgroundColor: theme.getColor(agentsPanelBackground),183activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER),184activeForegroundColor: theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND),185inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND),186badgeBackground: theme.getColor(agentsBadgeBackground),187badgeForeground: theme.getColor(agentsBadgeForeground),188dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER)189}),190compact: true191};192}193194protected shouldShowCompositeBar(): boolean {195return false;196}197198protected getCompositeBarPosition(): CompositeBarPosition {199return CompositeBarPosition.TITLE;200}201202override toJSON(): object {203return {204type: Parts.CHATBAR_PART205};206}207}208209210