fix(tui): keep Main header visible in single-column mode
In StatusModeSingle (cols<80 || rows<20), an active panel whose intrinsic content exceeds the container could push the frame past terminal rows under tmux, scrolling the header off the top. Yoga defaults flexShrink to 0 (unlike CSS), so panels refused to shrink when wrapped Text grew them vertically. - Default Panel flexShrink to 1 so panels collapse to fit container - Pin StatusModeSingle's three rows to explicit heights with overflow:hidden, eliminating overflow paths entirely - Pass availableRows to LogsPanel from the capped middleHeight so its rowBudget matches the actual height instead of stdout.rows-11 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,16 +181,20 @@ function Panel({ title, isFocused, children, flexGrow, flexShrink, width }: Pane
|
||||
borderColor={isFocused ? "cyanBright" : "gray"}
|
||||
flexDirection="column"
|
||||
flexGrow={flexGrow}
|
||||
flexShrink={flexShrink}
|
||||
// Default flexShrink to 1. Yoga's default is 0 (unlike CSS), so without
|
||||
// this a panel whose intrinsic content height exceeds its container —
|
||||
// common at narrow widths where Text wraps — refuses to shrink, the
|
||||
// frame grows past terminal rows, and Ink scrolls the top (header) off.
|
||||
flexShrink={flexShrink ?? 1}
|
||||
width={width}
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box paddingX={1}>
|
||||
<Box paddingX={1} flexShrink={0}>
|
||||
<Text bold={isFocused} color={isFocused ? "cyanBright" : undefined} dimColor={!isFocused}>
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box flexDirection="column" paddingX={1} flexGrow={1} overflow="hidden">
|
||||
<Box flexDirection="column" paddingX={1} flexGrow={1} flexShrink={1} overflow="hidden">
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -795,11 +799,19 @@ function StatusModeSingle({
|
||||
controller: DashboardTUI;
|
||||
}) {
|
||||
const focused = state.activeSection;
|
||||
const { stdout } = useStdout();
|
||||
// Cap the middle pane so a panel's intrinsic content height (e.g. LogsPanel's
|
||||
// own rowBudget computation) can never exceed terminal rows. Without this,
|
||||
// Ink writes a frame taller than the terminal under tmux narrow panes and the
|
||||
// top row (header) scrolls off — and stays off across re-renders.
|
||||
const rows = stdout?.rows ?? 24;
|
||||
const cols = stdout?.columns ?? 80;
|
||||
const middleHeight = Math.max(1, rows - 2);
|
||||
|
||||
const activePanel = () => {
|
||||
switch (focused) {
|
||||
case "system": return <SystemPanel state={state} isFocused />;
|
||||
case "logs": return <LogsPanel state={state} isFocused />;
|
||||
case "logs": return <LogsPanel state={state} isFocused availableRows={middleHeight - 4} />;
|
||||
case "utilities": return <UtilitiesPanel state={state} isFocused />;
|
||||
case "stats": return <StatsPanel state={state} isFocused />;
|
||||
case "settings": return <SettingsPanel state={state} isFocused />;
|
||||
@@ -807,14 +819,14 @@ function StatusModeSingle({
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" flexGrow={1}>
|
||||
<Box flexShrink={0}>
|
||||
<Box flexDirection="column" height={rows} width={cols} overflow="hidden">
|
||||
<Box height={1} flexShrink={0} overflow="hidden">
|
||||
<MainHeader state={state} />
|
||||
</Box>
|
||||
<Box flexGrow={1} flexDirection="column" overflow="hidden">
|
||||
<Box height={middleHeight} flexShrink={0} flexDirection="column" overflow="hidden">
|
||||
{activePanel()}
|
||||
</Box>
|
||||
<Box flexShrink={0}>
|
||||
<Box height={1} flexShrink={0} overflow="hidden">
|
||||
<StatusBar state={state} controller={controller} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user