diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts index 215541955394f..599d70250ac05 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts @@ -20,7 +20,7 @@ import { ACTION_ID_OPEN_CHAT } from '../actions/chatActions.js'; import { IChatEditorOptions } from '../chatEditor.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { Event } from '../../../../../base/common/event.js'; -import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { ITreeContextMenuEvent } from '../../../../../base/browser/ui/tree/tree.js'; import { MarshalledId } from '../../../../../base/common/marshallingIds.js'; import { Separator } from '../../../../../base/common/actions.js'; @@ -36,6 +36,9 @@ import { IStyleOverride } from '../../../../../platform/theme/browser/defaultSty import { ChatEditorInput } from '../chatEditorInput.js'; import { IAgentSessionsControl } from './agentSessions.js'; import { Schemas } from '../../../../../base/common/network.js'; +import { ResourceMap } from '../../../../../base/common/map.js'; +import { autorun, autorunIterableDelta } from '../../../../../base/common/observable.js'; +import { IChatEditingService, ModifiedFileEntryState } from '../../common/chatEditingService.js'; export interface IAgentSessionsControlOptions { readonly overrideStyles?: IStyleOverride; @@ -79,6 +82,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo @IAgentSessionsService private readonly agentSessionsService: IAgentSessionsService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEditorService private readonly editorService: IEditorService, + @IChatEditingService private readonly chatEditingService: IChatEditingService, ) { super(); @@ -91,6 +95,42 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo if (this.options?.trackActiveEditor) { this._register(this.editorService.onDidActiveEditorChange(() => this.revealAndFocusActiveEditorSession())); } + // Register to editing session changes to refresh the list when undo/redo state changes + const listeners = new ResourceMap(); + const previousEntryStates = new ResourceMap>(); + const autoRunDisposable = autorunIterableDelta( + reader => this.chatEditingService.editingSessionsObs.read(reader), + ({ addedValues, removedValues }) => { + removedValues.forEach((removed) => { + const listener = listeners.get(removed.chatSessionResource); + if (listener) { + listeners.delete(removed.chatSessionResource); + listener.dispose(); + } + previousEntryStates.delete(removed.chatSessionResource); + }); + addedValues.forEach((added) => { + previousEntryStates.set(added.chatSessionResource, new Map()); + listeners.set(added.chatSessionResource, autorun(reader => { + const entries = added.entries.read(reader); + const previous = previousEntryStates.get(added.chatSessionResource); + + entries.forEach(entry => { + const currentState = entry.state.read(reader); + const previousState = previous?.get(entry.entryId); + if (previous && currentState !== previousState) { + this.refresh(); + previous.set(entry.entryId, currentState); + } + }); + })); + }); + } + ); + this._register(toDisposable(() => { + for (const listener of listeners.values()) { listener.dispose(); } + })); + this._register(autoRunDisposable); } private revealAndFocusActiveEditorSession(): void { diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsView.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsView.ts index cb31f1c9420e3..8d3d57fca0b81 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsView.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsView.ts @@ -35,6 +35,8 @@ import { AgentSessionsFilter } from './agentSessionsFilter.js'; import { AgentSessionsControl } from './agentSessionsControl.js'; import { IAgentSessionsService } from './agentSessionsService.js'; import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; +import { autorun } from '../../../../../base/common/observable.js'; +import { IChatEditingService } from '../../common/chatEditingService.js'; type AgentSessionsViewPaneOpenedClassification = { owner: 'bpasero'; @@ -60,6 +62,7 @@ export class AgentSessionsView extends ViewPane { @IMenuService private readonly menuService: IMenuService, @IAgentSessionsService private readonly agentSessionsService: IAgentSessionsService, @ITelemetryService private readonly telemetryService: ITelemetryService, + @IChatEditingService private readonly chatEditingService: IChatEditingService, ) { super({ ...options, titleMenuId: MenuId.AgentSessionsViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); @@ -82,6 +85,16 @@ export class AgentSessionsView extends ViewPane { () => didResolve.p ); })); + + this._register(autorun(reader => { + const editingSessions = this.chatEditingService.editingSessionsObs.read(reader); + for (const session of editingSessions) { + session.canUndo.read(reader); + session.canRedo.read(reader); + } + + this.sessionsControl?.refresh(); + })); } protected override renderBody(container: HTMLElement): void {