/** * TabItem — local shell uses the same Terminal icon as the launcher (+) */ import { X, Terminal, FolderSync, Cable, Crown, TerminalSquare } from 'lucide-react' import { useTranslation } from 'react-i18next' import type { Tab } from '../../../stores/tabStore' import { useSessionStore } from '../../../stores/sessionStore' import HostDistroIcon from '../../common/HostDistroIcon' import { resolveTabTitle } from '../../../utils/tabTitle' interface TabItemProps { tab: Tab index: number isActive: boolean isDragging: boolean dragOverIndex: number | null onActivate: () => void onClose: (e: { stopPropagation(): void }) => void onContextMenu: (e: React.MouseEvent) => void onDragStart: (e: React.DragEvent, index: number) => void onDragOver: (e: React.DragEvent, index: number) => void onDrop: (e: React.DragEvent, index: number) => void onDragEnd: () => void } export function TabItem({ tab, index, isActive, isDragging, dragOverIndex, onActivate, onClose, onContextMenu, onDragStart, onDragOver, onDrop, onDragEnd, }: TabItemProps) { const { t } = useTranslation(['terminal', 'common']) const displayTitle = resolveTabTitle(tab) const isLocalTerm = tab.type === 'terminal' && tab.isLocal const session = useSessionStore((state) => tab.sessionId ? state.sessions.find((item) => item.id === tab.sessionId) : undefined ) const getIcon = () => { if (isLocalTerm) { return ( ) } if (tab.type === 'rdp' || tab.type === 'vnc' || (tab.type === 'terminal' && session)) { return ( ) } switch (tab.type) { case 'terminal': return case 'sftp': return case 'serial': return case 'upgrade': return case 'batch': return default: return null } } const showLeftIndicator = dragOverIndex === index && !isDragging const showRightIndicator = dragOverIndex === index + 1 && !isDragging const statusDot = tab.connectionStatus === 'connecting' ? ( ) : tab.connectionStatus === 'connected' ? ( ) : tab.connectionStatus === 'error' ? ( ) : null return (
{ if (e.button === 1) { e.preventDefault() e.stopPropagation() onClose(e) } }} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() onActivate() } if (e.key === 'Delete') { e.preventDefault() onClose(e) } }} onContextMenu={onContextMenu} onDragStart={(e) => onDragStart(e, index)} onDragOver={(e) => onDragOver(e, index)} onDrop={(e) => onDrop(e, index)} onDragEnd={onDragEnd} className={` group relative flex min-w-[120px] max-w-[220px] cursor-pointer select-none items-center gap-2 border-r border-dark-700/20 px-3 py-2 titlebar-no-drag transition-all duration-150 ${ isActive ? 'bg-dark-900 text-content-primary' : 'bg-dark-800 text-txt-muted hover:bg-dark-700 hover:text-txt-muted' } ${isDragging ? 'opacity-50' : 'opacity-100'} `} > {showLeftIndicator && (
)} {showRightIndicator && (
)} {statusDot} {getIcon()} {displayTitle}
) }