Download src/components/layout/BottomToolWindowBar.tsx from Leon4gr45/Velo: direct link, hf CLI and curl.
- Browser
- Download file 2.84 kB
-
https://huggingface.co/spaces/Leon4gr45/Velo/resolve/main/src/components/layout/BottomToolWindowBar.tsx
- Command line
-
hf download hf://spaces/Leon4gr45/Velo/src/components/layout/BottomToolWindowBar.tsx
-
curl -L -o BottomToolWindowBar.tsx https://huggingface.co/spaces/Leon4gr45/Velo/resolve/main/src/components/layout/BottomToolWindowBar.tsx
2.84 kB
| import { Zap, ScrollText, Cpu } from 'lucide-react' | |
| import { useLayoutStore, type BottomTab } from '../../stores/layout-store' | |
| import { useWorkflowStore } from '../../stores/workflow-store' | |
| import { useTranslation } from 'react-i18next' | |
| /** | |
| * 底部工具窗口左侧按钮栏(BottomToolWindowBar) | |
| * JetBrains 风格:28px 宽,纯图标 + title tooltip,激活时右侧 2px 竖线 | |
| * 对应截图左下角红框区域 | |
| */ | |
| export default function BottomToolWindowBar() { | |
| const { t } = useTranslation('layout') | |
| const bottomTab = useLayoutStore(s => s.bottomTab) | |
| const setBottomTab = useLayoutStore(s => s.setBottomTab) | |
| const activeRuns = useWorkflowStore(s => s.activeRuns) | |
| // Bottom tabs | |
| const bottomTabs: Array<{ id: BottomTab; icon: typeof Zap; label: string }> = [ | |
| { id: 'tasks', icon: Zap, label: t('statusBar.tasksLabel') }, | |
| { id: 'log', icon: ScrollText, label: t('bottomPanel.tabs.log') }, | |
| { id: 'models', icon: Cpu, label: t('bottomPanel.tabs.models') }, | |
| ] | |
| return ( | |
| <div | |
| className="no-select flex flex-col items-center justify-start h-full py-0.5" | |
| style={{ | |
| width: 'var(--width-bottom-bar)', /* 28px */ | |
| backgroundColor: 'var(--color-activity-bar)', | |
| borderRight: '1px solid var(--color-border)', | |
| flexShrink: 0, | |
| }} | |
| > | |
| {bottomTabs.map(({ id, icon: Icon, label }) => { | |
| const isActive = bottomTab === id | |
| // 任务Tab:任何工作流运行中时显示脉冲小点 | |
| const hasRunning = activeRuns.some(r => r.status === 'running') | |
| const hasWaiting = activeRuns.some(r => r.status === 'waiting') | |
| const showPulse = id === 'tasks' && (hasRunning || hasWaiting) | |
| return ( | |
| <div key={id} className="relative w-full"> | |
| <button | |
| onClick={() => setBottomTab(id)} | |
| title={label} | |
| className="bottom-tool-btn" | |
| style={{ | |
| boxShadow: isActive | |
| ? 'inset -2px 0 0 var(--color-accent)' | |
| : 'none', | |
| color: isActive | |
| ? 'var(--color-accent)' | |
| : 'var(--color-activity-icon)', | |
| }} | |
| > | |
| <Icon size={13} strokeWidth={isActive ? 2 : 1.5} /> | |
| </button> | |
| {/* 状态脉冲点(工作流运行中) */} | |
| {showPulse && ( | |
| <span | |
| className="absolute top-[3px] right-[3px] w-[5px] h-[5px] rounded-full animate-pulse pointer-events-none" | |
| style={{ | |
| backgroundColor: hasWaiting && !hasRunning | |
| ? 'var(--color-warning)' | |
| : 'var(--color-accent)', | |
| }} | |
| /> | |
| )} | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| ) | |
| } | |