feat(ui): redesign sidebar and homepage for cleaner session creation#501
feat(ui): redesign sidebar and homepage for cleaner session creation#501StanGirard wants to merge 12 commits intomainfrom
Conversation
Simplify the sidebar and homepage to feel more like Cursor's Agents tab: - Sidebar: prominent "New Session" button, flat session list, gear menu for navigation (replaces footer nav), narrower width (240px) - HomePage: smaller logo, no title text, folder/branch always visible, advanced options (backend, model, env) behind collapsible toggle - New SidebarMenu component for navigation dropdown - Store: add sidebarGroupByProject flag for opt-in project grouping Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR redesigns the sidebar and homepage for a cleaner "Cursor-like" session-creation experience: the sidebar footer nav is replaced with a gear-icon dropdown ( Two fixable issues:
All 3520 tests pass, typecheck is clean, gear button remains fully accessible on mobile, and the "Group by project" toggle is properly wired to Confidence Score: 4/5
Last reviewed commit: 37a832d |
| <button | ||
| ref={menuButtonRef} | ||
| onClick={() => setMenuOpen(!menuOpen)} | ||
| title="Menu" | ||
| aria-label="Navigation menu" | ||
| aria-expanded={menuOpen} | ||
| className="hidden md:flex w-9 h-9 shrink-0 rounded-lg items-center justify-center text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer border border-cc-border/40" | ||
| > | ||
| {/* Gear icon */} | ||
| <svg viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4"> | ||
| <path fillRule="evenodd" clipRule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.53 1.53 0 01-2.29.95c-1.35-.8-2.92.77-2.12 2.12.54.9.07 2.04-.95 2.29-1.56.38-1.56 2.6 0 2.98 1.02.25 1.49 1.39.95 2.29-.8 1.35.77 2.92 2.12 2.12.9-.54 2.04-.07 2.29.95.38 1.56 2.6 1.56 2.98 0 .25-1.02 1.39-1.49 2.29-.95 1.35.8 2.92-.77 2.12-2.12-.54-.9-.07-2.04.95-2.29 1.56-.38 1.56-2.6 0-2.98-1.02-.25-1.49-1.39-.95-2.29.8-1.35-.77-2.92-2.12-2.12-.9.54-2.04.07-2.29-.95zM10 13a3 3 0 100-6 3 3 0 000 6z" /> | ||
| </svg> | ||
| </button> |
There was a problem hiding this comment.
Navigation inaccessible on mobile
Both the "New Session" header button and the gear/navigation menu button use hidden md:flex, which hides them entirely at viewport widths below 768px. The old sidebar footer nav had no such guard — it rendered at all screen sizes. As a result, on any mobile device, users can no longer reach Settings, Environments, Prompts, Integrations, Runs, Agents, or the Terminal via the sidebar.
The mobile FAB at the bottom of the sidebar only creates a new session; it provides no path to navigation items.
No existing test exercises this path because the jsdom environment doesn't set window.innerWidth, so the regression is invisible to the current test suite.
A minimal fix is to add a mobile-visible trigger (e.g., a hamburger/gear button in the mobile header row, or include it alongside the close button) and ensure SidebarMenu can be opened on small screens:
{/* Mobile gear button — sits beside the close button */}
<button
ref={menuButtonRef}
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Navigation menu"
aria-expanded={menuOpen}
className="md:hidden w-8 h-8 rounded-lg flex items-center justify-center text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer"
>
{/* gear icon */}
</button>Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/components/Sidebar.tsx
Line: 350-362
Comment:
**Navigation inaccessible on mobile**
Both the "New Session" header button and the gear/navigation menu button use `hidden md:flex`, which hides them entirely at viewport widths below `768px`. The old sidebar footer nav had no such guard — it rendered at all screen sizes. As a result, on any mobile device, users can no longer reach Settings, Environments, Prompts, Integrations, Runs, Agents, or the Terminal via the sidebar.
The mobile FAB at the bottom of the sidebar only creates a new session; it provides no path to navigation items.
No existing test exercises this path because the jsdom environment doesn't set `window.innerWidth`, so the regression is invisible to the current test suite.
A minimal fix is to add a mobile-visible trigger (e.g., a hamburger/gear button in the mobile header row, or include it alongside the close button) and ensure `SidebarMenu` can be opened on small screens:
```tsx
{/* Mobile gear button — sits beside the close button */}
<button
ref={menuButtonRef}
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Navigation menu"
aria-expanded={menuOpen}
className="md:hidden w-8 h-8 rounded-lg flex items-center justify-center text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer"
>
{/* gear icon */}
</button>
```
How can I resolve this? If you propose a fix, please make it concise.The gear menu button was hidden on mobile (hidden md:flex), making Settings, Environments, Prompts, and other navigation items unreachable on small screens. Now the gear button is always visible. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@greptile review - Fixed the mobile navigation accessibility issue. The gear menu button is now visible on all screen sizes (removed |
There was a problem hiding this comment.
1 issue found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/src/components/Sidebar.tsx">
<violation number="1" location="web/src/components/Sidebar.tsx:356">
P2: The gear menu is hidden on mobile (`hidden md:flex`), but footer navigation was removed, so mobile users lose access to the navigation items. Make the menu button visible on small screens or provide another mobile nav entry.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| setSidebarGroupByProject: (v) => { | ||
| localStorage.setItem("cc-sidebar-group-by-project", String(v)); | ||
| set({ sidebarGroupByProject: v }); | ||
| }, |
There was a problem hiding this comment.
setSidebarGroupByProject has no UI exposure — feature effectively removed
sidebarGroupByProject is added to the store with a setter (setSidebarGroupByProject) and a localStorage initializer (getInitialSidebarGroupByProject()), but a search of the entire web/src tree shows that no component ever calls setSidebarGroupByProject. The key "cc-sidebar-group-by-project" was never written by the old code either, so getInitialSidebarGroupByProject() will return false for every existing user.
The old Sidebar always grouped sessions by project (projectGroups.map(group => <ProjectGroup .../>)). After this PR, all existing users will silently see a flat session list on first launch with no in-app way to re-enable grouping. The store wiring is orphaned dead code until a toggle is surfaced in SettingsPage or the Sidebar itself.
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/store.ts
Line: 811-814
Comment:
**`setSidebarGroupByProject` has no UI exposure — feature effectively removed**
`sidebarGroupByProject` is added to the store with a setter (`setSidebarGroupByProject`) and a localStorage initializer (`getInitialSidebarGroupByProject()`), but a search of the entire `web/src` tree shows that **no component ever calls `setSidebarGroupByProject`**. The key `"cc-sidebar-group-by-project"` was never written by the old code either, so `getInitialSidebarGroupByProject()` will return `false` for every existing user.
The old `Sidebar` **always** grouped sessions by project (`projectGroups.map(group => <ProjectGroup .../>)`). After this PR, all existing users will silently see a flat session list on first launch with no in-app way to re-enable grouping. The store wiring is orphaned dead code until a toggle is surfaced in SettingsPage or the Sidebar itself.
How can I resolve this? If you propose a fix, please make it concise.| <button | ||
| key={item.id} | ||
| role="menuitem" | ||
| onClick={() => { | ||
| if (item.id !== "terminal") { | ||
| useStore.getState().closeTerminal(); |
There was a problem hiding this comment.
aria-current removed from active nav items
The old footer nav set aria-current="page" on the active navigation button, which screen readers use to announce "current page" context. The new SidebarMenu only applies a visual CSS class (bg-cc-active) for the active item but never sets the ARIA attribute. This is a measurable accessibility regression.
The existing test that verified this attribute was explicitly removed from Sidebar.test.tsx:
- expect(agentsBtn).toHaveAttribute("aria-current", "page");Each active nav <button> should carry aria-current={isActive ? "page" : undefined} so that screen readers can correctly identify the current location.
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/components/SidebarMenu.tsx
Line: 158-163
Comment:
**`aria-current` removed from active nav items**
The old footer nav set `aria-current="page"` on the active navigation button, which screen readers use to announce "current page" context. The new `SidebarMenu` only applies a visual CSS class (`bg-cc-active`) for the active item but never sets the ARIA attribute. This is a measurable accessibility regression.
The existing test that verified this attribute was explicitly removed from `Sidebar.test.tsx`:
```diff
- expect(agentsBtn).toHaveAttribute("aria-current", "page");
```
Each active nav `<button>` should carry `aria-current={isActive ? "page" : undefined}` so that screen readers can correctly identify the current location.
How can I resolve this? If you propose a fix, please make it concise.…oject toggle - Add aria-current="page" to active nav items in SidebarMenu for screen reader accessibility - Add "Group by project" toggle in the gear menu so users can switch between flat and grouped session lists - Update tests for new menuitem count and aria-current assertion Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@greptile review - Fixed both issues from the previous review:
Please re-review. |
| async function openAdvancedPanel() { | ||
| const advancedBtn = screen.getByRole("button", { name: /^Advanced$/i }); | ||
| fireEvent.click(advancedBtn); | ||
| } |
There was a problem hiding this comment.
openAdvancedPanel is declared async and returns a Promise<void>, but all 18+ call sites (lines ~162, ~208, ~247, ~292, ~334, ~394, ~523, ~553, ~572, ~601, ~631, ~908, ~974, ~991, ~1031, ~1082, ~1107, ~1131) drop the returned Promise:
openAdvancedPanel(); // unhandled Promise
const issueTitle = await screen.findByText(/THE-147/i);Because the function body is entirely synchronous today (fireEvent.click is synchronous), tests pass. However, if refactored to use userEvent.click (async, recommended for React 18) or any other awaitable action, every call site will silently race—the panel won't have opened by the time the next assertion runs.
Fix: Remove the async keyword since the body contains no await:
| async function openAdvancedPanel() { | |
| const advancedBtn = screen.getByRole("button", { name: /^Advanced$/i }); | |
| fireEvent.click(advancedBtn); | |
| } | |
| function openAdvancedPanel() { | |
| const advancedBtn = screen.getByRole("button", { name: /^Advanced$/i }); | |
| fireEvent.click(advancedBtn); | |
| } |
Or update all 18+ call sites to await openAdvancedPanel() if async shape is intentional for future expansion.
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/components/HomePage.test.tsx
Line: 103-106
Comment:
`openAdvancedPanel` is declared `async` and returns a `Promise<void>`, but all 18+ call sites (lines ~162, ~208, ~247, ~292, ~334, ~394, ~523, ~553, ~572, ~601, ~631, ~908, ~974, ~991, ~1031, ~1082, ~1107, ~1131) drop the returned Promise:
```typescript
openAdvancedPanel(); // unhandled Promise
const issueTitle = await screen.findByText(/THE-147/i);
```
Because the function body is entirely synchronous today (`fireEvent.click` is synchronous), tests pass. However, if refactored to use `userEvent.click` (async, recommended for React 18) or any other awaitable action, every call site will silently race—the panel won't have opened by the time the next assertion runs.
**Fix:** Remove the `async` keyword since the body contains no `await`:
```suggestion
function openAdvancedPanel() {
const advancedBtn = screen.getByRole("button", { name: /^Advanced$/i });
fireEvent.click(advancedBtn);
}
```
Or update all 18+ call sites to `await openAdvancedPanel()` if async shape is intentional for future expansion.
How can I resolve this? If you propose a fix, please make it concise.| onClick={() => setShowAdvanced((v) => !v)} | ||
| className={`flex items-center gap-1.5 px-2.5 py-2 text-xs rounded-md transition-colors cursor-pointer ${ | ||
| showAdvanced | ||
| ? "text-cc-primary bg-cc-primary/10 hover:bg-cc-primary/15" | ||
| : "text-cc-muted hover:text-cc-fg hover:bg-cc-hover" | ||
| }`} | ||
| aria-expanded={showAdvanced} | ||
| > | ||
| <svg viewBox="0 0 20 20" fill="currentColor" className="w-3.5 h-3.5 opacity-60"> | ||
| <path fillRule="evenodd" clipRule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.53 1.53 0 01-2.29.95c-1.35-.8-2.92.77-2.12 2.12.54.9.07 2.04-.95 2.29-1.56.38-1.56 2.6 0 2.98 1.02.25 1.49 1.39.95 2.29-.8 1.35.77 2.92 2.12 2.12.9-.54 2.04-.07 2.29.95.38 1.56 2.6 1.56 2.98 0 .25-1.02 1.39-1.49 2.29-.95 1.35.8 2.92-.77 2.12-2.12-.54-.9-.07-2.04.95-2.29 1.56-.38 1.56-2.6 0-2.98-1.02-.25-1.49-1.39-.95-2.29.8-1.35-.77-2.92-2.12-2.12-.9.54-2.04.07-2.29-.95zM10 13a3 3 0 100-6 3 3 0 000 6z" /> | ||
| </svg> | ||
| {showAdvanced ? "Hide advanced" : "Advanced"} | ||
| </button> |
There was a problem hiding this comment.
showBranchingControls state is not reset when the Advanced panel is collapsed. User sequence:
- Click "Advanced" → panel opens with branch sub-panel closed (
showBranchingControls = false) - Click "Branch from session" → branch panel expands (
showBranchingControls = true) - Click "Hide advanced" → Advanced block unmounts, hiding branch panel visually
- Click "Advanced" again → Advanced remounts with
showBranchingControlsstilltrue→ branch panel is already expanded with no user action
This can confuse users who opened, interacted with, and then dismissed the Advanced panel.
Fix: Reset showBranchingControls when collapsing Advanced:
| onClick={() => setShowAdvanced((v) => !v)} | |
| className={`flex items-center gap-1.5 px-2.5 py-2 text-xs rounded-md transition-colors cursor-pointer ${ | |
| showAdvanced | |
| ? "text-cc-primary bg-cc-primary/10 hover:bg-cc-primary/15" | |
| : "text-cc-muted hover:text-cc-fg hover:bg-cc-hover" | |
| }`} | |
| aria-expanded={showAdvanced} | |
| > | |
| <svg viewBox="0 0 20 20" fill="currentColor" className="w-3.5 h-3.5 opacity-60"> | |
| <path fillRule="evenodd" clipRule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.53 1.53 0 01-2.29.95c-1.35-.8-2.92.77-2.12 2.12.54.9.07 2.04-.95 2.29-1.56.38-1.56 2.6 0 2.98 1.02.25 1.49 1.39.95 2.29-.8 1.35.77 2.92 2.12 2.12.9-.54 2.04-.07 2.29.95.38 1.56 2.6 1.56 2.98 0 .25-1.02 1.39-1.49 2.29-.95 1.35.8 2.92-.77 2.12-2.12-.54-.9-.07-2.04.95-2.29 1.56-.38 1.56-2.6 0-2.98-1.02-.25-1.49-1.39-.95-2.29.8-1.35-.77-2.92-2.12-2.12-.9.54-2.04.07-2.29-.95zM10 13a3 3 0 100-6 3 3 0 000 6z" /> | |
| </svg> | |
| {showAdvanced ? "Hide advanced" : "Advanced"} | |
| </button> | |
| onClick={() => { | |
| if (showAdvanced) setShowBranchingControls(false); | |
| setShowAdvanced((v) => !v); | |
| }} |
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/src/components/HomePage.tsx
Line: 1013-1025
Comment:
`showBranchingControls` state is not reset when the Advanced panel is collapsed. User sequence:
1. Click "Advanced" → panel opens with branch sub-panel closed (`showBranchingControls = false`)
2. Click "Branch from session" → branch panel expands (`showBranchingControls = true`)
3. Click "Hide advanced" → Advanced block unmounts, hiding branch panel visually
4. Click "Advanced" again → Advanced remounts with `showBranchingControls` still `true` → **branch panel is already expanded with no user action**
This can confuse users who opened, interacted with, and then dismissed the Advanced panel.
**Fix:** Reset `showBranchingControls` when collapsing Advanced:
```suggestion
onClick={() => {
if (showAdvanced) setShowBranchingControls(false);
setShowAdvanced((v) => !v);
}}
```
How can I resolve this? If you propose a fix, please make it concise.- Remove unnecessary `async` from `openAdvancedPanel()` test helper - Reset `showBranchingControls` state when Advanced panel is collapsed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…v layout - Replace prominent blue "New Session" button with subtle icon-only button - Remove Advanced toggle — show backend, model, env, Linear always - Add AdminLayout component with persistent left nav for admin pages - Consolidate admin page rendering in App.tsx with AdminLayout wrapper - Update tests: remove openAdvancedPanel helper, add AdminLayout tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/src/components/HomePage.tsx">
<violation number="1" location="web/src/components/HomePage.tsx:1011">
P2: Advanced options are now always rendered because the conditional `showAdvanced` guard was removed, so users can no longer collapse the advanced section.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| </div> | ||
| )} | ||
| </div> | ||
| <div className="mt-2 px-1 flex flex-col gap-2"> |
There was a problem hiding this comment.
P2: Advanced options are now always rendered because the conditional showAdvanced guard was removed, so users can no longer collapse the advanced section.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/src/components/HomePage.tsx, line 1011:
<comment>Advanced options are now always rendered because the conditional `showAdvanced` guard was removed, so users can no longer collapse the advanced section.</comment>
<file context>
@@ -1007,31 +1006,9 @@ export function HomePage() {
- {/* Advanced options panel — collapsed by default */}
- {showAdvanced && (<>
- <div className="mt-2 px-1 flex flex-col gap-2 animate-[menu-appear_150ms_ease-out]">
+ <div className="mt-2 px-1 flex flex-col gap-2">
<div className="flex items-center gap-1 sm:gap-2 flex-wrap">
{/* Backend toggle */}
</file context>
…links footer - Remove AdminLayout component (caused ugly double-sidebar on Settings page) - Show admin nav items directly in sidebar body when on admin pages - Add external links (Docs, GitHub, Website) as icon-only links in sidebar footer - Restore individual admin page conditional blocks in App.tsx - Update Sidebar tests with within() scoping and new admin nav + footer tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/src/components/Sidebar.tsx">
<violation number="1" location="web/src/components/Sidebar.tsx:451">
P2: Include `agentSessions` in the empty-state check; otherwise users with only agent sessions see an incorrect “No sessions yet” state.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| ) : ( | ||
| /* Session list */ | ||
| <> | ||
| {activeSessions.length === 0 && cronSessions.length === 0 && archivedSessions.length === 0 ? ( |
There was a problem hiding this comment.
P2: Include agentSessions in the empty-state check; otherwise users with only agent sessions see an incorrect “No sessions yet” state.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/src/components/Sidebar.tsx, line 451:
<comment>Include `agentSessions` in the empty-state check; otherwise users with only agent sessions see an incorrect “No sessions yet” state.</comment>
<file context>
@@ -403,121 +409,74 @@ export function Sidebar() {
- {...sessionItemProps}
- />
- ))
+ {activeSessions.length === 0 && cronSessions.length === 0 && archivedSessions.length === 0 ? (
+ <p className="px-3 py-8 text-xs text-cc-muted text-center leading-relaxed">
+ No sessions yet.
</file context>
| {activeSessions.length === 0 && cronSessions.length === 0 && archivedSessions.length === 0 ? ( | |
| {activeSessions.length === 0 && cronSessions.length === 0 && agentSessions.length === 0 && archivedSessions.length === 0 ? ( |
The icon-only "+" button was too subtle. Replace with a text button showing "New Session" with a small "+" icon prefix for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gear dropdown menu was perceived as a modal barrier to reach Settings and other admin pages. Nav items are now always visible at the bottom of the sidebar for direct one-click access. - Remove SidebarMenu dropdown from sidebar header (gear button gone) - Add nav items (Settings, Prompts, etc.) as persistent section at bottom of sidebar, always visible - Move "Group by project" toggle inline as icon button in session list - Update tests: remove gear menu references, test nav items directly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign sidebar header to match Cursor-style layout: - Add Companion logo + "The Companion" brand text at top - Show "New Session" and "Settings" as two side-by-side buttons - Remove 7-item nav section from sidebar bottom (was cluttered) - Remove group-by-project toggle (can be re-added later) - Keep external links footer (Docs, GitHub, Website) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ed buttons Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
SidebarMenucomponent for navigation dropdown with all nav items and external linkssidebarGroupByProjectflag (default off, opt-in for project grouping)Why
Testing
Review provenance