The reference page's composition in our tokens and words, every number from the observer: the header row (eyebrow, the title with its ember full stop, one sentence, two buttons, the state word), six tiles in one hairline row each with a definition, the block graph panel (our live-dag.js in the graph slot, the block-count chip, selected chain only, pause and follow, the inspector column with hash, lane, time, blue score, DAA, chain, colour word, shards, checkpoint and lock state, the drag-to-explore line, the zoom control, the legend row and the blue score range), the three panels (block activity with per-minute bars, mining identities with share bars and the reader's own key marked when set, the event stream), the blocks table, our footer. live-dag.js gains an onSelect hook on click (additive; the app's copy unchanged). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
464 lines
42 KiB
HTML
464 lines
42 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<title>Igneum live devnet</title>
|
||
<meta name="description" content="The Igneum network observatory: every block and its connections, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<link rel="canonical" href="https://igneum.network/live">
|
||
<meta name="theme-color" content="#0C0C0E">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:site_name" content="Igneum">
|
||
<meta property="og:title" content="Igneum live devnet">
|
||
<meta property="og:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<meta property="og:url" content="https://igneum.network/live">
|
||
<meta property="og:image" content="https://igneum.network/og-small.png?v=3">
|
||
<meta property="og:image:width" content="256">
|
||
<meta property="og:image:height" content="256">
|
||
<meta property="og:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="Igneum live devnet">
|
||
<meta name="twitter:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<meta name="twitter:image" content="https://igneum.network/og-small.png?v=3">
|
||
<meta name="twitter:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Crect width='1024' height='1024' fill='%230C0C0E'/%3E%3Cg transform='translate(166.95 166.95) scale(6.901)'%3E%3Cpolygon points='50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34' fill='%23F2541B'/%3E%3Cpolygon points='50,42 59,58 50,82 41,58' fill='%230C0C0E'/%3E%3C/g%3E%3C/svg%3E" type="image/svg+xml">
|
||
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
||
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
|
||
<link rel="icon" href="/icon-192.png" type="image/png" sizes="192x192">
|
||
<link rel="icon" href="/icon-512.png" type="image/png" sizes="512x512">
|
||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
||
<link rel="manifest" href="/site.webmanifest">
|
||
<!-- head:start -->
|
||
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="stylesheet" href="/site.css">
|
||
<style>
|
||
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
|
||
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-sans-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-sans-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-sans-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
|
||
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
|
||
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
|
||
</style>
|
||
<script>
|
||
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
|
||
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
|
||
</script>
|
||
<!-- head:end -->
|
||
<style>
|
||
/* live page block (7 Oct 2026, site-ui-4: the observatory composition, every number read from the observer); tokens and components are in /site.css */
|
||
.obs{--pr:8px;--hair:1px solid var(--line)}
|
||
.obs-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--s-4) var(--s-5);padding:var(--s-6) 0 var(--s-5)}
|
||
.obs-head .eyebrow{display:flex;align-items:center;gap:8px;margin-bottom:var(--s-3)}
|
||
.obs-head .eyebrow i{width:7px;height:7px;background:var(--ember);transform:rotate(45deg);display:inline-block}
|
||
.obs-head h1{font-size:clamp(34px,4.6vw,56px);font-weight:700;letter-spacing:-.02em;margin:0}
|
||
.obs-head h1 b{color:var(--ember-text)}
|
||
.obs-head p{margin:var(--s-3) 0 0;color:var(--ink-2);max-width:60ch}
|
||
.obs-head .side{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s-2)}
|
||
.obs-head .side .state{font-size:var(--t-xs)}
|
||
.tiles6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));border:var(--hair);border-radius:var(--pr);background:var(--graphite);overflow:hidden}
|
||
.tiles6>div{padding:18px 19px 19px;border-right:var(--hair);min-width:0}
|
||
.tiles6>div:last-child{border-right:0}
|
||
.tiles6 .k{display:flex;justify-content:space-between;align-items:center;font:400 13px/1.4 var(--f-sans);color:var(--ink-2)}
|
||
.tiles6 .k i{color:var(--ash);font:400 12px/1 var(--f-mono)}
|
||
.tiles6 .v{font:500 clamp(22px,2vw,30px)/1.15 var(--f-sans);color:var(--bone);margin:14px 0 10px;letter-spacing:-.01em;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
||
.tiles6 .v.word{font:500 15px/1.3 var(--f-mono);color:var(--ash)}
|
||
.tiles6 .d{font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
@media (max-width:1100px){.tiles6{grid-template-columns:repeat(3,minmax(0,1fr))}.tiles6>div:nth-child(3){border-right:0}.tiles6>div:nth-child(-n+3){border-bottom:var(--hair)}}
|
||
@media (max-width:600px){.tiles6{grid-template-columns:repeat(2,minmax(0,1fr))}.tiles6>div{border-right:var(--hair)!important;border-bottom:var(--hair)}.tiles6>div:nth-child(2n){border-right:0!important}.tiles6>div:nth-last-child(-n+2){border-bottom:0}}
|
||
.panel{border:var(--hair);border-radius:var(--pr);background:var(--graphite);margin-top:var(--s-5);overflow:hidden}
|
||
.toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:15px 21px;border-bottom:var(--hair);background:var(--row)}
|
||
.toolbar .title{display:flex;align-items:center;gap:10px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
|
||
.toolbar .title i{width:14px;height:14px;border:1.5px solid var(--ember);border-radius:50%;display:inline-block;position:relative}
|
||
.toolbar .title i::after{content:"";position:absolute;inset:4px;background:var(--ember);border-radius:50%}
|
||
.chip{font:400 11px/1.2 var(--f-mono);color:var(--ash);border:var(--hair);border-radius:4px;padding:5px 8px;white-space:nowrap}
|
||
.toolbar .right{margin-left:auto;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||
.tb{background:transparent;border:1px solid transparent;color:var(--ash);font:400 13px/1.2 var(--f-sans);padding:7px 10px;border-radius:6px;cursor:pointer}
|
||
.tb:hover{color:var(--bone)}
|
||
.tb[aria-pressed="true"]{color:var(--bone);border-color:var(--line-2);background:var(--graphite)}
|
||
.tb.on{color:var(--ember-text);border-color:var(--ember-40)}
|
||
.sep{width:1px;height:18px;background:var(--line)}
|
||
.graph{display:grid;grid-template-columns:minmax(0,1fr) 250px}
|
||
@media (max-width:1000px){.graph{grid-template-columns:1fr}}
|
||
.gcol{padding:18px 21px 14px;min-width:0;border-right:var(--hair)}
|
||
@media (max-width:1000px){.gcol{border-right:0;border-bottom:var(--hair)}}
|
||
.gcol .state{margin-bottom:10px;font-size:var(--t-xs)}
|
||
.scene{position:relative}
|
||
#dag{display:block;width:100%;height:clamp(300px,36vw,440px);border-radius:6px;background:var(--row);touch-action:pan-y}
|
||
.tip{position:absolute;left:0;top:0;pointer-events:none;background:var(--graphite);border:1px solid var(--line-2);border-radius:6px;padding:10px 12px;font:400 12px/1.5 var(--f-mono);color:var(--ink-2);white-space:nowrap;z-index:5}
|
||
.tip[hidden]{display:none}
|
||
.gfoot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-top:12px;font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
.zoom{display:inline-flex;align-items:center;border:var(--hair);border-radius:6px;overflow:hidden}
|
||
.zoom button{background:transparent;border:0;color:var(--ink-2);font:400 13px/1 var(--f-mono);padding:8px 12px;cursor:pointer}
|
||
.zoom button:hover{background:var(--hover)}
|
||
.zoom span{font:400 11px/1 var(--f-mono);color:var(--ash);padding:0 6px;min-width:44px;text-align:center}
|
||
.icol{padding:18px 21px;min-width:0;font-size:13px}
|
||
.icol .ih{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:14px}
|
||
.icol .ih .eyebrow{margin:0}
|
||
.icol .num{font:500 22px/1.2 var(--f-sans);color:var(--bone);margin:6px 0 4px;overflow-wrap:anywhere}
|
||
.icol .hash{font:400 11px/1.5 var(--f-mono);color:var(--ash);overflow-wrap:anywhere;margin-bottom:14px}
|
||
.insp{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 10px;margin:0;border-top:var(--hair);padding-top:8px}
|
||
.insp dt{font:400 11px/1.3 var(--f-mono);color:var(--ash);padding:6px 0}
|
||
.insp dd{margin:0;font:400 11px/1.3 var(--f-mono);color:var(--bone);text-align:right;padding:6px 0;overflow-wrap:anywhere;min-width:0}
|
||
.insp dd.hot{color:var(--molten-text)}
|
||
.icol .note{font-size:12px;margin-top:14px;padding-top:12px;border-top:var(--hair)}
|
||
.insp-empty{color:var(--ash);font-size:13px;line-height:1.5;padding:6px 0 2px}
|
||
.legend-row{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 18px;padding:12px 21px;border-top:var(--hair);font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
.legend-row .lg{display:flex;flex-wrap:wrap;gap:8px 16px}
|
||
.legend-row .lg span{display:inline-flex;align-items:center;gap:7px;color:var(--ink-2);font-family:var(--f-sans);font-size:12px}
|
||
.sw{width:9px;height:9px;transform:rotate(45deg);display:inline-block;flex:none}
|
||
.three{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);margin-top:var(--s-4)}
|
||
@media (max-width:1000px){.three{grid-template-columns:1fr}}
|
||
.dp{border:var(--hair);border-radius:7px;background:var(--graphite);padding:20px 21px 16px;min-width:0}
|
||
.dp .ph{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:14px}
|
||
.dp h2{font:500 var(--t-md)/1.3 var(--f-sans);margin:0;color:var(--bone)}
|
||
.dp .tag{font:400 10px/1.2 var(--f-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ash)}
|
||
.big{display:flex;align-items:baseline;gap:10px;margin-bottom:8px}
|
||
.big b{font:500 30px/1 var(--f-sans);color:var(--bone)}
|
||
.big span{font:400 12px/1.4 var(--f-sans);color:var(--ash)}
|
||
.bars{display:flex;align-items:flex-end;gap:3px;height:80px;margin-top:10px}
|
||
.bars i{flex:1 1 0;background:linear-gradient(to top,var(--ember-40),var(--molten));border-radius:2px 2px 0 0;min-height:2px;opacity:.9}
|
||
.bars i.now{background:var(--molten)}
|
||
.axis{display:flex;justify-content:space-between;font:400 10px/1.4 var(--f-mono);color:var(--ash);margin-top:8px}
|
||
.ids{display:flex;flex-direction:column;gap:12px}
|
||
.id-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 10px;font:400 12px/1.4 var(--f-mono);color:var(--ink-2);align-items:center}
|
||
.id-row .name{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.id-row .name .sw{background:var(--line-2)}
|
||
.id-row.mine .name{color:var(--ember-text)}
|
||
.id-row.mine .name .sw{background:var(--ember)}
|
||
.id-row .pct{color:var(--bone);white-space:nowrap}
|
||
.id-row .bar{grid-column:1/-1;height:3px;background:var(--line);border-radius:2px;overflow:hidden}
|
||
.id-row .bar i{display:block;height:100%;background:var(--ember);opacity:.85}
|
||
.id-row.mine .bar i{background:var(--molten)}
|
||
.evs{display:flex;flex-direction:column;max-height:330px;overflow:auto}
|
||
.evs div{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:baseline;padding:9px 0;border-bottom:var(--hair);font:400 12px/1.4 var(--f-mono);color:var(--ink-2)}
|
||
.evs div:last-child{border-bottom:0}
|
||
.evs .dot{width:6px;height:6px;background:var(--molten);border-radius:50%;animation:none}
|
||
.evs .dot.lock{background:var(--ember)}.evs .dot.quiet{background:var(--ash)}
|
||
.evs .what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.evs .what b{font-weight:500;color:var(--bone)}
|
||
.evs .t{color:var(--ash);font-size:11px;white-space:nowrap}
|
||
.evs div.new{animation:land .9s ease-out}
|
||
@keyframes land{0%{background:var(--ember-12)}100%{background:transparent}}
|
||
@media (prefers-reduced-motion:reduce){.evs div.new{animation:none}}
|
||
.tablebar{margin-top:var(--s-4);border:var(--hair);border-radius:7px;background:var(--graphite)}
|
||
.tablebar summary{padding:16px 21px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
|
||
.tablebar summary::after{content:"+";font:400 14px/1 var(--f-mono);color:var(--ash)}
|
||
.tablebar[open] summary::after{content:"\2212"}
|
||
.tablebar summary small{font:400 11px/1.4 var(--f-mono);color:var(--ash);margin-left:auto;margin-right:12px}
|
||
.tablebar .tbl{border:0;border-top:var(--hair);border-radius:0}
|
||
.tablebar table{font-size:12px;font-family:var(--f-mono)}
|
||
.tablebar td,.tablebar th{padding:8px 21px}
|
||
.tablebar tr[data-hash]{cursor:pointer}
|
||
.tablebar tr[data-hash]:hover td{background:var(--hover)}
|
||
.obs-foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px;margin-top:var(--s-5);font:400 10px/1.5 var(--f-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ash)}
|
||
.linkish{background:none;border:0;padding:0;color:var(--ember-text);font:inherit;cursor:pointer}
|
||
.linkish:hover{text-decoration:underline}
|
||
details.tablebar{border-top:var(--hair);border-bottom:var(--hair)}
|
||
details.tablebar summary{display:flex;align-items:center}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- nav:start -->
|
||
<a class="skip" href="#main">Skip to content</a>
|
||
<nav class="nav" aria-label="Main">
|
||
<div class="wrap">
|
||
<a href="/" class="brand" aria-label="Igneum home">
|
||
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
|
||
<span class="word">IGNEUM</span>
|
||
</a>
|
||
<div class="links" id="nav-links">
|
||
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
|
||
<a href="/miner" data-nav="miner">Miner</a>
|
||
<a href="/app" data-nav="app">App</a>
|
||
<a href="/wallet" data-nav="wallet">Wallet</a>
|
||
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
|
||
<a href="/ledger" data-nav="ledger">Ledger</a>
|
||
</div>
|
||
<a href="/miner#get" class="btn primary small cta">Download</a>
|
||
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Menu"><span class="bars"></span></button>
|
||
<div class="nav-sheet" id="nav-sheet">
|
||
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
|
||
<a href="/miner" data-nav="miner">Miner</a>
|
||
<a href="/app" data-nav="app">App</a>
|
||
<a href="/wallet" data-nav="wallet">Wallet</a>
|
||
<a href="/live" data-nav="live">Live devnet</a>
|
||
<a href="/ledger" data-nav="ledger">Ledger</a>
|
||
<a href="/bench" data-nav="bench">Engineering log</a>
|
||
<a href="/evidence" data-nav="evidence">Evidence</a>
|
||
<a href="/explorer">Explorer</a>
|
||
<a href="https://github.com/igneum-network/spec" rel="noopener">GitHub</a>
|
||
<a href="/miner#get" class="btn primary">Download the miner</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<script>
|
||
(function(){
|
||
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
|
||
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
|
||
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');document.documentElement.style.overflow=o?'hidden':'';}
|
||
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
|
||
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
|
||
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
|
||
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
|
||
})();
|
||
</script>
|
||
<!-- nav:end -->
|
||
|
||
<main id="main" class="wrap obs">
|
||
<header class="obs-head" id="top">
|
||
<div>
|
||
<div class="eyebrow"><i></i>Network observatory</div>
|
||
<h1>Live devnet<b>.</b></h1>
|
||
<p>Every block has a story. Follow the connections.</p>
|
||
</div>
|
||
<div class="side">
|
||
<div class="cta"><a href="/miner#get" class="btn primary">Download the miner</a><a href="/explorer" class="btn">Open the explorer</a></div>
|
||
<div class="state" id="live-state"><span class="dot off"></span>connecting to the observer</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="tiles6" aria-live="polite">
|
||
<div><div class="k">Blocks / second <i>↗</i></div><div class="v tick" id="s-bps">pending</div><div class="d">counted over the node’s last 60 s; the target is 1</div></div>
|
||
<div><div class="k">Blue score <i>#</i></div><div class="v tick" id="s-blue">pending</div><div class="d">the newest block’s count of blue ancestors</div></div>
|
||
<div><div class="k">Network hash rate <i>∿</i></div><div class="v tick" id="s-hash">pending</div><div class="d">the node’s estimate from the last 1,000 blocks’ difficulty</div></div>
|
||
<div><div class="k">Active vote keys <i>◇</i></div><div class="v tick" id="s-keys">pending</div><div class="d">keys with a block in 10 min; a card runs several</div></div>
|
||
<div><div class="k">Connected peers <i>⇄</i></div><div class="v tick" id="s-peers">pending</div><div class="d">peers of the observer’s node</div></div>
|
||
<div><div class="k">Locked checkpoint <i>●</i></div><div class="v tick" id="s-lock">pending</div><div class="d" id="s-lock-d">signed by two thirds of all 30-day weight</div></div>
|
||
</div>
|
||
|
||
<section class="panel" aria-label="The block graph">
|
||
<div class="toolbar">
|
||
<div class="title"><i></i>The block graph</div>
|
||
<span class="chip" id="c-count">0 blocks</span>
|
||
<div class="right">
|
||
<button type="button" class="tb" id="chain-only" aria-pressed="false">Selected chain only</button>
|
||
<span class="sep"></span>
|
||
<button type="button" class="tb" id="dag-pause" aria-pressed="false">❚❚ Pause view</button>
|
||
<button type="button" class="tb on" id="dag-follow">Follow</button>
|
||
</div>
|
||
</div>
|
||
<div class="graph">
|
||
<div class="gcol">
|
||
<div class="state" id="dag-state"><span class="dot off"></span>live devnet</div>
|
||
<div class="scene"><canvas id="dag" aria-label="The devnet's block DAG, live: blocks by time and miner lane, parent edges, the selected chain, checkpoint bands with their lock weight, proofs filling blocks as segments are paid"></canvas><div class="tip" id="dag-tip" hidden></div><div class="chip-zoom" id="dag-chip" hidden>scroll to zoom · Esc to release</div></div>
|
||
<div class="gfoot">
|
||
<span>Drag to explore · Select a block to inspect</span>
|
||
<span class="zoom"><button type="button" id="z-out" aria-label="Zoom out">−</button><span id="z-pct">100%</span><button type="button" id="z-in" aria-label="Zoom in">+</button></span>
|
||
</div>
|
||
</div>
|
||
<aside class="icol" id="inspector" aria-live="polite">
|
||
<div class="ih"><div class="eyebrow">Block inspector</div><span class="chip" id="i-kind">select a block</span></div>
|
||
<div class="insp-empty" id="insp-empty">Click a block in the graph, or a row of the table below. Hover shows the short form.</div>
|
||
<div id="insp" hidden>
|
||
<div class="num" id="i-number"></div>
|
||
<div class="hash" id="i-hash"></div>
|
||
<dl class="insp">
|
||
<dt>Vote key</dt><dd id="i-miner"></dd>
|
||
<dt>Parents</dt><dd id="i-parents"></dd>
|
||
<dt>Selected chain</dt><dd id="i-chain"></dd>
|
||
<dt>Colour</dt><dd id="i-color"></dd>
|
||
<dt>Blue score</dt><dd id="i-blue"></dd>
|
||
<dt>DAA score</dt><dd id="i-daa"></dd>
|
||
<dt>Shards</dt><dd id="i-shards"></dd>
|
||
<dt>Checkpoint</dt><dd id="i-cp"></dd>
|
||
<dt>Lock</dt><dd id="i-lock"></dd>
|
||
<dt>Header time</dt><dd id="i-time"></dd>
|
||
</dl>
|
||
<p class="note">A selected-chain block is not final by itself. Only a checkpoint lock, signed by two thirds of all 30-day weight, makes the blocks under it final.</p>
|
||
<p style="margin:10px 0 0"><button type="button" class="linkish" id="insp-close">Clear</button></p>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
<div class="legend-row">
|
||
<div class="lg">
|
||
<span><i class="sw" style="background:var(--ember)"></i>Selected chain</span>
|
||
<span><i class="sw" style="border:1.5px solid var(--bone)"></i>Included</span>
|
||
<span><i class="sw" style="border:1.5px solid var(--line-2);opacity:.6"></i>Excluded</span>
|
||
<span><i class="sw" style="background:var(--molten);border-radius:50%;transform:none"></i>Locked checkpoint</span>
|
||
<span><i class="sw" style="width:14px;height:0;border-top:1px solid var(--ash);transform:none"></i>Parent reference</span>
|
||
</div>
|
||
<span id="blue-range">blue score pending</span>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="three">
|
||
<div class="dp">
|
||
<div class="ph"><h2>Block activity</h2><span class="tag">header times / window</span></div>
|
||
<div class="big"><b id="a-count">0</b><span>blocks in this view</span></div>
|
||
<div class="bars" id="a-bars" aria-label="Blocks per minute over the last hour, newest on the right"></div>
|
||
<div class="axis"><span>one bar a minute, the last 30 min</span><span>now</span></div>
|
||
<p class="note" id="a-note" style="margin-top:12px;font-size:12px">Counted by the observer as blocks arrive.</p>
|
||
</div>
|
||
<div class="dp">
|
||
<div class="ph"><h2>Mining identities</h2><span class="tag">displayed window</span></div>
|
||
<div class="ids" id="miners"><div class="empty">No blocks in the window.</div></div>
|
||
<p class="note" style="margin-top:14px;font-size:12px" id="miners-more">Vote-key share of the displayed blocks. Several keys may belong to one GPU. <button type="button" class="linkish" id="mine-btn">Mark my key</button></p>
|
||
</div>
|
||
<div class="dp">
|
||
<div class="ph"><h2>Event stream</h2><span class="tag" id="ev-count">live</span></div>
|
||
<div class="evs" id="events"><div class="empty">Waiting for the first read.</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<details class="tablebar" id="table">
|
||
<summary>Explore blocks as a table <small>keyboard-accessible view</small></summary>
|
||
<div class="tbl"><table>
|
||
<thead><tr><th>Blue score</th><th>Hash</th><th>Vote key</th><th>Parents</th><th>State</th><th>Chain block</th></tr></thead>
|
||
<tbody id="tbody"><tr><td colspan="6">Waiting for the first read.</td></tr></tbody>
|
||
</table></div>
|
||
</details>
|
||
|
||
<div class="obs-foot"><span>Igneum / observatory</span><span>One node read every 2 s. Nothing here is a replay.</span></div>
|
||
</main>
|
||
|
||
<!-- footer:start -->
|
||
<footer class="foot">
|
||
<div class="wrap">
|
||
<div class="foot-grid">
|
||
<div class="foot-brand">
|
||
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
|
||
<p>Mined by GPUs. Proven by fire.</p>
|
||
</div>
|
||
<nav class="foot-col" aria-label="Read">
|
||
<div class="eyebrow">Read</div>
|
||
<a href="/litepaper">Litepaper</a>
|
||
<a href="/litepaper#limits">What Igneum does not claim</a>
|
||
<a href="/litepaper#randomx">Igneum vs RandomX</a>
|
||
<a href="/litepaper#shoulders">Built on the shoulders</a>
|
||
<a href="/bench">Engineering log</a>
|
||
<a href="/evidence">Evidence</a>
|
||
<a href="/ledger">Ledger: every criticism</a>
|
||
</nav>
|
||
<nav class="foot-col" aria-label="Run">
|
||
<div class="eyebrow">Run</div>
|
||
<a href="/miner">The miner</a>
|
||
<a href="/app">The app</a>
|
||
<a href="/wallet">The wallet</a>
|
||
<a href="/miners">GPU bench table</a>
|
||
<a href="/miner#fee">The dev fee</a>
|
||
<a href="/faucet">Testnet faucet</a>
|
||
</nav>
|
||
<nav class="foot-col" aria-label="Follow">
|
||
<div class="eyebrow">Follow</div>
|
||
<a href="/live">Live devnet</a>
|
||
<a href="/explorer">Explorer</a>
|
||
<a href="/litepaper#roadmap">Journey</a>
|
||
<a href="/metamask">Add Igneum to MetaMask</a>
|
||
<a href="https://github.com/igneum-network/spec" rel="noopener"><svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg>GitHub, spec and vectors</a>
|
||
<a href="/miner#get">Miner downloads: devnet build</a>
|
||
<a href="mailto:hello@igneum.network">Report a flaw: hello@igneum.network</a>
|
||
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">Or open an issue on the spec</a>
|
||
</nav>
|
||
</div>
|
||
<div class="foot-base">
|
||
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre · <a href="mailto:hello@igneum.network">hello@igneum.network</a></span>
|
||
<span>© 2026 Igneum. Nothing on this page is an offer to sell anything.</span>
|
||
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
|
||
<span class="mono">igneum.network</span>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
(function(){
|
||
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
|
||
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
|
||
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
|
||
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
|
||
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
|
||
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
|
||
paint();
|
||
})();
|
||
</script>
|
||
<!-- footer:end -->
|
||
|
||
<script src="/live-dag.js" defer></script>
|
||
<script>
|
||
(function(){
|
||
function $(id){return document.getElementById(id);}
|
||
var fmtN=function(v){return v===null||v===undefined?'pending':Number(v).toLocaleString('en-GB');};
|
||
function fmtHash(h){if(h===null||h===undefined||!(h>0))return 'pending';return h>=1e9?(h/1e9).toFixed(1)+' GH/s':(h/1e6).toFixed(1)+' MH/s';}
|
||
function rel(ms){var s=Math.max(0,Math.round(ms/1000));if(s<60)return s+' s ago';var m=Math.floor(s/60);if(m<60)return m+' min ago';var h=Math.floor(m/60);if(h<24)return h+' h ago';return Math.floor(h/24)+' d ago';}
|
||
function hms(ms){var d=new Date(ms);return String(d.getUTCHours()).padStart(2,'0')+':'+String(d.getUTCMinutes()).padStart(2,'0')+':'+String(d.getUTCSeconds()).padStart(2,'0');}
|
||
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
|
||
// a number that changed glows for a frame: motion only from a real change
|
||
function set(id,text){var el=$(id);if(!el||el.textContent===text)return;el.textContent=text;el.classList.add('flash');setTimeout(function(){el.classList.remove('flash');},60);}
|
||
var seen={},shardState={},cpState={},evSeen={},evList=[],ready=false,sel=null,lastD=null,dag=null,chainOnly=false,zoomPct=100;
|
||
var mine=(function(){try{return localStorage.getItem('igneum-my-key')||'';}catch(e){return '';}})();
|
||
document.addEventListener('click',function(e){var t=e.target;if(t&&t.id==='mine-btn'){var v=prompt('Your vote key’s short id, the first 8 hex characters shown in the app or the explorer:',mine||'');if(v!==null){mine=v.trim().toLowerCase();try{if(mine)localStorage.setItem('igneum-my-key',mine);else localStorage.removeItem('igneum-my-key');}catch(x){}if(lastD)render(lastD);}}
|
||
var row=t&&t.closest?t.closest('tr[data-hash]'):null;if(row&&lastD){var b=(lastD.blocks||[]).filter(function(x){return x.hash===row.getAttribute('data-hash');})[0];if(b)inspect(b,lastD);}});
|
||
// the inspector: a clicked block's facts from the observer, kept fresh on every read
|
||
function inspect(b,d){sel=b;$('insp-empty').hidden=true;$('insp').hidden=false;$('i-kind').textContent=b.locked?'checkpoint':b.chain?'chain block':b.color==='red'?'excluded':'side block';
|
||
$('i-number').textContent=b.number?'#'+fmtN(b.number):'blue '+fmtN(b.blue_score);$('i-hash').textContent=b.hash+(b.number?'':' · not yet planned onto the chain');
|
||
$('i-miner').textContent=b.miner+(mine&&b.miner===mine?' (you)':'');$('i-parents').textContent=(b.parents||[]).length?(b.parents.length+(b.parents.length===1?' reference: ':' references: ')+b.parents.join(', ')):'none';
|
||
$('i-chain').textContent=b.chain?'yes':'no';var col=$('i-color');col.textContent=b.color==='blue'?'blue, merged and paid':b.color==='red'?'red, excluded':'pending';col.className=b.color==='blue'?'hot':'';
|
||
$('i-blue').textContent=fmtN(b.blue_score);$('i-daa').textContent=fmtN(b.daa);
|
||
var sh=b.shards||[];$('i-shards').textContent=sh.length?sh.map(function(s){return (s.i+1)+'/'+s.n+' '+s.state;}).join(', '):(b.chain?'no plan yet':'none');
|
||
var fn=d.finality,cp=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(b.locked&&c.hash===b.hash)cp=c;});
|
||
$('i-cp').textContent=cp?'this block, #'+fmtN(cp.index):(b.final?'under the newest lock':'not a checkpoint');var lk=$('i-lock');lk.textContent=cp?(cp.state==='locked'?'locked'+(cp.fraction_total?', '+Math.round(cp.fraction_total*100)+'% of weight':''):cp.state):(b.final?'final':'not final');lk.className=(cp&&cp.state==='locked')||b.final?'hot':'';
|
||
$('i-time').textContent=hms(b.ts)+' UTC';}
|
||
function uninspect(){sel=null;$('insp').hidden=true;$('insp-empty').hidden=false;$('i-kind').textContent='select a block';}
|
||
$('insp-close').addEventListener('click',function(){uninspect();if(dag)dag.engage(false);});
|
||
function render(d){var s=d.state,pv=d.proving,fn=d.finality,now=new Date(d.now).getTime();lastD=d;
|
||
// the six tiles
|
||
set('s-bps',s.blocks_per_second_60s!==null&&s.blocks_per_second_60s!==undefined?Number(s.blocks_per_second_60s).toFixed(2):'pending');
|
||
set('s-blue',fmtN(s.blue_score));set('s-hash',fmtHash(s.hashes_per_second_estimate));
|
||
set('s-keys',fmtN(s.miners_10m!==null&&s.miners_10m!==undefined?s.miners_10m:(d.miners||[]).length));set('s-peers',fmtN(s.peers));
|
||
var lock=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(c.state==='locked'&&(!lock||c.index>lock.index))lock=c;});
|
||
var le=$('s-lock');if(fn&&fn.supported&&!fn.active){set('s-lock',fn.latest_locked_index?'#'+fmtN(fn.latest_locked_index):'none');le.className='v tick';$('s-lock-d').textContent='finality paused: under two thirds of the weight is signing';}
|
||
else if(lock){set('s-lock','#'+fmtN(lock.index));le.className='v tick';var t=lock.locked_at?new Date(String(lock.locked_at).replace(' ','T')).getTime():NaN;$('s-lock-d').textContent=(isNaN(t)?'locked':'locked '+rel(now-t))+(lock.fraction_total?' with '+Math.round(lock.fraction_total*100)+'% of all 30-day weight':'');}
|
||
else{set('s-lock',fn&&fn.supported?'none yet':'no rule');le.className='v tick word';$('s-lock-d').textContent=fn&&fn.supported?'the 30-day weight window is filling':'';}
|
||
// the graph's count, the blue range, the chain-only view
|
||
var w=dag?dag.getWindow():120,cut=now-w*1000,on=[],lo=null,hi=null;(d.blocks||[]).forEach(function(b){if(b.ts>=cut&&(!chainOnly||b.chain)){on.push(b);if(lo===null||b.blue_score<lo)lo=b.blue_score;if(hi===null||b.blue_score>hi)hi=b.blue_score;}});
|
||
$('c-count').textContent=on.length+' blocks';$('blue-range').textContent=lo===null?'blue score pending':'blue score '+fmtN(lo)+' to '+fmtN(hi);
|
||
// block activity: the last 30 minutes, one bar a minute, from the observer's per-minute counts
|
||
var bpm=(s.blocks_per_minute||[]).slice(-30),max=Math.max(1,Math.max.apply(null,bpm.map(function(p){return p[1]||0;}).concat([1])));
|
||
$('a-count').textContent=on.length;$('a-bars').innerHTML=bpm.map(function(p,i){return '<i style="height:'+Math.max(2,Math.round((p[1]||0)/max*100))+'%" title="'+(p[1]||0)+' blocks"'+(i===bpm.length-1?' class="now"':'')+'></i>';}).join('');
|
||
var tot30=bpm.reduce(function(a,p){return a+(p[1]||0);},0);$('a-note').textContent=fmtN(tot30)+' blocks in the last '+bpm.length+' min, '+(bpm.length?(tot30/bpm.length).toFixed(1):'0')+' a minute on average; the target is 60.';
|
||
// mining identities: share of the displayed blocks, the reader's own key marked when known
|
||
var share={},tot=0;on.forEach(function(b){share[b.miner]=(share[b.miner]||0)+1;tot++;});var ids=Object.keys(share).sort(function(a,b){return share[b]-share[a];}),mel=$('miners');
|
||
if(ids.length){var top=ids.slice(0,8),rest=ids.length-top.length;mel.innerHTML=top.map(function(id){var n=share[id],pc=n/tot*100;return '<div class="id-row'+(mine&&id===mine?' mine':'')+'"><span class="name"><i class="sw"></i>'+esc(id)+(mine&&id===mine?' (you)':'')+'</span><span class="pct">'+pc.toFixed(1)+'%</span><div class="bar"><i style="width:'+Math.round(pc)+'%"></i></div></div>';}).join('');
|
||
$('miners-more').innerHTML=(rest>0?'and '+rest+' more. ':'')+'Vote-key share of the displayed blocks. Several keys may belong to one GPU. <button type="button" class="linkish" id="mine-btn">'+(mine?'Change my key':'Mark my key')+'</button>';}
|
||
else mel.innerHTML='<div class="empty">No blocks in the window.</div>';
|
||
// the event stream: the observer's own events plus what changed between reads
|
||
var evs=[];(d.events||[]).forEach(function(e){evs.push({ts:new Date(e.ts).getTime(),k:e.kind.replace(/_/g,' '),cls:e.kind==='checkpoint_locked'?'lock':/quiet/.test(e.kind)?'quiet':'',text:e.text});});
|
||
(d.blocks||[]).forEach(function(b){if(!seen[b.hash]){seen[b.hash]=1;if(ready)evs.push({ts:b.ts,k:'Block observed',cls:'',text:(b.number?'#'+fmtN(b.number):b.hash)+' by '+b.miner});}
|
||
(b.shards||[]).forEach(function(sh){var key=b.hash+':'+sh.i,prev=shardState[key];shardState[key]=sh.state;if(ready&&prev!==sh.state&&(sh.state==='verified'||sh.state==='paid'))evs.push({ts:now,k:sh.state==='paid'?'Shard paid':'Shard proven',cls:'',text:(sh.i+1)+' of '+sh.n+' for '+(b.number?'#'+fmtN(b.number):b.hash)+(sh.prover?' by '+sh.prover:'')});});});
|
||
if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){var prev=cpState[c.index];cpState[c.index]=c.state;if(ready&&prev!==c.state)evs.push({ts:c.state==='locked'&&c.locked_at?new Date(String(c.locked_at).replace(' ','T')).getTime():now,k:'Checkpoint '+c.state,cls:c.state==='locked'?'lock':'',text:'#'+fmtN(c.index)+(c.fraction_total?', '+Math.round(c.fraction_total*100)+'% of weight':'')});});
|
||
evs.forEach(function(e){var id=e.k+'|'+e.text+'|'+Math.round(e.ts/1000);if(!evSeen[id]){evSeen[id]=1;evList.unshift({id:id,fresh:ready,ts:e.ts,k:e.k,cls:e.cls,text:e.text});}});
|
||
evList.sort(function(a,b){return b.ts-a.ts;});evList=evList.slice(0,14);ready=true;
|
||
var eel=$('events');if(evList.length){eel.innerHTML=evList.map(function(e){var f=e.fresh;e.fresh=false;return '<div'+(f?' class="new"':'')+'><span class="dot '+e.cls+'"></span><span class="what"><b>'+esc(e.k)+'</b> '+esc(e.text)+'</span><span class="t">'+hms(e.ts)+'</span></div>';}).join('');$('ev-count').textContent=evList.length+' recent';}
|
||
// the table: the displayed blocks, newest first
|
||
var rows=on.slice().sort(function(a,b){return b.blue_score-a.blue_score;}).slice(0,40);
|
||
$('tbody').innerHTML=rows.length?rows.map(function(b){var word=b.locked?'locked checkpoint':b.proven?'proven':b.color==='red'?'excluded':b.chain?'selected chain':b.color==='blue'?'included':'pending';return '<tr data-hash="'+esc(b.hash)+'"><td class="num">'+fmtN(b.blue_score)+'</td><td>'+esc(b.hash)+'</td><td>'+esc(b.miner)+'</td><td class="num">'+(b.parents||[]).length+'</td><td>'+word+'</td><td class="num">'+(b.number?fmtN(b.number):'')+'</td></tr>';}).join(''):'<tr><td colspan="6">No blocks in the window.</td></tr>';
|
||
if(sel){var cur=(d.blocks||[]).filter(function(b){return b.hash===sel.hash;})[0];if(cur)inspect(cur,d);}
|
||
}
|
||
// the graph: the shared module, fed by this page's own reads so the chain-only toggle can filter what it draws
|
||
var failures=0;
|
||
function mountDag(){if(dag||!window.IgneumDag)return;dag=IgneumDag.mount($('dag'),{poll:false,window:120,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
|
||
onWindow:function(w){zoomPct=Math.round(120/w*100);$('z-pct').textContent=zoomPct+'%';if(lastD)render(lastD);},
|
||
onState:function(st,reason){var el=$('dag-state');el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet':esc(reason||st));},
|
||
onSelect:function(b){if(b&&lastD)inspect(b,lastD);else uninspect();}});
|
||
if(lastD)feed(lastD);}
|
||
function feed(d){if(!dag)return;if(!chainOnly){dag.push(d);return;}var c=Object.assign({},d);c.blocks=(d.blocks||[]).filter(function(b){return b.chain;});dag.push(c);}
|
||
if(window.IgneumDag)mountDag();else{var ms=document.querySelector('script[src="/live-dag.js"]');if(ms)ms.addEventListener('load',mountDag);}
|
||
$('chain-only').addEventListener('click',function(){chainOnly=!chainOnly;this.setAttribute('aria-pressed',chainOnly?'true':'false');this.classList.toggle('on',chainOnly);if(lastD){feed(lastD);render(lastD);}});
|
||
$('dag-pause').addEventListener('click',function(){var p=this.getAttribute('aria-pressed')!=='true';this.setAttribute('aria-pressed',p?'true':'false');$('dag-follow').classList.toggle('on',!p);});
|
||
$('dag-follow').addEventListener('click',function(){if(dag)dag.setPaused(false);$('dag-pause').setAttribute('aria-pressed','false');this.classList.add('on');});
|
||
$('z-in').addEventListener('click',function(){if(dag)dag.zoom(0.75);});$('z-out').addEventListener('click',function(){if(dag)dag.zoom(1/0.75);});
|
||
function setLive(st,reason){var el=$('live-state');el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet · one node read every 2 s':st==='connecting'?'connecting to the observer':esc(reason||st));}
|
||
function poll(){fetch('/api/live?window=300',{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error(r.status);return r.json();}).then(function(d){if(!d.ok)throw new Error(d.error);failures=0;setLive(d.state.stale?'stale':'live',d.state.stale?'observer stale':'');render(d);feed(d);}).catch(function(){failures++;if(failures>=3)setLive('failed','live feed unavailable');});}
|
||
poll();setInterval(function(){if(!document.hidden)poll();},2000);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|