From 32c00d30f86cced3695c4c4b5d187bc2fa1dadaa Mon Sep 17 00:00:00 2001 From: ehz0ah Date: Wed, 9 Sep 2026 10:36:40 +0800 Subject: [PATCH] fix(desktop): honor effective project profile scope --- apps/desktop/src/store/projects.test.ts | 31 ++++++++++++++++++++++++- apps/desktop/src/store/projects.ts | 11 ++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/store/projects.test.ts b/apps/desktop/src/store/projects.test.ts index 9f720ea887..4bc5acff59 100644 --- a/apps/desktop/src/store/projects.test.ts +++ b/apps/desktop/src/store/projects.test.ts @@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { NO_PROJECT_ID, type SidebarProjectTree } from '@/app/chat/sidebar/projects/workspace-groups' import { $sidebarAgentsGrouped, setSidebarAgentsGrouped } from '@/store/layout' -import { $activeGatewayProfile, $profileScope, ALL_PROFILES, setShowAllProfiles } from '@/store/profile' +import { $activeGatewayProfile, $profiles, $profileScope, ALL_PROFILES, setShowAllProfiles } from '@/store/profile' import { $currentCwd, $selectedStoredSessionId, $sessions, applyConfiguredDefaultProjectDir } from '@/store/session' import { deferred } from '@/test/deferred' import type { ProjectInfo } from '@/types/hermes' @@ -135,6 +135,7 @@ describe('projects RPC profile forwarding', () => { beforeEach(() => { vi.clearAllMocks() $activeGatewayProfile.set('default') + $profiles.set([{ is_default: true, name: 'default' } as never]) $activeProjectId.set(null) $projectTree.set([]) setShowAllProfiles(false) @@ -199,6 +200,7 @@ describe('projects RPC profile forwarding', () => { const gateway = { connectionState: 'open', request } activeGateway.mockReturnValue(gateway as never) gatewayAtom.set(gateway as never) + $profiles.set([{ is_default: true, name: 'default' } as never, { is_default: false, name: 'work' } as never]) setShowAllProfiles(true) await refreshProjects() @@ -208,6 +210,32 @@ describe('projects RPC profile forwarding', () => { expect(request).not.toHaveBeenCalled() setShowAllProfiles(false) }) + + it('uses the active profile when a persisted all-profiles preference is hidden for one profile', async () => { + const project = { id: 'p_123', label: 'P', path: null, repos: [], sessionCount: 0 } as SidebarProjectTree + + const request = vi.fn(async (method: string) => + method === 'projects.project_sessions' ? { project } : { active_id: null, projects: [], scoped_session_ids: [] } + ) + + const gateway = { connectionState: 'open', request } + + activeGateway.mockReturnValue(gateway as never) + gatewayAtom.set(gateway as never) + setShowAllProfiles(true) + + await refreshProjects() + await refreshProjectTree() + await expect(fetchProjectSessions('p_123')).resolves.toBe(project) + + expect(request).toHaveBeenNthCalledWith(1, 'projects.list', { profile: 'default' }) + expect(request).toHaveBeenNthCalledWith(2, 'projects.tree', { preview_limit: 3, profile: 'default' }) + expect(request).toHaveBeenNthCalledWith(3, 'projects.project_sessions', { + profile: 'default', + project_id: 'p_123' + }) + setShowAllProfiles(false) + }) }) describe('resolveNewSessionCwd', () => { @@ -425,6 +453,7 @@ describe('createProject', () => { $projects.set([]) $projectTree.set([]) $activeGatewayProfile.set('default') + $profiles.set([{ is_default: true, name: 'default' } as never, { is_default: false, name: 'work' } as never]) setShowAllProfiles(false) }) diff --git a/apps/desktop/src/store/projects.ts b/apps/desktop/src/store/projects.ts index 3d9a5cd396..d3f4145b8b 100644 --- a/apps/desktop/src/store/projects.ts +++ b/apps/desktop/src/store/projects.ts @@ -29,6 +29,7 @@ import { $sidebarShowAllSessions, setSidebarAgentsGrouped } from '@/store/layout import { notify } from '@/store/notifications' import { $activeGatewayProfile, + $profiles, $profileScope, ALL_PROFILES, normalizeProfileKey, @@ -304,10 +305,14 @@ async function gatewayRequest(method: string, params: Record return gateway.request(method, params) } +function viewingAllProfiles(): boolean { + return $profileScope.get() === ALL_PROFILES && $profiles.get().length > 1 +} + export function projectProfile(): null | string { const profile = normalizeProfileKey($activeGatewayProfile.get()) - return $profileScope.get() === ALL_PROFILES || profile === ALL_PROFILES ? null : profile + return viewingAllProfiles() || profile === ALL_PROFILES ? null : profile } // All profiles filters the sidebar. Writes still belong to the live gateway profile. @@ -561,7 +566,7 @@ async function refreshProjectTreeOn(context: ActiveProjectsContext): Promise { - if ($profileScope.get() === ALL_PROFILES) { + if (viewingAllProfiles()) { await refreshProjectTreeAcrossProfiles() return @@ -593,7 +598,7 @@ async function refreshProjectTreeAcrossProfiles(): Promise { // scope; the newer refresh owns the tree. if ( generation !== projectTreeRefreshGeneration || - $profileScope.get() !== ALL_PROFILES || + !viewingAllProfiles() || activeGatewayConnectionId() !== owner.connectionId ) { return