Skip to content

[feat] Show the checked-out branch on the "local" workspace row in the v2 sidebar #6520

Open

Description

@thomas-seres

What problem are you trying to solve?

In the v2 sidebar, every project's main workspace row reads exactly local. With more than one project in the sidebar the rows are indistinguishable, and the one thing that actually differs between them — the branch each checkout is sitting on — is invisible.

Right now I have two projects open: one checkout is on main, the other on feat/app-platform. Both rows say local, and nothing on either row says which is which. Coming back to a checkout I left on a feature branch the day before, the sidebar gives me no signal at all.

v1 showed it: WorkspaceListItem computed showBranchSubtitle = isBranchWorkspace || … and rendered the branch in muted mono under the name. The v2 row dropped that line.

The data is already one property away from the JSX — decorateSidebarWorkspace() puts branch: workspace.branch on the decorated model, but the row renders {name || branch} and, for a main workspace, getV2WorkspaceDisplayName() always returns "local", so branch is never reached:

  • apps/desktop/src/renderer/utils/getV2WorkspaceDisplayName/getV2WorkspaceDisplayName.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx#L297
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/useDashboardSidebarData/buildDashboardSidebarProjects.ts

Proposed solution

Render the branch as a secondary line under local, the way v1 did.

Not as the name: the docstring on getV2WorkspaceDisplayName is right that a main workspace's stored name tracks the checked-out branch rather than a user-chosen label, so it shouldn't pose as one — keeping local as the title and the branch as a subtitle preserves that distinction. The row already has a second line for the agents / ports chips, so the branch could sit there in the same muted mono the v1 subtitle used.

Two lighter variants if a permanent line is too much:

  • show it only when the checkout is not on the repository's default branch — that's the case where not knowing actually costs something;
  • ship it behind an Experimental toggle, next to Workspace agents and Inline workspace ports.

Either way it needs no new query — branch is already on the decorated workspace object.

Workarounds you've tried

  • Hover card — it does show BRANCH, and it's good, but one row at a time and only on hover. It answers "what is this workspace?", not "which of my checkouts is on a feature branch?".
  • ⌘K lists workspaces with their branch, the header chip on the right shows the active one, and right-click → Copy Branch Name works.
  • Renaming the row: not possible by design (onRename: isMainWorkspace ? void 0 : startRename, and the stored name is ignored for main workspaces).
  • Moving the row into a section I could name myself: the Create section / Move to section submenu is gated on !isLocalMainWorkspace.

Superset 1.22.0, macOS 26 (arm64).

