diff --git a/ui/src/components/health/health-priority-card.tsx b/ui/src/components/health/health-priority-card.tsx index 9c817807..b74df697 100644 --- a/ui/src/components/health/health-priority-card.tsx +++ b/ui/src/components/health/health-priority-card.tsx @@ -127,14 +127,20 @@ export function HealthPriorityCard({ check }: HealthPriorityCardProps) { {(check.fix || check.fixable) && (
{check.fix && ( -
+
{check.fix} + {/* + * Always-visible copy button. The previous design hid it behind + * group-hover, which excluded keyboard users and touch-only + * devices (no hover state on mobile = effectively unreachable). + */} diff --git a/ui/src/components/health/health-status-ribbon.tsx b/ui/src/components/health/health-status-ribbon.tsx index 320ece6d..5e6c9867 100644 --- a/ui/src/components/health/health-status-ribbon.tsx +++ b/ui/src/components/health/health-status-ribbon.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react'; import { Cpu, RefreshCw, Terminal, Copy } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; @@ -41,6 +42,18 @@ export function HealthStatusRibbon({ }: HealthStatusRibbonProps) { const { t } = useTranslation(); + // formatRelativeTime reads Date.now() during render. Without a ticking + // re-render, the "last scan" label freezes at whatever it showed on mount + // and never advances to "1 minute ago", "2 hours ago", etc. — even though + // the underlying lastScan timestamp is stale. Force a re-render every + // second; the cost is negligible (one tiny component) and the UX win is + // a label that actually behaves like a relative timestamp. + const [, setTick] = useState(0); + useEffect(() => { + const interval = setInterval(() => setTick((n) => n + 1), 1000); + return () => clearInterval(interval); + }, []); + const copyDoctorCommand = () => { navigator.clipboard.writeText('ccs doctor'); toast.success(t('health.copied'));