qmd-web / src /components /ModelStatus.tsx
shreyask's picture
Deploy qmd-web
ac50275 verified
import type { ModelState } from '../types';
interface ModelStatusProps {
models: ModelState[];
}
const STATUS_COLOR: Record<ModelState['status'], string> = {
pending: '#9e9e9e',
downloading: '#1976d2',
loading: '#f9a825',
ready: '#388e3c',
error: '#d32f2f',
};
const STATUS_LABEL: Record<ModelState['status'], string> = {
pending: 'Pending',
downloading: 'Downloading',
loading: 'Loading',
ready: 'Ready',
error: 'Error',
};
function ProgressBar({ progress, color }: { progress: number; color: string }) {
return (
<div style={{
height: '4px',
background: 'var(--border)',
borderRadius: '2px',
overflow: 'hidden',
marginTop: '4px',
}}>
<div style={{
height: '100%',
width: `${Math.round(progress * 100)}%`,
background: color,
borderRadius: '2px',
transition: 'width 0.3s ease',
}} />
</div>
);
}
function ModelRow({ model }: { model: ModelState }) {
const color = STATUS_COLOR[model.status];
const showProgress = model.status === 'downloading' || model.status === 'loading';
return (
<div style={{
padding: '0.5rem 0.75rem',
background: 'var(--bg-card)',
border: '1px solid var(--border)',
borderRadius: '6px',
marginBottom: '0.4rem',
}}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<span style={{
fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace",
fontSize: '0.78rem',
color: 'var(--text)',
}}>
{model.name}
</span>
<span style={{
fontSize: '0.72rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontWeight: 600,
color,
display: 'flex',
alignItems: 'center',
gap: '0.3rem',
}}>
{model.status === 'ready' && (
<span style={{ fontSize: '0.85rem' }}>{'\u2713'}</span>
)}
{model.status === 'error' && (
<span style={{ fontSize: '0.85rem' }}>{'\u2717'}</span>
)}
{STATUS_LABEL[model.status]}
{showProgress && (
<span style={{ color: 'var(--text-secondary)', fontWeight: 400 }}>
{Math.round(model.progress * 100)}%
</span>
)}
</span>
</div>
{showProgress && <ProgressBar progress={model.progress} color={color} />}
{model.status === 'error' && model.error && (
<div style={{
marginTop: '4px',
fontSize: '0.72rem',
color: '#d32f2f',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}>
{model.error}
</div>
)}
</div>
);
}
export default function ModelStatus({ models }: ModelStatusProps) {
const coreModels = models.filter((model) => model.name !== 'expansion');
const expansionModel = models.find((model) => model.name === 'expansion');
const coreReady = coreModels.length > 0 && coreModels.every((model) => model.status === 'ready');
const expansionReady = expansionModel?.status === 'ready';
const expansionUnavailable = expansionModel?.status === 'error';
return (
<div style={{
padding: '1rem',
background: 'var(--bg-section)',
border: '1px solid var(--border)',
borderRadius: '8px',
marginBottom: '1.5rem',
}}>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: '0.6rem',
}}>
<h3 style={{
margin: 0,
fontSize: '0.85rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontWeight: 600,
color: 'var(--text-secondary)',
textTransform: 'uppercase',
letterSpacing: '0.05em',
}}>
Models
</h3>
{coreReady && (
<span style={{
fontSize: '0.75rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
color: '#388e3c',
fontWeight: 600,
}}>
Search ready
</span>
)}
</div>
{!coreReady && (
<p style={{
margin: '0 0 0.5rem',
fontSize: '0.75rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
color: 'var(--text-secondary)',
lineHeight: 1.4,
}}>
First load downloads several GB of model weights. Subsequent visits use the browser cache.
</p>
)}
{coreReady && !expansionReady && !expansionUnavailable && (
<p style={{
margin: '0 0 0.5rem',
fontSize: '0.75rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
color: 'var(--text-secondary)',
lineHeight: 1.4,
}}>
Embedding and reranker ready. Compact expansion model (single-file download) loading...
</p>
)}
{coreReady && expansionUnavailable && (
<p style={{
margin: '0 0 0.5rem',
fontSize: '0.75rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
color: 'var(--text-secondary)',
lineHeight: 1.4,
}}>
Expansion model unavailable. Search uses the original query directly.
</p>
)}
{models.map(m => (
<ModelRow key={m.name} model={m} />
))}
{models.length === 0 && (
<div style={{
color: 'var(--text-muted)',
fontSize: '0.85rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}>
No models configured.
</div>
)}
</div>
);
}