import React, { createContext, useState, useContext, useCallback, useEffect } from 'react'; import { useTranslation } from '@/hooks/useTranslation'; export interface Tab { id: string; type: 'chat' | 'agent' | 'projects' | 'usage' | 'mcp' | 'settings' | 'agent-execution' | 'create-agent' | 'import-agent'; title: string; sessionId?: string; // for chat tabs sessionData?: any; // for chat tabs - stores full session object agentRunId?: string; // for agent tabs agentData?: any; // for agent-execution tabs claudeFileId?: string; // for claude-file tabs initialProjectPath?: string; // for chat tabs status: 'active' | 'idle' | 'running' | 'complete' | 'error'; hasUnsavedChanges: boolean; order: number; icon?: string; createdAt: Date; updatedAt: Date; } interface TabContextType { tabs: Tab[]; activeTabId: string | null; addTab: (tab: Omit) => string; removeTab: (id: string) => void; updateTab: (id: string, updates: Partial) => void; setActiveTab: (id: string) => void; reorderTabs: (startIndex: number, endIndex: number) => void; getTabById: (id: string) => Tab | undefined; closeAllTabs: () => void; getTabsByType: (type: 'chat' | 'agent') => Tab[]; } const TabContext = createContext(undefined); // const STORAGE_KEY = 'claudia_tabs'; // No longer needed - persistence disabled const MAX_TABS = 20; export const TabProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { t } = useTranslation(); const [tabs, setTabs] = useState([]); const [activeTabId, setActiveTabId] = useState(null); // Ensure there is always at least one projects tab, but avoid clobbering existing tabs useEffect(() => { if (tabs.length > 0) { return; } const defaultTab: Tab = { id: generateTabId(), type: 'projects', title: t('ccProjects'), status: 'idle', hasUnsavedChanges: false, order: 0, icon: 'folder', createdAt: new Date(), updatedAt: new Date() }; setTabs([defaultTab]); setActiveTabId(defaultTab.id); }, [tabs.length, t]); // Keep the projects tab title in sync with the active locale without resetting other tabs useEffect(() => { if (tabs.length === 0) { return; } const translatedTitle = t('ccProjects'); setTabs(prevTabs => { let hasChanges = false; const updatedTabs = prevTabs.map(tab => { if (tab.type === 'projects' && tab.title !== translatedTitle) { hasChanges = true; return { ...tab, title: translatedTitle }; } return tab; }); return hasChanges ? updatedTabs : prevTabs; }); }, [t, tabs.length]); // Guard against an empty activeTabId when tabs exist useEffect(() => { if (!activeTabId && tabs.length > 0) { setActiveTabId(tabs[0].id); } }, [activeTabId, tabs]); // Tab persistence disabled - no longer saving to localStorage // useEffect(() => { // if (tabs.length > 0) { // const tabsToSave = tabs.map(tab => ({ // ...tab, // createdAt: tab.createdAt.toISOString(), // updatedAt: tab.updatedAt.toISOString() // })); // localStorage.setItem(STORAGE_KEY, JSON.stringify(tabsToSave)); // } // }, [tabs]); const generateTabId = () => { return `tab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; }; const addTab = useCallback((tabData: Omit): string => { if (tabs.length >= MAX_TABS) { throw new Error(t('maximumTabsReached', { max: MAX_TABS })); } const newTab: Tab = { ...tabData, id: generateTabId(), order: tabs.length, createdAt: new Date(), updatedAt: new Date() }; setTabs(prevTabs => [...prevTabs, newTab]); setActiveTabId(newTab.id); return newTab.id; }, [tabs.length, t]); const removeTab = useCallback((id: string) => { setTabs(prevTabs => { const filteredTabs = prevTabs.filter(tab => tab.id !== id); // Reorder remaining tabs const reorderedTabs = filteredTabs.map((tab, index) => ({ ...tab, order: index })); // Update active tab if necessary if (activeTabId === id && reorderedTabs.length > 0) { const removedTabIndex = prevTabs.findIndex(tab => tab.id === id); const newActiveIndex = Math.min(removedTabIndex, reorderedTabs.length - 1); setActiveTabId(reorderedTabs[newActiveIndex].id); } else if (reorderedTabs.length === 0) { setActiveTabId(null); } return reorderedTabs; }); }, [activeTabId]); const updateTab = useCallback((id: string, updates: Partial) => { setTabs(prevTabs => prevTabs.map(tab => tab.id === id ? { ...tab, ...updates, updatedAt: new Date() } : tab ) ); }, []); const setActiveTab = useCallback((id: string) => { const tabExists = tabs.find(tab => tab.id === id); if (tabExists) { setActiveTabId(id); } }, [tabs, activeTabId]); const reorderTabs = useCallback((startIndex: number, endIndex: number) => { setTabs(prevTabs => { const newTabs = [...prevTabs]; const [removed] = newTabs.splice(startIndex, 1); newTabs.splice(endIndex, 0, removed); // Update order property return newTabs.map((tab, index) => ({ ...tab, order: index })); }); }, []); const getTabById = useCallback((id: string): Tab | undefined => { return tabs.find(tab => tab.id === id); }, [tabs]); const closeAllTabs = useCallback(() => { setTabs([]); setActiveTabId(null); // localStorage.removeItem(STORAGE_KEY); // Persistence disabled }, []); const getTabsByType = useCallback((type: 'chat' | 'agent'): Tab[] => { return tabs.filter(tab => tab.type === type); }, [tabs]); const value: TabContextType = { tabs, activeTabId, addTab, removeTab, updateTab, setActiveTab, reorderTabs, getTabById, closeAllTabs, getTabsByType }; return ( {children} ); }; export const useTabContext = () => { const context = useContext(TabContext); if (!context) { throw new Error('useTabContext must be used within a TabProvider'); } return context; };