((set) => ({
toggleProject: (projectId) => set((state) => toggleProject(state, projectId)),
setProjectExpanded: (projectId, expanded) =>
set((state) => setProjectExpanded(state, projectId, expanded)),
+ setAllProjectsExpanded: (expanded) => set((state) => setAllProjectsExpanded(state, expanded)),
reorderProjects: (draggedProjectId, targetProjectId) =>
set((state) => reorderProjects(state, draggedProjectId, targetProjectId)),
}));
diff --git a/bun.lock b/bun.lock
index af243cf4eb0..c56f9da4b81 100644
--- a/bun.lock
+++ b/bun.lock
@@ -169,6 +169,7 @@
},
},
"trustedDependencies": [
+ "electron",
"node-pty",
],
"overrides": {
diff --git a/package.json b/package.json
index a26a359c037..7f4011fce04 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
]
},
"trustedDependencies": [
+ "electron",
"node-pty"
]
}
From 31b98f4658ebe2a86cda2b44bef600f0f4cb48d8 Mon Sep 17 00:00:00 2001
From: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com>
Date: Fri, 3 Apr 2026 17:38:15 -0400
Subject: [PATCH 2/2] Scope collapse-all to known projects
- Clear expansion state for every sidebar project
- Preserve existing project order and add coverage for missing state
---
apps/web/src/components/Sidebar.tsx | 7 +++++--
apps/web/src/uiStateStore.test.ts | 18 +++++++++++++++++-
apps/web/src/uiStateStore.ts | 28 ++++++++++++++++++++++------
bun.lock | 1 -
package.json | 1 -
5 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
index 1043a841a02..a1932feb258 100644
--- a/apps/web/src/components/Sidebar.tsx
+++ b/apps/web/src/components/Sidebar.tsx
@@ -1973,8 +1973,11 @@ export default function Sidebar() {
const handleCollapseAllProjects = useCallback(() => {
setExpandedThreadListsByProject((current) => (current.size === 0 ? current : new Set()));
- setAllProjectsExpanded(false);
- }, [setAllProjectsExpanded]);
+ setAllProjectsExpanded(
+ sidebarProjects.map((project) => project.id),
+ false,
+ );
+ }, [setAllProjectsExpanded, sidebarProjects]);
const wordmark = (
diff --git a/apps/web/src/uiStateStore.test.ts b/apps/web/src/uiStateStore.test.ts
index e1a57b3b722..719e685b585 100644
--- a/apps/web/src/uiStateStore.test.ts
+++ b/apps/web/src/uiStateStore.test.ts
@@ -189,7 +189,23 @@ describe("uiStateStore pure functions", () => {
projectOrder: [project2, project1],
});
- const next = setAllProjectsExpanded(initialState, false);
+ const next = setAllProjectsExpanded(initialState, [project1, project2], false);
+
+ expect(next.projectExpandedById).toEqual({
+ [project1]: false,
+ [project2]: false,
+ });
+ expect(next.projectOrder).toEqual([project2, project1]);
+ });
+
+ it("setAllProjectsExpanded seeds missing project expansion state for known projects", () => {
+ const project1 = ProjectId.makeUnsafe("project-1");
+ const project2 = ProjectId.makeUnsafe("project-2");
+ const initialState = makeUiState({
+ projectOrder: [project2, project1],
+ });
+
+ const next = setAllProjectsExpanded(initialState, [project1, project2], false);
expect(next.projectExpandedById).toEqual({
[project1]: false,
diff --git a/apps/web/src/uiStateStore.ts b/apps/web/src/uiStateStore.ts
index 4f60488a554..ac2cf11bcf5 100644
--- a/apps/web/src/uiStateStore.ts
+++ b/apps/web/src/uiStateStore.ts
@@ -356,10 +356,25 @@ export function setProjectExpanded(
};
}
-export function setAllProjectsExpanded(state: UiState, expanded: boolean): UiState {
- const nextExpandedById = Object.fromEntries(
- Object.keys(state.projectExpandedById).map((projectId) => [projectId, expanded]),
- );
+export function setAllProjectsExpanded(
+ state: UiState,
+ projectIds: readonly ProjectId[],
+ expanded: boolean,
+): UiState {
+ const nextExpandedById = { ...state.projectExpandedById };
+ let changed = false;
+
+ for (const projectId of projectIds) {
+ if (nextExpandedById[projectId] !== expanded) {
+ nextExpandedById[projectId] = expanded;
+ changed = true;
+ }
+ }
+
+ if (!changed) {
+ return state;
+ }
+
if (recordsEqual(state.projectExpandedById, nextExpandedById)) {
return state;
}
@@ -402,7 +417,7 @@ interface UiStateStore extends UiState {
clearThreadUi: (threadId: ThreadId) => void;
toggleProject: (projectId: ProjectId) => void;
setProjectExpanded: (projectId: ProjectId, expanded: boolean) => void;
- setAllProjectsExpanded: (expanded: boolean) => void;
+ setAllProjectsExpanded: (projectIds: readonly ProjectId[], expanded: boolean) => void;
reorderProjects: (draggedProjectId: ProjectId, targetProjectId: ProjectId) => void;
}
@@ -418,7 +433,8 @@ export const useUiStateStore = create((set) => ({
toggleProject: (projectId) => set((state) => toggleProject(state, projectId)),
setProjectExpanded: (projectId, expanded) =>
set((state) => setProjectExpanded(state, projectId, expanded)),
- setAllProjectsExpanded: (expanded) => set((state) => setAllProjectsExpanded(state, expanded)),
+ setAllProjectsExpanded: (projectIds, expanded) =>
+ set((state) => setAllProjectsExpanded(state, projectIds, expanded)),
reorderProjects: (draggedProjectId, targetProjectId) =>
set((state) => reorderProjects(state, draggedProjectId, targetProjectId)),
}));
diff --git a/bun.lock b/bun.lock
index c56f9da4b81..af243cf4eb0 100644
--- a/bun.lock
+++ b/bun.lock
@@ -169,7 +169,6 @@
},
},
"trustedDependencies": [
- "electron",
"node-pty",
],
"overrides": {
diff --git a/package.json b/package.json
index 7f4011fce04..a26a359c037 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,6 @@
]
},
"trustedDependencies": [
- "electron",
"node-pty"
]
}