mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-29 18:11:40 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd1c2786b3 | ||
|
|
be43afe31e | ||
|
|
e86f38203c | ||
|
|
b7dc61efb0 | ||
|
|
6e5d08c005 |
No files matched your search
@@ -1,4 +1,5 @@
|
||||
export const enMonitorMessages = {
|
||||
"monitor.actions": "Panel actions",
|
||||
"monitor.unavailable": "Monitoring is unavailable. Try starting again when the connection is restored.",
|
||||
"monitor.username": "Username",
|
||||
"monitor.password": "Password (this run only)",
|
||||
@@ -43,6 +44,8 @@ export const enMonitorMessages = {
|
||||
"monitor.diskCapacityUnknown": "Capacity unavailable",
|
||||
"monitor.rootDisk": "Root partition",
|
||||
"monitor.network": "Network",
|
||||
"monitor.download": "Download",
|
||||
"monitor.upload": "Upload",
|
||||
"monitor.updated": "Updated",
|
||||
"monitor.updatedAt": "Updated {time}",
|
||||
"monitor.noSamples": "Waiting for the first reading",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const esMonitorMessages = {
|
||||
"monitor.actions": "Acciones del panel",
|
||||
"monitor.unavailable": "La monitorización no está disponible. Iníciala de nuevo cuando se restablezca la conexión.",
|
||||
"monitor.username": "Usuario",
|
||||
"monitor.password": "Contraseña (solo esta ejecución)",
|
||||
@@ -43,6 +44,8 @@ export const esMonitorMessages = {
|
||||
"monitor.diskCapacityUnknown": "Capacidad no disponible",
|
||||
"monitor.rootDisk": "Partición raíz",
|
||||
"monitor.network": "Red",
|
||||
"monitor.download": "Descarga",
|
||||
"monitor.upload": "Subida",
|
||||
"monitor.updated": "Actualizado",
|
||||
"monitor.updatedAt": "Actualizado: {time}",
|
||||
"monitor.noSamples": "Esperando la primera lectura",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const ruMonitorMessages = {
|
||||
"monitor.actions": "Действия панели",
|
||||
"monitor.unavailable": "Мониторинг недоступен. Запустите его снова после восстановления связи.",
|
||||
"monitor.username": "Пользователь",
|
||||
"monitor.password": "Пароль (только для этого запуска)",
|
||||
@@ -42,6 +43,8 @@ export const ruMonitorMessages = {
|
||||
"monitor.diskCapacityUnknown": "Данные об объёме недоступны",
|
||||
"monitor.rootDisk": "Корневой раздел",
|
||||
"monitor.network": "Сеть",
|
||||
"monitor.download": "Загрузка",
|
||||
"monitor.upload": "Отправка",
|
||||
"monitor.updated": "Обновлено",
|
||||
"monitor.updatedAt": "Обновлено: {time}",
|
||||
"monitor.noSamples": "Ожидание первого измерения",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const zhCNMonitorMessages = {
|
||||
"monitor.actions": "面板操作",
|
||||
"monitor.unavailable": "监控暂不可用,连接恢复后请重新开始。",
|
||||
"monitor.username": "用户名",
|
||||
"monitor.password": "密码(仅本次使用)",
|
||||
@@ -41,6 +42,8 @@ export const zhCNMonitorMessages = {
|
||||
"monitor.diskCapacityUnknown": "暂无容量读数",
|
||||
"monitor.rootDisk": "根分区",
|
||||
"monitor.network": "网络",
|
||||
"monitor.download": "下载",
|
||||
"monitor.upload": "上传",
|
||||
"monitor.updated": "更新时间",
|
||||
"monitor.updatedAt": "更新于 {time}",
|
||||
"monitor.noSamples": "等待第一次读数",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const zhTWMonitorMessages = {
|
||||
"monitor.actions": "面板操作",
|
||||
"monitor.unavailable": "監控暫時無法使用,連線恢復後請重新開始。",
|
||||
"monitor.username": "使用者名稱",
|
||||
"monitor.password": "密碼(僅本次使用)",
|
||||
@@ -41,6 +42,8 @@ export const zhTWMonitorMessages = {
|
||||
"monitor.diskCapacityUnknown": "暫無容量讀數",
|
||||
"monitor.rootDisk": "根分割區",
|
||||
"monitor.network": "網路",
|
||||
"monitor.download": "下載",
|
||||
"monitor.upload": "上傳",
|
||||
"monitor.updated": "更新時間",
|
||||
"monitor.updatedAt": "更新於 {time}",
|
||||
"monitor.noSamples": "等待第一次讀數",
|
||||
|
||||
@@ -429,7 +429,7 @@ export const SelectHostPanelContent: React.FC<SelectHostPanelContentProps> = ({
|
||||
data-active={isActive ? 'true' : undefined}
|
||||
className={cn(
|
||||
'flex h-full min-h-0 items-center gap-2.5 overflow-hidden rounded-lg px-2.5 transition-colors',
|
||||
isActive ? 'bg-primary/10 ring-1 ring-primary/40' : 'hover:bg-muted/70',
|
||||
isActive ? 'bg-primary/10' : 'hover:bg-muted/70',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (navIndex >= 0) setActiveNavIndex(navIndex);
|
||||
@@ -487,9 +487,7 @@ export const SelectHostPanelContent: React.FC<SelectHostPanelContentProps> = ({
|
||||
aria-label={t('selectHost.toggleHost', { name: host.label })}
|
||||
className={cn(
|
||||
'flex h-full min-h-0 cursor-pointer items-center gap-2.5 overflow-hidden rounded-lg px-2.5 transition-colors',
|
||||
isSelected ? 'bg-muted' : isActive ? 'bg-primary/10' : 'hover:bg-muted/70',
|
||||
// Keep keyboard cursor visible even when the host is already selected.
|
||||
isActive && 'ring-1 ring-primary/40',
|
||||
isActive ? 'bg-primary/10' : isSelected ? 'bg-muted' : 'hover:bg-muted/70',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (navIndex >= 0) setActiveNavIndex(navIndex);
|
||||
|
||||
@@ -34,13 +34,9 @@ test('SelectHostPanelContent virtualizes with listbox keyboard model', () => {
|
||||
assert.match(source, /navigableIndexByKey/);
|
||||
assert.match(source, /navigableIndexByKey\.get\(row\.key\)/);
|
||||
assert.doesNotMatch(source, /navigable\.findIndex/);
|
||||
// Selected hosts must still show the keyboard active ring.
|
||||
assert.match(source, /isSelected \? 'bg-muted' : isActive \? 'bg-primary\/10' : 'hover:bg-muted\/70'/);
|
||||
assert.match(source, /isActive && 'ring-1 ring-primary\/40'/);
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/isSelected \? 'bg-muted' : isActive \? 'bg-primary\/10 ring-1 ring-primary\/40'/,
|
||||
);
|
||||
// The keyboard cursor stays visible on selected hosts through background alone.
|
||||
assert.match(source, /isActive \? 'bg-primary\/10' : isSelected \? 'bg-muted' : 'hover:bg-muted\/70'/);
|
||||
assert.doesNotMatch(source, /ring-1 ring-primary\/40/);
|
||||
assert.doesNotMatch(source, /filteredHosts\.map\(\(host\) =>/);
|
||||
// One listbox tab stop — not per-host tabIndex under virtualization.
|
||||
assert.match(source, /role="listbox"[\s\S]*tabIndex=\{0\}/);
|
||||
|
||||
@@ -173,16 +173,18 @@ export const AsidePanelFooter: React.FC<{ children: ReactNode; className?: strin
|
||||
// Action Menu Component (for the ... button)
|
||||
interface AsideActionMenuProps {
|
||||
children: ReactNode;
|
||||
triggerClassName?: string;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export const AsideActionMenu: React.FC<AsideActionMenuProps> = ({ children }) => {
|
||||
export const AsideActionMenu: React.FC<AsideActionMenuProps> = ({ children, triggerClassName, ariaLabel }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const close = useCallback(() => setOpen(false), []);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="p-1.5 hover:bg-muted rounded-md transition-colors cursor-pointer">
|
||||
<button aria-label={ariaLabel} className={cn("p-1.5 hover:bg-muted rounded-md transition-colors cursor-pointer", triggerClassName)}>
|
||||
<MoreVertical size={18} />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { Gauge } from "lucide-react";
|
||||
import { useI18n } from "../../application/i18n/I18nProvider";
|
||||
import { useMonitorSnapshot } from "../../application/state/monitorRuntimeStore";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { RippleButton } from "../ui/ripple";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||
@@ -15,13 +14,13 @@ export function MonitorNavButton({
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { count } = useMonitorSnapshot();
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<RippleButton
|
||||
variant={active ? "secondary" : "ghost"}
|
||||
onClick={onClick}
|
||||
aria-label={t("monitor.title")}
|
||||
className={cn(
|
||||
"w-full h-10",
|
||||
collapsed ? "justify-center p-0" : "justify-start gap-3",
|
||||
@@ -30,19 +29,7 @@ export function MonitorNavButton({
|
||||
)}
|
||||
>
|
||||
<Gauge size={16} className="shrink-0" />
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span>{t("monitor.title")}</span>
|
||||
{count > 0 && (
|
||||
<span className="ml-auto text-xs text-primary tabular-nums">
|
||||
{count}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{collapsed && count > 0 && (
|
||||
<span className="text-[10px] text-primary">{count}</span>
|
||||
)}
|
||||
{!collapsed && <span>{t("monitor.title")}</span>}
|
||||
</RippleButton>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">{t("monitor.title")}</TooltipContent>
|
||||
|
||||
@@ -158,7 +158,11 @@ for (const view of ["list", "charts"] as const) {
|
||||
assert.ok(icon);
|
||||
assert.equal((icon.parentElement as HTMLElement).style.backgroundColor, "rgb(147, 51, 234)");
|
||||
assert.ok(document.querySelector('img[src="/distro/ubuntu.svg"]'));
|
||||
assert.ok(document.querySelector(".lucide-gauge"));
|
||||
assert.equal(document.querySelector("header .lucide-gauge"), null);
|
||||
assert.equal(document.querySelector(".lucide-activity"), null);
|
||||
if (view === "list") {
|
||||
const network = document.querySelector("tbody tr")!.children[5];
|
||||
assert.deepEqual([...network.querySelectorAll(".sr-only")].map((e) => e.textContent), ["Download: ", "Upload: "]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -42,7 +42,6 @@ import { Input } from "../ui/input";
|
||||
import { Label } from "../ui/label";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Combobox } from "../ui/combobox";
|
||||
import { ScrollArea } from "../ui/scroll-area";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -69,7 +68,7 @@ import SelectHostPanel from "../SelectHostPanel";
|
||||
import { DistroAvatar } from "../DistroAvatar";
|
||||
import { ResourceBar } from "../systemManager/ResourceBar";
|
||||
import { MonitorDiskUsage } from "./MonitorDiskUsage";
|
||||
import { VaultHeaderSearch, VaultPageHeader } from "./VaultPageHeader";
|
||||
import { VaultHeaderSearch, VaultPageHeader, vaultHeaderIconButtonClass, vaultHeaderSecondaryButtonClass } from "./VaultPageHeader";
|
||||
import { toast } from "../ui/toast";
|
||||
|
||||
const emptyMonitorPoints: MonitorPoint[] = [];
|
||||
@@ -451,19 +450,87 @@ export function MonitorPanels({
|
||||
className="relative flex min-h-0 min-w-0 flex-1 overflow-hidden"
|
||||
data-section="vault-monitor"
|
||||
>
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
||||
<VaultPageHeader>
|
||||
<Gauge size={18} />
|
||||
<h1 className="font-semibold text-sm">{t("monitor.title")}</h1>
|
||||
<div className="flex-1" />
|
||||
{!!runtime.count && (
|
||||
<Button variant="ghost" size="sm" onClick={monitorActions.stopAll}>
|
||||
{t("monitor.stopAll")}
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col [container-type:inline-size] [container-name:monitor-main]">
|
||||
<VaultPageHeader contentClassName="gap-2 overflow-x-auto [scrollbar-width:none]">
|
||||
{panel && (
|
||||
<>
|
||||
<Combobox
|
||||
options={panels.map((p) => ({
|
||||
value: p.id,
|
||||
label: p.name,
|
||||
sublabel: p.description,
|
||||
}))}
|
||||
value={panel.id}
|
||||
onValueChange={(id) => {
|
||||
setSelected(id);
|
||||
setDetail(null);
|
||||
setEditor(null);
|
||||
}}
|
||||
triggerClassName="w-56 min-w-24 shrink"
|
||||
clearable={false}
|
||||
ariaLabel={t("monitor.choosePanel")}
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(vaultHeaderIconButtonClass, "shrink-0")}
|
||||
aria-label={t("monitor.edit")}
|
||||
onClick={() => startEdit(panel)}
|
||||
>
|
||||
<Pencil size={15} />
|
||||
</Button>
|
||||
<AsideActionMenu triggerClassName={cn(vaultHeaderIconButtonClass, "shrink-0 inline-flex items-center justify-center")} ariaLabel={t("monitor.actions")}>
|
||||
<AsideActionMenuItem
|
||||
onClick={() => {
|
||||
void duplicate();
|
||||
}}
|
||||
>
|
||||
<Copy size={14} className="mr-2" />
|
||||
{t("monitor.copy")}
|
||||
</AsideActionMenuItem>
|
||||
<AsideActionMenuItem
|
||||
variant="destructive"
|
||||
onClick={() => setDeleting(panel)}
|
||||
>
|
||||
<Trash2 size={14} className="mr-2" />
|
||||
{t("monitor.delete")}
|
||||
</AsideActionMenuItem>
|
||||
{!!runtime.count && (
|
||||
<AsideActionMenuItem onClick={monitorActions.stopAll}>
|
||||
<Square size={14} className="mr-2" />
|
||||
{t("monitor.stopAll")}
|
||||
</AsideActionMenuItem>
|
||||
)}
|
||||
</AsideActionMenu>
|
||||
</>
|
||||
)}
|
||||
<div className="min-w-0 flex-1" />
|
||||
{panel && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className={cn(vaultHeaderSecondaryButtonClass, "shrink-0")}
|
||||
aria-label={t(running ? "monitor.stop" : "monitor.start")}
|
||||
title={t(running ? "monitor.stop" : "monitor.start")}
|
||||
onClick={() =>
|
||||
running
|
||||
? monitorActions.stop(panel.id)
|
||||
: monitorActions.start(panel.id)
|
||||
}
|
||||
>
|
||||
{running ? <Square size={13} /> : <Play size={13} />}
|
||||
<span className="monitor-header-action-label">{t(running ? "monitor.stop" : "monitor.start")}</span>
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" className="gap-1.5" onClick={() => startEdit(null)}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={cn(vaultHeaderSecondaryButtonClass, "shrink-0")}
|
||||
aria-label={t("monitor.create")}
|
||||
title={t("monitor.create")}
|
||||
onClick={() => startEdit(null)}
|
||||
>
|
||||
<Plus size={14} />
|
||||
{t("monitor.create")}
|
||||
<span className="monitor-header-action-label">{t("monitor.create")}</span>
|
||||
</Button>
|
||||
</VaultPageHeader>
|
||||
{runtime.unavailable && (
|
||||
@@ -487,63 +554,6 @@ export function MonitorPanels({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-wrap items-center gap-2 p-4 pb-2">
|
||||
<Combobox
|
||||
options={panels.map((p) => ({
|
||||
value: p.id,
|
||||
label: p.name,
|
||||
sublabel: p.description,
|
||||
}))}
|
||||
value={panel.id}
|
||||
onValueChange={(id) => {
|
||||
setSelected(id);
|
||||
setDetail(null);
|
||||
setEditor(null);
|
||||
}}
|
||||
triggerClassName="w-64 max-w-full shrink-0"
|
||||
clearable={false}
|
||||
ariaLabel={t("monitor.choosePanel")}
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={t("monitor.edit")}
|
||||
onClick={() => startEdit(panel)}
|
||||
>
|
||||
<Pencil size={15} />
|
||||
</Button>
|
||||
<AsideActionMenu>
|
||||
<AsideActionMenuItem
|
||||
onClick={() => {
|
||||
void duplicate();
|
||||
}}
|
||||
>
|
||||
<Copy size={14} className="mr-2" />
|
||||
{t("monitor.copy")}
|
||||
</AsideActionMenuItem>
|
||||
<AsideActionMenuItem
|
||||
variant="destructive"
|
||||
onClick={() => setDeleting(panel)}
|
||||
>
|
||||
<Trash2 size={14} className="mr-2" />
|
||||
{t("monitor.delete")}
|
||||
</AsideActionMenuItem>
|
||||
</AsideActionMenu>
|
||||
<div className="flex-1" />
|
||||
<Button
|
||||
variant={running ? "secondary" : "default"}
|
||||
size="sm"
|
||||
className="gap-1.5"
|
||||
onClick={() =>
|
||||
running
|
||||
? monitorActions.stop(panel.id)
|
||||
: monitorActions.start(panel.id)
|
||||
}
|
||||
>
|
||||
{running ? <Square size={13} /> : <Play size={13} />}
|
||||
{t(running ? "monitor.stop" : "monitor.start")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2 px-4 py-2">
|
||||
<div className="flex h-8 rounded-md bg-muted/50 p-0.5">
|
||||
{(["list", "charts"] as const).map((view) => (
|
||||
@@ -637,7 +647,7 @@ export function MonitorPanels({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<ScrollArea className="min-h-0 min-w-0 flex-1 [&>[data-radix-scroll-area-viewport]>div]:!block [&>[data-radix-scroll-area-viewport]>div]:!min-w-0">
|
||||
<div className="min-h-0 min-w-0 flex-1 overflow-auto" data-section="vault-monitor-list">
|
||||
<div className="p-4">
|
||||
{!rows.length ? (
|
||||
<p className="p-8 text-center text-sm text-muted-foreground">
|
||||
@@ -650,8 +660,8 @@ export function MonitorPanels({
|
||||
key={id}
|
||||
{...dragProps(id)}
|
||||
className={cn(
|
||||
"flex h-full min-w-0 cursor-grab flex-col overflow-hidden rounded-xl bg-card/70 p-4 text-left shadow-sm shadow-black/[0.02] transition-colors hover:bg-card active:cursor-grabbing focus-visible:outline focus-visible:outline-primary",
|
||||
dragging?.panelId === panel.id && dragging.hostId === id && "opacity-40",
|
||||
"flex h-full min-w-0 cursor-default flex-col overflow-hidden rounded-xl bg-card/70 p-4 text-left shadow-sm shadow-black/[0.02] transition-colors hover:bg-card focus-visible:outline focus-visible:outline-primary",
|
||||
dragging?.panelId === panel.id && dragging.hostId === id && "cursor-grabbing opacity-40",
|
||||
dropTarget === id && "bg-accent",
|
||||
status(host, state) !== "live" &&
|
||||
"text-muted-foreground",
|
||||
@@ -747,8 +757,8 @@ export function MonitorPanels({
|
||||
key={id}
|
||||
{...dragProps(id)}
|
||||
className={cn(
|
||||
"cursor-grab [&>td]:bg-card/70 [&>td]:transition-colors hover:[&>td]:bg-card [&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
|
||||
dragging?.panelId === panel.id && dragging.hostId === id && "opacity-40",
|
||||
"cursor-default [&>td]:bg-card/70 [&>td]:transition-colors hover:[&>td]:bg-card [&>td:first-child]:rounded-l-xl [&>td:last-child]:rounded-r-xl",
|
||||
dragging?.panelId === panel.id && dragging.hostId === id && "cursor-grabbing opacity-40",
|
||||
dropTarget === id && "[&>td]:bg-accent",
|
||||
status(host, state) !== "live" &&
|
||||
"text-muted-foreground",
|
||||
@@ -757,7 +767,7 @@ export function MonitorPanels({
|
||||
<td className="px-3 py-3">
|
||||
<button
|
||||
draggable
|
||||
className="flex items-center gap-2.5 text-left hover:underline"
|
||||
className="flex cursor-inherit items-center gap-2.5 text-left hover:underline"
|
||||
onClick={() => {
|
||||
setEditor(null);
|
||||
setDetail(id);
|
||||
@@ -798,10 +808,16 @@ export function MonitorPanels({
|
||||
<MonitorDiskUsage stats={s} />
|
||||
</td>
|
||||
<td className="px-3 tabular-nums whitespace-nowrap">
|
||||
↓{" "}
|
||||
{rate(s?.networkReady ? s.netRxSpeed : null)}
|
||||
<br />↑{" "}
|
||||
{rate(s?.networkReady ? s.netTxSpeed : null)}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ArrowDown size={13} className="shrink-0 text-sky-600 dark:text-sky-400" />
|
||||
<span className="sr-only">{t("monitor.download")}: </span>
|
||||
{rate(s?.networkReady ? s.netRxSpeed : null)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<ArrowUp size={13} className="shrink-0 text-violet-600 dark:text-violet-400" />
|
||||
<span className="sr-only">{t("monitor.upload")}: </span>
|
||||
{rate(s?.networkReady ? s.netTxSpeed : null)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 whitespace-nowrap">
|
||||
{state?.at
|
||||
@@ -819,7 +835,7 @@ export function MonitorPanels({
|
||||
{t("monitor.memoryOnlyHint")}
|
||||
</p>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2130,3 +2130,8 @@ html[data-theme-transition] *::after {
|
||||
--terminal-host-tree-drop-bg: var(--nc-term-host-tree-drop-bg, transparent);
|
||||
--terminal-host-tree-folder-fg: var(--nc-term-host-tree-folder-fg, var(--foreground));
|
||||
}
|
||||
|
||||
/* Monitoring's single-row header follows the available pane width. */
|
||||
@container monitor-main (max-width: 760px) {
|
||||
.monitor-header-action-label { display: none; }
|
||||
}
|
||||
Reference in new issue
Block a user