Path: blob/main/src/vs/workbench/browser/parts/globalCompositeBar.ts
3296 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 { localize } from '../../../nls.js';6import { ActionBar, ActionsOrientation } from '../../../base/browser/ui/actionbar/actionbar.js';7import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from '../../common/activity.js';8import { IActivityService } from '../../services/activity/common/activity.js';9import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';10import { DisposableStore, Disposable } from '../../../base/common/lifecycle.js';11import { IColorTheme, IThemeService } from '../../../platform/theme/common/themeService.js';12import { IStorageService, StorageScope, StorageTarget } from '../../../platform/storage/common/storage.js';13import { IExtensionService } from '../../services/extensions/common/extensions.js';14import { CompositeBarActionViewItem, CompositeBarAction, IActivityHoverOptions, ICompositeBarActionViewItemOptions, ICompositeBarColors } from './compositeBarActions.js';15import { Codicon } from '../../../base/common/codicons.js';16import { ThemeIcon } from '../../../base/common/themables.js';17import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';18import { Action, IAction, Separator, SubmenuAction, toAction } from '../../../base/common/actions.js';19import { IMenu, IMenuService, MenuId } from '../../../platform/actions/common/actions.js';20import { addDisposableListener, EventType, append, clearNode, hide, show, EventHelper, $, runWhenWindowIdle, getWindow } from '../../../base/browser/dom.js';21import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js';22import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js';23import { EventType as TouchEventType, GestureEvent } from '../../../base/browser/touch.js';24import { AnchorAlignment, AnchorAxisAlignment } from '../../../base/browser/ui/contextview/contextview.js';25import { Lazy } from '../../../base/common/lazy.js';26import { getActionBarActions } from '../../../platform/actions/browser/menuEntryActionViewItem.js';27import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';28import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';29import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js';30import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';31import { ILogService } from '../../../platform/log/common/log.js';32import { IProductService } from '../../../platform/product/common/productService.js';33import { ISecretStorageService } from '../../../platform/secrets/common/secrets.js';34import { AuthenticationSessionInfo, getCurrentAuthenticationSessionInfo } from '../../services/authentication/browser/authenticationService.js';35import { AuthenticationSessionAccount, IAuthenticationService, INTERNAL_AUTH_PROVIDER_PREFIX } from '../../services/authentication/common/authentication.js';36import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js';37import { IHoverService } from '../../../platform/hover/browser/hover.js';38import { ILifecycleService, LifecyclePhase } from '../../services/lifecycle/common/lifecycle.js';39import { IUserDataProfileService } from '../../services/userDataProfile/common/userDataProfile.js';40import { DEFAULT_ICON } from '../../services/userDataProfile/common/userDataProfileIcons.js';41import { isString } from '../../../base/common/types.js';42import { KeyCode } from '../../../base/common/keyCodes.js';43import { ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND } from '../../common/theme.js';44import { IBaseActionViewItemOptions } from '../../../base/browser/ui/actionbar/actionViewItems.js';45import { ICommandService } from '../../../platform/commands/common/commands.js';4647export class GlobalCompositeBar extends Disposable {4849private static readonly ACCOUNTS_ACTION_INDEX = 0;50static readonly ACCOUNTS_ICON = registerIcon('accounts-view-bar-icon', Codicon.account, localize('accountsViewBarIcon', "Accounts icon in the view bar."));5152readonly element: HTMLElement;5354private readonly globalActivityAction = this._register(new Action(GLOBAL_ACTIVITY_ID));55private readonly accountAction = this._register(new Action(ACCOUNTS_ACTIVITY_ID));56private readonly globalActivityActionBar: ActionBar;5758constructor(59private readonly contextMenuActionsProvider: () => IAction[],60private readonly colors: (theme: IColorTheme) => ICompositeBarColors,61private readonly activityHoverOptions: IActivityHoverOptions,62@IConfigurationService configurationService: IConfigurationService,63@IInstantiationService private readonly instantiationService: IInstantiationService,64@IStorageService private readonly storageService: IStorageService,65@IExtensionService private readonly extensionService: IExtensionService,66) {67super();6869this.element = $('div');70const contextMenuAlignmentOptions = () => ({71anchorAlignment: configurationService.getValue('workbench.sideBar.location') === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT,72anchorAxisAlignment: AnchorAxisAlignment.HORIZONTAL73});74this.globalActivityActionBar = this._register(new ActionBar(this.element, {75actionViewItemProvider: (action, options) => {76if (action.id === GLOBAL_ACTIVITY_ID) {77return this.instantiationService.createInstance(GlobalActivityActionViewItem, this.contextMenuActionsProvider, { ...options, colors: this.colors, hoverOptions: this.activityHoverOptions }, contextMenuAlignmentOptions);78}7980if (action.id === ACCOUNTS_ACTIVITY_ID) {81return this.instantiationService.createInstance(AccountsActivityActionViewItem,82this.contextMenuActionsProvider,83{84...options,85colors: this.colors,86hoverOptions: this.activityHoverOptions87},88contextMenuAlignmentOptions,89(actions: IAction[]) => {90actions.unshift(...[91toAction({ id: 'hideAccounts', label: localize('hideAccounts', "Hide Accounts"), run: () => setAccountsActionVisible(storageService, false) }),92new Separator()93]);94});95}9697throw new Error(`No view item for action '${action.id}'`);98},99orientation: ActionsOrientation.VERTICAL,100ariaLabel: localize('manage', "Manage"),101preventLoopNavigation: true102}));103104if (this.accountsVisibilityPreference) {105this.globalActivityActionBar.push(this.accountAction, { index: GlobalCompositeBar.ACCOUNTS_ACTION_INDEX });106}107108this.globalActivityActionBar.push(this.globalActivityAction);109110this.registerListeners();111}112113private registerListeners(): void {114this.extensionService.whenInstalledExtensionsRegistered().then(() => {115if (!this._store.isDisposed) {116this._register(this.storageService.onDidChangeValue(StorageScope.PROFILE, AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, this._store)(() => this.toggleAccountsActivity()));117}118});119}120121create(parent: HTMLElement): void {122parent.appendChild(this.element);123}124125focus(): void {126this.globalActivityActionBar.focus(true);127}128129size(): number {130return this.globalActivityActionBar.viewItems.length;131}132133getContextMenuActions(): IAction[] {134return [toAction({ id: 'toggleAccountsVisibility', label: localize('accounts', "Accounts"), checked: this.accountsVisibilityPreference, run: () => this.accountsVisibilityPreference = !this.accountsVisibilityPreference })];135}136137private toggleAccountsActivity() {138if (this.globalActivityActionBar.length() === 2 && this.accountsVisibilityPreference) {139return;140}141if (this.globalActivityActionBar.length() === 2) {142this.globalActivityActionBar.pull(GlobalCompositeBar.ACCOUNTS_ACTION_INDEX);143} else {144this.globalActivityActionBar.push(this.accountAction, { index: GlobalCompositeBar.ACCOUNTS_ACTION_INDEX });145}146}147148private get accountsVisibilityPreference(): boolean {149return isAccountsActionVisible(this.storageService);150}151152private set accountsVisibilityPreference(value: boolean) {153setAccountsActionVisible(this.storageService, value);154}155}156157abstract class AbstractGlobalActivityActionViewItem extends CompositeBarActionViewItem {158159constructor(160private readonly menuId: MenuId,161action: CompositeBarAction,162options: ICompositeBarActionViewItemOptions,163private readonly contextMenuActionsProvider: () => IAction[],164private readonly contextMenuAlignmentOptions: () => Readonly<{ anchorAlignment: AnchorAlignment; anchorAxisAlignment: AnchorAxisAlignment }> | undefined,165@IThemeService themeService: IThemeService,166@IHoverService hoverService: IHoverService,167@IMenuService private readonly menuService: IMenuService,168@IContextMenuService private readonly contextMenuService: IContextMenuService,169@IContextKeyService private readonly contextKeyService: IContextKeyService,170@IConfigurationService configurationService: IConfigurationService,171@IKeybindingService keybindingService: IKeybindingService,172@IActivityService private readonly activityService: IActivityService,173) {174super(action, { draggable: false, icon: true, hasPopup: true, ...options }, () => true, themeService, hoverService, configurationService, keybindingService);175176this.updateItemActivity();177this._register(this.activityService.onDidChangeActivity(viewContainerOrAction => {178if (isString(viewContainerOrAction) && viewContainerOrAction === this.compositeBarActionItem.id) {179this.updateItemActivity();180}181}));182}183184private updateItemActivity(): void {185(this.action as CompositeBarAction).activities = this.activityService.getActivity(this.compositeBarActionItem.id);186}187188override render(container: HTMLElement): void {189super.render(container);190191this._register(addDisposableListener(this.container, EventType.MOUSE_DOWN, async (e: MouseEvent) => {192EventHelper.stop(e, true);193const isLeftClick = e?.button !== 2;194// Left-click run195if (isLeftClick) {196this.run();197}198}));199200// The rest of the activity bar uses context menu event for the context menu, so we match this201this._register(addDisposableListener(this.container, EventType.CONTEXT_MENU, async (e: MouseEvent) => {202// Let the item decide on the context menu instead of the toolbar203e.stopPropagation();204205const disposables = new DisposableStore();206const actions = await this.resolveContextMenuActions(disposables);207208const event = new StandardMouseEvent(getWindow(this.container), e);209210this.contextMenuService.showContextMenu({211getAnchor: () => event,212getActions: () => actions,213onHide: () => disposables.dispose()214});215}));216217this._register(addDisposableListener(this.container, EventType.KEY_UP, (e: KeyboardEvent) => {218const event = new StandardKeyboardEvent(e);219if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {220EventHelper.stop(e, true);221this.run();222}223}));224225this._register(addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {226EventHelper.stop(e, true);227this.run();228}));229}230231protected async resolveContextMenuActions(disposables: DisposableStore): Promise<IAction[]> {232return this.contextMenuActionsProvider();233}234235private async run(): Promise<void> {236const disposables = new DisposableStore();237const menu = disposables.add(this.menuService.createMenu(this.menuId, this.contextKeyService));238const actions = await this.resolveMainMenuActions(menu, disposables);239const { anchorAlignment, anchorAxisAlignment } = this.contextMenuAlignmentOptions() ?? { anchorAlignment: undefined, anchorAxisAlignment: undefined };240241this.contextMenuService.showContextMenu({242getAnchor: () => this.label,243anchorAlignment,244anchorAxisAlignment,245getActions: () => actions,246onHide: () => disposables.dispose(),247menuActionOptions: { renderShortTitle: true },248});249250}251252protected async resolveMainMenuActions(menu: IMenu, _disposable: DisposableStore): Promise<IAction[]> {253return getActionBarActions(menu.getActions({ renderShortTitle: true })).secondary;254}255}256257export class AccountsActivityActionViewItem extends AbstractGlobalActivityActionViewItem {258259static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';260261private readonly groupedAccounts: Map<string, (AuthenticationSessionAccount & { canSignOut: boolean })[]> = new Map();262private readonly problematicProviders: Set<string> = new Set();263264private initialized = false;265private sessionFromEmbedder = new Lazy<Promise<AuthenticationSessionInfo | undefined>>(() => getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService));266267constructor(268contextMenuActionsProvider: () => IAction[],269options: ICompositeBarActionViewItemOptions,270contextMenuAlignmentOptions: () => Readonly<{ anchorAlignment: AnchorAlignment; anchorAxisAlignment: AnchorAxisAlignment }> | undefined,271private readonly fillContextMenuActions: (actions: IAction[]) => void,272@IThemeService themeService: IThemeService,273@ILifecycleService private readonly lifecycleService: ILifecycleService,274@IHoverService hoverService: IHoverService,275@IContextMenuService contextMenuService: IContextMenuService,276@IMenuService menuService: IMenuService,277@IContextKeyService contextKeyService: IContextKeyService,278@IAuthenticationService private readonly authenticationService: IAuthenticationService,279@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,280@IProductService private readonly productService: IProductService,281@IConfigurationService configurationService: IConfigurationService,282@IKeybindingService keybindingService: IKeybindingService,283@ISecretStorageService private readonly secretStorageService: ISecretStorageService,284@ILogService private readonly logService: ILogService,285@IActivityService activityService: IActivityService,286@IInstantiationService instantiationService: IInstantiationService,287@ICommandService private readonly commandService: ICommandService288) {289const action = instantiationService.createInstance(CompositeBarAction, {290id: ACCOUNTS_ACTIVITY_ID,291name: localize('accounts', "Accounts"),292classNames: ThemeIcon.asClassNameArray(GlobalCompositeBar.ACCOUNTS_ICON)293});294super(MenuId.AccountsContext, action, options, contextMenuActionsProvider, contextMenuAlignmentOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, keybindingService, activityService);295this._register(action);296this.registerListeners();297this.initialize();298}299300private registerListeners(): void {301this._register(this.authenticationService.onDidRegisterAuthenticationProvider(async (e) => {302await this.addAccountsFromProvider(e.id);303}));304305this._register(this.authenticationService.onDidUnregisterAuthenticationProvider((e) => {306this.groupedAccounts.delete(e.id);307this.problematicProviders.delete(e.id);308}));309310this._register(this.authenticationService.onDidChangeSessions(async e => {311if (e.event.removed) {312for (const removed of e.event.removed) {313this.removeAccount(e.providerId, removed.account);314}315}316for (const changed of [...(e.event.changed ?? []), ...(e.event.added ?? [])]) {317try {318await this.addOrUpdateAccount(e.providerId, changed.account);319} catch (e) {320this.logService.error(e);321}322}323}));324}325326// This function exists to ensure that the accounts are added for auth providers that had already been registered327// before the menu was created.328private async initialize(): Promise<void> {329// Resolving the menu doesn't need to happen immediately, so we can wait until after the workbench has been restored330// and only run this when the system is idle.331await this.lifecycleService.when(LifecyclePhase.Restored);332if (this._store.isDisposed) {333return;334}335const disposable = this._register(runWhenWindowIdle(getWindow(this.element), async () => {336await this.doInitialize();337disposable.dispose();338}));339}340341private async doInitialize(): Promise<void> {342const providerIds = this.authenticationService.getProviderIds();343const results = await Promise.allSettled(providerIds.map(providerId => this.addAccountsFromProvider(providerId)));344345// Log any errors that occurred while initializing. We try to be best effort here to show the most amount of accounts346for (const result of results) {347if (result.status === 'rejected') {348this.logService.error(result.reason);349}350}351352this.initialized = true;353}354355//#region overrides356357protected override async resolveMainMenuActions(accountsMenu: IMenu, disposables: DisposableStore): Promise<IAction[]> {358await super.resolveMainMenuActions(accountsMenu, disposables);359360const providers = this.authenticationService.getProviderIds().filter(p => !p.startsWith(INTERNAL_AUTH_PROVIDER_PREFIX));361const otherCommands = accountsMenu.getActions();362let menus: IAction[] = [];363364const registeredProviders = providers.filter(providerId => !this.authenticationService.isDynamicAuthenticationProvider(providerId));365const dynamicProviders = providers.filter(providerId => this.authenticationService.isDynamicAuthenticationProvider(providerId));366367if (!this.initialized) {368const noAccountsAvailableAction = disposables.add(new Action('noAccountsAvailable', localize('loading', "Loading..."), undefined, false));369menus.push(noAccountsAvailableAction);370} else {371for (const providerId of registeredProviders) {372const provider = this.authenticationService.getProvider(providerId);373const accounts = this.groupedAccounts.get(providerId);374if (!accounts) {375if (this.problematicProviders.has(providerId)) {376const providerUnavailableAction = disposables.add(new Action('providerUnavailable', localize('authProviderUnavailable', '{0} is currently unavailable', provider.label), undefined, false));377menus.push(providerUnavailableAction);378// try again in the background so that if the failure was intermittent, we can resolve it on the next showing of the menu379try {380await this.addAccountsFromProvider(providerId);381} catch (e) {382this.logService.error(e);383}384}385continue;386}387388const canUseMcp = !!provider.authorizationServers?.length;389for (const account of accounts) {390const manageExtensionsAction = toAction({391id: `configureSessions${account.label}`,392label: localize('manageTrustedExtensions', "Manage Trusted Extensions"),393enabled: true,394run: () => this.commandService.executeCommand('_manageTrustedExtensionsForAccount', { providerId, accountLabel: account.label })395});396397398const providerSubMenuActions: IAction[] = [manageExtensionsAction];399if (canUseMcp) {400const manageMCPAction = toAction({401id: `configureSessions${account.label}`,402label: localize('manageTrustedMCPServers', "Manage Trusted MCP Servers"),403enabled: true,404run: () => this.commandService.executeCommand('_manageTrustedMCPServersForAccount', { providerId, accountLabel: account.label })405});406providerSubMenuActions.push(manageMCPAction);407}408if (account.canSignOut) {409providerSubMenuActions.push(toAction({410id: 'signOut',411label: localize('signOut', "Sign Out"),412enabled: true,413run: () => this.commandService.executeCommand('_signOutOfAccount', { providerId, accountLabel: account.label })414}));415}416417const providerSubMenu = new SubmenuAction('activitybar.submenu', `${account.label} (${provider.label})`, providerSubMenuActions);418menus.push(providerSubMenu);419}420}421422if (dynamicProviders.length && registeredProviders.length) {423menus.push(new Separator());424}425426for (const providerId of dynamicProviders) {427const provider = this.authenticationService.getProvider(providerId);428const accounts = this.groupedAccounts.get(providerId);429// Provide _some_ discoverable way to manage dynamic authentication providers.430// This will either show up inside the account submenu or as a top-level menu item if there431// are no accounts.432const manageDynamicAuthProvidersAction = toAction({433id: 'manageDynamicAuthProviders',434label: localize('manageDynamicAuthProviders', "Manage Dynamic Authentication Providers..."),435enabled: true,436run: () => this.commandService.executeCommand('workbench.action.removeDynamicAuthenticationProviders')437});438if (!accounts) {439if (this.problematicProviders.has(providerId)) {440const providerUnavailableAction = disposables.add(new Action('providerUnavailable', localize('authProviderUnavailable', '{0} is currently unavailable', provider.label), undefined, false));441menus.push(providerUnavailableAction);442// try again in the background so that if the failure was intermittent, we can resolve it on the next showing of the menu443try {444await this.addAccountsFromProvider(providerId);445} catch (e) {446this.logService.error(e);447}448}449menus.push(manageDynamicAuthProvidersAction);450continue;451}452453for (const account of accounts) {454// TODO@TylerLeonhardt: Is there a nice way to bring this back?455// const manageExtensionsAction = toAction({456// id: `configureSessions${account.label}`,457// label: localize('manageTrustedExtensions', "Manage Trusted Extensions"),458// enabled: true,459// run: () => this.commandService.executeCommand('_manageTrustedExtensionsForAccount', { providerId, accountLabel: account.label })460// });461462const providerSubMenuActions: IAction[] = [];463const manageMCPAction = toAction({464id: `configureSessions${account.label}`,465label: localize('manageTrustedMCPServers', "Manage Trusted MCP Servers"),466enabled: true,467run: () => this.commandService.executeCommand('_manageTrustedMCPServersForAccount', { providerId, accountLabel: account.label })468});469providerSubMenuActions.push(manageMCPAction);470providerSubMenuActions.push(manageDynamicAuthProvidersAction);471if (account.canSignOut) {472providerSubMenuActions.push(toAction({473id: 'signOut',474label: localize('signOut', "Sign Out"),475enabled: true,476run: () => this.commandService.executeCommand('_signOutOfAccount', { providerId, accountLabel: account.label })477}));478}479480const providerSubMenu = new SubmenuAction('activitybar.submenu', `${account.label} (${provider.label})`, providerSubMenuActions);481menus.push(providerSubMenu);482}483}484}485486if (menus.length && otherCommands.length) {487menus.push(new Separator());488}489490otherCommands.forEach((group, i) => {491const actions = group[1];492menus = menus.concat(actions);493if (i !== otherCommands.length - 1) {494menus.push(new Separator());495}496});497498return menus;499}500501protected override async resolveContextMenuActions(disposables: DisposableStore): Promise<IAction[]> {502const actions = await super.resolveContextMenuActions(disposables);503this.fillContextMenuActions(actions);504return actions;505}506507//#endregion508509//#region groupedAccounts helpers510511private async addOrUpdateAccount(providerId: string, account: AuthenticationSessionAccount): Promise<void> {512let accounts = this.groupedAccounts.get(providerId);513if (!accounts) {514accounts = [];515this.groupedAccounts.set(providerId, accounts);516}517518const sessionFromEmbedder = await this.sessionFromEmbedder.value;519let canSignOut = true;520if (521sessionFromEmbedder // if we have a session from the embedder522&& !sessionFromEmbedder.canSignOut // and that session says we can't sign out523&& (await this.authenticationService.getSessions(providerId)) // and that session is associated with the account we are adding/updating524.some(s =>525s.id === sessionFromEmbedder.id526&& s.account.id === account.id527)528) {529canSignOut = false;530}531532const existingAccount = accounts.find(a => a.label === account.label);533if (existingAccount) {534// if we have an existing account and we discover that we535// can't sign out of it, update the account to mark it as "can't sign out"536if (!canSignOut) {537existingAccount.canSignOut = canSignOut;538}539} else {540accounts.push({ ...account, canSignOut });541}542}543544private removeAccount(providerId: string, account: AuthenticationSessionAccount): void {545const accounts = this.groupedAccounts.get(providerId);546if (!accounts) {547return;548}549550const index = accounts.findIndex(a => a.id === account.id);551if (index === -1) {552return;553}554555accounts.splice(index, 1);556if (accounts.length === 0) {557this.groupedAccounts.delete(providerId);558}559}560561private async addAccountsFromProvider(providerId: string): Promise<void> {562try {563const sessions = await this.authenticationService.getSessions(providerId);564this.problematicProviders.delete(providerId);565566for (const session of sessions) {567try {568await this.addOrUpdateAccount(providerId, session.account);569} catch (e) {570this.logService.error(e);571}572}573} catch (e) {574this.logService.error(e);575this.problematicProviders.add(providerId);576}577}578579//#endregion580}581582export class GlobalActivityActionViewItem extends AbstractGlobalActivityActionViewItem {583584private profileBadge: HTMLElement | undefined;585private profileBadgeContent: HTMLElement | undefined;586587constructor(588contextMenuActionsProvider: () => IAction[],589options: ICompositeBarActionViewItemOptions,590contextMenuAlignmentOptions: () => Readonly<{ anchorAlignment: AnchorAlignment; anchorAxisAlignment: AnchorAxisAlignment }> | undefined,591@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,592@IThemeService themeService: IThemeService,593@IHoverService hoverService: IHoverService,594@IMenuService menuService: IMenuService,595@IContextMenuService contextMenuService: IContextMenuService,596@IContextKeyService contextKeyService: IContextKeyService,597@IConfigurationService configurationService: IConfigurationService,598@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,599@IKeybindingService keybindingService: IKeybindingService,600@IInstantiationService instantiationService: IInstantiationService,601@IActivityService activityService: IActivityService,602) {603const action = instantiationService.createInstance(CompositeBarAction, {604id: GLOBAL_ACTIVITY_ID,605name: localize('manage', "Manage"),606classNames: ThemeIcon.asClassNameArray(userDataProfileService.currentProfile.icon ? ThemeIcon.fromId(userDataProfileService.currentProfile.icon) : DEFAULT_ICON)607});608super(MenuId.GlobalActivity, action, options, contextMenuActionsProvider, contextMenuAlignmentOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, keybindingService, activityService);609this._register(action);610this._register(this.userDataProfileService.onDidChangeCurrentProfile(e => {611action.compositeBarActionItem = {612...action.compositeBarActionItem,613classNames: ThemeIcon.asClassNameArray(userDataProfileService.currentProfile.icon ? ThemeIcon.fromId(userDataProfileService.currentProfile.icon) : DEFAULT_ICON)614};615}));616}617618override render(container: HTMLElement): void {619super.render(container);620621this.profileBadge = append(container, $('.profile-badge'));622this.profileBadgeContent = append(this.profileBadge, $('.profile-badge-content'));623this.updateProfileBadge();624}625626private updateProfileBadge(): void {627if (!this.profileBadge || !this.profileBadgeContent) {628return;629}630631clearNode(this.profileBadgeContent);632hide(this.profileBadge);633634if (this.userDataProfileService.currentProfile.isDefault) {635return;636}637638if (this.userDataProfileService.currentProfile.icon && this.userDataProfileService.currentProfile.icon !== DEFAULT_ICON.id) {639return;640}641642if ((this.action as CompositeBarAction).activities.length > 0) {643return;644}645646show(this.profileBadge);647this.profileBadgeContent.classList.add('profile-text-overlay');648this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase();649}650651protected override updateActivity(): void {652super.updateActivity();653this.updateProfileBadge();654}655656protected override computeTitle(): string {657return this.userDataProfileService.currentProfile.isDefault ? super.computeTitle() : localize('manage profile', "Manage {0} (Profile)", this.userDataProfileService.currentProfile.name);658}659}660661export class SimpleAccountActivityActionViewItem extends AccountsActivityActionViewItem {662663constructor(664hoverOptions: IActivityHoverOptions,665options: IBaseActionViewItemOptions,666@IThemeService themeService: IThemeService,667@ILifecycleService lifecycleService: ILifecycleService,668@IHoverService hoverService: IHoverService,669@IContextMenuService contextMenuService: IContextMenuService,670@IMenuService menuService: IMenuService,671@IContextKeyService contextKeyService: IContextKeyService,672@IAuthenticationService authenticationService: IAuthenticationService,673@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,674@IProductService productService: IProductService,675@IConfigurationService configurationService: IConfigurationService,676@IKeybindingService keybindingService: IKeybindingService,677@ISecretStorageService secretStorageService: ISecretStorageService,678@IStorageService storageService: IStorageService,679@ILogService logService: ILogService,680@IActivityService activityService: IActivityService,681@IInstantiationService instantiationService: IInstantiationService,682@ICommandService commandService: ICommandService683) {684super(() => simpleActivityContextMenuActions(storageService, true),685{686...options,687colors: theme => ({688badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),689badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),690}),691hoverOptions,692compact: true,693}, () => undefined, actions => actions, themeService, lifecycleService, hoverService, contextMenuService, menuService, contextKeyService, authenticationService, environmentService, productService, configurationService, keybindingService, secretStorageService, logService, activityService, instantiationService, commandService);694}695}696697export class SimpleGlobalActivityActionViewItem extends GlobalActivityActionViewItem {698699constructor(700hoverOptions: IActivityHoverOptions,701options: IBaseActionViewItemOptions,702@IUserDataProfileService userDataProfileService: IUserDataProfileService,703@IThemeService themeService: IThemeService,704@IHoverService hoverService: IHoverService,705@IMenuService menuService: IMenuService,706@IContextMenuService contextMenuService: IContextMenuService,707@IContextKeyService contextKeyService: IContextKeyService,708@IConfigurationService configurationService: IConfigurationService,709@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,710@IKeybindingService keybindingService: IKeybindingService,711@IInstantiationService instantiationService: IInstantiationService,712@IActivityService activityService: IActivityService,713@IStorageService storageService: IStorageService714) {715super(() => simpleActivityContextMenuActions(storageService, false),716{717...options,718colors: theme => ({719badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),720badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),721}),722hoverOptions,723compact: true,724}, () => undefined, userDataProfileService, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService, instantiationService, activityService);725}726}727728function simpleActivityContextMenuActions(storageService: IStorageService, isAccount: boolean): IAction[] {729const currentElementContextMenuActions: IAction[] = [];730if (isAccount) {731currentElementContextMenuActions.push(732toAction({ id: 'hideAccounts', label: localize('hideAccounts', "Hide Accounts"), run: () => setAccountsActionVisible(storageService, false) }),733new Separator()734);735}736return [737...currentElementContextMenuActions,738toAction({ id: 'toggle.hideAccounts', label: localize('accounts', "Accounts"), checked: isAccountsActionVisible(storageService), run: () => setAccountsActionVisible(storageService, !isAccountsActionVisible(storageService)) }),739toAction({ id: 'toggle.hideManage', label: localize('manage', "Manage"), checked: true, enabled: false, run: () => { throw new Error('"Manage" can not be hidden'); } })740];741}742743export function isAccountsActionVisible(storageService: IStorageService): boolean {744return storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.PROFILE, true);745}746747function setAccountsActionVisible(storageService: IStorageService, visible: boolean) {748storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, visible, StorageScope.PROFILE, StorageTarget.USER);749}750751752