diff --git a/desktop/src/components/shared/DirectoryPicker.test.tsx b/desktop/src/components/shared/DirectoryPicker.test.tsx index eeb86578..5565c968 100644 --- a/desktop/src/components/shared/DirectoryPicker.test.tsx +++ b/desktop/src/components/shared/DirectoryPicker.test.tsx @@ -70,10 +70,42 @@ describe('DirectoryPicker', () => { const trigger = screen.getByRole('button') expect(trigger).toHaveTextContent('project') - expect(trigger.className).toContain('rounded-lg') + expect(trigger.className).toContain('rounded-[7px]') expect(trigger.className).not.toContain('rounded-full') }) + it('constrains long workbar project names without hiding the full path from hover users', () => { + const longProjectName = 'project-with-a-very-long-directory-name-that-should-not-stretch-the-launch-bar' + const longPath = `/workspace/${longProjectName}` + + render( + , + ) + + const trigger = screen.getByRole('button') + const label = screen.getByText(longProjectName) + expect(trigger).toHaveAttribute('title', longPath) + expect(trigger.className).toContain('w-full') + expect(label.className).toContain('truncate') + }) + + it('can show a Git icon for workbar projects before the recent-project cache is loaded', () => { + render( + , + ) + + expect(screen.getByRole('button').querySelector('svg')).toBeInTheDocument() + }) + it('renders browse entries without nesting interactive buttons', async () => { vi.mocked(sessionsApi.getRecentProjects).mockResolvedValue({ projects: [] }) vi.mocked(filesystemApi.browse).mockResolvedValue({ diff --git a/desktop/src/components/shared/DirectoryPicker.tsx b/desktop/src/components/shared/DirectoryPicker.tsx index e4b15fdf..124977bf 100644 --- a/desktop/src/components/shared/DirectoryPicker.tsx +++ b/desktop/src/components/shared/DirectoryPicker.tsx @@ -8,6 +8,7 @@ type Props = { value: string onChange: (path: string) => void variant?: 'chip' | 'workbar' + isGitProject?: boolean } type DirEntry = { name: string; path: string; isDirectory: boolean } @@ -29,7 +30,7 @@ function projectNameFromPath(filePath: string) { return displayRoot.split('/').filter(Boolean).pop() || filePath } -export function DirectoryPicker({ value, onChange, variant = 'chip' }: Props) { +export function DirectoryPicker({ value, onChange, variant = 'chip', isGitProject = false }: Props) { const t = useTranslation() const [isOpen, setIsOpen] = useState(false) const [mode, setMode] = useState<'recent' | 'browse'>('recent') @@ -146,42 +147,46 @@ export function DirectoryPicker({ value, onChange, variant = 'chip' }: Props) { // Find selected project info const selectedProject = projects.find((p) => p.realPath === value) const isWorkbar = variant === 'workbar' + const selectedLabel = selectedProject?.repoName || selectedProject?.projectName || projectNameFromPath(value) + const showGitIcon = selectedProject?.isGit || isGitProject const triggerClassName = isWorkbar - ? 'flex min-w-0 max-w-full items-center gap-2 rounded-lg px-2.5 py-2 text-sm font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35' + ? 'flex h-9 w-full min-w-0 items-center gap-1.5 rounded-[7px] border border-transparent px-2.5 text-[13px] font-medium leading-none text-[var(--color-text-secondary)] transition-colors hover:border-[var(--color-border)] hover:bg-[var(--color-surface-container-lowest)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35' : 'flex items-center gap-2 px-3 py-1.5 bg-[var(--color-surface-container-low)] hover:bg-[var(--color-surface-hover)] rounded-full text-xs transition-colors border border-[var(--color-border)]' const emptyTriggerClassName = isWorkbar - ? 'flex min-w-0 items-center gap-2 rounded-lg px-2.5 py-2 text-sm font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35' + ? 'flex h-9 min-w-0 items-center gap-1.5 rounded-[7px] border border-transparent px-2.5 text-[13px] font-medium leading-none text-[var(--color-text-secondary)] transition-colors hover:border-[var(--color-border)] hover:bg-[var(--color-surface-container-lowest)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35' : 'flex items-center gap-2 text-xs text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors' return ( -
+
{/* Trigger — shows selected project chip or placeholder */} {value ? ( ) : ( )} diff --git a/desktop/src/components/shared/RepositoryLaunchControls.tsx b/desktop/src/components/shared/RepositoryLaunchControls.tsx index 524fc448..184c1b94 100644 --- a/desktop/src/components/shared/RepositoryLaunchControls.tsx +++ b/desktop/src/components/shared/RepositoryLaunchControls.tsx @@ -256,15 +256,16 @@ export function RepositoryLaunchControls({ onLaunchReadyChange?.(isLaunchReady) }, [isLaunchReady, onLaunchReadyChange]) - const workbarButtonClassName = 'inline-flex min-w-0 items-center gap-2 rounded-lg px-2.5 py-2 text-sm font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35 disabled:cursor-not-allowed disabled:opacity-50' + const worktreeLabel = useWorktree ? t('repoLaunch.worktreeIsolated') : t('repoLaunch.worktreeCurrent') + const workbarButtonClassName = 'group inline-flex h-9 min-w-0 items-center gap-1.5 rounded-[7px] border border-transparent px-2.5 text-[13px] font-medium leading-none text-[var(--color-text-secondary)] transition-colors hover:border-[var(--color-border)] hover:bg-[var(--color-surface-container-lowest)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35 disabled:cursor-not-allowed disabled:opacity-50' return (
-
- +
+ {loading && workDir && ( -
+
{t('common.loading')}
@@ -272,7 +273,7 @@ export function RepositoryLaunchControls({ {isGitReady && ( <> -