From 8599ae93760dc689491ef7d981b14337b83b08cd Mon Sep 17 00:00:00 2001 From: satyakwok <119509589+satyakwok@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:18:21 +0200 Subject: [PATCH 1/2] fix(scan): show real block cadence, not the windowed perf average The block time stat read the chain-performance bucket average, which is windowed by the selected range and inflated by past stalls: it showed ~1.8s on the 1h bucket while testnet was producing ~0.5s blocks. Compute it from the latest blocks instead, falling back to the perf value only when there are not enough blocks to measure. --- apps/scan/app/[locale]/HomeContent.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/scan/app/[locale]/HomeContent.tsx b/apps/scan/app/[locale]/HomeContent.tsx index 91e07e3..9d8b3b5 100644 --- a/apps/scan/app/[locale]/HomeContent.tsx +++ b/apps/scan/app/[locale]/HomeContent.tsx @@ -125,9 +125,16 @@ export function HomeContent({ initial }: { initial: HomeBundle }) { const { data: chainStatus } = useChainStatus(network, initial.status); const latestPerf = performance?.points?.[performance.points.length - 1]; - const blockTime = latestPerf?.block_time_sec - ? `${latestPerf.block_time_sec.toFixed(1)}s` - : (blocks ? computeBlockTime(blocks.map((b) => b.timestamp as unknown as number | string)) : CHAIN_TARGET_BLOCK_TIME); + // Show the actual recent block cadence from the latest blocks. The + // chain-performance bucket average is windowed (range-dependent) and gets + // inflated by past stalls: it read ~1.8s on a 1h bucket while testnet was + // genuinely producing ~0.5s blocks. Fall back to the perf value, then the + // target, only when we don't have enough blocks to measure. + const blockTime = blocks && blocks.length >= 2 + ? computeBlockTime(blocks.map((b) => b.timestamp as unknown as number | string)) + : latestPerf?.block_time_sec + ? `${latestPerf.block_time_sec.toFixed(1)}s` + : CHAIN_TARGET_BLOCK_TIME; const totalTxValue = stats?.total_transactions != null ? formatNumber(stats.total_transactions) : estimateTotalTransactions(stats?.total_blocks, blocks); From 630647c2cea6d05f7bb831955a534ec7f9af717f Mon Sep 17 00:00:00 2001 From: satyakwok <119509589+satyakwok@users.noreply.github.com> Date: Thu, 25 Jun 2026 12:07:06 +0200 Subject: [PATCH 2/2] fix(scan): consistent page padding and fix unwrapped rail/detail pages Most pages used px-4 (16px) with no responsive bump, tighter than the header (px-4 lg:px-6) so content sat 8px left of the navbar on large screens. A second class of pages (EVM/native rail, forks, api-docs, contracts, mempool, epochs, supply) rendered a bare space-y-6 root with no max-width container at all, so content ran flush to the screen edges. Standardise every page-edge container to px-5 sm:px-6 lg:px-8 (20/24/32px) and wrap the bare pages in the same container. --- apps/scan/app/[locale]/HomeContent.tsx | 4 ++-- apps/scan/app/[locale]/accounts/page.tsx | 2 +- apps/scan/app/[locale]/address/[addr]/page.tsx | 2 +- apps/scan/app/[locale]/analytics/page.tsx | 2 +- apps/scan/app/[locale]/api-docs/page.tsx | 2 +- apps/scan/app/[locale]/blocks/[height]/page.tsx | 8 ++++---- apps/scan/app/[locale]/blocks/page.tsx | 2 +- apps/scan/app/[locale]/contracts/page.tsx | 2 +- apps/scan/app/[locale]/epochs/page.tsx | 2 +- apps/scan/app/[locale]/error.tsx | 2 +- apps/scan/app/[locale]/forks/page.tsx | 2 +- apps/scan/app/[locale]/glossary/page.tsx | 2 +- apps/scan/app/[locale]/leaderboard/layout.tsx | 2 +- apps/scan/app/[locale]/mempool/page.tsx | 2 +- apps/scan/app/[locale]/search/page.tsx | 2 +- apps/scan/app/[locale]/supply/page.tsx | 2 +- apps/scan/app/[locale]/tokens/[addr]/page.tsx | 8 ++++---- apps/scan/app/[locale]/tokens/page.tsx | 2 +- apps/scan/app/[locale]/tx/[hash]/page.tsx | 8 ++++---- apps/scan/app/[locale]/validators/[address]/page.tsx | 6 +++--- apps/scan/app/[locale]/validators/page.tsx | 2 +- apps/scan/app/[locale]/watchlist/page.tsx | 2 +- apps/scan/components/common/RailPage.tsx | 2 +- apps/scan/components/layout/footer.tsx | 2 +- apps/scan/components/layout/header.tsx | 2 +- 25 files changed, 37 insertions(+), 37 deletions(-) diff --git a/apps/scan/app/[locale]/HomeContent.tsx b/apps/scan/app/[locale]/HomeContent.tsx index 9d8b3b5..309ce6b 100644 --- a/apps/scan/app/[locale]/HomeContent.tsx +++ b/apps/scan/app/[locale]/HomeContent.tsx @@ -265,7 +265,7 @@ export function HomeContent({ initial }: { initial: HomeBundle }) { {(isChainIdle || chainUnreachable) && (
-
+
{chainUnreachable ? "RPC offline" : `${network === "testnet" ? "Testnet" : "Chain"} paused`} @@ -280,7 +280,7 @@ export function HomeContent({ initial }: { initial: HomeBundle }) {
)} -
+
{/* Title rail — flush-left wordmark + status eyebrow on the left, search rail on the right. Per sentris-design: explorer is dense, not sparse — the prior centered 72px hero pushed live data below the fold and read as a marketing site instead of diff --git a/apps/scan/app/[locale]/accounts/page.tsx b/apps/scan/app/[locale]/accounts/page.tsx index 8639069..11b3139 100644 --- a/apps/scan/app/[locale]/accounts/page.tsx +++ b/apps/scan/app/[locale]/accounts/page.tsx @@ -54,7 +54,7 @@ export default function AccountsPage() { }, [data, page]); return ( -
+
+
+
{/* Headline stats — one row showing chain health at a glance. diff --git a/apps/scan/app/[locale]/api-docs/page.tsx b/apps/scan/app/[locale]/api-docs/page.tsx index 2a4a958..aec4a71 100644 --- a/apps/scan/app/[locale]/api-docs/page.tsx +++ b/apps/scan/app/[locale]/api-docs/page.tsx @@ -112,7 +112,7 @@ export default function ApiDocsPage() { const wsBase = network === "mainnet" ? "wss://rpc.sentrixchain.com/ws" : "wss://testnet-rpc.sentrixchain.com/ws"; return ( -
+
diff --git a/apps/scan/app/[locale]/blocks/[height]/page.tsx b/apps/scan/app/[locale]/blocks/[height]/page.tsx index 231ee87..c845b92 100644 --- a/apps/scan/app/[locale]/blocks/[height]/page.tsx +++ b/apps/scan/app/[locale]/blocks/[height]/page.tsx @@ -88,7 +88,7 @@ export default function BlockDetailPage({ params }: { params: Promise<{ height: // still in flight (same race the tx page used to hit). if (loading || (!block && loadingOther)) { return ( -
+
@@ -99,7 +99,7 @@ export default function BlockDetailPage({ params }: { params: Promise<{ height: if (blockOther) { // Auto-switch effect above already in flight; transient placeholder. return ( -
+
@@ -112,7 +112,7 @@ export default function BlockDetailPage({ params }: { params: Promise<{ height: ); } return ( -
+

Block #{height} not found

@@ -127,7 +127,7 @@ export default function BlockDetailPage({ params }: { params: Promise<{ height: const totalTxPages = Math.max(1, Math.ceil(filteredTxs.length / TX_PAGE_SIZE)); return ( -
+
+
+
+
+
diff --git a/apps/scan/app/[locale]/forks/page.tsx b/apps/scan/app/[locale]/forks/page.tsx index ebf97b1..e398348 100644 --- a/apps/scan/app/[locale]/forks/page.tsx +++ b/apps/scan/app/[locale]/forks/page.tsx @@ -37,7 +37,7 @@ export default function ForksPage() { }); return ( -
+
+
diff --git a/apps/scan/app/[locale]/leaderboard/layout.tsx b/apps/scan/app/[locale]/leaderboard/layout.tsx index 0594e0d..29cb859 100644 --- a/apps/scan/app/[locale]/leaderboard/layout.tsx +++ b/apps/scan/app/[locale]/leaderboard/layout.tsx @@ -12,7 +12,7 @@ export const metadata: Metadata = { export default function LeaderboardLayout({ children }: { children: ReactNode }) { return ( -
+
diff --git a/apps/scan/app/[locale]/mempool/page.tsx b/apps/scan/app/[locale]/mempool/page.tsx index 47205c7..8760511 100644 --- a/apps/scan/app/[locale]/mempool/page.tsx +++ b/apps/scan/app/[locale]/mempool/page.tsx @@ -26,7 +26,7 @@ export default function MempoolPage() { const { data, loading } = useMempool(network); return ( -
+
+
Loading...

}> diff --git a/apps/scan/app/[locale]/supply/page.tsx b/apps/scan/app/[locale]/supply/page.tsx index 776dd46..09f6272 100644 --- a/apps/scan/app/[locale]/supply/page.tsx +++ b/apps/scan/app/[locale]/supply/page.tsx @@ -96,7 +96,7 @@ export default function SupplyPage() { ]; return ( -
+
+
@@ -88,7 +88,7 @@ export default function TokenDetailPage({ params }: { params: Promise<{ addr: st if (tokenOther) { // Auto-switch effect above already in flight; transient placeholder. return ( -
+
@@ -102,7 +102,7 @@ export default function TokenDetailPage({ params }: { params: Promise<{ addr: st ); } return ( -
+

Token not found

@@ -114,7 +114,7 @@ export default function TokenDetailPage({ params }: { params: Promise<{ addr: st } return ( -
+
diff --git a/apps/scan/app/[locale]/tokens/page.tsx b/apps/scan/app/[locale]/tokens/page.tsx index 5157972..43f4389 100644 --- a/apps/scan/app/[locale]/tokens/page.tsx +++ b/apps/scan/app/[locale]/tokens/page.tsx @@ -113,7 +113,7 @@ export default function TokensPage() { } return ( -
+
+
@@ -83,7 +83,7 @@ export default function TxDetailPage({ params }: { params: Promise<{ hash: strin // and the next-render useTransaction(network, hash) refetches against // the right network. No button — see the comment on the effect for why. return ( -
+
@@ -97,7 +97,7 @@ export default function TxDetailPage({ params }: { params: Promise<{ hash: strin ); } return ( -
+

Transaction not found

@@ -129,7 +129,7 @@ export default function TxDetailPage({ params }: { params: Promise<{ hash: strin }); return ( -
+
+
@@ -84,7 +84,7 @@ export default function ValidatorDetailPage({ params }: { params: Promise<{ addr if (!validator) { return ( -
+

Validator not found

@@ -99,7 +99,7 @@ export default function ValidatorDetailPage({ params }: { params: Promise<{ addr const st = statusLabel(validator.status); return ( -
+
+
+
diff --git a/apps/scan/components/common/RailPage.tsx b/apps/scan/components/common/RailPage.tsx index f202b99..e175024 100644 --- a/apps/scan/components/common/RailPage.tsx +++ b/apps/scan/components/common/RailPage.tsx @@ -116,7 +116,7 @@ export function RailPage({ rail }: RailPageProps) { ); return ( -
+
diff --git a/apps/scan/components/layout/footer.tsx b/apps/scan/components/layout/footer.tsx index 98e2dcf..15c1175 100644 --- a/apps/scan/components/layout/footer.tsx +++ b/apps/scan/components/layout/footer.tsx @@ -8,7 +8,7 @@ export function Footer() { return (