Revert "Rename terminals, add welcome card for the survey, add buttons to make it clear how to open the sidebar/topbar after closing."
This reverts commit b27916852f.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
import {Homepage} from "@/apps/Homepage/Homepage.tsx"
|
import {Homepage} from "@/apps/Homepage/Homepage.tsx"
|
||||||
import {Terminal} from "@/apps/SSH/Terminal/Terminal.tsx"
|
import {SSH} from "@/apps/SSH/Terminal/SSH.tsx"
|
||||||
import {SSHTunnel} from "@/apps/SSH/Tunnel/SSHTunnel.tsx";
|
import {SSHTunnel} from "@/apps/SSH/Tunnel/SSHTunnel.tsx";
|
||||||
import {ConfigEditor} from "@/apps/SSH/Config Editor/ConfigEditor.tsx";
|
import {ConfigEditor} from "@/apps/SSH/Config Editor/ConfigEditor.tsx";
|
||||||
import {SSHManager} from "@/apps/SSH/Manager/SSHManager.tsx"
|
import {SSHManager} from "@/apps/SSH/Manager/SSHManager.tsx"
|
||||||
@@ -35,7 +35,7 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
{mountedViews.has("terminal") && (
|
{mountedViews.has("terminal") && (
|
||||||
<div style={{display: view === "terminal" ? "block" : "none"}}>
|
<div style={{display: view === "terminal" ? "block" : "none"}}>
|
||||||
<Terminal onSelectView={handleSelectView} />
|
<SSH onSelectView={handleSelectView} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{mountedViews.has("tunnel") && (
|
{mountedViews.has("tunnel") && (
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, {useEffect, useState} from "react";
|
|||||||
import {HomepageAuth} from "@/apps/Homepage/HomepageAuth.tsx";
|
import {HomepageAuth} from "@/apps/Homepage/HomepageAuth.tsx";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {HomepageUpdateLog} from "@/apps/Homepage/HompageUpdateLog.tsx";
|
import {HomepageUpdateLog} from "@/apps/Homepage/HompageUpdateLog.tsx";
|
||||||
import {HomepageWelcomeCard} from "@/apps/Homepage/HomepageWelcomeCard.tsx";
|
|
||||||
|
|
||||||
interface HomepageProps {
|
interface HomepageProps {
|
||||||
onSelectView: (view: string) => void;
|
onSelectView: (view: string) => void;
|
||||||
@@ -33,12 +32,9 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
|
|||||||
const [username, setUsername] = useState<string | null>(null);
|
const [username, setUsername] = useState<string | null>(null);
|
||||||
const [authLoading, setAuthLoading] = useState(true);
|
const [authLoading, setAuthLoading] = useState(true);
|
||||||
const [dbError, setDbError] = useState<string | null>(null);
|
const [dbError, setDbError] = useState<string | null>(null);
|
||||||
const [showWelcomeCard, setShowWelcomeCard] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const jwt = getCookie("jwt");
|
const jwt = getCookie("jwt");
|
||||||
const welcomeHidden = getCookie("welcome_hidden");
|
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
setAuthLoading(true);
|
setAuthLoading(true);
|
||||||
Promise.all([
|
Promise.all([
|
||||||
@@ -50,7 +46,6 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
|
|||||||
setIsAdmin(!!meRes.data.is_admin);
|
setIsAdmin(!!meRes.data.is_admin);
|
||||||
setUsername(meRes.data.username || null);
|
setUsername(meRes.data.username || null);
|
||||||
setDbError(null);
|
setDbError(null);
|
||||||
setShowWelcomeCard(welcomeHidden !== "true");
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setLoggedIn(false);
|
setLoggedIn(false);
|
||||||
@@ -69,11 +64,6 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleHideWelcomeCard = () => {
|
|
||||||
setShowWelcomeCard(false);
|
|
||||||
setCookie("welcome_hidden", "true", 365 * 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomepageSidebar
|
<HomepageSidebar
|
||||||
onSelectView={onSelectView}
|
onSelectView={onSelectView}
|
||||||
@@ -96,13 +86,6 @@ export function Homepage({onSelectView}: HomepageProps): React.ReactElement {
|
|||||||
loggedIn={loggedIn}
|
loggedIn={loggedIn}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loggedIn && !authLoading && showWelcomeCard && (
|
|
||||||
<div
|
|
||||||
className="absolute inset-0 flex items-center justify-center bg-background/80 backdrop-blur-sm z-10">
|
|
||||||
<HomepageWelcomeCard onHidePermanently={handleHideWelcomeCard}/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</HomepageSidebar>
|
</HomepageSidebar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {Card, CardContent, CardFooter, CardHeader, CardTitle} from "@/components/ui/card";
|
|
||||||
import {Button} from "@/components/ui/button";
|
|
||||||
|
|
||||||
interface HomepageWelcomeCardProps {
|
|
||||||
onHidePermanently: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HomepageWelcomeCard({onHidePermanently}: HomepageWelcomeCardProps): React.ReactElement {
|
|
||||||
return (
|
|
||||||
<Card className="w-full max-w-2xl mx-auto">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-2xl font-bold text-center">
|
|
||||||
The Future of Termix
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-muted-foreground text-center leading-relaxed">
|
|
||||||
Please checkout the linked survey{" "}
|
|
||||||
<a
|
|
||||||
href="https://docs.google.com/forms/d/e/1FAIpQLSeGvnQODFtnpjmJsMKgASbaQ87CLQEBCcnzK_Vuw5TdfbfIyA/viewform?usp=sharing&ouid=107601685503825301492"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary hover:underline hover:text-primary/80 transition-colors"
|
|
||||||
>
|
|
||||||
here
|
|
||||||
</a>
|
|
||||||
. The purpose of this survey is to gather feedback from users on what the future UI of Termix could
|
|
||||||
look like to optimize server management. Please take a minute or two to read the survey questions
|
|
||||||
and answer them to the best of your ability. Thank you!
|
|
||||||
</p>
|
|
||||||
<p className="text-muted-foreground text-center leading-relaxed mt-6">
|
|
||||||
A special thanks to those in Asia who recently joined Termix through various forum posts, keep
|
|
||||||
sharing it! A Chinese translation is planned for Termix, but since I don’t speak Chinese, I’ll need
|
|
||||||
to hire someone to help with the translation. If you’d like to support me financially, you can do
|
|
||||||
so{" "}
|
|
||||||
<a
|
|
||||||
href="https://github.com/sponsors/LukeGus"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary hover:underline hover:text-primary/80 transition-colors"
|
|
||||||
>
|
|
||||||
here.
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="justify-center">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={onHidePermanently}
|
|
||||||
className="w-full max-w-xs"
|
|
||||||
>
|
|
||||||
Hide Permanently
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, {useState, useRef, useEffect} from "react";
|
import React, {useState, useRef, useEffect} from "react";
|
||||||
import {TerminalSidebar} from "@/apps/SSH/Terminal/TerminalSidebar.tsx";
|
import {SSHSidebar} from "@/apps/SSH/Terminal/SSHSidebar.tsx";
|
||||||
import {TerminalComponent} from "./TerminalComponent.tsx";
|
import {SSHTerminal} from "./SSHTerminal.tsx";
|
||||||
import {TerminalTopbar} from "@/apps/SSH/Terminal/TerminalTopbar.tsx";
|
import {SSHTopbar} from "@/apps/SSH/Terminal/SSHTopbar.tsx";
|
||||||
import {ResizablePanelGroup, ResizablePanel, ResizableHandle} from '@/components/ui/resizable.tsx';
|
import {ResizablePanelGroup, ResizablePanel, ResizableHandle} from '@/components/ui/resizable.tsx';
|
||||||
import * as ResizablePrimitive from "react-resizable-panels";
|
import * as ResizablePrimitive from "react-resizable-panels";
|
||||||
import {ChevronDown, ChevronRight} from "lucide-react";
|
|
||||||
|
|
||||||
interface ConfigEditorProps {
|
interface ConfigEditorProps {
|
||||||
onSelectView: (view: string) => void;
|
onSelectView: (view: string) => void;
|
||||||
@@ -17,7 +16,7 @@ type Tab = {
|
|||||||
terminalRef: React.RefObject<any>;
|
terminalRef: React.RefObject<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement {
|
export function SSH({onSelectView}: ConfigEditorProps): React.ReactElement {
|
||||||
const [allTabs, setAllTabs] = useState<Tab[]>([]);
|
const [allTabs, setAllTabs] = useState<Tab[]>([]);
|
||||||
const [currentTab, setCurrentTab] = useState<number | null>(null);
|
const [currentTab, setCurrentTab] = useState<number | null>(null);
|
||||||
const [allSplitScreenTab, setAllSplitScreenTab] = useState<number[]>([]);
|
const [allSplitScreenTab, setAllSplitScreenTab] = useState<number[]>([]);
|
||||||
@@ -26,7 +25,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
|
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
|
||||||
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(true);
|
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(true);
|
||||||
const SIDEBAR_WIDTH = 256;
|
const SIDEBAR_WIDTH = 256;
|
||||||
const HANDLE_THICKNESS = 10;
|
const HANDLE_THICKNESS = 6;
|
||||||
|
|
||||||
const [panelRects, setPanelRects] = useState<Record<string, DOMRect | null>>({});
|
const [panelRects, setPanelRects] = useState<Record<string, DOMRect | null>>({});
|
||||||
const panelRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
const panelRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
@@ -161,7 +160,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
const isVisible = !!layoutStyles[tab.id];
|
const isVisible = !!layoutStyles[tab.id];
|
||||||
return (
|
return (
|
||||||
<div key={tab.id} style={style} data-terminal-id={tab.id}>
|
<div key={tab.id} style={style} data-terminal-id={tab.id}>
|
||||||
<TerminalComponent
|
<SSHTerminal
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
ref={tab.terminalRef}
|
ref={tab.terminalRef}
|
||||||
hostConfig={tab.hostConfig}
|
hostConfig={tab.hostConfig}
|
||||||
@@ -594,6 +593,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{display: 'flex', width: '100vw', height: '100vh', overflow: 'hidden', position: 'relative'}}>
|
<div style={{display: 'flex', width: '100vw', height: '100vh', overflow: 'hidden', position: 'relative'}}>
|
||||||
|
{/* Sidebar (collapsible) */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: isSidebarOpen ? SIDEBAR_WIDTH : 0,
|
width: isSidebarOpen ? SIDEBAR_WIDTH : 0,
|
||||||
@@ -609,7 +609,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
willChange: 'width',
|
willChange: 'width',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TerminalSidebar
|
<SSHSidebar
|
||||||
onSelectView={onSelectView}
|
onSelectView={onSelectView}
|
||||||
onHostConnect={onHostConnect}
|
onHostConnect={onHostConnect}
|
||||||
allTabs={allTabs}
|
allTabs={allTabs}
|
||||||
@@ -655,7 +655,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
willChange: 'height',
|
willChange: 'height',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TerminalTopbar
|
<SSHTopbar
|
||||||
allTabs={allTabs}
|
allTabs={allTabs}
|
||||||
currentTab={currentTab ?? -1}
|
currentTab={currentTab ?? -1}
|
||||||
setActiveTab={setActiveTab}
|
setActiveTab={setActiveTab}
|
||||||
@@ -677,15 +677,12 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
background: '#222224',
|
background: '#222224',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
zIndex: 12,
|
zIndex: 12,
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
}}
|
||||||
title="Show top bar">
|
title="Show top bar"
|
||||||
<ChevronDown size={HANDLE_THICKNESS} />
|
/>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Main terminal area (height adapts to topbar) */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: isTopbarOpen ? 'calc(100% - 46px)' : '100%',
|
height: isTopbarOpen ? 'calc(100% - 46px)' : '100%',
|
||||||
@@ -759,6 +756,7 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar reopen handle */}
|
||||||
{!isSidebarOpen && (
|
{!isSidebarOpen && (
|
||||||
<div
|
<div
|
||||||
onClick={() => setIsSidebarOpen(true)}
|
onClick={() => setIsSidebarOpen(true)}
|
||||||
@@ -771,13 +769,9 @@ export function Terminal({onSelectView}: ConfigEditorProps): React.ReactElement
|
|||||||
background: '#222224',
|
background: '#222224',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
zIndex: 20,
|
zIndex: 20,
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
}}
|
||||||
title="Show sidebar">
|
title="Show sidebar"
|
||||||
<ChevronRight size={HANDLE_THICKNESS} />
|
/>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -74,7 +74,7 @@ export interface SidebarProps {
|
|||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TerminalSidebar({
|
export function SSHSidebar({
|
||||||
onSelectView,
|
onSelectView,
|
||||||
onHostConnect,
|
onHostConnect,
|
||||||
allTabs,
|
allTabs,
|
||||||
@@ -16,7 +16,7 @@ interface SSHTabListProps {
|
|||||||
setCloseTab: (tab: number) => void;
|
setCloseTab: (tab: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TerminalTabList({
|
export function SSHTabList({
|
||||||
allTabs,
|
allTabs,
|
||||||
currentTab,
|
currentTab,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
@@ -13,7 +13,7 @@ interface SSHTerminalProps {
|
|||||||
splitScreen?: boolean;
|
splitScreen?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TerminalComponent = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
||||||
{hostConfig, isVisible, splitScreen = false},
|
{hostConfig, isVisible, splitScreen = false},
|
||||||
ref
|
ref
|
||||||
) {
|
) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {TerminalTabList} from "@/apps/SSH/Terminal/TerminalTabList.tsx";
|
import {SSHTabList} from "@/apps/SSH/Terminal/SSHTabList.tsx";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {ChevronUp} from "lucide-react";
|
import {ChevronUp} from "lucide-react";
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ interface SSHTopbarProps {
|
|||||||
onHideTopbar?: () => void;
|
onHideTopbar?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TerminalTopbar({
|
export function SSHTopbar({
|
||||||
allTabs,
|
allTabs,
|
||||||
currentTab,
|
currentTab,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
@@ -38,7 +38,7 @@ export function TerminalTopbar({
|
|||||||
}}>
|
}}>
|
||||||
<div style={{flex: 1, minWidth: 0, height: '100%', overflowX: 'auto'}}>
|
<div style={{flex: 1, minWidth: 0, height: '100%', overflowX: 'auto'}}>
|
||||||
<div style={{minWidth: 'max-content', height: '100%', paddingLeft: 8, overflowY: 'hidden'}}>
|
<div style={{minWidth: 'max-content', height: '100%', paddingLeft: 8, overflowY: 'hidden'}}>
|
||||||
<TerminalTabList
|
<SSHTabList
|
||||||
allTabs={allTabs}
|
allTabs={allTabs}
|
||||||
currentTab={currentTab}
|
currentTab={currentTab}
|
||||||
setActiveTab={setActiveTab}
|
setActiveTab={setActiveTab}
|
||||||
Reference in New Issue
Block a user