Files
vscode/src/vs/sessions/contrib/changes/browser/sessionFilesViewModel.ts
T
Benjamin Christopher Simmonds 637904797e sessions: add Session Files section to the Changes view (#323725)
* sessions: add Session Files section to the Changes view

Add a collapsible/resizable 'Session Files' section to the agents Changes view, modeled on the CI Checks section, listing files created/edited/deleted outside the session workspace folders during the session (these are not committed).

Data is parsed from the agent-host chat-state turns (response parts -> tool calls -> FileEdit results/pending edits) and exposed via a new optional 'externalChanges' observable on ISession. Parsing is incremental: completed turns are memoized by id and only the active turn is re-parsed per streamed delta, with equality functions to suppress redundant downstream work. Computation is gated on the active, non-archived session.

Click opens created/deleted files normally and edited files as a diff. Adds component-explorer fixtures and unit tests covering delta-only recomputation and reduce semantics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* sessions: register ResourceLabels services in Session Files fixture

The Session Files widget fixture failed to render because ResourceLabels depends on IWorkspaceContextService, IDecorationsService, ITextFileService and INotebookDocumentService, which the base fixture services don't provide. Register mock instances the same way the multiDiffEditor fixture does.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* sessions: address Session Files review feedback

- Add a Changes-view accessibility help dialog (SessionsChangesAccessibilityHelp) with a new verbosity setting, documenting the file tree and the Session Files / Checks sections and how to operate them (collapse/expand, open file vs diff).

- Handle touch in the Session Files header via Gesture.addTarget + TouchEventType.Tap so the collapse/expand toggle works on touch platforms.

- Use getComparisonKey(uri) instead of uri.toString() for Map keys and sorting in reduceSessionFiles, for safe cross-platform URI identity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* sessions: register IListService in Session Files fixture

SessionFilesWidget creates a WorkbenchList, which depends on IListService. Register it in the fixture (as the aiCustomizationListWidget fixture does) so the widget can be instantiated under the component explorer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-30 15:31:07 +00:00

36 lines
1.6 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { derived, IObservable } from '../../../../base/common/observable.js';
import { ISessionsService } from '../../../services/sessions/browser/sessionsService.js';
import { ISessionFile } from '../../../services/sessions/common/session.js';
/** Shared stable empty result so "no session / no files" doesn't churn observers. */
const EMPTY_SESSION_FILES: readonly ISessionFile[] = Object.freeze([]);
/**
* View model backing the "Session Files" section in the changes view. Exposes
* the files created/edited/deleted outside the workspace by the active session.
*/
export class SessionFilesViewModel extends Disposable {
readonly sessionFilesObs: IObservable<readonly ISessionFile[]>;
constructor(
@ISessionsService sessionsService: ISessionsService,
) {
super();
// The underlying `externalChanges` observable carries its own structural
// equality, so when it is unchanged it returns the same array reference
// and this derived does not propagate. The shared empty constant keeps
// the "no session" case equally stable.
this.sessionFilesObs = derived(this, reader => {
const activeSession = sessionsService.activeSession.read(reader);
return activeSession?.externalChanges?.read(reader) ?? EMPTY_SESSION_FILES;
});
}
}