From 1260cc6d644f9100a0d9190f8f6e779b4a55356d Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Wed, 9 Sep 2026 17:44:58 +0300 Subject: [PATCH] ui: the block route is a page too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same reasoning as the miner and account routes one commit ago, which the block route was deliberately left out of: a block is read against the standings, and the ticker row below is what you clicked to get here. On reflection that is an argument about how someone arrived, not about what the URL names — and the URL names one block. The board is what `/:chain/:window` is for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jp6a8EDar9ueEhAxzep4V5 --- web/src/App.tsx | 39 ++++++++++++++++++++----------- web/src/components/BlockPanel.tsx | 20 +++------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index b52261c..58dace1 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -16,6 +16,7 @@ import { BlockTicker } from './components/BlockTicker' import { ChainSwitcher } from './components/ChainSwitcher' import { Leaderboard } from './components/Leaderboard' import { MinerPanel } from './components/MinerPanel' +import { RuntimePanel } from './components/RuntimePanel' import { StatBar } from './components/StatBar' import { seconds, windowSpan } from './lib/format' import { href, parse, WINDOWS } from './lib/routes' @@ -67,12 +68,6 @@ export default function App() { [chain, navigate], ) - /** Back to the standings, keeping the window the panel was opened from. */ - const closePanel = useCallback( - () => navigate(href({ chain, window: route.window })), - [chain, route.window, navigate], - ) - const interval = state.summary?.block_interval_seconds ?? state.summary?.target_block_time_seconds ?? 6 const activeWindow = WINDOWS.find((w) => w.id === route.window) ?? WINDOWS[1]! @@ -143,7 +138,6 @@ export default function App() { block={route.block} targetSeconds={info?.target_block_time_seconds ?? 6} onResolved={blockResolved} - onClose={closePanel} /> )} @@ -163,13 +157,14 @@ export default function App() { )} - {/* A miner and an account are pages, not things opened over the board. - Each is somebody's own record and the whole subject of the URL that - reached it; leaving eleven other miners' rows underneath is the site - failing to notice which page it is on. A block keeps the board, - because a block is read *against* it — who won this one, and where do - they sit — and the ticker below is the row you clicked to get here. */} - {!route.account && !route.miner && ( + {/* Every one of these is a page, not something opened over the board. A + URL that names one thing has that thing as its whole subject, and + leaving eleven unrelated mining rows underneath is the site failing to + notice which page it is on. The board is what `/:chain/:window` is + for. */} + {route.runtime !== null && chain && } + + {!route.account && !route.miner && !route.block && route.runtime === null && (
@@ -215,6 +210,22 @@ export default function App() { is the only identity the chain itself asserts. An elsewhere tag means the name was earned by the same reward preimage on another chain, not observed on this one. + {state.summary?.spec_version !== null && state.summary?.spec_version !== undefined && ( + + {/* "Decoded how?" has an answer, and it is a page. Every event and + reward on this site was read against this runtime's own type + registry rather than against anything written here, so the + runtime is a load-bearing part of the explanation and not a + footnote. */} + Rewards and transfers are decoded against the chain's own metadata — the type registry + the node produces by running the runtime WASM in each block's state, not a schema + written here. This chain is on{' '} + + {state.summary.spec_name ?? 'runtime'} v{state.summary.spec_version} + + . + + )} {/* Its own line rather than a fourth item in the `space-between` flow: the three above are caveats about the numbers, this is diff --git a/web/src/components/BlockPanel.tsx b/web/src/components/BlockPanel.tsx index 8a8c556..ad7f8da 100644 --- a/web/src/components/BlockPanel.tsx +++ b/web/src/components/BlockPanel.tsx @@ -56,7 +56,6 @@ export function BlockPanel({ block, targetSeconds, onResolved, - onClose, }: { chain: string /** Height or hash, as it appeared in the URL. */ @@ -65,7 +64,6 @@ export function BlockPanel({ targetSeconds: number /** Called with the canonical hash once a height has resolved to one. */ onResolved: (hash: string) => void - onClose: () => void }) { const [detail, setDetail] = useState(null) const [error, setError] = useState(null) @@ -122,21 +120,9 @@ export function BlockPanel({ {detail?.hash ?? ''}
- + {/* No close button: this is a URL somebody navigated to, not a dialog + that opened over something they were reading. The back button and + the chain chip both already do what it would. */} {error &&

{error}

}