Both moved from quantus/ to blackbeard/: the observer to blackbeard/observer, the miner to blackbeard/miner. Gitea 301s the old paths, but a redirect survives only until something else takes the name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E4Ldjix2ofMdQ7dzqgnYA8
143 lines
5.5 KiB
TypeScript
143 lines
5.5 KiB
TypeScript
/**
|
|
* Who made this, and how to say thanks.
|
|
*
|
|
* The QR is **generated in the browser from the same string the page prints**,
|
|
* never shipped as an image. A supplied PNG and the text beneath it are two
|
|
* independent claims about where money should go, and nothing on the page could
|
|
* tell you if they ever stopped agreeing — a mismatch sends funds somewhere
|
|
* unrecoverable. Generated, they cannot disagree: there is one address, and the
|
|
* code is a rendering of it.
|
|
*/
|
|
|
|
import qrcode from 'qrcode-generator'
|
|
|
|
import robPortrait from '../assets/rob.png'
|
|
|
|
/** Where donations go. Also the string the QR is built from — see above. */
|
|
export const DONATION_ADDRESS = 'qzkQq9ybiPm7gQR9E1ViQbW9RhqA2Rex9ZQkScU5zdJqjrre4'
|
|
|
|
const MINER_REPO = 'https://git.lair.cafe/blackbeard/miner'
|
|
const OBSERVER_REPO = 'https://git.lair.cafe/blackbeard/observer'
|
|
const AUTHOR_NAME = 'Rob Thijssen'
|
|
const AUTHOR_SITE = 'https://rob.tn'
|
|
|
|
/**
|
|
* The address as a QR, drawn as one SVG path.
|
|
*
|
|
* Error-correction level `M` and an automatic version: the address is 49
|
|
* characters, which fits comfortably and leaves the code readable at the size
|
|
* a phone is held from a screen. Rendered as a single path of rectangles rather
|
|
* than a grid of elements — one node instead of a thousand, and it scales to
|
|
* whatever box the layout gives it.
|
|
*/
|
|
function AddressQr({ value }: { value: string }) {
|
|
const qr = qrcode(0, 'M')
|
|
qr.addData(value)
|
|
qr.make()
|
|
const count = qr.getModuleCount()
|
|
// A four-module quiet zone, which the spec requires and scanners rely on.
|
|
const quiet = 4
|
|
const size = count + quiet * 2
|
|
|
|
let path = ''
|
|
for (let row = 0; row < count; row += 1) {
|
|
for (let col = 0; col < count; col += 1) {
|
|
if (qr.isDark(row, col)) {
|
|
path += `M${col + quiet} ${row + quiet}h1v1h-1z`
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<svg
|
|
className="qr"
|
|
viewBox={`0 0 ${size} ${size}`}
|
|
role="img"
|
|
aria-label={`QR code for the donation address ${value}`}
|
|
>
|
|
{/* The light modules are drawn, not left transparent: a QR on a dark
|
|
surface with a transparent background does not scan, and this site has
|
|
a dark theme. */}
|
|
<rect x="0" y="0" width={size} height={size} className="qr-light" />
|
|
<path d={path} className="qr-dark" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function AboutPanel() {
|
|
return (
|
|
<>
|
|
<section className="panel" style={{ marginBottom: 26 }}>
|
|
<div className="panel-head">
|
|
<h2 className="panel-title">About</h2>
|
|
<span className="eyebrow">who made this</span>
|
|
</div>
|
|
<div className="about-body">
|
|
<div className="about-prose">
|
|
<p>
|
|
<strong>blackbeard.observer</strong> is a mining leaderboard and block explorer for
|
|
the Quantus network. It is built and its source provided freely by a member of the
|
|
Quantus user community — not by the Quantus project, and with no standing behind it
|
|
beyond the code being open and the numbers being checkable.
|
|
</p>
|
|
<p>
|
|
The same author maintains a{' '}
|
|
<a href={MINER_REPO} rel="noreferrer">
|
|
CUDA performance-optimised miner
|
|
</a>
|
|
, also free and open source. Between them they are the reason a good deal of this site
|
|
exists: a miner needs somewhere to see whether it is winning.
|
|
</p>
|
|
<p>
|
|
Everything the observer shows is derived from public block headers, chain state and
|
|
the network's own telemetry feed. The code that derives it is at{' '}
|
|
<a href={OBSERVER_REPO} rel="noreferrer">
|
|
git.lair.cafe/blackbeard/observer
|
|
</a>
|
|
, so any figure here can be traced to the read that produced it.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Imported rather than served from `public/`: Vite hashes it by
|
|
content, so a replaced photograph invalidates its own cache instead
|
|
of being served stale from behind a year-long max-age. */}
|
|
<a className="about-portrait" href={AUTHOR_SITE} rel="noreferrer" title={AUTHOR_NAME}>
|
|
<img
|
|
src={robPortrait}
|
|
alt={AUTHOR_NAME}
|
|
width={280}
|
|
height={320}
|
|
loading="lazy"
|
|
decoding="async"
|
|
/>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="panel" style={{ marginBottom: 26 }}>
|
|
<div className="panel-head">
|
|
<h2 className="panel-title">Donations</h2>
|
|
<span className="eyebrow">entirely optional</span>
|
|
</div>
|
|
<div className="about-donate">
|
|
<AddressQr value={DONATION_ADDRESS} />
|
|
<div className="about-prose">
|
|
<p>
|
|
If the observer or the miner have been useful and you are inclined, donations toward
|
|
development, maintenance and hosting are gratefully accepted in QTC at:
|
|
</p>
|
|
<p>
|
|
<code className="about-address">{DONATION_ADDRESS}</code>
|
|
</p>
|
|
<p className="panel-note" style={{ padding: 0, border: 0 }}>
|
|
Nothing here is gated behind it, and nothing about the site changes if you do. The
|
|
code above is generated in your browser from the address printed beside it, so the two
|
|
cannot drift apart — scan it or copy the text, whichever you trust more.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</>
|
|
)
|
|
}
|