feat: bound container health pane so logs stay visible on large stacks (#1556)

* feat: bound container health pane so logs stay visible on large stacks

On the stack overview page, multi-container stacks (8+ services) pushed the
logs pane off-screen because the Command Center card had shrink-0 and no
scroll mechanism. This reworks the desktop layout for stacks with more than
one container:

- The Command Center card is bounded at max-h-[42%] so it never consumes
  the full left column. The container list scrolls internally via Sencho's
  existing Radix ScrollArea.
- A summary strip shows total / running / paused / unhealthy counts above
  the list, with a Compact / Detailed density toggle. Detailed (default)
  shows CPU / Mem / Net sparklines; Compact hides them for a denser list.
- The logs pane has a guaranteed min-h-[180px] so it is always reachable.
- Single-container stacks are untouched: the card keeps its original
  shrink-0 behaviour, and no summary strip or density toggle appears.

Mobile is unaffected (it already uses segmented Health / Logs / Compose
panes).

* fix: key ContainersHealth by node+stack so density resets on navigation

The density toggle state (compact/detailed) is local to ContainersHealth.
Without a key, selecting Compact on one multi-container stack would persist
when navigating to a single-container stack, hiding sparklines while also
hiding the toggle itself. Keying by node+stack ensures a clean remount.

Also added test coverage for zero containers and density reset on remount.

* fix: remove unused rerender variable from ContainersHealth test
This commit is contained in:
Anso
2026-07-05 02:53:26 -04:00
committed by GitHub
parent bb35c1bc92
commit fbe98676cb
6 changed files with 221 additions and 5 deletions
@@ -38,6 +38,7 @@ import ErrorBoundary from '../ErrorBoundary';
import StackAnatomyPanel from '../StackAnatomyPanel';
import { StackFileExplorer } from '@/components/files/StackFileExplorer';
import { useIsMobile } from '@/hooks/use-is-mobile';
import { ScrollArea } from '../ui/scroll-area';
import { StackIdentityHeader, ContainersHealth, StackLogsSection } from './editor-view-blocks';
import { MobileStackDetail } from './MobileStackDetail';
import { RecoveryChip } from './RecoveryChip';
@@ -355,7 +356,7 @@ export function EditorView(props: EditorViewProps) {
{/* Command Center Card (identity + health strip). Hidden when
the logs are expanded so the logs pane fills the column. */}
{!logsExpanded && (
<Card className="rounded-xl border-muted bg-card shrink-0">
<Card className={`rounded-xl border-muted bg-card ${safeContainers.length > 1 ? 'flex flex-col min-h-0 max-h-[42%]' : 'shrink-0'}`}>
<CardHeader className="p-4 pb-2">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1">
@@ -407,6 +408,23 @@ export function EditorView(props: EditorViewProps) {
panelStartedAt={panelStartedAt}
variant="band"
/>
{safeContainers.length > 1 ? (
<CardContent className="p-4 pt-2 flex-1 min-h-0">
<ScrollArea className="h-full">
<ContainersHealth
safeContainers={safeContainers}
containerStats={containerStats}
containerStatsError={containerStatsError}
isAdmin={isAdmin}
activeNode={activeNode}
openLogViewer={openLogViewer}
openBashModal={openBashModal}
serviceAction={serviceAction}
key={`${activeNode?.id ?? 'local'}:${stackName}`}
/>
</ScrollArea>
</CardContent>
) : (
<CardContent className="p-4 pt-2">
<ContainersHealth
safeContainers={safeContainers}
@@ -417,12 +435,17 @@ export function EditorView(props: EditorViewProps) {
openLogViewer={openLogViewer}
openBashModal={openBashModal}
serviceAction={serviceAction}
key={`${activeNode?.id ?? 'local'}:${stackName}`}
/>
</CardContent>
)}
</Card>
)}
{/* Logs Section (fills remaining left-column height) */}
{/* Logs Section (fills remaining left-column height). On multi-
container stacks a min-h guarantees logs are never hidden. */}
{safeContainers.length > 1 ? (
<div className="flex-1 min-h-[180px] flex flex-col">
<StackLogsSection
stackName={stackName}
logsMode={logsMode}
@@ -430,6 +453,16 @@ export function EditorView(props: EditorViewProps) {
logsExpanded={logsExpanded}
onToggleLogsExpand={() => setLogsExpanded((v) => !v)}
/>
</div>
) : (
<StackLogsSection
stackName={stackName}
logsMode={logsMode}
setLogsMode={setLogsMode}
logsExpanded={logsExpanded}
onToggleLogsExpand={() => setLogsExpanded((v) => !v)}
/>
)}
</div>
)}