iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 8cb635aa94de51586e3be61a30b14f49f2dc4e2d
parent c9037e90d61c7e80cd295f7ffc9c42746ecd56d1
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Fri, 31 Jul 2026 10:52:02 +0200

Add metrics block range filter

Diffstat:
Msrc/adapters/http.rs | 26+++++++++++++++++++++++++-
Mwww/assets/iuna-ui.js | 28+++++++++++++++++++++++++++-
2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/src/adapters/http.rs b/src/adapters/http.rs @@ -2453,6 +2453,10 @@ const INDEX_HTML: &str = r#"<!doctype html> .settings-form { display: grid; gap: 10px; align-items: stretch; } .settings-form label, .settings-form input { width: 100%; } .metrics-shell { display: grid; gap: 12px; } + .metrics-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; } + .metrics-head h2 { margin: 0; } + .metrics-range { flex: 0 0 auto; } + .metrics-range button { padding: 5px 9px; font-size: 12px; white-space: nowrap; } .metrics-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; } .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 430px), 1fr)); gap: 12px; } .metric-chart-card { display: grid; gap: 10px; min-width: 0; border: 1px solid #2a3035; border-radius: 8px; padding: 12px; background: #181b1f; } @@ -2654,6 +2658,9 @@ const INDEX_HTML: &str = r#"<!doctype html> header, .split, .setup-grid, .wallet-grid, .mining-grid, .detail-grid, .wallet-tx-row { grid-template-columns: 1fr; } header { display: grid; } .settings-mode-row { align-items: flex-start; } + .metrics-head { align-items: flex-start; flex-direction: column; } + .metrics-range { width: 100%; } + .metrics-range button { flex: 1 1 0; } .metrics-grid { grid-template-columns: 1fr; } input { min-width: 0; width: 100%; } .switch input { width: auto; } @@ -2661,7 +2668,7 @@ const INDEX_HTML: &str = r#"<!doctype html> .block-card { flex-basis: 108px; } } </style> - <script defer src="/assets/iuna-ui.js?v=64"></script> + <script defer src="/assets/iuna-ui.js?v=65"></script> <script defer src="/assets/alpine.min.js"></script> </head> <body x-data="iunaApp()" x-init="init()" @keydown.window.escape="closeModals()" x-cloak> @@ -3113,6 +3120,14 @@ const INDEX_HTML: &str = r#"<!doctype html> </section> <section x-show="tab === 'metrics'"> <div class="metrics-shell"> + <div class="metrics-head"> + <h2>Metrics</h2> + <div class="segmented metrics-range" role="group" aria-label="Metrics block range"> + <button type="button" :class="{ active: metricsRange === 100 }" @click="setMetricsRange(100)">Last 100</button> + <button type="button" :class="{ active: metricsRange === 1000 }" @click="setMetricsRange(1000)">Last 1000</button> + <button type="button" :class="{ active: metricsRange === 'all' }" @click="setMetricsRange('all')">All</button> + </div> + </div> <div class="metrics-summary"> <div class="metric"><div class="label">Latest block</div><div class="value" x-text="metricsLatest().height ?? '-'"></div></div> <div class="metric"><div class="label">Supply</div><div class="value" x-text="metricAmountLabel(metricsLatest().circulatingSupply)"></div></div> @@ -4390,6 +4405,15 @@ mod tests { ); } + #[test] + fn metrics_screen_includes_block_range_filter() { + assert!(super::INDEX_HTML.contains("iuna-ui.js?v=65")); + assert!(super::INDEX_HTML.contains("aria-label=\"Metrics block range\"")); + assert!(super::INDEX_HTML.contains("setMetricsRange(100)")); + assert!(super::INDEX_HTML.contains("setMetricsRange(1000)")); + assert!(super::INDEX_HTML.contains("setMetricsRange('all')")); + } + async fn auth_test_state(config_path: std::path::PathBuf, config: UiConfig) -> HttpState { config_store::save(&config_path, &config).unwrap(); let wallet_path = config_path.with_file_name("wallet.json"); diff --git a/www/assets/iuna-ui.js b/www/assets/iuna-ui.js @@ -14,6 +14,16 @@ window.iunaApp = function iunaApp() { p2pMetrics: {}, blockchainMetrics: { enabled: false, latest: null, charts: [] }, metricHover: null, + metricsRange: (() => { + try { + const stored = localStorage.getItem("iunaMetricsRange"); + if (stored === "1000") return 1000; + if (stored === "all") return "all"; + } catch { + // Ignore storage failures; the in-memory default is enough. + } + return 100; + })(), networkHealth: {}, uiMode: (() => { try { @@ -970,6 +980,16 @@ window.iunaApp = function iunaApp() { return this.blockchainMetrics?.latest || {}; }, + setMetricsRange(range) { + this.metricsRange = range === 1000 || range === "all" ? range : 100; + this.metricHover = null; + try { + localStorage.setItem("iunaMetricsRange", String(this.metricsRange)); + } catch { + // Non-persistent filtering is fine when storage is unavailable. + } + }, + metricChartPoints(chart) { const points = this.metricVisiblePoints(chart); if (points.length === 0) return ""; @@ -1014,7 +1034,13 @@ window.iunaApp = function iunaApp() { metricVisiblePoints(chart) { const points = Array.isArray(chart?.points) ? chart.points : []; - return points.filter((point) => Number.isFinite(Number(point.value))); + const validPoints = points.filter((point) => Number.isFinite(Number(point.value))); + const limit = this.metricsRange; + if (limit === "all") return validPoints; + const latestHeight = Number(this.metricsLatest().height); + if (!Number.isFinite(latestHeight)) return validPoints.slice(-limit); + const minHeight = Math.max(0, latestHeight - limit + 1); + return validPoints.filter((point) => Number(point.height) >= minHeight); }, metricLatestValueLabel(chart) {