Activity

  1. github-actions commented on Aug 16, 2026

    @github-actions
    Contributor

    This is a feature request rather than a bug, and the code confirms every claim in it. Here's what I found.


    Summary

    The v2 sidebar renders every local main workspace row with the literal text local, so with two or more projects open the rows carry no information about which branch each checkout is sitting on. The branch data is present on the decorated model the row already receives — it's just never rendered, because the row prints {name || branch} and name is always the non-empty string "local" for main workspaces. v1's WorkspaceListItem rendered the branch as a muted mono subtitle; the v2 row has no equivalent.

    Affected code

    The row title, which short-circuits on a non-empty name:

    • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx:297 — {name || branch} inside the title <span>; branch is destructured at line 98 and otherwise unused in the expanded row.

    The name source:

    • apps/desktop/src/renderer/utils/getV2WorkspaceDisplayName/getV2WorkspaceDisplayName.ts:15 — if (workspace.type === "main") return "local";. Locked in by getV2WorkspaceDisplayName.test.ts:5-13 ("always displays main workspaces as 'local'").

    The data plumbing, which already carries the branch through:

    • .../hooks/useDashboardSidebarData/buildDashboardSidebarProjects.ts:91-92 — decorateSidebarWorkspace sets name: getV2WorkspaceDisplayName(workspace) and branch: workspace.branch side by side. Same decorator feeds project groups, the Pinned section (buildDashboardSidebarPinnedWorkspaces), and Sessions, so anything added to the expanded row shows up in all three.

    v1, for reference:

    • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx:259 — const showBranchSubtitle = isBranchWorkspace || (!!name && name !== branch); and :436-442, rendering <span className="text-muted-foreground/60 truncate font-mono leading-tight">{branch}</span>. Note lines 309 and 319: enabling the subtitle also switched the row's vertical padding and icon alignment.

    The workarounds are gated exactly as described: onRename={isMainWorkspace ? undefined : startRename} (DashboardSidebarWorkspaceItem.tsx:252 and :344), the section submenu behind {!isPinned && !isLocalMainWorkspace && projectId !== null && (…)} (DashboardSidebarWorkspaceContextMenu.tsx:193), and the hover card's Branch block at DashboardSidebarWorkspaceHoverCardContent.tsx:64-80.

    Two things worth confirming before implementing

    The branch value is live, not stale. I checked this because a subtitle sourced from a stale column would be worse than nothing. packages/host-service/src/runtime/pull-requests/pull-requests.ts:529-582 (syncWorkspaceRow) reads the real HEAD from the worktree on GitWatcher events and writes workspaces.branch back; it skips only session (projectId === null) and archived rows, so local main workspaces are covered. A branch subtitle should track checkouts made in a terminal.

    The "only when off the default branch" variant does need new plumbing, contrary to the "needs no new query" note — that applies to the always-on variant only. The sidebar's project identity comes from the host fan-out project.list, and neither HostProjectRow (apps/desktop/src/renderer/hooks/host-projects/useHostProjects/useHostProjects.utils.ts:6-20) nor the procedure that fills it (packages/host-service/src/trpc/router/project/project.ts:65-83) returns a default branch, even though projects.defaultBranch exists in packages/local-db/src/schema/schema.ts:46. SidebarProjectInput would need the field too (buildDashboardSidebarProjects.ts:14-25). Resolving it live per project via resolveDefaultBranchName (packages/host-service/src/runtime/git/refs.ts:207) is the other option. The always-on and Experimental-toggle variants need no new data.

    Fix direction

    The narrowest change that matches existing precedent:

    1. Add a sibling helper next to getV2WorkspaceDisplayName — e.g. getV2WorkspaceBranchSubtitle({ type, name, branch }) returning branch when type === "main", null when type === "session", and for worktrees branch only when it differs from the display name. That last guard matters: getV2WorkspaceDisplayName already falls back to branch for unnamed worktrees, so an unguarded subtitle would print the same string twice. Both other v2 surfaces already guard exactly this way — V2WorkspaceTitle.tsx:17 (rawBranch === name ? null : rawBranch) and V2WorkspaceRow.tsx:219-221 (case-insensitive compare, plus a type !== "session" check).
    2. Render it under the title in DashboardSidebarExpandedWorkspaceRow.tsx, styled like v1's (truncate font-mono text-[11px] leading-tight text-muted-foreground/60).

    Two layout details to watch in step 2. The title cell is a grid-cols-[minmax(0,1fr)_auto] items-center grid (line 277) whose right-hand cell is pinned col-start-2 row-start-1 (line 302) — a second line in column 1 will want that cell to span both rows or self-center, or the diff stats and hover buttons will ride up against the title. And the chips strip below (DashboardSidebarWorkspaceChips) early-returns null when there are no agents/ports (line 37-39) and sits outside the clickable row div (line 390-396), so reusing it as the branch line means both relaxing that early return and accepting its fixed h-7; putting the subtitle inside the title column keeps the two independent. Its pl-[42px] / pl-[50px] values (line 50) are the alignment reference if you want the branch to line up with the title.

    Reproduction

    1. Add two projects to the v2 sidebar, both with their local main workspace row visible.
    2. In a terminal, git checkout -b feat/app-platform in one of the two checkouts; leave the other on main.
    3. Both rows read local, with the branch visible only on hover, in ⌘K, or in the top-bar chip for the active workspace.

    For a regression test, the renderer has essentially no component-test infrastructure (one @testing-library/react file, Redirect.test.tsx), so the idiomatic coverage is a pure-function test — extend getV2WorkspaceDisplayName.test.ts for the new helper:

    expect(getV2WorkspaceBranchSubtitle({ type: "main", name: "whatever", branch: "feat/app-platform" }))
      .toBe("feat/app-platform");
    // no echo of the title for unnamed worktrees
    expect(getV2WorkspaceBranchSubtitle({ type: "worktree", name: "", branch: "fix-login-bug" }))
      .toBeNull();

    buildDashboardSidebarProjects.test.ts is the place for a companion assertion if the subtitle ends up precomputed on the decorated model instead of derived in the row.

  2. svenpeeters commented on Oct 27, 2026

    @svenpeeters
    Contributor

    Most of the original problem ("every main row reads local") went away with #7485: local workspaces now have names that users pick. But a named local workspace still does not show which branch the shared checkout is on.

    I'd like to pick this up. My approach: show the branch as a muted mono suffix after the name on type: "local" rows. The suffix is hidden when the branch is the same as the name, and the row stays on one line. Worktree and session rows do not change. I have a working version with before/after screenshots and can open a PR. Does this approach work for you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions