From 2fbb3dd45a7380f6f90f20240c6719f3296c0eeb Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 25 May 2026 17:05:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=BC=E8=88=AA=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- onchain_scout_gate/static/app.js | 108 ++++++++++---------- onchain_scout_gate/static/style.css | 75 ++++++++++++++ onchain_scout_gate/templates/dashboard.html | 99 +++++++++--------- 3 files changed, 175 insertions(+), 107 deletions(-) diff --git a/onchain_scout_gate/static/app.js b/onchain_scout_gate/static/app.js index d0315ad..aad6d41 100644 --- a/onchain_scout_gate/static/app.js +++ b/onchain_scout_gate/static/app.js @@ -262,59 +262,62 @@ function renderDailyReport(payload) { `; } -const PANEL_FOLD_STORAGE_KEY = "matrix_panel_fold_v1"; +const SECTION_NAV_STORAGE = "matrix_section_nav_v1"; -function initPanelFolds() { - let saved = {}; - try { - const raw = localStorage.getItem(PANEL_FOLD_STORAGE_KEY); - if (raw) saved = JSON.parse(raw); - } catch (_) { - saved = {}; - } +function isNavSectionActive(navId) { + const sec = document.querySelector(`.matrix-nav-section[data-nav-id="${navId}"]`); + return !!(sec && sec.classList.contains("is-nav-active")); +} - function applyFold(panel, folded) { - const toggle = panel.querySelector(".matrix-panel-fold-toggle"); - if (folded) { - panel.classList.add("is-folded"); - if (toggle) toggle.setAttribute("aria-expanded", "false"); - } else { - panel.classList.remove("is-folded"); - if (toggle) toggle.setAttribute("aria-expanded", "true"); +function initSectionNav() { + const nav = document.getElementById("sectionNav"); + if (!nav) return; + const buttons = nav.querySelectorAll(".matrix-nav-item[data-nav-id]"); + const sections = document.querySelectorAll(".matrix-nav-section[data-nav-id]"); + + function activate(navId, opts = {}) { + const scrollTop = opts.scrollTop !== false; + buttons.forEach((b) => b.classList.toggle("is-active", b.dataset.navId === navId)); + sections.forEach((s) => s.classList.toggle("is-nav-active", s.dataset.navId === navId)); + try { + sessionStorage.setItem(SECTION_NAV_STORAGE, navId); + } catch (_) { + /* ignore */ + } + if (scrollTop) { + nav.scrollIntoView({ behavior: "smooth", block: "nearest" }); + } + try { + const url = `#${navId}`; + if (location.hash !== url) history.replaceState(null, "", url); + } catch (_) { + /* ignore */ } } - document.querySelectorAll(".matrix-panel-fold[data-fold-id]").forEach((panel) => { - const id = panel.dataset.foldId; - const defaultOpen = panel.dataset.foldDefault === "open"; - let folded; - if (Object.prototype.hasOwnProperty.call(saved, id)) { - folded = saved[id] === true; - } else { - folded = !defaultOpen; - } - applyFold(panel, folded); - - const toggle = panel.querySelector(".matrix-panel-fold-toggle"); - if (!toggle) return; - - const onToggle = (e) => { - if (e.target.closest("[data-fold-ignore]")) return; - const willFold = !panel.classList.contains("is-folded"); - applyFold(panel, willFold); - saved[id] = willFold; - try { - localStorage.setItem(PANEL_FOLD_STORAGE_KEY, JSON.stringify(saved)); - } catch (_) { - /* ignore quota */ + let initial = "gemma-funnel"; + const hash = (location.hash || "").replace(/^#/, ""); + if (hash && document.querySelector(`.matrix-nav-section[data-nav-id="${hash}"]`)) { + initial = hash; + } else { + try { + const saved = sessionStorage.getItem(SECTION_NAV_STORAGE); + if (saved && document.querySelector(`.matrix-nav-section[data-nav-id="${saved}"]`)) { + initial = saved; } - }; + } catch (_) { + /* ignore */ + } + } + activate(initial, { scrollTop: false }); - toggle.addEventListener("click", onToggle); - toggle.addEventListener("keydown", (e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - onToggle(e); + buttons.forEach((btn) => { + btn.addEventListener("click", () => { + const id = btn.dataset.navId; + if (!id) return; + activate(id); + if (id === "scan-layers" || id === "runtime-logs" || id === "telemetry") { + refresh(); } }); }); @@ -430,11 +433,6 @@ async function saveDailyReportSettings() { } } -function isPanelFolded(foldId) { - const panel = document.querySelector(`.matrix-panel-fold[data-fold-id="${foldId}"]`); - return !!(panel && panel.classList.contains("is-folded")); -} - async function refresh() { if (document.visibilityState !== "visible") return; const mobileLite = isMobileLite(); @@ -452,7 +450,7 @@ async function refresh() { const statusPre = document.getElementById("status"); const cf = document.getElementById("config"); - if (!mobileLite) { + if (!mobileLite || isNavSectionActive("telemetry")) { if (statusPre) { const st = pretty(status); if (statusPre.textContent !== st) statusPre.textContent = st; @@ -512,7 +510,7 @@ async function refresh() { const watchRows = allAlerts.filter((a) => (a.details && a.details.signal_level) === "WATCH"); const triggerRows = allAlerts.filter((a) => (a.details && a.details.signal_level) === "TRIGGER"); - if (!mobileLite || !isPanelFolded("scan-layers")) { + if (isNavSectionActive("scan-layers")) { renderItems("watchAlerts", watchRows, (a) => `
${a.symbol} ${escapeHtml(a.chain || "")}
级别: ${(a.details && a.details.signal_level) || "N/A"}
@@ -541,7 +539,7 @@ async function refresh() { } } - if (!mobileLite || !isPanelFolded("runtime-logs")) { + if (isNavSectionActive("runtime-logs")) { renderItems("logs", logs.items || [], (l) => `
[${l.level}] ${escapeHtml(l.message)}
${formatIsoToBeijing(l.created_at)}
@@ -877,7 +875,7 @@ tickClock(); setInterval(tickClock, 1000); initMatrixRain(); initFunnelWindowControls(); -initPanelFolds(); +initSectionNav(); refresh(); const REFRESH_MS = isMobileLite() ? 10000 : 4000; setInterval(refresh, REFRESH_MS); diff --git a/onchain_scout_gate/static/style.css b/onchain_scout_gate/static/style.css index d6ae676..bc6123b 100644 --- a/onchain_scout_gate/static/style.css +++ b/onchain_scout_gate/static/style.css @@ -885,6 +885,70 @@ pre { background: linear-gradient(180deg, rgba(0, 30, 28, 0.65), transparent); } +.matrix-section-nav { + position: sticky; + top: 0; + z-index: 85; + margin: 0 auto; + width: 100%; + max-width: min(1760px, 100%); + padding: 0 0 10px; + background: linear-gradient(180deg, rgba(0, 12, 10, 0.97) 70%, rgba(0, 12, 10, 0.85)); + border-bottom: 1px solid rgba(0, 255, 213, 0.2); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); +} + +.matrix-section-nav-inner { + display: flex; + flex-wrap: nowrap; + gap: 8px; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + padding: 4px 2px 6px; +} + +.matrix-nav-item { + flex: 0 0 auto; + font-family: inherit; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + padding: 10px 14px; + border-radius: 2px; + border: 1px solid rgba(0, 255, 213, 0.28); + background: rgba(0, 20, 18, 0.75); + color: #7dffc8; + cursor: pointer; + white-space: nowrap; + transition: background 0.15s, border-color 0.15s, color 0.15s; +} + +.matrix-nav-item:hover { + border-color: rgba(0, 255, 213, 0.55); + color: #b8fff0; +} + +.matrix-nav-item.is-active { + border-color: rgba(255, 0, 170, 0.55); + background: linear-gradient(135deg, rgba(0, 255, 213, 0.18), rgba(255, 0, 170, 0.12)); + color: #e8fff9; + box-shadow: 0 0 16px rgba(0, 255, 213, 0.15); +} + +.matrix-nav-section { + display: none; +} + +.matrix-nav-section.is-nav-active { + display: block; +} + +.matrix-panel-body { + margin-top: 4px; +} + .matrix-title-wrap { position: relative; } @@ -1527,6 +1591,17 @@ body.matrix-theme { padding: 8px 10px; } + .matrix-section-nav { + top: 0; + padding-bottom: 8px; + } + + .matrix-nav-item { + font-size: 10px; + padding: 10px 12px; + min-height: 40px; + } + .matrix-main { padding: 12px 0 24px; gap: 14px; diff --git a/onchain_scout_gate/templates/dashboard.html b/onchain_scout_gate/templates/dashboard.html index 6c74bfc..a03efd1 100644 --- a/onchain_scout_gate/templates/dashboard.html +++ b/onchain_scout_gate/templates/dashboard.html @@ -87,16 +87,27 @@ + +
-
-
+ -
-
+
+

// 关键位突破监控

- - 人工录入 · 5m 门控 - - + 人工录入 · 5m 门控
-
+

GEMMA 漏斗仅供参考;在此录入上/下沿。箱体突破与收敛突破均支持「标准突破」或「趋势突破」(无 1.5H 方案)。

// 规则加载中…

@@ -166,15 +174,12 @@
-
-