Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ function createMockProvider(id: string, opts?: {
unarchiveSession: async () => { },
deleteSession: async () => { },
deleteChat: async () => { },
setRead: () => { },
sendAndCreateChat: async () => { throw new Error('Not implemented'); },
capabilities: { multipleChatsPerSession: false },
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1317,13 +1317,6 @@ export class CopilotChatSessionsProvider extends Disposable implements ISessions
}
}

setRead(sessionId: string, read: boolean): void {
const agentSession = this._findAgentSession(sessionId);
if (agentSession) {
agentSession.setRead(read);
}
}

// -- Send --

async sendAndCreateChat(sessionId: string, options: ISendRequestOptions): Promise<ISession> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,6 @@ function createProviderForSendTests(
return disposables.add(instantiationService.createInstance(CopilotChatSessionsProvider));
}



suite('CopilotChatSessionsProvider', () => {
const disposables = new DisposableStore();
let model: MockAgentSessionsModel;
Expand Down Expand Up @@ -356,20 +354,6 @@ suite('CopilotChatSessionsProvider', () => {
assert.strictEqual(agentSession.isArchived(), false);
});

test('setRead marks session as read', () => {
const resource = URI.from({ scheme: AgentSessionProviders.Background, path: '/session-1' });
const agentSession = createMockAgentSession(resource, { read: false });
model.addSession(agentSession);

const provider = createProvider(disposables, model);
provider.getSessions();

const session = provider.getSessions()[0];
provider.setRead(session.sessionId, true);

assert.strictEqual(agentSession.isRead(), true);
});

// ---- Single-chat mode (multi-chat disabled) -------

test('single-chat mode: each session has exactly one chat', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,16 +510,6 @@ export class LocalAgentHostSessionsProvider extends Disposable implements ISessi
// Agent host sessions don't support deleting individual chats
}

setRead(sessionId: string, read: boolean): void {
const rawId = this._rawIdFromChatId(sessionId);
const cached = rawId ? this._sessionCache.get(rawId) : undefined;
if (cached && rawId) {
cached.isRead.set(read, undefined);
const action = { type: ActionType.SessionIsReadChanged as const, session: AgentSession.uri(cached.agentProvider, rawId).toString(), isRead: read };
this._agentHostService.dispatch(action);
}
}

async sendAndCreateChat(chatId: string, options: ISendRequestOptions): Promise<ISession> {
const session = this._currentNewSession;
if (!session || session.sessionId !== chatId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,19 +517,6 @@ suite('LocalAgentHostSessionsProvider', () => {
assert.strictEqual(provider.getSessions().find(s => s.title.get() === 'To Delete'), undefined);
});

test('setRead toggles read state locally', () => {
const provider = createProvider(disposables, agentHost);
fireSessionAdded(agentHost, 'read-sess', { title: 'Read Test' });

const sessions = provider.getSessions();
const target = sessions.find(s => s.title.get() === 'Read Test');
assert.ok(target);

assert.strictEqual(target!.isRead.get(), true);
provider.setRead(target!.sessionId, false);
assert.strictEqual(target!.isRead.get(), false);
});

// ---- Rename -------

test('renameChat dispatches SessionTitleChanged action', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,18 +641,6 @@ export class RemoteAgentHostSessionsProvider extends Disposable implements ISess
// Agent host sessions don't support deleting individual chats
}

setRead(sessionId: string, read: boolean): void {
const rawId = this._rawIdFromChatId(sessionId);
const cached = rawId ? this._sessionCache.get(rawId) : undefined;
if (cached) {
cached.isRead.set(read, undefined);
if (this._connection && rawId) {
const action = { type: ActionType.SessionIsReadChanged as const, session: AgentSession.uri(cached.agentProvider, rawId).toString(), isRead: read };
this._connection.dispatch(action);
}
}
}

async sendAndCreateChat(chatId: string, options: ISendRequestOptions): Promise<ISession> {
if (!this._connection) {
throw new Error(localize('notConnectedSend', "Cannot send request: not connected to remote agent host '{0}'.", this.label));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,19 +500,6 @@ suite('RemoteAgentHostSessionsProvider', () => {
assert.strictEqual(remaining.find((s) => s.title.get() === 'To Delete'), undefined);
});

test('setRead toggles read state locally', () => {
const provider = createProvider(disposables, connection);
fireSessionAdded(connection, 'read-sess', { title: 'Read Test' });

const sessions = provider.getSessions();
const target = sessions.find((s) => s.title.get() === 'Read Test');
assert.ok(target, 'Session should exist');

assert.strictEqual(target!.isRead.get(), true);
provider.setRead(target!.sessionId, false);
assert.strictEqual(target!.isRead.get(), false);
});

// ---- Rename -------

test('renameSession dispatches SessionTitleChanged action with correct session URI', async () => {
Expand Down
24 changes: 12 additions & 12 deletions src/vs/sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ import { IsAuxiliaryWindowContext } from '../../../../workbench/common/contextke
import { ChatSessionProviderIdContext, IsNewChatSessionContext, SessionsWelcomeVisibleContext } from '../../../common/contextkeys.js';
import { ISessionsProvidersService } from '../../../services/sessions/browser/sessionsProvidersService.js';
import { SessionStatus } from '../../../services/sessions/common/session.js';
import { ISessionsListModelService } from './views/sessionsListModelService.js';
import { SHOW_SESSIONS_PICKER_COMMAND_ID } from './sessionsActions.js';
import { IsSessionArchivedContext, IsSessionPinnedContext, IsSessionReadContext, SessionItemContextMenuId } from './views/sessionsList.js';
import { SessionsView, SessionsViewId } from './views/sessionsView.js';
import { IViewsService } from '../../../../workbench/services/views/common/viewsService.js';
import { basename } from '../../../../base/common/resources.js';
import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';

Expand Down Expand Up @@ -65,12 +64,12 @@ export class SessionsTitleBarWidget extends BaseActionViewItem {
options: IBaseActionViewItemOptions | undefined,
@IHoverService private readonly hoverService: IHoverService,
@ISessionsManagementService private readonly sessionsManagementService: ISessionsManagementService,
@ISessionsListModelService private readonly sessionsListModelService: ISessionsListModelService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IMenuService private readonly menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@ISessionsProvidersService private readonly sessionsProvidersService: ISessionsProvidersService,
@ICommandService private readonly commandService: ICommandService,
@IViewsService private readonly viewsService: IViewsService,
) {
super(undefined, action, options);

Expand Down Expand Up @@ -302,11 +301,13 @@ export class SessionsTitleBarWidget extends BaseActionViewItem {
return;
}

const isPinned = this.viewsService.getViewWithId<SessionsView>(SessionsViewId)?.sessionsControl?.isSessionPinned(sessionData) ?? false;
const isPinned = this.sessionsListModelService.isSessionPinned(sessionData);
const isArchived = sessionData.isArchived.get();
const isRead = this.sessionsListModelService.isSessionRead(sessionData);
const contextOverlay: [string, boolean | string][] = [
[IsSessionPinnedContext.key, isPinned],
[IsSessionArchivedContext.key, sessionData.isArchived.get()],
[IsSessionReadContext.key, sessionData.isRead.get()],
[IsSessionArchivedContext.key, isArchived],
[IsSessionReadContext.key, isRead],
['chatSessionType', sessionData.sessionType],
[ChatSessionProviderIdContext.key, sessionData.providerId],
];
Expand Down Expand Up @@ -339,6 +340,7 @@ class SidebarToggleActionViewItem extends ActionViewItem {
action: IAction,
options: IBaseActionViewItemOptions | undefined,
@ISessionsManagementService private readonly sessionsManagementService: ISessionsManagementService,
@ISessionsListModelService private readonly sessionsListModelService: ISessionsListModelService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
) {
super(context, action, { ...options, icon: true, label: false });
Expand All @@ -354,23 +356,21 @@ class SidebarToggleActionViewItem extends ActionViewItem {
this._countBadge.setAttribute('aria-hidden', 'true');
this._updateBadge();

// Single autorun that tracks all badge-relevant state:
// - session list changes (add/remove) via observableSignalFromEvent
// - individual session observable state (status, isRead, isArchived)
// - sidebar visibility changes
// Track session list changes, status changes, and sidebar visibility
const sessionsChanged = observableSignalFromEvent(this, this.sessionsManagementService.onDidChangeSessions);
const listModelChanged = observableSignalFromEvent(this, this.sessionsListModelService.onDidChange);
const sidebarVisibilityChanged = observableSignalFromEvent(this, handler => this.layoutService.onDidChangePartVisibility(e => {
if (e.partId === Parts.SIDEBAR_PART) {
handler(e);
}
}));
this._register(autorun(reader => {
sessionsChanged.read(reader);
listModelChanged.read(reader);
sidebarVisibilityChanged.read(reader);
for (const session of this.sessionsManagementService.getSessions()) {
session.isArchived.read(reader);
session.status.read(reader);
session.isRead.read(reader);
}
this.updateClass();
this._updateBadge();
Expand Down Expand Up @@ -412,7 +412,7 @@ class SidebarToggleActionViewItem extends ActionViewItem {
private _countUnreadSessions(): number {
let unread = 0;
for (const session of this.sessionsManagementService.getSessions()) {
if (!session.isArchived.get() && session.status.get() === SessionStatus.Completed && !session.isRead.get()) {
if (!session.isArchived.get() && session.status.get() === SessionStatus.Completed && !this.sessionsListModelService.isSessionRead(session)) {
unread++;
}
}
Expand Down
Loading
Loading