Skip to content

Commit cf61afe

Browse files
authored
Improve the design of the sidebar (#934)
- Rename 'Agents' to 'Projects' - Remove 'New Workspace' button with 'New Chat' inline on the project - Remove hover-state for project items - you can click to make a new chat - Remove hover background for renaming workspaces, there is now a tooltip - Do not display the full path for a project, just the last item. Users can hover for the full path <img width="2277" height="1568" alt="image" src="https://github.com/user-attachments/assets/c7d93ae4-bb30-4235-8fb7-99206910258e" /> See the Storybook.
1 parent bf49482 commit cf61afe

File tree

11 files changed

+88
-72
lines changed

11 files changed

+88
-72
lines changed

src/browser/components/ChatMetaSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const ChatMetaSidebarComponent: React.FC<ChatMetaSidebarProps> = ({ workspaceId,
6161
return (
6262
<div
6363
className={cn(
64-
"bg-separator border-l border-border-light flex flex-col overflow-hidden transition-[width] duration-200 flex-shrink-0",
64+
"bg-sidebar border-l border-border-light flex flex-col overflow-hidden transition-[width] duration-200 flex-shrink-0",
6565
showCollapsed ? "w-5 sticky right-0 z-10 shadow-[-2px_0_4px_rgba(0,0,0,0.2)]" : "w-80"
6666
)}
6767
role="complementary"

src/browser/components/LeftSidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function LeftSidebar(props: LeftSidebarProps) {
2626
aria-label="Open sidebar menu"
2727
className={cn(
2828
"hidden mobile-menu-btn fixed top-3 left-3 z-[998]",
29-
"w-10 h-10 bg-separator border border-border-light rounded-md cursor-pointer",
29+
"w-10 h-10 bg-sidebar border border-border-light rounded-md cursor-pointer",
3030
"items-center justify-center text-foreground text-xl transition-all duration-200",
3131
"shadow-[0_2px_4px_rgba(0,0,0,0.3)]",
3232
"hover:bg-hover hover:border-bg-light",
@@ -49,7 +49,7 @@ export function LeftSidebar(props: LeftSidebarProps) {
4949
{/* Sidebar */}
5050
<div
5151
className={cn(
52-
"h-screen bg-separator border-r border-dark flex flex-col shrink-0",
52+
"h-screen bg-sidebar border-r border-border flex flex-col shrink-0",
5353
"transition-all duration-200 overflow-hidden relative z-[100]",
5454
collapsed ? "w-8" : "w-72",
5555
"mobile-sidebar",

src/browser/components/ProjectSidebar.tsx

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ const DraggableProjectItemBase: React.FC<DraggableProjectItemProps> = ({
8787
<div
8888
ref={(node) => drag(drop(node))}
8989
className={cn(
90-
"py-2 px-3 flex items-center border-l-transparent transition-all duration-150 bg-separator",
90+
"py-2 px-3 flex items-center border-l-transparent transition-all duration-150 bg-sidebar",
9191
isDragging ? "cursor-grabbing opacity-40 [&_*]:!cursor-grabbing" : "cursor-grab",
9292
isOver && "bg-accent/[0.08]",
9393
selected && "bg-hover border-l-accent",
94-
"hover:bg-hover hover:[&_button]:opacity-100 hover:[&_[data-drag-handle]]:opacity-100"
94+
"hover:[&_button]:opacity-100 hover:[&_[data-drag-handle]]:opacity-100"
9595
)}
9696
{...rest}
9797
>
@@ -465,19 +465,19 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
465465
<DndProvider backend={HTML5Backend}>
466466
<ProjectDragLayer />
467467
<div
468-
className="font-primary bg-dark border-border-light flex flex-1 flex-col overflow-hidden border-r"
468+
className="font-primary bg-sidebar border-border-light flex flex-1 flex-col overflow-hidden border-r"
469469
role="navigation"
470470
aria-label="Projects"
471471
>
472472
{!collapsed && (
473473
<>
474474
<div className="border-dark flex items-center justify-between border-b p-4">
475-
<h2 className="text-foreground text-md m-0 font-semibold">Agents</h2>
475+
<h2 className="text-foreground m-0 text-lg font-medium">Projects</h2>
476476
<TooltipWrapper inline>
477477
<button
478478
onClick={onAddProject}
479479
aria-label="Add project"
480-
className="text-foreground hover:bg-hover hover:border-border-light flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 text-lg transition-all duration-200"
480+
className="text-secondary hover:bg-hover hover:border-border-light flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 text-2xl transition-all duration-200"
481481
>
482482
+
483483
</button>
@@ -513,42 +513,47 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
513513
projectPath={projectPath}
514514
onReorder={handleReorder}
515515
selected={false}
516-
onClick={() => toggleProject(projectPath)}
516+
onClick={() => handleAddWorkspace(projectPath)}
517517
onKeyDown={(e: React.KeyboardEvent) => {
518+
// Ignore key events from child buttons
519+
if (e.target instanceof HTMLElement && e.target !== e.currentTarget) {
520+
return;
521+
}
518522
if (e.key === "Enter" || e.key === " ") {
519523
e.preventDefault();
520-
toggleProject(projectPath);
524+
handleAddWorkspace(projectPath);
521525
}
522526
}}
523527
role="button"
524528
tabIndex={0}
525529
aria-expanded={isExpanded}
526530
aria-controls={workspaceListId}
527-
aria-label={`${isExpanded ? "Collapse" : "Expand"} project ${projectName}`}
531+
aria-label={`Create workspace in ${projectName}`}
528532
data-project-path={projectPath}
529533
>
530-
<span
534+
<button
535+
onClick={(event) => {
536+
event.stopPropagation();
537+
toggleProject(projectPath);
538+
}}
539+
aria-label={`${isExpanded ? "Collapse" : "Expand"} project ${projectName}`}
531540
data-project-path={projectPath}
532-
aria-hidden="true"
533-
className="text-muted mr-2 shrink-0 text-xs transition-transform duration-200"
534-
style={{ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)" }}
541+
className="text-secondary hover:bg-hover hover:border-border-light mr-2 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 transition-all duration-200"
535542
>
536-
<ChevronRight size={12} />
537-
</span>
543+
<ChevronRight
544+
size={12}
545+
className="transition-transform duration-200"
546+
style={{ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)" }}
547+
/>
548+
</button>
538549
<div className="flex min-w-0 flex-1 items-center pr-2">
539550
<TooltipWrapper inline>
540-
<div className="text-muted-dark truncate text-sm">
551+
<div className="text-muted-dark flex gap-2 truncate text-sm">
541552
{(() => {
542553
const abbrevPath = PlatformPaths.abbreviate(projectPath);
543-
const { dirPath, basename } =
544-
PlatformPaths.splitAbbreviated(abbrevPath);
554+
const { basename } = PlatformPaths.splitAbbreviated(abbrevPath);
545555
return (
546-
<>
547-
<span>{dirPath}</span>
548-
<span className="text-foreground font-medium">
549-
{basename}
550-
</span>
551-
</>
556+
<span className="text-foreground font-medium">{basename}</span>
552557
);
553558
})()}
554559
</div>
@@ -582,34 +587,34 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
582587
title="Remove project"
583588
aria-label={`Remove project ${projectName}`}
584589
data-project-path={projectPath}
585-
className="text-muted-dark hover:text-danger-light hover:bg-danger-light/10 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-base opacity-0 transition-all duration-200"
590+
className="text-muted-dark hover:text-danger-light hover:bg-danger-light/10 mr-1 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-base opacity-0 transition-all duration-200"
586591
>
587592
×
588593
</button>
589594
<Tooltip className="tooltip" align="right">
590595
Remove project
591596
</Tooltip>
592597
</TooltipWrapper>
598+
<button
599+
onClick={(event) => {
600+
event.stopPropagation();
601+
handleAddWorkspace(projectPath);
602+
}}
603+
aria-label={`New chat in ${projectName}`}
604+
data-project-path={projectPath}
605+
className="text-secondary hover:bg-hover hover:border-border-light shrink-0 cursor-pointer rounded border border-transparent bg-transparent px-1.5 py-0.5 text-[11px] transition-all duration-200"
606+
>
607+
+ New Chat
608+
</button>
593609
</DraggableProjectItem>
594610

595611
{isExpanded && (
596612
<div
597613
id={workspaceListId}
598614
role="region"
599615
aria-label={`Workspaces for ${projectName}`}
616+
className="pt-1"
600617
>
601-
<div className="border-hover border-b px-3 py-2">
602-
<button
603-
onClick={() => handleAddWorkspace(projectPath)}
604-
data-project-path={projectPath}
605-
aria-label={`Add workspace to ${projectName}`}
606-
className="text-muted border-border-medium hover:bg-hover hover:border-border-darker hover:text-foreground w-full cursor-pointer rounded border border-dashed bg-transparent px-3 py-1.5 text-left text-[13px] transition-all duration-200"
607-
>
608-
+ New Workspace
609-
{selectedWorkspace?.projectPath === projectPath &&
610-
` (${formatKeybind(KEYBINDS.NEW_WORKSPACE)})`}
611-
</button>
612-
</div>
613618
{(() => {
614619
const allWorkspaces =
615620
sortedWorkspacesByProject.get(projectPath) ?? [];

src/browser/components/RightSidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const SidebarContainer: React.FC<SidebarContainerProps> = ({
5050
return (
5151
<div
5252
className={cn(
53-
"bg-separator border-l border-border-light flex flex-col overflow-hidden flex-shrink-0",
53+
"bg-sidebar border-l border-border-light flex flex-col overflow-hidden flex-shrink-0",
5454
customWidth ? "" : "transition-[width] duration-200",
5555
collapsed && "sticky right-0 z-10 shadow-[-2px_0_4px_rgba(0,0,0,0.2)]",
5656
// Mobile: Show vertical meter when collapsed (20px), full width when expanded
@@ -215,7 +215,7 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
215215
<div className={cn("flex-row h-full", !showCollapsed ? "flex" : "hidden")}>
216216
{/* Render meter when Review tab is active */}
217217
{selectedTab === "review" && (
218-
<div className="bg-separator border-border-light flex w-5 shrink-0 flex-col border-r">
218+
<div className="bg-sidebar border-border-light flex w-5 shrink-0 flex-col border-r">
219219
{verticalMeter}
220220
</div>
221221
)}

src/browser/components/RightSidebar/VerticalTokenMeter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const VerticalTokenMeterComponent: React.FC<VerticalTokenMeterProps> = ({
2525

2626
return (
2727
<div
28-
className="bg-separator border-border-light flex h-full w-5 flex-col items-center border-l py-3"
28+
className="bg-sidebar border-border-light flex h-full w-5 flex-col items-center border-l py-3"
2929
data-component="vertical-token-meter"
3030
>
3131
{/* Percentage label at top */}

src/browser/components/TitleBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export function TitleBar() {
231231
const showUpdateIndicator = true;
232232

233233
return (
234-
<div className="bg-separator border-border-light font-primary text-muted flex h-8 shrink-0 items-center justify-between border-b px-4 text-[11px] select-none">
234+
<div className="bg-sidebar border-border-light font-primary text-muted flex h-8 shrink-0 items-center justify-between border-b px-4 text-[11px] select-none">
235235
<div className="mr-4 flex min-w-0 items-center gap-2">
236236
{showUpdateIndicator && (
237237
<TooltipWrapper>

src/browser/components/VimTextArea.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export const VimTextArea = React.forwardRef<HTMLTextAreaElement, VimTextAreaProp
250250
...(trailingAction ? { scrollbarGutter: "stable both-edges" } : {}),
251251
}}
252252
className={cn(
253-
"w-full border text-light py-1.5 px-2 rounded font-mono text-[13px] resize-none min-h-8 max-h-[50vh] overflow-y-auto",
253+
"w-full border text-light py-1.5 px-2 rounded font-sans text-[13px] resize-none min-h-8 max-h-[50vh] overflow-y-auto",
254254
"placeholder:text-placeholder",
255255
"focus:outline-none",
256256
trailingAction && "pr-10",

src/browser/components/WorkspaceHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const WorkspaceHeader: React.FC<WorkspaceHeaderProps> = ({
4747
}, [startTutorial, isSequenceCompleted]);
4848

4949
return (
50-
<div className="bg-separator border-border-light flex h-8 items-center justify-between border-b px-[15px] [@media(max-width:768px)]:h-auto [@media(max-width:768px)]:flex-wrap [@media(max-width:768px)]:gap-2 [@media(max-width:768px)]:py-2 [@media(max-width:768px)]:pl-[60px]">
50+
<div className="bg-sidebar border-border-light flex h-8 items-center justify-between border-b px-[15px] [@media(max-width:768px)]:h-auto [@media(max-width:768px)]:flex-wrap [@media(max-width:768px)]:gap-2 [@media(max-width:768px)]:py-2 [@media(max-width:768px)]:pl-[60px]">
5151
<div className="text-foreground flex min-w-0 items-center gap-2 overflow-hidden font-semibold">
5252
<RuntimeBadge runtimeConfig={runtimeConfig} isWorking={canInterrupt} />
5353
<GitStatusIndicator

src/browser/components/WorkspaceListItem.tsx

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -160,26 +160,31 @@ const WorkspaceListItemInner: React.FC<WorkspaceListItemProps> = ({
160160
data-workspace-id={workspaceId}
161161
/>
162162
) : (
163-
<span
164-
className={cn(
165-
"text-foreground -mx-1 min-w-0 flex-1 truncate rounded-sm px-1 text-left text-[14px] transition-colors duration-200",
166-
!isDisabled && "cursor-pointer hover:bg-white/5"
167-
)}
168-
onDoubleClick={(e) => {
169-
if (isDisabled) return;
170-
e.stopPropagation();
171-
startRenaming();
172-
}}
173-
title={isDisabled ? undefined : "Double-click to rename"}
174-
>
175-
{canInterrupt || isCreating ? (
176-
<Shimmer className="w-full truncate" colorClass="var(--color-foreground)">
177-
{displayName}
178-
</Shimmer>
179-
) : (
180-
displayName
181-
)}
182-
</span>
163+
<TooltipWrapper inline>
164+
<span
165+
className={cn(
166+
"text-foreground -mx-1 min-w-0 flex-1 truncate rounded-sm px-1 text-left text-[14px] transition-colors duration-200",
167+
!isDisabled && "cursor-pointer"
168+
)}
169+
onDoubleClick={(e) => {
170+
if (isDisabled) return;
171+
e.stopPropagation();
172+
startRenaming();
173+
}}
174+
title={isDisabled ? undefined : "Double-click to rename"}
175+
>
176+
{canInterrupt || isCreating ? (
177+
<Shimmer className="w-full truncate" colorClass="var(--color-foreground)">
178+
{displayName}
179+
</Shimmer>
180+
) : (
181+
displayName
182+
)}
183+
</span>
184+
<Tooltip className="tooltip" align="left">
185+
Double-click to rename
186+
</Tooltip>
187+
</TooltipWrapper>
183188
)}
184189

185190
<div className="ml-auto flex items-center gap-1">

src/browser/styles/globals.css

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
/* Background & Layout */
7373
--color-background: hsl(0 0% 12%);
7474
--color-background-secondary: hsl(60 1% 15%);
75-
--color-border: hsl(240 2% 25%);
75+
--color-border: #262626; /* neutral-800 */
7676
--color-foreground: hsl(0 0% 83%);
7777
--color-text: hsl(0 0% 83%);
7878
--color-text-light: hsl(0 0% 73%);
@@ -189,20 +189,21 @@
189189
--color-gray: hsl(0 0% 48%); /* #7a7a7a - gray */
190190
--color-medium: hsl(0 0% 59%); /* #969696 - medium */
191191

192-
--color-border-light: hsl(240 3% 25%); /* #3e3e42 - lighter borders */
192+
--color-border-light: #262626; /* neutral-800 - borders */
193193
--color-border-medium: hsl(0 0% 27%); /* #444 - medium borders */
194194
--color-border-darker: hsl(0 0% 33%); /* #555 - darker borders */
195195
--color-border-subtle: hsl(0 0% 40%); /* #666 - subtle border */
196196
--color-border-gray: hsl(240 1% 31%); /* #4e4e52 - gray border */
197197

198-
--color-dark: hsl(0 0% 11.5%); /* #1e1e1e - dark backgrounds */
198+
--color-dark: #1D1D1D; /* dark backgrounds */
199199
--color-darker: hsl(0 0% 8.6%); /* #161616 - darker backgrounds */
200200
--color-hover: hsl(0 0% 16.5%); /* #2a2a2b - hover states */
201201
--color-bg-medium: hsl(0 0% 27%); /* #454545 - medium bg */
202202
--color-bg-light: hsl(0 0% 30%); /* #4c4c4c - light bg */
203203
--color-bg-subtle: hsl(240 3% 22%); /* #37373d - subtle bg */
204204

205-
--color-separator: hsl(0 0% 15%); /* #252526 - separators */
205+
--color-sidebar: #171717; /* neutral-900 - sidebar background */
206+
--color-separator: #262626; /* neutral-800 - dividers/separators */
206207
--color-separator-light: hsl(0 0% 27%); /* #464647 - lighter separator */
207208
--color-modal-bg: hsl(0 0% 18%); /* #2d2d30 - modal backgrounds */
208209

@@ -434,6 +435,7 @@
434435
--color-bg-light: hsl(210 24% 84%);
435436
--color-bg-subtle: hsl(210 32% 92%);
436437

438+
--color-sidebar: hsl(0 0% 96%); /* light theme sidebar */
437439
--color-separator: hsl(0 0% 91%);
438440
--color-separator-light: hsl(0 0% 96%);
439441
--color-modal-bg: hsl(210 35% 96%);
@@ -662,7 +664,8 @@
662664
--color-bg-light: #d5ceb8;
663665
--color-bg-subtle: #eee8d5;
664666

665-
--color-separator: #eee8d5; /* base2 - sidebar background */
667+
--color-sidebar: #eee8d5; /* base2 - sidebar background */
668+
--color-separator: #d5ceb8;
666669
--color-separator-light: #f5efdc;
667670
--color-modal-bg: #fdf6e3;
668671

@@ -881,7 +884,8 @@
881884
--color-bg-light: #106577;
882885
--color-bg-subtle: #073642;
883886

884-
--color-separator: #073642; /* base02 - sidebar background */
887+
--color-sidebar: #073642; /* base02 - sidebar background */
888+
--color-separator: #0a4555;
885889
--color-separator-light: #094555; /* slightly lighter for hover states */
886890
--color-modal-bg: #073642;
887891

0 commit comments

Comments
 (0)