fix: keep compact status bar on one row

This commit is contained in:
lucaronin
2026-04-26 10:33:16 +02:00
parent aebd2bea4a
commit 133242eff5
4 changed files with 82 additions and 18 deletions

View File

@@ -353,21 +353,42 @@ describe('StatusBar', () => {
})
it('collapses status labels to icon-first controls at very narrow widths', () => {
setWindowWidth(760)
setWindowWidth(880)
renderDenseStatusBar()
expect(screen.getByTestId('status-bar')).toHaveStyle({
flexWrap: 'wrap',
height: 'auto',
flexWrap: 'nowrap',
height: '30px',
})
expect(screen.getByTestId('status-commit-push')).toBeInTheDocument()
expect(screen.getByTestId('status-pulse')).toBeInTheDocument()
expect(screen.getByTestId('status-feedback')).toBeInTheDocument()
expect(screen.getByTestId('status-build-number')).toBeInTheDocument()
expect(screen.getByTestId('status-claude-code')).toBeInTheDocument()
expect(screen.queryByText('Commit')).not.toBeInTheDocument()
expect(screen.queryByText('History')).not.toBeInTheDocument()
expect(screen.queryByText('Contribute')).not.toBeInTheDocument()
expect(screen.queryByText('No remote')).not.toBeInTheDocument()
expect(screen.queryByText('MCP')).not.toBeInTheDocument()
expect(screen.queryByText('b281')).not.toBeInTheDocument()
expect(screen.queryByText('Claude Code missing')).not.toBeInTheDocument()
})
it('hides the active AI agent label in compact status layout', () => {
setWindowWidth(880)
render(
<StatusBar
noteCount={100}
vaultPath="/Users/luca/Laputa"
vaults={vaults}
onSwitchVault={vi.fn()}
aiAgentsStatus={installedAiAgentsStatus}
defaultAiAgent="claude_code"
/>
)
expect(screen.getByTestId('status-ai-agents')).toBeInTheDocument()
expect(screen.queryByText('Claude')).not.toBeInTheDocument()
})
it('does not show Changes badge when modifiedCount is 0', () => {

View File

@@ -15,16 +15,18 @@ import type { VaultOption } from './status-bar/types'
export type { VaultOption } from './status-bar/types'
const STACKED_STATUS_BAR_MAX_WIDTH = 1040
const COMPACT_STATUS_BAR_MAX_WIDTH = 820
const COMPACT_STATUS_BAR_MAX_WIDTH = 900
function getWindowWidth() {
return typeof window === 'undefined' ? Number.POSITIVE_INFINITY : window.innerWidth
}
function getStatusBarLayout(windowWidth: number) {
const compact = windowWidth <= COMPACT_STATUS_BAR_MAX_WIDTH
return {
compact: windowWidth <= COMPACT_STATUS_BAR_MAX_WIDTH,
stacked: windowWidth <= STACKED_STATUS_BAR_MAX_WIDTH,
compact,
stacked: !compact && windowWidth <= STACKED_STATUS_BAR_MAX_WIDTH,
}
}
@@ -154,7 +156,7 @@ function StatusBarFooter({
alignItems: stacked ? 'flex-start' : 'center',
justifyContent: stacked ? 'flex-start' : 'space-between',
rowGap: stacked ? 4 : 0,
columnGap: 12,
columnGap: compact ? 8 : 12,
background: 'var(--sidebar)',
borderTop: '1px solid var(--border)',
padding: stacked ? '4px 8px' : '0 8px',

View File

@@ -38,6 +38,7 @@ interface AiAgentsBadgeProps {
defaultAgent: AiAgentId
onSetDefaultAgent?: (agent: AiAgentId) => void
onRestoreGuidance?: () => void
compact?: boolean
}
function badgeTooltip(
@@ -95,6 +96,48 @@ function canSwitchAgents(
return installedAgents.some((definition) => definition.id !== defaultAgent)
}
function hasAiAgentWarning(
statuses: AiAgentsStatus,
defaultAgent: AiAgentId,
guidanceStatus?: VaultAiGuidanceStatus,
): boolean {
return !hasAnyInstalledAiAgent(statuses)
|| !isAiAgentInstalled(statuses, defaultAgent)
|| !!(guidanceStatus && vaultAiGuidanceNeedsRestore(guidanceStatus))
}
function canShowSwitcherCue(statuses: AiAgentsStatus, defaultAgent: AiAgentId): boolean {
return canSwitchAgents(installedAgentDefinitions(statuses), defaultAgent)
}
function triggerButtonClassName(compact: boolean): string {
return compact
? 'h-6 w-6 rounded-sm p-0 text-[11px] font-medium'
: 'h-6 px-2 text-[11px] font-medium'
}
function CompactSeparator({ compact }: { compact: boolean }) {
if (compact) return null
return <span style={SEP_STYLE}>|</span>
}
function TriggerLabel({ compact, defaultAgent }: { compact: boolean; defaultAgent: AiAgentId }) {
if (compact) return null
return triggerLabel(defaultAgent)
}
function TriggerStateIcon({
showWarning,
showSwitcherCue,
}: {
showWarning: boolean
showSwitcherCue: boolean
}) {
if (showWarning) return <AlertTriangle size={10} style={{ marginLeft: 2 }} />
if (showSwitcherCue) return <ChevronsUpDown size={10} style={{ marginLeft: 2 }} />
return null
}
function GuidanceMenuSection({
guidanceStatus,
onRestoreGuidance,
@@ -184,19 +227,17 @@ export function AiAgentsBadge({
defaultAgent,
onSetDefaultAgent,
onRestoreGuidance,
compact = false,
}: AiAgentsBadgeProps) {
const hasInstalledAgent = hasAnyInstalledAiAgent(statuses)
const selectedAgentReady = isAiAgentInstalled(statuses, defaultAgent)
const showWarning = !hasInstalledAgent
|| !selectedAgentReady
|| !!(guidanceStatus && vaultAiGuidanceNeedsRestore(guidanceStatus))
const showSwitcherCue = canSwitchAgents(installedAgentDefinitions(statuses), defaultAgent)
const showWarning = hasAiAgentWarning(statuses, defaultAgent, guidanceStatus)
const showSwitcherCue = !showWarning && canShowSwitcherCue(statuses, defaultAgent)
if (isAiAgentsStatusChecking(statuses)) return null
return (
<>
<span style={SEP_STYLE}>|</span>
<CompactSeparator compact={compact} />
<DropdownMenu>
<ActionTooltip copy={{ label: badgeTooltip(statuses, defaultAgent, guidanceStatus) }} side="top">
<DropdownMenuTrigger asChild={true}>
@@ -204,15 +245,14 @@ export function AiAgentsBadge({
type="button"
variant="ghost"
size="xs"
className="h-6 px-2 text-[11px] font-medium"
className={triggerButtonClassName(compact)}
aria-label="Open AI agent options"
data-testid="status-ai-agents"
>
<span style={{ ...ICON_STYLE, color: showWarning ? 'var(--accent-orange)' : 'var(--muted-foreground)' }}>
<Sparkle size={13} weight="fill" />
{triggerLabel(defaultAgent)}
{showWarning && <AlertTriangle size={10} style={{ marginLeft: 2 }} />}
{!showWarning && showSwitcherCue && <ChevronsUpDown size={10} style={{ marginLeft: 2 }} />}
<TriggerLabel compact={compact} defaultAgent={defaultAgent} />
<TriggerStateIcon showWarning={showWarning} showSwitcherCue={showSwitcherCue} />
</span>
</Button>
</DropdownMenuTrigger>

View File

@@ -118,7 +118,7 @@ function BuildNumberButton({
>
<span style={ICON_STYLE}>
<Package size={13} />
{buildNumber ?? 'b?'}
{compact ? null : buildNumber ?? 'b?'}
</span>
</Button>
</ActionTooltip>
@@ -153,6 +153,7 @@ function StatusBarAiBadge({
defaultAgent={defaultAiAgent}
onSetDefaultAgent={onSetDefaultAiAgent}
onRestoreGuidance={onRestoreVaultAiGuidance}
compact={compact}
/>
)
}