From b0bc7c3d996d25c9fefdfbcba3ca19fa46ad199f Mon Sep 17 00:00:00 2001
From: Jacob Richman <jacob314@gmail.com>
Date: Thu, 19 Jun 2025 20:17:23 +0000
Subject: Fix flicker issues by ensuring all actively changing content fits in
 the viewport (#1217)

---
 packages/cli/src/ui/App.tsx | 47 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 37 insertions(+), 10 deletions(-)

(limited to 'packages/cli/src/ui/App.tsx')

diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx
index a5317b30..a9c5f0e7 100644
--- a/packages/cli/src/ui/App.tsx
+++ b/packages/cli/src/ui/App.tsx
@@ -115,6 +115,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
   const ctrlCTimerRef = useRef<NodeJS.Timeout | null>(null);
   const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
   const ctrlDTimerRef = useRef<NodeJS.Timeout | null>(null);
+  const [constrainHeight, setConstrainHeight] = useState<boolean>(true);
 
   const errorCount = useMemo(
     () => consoleMessages.filter((msg) => msg.type === 'error').length,
@@ -217,7 +218,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
   const widthFraction = 0.9;
   const inputWidth = Math.max(
     20,
-    Math.round(terminalWidth * widthFraction) - 3,
+    Math.floor(terminalWidth * widthFraction) - 3,
   );
   const suggestionsWidth = Math.max(60, Math.floor(terminalWidth * 0.8));
 
@@ -279,6 +280,8 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
         return;
       }
       handleExit(ctrlDPressedOnce, setCtrlDPressedOnce, ctrlDTimerRef);
+    } else if (key.ctrl && input === 's') {
+      setConstrainHeight((prev) => !prev);
     }
   });
 
@@ -393,10 +396,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
     }
   }, [terminalHeight, consoleMessages, showErrorDetails]);
 
-  const availableTerminalHeight = useMemo(() => {
-    const staticExtraHeight = /* margins and padding */ 3;
-    return terminalHeight - footerHeight - staticExtraHeight;
-  }, [terminalHeight, footerHeight]);
+  const staticExtraHeight = /* margins and padding */ 3;
+  const availableTerminalHeight = useMemo(
+    () => terminalHeight - footerHeight - staticExtraHeight,
+    [terminalHeight, footerHeight],
+  );
 
   useEffect(() => {
     if (!pendingHistoryItems.length) {
@@ -445,7 +449,10 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
         {quittingMessages.map((item) => (
           <HistoryItemDisplay
             key={item.id}
-            availableTerminalHeight={availableTerminalHeight}
+            availableTerminalHeight={
+              constrainHeight ? availableTerminalHeight : undefined
+            }
+            terminalWidth={terminalWidth}
             item={item}
             isPending={false}
             config={config}
@@ -454,7 +461,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
       </Box>
     );
   }
-
+  const mainAreaWidth = Math.floor(terminalWidth * 0.9);
+  const debugConsoleMaxHeight = Math.max(terminalHeight * 0.2, 5);
+  // Arbitrary threshold to ensure that items in the static area are large
+  // enough but not too large to make the terminal hard to use.
+  const staticAreaMaxItemHeight = Math.max(terminalHeight * 4, 100);
   return (
     <StreamingContext.Provider value={streamingState}>
       <Box flexDirection="column" marginBottom={1} width="90%">
@@ -479,7 +490,8 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
             </Box>,
             ...history.map((h) => (
               <HistoryItemDisplay
-                availableTerminalHeight={availableTerminalHeight}
+                terminalWidth={mainAreaWidth}
+                availableTerminalHeight={staticAreaMaxItemHeight}
                 key={h.id}
                 item={h}
                 isPending={false}
@@ -494,7 +506,10 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
           {pendingHistoryItems.map((item, i) => (
             <HistoryItemDisplay
               key={i}
-              availableTerminalHeight={availableTerminalHeight}
+              availableTerminalHeight={
+                constrainHeight ? availableTerminalHeight : undefined
+              }
+              terminalWidth={mainAreaWidth}
               // TODO(taehykim): It seems like references to ids aren't necessary in
               // HistoryItemDisplay. Refactor later. Use a fake id for now.
               item={{ ...item, id: 0 }}
@@ -534,6 +549,12 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
                 onSelect={handleThemeSelect}
                 onHighlight={handleThemeHighlight}
                 settings={settings}
+                availableTerminalHeight={
+                  constrainHeight
+                    ? terminalHeight - staticExtraHeight
+                    : undefined
+                }
+                terminalWidth={mainAreaWidth}
               />
             </Box>
           ) : isEditorDialogOpen ? (
@@ -604,7 +625,13 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
               </Box>
 
               {showErrorDetails && (
-                <DetailedMessagesDisplay messages={filteredConsoleMessages} />
+                <DetailedMessagesDisplay
+                  messages={filteredConsoleMessages}
+                  maxHeight={
+                    constrainHeight ? debugConsoleMaxHeight : undefined
+                  }
+                  width={inputWidth}
+                />
               )}
 
               {isInputActive && (
-- 
cgit v1.2.3

