import React, { createContext, useContext, useState, useRef, ReactNode } from 'react'; export interface Tab { id: number; type: 'home' | 'terminal' | 'ssh_manager'; title: string; hostConfig?: any; terminalRef?: React.RefObject; } interface TabContextType { tabs: Tab[]; currentTab: number | null; allSplitScreenTab: number[]; addTab: (tab: Omit) => number; removeTab: (tabId: number) => void; setCurrentTab: (tabId: number) => void; setSplitScreenTab: (tabId: number) => void; getTab: (tabId: number) => Tab | undefined; } const TabContext = createContext(undefined); export function useTabs() { const context = useContext(TabContext); if (context === undefined) { throw new Error('useTabs must be used within a TabProvider'); } return context; } interface TabProviderProps { children: ReactNode; } export function TabProvider({ children }: TabProviderProps) { const [tabs, setTabs] = useState([ { id: 1, type: 'home', title: 'Home' } ]); const [currentTab, setCurrentTab] = useState(1); const [allSplitScreenTab, setAllSplitScreenTab] = useState([]); const nextTabId = useRef(2); function computeUniqueTerminalTitle(desiredTitle: string | undefined): string { const baseTitle = (desiredTitle || 'Terminal').trim(); // Extract base name without trailing " (n)" const match = baseTitle.match(/^(.*) \((\d+)\)$/); const root = match ? match[1] : baseTitle; const usedNumbers = new Set(); let rootUsed = false; tabs.forEach(t => { if (t.type !== 'terminal' || !t.title) return; if (t.title === root) { rootUsed = true; return; } const m = t.title.match(new RegExp(`^${root.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")} \\((\\d+)\\)$`)); if (m) { const n = parseInt(m[1], 10); if (!isNaN(n)) usedNumbers.add(n); } }); if (!rootUsed) return root; // Start at (2) for the second instance let n = 2; while (usedNumbers.has(n)) n += 1; return `${root} (${n})`; } const addTab = (tabData: Omit): number => { const id = nextTabId.current++; const effectiveTitle = tabData.type === 'terminal' ? computeUniqueTerminalTitle(tabData.title) : (tabData.title || ''); const newTab: Tab = { ...tabData, id, title: effectiveTitle, terminalRef: tabData.type === 'terminal' ? React.createRef() : undefined }; console.log('Adding new tab:', newTab); setTabs(prev => [...prev, newTab]); setCurrentTab(id); setAllSplitScreenTab(prev => prev.filter(tid => tid !== id)); return id; }; const removeTab = (tabId: number) => { const tab = tabs.find(t => t.id === tabId); if (tab && tab.terminalRef?.current && typeof tab.terminalRef.current.disconnect === "function") { tab.terminalRef.current.disconnect(); } setTabs(prev => prev.filter(tab => tab.id !== tabId)); setAllSplitScreenTab(prev => prev.filter(id => id !== tabId)); if (currentTab === tabId) { const remainingTabs = tabs.filter(tab => tab.id !== tabId); setCurrentTab(remainingTabs.length > 0 ? remainingTabs[0].id : 1); } }; const setSplitScreenTab = (tabId: number) => { setAllSplitScreenTab(prev => { if (prev.includes(tabId)) { return prev.filter(id => id !== tabId); } else if (prev.length < 3) { return [...prev, tabId]; } return prev; }); }; const getTab = (tabId: number) => { return tabs.find(tab => tab.id === tabId); }; const value: TabContextType = { tabs, currentTab, allSplitScreenTab, addTab, removeTab, setCurrentTab, setSplitScreenTab, getTab, }; return ( {children} ); }