v1.9.0 #437
File diff suppressed because it is too large
Load Diff
@@ -641,10 +641,14 @@ export function AdminSettings({
|
|||||||
const bottomMarginPx = 8;
|
const bottomMarginPx = 8;
|
||||||
const wrapperStyle: React.CSSProperties = {
|
const wrapperStyle: React.CSSProperties = {
|
||||||
marginLeft: leftMarginPx,
|
marginLeft: leftMarginPx,
|
||||||
marginRight: rightSidebarOpen ? rightSidebarWidth + 17 : 17,
|
marginRight: rightSidebarOpen
|
||||||
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
|
: 17,
|
||||||
marginTop: topMarginPx,
|
marginTop: topMarginPx,
|
||||||
marginBottom: bottomMarginPx,
|
marginBottom: bottomMarginPx,
|
||||||
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
||||||
|
transition:
|
||||||
|
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export function Dashboard({
|
|||||||
|
|
||||||
const topMarginPx = isTopbarOpen ? 74 : 26;
|
const topMarginPx = isTopbarOpen ? 74 : 26;
|
||||||
const leftMarginPx = sidebarState === "collapsed" ? 26 : 8;
|
const leftMarginPx = sidebarState === "collapsed" ? 26 : 8;
|
||||||
const rightMarginPx = rightSidebarOpen ? rightSidebarWidth + 17 : 17;
|
const rightMarginPx = 17; // Base margin when closed
|
||||||
const bottomMarginPx = 8;
|
const bottomMarginPx = 8;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -341,10 +341,14 @@ export function Dashboard({
|
|||||||
className="bg-dark-bg text-white rounded-lg border-2 border-dark-border overflow-hidden flex"
|
className="bg-dark-bg text-white rounded-lg border-2 border-dark-border overflow-hidden flex"
|
||||||
style={{
|
style={{
|
||||||
marginLeft: leftMarginPx,
|
marginLeft: leftMarginPx,
|
||||||
marginRight: rightMarginPx,
|
marginRight: rightSidebarOpen
|
||||||
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
|
: rightMarginPx,
|
||||||
marginTop: topMarginPx,
|
marginTop: topMarginPx,
|
||||||
marginBottom: bottomMarginPx,
|
marginBottom: bottomMarginPx,
|
||||||
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
||||||
|
transition:
|
||||||
|
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col relative z-10 w-full h-full">
|
<div className="flex flex-col relative z-10 w-full h-full">
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import type { FileItem } from "../../../types/index.js";
|
import type { FileItem } from "../../../types/index.js";
|
||||||
import { LoadingOverlay } from "@/ui/components/LoadingOverlay";
|
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
|
||||||
|
|
||||||
interface CreateIntent {
|
interface CreateIntent {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -1004,7 +1004,7 @@ export function FileManagerGrid({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
{dragState.type === "external" && (
|
{dragState.type === "external" && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50 backdrop-blur-sm z-10 pointer-events-none animate-in fade-in-0">
|
<div className="absolute inset-0 flex items-center justify-center bg-background/50 backdrop-blur-sm z-10 pointer-events-none">
|
||||||
<div className="text-center p-8 bg-background/95 border-2 border-dashed border-primary rounded-lg shadow-lg">
|
<div className="text-center p-8 bg-background/95 border-2 border-dashed border-primary rounded-lg shadow-lg">
|
||||||
<Upload className="w-16 h-16 mx-auto mb-4 text-primary" />
|
<Upload className="w-16 h-16 mx-auto mb-4 text-primary" />
|
||||||
<p className="text-xl font-semibold text-foreground mb-2">
|
<p className="text-xl font-semibold text-foreground mb-2">
|
||||||
@@ -1057,7 +1057,6 @@ export function FileManagerGrid({
|
|||||||
draggable={true}
|
draggable={true}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group p-3 rounded-lg cursor-pointer",
|
"group p-3 rounded-lg cursor-pointer",
|
||||||
"transition-all duration-150 ease-out",
|
|
||||||
"hover:bg-accent hover:text-accent-foreground hover:scale-[1.02] border-2 border-transparent",
|
"hover:bg-accent hover:text-accent-foreground hover:scale-[1.02] border-2 border-transparent",
|
||||||
isSelected &&
|
isSelected &&
|
||||||
"bg-primary/20 border-primary ring-2 ring-primary/20",
|
"bg-primary/20 border-primary ring-2 ring-primary/20",
|
||||||
@@ -1148,7 +1147,6 @@ export function FileManagerGrid({
|
|||||||
draggable={true}
|
draggable={true}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-3 p-2 rounded cursor-pointer",
|
"flex items-center gap-3 p-2 rounded cursor-pointer",
|
||||||
"transition-all duration-150 ease-out",
|
|
||||||
"hover:bg-accent hover:text-accent-foreground",
|
"hover:bg-accent hover:text-accent-foreground",
|
||||||
isSelected && "bg-primary/20 ring-2 ring-primary/20",
|
isSelected && "bg-primary/20 ring-2 ring-primary/20",
|
||||||
dragState.target?.path === file.path &&
|
dragState.target?.path === file.path &&
|
||||||
@@ -1322,12 +1320,7 @@ export function FileManagerGrid({
|
|||||||
document.body,
|
document.body,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<LoadingOverlay
|
<SimpleLoader visible={isLoading} message={t("common.loading")} />
|
||||||
visible={isLoading}
|
|
||||||
minDuration={600}
|
|
||||||
message={t("common.loading")}
|
|
||||||
showLogo={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,10 +92,14 @@ export function HostManager({
|
|||||||
className="bg-dark-bg text-white p-4 pt-0 rounded-lg border-2 border-dark-border flex flex-col min-h-0 overflow-hidden"
|
className="bg-dark-bg text-white p-4 pt-0 rounded-lg border-2 border-dark-border flex flex-col min-h-0 overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
marginLeft: leftMarginPx,
|
marginLeft: leftMarginPx,
|
||||||
marginRight: rightSidebarOpen ? rightSidebarWidth + 17 : 17,
|
marginRight: rightSidebarOpen
|
||||||
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
|
: 17,
|
||||||
marginTop: topMarginPx,
|
marginTop: topMarginPx,
|
||||||
marginBottom: bottomMarginPx,
|
marginBottom: bottomMarginPx,
|
||||||
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
||||||
|
transition:
|
||||||
|
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
type StatsConfig,
|
type StatsConfig,
|
||||||
DEFAULT_STATS_CONFIG,
|
DEFAULT_STATS_CONFIG,
|
||||||
} from "@/types/stats-widgets";
|
} from "@/types/stats-widgets";
|
||||||
import { LoadingOverlay } from "@/ui/components/LoadingOverlay";
|
|
||||||
import {
|
import {
|
||||||
CpuWidget,
|
CpuWidget,
|
||||||
MemoryWidget,
|
MemoryWidget,
|
||||||
@@ -28,6 +27,7 @@ import {
|
|||||||
SystemWidget,
|
SystemWidget,
|
||||||
LoginStatsWidget,
|
LoginStatsWidget,
|
||||||
} from "./widgets";
|
} from "./widgets";
|
||||||
|
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
|
||||||
|
|
||||||
interface HostConfig {
|
interface HostConfig {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -469,11 +469,9 @@ export function Server({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<LoadingOverlay
|
<SimpleLoader
|
||||||
visible={isLoadingMetrics && !metrics}
|
visible={isLoadingMetrics && !metrics}
|
||||||
minDuration={700}
|
|
||||||
message={t("serverStats.loadingMetrics")}
|
message={t("serverStats.loadingMetrics")}
|
||||||
showLogo={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { useCommandTracker } from "@/ui/hooks/useCommandTracker";
|
|||||||
import { useCommandHistory } from "@/ui/hooks/useCommandHistory";
|
import { useCommandHistory } from "@/ui/hooks/useCommandHistory";
|
||||||
import { CommandHistoryDialog } from "./CommandHistoryDialog";
|
import { CommandHistoryDialog } from "./CommandHistoryDialog";
|
||||||
import { CommandAutocomplete } from "./CommandAutocomplete";
|
import { CommandAutocomplete } from "./CommandAutocomplete";
|
||||||
import { LoadingOverlay } from "@/ui/components/LoadingOverlay";
|
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
|
||||||
|
|
||||||
interface HostConfig {
|
interface HostConfig {
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -1446,7 +1446,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
style={{
|
style={{
|
||||||
visibility:
|
visibility:
|
||||||
isReady && !isConnecting && isFitted ? "visible" : "hidden",
|
isReady && !isConnecting && isFitted ? "visible" : "hidden",
|
||||||
opacity: isReady && !isConnecting && isFitted ? 1 : 0,
|
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (terminal && !splitScreen) {
|
if (terminal && !splitScreen) {
|
||||||
@@ -1494,12 +1493,10 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
onSelect={handleAutocompleteSelect}
|
onSelect={handleAutocompleteSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LoadingOverlay
|
<SimpleLoader
|
||||||
visible={isConnecting}
|
visible={isConnecting}
|
||||||
minDuration={800}
|
|
||||||
message={t("terminal.connecting")}
|
message={t("terminal.connecting")}
|
||||||
backgroundColor={backgroundColor}
|
backgroundColor={backgroundColor}
|
||||||
showLogo={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,16 +23,7 @@ import {
|
|||||||
SidebarProvider,
|
SidebarProvider,
|
||||||
SidebarGroupLabel,
|
SidebarGroupLabel,
|
||||||
} from "@/components/ui/sidebar.tsx";
|
} from "@/components/ui/sidebar.tsx";
|
||||||
import {
|
import { Plus, Play, Edit, Trash2, Copy, X, RotateCcw } from "lucide-react";
|
||||||
Plus,
|
|
||||||
Play,
|
|
||||||
Edit,
|
|
||||||
Trash2,
|
|
||||||
Copy,
|
|
||||||
X,
|
|
||||||
RotateCcw,
|
|
||||||
ChevronRight,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useConfirmation } from "@/hooks/use-confirmation.ts";
|
import { useConfirmation } from "@/hooks/use-confirmation.ts";
|
||||||
@@ -108,6 +99,14 @@ export function SSHUtilitySidebar({
|
|||||||
|
|
||||||
const terminalTabs = tabs.filter((tab: TabData) => tab.type === "terminal");
|
const terminalTabs = tabs.filter((tab: TabData) => tab.type === "terminal");
|
||||||
|
|
||||||
|
// Initialize CSS variable on mount and when sidebar width changes
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--right-sidebar-width",
|
||||||
|
`${sidebarWidth}px`,
|
||||||
|
);
|
||||||
|
}, [sidebarWidth]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && activeTab === "snippets") {
|
if (isOpen && activeTab === "snippets") {
|
||||||
fetchSnippets();
|
fetchSnippets();
|
||||||
@@ -131,13 +130,22 @@ export function SSHUtilitySidebar({
|
|||||||
const newWidth = Math.round(startWidthRef.current + dx);
|
const newWidth = Math.round(startWidthRef.current + dx);
|
||||||
const minWidth = 300;
|
const minWidth = 300;
|
||||||
const maxWidth = Math.round(window.innerWidth * 0.5);
|
const maxWidth = Math.round(window.innerWidth * 0.5);
|
||||||
if (newWidth >= minWidth && newWidth <= maxWidth) {
|
|
||||||
setSidebarWidth(newWidth);
|
let finalWidth = newWidth;
|
||||||
} else if (newWidth < minWidth) {
|
if (newWidth < minWidth) {
|
||||||
setSidebarWidth(minWidth);
|
finalWidth = minWidth;
|
||||||
} else if (newWidth > maxWidth) {
|
} else if (newWidth > maxWidth) {
|
||||||
setSidebarWidth(maxWidth);
|
finalWidth = maxWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update CSS variable immediately for smooth animation
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--right-sidebar-width",
|
||||||
|
`${finalWidth}px`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update React state (this will be batched/debounced naturally)
|
||||||
|
setSidebarWidth(finalWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
@@ -156,7 +164,7 @@ export function SSHUtilitySidebar({
|
|||||||
document.body.style.cursor = "";
|
document.body.style.cursor = "";
|
||||||
document.body.style.userSelect = "";
|
document.body.style.userSelect = "";
|
||||||
};
|
};
|
||||||
}, [isResizing, sidebarWidth, setSidebarWidth]);
|
}, [isResizing]);
|
||||||
|
|
||||||
// SSH Tools handlers
|
// SSH Tools handlers
|
||||||
const handleTabToggle = (tabId: number) => {
|
const handleTabToggle = (tabId: number) => {
|
||||||
@@ -433,19 +441,22 @@ export function SSHUtilitySidebar({
|
|||||||
toast.success(t("snippets.copySuccess", { name: snippet.name }));
|
toast.success(t("snippets.copySuccess", { name: snippet.name }));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-svh">
|
{isOpen && (
|
||||||
<SidebarProvider
|
<div className="fixed top-0 right-0 h-0 w-0 pointer-events-none">
|
||||||
open={isOpen}
|
<SidebarProvider
|
||||||
style={
|
open={isOpen}
|
||||||
{ "--sidebar-width": `${sidebarWidth}px` } as React.CSSProperties
|
style={
|
||||||
}
|
{ "--sidebar-width": `${sidebarWidth}px` } as React.CSSProperties
|
||||||
>
|
}
|
||||||
<div className="flex h-screen w-full justify-end">
|
className="!min-h-0 !h-0 !w-0"
|
||||||
<Sidebar variant="floating" side="right">
|
>
|
||||||
|
<Sidebar
|
||||||
|
variant="floating"
|
||||||
|
side="right"
|
||||||
|
className="pointer-events-auto"
|
||||||
|
>
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<SidebarGroupLabel className="text-lg font-bold text-white">
|
<SidebarGroupLabel className="text-lg font-bold text-white">
|
||||||
{t("nav.tools")}
|
{t("nav.tools")}
|
||||||
@@ -780,24 +791,9 @@ export function SSHUtilitySidebar({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</div>
|
</SidebarProvider>
|
||||||
</SidebarProvider>
|
</div>
|
||||||
|
)}
|
||||||
{!isOpen && (
|
|
||||||
<div
|
|
||||||
onClick={onClose}
|
|
||||||
className="fixed top-0 right-0 w-[10px] h-full cursor-pointer flex items-center justify-center rounded-tl-md rounded-bl-md"
|
|
||||||
style={{
|
|
||||||
zIndex: 9999,
|
|
||||||
backgroundColor: "#18181b",
|
|
||||||
border: "2px solid #27272a",
|
|
||||||
borderRight: "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ChevronRight size={10} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showDialog && (
|
{showDialog && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -652,10 +652,14 @@ export function AppView({
|
|||||||
style={{
|
style={{
|
||||||
background: containerBackground,
|
background: containerBackground,
|
||||||
marginLeft: leftMarginPx,
|
marginLeft: leftMarginPx,
|
||||||
marginRight: rightSidebarOpen ? rightSidebarWidth + 17 : 17,
|
marginRight: rightSidebarOpen
|
||||||
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
|
: 17,
|
||||||
marginTop: topMarginPx,
|
marginTop: topMarginPx,
|
||||||
marginBottom: bottomMarginPx,
|
marginBottom: bottomMarginPx,
|
||||||
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
||||||
|
transition:
|
||||||
|
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{renderTerminalsLayer()}
|
{renderTerminalsLayer()}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function TopNavbar({
|
|||||||
}, [toolsSidebarOpen, rightSidebarWidth, onRightSidebarStateChange]);
|
}, [toolsSidebarOpen, rightSidebarWidth, onRightSidebarStateChange]);
|
||||||
|
|
||||||
const rightPosition = toolsSidebarOpen
|
const rightPosition = toolsSidebarOpen
|
||||||
? `${rightSidebarWidth + 17}px`
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
: "17px";
|
: "17px";
|
||||||
const [justDroppedTabId, setJustDroppedTabId] = useState<number | null>(null);
|
const [justDroppedTabId, setJustDroppedTabId] = useState<number | null>(null);
|
||||||
const [isInDropAnimation, setIsInDropAnimation] = useState(false);
|
const [isInDropAnimation, setIsInDropAnimation] = useState(false);
|
||||||
@@ -315,12 +315,13 @@ export function TopNavbar({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="fixed z-10 h-[50px] border-2 border-dark-border rounded-lg transition-all duration-200 ease-linear flex flex-row transform-none m-0 p-0"
|
className="fixed z-10 h-[50px] border-2 border-dark-border rounded-lg flex flex-row transform-none m-0 p-0"
|
||||||
style={{
|
style={{
|
||||||
top: isTopbarOpen ? "0.5rem" : "-3rem",
|
top: isTopbarOpen ? "0.5rem" : "-3rem",
|
||||||
left: leftPosition,
|
left: leftPosition,
|
||||||
right: rightPosition,
|
right: rightPosition,
|
||||||
backgroundColor: "#18181b",
|
backgroundColor: "#18181b",
|
||||||
|
transition: "top 200ms linear, left 200ms linear, right 200ms linear",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
61
src/ui/desktop/navigation/animations/SimpleLoader.tsx
Normal file
61
src/ui/desktop/navigation/animations/SimpleLoader.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { cn } from "@/lib/utils.ts";
|
||||||
|
|
||||||
|
interface SimpleLoaderProps {
|
||||||
|
visible: boolean;
|
||||||
|
message?: string;
|
||||||
|
className?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SimpleLoader({
|
||||||
|
visible,
|
||||||
|
message,
|
||||||
|
className,
|
||||||
|
backgroundColor,
|
||||||
|
}: SimpleLoaderProps) {
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-spinner {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 4px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute inset-0 flex items-center justify-center z-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
style={{ backgroundColor: backgroundColor || "#18181b" }}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<div className="simple-spinner"></div>
|
||||||
|
{message && (
|
||||||
|
<p className="text-sm text-gray-300 font-medium">{message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -173,10 +173,14 @@ export function UserProfile({
|
|||||||
const bottomMarginPx = 8;
|
const bottomMarginPx = 8;
|
||||||
const wrapperStyle: React.CSSProperties = {
|
const wrapperStyle: React.CSSProperties = {
|
||||||
marginLeft: leftMarginPx,
|
marginLeft: leftMarginPx,
|
||||||
marginRight: rightSidebarOpen ? rightSidebarWidth + 17 : 17,
|
marginRight: rightSidebarOpen
|
||||||
|
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
|
||||||
|
: 17,
|
||||||
marginTop: topMarginPx,
|
marginTop: topMarginPx,
|
||||||
marginBottom: bottomMarginPx,
|
marginBottom: bottomMarginPx,
|
||||||
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
|
||||||
|
transition:
|
||||||
|
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={xtermRef}
|
ref={xtermRef}
|
||||||
className={`h-full w-full m-1 ${isReady && isVisible ? "opacity-100" : "opacity-0"} transition-opacity duration-150 overflow-hidden`}
|
className="h-full w-full m-1 overflow-hidden"
|
||||||
style={{ visibility: isReady ? "visible" : "hidden" }}
|
style={{ visibility: isReady ? "visible" : "hidden" }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user