The audit (docs/plans/miner-ui-3-audit.md, 27 screenshots): no money anywhere, three red n/a per Apple row, tuning split across two pages, 370 px of controls per card, Prove's 95 words and four zeros, Node's eleven numbers, the jobs table on every Updates page, fixes two taps away, no light mode, no layout under 900 px, developer lines on user surfaces. The design (docs/plans/miner-ui-3.md): four sections (Mine, Earnings, Prove, Settings); the card row carries the state word with its reason, MH/s, W with MH/W, £ a day at the user's electricity price, °C, Tune, the switch, and its details under a chevron (cap slider, identities, pin, telemetry, the tune table); the tune line per row (not tuned yet / tuning: step k of n with a bar / tuned N ago · point · next check <weekday> / measured only and why / pinned / stopped / fleet pause); Tune all; the goal (Efficiency, Balanced, Maximum) with its £ a day; Power control as one switch where it is the fix; the node as one line with Details; updates as one card; earnings money first, IGN second; proving as a tier sentence per card; every costly action confirmed in place (quit, change address, show key, trust mode), no dialogs; light and dark; a bottom tab bar under 720 px. The build: index.html, app.css, app.js rewritten on the same engine routes; the pure blocks keep their API and carry ember-tune's tuneNotice, tuneWord, toggle states and tune-line test (e9738dd) so the merge is clean; the update card is named Igneum Miner; the Rust side is untouched. node --test on the four UI files: 35 pass. Screenshots of the result: docs/plans/miner-ui-3/n00 to n26. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
23 KiB
Igneum Miner UI 3: the card is the product
6 October 2026. the project lead, 18:00 UTC: "as gorgeous as possible and as simple as possible but not lacking any features,
think of this as an Apple product in terms of UI and UX, with tuning easy, buttons to tune each card again, or if
automated then fair enough." The audit is docs/plans/miner-ui-3-audit.md. Branch miner-ui-3, worktree
../igneum-wt-miner-ui-3 from master b79f2cb. The 0.3.14 cut takes it. Ember Tune's engine (branch ember-tune,
agent a855dcc4bd05e0615) is not touched: this plan reads its fields and posts to its routes.
1. The one job of each screen
| Screen | Its one job | What it holds | What left it |
|---|---|---|---|
| Welcome, GPU, address, key | get to mining in two taps with an address the user owns | as miner-ui-2, shorter words, the mark instead of the coin, the key sheet alone | the aphorism, "seed", "secp256k1", "worker", the devnet vote-key note |
| Mine | is the machine mining, and what is it worth | the big button, the live total (MH/s, W, £ a day), the card rows, the tuning card, the node line, activity | the next-program tile (to the node details), the four bare counts, the JSON in the feed |
| Earnings | what did it make, where does it go | money and IGN first, blocks, the address, change address, show key, the dev fee, the wallet | the balance placeholder, the "once" eyebrow |
| Prove | can this machine prove, and is it | one switch, one tier sentence per card, one counts sentence, Set up when needed; details behind a disclosure | the 95-word lead, the four zero tiles, the two hashes on the surface |
| Settings | the switches, one sentence each | tuning (goal, Ember Tune, Power control, electricity price), this machine, updates (one card), remote jobs (one row, history behind it), logs, appearance, advanced | the per-card controls (to the card), the spec paragraph, the second "Off" sentence |
| Node | gone as a section | one line on Mine; "Details" opens the chain numbers, digest, switches, finality, version, clock | the eleven-number page |
| Updates | gone as a section | one card on Settings; the update card and the strip as they are | the jobs table on the surface |
| Logs | the engineer's view | the drawer as it is | nothing |
Four sections in the rail: Mine, Earnings, Prove, Settings. Logs and Quit in the foot. The rail keeps the arrow
keys and aria-current.
2. The card row, the hero object
One row per card, ordered by performance (branch card-order ffb2bfa, View.shownCards). Everything a card can
say is on its row; everything a card can be told is on its row or one tap under it.
[NV] NVIDIA GeForce RTX 5090 DISCRETE 122 MH/s 290 W £1.95 a day 61 °C [Tune] (o)
● mining · 32 GB · 3 blocks 0.42 MH/W at 28p/kWh
Tuned 2 h ago · 2470 MHz at 100% · next check Sunday ⌄
| Part | Rule |
|---|---|
| Name and model | cd.name, the kind pill (Discrete, Integrated, External, Apple silicon), the tooltip as today (View.cardTitle) |
| State word | always present, with the reason: mining, tuning: step k of n (plus "about N min left"), paused, waiting for the node, restarting in N s (plus the engine's message), starting, off (plus the reason when the engine gives one), held for a remote job: <title>, stopped after repeated faults, failed, not usable (Code 43) (plus the reboot hint), removed |
| Rate | hash_now as 122 MH/s (one decimal under 100); blank with the state word when not mining; never a 0 |
| Power | power_w as 290 W with 0.42 MH/W under it (eff_mhw, or rate over watts); a card that reports no draw (Apple silicon, an integrated card) hides the cell and the sub-line says no power or temperature reading on Apple silicon once |
| Money | £1.95 a day = W × 24 / 1000 × price / 100 at the electricity price; with no price the cell reads set a price and taps to the price field; with no draw the cell is hidden |
| Temperature | 61 °C, amber from 86 °C (memory 90), ember from 92 °C (memory 95); hidden when not reported |
| Switch | the card on or off, as today (api/cards with the whole list) |
| Tune | Tune queues api/sweep/start {key}; during a run the button is Stop (api/sweep/stop); hidden for a card with no control and no measurement (sweep_supported false and vendor other) |
| Tune line | the schedule sentence, section 4 |
| Disclosure | the chevron opens the card's details: the power cap slider (NVIDIA, with the watt number, the applied or Retry line), identities (stepper, one sentence), pin or unpin, the memory temperature, the clocks, the device facts, the last tune's table (tune_curve) when there is one |
The rule from ember-tune.md 6a holds everywhere: a card never shows 0 MH/s without a reason word. A paused miner's
row says paused; the switch stays on because the card is on. The old n/a cells are gone: a cell is shown with a
number or not shown.
3. The Mine page
[ Start mining ] 122 MH/s · 290 W · £1.95 a day · 3 blocks today
mining on 1 of 1 card
Your cards [Tune all]
row
row
Tuning Efficiency | Balanced | Maximum about £1.95 a day at this goal
Ember Tune on: once after install, then every 7 days. Last tune 2 h ago, next check Sunday.
Power control [switch] Lets the app set NVIDIA power and clock limits. Windows asks once.
Node synced · 6 peers · 109,859 blocks · read 5 s ago Details ⌄
Activity Open the log
strip (today's blocks on a 10-minute line)
the last 8 events, one line each
| Element | Rule |
|---|---|
| The big button | the only primary (ember) control on the page. Start mining / Stop mining / Tuning (disabled, "mining again in about N min") / Held / Stopping. View.toggle, with ember-tune's tuning and held states. |
| The live total | hash_total MH/s; W = the sum of power_w over present cards; £ a day from that sum; blocks today = found in the last 24 h is not on the state, so "N this run" (accepted_session) with the lifetime count in the sub-line |
| Tune all | queues api/sweep/start for every present, enabled, tunable card in list order (the engine runs one at a time). Hidden when Ember Tune is off fleet-wide (settings.tuning_off), where the line says why. |
| Goal | a three-way segmented control, settings.tune_goal efficiency / balanced / rate (labelled Maximum), POST /api/tune/goal {goal}. The consequence line: at Efficiency the tuned watts (sweep_watts summed) at the price; at Maximum the default watts (power_default_w summed); Balanced between. A build without the route (0.3.13) gets a toast and the control stays on its old value. |
| Schedule | sweep_at and settings.tune_period_s (default 604,800): "Last tune 2 h ago, next check Sunday" per the last-tuned card; "Not tuned yet: the first tune starts 2 min into steady mining" when nothing has run; "Tuning now: RTX 5090, step 7 of 9" while one runs. |
| Power control | one switch, one sentence. Shown on Mine only when an NVIDIA card is present and control is off (the fix where the problem is); always on Settings. |
| Node line | View.nodeWords; Details opens the old Node page's content in place: the chain numbers (headers, DAA score, difficulty, tips, blue score, each with its one-line meaning), the digest with Copy, the switches with the next one, finality, the version, the clock card when the clock is off (the clock card also shows above the big button when it blocks mining). |
| Activity | the blocks canvas at 110 px with "N blocks in the last 10 min · N in the last hour"; the feed shows the newest 8 events, a line cut at 140 characters, a JSON tail replaced by "(details in the log)"; a link opens the drawer |
4. Tuning in words
| State | The row's tune line | Where else |
|---|---|---|
| never run, Ember Tune on | Not tuned yet: starts after 2 min of steady mining |
the Tuning card's schedule line |
| running | Tuning: step 7 of 9 · about 4 min left with a thin progress bar (tune_step / tune_steps) and the Stop button; the state word reads tuning: step 7 of 9 and the rate stays live |
the strip (ember-tune's tuneNotice), the big button |
| tuned | Tuned 2 h ago · 2470 MHz at 100% · next check Sunday, the Tuned line's numbers (tune_line: 122.3 MH/s at 290 W, 0.422 MH/W) in the details |
the Tuning card |
| from the fleet prior | Tuned 2 min ago from the fleet prior, confirmed · next check Sunday |
|
| measure only (Apple, NVIDIA with Power control off, AMD on Linux) | Measured 122 MH/s at 290 W (0.42 MH/W) as it runs and the reason from sweep_note (measure only on Apple silicon: the system sets the clocks and the power) |
Power control switch on Mine for the NVIDIA case |
| pinned | Your cap stays pinned at 80% · measured ... with Unpin in the details |
|
| stopped | Tuning stopped: a remote job took the GPU until the next run |
|
| fleet pause | Tuning paused fleet-wide by the signed manifest on the Tuning card; the Tune buttons hidden |
Settings |
| no control, no measurement (an integrated card, vendor other) | no tune line, no button |
Next check = sweep_at + settings.tune_period_s, as the weekday when under 7 days away, else the date; "due now"
when sweep_at is 0 and the card mines. The engine also re-tunes on a driver or program change; the line does not
predict that.
5. Earnings
£0.00 earned IGN: nothing is bought or sold on devnet
0.0000 IGN from 0 proofs
23,056 blocks lifetime · 0 this run · 1 in 100 to the dev fee (0 so far) [dev fee switch]
£1.95 a day electricity at 28p/kWh for 290 W Set the price
Rewards address 0xdd44…86E8 [Copy] made on this machine
Change address ⌄ → a box, Change, then in place: "Pay 0x12…34 from the next block? Confirm Cancel"
Show my key ⌄ → in place: "Show the private key on screen? Anyone who sees it can spend." Show Cancel
then the key box, Copy, Hide, the file path
[Open the wallet] the balance is in the wallet
Money first, IGN second, as asked. Devnet has no price for IGN, so the £ earned line says why it is £0.00 instead
of showing a placeholder; the electricity line is real money and is shown as a cost. When a market price exists the
engine adds price_gbp_per_ign and the line fills in (owed, engine).
6. Prove
Prove on this machine [switch]
RTX 5090: proves while it mines (32 GB; a full shard needs 20.4 GB).
RTX 4070: cannot prove a full shard (12 GB; a full shard needs 20.4 GB).
Apple M5 Max: proves on the CPU, slowly.
State: idle · nothing assigned · 0 assigned · 0 proven · 0 paid · 0.0000 IGN
[Set up] About 20 minutes, once. (only when the prover is not installed)
Details ⌄ verifier (word and reason), the program id and the aggregator id with Copy, the segments line
The tier sentence per card: NVIDIA with 24 GB or more "proves while it mines"; NVIDIA under 24 GB "cannot prove a
full shard (N GB; a full shard needs 20.4 GB)"; Apple silicon "proves on the CPU, slowly"; AMD and other "cannot
prove yet (the prover is CUDA only)". The 20.4 GB and the 24 GB rule are the app's own words today
(index.html, Prove lead); the engine's proving.available and backend win when they disagree.
7. Settings
Plain rows, one sentence each, grouped:
| Group | Rows |
|---|---|
| Tuning | goal (segmented) · Ember Tune (switch: "Tunes every card for hashes per watt: once after install, then every 7 days, and after a driver or program change.") · Power control (switch: "Lets the app set NVIDIA power and clock limits. Windows asks for administrator rights once. Off, NVIDIA cards are measured only.") · Electricity price (pence per kWh; the £ figures use it) · Hill climb (switch, Ember 2, when tune_climb is on the state) |
| This machine | Start at login · Name · Allow remote jobs from Igneum ("Signed jobs run here once and report back.") with "History" opening the table and the signing key · Appearance: System / Light / Dark |
| Updates | one card: "Igneum Miner 0.3.13 · up to date, checked 13 min ago" · Check · Install now when ready · "Install updates by itself" switch |
| Logs | Copy the log · Show the log · the two folders |
| Advanced | Vote on finality checkpoints · Trust proof records (devnet, confirm in place, restarts the node) · Open the live devnet page · the machine id |
The dev fee moved to Earnings (it is money). Identities and the power cap moved to the card.
8. Confirmations in place
No confirm(), no dialogs (the viewer never shows them). A destructive or costly action turns its own row into a
question with two buttons:
| Action | The row says |
|---|---|
| Quit | Quit Igneum Miner? Mining stops first, then the node. Quit Cancel |
| Change address | Pay 0x12…34 from the next block? The miner restarts. Confirm Cancel |
| Show my key | Show the private key on screen? Anyone who sees it can spend what the address holds. Show Cancel |
| Trust mode | Include unverified proof records? Devnet only; the node restarts. Turn on Cancel |
| Stop a tune, stop mining, switch a card off, Tune all, the dev fee, identities | no question: each reverts in one tap and the toast says what happened |
9. Type, spacing, colour
| Token | Value | Use |
|---|---|---|
--t-xs 11, --t-sm 12, --t-base 13, --t-md 14, --t-lg 15, --t-xl 17, --t-num 24, --t-hero 34, --t-h2 28, --t-h1 44 |
px | eight sizes; Unbounded only for the page title, the big button, the hero numbers and the row's rate; IBM Plex Sans for words; IBM Plex Mono for labels, units and small numbers |
--s-1 4, --s-2 8, --s-3 12, --s-4 16, --s-5 24, --s-6 32 |
px | one spacing scale; cards pad --s-5, rows pad --s-4, gaps --s-3 |
| radius | 16 card, 12 row, 10 button, 999 pill | |
| dark | obsidian #0C0C0E page, graphite #16161A card, #111114 row, lines #2A2A30 |
the brand |
| light | #F4F1EC (bone) page, #FFFFFF card, #FAF8F5 row, lines #E2DED8, ink #16161A, ash #6B6B70 |
prefers-color-scheme: light and [data-theme=light]; ember stays #F2541B, molten darkens to #B8731F for text on light |
| ember | the big button, the one primary per view, the rate, errors | one accent |
| molten | live states (mining, synced), the pill |
Contrast: every text on its background at 4.5:1 or more (ash on obsidian 5.9:1; ash on bone 5.2:1).
10. Phone width and the narrow window
| Width | Layout |
|---|---|
| 1180 px and up | rail 196 px, the hero in one row, the rows with every cell |
| 900 to 1180 | rail folds to icons, the hero wraps to two lines |
| 720 to 900 | rail 76 px, the row's money and temperature cells go under the name as a second line |
| under 720 | the rail becomes a bottom tab bar (Mine, Earnings, Prove, Settings; Logs and Quit under Settings), the hero stacks, every row is two lines (name and state; numbers in a 2 by 2 grid), the big button full width, 16 px gutters, no horizontal scroll |
11. What the engine is asked for (none of it built here)
| Field or route | Why | Owner |
|---|---|---|
settings.tune_goal, settings.power_price_pence, settings.tune_period_s, POST /api/tune/goal |
the goal, the price, the schedule | ember-tune (exists at e9738dd) |
tune_step, tune_steps, tune_eta_s, tune_plan, tune_curve, states tuning and held |
the running line, the details table | ember-tune (exists) |
watts_total on mining and pounds_per_day |
the tray tip and the menu bar; the UI sums the cards itself | engine, owed |
price_gbp_per_ign |
£ earned when a market exists | engine, later |
address.balance_wei |
the balance on Earnings (miner-ui-2 follow-up) | engine, owed |
Until a field arrives the UI treats it as unset: no goal control change is sent to a build without the route (the toast says "needs the Ember Tune update"), the price is kept in the page's own storage as a fallback, the schedule line says "not tuned yet".
12. Tests
view.test.mjs grows with: the row model for every state word (mining, tuning with step and eta, paused, waiting,
restarting with the message, off with the reason, held, faulted, failed, unusable, removed), the money line at a
price and without one, the power and temperature cells hidden when not reported, the tune line for never, running,
tuned, prior, measured, pinned, stopped and fleet-paused, the next-check weekday, the prove tier sentence per vendor
and memory, the node line, the goal consequence, the quit and address confirmations' words. tune-line.test.mjs
is ember-tune's version (the row word, the button, the strip). notices.test.mjs and update-card.test.mjs are
unchanged and pass.
13. The build (6 October 2026, evening)
Branch miner-ui-3, three UI files rewritten, nothing on the Rust side (the engine is read as it is; the Ember
fields are read when present and treated as unset when a build lacks them). Screenshots of the result beside the
audit's: docs/plans/miner-ui-3/n00-*.png to n26-*.png (the mock on port 4317 from this worktree, the rig,
the Mac, tuning, tuned with the details open, the node details, earnings with the address ask, prove, settings,
the quit ask, paused, syncing, the clock, the update card, the job strip, the logs, 900 px, 390 px, light).
| File | What |
|---|---|
app/igneum-app/ui/index.html |
four pages behind the rail; the Mine hero (big button, the live total: MH/s, W, £ a day, blocks); the Tuning card (goal, schedule, Power control when it is the fix); the node line with Details; Activity with the strip and eight events; Earnings (money rows, the dev-fee switch, the address, Change address and Show my key as disclosures with their asks in place); Prove (switch, tier list, one counts line, Set up, Details); Settings (Tuning, This machine with the jobs row and History, Appearance, the update card, Logs, Advanced with the trust ask); the quit ask strip; the key sheet with the key first; the welcome with the mark, not the retired coin |
app/igneum-app/ui/app.css |
dark and light token sets (prefers-color-scheme and [data-theme]), the row grid (.gl-main, .nums, .acts, .gl-tune, .gl-details), the totals, the segmented control, the disclosures, the asks, the bottom tab bar under 720 px, the folded rail under 1000 px |
app/igneum-app/ui/app.js |
Notices with ember-tune's tuneNotice; UpdateCard.NAME is "Igneum Miner" (was "Igneum Ember"); View adds rowWord, cells, poundsPerDay, money, effOf, wattsTotal, noReading, tuneWords, nextCheck, schedule, goalWords, nodeLine, proveTier, proveLine, eventLine, addressAsk, trustAsk and ember-tune's tuneWord, tuneEta, tuningCard and toggle states; TuneLine is ember-tune's plus pounds; the DOM block renders the four pages, the row with its details (cap slider, identities, pin, telemetry, the tune table, the facts), the asks, the goal (POST api/tune/goal, a toast when the build lacks it), the price (the engine's power_price_pence when set, else the page's own storage), the appearance; `?tune=running |
app/igneum-app/ui/view.test.mjs |
7 new tests: the row word per state (paused on an on card, held, tuning, starting, failed), money and the cells, the tune line for every state, the next check, the schedule and the goal, the prove tier and line, the node line, the activity line, the asks |
app/igneum-app/ui/tune-line.test.mjs |
ember-tune's version (e9738dd) as is, so the ember-tune merge carries no test change |
app/igneum-app/ui/update-card.test.mjs |
one expectation: the card's name |
node --test on the four UI files: 35 pass. The Rust side is untouched, so the app's unit tests are as master's; the
0.3.14 shipper runs them on PC 2 as usual (the rule: test suites run on the PCs).
The tick list (section 3 of the audit)
| # | Feature | Where it is now |
|---|---|---|
| 1 | Setup screens and the key sheet | as before; the address screen's button says Continue until "Use my own address" is picked |
| 2 | Start / stop | the big button |
| 3 | Per-card switch | the row |
| 4 | Rate, temperature, power per card; totals | the row's cells (left out when not reported); the hero's total with W and £ |
| 5 | Blocks found, this run, last 10 min, last hour, dev-fee count, chain height | the hero (this run, lifetime), Activity's line, the dev fee on Earnings, the height on the node line |
| 6 | Next program countdown and epoch | the node details |
| 7 | Blocks strip | Activity, 110 px |
| 8 | Activity feed | Activity, eight lines, JSON cut |
| 9 | Hot-plug states | the row (the words and the strip as before) |
| 10 | Proving switch, setup, counts, segments, verifier, program ids | Prove: the switch, the tier list, one line, Set up, Details |
| 11 | Address with Copy, source, blocks | Earnings |
| 12 | Show / hide the key, the file path | Earnings, "Show my key" with the ask |
| 13 | Change the address | Earnings, "Change address" with the ask |
| 14 | Open the wallet | Earnings |
| 15 | Node state, height, peers, version, chain numbers, digest, switches, finality, sync ETA | the node line and its Details |
| 16 | Clock card with Sync clock | Mine, above the hero (and the strip on the setup screens) |
| 17 | Version, update note, Check, Install now, auto-update | Settings, one card |
| 18 | The update card and the strip | unchanged |
| 19 | Remote jobs: allow, Check now, note, history, signing key | Settings, This machine: the switch, the line, History |
| 20 | Power cap slider, applied / Retry, telemetry, efficiency | the row's details; MH/W on the row |
| 21 | Identities | the row's details |
| 22 | Ember Tune switch, Tune / Stop / Unpin, the tune line | Settings (switch); the row (Tune, Stop, the line); Unpin in the details; Tune all on the card list |
| 23 | Power control | Settings, and Mine's Tuning card when an NVIDIA card has it off |
| 24 | Start at login, vote, dev fee, name, trust, live page | Settings (vote and trust under Advanced), the dev fee on Earnings |
| 25 | Copy the log, show the log, the folders | Settings, Logs |
| 26 | The log drawer | unchanged |
| 27 | Quit | the rail, with the ask strip instead of a dialog |
| 28 | The pill, the rail foot | the pill (with "tuning"); the foot has the name, the address, the uptime |
| 29 | Keyboard | the rail's arrows (left and right too), Escape on the card and the quit ask, Enter on the price and the time box |
| 30 | The URL switches | as before, plus ?tune= |
| new | Goal, Tune all, the price, £ a day, MH/W on the row, the tune schedule, the prove tier, the node line, light mode, appearance, the bottom tab bar | this build |
Owed
| What | Who |
|---|---|
watts_total and pounds_per_day on the state for the tray tip and the menu bar; address.balance_wei; price_gbp_per_ign |
the engine (section 11) |
The wallet's copy of the update card still says "Igneum Ember" (app/igneum-wallet/ui/update-card.js) |
the wallet |
| A Windows and a PC screenshot of the row with a real NVIDIA card (the telemetry cells, the cap slider, Retry) | the 0.3.14 cut, first thing to look at |
| The row's state word while tuning carries the meta after it ("tuning: step 7 of 9 · measuring 441 W · 32 GB · ...") and is cut on a narrow row | a later pass: drop the meta while tuning |
The merge with ember-tune: app.js takes this branch's file (it already carries ember-tune's UI words and test); the engine files merge by themselves |
the 0.3.14 shipper |