import { useRef, useState, useEffect, useCallback } from 'react' import { useTabStore, type Tab } from '../../stores/tabStore' import { useChatStore } from '../../stores/chatStore' import { useTranslation } from '../../i18n' import { WindowControls, showWindowControls } from './WindowControls' const TAB_WIDTH = 180 export function TabBar() { const tabs = useTabStore((s) => s.tabs) const activeTabId = useTabStore((s) => s.activeTabId) const setActiveTab = useTabStore((s) => s.setActiveTab) const closeTab = useTabStore((s) => s.closeTab) const disconnectSession = useChatStore((s) => s.disconnectSession) const moveTab = useTabStore((s) => s.moveTab) const scrollRef = useRef(null) const [canScrollLeft, setCanScrollLeft] = useState(false) const [canScrollRight, setCanScrollRight] = useState(false) const [contextMenu, setContextMenu] = useState<{ sessionId: string; x: number; y: number } | null>(null) const [closingTabId, setClosingTabId] = useState(null) const [dragOverIndex, setDragOverIndex] = useState(null) const dragIndexRef = useRef(null) const t = useTranslation() const updateScrollState = useCallback(() => { const el = scrollRef.current if (!el) return setCanScrollLeft(el.scrollLeft > 0) setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1) }, []) useEffect(() => { updateScrollState() const el = scrollRef.current if (!el) return el.addEventListener('scroll', updateScrollState) const ro = new ResizeObserver(updateScrollState) ro.observe(el) return () => { el.removeEventListener('scroll', updateScrollState) ro.disconnect() } }, [updateScrollState, tabs.length]) useEffect(() => { if (!contextMenu) return const close = () => setContextMenu(null) document.addEventListener('click', close) return () => document.removeEventListener('click', close) }, [contextMenu]) const scroll = (direction: 'left' | 'right') => { const el = scrollRef.current if (!el) return el.scrollBy({ left: direction === 'left' ? -TAB_WIDTH : TAB_WIDTH, behavior: 'smooth' }) } const handleClose = (sessionId: string) => { // Special tabs can always be closed directly const tab = tabs.find((t) => t.sessionId === sessionId) if (tab && tab.type !== 'session') { closeTab(sessionId) return } const sessionState = useChatStore.getState().sessions[sessionId] const isRunning = sessionState && sessionState.chatState !== 'idle' if (isRunning) { setClosingTabId(sessionId) return } disconnectSession(sessionId) closeTab(sessionId) } const handleContextMenu = (e: React.MouseEvent, sessionId: string) => { e.preventDefault() setContextMenu({ sessionId, x: e.clientX, y: e.clientY }) } const handleCloseOthers = (sessionId: string) => { setContextMenu(null) const otherIds = tabs.filter((t) => t.sessionId !== sessionId).map((t) => t.sessionId) for (const id of otherIds) { disconnectSession(id) closeTab(id) } } const handleCloseLeft = (sessionId: string) => { setContextMenu(null) const idx = tabs.findIndex((t) => t.sessionId === sessionId) const leftIds = tabs.slice(0, idx).map((t) => t.sessionId) for (const id of leftIds) { disconnectSession(id) closeTab(id) } } const handleCloseRight = (sessionId: string) => { setContextMenu(null) const idx = tabs.findIndex((t) => t.sessionId === sessionId) const rightIds = tabs.slice(idx + 1).map((t) => t.sessionId) for (const id of rightIds) { disconnectSession(id) closeTab(id) } } const handleCloseAll = () => { setContextMenu(null) const allIds = tabs.map((t) => t.sessionId) for (const id of allIds) { disconnectSession(id) closeTab(id) } } const handleDragStart = (index: number) => { dragIndexRef.current = index } const handleDragOver = (e: React.DragEvent, index: number) => { e.preventDefault() if (dragIndexRef.current === null || dragIndexRef.current === index) { setDragOverIndex(null) return } setDragOverIndex(index) } const handleDrop = (index: number) => { if (dragIndexRef.current !== null && dragIndexRef.current !== index) { moveTab(dragIndexRef.current, index) } dragIndexRef.current = null setDragOverIndex(null) } const handleDragEnd = () => { dragIndexRef.current = null setDragOverIndex(null) } if (tabs.length === 0 && !showWindowControls) return null return (
{canScrollLeft && ( )}
e.preventDefault()}> {tabs.map((tab, index) => ( setActiveTab(tab.sessionId)} onClose={() => handleClose(tab.sessionId)} onContextMenu={(e) => handleContextMenu(e, tab.sessionId)} onDragStart={() => handleDragStart(index)} onDragOver={(e) => handleDragOver(e, index)} onDrop={() => handleDrop(index)} onDragEnd={handleDragEnd} /> ))}
{canScrollRight && ( )} {contextMenu && (
)} {closingTabId && (

{t('tabs.closeConfirmTitle')}

{t('tabs.closeConfirmMessage')}

)}
) } function TabItem({ tab, isActive, isDragOver, onClick, onClose, onContextMenu, onDragStart, onDragOver, onDrop, onDragEnd }: { tab: Tab isActive: boolean isDragOver: boolean onClick: () => void onClose: () => void onContextMenu: (e: React.MouseEvent) => void onDragStart: () => void onDragOver: (e: React.DragEvent) => void onDrop: () => void onDragEnd: () => void }) { return (
{tab.type === 'session' && tab.status === 'running' && ( )} {tab.type === 'session' && tab.status === 'error' && ( )} {tab.type === 'settings' && ( settings )} {tab.type === 'scheduled' && ( schedule )} {tab.title || 'Untitled'}
) }