[claude] Bitcoin block height live counter (#273) #325

Merged
claude merged 1 commits from claude/issue-273 into main 2026-03-24 05:00:15 +00:00
3 changed files with 74 additions and 0 deletions

33
app.js
View File

@@ -2030,3 +2030,36 @@ function showTimmySpeech(text) {
timmySpeechSprite = sprite;
timmySpeechState = { startTime: clock.getElapsedTime(), sprite };
}
// === BITCOIN BLOCK HEIGHT ===
// Polls blockstream.info every 60 s for the current tip block height.
// Shows a flash animation when the block number increments.
const blockHeightDisplay = document.getElementById('block-height-display');
const blockHeightValue = document.getElementById('block-height-value');
let lastKnownBlockHeight = null;
async function fetchBlockHeight() {
try {
const res = await fetch('https://blockstream.info/api/blocks/tip/height');
if (!res.ok) return;
const height = parseInt(await res.text(), 10);
if (isNaN(height)) return;
if (lastKnownBlockHeight !== null && height !== lastKnownBlockHeight) {
// New block — trigger flash
blockHeightDisplay.classList.remove('fresh');
// Force reflow so animation restarts
void blockHeightDisplay.offsetWidth;
blockHeightDisplay.classList.add('fresh');
}
lastKnownBlockHeight = height;
blockHeightValue.textContent = height.toLocaleString();
} catch (_) {
// Network unavailable — keep last known value
}
}
fetchBlockHeight();
setInterval(fetchBlockHeight, 60000);

View File

@@ -51,6 +51,11 @@
<div id="sovereignty-msg">⚡ SOVEREIGNTY ⚡</div>
<div id="block-height-display">
<span class="block-height-label">⛏ BLOCK</span>
<span id="block-height-value"></span>
</div>
<div id="zoom-indicator">
<span>ZOOMED: <span id="zoom-label">Object</span></span>
<span class="zoom-hint">[Esc] or double-click to exit</span>

View File

@@ -234,6 +234,42 @@ body.photo-mode #overview-indicator {
100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}
/* === BITCOIN BLOCK HEIGHT === */
#block-height-display {
position: fixed;
bottom: 12px;
right: 12px;
z-index: 20;
font-family: var(--font-body);
font-size: 11px;
letter-spacing: 0.15em;
color: var(--color-primary);
background: rgba(0, 0, 8, 0.7);
border: 1px solid var(--color-secondary);
padding: 4px 10px;
pointer-events: none;
white-space: nowrap;
}
.block-height-label {
color: var(--color-text-muted);
margin-right: 6px;
font-size: 10px;
}
#block-height-value {
color: var(--color-primary);
}
#block-height-display.fresh #block-height-value {
animation: block-flash 0.6s ease-out;
}
@keyframes block-flash {
0% { color: #ffffff; text-shadow: 0 0 8px #4488ff; }
100% { color: var(--color-primary); text-shadow: none; }
}
/* === CRT / CYBERPUNK OVERLAY === */
.crt-overlay {
position: fixed;