authorgravatar for 206951365+cursor[bot]@users.noreply.github.comcursor[bot] <206951365+cursor[bot]@users.noreply.github.com> 2026-05-11 10:07:50+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-05-11 10:07:50+00:00
log447236d51f4d6835482f6707d627dbc8a39eb553
treedbc06768f653be34829b8590f21ae70c1fe7a1ea
parent5165b8c333c0da8566a93f98011672ebd1dff9bb
signature Signed by PGP key B5690EEEBB952194

Stabilize project grouping settings selector (#2629)

Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com> Co-authored-by: Julius Marminge <julius0216@outlook.com>

5 files changed, 24 insertions(+), 22 deletions(-)

apps/web/src/components/ChatView.tsx+5-5
......@@ -119,7 +119,10 @@ import { getProviderModelCapabilities, resolveSelectableProvider } from "../prov
119119import { useSettings } from "../hooks/useSettings";
120120import { resolveAppModelSelectionForInstance } from "../modelSelection";
121121import { isTerminalFocused } from "../lib/terminalFocus";
122import { deriveLogicalProjectKeyFromSettings } from "../logicalProject";
122import {
123 deriveLogicalProjectKeyFromSettings,
124 selectProjectGroupingSettings,
125} from "../logicalProject";
123126import {
124127 reconnectSavedEnvironment,
125128 useSavedEnvironmentRegistryStore,
......@@ -942,10 +945,7 @@ export default function ChatView(props: ChatViewProps) {
942945 },
943946 [],
944947 );
945 const projectGroupingSettings = useSettings((settings) => ({
946 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
947 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
948 }));
948 const projectGroupingSettings = useSettings(selectProjectGroupingSettings);
949949 const logicalProjectEnvironments = useMemo(() => {
950950 if (!activeProject) return [];
951951 const logicalKey = deriveLogicalProjectKeyFromSettings(activeProject, projectGroupingSettings);
apps/web/src/components/Sidebar.tsx+3-8
......@@ -183,6 +183,7 @@ import {
183183 derivePhysicalProjectKey,
184184 deriveProjectGroupingOverrideKey,
185185 getProjectOrderKey,
186 selectProjectGroupingSettings,
186187} from "../logicalProject";
187188import {
188189 useSavedEnvironmentRegistryStore,
......@@ -940,10 +941,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
940941 const defaultThreadEnvMode = useSettings<ThreadEnvMode>(
941942 (settings) => settings.defaultThreadEnvMode,
942943 );
943 const projectGroupingSettings = useSettings((settings) => ({
944 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
945 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
946 }));
944 const projectGroupingSettings = useSettings(selectProjectGroupingSettings);
947945 const { updateSettings } = useUpdateSettings();
948946 const sidebarThreadPreviewCount = useSettings<SidebarThreadPreviewCount>(
949947 (settings) => settings.sidebarThreadPreviewCount,
......@@ -2800,10 +2798,7 @@ export default function Sidebar() {
28002798 const sidebarThreadSortOrder = useSettings((s) => s.sidebarThreadSortOrder);
28012799 const sidebarProjectSortOrder = useSettings((s) => s.sidebarProjectSortOrder);
28022800 const sidebarProjectGroupingMode = useSettings((s) => s.sidebarProjectGroupingMode);
2803 const projectGroupingSettings = useSettings((settings) => ({
2804 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
2805 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
2806 }));
2801 const projectGroupingSettings = useSettings(selectProjectGroupingSettings);
28072802 const sidebarThreadPreviewCount = useSettings((s) => s.sidebarThreadPreviewCount);
28082803 const { updateSettings } = useUpdateSettings();
28092804 const { handleNewThread } = useNewThreadHandler();
apps/web/src/hooks/useHandleNewThread.ts+6-5
......@@ -10,7 +10,11 @@ import {
1010} from "../composerDraftStore";
1111import { newDraftId, newThreadId } from "../lib/utils";
1212import { orderItemsByPreferredIds } from "../components/Sidebar.logic";
13import { deriveLogicalProjectKeyFromSettings, getProjectOrderKey } from "../logicalProject";
13import {
14 deriveLogicalProjectKeyFromSettings,
15 getProjectOrderKey,
16 selectProjectGroupingSettings,
17} from "../logicalProject";
1418import { selectProjectsAcrossEnvironments, useStore } from "../store";
1519import { createThreadSelectorByRef } from "../storeSelectors";
1620import { resolveThreadRouteTarget } from "../threadRoutes";
......@@ -19,10 +23,7 @@ import { useSettings } from "./useSettings";
1923
2024function useNewThreadState() {
2125 const projects = useStore(useShallow((store) => selectProjectsAcrossEnvironments(store)));
22 const projectGroupingSettings = useSettings((settings) => ({
23 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
24 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
25 }));
26 const projectGroupingSettings = useSettings(selectProjectGroupingSettings);
2627 const router = useRouter();
2728 const getCurrentRouteTarget = useCallback(() => {
2829 const currentRouteParams = router.state.matches[router.state.matches.length - 1]?.params ?? {};
apps/web/src/logicalProject.ts+8
......@@ -1,5 +1,6 @@
11import { scopedProjectKey, scopeProjectRef } from "@t3tools/client-runtime";
22import type { ScopedProjectRef, SidebarProjectGroupingMode } from "@t3tools/contracts";
3import type { UnifiedSettings } from "@t3tools/contracts/settings";
34import { normalizeProjectPathForComparison } from "./lib/projectPaths";
45import type { Project } from "./types";
56
......@@ -10,6 +11,13 @@ export interface ProjectGroupingSettings {
1011
1112export type ProjectGroupingMode = SidebarProjectGroupingMode;
1213
14export function selectProjectGroupingSettings(settings: UnifiedSettings): ProjectGroupingSettings {
15 return {
16 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
17 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
18 };
19}
20
1321function uniqueNonEmptyValues(values: ReadonlyArray<string | null | undefined>): string[] {
1422 const seen = new Set<string>();
1523 const unique: string[] = [];
apps/web/src/routes/__root.tsx+2-4
......@@ -33,6 +33,7 @@ import { useSettings } from "../hooks/useSettings";
3333import {
3434 deriveLogicalProjectKeyFromSettings,
3535 derivePhysicalProjectKeyFromPath,
36 selectProjectGroupingSettings,
3637} from "../logicalProject";
3738import {
3839 getServerConfigUpdatedNotification,
......@@ -281,10 +282,7 @@ function EventRouter() {
281282 const setActiveEnvironmentId = useStore((store) => store.setActiveEnvironmentId);
282283 const navigate = useNavigate();
283284 const pathname = useLocation({ select: (loc) => loc.pathname });
284 const projectGroupingSettings = useSettings((settings) => ({
285 sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
286 sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
287 }));
285 const projectGroupingSettings = useSettings(selectProjectGroupingSettings);
288286 const readPathname = useEffectEvent(() => pathname);
289287 const handledBootstrapThreadIdRef = useRef<string | null>(null);
290288 const seenServerConfigUpdateIdRef = useRef(getServerConfigUpdatedNotification()?.id ?? 0);