Path: blob/main/src/vs/sessions/contrib/layout/browser/layoutController.ts
13401 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 { autorun, derived, derivedOpts } from '../../../../base/common/observable.js';6import { isEqual } from '../../../../base/common/resources.js';7import { Disposable } from '../../../../base/common/lifecycle.js';8import { ResourceMap } from '../../../../base/common/map.js';9import { isMobile, isWeb } from '../../../../base/common/platform.js';10import { URI } from '../../../../base/common/uri.js';11import { IChatService } from '../../../../workbench/contrib/chat/common/chatService/chatService.js';12import { IWorkbenchLayoutService, Parts } from '../../../../workbench/services/layout/browser/layoutService.js';13import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';14import { IViewsService } from '../../../../workbench/services/views/common/viewsService.js';15import { CHANGES_VIEW_ID } from '../../changes/common/changes.js';16import { SESSIONS_FILES_CONTAINER_ID } from '../../files/browser/files.contribution.js';17import { SessionStatus } from '../../../services/sessions/common/session.js';1819interface IPendingTurnState {20readonly hadChangesBeforeSend: boolean;21readonly submittedAt: number;22}2324export class LayoutController extends Disposable {2526static readonly ID = 'workbench.contrib.sessionsLayoutController';2728private readonly _pendingTurnStateByResource = new ResourceMap<IPendingTurnState>();29private readonly _panelVisibilityBySession = new ResourceMap<boolean>();3031constructor(32@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,33@ISessionsManagementService private readonly _sessionManagementService: ISessionsManagementService,34@IChatService private readonly _chatService: IChatService,35@IViewsService private readonly _viewsService: IViewsService,36) {37super();3839const activeSessionResourceObs = derivedOpts<URI | undefined>({40equalsFn: isEqual41}, reader => {42const activeSession = this._sessionManagementService.activeSession.read(reader);43return activeSession?.resource;44});4546const activeSessionHasChangesObs = derived<boolean>(reader => {47const activeSession = this._sessionManagementService.activeSession.read(reader);48if (!activeSession) {49return false;50}51const changes = activeSession.changes.read(reader);52return changes.length > 0;53});5455const activeSessionIsUntitledObs = derived<boolean>(reader => {56const activeSession = this._sessionManagementService.activeSession.read(reader);57const activeSessionStatus = activeSession?.status.read(reader);5859return activeSessionStatus === SessionStatus.Untitled;60});6162const activeSessionHasWorkspaceObs = derived<boolean>(reader => {63const activeSession = this._sessionManagementService.activeSession.read(reader);64return activeSession?.workspace.read(reader)?.repositories?.[0]?.uri !== undefined;65});6667// Switch between sessions — sync auxiliary bar (skip on mobile to avoid68// disruptive auto-expand on narrow viewports)69if (!(isWeb && isMobile)) {70this._register(autorun(reader => {71const isUntitled = activeSessionIsUntitledObs.read(reader);72const activeSessionHasWorkspace = activeSessionHasWorkspaceObs.read(reader);73const activeSessionHasChanges = activeSessionHasChangesObs.read(reader);7475this._syncAuxiliaryBarVisibility(activeSessionHasWorkspace, isUntitled, activeSessionHasChanges);76}));77}7879// Switch between sessions — sync panel visibility80this._register(autorun(reader => {81const activeSessionResource = activeSessionResourceObs.read(reader);82this._syncPanelVisibility(activeSessionResource);83}));8485// When a turn is completed, check if there were changes before the turn and86// if there are changes after the turn. If there were no changes before the87// turn and there are changes after the turn, show the auxiliary bar.88// Skip on mobile to avoid disruptive auto-expand on narrow viewports.89if (!(isWeb && isMobile)) {90this._register(autorun((reader) => {91const activeSession = this._sessionManagementService.activeSession.read(reader);92const activeSessionHasChanges = activeSessionHasChangesObs.read(reader);93if (!activeSession) {94return;95}9697const pendingTurnState = this._pendingTurnStateByResource.get(activeSession.resource);98if (!pendingTurnState) {99return;100}101102const lastTurnEnd = activeSession.lastTurnEnd.read(reader);103const turnCompleted = !!lastTurnEnd && lastTurnEnd.getTime() >= pendingTurnState.submittedAt;104if (!turnCompleted) {105return;106}107108if (!pendingTurnState.hadChangesBeforeSend && activeSessionHasChanges) {109this._layoutService.setPartHidden(false, Parts.AUXILIARYBAR_PART);110}111112this._pendingTurnStateByResource.delete(activeSession.resource);113}));114115this._register(this._chatService.onDidSubmitRequest(({ chatSessionResource }) => {116this._pendingTurnStateByResource.set(chatSessionResource, {117hadChangesBeforeSend: activeSessionHasChangesObs.get(),118submittedAt: Date.now(),119});120}));121}122123// Track panel visibility changes by the user124this._register(this._layoutService.onDidChangePartVisibility(e => {125if (e.partId !== Parts.PANEL_PART) {126return;127}128const activeSession = this._sessionManagementService.activeSession.get();129if (activeSession) {130this._panelVisibilityBySession.set(activeSession.resource, e.visible);131}132}));133}134135private _syncAuxiliaryBarVisibility(hasWorkspace: boolean, isUntitled: boolean, hasChanges: boolean): void {136if (!hasWorkspace) {137// Hide the auxiliary bar138this._viewsService.closeViewContainer(SESSIONS_FILES_CONTAINER_ID);139} else if (isUntitled) {140// Show the auxiliary bar (files view)141this._viewsService.openViewContainer(SESSIONS_FILES_CONTAINER_ID, false);142} else if (hasChanges) {143// Show the auxiliary bar (changes view)144this._viewsService.openView(CHANGES_VIEW_ID, false);145}146}147148private _syncPanelVisibility(sessionResource: URI | undefined): void {149if (!sessionResource) {150this._layoutService.setPartHidden(true, Parts.PANEL_PART);151return;152}153154const wasVisible = this._panelVisibilityBySession.get(sessionResource);155// Default to hidden if we have no record for this session156this._layoutService.setPartHidden(wasVisible !== true, Parts.PANEL_PART);157}158}159160161