({
initial,
sessionModel,
onSelect,
onCancel,
isStandaloneCommand,
showFastModeNotice,
headerText,
skipSettingsWrite,
}: Props)
| 60 | const NO_PREFERENCE = '__NO_PREFERENCE__'; |
| 61 | |
| 62 | export function ModelPicker({ |
| 63 | initial, |
| 64 | sessionModel, |
| 65 | onSelect, |
| 66 | onCancel, |
| 67 | isStandaloneCommand, |
| 68 | showFastModeNotice, |
| 69 | headerText, |
| 70 | skipSettingsWrite, |
| 71 | }: Props): React.ReactNode { |
| 72 | const setAppState = useSetAppState(); |
| 73 | const exitState = useExitOnCtrlCDWithKeybindings(); |
| 74 | const maxVisible = 10; |
| 75 | |
| 76 | const initialValue = initial === null ? NO_PREFERENCE : initial; |
| 77 | const [focusedValue, setFocusedValue] = useState<string | undefined>(initialValue); |
| 78 | |
| 79 | const isFastMode = useAppState(s => (isFastModeEnabled() ? s.fastMode : false)); |
| 80 | |
| 81 | const [marked1MValues, setMarked1MValues] = useState<Set<string>>( |
| 82 | () => new Set(has1mContext(initialValue) ? [initialValue.replace(/\[1m\]/i, '')] : []), |
| 83 | ); |
| 84 | |
| 85 | const handleToggle1M = useCallback(() => { |
| 86 | if (!focusedValue || focusedValue === NO_PREFERENCE) return; |
| 87 | // Key on the base value so lookups in handleSelect / is1MMarked match the |
| 88 | // initializer — predefined 1M options arrive with a `[1m]` suffix in |
| 89 | // `focusedValue`, which would diverge from the base-value key set. |
| 90 | const baseKey = focusedValue.replace(/\[1m\]/i, ''); |
| 91 | setMarked1MValues(prev => { |
| 92 | const next = new Set(prev); |
| 93 | if (next.has(baseKey)) { |
| 94 | next.delete(baseKey); |
| 95 | } else { |
| 96 | next.add(baseKey); |
| 97 | } |
| 98 | return next; |
| 99 | }); |
| 100 | }, [focusedValue]); |
| 101 | |
| 102 | const [hasToggledEffort, setHasToggledEffort] = useState(false); |
| 103 | const effortValue = useAppState(s => s.effortValue); |
| 104 | const [effort, setEffort] = useState<EffortLevel | undefined>( |
| 105 | effortValue !== undefined ? convertEffortValueToLevel(effortValue) : undefined, |
| 106 | ); |
| 107 | |
| 108 | // Memoize all derived values to prevent re-renders |
| 109 | const modelOptions = useMemo(() => getModelOptions(isFastMode ?? false), [isFastMode]); |
| 110 | |
| 111 | // Ensure the initial value is in the options list |
| 112 | // This handles edge cases where the user's current model (e.g., 'haiku' for 3P users) |
| 113 | // is not in the base options but should still be selectable and shown as selected |
| 114 | const optionsWithInitial = useMemo(() => { |
| 115 | if (initial !== null && !modelOptions.some(opt => opt.value === initial)) { |
| 116 | return [ |
| 117 | ...modelOptions, |
| 118 | { |
| 119 | value: initial, |
nothing calls this directly
no test coverage detected