Natnael B. Haile
Cursor
feat: complete IR Studio with Git LFS tracking for all binary assets and NFCorpus index
fbad523 Download src/components/Header.tsx from NathyB/ir-studio: direct link, hf CLI and curl.
- Browser
- Download file 7.05 kB
-
https://huggingface.co/spaces/NathyB/ir-studio/resolve/main/src/components/Header.tsx
- Command line
-
hf download hf://spaces/NathyB/ir-studio/src/components/Header.tsx
-
curl -L -o Header.tsx https://huggingface.co/spaces/NathyB/ir-studio/resolve/main/src/components/Header.tsx
7.05 kB
| import React, { useEffect } from 'react'; | |
| import { | |
| Sparkles, | |
| Layers, | |
| BookOpen, | |
| FolderOpen, | |
| Database, | |
| Sun, | |
| Moon, | |
| } from 'lucide-react'; | |
| import type { CorpusDataset } from '../types/corpus'; | |
| interface HeaderProps { | |
| activeTab: string; | |
| setActiveTab: (tab: string) => void; | |
| datasets?: CorpusDataset[]; | |
| activeDatasetId?: string; | |
| onSelectDataset?: (id: string) => void; | |
| onOpenExplorer?: () => void; | |
| theme?: 'dark' | 'light'; | |
| onToggleTheme?: () => void; | |
| } | |
| export const Header: React.FC<HeaderProps> = ({ | |
| activeTab, | |
| setActiveTab, | |
| datasets = [], | |
| activeDatasetId, | |
| onSelectDataset, | |
| onOpenExplorer, | |
| theme = 'light', | |
| onToggleTheme, | |
| }) => { | |
| const tabs = [ | |
| { id: 'playground', label: 'Retrieval Arena', icon: Sparkles }, | |
| { id: 'index', label: 'Index Inspector', icon: Layers }, | |
| { id: 'concepts', label: 'Core Concepts', icon: BookOpen }, | |
| ]; | |
| // Quick hotkeys: 1 -> Arena, 2 -> Inspector, 3 -> Core Concepts (when not in an input/textarea) | |
| useEffect(() => { | |
| const handleKeyDown = (e: KeyboardEvent) => { | |
| const target = e.target as HTMLElement; | |
| if ( | |
| target && | |
| (target.tagName === 'INPUT' || | |
| target.tagName === 'TEXTAREA' || | |
| target.tagName === 'SELECT' || | |
| target.isContentEditable) | |
| ) { | |
| return; | |
| } | |
| if (e.key === '1' && !e.metaKey && !e.ctrlKey) { | |
| setActiveTab('playground'); | |
| } else if (e.key === '2' && !e.metaKey && !e.ctrlKey) { | |
| setActiveTab('index'); | |
| } else if (e.key === '3' && !e.metaKey && !e.ctrlKey) { | |
| setActiveTab('concepts'); | |
| } | |
| }; | |
| window.addEventListener('keydown', handleKeyDown); | |
| return () => window.removeEventListener('keydown', handleKeyDown); | |
| }, [setActiveTab]); | |
| return ( | |
| <header className="border-b border-zinc-800/80 bg-zinc-950/90 backdrop-blur-md sticky top-0 z-40"> | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div className="flex items-center justify-between h-14 gap-3"> | |
| {/* Left: Brand */} | |
| <div className="flex items-center space-x-2.5 shrink-0"> | |
| <div className="w-7 h-7 rounded-lg bg-zinc-100 dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-800 flex items-center justify-center text-sm shadow-xs select-none"> | |
| 🦫 | |
| </div> | |
| <span className="font-semibold text-sm tracking-tight text-zinc-900 dark:text-zinc-100"> | |
| IR Studio | |
| </span> | |
| </div> | |
| {/* Center: Primary Navigation Tabs (Retrieval Arena vs Index Inspector) */} | |
| <nav className="flex items-center p-1 rounded-xl bg-zinc-950/80 border border-zinc-800 shadow-inner gap-1"> | |
| {tabs.map((tab, idx) => { | |
| const Icon = tab.icon; | |
| const isActive = activeTab === tab.id; | |
| const accentColor = | |
| tab.id === 'playground' | |
| ? 'text-sky-400' | |
| : tab.id === 'index' | |
| ? 'text-emerald-400' | |
| : 'text-amber-400'; | |
| return ( | |
| <button | |
| key={tab.id} | |
| onClick={() => setActiveTab(tab.id)} | |
| title={`Switch to ${tab.label} (Press ${idx + 1})`} | |
| className={`group relative flex items-center space-x-2 px-3.5 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 cursor-pointer ${ | |
| isActive | |
| ? 'bg-zinc-850 text-zinc-100 font-semibold shadow-xs border border-zinc-750' | |
| : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-900/60 border border-transparent' | |
| }`} | |
| > | |
| <Icon | |
| className={`w-3.5 h-3.5 ${ | |
| isActive ? accentColor : 'text-zinc-500 group-hover:text-zinc-300' | |
| } transition-colors`} | |
| /> | |
| <span>{tab.label}</span> | |
| <span | |
| className={`text-[9px] font-mono px-1 py-0.2 rounded transition-colors ${ | |
| isActive | |
| ? 'bg-zinc-900 text-zinc-400 border border-zinc-800' | |
| : 'text-zinc-600 group-hover:text-zinc-400' | |
| }`} | |
| > | |
| {idx + 1} | |
| </span> | |
| </button> | |
| ); | |
| })} | |
| </nav> | |
| {/* Right: Mounted Dataset Picker & Theme Toggle */} | |
| <div className="flex items-center space-x-2 shrink-0"> | |
| {datasets.length > 0 && onSelectDataset && ( | |
| <div className="hidden sm:flex items-center space-x-2 border border-zinc-800 bg-zinc-900/80 px-2.5 py-1 rounded-xl text-xs shadow-xs h-9"> | |
| <Database className="w-3.5 h-3.5 text-zinc-400 shrink-0" /> | |
| <span className="text-zinc-500 font-mono text-[11px]">corpus:</span> | |
| <select | |
| value={activeDatasetId} | |
| onChange={(e) => onSelectDataset(e.target.value)} | |
| className="bg-transparent text-zinc-200 font-medium focus:outline-none max-w-[150px] truncate cursor-pointer text-xs" | |
| > | |
| {datasets.map((d) => ( | |
| <option key={d.id} value={d.id} className="bg-zinc-900 text-zinc-200"> | |
| {d.name.replace(' (Pedagogical Subset)', '').replace(' (BEIR Pedagogical Subset)', '')} | |
| </option> | |
| ))} | |
| </select> | |
| <div className="w-px h-3.5 bg-zinc-800 shrink-0" /> | |
| {onOpenExplorer && ( | |
| <button | |
| type="button" | |
| onClick={onOpenExplorer} | |
| className="flex items-center space-x-1 text-[11px] font-mono text-zinc-400 hover:text-zinc-100 px-1.5 py-0.5 rounded-md hover:bg-zinc-800 transition cursor-pointer" | |
| title="Open virtual file explorer" | |
| > | |
| <FolderOpen className="w-3 h-3 text-zinc-400" /> | |
| <span>files</span> | |
| </button> | |
| )} | |
| </div> | |
| )} | |
| {/* Theme Toggle (Dark / Light) */} | |
| {onToggleTheme && ( | |
| <button | |
| type="button" | |
| onClick={onToggleTheme} | |
| className="w-9 h-9 rounded-xl border border-zinc-800 bg-zinc-900/80 hover:bg-zinc-850 text-zinc-400 hover:text-zinc-100 transition shadow-xs flex items-center justify-center cursor-pointer shrink-0" | |
| title={theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'} | |
| aria-label="Toggle theme" | |
| > | |
| {theme === 'dark' ? ( | |
| <Sun className="w-4 h-4 text-amber-400" /> | |
| ) : ( | |
| <Moon className="w-4 h-4 text-sky-400" /> | |
| )} | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| ); | |
| }; | |