From 4f2974dbfe36638915f1b08448d2563c64f88644 Mon Sep 17 00:00:00 2001
From: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Date: Thu, 7 Aug 2025 15:55:53 -0700
Subject: feat(ui): Improve UI layout adaptation for narrow terminals (#5651)

Co-authored-by: Jacob Richman <jacob314@gmail.com>
---
 .../cli/src/ui/components/LoadingIndicator.tsx     | 57 +++++++++++++++-------
 1 file changed, 39 insertions(+), 18 deletions(-)

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

diff --git a/packages/cli/src/ui/components/LoadingIndicator.tsx b/packages/cli/src/ui/components/LoadingIndicator.tsx
index 6e1bc758..7ac356dd 100644
--- a/packages/cli/src/ui/components/LoadingIndicator.tsx
+++ b/packages/cli/src/ui/components/LoadingIndicator.tsx
@@ -12,6 +12,8 @@ import { useStreamingContext } from '../contexts/StreamingContext.js';
 import { StreamingState } from '../types.js';
 import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
 import { formatDuration } from '../utils/formatters.js';
+import { useTerminalSize } from '../hooks/useTerminalSize.js';
+import { isNarrowWidth } from '../utils/isNarrowWidth.js';
 
 interface LoadingIndicatorProps {
   currentLoadingPhrase?: string;
@@ -27,6 +29,8 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
   thought,
 }) => {
   const streamingState = useStreamingContext();
+  const { columns: terminalWidth } = useTerminalSize();
+  const isNarrow = isNarrowWidth(terminalWidth);
 
   if (streamingState === StreamingState.Idle) {
     return null;
@@ -34,28 +38,45 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
 
   const primaryText = thought?.subject || currentLoadingPhrase;
 
+  const cancelAndTimerContent =
+    streamingState !== StreamingState.WaitingForConfirmation
+      ? `(esc to cancel, ${elapsedTime < 60 ? `${elapsedTime}s` : formatDuration(elapsedTime * 1000)})`
+      : null;
+
   return (
-    <Box marginTop={1} paddingLeft={0} flexDirection="column">
+    <Box paddingLeft={0} flexDirection="column">
       {/* Main loading line */}
-      <Box>
-        <Box marginRight={1}>
-          <GeminiRespondingSpinner
-            nonRespondingDisplay={
-              streamingState === StreamingState.WaitingForConfirmation
-                ? '⠏'
-                : ''
-            }
-          />
+      <Box
+        width="100%"
+        flexDirection={isNarrow ? 'column' : 'row'}
+        alignItems={isNarrow ? 'flex-start' : 'center'}
+      >
+        <Box>
+          <Box marginRight={1}>
+            <GeminiRespondingSpinner
+              nonRespondingDisplay={
+                streamingState === StreamingState.WaitingForConfirmation
+                  ? '⠏'
+                  : ''
+              }
+            />
+          </Box>
+          {primaryText && (
+            <Text color={Colors.AccentPurple}>{primaryText}</Text>
+          )}
+          {!isNarrow && cancelAndTimerContent && (
+            <Text color={Colors.Gray}> {cancelAndTimerContent}</Text>
+          )}
         </Box>
-        {primaryText && <Text color={Colors.AccentPurple}>{primaryText}</Text>}
-        <Text color={Colors.Gray}>
-          {streamingState === StreamingState.WaitingForConfirmation
-            ? ''
-            : ` (esc to cancel, ${elapsedTime < 60 ? `${elapsedTime}s` : formatDuration(elapsedTime * 1000)})`}
-        </Text>
-        <Box flexGrow={1}>{/* Spacer */}</Box>
-        {rightContent && <Box>{rightContent}</Box>}
+        {!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
+        {!isNarrow && rightContent && <Box>{rightContent}</Box>}
       </Box>
+      {isNarrow && cancelAndTimerContent && (
+        <Box>
+          <Text color={Colors.Gray}>{cancelAndTimerContent}</Text>
+        </Box>
+      )}
+      {isNarrow && rightContent && <Box>{rightContent}</Box>}
     </Box>
   );
 };
-- 
cgit v1.2.3

