From 2b2e756ab030aea094efcc9d884a7881d65062d3 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:54:31 -0700 Subject: [PATCH] fix(source-control): compact detached head identity --- .../src/components/DetachedHeadBadge.tsx | 3 +- .../right-sidebar/SourceControl.tsx | 10 +----- .../source-control-header-toolbar.test.tsx | 36 ++++++++++++++++--- .../source-control-header-toolbar.tsx | 25 ++++++++++--- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/components/DetachedHeadBadge.tsx b/src/renderer/src/components/DetachedHeadBadge.tsx index 58a12e6395e..461c202d8f5 100644 --- a/src/renderer/src/components/DetachedHeadBadge.tsx +++ b/src/renderer/src/components/DetachedHeadBadge.tsx @@ -27,6 +27,7 @@ export function DetachedHeadBadge({ - {visibleLabel} + {visibleLabel} diff --git a/src/renderer/src/components/right-sidebar/SourceControl.tsx b/src/renderer/src/components/right-sidebar/SourceControl.tsx index 3bd3fc27c13..4673c234d9d 100644 --- a/src/renderer/src/components/right-sidebar/SourceControl.tsx +++ b/src/renderer/src/components/right-sidebar/SourceControl.tsx @@ -45,7 +45,6 @@ import { WORKSPACE_FILE_PATH_MIME } from '@/lib/workspace-file-drag' import { isFolderRepo } from '../../../../shared/repo-kind' import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip' import { Button } from '@/components/ui/button' -import { DetachedHeadBadge } from '@/components/DetachedHeadBadge' import { DropdownMenu, DropdownMenuContent, @@ -808,7 +807,6 @@ function SourceControlInner(): React.JSX.Element { const activeRepoConnectionId = activeRepo?.connectionId ?? null const activeRepoExecutionHostId = activeRepo?.executionHostId ?? null const gitIdentityDisplay = activeWorktree ? getWorktreeGitIdentityDisplay(activeWorktree) : null - const detachedHeadDisplay = gitIdentityDisplay?.kind === 'detached' ? gitIdentityDisplay : null const branchName = gitIdentityDisplay?.kind === 'branch' ? gitIdentityDisplay.branchName : '' const entries = useAppStore((s) => activeWorktreeId @@ -5446,7 +5444,7 @@ function SourceControlInner(): React.JSX.Element { <>
- {detachedHeadDisplay && ( -
- -
- )} - {/* Why: hidden when count is 0 — notes are created from the diff view, so an empty Notes shelf here is pure chrome. */} {activeWorktreeId && worktreePath && diffCommentCount > 0 && (
diff --git a/src/renderer/src/components/right-sidebar/source-control-header-toolbar.test.tsx b/src/renderer/src/components/right-sidebar/source-control-header-toolbar.test.tsx index f0fb8aa53c5..3fe0fc97769 100644 --- a/src/renderer/src/components/right-sidebar/source-control-header-toolbar.test.tsx +++ b/src/renderer/src/components/right-sidebar/source-control-header-toolbar.test.tsx @@ -1,6 +1,7 @@ import { renderToStaticMarkup } from 'react-dom/server' import type { ReactNode } from 'react' import { describe, expect, it, vi } from 'vitest' +import type { WorktreeGitIdentityDisplay } from '@/lib/worktree-git-identity-display' import { SourceControlHeaderToolbar } from './source-control-header-toolbar' vi.mock('@/components/ui/tooltip', () => ({ @@ -18,10 +19,15 @@ vi.mock('./source-control-branch-context-row', () => ({ SourceControlBranchContextRow: () => null })) -function renderToolbar(branchName = 'brennanb2025/source-control-branch-name'): string { +function renderToolbar( + gitIdentityDisplay: WorktreeGitIdentityDisplay | null = { + kind: 'branch', + branchName: 'brennanb2025/source-control-branch-name' + } +): string { return renderToStaticMarkup( { expect(markup).toContain('min-w-0 truncate') }) - it('does not announce a branch while HEAD is detached', () => { - const markup = renderToolbar('') + it('renders detached HEAD in the same identity slot', () => { + const markup = renderToolbar({ + kind: 'detached', + shortHead: '8cec248', + sidebarLabel: 'Detached HEAD @ 8cec248', + sourceControlLabel: 'Detached HEAD · 8cec248', + tooltip: 'Detached HEAD at 8cec248. You are viewing a commit, not a branch.' + }) + const identityIndex = markup.indexOf('Detached HEAD · 8cec248') + const filterIndex = markup.indexOf('data-testid="source-control-filter-toggle"') expect(markup).not.toContain('aria-label="Current branch:') - expect(markup).not.toContain('lucide-git-branch') + expect(identityIndex).toBeGreaterThan(-1) + expect(filterIndex).toBeGreaterThan(identityIndex) + expect(markup).toContain( + 'aria-label="Detached HEAD at 8cec248. You are viewing a commit, not a branch."' + ) + expect(markup).toContain('lucide-git-commit-horizontal') + }) + + it('keeps the identity slot empty until git identity is known', () => { + const markup = renderToolbar(null) + + expect(markup).not.toContain('aria-label="Current branch:') + expect(markup).not.toContain('Detached HEAD') }) }) diff --git a/src/renderer/src/components/right-sidebar/source-control-header-toolbar.tsx b/src/renderer/src/components/right-sidebar/source-control-header-toolbar.tsx index 27f1f1bc1d4..92bdf3534d8 100644 --- a/src/renderer/src/components/right-sidebar/source-control-header-toolbar.tsx +++ b/src/renderer/src/components/right-sidebar/source-control-header-toolbar.tsx @@ -10,6 +10,8 @@ import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' +import { DetachedHeadBadge } from '@/components/DetachedHeadBadge' +import type { WorktreeGitIdentityDisplay } from '@/lib/worktree-git-identity-display' import { shouldShowSourceControlBranchContextRow, SourceControlBranchContextRow @@ -17,7 +19,7 @@ import { import { SourceControlHeaderOverflowMenu } from './source-control-header-overflow-menu' type SourceControlHeaderToolbarProps = { - branchName: string + gitIdentityDisplay: WorktreeGitIdentityDisplay | null filterQuery: string filterExpanded: boolean onFilterQueryChange: (value: string) => void @@ -40,7 +42,20 @@ type SourceControlHeaderToolbarProps = { manualReviewUrl?: string | null } -function SourceControlBranchHeaderLabel({ branchName }: { branchName: string }): React.JSX.Element { +function SourceControlGitIdentityLabel({ + display +}: { + display: WorktreeGitIdentityDisplay +}): React.JSX.Element { + if (display.kind === 'detached') { + return ( + + + + ) + } + + const branchName = display.branchName const label = translate( 'auto.components.right.sidebar.SourceControl.a4e93c21d7', 'Current branch: {{value0}}', @@ -121,7 +136,7 @@ function renderOverflowMenu( } export function SourceControlHeaderToolbar({ - branchName, + gitIdentityDisplay, filterQuery, filterExpanded, onFilterQueryChange, @@ -192,8 +207,8 @@ export function SourceControlHeaderToolbar({ > {showCollapsedToolbar ? ( <> - {branchName ? ( - + {gitIdentityDisplay ? ( + ) : (