diff --git a/packages/ui/src/components/layout/ContextPanelRail.tsx b/packages/ui/src/components/layout/ContextPanelRail.tsx index 43692cbb..f7f5ce0d 100644 --- a/packages/ui/src/components/layout/ContextPanelRail.tsx +++ b/packages/ui/src/components/layout/ContextPanelRail.tsx @@ -49,17 +49,30 @@ type RailItemProps = { showActivityDot: boolean; label: string; description: string; + /** Numeric badge (e.g. the Git changed-files count); takes precedence over the activity dot. */ + badgeCount?: number | null; + /** Accessible label that includes the badge count; falls back to `label`. */ + badgeAriaLabel?: string | null; + /** Extra tooltip line describing the badge; rendered under the description. */ + badgeDescription?: string | null; orderNumber?: number | null; showOrderNumber?: boolean; onSelect: (surface: ContextSurfaceDescriptor) => void; }; +// The badge corner is 16px tall; cap large counts so the pill stays compact +// on the 36px rail button (matching the order-number badge's footprint). +const formatRailBadgeCount = (count: number): string => (count > 99 ? '99+' : String(count)); + const ContextPanelRailItem: React.FC = ({ surface, isActive, showActivityDot, label, description, + badgeCount, + badgeAriaLabel, + badgeDescription, orderNumber, showOrderNumber, onSelect, @@ -68,6 +81,8 @@ const ContextPanelRailItem: React.FC = ({ id: surface.id, }); + const displayBadgeCount = badgeCount != null && badgeCount > 0 ? formatRailBadgeCount(badgeCount) : null; + return (
= ({ {...attributes} {...listeners} onClick={() => onSelect(surface)} - aria-label={label} + aria-label={badgeAriaLabel ?? label} aria-pressed={isActive} className={cn( 'flex h-9 w-9 touch-none select-none items-center justify-center rounded-md transition-colors', @@ -95,12 +110,6 @@ const ContextPanelRailItem: React.FC = ({ ) : ( )} - {showActivityDot && !showOrderNumber ? ( -