55
66import { useState , useEffect } from 'react' ;
77import { useIntl } from 'react-intl' ;
8- import { Check , Eye , EyeOff , X , Plus } from 'lucide-react' ;
8+ import { Check , Eye , EyeOff , X , Plus , Loader2 , Download } from 'lucide-react' ;
99import {
1010 Dialog ,
1111 DialogContent ,
@@ -23,6 +23,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
2323import { Tabs , TabsList , TabsTrigger , TabsContent } from '@/components/ui/Tabs' ;
2424import { useCreateCliSettings , useUpdateCliSettings , useProviders } from '@/hooks/useApiSettings' ;
2525import { useNotifications } from '@/hooks/useNotifications' ;
26+ import { fetchCodexConfigPreview , fetchGeminiConfigPreview } from '@/lib/api' ;
2627import type { CliSettingsEndpoint , CliProvider } from '@/lib/api' ;
2728
2829// ========== Types ==========
@@ -101,6 +102,8 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
101102 // Gemini specific
102103 const [ geminiApiKey , setGeminiApiKey ] = useState ( '' ) ;
103104 const [ showGeminiKey , setShowGeminiKey ] = useState ( false ) ;
105+ const [ geminiSettingsJson , setGeminiSettingsJson ] = useState ( '' ) ;
106+ const [ isLoadingGeminiConfig , setIsLoadingGeminiConfig ] = useState ( false ) ;
104107
105108 // Shared
106109 const [ model , setModel ] = useState ( '' ) ;
@@ -112,6 +115,9 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
112115 const [ showJsonInput , setShowJsonInput ] = useState ( false ) ;
113116 const [ errors , setErrors ] = useState < Record < string , string > > ( { } ) ;
114117
118+ // Codex config preview loading state
119+ const [ isLoadingCodexConfig , setIsLoadingCodexConfig ] = useState ( false ) ;
120+
115121 // Initialize form
116122 useEffect ( ( ) => {
117123 if ( cliSettings ) {
@@ -171,6 +177,7 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
171177 setWriteCommonConfig ( false ) ;
172178 setGeminiApiKey ( '' ) ;
173179 setShowGeminiKey ( false ) ;
180+ setGeminiSettingsJson ( '' ) ;
174181 setAvailableModels ( [ ] ) ;
175182 setModelInput ( '' ) ;
176183 setTags ( [ ] ) ;
@@ -224,6 +231,47 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
224231 return Object . keys ( newErrors ) . length === 0 ;
225232 } ;
226233
234+ // Handle load Codex config preview
235+ const handleLoadCodexConfig = async ( ) => {
236+ setIsLoadingCodexConfig ( true ) ;
237+ try {
238+ const result = await fetchCodexConfigPreview ( ) ;
239+ if ( result . success ) {
240+ if ( result . configToml ) {
241+ setConfigToml ( result . configToml ) ;
242+ }
243+ if ( result . authJson ) {
244+ setAuthJson ( result . authJson ) ;
245+ }
246+ } else {
247+ error ( formatMessage ( { id : 'apiSettings.cliSettings.loadConfigError' } ) || 'Failed to load config' ) ;
248+ }
249+ } catch ( err ) {
250+ error ( formatMessage ( { id : 'apiSettings.cliSettings.loadConfigError' } ) || 'Failed to load config' ) ;
251+ } finally {
252+ setIsLoadingCodexConfig ( false ) ;
253+ }
254+ } ;
255+
256+ // Handle load Gemini config preview
257+ const handleLoadGeminiConfig = async ( ) => {
258+ setIsLoadingGeminiConfig ( true ) ;
259+ try {
260+ const result = await fetchGeminiConfigPreview ( ) ;
261+ if ( result . success ) {
262+ if ( result . settingsJson ) {
263+ setGeminiSettingsJson ( result . settingsJson ) ;
264+ }
265+ } else {
266+ error ( formatMessage ( { id : 'apiSettings.cliSettings.loadConfigError' } ) || 'Failed to load config' ) ;
267+ }
268+ } catch ( err ) {
269+ error ( formatMessage ( { id : 'apiSettings.cliSettings.loadConfigError' } ) || 'Failed to load config' ) ;
270+ } finally {
271+ setIsLoadingGeminiConfig ( false ) ;
272+ }
273+ } ;
274+
227275 // Handle save
228276 const handleSave = async ( ) => {
229277 if ( ! validateForm ( ) ) return ;
@@ -521,6 +569,35 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
521569 { /* ========== Codex Settings ========== */ }
522570 { cliProvider === 'codex' && (
523571 < div className = "space-y-4" >
572+ { /* Load Global Config Button */ }
573+ < div className = "flex items-center justify-between p-3 bg-muted/30 rounded-lg" >
574+ < div >
575+ < p className = "text-sm font-medium" > Load Global Config</ p >
576+ < p className = "text-xs text-muted-foreground" >
577+ Load config.toml and auth.json from global Codex config directory
578+ </ p >
579+ </ div >
580+ < Button
581+ type = "button"
582+ variant = "outline"
583+ size = "sm"
584+ onClick = { handleLoadCodexConfig }
585+ disabled = { isLoadingCodexConfig }
586+ >
587+ { isLoadingCodexConfig ? (
588+ < >
589+ < Loader2 className = "w-4 h-4 mr-2 animate-spin" />
590+ Loading...
591+ </ >
592+ ) : (
593+ < >
594+ < Download className = "w-4 h-4 mr-2" />
595+ Load Config
596+ </ >
597+ ) }
598+ </ Button >
599+ </ div >
600+
524601 { /* API Key */ }
525602 < div className = "space-y-2" >
526603 < Label htmlFor = "codex-apikey" > API Key</ Label >
@@ -645,6 +722,35 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
645722 { /* ========== Gemini Settings ========== */ }
646723 { cliProvider === 'gemini' && (
647724 < div className = "space-y-4" >
725+ { /* Load Global Config Button */ }
726+ < div className = "flex items-center justify-between p-3 bg-muted/30 rounded-lg" >
727+ < div >
728+ < p className = "text-sm font-medium" > Load Global Config</ p >
729+ < p className = "text-xs text-muted-foreground" >
730+ Load settings.json from global Gemini config directory
731+ </ p >
732+ </ div >
733+ < Button
734+ type = "button"
735+ variant = "outline"
736+ size = "sm"
737+ onClick = { handleLoadGeminiConfig }
738+ disabled = { isLoadingGeminiConfig }
739+ >
740+ { isLoadingGeminiConfig ? (
741+ < >
742+ < Loader2 className = "w-4 h-4 mr-2 animate-spin" />
743+ Loading...
744+ </ >
745+ ) : (
746+ < >
747+ < Download className = "w-4 h-4 mr-2" />
748+ Load Config
749+ </ >
750+ ) }
751+ </ Button >
752+ </ div >
753+
648754 < div className = "space-y-2" >
649755 < Label htmlFor = "gemini-apikey" > API Key</ Label >
650756 < div className = "relative" >
@@ -675,6 +781,38 @@ export function CliSettingsModal({ open, onClose, cliSettings, defaultProvider }
675781 placeholder = "gemini-2.5-flash"
676782 />
677783 </ div >
784+
785+ { /* settings.json Editor */ }
786+ { geminiSettingsJson && (
787+ < div className = "space-y-2" >
788+ < div className = "flex items-center justify-between" >
789+ < Label htmlFor = "gemini-settingsjson" > settings.json (Preview)</ Label >
790+ < Button
791+ type = "button" variant = "ghost" size = "sm"
792+ onClick = { ( ) => {
793+ try {
794+ const formatted = JSON . stringify ( JSON . parse ( geminiSettingsJson ) , null , 2 ) ;
795+ setGeminiSettingsJson ( formatted ) ;
796+ } catch { /* skip */ }
797+ } }
798+ >
799+ Format
800+ </ Button >
801+ </ div >
802+ < Textarea
803+ id = "gemini-settingsjson"
804+ value = { geminiSettingsJson }
805+ onChange = { ( e ) => setGeminiSettingsJson ( e . target . value ) }
806+ placeholder = '{"model": "gemini-2.5-flash", ...}'
807+ className = "font-mono text-sm"
808+ rows = { 8 }
809+ readOnly
810+ />
811+ < p className = "text-xs text-muted-foreground" >
812+ Gemini settings.json content (read-only preview)
813+ </ p >
814+ </ div >
815+ ) }
678816 </ div >
679817 ) }
680818
0 commit comments