feat(combos): display vision adapter models in an ordered table view
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -734,8 +734,15 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
||||
const patch = (p) => onChange({ ...entry, ...p });
|
||||
|
||||
const handleAdd = (model) => {
|
||||
if (models.includes(model.value)) return;
|
||||
patch({ models: [...models, model.value] });
|
||||
const value = model?.value || model?.name || model;
|
||||
if (!value || models.includes(value)) return;
|
||||
patch({ models: [...models, value] });
|
||||
};
|
||||
|
||||
const handleDeselect = (model) => {
|
||||
const value = model?.value || model?.name || model;
|
||||
const next = models.filter((m) => m !== value);
|
||||
patch({ models: next.length === 0 ? [DEFAULT_FALLBACK_MODEL] : next });
|
||||
};
|
||||
|
||||
const handleRemove = (index) => {
|
||||
@@ -754,7 +761,7 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
||||
return (
|
||||
<Card padding="sm" className={`group ${!enabled ? "opacity-50" : ""}`}>
|
||||
<div className="flex min-w-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
{/* Master toggle + icon + label + chips */}
|
||||
{/* Master toggle + icon + label */}
|
||||
<div className="flex min-w-0 flex-1 items-start gap-2.5 sm:items-center">
|
||||
<Toggle
|
||||
checked={enabled}
|
||||
@@ -769,33 +776,6 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
||||
<code className="font-mono text-sm font-medium">{cap.label}</code>
|
||||
<span className="text-[10px] text-text-muted">— {cap.desc}</span>
|
||||
</div>
|
||||
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-1">
|
||||
{models.length === 0 ? (
|
||||
<span className="text-xs text-text-muted italic">No models</span>
|
||||
) : (
|
||||
models.slice(0, 3).map((model, index) => (
|
||||
<code
|
||||
key={`${model}-${index}`}
|
||||
className="group/chip inline-flex items-center gap-1 rounded bg-black/5 px-1.5 py-0.5 font-mono text-xs text-text-muted dark:bg-white/5"
|
||||
>
|
||||
<span>{model}</span>
|
||||
<CapacityBadges caps={getCaps?.(model)} />
|
||||
<button onClick={() => handleMove(index, -1)} disabled={index === 0} className={`leading-none opacity-0 group-hover/chip:opacity-100 ${index === 0 ? "text-text-muted/20" : "text-text-muted hover:text-primary"}`}>
|
||||
<span className="material-symbols-outlined text-[12px]">arrow_upward</span>
|
||||
</button>
|
||||
<button onClick={() => handleMove(index, 1)} disabled={index === models.length - 1} className={`leading-none opacity-0 group-hover/chip:opacity-100 ${index === models.length - 1 ? "text-text-muted/20" : "text-text-muted hover:text-primary"}`}>
|
||||
<span className="material-symbols-outlined text-[12px]">arrow_downward</span>
|
||||
</button>
|
||||
<button onClick={() => handleRemove(index)} className="leading-none opacity-0 group-hover/chip:opacity-100 text-text-muted hover:text-red-500">
|
||||
<span className="material-symbols-outlined text-[12px]">close</span>
|
||||
</button>
|
||||
</code>
|
||||
))
|
||||
)}
|
||||
{models.length > 3 && (
|
||||
<span className="text-[10px] text-text-muted">+{models.length - 3} more</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -823,11 +803,97 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Model pool list/table */}
|
||||
{models.length === 0 ? (
|
||||
<div className="mt-3 py-2 text-center text-xs text-text-muted italic">
|
||||
No models in pool (will fallback to {DEFAULT_FALLBACK_MODEL})
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-3 overflow-hidden rounded-lg border border-border/50">
|
||||
<table className="w-full text-left text-xs">
|
||||
<thead>
|
||||
<tr className="border-b border-border/40 bg-black/[0.02] text-text-muted dark:bg-white/[0.02]">
|
||||
<th className="w-12 px-3 py-1.5 font-medium text-center">#</th>
|
||||
<th className="px-3 py-1.5 font-medium">Model</th>
|
||||
<th className="w-24 px-3 py-1.5 font-medium text-center">Order</th>
|
||||
<th className="w-12 px-3 py-1.5 font-medium text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border/30 font-mono">
|
||||
{models.map((model, index) => (
|
||||
<tr key={`${model}-${index}`} className="hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors">
|
||||
<td className="px-3 py-2 text-center text-text-muted text-[11px] font-sans">
|
||||
#{index + 1}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-text-main">
|
||||
<div className="flex items-center gap-1.5 flex-wrap">
|
||||
<span className="truncate">{model}</span>
|
||||
<CapacityBadges caps={getCaps?.(model)} />
|
||||
{model === DEFAULT_FALLBACK_MODEL && (
|
||||
<span className="rounded bg-emerald-500/10 px-1.5 py-0.5 font-sans text-[10px] font-medium text-emerald-600 dark:text-emerald-400">
|
||||
free default
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-center">
|
||||
<div className="inline-flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMove(index, -1)}
|
||||
disabled={!enabled || index === 0}
|
||||
className={`p-1 rounded transition-colors ${
|
||||
!enabled || index === 0
|
||||
? "text-text-muted/20 cursor-not-allowed"
|
||||
: "text-text-muted hover:text-primary hover:bg-black/5 dark:hover:bg-white/5"
|
||||
}`}
|
||||
title="Move up"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px] leading-none">arrow_upward</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMove(index, 1)}
|
||||
disabled={!enabled || index === models.length - 1}
|
||||
className={`p-1 rounded transition-colors ${
|
||||
!enabled || index === models.length - 1
|
||||
? "text-text-muted/20 cursor-not-allowed"
|
||||
: "text-text-muted hover:text-primary hover:bg-black/5 dark:hover:bg-white/5"
|
||||
}`}
|
||||
title="Move down"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px] leading-none">arrow_downward</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemove(index)}
|
||||
disabled={!enabled}
|
||||
className={`p-1 rounded transition-colors ${
|
||||
!enabled
|
||||
? "text-text-muted/20 cursor-not-allowed"
|
||||
: "text-text-muted hover:text-red-500 hover:bg-red-500/10"
|
||||
}`}
|
||||
title="Remove model"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px] leading-none">close</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showModelSelect && (
|
||||
<ModelSelectModal
|
||||
isOpen={showModelSelect}
|
||||
onClose={() => setShowModelSelect(false)}
|
||||
onSelect={handleAdd}
|
||||
onDeselect={handleDeselect}
|
||||
activeProviders={activeProviders}
|
||||
title={`Add ${cap.label} Model`}
|
||||
addedModelValues={models}
|
||||
|
||||
Reference in New Issue
Block a user