fix: stabilize streaming status row to prevent footer flicker (#880)

This commit is contained in:
Charles Packer
2026-02-09 18:30:47 -08:00
committed by GitHub
parent 19e95153bb
commit 97c4526874
2 changed files with 44 additions and 8 deletions

View File

@@ -211,6 +211,7 @@ const StreamingStatus = memo(function StreamingStatus({
agentName,
interruptRequested,
networkPhase,
terminalWidth,
}: {
streaming: boolean;
visible: boolean;
@@ -220,6 +221,7 @@ const StreamingStatus = memo(function StreamingStatus({
agentName: string | null | undefined;
interruptRequested: boolean;
networkPhase: "upload" | "download" | "error" | null;
terminalWidth: number;
}) {
const [shimmerOffset, setShimmerOffset] = useState(-3);
const [elapsedMs, setElapsedMs] = useState(0);
@@ -275,6 +277,24 @@ const StreamingStatus = memo(function StreamingStatus({
return "↑\u0338";
}, [networkPhase]);
const showErrorArrow = networkArrow === "↑\u0338";
const statusContentWidth = Math.max(0, terminalWidth - 2);
const minMessageWidth = 12;
const desiredHintWidth = Math.max(18, Math.floor(statusContentWidth * 0.34));
const cappedHintWidth = Math.min(44, desiredHintWidth);
const hintColumnWidth = Math.max(
0,
Math.min(
cappedHintWidth,
Math.max(0, statusContentWidth - minMessageWidth),
),
);
const maxMessageWidth = Math.max(0, statusContentWidth - hintColumnWidth);
const statusLabel = `${agentName ? `${agentName} ` : ""}${thinkingMessage}`;
const statusLabelWidth = Array.from(statusLabel).length;
const messageColumnWidth = Math.max(
0,
Math.min(maxMessageWidth, Math.max(minMessageWidth, statusLabelWidth)),
);
// Build the status hint text (esc to interrupt · 2m · 1.2k ↑)
// Uses chalk.dim to match reasoning text styling
@@ -321,13 +341,21 @@ const StreamingStatus = memo(function StreamingStatus({
<Spinner type="layer" />
</Text>
</Box>
<Box flexGrow={1} flexDirection="row">
<ShimmerText
boldPrefix={agentName || undefined}
message={thinkingMessage}
shimmerOffset={shimmerOffset}
/>
<Text>{statusHintText}</Text>
<Box width={statusContentWidth} flexShrink={0} flexDirection="row">
<Box width={messageColumnWidth} flexShrink={0}>
<ShimmerText
boldPrefix={agentName || undefined}
message={thinkingMessage}
shimmerOffset={shimmerOffset}
wrap="truncate-end"
/>
</Box>
{hintColumnWidth > 0 && (
<Box width={hintColumnWidth} flexShrink={0}>
<Text wrap="truncate-end">{statusHintText}</Text>
</Box>
)}
<Box flexGrow={1} />
</Box>
</Box>
);
@@ -1002,6 +1030,7 @@ export function Input({
agentName={agentName}
interruptRequested={interruptRequested}
networkPhase={networkPhase}
terminalWidth={columns}
/>
{/* Queue display - show whenever there are queued messages */}

View File

@@ -8,6 +8,12 @@ interface ShimmerTextProps {
boldPrefix?: string;
message: string;
shimmerOffset: number;
wrap?:
| "wrap"
| "truncate"
| "truncate-start"
| "truncate-middle"
| "truncate-end";
}
export const ShimmerText = memo(function ShimmerText({
@@ -15,6 +21,7 @@ export const ShimmerText = memo(function ShimmerText({
boldPrefix,
message,
shimmerOffset,
wrap,
}: ShimmerTextProps) {
const fullText = `${boldPrefix ? `${boldPrefix} ` : ""}${message}`;
const prefixLength = boldPrefix ? boldPrefix.length + 1 : 0; // +1 for space
@@ -36,5 +43,5 @@ export const ShimmerText = memo(function ShimmerText({
})
.join("");
return <Text>{shimmerText}</Text>;
return <Text wrap={wrap}>{shimmerText}</Text>;
});