mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-12 01:39:47 +02:00
Feature: Recherche et modèle par défaut dans l'assistant IA
Améliorations du chatbox IA: Système de recherche: - Ajout d'un champ de recherche pour filtrer les modèles par nom ou ID - Filtre en temps réel pendant la saisie - Interface avec liste scrollable des modèles (max-h-32) Système de modèle par défaut: - Possibilité de définir un modèle comme favori (étoile ★) - Sauvegarde du modèle par défaut dans localStorage - Chargement automatique du modèle par défaut à l'ouverture du chatbox - Indication visuelle: étoile jaune pleine (★) pour le modèle par défaut, étoile vide (☆) pour les autres Interface améliorée: - Remplacement du select natif par une liste interactive - Highlighting du modèle sélectionné (bg-accent) - Hover effect sur les modèles - Bouton étoile cliquable sans sélectionner le modèle (stopPropagation) L'utilisateur peut maintenant facilement trouver et définir son modèle préféré qui sera automatiquement sélectionné à chaque ouverture.
This commit is contained in:
1 file changed
+75
-17
+75
-17
@@ -96,6 +96,8 @@ const Index = () => {
|
||||
const [chatInput, setChatInput] = useState("");
|
||||
const [chatLoading, setChatLoading] = useState(false);
|
||||
const [selectedModel, setSelectedModel] = useState<string>("");
|
||||
const [defaultModel, setDefaultModel] = useState<string>("");
|
||||
const [modelSearchQuery, setModelSearchQuery] = useState<string>("");
|
||||
|
||||
// Debounce timer for auto-save
|
||||
const saveTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
@@ -527,6 +529,14 @@ const Index = () => {
|
||||
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [chatMessages]);
|
||||
|
||||
// Load default model from localStorage on mount
|
||||
useEffect(() => {
|
||||
const savedDefaultModel = localStorage.getItem('defaultAiModel');
|
||||
if (savedDefaultModel) {
|
||||
setDefaultModel(savedDefaultModel);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Load models when chatbox opens
|
||||
useEffect(() => {
|
||||
if (chatOpen && openRouterModels.length === 0) {
|
||||
@@ -537,9 +547,22 @@ const Index = () => {
|
||||
// Set default model when models are loaded
|
||||
useEffect(() => {
|
||||
if (openRouterModels.length > 0 && !selectedModel) {
|
||||
setSelectedModel(openRouterModels[0].id);
|
||||
// If there's a default model saved, use it
|
||||
if (defaultModel && openRouterModels.find(m => m.id === defaultModel)) {
|
||||
setSelectedModel(defaultModel);
|
||||
} else {
|
||||
// Otherwise use the first model
|
||||
setSelectedModel(openRouterModels[0].id);
|
||||
}
|
||||
}
|
||||
}, [openRouterModels, selectedModel]);
|
||||
}, [openRouterModels, selectedModel, defaultModel]);
|
||||
|
||||
// Save default model to localStorage
|
||||
const handleSetDefaultModel = (modelId: string) => {
|
||||
setDefaultModel(modelId);
|
||||
localStorage.setItem('defaultAiModel', modelId);
|
||||
showSuccess("Modèle par défaut défini");
|
||||
};
|
||||
|
||||
const confirmAddUser = async (username: string, password: string, isAdmin: boolean) => {
|
||||
try {
|
||||
@@ -2266,25 +2289,60 @@ const Index = () => {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<select
|
||||
value={selectedModel}
|
||||
onChange={(e) => setSelectedModel(e.target.value)}
|
||||
className="w-full text-xs px-2 py-1.5 border border-input rounded-md bg-background"
|
||||
disabled={chatLoading || loadingModels}
|
||||
>
|
||||
<div className="mt-3 space-y-2">
|
||||
{/* Search field */}
|
||||
<Input
|
||||
placeholder="Rechercher un modèle..."
|
||||
value={modelSearchQuery}
|
||||
onChange={(e) => setModelSearchQuery(e.target.value)}
|
||||
className="text-xs h-8"
|
||||
disabled={loadingModels}
|
||||
/>
|
||||
|
||||
{/* Models list */}
|
||||
<div className="max-h-32 overflow-y-auto border rounded-md">
|
||||
{loadingModels ? (
|
||||
<option value="">Chargement des modèles...</option>
|
||||
<div className="text-xs text-center py-2 text-muted-foreground">
|
||||
Chargement des modèles...
|
||||
</div>
|
||||
) : openRouterModels.length === 0 ? (
|
||||
<option value="">Aucun modèle disponible</option>
|
||||
<div className="text-xs text-center py-2 text-muted-foreground">
|
||||
Aucun modèle disponible
|
||||
</div>
|
||||
) : (
|
||||
openRouterModels.map(model => (
|
||||
<option key={model.id} value={model.id}>
|
||||
{model.name}
|
||||
</option>
|
||||
))
|
||||
openRouterModels
|
||||
.filter(model =>
|
||||
model.name.toLowerCase().includes(modelSearchQuery.toLowerCase()) ||
|
||||
model.id.toLowerCase().includes(modelSearchQuery.toLowerCase())
|
||||
)
|
||||
.map(model => (
|
||||
<div
|
||||
key={model.id}
|
||||
className={`flex items-center justify-between px-2 py-1.5 hover:bg-accent cursor-pointer text-xs ${
|
||||
selectedModel === model.id ? 'bg-accent' : ''
|
||||
}`}
|
||||
onClick={() => setSelectedModel(model.id)}
|
||||
>
|
||||
<span className="flex-1 truncate">{model.name}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-5 w-5"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSetDefaultModel(model.id);
|
||||
}}
|
||||
>
|
||||
{defaultModel === model.id ? (
|
||||
<span className="text-yellow-500">★</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">☆</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
|
||||
Reference in new issue
Block a user