mirror of
https://github.com/tale/headplane.git
synced 2026-08-17 16:47:51 +00:00
fix: better ssh resilience and customizable timeout for future backoff
This commit is contained in:
@@ -156,7 +156,7 @@ export default function Page() {
|
||||
<div className="w-screen h-screen bg-headplane-900">
|
||||
{ipn === null ? (
|
||||
<div className="mx-auto h-screen flex items-center justify-center">
|
||||
<Loader2 className="animate-spin size-10" />
|
||||
<Loader2 className="animate-spin size-10 text-headplane-50" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col h-screen">
|
||||
|
||||
@@ -5,9 +5,11 @@ import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { WebglAddon } from '@xterm/addon-webgl';
|
||||
import * as xterm from '@xterm/xterm';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import cn from '~/utils/cn';
|
||||
import { useLiveData } from '~/utils/live-data';
|
||||
import toast from '~/utils/toast';
|
||||
|
||||
interface XTermProps {
|
||||
ipn: TsWasmNet;
|
||||
@@ -15,17 +17,17 @@ interface XTermProps {
|
||||
hostname: string;
|
||||
}
|
||||
|
||||
const RED = new TextEncoder().encode('\x1b[31m');
|
||||
const RESET = new TextEncoder().encode('\x1b[0m');
|
||||
|
||||
export default function XTerm({ ipn, username, hostname }: XTermProps) {
|
||||
const { pause } = useLiveData();
|
||||
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const term = useRef<xterm.Terminal>(null);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const inputRef = useRef<((input: string) => void) | null>(null);
|
||||
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isFailed, setIsFailed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
pause();
|
||||
const terminal = new xterm.Terminal({
|
||||
@@ -76,12 +78,14 @@ export default function XTerm({ ipn, username, hostname }: XTermProps) {
|
||||
OnStderr: (data) => {
|
||||
terminal.write(data);
|
||||
console.log('SSH stderr:', data);
|
||||
toast(data);
|
||||
},
|
||||
OnStdin: (func) => {
|
||||
inputRef.current = func;
|
||||
},
|
||||
OnConnect: () => {
|
||||
console.log('SSH session connected');
|
||||
setIsLoading(false);
|
||||
},
|
||||
|
||||
OnDisconnect: () => {
|
||||
@@ -92,6 +96,9 @@ export default function XTerm({ ipn, username, hostname }: XTermProps) {
|
||||
}
|
||||
|
||||
console.log('SSH session disconnected');
|
||||
terminal.writeln('Disconnected from SSH session');
|
||||
setIsLoading(false);
|
||||
setIsFailed(true);
|
||||
term.current = null;
|
||||
},
|
||||
});
|
||||
@@ -123,8 +130,15 @@ export default function XTerm({ ipn, username, hostname }: XTermProps) {
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full group">
|
||||
<div ref={container} className="w-full h-full" />
|
||||
|
||||
{isLoading ? (
|
||||
<div className="mx-auto h-screen flex items-center justify-center">
|
||||
<Loader2 className="animate-spin size-10 text-headplane-50" />
|
||||
</div>
|
||||
) : undefined}
|
||||
<div
|
||||
ref={container}
|
||||
className={cn('w-full h-full', isLoading ? 'opacity-0' : 'opacity-100')}
|
||||
/>
|
||||
{term.current && isResizing ? (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -135,6 +149,16 @@ export default function XTerm({ ipn, username, hostname }: XTermProps) {
|
||||
{term.current.cols}x{term.current.rows}
|
||||
</div>
|
||||
) : undefined}
|
||||
{isFailed ? (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
|
||||
'px-4 py-2 bg-headplane-800 text-white rounded-full shadow z-50',
|
||||
)}
|
||||
>
|
||||
Failed to connect to SSH session
|
||||
</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user