Feature: Amélioration du sélecteur de modèles IA avec recherche

Amélioration de l'interface de sélection de modèles dans l'assistant IA
pour une meilleure expérience utilisateur.

Fonctionnalités ajoutées:

1. **Système de recherche intégré**
   - Remplacement du <select> HTML par un Combobox avec recherche
   - Utilisation des composants Command et Popover de shadcn/ui
   - Recherche en temps réel dans la liste des modèles
   - Filtrage instantané par nom de modèle

2. **Affichage amélioré du modèle par défaut**
   - Indicateur ★ visible dans le bouton du sélecteur
   - Indicateur ★ visible dans chaque élément de la liste
   - Affichage du nom complet du modèle sélectionné

3. **UX améliorée**
   - Interface moderne avec icône ChevronsUpDown
   - Indicateur visuel (✓) pour le modèle actuellement sélectionné
   - Fermeture automatique après sélection
   - Meilleure gestion des états (chargement, vide, désactivé)
   - Truncate pour les noms longs de modèles

Changements techniques:
- src/pages/Index.tsx:
  * Import Popover, PopoverContent, PopoverTrigger
  * Import Command, CommandInput, CommandList, CommandItem, etc.
  * Import icônes Check et ChevronsUpDown
  * Ajout state modelSelectorOpen pour gérer l'ouverture du popover
  * Remplacement du <select> HTML (lignes 2379-2396) par Combobox
  * Ajustement taille bouton étoile (h-9 w-9 pour cohérence)

Comportement préservé:
- Le modèle par défaut est toujours chargé depuis localStorage
- Sélection automatique du modèle par défaut au chargement
- Possibilité de définir/changer le modèle par défaut avec ★
- Validation et désactivation selon les états de chargement

Cette amélioration facilite grandement la navigation dans une longue
liste de modèles IA et offre une expérience utilisateur moderne.
This commit is contained in:
Claude committed 2025-11-13 09:00:52 +00:00
1 parent f07451b1ad
commit 9df41f51f1
1 file changed
+67 -20
+67 -20
View File
@@ -10,10 +10,12 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Badge } from "@/components/ui/badge";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { RichTextEditor } from "@/components/RichTextEditor";
import {
PlusCircle, Search, User, LogOut, Settings, ChevronDown, Plus, Archive, Trash2,
Image as ImageIcon, CheckSquare, FileText, Rss, ExternalLink, RefreshCw, Key, Zap, Paperclip, X, Edit, Calendar as CalendarIcon, Tag as TagIcon, MessageSquare, Send
Image as ImageIcon, CheckSquare, FileText, Rss, ExternalLink, RefreshCw, Key, Zap, Paperclip, X, Edit, Calendar as CalendarIcon, Tag as TagIcon, MessageSquare, Send, Check, ChevronsUpDown
} from "lucide-react";
import AuthService from "@/services/AuthService";
import AdminService from "@/services/AdminService";
@@ -98,6 +100,7 @@ const Index = () => {
const [chatLoading, setChatLoading] = useState(false);
const [selectedModel, setSelectedModel] = useState<string>("");
const [defaultModel, setDefaultModel] = useState<string>("");
const [modelSelectorOpen, setModelSelectorOpen] = useState(false);
// Debounce timer for auto-save
const saveTimerRef = useRef<NodeJS.Timeout | null>(null);
@@ -2376,29 +2379,73 @@ const Index = () => {
</div>
</div>
<div className="mt-3 flex gap-2">
<select
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
className="flex-1 text-xs px-2 py-1.5 border border-input rounded-md bg-background"
disabled={chatLoading || loadingModels}
>
{loadingModels ? (
<option value="">Chargement des modèles...</option>
) : openRouterModels.length === 0 ? (
<option value="">Aucun modèle disponible</option>
) : (
openRouterModels.map(model => (
<option key={model.id} value={model.id}>
{model.name}
</option>
))
)}
</select>
<Popover open={modelSelectorOpen} onOpenChange={setModelSelectorOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={modelSelectorOpen}
className="flex-1 justify-between text-xs h-9"
disabled={chatLoading || loadingModels}
>
<span className="truncate">
{loadingModels ? (
"Chargement des modèles..."
) : openRouterModels.length === 0 ? (
"Aucun modèle disponible"
) : selectedModel ? (
<>
{openRouterModels.find(m => m.id === selectedModel)?.name || "Sélectionner un modèle"}
{defaultModel === selectedModel && (
<span className="ml-1 text-yellow-500">★</span>
)}
</>
) : (
"Sélectionner un modèle"
)}
</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[320px] p-0" align="start">
<Command>
<CommandInput placeholder="Rechercher un modèle..." className="h-9" />
<CommandList>
<CommandEmpty>Aucun modèle trouvé.</CommandEmpty>
<CommandGroup>
{openRouterModels.map((model) => (
<CommandItem
key={model.id}
value={model.name}
onSelect={() => {
setSelectedModel(model.id);
setModelSelectorOpen(false);
}}
className="text-xs"
>
<Check
className={`mr-2 h-4 w-4 ${
selectedModel === model.id ? "opacity-100" : "opacity-0"
}`}
/>
<div className="flex-1 truncate">
{model.name}
</div>
{defaultModel === model.id && (
<span className="ml-1 text-yellow-500 text-sm">★</span>
)}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
{selectedModel && (
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
className="h-9 w-9"
onClick={() => handleSetDefaultModel(selectedModel)}
disabled={loadingModels}
title="Définir comme modèle par défaut"