From 7e8f379dfbd4d70050ce301a42a38ba9c1f052f4 Mon Sep 17 00:00:00 2001
From: Jacob Richman <jacob314@gmail.com>
Date: Thu, 1 May 2025 10:34:07 -0700
Subject: Save settings to ~/.gemini/settings.json and optionally
 /your/workspace/.gemini/settings.json (#237)

---
 packages/cli/src/ui/components/ThemeDialog.tsx | 98 ++++++++++++++++++++++----
 1 file changed, 85 insertions(+), 13 deletions(-)

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

diff --git a/packages/cli/src/ui/components/ThemeDialog.tsx b/packages/cli/src/ui/components/ThemeDialog.tsx
index 62ede336..7e8c5afd 100644
--- a/packages/cli/src/ui/components/ThemeDialog.tsx
+++ b/packages/cli/src/ui/components/ThemeDialog.tsx
@@ -4,33 +4,87 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import React from 'react';
-import { Box, Text } from 'ink';
+import React, { useState } from 'react';
+import { Box, Text, useInput } from 'ink';
 import { Colors } from '../colors.js';
-import { themeManager } from '../themes/theme-manager.js';
+import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
 import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
 import { DiffRenderer } from './messages/DiffRenderer.js';
 import { colorizeCode } from '../utils/CodeColorizer.js';
+import { LoadedSettings, SettingScope } from '../../config/settings.js';
 
 interface ThemeDialogProps {
   /** Callback function when a theme is selected */
-  onSelect: (themeName: string) => void;
+  onSelect: (themeName: string | undefined, scope: SettingScope) => void;
 
   /** Callback function when a theme is highlighted */
-  onHighlight: (themeName: string) => void;
+  onHighlight: (themeName: string | undefined) => void;
+  /** The settings object */
+  settings: LoadedSettings;
 }
 
 export function ThemeDialog({
   onSelect,
   onHighlight,
+  settings,
 }: ThemeDialogProps): React.JSX.Element {
+  const [selectedScope, setSelectedScope] = useState<SettingScope>(
+    SettingScope.User,
+  );
+
   const themeItems = themeManager.getAvailableThemes().map((theme) => ({
     label: theme.active ? `${theme.name} (Active)` : theme.name,
     value: theme.name,
   }));
-  const initialIndex = themeItems.findIndex(
-    (item) => item.value === themeManager.getActiveTheme().name,
+  const [selectInputKey, setSelectInputKey] = useState(Date.now());
+
+  const initialThemeIndex = themeItems.findIndex(
+    (item) =>
+      item.value ===
+      (settings.forScope(selectedScope).settings.theme || DEFAULT_THEME.name),
+  );
+
+  const scopeItems = [
+    { label: 'User Settings', value: SettingScope.User },
+    { label: 'Workspace Settings', value: SettingScope.Workspace },
+  ];
+
+  const handleThemeSelect = (themeName: string) => {
+    onSelect(themeName, selectedScope);
+  };
+
+  const handleScopeHighlight = (scope: SettingScope) => {
+    setSelectedScope(scope);
+    setSelectInputKey(Date.now());
+  };
+
+  const handleScopeSelect = (scope: SettingScope) => {
+    handleScopeHighlight(scope);
+    setFocusedSection('theme'); // Reset focus to theme section
+  };
+
+  const [focusedSection, setFocusedSection] = useState<'theme' | 'scope'>(
+    'theme',
   );
+
+  useInput((input, key) => {
+    if (key.tab) {
+      setFocusedSection((prev) => (prev === 'theme' ? 'scope' : 'theme'));
+    }
+  });
+
+  let otherScopeModifiedMessage = '';
+  const otherScope =
+    selectedScope === SettingScope.User
+      ? SettingScope.Workspace
+      : SettingScope.User;
+  if (settings.forScope(otherScope).settings.theme !== undefined) {
+    otherScopeModifiedMessage =
+      settings.forScope(selectedScope).settings.theme !== undefined
+        ? `(Also modified in ${otherScope})`
+        : `(Modified in ${otherScope})`;
+  }
+
   return (
     <Box
       borderStyle="round"
@@ -39,18 +93,36 @@ export function ThemeDialog({
       padding={1}
       width="50%"
     >
-      <Box marginBottom={1}>
-        <Text bold>Select Theme</Text>
-      </Box>
+      <Text bold={focusedSection === 'theme'}>
+        {focusedSection === 'theme' ? '> ' : '  '}Select Theme{' '}
+        <Text color={Colors.SubtleComment}>{otherScopeModifiedMessage}</Text>
+      </Text>
+
       <RadioButtonSelect
+        key={selectInputKey}
         items={themeItems}
-        initialIndex={initialIndex}
-        onSelect={onSelect}
+        initialIndex={initialThemeIndex}
+        onSelect={handleThemeSelect} // Use the wrapper handler
         onHighlight={onHighlight}
+        isFocused={focusedSection === 'theme'}
       />
+      {/* Scope Selection */}
+      <Box marginTop={1} flexDirection="column">
+        <Text bold={focusedSection === 'scope'}>
+          {focusedSection === 'scope' ? '> ' : '  '}Apply To
+        </Text>
+        <RadioButtonSelect
+          items={scopeItems}
+          initialIndex={0} // Default to User Settings
+          onSelect={handleScopeSelect}
+          onHighlight={handleScopeHighlight}
+          isFocused={focusedSection === 'scope'}
+        />
+      </Box>
+
       <Box marginTop={1}>
         <Text color={Colors.SubtleComment}>
-          (Use ↑/↓ arrows and Enter to select)
+          (Use ↑/↓ arrows and Enter to select, Tab to change focus)
         </Text>
       </Box>
 
-- 
cgit v1.2.3

