diff --git a/netx_api/host_metrics.py b/netx_api/host_metrics.py
index 72d264b..df7026e 100644
--- a/netx_api/host_metrics.py
+++ b/netx_api/host_metrics.py
@@ -97,10 +97,11 @@ def _windows_cpu_utility_pdh(sample_sec: float = 0.25) -> float | None:
_PDH_COUNTER = h_counter
_PDH_PRIMED = False
+ # After prime, CollectQueryData alone is enough (matches Task Manager Utility).
if pdh.PdhCollectQueryData(_PDH_QUERY) != 0:
return _PDH_LAST
if not _PDH_PRIMED:
- time.sleep(max(0.05, float(sample_sec)))
+ time.sleep(max(0.05, min(0.15, float(sample_sec))))
if pdh.PdhCollectQueryData(_PDH_QUERY) != 0:
return _PDH_LAST
_PDH_PRIMED = True
diff --git a/web/src/App.tsx b/web/src/App.tsx
index ea4d1b5..b588064 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -1,33 +1,63 @@
+import { lazy, Suspense } from "react";
import { Navigate, Route, Routes, useLocation } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { AppLayout } from "./layout/AppLayout";
import { queryKeys } from "./constants/queryKeys";
import { WorkbenchPage } from "./pages/WorkbenchPage";
-import { CollectPage } from "./pages/CollectPage";
-import { ConfigSyncPage } from "./pages/ConfigSyncPage";
-import { NePage } from "./pages/NePage";
-import { UmePage } from "./pages/UmePage";
-import { WebcrtPage } from "./pages/WebcrtPage";
-import { TopologyPage } from "./pages/TopologyPage";
-import { NetworkLayout } from "./pages/network/NetworkLayout";
-import { NetworkDevicesPage } from "./pages/network/NetworkDevicesPage";
-import { NetworkAlarmsPage } from "./pages/network/NetworkAlarmsPage";
-import { NetworkConfigsPage } from "./pages/network/NetworkConfigsPage";
-import { LldpLinksPage } from "./pages/network/LldpLinksPage";
-import { TopologyClassifyPage } from "./pages/network/TopologyClassifyPage";
-import { PortTrafficPage } from "./pages/network/PortTrafficPage";
-import { PortTrafficBoardListPage } from "./pages/network/PortTrafficBoardListPage";
-import { PortTrafficWallPage } from "./pages/network/PortTrafficWallPage";
import { LoginPage } from "./pages/LoginPage";
-import { UsersPage } from "./pages/UsersPage";
-import { AuditLayout } from "./pages/audit/AuditLayout";
-import { TaskOverviewPage } from "./pages/audit/TaskOverviewPage";
-import { AuditPage } from "./pages/AuditPage";
-import { ApiTokensPage } from "./pages/ApiTokensPage";
import { ForceChangePasswordPage } from "./pages/ForceChangePasswordPage";
import { fetchIntegrationStatus } from "./services/api";
import { useAuth } from "./auth/AuthContext";
+const CollectPage = lazy(() => import("./pages/CollectPage").then((m) => ({ default: m.CollectPage })));
+const ConfigSyncPage = lazy(() =>
+ import("./pages/ConfigSyncPage").then((m) => ({ default: m.ConfigSyncPage })),
+);
+const NePage = lazy(() => import("./pages/NePage").then((m) => ({ default: m.NePage })));
+const UmePage = lazy(() => import("./pages/UmePage").then((m) => ({ default: m.UmePage })));
+const WebcrtPage = lazy(() => import("./pages/WebcrtPage").then((m) => ({ default: m.WebcrtPage })));
+const TopologyPage = lazy(() => import("./pages/TopologyPage").then((m) => ({ default: m.TopologyPage })));
+const NetworkLayout = lazy(() =>
+ import("./pages/network/NetworkLayout").then((m) => ({ default: m.NetworkLayout })),
+);
+const NetworkDevicesPage = lazy(() =>
+ import("./pages/network/NetworkDevicesPage").then((m) => ({ default: m.NetworkDevicesPage })),
+);
+const NetworkAlarmsPage = lazy(() =>
+ import("./pages/network/NetworkAlarmsPage").then((m) => ({ default: m.NetworkAlarmsPage })),
+);
+const NetworkConfigsPage = lazy(() =>
+ import("./pages/network/NetworkConfigsPage").then((m) => ({ default: m.NetworkConfigsPage })),
+);
+const LldpLinksPage = lazy(() =>
+ import("./pages/network/LldpLinksPage").then((m) => ({ default: m.LldpLinksPage })),
+);
+const TopologyClassifyPage = lazy(() =>
+ import("./pages/network/TopologyClassifyPage").then((m) => ({ default: m.TopologyClassifyPage })),
+);
+const PortTrafficPage = lazy(() =>
+ import("./pages/network/PortTrafficPage").then((m) => ({ default: m.PortTrafficPage })),
+);
+const PortTrafficBoardListPage = lazy(() =>
+ import("./pages/network/PortTrafficBoardListPage").then((m) => ({
+ default: m.PortTrafficBoardListPage,
+ })),
+);
+const PortTrafficWallPage = lazy(() =>
+ import("./pages/network/PortTrafficWallPage").then((m) => ({ default: m.PortTrafficWallPage })),
+);
+const UsersPage = lazy(() => import("./pages/UsersPage").then((m) => ({ default: m.UsersPage })));
+const AuditLayout = lazy(() =>
+ import("./pages/audit/AuditLayout").then((m) => ({ default: m.AuditLayout })),
+);
+const TaskOverviewPage = lazy(() =>
+ import("./pages/audit/TaskOverviewPage").then((m) => ({ default: m.TaskOverviewPage })),
+);
+const AuditPage = lazy(() => import("./pages/AuditPage").then((m) => ({ default: m.AuditPage })));
+const ApiTokensPage = lazy(() =>
+ import("./pages/ApiTokensPage").then((m) => ({ default: m.ApiTokensPage })),
+);
+
/** Preserve query when redirecting legacy /network/webcrt → /webcrt. */
function NetworkWebcrtRedirect() {
const { search } = useLocation();
@@ -44,13 +74,21 @@ function LegacyPortTrafficWallRedirect() {
return ;
}
+function PageFallback() {
+ return (
+
+ Loading…
+
+ );
+}
+
function ProtectedApp() {
const { ready, user } = useAuth();
const integrationsQuery = useQuery({
queryKey: queryKeys.integrationsStatus,
queryFn: fetchIntegrationStatus,
- refetchInterval: 5000,
- staleTime: 2000,
+ refetchInterval: 10_000,
+ staleTime: 5_000,
enabled: ready && Boolean(user) && !user?.must_change_password,
});
@@ -95,38 +133,40 @@ function ProtectedApp() {
: undefined,
}}
>
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- }>
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
- } />
- } />
- }>
- } />
- } />
- } />
-
- } />
- } />
-
+ }>
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ } />
+ } />
+ }>
+ } />
+ } />
+ } />
+
+ } />
+ } />
+
+
);
}
diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts
index 2d25b15..b0cd873 100644
--- a/web/src/i18n/en.ts
+++ b/web/src/i18n/en.ts
@@ -1464,6 +1464,9 @@ const en = {
fullscreen: "Fullscreen",
exitFullscreen: "Exit fullscreen",
display: "Display",
+ canvasBg: "Canvas background",
+ canvasBgColor: "Background color",
+ canvasBgReset: "Reset light blue",
hideIp: "Hide IP",
hideVendor: "Hide vendor",
hidePorts: "Hide ports",
diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts
index c2d37ed..0dd5e3f 100644
--- a/web/src/i18n/zh.ts
+++ b/web/src/i18n/zh.ts
@@ -1458,6 +1458,9 @@ const zh = {
fullscreen: "全屏显示",
exitFullscreen: "退出全屏",
display: "显示",
+ canvasBg: "画布背景",
+ canvasBgColor: "背景色",
+ canvasBgReset: "恢复浅蓝",
hideIp: "隐藏 IP",
hideVendor: "隐藏厂商",
hidePorts: "隐藏端口",
diff --git a/web/src/index.css b/web/src/index.css
index 0bd85fc..dcc6586 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -969,18 +969,19 @@ pre {
margin-top: 12px;
max-height: 240px;
overflow-y: auto;
- background: #f8fafc;
- border: 1px solid #e2e8f0;
+ background: rgba(2, 8, 23, 0.55);
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
padding: 8px 10px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
line-height: 1.45;
+ color: #cbd5e1;
}
.ws-log-line {
padding: 3px 0;
- border-bottom: 1px solid #e8edf3;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.12);
word-break: break-word;
}
@@ -989,15 +990,15 @@ pre {
}
.ws-log-line--error {
- color: #c62828;
+ color: #fca5a5;
}
.ws-log-line--warning {
- color: #e65100;
+ color: #fdba74;
}
.ws-log-line--info {
- color: #475569;
+ color: #94a3b8;
}
.ws-log-ts {
@@ -1011,27 +1012,27 @@ pre {
border-radius: 6px;
width: fit-content;
font-size: 12px;
- background: #f1f5f9;
- color: #334155;
- border: 1px solid #e2e8f0;
+ background: rgba(148, 163, 184, 0.12);
+ color: #cbd5e1;
+ border: 1px solid rgba(148, 163, 184, 0.22);
}
.pill--high {
- background: #ffebee;
- border-color: #ffcdd2;
- color: #b71c1c;
+ background: rgba(239, 68, 68, 0.16);
+ border-color: rgba(248, 113, 113, 0.35);
+ color: #fecaca;
}
.pill--medium {
- background: #fff8e1;
- border-color: #ffe082;
- color: #e65100;
+ background: rgba(245, 158, 11, 0.14);
+ border-color: rgba(251, 191, 36, 0.35);
+ color: #fde68a;
}
.pill--low {
- background: #e8f5e9;
- border-color: #c8e6c9;
- color: #2e7d32;
+ background: rgba(34, 197, 94, 0.14);
+ border-color: rgba(74, 222, 128, 0.3);
+ color: #bbf7d0;
}
.link-btn {
@@ -1066,36 +1067,38 @@ pre {
.app-main .conn-pill:not(.conn-pill--on-brand) {
font-size: 12px;
- border: 1px solid #cbd5e1;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 999px;
padding: 4px 10px;
- background: #f8fafc;
- color: #475569;
+ background: rgba(15, 23, 42, 0.75);
+ color: #cbd5e1;
}
.conn-pill {
font-size: 12px;
- border: 1px solid #cbd5e1;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 999px;
padding: 4px 10px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.75);
+ color: #cbd5e1;
}
.conn-pill--up {
- border-color: #a5d6a7;
- color: #2e7d32;
- background: #e8f5e9;
+ border-color: rgba(74, 222, 128, 0.4);
+ color: #86efac;
+ background: rgba(34, 197, 94, 0.14);
}
.conn-pill--down {
- border-color: #ef9a9a;
- color: #c62828;
- background: #ffebee;
+ border-color: rgba(248, 113, 113, 0.4);
+ color: #fca5a5;
+ background: rgba(239, 68, 68, 0.14);
}
.conn-pill--unknown {
- border-color: #cbd5e1;
- color: #64748b;
+ border-color: rgba(148, 163, 184, 0.3);
+ color: #94a3b8;
+ background: rgba(148, 163, 184, 0.1);
}
/* Brand header chips/buttons — must follow global button/conn-pill rules. */
@@ -1280,9 +1283,9 @@ pre {
}
.conn-pill--warn {
- border-color: #ffe082;
- color: #e65100;
- background: #fff8e1;
+ border-color: rgba(251, 191, 36, 0.4);
+ color: #fde68a;
+ background: rgba(245, 158, 11, 0.14);
}
.app-main .inline-spinner {
@@ -1453,7 +1456,7 @@ pre {
gap: 6px;
margin: 0;
font-size: 13px;
- color: #334155;
+ color: #cbd5e1;
white-space: nowrap;
cursor: pointer;
}
@@ -1471,7 +1474,7 @@ pre {
gap: 6px;
margin: 0;
font-size: 13px;
- color: #334155;
+ color: #cbd5e1;
white-space: nowrap;
}
@@ -1514,7 +1517,7 @@ pre {
.ne-stats-card__title {
font-size: 15px;
font-weight: 600;
- color: #1e293b;
+ color: #e2e8f0;
}
.ne-stats-pill {
@@ -1527,10 +1530,10 @@ pre {
font-weight: 500;
}
-.ne-stats-pill--pass { background: #dcfce7; color: #166534; }
-.ne-stats-pill--fail { background: #fee2e2; color: #991b1b; }
-.ne-stats-pill--testing{ background: #fef9c3; color: #854d0e; }
-.ne-stats-pill--unknown{ background: #f1f5f9; color: #475569; }
+.ne-stats-pill--pass { background: rgba(34, 197, 94, 0.16); color: #86efac; }
+.ne-stats-pill--fail { background: rgba(239, 68, 68, 0.16); color: #fca5a5; }
+.ne-stats-pill--testing{ background: rgba(245, 158, 11, 0.16); color: #fde68a; }
+.ne-stats-pill--unknown{ background: rgba(148, 163, 184, 0.14); color: #94a3b8; }
.ne-stats-card__actions {
display: flex;
@@ -1543,9 +1546,10 @@ pre {
height: 32px;
font-size: 13px;
padding: 0 8px;
- border: 1px solid #cbd5e1;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
+ background: #0b1220;
+ color: #e2e8f0;
min-width: 150px;
}
@@ -1563,16 +1567,16 @@ pre {
}
.ne-tag-card {
- border: 1px solid #cdd6e2;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 8px;
- background: #eef2f7;
+ background: rgba(15, 23, 42, 0.72);
padding: 10px 12px;
}
.ne-tag-card__title {
font-size: 13px;
font-weight: 650;
- color: var(--nm-brand, #0f1b2d);
+ color: #e2e8f0;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
@@ -1581,7 +1585,7 @@ pre {
.ne-tag-card__total {
font-size: 12px;
- color: #5b6b7c;
+ color: #94a3b8;
margin-bottom: 8px;
}
@@ -1674,9 +1678,9 @@ pre {
align-items: center;
margin-bottom: 0;
padding: 10px 12px;
- border: 1px solid #cdd6e2;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
- background: #eef2f7;
+ background: rgba(15, 23, 42, 0.55);
grid-template-columns: none;
}
@@ -1692,9 +1696,10 @@ pre {
max-width: 280px;
height: 32px;
margin: 0;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
+ background: #0b1220;
+ color: #e2e8f0;
padding: 0 10px;
font-size: 13px;
}
@@ -1715,14 +1720,14 @@ pre {
.ne-page .pt-list-table-wrap,
.ume-page .pt-list-table-wrap,
.system-page .pt-list-table-wrap {
- border-color: #cdd6e2;
+ border-color: rgba(148, 163, 184, 0.2);
}
.ne-page .pt-list-table thead th,
.ume-page .pt-list-table thead th,
.system-page .pt-list-table thead th {
- background: #eef2f7;
- color: var(--nm-muted);
+ background: rgba(15, 23, 42, 0.9);
+ color: #94a3b8;
}
.ne-page .pager.pt-list-pager,
@@ -1918,7 +1923,7 @@ pre {
.token-scopes-modal__backdrop {
position: absolute;
inset: 0;
- background: rgba(15, 27, 45, 0.45);
+ background: rgba(2, 8, 23, 0.72);
}
.token-scopes-modal__panel {
@@ -1931,9 +1936,10 @@ pre {
gap: 10px;
padding: 16px 18px;
border-radius: 10px;
- border: 1px solid #cdd6e2;
- background: #fff;
- box-shadow: 0 16px 40px rgba(15, 27, 45, 0.18);
+ border: 1px solid rgba(148, 163, 184, 0.22);
+ background: #0f172a;
+ color: #e2e8f0;
+ box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
}
.token-scopes-modal__head {
@@ -1947,7 +1953,7 @@ pre {
margin: 0;
font-size: 16px;
font-weight: 650;
- color: var(--nm-brand, #0f1b2d);
+ color: #f1f5f9;
}
.token-scopes-modal__list {
@@ -1955,13 +1961,13 @@ pre {
margin: 0;
padding: 0;
overflow: auto;
- border: 1px solid #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
- background: #f8fafc;
+ background: rgba(2, 8, 23, 0.45);
}
.token-scopes-modal__list > li + li {
- border-top: 1px solid #e2e8f0;
+ border-top: 1px solid rgba(148, 163, 184, 0.14);
}
.token-scopes-modal__row {
@@ -1972,11 +1978,11 @@ pre {
margin: 0;
cursor: pointer;
font-size: 13px;
- color: #334155;
+ color: #cbd5e1;
}
.token-scopes-modal__row:hover {
- background: #eef2f7;
+ background: rgba(30, 41, 59, 0.65);
}
.token-scopes-modal__row input {
@@ -2132,7 +2138,7 @@ pre {
position: fixed;
inset: 0;
z-index: 50;
- background: rgba(15, 23, 42, 0.45);
+ background: rgba(2, 8, 23, 0.72);
display: flex;
align-items: center;
justify-content: center;
@@ -2143,10 +2149,12 @@ pre {
width: min(720px, 100%);
max-height: 90vh;
overflow: auto;
- background: #fff;
+ background: #0f172a;
+ color: #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 10px;
padding: 20px;
- box-shadow: 0 16px 48px rgba(15, 23, 42, 0.2);
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
}
.modal--wide {
@@ -2155,7 +2163,7 @@ pre {
.connect-detail-summary {
margin-left: 8px;
- color: #64748b;
+ color: #94a3b8;
font-size: 13px;
}
@@ -2389,8 +2397,9 @@ pre {
padding: 1px 6px;
border-radius: 4px;
font-size: 11px;
- background: #e0f2fe;
- color: #0369a1;
+ background: rgba(56, 189, 248, 0.14);
+ color: #7dd3fc;
+ border: 1px solid rgba(56, 189, 248, 0.22);
}
.form-grid--single {
@@ -2625,7 +2634,7 @@ pre {
gap: 8px;
padding: 10px 12px;
background: transparent;
- border-bottom: 1px solid #d7dee8;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
flex-shrink: 0;
}
@@ -2642,8 +2651,8 @@ pre {
flex-direction: column;
gap: 8px;
padding: 8px 10px;
- background: linear-gradient(180deg, #f7f9fc 0%, #eef2f7 100%);
- border-bottom: 1px solid #d7dee8;
+ background: rgba(15, 23, 42, 0.55);
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
flex-shrink: 0;
}
@@ -2730,10 +2739,11 @@ pre {
z-index: 40;
min-width: 180px;
padding: 4px 0;
- background: #fff;
- border: 1px solid #c5d0de;
+ background: #0f172a;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 4px;
- box-shadow: 0 8px 20px rgba(15, 39, 68, 0.14);
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
+ color: #e2e8f0;
}
.webcrt-menubar__menu--end {
@@ -2747,7 +2757,7 @@ pre {
padding: 7px 14px;
border: 0;
background: transparent;
- color: #1e293b;
+ color: #e2e8f0;
font-size: 12px;
text-align: left;
cursor: pointer;
@@ -2756,8 +2766,8 @@ pre {
}
.webcrt-menubar__menu-item:hover {
- background: #e8f0fe;
- color: var(--nm-brand);
+ background: rgba(37, 99, 235, 0.2);
+ color: #f1f5f9;
}
.app-main .webcrt-menubar__menu-item {
@@ -2858,7 +2868,7 @@ pre {
align-items: center;
gap: 6px;
font-size: 12px;
- color: #334155;
+ color: #94a3b8;
padding-bottom: 8px;
white-space: nowrap;
}
@@ -2879,7 +2889,7 @@ pre {
padding: 0 8px;
font-size: 11px;
font-weight: 650;
- color: #64748b;
+ color: #94a3b8;
}
.webcrt-keyword-list {
@@ -2890,9 +2900,9 @@ pre {
min-height: 160px;
max-height: 220px;
overflow: auto;
- border: 1px solid #c5d0de;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #fff;
+ background: rgba(2, 8, 23, 0.55);
}
.webcrt-keyword-list__empty {
@@ -2909,9 +2919,9 @@ pre {
width: 100%;
padding: 7px 8px;
border: 0;
- border-bottom: 1px solid #eef2f7;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.12);
background: transparent;
- color: #0f172a;
+ color: #e2e8f0;
font-size: 12px;
text-align: left;
cursor: pointer;
@@ -2920,12 +2930,12 @@ pre {
}
.webcrt-keyword-list__row:hover {
- background: #f1f5f9;
+ background: rgba(30, 41, 59, 0.75);
}
.webcrt-keyword-list__row.is-selected {
- background: #e8f0fe;
- color: var(--nm-brand);
+ background: rgba(37, 99, 235, 0.22);
+ color: #93c5fd;
}
.app-main .webcrt-keyword-list__row {
@@ -2968,9 +2978,9 @@ pre {
width: 48px;
height: 32px;
padding: 2px;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
+ background: #0b1220;
cursor: pointer;
}
@@ -2989,10 +2999,10 @@ pre {
box-sizing: border-box;
height: 30px;
padding: 0 10px;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
- color: var(--nm-text);
+ background: #0b1220;
+ color: #e2e8f0;
font-size: 12px;
}
@@ -3010,8 +3020,8 @@ pre {
gap: 2px;
margin: 0;
padding: 2px;
- background: #d7dee8;
- border: 1px solid var(--nm-nav-border);
+ background: rgba(2, 8, 23, 0.55);
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
flex-shrink: 0;
}
@@ -3071,9 +3081,9 @@ pre {
.app-main .webcrt-source-tabs__btn.is-active,
.app-main .webcrt-sidebar__footer button.is-active {
- background: #fff;
- color: var(--nm-accent);
- box-shadow: inset 0 0 0 1px #b8c4d2;
+ background: rgba(30, 41, 59, 0.95);
+ color: #93c5fd;
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
}
.app-main .webcrt-tree__item {
@@ -3092,8 +3102,9 @@ pre {
}
.app-main .webcrt-action-btn {
- border: 1px solid #b8c4d2;
- background: #fff;
+ border: 1px solid rgba(148, 163, 184, 0.28);
+ background: #0b1220;
+ color: #e2e8f0;
height: 28px;
padding: 0 10px;
}
@@ -3103,9 +3114,9 @@ pre {
}
.webcrt-source-tabs__btn.is-active {
- background: #fff;
- color: var(--nm-accent);
- box-shadow: inset 0 0 0 1px #b8c4d2;
+ background: rgba(30, 41, 59, 0.95);
+ color: #93c5fd;
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
font-weight: 650;
}
@@ -3203,8 +3214,8 @@ pre {
justify-content: space-between;
gap: 6px;
padding: 8px 10px;
- border-top: 1px solid #d7dee8;
- background: #eef2f7;
+ border-top: 1px solid rgba(148, 163, 184, 0.18);
+ background: rgba(15, 23, 42, 0.55);
font-size: 11px;
color: var(--nm-muted);
flex-shrink: 0;
@@ -3223,10 +3234,10 @@ pre {
width: 26px;
height: 24px;
padding: 0;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
- color: var(--nm-text);
+ background: #0b1220;
+ color: #e2e8f0;
cursor: pointer;
line-height: 1;
font-size: 14px;
@@ -3243,9 +3254,9 @@ pre {
gap: 2px;
margin: 0 10px 10px;
padding: 2px;
- border: 1px solid var(--nm-nav-border);
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #d7dee8;
+ background: rgba(2, 8, 23, 0.55);
flex-shrink: 0;
}
@@ -3266,9 +3277,9 @@ pre {
}
.webcrt-sidebar__footer button.is-active {
- background: #fff;
- color: var(--nm-accent);
- box-shadow: inset 0 0 0 1px #b8c4d2;
+ background: rgba(30, 41, 59, 0.95);
+ color: #93c5fd;
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
font-weight: 650;
}
@@ -4244,8 +4255,8 @@ pre {
}
.topo-sidebar__rail--live.is-on {
- color: #1d4ed8;
- background: #eff6ff;
+ color: #93c5fd;
+ background: rgba(37, 99, 235, 0.2);
}
.topo-sidebar__rail-live-dot {
@@ -4258,7 +4269,7 @@ pre {
.topo-sidebar__rail--live.is-on .topo-sidebar__rail-live-dot {
opacity: 1;
- box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}
.topo-sidebar__rail .topo-sidebar__rail-icon {
@@ -4269,8 +4280,8 @@ pre {
}
.topo-sidebar__rail:hover .topo-sidebar__rail-icon {
- border-color: var(--nm-nav-border);
- background: #fff;
+ border-color: rgba(148, 163, 184, 0.28);
+ background: rgba(30, 41, 59, 0.85);
}
.topo-sidebar__rail:hover {
@@ -4492,9 +4503,9 @@ pre {
}
.topo-palette-source__btn.is-active {
- background: #fff;
- color: var(--nm-accent);
- box-shadow: inset 0 0 0 1px #b8c4d2;
+ background: rgba(30, 41, 59, 0.95);
+ color: #93c5fd;
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
}
.topo-page .topo-sidebar__section {
@@ -4508,9 +4519,9 @@ pre {
flex: 1;
min-height: 0;
overflow: auto;
- border: 1px solid #c8d2de;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #f7f9fc;
+ background: rgba(2, 8, 23, 0.45);
}
.topo-dir__list {
list-style: none;
@@ -4520,7 +4531,7 @@ pre {
.topo-dir__list--nested {
margin: 0 0 2px 10px;
padding: 2px 0 4px;
- border-left: 1px solid #d5dde8;
+ border-left: 1px solid rgba(148, 163, 184, 0.18);
}
.topo-dir__block {
list-style: none;
@@ -4528,7 +4539,7 @@ pre {
.topo-dir__block--sep {
margin-top: 4px;
padding-top: 4px;
- border-top: 1px solid #d5dde8;
+ border-top: 1px solid rgba(148, 163, 184, 0.18);
}
.topo-dir__row {
display: flex;
@@ -4540,15 +4551,15 @@ pre {
border-radius: 4px;
}
.topo-dir__row:hover {
- background: rgba(255, 255, 255, 0.72);
+ background: rgba(30, 41, 59, 0.65);
}
.topo-dir__row:last-child {
border-bottom: 0;
}
.topo-dir__row--root {
min-height: 32px;
- background: #eef3f9;
- border-bottom: 1px solid #dbe3ee;
+ background: rgba(30, 41, 59, 0.55);
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
position: sticky;
top: 0;
z-index: 1;
@@ -4557,8 +4568,8 @@ pre {
min-height: 26px;
}
.topo-dir__row.is-active {
- background: #fff;
- box-shadow: inset 0 0 0 1px #b7c9e2;
+ background: rgba(37, 99, 235, 0.22);
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
}
.topo-dir__row.is-active .topo-dir__name {
font-weight: 600;
@@ -4728,9 +4739,9 @@ pre {
margin: 0;
border: 0;
border-radius: 0;
- background:
- linear-gradient(180deg, #f4f7fb 0%, #eef2f7 100%);
+ background: rgba(7, 11, 20, 0.92);
overflow: hidden;
+ color: #e2e8f0;
}
.topo-browser__head {
display: flex;
@@ -4738,18 +4749,18 @@ pre {
justify-content: space-between;
gap: 12px;
padding: 14px 18px 10px;
- border-bottom: 1px solid #dbe3ee;
- background: #fff;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
+ background: rgba(15, 23, 42, 0.85);
}
.topo-browser__head strong {
font-size: 15px;
font-weight: 650;
- color: var(--nm-brand);
+ color: #f1f5f9;
}
.topo-browser__sub {
margin: 4px 0 0;
font-size: 12px;
- color: #64748b;
+ color: #94a3b8;
}
.topo-browser__actions {
display: flex;
@@ -4770,7 +4781,7 @@ pre {
justify-content: center;
gap: 10px;
padding: 48px 20px;
- color: #64748b;
+ color: #94a3b8;
font-size: 13px;
}
.topo-browser__empty p {
@@ -4782,10 +4793,10 @@ pre {
width: 64px;
height: 64px;
border-radius: 14px;
- background: #fff;
- border: 1px solid #d5dde8;
- color: #0f766e;
- box-shadow: 0 1px 2px rgba(15, 39, 68, 0.05);
+ background: rgba(15, 23, 42, 0.85);
+ border: 1px solid rgba(148, 163, 184, 0.22);
+ color: #5eead4;
+ box-shadow: none;
}
.topo-browser__grid {
flex: 1;
@@ -4796,13 +4807,13 @@ pre {
gap: 14px;
align-content: start;
padding: 16px 18px 20px;
- background: #eef2f7;
+ background: rgba(2, 8, 23, 0.35);
}
.topo-browser__grid--regions {
grid-template-columns: repeat(auto-fill, minmax(148px, 160px));
gap: 20px 22px;
padding: 24px 22px 32px;
- background: #e8eef6;
+ background: rgba(2, 8, 23, 0.45);
justify-content: start;
}
/* Beat .app-main button { background:#fff; color:#1f2937; border } */
@@ -4874,13 +4885,13 @@ pre {
}
.app-main .topo-region-hex--add,
.topo-region-hex--add {
- background: linear-gradient(160deg, #f8fafc 0%, #dbe7f5 100%) !important;
- color: #1a56a8 !important;
+ background: linear-gradient(160deg, #1e293b 0%, #0f172a 100%) !important;
+ color: #93c5fd !important;
}
.app-main .topo-region-hex--add:hover,
.topo-region-hex--add:hover {
- background: linear-gradient(160deg, #ffffff 0%, #cfe0f4 100%) !important;
- color: #0f2744 !important;
+ background: linear-gradient(160deg, #334155 0%, #1e293b 100%) !important;
+ color: #bfdbfe !important;
}
.app-main .topo-region-hex--add:disabled,
.topo-region-hex--add:disabled {
@@ -4951,21 +4962,22 @@ pre {
display: flex;
flex-direction: column;
min-height: 168px;
- border: 1px solid #cfd8e6;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 10px;
- background: #fff;
- box-shadow: 0 1px 2px rgba(15, 39, 68, 0.05);
+ background: rgba(15, 23, 42, 0.85);
+ box-shadow: none;
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
+ color: #e2e8f0;
}
.topo-map-card:hover {
- border-color: #9eb4d0;
- box-shadow: 0 4px 14px rgba(15, 39, 68, 0.08);
+ border-color: rgba(59, 130, 246, 0.45);
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
transform: translateY(-1px);
}
.topo-map-card.is-physical {
- border-color: #f0c48a;
- background: linear-gradient(180deg, #fffdf8 0%, #fff 42%);
+ border-color: rgba(245, 158, 11, 0.4);
+ background: linear-gradient(180deg, rgba(69, 26, 3, 0.35) 0%, rgba(15, 23, 42, 0.9) 42%);
}
.topo-map-card__body {
flex: 1;
@@ -4987,32 +4999,32 @@ pre {
border-radius: 10px;
display: grid;
place-items: center;
- background: #eef3f9;
- color: #1d4ed8;
- border: 1px solid #dbe3ee;
+ background: rgba(37, 99, 235, 0.18);
+ color: #93c5fd;
+ border: 1px solid rgba(59, 130, 246, 0.3);
}
.topo-map-card.is-physical .topo-map-card__icon {
- background: #fff7ed;
- color: #b45309;
- border-color: #fed7aa;
+ background: rgba(245, 158, 11, 0.16);
+ color: #fbbf24;
+ border-color: rgba(245, 158, 11, 0.35);
}
.topo-map-card__kind {
font-size: 11px;
font-weight: 650;
letter-spacing: 0.02em;
- color: #1d4ed8;
- background: #e8eef8;
+ color: #93c5fd;
+ background: rgba(37, 99, 235, 0.18);
border-radius: 999px;
padding: 2px 8px;
}
.topo-map-card__kind.is-physical {
- color: #9a3412;
- background: #ffedd5;
+ color: #fdba74;
+ background: rgba(245, 158, 11, 0.16);
}
.topo-map-card__name {
font-size: 14px;
font-weight: 650;
- color: var(--nm-brand);
+ color: #f1f5f9;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -5126,13 +5138,14 @@ pre {
max-width: 220px;
padding: 12px 14px;
border-radius: 14px;
- border: 1px solid #c9d4e2;
- background: linear-gradient(165deg, #ffffff 0%, #f3f7fb 100%);
- box-shadow: 0 4px 14px rgba(15, 23, 42, 0.07);
+ border: 1px solid rgba(148, 163, 184, 0.28);
+ background: linear-gradient(165deg, #1e293b 0%, #0f172a 100%);
+ box-shadow: none;
cursor: pointer;
display: flex;
align-items: flex-start;
gap: 12px;
+ color: #e2e8f0;
}
.topo-nav-node.is-selected,
.topo-nav-node:hover {
@@ -5199,7 +5212,7 @@ pre {
.topo-modal__backdrop {
position: absolute;
inset: 0;
- background: rgba(15, 23, 42, 0.35);
+ background: rgba(2, 8, 23, 0.72);
}
.topo-modal__panel {
position: relative;
@@ -5211,8 +5224,10 @@ pre {
gap: 10px;
padding: 14px;
border-radius: 12px;
- background: #fff;
- box-shadow: 0 16px 48px rgba(15, 23, 42, 0.22);
+ background: #0f172a;
+ color: #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.22);
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
}
.topo-modal__head {
display: flex;
@@ -5248,7 +5263,7 @@ pre {
justify-content: flex-end;
gap: 8px;
padding-top: 6px;
- border-top: 1px solid #e2e8f0;
+ border-top: 1px solid rgba(148, 163, 184, 0.18);
}
.topo-palette__check {
display: inline-flex;
@@ -5256,13 +5271,13 @@ pre {
justify-content: center;
flex: 0 0 auto;
padding: 0 8px;
- border-right: 1px solid #cdd6e2;
+ border-right: 1px solid rgba(148, 163, 184, 0.22);
cursor: pointer;
}
.topo-palette__row.is-selected {
- border-color: var(--nm-accent, #2563eb);
- background: var(--nm-nav-active, #e8f0fb);
- box-shadow: inset 2px 0 0 var(--nm-accent, #2563eb);
+ border-color: rgba(59, 130, 246, 0.5);
+ background: rgba(37, 99, 235, 0.2);
+ box-shadow: inset 2px 0 0 #3b82f6;
}
.topo-map-list,
.topo-palette {
@@ -5298,16 +5313,16 @@ pre {
align-items: center;
margin: 0 0 12px;
padding: 10px;
- border: 1px solid #d5dde8;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.65);
}
.topo-classify__regex-input {
min-width: 220px;
flex: 1 1 220px;
}
.topo-classify__table tr.is-match {
- background: #eef6ff;
+ background: rgba(37, 99, 235, 0.18);
}
.topo-classify__row-actions {
display: inline-flex;
@@ -5397,9 +5412,9 @@ pre {
.topo-classify__preview {
margin: 10px 0 14px;
padding: 10px 12px;
- border: 1px solid #d5dde8;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.65);
}
.topo-classify__pager {
display: flex;
@@ -5412,7 +5427,7 @@ pre {
align-items: center;
gap: 6px;
font-size: 12px;
- color: #475569;
+ color: #94a3b8;
}
.topo-classify__slice-list,
.topo-classify__view-links {
@@ -5668,9 +5683,9 @@ pre {
align-items: stretch;
width: 100%;
box-sizing: border-box;
- border: 1px solid #cdd6e2;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
- background: #fff;
+ background: rgba(15, 23, 42, 0.75);
overflow: hidden;
min-width: 0;
}
@@ -5705,8 +5720,8 @@ pre {
}
.topo-palette > li.is-on-canvas .topo-palette__row {
- background: #eef2f7;
- border-color: #d7dee8;
+ background: rgba(30, 41, 59, 0.55);
+ border-color: rgba(148, 163, 184, 0.18);
}
.topo-map-list__item,
@@ -5831,11 +5846,12 @@ pre {
flex-direction: column;
min-width: 0;
min-height: 0;
- background: #fff;
- border: 1px solid var(--nm-panel-border, #c8d2de);
+ background: #070b14;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
overflow: hidden;
- box-shadow: 0 1px 2px rgba(15, 27, 45, 0.06);
+ box-shadow: none;
+ color: #e2e8f0;
}
.topo-toolbar {
@@ -5843,8 +5859,8 @@ pre {
flex-direction: column;
gap: 6px;
padding: 8px 10px;
- border-bottom: 1px solid #cdd6e2;
- background: #eef2f7;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
+ background: rgba(15, 23, 42, 0.9);
}
.topo-toolbar__row {
@@ -5860,7 +5876,7 @@ pre {
flex-wrap: wrap;
gap: 8px;
padding-top: 6px;
- border-top: 1px solid #d7dee8;
+ border-top: 1px solid rgba(148, 163, 184, 0.16);
}
.topo-toolbar__group + .topo-toolbar__group,
@@ -5869,7 +5885,7 @@ pre {
.topo-toolbar__panel + .topo-toolbar__group--find {
margin-left: 2px;
padding-left: 10px;
- border-left: 1px solid #cdd6e2;
+ border-left: 1px solid rgba(148, 163, 184, 0.2);
}
.topo-toolbar__panel {
@@ -5878,17 +5894,17 @@ pre {
gap: 6px;
flex-shrink: 0;
padding: 3px 6px;
- background: #d7dee8;
- border: 1px solid #c0cad6;
+ background: rgba(2, 8, 23, 0.55);
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
}
.topo-toolbar__panel .topo-tools {
- background: #cdd6e2;
+ background: rgba(15, 23, 42, 0.8);
}
.topo-toolbar__panel .topo-toolbar__display > summary {
- background: #fff;
+ background: #0b1220;
}
.topo-toolbar__group--find {
@@ -5909,10 +5925,11 @@ pre {
width: min(340px, 72vw);
max-height: 280px;
overflow: auto;
- background: #fff;
- border: 1px solid #c0cad6;
+ background: #0f172a;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 8px;
- box-shadow: 0 8px 20px rgba(15, 39, 68, 0.14);
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
+ color: #e2e8f0;
}
.topo-find-suggest__item {
@@ -5926,22 +5943,23 @@ pre {
background: transparent;
text-align: left;
cursor: pointer;
+ color: inherit;
}
.topo-find-suggest__item:hover,
.topo-find-suggest__item.is-active {
- background: #d6e4f5;
+ background: rgba(37, 99, 235, 0.22);
}
.topo-find-suggest__name {
font-size: 12px;
font-weight: 650;
- color: var(--nm-brand, #0f1b2d);
+ color: #f1f5f9;
}
.topo-find-suggest__meta {
font-size: 11px;
- color: #5b6b7c;
+ color: #94a3b8;
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
}
@@ -5949,7 +5967,7 @@ pre {
.topo-find-suggest__more {
padding: 8px 10px;
font-size: 12px;
- color: #5b6b7c;
+ color: #94a3b8;
}
.app-main .topo-toolbar__find,
@@ -6027,8 +6045,8 @@ pre {
display: inline-flex;
gap: 2px;
padding: 2px;
- background: #d7dee8;
- border: 1px solid #c0cad6;
+ background: rgba(2, 8, 23, 0.55);
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 6px;
flex-shrink: 0;
}
@@ -6039,7 +6057,7 @@ pre {
gap: 4px;
border: 0;
background: transparent;
- color: var(--nm-muted, #5b6b7c);
+ color: #94a3b8;
font-size: 12px;
font-weight: 600;
padding: 4px 8px;
@@ -6049,8 +6067,8 @@ pre {
}
.topo-tools__btn:hover {
- background: #fff;
- color: var(--nm-brand, #0f1b2d);
+ background: rgba(30, 41, 59, 0.9);
+ color: #f1f5f9;
}
.topo-tools__btn.is-active {
@@ -6079,10 +6097,10 @@ pre {
min-width: 88px;
height: 30px;
box-sizing: border-box;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
- color: var(--nm-brand, #0f1b2d);
+ background: #0b1220;
+ color: #e2e8f0;
font-size: 12px;
line-height: 1.2;
padding: 0 8px;
@@ -6096,10 +6114,10 @@ pre {
user-select: none;
height: 30px;
padding: 0 10px;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
- color: var(--nm-brand, #0f1b2d);
+ background: #0b1220;
+ color: #e2e8f0;
font-size: 12px;
font-weight: 600;
line-height: 1.2;
@@ -6174,7 +6192,7 @@ pre {
gap: 8px;
margin-top: 6px;
padding-top: 8px;
- border-top: 1px solid #e2e8f0;
+ border-top: 1px solid rgba(148, 163, 184, 0.18);
}
.topo-display-defaults__head {
@@ -6183,7 +6201,7 @@ pre {
justify-content: space-between;
gap: 8px;
font-size: 12px;
- color: #334155;
+ color: #cbd5e1;
}
.topo-display-defaults__row {
@@ -6193,18 +6211,27 @@ pre {
gap: 6px;
}
+.topo-display-defaults--canvas-bg .topo-display-defaults__row {
+ grid-template-columns: minmax(52px, auto) 28px minmax(88px, 1fr);
+}
+
+.topo-canvas-bg-hex {
+ min-width: 88px !important;
+ font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
+}
+
.topo-display-defaults__name {
font-size: 12px;
- color: #475569;
+ color: #94a3b8;
}
.topo-display-defaults__row input[type="color"] {
width: 28px;
height: 28px;
padding: 0;
- border: 1px solid #cbd5e1;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
+ background: #0b1220;
cursor: pointer;
}
@@ -6838,14 +6865,14 @@ pre {
position: relative;
flex: 1;
min-height: 0;
- background: #f4f7fb;
+ background: var(--topo-canvas-bg, #dbeafe);
}
.topo-canvas:fullscreen,
.topo-canvas.is-fullscreen:fullscreen {
width: 100vw;
height: 100vh;
- background: #f4f7fb;
+ background: var(--topo-canvas-bg, #dbeafe);
}
/* Place fullscreen above zoom-in (+); Controls renders custom children last. */
@@ -6925,10 +6952,10 @@ pre {
height: 32px;
margin: 0;
padding: 0 10px;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
- color: var(--nm-text);
+ background: #0b1220;
+ color: #e2e8f0;
font-size: 12px;
}
@@ -6944,9 +6971,9 @@ pre {
padding: 0 10px;
line-height: 1;
border-radius: 6px;
- border-color: #b8c4d2;
- background: #fff;
- color: var(--nm-brand);
+ border-color: rgba(148, 163, 184, 0.28);
+ background: #0b1220;
+ color: #e2e8f0;
font-size: 12px;
}
@@ -7885,9 +7912,9 @@ pre {
align-items: center;
margin-bottom: 0;
padding: 10px 12px;
- border: 1px solid #cdd6e2;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
- background: #eef2f7;
+ background: rgba(15, 23, 42, 0.55);
grid-template-columns: none;
}
@@ -7899,9 +7926,10 @@ pre {
max-width: 280px;
height: 32px;
margin: 0;
- border: 1px solid #b8c4d2;
+ border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: 6px;
- background: #fff;
+ background: #0b1220;
+ color: #e2e8f0;
padding: 0 10px;
font-size: 13px;
}
@@ -7916,12 +7944,12 @@ pre {
}
.network-main .pt-list-table-wrap {
- border-color: #cdd6e2;
+ border-color: rgba(148, 163, 184, 0.2);
}
.network-main .pt-list-table thead th {
- background: #eef2f7;
- color: var(--nm-muted);
+ background: rgba(15, 23, 42, 0.9);
+ color: #94a3b8;
}
.network-main .pager.pt-list-pager,
@@ -7958,36 +7986,36 @@ pre {
.pt-list-status--ok,
.pt-list-status--up,
.pt-list-status--connected {
- background: #dcfce7;
- color: #166534;
+ background: rgba(34, 197, 94, 0.16);
+ color: #86efac;
}
.pt-list-status--down,
.pt-list-status--failed,
.pt-list-status--critical {
- background: #fee2e2;
- color: #991b1b;
+ background: rgba(239, 68, 68, 0.16);
+ color: #fca5a5;
}
.pt-list-status--major {
- background: #ffedd5;
- color: #9a3412;
+ background: rgba(249, 115, 22, 0.16);
+ color: #fdba74;
}
.pt-list-status--minor,
.pt-list-status--warning {
- background: #fef9c3;
- color: #854d0e;
+ background: rgba(245, 158, 11, 0.16);
+ color: #fde68a;
}
.pt-list-status--info {
- background: #e0f2fe;
- color: #075985;
+ background: rgba(56, 189, 248, 0.14);
+ color: #7dd3fc;
}
.pt-list-status--unknown {
- background: #f1f5f9;
- color: #475569;
+ background: rgba(148, 163, 184, 0.14);
+ color: #94a3b8;
}
.network-main .nm-config-view {
@@ -8122,19 +8150,19 @@ pre {
.pt-list-kpi {
padding: 12px 14px;
- border: 1px solid #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 8px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.72);
}
.pt-list-kpi--live {
- background: #eff6ff;
- border-color: #bfdbfe;
+ background: rgba(37, 99, 235, 0.18);
+ border-color: rgba(59, 130, 246, 0.4);
}
.pt-list-kpi__label {
font-size: 12px;
- color: #64748b;
+ color: #94a3b8;
margin-bottom: 4px;
}
@@ -8142,16 +8170,16 @@ pre {
font-size: 22px;
font-weight: 650;
font-variant-numeric: tabular-nums;
- color: #0f172a;
+ color: #f1f5f9;
line-height: 1.2;
}
.pt-list-kpi--live .pt-list-kpi__value {
- color: #1d4ed8;
+ color: #93c5fd;
}
.pt-list-table-wrap {
- border: 1px solid #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
overflow: auto;
}
@@ -8161,10 +8189,10 @@ pre {
}
.pt-list-table thead th {
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.9);
font-size: 12px;
font-weight: 600;
- color: #64748b;
+ color: #94a3b8;
white-space: nowrap;
}
@@ -8173,12 +8201,12 @@ pre {
}
.pt-list-table tbody tr:hover td {
- background: #f8fafc;
+ background: rgba(30, 41, 59, 0.55);
}
.pt-list-task-name {
font-weight: 600;
- color: #0f172a;
+ color: #e2e8f0;
}
.pt-list-task-err {
@@ -8232,20 +8260,21 @@ pre {
}
.pt-log-item {
- border: 1px solid #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 8px;
padding: 10px 12px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.72);
+ color: #e2e8f0;
}
.pt-log-item--error {
- border-color: #fecaca;
- background: #fef2f2;
+ border-color: rgba(248, 113, 113, 0.35);
+ background: rgba(239, 68, 68, 0.12);
}
.pt-log-item--warn {
- border-color: #fde68a;
- background: #fffbeb;
+ border-color: rgba(251, 191, 36, 0.35);
+ background: rgba(245, 158, 11, 0.12);
}
.pt-log-item__meta {
@@ -8255,41 +8284,40 @@ pre {
gap: 8px 12px;
margin-bottom: 6px;
font-size: 12px;
- color: #64748b;
+ color: #94a3b8;
}
.pt-log-item__level {
text-transform: uppercase;
letter-spacing: 0.04em;
font-weight: 700;
- color: #b91c1c;
+ color: #fca5a5;
}
.pt-log-item--warn .pt-log-item__level {
- color: #b45309;
+ color: #fde68a;
}
.pt-log-item--info .pt-log-item__level {
- color: #0369a1;
+ color: #7dd3fc;
}
.pt-log-item__if {
font-family: "IBM Plex Mono", "Cascadia Mono", Consolas, monospace;
- color: #0f172a;
+ color: #cbd5e1;
}
.pt-log-item__msg {
margin: 0;
- /* Override global `pre` dark terminal chrome — logs sit on light cards. */
- background: #f1f5f9;
- border: 1px solid #e2e8f0;
+ background: rgba(2, 8, 23, 0.55);
+ border: 1px solid rgba(148, 163, 184, 0.2);
border-radius: 6px;
padding: 8px 10px;
white-space: pre-wrap;
word-break: break-word;
font-size: 12px;
line-height: 1.45;
- color: #0f172a;
+ color: #e2e8f0;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
@@ -8298,9 +8326,10 @@ pre {
flex-direction: column;
gap: 4px;
padding: 10px 12px;
- border: 1px solid #e2e8f0;
+ border: 1px solid rgba(148, 163, 184, 0.22);
border-radius: 8px;
- background: #f8fafc;
+ background: rgba(15, 23, 42, 0.72);
+ color: #e2e8f0;
}
.pt-rebind-modal__current strong {
@@ -8341,8 +8370,8 @@ pre {
}
.pt-list-status--running {
- background: #dcfce7;
- color: #166534;
+ background: rgba(34, 197, 94, 0.16);
+ color: #86efac;
}
.pt-list-status--running::before,
@@ -8355,26 +8384,26 @@ pre {
}
.pt-list-status--paused {
- background: #fef9c3;
- color: #854d0e;
+ background: rgba(245, 158, 11, 0.16);
+ color: #fde68a;
}
.pt-list-status--stopped,
.pt-list-status--other {
- background: #f1f5f9;
- color: #475569;
+ background: rgba(148, 163, 184, 0.14);
+ color: #94a3b8;
}
.pt-list-status--collect {
- background: #e0e7ff;
- color: #3730a3;
+ background: rgba(99, 102, 241, 0.18);
+ color: #a5b4fc;
}
.pt-list-num,
.pt-list-time {
font-variant-numeric: tabular-nums;
white-space: nowrap;
- color: #334155;
+ color: #94a3b8;
}
.pt-list-actions {
@@ -8400,10 +8429,10 @@ pre {
gap: 12px;
min-height: 200px;
padding: 32px 16px;
- border: 1px dashed #cbd5e1;
+ border: 1px dashed rgba(148, 163, 184, 0.28);
border-radius: 8px;
- background: #f8fafc;
- color: #64748b;
+ background: rgba(15, 23, 42, 0.55);
+ color: #94a3b8;
text-align: center;
}
@@ -9273,3 +9302,222 @@ pre {
border-bottom: 1px solid rgba(148, 163, 184, 0.1);
}
}
+
+/* ===== Unified dark content surfaces (forms / tables / status) ===== */
+.page-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 240px;
+ color: #94a3b8;
+ font-size: 13px;
+}
+
+.app-main option,
+option {
+ background: #0f172a;
+ color: #e2e8f0;
+}
+
+.app-main .filter-inline,
+.network-main .filter-inline,
+.ne-page .filter-inline,
+.ume-page .filter-inline,
+.system-page .filter-inline {
+ background: rgba(15, 23, 42, 0.55) !important;
+ border-color: rgba(148, 163, 184, 0.2) !important;
+}
+
+.app-main .filter-inline input,
+.app-main .filter-inline select,
+.network-main .filter-inline input,
+.network-main .filter-inline select,
+.ne-page .filter-inline input,
+.ne-page .filter-inline select,
+.ume-page .filter-inline input,
+.ume-page .filter-inline select,
+.system-page .filter-inline input,
+.system-page .filter-inline select {
+ background: #0b1220 !important;
+ border-color: rgba(148, 163, 184, 0.28) !important;
+ color: #e2e8f0 !important;
+}
+
+.app-main .pt-list-table-wrap,
+.network-main .pt-list-table-wrap,
+.ne-page .pt-list-table-wrap,
+.ume-page .pt-list-table-wrap,
+.system-page .pt-list-table-wrap,
+.pt-list-table-wrap {
+ border-color: rgba(148, 163, 184, 0.2) !important;
+ background: transparent;
+}
+
+.app-main .pt-list-table thead th,
+.network-main .pt-list-table thead th,
+.ne-page .pt-list-table thead th,
+.ume-page .pt-list-table thead th,
+.system-page .pt-list-table thead th,
+.pt-list-table thead th,
+.app-main .data-table thead th,
+.data-table thead th {
+ background: rgba(15, 23, 42, 0.95) !important;
+ color: #94a3b8 !important;
+ border-bottom-color: rgba(148, 163, 184, 0.18);
+}
+
+.app-main .pt-list-table tbody tr:hover td,
+.pt-list-table tbody tr:hover td,
+.app-main .data-table tbody tr:hover td,
+.data-table tbody tr:hover td {
+ background: rgba(30, 41, 59, 0.55) !important;
+}
+
+.app-main .pt-list-status--unknown,
+.pt-list-status--unknown,
+.app-main .pt-list-status--other,
+.pt-list-status--other,
+.app-main .pt-list-status--stopped,
+.pt-list-status--stopped {
+ background: rgba(148, 163, 184, 0.14) !important;
+ color: #94a3b8 !important;
+}
+
+.app-main .pt-list-status--ok,
+.pt-list-status--ok,
+.app-main .pt-list-status--pass,
+.pt-list-status--pass,
+.app-main .pt-list-status--up,
+.pt-list-status--up {
+ background: rgba(34, 197, 94, 0.16) !important;
+ color: #86efac !important;
+}
+
+.app-main table,
+.app-main .data-table {
+ color: #e2e8f0;
+}
+
+.app-main .muted,
+.muted {
+ color: #94a3b8;
+}
+
+/* ===== Dark modals + topology non-canvas ===== */
+.modal-backdrop,
+.token-scopes-modal__backdrop,
+.topo-modal__backdrop {
+ background: rgba(2, 8, 23, 0.72) !important;
+}
+
+.modal,
+.app-main .modal,
+.token-scopes-modal__panel,
+.topo-modal__panel,
+.webcrt-auth-modal,
+.webcrt-new-session-modal,
+.webcrt-keyword-modal,
+.webcrt-session-opts-modal {
+ background: #0f172a !important;
+ color: #e2e8f0 !important;
+ border: 1px solid rgba(148, 163, 184, 0.22) !important;
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45) !important;
+}
+
+.modal h3,
+.token-scopes-modal__head h3,
+.topo-modal__head h3,
+.pt-log-modal__head h3 {
+ color: #f1f5f9 !important;
+}
+
+.modal .muted,
+.modal .form-hint,
+.modal label,
+.modal .form-label,
+.token-scopes-modal__row,
+.topo-modal__hint,
+.topo-modal__selectall {
+ color: #cbd5e1 !important;
+}
+
+.modal input,
+.modal select,
+.modal textarea,
+.topo-modal__panel input,
+.topo-modal__panel select,
+.token-scopes-modal__panel input,
+.token-scopes-modal__panel select {
+ background: #0b1220 !important;
+ border-color: rgba(148, 163, 184, 0.28) !important;
+ color: #e2e8f0 !important;
+}
+
+.topo-browser,
+.topo-main {
+ background: #070b14 !important;
+ color: #e2e8f0 !important;
+ border-color: rgba(148, 163, 184, 0.2) !important;
+}
+.topo-browser__head,
+.topo-toolbar {
+ background: rgba(15, 23, 42, 0.9) !important;
+ border-color: rgba(148, 163, 184, 0.18) !important;
+}
+.topo-browser__grid,
+.topo-browser__grid--regions {
+ background: rgba(2, 8, 23, 0.4) !important;
+}
+.topo-map-card {
+ background: rgba(15, 23, 42, 0.85) !important;
+ border-color: rgba(148, 163, 184, 0.22) !important;
+ color: #e2e8f0 !important;
+}
+.topo-map-card__name { color: #f1f5f9 !important; }
+.topo-map-list__row,
+.topo-palette__row {
+ background: rgba(15, 23, 42, 0.75) !important;
+ border-color: rgba(148, 163, 184, 0.22) !important;
+}
+
+.app-main .topo-toolbar__find,
+.topo-toolbar__find,
+.app-main .topo-toolbar__select,
+.topo-toolbar__select,
+.app-main .topo-toolbar__display > summary,
+.topo-toolbar__display > summary,
+.app-main .topo-toolbar__actions > .btn,
+.topo-toolbar__actions > .btn,
+.app-main .topo-toolbar__group > .btn,
+.topo-toolbar__group > .btn {
+ background: #0b1220 !important;
+ border-color: rgba(148, 163, 184, 0.28) !important;
+ color: #e2e8f0 !important;
+}
+
+.topo-nav-node {
+ background: linear-gradient(165deg, #1e293b 0%, #0f172a 100%) !important;
+ border-color: rgba(148, 163, 184, 0.28) !important;
+ color: #e2e8f0 !important;
+}
+.topo-nav-node__icon {
+ background: rgba(37, 99, 235, 0.18) !important;
+}
+
+.webcrt-menubar__menu {
+ background: #0f172a !important;
+ border-color: rgba(148, 163, 184, 0.28) !important;
+ color: #e2e8f0 !important;
+}
+.webcrt-menubar__menu-item {
+ color: #e2e8f0 !important;
+}
+.webcrt-menubar__menu-item:hover {
+ background: rgba(37, 99, 235, 0.2) !important;
+}
+
+.pt-log-modal__list {
+ background: rgba(2, 8, 23, 0.55) !important;
+ border-color: rgba(148, 163, 184, 0.2) !important;
+ color: #e2e8f0 !important;
+}
diff --git a/web/src/pages/TopologyPage.tsx b/web/src/pages/TopologyPage.tsx
index 4caf866..59d0046 100644
--- a/web/src/pages/TopologyPage.tsx
+++ b/web/src/pages/TopologyPage.tsx
@@ -451,6 +451,36 @@ const AUTO_LAYOUT_DISCOVER_KEY = "netx.topology.autoLayoutAfterDiscover";
const DISCOVER_AUTO_ADD_KEY = "netx.topology.discoverAutoAddUnmatched";
const DISCOVER_PROJECT_NEIGHBORS_KEY = "netx.topology.discoverProjectNeighbors";
const SCALE_BUNDLE_WIDTH_KEY = "netx.topology.scaleBundleWidth";
+const CANVAS_BG_KEY = "netx.topology.canvasBg";
+const DEFAULT_CANVAS_BG = "#dbeafe";
+
+function loadCanvasBg(): string {
+ try {
+ const raw = String(localStorage.getItem(CANVAS_BG_KEY) || "").trim();
+ if (/^#[0-9a-fA-F]{6}$/.test(raw)) return raw.toLowerCase();
+ } catch {
+ /* ignore */
+ }
+ return DEFAULT_CANVAS_BG;
+}
+
+function persistCanvasBg(value: string) {
+ try {
+ localStorage.setItem(CANVAS_BG_KEY, value);
+ } catch {
+ /* ignore */
+ }
+}
+
+function canvasDotColor(bg: string): string {
+ const hex = String(bg || "").replace("#", "");
+ if (hex.length !== 6) return "#93c5fd";
+ const r = parseInt(hex.slice(0, 2), 16);
+ const g = parseInt(hex.slice(2, 4), 16);
+ const b = parseInt(hex.slice(4, 6), 16);
+ const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
+ return lum > 0.55 ? "#93c5fd" : "#64748b";
+}
function loadBoolFlag(key: string, defaultValue: boolean): boolean {
try {
@@ -703,6 +733,7 @@ export function TopologyPage() {
);
const [edgeFlow, setEdgeFlow] = useState(false);
const [edgeDefaults, setEdgeDefaults] = useState(() => loadEdgeDefaults());
+ const [canvasBg, setCanvasBg] = useState(loadCanvasBg);
const [toolMode, setToolMode] = useState("select");
const [snapToGrid, setSnapToGrid] = useState(true);
const [autoLayoutAfterDiscover, setAutoLayoutAfterDiscover] = useState(loadAutoLayoutAfterDiscover);
@@ -2827,6 +2858,51 @@ export function TopologyPage() {
/>
{t("topology.discoverProjectNeighbors")}
+
{t("topology.edgeDefaults")}
@@ -3244,6 +3320,7 @@ export function TopologyPage() {
@@ -3383,7 +3460,13 @@ export function TopologyPage() {
deleteKeyCode={null}
edgesFocusable
>
-
+
{toolMode === "connect" ? (
{t("topology.connectHint")}