Skip to content

Commit a84e846

Browse files
committed
🤖 fix: restore pre-redesign sidebar hierarchy
Restore the flatter sidebar hierarchy so project rows, section headers, and "Older than …" buckets read as distinct layers again. --- _Generated with `mux` • Model: `openai:gpt-5.4` • Thinking: `xhigh` • Cost: `n/a`_ <!-- mux-attribution: model=openai:gpt-5.4 thinking=xhigh costs=n/a -->
1 parent 3102607 commit a84e846

File tree

5 files changed

+89
-136
lines changed

5 files changed

+89
-136
lines changed

src/browser/components/AddSectionButton/AddSectionButton.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ export const AddSectionButton: React.FC<AddSectionButtonProps> = ({ onCreateSect
2828

2929
if (isCreating) {
3030
return (
31-
<div className="flex items-center px-2 py-0.5">
31+
<div className="flex items-center gap-1 px-2 py-0.5">
32+
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
33+
<Plus size={12} className="text-muted/60" />
34+
</div>
3235
<input
3336
ref={inputRef}
3437
type="text"
@@ -44,7 +47,7 @@ export const AddSectionButton: React.FC<AddSectionButtonProps> = ({ onCreateSect
4447
}}
4548
placeholder="Section name..."
4649
data-testid="add-section-input"
47-
className="bg-background/50 text-foreground ml-6 min-w-0 flex-1 rounded border border-white/20 px-1.5 py-0.5 text-[11px] outline-none select-text"
50+
className="bg-background/50 text-foreground min-w-0 flex-1 rounded border border-white/20 px-1.5 py-0.5 text-[11px] outline-none select-text"
4851
/>
4952
</div>
5053
);

src/browser/components/AgentListItem/AgentListItem.tsx

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ const SHOW_INLINE_ACTIONS_ON_WIDE_TOUCH =
128128
/** Calculate left padding based on nesting depth */
129129
function getItemPaddingLeft(depth?: number): number {
130130
const safeDepth = typeof depth === "number" && Number.isFinite(depth) ? Math.max(0, depth) : 0;
131-
return 8 + Math.min(32, safeDepth) * 12;
131+
return 12 + Math.min(32, safeDepth) * 12;
132132
}
133133

134134
function getSubAgentConnectorLeft(
@@ -294,31 +294,20 @@ function ActionButtonWrapper(props: { children: React.ReactNode; className?: str
294294
// ─────────────────────────────────────────────────────────────────────────────
295295

296296
function DraftAgentListItemInner(props: DraftAgentListItemProps) {
297-
const { projectPath, isSelected, depth, sectionId, draft } = props;
297+
const { projectPath, isSelected, depth, draft } = props;
298298
const paddingLeft = getItemPaddingLeft(depth);
299299
const hasPromptPreview = draft.promptPreview.length > 0;
300-
const draftBorderStyle: React.CSSProperties = {
301-
backgroundImage: [
302-
"repeating-linear-gradient(to right, var(--color-border) 0 5px, transparent 5px 10px)",
303-
"repeating-linear-gradient(to right, var(--color-border) 0 5px, transparent 5px 10px)",
304-
"repeating-linear-gradient(to bottom, var(--color-border) 0 5px, transparent 5px 10px)",
305-
].join(", "),
306-
backgroundSize: "100% 1.5px, 100% 1.5px, 1.5px 100%",
307-
backgroundPosition: "left top, left bottom, left top",
308-
backgroundRepeat: "no-repeat",
309-
};
310300

311301
const ctxMenu = useContextMenuPosition({ longPress: true });
312302

313303
return (
314304
<div
315305
className={cn(
316306
LIST_ITEM_BASE_CLASSES,
317-
sectionId != null ? "ml-8" : "ml-6.5",
318-
"cursor-pointer pl-1 hover:bg-surface-secondary [&:hover_button]:opacity-100",
307+
"border-border cursor-pointer border-t border-b border-l border-dashed pl-1 hover:bg-surface-secondary [&:hover_button]:opacity-100",
319308
isSelected && "bg-surface-secondary"
320309
)}
321-
style={{ paddingLeft, ...draftBorderStyle }}
310+
style={{ paddingLeft }}
322311
onClick={() => {
323312
if (ctxMenu.suppressClickIfLongPress()) return;
324313
draft.onOpen();
@@ -687,7 +676,6 @@ function RegularAgentListItemInner(props: AgentListItemProps) {
687676
className={cn(
688677
LIST_ITEM_BASE_CLASSES,
689678
"group/row",
690-
sectionId != null ? "ml-7.5" : "ml-5",
691679
isDragging && "opacity-50",
692680
isRemoving && "opacity-70",
693681
// Keep hover styles enabled for initializing workspaces so the row feels interactive.

src/browser/components/ProjectSidebar/ProjectSidebar.tsx

Lines changed: 49 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ import {
8787
ChevronRight,
8888
CircleHelp,
8989
EllipsisVertical,
90-
Folder,
91-
FolderOpen,
9290
KeyRound,
9391
Palette,
9492
Pencil,
@@ -102,6 +100,7 @@ import { usePopoverError } from "@/browser/hooks/usePopoverError";
102100
import { forkWorkspace } from "@/browser/utils/chatCommands";
103101
import { PopoverError } from "../PopoverError/PopoverError";
104102
import { SectionHeader } from "../SectionHeader/SectionHeader";
103+
import { AddSectionButton } from "../AddSectionButton/AddSectionButton";
105104
import { WorkspaceSectionDropZone } from "../WorkspaceSectionDropZone/WorkspaceSectionDropZone";
106105
import { WorkspaceDragLayer } from "../WorkspaceDragLayer/WorkspaceDragLayer";
107106
import { SectionDragLayer } from "../SectionDragLayer/SectionDragLayer";
@@ -284,9 +283,8 @@ function useWorkspaceAttentionSubscription(
284283
// Keep the project header visible while scrolling through long workspace lists.
285284
// Project rows are also drag handles, so disable text selection to avoid
286285
// highlighting the whole sidebar before a reorder gesture locks in.
287-
// pr-2 matches AgentListItem LIST_ITEM_BASE_CLASSES so project kebab aligns with workspace rows.
288286
const PROJECT_ITEM_BASE_CLASS =
289-
"group sticky top-0 z-10 py-2 pl-2 pr-1 flex select-none items-center border-l-transparent bg-surface-primary transition-colors duration-150";
287+
"sticky top-0 z-10 py-2 pl-2 pr-3 flex select-none items-center border-l-transparent bg-surface-primary transition-colors duration-150";
290288

291289
function getProjectFallbackLabel(projectPath: string): string {
292290
const abbreviatedPath = PlatformPaths.abbreviate(projectPath);
@@ -985,6 +983,14 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
985983
}));
986984
};
987985

986+
const handleCreateSection = async (projectPath: string, name: string) => {
987+
const result = await createSection(projectPath, name);
988+
if (result.success) {
989+
// Auto-expand the new section so the sidebar immediately shows the new bucket.
990+
const key = getSectionExpandedKey(projectPath, result.data.id);
991+
setExpandedSections((prev) => ({ ...prev, [key]: true }));
992+
}
993+
};
988994
const handleForkWorkspace = useCallback(
989995
async (workspaceId: string, buttonElement?: HTMLElement) => {
990996
if (!api) {
@@ -1791,28 +1797,22 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
17911797
viewportClassName="overflow-x-hidden"
17921798
>
17931799
{multiProjectWorkspaces.length > 0 && (
1794-
<div>
1800+
<div className="border-hover border-b">
17951801
<div className={PROJECT_ITEM_BASE_CLASS}>
17961802
<button
17971803
onClick={() => toggleProject(MULTI_PROJECT_SIDEBAR_SECTION_ID)}
17981804
aria-label={`${isMultiProjectSectionExpanded ? "Collapse" : "Expand"} multi-project workspaces`}
17991805
className="text-secondary hover:bg-hover hover:border-border-light mr-1.5 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"
18001806
>
1801-
<span className="relative flex h-4 w-4 items-center justify-center">
1802-
<ChevronRight
1803-
className="absolute inset-0 h-4 w-4 opacity-0 transition-[opacity,transform] duration-200 group-hover:opacity-100"
1804-
style={{
1805-
transform: isMultiProjectSectionExpanded
1806-
? "rotate(90deg)"
1807-
: "rotate(0deg)",
1808-
}}
1809-
/>
1810-
{isMultiProjectSectionExpanded ? (
1811-
<FolderOpen className="h-4 w-4 transition-opacity duration-200 group-hover:opacity-0" />
1812-
) : (
1813-
<Folder className="h-4 w-4 transition-opacity duration-200 group-hover:opacity-0" />
1814-
)}
1815-
</span>
1807+
<ChevronRight
1808+
className="h-4 w-4 shrink-0 transition-transform duration-200"
1809+
strokeWidth={1.8}
1810+
style={{
1811+
transform: isMultiProjectSectionExpanded
1812+
? "rotate(90deg)"
1813+
: "rotate(0deg)",
1814+
}}
1815+
/>
18161816
</button>
18171817
<div className="flex min-w-0 flex-1 items-center pr-2">
18181818
<span className="text-foreground truncate text-sm font-medium">
@@ -1889,13 +1889,12 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
18891889
const isEditingProjectDisplayName = editingProjectPath === projectPath;
18901890
const projectWorkspaces =
18911891
singleProjectWorkspacesByProject.get(projectPath) ?? [];
1892-
const projectAgentCount = projectWorkspaces.length;
18931892
const projectHasAttention = projectWorkspaces.some(
18941893
(workspace) => workspaceAttentionById.get(workspace.id) === true
18951894
);
18961895

18971896
return (
1898-
<div key={projectPath}>
1897+
<div key={projectPath} className="border-hover border-b">
18991898
<DraggableProjectItem
19001899
projectPath={projectPath}
19011900
onReorder={handleReorder}
@@ -1941,30 +1940,11 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
19411940
data-project-path={projectPath}
19421941
className="text-secondary hover:bg-hover hover:border-border-light mr-1.5 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"
19431942
>
1944-
{/* Mobile: nudge folder icon left so it visually centers above connector line. */}
1945-
<span className="relative flex h-4 w-4 -translate-x-2 items-center justify-center md:translate-x-0">
1946-
<ChevronRight
1947-
className="absolute inset-0 h-4 w-4 opacity-0 transition-[opacity,transform] duration-200 group-hover:opacity-100"
1948-
style={{
1949-
transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
1950-
}}
1951-
/>
1952-
{isExpanded ? (
1953-
<FolderOpen
1954-
className="h-4 w-4 transition-opacity duration-200 group-hover:opacity-0"
1955-
style={
1956-
projectFolderColor ? { color: projectFolderColor } : undefined
1957-
}
1958-
/>
1959-
) : (
1960-
<Folder
1961-
className="h-4 w-4 transition-opacity duration-200 group-hover:opacity-0"
1962-
style={
1963-
projectFolderColor ? { color: projectFolderColor } : undefined
1964-
}
1965-
/>
1966-
)}
1967-
</span>
1943+
<ChevronRight
1944+
className="h-4 w-4 shrink-0 transition-transform duration-200"
1945+
strokeWidth={1.8}
1946+
style={{ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)" }}
1947+
/>
19681948
</button>
19691949
<div
19701950
className="flex min-w-0 flex-1 items-center pr-2"
@@ -2011,27 +1991,17 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
20111991
}}
20121992
/>
20131993
) : (
2014-
<div className="text-muted-dark flex min-w-0 items-center gap-1.5 text-sm">
1994+
<div className="text-muted-dark flex gap-2 truncate text-sm">
20151995
<span
20161996
className={cn(
2017-
"min-w-0 flex-1 truncate font-medium",
1997+
"truncate font-medium",
20181998
projectHasAttention
20191999
? "text-content-primary"
2020-
: "text-content-secondary"
2000+
: "text-foreground"
20212001
)}
20222002
>
20232003
{displayProjectName}
20242004
</span>
2025-
<span
2026-
className={cn(
2027-
"shrink-0",
2028-
projectHasAttention
2029-
? "text-content-primary"
2030-
: "text-content-secondary"
2031-
)}
2032-
>
2033-
({projectAgentCount})
2034-
</span>
20352005
</div>
20362006
)}
20372007
</TooltipTrigger>
@@ -2047,7 +2017,7 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
20472017
}}
20482018
aria-label={`New chat in ${projectName}`}
20492019
data-project-path={projectPath}
2050-
className="text-content-secondary hover:bg-hover hover:border-border-light flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent text-sm leading-none transition-all duration-200"
2020+
className="text-content-secondary hover:bg-hover hover:border-border-light mr-1 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent text-sm leading-none transition-all duration-200"
20512021
>
20522022
<Plus className="h-4 w-4 shrink-0" strokeWidth={1.8} />
20532023
</button>
@@ -2698,8 +2668,16 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
26982668
: `Expand workspaces older than ${thresholdLabel}`
26992669
}
27002670
aria-expanded={isTierExpanded}
2701-
className="text-muted border-hover hover:text-label [&:hover_.arrow]:text-label flex w-full cursor-pointer items-center gap-1 border-t border-none bg-transparent px-3 py-2 pl-7 text-xs font-medium transition-all duration-150 hover:bg-white/3"
2671+
// Keep recency buckets styled like dividers instead of another tree
2672+
// node so “Older than …” still reads as a filter on the list.
2673+
className="text-muted border-hover hover:text-label [&:hover_.arrow]:text-label flex w-full cursor-pointer items-center justify-between border-t border-none bg-transparent px-3 py-2 pl-[22px] text-xs font-medium transition-all duration-150 hover:bg-white/[0.03]"
27022674
>
2675+
<div className="flex items-center gap-1.5">
2676+
<span>Older than {thresholdLabel}</span>
2677+
<span className="text-dim font-normal">
2678+
({displayCount})
2679+
</span>
2680+
</div>
27032681
<span
27042682
className="arrow text-dim text-[11px] transition-transform duration-200 ease-in-out"
27052683
style={{
@@ -2708,14 +2686,11 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
27082686
: "rotate(0deg)",
27092687
}}
27102688
>
2711-
<ChevronRight className="h-4 w-4" />
2689+
<ChevronRight
2690+
className="h-4 w-4 shrink-0"
2691+
strokeWidth={1.8}
2692+
/>
27122693
</span>
2713-
<div className="flex items-center gap-1.5">
2714-
<span>Older than {thresholdLabel}</span>
2715-
<span className="text-dim font-normal">
2716-
({displayCount})
2717-
</span>
2718-
</div>
27192694
</button>
27202695
{isTierExpanded && (
27212696
<>
@@ -2968,6 +2943,12 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
29682943

29692944
{/* Sections */}
29702945
{sections.map(renderSection)}
2946+
2947+
<AddSectionButton
2948+
onCreateSection={(name) => {
2949+
void handleCreateSection(projectPath, name);
2950+
}}
2951+
/>
29712952
</>
29722953
);
29732954
})()}

src/browser/components/ProjectSidebar/TaskGroupListItem.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export function TaskGroupListItem(props: TaskGroupListItemProps) {
5656
data-testid={`task-group-${props.groupId}`}
5757
className={cn(
5858
"bg-surface-primary relative flex items-start gap-1.5 rounded-l-sm py-2 pr-2 pl-1 select-none transition-all duration-150 hover:bg-surface-secondary",
59-
props.sectionId != null ? "ml-7.5" : "ml-5",
6059
props.isSelected && "bg-surface-secondary"
6160
)}
6261
style={{ paddingLeft }}
@@ -72,7 +71,7 @@ export function TaskGroupListItem(props: TaskGroupListItemProps) {
7271
>
7372
<span
7473
aria-hidden="true"
75-
className="text-muted mt-0.5 -ml-4 inline-flex h-4 w-4 shrink-0 items-center justify-center"
74+
className="text-muted mt-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center"
7675
>
7776
<ChevronRight
7877
className="h-3 w-3 transition-transform duration-150"

0 commit comments

Comments
 (0)