| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 'use client';
- import { useState, useEffect } from 'react';
- import { apiClient, type ModelInfo, type QueueStatus, type JobInfo } from '@/lib/api';
- import { AlertCircle, CheckCircle2, Loader2, Activity } from 'lucide-react';
- import { cn } from '@/lib/utils';
- export function ModelStatusBar() {
- const [loadedModel, setLoadedModel] = useState<ModelInfo | null>(null);
- const [loading, setLoading] = useState(true);
- const [queueStatus, setQueueStatus] = useState<QueueStatus | null>(null);
- const [activeJob, setActiveJob] = useState<JobInfo | null>(null);
- useEffect(() => {
- const checkStatus = async () => {
- try {
- const [loadedModels, queue] = await Promise.all([
- apiClient.getModels(undefined, true),
- apiClient.getQueueStatus(),
- ]);
- setLoadedModel(loadedModels.length > 0 ? loadedModels[0] : null);
- setQueueStatus(queue);
- // Find active/processing job
- const processing = queue.jobs.find(
- (job) => job.status === 'processing' || job.status === 'queued'
- );
- setActiveJob(processing || null);
- } catch (error) {
- console.error('Failed to check status:', error);
- } finally {
- setLoading(false);
- }
- };
- checkStatus();
- // Poll every 1 second when there's an active job, otherwise every 5 seconds
- const pollInterval = activeJob ? 1000 : 5000;
- const interval = setInterval(checkStatus, pollInterval);
- return () => clearInterval(interval);
- }, [activeJob]);
- if (loading) {
- return null;
- }
- // Determine status styling
- let statusBg = '';
- let statusBorder = '';
- let statusText = '';
- let icon = null;
- let content = null;
- if (activeJob && activeJob.status === 'processing') {
- // Active generation in progress
- statusBg = 'bg-blue-600 dark:bg-blue-700';
- statusBorder = 'border-blue-500 dark:border-blue-600';
- statusText = 'text-white';
- icon = <Loader2 className="h-4 w-4 flex-shrink-0 animate-spin" />;
- const progress = activeJob.progress !== undefined ? Math.round(activeJob.progress * 100) : 0;
- content = (
- <>
- <span className="font-semibold">Generating:</span>
- <span className="truncate">{activeJob.id}</span>
- <div className="flex items-center gap-2 ml-auto">
- <div className="w-40 h-2.5 bg-blue-900/50 dark:bg-blue-950/50 rounded-full overflow-hidden border border-blue-400/30">
- <div
- className="h-full bg-blue-200 dark:bg-blue-300 transition-all duration-300"
- style={{ width: `${progress}%` }}
- />
- </div>
- <span className="text-sm font-semibold min-w-[3rem] text-right">{progress}%</span>
- </div>
- </>
- );
- } else if (activeJob && activeJob.status === 'queued') {
- // Job queued but not processing yet
- statusBg = 'bg-purple-600 dark:bg-purple-700';
- statusBorder = 'border-purple-500 dark:border-purple-600';
- statusText = 'text-white';
- icon = <Activity className="h-4 w-4 flex-shrink-0" />;
- content = (
- <>
- <span className="font-semibold">Queued:</span>
- <span className="truncate">{queueStatus?.size || 0} job(s) waiting</span>
- {activeJob.queue_position !== undefined && (
- <span className="text-sm ml-auto">Position: {activeJob.queue_position}</span>
- )}
- </>
- );
- } else if (loadedModel) {
- // Model loaded, ready
- statusBg = 'bg-green-600 dark:bg-green-700';
- statusBorder = 'border-green-500 dark:border-green-600';
- statusText = 'text-white';
- icon = <CheckCircle2 className="h-4 w-4 flex-shrink-0" />;
- content = (
- <>
- <span className="font-semibold">Model Ready:</span>
- <span className="truncate">{loadedModel.name}</span>
- {loadedModel.sha256_short && (
- <span className="text-sm opacity-90 ml-auto">({loadedModel.sha256_short})</span>
- )}
- </>
- );
- } else {
- // No model loaded
- statusBg = 'bg-amber-600 dark:bg-amber-700';
- statusBorder = 'border-amber-500 dark:border-amber-600';
- statusText = 'text-white';
- icon = <AlertCircle className="h-4 w-4 flex-shrink-0" />;
- content = (
- <>
- <span className="font-semibold">No Model Loaded</span>
- <span className="text-sm opacity-90">Please load a model from the Models page</span>
- </>
- );
- }
- return (
- <div
- className={cn(
- 'fixed bottom-0 left-0 right-0 border-t-2 px-4 py-3 shadow-lg z-50',
- statusBg,
- statusBorder,
- statusText
- )}
- >
- <div className="container mx-auto flex items-center gap-3 text-sm">
- {icon}
- {content}
- </div>
- </div>
- );
- }
|