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 patch = (p) => onChange({ ...entry, ...p });
|
||||||
|
|
||||||
const handleAdd = (model) => {
|
const handleAdd = (model) => {
|
||||||
if (models.includes(model.value)) return;
|
const value = model?.value || model?.name || model;
|
||||||
patch({ models: [...models, model.value] });
|
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) => {
|
const handleRemove = (index) => {
|
||||||
@@ -754,7 +761,7 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
|||||||
return (
|
return (
|
||||||
<Card padding="sm" className={`group ${!enabled ? "opacity-50" : ""}`}>
|
<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">
|
<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">
|
<div className="flex min-w-0 flex-1 items-start gap-2.5 sm:items-center">
|
||||||
<Toggle
|
<Toggle
|
||||||
checked={enabled}
|
checked={enabled}
|
||||||
@@ -769,33 +776,6 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
|||||||
<code className="font-mono text-sm font-medium">{cap.label}</code>
|
<code className="font-mono text-sm font-medium">{cap.label}</code>
|
||||||
<span className="text-[10px] text-text-muted">— {cap.desc}</span>
|
<span className="text-[10px] text-text-muted">— {cap.desc}</span>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -823,11 +803,97 @@ function CapacityAdapterCap({ cap, entry, onChange, activeProviders, getCaps })
|
|||||||
</div>
|
</div>
|
||||||
</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 && (
|
{showModelSelect && (
|
||||||
<ModelSelectModal
|
<ModelSelectModal
|
||||||
isOpen={showModelSelect}
|
isOpen={showModelSelect}
|
||||||
onClose={() => setShowModelSelect(false)}
|
onClose={() => setShowModelSelect(false)}
|
||||||
onSelect={handleAdd}
|
onSelect={handleAdd}
|
||||||
|
onDeselect={handleDeselect}
|
||||||
activeProviders={activeProviders}
|
activeProviders={activeProviders}
|
||||||
title={`Add ${cap.label} Model`}
|
title={`Add ${cap.label} Model`}
|
||||||
addedModelValues={models}
|
addedModelValues={models}
|
||||||
|
|||||||
Reference in New Issue
Block a user