2025-09-23 12:50:09 -04:00
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react' ;
2025-12-11 09:57:27 -05:00
import { useCallback , useContext , useMemo , useState } from 'react' ;
2025-09-23 12:50:09 -04:00
import { Box , Text } from 'ink' ;
import {
2025-11-18 12:01:16 -05:00
PREVIEW_GEMINI_MODEL ,
2025-12-11 09:57:27 -05:00
PREVIEW_GEMINI_FLASH_MODEL ,
PREVIEW_GEMINI_MODEL_AUTO ,
2025-09-23 12:50:09 -04:00
DEFAULT_GEMINI_MODEL ,
2025-11-18 12:01:16 -05:00
DEFAULT_GEMINI_FLASH_MODEL ,
DEFAULT_GEMINI_FLASH_LITE_MODEL ,
2025-09-23 12:50:09 -04:00
DEFAULT_GEMINI_MODEL_AUTO ,
2025-09-23 16:52:34 -07:00
ModelSlashCommandEvent ,
logModelSlashCommand ,
2025-12-11 09:57:27 -05:00
getDisplayString ,
2025-09-23 12:50:09 -04:00
} from '@google/gemini-cli-core' ;
import { useKeypress } from '../hooks/useKeypress.js' ;
import { theme } from '../semantic-colors.js' ;
import { DescriptiveRadioButtonSelect } from './shared/DescriptiveRadioButtonSelect.js' ;
import { ConfigContext } from '../contexts/ConfigContext.js' ;
2025-11-18 20:18:18 -08:00
import { ThemedGradient } from './ThemedGradient.js' ;
2025-09-23 12:50:09 -04:00
interface ModelDialogProps {
onClose : ( ) = > void ;
}
export function ModelDialog ( { onClose } : ModelDialogProps ) : React . JSX . Element {
const config = useContext ( ConfigContext ) ;
2025-12-11 09:57:27 -05:00
const [ view , setView ] = useState < 'main' | 'manual' > ( 'main' ) ;
2025-09-23 12:50:09 -04:00
// Determine the Preferred Model (read once when the dialog opens).
const preferredModel = config ? . getModel ( ) || DEFAULT_GEMINI_MODEL_AUTO ;
2025-12-11 09:57:27 -05:00
const manualModelSelected = useMemo ( ( ) = > {
const manualModels = [
DEFAULT_GEMINI_MODEL ,
DEFAULT_GEMINI_FLASH_MODEL ,
DEFAULT_GEMINI_FLASH_LITE_MODEL ,
PREVIEW_GEMINI_MODEL ,
PREVIEW_GEMINI_FLASH_MODEL ,
] ;
if ( manualModels . includes ( preferredModel ) ) {
return preferredModel ;
}
return '' ;
} , [ preferredModel ] ) ;
2025-09-23 12:50:09 -04:00
useKeypress (
( key ) = > {
if ( key . name === 'escape' ) {
2025-12-11 09:57:27 -05:00
if ( view === 'manual' ) {
setView ( 'main' ) ;
} else {
onClose ( ) ;
}
2025-09-23 12:50:09 -04:00
}
} ,
{ isActive : true } ,
) ;
2025-12-11 09:57:27 -05:00
const mainOptions = useMemo ( ( ) = > {
const list = [
2025-11-18 12:01:16 -05:00
{
value : DEFAULT_GEMINI_MODEL_AUTO ,
2025-12-11 09:57:27 -05:00
title : getDisplayString ( DEFAULT_GEMINI_MODEL_AUTO ) ,
description :
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash' ,
2025-11-18 12:01:16 -05:00
key : DEFAULT_GEMINI_MODEL_AUTO ,
} ,
{
2025-12-11 09:57:27 -05:00
value : 'Manual' ,
title : manualModelSelected
? ` Manual ( ${ manualModelSelected } ) `
: 'Manual' ,
description : 'Manually select a model' ,
key : 'Manual' ,
} ,
] ;
if ( config ? . getPreviewFeatures ( ) ) {
list . unshift ( {
value : PREVIEW_GEMINI_MODEL_AUTO ,
title : getDisplayString ( PREVIEW_GEMINI_MODEL_AUTO ) ,
2025-11-18 12:01:16 -05:00
description :
2025-12-11 09:57:27 -05:00
'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash' ,
key : PREVIEW_GEMINI_MODEL_AUTO ,
} ) ;
}
return list ;
} , [ config , manualModelSelected ] ) ;
const manualOptions = useMemo ( ( ) = > {
const list = [
{
value : DEFAULT_GEMINI_MODEL ,
title : DEFAULT_GEMINI_MODEL ,
key : DEFAULT_GEMINI_MODEL ,
2025-11-18 12:01:16 -05:00
} ,
{
2025-12-11 09:57:27 -05:00
value : DEFAULT_GEMINI_FLASH_MODEL ,
title : DEFAULT_GEMINI_FLASH_MODEL ,
key : DEFAULT_GEMINI_FLASH_MODEL ,
2025-11-18 12:01:16 -05:00
} ,
{
2025-12-11 09:57:27 -05:00
value : DEFAULT_GEMINI_FLASH_LITE_MODEL ,
title : DEFAULT_GEMINI_FLASH_LITE_MODEL ,
key : DEFAULT_GEMINI_FLASH_LITE_MODEL ,
2025-11-18 12:01:16 -05:00
} ,
2025-12-11 09:57:27 -05:00
] ;
if ( config ? . getPreviewFeatures ( ) ) {
list . unshift (
{
value : PREVIEW_GEMINI_MODEL ,
title : PREVIEW_GEMINI_MODEL ,
key : PREVIEW_GEMINI_MODEL ,
} ,
{
value : PREVIEW_GEMINI_FLASH_MODEL ,
title : PREVIEW_GEMINI_FLASH_MODEL ,
key : PREVIEW_GEMINI_FLASH_MODEL ,
} ,
) ;
}
return list ;
} , [ config ] ) ;
const options = view === 'main' ? mainOptions : manualOptions ;
2025-11-18 12:01:16 -05:00
2025-09-23 12:50:09 -04:00
// Calculate the initial index based on the preferred model.
2025-12-11 09:57:27 -05:00
const initialIndex = useMemo ( ( ) = > {
const idx = options . findIndex ( ( option ) = > option . value === preferredModel ) ;
if ( idx !== - 1 ) {
return idx ;
}
if ( view === 'main' ) {
const manualIdx = options . findIndex ( ( o ) = > o . value === 'Manual' ) ;
return manualIdx !== - 1 ? manualIdx : 0 ;
}
return 0 ;
} , [ preferredModel , options , view ] ) ;
2025-09-23 12:50:09 -04:00
// Handle selection internally (Autonomous Dialog).
const handleSelect = useCallback (
( model : string ) = > {
2025-12-11 09:57:27 -05:00
if ( model === 'Manual' ) {
setView ( 'manual' ) ;
return ;
}
2025-09-23 12:50:09 -04:00
if ( config ) {
config . setModel ( model ) ;
2025-09-23 18:06:03 -04:00
const event = new ModelSlashCommandEvent ( model ) ;
logModelSlashCommand ( config , event ) ;
2025-09-23 12:50:09 -04:00
}
onClose ( ) ;
} ,
[ config , onClose ] ,
) ;
2025-11-18 12:01:16 -05:00
const header = config ? . getPreviewFeatures ( )
? 'Gemini 3 is now enabled.'
: 'Gemini 3 is now available.' ;
const subheader = config ? . getPreviewFeatures ( )
? ` To disable Gemini 3, disable "Preview features" in /settings. \ nLearn more at https://goo.gle/enable-preview-features \ n \ nWhen you select Auto or Pro, Gemini CLI will attempt to use ${ PREVIEW_GEMINI_MODEL } first, before falling back to ${ DEFAULT_GEMINI_MODEL } . `
: ` To use Gemini 3, enable "Preview features" in /settings. \ nLearn more at https://goo.gle/enable-preview-features ` ;
2025-09-23 12:50:09 -04:00
return (
< Box
borderStyle = "round"
borderColor = { theme . border . default }
flexDirection = "column"
padding = { 1 }
width = "100%"
>
< Text bold > Select Model < / Text >
2025-11-18 12:01:16 -05:00
< Box marginTop = { 1 } marginBottom = { 1 } flexDirection = "column" >
2025-11-18 20:18:18 -08:00
< ThemedGradient >
2025-11-18 12:01:16 -05:00
< Text > { header } < / Text >
2025-11-18 20:18:18 -08:00
< / ThemedGradient >
2025-11-18 12:01:16 -05:00
< Text > { subheader } < / Text >
< / Box >
2025-09-23 12:50:09 -04:00
< Box marginTop = { 1 } >
< DescriptiveRadioButtonSelect
2025-11-18 12:01:16 -05:00
items = { options }
2025-09-23 12:50:09 -04:00
onSelect = { handleSelect }
initialIndex = { initialIndex }
showNumbers = { true }
/ >
< / Box >
2025-11-18 12:01:16 -05:00
< Box marginTop = { 1 } flexDirection = "column" >
2025-09-23 12:50:09 -04:00
< Text color = { theme . text . secondary } >
2025-11-18 12:01:16 -05:00
{ 'To use a specific Gemini model on startup, use the --model flag.' }
2025-09-23 12:50:09 -04:00
< / Text >
< / Box >
< Box marginTop = { 1 } flexDirection = "column" >
< Text color = { theme . text . secondary } > ( Press Esc to close ) < / Text >
< / Box >
< / Box >
) ;
}