igneum/app/igneum-app/ui/index.html
igneum-labs 6bf6af9533 Igneum Miner UI 2: six sections behind a rail (Mine, Prove, Rewards, Node, Updates, Settings)
The one long page becomes a rail with six sections, a thin top bar and the status strip under it; the setup
screens and the key sheet stay. Mine: one big start/stop, the numbers, one row per GPU with its switch, hash
rate, temperature and power. Prove: the switch with plain words, the counts, the verifier, the pinned ids.
Rewards: the address with one Copy, the key backup card, another address. Node: the plain lines, the consensus
digest, the next switch. Updates: the version, the jobs. Settings: one switch or slider per setting with one line
of help. Every function that existed is placed, none removed.

Engine (three small additions, each with a unit test): node.consensus_digest from the node's own line,
node.consensus_switches from the override file, proving.program_id and aggregator_id from the host's --mode id.
Hosts: the window minimum is 900 x 600 on both. UI tests: view.test.mjs (10) joins notices and update-card in CI.
Proof: docs/plans/miner-ui-2.md and the 19 screenshots under docs/plans/miner-ui-2/, taken from a build of this
tree running on its own port against the devnet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 19:06:27 +00:00

498 lines
30 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Igneum Miner</title>
<meta name="color-scheme" content="dark">
<link rel="icon" href="mark.svg" type="image/svg+xml">
<link rel="stylesheet" href="app.css">
</head>
<body class="phase-welcome" data-phase="welcome" data-page="mine">
<!-- the rail: one button per section (miner-ui-2). Shown on the dashboard; the setup screens have no rail. -->
<aside class="rail" id="rail" hidden>
<div class="rail-brand">
<img src="mark.svg" width="28" height="28" alt="">
<span class="word">IGNEUM</span>
</div>
<nav class="rail-nav" id="rail-nav" aria-label="Sections">
<button class="nav on" data-page="mine" aria-current="page"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8z"/></svg><span>Mine</span></button>
<button class="nav" data-page="prove"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 4 5v6c0 5 3.4 9.4 8 11 4.6-1.6 8-6 8-11V5l-8-3z"/><path d="m9 12 2 2 4-4"/></svg><span>Prove</span></button>
<button class="nav" data-page="rewards"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18M16 15h2"/></svg><span>Rewards</span></button>
<button class="nav" data-page="node"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3.5 3 14.5 0 18M12 3c-3 3.5-3 14.5 0 18"/></svg><span>Node</span></button>
<button class="nav" data-page="updates"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v11M7 10l5 5 5-5"/><path d="M4 19h16"/></svg><span>Updates</span><i class="nav-dot" id="nav-updates-dot" hidden></i></button>
<button class="nav" data-page="settings"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/></svg><span>Settings</span></button>
</nav>
<div class="rail-foot">
<div class="rail-status mono" id="rail-status"></div>
<button class="nav small" id="btn-logs" aria-expanded="false" aria-controls="drawer"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h10"/></svg><span id="btn-logs-text">Logs</span></button>
<button class="nav small danger" id="btn-quit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v9"/><path d="M6.5 6.5a8 8 0 1 0 11 0"/></svg><span>Quit</span></button>
<div class="rail-version mono" id="foot-version"></div>
</div>
</aside>
<header class="top">
<div class="brand" id="top-brand">
<img src="mark.svg" width="30" height="30" alt="">
<span class="word">IGNEUM</span><span class="miner">MINER</span>
</div>
<div class="page-title" id="page-title" hidden>
<h1 id="page-title-text">Mine</h1>
<span class="page-sub" id="page-sub"></span>
</div>
<div class="top-right">
<span class="top-status mono" id="top-status"></span>
<div class="pill" id="pill"><span class="dot"></span><span id="pill-text">starting</span></div>
</div>
</header>
<!-- the status strip: one notice at a time (updates, remote jobs, the clock), the most important first; app.js fills
it from the state (Notices) and the content below moves once when it appears or goes. -->
<div class="notices" id="notices" hidden>
<div class="notice" id="notice" role="status" aria-live="polite">
<span class="notice-text" id="notice-text"></span>
<span class="notice-actions" id="notice-actions"></span>
<button class="notice-close" id="notice-close" data-act="close" title="Close" aria-label="Close">&times;</button>
<span class="notice-detail mono" id="notice-detail" hidden></span>
<span class="prog" id="notice-prog" hidden><i></i></span>
</div>
</div>
<main id="main">
<!-- 1. welcome -->
<section class="screen" id="screen-welcome">
<div class="hero">
<div class="coin-wrap"><img class="coin" src="coin.png" width="148" height="148" alt=""></div>
<div class="eyebrow ember">devnet v4 &middot; nothing is bought or sold</div>
<h1>Igneum Miner</h1>
<p class="lead">Mined by GPUs. Proven by fire. This app runs a node on this machine and mines Igneum with your graphics card.</p>
<div class="three">
<div class="tile">
<div class="k">01</div>
<div class="t">Runs a node</div>
<div class="s">Syncs the chain from the seed node, usually under a minute.</div>
</div>
<div class="tile">
<div class="k">02</div>
<div class="t">Mines on your GPU</div>
<div class="s">A new program every hour. The next one compiles while you mine.</div>
</div>
<div class="tile">
<div class="k">03</div>
<div class="t">Pays your address</div>
<div class="s">Block rewards go to an EVM address you choose or one made here.</div>
</div>
</div>
<div class="cta">
<button class="btn primary big" id="btn-begin">Get started</button>
</div>
<p class="seedline mono">Nobody from Igneum will ever ask for your seed.</p>
</div>
</section>
<!-- 2. cards -->
<section class="screen" id="screen-cards">
<div class="step">
<div class="eyebrow">step 1 of 2</div>
<h2>Your GPU</h2>
<p class="sub" id="cards-sub">Asking the graphics cards to report in.</p>
<div class="cards" id="cards-list">
<div class="card detecting" id="cards-detecting">
<div class="spinner"></div>
<div>
<div class="t">Detecting</div>
<div class="s mono" id="detect-text">loading the Metal worker</div>
</div>
</div>
</div>
<p class="note" id="cards-note" hidden></p>
<p class="note" id="cards-power" hidden>Igneum caps each NVIDIA card's power at 80% of its default limit to keep it stable. This needs administrator rights once, when mining starts; the limit goes back to what it was on quit. Settings has a slider per card.</p>
<p class="note" id="cards-help" hidden>Each card you switch on gets its own worker. An integrated GPU is off by default: it is slow and shares the machine's memory.</p>
<div class="cta">
<button class="btn primary" id="btn-cards-next" disabled>Continue</button>
<button class="btn ghost" id="btn-cards-retry" hidden>Detect again</button>
</div>
</div>
</section>
<!-- 3. address -->
<section class="screen" id="screen-address">
<div class="step">
<div class="eyebrow">step 2 of 2</div>
<h2>Where should rewards go?</h2>
<p class="sub">Every block this machine finds pays one EVM address. Pick one way. One block in 100 pays the miner software's dev fee; Settings turns it off.</p>
<div class="options">
<label class="option on" id="opt-generate">
<input type="radio" name="mode" value="generate" checked>
<span class="radio"></span>
<span class="body">
<span class="t">Make me an address <span class="tag">recommended</span></span>
<span class="s">A secp256k1 key is made on this machine and stored in the app folder with the file locked to your user. You see the key once, to save it.</span>
</span>
</label>
<label class="option" id="opt-paste">
<input type="radio" name="mode" value="paste">
<span class="radio"></span>
<span class="body">
<span class="t">Use my own address</span>
<span class="s">Paste an EVM address you control. 0x followed by 40 hex characters.</span>
<input type="text" class="addr-input mono" id="addr-input" placeholder="0x" spellcheck="false" autocomplete="off">
<span class="err" id="addr-err" hidden>That is not an EVM address. 0x followed by 40 hex characters.</span>
</span>
</label>
</div>
<p class="err" id="start-blocked" hidden></p>
<div class="cta">
<button class="btn primary big" id="btn-start">Start mining</button>
<button class="btn ghost" id="btn-address-back">Back</button>
</div>
</div>
</section>
<!-- 4. the dashboard: six pages behind the rail -->
<section class="screen" id="screen-dashboard">
<!-- Mine -->
<section class="page" id="page-mine" data-page="mine">
<div class="hero-row">
<button class="toggle-big" id="btn-toggle" disabled>
<span class="ring"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v9"/><path d="M6.5 6.5a8 8 0 1 0 11 0"/></svg></span>
<span class="tl" id="btn-toggle-text">Start mining</span>
<span class="ts" id="btn-toggle-sub">waiting for the engine</span>
</button>
<div class="cell big ember">
<div class="k">hash rate</div>
<div class="v"><span id="d-hash">0.0</span><span class="unit">MH/s</span></div>
<div class="s" id="d-hash-sub">waiting for the worker</div>
</div>
<div class="cell big">
<div class="k">blocks found</div>
<div class="v" id="d-blocks">0</div>
<div class="s" id="d-blocks-sub">accepted by the node</div>
</div>
<div class="cell big">
<div class="k">next program</div>
<div class="v" id="d-eta">--:--</div>
<div class="s" id="d-eta-sub">waiting for the node</div>
</div>
</div>
<div class="card">
<div class="card-head"><h3>Your GPUs</h3><div class="eyebrow" id="d-cards-eyebrow">detecting</div></div>
<div class="gpu-list" id="d-cards"><div class="empty">Waiting for the engine.</div></div>
<p class="note" id="d-cards-note" hidden></p>
</div>
<div class="card">
<div class="card-head">
<h3>Your blocks</h3>
<div class="stats mono"><span>last 10 min <b id="d-found-10">0</b></span><span>last hour <b id="d-found-60">0</b></span><span>dev fee <b id="d-fee">0</b></span><span>chain <b id="d-chain-blocks">0</b></span></div>
</div>
<canvas id="dag" aria-hidden="true"></canvas>
<div class="legend"><span><i class="sw ember"></i>block this machine found</span><span><i class="sw glow"></i>just accepted</span><span><i class="sw line"></i>one minute</span></div>
</div>
<div class="card">
<div class="card-head"><h3>Activity</h3><div class="eyebrow">newest first</div></div>
<div class="feed" id="d-events"><div class="empty">No events yet.</div></div>
</div>
</section>
<!-- Prove -->
<section class="page" id="page-prove" data-page="prove" hidden>
<div class="card lead-card">
<div class="lead-row">
<div class="lead-text">
<h3>Prove shards on this machine</h3>
<p class="help">Every block on Igneum is turned into a short mathematical proof, in pieces called shards. The chain assigns shards to your keys; this machine proves them and is paid for each one.</p>
</div>
<label class="switch lg" title="Prove assigned shards"><input type="checkbox" id="s-prove" aria-label="Prove shards on this machine"><span class="track"></span></label>
</div>
<p class="note" id="pv-note">Off. Switch it on and this machine proves the shards the chain assigns to its keys.</p>
<div class="row" id="pv-setup-row" hidden><button class="btn small primary" id="pv-setup">Set up</button><span class="note">About 20 minutes, once.</span></div>
</div>
<div class="strip four">
<div class="cell"><div class="k">state</div><div class="v state" id="pv-state">off</div><div class="s" id="pv-state-sub">not proving</div></div>
<div class="cell"><div class="k">assigned</div><div class="v" id="pv-assigned">0</div><div class="s">shards given to your keys</div></div>
<div class="cell"><div class="k">proven</div><div class="v" id="pv-submitted">0</div><div class="s">proofs sent to the node</div></div>
<div class="cell"><div class="k">paid</div><div class="v" id="pv-paid">0</div><div class="s" id="pv-paid-sub">shards paid out</div></div>
</div>
<div class="grid2">
<div class="card">
<div class="card-head"><h3>Verifier</h3><div class="eyebrow">the node's check</div></div>
<div class="big-word" id="pv-verifier">not read yet</div>
<p class="help" id="pv-verifier-help">Before a proof counts, the node checks it. A node without a verifier passes proofs along and never includes them.</p>
<p class="note" id="pv-verifier-note" hidden></p>
</div>
<div class="card">
<div class="card-head"><h3>Program</h3><div class="eyebrow">pinned guest</div></div>
<div class="field">
<div class="k">shard program id</div>
<div class="box mono"><span id="pv-program">not read yet</span><button class="btn tiny" data-copy="pv-program">Copy</button></div>
</div>
<div class="field">
<div class="k">aggregator id</div>
<div class="box mono"><span id="pv-aggregator">not read yet</span><button class="btn tiny" data-copy="pv-aggregator">Copy</button></div>
</div>
<p class="help">Every proof names the program that made it. Other nodes accept a proof only from these two ids.</p>
</div>
</div>
</section>
<!-- Rewards -->
<section class="page" id="page-rewards" data-page="rewards" hidden>
<div class="card">
<div class="card-head"><h3>Rewards address</h3><div class="eyebrow" id="r-source"></div></div>
<div class="addr-big mono"><span id="r-address">not set</span><button class="btn small" data-copy="r-address">Copy</button></div>
<p class="help" id="r-address-help">Every block this machine finds pays this address.</p>
</div>
<div class="strip three">
<div class="cell"><div class="k">blocks found</div><div class="v" id="r-blocks">0</div><div class="s">lifetime, accepted by the node</div></div>
<div class="cell"><div class="k">this run</div><div class="v" id="r-session">0</div><div class="s" id="r-session-sub">since the app started</div></div>
<div class="cell"><div class="k">balance</div><div class="v dim" id="r-balance">--</div><div class="s">shown in the wallet, not here yet</div></div>
</div>
<div class="grid2">
<div class="card" id="r-key-card">
<div class="card-head"><h3>Save your key</h3><div class="eyebrow ember">once</div></div>
<p class="help" id="r-key-help">The key for this address was made on this machine and is stored in the app folder, readable by your user only. Keep a copy somewhere safe: anyone with the key can spend what the address holds.</p>
<div class="row" id="r-key-row"><button class="btn small" id="s-reveal">Show my key</button><button class="btn small ghost" id="s-hide" hidden>Hide</button></div>
<div class="box mono key" id="s-key-box" hidden><span id="s-key"></span><button class="btn tiny" data-copy="s-key">Copy</button></div>
<p class="note mono small" id="r-key-file"></p>
</div>
<div class="card">
<div class="card-head"><h3>Use another address</h3></div>
<p class="help">Paste an EVM address you control. The miner restarts and pays the new address from the next block.</p>
<div class="row">
<input type="text" class="addr-input mono" id="s-address-input" placeholder="0x" spellcheck="false" autocomplete="off" aria-label="New rewards address">
<button class="btn small" id="s-address-save">Change</button>
</div>
<div class="err" id="s-address-err" hidden></div>
<p class="note" id="r-devfee-line"></p>
<div class="row"><button class="btn small ghost" id="r-wallet">Open the wallet page</button></div>
</div>
</div>
</section>
<!-- Node -->
<section class="page" id="page-node" data-page="node" hidden>
<div class="strip four">
<div class="cell" id="n-state-cell"><div class="k">node</div><div class="v state" id="d-node">starting</div><div class="s" id="d-node-sub">opening the database</div></div>
<div class="cell"><div class="k">height</div><div class="v" id="n-blocks">0</div><div class="s" id="n-blocks-sub">blocks this node holds</div></div>
<div class="cell"><div class="k">peers</div><div class="v" id="n-peers">0</div><div class="s" id="n-peers-sub">other nodes it talks to</div></div>
<div class="cell"><div class="k">version</div><div class="v small" id="n-version">--</div><div class="s" id="d-node-net">devnet v4</div></div>
</div>
<div class="clock-card" id="n-clock" hidden>
<p class="clock-msg" id="n-clock-msg"></p>
<div class="row"><button class="btn small primary" id="n-clock-sync">Sync clock</button><span class="note mono small" id="n-clock-result"></span></div>
<p class="note" id="n-clock-hint"></p>
</div>
<div class="grid2">
<div class="card">
<div class="card-head"><h3>Chain</h3><div class="eyebrow" id="n-reading">reading</div></div>
<div class="kv">
<div><span class="k">headers</span><span class="v mono" id="n-headers">0</span></div>
<div><span class="k">daa score</span><span class="v mono" id="n-daa">0</span></div>
<div><span class="k">difficulty</span><span class="v mono" id="n-diff">0</span></div>
<div><span class="k">tips</span><span class="v mono" id="n-tips">0</span></div>
<div><span class="k">blue score</span><span class="v mono" id="n-blue">0</span></div>
</div>
<p class="help">Headers arrive before blocks. The DAA score counts blocks the whole network made; the difficulty is how hard the next one is to find.</p>
</div>
<div class="card">
<div class="card-head"><h3>Consensus</h3><div class="eyebrow">the rules</div></div>
<div class="field">
<div class="k">digest</div>
<div class="box mono"><span id="n-digest">not printed yet</span><button class="btn tiny" data-copy="n-digest">Copy</button></div>
<p class="help">The fingerprint of the rules this node runs. Every node on the network shows the same one; a peer with another is refused.</p>
</div>
<div class="field">
<div class="k">next switch</div>
<div class="big-word" id="n-switch">none planned</div>
<p class="help" id="n-switch-help">A switch is a planned rule change. The node applies it by itself when the chain reaches that height.</p>
<div class="switch-list mono" id="n-switches"></div>
</div>
</div>
<div class="card">
<div class="card-head"><h3>Finality</h3><div class="eyebrow">miner-only</div></div>
<div class="kv">
<div><span class="k">last lock</span><span class="v mono" id="f-lock">none yet</span></div>
<div><span class="k">age</span><span class="v mono" id="f-age">n/a</span></div>
<div><span class="k">votes sent</span><span class="v mono" id="f-votes">0</span></div>
</div>
<p class="help" id="f-note">A lock is a point the miners have agreed can never be undone. This machine votes on one every 30 s.</p>
</div>
<div class="card">
<div class="card-head"><h3>Sync</h3><div class="eyebrow" id="n-sync-eyebrow"></div></div>
<div class="big-word" id="n-sync-word">starting</div>
<p class="help" id="n-note"></p>
</div>
</div>
</section>
<!-- Updates -->
<section class="page" id="page-updates" data-page="updates" hidden>
<div class="card lead-card">
<div class="lead-row">
<div class="lead-text">
<h3 id="s-version">Igneum Miner</h3>
<p class="help" id="s-update-note">Not checked yet.</p>
</div>
<div class="row">
<button class="btn small primary" id="s-install" hidden>Install now</button>
<button class="btn small" id="s-update">Check now</button>
</div>
</div>
<label class="switch"><input type="checkbox" id="s-auto-update"><span class="track"></span><span>Install updates by itself</span></label>
<p class="help">Downloads in the background and installs at a quiet moment, never mid-program. Off: it downloads, then waits for Install now.</p>
</div>
<div class="card">
<div class="lead-row">
<div class="lead-text">
<h3>Remote jobs</h3>
<p class="help">Igneum publishes signed jobs (a benchmark, a script, logs to collect) next to the update manifest. This machine runs each one once and reports back. Only jobs signed by Igneum's key run.</p>
</div>
<button class="btn small" id="s-jobs-check">Check now</button>
</div>
<p class="note" id="s-jobs-note"></p>
<div class="job-history" id="s-jobs-history"></div>
<p class="note mono small" id="s-jobs-key"></p>
</div>
</section>
<!-- Settings -->
<section class="page" id="page-settings" data-page="settings" hidden>
<div class="card">
<div class="card-head"><h3>Graphics cards</h3><div class="eyebrow" id="s-cards-eyebrow"></div></div>
<div class="set-cards" id="s-cards"><div class="empty">No card yet.</div></div>
<label class="switch"><input type="checkbox" id="s-sweep"><span class="track"></span><span>Find each NVIDIA card's best efficiency</span></label>
<p class="help">Once after install, then weekly, the power cap steps from 100% down to 50% and holds the step with the most hashes per watt. A cap you set by hand is left alone.</p>
</div>
<div class="card">
<div class="card-head"><h3>This machine</h3></div>
<label class="switch"><input type="checkbox" id="s-login"><span class="track"></span><span>Start at login</span></label>
<p class="help">The miner opens when you sign in and keeps mining in the background.</p>
<label class="switch"><input type="checkbox" id="s-jobs-allow"><span class="track"></span><span>Allow remote jobs from Igneum</span></label>
<p class="help">Signed jobs from Igneum run on this machine and report back. Updates shows what ran.</p>
<label class="switch"><input type="checkbox" id="s-vote"><span class="track"></span><span>Vote on finality checkpoints</span></label>
<p class="help">Your miner signs a checkpoint every 30 s. Votes are what lock the chain; leave it on.</p>
<div class="field">
<div class="k">name</div>
<div class="row">
<input type="text" class="addr-input" id="s-name" placeholder="a name for this machine" maxlength="40" spellcheck="false" aria-label="Machine name">
<button class="btn small" id="s-name-save">Rename</button>
</div>
<p class="help">A label for you only. Keys come from the machine id <span class="mono" id="s-mid"></span>, never from the name.</p>
</div>
</div>
<div class="card">
<div class="card-head"><h3>Dev fee</h3></div>
<label class="switch"><input type="checkbox" id="s-devfee"><span class="track"></span><span id="s-devfee-text">Dev fee 1% (1 block in 100)</span></label>
<p class="help" id="s-devfee-note">One block in 100 is mined for the miner software's author, the same way every GPU miner takes a fee. The protocol itself takes nothing. This switch turns it off.</p>
</div>
<div class="card">
<div class="card-head"><h3>Logs</h3></div>
<div class="row wrap">
<button class="btn small" id="s-log-copy">Copy the log</button>
<button class="btn small ghost" id="s-log-open">Show the log</button>
</div>
<p class="help">Copies the last lines the node and the miner wrote, for a support message. Show the log opens the drawer with every line.</p>
<p class="note mono small" id="s-log-dir"></p>
<p class="note mono small" id="s-node-dir"></p>
</div>
<details class="card adv" id="s-advanced">
<summary><h3>Advanced</h3><span class="eyebrow">devnet tools</span></summary>
<label class="switch"><input type="checkbox" id="s-trust"><span class="track"></span><span>Trust proof records without verifying them</span></label>
<p class="help" id="s-trust-note">Devnet only. When no verifier is found next to the engine, the node includes proof records it never checked. A found verifier always wins. Changing this restarts the node.</p>
<div class="row"><button class="btn small ghost" id="s-live" hidden>Open the live devnet page</button></div>
</details>
</section>
</section>
</main>
<!-- the update card (app.js, UpdateCard): one update, centred; Later, Escape or the backdrop leaves the strip above -->
<div class="upd-wrap" id="upd" hidden>
<div class="upd-card" id="upd-card" role="dialog" aria-modal="true" aria-labelledby="upd-name" aria-describedby="upd-line" tabindex="-1">
<div class="upd-mark" id="upd-mark">
<svg class="upd-ring" viewBox="0 0 96 96" aria-hidden="true"><circle class="track" cx="48" cy="48" r="44"></circle><circle class="arc" id="upd-arc" cx="48" cy="48" r="44"></circle></svg>
<img src="mark.svg" width="40" height="40" alt="">
<span class="upd-pct mono" id="upd-pct" hidden></span>
</div>
<div class="eyebrow ember">update</div>
<h2 class="upd-name" id="upd-name"></h2>
<p class="upd-line" id="upd-line"></p>
<p class="upd-cause mono" id="upd-cause" hidden></p>
<ul class="upd-notes" id="upd-notes"></ul>
<button class="upd-more" id="upd-more" type="button" aria-expanded="false" hidden>What changed</button>
<ul class="upd-all" id="upd-all" hidden></ul>
<p class="upd-meta mono" id="upd-meta"></p>
<div class="upd-actions" id="upd-actions"></div>
</div>
</div>
<!-- the key sheet, shown once -->
<div class="sheet-wrap" id="sheet-key" hidden>
<div class="sheet">
<div class="eyebrow ember">shown once</div>
<h2>Save your key</h2>
<p class="sub">This key controls the rewards address. Keep a copy somewhere safe. Anyone with the key can spend what the address holds.</p>
<div class="field">
<div class="k">rewards address</div>
<div class="box mono"><span id="key-address"></span><button class="btn tiny" data-copy="key-address">Copy</button></div>
</div>
<div class="field">
<div class="k">private key</div>
<div class="box mono key"><span id="key-private"></span><button class="btn tiny" data-copy="key-private">Copy</button></div>
</div>
<p class="note">Stored at <span class="mono" id="key-file"></span>, readable by your user only. Rewards can show it again.</p>
<p class="note">On devnet the vote keys are test keys derived from the miner's label. Mainnet vote keys will be random and stored like this wallet.</p>
<label class="check"><input type="checkbox" id="key-ack"><span>I have saved my key</span></label>
<div class="cta">
<button class="btn primary big" id="btn-key-done" disabled>Start mining</button>
</div>
</div>
</div>
<!-- the log drawer: chips filter by source, search filters by text, the ruler and the jump controls move in time.
The list is virtualised (fixed row height, only the visible rows are in the DOM) so 20,000 lines scroll smoothly. -->
<div class="drawer" id="drawer" aria-label="Logs">
<div class="drawer-grip" id="drawer-grip" title="Drag to resize"><i></i></div>
<div class="drawer-head">
<div class="chips" id="log-chips">
<button class="chip on" data-src="">all</button>
<button class="chip" data-src="app">app</button>
<button class="chip" data-src="node">node</button>
<button class="chip" data-src="miner">miner</button>
<button class="chip" data-src="watch">watch</button>
</div>
<div class="log-search">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"></circle><path d="m20 20-3.5-3.5"></path></svg>
<input type="search" id="log-search" placeholder="search" spellcheck="false" autocomplete="off" aria-label="Search the log">
<span class="count mono" id="log-count"></span>
</div>
<div class="log-nav">
<input type="text" class="mono" id="log-time" placeholder="HH:MM:SS" maxlength="8" spellcheck="false" autocomplete="off" aria-label="Jump to a time" title="Jump to a time (Enter)">
<button class="chip" id="log-last-error" title="Jump to the last error">last error</button>
<button class="chip" id="log-last-swap" title="Jump to the last program swap">last swap</button>
</div>
<div class="log-tools">
<span class="mono dim at" id="log-at" title="Time of the top line in view"></span>
<button class="chip" id="log-wrap" title="Wrap long lines (up to 5,000 lines)">wrap</button>
<button class="chip" id="log-copy" title="Copy the lines in view">copy</button>
<label class="check small"><input type="checkbox" id="log-follow" checked><span>follow</span></label>
<button class="chip" id="log-close" title="Close the log">close</button>
</div>
</div>
<div class="log-ruler" id="log-ruler" title="Click to jump"><span class="t0 mono" id="log-ruler-0"></span><span class="t1 mono" id="log-ruler-1"></span><i class="win" id="log-ruler-win"></i></div>
<div class="log-view" id="log-view">
<div class="log-pad" id="log-pad"><pre class="log" id="log"></pre></div>
<div class="log-empty mono" id="log-empty" hidden>Nothing logged yet.</div>
</div>
<button class="btn small log-jump" id="log-jump" hidden><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"></path></svg><span id="log-jump-text">Newest</span></button>
</div>
<div class="toast" id="toast" hidden></div>
<script src="app.js"></script>
</body>
</html>