glyph
func(c uint8) stringglyph returns the stroke encoding for one character, or "" for anything unknown — an unknown character draws nothing rather than a box, because this markup lands inside a token that other people's software renders and a missing letter is better than a wrong one.
- OID
- 058c22…fb3a:3
glyph details
glyphW
untyped bigint// grid width of one glyph
Value
(10 <untyped> bigint)
glyphH
untyped bigint// grid height, top to bottom
Value
(14 <untyped> bigint)
glyphAd
untyped bigint// advance: glyph width plus the gap to the next one
Value
(12 <untyped> bigint)
hexish
func(c uint8) inthexish decodes one coordinate character. Digits are themselves; a..d carry 10..13, which is the only reason y can exceed nine in two characters.
- OID
- 058c22…fb3a:5
hexish details
MarkerText
func(s string, x int, y int, size int, colour string, weight int) stringMarkerText draws a line of text as stroked paths. \`size\` is the CAP HEIGHT in user units, not an em size: it is the height of a capital letter, which is the measurement anybody positioning this by eye is actually thinking in. \`x\` is the left edge and \`y\` is the TOP of the capitals, matching how the rest of this file positions things and unlike SVG's own text baseline, which is a trap when mixing the two. The jitter is what makes it read as a pen rather than a plotter. It is derived from the character and its position, so it is stable: the same string renders identically on every read, which matters because these cards are regenerated constantly and a letter that wobbled between reads would look like a rendering fault.
- OID
- 058c22…fb3a:6
MarkerText details
strokes
func(enc string, ox int, oy int, size int, idx int) stringstrokes turns one glyph's encoding into path elements at a position.
- OID
- 058c22…fb3a:7
strokes details
wobble
func(seed int) intwobble is a tiny deterministic offset in the range -1..1. Not random: the cards are regenerated on every read, and anything genuinely random would make the lettering shimmer.
- OID
- 058c22…fb3a:8
wobble details
MarkerWidth
func(s string, size int) intMarkerWidth is what MarkerText will occupy, so a caller can centre it. Counts every character including the ones that draw nothing, because a space still advances.
- OID
- 058c22…fb3a:9
MarkerWidth details
MarkerCentred
func(s string, cx int, y int, size int, colour string, weight int) stringMarkerCentred draws text centred on cx, which is what almost every caller here wants and what the SVG text-anchor attribute would have done for them if a font had been available.
- OID
- 058c22…fb3a:10
MarkerCentred details
selfPath
stringselfPath is this realm's path without the chain domain, for links to its own gnoweb pages. Set at init, because inside a renderer call the realm context belongs to whoever asked, not to this realm.
Value
"/r/nym-mnsrehearsal001/nsview/v1"
ledgerArt
typeValue
v1.ledgerArt
init.12
func()- OID
- 058c22…fb3a:12
init.12 details
RegisterWithLedger
func()RegisterWithLedger hands the ledger this realm's renderer. Anyone may call it: it only ever registers THIS realm's own drawing, and the ledger refuses unless this realm is the one it has approved.
- OID
- 058c22…fb3a:14
RegisterWithLedger details
tokenMetadata
func(tid string) stringtokenMetadata is the camelCase JSON Adena reads: name, description, externalUrl, attributes of {traitType, value}. Everything in it is read from the registry at the moment of asking, like the card, so a renewal or a sale shows up without anybody writing anything.
- OID
- 058c22…fb3a:15
tokenMetadata details
attr
func(k string, v string) string- OID
- 058c22…fb3a:16
attr details
escapeJSON
func(s string) string- OID
- 058c22…fb3a:17
escapeJSON details
plateW
untyped bigintThe card is 400x400 and the name may use all but a margin either side.
Value
(400 <untyped> bigint)
plateMargin
untyped bigintThe card is 400x400 and the name may use all but a margin either side.
Value
(30 <untyped> bigint)
plateAvail
untyped bigint// 340
Value
(340 <untyped> bigint)
plateMaxSize
untyped bigint// any larger and a two-letter name dominates the art
Value
(56 <untyped> bigint)
plateMinSize
untyped bigintThe card is 400x400 and the name may use all but a margin either side.
Value
(4 <untyped> bigint)
plateOrder
untyped stringGLYPH WIDTHS, per generic family, in thousandths of the type size. This replaced a single average-width ratio per family, and the reason is worth keeping, because the average was not merely imprecise — it put names visibly off centre, by an amount that depended on which letters somebody had picked. A serif "l" is 278 and a serif "m" is 833. One number cannot stand in for both. The old ratio was 560 against a true average of 488, so a name made of narrow letters — tessellation, say — came out forty per cent too wide, and because the asterisk is placed using that figure, the whole name slid sideways by half the error. That is exactly what showed up on the proof sheet: tessellation pushed right, cosmos slightly left, say\*gm slightly right. Monospace was never off, because there one number IS the answer and 600 was within two thousandths of it. Only the three generic families are offered. Every renderer that shows these puts the SVG in a sandbox with no network, so a web font never loads — it silently substitutes, and the card becomes whatever that viewer happens to default to. serif, sans-serif and monospace are the only families that resolve everywhere. These are the metrics of the bold faces they resolve to almost everywhere; textLength covers the rest. Three digits per character, in the order of plateOrder. Names are \[a-z0-9-] — lowercase is enforced at registration — so that is the whole alphabet this has to cover.
Value
"abcdefghijklmnopqrstuvwxyz0123456789-"
plateWSerif
untyped stringValue
"500556444556444333500556278333556278833556500556556444389333556500722500500444500500500500500500500500500500333"
plateWSans
untyped stringValue
"556611556611556333611611278278556278889611611611611389556333611556778556556500556556556556556556556556556556333"
plateWMono
untyped stringValue
"602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602602"
plateWidths
func(face string) (tbl string, fallback int)- OID
- 058c22…fb3a:18
plateWidths details
plateFamily
func(face string) string- OID
- 058c22…fb3a:20
plateFamily details
plateAdvance
func(s string, face string) intplateAdvance sums a string's width in thousandths of the type size. A character outside the alphabet falls back to the family's average rather than to zero: a name that somehow escapes the pattern should lay out slightly wrong, not collapse into the asterisk.
- OID
- 058c22…fb3a:21
plateAdvance details
plateSize
func(adv int, face string) intplateSize solves for the largest type size whose width fits. The asterisk occupies a column of its own, 0.86 of the type size wide, which is why it is in the denominator rather than added afterwards: the letters and the asterisk have to shrink together or the proportions drift as a name gets longer. That is the whole reason this is one calculation instead of two. \`adv\` is the summed advance of both halves, in thousandths.
- OID
- 058c22…fb3a:22
plateSize details
plateGeom
func(size int) (capH int, xH int, starR int, starArm int, outline int)GEOMETRY, all derived from the type size so every proportion holds at every length. capH 0.70 * size the height of an uppercase letter xH 0.52 * size the height of a lowercase e — no ascender starR 0.26 * size half the x-height, so the asterisk is exactly as tall as an "e" and not as tall as a "b". Matching the cap height made it the tallest thing on the line; names are lowercase, so the x-height is what the eye reads as "letter height" and anything above it reads as a mark starArm 0.104 * size the asterisk's own stroke. This is the weight a MONOSPACED face sets an asterisk at — lighter than the letters around it, because every glyph there has to fit one fixed width — and it is the one that read best of the three that were tried, so it is now the weight in every face outline 0.05 * size the border, on the letters AND the asterisk, the same on both because a border that differs between them looks like a mistake rather than a choice. Thin enough to be a contrast device and not a second typeface: a heavy outline thickens every stem and the name stops looking like the face it was set in
- OID
- 058c22…fb3a:23
plateGeom details
starRays
[6][2]intTHE SIX RAYS, as unit vectors in thousandths. An asterisk is six rays at sixty degrees. In SVG's coordinate system y grows downward, so "up" is negative. cos30 is 866 and cos60 is 500. Listed in CLOCKWISE SCREEN ORDER starting from straight up, which starPoly depends on: it walks them in sequence to trace the outline, and any other order produces a self-intersecting polygon.
- OID
- 058c22…fb3a:25
starRays details
starPoints
func(cx int, cy int, r int, arm int) [18][2]intstarPoints returns the asterisk's outline, IN HUNDREDTHS OF A PIXEL. WHY IT IS AN OUTLINE AND NOT THREE CROSSING LINES. The border has to be the same thickness the whole way round, exactly as it is on the letters, and drawing the shape twice at two stroke widths cannot give that. A wider stroke offsets a line sideways, but where two arms MEET the two wide strokes overlap and fill the valley between them in, so the border is fat at the hub. At the tips it only extends by its own half-width, so it is thin there. That is not a rounding error, it is the wrong tool. Widening a stroke is not the same operation as offsetting a boundary, and they only agree on a shape with no concave corners. An asterisk is nothing but concave corners. The letters do not have this problem because a renderer strokes a glyph's OUTLINE, which is a true offset — every point of the border ends up the same distance from the edge. So the asterisk gets an outline too, and then exactly the same two passes the text gets. WHY HUNDREDTHS, AND NOT WHOLE PIXELS. Because whole pixels made the six arms different widths, which is worse than the problem above and was plainly visible. The arm half-width is arm/2, and at the sizes this actually renders arm is 5, so the half-width is 2.5 — a number a pixel grid cannot hold. Worse, each direction lost a DIFFERENT amount to truncation: the vertical arm computed 1000\*5/2000 = 2 exactly, while a diagonal computed (500\*5/2000, 866\*5/2000) = (1,2), whose length is 2.236. So four arms came out twelve per cent fatter than the other two, and every arm was a fifth thinner than intended. SVG coordinates are not integers, so the fix is simply to stop rounding to them. Everything below is computed in hundredths of a pixel and printed with two decimals, which brings the six half-widths to 2.500 and 2.496 — a spread of 0.16% instead of 12%. THE GEOMETRY. Each ray is a rectangle of half-width arm/2 out to radius r, cut square at the tip. Where two adjacent rays meet, their inner edges cross on the bisector — and for rays sixty degrees apart that crossing sits at radius exactly arm, which falls out of the algebra rather than being tuned. Eighteen points: two tip corners and one valley per ray, walked in the ray order above.
- OID
- 058c22…fb3a:32
starPoints details
hundredths
func(v int) stringhundredths formats a coordinate held in hundredths of a pixel. Two things it must not do, both found the hard way. The sign is peeled off first, because integer division truncates toward zero on BOTH halves and -250 would otherwise print as "-2.-50". And the fraction is assembled a digit at a time rather than with "%02d", because ufmt does not implement the zero-padding flag — it silently printed 68.0 where two digits were asked for, which the mirror diff caught as drift.
- OID
- 058c22…fb3a:33
hundredths details
starPoly
func(cx int, cy int, r int, arm int) string- OID
- 058c22…fb3a:34
starPoly details
starSVG
func(cx int, cy int, r int, arm int, outline int, ink string, edge string) stringstarSVG draws the asterisk in the two passes the lettering uses: border first at twice the width, ink over it. No face argument — one asterisk in every face, see the note at the top of the file.
- OID
- 058c22…fb3a:35
starSVG details
NameplateSVG
func(label string, domain string, face string, pos string, light bool, expires int64) stringNameplateSVG composites the name, the asterisk and the expiry over whatever is already on the card. face "mono" | "serif" | "sans" pos "top" | "middle" | "bottom" light true = white letters with a black border false = black letters with a white border Only two ink pairs exist, and that is not a limitation waiting to be lifted. Both read on every background there is; a free colour choice does not, and mid-grey lettering on a mid-grey drawing is exactly what somebody will pick. \`now\` is deliberately absent from the signature. Nothing here counts down, so nothing here needs the clock — see the note on the expiry.
- OID
- 058c22…fb3a:36
NameplateSVG details
keyFX
untyped stringSeasonal decoration, driven by DATA rather than by code. The card is generated on every read, so anything it draws can change after names have been sold. That makes a holiday overlay possible — snow in December, falling leaves in October — across every token at once. The temptation is to write a drawSnow() into this realm and a drawPumpkins() next year, which would make every new decoration a redeploy of the view. So it is parameterised instead. This realm knows how to draw drifting particles; WHICH particles, what colour, how many and until when live in a global that any admin call can rewrite. Christmas is then one transaction, not a deployment, and so is turning it off. Format, read from the global key \`cardfx\`: dir:fall;n:22;c:#ffffff;c2:#dbeafe;s:3;d:9;until:1767225600 dir fall | rise | burst drift down, drift up, or fireworks n 1..40 how many c #rrggbb colour (c2, c3 optional, cycled) s 1..6 pixel size d 2..30 seconds for one traverse (lower = faster) sv 0..3 size jitter, so they are not all identical k 4..16 sparks per shell, when dir is burst sh sq | leaf | petal particle shape; leaf and petal are angled until unix seconds, 0 = no expiry \`until\` matters more than it looks: an overlay that expires on its own cannot be forgotten in January. Everything is parsed defensively and a malformed value yields NO overlay, for the same reason the sprite parser does — this markup lands inside a token that other people's software renders.
Value
"cardfx"
overlaySVG
func(now int64) string- OID
- 058c22…fb3a:37
overlaySVG details
pickRule
func(spec string, now int64) stringA SCHEDULE, so December snows without anybody remembering to make it. This is free in a way the treasury swap was not. That needed somebody to send a transaction, because a realm cannot act on its own. This needs nobody, because the card is generated on every read and can simply look at the clock — the decoration is a rendering decision, not an action. Rules are separated by "|" and the FIRST one whose window contains today wins, so put the specific ones first: from:12-01;to:01-06;dir:fall;n:26;c:#ffffff|from:10-24;to:11-02;dir:rise;n:22;c:#f97316 from/to are MM-DD and RECUR every year, which is the point — set it once and every December snows. A window whose start is after its end wraps the new year, so 12-01 to 01-06 is what you would expect rather than an empty range. A rule with no window is always eligible, so a bare spec still behaves as it did.
- OID
- 058c22…fb3a:39
pickRule details
ruleField
func(rule string, key string) string- OID
- 058c22…fb3a:40
ruleField details
mmdd
func(s string) (int, bool)mmdd reads "12-01" into 1201. Rejects anything that is not a real calendar date, so a typo cannot silently make a rule match all year.
- OID
- 058c22…fb3a:41
mmdd details
buildOverlay
func(spec string, now int64) string- OID
- 058c22…fb3a:42
buildOverlay details
hash
func(i int) intA small deterministic spread. Not random: the same index must always give the same particle, or a cached render and a fresh one disagree.
- OID
- 058c22…fb3a:43
hash details
atoiClamp
func(s string, lo int, hi int) int- OID
- 058c22…fb3a:44
atoiClamp details
fireworks
func(shells int, sparks int, size int, dur int, cols []string) stringFireworks: a shell climbs, vanishes, and throws sparks outward. The cost question is worth answering plainly, because it is the first decoration that is not one rect per particle. A shell is 1 rect for the climb plus k rects for the burst, so \`n:5;k:10\` is 55 rects — the same order as the 34 the rain already draws, and a fifth of the 128 the domain sprite draws on every card anyway. None of it is stored: the card is built during a read, so this costs no gas and no storage, only the browser's time. That is the real ceiling, so the totals are capped below rather than left to whoever writes the spec. Two phases in one loop, driven by keyTimes on a single animation each, because a \<set> chain or nested \<g> would double the element count for the same picture.
- OID
- 058c22…fb3a:45
fireworks details
dirCos
[16]intSixteen directions on a circle, as hundredths. A table rather than math.Sin: the same integers must come out on every node, and this is the whole of the trigonometry the card needs.
- OID
- 058c22…fb3a:47
dirCos details
dirSin
[16]intSixteen directions on a circle, as hundredths. A table rather than math.Sin: the same integers must come out on every node, and this is the whole of the trigonometry the card needs.
- OID
- 058c22…fb3a:49
dirSin details
shapeAt
func(kind string, x int, y int, px int, ang int, col string) stringshapeAt draws one non-square particle at a position, turned to its own angle. Leaves and petals are two curves each rather than a polygon: a leaf is pointed at both ends, a petal is round at one, and that single difference is the whole of what distinguishes October from April. The numbers are derived from the particle size so \`s\` still means what it means everywhere else, and both shapes stay legible at the size a card is actually looked at.
- OID
- 058c22…fb3a:50
shapeAt details
weekdayMatch
func(spec string, t time.Time) boolweekdayMatch reads "fri" or "fri-13": a weekday, optionally pinned to a day of the month. Anything it does not understand matches nothing, so a typo disables its own rule rather than firing every day.
- OID
- 058c22…fb3a:51
weekdayMatch details
keyDisplayName
untyped stringProfile keys inside a name's extension slot. Conventions owned by this realm and the logic realm, not by the vault — the vault stores strings and has no opinion about what they mean.
Value
"displayName"
keyBio
untyped stringProfile keys inside a name's extension slot. Conventions owned by this realm and the logic realm, not by the vault — the vault stores strings and has no opinion about what they mean.
Value
"bio"
keyWebsite
untyped stringProfile keys inside a name's extension slot. Conventions owned by this realm and the logic realm, not by the vault — the vault stores strings and has no opinion about what they mean.
Value
"website"
Render
func(path string) string- OID
- 058c22…fb3a:52
Render details
renderHome
func() string- OID
- 058c22…fb3a:54
renderHome details
renderDomain
func(domainLabel string, after string) string- OID
- 058c22…fb3a:55
renderDomain details
namePage
untyped bigintnamePage is how many names one domain page shows. Small enough that the work is bounded whatever the registry does, large enough that an ordinary domain is one page.
Value
(100 <untyped> bigint)
renderDomainNames
func(domainLabel string, after string) string- OID
- 058c22…fb3a:56
renderDomainNames details
renderSkin
func(tid string) stringrenderSkin draws a skin DEFINITION's own card, matted and labelled, from a token id of the form "\<definition>#\<serial>". Deliberately not the name card: a skin for sale and a name for sale must not look alike, which is what skincard.gno's frame is for.
- OID
- 058c22…fb3a:57
renderSkin details
renderName
func(label string, domainLabel string) string- OID
- 058c22…fb3a:58
renderName details
splitCSV
func(s string) []string- OID
- 058c22…fb3a:59
splitCSV details
splitTokenID
func(tid string) (label string, domain string)splitTokenID splits \`label\*domain\`; a leading "\*" yields an empty label, which is how a domain page is addressed.
- OID
- 058c22…fb3a:60
splitTokenID details
escapeMarkdown
func(s string) stringescapeMarkdown neutralises user-authored text so it renders as the literal characters typed rather than as markup. Backslash-escapes the CommonMark ASCII punctuation set and folds newlines, which stops headings, lists, tables, images, links, and raw HTML alike.
- OID
- 058c22…fb3a:61
escapeMarkdown details
isSafeURL
func(u string) boolisSafeURL only admits plain http/https. Everything else — javascript:, data:, anything unrecognised — is refused a live link. Checked on read rather than on write so tightening the rule later also applies to profiles that already exist.
- OID
- 058c22…fb3a:62
isSafeURL details
skinTop
untyped bigint// the band the word SKIN sits in
Value
(74 <untyped> bigint)
skinFoot
untyped bigint// the band the skin's own name sits in
Value
(62 <untyped> bigint)
skinBox
untyped bigint// 264
Value
(264 <untyped> bigint)
skinPad
untyped bigint// 68
Value
(68 <untyped> bigint)
skinArtSVG
func(enc string, ox int, oy int, box int) stringskinArtSVG renders a sprite to fill a box, INFERRING THE GRID from the data rather than assuming twelve. spriteSVG hardcodes twelve in four places, which is right for the domain artwork it was written for and wrong here: SKINS.md §2a offers 12, 16, 24 and 32, and the whole point of inferring is that one format then covers every size and every sprite already on chain keeps working. Validation is spriteSVG's, unchanged and unrelaxed, because it is not merely a parser — it is the thing standing between community-authored data and markup inside somebody else's wallet. Any violation rejects the WHOLE sprite rather than skipping the bad part: a partial picture hides the fault.
- OID
- 058c22…fb3a:63
skinArtSVG details
SkinCardSVG
func(name string, art string, lock string, light bool) stringSkinCardSVG draws a skin token's own listing card. name the skin's name, as the shop shows it art the sprite string lock the extension it is restricted to, or "" for one that goes on any name light true = pale lettering on a dark matte false = dark lettering on a pale matte The return is a whole document rather than a fragment, because unlike the nameplate this composites over nothing — it IS the card.
- OID
- 058c22…fb3a:65
SkinCardSVG details
inkCrossover
untyped bigintTHE CROSSOVER IS AT 46, NOT 128, and getting that wrong is what made the hand-picked values worse than this rule. Contrast against white is 1.05/(L+0.05); against black it is (L+0.05)/0.05. Setting them equal gives (L+0.05)^2 = 0.0525, so L = 0.179 — about 46 out of 255. Contrast is NOT symmetric about mid-grey: black lettering on a mid-grey ground is far more readable than white on the same ground, and a threshold at 128 hands eleven cards to the wrong ink.
Value
(46 <untyped> bigint)
lumOf
func(hex string) intlumOf is relative luminance, 0..255, with a gamma of two. The real curve is sRGB's 2.4-with-a-linear-toe, which needs a pow(). Squaring is within a couple of points of it across the whole range and is exact in integers — and this feeds a THRESHOLD, where being two points out only matters for a colour already sitting on the boundary, on which no ink is much better than the other anyway.
- OID
- 058c22…fb3a:66
lumOf details
hexPair
func(a uint8, b uint8) int- OID
- 058c22…fb3a:68
hexPair details
hexDigit
func(c uint8) int- OID
- 058c22…fb3a:69
hexDigit details
bandMedian
func(art string, num int, den int) intbandMedian is the luminance of what the lettering actually sits on. THE MEDIAN, NOT THE MEAN, and the difference is not academic. A mean of black and hot pink is a mid-grey that exists nowhere on the card, and it sends the ink the wrong way on exactly the high-contrast patterns that most need the right answer. A median is what most of the band is. Three rows, because a line of type is taller than one cell at every grid size offered. The middle seventy per cent of the width, because the name is centred and the margins are not under it.
- OID
- 058c22…fb3a:70
bandMedian details
inkForArt
func(art string) boolinkForArt returns true for white lettering. The name's own band decides it alone: measured across the launch collection the date band never changed an outcome for the better, and letting it vote was what put white letters on a pale card.
- OID
- 058c22…fb3a:71
inkForArt details
faceForArt
func(art string) stringfaceForArt picks one of the three generic families from the art itself. Nothing about a background implies a typeface, so this is arbitrary — but it must be arbitrary in the SAME WAY on the chain and in the shop, or the preview shows a card that does not mint. Derived from the art rather than stored for the same reason as the ink: it costs nothing and cannot drift.
- OID
- 058c22…fb3a:72
faceForArt details
skinnedCardSVG
func(art string, label string, domain string, expires int64) stringskinnedCardSVG is the whole card: the art edge to edge, the name over it. FULL BLEED, unlike the skin's own listing card, which is matted. That contrast is deliberate and load-bearing — it is what stops somebody mistaking a skin for sale for a name for sale. See skincard.gno. The ground behind the art matters because a sprite may have transparent cells, and without it they would show whatever the viewer's page is, which is not a colour we chose.
- OID
- 058c22…fb3a:73
skinnedCardSVG details
wornArt
func(label string, domain string) stringskinFor a NAME, as opposed to skinFor the calendar. Separate because they answer different questions and only one of them involves money.
- OID
- 058c22…fb3a:74
wornArt details
skinFor
func(now int64) stringskinFor returns the skin named by whichever calendar rule is active today, or "" for the ordinary card. Reusing pickRule means a skin obeys the same window rules, the same first-match-wins ordering and the same year wrapping as every other season.
- OID
- 058c22…fb3a:75
skinFor details
skinFromSpec
func(spec string, now int64) stringSplit out so the calendar logic can be tested against a spec directly, without standing up a vault to hold one.
- OID
- 058c22…fb3a:77
skinFromSpec details
hasSkin
func(name string) boolhasSkin reports whether a name is renderable as this skin. Unknown names render nothing rather than a broken card — the same defensive stance the overlay parser takes, and for the same reason: this markup lands inside a token that other people's software renders.
- OID
- 058c22…fb3a:78
hasSkin details
seedOf
func(tid string) intseedOf turns a token id into a small number, so that a card can differ from the one beside it without any randomness — which a deterministic machine does not have. Every node computes the same answer for the same name, which is the requirement; looking arbitrary is merely the effect.
- OID
- 058c22…fb3a:79
seedOf details
skinSVG
func(name string, tid string, now int64) stringskinSVG renders a complete 400x400 card. Same frame as the ordinary one, so nothing downstream has to know which it received.
- OID
- 058c22…fb3a:80
skinSVG details
head
func(bg string) string- OID
- 058c22…fb3a:81
head details
foot
func() string- OID
- 058c22…fb3a:82
foot details
skinPizza
func(label string, size int, seed int) string\---------------- 22 May: the pizza ---------------- \* \* Eight wedges with a gap between them, so it reads as CUT rather than \* merely divided, and each wedge carries its own toppings inside its own \* group — that is what makes a slice leave with its pepperoni still on \* it instead of leaving them floating over the board. \* \* EVERY NAME EATS IN ITS OWN ORDER. The seed decides which slice goes \* first and how long each one waits, so a marketplace full of these is a \* room full of pizzas being eaten from different sides at different \* rates rather than one animation repeated. It is derived from the token \* id, so it is the same on every node and different for every name.
- OID
- 058c22…fb3a:83
skinPizza details
wedge
func(cx int, cy int, r float64, a0 float64, a1 float64, fill string) string- OID
- 058c22…fb3a:84
wedge details
polar
func(cx int, cy int, r float64, deg float64) (int, int)Degrees clockwise from twelve o'clock, which is how a pizza is cut. Integer maths throughout: a realm has no floating point worth trusting across nodes, and a pizza does not need sub-pixel accuracy.
- OID
- 058c22…fb3a:85
polar details
sinApprox
func(x float64) float64- OID
- 058c22…fb3a:86
sinApprox details
cosApprox
func(x float64) float64- OID
- 058c22…fb3a:87
cosApprox details
skinPaper
func(label string, size int, now int64) string\---------------- 3 Jan: the front page ----------------
- OID
- 058c22…fb3a:88
skinPaper details
skinCal
func(label string, size int) string\---------------- 29 Feb: the calendar page ----------------
- OID
- 058c22…fb3a:89
skinCal details
skinFog
func(label string, size int) string\---------------- 20 Apr: the fog ----------------
- OID
- 058c22…fb3a:90
skinFog details
skinSlash
func(label string, size int) string\---------------- Friday 13th: the poster ----------------
- OID
- 058c22…fb3a:91
skinSlash details
skinPi
func(label string, size int) string\---------------- 14 Mar: pi ----------------
- OID
- 058c22…fb3a:92
skinPi details
skinHeart
func(label string, size int) string\---------------- 14 Feb: the heart ---------------- \* \* A real double thump, not one pulse: squeeze, small recoil, second \* smaller squeeze, then a long rest. The rest is most of what makes it \* read as a heartbeat rather than a throb.
- OID
- 058c22…fb3a:93
skinHeart details
esc
func(s string) stringMarkup safety, same as everywhere else that puts a name into SVG: a label is validated on registration, but this is the last gate before it lands inside somebody else's renderer.
- OID
- 058c22…fb3a:94
esc details
TokenURI
func(tid string) stringTokenURI returns the full data: URI for a token, generated on every read so the image always reflects current state (never baked at mint).
- OID
- 058c22…fb3a:95
TokenURI details
BuildSVG
func(tid string) stringBuildSVG renders a token. Everything on the card derives from four stored values — owner, registered, expires, and the domain's drawing — so a renewal moves \`expires\` and the bar, the time remaining and the years held all follow from it. Nothing is baked at mint: the card is generated on every read, which is what lets a domain's artwork be changed after its names have been sold.
- OID
- 058c22…fb3a:97
BuildSVG details
cardSVG
func(tid string, art string, registered int64, expires int64, streak int64, frozen bool, now int64, overlay string) stringcardSVG is the card itself, with every stored value already resolved. Split out from BuildSVG so the markup can be rendered — and asserted against — without a vault under it. TestStaticFrameShowsTheDate is the reason: what a marketplace shows as a still is a property of this string and of nothing else, and it deserves to be checked rather than assumed.
- OID
- 058c22…fb3a:98
cardSVG details
graceHeartbeat
func(body string, statusWash string) stringgraceHeartbeat re-wraps three pieces of an already-built grace card in the failing-heartbeat flicker, leaving the status band and the date line between them untouched. See the "GRACE AND LAPSED, LAYERED ON TOP" comment in cardSVG for why this operates on the finished string rather than building a second template. Every anchor here is fixed template text, not a dynamic value, so it does not drift when a colour, a date or a name changes -- only when the template itself does, and TestGraceHeartbeatLeavesBandAndDateAlone would catch that (the -1 guards below make a missed anchor degrade to the plain card rather than a corrupted one).
- OID
- 058c22…fb3a:99
graceHeartbeat details
fitSize
func(s string) intfitSize solves for a size rather than stepping through a ladder. Monospace glyphs are ~0.6em, so a name up to the full 32-character limit sets on one line without overflowing 336px.
- OID
- 058c22…fb3a:100
fitSize details
domainAccent
func(domainLabel string) stringdomainAccent reads the accent out of the domain's stored drawing, so the card wears the domain's own colour without this realm carrying a palette of its own. Falls back to the house colour.
- OID
- 058c22…fb3a:101
domainAccent details
accentOf
func(enc string) string- OID
- 058c22…fb3a:102
accentOf details
remaining
func(expires int64, now int64) string- OID
- 058c22…fb3a:103
remaining details
keyArt
untyped stringkeyArt is the per-domain extension key holding the drawing. The value is "A#facc15,B#7c2d12|............|....AAAA....|..." — the palette, then twelve rows of twelve characters where '.' is transparent. PARSED DEFENSIVELY AND NEVER TRUSTED. It is written by whichever logic realm is current, so a future one could store something malformed; anything unexpected yields no sprite rather than broken markup inside a token that a marketplace will render.
Value
"art"
spriteSVG
func(enc string, px int, ox int, oy int, opacity string) string- OID
- 058c22…fb3a:104
spriteSVG details
isHexColour
func(s string) bool- OID
- 058c22…fb3a:105
isHexColour details
placeholderSVG
func(msg string) string- OID
- 058c22…fb3a:106
placeholderSVG details
daySeconds
int64statusFields formats a lifecycle state for display. Reads the grace period from nsdata rather than hardcoding it, so retuning the lifecycle is a config change and this stays correct. All lifecycle times are unix seconds, so this is integer arithmetic over a day constant rather than duration division.
Value
86400
metal
type\---- THE METAL LADDER ---- \* \* From the "Name States" study, 2026-08-23, where it was designed and \* then not built for a fortnight. One rung per year held, carried by the \* frame: 1 bronze, 2 silver, 3 gold, 4 platinum, 5+ diamond. \* \* THE LADDER IS PICKED FOR SEPARATION, not for what the metals are \* worth. Brown, then cool grey, then yellow, then warm white, then \* something that is not a metal at all. The obvious ladder — silver, \* platinum, rhodium — is three whitish metals in a row, and nobody can \* tell year two from year five across a grid of thumbnails. Platinum is \* pulled deliberately WARM to keep it off the gold below it. \* \* The highlight travels faster at the higher rungs, so a longer-held \* name reads as brighter as well as merely different. \* \* DIAMOND IS SATURATED AND KEEPS ITS WHITE. Fire in a real stone is \* spectral colour separated BY white; take the white bands out and it \* stops reading as a gem and starts reading as an oil slick. \* \* IT ADDS NO WORDS. The ribbon has said DIAMOND HANDS since year one and \* at year five the card finally makes good on it. The domain's own \* colour still runs through the type and the term bar, so a name reads \* as its domain first and its tenure second. \* \* YEAR ZERO GETS NO METAL, which is the whole point of a ladder: if a \* new name looked decorated, the rungs a name EARNS would mean nothing. \* \* Mirrored in the site's nft.js. These two move together or a preview \* stops matching what the chain draws.
Value
v1.metal
metalFor
func(streak int64) (v1.metal, bool)- OID
- 058c22…fb3a:107
metalFor details
metalFrame
func(tid string, status string, edge string, streak int64) (defs string, stroke string, width string)metalFrame returns the \<defs>, the stroke and the stroke width for a card, or the plain versions when no metal is earned. THE METAL ONLY APPLIES TO A LIVE NAME. A gilded frame around a lapsed card would celebrate a name that is about to belong to somebody else, and it would fight the red wash that exists to say so. Grace and lapsed lose the metal, which is itself information: the frame going plain is the tenure visibly at risk. THE GRADIENT ID CARRIES THE TOKEN. A Render() page draws many of these into one document and SVG resolves url(#id) document-wide, so a fixed id would make every card after the first wear the first card's metal. The token id is unique by construction; it is sanitised because "\*" and "-" are not valid in an id.
- OID
- 058c22…fb3a:108
metalFrame details
idSafe
func(tid string) stringAn XML id may not contain "\*", and a name may not contain anything else that would need escaping — the registry's own label rules see to that — so one substitution is enough.
- OID
- 058c22…fb3a:109
idSafe details
smallRibbon
func(status string, edge string, ribbon string, band string) stringThe foot of the card: a quiet ribbon while the name is alive, the loud band once it is not. One function so the two cannot both be drawn.
- OID
- 058c22…fb3a:110
smallRibbon details
graceValues
untyped string\---- FAILING HEARTBEAT (grace period) ---- \* \* From the "Name States" study, 2026-08-23 -- the "Failing heartbeat" \* treatment, built around pulses()/BURSTS/track() there. Bursts of two, \* one, three and one flash at 0.02/0.30/0.55/0.80 of a 7.5s cycle read \* as an unpredictable fault rather than an even, designed blink, so the \* rhythm carries over unchanged. graceValues and graceKeyTimes below are \* that generator's output for base=1, dip=0.25 -- computed once, not \* rebuilt on every read, since a fixed rhythm shared by every grace card \* on the chain has nothing left to vary. \* \* INVERTED FROM THE STUDY, and deliberately. There the track sits DARK \* and briefly flashes LIT -- fine for something animating live in a \* browser tab, useless for a marketplace that renders one still frame: \* the base opacity in the markup and the first keyframe are BOTH the \* dark value there, so a still render of that track is nearly \* invisible. Here the rest state is fully lit -- opacity 1, exactly the \* live card -- and the bursts are brief DIPS toward .25 instead, so \* whichever still a renderer takes (the base attribute for one that \* ignores SMIL, the first keyframe for one that honours it) lands on \* the fully-lit card. graceValues therefore reads "1;1;0.25;..." and \* graceKeyTimes starts "0.0000;...": both a "1" at index zero, on \* purpose. TestGraceHeartbeatLeavesBandAndDateAlone checks exactly that. \* \* Mirrored in the site's nft.js (pulseTrack/heartbeat there, same two \* numbers). If this changes, that changes, or a preview stops matching \* what the chain draws.
Value
"1;1;0.25;0.25;1;1;0.25;0.25;1;1;0.25;0.25;1;1;0.25;0.25;1;1;0.25;0.25;1;1;0.25;0.25;1;1;0.25;0.25;1;1"
graceKeyTimes
untyped stringValue
"0.0000;0.0200;0.0240;0.0350;0.0390;0.0570;0.0610;0.0720;0.0760;0.3000;0.3040;0.3150;0.3190;0.5500;0.5540;0.5650;0.5690;0.5870;0.5910;0.6020;0.6060;0.6240;0.6280;0.6390;0.6430;0.8000;0.8040;0.8150;0.8190;1.0000"
heartbeat
func(inner string) stringheartbeat wraps one piece of a grace card in the failing-heartbeat flicker. Called three separate times in graceHeartbeat rather than once around the whole card, so the status band and the date line can sit between the wrapped pieces untouched. Every call shares the same values/keyTimes/dur; SMIL times every \<animate> from document load, so three separate \<g>s stay in lockstep without needing to share one.
- OID
- 058c22…fb3a:111
heartbeat details
crackOverlay
untyped string\---- CRACKED (lapsed) ---- \* \* From the same study, under LAPSED. Static, no animation: a fracture \* across the glass with two short branches, drawn last so it sits over \* everything else on the card; the art behind it is untouched and \* undimmed. \* \* THE STUDY'S PATH IS NOT USED AS-IS. Its middle segment runs through \* x=140-176 at y=168-214 -- dead centre, right through where the name \* sits on the study's fixed-width demo cards. That is not fine here: \* the name is centred on x=200 with text-anchor="middle" and can run up \* to 32 characters (see fitSize), so its box can span nearly the full \* card width -- as far out as roughly x=30 to x=370 -- for a long one. \* No amount of routing the crack sideways dodges that band, because a \* long enough name is somewhere underneath any vertical line drawn \* across it. \* \* So the crack is drawn as two pieces rather than one continuous line: \* an upper fracture that stays above y=158 and a lower one that stays \* below y=224, leaving the name's row (166-206) and the date line under \* it (214-228) untouched regardless of how long the name is. It reads \* as the fracture running into the nameplate and continuing on the far \* side, rather than through it -- arguably truer to how a crack behaves \* against a raised, painted surface anyway. Other type on the card (the \* term bar, the held count) is still legitimately crossed, same as in \* the study; only the name and the date get the gap. \* \* Mirrored byte-for-byte in the site's nft.js (CRACK\_OVERLAY there).
Value
"<g stroke=\"#ffffff\" fill=\"none\" opacity=\"0.5\" stroke-width=\"2\"><path d=\"M92 24 L150 72 L112 118 L162 152\"/><path d=\"M112 118 L54 138\"/><path d=\"M158 224 L206 270 L172 318 L228 388\"/><path d=\"M172 318 L268 300\"/></g><g stroke=\"#000000\" fill=\"none\" opacity=\""...
statusFields
func(expires int64, frozen bool) (status string, edge string)- OID
- 058c22…fb3a:112
statusFields details
escapeXML
func(s string) string- OID
- 058c22…fb3a:113