provider-list.tsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use client';
  2. import { Button } from '@/components/ui/button';
  3. import { Box, Plus } from 'lucide-react';
  4. import { cn } from '@/lib/utils';
  5. import { useI18n } from '@/lib/hooks/use-i18n';
  6. import type { ProviderId, ProviderConfig } from '@/lib/ai/providers';
  7. import { MONO_LOGO_PROVIDERS } from '@/lib/ai/providers';
  8. interface ProviderWithServerInfo extends ProviderConfig {
  9. isServerConfigured?: boolean;
  10. }
  11. interface ProviderListProps {
  12. providers: ProviderWithServerInfo[];
  13. selectedProviderId: ProviderId;
  14. onSelect: (providerId: ProviderId) => void;
  15. onAddProvider: () => void;
  16. width?: number;
  17. }
  18. export function ProviderList({
  19. providers,
  20. selectedProviderId,
  21. onSelect,
  22. onAddProvider,
  23. width,
  24. }: ProviderListProps) {
  25. const { t } = useI18n();
  26. // Helper function to get translated provider name
  27. const getProviderDisplayName = (provider: ProviderConfig) => {
  28. const translationKey = `settings.providerNames.${provider.id}`;
  29. const translated = t(translationKey);
  30. // If translation exists (not equal to key), use it; otherwise fallback to provider.name
  31. return translated !== translationKey ? translated : provider.name;
  32. };
  33. return (
  34. <div className="flex-shrink-0 bg-background flex flex-col" style={{ width: width ?? 192 }}>
  35. <div className="flex-1 overflow-y-auto p-3 space-y-1.5">
  36. {providers.map((provider) => (
  37. <button
  38. key={provider.id}
  39. onClick={() => onSelect(provider.id)}
  40. className={cn(
  41. 'w-full flex items-center gap-2.5 px-3 py-2.5 rounded-lg transition-all border text-left',
  42. selectedProviderId === provider.id
  43. ? 'bg-primary/5 border-primary/50 shadow-sm'
  44. : 'border-transparent hover:bg-muted/50',
  45. )}
  46. >
  47. {provider.icon ? (
  48. <img
  49. src={provider.icon}
  50. alt={getProviderDisplayName(provider)}
  51. className={cn(
  52. 'w-5 h-5 rounded',
  53. MONO_LOGO_PROVIDERS.has(provider.id) && 'dark:invert',
  54. )}
  55. onError={(e) => {
  56. (e.target as HTMLImageElement).style.display = 'none';
  57. }}
  58. />
  59. ) : (
  60. <Box className="h-5 w-5 text-muted-foreground" />
  61. )}
  62. <span className="font-medium text-sm flex-1 truncate">
  63. {getProviderDisplayName(provider)}
  64. </span>
  65. {provider.isServerConfigured && (
  66. <span className="text-[10px] px-1 py-0 h-4 leading-4 rounded shrink-0 bg-muted text-muted-foreground">
  67. {t('settings.serverConfigured')}
  68. </span>
  69. )}
  70. </button>
  71. ))}
  72. </div>
  73. {/* Add Provider Button */}
  74. <div className="p-3 border-t">
  75. <Button variant="outline" size="sm" className="w-full gap-1.5" onClick={onAddProvider}>
  76. <Plus className="h-3.5 w-3.5" />
  77. {t('settings.addProviderButton')}
  78. </Button>
  79. </div>
  80. </div>
  81. );
  82. }