From 88d04a6267ed4944b9b582bb4e3d75e2d8ef2b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Fri, 12 Jun 2026 16:43:56 +0800 Subject: [PATCH] fix(desktop): include more sidebar sessions (#759) Increase the desktop session list fetch window so noisy observer sessions are less likely to hide real user sessions from the sidebar. Tested: cd desktop && bun run test -- src/stores/sessionStore.test.ts src/components/layout/Sidebar.test.tsx Tested: bun run check:desktop Confidence: high Scope-risk: narrow --- desktop/src/stores/sessionStore.test.ts | 25 +++++++++++++ desktop/src/stores/sessionStore.ts | 47 ++++++++++++++++++------- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/desktop/src/stores/sessionStore.test.ts b/desktop/src/stores/sessionStore.test.ts index 0f779826..51e7d340 100644 --- a/desktop/src/stores/sessionStore.test.ts +++ b/desktop/src/stores/sessionStore.test.ts @@ -21,6 +21,20 @@ import { useTabStore } from './tabStore' const initialState = useSessionStore.getState() +function makeSession(id: string, modifiedAt: string, title = id) { + return { + id, + title, + createdAt: modifiedAt, + modifiedAt, + messageCount: 1, + projectPath: '/workspace/project', + projectRoot: '/workspace/project', + workDir: '/workspace/project', + workDirExists: true, + } +} + function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)) } @@ -136,6 +150,17 @@ describe('sessionStore', () => { expect(useTabStore.getState().tabs[0]?.title).toBe('使用bash写一个shell,随便写点什么东西') }) + it('requests a large default session page for noisy history directories', async () => { + listMock.mockResolvedValue({ + sessions: [makeSession('session-newest', '2026-05-07T00:00:03.000Z')], + total: 474, + }) + + await useSessionStore.getState().fetchSessions() + + expect(listMock).toHaveBeenCalledWith({ limit: 400 }) + }) + it('ignores stale session list responses when a newer refresh finishes first', async () => { const slow = createDeferred<{ sessions: Array<{ diff --git a/desktop/src/stores/sessionStore.ts b/desktop/src/stores/sessionStore.ts index 4f81925c..ceeaaf82 100644 --- a/desktop/src/stores/sessionStore.ts +++ b/desktop/src/stores/sessionStore.ts @@ -11,6 +11,8 @@ import type { SessionListItem } from '../types/session' import type { PermissionMode } from '../types/settings' import { isPlaceholderSessionTitle } from '../lib/sessionTitle' +const SESSION_LIST_LIMIT = 400 + type CreateSessionOptions = { repository?: CreateSessionRepositoryOptions permissionMode?: PermissionMode @@ -61,22 +63,11 @@ export const useSessionStore = create((set, get) => ({ const requestId = ++fetchSessionsRequestId set({ isLoading: true, error: null }) try { - const { sessions: raw } = await sessionsApi.list({ project, limit: 100 }) + const { sessions: raw } = await sessionsApi.list(buildSessionListParams(project)) if (requestId !== fetchSessionsRequestId) return let syncedSessions: SessionListItem[] = [] set((state) => { - const currentById = new Map(state.sessions.map((session) => [session.id, session])) - // Deduplicate by session ID - keep the most recently modified entry. - const byId = new Map() - for (const s of raw) { - const current = currentById.get(s.id) - const candidate = preserveLocalTitle(current, s) - const existing = byId.get(s.id) - if (!existing || new Date(candidate.modifiedAt) > new Date(existing.modifiedAt)) { - byId.set(s.id, candidate) - } - } - const sessions = [...byId.values()] + const sessions = mergeSessionList(raw, state.sessions) syncedSessions = sessions return { sessions, isLoading: false } }) @@ -237,6 +228,36 @@ function removeIdsFromSet(selected: Set, ids: string[]): Set { return next } +function buildSessionListParams(project: string | undefined) { + return project + ? { project, limit: SESSION_LIST_LIMIT } + : { limit: SESSION_LIST_LIMIT } +} + +function mergeSessionList( + incoming: SessionListItem[], + currentForTitle: SessionListItem[], +): SessionListItem[] { + const currentById = new Map(currentForTitle.map((session) => [session.id, session])) + const byId = new Map() + + for (const item of incoming) { + const current = currentById.get(item.id) + const candidate = preserveLocalTitle(current, item) + const existing = byId.get(candidate.id) + if (!existing || sessionModifiedTime(candidate) > sessionModifiedTime(existing)) { + byId.set(candidate.id, candidate) + } + } + + return [...byId.values()].sort((a, b) => sessionModifiedTime(b) - sessionModifiedTime(a)) +} + +function sessionModifiedTime(session: SessionListItem): number { + const timestamp = new Date(session.modifiedAt).getTime() + return Number.isFinite(timestamp) ? timestamp : 0 +} + function preserveLocalTitle( current: SessionListItem | undefined, incoming: SessionListItem,