Prep for release, added ssh sidebar/topbar shrinking.

This commit is contained in:
LukeGus
2025-08-09 02:06:46 -05:00
parent 30b07ba8f2
commit 7dc1de913f
5 changed files with 167 additions and 32 deletions

View File

@@ -22,6 +22,11 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
const [allSplitScreenTab, setAllSplitScreenTab] = useState<number[]>([]);
const nextTabId = useRef(1);
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(true);
const SIDEBAR_WIDTH = 256;
const HANDLE_THICKNESS = 6;
const [panelRects, setPanelRects] = useState<Record<string, DOMRect | null>>({});
const panelRefs = useRef<Record<string, HTMLDivElement | null>>({});
const panelGroupRefs = useRef<{ [key: string]: any }>({});
@@ -587,20 +592,25 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
};
return (
<div style={{display: 'flex', width: '100vw', height: '100vh', overflow: 'hidden'}}>
<div style={{
width: 256,
flexShrink: 0,
height: '100vh',
position: 'relative',
zIndex: 2,
margin: 0,
padding: 0,
border: 'none'
}}>
<div style={{display: 'flex', width: '100vw', height: '100vh', overflow: 'hidden', position: 'relative'}}>
{/* Sidebar (collapsible) */}
<div
style={{
width: isSidebarOpen ? SIDEBAR_WIDTH : 0,
flexShrink: 0,
height: '100vh',
position: 'relative',
zIndex: 2,
margin: 0,
padding: 0,
border: 'none',
overflow: 'hidden',
transition: 'width 240ms ease-in-out',
willChange: 'width',
}}
>
<SSHSidebar
onSelectView={onSelectView}
onAddHostSubmit={onAddHostSubmit}
onHostConnect={onHostConnect}
allTabs={allTabs}
runCommandOnTabs={(tabIds: number[], command: string) => {
@@ -610,8 +620,12 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
}
});
}}
onCloseSidebar={() => setIsSidebarOpen(false)}
open={isSidebarOpen}
onOpenChange={setIsSidebarOpen}
/>
</div>
<div
className="terminal-container"
style={{
@@ -621,10 +635,26 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
overflow: 'hidden',
margin: 0,
padding: 0,
paddingLeft: isSidebarOpen ? 0 : HANDLE_THICKNESS,
paddingTop: isTopbarOpen ? 0 : HANDLE_THICKNESS,
border: 'none',
transition: 'padding-left 240ms ease-in-out, padding-top 240ms ease-in-out',
willChange: 'padding',
}}
>
<div style={{position: 'absolute', top: 0, left: 0, width: '100%', zIndex: 10}}>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: isTopbarOpen ? 46 : 0,
overflow: 'hidden',
zIndex: 10,
transition: 'height 240ms ease-in-out',
willChange: 'height',
}}
>
<SSHTopbar
allTabs={allTabs}
currentTab={currentTab ?? -1}
@@ -632,9 +662,35 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
allSplitScreenTab={allSplitScreenTab}
setSplitScreenTab={setSplitScreenTab}
setCloseTab={setCloseTab}
onHideTopbar={() => setIsTopbarOpen(false)}
/>
</div>
<div style={{height: 'calc(100% - 46px)', marginTop: 46, position: 'relative'}}>
{!isTopbarOpen && (
<div
onClick={() => 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) */}
<div
style={{
height: isTopbarOpen ? 'calc(100% - 46px)' : '100%',
marginTop: isTopbarOpen ? 46 : 0,
position: 'relative',
transition: 'margin-top 240ms ease-in-out, height 240ms ease-in-out',
}}
>
{allTabs.length === 0 && (
<div style={{
position: 'absolute',
@@ -699,6 +755,24 @@ export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
{renderSplitOverlays()}
</div>
</div>
{/* Sidebar reopen handle */}
{!isSidebarOpen && (
<div
onClick={() => setIsSidebarOpen(true)}
style={{
position: 'absolute',
top: 0,
left: 0,
width: HANDLE_THICKNESS,
height: '100%',
background: '#222224',
cursor: 'pointer',
zIndex: 20,
}}
title="Show sidebar"
/>
)}
</div>
);
}

View File

@@ -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<any> }[];
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<SSHHost[]>([]);
const [hostsLoading, setHostsLoading] = useState(false);
const [hostsError, setHostsError] = useState<string | null>(null);
@@ -199,12 +211,32 @@ export function SSHSidebar({onSelectView, onHostConnect, allTabs, runCommandOnTa
}
return (
<SidebarProvider>
<SidebarProvider open={open} onOpenChange={onOpenChange}>
<Sidebar className="h-full flex flex-col overflow-hidden">
<SidebarContent className="flex flex-col flex-grow h-full overflow-hidden">
<SidebarGroup className="flex flex-col flex-grow h-full overflow-hidden">
<SidebarGroupLabel className="text-lg font-bold text-white flex items-center gap-2">
Termix / Terminal
<SidebarGroupLabel
className="text-lg font-bold text-white flex items-center justify-between gap-2 w-full">
<span>Termix / Terminal</span>
<button
type="button"
onClick={() => onCloseSidebar?.()}
title="Hide sidebar"
style={{
height: 28,
width: 28,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
background: 'hsl(240 5% 9%)',
color: 'hsl(240 5% 64.9%)',
border: '1px solid hsl(240 3.7% 15.9%)',
borderRadius: 6,
cursor: 'pointer',
}}
>
<Menu className="h-4 w-4"/>
</button>
</SidebarGroupLabel>
<Separator className="p-0.25 mt-1 mb-1"/>
<SidebarGroupContent className="flex flex-col flex-grow h-full overflow-hidden">

View File

@@ -173,7 +173,7 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(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'

View File

@@ -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 (
<div className="flex h-11.5 z-100" style={{
@@ -30,15 +33,41 @@ export function SSHTopbar({
width: '100%',
backgroundColor: '#18181b',
borderBottom: '1px solid #222224',
display: 'flex',
alignItems: 'center',
}}>
<SSHTabList
allTabs={allTabs}
currentTab={currentTab}
setActiveTab={setActiveTab}
allSplitScreenTab={allSplitScreenTab}
setSplitScreenTab={setSplitScreenTab}
setCloseTab={setCloseTab}
/>
<div style={{flex: 1, minWidth: 0, height: '100%', overflowX: 'auto'}}>
<div style={{minWidth: 'max-content', height: '100%', paddingLeft: 8}}>
<SSHTabList
allTabs={allTabs}
currentTab={currentTab}
setActiveTab={setActiveTab}
allSplitScreenTab={allSplitScreenTab}
setSplitScreenTab={setSplitScreenTab}
setCloseTab={setCloseTab}
/>
</div>
</div>
<div style={{flex: '0 0 auto', paddingRight: 8, paddingLeft: 16}}>
<button
onClick={() => onHideTopbar?.()}
style={{
height: 28,
width: 28,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
background: 'hsl(240 5% 9%)',
color: 'hsl(240 5% 64.9%)',
border: '1px solid hsl(240 3.7% 15.9%)',
borderRadius: 6,
cursor: 'pointer',
}}
title="Hide top bar"
>
<ChevronUp size={16} strokeWidth={2}/>
</button>
</div>
</div>
)
}