import React, {useState} from "react"; import {useSidebar} from "@/components/ui/sidebar"; import {Button} from "@/components/ui/button.tsx"; import {ChevronDown, ChevronUpIcon, Hammer} from "lucide-react"; import {Tab} from "@/ui/Navigation/Tabs/Tab.tsx"; import {useTabs} from "@/ui/Navigation/Tabs/TabContext.tsx"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion.tsx"; import {Input} from "@/components/ui/input.tsx"; import {Checkbox} from "@/components/ui/checkbox.tsx"; import {Separator} from "@/components/ui/separator.tsx"; interface TopNavbarProps { isTopbarOpen: boolean; setIsTopbarOpen: (open: boolean) => void; } export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): React.ReactElement { const {state} = useSidebar(); const {tabs, currentTab, setCurrentTab, setSplitScreenTab, removeTab, allSplitScreenTab} = useTabs() as any; const leftPosition = state === "collapsed" ? "26px" : "264px"; const [toolsSheetOpen, setToolsSheetOpen] = useState(false); const [isRecording, setIsRecording] = useState(false); const [selectedTabIds, setSelectedTabIds] = useState([]); const handleTabActivate = (tabId: number) => { setCurrentTab(tabId); }; const handleTabSplit = (tabId: number) => { setSplitScreenTab(tabId); }; const handleTabClose = (tabId: number) => { removeTab(tabId); }; const handleTabToggle = (tabId: number) => { setSelectedTabIds(prev => prev.includes(tabId) ? prev.filter(id => id !== tabId) : [...prev, tabId]); }; const handleStartRecording = () => { setIsRecording(true); setTimeout(() => { const input = document.getElementById('ssh-tools-input') as HTMLInputElement; if (input) input.focus(); }, 100); }; const handleStopRecording = () => { setIsRecording(false); setSelectedTabIds([]); }; const handleInputChange = (e: React.ChangeEvent) => { }; const handleKeyDown = (e: React.KeyboardEvent) => { if (selectedTabIds.length === 0) return; const value = e.currentTarget.value; let commandToSend = ''; if (e.ctrlKey || e.metaKey) { if (e.key === 'c') { commandToSend = '\x03'; // Ctrl+C (SIGINT) e.preventDefault(); } else if (e.key === 'd') { commandToSend = '\x04'; // Ctrl+D (EOF) e.preventDefault(); } else if (e.key === 'l') { commandToSend = '\x0c'; // Ctrl+L (clear screen) e.preventDefault(); } else if (e.key === 'u') { commandToSend = '\x15'; // Ctrl+U (clear line) e.preventDefault(); } else if (e.key === 'k') { commandToSend = '\x0b'; // Ctrl+K (clear from cursor to end) e.preventDefault(); } else if (e.key === 'a') { commandToSend = '\x01'; // Ctrl+A (move to beginning of line) e.preventDefault(); } else if (e.key === 'e') { commandToSend = '\x05'; // Ctrl+E (move to end of line) e.preventDefault(); } else if (e.key === 'w') { commandToSend = '\x17'; // Ctrl+W (delete word before cursor) e.preventDefault(); } } else if (e.key === 'Enter') { commandToSend = '\n'; e.preventDefault(); } else if (e.key === 'Backspace') { commandToSend = '\x08'; // Backspace e.preventDefault(); } else if (e.key === 'Delete') { commandToSend = '\x7f'; // Delete e.preventDefault(); } else if (e.key === 'Tab') { commandToSend = '\x09'; // Tab e.preventDefault(); } else if (e.key === 'Escape') { commandToSend = '\x1b'; // Escape e.preventDefault(); } else if (e.key === 'ArrowUp') { commandToSend = '\x1b[A'; // Up arrow e.preventDefault(); } else if (e.key === 'ArrowDown') { commandToSend = '\x1b[B'; // Down arrow e.preventDefault(); } else if (e.key === 'ArrowLeft') { commandToSend = '\x1b[D'; // Left arrow e.preventDefault(); } else if (e.key === 'ArrowRight') { commandToSend = '\x1b[C'; // Right arrow e.preventDefault(); } else if (e.key === 'Home') { commandToSend = '\x1b[H'; // Home e.preventDefault(); } else if (e.key === 'End') { commandToSend = '\x1b[F'; // End e.preventDefault(); } else if (e.key === 'PageUp') { commandToSend = '\x1b[5~'; // Page Up e.preventDefault(); } else if (e.key === 'PageDown') { commandToSend = '\x1b[6~'; // Page Down e.preventDefault(); } else if (e.key === 'Insert') { commandToSend = '\x1b[2~'; // Insert e.preventDefault(); } else if (e.key === 'F1') { commandToSend = '\x1bOP'; // F1 e.preventDefault(); } else if (e.key === 'F2') { commandToSend = '\x1bOQ'; // F2 e.preventDefault(); } else if (e.key === 'F3') { commandToSend = '\x1bOR'; // F3 e.preventDefault(); } else if (e.key === 'F4') { commandToSend = '\x1bOS'; // F4 e.preventDefault(); } else if (e.key === 'F5') { commandToSend = '\x1b[15~'; // F5 e.preventDefault(); } else if (e.key === 'F6') { commandToSend = '\x1b[17~'; // F6 e.preventDefault(); } else if (e.key === 'F7') { commandToSend = '\x1b[18~'; // F7 e.preventDefault(); } else if (e.key === 'F8') { commandToSend = '\x1b[19~'; // F8 e.preventDefault(); } else if (e.key === 'F9') { commandToSend = '\x1b[20~'; // F9 e.preventDefault(); } else if (e.key === 'F10') { commandToSend = '\x1b[21~'; // F10 e.preventDefault(); } else if (e.key === 'F11') { commandToSend = '\x1b[23~'; // F11 e.preventDefault(); } else if (e.key === 'F12') { commandToSend = '\x1b[24~'; // F12 e.preventDefault(); } if (commandToSend) { selectedTabIds.forEach(tabId => { const tab = tabs.find((t: any) => t.id === tabId); if (tab?.terminalRef?.current?.sendInput) { tab.terminalRef.current.sendInput(commandToSend); } }); } }; const handleKeyPress = (e: React.KeyboardEvent) => { if (selectedTabIds.length === 0) return; if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) { const char = e.key; selectedTabIds.forEach(tabId => { const tab = tabs.find((t: any) => t.id === tabId); if (tab?.terminalRef?.current?.sendInput) { tab.terminalRef.current.sendInput(char); } }); } }; const isSplitScreenActive = Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0; const currentTabObj = tabs.find((t: any) => t.id === currentTab); const currentTabIsHome = currentTabObj?.type === 'home'; const currentTabIsSshManager = currentTabObj?.type === 'ssh_manager'; const currentTabIsAdmin = currentTabObj?.type === 'admin'; const terminalTabs = tabs.filter((tab: any) => tab.type === 'terminal'); function getCookie(name: string) { return document.cookie.split('; ').reduce((r, v) => { const parts = v.split('='); return parts[0] === name ? decodeURIComponent(parts[1]) : r; }, ""); } const updateRightClickCopyPaste = (checked: boolean) => { document.cookie = `rightClickCopyPaste=${checked}; expires=2147483647; path=/`; } return (
{tabs.map((tab: any) => { const isActive = tab.id === currentTab; const isSplit = Array.isArray(allSplitScreenTab) && allSplitScreenTab.includes(tab.id); const isTerminal = tab.type === 'terminal'; const isServer = tab.type === 'server'; const isFileManager = tab.type === 'file_manager'; const isSshManager = tab.type === 'ssh_manager'; const isAdmin = tab.type === 'admin'; const isSplittable = isTerminal || isServer || isFileManager; const isSplitButtonDisabled = (isActive && !isSplitScreenActive) || ((allSplitScreenTab?.length || 0) >= 3 && !isSplit); const disableSplit = !isSplittable || isSplitButtonDisabled || isActive || currentTabIsHome || currentTabIsSshManager || currentTabIsAdmin; const disableActivate = isSplit || ((tab.type === 'home' || tab.type === 'ssh_manager' || tab.type === 'admin') && isSplitScreenActive); const disableClose = (isSplitScreenActive && isActive) || isSplit; return ( handleTabActivate(tab.id)} onClose={isTerminal || isServer || isFileManager || isSshManager || isAdmin ? () => handleTabClose(tab.id) : undefined} onSplit={isSplittable ? () => handleTabSplit(tab.id) : undefined} canSplit={isSplittable} canClose={isTerminal || isServer || isFileManager || isSshManager || isAdmin} disableActivate={disableActivate} disableSplit={disableSplit} disableClose={disableClose} /> ); })}
{!isTopbarOpen && (
setIsTopbarOpen(true)} className="absolute top-0 left-0 w-full h-[10px] bg-[#18181b] cursor-pointer z-20 flex items-center justify-center rounded-bl-md rounded-br-md">
)} {toolsSheetOpen && (
setToolsSheetOpen(false)} style={{cursor: 'pointer'}} />
e.stopPropagation()} >

SSH Tools

Key Recording

{!isRecording ? ( ) : ( )}
{isRecording && ( <>
{terminalTabs.map(tab => ( ))}

Commands will be sent to {selectedTabIds.length} selected terminal(s).

)}

Settings

Have ideas for what should come next for ssh tools? Share them on{" "} GitHub !

)}
) }