mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-23 23:28:17 +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 [chatInput, setChatInput] = useState("");
|
||||||
const [chatLoading, setChatLoading] = useState(false);
|
const [chatLoading, setChatLoading] = useState(false);
|
||||||
const [selectedModel, setSelectedModel] = useState<string>("");
|
const [selectedModel, setSelectedModel] = useState<string>("");
|
||||||
|
const [defaultModel, setDefaultModel] = useState<string>("");
|
||||||
|
const [modelSearchQuery, setModelSearchQuery] = useState<string>("");
|
||||||
|
|
||||||
// Debounce timer for auto-save
|
// Debounce timer for auto-save
|
||||||
const saveTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const saveTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@@ -527,6 +529,14 @@ const Index = () => {
|
|||||||
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
}, [chatMessages]);
|
}, [chatMessages]);
|
||||||
|
|
||||||
|
// Load default model from localStorage on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const savedDefaultModel = localStorage.getItem('defaultAiModel');
|
||||||
|
if (savedDefaultModel) {
|
||||||
|
setDefaultModel(savedDefaultModel);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Load models when chatbox opens
|
// Load models when chatbox opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chatOpen && openRouterModels.length === 0) {
|
if (chatOpen && openRouterModels.length === 0) {
|
||||||
@@ -537,9 +547,22 @@ const Index = () => {
|
|||||||
// Set default model when models are loaded
|
// Set default model when models are loaded
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (openRouterModels.length > 0 && !selectedModel) {
|
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) => {
|
const confirmAddUser = async (username: string, password: string, isAdmin: boolean) => {
|
||||||
try {
|
try {
|
||||||
@@ -2266,25 +2289,60 @@ const Index = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-3 space-y-2">
|
||||||
<select
|
{/* Search field */}
|
||||||
value={selectedModel}
|
<Input
|
||||||
onChange={(e) => setSelectedModel(e.target.value)}
|
placeholder="Rechercher un modèle..."
|
||||||
className="w-full text-xs px-2 py-1.5 border border-input rounded-md bg-background"
|
value={modelSearchQuery}
|
||||||
disabled={chatLoading || loadingModels}
|
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 ? (
|
{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 ? (
|
) : 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 => (
|
openRouterModels
|
||||||
<option key={model.id} value={model.id}>
|
.filter(model =>
|
||||||
{model.name}
|
model.name.toLowerCase().includes(modelSearchQuery.toLowerCase()) ||
|
||||||
</option>
|
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>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user