feat: update to v3.4.0 and redesign model selection UI with a dropdown and descriptive panel

This commit is contained in:
CJACK
2026-04-13 02:27:12 +08:00
parent d53a2ea7d2
commit 7ef6a7d11f
5 changed files with 50 additions and 44 deletions

View File

@@ -1 +1 @@
3.3.0
3.4.0

View File

@@ -82,7 +82,7 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) {
})
return (
<div className={clsx('flex flex-col lg:grid lg:grid-cols-12 gap-6 h-[calc(100vh-140px)]')}>
<div className={clsx('flex flex-col lg:grid lg:grid-cols-12 gap-6 h-[calc(100vh-140px)] min-h-0')}>
<ConfigPanel
t={t}
configExpanded={configExpanded}

View File

@@ -38,13 +38,15 @@ export default function ConfigPanel({
ToggleLeft,
ToggleRight,
}
const selectedModel = models.find(m => m.id === model) || models[0]
const SelectedModelIcon = selectedModel ? (iconMap[selectedModel.icon] || MessageSquare) : MessageSquare
return (
<div className={clsx(
"lg:col-span-3 flex flex-col transition-all duration-300 ease-in-out z-20",
"lg:col-span-3 flex flex-col transition-all duration-300 ease-in-out z-20 min-h-0",
configExpanded ? "h-auto" : "h-14 lg:h-full"
)}>
<div className="bg-card border border-border rounded-xl flex flex-col h-full shadow-sm">
<div className="bg-card border border-border rounded-xl flex flex-col h-full shadow-sm min-h-0 overflow-hidden">
<button
onClick={() => setConfigExpanded(!configExpanded)}
className="lg:hidden flex items-center justify-between p-4 w-full bg-muted/20 hover:bg-muted/30 transition-colors"
@@ -61,49 +63,51 @@ export default function ConfigPanel({
</button>
<div className={clsx(
"p-4 space-y-6 overflow-y-auto custom-scrollbar flex-1",
!configExpanded && "hidden lg:block"
"p-4 flex flex-col gap-5",
!configExpanded && "hidden lg:flex"
)}>
<div className="space-y-3">
<div className="space-y-2 shrink-0">
<label className="text-[11px] font-semibold text-muted-foreground uppercase tracking-wider ml-0.5">{t('apiTester.modelLabel')}</label>
<div className="grid grid-cols-1 gap-2">
{models.map(m => {
const Icon = iconMap[m.icon] || MessageSquare
return (
<button
key={m.id}
onClick={() => setModel(m.id)}
className={clsx(
"group relative flex items-start gap-3 p-3 rounded-lg border text-left transition-all duration-200",
model === m.id
? "bg-secondary border-primary/50 shadow-sm"
: "bg-transparent border-transparent hover:bg-muted"
)}
>
<div className={clsx(
"p-1.5 rounded-md shrink-0 transition-colors",
model === m.id ? m.color : "text-muted-foreground group-hover:text-foreground"
)}>
<Icon className="w-4 h-4" />
</div>
<div className="min-w-0 flex-1">
<div className={clsx("font-medium text-sm", model === m.id ? "text-foreground" : "text-foreground/80") }>
{m.name}
</div>
<div className="text-[11px] text-muted-foreground mt-0.5">{m.desc}</div>
</div>
{model === m.id && (
<div className={clsx("absolute top-3 right-3", m.color)}>
<div className="w-1.5 h-1.5 rounded-full bg-current" />
</div>
)}
</button>
)
})}
<div className="relative">
<select
className="w-full h-11 pl-3 pr-9 bg-secondary border border-border rounded-lg text-sm appearance-none focus:outline-none focus:ring-1 focus:ring-ring focus:border-ring transition-all cursor-pointer hover:bg-muted/70 text-foreground"
value={model}
onChange={e => setModel(e.target.value)}
>
{models.map(m => (
<option key={m.id} value={m.id} className="bg-popover text-popover-foreground">
{m.name}
</option>
))}
</select>
<ChevronDown className="absolute right-2.5 top-3.5 w-4 h-4 text-muted-foreground pointer-events-none" />
</div>
{selectedModel && (
<div className="mt-3 rounded-lg border border-border bg-muted/20 p-3">
<div className="flex items-start gap-3">
<div className={clsx(
"p-2 rounded-md shrink-0 border border-border bg-background/80",
selectedModel.color
)}>
<SelectedModelIcon className="w-4 h-4" />
</div>
<div className="min-w-0 flex-1">
<div className="font-medium text-sm text-foreground truncate">
{selectedModel.name}
</div>
<div className="text-[11px] text-muted-foreground mt-1 leading-relaxed">
{selectedModel.desc}
</div>
</div>
</div>
<p className="text-[11px] text-muted-foreground/70 mt-2">
{t('apiTester.modelPickerHint')}
</p>
</div>
)}
</div>
<div className="space-y-2">
<div className="space-y-2 shrink-0">
<label className="text-[11px] font-semibold text-muted-foreground uppercase tracking-wider ml-0.5">{t('apiTester.streamMode')}</label>
<button
onClick={() => setStreamingMode(!streamingMode)}
@@ -124,7 +128,7 @@ export default function ConfigPanel({
</button>
</div>
<div className="space-y-2">
<div className="space-y-2 shrink-0">
<label className="text-[11px] font-semibold text-muted-foreground uppercase tracking-wider ml-0.5">{t('apiTester.accountSelector')}</label>
<div className="relative">
<select
@@ -147,7 +151,7 @@ export default function ConfigPanel({
</div>
</div>
<div className="space-y-2">
<div className="space-y-2 shrink-0">
<label className="text-[11px] font-semibold text-muted-foreground uppercase tracking-wider ml-0.5">{t('apiTester.apiKeyOptional')}</label>
<input
type="text"

View File

@@ -215,6 +215,7 @@
"testSuccess": "{account}: Token refresh successful ({time}ms)",
"config": "Configuration",
"modelLabel": "Model",
"modelPickerHint": "Use the dropdown to pick a model. The list scrolls automatically.",
"streamMode": "Streaming",
"accountSelector": "Account",
"autoRandom": "🤖 Auto / Random",

View File

@@ -215,6 +215,7 @@
"testSuccess": "{account}: Token 刷新成功 ({time}ms)",
"config": "配置",
"modelLabel": "模型",
"modelPickerHint": "使用下拉列表选择模型,长列表会自动滚动。",
"streamMode": "流式模式",
"accountSelector": "选择账号",
"autoRandom": "🤖 自动 / 随机",