chore: updating/match themes and split admin settings
This commit is contained in:
@@ -239,7 +239,7 @@ export function CommandPalette({
|
||||
>
|
||||
<Command
|
||||
className={cn(
|
||||
"w-3/4 max-w-2xl max-h-[60vh] rounded-lg border-2 border-edge shadow-md flex flex-col",
|
||||
"w-3/4 max-w-2xl max-h-[60vh] rounded-lg border-2 border-edge shadow-md flex flex-col bg-elevated",
|
||||
"transition-all duration-200 ease-out",
|
||||
!isOpen && "scale-95 opacity-0",
|
||||
)}
|
||||
|
||||
@@ -484,10 +484,16 @@ export function CredentialEditor({
|
||||
className="w-full"
|
||||
>
|
||||
<TabsList className="bg-button border border-edge-medium">
|
||||
<TabsTrigger value="general" className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium">
|
||||
<TabsTrigger
|
||||
value="general"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
>
|
||||
{t("credentials.general")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="authentication" className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium">
|
||||
<TabsTrigger
|
||||
value="authentication"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
>
|
||||
{t("credentials.authentication")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -693,10 +699,16 @@ export function CredentialEditor({
|
||||
className="flex-1 flex flex-col h-full min-h-0"
|
||||
>
|
||||
<TabsList className="bg-button border border-edge-medium">
|
||||
<TabsTrigger value="password" className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium">
|
||||
<TabsTrigger
|
||||
value="password"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
>
|
||||
{t("credentials.password")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="key" className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium">
|
||||
<TabsTrigger
|
||||
value="key"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
>
|
||||
{t("credentials.key")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -719,7 +731,7 @@ export function CredentialEditor({
|
||||
</TabsContent>
|
||||
<TabsContent value="key">
|
||||
<div className="mt-2">
|
||||
<div className="mb-3 p-3 bg-muted/20 border border-muted rounded-md">
|
||||
<div className="mb-3 p-3 border border-muted rounded-md">
|
||||
<FormLabel className="mb-2 font-bold block">
|
||||
{t("credentials.generateKeyPair")}
|
||||
</FormLabel>
|
||||
@@ -954,7 +966,8 @@ export function CredentialEditor({
|
||||
".cm-scroller": {
|
||||
overflow: "auto",
|
||||
scrollbarWidth: "thin",
|
||||
scrollbarColor: "var(--scrollbar-thumb) var(--scrollbar-track)",
|
||||
scrollbarColor:
|
||||
"var(--scrollbar-thumb) var(--scrollbar-track)",
|
||||
},
|
||||
}),
|
||||
]}
|
||||
@@ -1116,7 +1129,8 @@ export function CredentialEditor({
|
||||
".cm-scroller": {
|
||||
overflow: "auto",
|
||||
scrollbarWidth: "thin",
|
||||
scrollbarColor: "var(--scrollbar-thumb) var(--scrollbar-track)",
|
||||
scrollbarColor:
|
||||
"var(--scrollbar-thumb) var(--scrollbar-track)",
|
||||
},
|
||||
}),
|
||||
]}
|
||||
|
||||
@@ -407,7 +407,7 @@ export function Dashboard({
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
className="font-semibold shrink-0"
|
||||
className="font-semibold shrink-0 !bg-canvas"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
@@ -419,7 +419,7 @@ export function Dashboard({
|
||||
{t("dashboard.github")}
|
||||
</Button>
|
||||
<Button
|
||||
className="font-semibold shrink-0"
|
||||
className="font-semibold shrink-0 !bg-canvas"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
@@ -431,7 +431,7 @@ export function Dashboard({
|
||||
{t("dashboard.support")}
|
||||
</Button>
|
||||
<Button
|
||||
className="font-semibold shrink-0"
|
||||
className="font-semibold shrink-0 !bg-canvas"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
@@ -443,7 +443,7 @@ export function Dashboard({
|
||||
{t("dashboard.discord")}
|
||||
</Button>
|
||||
<Button
|
||||
className="font-semibold shrink-0"
|
||||
className="font-semibold shrink-0 !bg-canvas"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
window.open("https://github.com/sponsors/LukeGus", "_blank")
|
||||
@@ -467,10 +467,7 @@ export function Dashboard({
|
||||
<div className="bg-canvas w-full h-auto border-2 border-edge rounded-md px-3 py-3">
|
||||
<div className="flex flex-row items-center justify-between mb-3 min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<History
|
||||
size={20}
|
||||
className="shrink-0"
|
||||
/>
|
||||
<History size={20} className="shrink-0" />
|
||||
<p className="ml-2 leading-none truncate">
|
||||
{t("dashboard.version")}
|
||||
</p>
|
||||
@@ -495,10 +492,7 @@ export function Dashboard({
|
||||
|
||||
<div className="flex flex-row items-center justify-between mb-5 min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<Clock
|
||||
size={20}
|
||||
className="shrink-0"
|
||||
/>
|
||||
<Clock size={20} className="shrink-0" />
|
||||
<p className="ml-2 leading-none truncate">
|
||||
{t("dashboard.uptime")}
|
||||
</p>
|
||||
@@ -513,10 +507,7 @@ export function Dashboard({
|
||||
|
||||
<div className="flex flex-row items-center justify-between min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<Database
|
||||
size={20}
|
||||
className="shrink-0"
|
||||
/>
|
||||
<Database size={20} className="shrink-0" />
|
||||
<p className="ml-2 leading-none truncate">
|
||||
{t("dashboard.database")}
|
||||
</p>
|
||||
@@ -536,10 +527,7 @@ export function Dashboard({
|
||||
<div className="flex flex-col grid grid-cols-2 gap-2 mt-2">
|
||||
<div className="flex flex-row items-center justify-between bg-canvas w-full h-auto mt-3 border-2 border-edge rounded-md px-3 py-3 min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<Server
|
||||
size={16}
|
||||
className="mr-3 shrink-0"
|
||||
/>
|
||||
<Server size={16} className="mr-3 shrink-0" />
|
||||
<p className="m-0 leading-none truncate">
|
||||
{t("dashboard.totalServers")}
|
||||
</p>
|
||||
@@ -550,10 +538,7 @@ export function Dashboard({
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-between bg-canvas w-full h-auto mt-3 border-2 border-edge rounded-md px-3 py-3 min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<Network
|
||||
size={16}
|
||||
className="mr-3 shrink-0"
|
||||
/>
|
||||
<Network size={16} className="mr-3 shrink-0" />
|
||||
<p className="m-0 leading-none truncate">
|
||||
{t("dashboard.totalTunnels")}
|
||||
</p>
|
||||
@@ -566,10 +551,7 @@ export function Dashboard({
|
||||
<div className="flex flex-col grid grid-cols-2 gap-2 mt-2">
|
||||
<div className="flex flex-row items-center justify-between bg-canvas w-full h-auto mt-3 border-2 border-edge rounded-md px-3 py-3 min-w-0 gap-2">
|
||||
<div className="flex flex-row items-center min-w-0">
|
||||
<Key
|
||||
size={16}
|
||||
className="mr-3 shrink-0"
|
||||
/>
|
||||
<Key size={16} className="mr-3 shrink-0" />
|
||||
<p className="m-0 leading-none truncate">
|
||||
{t("dashboard.totalCredentials")}
|
||||
</p>
|
||||
@@ -591,7 +573,7 @@ export function Dashboard({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-2 !border-edge h-7"
|
||||
className="border-2 !border-edge h-7 !bg-canvas"
|
||||
onClick={handleResetActivity}
|
||||
>
|
||||
{t("dashboard.reset")}
|
||||
@@ -626,7 +608,7 @@ export function Dashboard({
|
||||
<Button
|
||||
key={item.id}
|
||||
variant="outline"
|
||||
className="border-2 !border-edge bg-canvas min-w-0"
|
||||
className="border-2 !border-edge !bg-canvas min-w-0"
|
||||
onClick={() => handleActivityClick(item)}
|
||||
>
|
||||
{item.type === "terminal" ? (
|
||||
@@ -654,7 +636,7 @@ export function Dashboard({
|
||||
<div className="grid gap-4 grid-cols-3 auto-rows-min overflow-y-auto overflow-x-hidden thin-scrollbar">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0 !bg-canvas"
|
||||
onClick={handleAddHost}
|
||||
>
|
||||
<Server
|
||||
@@ -667,7 +649,7 @@ export function Dashboard({
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0 !bg-canvas"
|
||||
onClick={handleAddCredential}
|
||||
>
|
||||
<Key
|
||||
@@ -681,7 +663,7 @@ export function Dashboard({
|
||||
{isAdmin && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0 !bg-canvas"
|
||||
onClick={handleOpenAdminSettings}
|
||||
>
|
||||
<Settings
|
||||
@@ -695,7 +677,7 @@ export function Dashboard({
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0"
|
||||
className="border-2 !border-edge flex flex-col items-center justify-center h-auto p-3 min-w-0 !bg-canvas"
|
||||
onClick={handleOpenUserProfile}
|
||||
>
|
||||
<User
|
||||
@@ -732,7 +714,7 @@ export function Dashboard({
|
||||
<Button
|
||||
key={server.id}
|
||||
variant="outline"
|
||||
className="border-2 !border-edge bg-canvas h-auto p-3 min-w-0"
|
||||
className="border-2 !border-edge bg-canvas h-auto p-3 min-w-0 !bg-canvas"
|
||||
onClick={() =>
|
||||
handleServerStatClick(server.id, server.name)
|
||||
}
|
||||
|
||||
@@ -230,8 +230,8 @@ export function DockerManager({
|
||||
};
|
||||
|
||||
const containerClass = embedded
|
||||
? "h-full w-full text-white overflow-hidden bg-transparent"
|
||||
: "bg-dark-bg text-white rounded-lg border-2 border-dark-border overflow-hidden";
|
||||
? "h-full w-full text-foreground overflow-hidden bg-transparent"
|
||||
: "bg-canvas text-foreground rounded-lg border-2 border-edge overflow-hidden";
|
||||
|
||||
// Check if Docker is enabled
|
||||
if (!currentHostConfig?.enableDocker) {
|
||||
@@ -252,9 +252,7 @@ export function DockerManager({
|
||||
<div className="flex-1 overflow-hidden min-h-0 p-4">
|
||||
<Alert variant="destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
{t("docker.notEnabled")}
|
||||
</AlertDescription>
|
||||
<AlertDescription>{t("docker.notEnabled")}</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
@@ -281,7 +279,7 @@ export function DockerManager({
|
||||
<div className="flex-1 overflow-hidden min-h-0 p-4 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<SimpleLoader size="lg" />
|
||||
<p className="text-gray-400 mt-4">
|
||||
<p className="text-muted-foreground mt-4">
|
||||
{isValidating
|
||||
? t("docker.validating")
|
||||
: t("docker.connectingToHost")}
|
||||
@@ -338,7 +336,7 @@ export function DockerManager({
|
||||
{currentHostConfig?.folder} / {title}
|
||||
</h1>
|
||||
{dockerValidation?.version && (
|
||||
<p className="text-xs text-gray-400">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("docker.version", { version: dockerValidation.version })}
|
||||
</p>
|
||||
)}
|
||||
@@ -363,7 +361,7 @@ export function DockerManager({
|
||||
/>
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-400">No session available</p>
|
||||
<p className="text-muted-foreground">No session available</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -377,7 +375,7 @@ export function DockerManager({
|
||||
/>
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-400">
|
||||
<p className="text-muted-foreground">
|
||||
Select a container to view details
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -93,9 +93,18 @@ export function ConsoleTerminal({
|
||||
terminal.options.cursorBlink = true;
|
||||
terminal.options.fontSize = 14;
|
||||
terminal.options.fontFamily = "monospace";
|
||||
|
||||
// Get theme colors from CSS variables
|
||||
const backgroundColor = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--bg-elevated")
|
||||
.trim();
|
||||
const foregroundColor = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--foreground")
|
||||
.trim();
|
||||
|
||||
terminal.options.theme = {
|
||||
background: "#18181b",
|
||||
foreground: "#c9d1d9",
|
||||
background: backgroundColor || "#ffffff",
|
||||
foreground: foregroundColor || "#09090b",
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -152,12 +161,11 @@ export function ConsoleTerminal({
|
||||
if (terminal) {
|
||||
try {
|
||||
terminal.clear();
|
||||
terminal.write(`${t("docker.disconnectedFromContainer")}\r\n`);
|
||||
} catch (error) {
|
||||
// Terminal might be disposed
|
||||
}
|
||||
}
|
||||
}, [terminal]);
|
||||
}, [terminal, t]);
|
||||
|
||||
const connect = React.useCallback(() => {
|
||||
if (!terminal || containerState !== "running") {
|
||||
@@ -216,7 +224,15 @@ export function ConsoleTerminal({
|
||||
case "connected":
|
||||
setIsConnected(true);
|
||||
setIsConnecting(false);
|
||||
toast.success(t("docker.connectedTo", { containerName }));
|
||||
|
||||
// Check if shell was changed due to unavailability
|
||||
if (msg.data?.shellChanged) {
|
||||
toast.warning(
|
||||
`Shell "${msg.data.requestedShell}" not available. Using "${msg.data.shell}" instead.`,
|
||||
);
|
||||
} else {
|
||||
toast.success(t("docker.connectedTo", { containerName }));
|
||||
}
|
||||
|
||||
// Fit terminal and send resize to ensure correct dimensions
|
||||
setTimeout(() => {
|
||||
@@ -251,7 +267,9 @@ export function ConsoleTerminal({
|
||||
case "error":
|
||||
setIsConnecting(false);
|
||||
toast.error(msg.message || t("docker.consoleError"));
|
||||
terminal.write(`\r\n\x1b[1;31m${t("docker.errorMessage", { message: msg.message })}\x1b[0m\r\n`);
|
||||
terminal.write(
|
||||
`\r\n\x1b[1;31m${t("docker.errorMessage", { message: msg.message })}\x1b[0m\r\n`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -341,9 +359,11 @@ export function ConsoleTerminal({
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center space-y-2">
|
||||
<TerminalIcon className="h-12 w-12 text-gray-600 mx-auto" />
|
||||
<p className="text-gray-400 text-lg">{t("docker.containerNotRunning")}</p>
|
||||
<p className="text-gray-500 text-sm">
|
||||
<TerminalIcon className="h-12 w-12 text-muted-foreground/50 mx-auto" />
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{t("docker.containerNotRunning")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("docker.startContainerToAccess")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -359,7 +379,9 @@ export function ConsoleTerminal({
|
||||
<div className="flex flex-col sm:flex-row gap-2 items-center sm:items-center">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<TerminalIcon className="h-5 w-5" />
|
||||
<span className="text-base font-medium">{t("docker.console")}</span>
|
||||
<span className="text-base font-medium">
|
||||
{t("docker.console")}
|
||||
</span>
|
||||
</div>
|
||||
<Select
|
||||
value={selectedShell}
|
||||
@@ -423,9 +445,11 @@ export function ConsoleTerminal({
|
||||
{!isConnected && !isConnecting && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center space-y-2">
|
||||
<TerminalIcon className="h-12 w-12 text-gray-600 mx-auto" />
|
||||
<p className="text-gray-400">{t("docker.notConnected")}</p>
|
||||
<p className="text-gray-500 text-sm">
|
||||
<TerminalIcon className="h-12 w-12 text-muted-foreground/50 mx-auto" />
|
||||
<p className="text-muted-foreground">
|
||||
{t("docker.notConnected")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("docker.clickToConnect")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -437,7 +461,7 @@ export function ConsoleTerminal({
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<SimpleLoader size="lg" />
|
||||
<p className="text-gray-400 mt-4">
|
||||
<p className="text-muted-foreground mt-4">
|
||||
{t("docker.connectingTo", { containerName })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
PlayCircle,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { DockerContainer } from "@/types/index.js";
|
||||
import {
|
||||
startDockerContainer,
|
||||
@@ -57,6 +58,7 @@ export function ContainerCard({
|
||||
isSelected = false,
|
||||
onRefresh,
|
||||
}: ContainerCardProps): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [isStarting, setIsStarting] = React.useState(false);
|
||||
const [isStopping, setIsStopping] = React.useState(false);
|
||||
const [isRestarting, setIsRestarting] = React.useState(false);
|
||||
@@ -102,10 +104,10 @@ export function ContainerCard({
|
||||
badge: "bg-purple-500/20 text-purple-300 border-purple-500/30",
|
||||
},
|
||||
dead: {
|
||||
bg: "bg-gray-500/10",
|
||||
border: "border-gray-500/20",
|
||||
text: "text-gray-400",
|
||||
badge: "bg-gray-500/20 text-gray-300 border-gray-500/30",
|
||||
bg: "bg-muted/10",
|
||||
border: "border-muted/20",
|
||||
text: "text-muted-foreground",
|
||||
badge: "bg-muted/20 text-muted-foreground border-muted/30",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -260,26 +262,32 @@ export function ContainerCard({
|
||||
<CardContent className="space-y-3 px-4 pb-3">
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-400 min-w-[50px] text-xs">{t("docker.image")}</span>
|
||||
<span className="truncate text-gray-200 text-xs">
|
||||
<span className="text-muted-foreground min-w-[50px] text-xs">
|
||||
{t("docker.image")}
|
||||
</span>
|
||||
<span className="truncate text-foreground text-xs">
|
||||
{container.image}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-400 min-w-[50px] text-xs">{t("docker.idLabel")}</span>
|
||||
<span className="font-mono text-xs text-gray-200">
|
||||
<span className="text-muted-foreground min-w-[50px] text-xs">
|
||||
{t("docker.idLabel")}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-foreground">
|
||||
{container.id.substring(0, 12)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-gray-400 min-w-[50px] text-xs shrink-0">{t("docker.ports")}</span>
|
||||
<span className="text-muted-foreground min-w-[50px] text-xs shrink-0">
|
||||
{t("docker.ports")}
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{portsList.length > 0 ? (
|
||||
portsList.map((port, idx) => (
|
||||
<Badge
|
||||
key={idx}
|
||||
variant="outline"
|
||||
className="text-xs font-mono bg-gray-500/10 text-gray-400 border-gray-500/30"
|
||||
className="text-xs font-mono bg-muted/10 text-muted-foreground border-muted/30"
|
||||
>
|
||||
{port}
|
||||
</Badge>
|
||||
@@ -287,7 +295,7 @@ export function ContainerCard({
|
||||
) : (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs bg-gray-500/10 text-gray-400 border-gray-500/30"
|
||||
className="text-xs bg-muted/10 text-muted-foreground border-muted/30"
|
||||
>
|
||||
{t("docker.noPorts")}
|
||||
</Badge>
|
||||
@@ -295,8 +303,10 @@ export function ContainerCard({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-400 min-w-[50px] text-xs">{t("docker.created")}</span>
|
||||
<span className="text-gray-200 text-xs">
|
||||
<span className="text-muted-foreground min-w-[50px] text-xs">
|
||||
{t("docker.created")}
|
||||
</span>
|
||||
<span className="text-foreground text-xs">
|
||||
{formatCreatedDate(container.created)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -390,7 +400,8 @@ export function ContainerCard({
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("docker.restart")}</TooltipContent> </Tooltip>
|
||||
<TooltipContent>{t("docker.restart")}</TooltipContent>{" "}
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -407,7 +418,8 @@ export function ContainerCard({
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("docker.remove")}</TooltipContent> </Tooltip>
|
||||
<TooltipContent>{t("docker.remove")}</TooltipContent>{" "}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -431,7 +443,9 @@ export function ContainerCard({
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={isRemoving}>{t("common.cancel")}</AlertDialogCancel>
|
||||
<AlertDialogCancel disabled={isRemoving}>
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
TabsTrigger,
|
||||
} from "@/components/ui/tabs.tsx";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { DockerContainer, SSHHost } from "@/types/index.js";
|
||||
import { LogViewer } from "./LogViewer.tsx";
|
||||
import { ContainerStats } from "./ContainerStats.tsx";
|
||||
@@ -28,6 +29,7 @@ export function ContainerDetail({
|
||||
hostConfig,
|
||||
onBack,
|
||||
}: ContainerDetailProps): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = React.useState("logs");
|
||||
|
||||
const container = containers.find((c) => c.id === containerId);
|
||||
@@ -36,7 +38,9 @@ export function ContainerDetail({
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center space-y-2">
|
||||
<p className="text-gray-400 text-lg">{t("docker.containerNotFound")}</p>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{t("docker.containerNotFound")}
|
||||
</p>
|
||||
<Button onClick={onBack} variant="outline">
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
{t("docker.backToList")}
|
||||
@@ -56,7 +60,9 @@ export function ContainerDetail({
|
||||
</Button>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="font-bold text-lg truncate">{container.name}</h2>
|
||||
<p className="text-sm text-gray-400 truncate">{container.image}</p>
|
||||
<p className="text-sm text-muted-foreground truncate">
|
||||
{container.image}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="p-0.25 w-full" />
|
||||
@@ -72,7 +78,9 @@ export function ContainerDetail({
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="logs">{t("docker.logs")}</TabsTrigger>
|
||||
<TabsTrigger value="stats">{t("docker.stats")}</TabsTrigger>
|
||||
<TabsTrigger value="console">{t("docker.consoleTab")}</TabsTrigger>
|
||||
<TabsTrigger value="console">
|
||||
{t("docker.consoleTab")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select.tsx";
|
||||
import { Search, Filter } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { DockerContainer } from "@/types/index.js";
|
||||
import { ContainerCard } from "./ContainerCard.tsx";
|
||||
|
||||
@@ -26,6 +27,7 @@ export function ContainerList({
|
||||
selectedContainerId = null,
|
||||
onRefresh,
|
||||
}: ContainerListProps): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [searchQuery, setSearchQuery] = React.useState("");
|
||||
const [statusFilter, setStatusFilter] = React.useState<string>("all");
|
||||
|
||||
@@ -55,8 +57,12 @@ export function ContainerList({
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center space-y-2">
|
||||
<p className="text-gray-400 text-lg">{t("docker.noContainersFound")}</p>
|
||||
<p className="text-gray-500 text-sm">{t("docker.noContainersFoundHint")}</p>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{t("docker.noContainersFound")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("docker.noContainersFoundHint")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -67,7 +73,7 @@ export function ContainerList({
|
||||
{/* Search and Filter Bar */}
|
||||
<div className="flex flex-col sm:flex-row gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder={t("docker.searchPlaceholder")}
|
||||
value={searchQuery}
|
||||
@@ -76,16 +82,23 @@ export function ContainerList({
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 sm:min-w-[200px]">
|
||||
<Filter className="h-4 w-4 text-gray-400" />
|
||||
<Filter className="h-4 w-4 text-muted-foreground" />
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={t("docker.filterByStatusPlaceholder")} />
|
||||
<SelectValue
|
||||
placeholder={t("docker.filterByStatusPlaceholder")}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">{t("docker.allContainersCount", { count: containers.length })}</SelectItem>
|
||||
<SelectItem value="all">
|
||||
{t("docker.allContainersCount", { count: containers.length })}
|
||||
</SelectItem>
|
||||
{Object.entries(statusCounts).map(([status, count]) => (
|
||||
<SelectItem key={status} value={status}>
|
||||
{t("docker.statusCount", { status: status.charAt(0).toUpperCase() + status.slice(1), count })}
|
||||
{t("docker.statusCount", {
|
||||
status: status.charAt(0).toUpperCase() + status.slice(1),
|
||||
count,
|
||||
})}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -97,8 +110,12 @@ export function ContainerList({
|
||||
{filteredContainers.length === 0 ? (
|
||||
<div className="flex items-center justify-center flex-1">
|
||||
<div className="text-center space-y-2">
|
||||
<p className="text-gray-400">{t("docker.noContainersMatchFilters")}</p>
|
||||
<p className="text-gray-500 text-sm">{t("docker.noContainersMatchFiltersHint")}</p>
|
||||
<p className="text-muted-foreground">
|
||||
{t("docker.noContainersMatchFilters")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("docker.noContainersMatchFiltersHint")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -42,7 +42,9 @@ export function ContainerStats({
|
||||
const data = await getContainerStats(sessionId, containerId);
|
||||
setStats(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : t("docker.failedToFetchStats"));
|
||||
setError(
|
||||
err instanceof Error ? err.message : t("docker.failedToFetchStats"),
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -61,11 +63,11 @@ export function ContainerStats({
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center space-y-2">
|
||||
<Activity className="h-12 w-12 text-gray-600 mx-auto" />
|
||||
<p className="text-gray-400 text-lg">
|
||||
<Activity className="h-12 w-12 text-muted-foreground/50 mx-auto" />
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{t("docker.containerNotRunning")}
|
||||
</p>
|
||||
<p className="text-gray-500 text-sm">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("docker.startContainerToViewStats")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -78,7 +80,9 @@ export function ContainerStats({
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center">
|
||||
<SimpleLoader size="lg" />
|
||||
<p className="text-gray-400 mt-4">{t("docker.loadingStats")}</p>
|
||||
<p className="text-muted-foreground mt-4">
|
||||
{t("docker.loadingStats")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -91,7 +95,7 @@ export function ContainerStats({
|
||||
<p className="text-red-400 text-lg">
|
||||
{t("docker.errorLoadingStats")}
|
||||
</p>
|
||||
<p className="text-gray-500 text-sm">{error}</p>
|
||||
<p className="text-muted-foreground text-sm">{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -100,7 +104,7 @@ export function ContainerStats({
|
||||
if (!stats) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-gray-400">{t("docker.noStatsAvailable")}</p>
|
||||
<p className="text-muted-foreground">{t("docker.noStatsAvailable")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -121,7 +125,10 @@ export function ContainerStats({
|
||||
<CardContent className="px-4 pb-3">
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.current")}</span> <span className="font-mono font-semibold text-blue-300">
|
||||
<span className="text-muted-foreground">
|
||||
{t("docker.current")}
|
||||
</span>{" "}
|
||||
<span className="font-mono font-semibold text-blue-400">
|
||||
{stats.cpu}
|
||||
</span>
|
||||
</div>
|
||||
@@ -141,14 +148,18 @@ export function ContainerStats({
|
||||
<CardContent className="px-4 pb-3">
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.usedLimit")}</span>
|
||||
<span className="font-mono font-semibold text-purple-300">
|
||||
<span className="text-muted-foreground">
|
||||
{t("docker.usedLimit")}
|
||||
</span>
|
||||
<span className="font-mono font-semibold text-purple-400">
|
||||
{stats.memoryUsed} / {stats.memoryLimit}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.percentage")}</span>
|
||||
<span className="font-mono text-purple-300">
|
||||
<span className="text-muted-foreground">
|
||||
{t("docker.percentage")}
|
||||
</span>
|
||||
<span className="font-mono text-purple-400">
|
||||
{stats.memoryPercent}
|
||||
</span>
|
||||
</div>
|
||||
@@ -168,12 +179,14 @@ export function ContainerStats({
|
||||
<CardContent className="px-4 pb-3">
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.input")}</span>
|
||||
<span className="font-mono text-green-300">{stats.netInput}</span>
|
||||
<span className="text-muted-foreground">{t("docker.input")}</span>
|
||||
<span className="font-mono text-green-400">{stats.netInput}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.output")}</span>
|
||||
<span className="font-mono text-green-300">
|
||||
<span className="text-muted-foreground">
|
||||
{t("docker.output")}
|
||||
</span>
|
||||
<span className="font-mono text-green-400">
|
||||
{stats.netOutput}
|
||||
</span>
|
||||
</div>
|
||||
@@ -192,21 +205,23 @@ export function ContainerStats({
|
||||
<CardContent className="px-4 pb-3">
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.read")}</span>
|
||||
<span className="font-mono text-orange-300">
|
||||
<span className="text-muted-foreground">{t("docker.read")}</span>
|
||||
<span className="font-mono text-orange-400">
|
||||
{stats.blockRead}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.write")}</span>
|
||||
<span className="font-mono text-orange-300">
|
||||
<span className="text-muted-foreground">{t("docker.write")}</span>
|
||||
<span className="font-mono text-orange-400">
|
||||
{stats.blockWrite}
|
||||
</span>
|
||||
</div>
|
||||
{stats.pids && (
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-gray-400">{t("docker.pids")}</span>
|
||||
<span className="font-mono text-orange-300">{stats.pids}</span>
|
||||
<span className="text-muted-foreground">
|
||||
{t("docker.pids")}
|
||||
</span>
|
||||
<span className="font-mono text-orange-400">{stats.pids}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -224,17 +239,17 @@ export function ContainerStats({
|
||||
<CardContent className="px-4 pb-3">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 text-sm">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400">{t("docker.name")}</span>
|
||||
<span className="font-mono text-gray-200">{containerName}</span>
|
||||
<span className="text-muted-foreground">{t("docker.name")}</span>
|
||||
<span className="font-mono text-foreground">{containerName}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400">{t("docker.id")}</span>
|
||||
<span className="font-mono text-sm text-gray-300">
|
||||
<span className="text-muted-foreground">{t("docker.id")}</span>
|
||||
<span className="font-mono text-sm text-foreground">
|
||||
{containerId.substring(0, 12)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400">{t("docker.state")}</span>
|
||||
<span className="text-muted-foreground">{t("docker.state")}</span>
|
||||
<span className="font-semibold text-green-400 capitalize">
|
||||
{containerState}
|
||||
</span>
|
||||
|
||||
@@ -209,13 +209,13 @@ export function LogViewer({
|
||||
{/* Search Filter */}
|
||||
<div className="mt-2">
|
||||
<div className="relative">
|
||||
<Filter className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Filter className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter logs..."
|
||||
value={searchFilter}
|
||||
onChange={(e) => setSearchFilter(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 bg-dark-bg border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
className="w-full pl-10 pr-4 py-2 border border-input rounded-md text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -231,9 +231,11 @@ export function LogViewer({
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full overflow-auto thin-scrollbar">
|
||||
<pre className="p-4 text-xs font-mono whitespace-pre-wrap break-words text-gray-200 leading-relaxed">
|
||||
<pre className="p-4 text-xs font-mono whitespace-pre-wrap break-words text-foreground leading-relaxed">
|
||||
{filteredLogs || (
|
||||
<span className="text-gray-500">No logs available</span>
|
||||
<span className="text-muted-foreground">
|
||||
No logs available
|
||||
</span>
|
||||
)}
|
||||
<div ref={logsEndRef} />
|
||||
</pre>
|
||||
|
||||
@@ -477,7 +477,7 @@ export function ServerStats({
|
||||
{(metricsEnabled && showStatsUI) ||
|
||||
(currentHostConfig?.quickActions &&
|
||||
currentHostConfig.quickActions.length > 0) ? (
|
||||
<div className="rounded-lg border-2 border-edge m-3 bg-elevated p-4 overflow-y-auto thin-scrollbar relative flex-1 flex flex-col">
|
||||
<div className="border-edge m-1 p-2 overflow-y-auto thin-scrollbar relative flex-1 flex flex-col">
|
||||
{currentHostConfig?.quickActions &&
|
||||
currentHostConfig.quickActions.length > 0 && (
|
||||
<div className={metricsEnabled && showStatsUI ? "mb-4" : ""}>
|
||||
|
||||
@@ -30,8 +30,7 @@ export function CpuWidget({ metrics, metricsHistory }: CpuWidgetProps) {
|
||||
}, [metricsHistory]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Cpu className="h-5 w-5 text-blue-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
|
||||
@@ -27,8 +27,7 @@ export function DiskWidget({ metrics }: DiskWidgetProps) {
|
||||
}, [metrics]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<HardDrive className="h-5 w-5 text-orange-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
|
||||
@@ -35,7 +35,7 @@ export function LoginStatsWidget({ metrics }: LoginStatsWidgetProps) {
|
||||
const uniqueIPs = loginStats?.uniqueIPs || 0;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<UserCheck className="h-5 w-5 text-green-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
@@ -45,7 +45,7 @@ export function LoginStatsWidget({ metrics }: LoginStatsWidgetProps) {
|
||||
|
||||
<div className="flex flex-col flex-1 min-h-0 gap-3">
|
||||
<div className="grid grid-cols-2 gap-2 flex-shrink-0">
|
||||
<div className="bg-elevated p-2 rounded border border-edge/30">
|
||||
<div className="bg-canvas/40 p-2 rounded border border-edge/30 hover:bg-canvas/50">
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
|
||||
<Activity className="h-3 w-3" />
|
||||
<span>{t("serverStats.totalLogins")}</span>
|
||||
@@ -54,7 +54,7 @@ export function LoginStatsWidget({ metrics }: LoginStatsWidgetProps) {
|
||||
{totalLogins}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-elevated p-2 rounded border border-edge/30">
|
||||
<div className="bg-canvas/40 p-2 rounded border border-edge/30 hover:bg-canvas/50">
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground mb-1">
|
||||
<MapPin className="h-3 w-3" />
|
||||
<span>{t("serverStats.uniqueIPs")}</span>
|
||||
@@ -80,7 +80,7 @@ export function LoginStatsWidget({ metrics }: LoginStatsWidgetProps) {
|
||||
{recentLogins.slice(0, 5).map((login, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="text-xs bg-elevated p-2 rounded border border-edge/30 flex justify-between items-center"
|
||||
className="text-xs bg-canvas/40 p-2 rounded border border-edge/30 hover:bg-canvas/50 flex justify-between items-center"
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span className="text-green-400 font-mono truncate">
|
||||
|
||||
@@ -30,8 +30,7 @@ export function MemoryWidget({ metrics, metricsHistory }: MemoryWidgetProps) {
|
||||
}, [metricsHistory]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<MemoryStick className="h-5 w-5 text-green-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
|
||||
@@ -24,8 +24,7 @@ export function NetworkWidget({ metrics }: NetworkWidgetProps) {
|
||||
const interfaces = network?.interfaces || [];
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Network className="h-5 w-5 text-indigo-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
@@ -43,7 +42,7 @@ export function NetworkWidget({ metrics }: NetworkWidgetProps) {
|
||||
interfaces.map((iface, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-3 rounded-lg bg-canvas/50 border border-edge/30 hover:bg-canvas/60 transition-colors"
|
||||
className="p-3 rounded-lg bg-canvas/40 border border-edge/30 hover:bg-canvas/50"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -28,7 +28,7 @@ export function ProcessesWidget({ metrics }: ProcessesWidgetProps) {
|
||||
const topProcesses = processes?.top || [];
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<List className="h-5 w-5 text-yellow-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
@@ -62,7 +62,7 @@ export function ProcessesWidget({ metrics }: ProcessesWidgetProps) {
|
||||
{topProcesses.map((proc, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-2.5 rounded-lg bg-canvas/30 hover:bg-canvas/50 transition-colors border border-edge/20"
|
||||
className="p-2.5 rounded-lg bg-canvas/40 hover:bg-canvas/50 border border-edge/30"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<span className="text-xs font-mono text-muted-foreground font-medium">
|
||||
|
||||
@@ -21,8 +21,7 @@ export function SystemWidget({ metrics }: SystemWidgetProps) {
|
||||
const system = metricsWithSystem?.system;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Server className="h-5 w-5 text-purple-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
|
||||
@@ -20,8 +20,7 @@ export function UptimeWidget({ metrics }: UptimeWidgetProps) {
|
||||
const uptime = metricsWithUptime?.uptime;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4 rounded-lg bg-canvas/50 border border-edge/50 hover:bg-canvas/70 transition-colors duration-200 flex flex-col overflow-hidden">
|
||||
|
||||
<div className="h-full w-full p-4 rounded-lg bg-elevated border border-edge/50 hover:bg-elevated/70 flex flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
|
||||
<Clock className="h-5 w-5 text-cyan-400" />
|
||||
<h3 className="font-semibold text-lg text-foreground">
|
||||
|
||||
@@ -247,7 +247,8 @@ export function SSHToolsSidebar({
|
||||
tab.type === "terminal" ||
|
||||
tab.type === "server" ||
|
||||
tab.type === "file_manager" ||
|
||||
tab.type === "user_profile",
|
||||
tab.type === "tunnel" ||
|
||||
tab.type === "docker",
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1246,7 +1247,7 @@ export function SSHToolsSidebar({
|
||||
{terminalTabs.length > 0 && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white">
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
{t("snippets.selectTerminals")}
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
@@ -1702,7 +1703,7 @@ export function SSHToolsSidebar({
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white">
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
{t("splitScreen.availableTabs")}
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground mb-2">
|
||||
@@ -1745,7 +1746,7 @@ export function SSHToolsSidebar({
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white">
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
{t("splitScreen.layout")}
|
||||
</label>
|
||||
<div
|
||||
@@ -1763,7 +1764,9 @@ export function SSHToolsSidebar({
|
||||
const assignedTabId =
|
||||
splitAssignments.get(idx);
|
||||
const assignedTab = assignedTabId
|
||||
? tabs.find((t) => t.id === assignedTabId)
|
||||
? splittableTabs.find(
|
||||
(t) => t.id === assignedTabId,
|
||||
)
|
||||
: null;
|
||||
const isHovered = dragOverCellIndex === idx;
|
||||
const isEmpty = !assignedTabId;
|
||||
@@ -2050,7 +2053,7 @@ export function SSHToolsSidebar({
|
||||
|
||||
<div className="space-y-5">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white flex items-center gap-1">
|
||||
<label className="text-sm font-medium text-foreground flex items-center gap-1">
|
||||
{t("snippets.folderName")}
|
||||
<span className="text-destructive">*</span>
|
||||
</label>
|
||||
@@ -2074,7 +2077,7 @@ export function SSHToolsSidebar({
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-base font-semibold text-white">
|
||||
<Label className="text-base font-semibold text-foreground">
|
||||
{t("snippets.folderColor")}
|
||||
</Label>
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
@@ -2101,7 +2104,7 @@ export function SSHToolsSidebar({
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-base font-semibold text-white">
|
||||
<Label className="text-base font-semibold text-foreground">
|
||||
{t("snippets.folderIcon")}
|
||||
</Label>
|
||||
<div className="grid grid-cols-5 gap-3">
|
||||
@@ -2126,7 +2129,7 @@ export function SSHToolsSidebar({
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-base font-semibold text-white">
|
||||
<Label className="text-base font-semibold text-foreground">
|
||||
{t("snippets.preview")}
|
||||
</Label>
|
||||
<div className="flex items-center gap-3 p-4 rounded-md bg-elevated border border-edge">
|
||||
|
||||
@@ -94,8 +94,8 @@ export function TunnelManager({
|
||||
};
|
||||
|
||||
const containerClass = embedded
|
||||
? "h-full w-full text-white overflow-hidden bg-transparent"
|
||||
: "bg-dark-bg text-white rounded-lg border-2 border-dark-border overflow-hidden";
|
||||
? "h-full w-full text-foreground overflow-hidden bg-transparent"
|
||||
: "bg-canvas text-foreground rounded-lg border-2 border-edge overflow-hidden";
|
||||
|
||||
return (
|
||||
<div style={wrapperStyle} className={containerClass}>
|
||||
|
||||
Reference in New Issue
Block a user