feat(app): rename providers to connection, enhance UX

This commit is contained in:
ItzCrazyKns
2025-10-27 15:08:50 +05:30
parent f6ffa9ebe0
commit 7397e33f29
8 changed files with 219 additions and 172 deletions

View File

@@ -82,10 +82,10 @@ const AddProvider = ({
setProviders((prev) => [...prev, data]); setProviders((prev) => [...prev, data]);
toast.success('Provider added successfully.'); toast.success('Connection added successfully.');
} catch (error) { } catch (error) {
console.error('Error adding provider:', error); console.error('Error adding provider:', error);
toast.error('Failed to add provider.'); toast.error('Failed to add connection.');
} finally { } finally {
setLoading(false); setLoading(false);
setOpen(false); setOpen(false);
@@ -99,7 +99,7 @@ const AddProvider = ({
className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-sm border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200" className="px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-xs sm:text-sm border border-light-200 dark:border-dark-200 text-black dark:text-white bg-light-secondary/50 dark:bg-dark-secondary/50 hover:bg-light-secondary hover:dark:bg-dark-secondary hover:border-light-300 hover:dark:border-dark-300 flex flex-row items-center space-x-1 active:scale-95 transition duration-200"
> >
<Plus className="w-3.5 h-3.5 md:w-4 md:h-4" /> <Plus className="w-3.5 h-3.5 md:w-4 md:h-4" />
<span>Add Provider</span> <span>Add Connection</span>
</button> </button>
<AnimatePresence> <AnimatePresence>
{open && ( {open && (
@@ -120,7 +120,7 @@ const AddProvider = ({
<form onSubmit={handleSubmit} className="flex flex-col flex-1"> <form onSubmit={handleSubmit} className="flex flex-col flex-1">
<div className="px-6 pt-6 pb-4"> <div className="px-6 pt-6 pb-4">
<h3 className="text-black/90 dark:text-white/90 font-medium"> <h3 className="text-black/90 dark:text-white/90 font-medium">
Add new provider Add new connection
</h3> </h3>
</div> </div>
<div className="border-t border-light-200 dark:border-dark-200" /> <div className="border-t border-light-200 dark:border-dark-200" />
@@ -128,7 +128,7 @@ const AddProvider = ({
<div className="flex flex-col space-y-4"> <div className="flex flex-col space-y-4">
<div className="flex flex-col items-start space-y-2"> <div className="flex flex-col items-start space-y-2">
<label className="text-xs text-black/70 dark:text-white/70"> <label className="text-xs text-black/70 dark:text-white/70">
Select provider type Select connection type
</label> </label>
<Select <Select
value={selectedProvider ?? ''} value={selectedProvider ?? ''}
@@ -149,13 +149,13 @@ const AddProvider = ({
className="flex flex-col items-start space-y-2" className="flex flex-col items-start space-y-2"
> >
<label className="text-xs text-black/70 dark:text-white/70"> <label className="text-xs text-black/70 dark:text-white/70">
Name* Connection Name*
</label> </label>
<input <input
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
placeholder={'Provider Name'} placeholder={'e.g., My OpenAI Connection'}
type="text" type="text"
required={true} required={true}
/> />
@@ -199,7 +199,7 @@ const AddProvider = ({
{loading ? ( {loading ? (
<Loader2 className="animate-spin" size={16} /> <Loader2 className="animate-spin" size={16} />
) : ( ) : (
'Add Provider' 'Add Connection'
)} )}
</button> </button>
</div> </div>

View File

@@ -34,10 +34,10 @@ const DeleteProvider = ({
return prev.filter((p) => p.id !== modelProvider.id); return prev.filter((p) => p.id !== modelProvider.id);
}); });
toast.success('Provider deleted successfully.'); toast.success('Connection deleted successfully.');
} catch (error) { } catch (error) {
console.error('Error deleting provider:', error); console.error('Error deleting provider:', error);
toast.error('Failed to delete provider.'); toast.error('Failed to delete connection.');
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -51,7 +51,7 @@ const DeleteProvider = ({
setOpen(true); setOpen(true);
}} }}
className="group p-1.5 rounded-md hover:bg-light-200 hover:dark:bg-dark-200 transition-colors group" className="group p-1.5 rounded-md hover:bg-light-200 hover:dark:bg-dark-200 transition-colors group"
title="Delete provider" title="Delete connection"
> >
<Trash2 <Trash2
size={14} size={14}
@@ -76,14 +76,15 @@ const DeleteProvider = ({
<DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg"> <DialogPanel className="w-full mx-4 lg:w-[600px] max-h-[85vh] flex flex-col border bg-light-primary dark:bg-dark-primary border-light-secondary dark:border-dark-secondary rounded-lg">
<div className="px-6 pt-6 pb-4"> <div className="px-6 pt-6 pb-4">
<h3 className="text-black/90 dark:text-white/90 font-medium"> <h3 className="text-black/90 dark:text-white/90 font-medium">
Delete provider Delete connection
</h3> </h3>
</div> </div>
<div className="border-t border-light-200 dark:border-dark-200" /> <div className="border-t border-light-200 dark:border-dark-200" />
<div className="flex-1 overflow-y-auto px-6 py-4"> <div className="flex-1 overflow-y-auto px-6 py-4">
<p className="text-SM text-black/60 dark:text-white/60"> <p className="text-sm text-black/60 dark:text-white/60">
Are you sure you want to delete the provider &quot; Are you sure you want to delete the connection &quot;
{modelProvider.name}&quot;? This action cannot be undone. {modelProvider.name}&quot;? This action cannot be undone.
All associated models will also be removed.
</p> </p>
</div> </div>
<div className="px-6 py-6 flex justify-end space-x-2"> <div className="px-6 py-6 flex justify-end space-x-2">

View File

@@ -1,7 +1,7 @@
import { UIConfigField, ConfigModelProvider } from '@/lib/config/types'; import { UIConfigField, ConfigModelProvider } from '@/lib/config/types';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { AlertCircle, ChevronDown, Pencil, Trash2, X } from 'lucide-react'; import { AlertCircle, Plug2, Plus, Pencil, Trash2, X } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import AddModel from './AddModelDialog'; import AddModel from './AddModelDialog';
@@ -17,7 +17,7 @@ const ModelProvider = ({
fields: UIConfigField[]; fields: UIConfigField[];
setProviders: React.Dispatch<React.SetStateAction<ConfigModelProvider[]>>; setProviders: React.Dispatch<React.SetStateAction<ConfigModelProvider[]>>;
}) => { }) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(true);
const handleModelDelete = async ( const handleModelDelete = async (
type: 'chat' | 'embedding', type: 'chat' | 'embedding',
@@ -66,146 +66,157 @@ const ModelProvider = ({
} }
}; };
const modelCount =
modelProvider.chatModels.filter((m) => m.key !== 'error').length +
modelProvider.embeddingModels.filter((m) => m.key !== 'error').length;
const hasError =
modelProvider.chatModels.some((m) => m.key === 'error') ||
modelProvider.embeddingModels.some((m) => m.key === 'error');
return ( return (
<div <div
key={modelProvider.id} key={modelProvider.id}
className="border border-light-200 dark:border-dark-200 rounded-lg overflow-hidden" className="border border-light-200 dark:border-dark-200 rounded-lg overflow-hidden bg-light-primary dark:bg-dark-primary"
> >
<div <div className="px-5 py-3.5 flex flex-row justify-between w-full items-center border-b border-light-200 dark:border-dark-200 bg-light-secondary/30 dark:bg-dark-secondary/30">
className={cn( <div className="flex items-center gap-2.5">
'group px-5 py-4 flex flex-row justify-between w-full cursor-pointer hover:bg-light-secondary hover:dark:bg-dark-secondary transition duration-200 items-center', <div className="p-1.5 rounded-md bg-sky-500/10 dark:bg-sky-500/10">
!open && 'rounded-lg', <Plug2 size={14} className="text-sky-500" />
)}
onClick={() => setOpen(!open)}
>
<p className="text-sm lg:text-base text-black dark:text-white font-medium">
{modelProvider.name}
</p>
<div className="flex items-center gap-4">
<div className="flex flex-row items-center">
<UpdateProvider
fields={fields}
modelProvider={modelProvider}
setProviders={setProviders}
/>
<DeleteProvider
modelProvider={modelProvider}
setProviders={setProviders}
/>
</div> </div>
<ChevronDown <div className="flex flex-col">
size={16} <p className="text-sm lg:text-base text-black dark:text-white font-medium">
className={cn( {modelProvider.name}
open ? 'rotate-180' : '', </p>
'transition duration-200 text-black/70 dark:text-white/70 group-hover:text-sky-500', {modelCount > 0 && (
<p className="text-[10px] lg:text-xs text-black/50 dark:text-white/50">
{modelCount} model{modelCount !== 1 ? 's' : ''} configured
</p>
)} )}
</div>
</div>
<div className="flex flex-row items-center gap-1">
<UpdateProvider
fields={fields}
modelProvider={modelProvider}
setProviders={setProviders}
/>
<DeleteProvider
modelProvider={modelProvider}
setProviders={setProviders}
/> />
</div> </div>
</div> </div>
<AnimatePresence> <div className="flex flex-col gap-y-4 px-5 py-4">
{open && ( <div className="flex flex-col gap-y-2">
<motion.div <div className="flex flex-row w-full justify-between items-center">
initial={{ height: 0, opacity: 0 }} <p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
animate={{ height: 'auto', opacity: 1 }} Chat Models
exit={{ height: 0, opacity: 0 }} </p>
transition={{ duration: 0.1 }} {!modelProvider.chatModels.some((m) => m.key === 'error') && (
> <AddModel
<div className="border-t border-light-200 dark:border-dark-200" /> providerId={modelProvider.id}
<div className="flex flex-col gap-y-4 px-5 py-4"> setProviders={setProviders}
<div className="flex flex-col gap-y-2"> type="chat"
<div className="flex flex-row w-full justify-between items-center"> />
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70"> )}
Chat models </div>
</p> <div className="flex flex-col gap-2">
<AddModel {modelProvider.chatModels.some((m) => m.key === 'error') ? (
providerId={modelProvider.id} <div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
setProviders={setProviders} <AlertCircle size={16} className="shrink-0" />
type="chat" <span className="break-words">
/> {
</div> modelProvider.chatModels.find((m) => m.key === 'error')
<div className="flex flex-col gap-2"> ?.name
{modelProvider.chatModels.some((m) => m.key === 'error') ? ( }
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30"> </span>
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.chatModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.chatModels.map((model, index) => (
<div
key={`${modelProvider.id}-chat-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('chat', model.key);
}}
>
<X size={12} />
</button>
</div>
))}
</div>
)}
</div>
</div> </div>
<div className="flex flex-col gap-y-2"> ) : modelProvider.chatModels.filter((m) => m.key !== 'error')
<div className="flex flex-row w-full justify-between items-center"> .length === 0 && !hasError ? (
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70"> <div className="flex flex-col items-center justify-center py-4 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/20 dark:bg-dark-secondary/20">
Embedding models <p className="text-xs text-black/50 dark:text-white/50 text-center">
</p> No chat models configured
<AddModel </p>
providerId={modelProvider.id}
setProviders={setProviders}
type="embedding"
/>
</div>
<div className="flex flex-col gap-2">
{modelProvider.embeddingModels.some(
(m) => m.key === 'error',
) ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.embeddingModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.embeddingModels.map((model, index) => (
<div
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('embedding', model.key);
}}
>
<X size={12} />
</button>
</div>
))}
</div>
)}
</div>
</div> </div>
</div> ) : modelProvider.chatModels.filter((m) => m.key !== 'error')
</motion.div> .length > 0 ? (
)} <div className="flex flex-row flex-wrap gap-2">
</AnimatePresence> {modelProvider.chatModels.map((model, index) => (
<div
key={`${modelProvider.id}-chat-${model.key}-${index}`}
className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('chat', model.key);
}}
className="hover:text-red-500 dark:hover:text-red-400 transition-colors"
>
<X size={12} />
</button>
</div>
))}
</div>
) : null}
</div>
</div>
<div className="flex flex-col gap-y-2">
<div className="flex flex-row w-full justify-between items-center">
<p className="text-[11px] lg:text-xs font-medium text-black/70 dark:text-white/70 uppercase tracking-wide">
Embedding Models
</p>
{!modelProvider.embeddingModels.some((m) => m.key === 'error') && (
<AddModel
providerId={modelProvider.id}
setProviders={setProviders}
type="embedding"
/>
)}
</div>
<div className="flex flex-col gap-2">
{modelProvider.embeddingModels.some((m) => m.key === 'error') ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.embeddingModels.find((m) => m.key === 'error')
?.name
}
</span>
</div>
) : modelProvider.embeddingModels.filter((m) => m.key !== 'error')
.length === 0 && !hasError ? (
<div className="flex flex-col items-center justify-center py-4 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/20 dark:bg-dark-secondary/20">
<p className="text-xs text-black/50 dark:text-white/50 text-center">
No embedding models configured
</p>
</div>
) : modelProvider.embeddingModels.filter((m) => m.key !== 'error')
.length > 0 ? (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.embeddingModels.map((model, index) => (
<div
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
className="flex flex-row items-center space-x-1.5 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5 border border-light-200 dark:border-dark-200"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('embedding', model.key);
}}
className="hover:text-red-500 dark:hover:text-red-400 transition-colors"
>
<X size={12} />
</button>
</div>
))}
</div>
) : null}
</div>
</div>
</div>
</div> </div>
); );
}; };

View File

@@ -64,8 +64,8 @@ const ModelSelect = ({
</h4> </h4>
<p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50"> <p className="text-[11px] lg:text-xs text-black/50 dark:text-white/50">
{type === 'chat' {type === 'chat'
? 'Select the model to use for chat responses' ? 'Choose which model to use for generating responses'
: 'Select the model to use for embeddings'} : 'Choose which model to use for generating embeddings'}
</p> </p>
</div> </div>
<Select <Select

View File

@@ -39,22 +39,50 @@ const Models = ({
<div className="border-t border-light-200 dark:border-dark-200" /> <div className="border-t border-light-200 dark:border-dark-200" />
<div className="flex flex-row justify-between items-center px-6 "> <div className="flex flex-row justify-between items-center px-6 ">
<p className="text-xs lg:text-sm text-black/70 dark:text-white/70"> <p className="text-xs lg:text-sm text-black/70 dark:text-white/70">
Manage model provider Manage connections
</p> </p>
<AddProvider modelProviders={fields} setProviders={setProviders} /> <AddProvider modelProviders={fields} setProviders={setProviders} />
</div> </div>
<div className="flex flex-col px-6 gap-y-4"> <div className="flex flex-col px-6 gap-y-4">
{providers.map((provider) => ( {providers.length === 0 ? (
<ModelProvider <div className="flex flex-col items-center justify-center py-12 px-4 rounded-lg border-2 border-dashed border-light-200 dark:border-dark-200 bg-light-secondary/10 dark:bg-dark-secondary/10">
key={`provider-${provider.id}`} <div className="p-3 rounded-full bg-sky-500/10 dark:bg-sky-500/10 mb-3">
fields={ <svg
(fields.find((f) => f.key === provider.type)?.fields ?? xmlns="http://www.w3.org/2000/svg"
[]) as UIConfigField[] className="w-8 h-8 text-sky-500"
} fill="none"
modelProvider={provider} viewBox="0 0 24 24"
setProviders={setProviders} stroke="currentColor"
/> >
))} <path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M13 10V3L4 14h7v7l9-11h-7z"
/>
</svg>
</div>
<p className="text-sm font-medium text-black/70 dark:text-white/70 mb-1">
No connections yet
</p>
<p className="text-xs text-black/50 dark:text-white/50 text-center max-w-sm mb-4">
Add your first connection to start using AI models. Connect to
OpenAI, Anthropic, Ollama, and more.
</p>
</div>
) : (
providers.map((provider) => (
<ModelProvider
key={`provider-${provider.id}`}
fields={
(fields.find((f) => f.key === provider.type)?.fields ??
[]) as UIConfigField[]
}
modelProvider={provider}
setProviders={setProviders}
/>
))
)}
</div> </div>
</div> </div>
); );

View File

@@ -67,10 +67,10 @@ const UpdateProvider = ({
}); });
}); });
toast.success('Provider updated successfully.'); toast.success('Connection updated successfully.');
} catch (error) { } catch (error) {
console.error('Error updating provider:', error); console.error('Error updating provider:', error);
toast.error('Failed to update provider.'); toast.error('Failed to update connection.');
} finally { } finally {
setLoading(false); setLoading(false);
setOpen(false); setOpen(false);
@@ -110,7 +110,7 @@ const UpdateProvider = ({
<form onSubmit={handleSubmit} className="flex flex-col flex-1"> <form onSubmit={handleSubmit} className="flex flex-col flex-1">
<div className="px-6 pt-6 pb-4"> <div className="px-6 pt-6 pb-4">
<h3 className="text-black/90 dark:text-white/90 font-medium"> <h3 className="text-black/90 dark:text-white/90 font-medium">
Update provider Update connection
</h3> </h3>
</div> </div>
<div className="border-t border-light-200 dark:border-dark-200" /> <div className="border-t border-light-200 dark:border-dark-200" />
@@ -121,13 +121,13 @@ const UpdateProvider = ({
className="flex flex-col items-start space-y-2" className="flex flex-col items-start space-y-2"
> >
<label className="text-xs text-black/70 dark:text-white/70"> <label className="text-xs text-black/70 dark:text-white/70">
Name* Connection Name*
</label> </label>
<input <input
value={name} value={name}
onChange={(event) => setName(event.target.value)} onChange={(event) => setName(event.target.value)}
className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60" className="w-full rounded-lg border border-light-200 dark:border-dark-200 bg-light-primary dark:bg-dark-primary px-4 py-3 pr-10 text-sm text-black/80 dark:text-white/80 placeholder:text-black/40 dark:placeholder:text-white/40 focus-visible:outline-none focus-visible:border-light-300 dark:focus-visible:border-dark-300 transition-colors disabled:cursor-not-allowed disabled:opacity-60"
placeholder={'Provider Name'} placeholder={'Connection Name'}
type="text" type="text"
required={true} required={true}
/> />
@@ -171,7 +171,7 @@ const UpdateProvider = ({
{loading ? ( {loading ? (
<Loader2 className="animate-spin" size={16} /> <Loader2 className="animate-spin" size={16} />
) : ( ) : (
'Update Provider' 'Update Connection'
)} )}
</button> </button>
</div> </div>

View File

@@ -28,7 +28,7 @@ const sections = [
{ {
key: 'models', key: 'models',
name: 'Models', name: 'Models',
description: 'Configure model settings.', description: 'Connect to AI services and manage connections.',
icon: BrainCog, icon: BrainCog,
component: Models, component: Models,
dataAdd: 'modelProviders', dataAdd: 'modelProviders',

View File

@@ -63,7 +63,11 @@ const SetupConfig = ({
} }
}; };
const hasProviders = providers.length > 0; const visibleProviders = providers.filter(
(p) => p.name.toLowerCase() !== 'transformers',
);
const hasProviders =
visibleProviders.filter((p) => p.chatModels.length > 0).length > 0;
return ( return (
<div className="w-[95vw] md:w-[80vw] lg:w-[65vw] mx-auto px-2 sm:px-4 md:px-6 flex flex-col space-y-6"> <div className="w-[95vw] md:w-[80vw] lg:w-[65vw] mx-auto px-2 sm:px-4 md:px-6 flex flex-col space-y-6">
@@ -81,10 +85,10 @@ const SetupConfig = ({
<div className="flex flex-row justify-between items-center mb-4 md:mb-6 pb-3 md:pb-4 border-b border-light-200 dark:border-dark-200"> <div className="flex flex-row justify-between items-center mb-4 md:mb-6 pb-3 md:pb-4 border-b border-light-200 dark:border-dark-200">
<div> <div>
<p className="text-xs sm:text-sm font-medium text-black dark:text-white"> <p className="text-xs sm:text-sm font-medium text-black dark:text-white">
Manage Providers Manage Connections
</p> </p>
<p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-0.5"> <p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-0.5">
Add and configure your model providers Add connections to access AI models
</p> </p>
</div> </div>
<AddProvider <AddProvider
@@ -100,14 +104,17 @@ const SetupConfig = ({
Loading providers... Loading providers...
</p> </p>
</div> </div>
) : providers.length === 0 ? ( ) : visibleProviders.length === 0 ? (
<div className="flex flex-col items-center justify-center py-8 md:py-12 text-center"> <div className="flex flex-col items-center justify-center py-8 md:py-12 text-center">
<p className="text-xs sm:text-sm font-medium text-black/70 dark:text-white/70"> <p className="text-xs sm:text-sm font-medium text-black/70 dark:text-white/70">
No providers configured No connections configured
</p>
<p className="text-[10px] sm:text-xs text-black/50 dark:text-white/50 mt-1">
Click &quot;Add Connection&quot; above to get started
</p> </p>
</div> </div>
) : ( ) : (
providers.map((provider) => ( visibleProviders.map((provider) => (
<ModelProvider <ModelProvider
key={`provider-${provider.id}`} key={`provider-${provider.id}`}
fields={ fields={