Download src/components/layout/header.tsx from Vrda/clinicpal: direct link, hf CLI and curl.
- Browser
- Download file 5.1 kB
-
https://huggingface.co/spaces/Vrda/clinicpal/resolve/main/src/components/layout/header.tsx
- Command line
-
hf download hf://spaces/Vrda/clinicpal/src/components/layout/header.tsx
-
curl -L -o header.tsx https://huggingface.co/spaces/Vrda/clinicpal/resolve/main/src/components/layout/header.tsx
5.1 kB
| 'use client'; | |
| import { useRef, useEffect } from 'react'; | |
| import Image from 'next/image'; | |
| import { motion } from 'framer-motion'; | |
| import { cn } from '@/lib/utils'; | |
| import { useConnectionStatus, useMedgemmaStatus, useLocalStatus, useAppStore, useSettings } from '@/store/app-store'; | |
| import { GlassTooltip } from '@/components/ui'; | |
| import { TabNavigation } from './tab-navigation'; | |
| export function Header() { | |
| const connectionStatus = useConnectionStatus(); | |
| const medgemmaStatus = useMedgemmaStatus(); | |
| const localStatus = useLocalStatus(); | |
| const settings = useSettings(); | |
| const setConnectionStatus = useAppStore((state) => state.setConnectionStatus); | |
| const setAvailableModels = useAppStore((state) => state.setAvailableModels); | |
| const updateSettings = useAppStore((state) => state.updateSettings); | |
| // Determine the active provider's status | |
| const activeStatus = | |
| settings.provider === 'local' | |
| ? localStatus | |
| : settings.provider === 'medgemma' | |
| ? medgemmaStatus | |
| : connectionStatus; | |
| const providerLabel = | |
| settings.provider === 'local' | |
| ? 'Local' | |
| : settings.provider === 'medgemma' | |
| ? 'MedGemma' | |
| : 'Ollama'; | |
| const abortControllerRef = useRef<AbortController | null>(null); | |
| // Cleanup on unmount | |
| useEffect(() => { | |
| return () => { | |
| abortControllerRef.current?.abort(); | |
| }; | |
| }, []); | |
| const handleRetryConnection = async () => { | |
| // Abort any in-flight request | |
| abortControllerRef.current?.abort(); | |
| abortControllerRef.current = new AbortController(); | |
| setConnectionStatus('checking'); | |
| try { | |
| const response = await fetch('/api/ollama', { | |
| signal: abortControllerRef.current.signal, | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| setConnectionStatus('connected'); | |
| setAvailableModels(data.data.models); | |
| // Auto-select first model if none selected | |
| if (data.data.models.length > 0) { | |
| updateSettings({ selectedModel: data.data.models[0] }); | |
| } | |
| } else { | |
| setConnectionStatus('disconnected'); | |
| } | |
| } catch (error) { | |
| // Ignore abort errors | |
| if (error instanceof Error && error.name !== 'AbortError') { | |
| setConnectionStatus('disconnected'); | |
| } | |
| } | |
| }; | |
| return ( | |
| <motion.header | |
| className={cn( | |
| 'fixed top-0 left-0 right-0 z-50', | |
| 'glass-elevated', | |
| 'border-b border-[var(--glass-border-subtle)]' | |
| )} | |
| initial={{ y: -100 }} | |
| animate={{ y: 0 }} | |
| transition={{ duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] }} | |
| > | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div className="flex items-center justify-between h-16"> | |
| {/* Logo / Title */} | |
| <div className="flex items-center gap-3"> | |
| <Image | |
| src="/clinicpal-iOS-Default-1024x1024@1x.png" | |
| alt="ClinicPal" | |
| width={36} | |
| height={36} | |
| /> | |
| <h1 className="text-lg font-semibold text-[var(--foreground)]"> | |
| Clinipal - Clinical Error Detector | |
| </h1> | |
| </div> | |
| {/* Right side: Tab Navigation + Connection Status */} | |
| <div className="flex items-center gap-4"> | |
| {/* Tab Navigation */} | |
| <div className="hidden md:block"> | |
| <TabNavigation /> | |
| </div> | |
| {/* Connection Status */} | |
| <GlassTooltip | |
| content={ | |
| activeStatus === 'connected' | |
| ? `${providerLabel} is connected and ready` | |
| : activeStatus === 'disconnected' | |
| ? `Cannot connect to ${providerLabel}. Click to retry.` | |
| : `Checking connection to ${providerLabel}...` | |
| } | |
| side="bottom" | |
| > | |
| <button | |
| onClick={ | |
| activeStatus === 'disconnected' | |
| ? handleRetryConnection | |
| : undefined | |
| } | |
| className={cn( | |
| 'flex items-center gap-2 px-3 py-1.5 rounded-full', | |
| 'glass text-sm font-medium', | |
| 'transition-all duration-200', | |
| activeStatus === 'disconnected' && | |
| 'hover:bg-[var(--glass-bg-elevated)] cursor-pointer' | |
| )} | |
| > | |
| <span | |
| className={cn( | |
| 'w-2 h-2 rounded-full', | |
| activeStatus === 'connected' && 'bg-green-500 pulse-glow-success', | |
| activeStatus === 'disconnected' && 'bg-red-500', | |
| activeStatus === 'checking' && 'bg-amber-500 animate-pulse' | |
| )} | |
| /> | |
| <span className="hidden sm:inline"> | |
| {activeStatus === 'connected' && `${providerLabel}`} | |
| {activeStatus === 'disconnected' && `${providerLabel} Off`} | |
| {activeStatus === 'checking' && 'Checking...'} | |
| </span> | |
| </button> | |
| </GlassTooltip> | |
| </div> | |
| </div> | |
| </div> | |
| </motion.header> | |
| ); | |
| } | |