From f1ae9fcc909436641029e14315f45f53a975f437 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Wed, 6 May 2026 12:21:30 -0700 Subject: [PATCH] fix(desktop): place new v2 workspace at top of sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The revert of #4120 (#4135) restored a hardcoded `tabOrder: 0` in the v2 workspace.create path. Existing rows default to `tabOrder: 0` too, so new workspaces tied with them and ordering was non-deterministic. Restores the `getPrependTabOrder` helper from #4120 as a shared util in `dashboardSidebarLocal/`, and uses it from `useWorkspaceCreates` so new rows land strictly above every existing top-level item — matching what the pending-row injection in `useDashboardSidebarData` already assumes. --- .../useDashboardSidebarState.ts | 23 ++++--------------- .../dashboardSidebarLocal/index.ts | 1 + .../dashboardSidebarLocal/tabOrder.ts | 22 ++++++++++++++++++ .../workspace-creates/useWorkspaceCreates.ts | 22 ++++++++++++++++-- 4 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/tabOrder.ts diff --git a/apps/desktop/src/renderer/routes/_authenticated/hooks/useDashboardSidebarState/useDashboardSidebarState.ts b/apps/desktop/src/renderer/routes/_authenticated/hooks/useDashboardSidebarState/useDashboardSidebarState.ts index 108ea0afb1f..829d82a06ba 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/hooks/useDashboardSidebarState/useDashboardSidebarState.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/hooks/useDashboardSidebarState/useDashboardSidebarState.ts @@ -8,26 +8,13 @@ import { } from "renderer/routes/_authenticated/components/utils/paneLifecycleRows"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; import type { AppCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider/collections"; -import { isSidebarWorkspaceVisible } from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal"; +import { + getNextTabOrder, + getPrependTabOrder, + isSidebarWorkspaceVisible, +} from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal"; import { PROJECT_CUSTOM_COLORS } from "shared/constants/project-colors"; -function getNextTabOrder(items: Array<{ tabOrder: number }>): number { - const maxTabOrder = items.reduce( - (maxValue, item) => Math.max(maxValue, item.tabOrder), - 0, - ); - return maxTabOrder + 1; -} - -function getPrependTabOrder(items: Array<{ tabOrder: number }>): number { - if (items.length === 0) return 1; - const minTabOrder = items.reduce( - (minValue, item) => Math.min(minValue, item.tabOrder), - Number.POSITIVE_INFINITY, - ); - return minTabOrder - 1; -} - type ProjectTopLevelItem = { type: "workspace" | "section"; id: string; diff --git a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/index.ts b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/index.ts index 53b72976220..28138c82fdc 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/index.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/index.ts @@ -1,2 +1,3 @@ export * from "./schema"; export * from "./sidebarVisibility"; +export * from "./tabOrder"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/tabOrder.ts b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/tabOrder.ts new file mode 100644 index 00000000000..c68a579ffd7 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/tabOrder.ts @@ -0,0 +1,22 @@ +/** + * Lower tabOrder = appears earlier in the sidebar (queries sort ASC). + * Prepending therefore picks one less than the smallest existing tabOrder + * so the new item lands at the top regardless of whether existing items + * are positive (newly defaulted) or negative (after prior prepends). + */ +export function getPrependTabOrder(items: Array<{ tabOrder: number }>): number { + if (items.length === 0) return 1; + const minTabOrder = items.reduce( + (minValue, item) => Math.min(minValue, item.tabOrder), + Number.POSITIVE_INFINITY, + ); + return minTabOrder - 1; +} + +export function getNextTabOrder(items: Array<{ tabOrder: number }>): number { + const maxTabOrder = items.reduce( + (maxValue, item) => Math.max(maxValue, item.tabOrder), + 0, + ); + return maxTabOrder + 1; +} diff --git a/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts b/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts index 56b7cab247c..0cff02529e1 100644 --- a/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts +++ b/apps/desktop/src/renderer/stores/workspace-creates/useWorkspaceCreates.ts @@ -5,6 +5,10 @@ import { authClient } from "renderer/lib/auth-client"; import { getHostServiceClientByUrl } from "renderer/lib/host-service-client"; import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; +import { + getPrependTabOrder, + isSidebarWorkspaceVisible, +} from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal"; import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; import { appendLaunchesToPaneLayout } from "./appendLaunchesToPaneLayout"; import { @@ -82,12 +86,26 @@ export function useWorkspaceCreates(): UseWorkspaceCreatesApi { }, ); } else { + const projectId = result.workspace.projectId; + const topLevelItems = [ + ...Array.from(collections.v2WorkspaceLocalState.state.values()) + .filter( + (item) => + item.sidebarState.projectId === projectId && + item.sidebarState.sectionId === null && + isSidebarWorkspaceVisible(item), + ) + .map((item) => ({ tabOrder: item.sidebarState.tabOrder })), + ...Array.from(collections.v2SidebarSections.state.values()) + .filter((item) => item.projectId === projectId) + .map((item) => ({ tabOrder: item.tabOrder })), + ]; collections.v2WorkspaceLocalState.insert({ workspaceId: result.workspace.id, createdAt: new Date(), sidebarState: { - projectId: result.workspace.projectId, - tabOrder: 0, + projectId, + tabOrder: getPrependTabOrder(topLevelItems), sectionId: null, changesFilter: { kind: "all" }, activeTab: "changes",