Kind Realm (renderable)
Name home
Namespace g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c
Exported functions 11
Module gno.land/r/g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c/home
gno 0.9 home.gno gno
⧉
1 // Package home is zôÖma's profile page on gno.land. 2 // 3 // gnoweb renders a profile by calling Render("") on the realm at exactly 4 // /r/<namespace>/home, so this path carries no /vN suffix. 5 // 6 // The page is assembled from SLOTS: named markdown fragments, one Set 7 // transaction each. The slot named "layout" is the template; every :slug: Append (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, slug string, body string)
Delete (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, slug string)
DeleteGallery (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string)
Get (slug string) string
HasHeart (addr string) bool
Heart (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
Hearts () int
Render (path string) string
Revision () int
Set (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, slug string, body string)
SetGallery (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, spec string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
8 // placeholder in it is replaced by the slot of that name, in a single pass.
9 // init seeds every slot from content.gno, so the page is complete the block
10 // it goes live and later edits are data, not redeploys.
11 //
12 // One thing on this page is not written by its owner: visitors can send a
13 // heart (<3), one per address, and the page counts them.
14 package home
15
16 import (
17 "strconv"
18 "strings"
19
20 "chain/runtime"
21
22 "gno.land/p/nt/avl/v0"
23 )
24
25 // admin is the only address allowed to edit slots.
26 const admin = address( "g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c" )
27
28 const (
29 layoutSlug = "layout"
30 maxSlugLen = 64
31 maxRecent = 7
32 )
33
34 type slot struct {
35 body string
36 rev int
37 updated int64
38 }
39
40 var (
41 slots = avl.NewTree() // slug -> *slot
42 rev int // total slot writes
43
44 hearts = avl.NewTree() // address string -> int64 height of the heart
45 recent []address // newest first, at most maxRecent
46 )
47
48 // reservedSlugs are placeholders computed at render time. A slot may not take
49 // one of these names, so nothing a slot says can shadow chain state.
50 var reservedSlugs = [] string { "chainid" , "height" , "hearts" , "hero" , "realm" , "rev" }
51
52 func init() {
53 for _, s := range defaultSlots {
54 rev++
55 slots.Set(s.slug, &slot{body: s.body, rev: rev, updated: runtime.ChainHeight()})
56 }
57 for _, g := range defaultGalleries {
58 galleries.Set(g.name, parseGallery(g.spec))
59 }
60 }
61
62 func assertAdmin(cur realm) {
63 if cur.Previous().Address() != admin {
64 panic( "restricted to admin" )
65 }
66 }
67
68 func validSlug(slug string ) bool {
69 if len(slug) == 0 || len(slug) > maxSlugLen {
70 return false
71 }
72 for i := 0 ; i < len(slug); i++ {
73 c := slug[i]
74 if !(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.' ) {
75 return false
76 }
77 }
78 return true
79 }
80
81 func assertWritableSlug(slug string ) {
82 if !validSlug(slug) {
83 panic( "invalid slug: want 1-" + strconv.Itoa(maxSlugLen) + " bytes of [a-z0-9._-], got " + strconv.Quote(slug))
84 }
85 for _, r := range reservedSlugs {
86 if slug == r {
87 panic( "reserved slug: " + slug)
88 }
89 }
90 if strings.HasPrefix(slug, galleryPrefix) {
91 panic( "reserved slug: " + galleryPrefix + "* renders a gallery, use SetGallery" )
92 }
93 }
94
95 // Set creates or replaces one slot.
96 func Set(cur realm, slug, body string ) {
97 assertAdmin(cur)
98 assertWritableSlug(slug)
99 rev++
100 slots.Set(slug, &slot{body: body, rev: rev, updated: runtime.ChainHeight()})
101 }
102
103 // Append adds to the end of a slot, creating it when absent, for bodies too
104 // large for one transaction.
105 func Append(cur realm, slug, body string ) {
106 assertAdmin(cur)
107 assertWritableSlug(slug)
108 prev := ""
109 if v := slots.Get(slug); v != nil {
110 prev = v.(*slot).body
111 }
112 rev++
113 slots.Set(slug, &slot{body: prev + body, rev: rev, updated: runtime.ChainHeight()})
114 }
115
116 // Delete removes a slot. Deleting "layout" falls back to the built-in layout.
117 func Delete(cur realm, slug string ) {
118 assertAdmin(cur)
119 if _, removed := slots.Remove(slug); !removed {
120 panic( "no such slot: " + slug)
121 }
122 rev++
123 }
124
125 // Get returns a slot body, or "" when the slot does not exist.
126 func Get(slug string ) string {
127 if v := slots.Get(slug); v != nil {
128 return v.(*slot).body
129 }
130 return ""
131 }
132
133 // Revision is the total number of slot writes accepted.
134 func Revision() int { return rev }
135
136 // Heart records one <3 from the caller. Each address can send exactly one.
137 func Heart(cur realm) {
138 from := cur.Previous().Address()
139 if hearts.Has(from.String()) {
140 panic( "already sent: one <3 per address" )
141 }
142 hearts.Set(from.String(), runtime.ChainHeight())
143
144 recent = append([]address{from}, recent...)
145 if len(recent) > maxRecent {
146 recent = recent[:maxRecent]
147 }
148 }
149
150 // Hearts returns how many distinct addresses sent a <3.
151 func Hearts() int { return hearts.Size() }
152
153 // HasHeart reports whether addr already sent a <3.
154 func HasHeart(addr address) bool { return hearts.Has(addr.String()) }
155
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.