v1.10.0 #471
No files matched your search
@@ -9,7 +9,7 @@ import {
|
||||
} from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
import { TopNavbar } from "@/ui/desktop/navigation/TopNavbar.tsx";
|
||||
import { CommandHistoryProvider } from "@/ui/desktop/apps/features/terminal/command-history/CommandHistoryContext.tsx";
|
||||
import { AdminSettings } from "@/ui/desktop/admin/AdminSettings.tsx";
|
||||
import { AdminSettings } from "@/ui/desktop/apps/admin/AdminSettings.tsx";
|
||||
import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx";
|
||||
import { Toaster } from "@/components/ui/sonner.tsx";
|
||||
import { CommandPalette } from "@/ui/desktop/apps/command-palette/CommandPalette.tsx";
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useSidebar } from "@/components/ui/sidebar";
|
||||
import { useSidebar } from "@/components/ui/sidebar.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import {
|
||||
Tabs,
|
||||
@@ -21,12 +21,12 @@ import {
|
||||
getSessions,
|
||||
unlinkOIDCFromPasswordAccount,
|
||||
} from "@/ui/main-axios.ts";
|
||||
import { RolesTab } from "./widgets/RolesTab.tsx";
|
||||
import { GeneralSettingsTab } from "./widgets/GeneralSettingsTab.tsx";
|
||||
import { OIDCSettingsTab } from "./widgets/OIDCSettingsTab.tsx";
|
||||
import { UserManagementTab } from "./widgets/UserManagementTab.tsx";
|
||||
import { SessionManagementTab } from "./widgets/SessionManagementTab.tsx";
|
||||
import { DatabaseSecurityTab } from "./widgets/DatabaseSecurityTab.tsx";
|
||||
import { RolesTab } from "@/ui/desktop/apps/admin/tabs/RolesTab.tsx";
|
||||
import { GeneralSettingsTab } from "@/ui/desktop/apps/admin/tabs/GeneralSettingsTab.tsx";
|
||||
import { OIDCSettingsTab } from "@/ui/desktop/apps/admin/tabs/OIDCSettingsTab.tsx";
|
||||
import { UserManagementTab } from "@/ui/desktop/apps/admin/tabs/UserManagementTab.tsx";
|
||||
import { SessionManagementTab } from "@/ui/desktop/apps/admin/tabs/SessionManagementTab.tsx";
|
||||
import { DatabaseSecurityTab } from "@/ui/desktop/apps/admin/tabs/DatabaseSecurityTab.tsx";
|
||||
import { CreateUserDialog } from "./dialogs/CreateUserDialog.tsx";
|
||||
import { UserEditDialog } from "./dialogs/UserEditDialog.tsx";
|
||||
import { LinkAccountDialog } from "./dialogs/LinkAccountDialog.tsx";
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -123,8 +123,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
const resizeTimeout = useRef<NodeJS.Timeout | null>(null);
|
||||
const wasDisconnectedBySSH = useRef(false);
|
||||
const pingIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [isFitted, setIsFitted] = useState(true);
|
||||
@@ -342,7 +340,7 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
if (
|
||||
!fitAddonRef.current ||
|
||||
!terminal ||
|
||||
!isVisibleRef.current ||
|
||||
!isVisible ||
|
||||
isFittingRef.current
|
||||
) {
|
||||
return;
|
||||
@@ -1144,6 +1142,15 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
|
||||
terminal.open(xtermRef.current);
|
||||
|
||||
// Immediately fit to establish correct dimensions
|
||||
fitAddonRef.current?.fit();
|
||||
if (terminal.cols < 10 || terminal.rows < 3) {
|
||||
// Terminal opened with invalid dimensions, retry fit in next frame
|
||||
requestAnimationFrame(() => {
|
||||
fitAddonRef.current?.fit();
|
||||
});
|
||||
}
|
||||
|
||||
const element = xtermRef.current;
|
||||
const handleContextMenu = async (e: MouseEvent) => {
|
||||
if (!getUseRightClickCopyPaste()) return;
|
||||
@@ -1225,22 +1232,19 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (resizeTimeout.current) clearTimeout(resizeTimeout.current);
|
||||
resizeTimeout.current = setTimeout(() => {
|
||||
if (!isVisibleRef.current || !isReady) return;
|
||||
if (isVisible && terminal?.cols > 0) {
|
||||
performFit();
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
|
||||
resizeObserver.observe(xtermRef.current);
|
||||
|
||||
setVisible(true);
|
||||
|
||||
return () => {
|
||||
isUnmountingRef.current = true;
|
||||
shouldNotReconnectRef.current = true;
|
||||
isReconnectingRef.current = false;
|
||||
setIsConnecting(false);
|
||||
setVisible(false);
|
||||
setIsReady(false);
|
||||
isFittingRef.current = false;
|
||||
resizeObserver.disconnect();
|
||||
element?.removeEventListener("contextmenu", handleContextMenu);
|
||||
@@ -1444,75 +1448,48 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
terminal.attachCustomKeyEventHandler(handleCustomKey);
|
||||
}, [terminal]);
|
||||
|
||||
// Connection initialization effect
|
||||
useEffect(() => {
|
||||
if (!terminal || !hostConfig || !visible) return;
|
||||
|
||||
if (!terminal || !hostConfig || !isVisible) return;
|
||||
if (isConnected || isConnecting) return;
|
||||
|
||||
setIsConnecting(true);
|
||||
|
||||
// Start connection immediately without waiting for fonts
|
||||
// Ensure terminal has valid dimensions before connecting
|
||||
if (terminal.cols < 10 || terminal.rows < 3) {
|
||||
// Wait for next frame when dimensions will be valid
|
||||
requestAnimationFrame(() => {
|
||||
if (terminal.cols > 0 && terminal.rows > 0) {
|
||||
setIsConnecting(true);
|
||||
fitAddonRef.current?.fit();
|
||||
if (terminal && terminal.cols > 0 && terminal.rows > 0) {
|
||||
scheduleNotify(terminal.cols, terminal.rows);
|
||||
connectToHost(terminal.cols, terminal.rows);
|
||||
}
|
||||
hardRefresh();
|
||||
|
||||
setVisible(true);
|
||||
setIsReady(true);
|
||||
|
||||
if (terminal && !splitScreen) {
|
||||
terminal.focus();
|
||||
}
|
||||
|
||||
const jwtToken = getCookie("jwt");
|
||||
|
||||
if (!jwtToken || jwtToken.trim() === "") {
|
||||
setIsConnected(false);
|
||||
setIsConnecting(false);
|
||||
setConnectionError("Authentication required");
|
||||
return;
|
||||
}
|
||||
|
||||
const cols = terminal.cols;
|
||||
const rows = terminal.rows;
|
||||
|
||||
connectToHost(cols, rows);
|
||||
});
|
||||
}, [terminal, hostConfig, visible, isConnected, isConnecting, splitScreen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVisible || !isReady || !fitAddonRef.current || !terminal) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rafId: number;
|
||||
setIsConnecting(true);
|
||||
fitAddonRef.current?.fit();
|
||||
if (terminal.cols > 0 && terminal.rows > 0) {
|
||||
scheduleNotify(terminal.cols, terminal.rows);
|
||||
connectToHost(terminal.cols, terminal.rows);
|
||||
}
|
||||
}, [terminal, hostConfig, isVisible, isConnected, isConnecting]);
|
||||
|
||||
rafId = requestAnimationFrame(() => {
|
||||
// Consolidated fitting and focus effect
|
||||
useEffect(() => {
|
||||
if (!terminal || !fitAddonRef.current || !isVisible) return;
|
||||
|
||||
const fitTimeoutId = setTimeout(() => {
|
||||
if (!isFittingRef.current && terminal.cols > 0 && terminal.rows > 0) {
|
||||
performFit();
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (rafId) cancelAnimationFrame(rafId);
|
||||
};
|
||||
}, [isVisible, isReady, splitScreen, terminal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isFitted &&
|
||||
isVisible &&
|
||||
isReady &&
|
||||
!isConnecting &&
|
||||
terminal &&
|
||||
!splitScreen
|
||||
) {
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
terminal.focus();
|
||||
});
|
||||
return () => cancelAnimationFrame(rafId);
|
||||
if (!splitScreen && !isConnecting) {
|
||||
requestAnimationFrame(() => terminal.focus());
|
||||
}
|
||||
}, [isFitted, isVisible, isReady, isConnecting, terminal, splitScreen]);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return () => clearTimeout(fitTimeoutId);
|
||||
}, [terminal, isVisible, splitScreen, isConnecting]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full relative" style={{ backgroundColor }}>
|
||||
@@ -1520,8 +1497,7 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
ref={xtermRef}
|
||||
className="h-full w-full"
|
||||
style={{
|
||||
visibility: isReady ? "visible" : "hidden",
|
||||
pointerEvents: isReady ? "auto" : "none",
|
||||
pointerEvents: isVisible ? "auto" : "none",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (terminal && !splitScreen) {
|
||||
|
||||
File renamed without changes.
@@ -38,7 +38,7 @@ import {
|
||||
} from "@/ui/main-axios.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CredentialSelector } from "@/ui/desktop/apps/host-manager/credentials/CredentialSelector.tsx";
|
||||
import { HostSharingTab } from "./HostSharingTab.tsx";
|
||||
import { HostSharingTab } from "./tabs/HostSharingTab.tsx";
|
||||
import CodeMirror from "@uiw/react-codemirror";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { githubLight } from "@uiw/codemirror-theme-github";
|
||||
|
||||
@@ -70,7 +70,7 @@ import type {
|
||||
SSHManagerHostViewerProps,
|
||||
} from "../../../../../types";
|
||||
import { DEFAULT_STATS_CONFIG } from "@/types/stats-widgets.ts";
|
||||
import { FolderEditDialog } from "../components/FolderEditDialog.tsx";
|
||||
import { FolderEditDialog } from "@/ui/desktop/apps/host-manager/dialogs/FolderEditDialog.tsx";
|
||||
import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
|
||||
export function HostManagerViewer({ onEditHost }: SSHManagerHostViewerProps) {
|
||||
|
||||
File renamed without changes.
@@ -101,13 +101,18 @@ export function AppView({
|
||||
const splitIds = allSplitScreenTab as number[];
|
||||
visibleIds.push(currentTab, ...splitIds.filter((i) => i !== currentTab));
|
||||
}
|
||||
terminalTabs.forEach((t: TabData) => {
|
||||
if (visibleIds.includes(t.id)) {
|
||||
const ref = t.terminalRef?.current;
|
||||
if (ref?.fit) ref.fit();
|
||||
if (ref?.notifyResize) ref.notifyResize();
|
||||
if (ref?.refresh) ref.refresh();
|
||||
}
|
||||
|
||||
const operations = terminalTabs
|
||||
.filter((t: TabData) => visibleIds.includes(t.id))
|
||||
.map((t: TabData) => t.terminalRef?.current)
|
||||
.filter((ref) => ref?.fit);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
operations.forEach((ref) => {
|
||||
ref.fit?.();
|
||||
ref.notifyResize?.();
|
||||
ref.refresh?.();
|
||||
});
|
||||
});
|
||||
}, [allSplitScreenTab, currentTab, terminalTabs]);
|
||||
|
||||
@@ -117,19 +122,15 @@ export function AppView({
|
||||
cancelAnimationFrame(layoutScheduleRef.current);
|
||||
layoutScheduleRef.current = requestAnimationFrame(() => {
|
||||
updatePanelRects();
|
||||
layoutScheduleRef.current = requestAnimationFrame(() => {
|
||||
fitActiveAndNotify();
|
||||
});
|
||||
});
|
||||
}, [updatePanelRects, fitActiveAndNotify]);
|
||||
|
||||
const hideThenFit = React.useCallback(() => {
|
||||
requestAnimationFrame(() => {
|
||||
updatePanelRects();
|
||||
requestAnimationFrame(() => {
|
||||
fitActiveAndNotify();
|
||||
});
|
||||
});
|
||||
}, [updatePanelRects, fitActiveAndNotify]);
|
||||
|
||||
const prevStateRef = useRef({
|
||||
@@ -237,7 +238,6 @@ export function AppView({
|
||||
display: "block" as const,
|
||||
pointerEvents: "auto" as const,
|
||||
opacity: 1,
|
||||
transition: "opacity 150ms ease-in-out",
|
||||
};
|
||||
styles[mainTab.id] = newStyle;
|
||||
previousStylesRef.current[mainTab.id] = newStyle;
|
||||
@@ -256,7 +256,6 @@ export function AppView({
|
||||
display: "block" as const,
|
||||
pointerEvents: "auto" as const,
|
||||
opacity: 1,
|
||||
transition: "opacity 150ms ease-in-out",
|
||||
};
|
||||
styles[t.id] = newStyle;
|
||||
previousStylesRef.current[t.id] = newStyle;
|
||||
@@ -298,7 +297,6 @@ export function AppView({
|
||||
pointerEvents: "auto",
|
||||
zIndex: 20,
|
||||
display: "block",
|
||||
transition: "opacity 150ms ease-in-out",
|
||||
overflow: "hidden",
|
||||
}
|
||||
: ({
|
||||
@@ -306,7 +304,7 @@ export function AppView({
|
||||
opacity: 0,
|
||||
pointerEvents: "none",
|
||||
zIndex: 0,
|
||||
transition: "opacity 150ms ease-in-out",
|
||||
display: "none",
|
||||
overflow: "hidden",
|
||||
} as React.CSSProperties);
|
||||
|
||||
|
||||
Reference in new issue
Block a user