Auto-scale hashrate display to 1-999 range with correct unit

Panel: MutationObserver intercepts the #hashrate DOM update and
rescales the GH/s value from the API to the appropriate unit
(MH/s, GH/s, TH/s, PH/s), also updating the unit label in the
card header.

Chart: nethashChart Y-axis tick callback detects the canvas ID
and applies the same scaling logic per tick; a btcpHashrateUnit
plugin updates the Y-axis title (e.g. "Hashrate (TH/s)") after
each chart update to stay in sync with the data range
This commit is contained in:
2026-04-28 14:56:20 +02:00
parent 88cba13e77
commit a91e5aceee
+119 -15
View File
@@ -1,23 +1,113 @@
$(document).ready(function() {
if (typeof Chart === 'undefined') return;
// ── Y-axis: abbreviate large numbers ─────────────────────────
// 1400000 → "1.4M", 45000 → "45K", 999 → "999"
function fmtAxis(value) {
var abs = Math.abs(value);
if (abs >= 1e9) return (value / 1e9).toFixed(1).replace(/\.0$/, '') + 'B';
if (abs >= 1e6) return (value / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
if (abs >= 1e3) return (value / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
return value;
// ══════════════════════════════════════════════════════════════
// HASHRATE AUTO-SCALING
// The server sends hashrate already divided by nethash_units ("G"),
// so values arrive in GH/s. We scale to keep the display 1999.
// ══════════════════════════════════════════════════════════════
var HASH_UNITS = [
{ div: 1e6, label: 'PH/s' },
{ div: 1e3, label: 'TH/s' },
{ div: 1, label: 'GH/s' },
{ div: 1e-3, label: 'MH/s' },
{ div: 1e-6, label: 'KH/s' },
{ div: 1e-9, label: 'H/s' }
];
// Returns {num, unit} where 1 ≤ |num| < 1000 (best fit)
function scaleFromGH(gh) {
var abs = Math.abs(gh);
for (var i = 0; i < HASH_UNITS.length; i++) {
if (abs >= HASH_UNITS[i].div) {
return { num: gh / HASH_UNITS[i].div, unit: HASH_UNITS[i].label };
}
}
return { num: gh, unit: 'GH/s' };
}
// ── Panel: reformat #hashrate + update unit label ─────────────
function refreshHashratePanel() {
var $el = $('#hashrate');
if (!$el.length) return;
// Read raw text ("42,650.8351" or similar), strip formatting
var raw = $el.text().replace(/[,\s]/g, '');
var ghVal = parseFloat(raw);
if (!isFinite(ghVal) || ghVal <= 0) return;
var s = scaleFromGH(ghVal);
// Format to max 4 significant digits, always 2 decimals
var decimals = (s.num >= 100) ? 1 : (s.num >= 10) ? 2 : 3;
var formatted = s.num.toFixed(decimals);
var parts = formatted.split('.');
// Temporarily disconnect observer to avoid loop
_hashrateObserver.disconnect();
$el.html(parts[0] + '.<span class="decimal">' + parts[1] + '</span>');
_hashrateObserver.observe($el[0], { childList: true, subtree: true });
// Update the unit label "(GH/s)" → "(TH/s)" etc.
var $unitSpan = $el.closest('.card').find('.card-header span.small');
if ($unitSpan.length) $unitSpan.text('(' + s.unit + ')');
}
var _hashrateObserver = new MutationObserver(function() {
refreshHashratePanel();
});
// Start watching once #hashrate appears in the DOM
var _panelWatcher = new MutationObserver(function(mutations, obs) {
var el = document.getElementById('hashrate');
if (el) {
obs.disconnect();
_hashrateObserver.observe(el, { childList: true, subtree: true });
}
});
_panelWatcher.observe(document.body, { childList: true, subtree: true });
// ══════════════════════════════════════════════════════════════
// CHART.JS IMPROVEMENTS
// ══════════════════════════════════════════════════════════════
if (typeof Chart === 'undefined') return;
var font = { family: 'Inter, system-ui, sans-serif', size: 11 };
// ── Linear scale (Y-axis on both charts) ─────────────────────
// ── Y-axis tick formatters ────────────────────────────────────
// Generic large-number formatter (difficulty, etc.)
function fmtAxis(value) {
var abs = Math.abs(value);
if (abs >= 1e9) return (value / 1e9).toFixed(1).replace(/\.0$/, '') + 'B';
if (abs >= 1e6) return (value / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
if (abs >= 1e3) return (value / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
return value;
}
// Hashrate formatter for nethashChart Y-axis ticks
// Scales GH/s values to the 1-999 range with the right unit
function fmtHashAxis(gh) {
if (gh === 0) return '0';
var s = scaleFromGH(gh);
var decimals = (Math.abs(s.num) >= 100) ? 0 : (Math.abs(s.num) >= 10) ? 1 : 2;
return s.num.toFixed(decimals) + ' ' + s.unit;
}
// ── Linear scale defaults ─────────────────────────────────────
var lin = Chart.defaults.scales.linear;
if (lin) {
lin.ticks = Object.assign({}, lin.ticks, {
callback: fmtAxis,
// 'this' inside callback is the scale; this.chart.canvas.id identifies the chart
callback: function(value) {
if (this.chart && this.chart.canvas &&
this.chart.canvas.id === 'nethashChart') {
return fmtHashAxis(value);
}
return fmtAxis(value);
},
font: font,
color: 'rgba(200,160,255,0.85)',
padding: 8,
@@ -33,7 +123,6 @@ $(document).ready(function() {
}
// ── Time scale (X-axis) ───────────────────────────────────────
// Reduce label density and avoid 90° rotation
['time', 'timeseries'].forEach(function(t) {
var s = Chart.defaults.scales[t];
if (!s) return;
@@ -45,9 +134,24 @@ $(document).ready(function() {
minRotation: 0,
padding: 6
});
s.grid = Object.assign({}, s.grid, {
color: 'rgba(80,40,130,0.3)'
});
s.grid = Object.assign({}, s.grid, { color: 'rgba(80,40,130,0.3)' });
});
// ── Plugin: update nethashChart Y-axis title to match auto unit ──
Chart.register({
id: 'btcpHashrateUnit',
afterUpdate: function(chart) {
if (!chart.canvas || chart.canvas.id !== 'nethashChart') return;
var yScale = chart.scales && chart.scales.y;
if (!yScale || !yScale.max) return;
var s = scaleFromGH(yScale.max);
var newTitle = 'Hashrate (' + s.unit + ')';
var opts = yScale.options;
if (opts && opts.title && opts.title.text !== newTitle) {
opts.title.text = newTitle;
}
}
});
// ── Tooltip ───────────────────────────────────────────────────