mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 00:03:20 +00:00
fix(source-control): compact detached head identity
This commit is contained in:
@@ -27,6 +27,7 @@ export function DetachedHeadBadge({
|
||||
<TooltipTrigger asChild>
|
||||
<Badge
|
||||
variant="outline"
|
||||
aria-label={display.tooltip}
|
||||
className={cn(
|
||||
'h-[18px] shrink-0 gap-1 rounded px-1.5 text-[10px] font-medium leading-none',
|
||||
'border-[color:color-mix(in_srgb,var(--git-decoration-modified)_30%,transparent)] bg-[color:color-mix(in_srgb,var(--git-decoration-modified)_8%,transparent)] text-[color:var(--git-decoration-modified)]',
|
||||
@@ -34,7 +35,7 @@ export function DetachedHeadBadge({
|
||||
)}
|
||||
>
|
||||
<GitCommitHorizontal className="size-2.5" />
|
||||
{visibleLabel}
|
||||
<span className="min-w-0 truncate">{visibleLabel}</span>
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={side} sideOffset={8}>
|
||||
|
||||
@@ -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 {
|
||||
<>
|
||||
<div ref={setSourceControlRoot} className="relative flex h-full flex-col overflow-hidden">
|
||||
<SourceControlHeaderToolbar
|
||||
branchName={branchName}
|
||||
gitIdentityDisplay={gitIdentityDisplay}
|
||||
filterQuery={filterQuery}
|
||||
filterExpanded={filterExpanded}
|
||||
onFilterQueryChange={setFilterQuery}
|
||||
@@ -5469,12 +5467,6 @@ function SourceControlInner(): React.JSX.Element {
|
||||
manualReviewUrl={manualReviewUrl}
|
||||
/>
|
||||
|
||||
{detachedHeadDisplay && (
|
||||
<div className="border-b border-border px-3 py-2">
|
||||
<DetachedHeadBadge display={detachedHeadDisplay} side="bottom" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 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 && (
|
||||
<div className="border-b border-border">
|
||||
|
||||
@@ -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(
|
||||
<SourceControlHeaderToolbar
|
||||
branchName={branchName}
|
||||
gitIdentityDisplay={gitIdentityDisplay}
|
||||
filterQuery=""
|
||||
filterExpanded={false}
|
||||
onFilterQueryChange={vi.fn()}
|
||||
@@ -56,10 +62,30 @@ describe('SourceControlHeaderToolbar', () => {
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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 (
|
||||
<span className="flex min-w-0 flex-1 items-center">
|
||||
<DetachedHeadBadge display={display} side="bottom" className="min-w-0 max-w-full shrink" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
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 ? (
|
||||
<SourceControlBranchHeaderLabel branchName={branchName} />
|
||||
{gitIdentityDisplay ? (
|
||||
<SourceControlGitIdentityLabel display={gitIdentityDisplay} />
|
||||
) : (
|
||||
<span className="min-w-0 flex-1" aria-hidden="true" />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user