From e341fd7b26c8f23bbcf621a11e8fb09f11881fca Mon Sep 17 00:00:00 2001 From: satoshi Date: Tue, 19 May 2026 02:34:15 +0300 Subject: [PATCH] Add node peers, blue banners, glowing diff ranges, fix back navigation - Show Bitcoin Core peer count on block height card (hover for in/out) - BestSharePage disclaimers now use solid blue styling - Difficulty distribution ranges glow from warm peach to deep red - Back-to-dashboard buttons always navigate to dashboard instead of using browser history --- api/internal/bitcoind/rpc.go | 14 ++++++++++++ api/internal/state/aggregator.go | 12 ++++++++++ ui/src/lib/BestSharePage.svelte | 8 +++---- ui/src/lib/PoolOverview.svelte | 7 +++++- ui/src/lib/StatsPage.svelte | 37 +++++++++++++++++++++++++++---- ui/src/stores/selection.svelte.ts | 13 +---------- ui/src/types.ts | 3 +++ 7 files changed, 73 insertions(+), 21 deletions(-) diff --git a/api/internal/bitcoind/rpc.go b/api/internal/bitcoind/rpc.go index 186e944..de5042e 100644 --- a/api/internal/bitcoind/rpc.go +++ b/api/internal/bitcoind/rpc.go @@ -343,6 +343,20 @@ func (c *RPC) GetRawMempoolVerbose(ctx context.Context) (map[string]RawMempoolEn return out, nil } +type NetworkInfo struct { + Connections int `json:"connections"` + ConnectionsIn int `json:"connections_in"` + ConnectionsOut int `json:"connections_out"` +} + +func (c *RPC) GetNetworkInfo(ctx context.Context) (*NetworkInfo, error) { + var out NetworkInfo + if err := c.Call(ctx, "getnetworkinfo", nil, &out); err != nil { + return nil, err + } + return &out, nil +} + // IsRPCError checks if err is an rpcError with a specific code. func IsRPCError(err error, code int) bool { var rerr *rpcError diff --git a/api/internal/state/aggregator.go b/api/internal/state/aggregator.go index 02caee3..5e105f1 100644 --- a/api/internal/state/aggregator.go +++ b/api/internal/state/aggregator.go @@ -98,6 +98,11 @@ type Snapshot struct { HasLastZMQEvent bool `json:"has_last_zmq_event"` TipChangedAge float64 `json:"tip_changed_age"` // seconds since tip height last changed + // Bitcoin Core peer connections. + PeerCount int `json:"peer_count"` + PeersIn int `json:"peers_in"` + PeersOut int `json:"peers_out"` + // Share counters: raw counts (1 submission = 1 share regardless of diff). // Session = since ckpool started; AllTime = persisted across restarts. SessionAccepted int64 `json:"session_accepted"` @@ -460,6 +465,13 @@ func (a *Aggregator) refresh(ctx context.Context) { } } + // --- bitcoind: peer connections --- + if ni, err := a.RPC.GetNetworkInfo(ctx); err == nil { + next.PeerCount = ni.Connections + next.PeersIn = ni.ConnectionsIn + next.PeersOut = ni.ConnectionsOut + } + // --- predicted difficulty adjustment --- // Fetch the timestamp of the first block in the current retarget // epoch (height - height%2016) once per epoch and cache it, then diff --git a/ui/src/lib/BestSharePage.svelte b/ui/src/lib/BestSharePage.svelte index b8a7366..7d04931 100644 --- a/ui/src/lib/BestSharePage.svelte +++ b/ui/src/lib/BestSharePage.svelte @@ -399,15 +399,15 @@ } .disclaimer { font-size: 0.82rem; - color: var(--fg-dim); - background: rgba(245, 196, 71, 0.08); - border: 1px solid rgba(245, 196, 71, 0.25); + color: rgb(160, 195, 240); + background: rgb(20, 35, 60); + border: 1px solid rgb(60, 100, 170); border-radius: 6px; padding: 0.5em 0.85em; line-height: 1.5; } .disclaimer strong { - color: var(--fg); + color: rgb(200, 220, 250); } .head { display: flex; diff --git a/ui/src/lib/PoolOverview.svelte b/ui/src/lib/PoolOverview.svelte index 8c679e1..0e0f297 100644 --- a/ui/src/lib/PoolOverview.svelte +++ b/ui/src/lib/PoolOverview.svelte @@ -208,7 +208,12 @@
Block height
{height ? height.toLocaleString() : "—"}
-
{data.chain?.chain === "main" ? "mainnet" : data.chain?.chain ?? "—"}
+
+ {data.chain?.chain === "main" ? "mainnet" : data.chain?.chain ?? "—"} + {#if data.peer_count > 0} +  · {data.peer_count} peers + {/if} +
diff --git a/ui/src/lib/StatsPage.svelte b/ui/src/lib/StatsPage.svelte index 88841d5..c22d5a0 100644 --- a/ui/src/lib/StatsPage.svelte +++ b/ui/src/lib/StatsPage.svelte @@ -51,6 +51,8 @@ // --- Difficulty distribution --- const bucketLabels = ["< 1M", "1M\u2013100M", "100M\u20131G", "1G\u2013100G", "100G\u20131T", "\u2265 1T"]; + // Fire intensity classes: none for <1M, then progressively more intense. + const fireClasses = ["", "fire-1", "fire-2", "fire-3", "fire-4", "fire-5"]; type BucketRow = { label: string; @@ -180,7 +182,7 @@
-

Difficulty Distribution

+

Difficulty Distribution - Amount of shares in different difficulty ranges

{#if !hasDistData}
No accepted shares yet
{:else} @@ -196,9 +198,16 @@ - {#each bucketRows as row} - - {row.label} + {#each bucketRows as row, i} + {@const hasShares = row.session > 0 || row.alltime > 0} + + + {#if fireClasses[i]} + {row.label} + {:else} + {row.label} + {/if} + {row.session.toLocaleString()} {row.sessionPct.toFixed(1)}% {row.alltime.toLocaleString()} @@ -310,4 +319,24 @@ tr[title]:hover td { color: var(--accent); } .dim-row td { opacity: 0.35; } + /* --- Glowing range labels --- */ + .fire-label { + display: inline-block; + font-weight: 600; + } + .fire-dimmed { opacity: 0.2; } + .fire-active { animation: glow-pulse 2s ease-in-out infinite alternate; } + + .fire-1.fire-active { color: #ffcc80; --glow: 255, 180, 100; } + .fire-2.fire-active { color: #ff9a5c; --glow: 255, 140, 60; } + .fire-3.fire-active { color: #ff6e40; --glow: 255, 90, 30; } + .fire-4.fire-active { color: #f44336; --glow: 230, 40, 20; } + .fire-5.fire-active { color: #c62828; --glow: 180, 20, 10; } + + @keyframes glow-pulse { + 0% { text-shadow: 0 0 8px rgba(var(--glow), 0.4); } + 100% { text-shadow: 0 0 8px rgba(var(--glow), 0.85), + 0 0 16px rgba(var(--glow), 0.3); } + } + diff --git a/ui/src/stores/selection.svelte.ts b/ui/src/stores/selection.svelte.ts index 9113f51..c33a8d7 100644 --- a/ui/src/stores/selection.svelte.ts +++ b/ui/src/stores/selection.svelte.ts @@ -74,16 +74,5 @@ export function selectBestShare(): void { } export function clearSelection(): void { - if ( - window.history.length > 1 && - (window.location.hash.startsWith(USER_PREFIX) || - window.location.hash.startsWith(WORKER_PREFIX) || - window.location.hash === ACCELERATOR_HASH || - window.location.hash === STATS_HASH || - window.location.hash === BESTSHARE_HASH) - ) { - window.history.back(); - } else { - window.location.hash = ""; - } + window.location.hash = ""; } diff --git a/ui/src/types.ts b/ui/src/types.ts index 150274a..489980f 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -147,6 +147,9 @@ export type Snapshot = { has_last_zmq_event: boolean; last_zmq_event_age?: number; // seconds tip_changed_age: number; // seconds since tip height last changed + peer_count: number; + peers_in: number; + peers_out: number; // Share counters (raw, 1 submission = 1 share). session_accepted: number; session_rejected: number;