fix: restructure sidebar project header to prevent name truncation

Restructure the ProjectGroup header from a single cramped line into a
two-row layout so the project name gets adequate space:

- Row 1: chevron + icon + project name (min-w-0 flex-1) + hover actions
- Row 2: git branch badge + running/discovery/session count badges

Additional polish:
- Branch label uses font-mono (JetBrains Mono) for developer feel
- Branch max-width increased from 80px to 140px for better readability
- Project name tooltip shows full name and path on hover
- Scratchpad keeps session count inline on the identity row

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-30 17:02:47 +02:00
co-authored by Copilot
parent 5946dc75ba
commit 9647b5fdb5
+26 -9
View File
@@ -120,7 +120,7 @@ function GitContextBadge({ git }: { git: ProjectGitContext }) {
return ( return (
<span className="flex items-center gap-1 text-[10px] text-[var(--color-text-muted)]" title={parts.join(' · ') || branchLabel}> <span className="flex items-center gap-1 text-[10px] text-[var(--color-text-muted)]" title={parts.join(' · ') || branchLabel}>
<GitBranch className="size-2.5 shrink-0" /> <GitBranch className="size-2.5 shrink-0" />
<span className="max-w-[80px] truncate">{branchLabel}</span> <span className="max-w-[140px] truncate font-mono">{branchLabel}</span>
{git.isDirty && <Circle className="size-1.5 shrink-0 fill-amber-500 text-amber-500" />} {git.isDirty && <Circle className="size-1.5 shrink-0 fill-amber-500 text-amber-500" />}
</span> </span>
); );
@@ -396,10 +396,13 @@ function ProjectGroup({
return ( return (
<div> <div>
<button <button
className="group flex w-full items-center gap-2 rounded-lg px-2 py-2 text-left text-[13px] font-semibold text-[var(--color-text-secondary)] transition-all duration-150 hover:bg-[var(--color-surface-2)]/40 hover:text-[var(--color-text-primary)]" className="group flex w-full flex-col gap-0.5 rounded-lg px-2 py-2 text-left transition-all duration-150 hover:bg-[var(--color-surface-2)]/40"
onClick={() => setExpanded(!expanded)} onClick={() => setExpanded(!expanded)}
type="button" type="button"
title={`${project.name}\n${project.path}`}
> >
{/* Row 1 — project identity + hover actions */}
<div className="flex w-full items-center gap-2 text-[13px] font-semibold text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]">
{expanded ? ( {expanded ? (
<ChevronDown className="size-3 shrink-0 text-[var(--color-text-muted)]" /> <ChevronDown className="size-3 shrink-0 text-[var(--color-text-muted)]" />
) : ( ) : (
@@ -410,14 +413,17 @@ function ProjectGroup({
) : ( ) : (
<FolderOpen className="size-3.5 shrink-0 text-[var(--color-text-muted)] transition group-hover:text-[var(--color-accent)]" /> <FolderOpen className="size-3.5 shrink-0 text-[var(--color-text-muted)] transition group-hover:text-[var(--color-accent)]" />
)} )}
<span className="truncate">{project.name}</span> <span className="min-w-0 flex-1 truncate">{project.name}</span>
{!isScratchpad && project.git && ( {isScratchpad && (
<GitContextBadge git={project.git} /> <span className="shrink-0 rounded-full bg-[var(--color-surface-2)] px-1.5 py-0.5 text-[10px] font-medium text-[var(--color-text-muted)]">
{visibleSessions.length}
</span>
)} )}
<div className="ml-auto flex items-center gap-1.5"> {!isScratchpad && (onOpenProjectSettings || onRefreshGitContext) && (
{!isScratchpad && onOpenProjectSettings && ( <div className="flex shrink-0 items-center gap-0.5">
{onOpenProjectSettings && (
<span <span
className="flex size-5 items-center justify-center rounded text-[var(--color-text-muted)] opacity-0 transition hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)] group-hover:opacity-100" className="flex size-5 items-center justify-center rounded text-[var(--color-text-muted)] opacity-0 transition hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)] group-hover:opacity-100"
onClick={(e) => { onClick={(e) => {
@@ -430,7 +436,7 @@ function ProjectGroup({
<Settings className="size-3" /> <Settings className="size-3" />
</span> </span>
)} )}
{!isScratchpad && onRefreshGitContext && ( {onRefreshGitContext && (
<span <span
className="flex size-5 items-center justify-center rounded text-[var(--color-text-muted)] opacity-0 transition hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)] group-hover:opacity-100" className="flex size-5 items-center justify-center rounded text-[var(--color-text-muted)] opacity-0 transition hover:bg-[var(--color-surface-3)] hover:text-[var(--color-text-primary)] group-hover:opacity-100"
onClick={(e) => { onClick={(e) => {
@@ -443,6 +449,15 @@ function ProjectGroup({
<RefreshCw className="size-3" /> <RefreshCw className="size-3" />
</span> </span>
)} )}
</div>
)}
</div>
{/* Row 2 — metadata strip: branch, status badges, session count */}
{!isScratchpad && (project.git || runningCount > 0 || pendingDiscoveryCount > 0 || visibleSessions.length > 0) && (
<div className="ml-[26px] flex items-center gap-2">
{project.git && <GitContextBadge git={project.git} />}
{runningCount > 0 && ( {runningCount > 0 && (
<span className="flex items-center gap-1 rounded-full bg-[var(--color-accent-sky)]/10 px-1.5 py-0.5 text-[10px] font-medium text-[var(--color-accent-sky)]"> <span className="flex items-center gap-1 rounded-full bg-[var(--color-accent-sky)]/10 px-1.5 py-0.5 text-[10px] font-medium text-[var(--color-accent-sky)]">
<span className="size-1.5 rounded-full bg-[var(--color-accent-sky)] sidebar-pulse" /> <span className="size-1.5 rounded-full bg-[var(--color-accent-sky)] sidebar-pulse" />
@@ -462,10 +477,12 @@ function ProjectGroup({
{pendingDiscoveryCount} new {pendingDiscoveryCount} new
</span> </span>
)} )}
<span className="rounded-full bg-[var(--color-surface-2)] px-1.5 py-0.5 text-[10px] font-medium text-[var(--color-text-muted)]"> <span className="ml-auto rounded-full bg-[var(--color-surface-2)] px-1.5 py-0.5 text-[10px] font-medium text-[var(--color-text-muted)]">
{visibleSessions.length} {visibleSessions.length}
</span> </span>
</div> </div>
)}
</button> </button>
{expanded && ( {expanded && (