diff --git a/README.md b/README.md index 2d6d13a3..64243c8f 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ Termix is an open-source, forever-free, self-hosted all-in-one server management - **SSH Tunnel Management** - Create and manage SSH tunnels with automatic reconnection and health monitoring - **Remote Config Editor** - Edit files directly on remote servers with syntax highlighting and file management - **SSH Host Manager** - Save, organize, and manage your SSH connections with tags and folders -- **User Authentication** - Secure user management with admin controls +- **User Authentication** - Secure user management with admin controls and OIDC support with more auth types planned - **Modern UI** - Clean interface built with React, Tailwind CSS, and the amazing Shadcn # Planned Features - **Improved Admin Control** - Ability to manage admins, and give more fine-grained control over their permissions, share hosts, reset passwords, delete accounts, etc -- **More auth types** - Add 2FA, OCID support, etc +- **More auth types** - Add 2FA, TOTP, etc - **Theming** - Modify themeing for all tools - **Improved SFTP Support** - Ability to manage files easier with the config editor by uploading, creating, and removing files - **Improved Terminal Support** - Add more terminal protocols such as VNC and RDP (anyone who has experience in integrating RDP into a web-application similar to Apache Guacamole, please contact me by creating an issue) diff --git a/src/apps/SSH/Terminal/SSH.tsx b/src/apps/SSH/Terminal/SSH.tsx index 46f42961..002be4ca 100644 --- a/src/apps/SSH/Terminal/SSH.tsx +++ b/src/apps/SSH/Terminal/SSH.tsx @@ -22,6 +22,11 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement { const [allSplitScreenTab, setAllSplitScreenTab] = useState([]); const nextTabId = useRef(1); + const [isSidebarOpen, setIsSidebarOpen] = useState(true); + const [isTopbarOpen, setIsTopbarOpen] = useState(true); + const SIDEBAR_WIDTH = 256; + const HANDLE_THICKNESS = 6; + const [panelRects, setPanelRects] = useState>({}); const panelRefs = useRef>({}); const panelGroupRefs = useRef<{ [key: string]: any }>({}); @@ -587,20 +592,25 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement { }; return ( -
-
+
+ {/* Sidebar (collapsible) */} +
{ @@ -610,8 +620,12 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement { } }); }} + onCloseSidebar={() => setIsSidebarOpen(false)} + open={isSidebarOpen} + onOpenChange={setIsSidebarOpen} />
+
-
+
setIsTopbarOpen(false)} />
-
+ {!isTopbarOpen && ( +
setIsTopbarOpen(true)} + style={{ + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: HANDLE_THICKNESS, + background: '#222224', + cursor: 'pointer', + zIndex: 12, + }} + title="Show top bar" + /> + )} + + {/* Main terminal area (height adapts to topbar) */} +
{allTabs.length === 0 && (
+ + {/* Sidebar reopen handle */} + {!isSidebarOpen && ( +
setIsSidebarOpen(true)} + style={{ + position: 'absolute', + top: 0, + left: 0, + width: HANDLE_THICKNESS, + height: '100%', + background: '#222224', + cursor: 'pointer', + zIndex: 20, + }} + title="Show sidebar" + /> + )}
); } \ No newline at end of file diff --git a/src/apps/SSH/Terminal/SSHSidebar.tsx b/src/apps/SSH/Terminal/SSHSidebar.tsx index f890d4fc..e92dbae5 100644 --- a/src/apps/SSH/Terminal/SSHSidebar.tsx +++ b/src/apps/SSH/Terminal/SSHSidebar.tsx @@ -2,7 +2,7 @@ import React, {useState} from 'react'; import { CornerDownLeft, - Hammer, Pin + Hammer, Pin, Menu } from "lucide-react" import { @@ -63,14 +63,26 @@ interface SSHHost { updatedAt: string; } -interface SidebarProps { +export interface SidebarProps { onSelectView: (view: string) => void; onHostConnect: (hostConfig: any) => void; allTabs: { id: number; title: string; terminalRef: React.RefObject }[]; runCommandOnTabs: (tabIds: number[], command: string) => void; + onCloseSidebar?: () => void; + onAddHostSubmit?: (data: any) => void; + open?: boolean; + onOpenChange?: (open: boolean) => void; } -export function SSHSidebar({onSelectView, onHostConnect, allTabs, runCommandOnTabs}: SidebarProps): React.ReactElement { +export function SSHSidebar({ + onSelectView, + onHostConnect, + allTabs, + runCommandOnTabs, + onCloseSidebar, + open, + onOpenChange + }: SidebarProps): React.ReactElement { const [hosts, setHosts] = useState([]); const [hostsLoading, setHostsLoading] = useState(false); const [hostsError, setHostsError] = useState(null); @@ -199,12 +211,32 @@ export function SSHSidebar({onSelectView, onHostConnect, allTabs, runCommandOnTa } return ( - + - - Termix / Terminal + + Termix / Terminal + diff --git a/src/apps/SSH/Terminal/SSHTerminal.tsx b/src/apps/SSH/Terminal/SSHTerminal.tsx index e26a1236..74089981 100644 --- a/src/apps/SSH/Terminal/SSHTerminal.tsx +++ b/src/apps/SSH/Terminal/SSHTerminal.tsx @@ -173,7 +173,7 @@ export const SSHTerminal = forwardRef(function SSHTermina fitAddon.fit(); setVisible(true); - const cols = terminal.cols + 1; + const cols = terminal.cols; const rows = terminal.rows; const wsUrl = window.location.hostname === 'localhost' ? 'ws://localhost:8082' diff --git a/src/apps/SSH/Terminal/SSHTopbar.tsx b/src/apps/SSH/Terminal/SSHTopbar.tsx index 0bfa8ace..991c340c 100644 --- a/src/apps/SSH/Terminal/SSHTopbar.tsx +++ b/src/apps/SSH/Terminal/SSHTopbar.tsx @@ -1,5 +1,6 @@ import {SSHTabList} from "@/apps/SSH/Terminal/SSHTabList.tsx"; import React from "react"; +import {ChevronUp} from "lucide-react"; interface TerminalTab { id: number; @@ -13,6 +14,7 @@ interface SSHTopbarProps { allSplitScreenTab: number[]; setSplitScreenTab: (tab: number) => void; setCloseTab: (tab: number) => void; + onHideTopbar?: () => void; } export function SSHTopbar({ @@ -21,7 +23,8 @@ export function SSHTopbar({ setActiveTab, allSplitScreenTab, setSplitScreenTab, - setCloseTab + setCloseTab, + onHideTopbar }: SSHTopbarProps): React.ReactElement { return (
- +
+
+ +
+
+
+ +
) } \ No newline at end of file