Repository navigation
feat(desktop): reorganize sidebar and workspace header chrome - #5824
Conversation
- Presets bar: remove bottom border, taller bar with more chip spacing - Move run button from presets bar into the tab bar trailing slot (drop TopBar portal fallback) - Move open-in button from TopBar into the PR action header; match chevron sizes - Files tab: search button next to New File, drop Explorer title - Dashboard sidebar: New Workspace first, hide recently-viewed button, help menu into org dropdown - Hide workspace diff stats unless active; remove section/project workspace counts - Inverted tab styling in shared panes tab bar
…tton - New collapsible 'Workspaces' header above the project list (hover chevron, persisted collapse via sidebar-workspaces-collapse store) - Add repository dropdown moves from the New Workspace row into that header - Remove expanded sidebar header bottom border; indent project rows pl-3->pl-5 - Section header chevron always visible; tab bar filler border tweak - Add plans/20260721-sidebar-header-reorg-notes.md revert guide for the branch
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThe PR adds command-palette project actions, reorganizes dashboard sidebar controls, moves v2 workspace controls into pane tab bars, updates sidebar and changes-panel interactions, and restyles shared pane and tab components. ChangesUI reorganization
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
Greptile SummaryThis PR reorganizes the desktop v2 chrome across the dashboard sidebar, workspace tab bar, and right sidebar. The sidebar footer now hosts the org/user dropdown and an icon-only Settings gear; a new Search button opens the command palette; the Workspaces list gains a collapsible header with an add-repo dropdown; and the workspace tab bar doubles as the Electron drag region and hosts the right-sidebar toggle and non-Mac window controls (replacing the TopBar on the expanded-sidebar v2 route).
Confidence Score: 4/5The reorganization is well-scoped with appropriate comments and conditional guards throughout. The main risks are in edge cases: FolderImportMount remounting and onClick/onSelect inconsistency in HelpSubMenu, neither of which affects the happy path. The core chrome changes (tab bar as drag region, TopBar hiding, collapsible Workspaces header, org dropdown relocation) are straightforward and well-guarded. The FolderImportMount tick counter initialised at 0 rather than the current store value is a latent replay issue under StrictMode or unexpected remounts. The HelpSubMenu onClick/onSelect inconsistency is a minor keyboard-navigation concern. No correctness regressions were found in the layout logic or Electron plumbing. FolderImportMount.tsx (stale-tick replay on remount) and HelpSubMenu.tsx (onClick vs onSelect for keyboard nav).
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx | Sidebar restructured: org dropdown moved to footer, help menu removed, Workspaces section collapsed behind DashboardSidebarWorkspacesHeader; OverflowFadeContainer added for the project list. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx | Adds New Workspace and Search buttons to both collapsed/expanded header; removes OrganizationDropdown and add-repo row from expanded state; SHOW_WORKSPACES_TAB flag gates hidden Workspaces tab. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/DashboardSidebarWorkspacesHeader.tsx | New collapsible Workspaces section header with embedded add-repo dropdown; directly mounts useFolderFirstImport for sidebar interactions (separate from FolderImportMount used by the command palette). |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx | Settings item removed; HelpSubMenu and SubmitPromptDialog subcomponents integrated; wrapped in a fragment to co-render the dialog alongside the dropdown. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/components/HelpSubMenu/HelpSubMenu.tsx | New component migrated from the old DashboardSidebarHelpMenu; uses onClick for most items instead of the onSelect pattern used elsewhere in the dropdown. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx | Run button moved from presets bar / TopBar portal into the tab bar trailing slot; RightSidebarToggle and WindowControls added to the same slot; isMac defaults to true during query load to suppress flash of non-Mac controls. |
| packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx | TabBar gains drag/no-drag Electron classes; border-b removed to support inverted active-tab styling; tabs track and button clusters opt out of the drag region. |
| packages/panes/src/react/components/Workspace/components/TabBar/components/TabItem/TabItem.tsx | Inverted tab scheme: inactive tabs carry shaded block, active tab shows left/right/top border with transparent bottom; every state keeps a 1px border to prevent layout shift. |
| apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx | New bridge component that watches useFolderImportIntent tick and calls useFolderFirstImport.start(); the lastTickRef reset on remount could replay a stale intent in React StrictMode or after unexpected remounts. |
| apps/desktop/src/renderer/stores/folder-import-intent.ts | New in-memory Zustand store bridging non-React palette commands to the React hook-based folder import flow via a monotonic tick counter. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx | TopBar conditionally hidden on the v2 workspace route when the sidebar is expanded (hideTopBar flag), so the tab bar can serve as the drag region and traffic-light inset. |
Comments Outside Diff (1)
-
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx, line 274-276 (link)Dead code behind a compile-time
falseconstantSHOW_WORKSPACES_TAB = falsegates two JSX branches that are dead in every build. Tree-shaking cannot remove them because they're valid JSX, and the constant beingfalsemeans the conditional is always falsy. If the intent is to keep this easy to re-enable, a feature-flag or a comment-block would avoid shipping unreachable render paths. This appears in the collapsed path as well (around line 349).Prompt To Fix With AI
This is a comment left during a code review. Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx Line: 274-276 Comment: **Dead code behind a compile-time `false` constant** `SHOW_WORKSPACES_TAB = false` gates two JSX branches that are dead in every build. Tree-shaking cannot remove them because they're valid JSX, and the constant being `false` means the conditional is always falsy. If the intent is to keep this easy to re-enable, a feature-flag or a comment-block would avoid shipping unreachable render paths. This appears in the collapsed path as well (around line 349). How can I resolve this? If you propose a fix, please make it concise.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx:24-33
**Stale tick replays on remount**
`lastTickRef` is a local ref that resets to `0` whenever the component mounts. If `tick` in the store is already `> 0` at mount time (e.g., the user triggered a folder import earlier in the same session, then this subtree remounted due to a parent re-render or React StrictMode's double-invoke), the condition `tick !== lastTickRef.current` fires immediately and calls `folderImport.start()` again with no user intent. Consider initialising `lastTickRef` to the current store tick at mount rather than `0`.
### Issue 2 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx:274-276
**Dead code behind a compile-time `false` constant**
`SHOW_WORKSPACES_TAB = false` gates two JSX branches that are dead in every build. Tree-shaking cannot remove them because they're valid JSX, and the constant being `false` means the conditional is always falsy. If the intent is to keep this easy to re-enable, a feature-flag or a comment-block would avoid shipping unreachable render paths. This appears in the collapsed path as well (around line 349).
### Issue 3 of 3
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/components/HelpSubMenu/HelpSubMenu.tsx:43-91
**`onClick` vs `onSelect` in Radix menu items**
Most items in this component (Documentation, Keyboard Shortcuts, Report Issue, and the Contact Us sub-items) use `onClick` while the parent `OrganizationDropdown` uses `onSelect` throughout. `onSelect` is the Radix-idiomatic handler: it fires regardless of input device and allows calling `event.preventDefault()` to keep the menu open. `onClick` only fires for pointer events in the React model; keyboard users who press Space/Enter on a focused item may not get the handler called reliably across Radix versions. Replace `onClick` with `onSelect` to match the pattern used elsewhere in the dropdown.
Reviews (1): Last reviewed commit: "feat(desktop): merge top bar into pane t..." | Re-trigger Greptile
| const folderImportRef = useRef(folderImport); | ||
| folderImportRef.current = folderImport; | ||
| const lastTickRef = useRef(0); | ||
|
|
||
| useEffect(() => { | ||
| if (tick === lastTickRef.current) return; | ||
| lastTickRef.current = tick; | ||
| void folderImportRef.current.start().then((result) => { | ||
| if (result) { | ||
| toast.success("Project ready — open it from the sidebar."); |
There was a problem hiding this comment.
lastTickRef is a local ref that resets to 0 whenever the component mounts. If tick in the store is already > 0 at mount time (e.g., the user triggered a folder import earlier in the same session, then this subtree remounted due to a parent re-render or React StrictMode's double-invoke), the condition tick !== lastTickRef.current fires immediately and calls folderImport.start() again with no user intent. Consider initialising lastTickRef to the current store tick at mount rather than 0.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx
Line: 24-33
Comment:
**Stale tick replays on remount**
`lastTickRef` is a local ref that resets to `0` whenever the component mounts. If `tick` in the store is already `> 0` at mount time (e.g., the user triggered a folder import earlier in the same session, then this subtree remounted due to a parent re-render or React StrictMode's double-invoke), the condition `tick !== lastTickRef.current` fires immediately and calls `folderImport.start()` again with no user intent. Consider initialising `lastTickRef` to the current store tick at mount rather than `0`.
How can I resolve this? If you propose a fix, please make it concise.| <LuMegaphone className="h-4 w-4" /> | ||
| Submit a prompt | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem onClick={() => openExternal(COMPANY.DOCS_URL)}> | ||
| <HiOutlineBookOpen className="h-4 w-4" /> | ||
| Documentation | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem | ||
| onClick={() => navigate({ to: "/settings/keyboard" })} | ||
| > | ||
| <LuKeyboard className="h-4 w-4" /> | ||
| Keyboard Shortcuts | ||
| {shortcutsHotkey !== "Unassigned" && ( | ||
| <DropdownMenuShortcut>{shortcutsHotkey}</DropdownMenuShortcut> | ||
| )} | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem | ||
| onClick={() => openExternal(COMPANY.REPORT_ISSUE_URL)} | ||
| > | ||
| <IoBugOutline className="h-4 w-4" /> | ||
| Report Issue | ||
| </DropdownMenuItem> | ||
| <DropdownMenuSub> | ||
| <DropdownMenuSubTrigger> | ||
| <HiOutlineChatBubbleLeftRight className="h-4 w-4" /> | ||
| Contact Us | ||
| </DropdownMenuSubTrigger> | ||
| <DropdownMenuSubContent sideOffset={8} className="w-56"> | ||
| <DropdownMenuItem onClick={() => openExternal(COMPANY.GITHUB_URL)}> | ||
| <FaGithub className="h-4 w-4" /> | ||
| GitHub | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem onClick={() => openExternal(COMPANY.DISCORD_URL)}> | ||
| <FaDiscord className="h-4 w-4" /> | ||
| Discord | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem onClick={() => openExternal(COMPANY.X_URL)}> | ||
| <FaXTwitter className="h-4 w-4" />X | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem onClick={() => openExternal(COMPANY.MAIL_TO)}> | ||
| <HiOutlineEnvelope className="h-4 w-4" /> | ||
| Email Support | ||
| </DropdownMenuItem> | ||
| </DropdownMenuSubContent> | ||
| </DropdownMenuSub> | ||
| </DropdownMenuSubContent> | ||
| </DropdownMenuSub> | ||
| ); | ||
| } |
There was a problem hiding this comment.
onClick vs onSelect in Radix menu items
Most items in this component (Documentation, Keyboard Shortcuts, Report Issue, and the Contact Us sub-items) use onClick while the parent OrganizationDropdown uses onSelect throughout. onSelect is the Radix-idiomatic handler: it fires regardless of input device and allows calling event.preventDefault() to keep the menu open. onClick only fires for pointer events in the React model; keyboard users who press Space/Enter on a focused item may not get the handler called reliably across Radix versions. Replace onClick with onSelect to match the pattern used elsewhere in the dropdown.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/components/HelpSubMenu/HelpSubMenu.tsx
Line: 43-91
Comment:
**`onClick` vs `onSelect` in Radix menu items**
Most items in this component (Documentation, Keyboard Shortcuts, Report Issue, and the Contact Us sub-items) use `onClick` while the parent `OrganizationDropdown` uses `onSelect` throughout. `onSelect` is the Radix-idiomatic handler: it fires regardless of input device and allows calling `event.preventDefault()` to keep the menu open. `onClick` only fires for pointer events in the React model; keyboard users who press Space/Enter on a focused item may not get the handler called reliably across Radix versions. Replace `onClick` with `onSelect` to match the pattern used elsewhere in the dropdown.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…lder-import tick on mount
… F1 sidebar-reorg interplay The 2026-07-22 merge reverted upstream ParadevAI#5784 (the POSIX launcher-script pipeline), so W20 collapsed back to its original unified 7de1cb8 form — no win32 branch, shell threaded through buildAgentCommandString for all platforms. Rewrote W20 to match and left a re-introduction trigger in case ParadevAI#5784 relands. Noted ParadevAI#5824's sidebar-reorg interplay on F1 (diff stats now isActive-gated; the un-gated commits-to-pull badge stays).
… survives sidebar collapse Upstream ParadevAI#5824 moved the v2 Open-in button into the right sidebar's PR action header, and the OPEN_IN_APP hotkey was registered inside that button. Collapsing the right sidebar unmounts the button and silently tears the shortcut down, so Cmd/Ctrl+O only worked while the button was visible. Register the hotkey via a new useV2OpenInAppHotkey hook mounted in the always-present V2WorkspaceContent, and drop the registration from the button (its click handler and shortcut tooltip are unchanged).
…ne tab bar Upstream ParadevAI#5824 moved the v2 Open-in button out of the top chrome and into the right sidebar's PR action header, so it disappears whenever the sidebar is collapsed. Add a second instance to the pane tab bar's trailing cluster (next to the run button and right-sidebar toggle), which stays visible in every sidebar state. The sidebar copy is left in place.
Summary
Reorganizes the v2 desktop chrome across the dashboard sidebar, workspace tab bar, and right sidebar (two parallel sessions on this branch; full revert-oriented change log in
apps/desktop/plans/20260721-sidebar-header-reorg-notes.md).Dashboard sidebar
Workspace view
Right sidebar
Test Plan
Summary by cubic
Reorganizes desktop chrome to simplify navigation and save space: moves org/help/search into the dashboard sidebar, adds a collapsible “Workspaces” header with Add‑repo, merges the TopBar into the v2 workspace tab bar, and makes the PR header responsive. Adds an “Add project” section to the command palette and subtle edge fades in Files and Changes.
New Features
packages/panes; tab bar doubles as the window drag region.Refactors
Written for commit 010191b. Summary will update on new commits.
Summary by CodeRabbit