fix: keep compact status bar on one row
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user