diff --git a/src/vs/sessions/contrib/accountMenu/browser/account.contribution.ts b/src/vs/sessions/contrib/accountMenu/browser/account.contribution.ts index 01af2bcf4bf..a0cc7a2d189 100644 --- a/src/vs/sessions/contrib/accountMenu/browser/account.contribution.ts +++ b/src/vs/sessions/contrib/accountMenu/browser/account.contribution.ts @@ -35,7 +35,7 @@ import { URI } from '../../../../base/common/uri.js'; import { isWindows, isMacintosh } from '../../../../base/common/platform.js'; import { UpdateHoverWidget } from './updateHoverWidget.js'; import { ChatEntitlement, ChatEntitlementService, IChatEntitlementService } from '../../../../workbench/services/chat/common/chatEntitlementService.js'; -import { ChatStatusDashboard } from '../../../../workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.js'; +import { ChatStatusDashboard, IChatStatusDashboardOptions } from '../../../../workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { getAccountProfileImageUrl, getAccountTitleBarBadgeKey, getAccountTitleBarState, resolveAccountInfo } from '../../../browser/accountTitleBarState.js'; @@ -608,22 +608,10 @@ class TitleBarAccountWidget extends BaseActionViewItem { const subscriptionHeader = append(subscriptionSection, $('.sessions-account-titlebar-panel-section-header')); const subscriptionHeading = append(subscriptionHeader, $('div.sessions-account-titlebar-panel-section-title', { id: subscriptionId })); subscriptionHeading.textContent = localize('sessionsAccountMenu.subscription', "Subscription"); - const dashboard = this.createCopilotHoverContent(); + // Render the dashboard's title header (plan name + manage / CTA actions) + // directly into our section header row via the dashboard's public API. + const dashboard = this.createCopilotHoverContent({ titleHeaderContainer: subscriptionHeader }); append(subscriptionSection, dashboard); - // Move the dashboard's plan-name + manage-action header into our section header row, - // so the plan name and settings button appear right-aligned next to "Subscription". - // The dashboard is wrapped in a `display: contents` div, hence reaching one level in. - // Note: the dashboard renders the title header synchronously and never re-creates it, - // so a one-time move is safe today. Tolerate non-`.header` first children defensively. - const tooltipRoot = dashboard.firstElementChild; - if (tooltipRoot) { - for (const child of Array.from(tooltipRoot.children)) { - if (child.classList.contains('header')) { - subscriptionHeader.appendChild(child); - break; - } - } - } } else if (!this.isAccountLoading) { const summary = append(contentSection, $('.sessions-account-titlebar-panel-summary')); summary.textContent = this.lastState.ariaLabel; @@ -760,7 +748,7 @@ class TitleBarAccountWidget extends BaseActionViewItem { return !this.chatEntitlementService.sentiment.hidden && !!this.accountName; } - private createCopilotHoverContent(): HTMLElement { + private createCopilotHoverContent(extraOptions?: Partial): HTMLElement { const store = new DisposableStore(); this.copilotDashboardStore.value = store; const dashboardElement = ChatStatusDashboard.instantiateInContents(this.instantiationService, store, { @@ -769,6 +757,7 @@ class TitleBarAccountWidget extends BaseActionViewItem { disableProviderOptions: true, disableCompletionsSnooze: true, disableQuickSettingsCollapsible: true, + ...extraOptions, }); store.add(disposableWindowInterval(mainWindow, () => { diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts index cda7536ff2f..036c20188a7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus/chatStatusDashboard.ts @@ -75,6 +75,13 @@ export interface IChatStatusDashboardOptions { disableCompletionsSnooze?: boolean; /** When true, the Quick Settings region is rendered always-expanded without a collapsible header. */ disableQuickSettingsCollapsible?: boolean; + /** + * When provided, the title header (plan name + manage / CTA actions) is + * rendered into this caller-owned container instead of inline at the top + * of the dashboard. Use this to embed the title header in a host layout + * without reaching into the dashboard's private DOM. + */ + titleHeaderContainer?: HTMLElement; } export class ChatStatusDashboard extends DomWidget { @@ -133,7 +140,8 @@ export class ChatStatusDashboard extends DomWidget { let headerAdditionalSpendButton: Button | undefined; if (hasUsageSection) { const planName = getChatPlanName(this.chatEntitlementService.entitlement); - const header = this.renderHeader(this.element, this._store, planName, toAction({ + const headerHost = this.options?.titleHeaderContainer ?? this.element; + const header = this.renderHeader(headerHost, this._store, planName, toAction({ id: 'workbench.action.manageCopilot', label: localize('quotaLabel', "Manage Chat"), tooltip: localize('quotaTooltip', "Manage Chat"),