import { useAppTranslation } from '@features/localization/renderer'; import { Select, SelectContent, SelectItem, SelectTrigger } from '@renderer/components/ui/select'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@renderer/components/ui/tooltip'; import { formatProviderBackendLabel } from '@renderer/utils/providerBackendIdentity'; import type { CliProviderStatus } from '@shared/types'; interface Props { provider: CliProviderStatus; disabled?: boolean; onSelect: (providerId: CliProviderStatus['providerId'], backendId: string) => void; } export interface ProviderRuntimeBackendSummaryText { auto: string; autoCurrently: (backend: string) => string; audienceInternal: string; states: { locked: string; disabled: string; authRequired: string; runtimeMissing: string; degraded: string; unavailable: string; }; } export function buildProviderRuntimeBackendSummaryText( t: ReturnType['t'] ): ProviderRuntimeBackendSummaryText { return { auto: t('runtimeBackendSelector.auto'), autoCurrently: (backend) => t('runtimeBackendSelector.autoCurrently', { backend }), audienceInternal: t('runtimeBackendSelector.audience.internal'), states: { locked: t('runtimeBackendSelector.states.locked'), disabled: t('runtimeBackendSelector.states.disabled'), authRequired: t('runtimeBackendSelector.states.authRequired'), runtimeMissing: t('runtimeBackendSelector.states.runtimeMissing'), degraded: t('runtimeBackendSelector.states.degraded'), unavailable: t('runtimeBackendSelector.states.unavailable'), }, }; } const DEFAULT_SUMMARY_TEXT: ProviderRuntimeBackendSummaryText = { auto: 'Auto', autoCurrently: (backend) => `Auto (currently: ${backend})`, audienceInternal: 'Internal', states: { locked: 'Locked', disabled: 'Disabled', authRequired: 'Auth required', runtimeMissing: 'Runtime missing', degraded: 'Degraded', unavailable: 'Unavailable', }, }; export function getProviderRuntimeBackendStateLabel( option: NonNullable[number] ): string | null { switch (option.state) { case 'ready': return null; case 'locked': return 'Locked'; case 'disabled': return 'Disabled'; case 'authentication-required': return 'Auth required'; case 'runtime-missing': return 'Runtime missing'; case 'degraded': return 'Degraded'; default: if (!option.available) { return 'Unavailable'; } if (option.selectable === false) { return 'Locked'; } return null; } } export function getProviderRuntimeBackendAudienceLabel( option: NonNullable[number] ): string | null { return option.audience === 'internal' ? 'Internal' : null; } export function getVisibleProviderRuntimeBackendOptions( provider: CliProviderStatus ): NonNullable { return provider.availableBackends ?? []; } export function getOptionDisplayLabel( provider: CliProviderStatus, option: NonNullable[number], resolvedOption: NonNullable[number] | null ): string { if (provider.providerId === 'codex') { const legacyLabel = formatProviderBackendLabel(provider.providerId, option.id); if (legacyLabel) { return legacyLabel; } } if (option.id !== 'auto') { return option.label; } if (resolvedOption?.label) { return `Auto (currently: ${resolvedOption.label})`; } return 'Auto'; } function getOptionSummaryDisplayLabel( provider: CliProviderStatus, option: NonNullable[number], resolvedOption: NonNullable[number] | null, text: ProviderRuntimeBackendSummaryText ): string { if (option.id !== 'auto') { return getOptionDisplayLabel(provider, option, resolvedOption); } if (resolvedOption?.label) { return text.autoCurrently(resolvedOption.label); } return text.auto; } function getProviderRuntimeBackendStateSummaryLabel( option: NonNullable[number], text: ProviderRuntimeBackendSummaryText ): string | null { switch (getProviderRuntimeBackendStateLabel(option)) { case 'Locked': return text.states.locked; case 'Disabled': return text.states.disabled; case 'Auth required': return text.states.authRequired; case 'Runtime missing': return text.states.runtimeMissing; case 'Degraded': return text.states.degraded; case 'Unavailable': return text.states.unavailable; default: return null; } } export function getProviderRuntimeBackendSummary( provider: CliProviderStatus, text: ProviderRuntimeBackendSummaryText = DEFAULT_SUMMARY_TEXT ): string | null { const options = provider.availableBackends ?? []; if (options.length === 0) { return null; } const selectedBackendId = provider.selectedBackendId ?? options[0]?.id ?? ''; const selectedOption = options.find((option) => option.id === selectedBackendId) ?? options[0]; const resolvedOption = options.find((option) => option.id === provider.resolvedBackendId) ?? null; const parts = [getOptionSummaryDisplayLabel(provider, selectedOption, resolvedOption, text)]; const audienceLabel = getProviderRuntimeBackendAudienceLabel(selectedOption) ? text.audienceInternal : null; const stateLabel = getProviderRuntimeBackendStateSummaryLabel(selectedOption, text); if (audienceLabel) { parts.push(audienceLabel.toLowerCase()); } if (stateLabel) { parts.push(stateLabel.toLowerCase()); } return parts.join(' - '); } export const ProviderRuntimeBackendSelector = ({ provider, disabled = false, onSelect, }: Props): React.JSX.Element | null => { const { t } = useAppTranslation('common'); const summaryText = buildProviderRuntimeBackendSummaryText(t); const options = getVisibleProviderRuntimeBackendOptions(provider); if (options.length === 0) { return null; } if (provider.providerId === 'codex' && options.length === 1) { return null; } const selectedBackendId = provider.selectedBackendId ?? options[0]?.id ?? ''; const selectedOption = options.find((option) => option.id === selectedBackendId) ?? options[0]; const resolvedOption = options.find((option) => option.id === provider.resolvedBackendId) ?? null; const localizeStateLabel = ( option: NonNullable[number] ): string | null => { switch (getProviderRuntimeBackendStateLabel(option)) { case 'Locked': return t('runtimeBackendSelector.states.locked'); case 'Disabled': return t('runtimeBackendSelector.states.disabled'); case 'Auth required': return t('runtimeBackendSelector.states.authRequired'); case 'Runtime missing': return t('runtimeBackendSelector.states.runtimeMissing'); case 'Degraded': return t('runtimeBackendSelector.states.degraded'); case 'Unavailable': return t('runtimeBackendSelector.states.unavailable'); default: return null; } }; const localizeAudienceLabel = ( option: NonNullable[number] ): string | null => getProviderRuntimeBackendAudienceLabel(option) ? t('runtimeBackendSelector.audience.internal') : null; const localizeOptionDisplayLabel = ( option: NonNullable[number] ): string => { if (option.id === 'auto') { if (resolvedOption?.label) { return summaryText.autoCurrently(resolvedOption.label); } return summaryText.auto; } return getOptionDisplayLabel(provider, option, resolvedOption); }; const selectedLabel = localizeOptionDisplayLabel(selectedOption); const selectedStateLabel = localizeStateLabel(selectedOption); const selectedAudienceLabel = localizeAudienceLabel(selectedOption); return (
{t('runtimeBackendSelector.label')} {provider.resolvedBackendId && provider.resolvedBackendId !== provider.selectedBackendId && ( {t('runtimeBackendSelector.resolved', { backend: resolvedOption?.label ?? provider.resolvedBackendId, })} )}
{selectedOption && (
{selectedLabel} {selectedOption.recommended ? ( {t('runtimeBackendSelector.recommended')} ) : null} {selectedAudienceLabel ? ( {selectedAudienceLabel} ) : null} {!selectedStateLabel && !selectedOption.available ? ( {t('runtimeBackendSelector.unavailable')} {selectedOption.detailMessage ?? selectedOption.statusMessage ?? t('runtimeBackendSelector.unavailable')} ) : selectedStateLabel ? ( {selectedStateLabel} {selectedOption.detailMessage ?? selectedOption.statusMessage ?? t('runtimeBackendSelector.cannotSelectYet')} ) : null}
{selectedOption.description}
{selectedOption.statusMessage ?
{selectedOption.statusMessage}
: null} {selectedOption.detailMessage && selectedOption.available ? (
{selectedOption.detailMessage}
) : null}
)}
); };