From 9c46acc793df3573e6fbcf53ac3e46663494e410 Mon Sep 17 00:00:00 2001
From: Taylor Mullen <ntaylormullen@google.com>
Date: Thu, 15 May 2025 22:56:03 -0700
Subject: Refactor: Improve UI rendering and address code review comments

This commit addresses several code review comments primarily focused on improving the rendering and stability of the CLI UI.

Key changes include:
- Passing `isPending` and `availableTerminalHeight` props to `MarkdownDisplay` to enable more intelligent rendering of content, especially for pending messages and code blocks.
- Adjusting height calculations in `ToolGroupMessage` and `ToolMessage` to more accurately reflect available space.
- Refining the logic in `App.tsx` for measuring and utilizing terminal height, including renaming `footerRef` to `mainControlsRef` for clarity.
- Ensuring consistent prop drilling for `isPending` and `availableTerminalHeight` through `HistoryItemDisplay`, `GeminiMessage`, and `GeminiMessageContent`.
- In `MarkdownDisplay`, when `isPending` is true and content exceeds `availableTerminalHeight`, the code block will now be truncated with a "... generating more ..." message. If there's insufficient space even for the
message, a simpler "... code is being written ..." will be shown.
---
 packages/cli/src/ui/App.tsx | 42 ++++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 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 b6275491..e607955a 100644
--- a/packages/cli/src/ui/App.tsx
+++ b/packages/cli/src/ui/App.tsx
@@ -5,8 +5,9 @@
  */
 
 import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
-import { Box, DOMElement, measureElement, Static, Text, useStdout } from 'ink';
+import { Box, DOMElement, measureElement, Static, Text } from 'ink';
 import { StreamingState, type HistoryItem } from './types.js';
+import { useTerminalSize } from './hooks/useTerminalSize.js';
 import { useGeminiStream } from './hooks/useGeminiStream.js';
 import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
 import { useThemeCommand } from './hooks/useThemeCommand.js';
@@ -56,8 +57,7 @@ export const App = ({
   const [debugMessage, setDebugMessage] = useState<string>('');
   const [showHelp, setShowHelp] = useState<boolean>(false);
   const [themeError, setThemeError] = useState<string | null>(null);
-  const [availableTerminalHeight, setAvailableTerminalHeight] =
-    useState<number>(0);
+  const [footerHeight, setFooterHeight] = useState<number>(0);
   const {
     isThemeDialogOpen,
     openThemeDialog,
@@ -195,26 +195,24 @@ export const App = ({
 
   // --- Render Logic ---
 
-  // Get terminal dimensions
-
-  const { stdout } = useStdout();
-  const terminalWidth = stdout?.columns ?? 80;
-  const terminalHeight = stdout?.rows ?? 24;
-  const footerRef = useRef<DOMElement>(null);
+  const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize();
+  const mainControlsRef = useRef<DOMElement>(null);
   const pendingHistoryItemRef = useRef<DOMElement>(null);
 
   // Calculate width for suggestions, leave some padding
   const suggestionsWidth = Math.max(60, Math.floor(terminalWidth * 0.8));
 
   useEffect(() => {
-    const staticExtraHeight = /* margins and padding */ 3;
-    const fullFooterMeasurement = measureElement(footerRef.current!);
-    const fullFooterHeight = fullFooterMeasurement.height;
+    if (mainControlsRef.current) {
+      const fullFooterMeasurement = measureElement(mainControlsRef.current);
+      setFooterHeight(fullFooterMeasurement.height);
+    }
+  }, [terminalHeight]); // Re-calculate if terminalHeight changes, as it might affect footer's rendered height.
 
-    setAvailableTerminalHeight(
-      terminalHeight - fullFooterHeight - staticExtraHeight,
-    );
-  }, [terminalHeight]);
+  const availableTerminalHeight = useMemo(() => {
+    const staticExtraHeight = /* margins and padding */ 3;
+    return terminalHeight - footerHeight - staticExtraHeight;
+  }, [terminalHeight, footerHeight]);
 
   useEffect(() => {
     if (!pendingHistoryItem) {
@@ -260,7 +258,14 @@ export const App = ({
             <Header />
             <Tips />
           </Box>,
-          ...history.map((h) => <HistoryItemDisplay availableTerminalHeight={availableTerminalHeight} key={h.id} item={h} />),
+          ...history.map((h) => (
+            <HistoryItemDisplay
+              availableTerminalHeight={availableTerminalHeight}
+              key={h.id}
+              item={h}
+              isPending={false}
+            />
+          )),
         ]}
       >
         {(item) => item}
@@ -272,12 +277,13 @@ export const App = ({
             // TODO(taehykim): It seems like references to ids aren't necessary in
             // HistoryItemDisplay. Refactor later. Use a fake id for now.
             item={{ ...pendingHistoryItem, id: 0 }}
+            isPending={true}
           />
         </Box>
       )}
       {showHelp && <Help commands={slashCommands} />}
 
-      <Box flexDirection="column" ref={footerRef}>
+      <Box flexDirection="column" ref={mainControlsRef}>
         {startupWarnings.length > 0 && (
           <Box
             borderStyle="round"
-- 
cgit v1.2.3

