igneum/docs/plans/miner-ui-4.md

8.2 KiB
Raw Blame History

Igneum Miner UI 4: Overview, Cards, Ember on the row

6 October 2026, evening. the project lead: "add Cards under its own tab, and have Overview and the cool mining demo on the main page with the revamped C option", then "incorporate Ember into the Cards tab beautifully", then "I would like the new miner UI to go into .15". Branch miner-ui-4 from the 0.3.15 tip 6ea9497 (untouched), worktree ../igneum-wt-miner-ui-4. Rust: one route (GET /live-dag.js, server.rs), nothing else.

1. The rail

Overview, Cards, Earnings, Prove, Settings; Logs and Quit in the foot; a bottom tab bar under 720 px.

2. Overview

Block What
The hero (the project lead's pick, C) the fleet rate at 84 px on a graphite-to-obsidian fade, W with MH/W, £ a day at the user's price, blocks (this run, lifetime) beside it; Start/Stop as the full-width bar under it with its state sentence at the right
The chain, live the site's live-dag.js (a byte copy at app/igneum-app/ui/live-dag.js, served by the engine at /live-dag.js), mounted on the engine's own api/live with mine: 'you' so this machine's lane reads "you" and its blocks take a molten ring; the legend words in HTML under the canvas: yours, pending, included, excluded, proven, locked; Pause; a tooltip per block. The engine's /api/live is OWED (it maps the local node's RPC into the site's reply shape: {ok, state, blocks[{hash, ts, blue_score, daa, parents, chain, color, miner, locked, final, shards, proven}], finality.checkpoints}); until it exists the module reports failed and the app's own 10-minute blocks strip draws in its place with the note "The full chain view needs the next engine update".
The node line as 0.3.15, Details under it
Activity the event feed in the app's voice (the strip canvas moved into the chain card as the fallback)

3. Cards

Block What
The Tuning strip the goal control (Efficiency, Balanced, Maximum) with "about £X a day at Y W"; the fleet saving ("Ember saves 84 W, £0.56 a day over 1 tuned card", View.fleetSaving); Tune all; the schedule line; Power control only when an NVIDIA card has it off
The rows as 0.3.15 (state word with its reason, MH/s, W with MH/W, £ a day, °C, the switch, the chevron) with Ember as the second layer
The flame mark the brand mark's two polygons, 16 px, outline when not tuned, filling bottom-up with step k of n while tuning (molten), solid ember when tuned, half grey when measured only
The Ember line "Tuned 2 h ago · 2470 MHz at 80% · next check Sunday · saves 84 W, 0.16% of rate"; "Tuning: step 4 of 9 · about 7 min left" with the bar; "Measured, not tuned · Power control is off"; "Not tuned yet · starts after 2 min of steady mining"; the fleet pause and a stop in the same place
The W cell before → after when tuned (tune_before_watts → the live draw)
The MH/W cell a 44 px sparkline of the ladder's MH/W per step, the chosen step as a dot
Tune / Retune / Stop the row's button; Retune once tuned
The chevron the cap slider, the applied or Retry line, memory and clocks, the card's own goal (As the fleet, Efficiency, Balanced, Maximum: POST api/tune/goal {key, goal}, "" clears; a build without it gets a toast), Retune, the Tuned line, the curve (W across, MH/s up, every step a dot, the chosen ringed, a rejected one hollow), identities, the facts

Fields read (Ember Tune 5429e82, on the 0.3.16 lane): tune_before_watts, tune_before_mhs (step 0 of the last full plan; 0 = never; the UI falls back to tune_curve[0] only for a full plan on an older build and claims no saving for a confirm or baseline plan), sweep_watts, sweep_mhs, sweep_eff, tune_curve, tune_goal, tune_step, tune_steps, tune_eta_s, sweep_state, sweep_note, tune_floor (asked for). View.ember and View.beforeOf carry the rules; view.test.mjs covers every state.

4. Screenshots (the mock on port 4318 with the rig state, the 5090 tuned, the 4070 at step 4 of 9)

docs/plans/miner-ui-4/u01-overview-dark-1440.png, u02-overview-light-1440.png, u03-cards-dark-1440.png (the 5090's details open: cap, goal, Retune, the curve), u04-cards-light-1440.png, u05-overview-dark-390.png, u06-cards-dark-390.png, u07-overview-light-390.png, u08-cards-light-390.png, u09-overview-mac-dark.png. The chain scene in the shots is the mock's /api/live (four lanes, one block a second, "you" ringed); on a real 0.3.15 engine the fallback strip shows until /api/live lands.

5. Owed

What Who
GET /api/live on the engine: the local node's blocks over the window, mapped into the site's shape, this machine's vote keys as the lane "you" engine
card.tune_floor Ember (said yes)
watts_total, pounds_per_day for the tray tip; address.balance_wei; price_gbp_per_ign engine (from miner-ui-3)
Re-copy site/live-dag.js over app/igneum-app/ui/live-dag.js when the site agent's audit commit lands, so the two stay identical the shipper at merge

Tests: node --test app/igneum-app/ui/*.test.mjs: 37 pass.

6. The second tip (20:0x UK): /api/live on the engine, the copy sweep, the project lead's goal rulings

What Where
GET /api/live on the engine: the observer's /api/live (the URL ota.rs already polls for the identity count) through curl, cached 2 s per window, this machine's blocks marked as the lane "you" by matching the block's miner id (the first 8 hex of its vote key hash) against every card's ids; state.you_blocks and state.source: "observer" added; {ok:false, error} when the observer is unreachable or the build has no observer address, which the UI meets with its own blocks strip. Four unit tests on the shaping from a fixture in the observer's shape (live::tests). The local node speaks gRPC and wRPC only and the engine carries no client for either (no TLS, no websocket stack), so a local-node source is OWED (a JSON endpoint on igneumd, or a wRPC client here); the reply shape stays the same when it comes. src/live.rs, src/server.rs, src/main.rs, ota::live_api_from made pub
The copy sweep: 150 strings, every one listed in docs/plans/miner-ui-4-copy.md ui/index.html, ui/app.js, the tests
the project lead's rulings: the per-card inherit option is "Default · Balanced" (grey, following the shared goal); "Default" wherever a per-card setting follows the shared one (the cap's "Back to Default"); the Cards strip is titled "Ember Tune"; every goal option carries its meaning as a tooltip (Efficiency "most hashes per watt, a little rate traded", Balanced "a little rate for most of the saving", Maximum "most hashes, watts second", Default "the goal set for all cards") ui/index.html, ui/app.js

The Rust tests did not run on this Mac (its cargo predates the lock file's version 4); the box runs them at the merge, as the rule says. UI tests: 37 pass. Screenshots u01, u03, u04, u06 reshot after the sweep.

7. Pinned to the 0.3.16 engine (ember-tune fd03f35), 20:5x UK

Field Where the UI reads it
mining.watts_total, mining.pounds_per_day the Overview hero and Earnings use the engine's figures when present (View.fleetWatts, View.fleetPounds), else the UI's own sums as before
address.balance_wei (string or null), address.balance_age_s (-1 until read), address.balance_note, address.price_gbp_per_ign (null until a signed manifest carries a price) Earnings' first row: "12.3456 IGN · in the wallet · read 20 s ago" (View.balanceWords); a priced balance shows £ with "at the signed price"; an older engine without the field keeps the row as it was
card.tune_floor the Ember line's "(the lowest clock Ember will set)"
/api/live reply source ("node" when igneumd answers igneum_getRecentBlocks, "site" until then; pending: true before the first body) the chain scene's note (View.liveNote): "From your node. Your blocks are ringed." or "From Igneum’s observer. Your blocks are ringed."; "you" is matched in the UI from every card's vote-key ids whatever the source names the lane

At the 0.3.16 merge src/live.rs is ember-tune's (the fuller one, node first, site second); this branch's 0.3.15 file is superseded and its source word is "site" too, so nothing in the UI changes between the two.