Bonsai-Chat-WebGPU / src /app /components /ModelRail.tsx
Valeriy Selitskiy
Release v1.2.2 interactive chat workspace
21ad36a
Raw
History Blame Contribute Delete
2.32 kB
import type { ModelId, ModelOption } from '../../lib/contracts';
interface ModelRailProps {
hidden?: boolean;
models: ModelOption[];
activeModelId: ModelId;
busy: boolean;
onModelSelect: (modelId: ModelId) => void;
}
const availabilityLabels: Record<ModelOption['availability'], string> = {
loaded: 'loaded',
cached: 'cached',
remote: 'remote',
limited: 'limited',
loading: 'loading',
};
export function ModelRail({
hidden = false,
models,
activeModelId,
busy,
onModelSelect,
}: ModelRailProps) {
return (
<aside className="model-rail" aria-labelledby="model-rail-title" hidden={hidden}>
<div className="rail-heading">
<div>
<h2 id="model-rail-title">Models</h2>
<p>Choose what runs in this browser.</p>
</div>
</div>
<div className="model-list" role="list" aria-label="Available Bonsai models">
{models.map((model) => {
const active = model.id === activeModelId;
return (
<article className={`model-card${active ? ' is-active' : ''}`} key={model.id} role="listitem">
<button
className="model-select"
type="button"
disabled={busy}
onClick={() => onModelSelect(model.id)}
aria-pressed={active}
>
<span className="model-copy">
<span className="model-title-line">
<strong>{model.parameters}</strong>
<small className={`availability availability-${model.availability}`}>
{availabilityLabels[model.availability]}
</small>
</span>
<span className="model-architecture">{model.architectureLabel}</span>
<span>{model.runtimeLabel}</span>
</span>
<span className="model-footprint">{model.footprint}</span>
</button>
{model.availability === 'limited' && <small className="model-limit-reason">{model.limitReason ?? 'Device gate unavailable'}</small>}
</article>
);
})}
</div>
<footer className="rail-footnote">
<span>GGUF · Q1_0</span>
<span>Commit pinned</span>
</footer>
</aside>
);
}