Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/layout/browser/layoutController.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { autorun, derived, derivedOpts } from '../../../../base/common/observable.js';
7
import { isEqual } from '../../../../base/common/resources.js';
8
import { Disposable } from '../../../../base/common/lifecycle.js';
9
import { ResourceMap } from '../../../../base/common/map.js';
10
import { isMobile, isWeb } from '../../../../base/common/platform.js';
11
import { URI } from '../../../../base/common/uri.js';
12
import { IChatService } from '../../../../workbench/contrib/chat/common/chatService/chatService.js';
13
import { IWorkbenchLayoutService, Parts } from '../../../../workbench/services/layout/browser/layoutService.js';
14
import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';
15
import { IViewsService } from '../../../../workbench/services/views/common/viewsService.js';
16
import { CHANGES_VIEW_ID } from '../../changes/common/changes.js';
17
import { SESSIONS_FILES_CONTAINER_ID } from '../../files/browser/files.contribution.js';
18
import { SessionStatus } from '../../../services/sessions/common/session.js';
19
20
interface IPendingTurnState {
21
readonly hadChangesBeforeSend: boolean;
22
readonly submittedAt: number;
23
}
24
25
export class LayoutController extends Disposable {
26
27
static readonly ID = 'workbench.contrib.sessionsLayoutController';
28
29
private readonly _pendingTurnStateByResource = new ResourceMap<IPendingTurnState>();
30
private readonly _panelVisibilityBySession = new ResourceMap<boolean>();
31
32
constructor(
33
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
34
@ISessionsManagementService private readonly _sessionManagementService: ISessionsManagementService,
35
@IChatService private readonly _chatService: IChatService,
36
@IViewsService private readonly _viewsService: IViewsService,
37
) {
38
super();
39
40
const activeSessionResourceObs = derivedOpts<URI | undefined>({
41
equalsFn: isEqual
42
}, reader => {
43
const activeSession = this._sessionManagementService.activeSession.read(reader);
44
return activeSession?.resource;
45
});
46
47
const activeSessionHasChangesObs = derived<boolean>(reader => {
48
const activeSession = this._sessionManagementService.activeSession.read(reader);
49
if (!activeSession) {
50
return false;
51
}
52
const changes = activeSession.changes.read(reader);
53
return changes.length > 0;
54
});
55
56
const activeSessionIsUntitledObs = derived<boolean>(reader => {
57
const activeSession = this._sessionManagementService.activeSession.read(reader);
58
const activeSessionStatus = activeSession?.status.read(reader);
59
60
return activeSessionStatus === SessionStatus.Untitled;
61
});
62
63
const activeSessionHasWorkspaceObs = derived<boolean>(reader => {
64
const activeSession = this._sessionManagementService.activeSession.read(reader);
65
return activeSession?.workspace.read(reader)?.repositories?.[0]?.uri !== undefined;
66
});
67
68
// Switch between sessions — sync auxiliary bar (skip on mobile to avoid
69
// disruptive auto-expand on narrow viewports)
70
if (!(isWeb && isMobile)) {
71
this._register(autorun(reader => {
72
const isUntitled = activeSessionIsUntitledObs.read(reader);
73
const activeSessionHasWorkspace = activeSessionHasWorkspaceObs.read(reader);
74
const activeSessionHasChanges = activeSessionHasChangesObs.read(reader);
75
76
this._syncAuxiliaryBarVisibility(activeSessionHasWorkspace, isUntitled, activeSessionHasChanges);
77
}));
78
}
79
80
// Switch between sessions — sync panel visibility
81
this._register(autorun(reader => {
82
const activeSessionResource = activeSessionResourceObs.read(reader);
83
this._syncPanelVisibility(activeSessionResource);
84
}));
85
86
// When a turn is completed, check if there were changes before the turn and
87
// if there are changes after the turn. If there were no changes before the
88
// turn and there are changes after the turn, show the auxiliary bar.
89
// Skip on mobile to avoid disruptive auto-expand on narrow viewports.
90
if (!(isWeb && isMobile)) {
91
this._register(autorun((reader) => {
92
const activeSession = this._sessionManagementService.activeSession.read(reader);
93
const activeSessionHasChanges = activeSessionHasChangesObs.read(reader);
94
if (!activeSession) {
95
return;
96
}
97
98
const pendingTurnState = this._pendingTurnStateByResource.get(activeSession.resource);
99
if (!pendingTurnState) {
100
return;
101
}
102
103
const lastTurnEnd = activeSession.lastTurnEnd.read(reader);
104
const turnCompleted = !!lastTurnEnd && lastTurnEnd.getTime() >= pendingTurnState.submittedAt;
105
if (!turnCompleted) {
106
return;
107
}
108
109
if (!pendingTurnState.hadChangesBeforeSend && activeSessionHasChanges) {
110
this._layoutService.setPartHidden(false, Parts.AUXILIARYBAR_PART);
111
}
112
113
this._pendingTurnStateByResource.delete(activeSession.resource);
114
}));
115
116
this._register(this._chatService.onDidSubmitRequest(({ chatSessionResource }) => {
117
this._pendingTurnStateByResource.set(chatSessionResource, {
118
hadChangesBeforeSend: activeSessionHasChangesObs.get(),
119
submittedAt: Date.now(),
120
});
121
}));
122
}
123
124
// Track panel visibility changes by the user
125
this._register(this._layoutService.onDidChangePartVisibility(e => {
126
if (e.partId !== Parts.PANEL_PART) {
127
return;
128
}
129
const activeSession = this._sessionManagementService.activeSession.get();
130
if (activeSession) {
131
this._panelVisibilityBySession.set(activeSession.resource, e.visible);
132
}
133
}));
134
}
135
136
private _syncAuxiliaryBarVisibility(hasWorkspace: boolean, isUntitled: boolean, hasChanges: boolean): void {
137
if (!hasWorkspace) {
138
// Hide the auxiliary bar
139
this._viewsService.closeViewContainer(SESSIONS_FILES_CONTAINER_ID);
140
} else if (isUntitled) {
141
// Show the auxiliary bar (files view)
142
this._viewsService.openViewContainer(SESSIONS_FILES_CONTAINER_ID, false);
143
} else if (hasChanges) {
144
// Show the auxiliary bar (changes view)
145
this._viewsService.openView(CHANGES_VIEW_ID, false);
146
}
147
}
148
149
private _syncPanelVisibility(sessionResource: URI | undefined): void {
150
if (!sessionResource) {
151
this._layoutService.setPartHidden(true, Parts.PANEL_PART);
152
return;
153
}
154
155
const wasVisible = this._panelVisibilityBySession.get(sessionResource);
156
// Default to hidden if we have no record for this session
157
this._layoutService.setPartHidden(wasVisible !== true, Parts.PANEL_PART);
158
}
159
}
160
161