From a4f457a9de75003891a803439c9e9ab55b7f733b Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 16:16:56 +0000 Subject: [PATCH 1/2] web(d-web4): neutralize inline min-widths + wrap monospace config on <=480px The embedded dashboard header info boxes (#miner-info / #network-info) carry inline min-width (280px / 220px) that the 768px/900px breakpoints cannot override, and the monospace config lines (stratum URL, the ,.WORKER username) are unbreakable tokens. On a phone (<=414px) these force horizontal overflow that clips the view. Add a <=480px breakpoint that drops the inline mins to full-width and lets the long tokens wrap. Static drop-in (served from --dashboard-dir), no rebuild. --- web-static/dashboard.html | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/web-static/dashboard.html b/web-static/dashboard.html index 1c6e57f69..2ef86e1cc 100644 --- a/web-static/dashboard.html +++ b/web-static/dashboard.html @@ -1248,7 +1248,26 @@ max-width: 100%; } } - + + /* Narrow phones (<=480px): the header info boxes carry INLINE + min-width (280px / 220px) which the wider breakpoints cannot + override, and the monospace config lines (stratum URL, the + ,.WORKER username) are unbreakable tokens. + Together they force horizontal overflow that clips the view on + a phone. Neutralize the inline mins and let long tokens wrap. + Static drop-in; serves the operator-flagged D-WEB.4 mobile clip. */ + @media (max-width: 480px) { + #miner-info, #network-info { + min-width: 0 !important; + flex: 1 1 100% !important; + } + .miner-info [style*="monospace"], + .miner-info [style*="monospace"] span { + overflow-wrap: anywhere; + word-break: break-word; + } + } + /* Two-column layout for sections */ .two-col { display: grid; From ba2f4b4af6f5841950800ac7c3276cccd8714b79 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Wed, 24 Jun 2026 17:25:29 +0000 Subject: [PATCH 2/2] web(d-web): surface per-node topology card on embedded dashboard The /api/node_topology endpoint already returns THIS node's real coin set (primary + auto-detected embedded/aux coins, per-coin peer counts, embedded/has_rpc flags) but the embedded dashboard never rendered it -- the operator could not see the node's actual topology at a glance. Adds a config-driven Node Topology card (static drop-in, no backend change) that consumes the existing endpoint, mirroring the V36 crossing widget pattern. Hidden until the endpoint returns a non-empty coin list, so nodes with no topology data show nothing rather than a stub. Charter #2 (per-node truthful dashboard): each node now shows its own real topology, never a baked-in coin list. --- web-static/dashboard.html | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/web-static/dashboard.html b/web-static/dashboard.html index 2ef86e1cc..66b29bc5b 100644 --- a/web-static/dashboard.html +++ b/web-static/dashboard.html @@ -1809,6 +1809,12 @@

🔀 Share-Version Crossing

Vote: -  Â·  Work-weighted: -
--
+ + @@ -2907,6 +2913,7 @@

Legend

d3.json('../merged_stats', function(d) { _ms = d || {}; _tryRenderStats(); }); d3.json('../best_share', function(d) { _bs = d || {}; _tryRenderStats(); }); d3.json('../v36_status', function(d) { if (d) renderV36Status(d); }); + d3.json('../api/node_topology', function(d) { if (d) renderNodeTopology(d); }); } // V36 crossing widget: maps /v36_status ratchet state to an at-a-glance label. @@ -2933,6 +2940,28 @@

Legend

d3.select('#v36_msg').text(v.message ? '\u00b7 ' + v.message : ''); } + // Per-node topology: renders THIS node's real coin set (primary + embedded/aux), + // peer counts and embedded/RPC flags, straight from /api/node_topology. + // Config-driven/auto-detected -- never a baked-in coin list (charter #2). + function renderNodeTopology(t) { + var card = document.getElementById('node_topology_card'); + if (!card || !t || !Array.isArray(t.coins) || !t.coins.length) return; + var rows = t.coins.map(function(c) { + var tags = []; + if (c.primary) tags.push('primary'); + if (c.embedded) tags.push('embedded'); + if (c.has_rpc) tags.push('rpc'); + var tagStr = tags.length ? ' [' + tags.join(', ') + ']' : ''; + var peers = (c.peers != null ? c.peers : 0); + return '
' + c.coin + '' + tagStr + + '  ·  ' + peers + ' peer' + (peers === 1 ? '' : 's') + '
'; + }).join(''); + document.getElementById('node_topology_coins').innerHTML = rows; + d3.select('#node_topology_symbol').text(t.node_symbol ? '(' + t.node_symbol + ')' : ''); + d3.select('#node_topology_note').text(t.auto_detected ? 'auto-detected' : ''); + card.style.display = ''; + } + function renderBestShare(bs) { if (!bs || !bs.round) return; d3.select('#best_share_round_pct_fancy').html(format_pct_fancy(bs.round.pct_of_block));