fix: make Reduced motion gate overlays, standardize the tab band, and polish fleet/snapshots (#1504)

* fix: address review feedback on motion, fleet band, and snapshots

- Reduced motion now also skips the animate-ui overlay open/close animations
  (dialog, sheet, popover, dropdown-menu) via a shared useReducedTransition that
  zeroes the transition when reduced motion is active; MotionConfig alone only
  neutralized transform/layout, leaving the opacity/blur fade.
- Fleet tab band: flatten the list's own pill band so the tabs sit in a single
  full-width band instead of a nested second band.
- Fleet Overview: label the node-update button "Node Update"; the add-node
  button becomes a ChartNetwork icon that opens Settings > Nodes (outline style,
  matching the node-update button) instead of the add-node modal.
- Blueprint deployments empty state: drive the serif headings from the theme
  heading-style token so Calm drops the italic.
- Snapshots: move the per-stack Restore onto the stack header row (right side),
  matching the ghost action style.

* feat: standardize the tab band on Security and add a tab hover state

- Apply the Fleet full-width tab band to the Security page (single band, the
  list's pill background flattened so the tabs sit directly in it).
- Add a hover highlight to tabs so the band reads as interactive, not flat;
  the active tab is unaffected.

* fix: drive reduced-motion transitions from the app setting, not the OS query

useReducedTransition called framer-motion's useReducedMotion(), which only
reflects the OS prefers-reduced-motion media query and ignores our MotionConfig
/ appearance toggle, so dialogs kept animating with the setting on. Read the
app's useReducedMotion selector instead. Verified: with the setting on, the
dialog opacity snaps 0->1 in one frame; with it off, it ramps over the spring.

* feat: standardize stack-detail body text to text-xs and add a log expand toggle

- Bump the anatomy panel body text (rows, field values, warnings) and the
  structured log lines from 10/11/12px to text-xs (12px), leaving the uppercase
  kicker labels and the log level/source badges as-is.
- Add an expand control next to the log download button that collapses the
  Command Center so the logs pane fills the left column; toggles back to restore.
This commit is contained in:
Anso
2026-06-28 08:17:34 -04:00
committed by GitHub
parent 04e69021e0
commit d6ce60d280
17 changed files with 167 additions and 91 deletions
@@ -1,12 +1,15 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { Button } from './ui/button';
import { Download, RefreshCw } from 'lucide-react';
import { Download, RefreshCw, Maximize2, Minimize2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useLogChipColorMode } from '@/hooks/use-log-chip-color-mode';
import { hashLabel } from '@/lib/label-colors';
interface StructuredLogViewerProps {
stackName: string;
/** When set, renders an expand/collapse control next to the download button. */
expanded?: boolean;
onToggleExpand?: () => void;
}
type LogLevel = 'info' | 'warn' | 'err';
@@ -65,7 +68,7 @@ function formatTs(iso: string | null): string {
return `${hh}:${mm}:${ss}`;
}
export default function StructuredLogViewer({ stackName }: StructuredLogViewerProps) {
export default function StructuredLogViewer({ stackName, expanded, onToggleExpand }: StructuredLogViewerProps) {
const [rows, setRows] = useState<LogRow[]>([]);
const [filter, setFilter] = useState<Filter>('all');
const [following, setFollowing] = useState(true);
@@ -281,6 +284,21 @@ export default function StructuredLogViewer({ stackName }: StructuredLogViewerPr
</button>
))}
<div className="mx-1 h-4 w-px bg-muted" />
{onToggleExpand && (
<Button
type="button"
size="sm"
variant="ghost"
className="h-7 w-7 p-0"
onClick={onToggleExpand}
aria-label={expanded ? 'Collapse logs' : 'Expand logs'}
title={expanded ? 'Collapse logs' : 'Expand logs'}
>
{expanded
? <Minimize2 className="h-3.5 w-3.5" strokeWidth={1.5} />
: <Maximize2 className="h-3.5 w-3.5" strokeWidth={1.5} />}
</Button>
)}
<Button type="button" size="sm" variant="ghost" className="h-7 w-7 p-0" onClick={downloadLogs} aria-label="Download logs">
<Download className="h-3.5 w-3.5" strokeWidth={1.5} />
</Button>
@@ -289,7 +307,7 @@ export default function StructuredLogViewer({ stackName }: StructuredLogViewerPr
<div
ref={scrollRef}
onScroll={handleScroll}
className="flex-1 min-h-0 overflow-y-auto font-mono text-[11px] leading-[1.5]"
className="flex-1 min-h-0 overflow-y-auto font-mono text-xs leading-[1.5]"
>
{filtered.length === 0 ? (
<div className="px-3 py-2 text-stat-subtitle">Waiting for log output</div>