From 41cd3117ae62a689c2b37a137e78eee21bc7650c Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Fri, 25 Sep 2026 12:33:22 -0500 Subject: [PATCH] fix(desktop): keep the sidebar alive for messaging-only and cron-only profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit showSessionSections gated the whole session area — search, sessions, messaging platforms, cron jobs — on normal-session visibility. A profile whose only sessions are messaging threads, or that only has scheduled jobs, collapsed the sidebar to the 'No sessions yet' blank state, hiding its Telegram/Slack conversations and jobs until a normal session was created (the messaging half of #63593). Messaging sessions and cron jobs are already profile-scoped upstream (sidebarProfileForScope / filterSessionsByProfileScope), so the sections rendered inside the area are the active profile's own — no state is shared between profiles. Fixes #63593 --- .../sidebar/chat-sidebar.integration.test.tsx | 55 +++++++++++++++++++ apps/desktop/src/app/chat/sidebar/index.tsx | 11 +++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/app/chat/sidebar/chat-sidebar.integration.test.tsx b/apps/desktop/src/app/chat/sidebar/chat-sidebar.integration.test.tsx index 5277809d56..0a94d88820 100644 --- a/apps/desktop/src/app/chat/sidebar/chat-sidebar.integration.test.tsx +++ b/apps/desktop/src/app/chat/sidebar/chat-sidebar.integration.test.tsx @@ -345,6 +345,61 @@ describe('ChatSidebar section labels', () => { }) }) +describe('ChatSidebar empty-profile sections', () => { + // A profile with no normal sessions but messaging threads / cron jobs must + // still show them: the session area (and the sections inside it) is gated by + // showSessionSections, which used to key only on normal-session visibility + // and collapsed these profiles to the blank state (#63593). + const telegramThread = (id: string, last_active: number) => + makeSessionInfo({ connection_id: 'local', id, last_active, profile: 'default', source: 'telegram', title: id }) + + beforeEach(() => { + $connectionsRegistry.set({ + version: 2, + primary: 'local', + secureTokenStorage: true, + connections: [{ id: 'local', label: 'This computer', kind: 'local', tokenSet: false, tokenPreview: null }] + } as NonNullable) + $profiles.set([{ name: 'default', is_default: true }] as typeof $profiles.value) + $sessions.set([]) + $messagingSessions.set([telegramThread('tg-one', 60), telegramThread('tg-two', 30)]) + $messagingTruncated.set(false) + $sidebarMessagingOpenIds.set(['telegram']) + $sessionsLoading.set(false) + }) + + afterEach(() => { + cleanup() + $messagingSessions.set([]) + $sidebarMessagingOpenIds.set([]) + $sessions.set([]) + }) + + it('shows the messaging section for a profile with no normal sessions', () => { + const { container } = renderSidebar('/', 'chat') + + // The Telegram platform section renders with its threads… + expect(screen.getAllByText('Telegram').length).toBeGreaterThan(0) + expect(screen.getByText('tg-one')).toBeTruthy() + // …and the sidebar did not collapse to the blank state: the search field + // only renders inside the session area, so its presence proves the area + // (and the messaging sections inside it) rendered. + expect(screen.getByPlaceholderText('Search sessions…')).toBeTruthy() + }) + + it('still shows the blank state when a profile has nothing at all', () => { + $messagingSessions.set([]) + $sidebarMessagingOpenIds.set([]) + + // SidebarBlankState: a centered "No sessions yet" plus a "New project" + // button, rendered INSTEAD of the whole session area (search included). + const { container } = renderSidebar('/', 'chat') + + expect(container.textContent).toContain('No sessions yet') + expect(screen.queryByPlaceholderText('Search sessions…')).toBeNull() + }) +}) + describe('ChatSidebar messaging owners', () => { const telegram = (id: string, profile: string, last_active: number) => makeSessionInfo({ connection_id: 'local', id, last_active, profile, source: 'telegram', title: id }) diff --git a/apps/desktop/src/app/chat/sidebar/index.tsx b/apps/desktop/src/app/chat/sidebar/index.tsx index 80d282e7b9..e58fe3af35 100644 --- a/apps/desktop/src/app/chat/sidebar/index.tsx +++ b/apps/desktop/src/app/chat/sidebar/index.tsx @@ -1524,8 +1524,17 @@ export function ChatSidebar({ // Filtered down to nothing still renders the section: the empty state is what // tells you the filter — not an empty account — is why the list is bare. + // Messaging threads and cron jobs live inside this area too: a profile whose + // only sessions are messaging threads (or that only has scheduled jobs) must + // not collapse the whole sidebar to the blank state (#63593). const showSessionSections = - showSessionSkeletons || sessionsLoadError || filtersActive || sortedSessions.length > 0 || projectModel.length > 0 + showSessionSkeletons || + sessionsLoadError || + filtersActive || + sortedSessions.length > 0 || + projectModel.length > 0 || + messagingGroups.length > 0 || + (showsAdvancedChrome && cronJobs.length > 0) // The sidebar's session-area mode — exposed as data-attributes so custom // skins can target project mode (overview vs. entered), archived, or search