| import { useState } from 'react'; |
| import { Cloud, ChevronDown, ChevronRight } from 'lucide-react'; |
|
|
| export default function ComparisonSelector({ providers, selected, onSelectionChange }) { |
| const [expandedProviders, setExpandedProviders] = useState({}); |
|
|
| const toggleExpand = (providerId) => { |
| setExpandedProviders(prev => ({ ...prev, [providerId]: !prev[providerId] })); |
| }; |
|
|
| const toggleModel = (modelId) => { |
| if (selected.includes(modelId)) { |
| onSelectionChange(selected.filter(id => id !== modelId)); |
| } else { |
| onSelectionChange([...selected, modelId]); |
| } |
| }; |
|
|
| const toggleProvider = (provider) => { |
| const providerModelIds = provider.models.map(m => m.id); |
| const allSelected = providerModelIds.every(id => selected.includes(id)); |
| if (allSelected) { |
| onSelectionChange(selected.filter(id => !providerModelIds.includes(id))); |
| } else { |
| const newIds = providerModelIds.filter(id => !selected.includes(id)); |
| onSelectionChange([...selected, ...newIds]); |
| } |
| }; |
|
|
| if (!providers.length) { |
| return ( |
| <div className="selector-section"> |
| <h3 className="selector-title"><Cloud size={16} /> Comparison Models</h3> |
| <p className="selector-hint">Configure API keys in .env to enable comparison providers</p> |
| </div> |
| ); |
| } |
|
|
| return ( |
| <div className="selector-section"> |
| <h3 className="selector-title"><Cloud size={16} /> Comparison Models</h3> |
| <p className="selector-hint">Select models to compare against</p> |
| <div className="comparison-providers"> |
| {[...providers].sort((a, b) => a.name.localeCompare(b.name)).map(provider => { |
| const providerModelIds = provider.models.map(m => m.id); |
| const allSelected = providerModelIds.every(id => selected.includes(id)); |
| const selectedCount = providerModelIds.filter(id => selected.includes(id)).length; |
| const isExpanded = expandedProviders[provider.id] === true; |
| return ( |
| <div key={provider.id} className="comparison-provider-group"> |
| <div className="provider-group-header"> |
| <label className="provider-group-checkbox" onClick={(e) => e.stopPropagation()}> |
| <input |
| type="checkbox" |
| checked={allSelected} |
| onChange={() => toggleProvider(provider)} |
| /> |
| </label> |
| <button className="provider-group-toggle" onClick={() => toggleExpand(provider.id)}> |
| <span className="provider-group-name">{provider.name}</span> |
| {selectedCount > 0 && <span className="provider-selected-count">{selectedCount}</span>} |
| {isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />} |
| </button> |
| </div> |
| {isExpanded && ( |
| <div className="provider-model-list"> |
| {provider.models.map(model => ( |
| <label key={model.id} className={`comparison-model-item ${selected.includes(model.id) ? 'selected' : ''}`}> |
| <input |
| type="checkbox" |
| checked={selected.includes(model.id)} |
| onChange={() => toggleModel(model.id)} |
| /> |
| <span className="comparison-model-name">{model.name}</span> |
| </label> |
| ))} |
| </div> |
| )} |
| </div> |
| ); |
| })} |
| </div> |
| |
| <style>{` |
| .comparison-providers { |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| } |
| .comparison-provider-group { |
| border: 1px solid var(--border-primary); |
| border-radius: 8px; |
| overflow: hidden; |
| background: #E0EAFD; |
| } |
| :root[data-theme="dark"] .comparison-provider-group { |
| background: #1E2A40; |
| } |
| .provider-group-header { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| padding: 8px 12px; |
| font-size: 13px; |
| border-bottom: 1px solid var(--border-muted); |
| } |
| .provider-group-checkbox { |
| display: flex; |
| align-items: center; |
| cursor: pointer; |
| } |
| .provider-group-checkbox input[type="checkbox"] { |
| accent-color: var(--accent-primary); |
| box-shadow: 0 1px 6px 1px rgba(0, 0, 0, 0.3); |
| border-radius: 3px; |
| } |
| .provider-group-toggle { |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| flex: 1; |
| background: none; |
| border: none; |
| color: var(--text-primary); |
| font-size: 13px; |
| cursor: pointer; |
| padding: 0; |
| text-align: left; |
| } |
| .provider-group-toggle:hover { |
| opacity: 0.8; |
| } |
| .provider-selected-count { |
| font-size: 11px; |
| font-weight: 600; |
| color: var(--accent-primary); |
| background: var(--accent-light); |
| padding: 1px 6px; |
| border-radius: 10px; |
| margin-left: auto; |
| } |
| .provider-model-list { |
| padding: 4px; |
| } |
| .comparison-model-item { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| padding: 6px 8px; |
| border-radius: 6px; |
| cursor: pointer; |
| transition: background 0.15s; |
| font-size: 13px; |
| } |
| .comparison-model-item:hover { |
| background: var(--card-hover); |
| } |
| .comparison-model-item.selected { |
| background: var(--accent-light); |
| } |
| .comparison-model-item input[type="checkbox"] { |
| accent-color: var(--accent-primary); |
| } |
| .provider-group-name { |
| font-weight: 600; |
| color: var(--text-primary); |
| font-size: 13px; |
| text-transform: uppercase; |
| } |
| .comparison-model-name { |
| color: var(--text-primary); |
| } |
| `}</style> |
| </div> |
| ); |
| } |
|
|