Spaces:
Running
Running
File size: 3,820 Bytes
a3976c3 052a316 a3976c3 052a316 a3976c3 052a316 a3976c3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | // src/components/ProviderDropdown.js
import React, { useState, useRef, useEffect } from 'react';
import { ChevronDown, Cpu, Cloud, Server, Loader2 } from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
const ProviderDropdown = ({ currentProvider, onProviderChange, isLoading = false }) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const { isDark } = useTheme();
const providers = [
{
id: 'gemini',
name: 'Gemini',
description: 'Google\'s Gemini AI',
icon: Cloud,
badge: 'Cloud'
},
{
id: 'ollama',
name: 'Ollama',
description: 'Local LLM via Ollama',
icon: Cpu,
badge: 'Local'
},
{
id: 'vllm',
name: 'vLLM',
description: 'vLLM inference endpoint',
icon: Server,
badge: 'API'
}
];
const currentProviderInfo = providers.find(p => p.id === currentProvider);
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const handleProviderSelect = (providerId) => {
if (providerId !== currentProvider && !isLoading) {
onProviderChange(providerId);
setIsOpen(false);
}
};
const toggleDropdown = () => {
if (!isLoading) {
setIsOpen(!isOpen);
}
};
return (
<div className="provider-dropdown" ref={dropdownRef}>
<button
className={`provider-button ${isOpen ? 'open' : ''} ${isLoading ? 'loading' : ''}`}
onClick={toggleDropdown}
disabled={isLoading}
>
<div className="provider-button-content">
{isLoading ? (
<Loader2 className="provider-icon spinning" size={16} />
) : (
<currentProviderInfo.icon className="provider-icon" size={16} />
)}
<div className="provider-info">
<span className="provider-name">{currentProviderInfo?.name || 'Unknown'}</span>
<span className={`provider-badge ${currentProvider}`}>{currentProviderInfo?.badge}</span>
</div>
</div>
<ChevronDown
className={`dropdown-arrow ${isOpen ? 'rotated' : ''}`}
size={16}
/>
</button>
{isOpen && (
<div className="provider-dropdown-menu">
{providers.map((provider) => {
const Icon = provider.icon;
const isSelected = provider.id === currentProvider;
return (
<button
key={provider.id}
className={`provider-option ${isSelected ? 'selected' : ''}`}
onClick={() => handleProviderSelect(provider.id)}
disabled={isSelected}
>
<Icon className="provider-option-icon" size={16} />
<div className="provider-option-info">
<div className="provider-option-header">
<span className="provider-option-name">{provider.name}</span>
<span className={`provider-option-badge ${provider.id}`}>
{provider.badge}
</span>
</div>
<span className="provider-option-description">{provider.description}</span>
</div>
{isSelected && (
<div className="provider-option-checkmark">✓</div>
)}
</button>
);
})}
</div>
)}
</div>
);
};
export default ProviderDropdown; |