|
1 | 1 | import React, { useState, useEffect, useRef } from "react"; |
2 | | -import { Box, Text } from "ink"; |
| 2 | +import { Box, Text, useStdout } from "ink"; |
3 | 3 | import BigText from "ink-big-text"; |
4 | 4 | import Gradient from "ink-gradient"; |
5 | 5 | import { isLightMode } from "../utils/theme.js"; |
6 | | -import { useViewportHeight } from "../hooks/useViewportHeight.js"; |
7 | 6 |
|
8 | 7 | // Dramatic shades of green shimmer - wide range |
9 | 8 | const DARK_SHIMMER_COLORS = [ |
@@ -91,18 +90,51 @@ const LIGHT_FRAMES = precomputeFrames( |
91 | 90 |
|
92 | 91 | // Minimum width to show the full BigText banner (simple3d font needs ~80 chars for "RUNLOOP.ai") |
93 | 92 | const MIN_WIDTH_FOR_BIG_BANNER = 90; |
| 93 | +// Minimum height to show the full BigText banner - require generous room (40 lines) |
| 94 | +const MIN_HEIGHT_FOR_BIG_BANNER = 40; |
94 | 95 |
|
95 | 96 | // Animation interval in ms |
96 | 97 | const SHIMMER_INTERVAL = 400; |
97 | 98 |
|
98 | 99 | export const Banner = React.memo(() => { |
99 | 100 | const [frameIndex, setFrameIndex] = useState(0); |
100 | 101 | const frames = isLightMode() ? LIGHT_FRAMES : DARK_FRAMES; |
101 | | - const { terminalWidth } = useViewportHeight(); |
| 102 | + const { stdout } = useStdout(); |
102 | 103 | const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
103 | 104 |
|
104 | | - // Determine if we should show compact mode |
105 | | - const isCompact = terminalWidth < MIN_WIDTH_FOR_BIG_BANNER; |
| 105 | + // Get raw terminal dimensions, responding to resize events |
| 106 | + // Default to conservative values if we can't detect (triggers compact mode) |
| 107 | + const getDimensions = React.useCallback( |
| 108 | + () => ({ |
| 109 | + width: stdout?.columns && stdout.columns > 0 ? stdout.columns : 80, |
| 110 | + height: stdout?.rows && stdout.rows > 0 ? stdout.rows : 20, |
| 111 | + }), |
| 112 | + [stdout], |
| 113 | + ); |
| 114 | + |
| 115 | + const [dimensions, setDimensions] = useState(getDimensions); |
| 116 | + |
| 117 | + useEffect(() => { |
| 118 | + // Update immediately on mount and when stdout changes |
| 119 | + setDimensions(getDimensions()); |
| 120 | + |
| 121 | + if (!stdout) return; |
| 122 | + |
| 123 | + const handleResize = () => { |
| 124 | + setDimensions(getDimensions()); |
| 125 | + }; |
| 126 | + |
| 127 | + stdout.on("resize", handleResize); |
| 128 | + |
| 129 | + return () => { |
| 130 | + stdout.off("resize", handleResize); |
| 131 | + }; |
| 132 | + }, [stdout, getDimensions]); |
| 133 | + |
| 134 | + // Determine if we should show compact mode (not enough width OR height) |
| 135 | + const isCompact = |
| 136 | + dimensions.width < MIN_WIDTH_FOR_BIG_BANNER || |
| 137 | + dimensions.height < MIN_HEIGHT_FOR_BIG_BANNER; |
106 | 138 |
|
107 | 139 | useEffect(() => { |
108 | 140 | const tick = () => { |
|
0 commit comments