From 2a650ca09bbb6b4468afc9c90e27eb325d0ffe15 Mon Sep 17 00:00:00 2001 From: oliver Date: Wed, 5 Aug 2026 00:16:20 +0800 Subject: [PATCH] Switch topology canvas and left chrome to a dark theme. Align the default canvas background, region list, and canvas name styles with the dark ops shell, and migrate the old light canvas default. Co-authored-by: Cursor --- web/src/index.css | 136 ++++++++++++++++++++------------- web/src/pages/TopologyPage.tsx | 12 ++- 2 files changed, 90 insertions(+), 58 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 5d740db..52f5a1a 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -4186,7 +4186,7 @@ pre { } } -/* ---- Topology — light sidebar + canvas (shared chrome tokens) ---- */ +/* ---- Topology — dark sidebar + canvas (shared chrome tokens) ---- */ .topo-page { display: grid; grid-template-columns: 260px 1fr; @@ -5050,8 +5050,8 @@ pre { gap: 8px; min-height: 168px; border-style: dashed; - border-color: #b7c4d4; - background: rgba(255, 255, 255, 0.55); + border-color: rgba(148, 163, 184, 0.35); + background: rgba(15, 23, 42, 0.45); color: var(--nm-accent); font-size: 13px; font-weight: 600; @@ -5059,10 +5059,10 @@ pre { box-shadow: none; } .topo-map-card--add:hover { - background: #fff; + background: rgba(37, 99, 235, 0.14); border-color: var(--nm-accent); - color: var(--nm-brand); - box-shadow: 0 2px 8px rgba(15, 39, 68, 0.06); + color: #93c5fd; + box-shadow: none; } .topo-map-card__add-plus { width: 36px; @@ -5090,14 +5090,14 @@ pre { font-weight: 550; letter-spacing: -0.01em; line-height: 1.3; - color: var(--nm-brand, #0f1b2d); + color: var(--nm-brand, #f1f5f9); } .app-main .topo-breadcrumb__link, .topo-breadcrumb__link { border: 0 !important; background: transparent !important; box-shadow: none !important; - color: #5b6b7c !important; + color: #94a3b8 !important; cursor: pointer; padding: 0 !important; margin: 0; @@ -5475,20 +5475,20 @@ pre { .topo-region-list .topo-map-list__row:hover, .topo-region-list__block.is-hot > .topo-region-list__row, .topo-region-list__maps > li.is-hot > .topo-map-list__row { - background: rgba(255, 255, 255, 0.78); + background: var(--nm-nav-hover, #152033); } /* Selected = deep wash + left accent */ .topo-region-list__block.is-active > .topo-region-list__row, .topo-region-list__maps > li.is-active > .topo-map-list__row { border: 0; - background: #8eb6e3; - box-shadow: inset 3px 0 0 #0f3f7a; + background: var(--nm-nav-active, #1a2740); + box-shadow: inset 3px 0 0 var(--nm-accent, #3b82f6); } .topo-region-list__block.is-branch-active > .topo-region-list__row { border: 0; - background: rgba(142, 182, 227, 0.35); + background: rgba(37, 99, 235, 0.16); box-shadow: none; } @@ -5505,14 +5505,14 @@ pre { height: 22px; margin-right: 2px; border-radius: 6px; - background: rgba(15, 118, 110, 0.1); - color: #0f766e; + background: rgba(45, 212, 191, 0.12); + color: #5eead4; flex: 0 0 auto; } .topo-region-list__maps .topo-region-list__glyph { - background: rgba(26, 86, 168, 0.1); - color: #1a56a8; + background: rgba(59, 130, 246, 0.18); + color: #93c5fd; } .topo-region-list__maps { @@ -5595,7 +5595,7 @@ pre { .app-main .topo-region-list .topo-map-list__icon:hover:not(:disabled), .topo-region-list .topo-map-list__icon:hover:not(:disabled) { - background: rgba(15, 27, 45, 0.08) !important; + background: rgba(148, 163, 184, 0.16) !important; color: var(--nm-brand); } @@ -5615,7 +5615,7 @@ pre { white-space: nowrap; font-size: 13px; font-weight: 650; - color: var(--nm-brand, #0f1b2d); + color: var(--nm-brand, #f1f5f9); } .topo-region-list__maps .topo-map-list__title { @@ -5645,7 +5645,7 @@ pre { .topo-region-list__block.is-active .topo-map-list__count, .topo-region-list__maps > li.is-active .topo-map-list__count { background: transparent; - color: #0f1b2d; + color: #cbd5e1; } .topo-region-list__block.is-hot:not(.is-active) .topo-map-list__count, @@ -5656,13 +5656,13 @@ pre { .topo-region-list__block.is-active .topo-map-list__title, .topo-region-list__maps > li.is-active .topo-map-list__title { - color: #0a274f; + color: #f1f5f9; font-weight: 700; } .topo-region-list__maps > li.is-active .topo-region-list__glyph { - background: #0f3f7a; - color: #fff; + background: rgba(37, 99, 235, 0.35); + color: #dbeafe; } .topo-map-list li, @@ -5699,24 +5699,24 @@ pre { .topo-region-list__block.is-active > .topo-map-list__row, .topo-region-list__maps > li.is-active > .topo-map-list__row { border: 0 !important; - background: #8eb6e3 !important; - box-shadow: inset 3px 0 0 #0f3f7a !important; + background: var(--nm-nav-active, #1a2740) !important; + box-shadow: inset 3px 0 0 var(--nm-accent, #3b82f6) !important; } .topo-region-list__block.is-active > .topo-map-list__row:hover, .topo-region-list__maps > li.is-active > .topo-map-list__row:hover { - background: #7aa8db !important; + background: #243552 !important; } .topo-region-list__block.is-branch-active > .topo-map-list__row { border: 0 !important; - background: rgba(142, 182, 227, 0.35) !important; + background: rgba(37, 99, 235, 0.16) !important; box-shadow: none !important; } .topo-region-list__block.is-hot:not(.is-active):not(.is-branch-active) > .topo-map-list__row, .topo-region-list__maps > li.is-hot:not(.is-active) > .topo-map-list__row { - background: #c5daf3 !important; + background: rgba(59, 130, 246, 0.14) !important; } .topo-palette > li.is-on-canvas .topo-palette__row { @@ -5772,18 +5772,18 @@ pre { } .topo-map-list li.is-active .topo-map-list__item:hover { - background: #c9daf0; + background: rgba(37, 99, 235, 0.12); } .topo-map-list__actions { display: flex; align-items: stretch; flex: 0 0 auto; - border-left: 1px solid #cdd6e2; + border-left: 1px solid rgba(148, 163, 184, 0.22); } .topo-map-list li.is-active .topo-map-list__actions { - border-left-color: #93b4e0; + border-left-color: rgba(59, 130, 246, 0.35); } .topo-map-list__icon { @@ -6134,9 +6134,9 @@ pre { } .topo-toolbar__display[open] > summary { - border-color: #1a56a8; - background: #d6e4f5; - color: #1a56a8; + border-color: rgba(59, 130, 246, 0.55); + background: rgba(37, 99, 235, 0.22); + color: #93c5fd; } .topo-toolbar__display .topo-display-toggles { @@ -6148,10 +6148,11 @@ pre { gap: 6px; min-width: 260px; padding: 8px 10px; - border: 1px solid #c0cad6; + border: 1px solid rgba(148, 163, 184, 0.28); border-radius: 8px; - background: #fff; - box-shadow: 0 8px 20px rgba(15, 39, 68, 0.14); + background: #0f172a; + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4); + color: #e2e8f0; } .topo-mode-hint { @@ -6865,20 +6866,45 @@ pre { position: relative; flex: 1; min-height: 0; - background: var(--topo-canvas-bg, #dbeafe); + background: var(--topo-canvas-bg, #0f172a); } .topo-canvas:fullscreen, .topo-canvas.is-fullscreen:fullscreen { width: 100vw; height: 100vh; - background: var(--topo-canvas-bg, #dbeafe); + background: var(--topo-canvas-bg, #0f172a); } /* Place fullscreen above zoom-in (+); Controls renders custom children last. */ .topo-canvas .react-flow__controls { display: flex; flex-direction: column; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35); + border: 1px solid rgba(148, 163, 184, 0.28); + border-radius: 6px; + overflow: hidden; +} + +.topo-canvas .react-flow__controls-button { + background: #0f172a; + border-bottom-color: rgba(148, 163, 184, 0.22); + fill: #cbd5e1; +} + +.topo-canvas .react-flow__controls-button:hover { + background: #1e293b; +} + +.topo-canvas .react-flow__controls-button svg { + fill: #cbd5e1; +} + +.topo-canvas .react-flow__minimap { + background: #0b1220; + border: 1px solid rgba(148, 163, 184, 0.28); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35); } .topo-canvas .react-flow__controls-button.topo-fs-control { @@ -6900,9 +6926,9 @@ pre { gap: 6px; padding: 8px; border-radius: 10px; - background: rgba(255, 255, 255, 0.92); - border: 1px solid rgba(15, 23, 42, 0.12); - box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12); + background: rgba(15, 23, 42, 0.92); + border: 1px solid rgba(148, 163, 184, 0.22); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); pointer-events: auto; } @@ -6912,21 +6938,21 @@ pre { gap: 6px; padding: 6px 10px; border-radius: 8px; - border: 1px solid rgba(15, 23, 42, 0.12); - background: #fff; - color: #0f172a; + border: 1px solid rgba(148, 163, 184, 0.28); + background: #0b1220; + color: #e2e8f0; font-size: 12px; cursor: pointer; } .topo-fs-toolbar__btn:hover { - background: #f8fafc; + background: #152033; } .topo-fs-toolbar__btn.is-active { - border-color: #0ea5e9; - background: #e0f2fe; - color: #075985; + border-color: #3b82f6; + background: rgba(37, 99, 235, 0.28); + color: #93c5fd; } .topo-fs-toolbar__btn kbd { @@ -7149,27 +7175,27 @@ pre { } .react-flow__edge-textbg { - fill: #ffffff; + fill: #0f172a; } .react-flow__edge-text { - fill: #475569; + fill: #cbd5e1; font-size: 11px; } .topo-edge-label { font-size: 11px; - color: #475569; - background: rgba(255, 255, 255, 0.92); - border: 1px solid #e2e8f0; + color: #cbd5e1; + background: rgba(15, 23, 42, 0.92); + border: 1px solid rgba(148, 163, 184, 0.28); border-radius: 4px; padding: 1px 6px; white-space: nowrap; } .topo-edge-label.is-selected { - color: #1d4ed8; - border-color: #93c5fd; + color: #93c5fd; + border-color: rgba(59, 130, 246, 0.55); font-weight: 600; } diff --git a/web/src/pages/TopologyPage.tsx b/web/src/pages/TopologyPage.tsx index 59d0046..d74ad7b 100644 --- a/web/src/pages/TopologyPage.tsx +++ b/web/src/pages/TopologyPage.tsx @@ -452,12 +452,18 @@ 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"; +const DEFAULT_CANVAS_BG = "#0f172a"; +/** Previous light default — migrate so existing sessions pick up dark canvas. */ +const LEGACY_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(); + const raw = String(localStorage.getItem(CANVAS_BG_KEY) || "").trim().toLowerCase(); + if (raw === LEGACY_CANVAS_BG) { + persistCanvasBg(DEFAULT_CANVAS_BG); + return DEFAULT_CANVAS_BG; + } + if (/^#[0-9a-fA-F]{6}$/i.test(raw)) return raw; } catch { /* ignore */ }