Kind Realm (renderable)
Name home
Namespace g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c
Exported functions 11
Module gno.land/r/g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c/home
gno 0.9 render.gno gno
⧉
1 package home 2 3 import ( 4 "strconv" 5 "strings" 6 7 "chain/runtime" 8 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.
)
9
10 const (
11 realmPath = "gno.land/r/g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c/home"
12 webPath = "/r/g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c/home"
13 )
14
15 // fallbackLayout renders when the layout slot is deleted.
16 const fallbackLayout = "# zôÖma\n\n:bio:\n\n:hearts:\n"
17
18 // Render serves:
19 //
20 // "" the profile page
21 // "slots" the slot index
22 // "slots/<slug>" one slot's raw markdown
23 // "hearts" everyone who sent a <3
24 func Render(path string ) string {
25 switch {
26 case path == "" :
27 return renderPage()
28 case path == "slots" :
29 return renderIndex()
30 case strings.HasPrefix(path, "slots/" ):
31 return renderSlot(strings.TrimPrefix(path, "slots/" ))
32 case path == "hearts" :
33 return renderHearts()
34 }
35 return "# Not found\n\nNo such path: " + strconv.Quote(path) + "\n\n[Back home](" + webPath + ")\n"
36 }
37
38 func renderPage() string {
39 layout := Get(layoutSlug)
40 if layout == "" {
41 layout = fallbackLayout
42 }
43 return fill(layout)
44 }
45
46 // fill replaces every :slug: in s, in one left-to-right pass. Replacement text
47 // is never rescanned, so a slot cannot expand into another slot. A placeholder
48 // with no value stays verbatim: a missing section should be visible.
49 func fill(s string ) string {
50 var b strings.Builder
51 for {
52 i := strings.IndexByte(s, ':' )
53 if i < 0 {
54 b.WriteString(s)
55 return b.String()
56 }
57 b.WriteString(s[:i])
58 s = s[i:]
59 j := strings.IndexByte(s[ 1 :], ':' )
60 if j < 0 {
61 b.WriteString(s)
62 return b.String()
63 }
64 slug := s[ 1 : j+ 1 ]
65 if v, ok := lookup(slug); ok {
66 b.WriteString(v)
67 s = s[j+ 2 :]
68 continue
69 }
70 // Not a placeholder: emit the first ':' and rescan from the next one,
71 // so "a:b :bio:" still finds :bio:.
72 b.WriteString( ":" )
73 s = s[ 1 :]
74 }
75 }
76
77 func lookup(slug string ) ( string , bool ) {
78 if !validSlug(slug) {
79 return "" , false
80 }
81 switch slug {
82 case "chainid" :
83 return runtime.ChainID(), true
84 case "height" :
85 return strconv.FormatInt(runtime.ChainHeight(), 10 ), true
86 case "hearts" :
87 return heartsBlock(), true
88 case "hero" :
89 return heroImage(), true
90 case "realm" :
91 return realmPath, true
92 case "rev" :
93 return strconv.Itoa(rev), true
94 }
95 if strings.HasPrefix(slug, galleryPrefix) {
96 return renderGallery(strings.TrimPrefix(slug, galleryPrefix))
97 }
98 if v := slots.Get(slug); v != nil {
99 return v.(*slot).body, true
100 }
101 return "" , false
102 }
103
104 func heartsBlock() string {
105 n := hearts.Size()
106 var b strings.Builder
107 b.WriteString( "**" )
108 b.WriteString(strconv.Itoa(n))
109 if n == 1 {
110 b.WriteString( "** heart so far." )
111 } else {
112 b.WriteString( "** hearts so far." )
113 }
114 b.WriteString( " [Send yours](" + webPath + "$help&func=Heart): one transaction, one per address, forever on chain." )
115 if len(recent) > 0 {
116 b.WriteString( "\n\nLatest: " )
117 for i, a := range recent {
118 if i > 0 {
119 b.WriteString( " · " )
120 }
121 b.WriteString(userLink(a))
122 }
123 b.WriteString( " · [all](" + webPath + ":hearts)" )
124 }
125 return b.String()
126 }
127
128 // userLink renders an address as a short link to its gno.land profile.
129 func userLink(a address) string {
130 s := a.String()
131 short := s
132 if len(s) > 12 {
133 short = s[: 7 ] + "…" + s[len(s)- 4 :]
134 }
135 return "[" + short + "](/u/" + s + ")"
136 }
137
138 func renderHearts() string {
139 var b strings.Builder
140 b.WriteString( "# <3\n\n" )
141 b.WriteString(strconv.Itoa(hearts.Size()) + " address(es) sent a heart. [Send yours](" + webPath + "$help&func=Heart).\n\n" )
142 if hearts.Size() == 0 {
143 b.WriteString( "_Be the first._\n" )
144 return b.String()
145 }
146 b.WriteString( "| from | block |\n| --- | ---: |\n" )
147 hearts.Iterate( "" , "" , func (key string , value any ) bool {
148 b.WriteString( "| " + userLink(address(key)) + " | " + strconv.FormatInt(value.( int64 ), 10 ) + " |\n" )
149 return false
150 })
151 b.WriteString( "\n[Back home](" + webPath + ")\n" )
152 return b.String()
153 }
154
155 func renderIndex() string {
156 var b strings.Builder
157 b.WriteString( "# Slots\n\nrev " + strconv.Itoa(rev) + " · " + strconv.Itoa(slots.Size()) + " slot(s)\n\n" )
158 b.WriteString( "| slot | bytes | rev | height |\n| --- | ---: | ---: | ---: |\n" )
159 slots.Iterate( "" , "" , func (key string , value any ) bool {
160 s := value.(*slot)
161 b.WriteString( "| [" + key + "](" + webPath + ":slots/" + key + ") | " +
162 strconv.Itoa(len(s.body)) + " | " + strconv.Itoa(s.rev) + " | " +
163 strconv.FormatInt(s.updated, 10 ) + " |\n" )
164 return false
165 })
166 return b.String()
167 }
168
169 func renderSlot(slug string ) string {
170 v := slots.Get(slug)
171 if v == nil {
172 return "# Not found\n\nNo slot named " + strconv.Quote(slug) + ".\n\n[Slot index](" + webPath + ":slots)\n"
173 }
174 s := v.(*slot)
175 body := s.body
176 if !strings.HasSuffix(body, "\n" ) {
177 body += "\n"
178 }
179 return "# " + slug + "\n\n" + strconv.Itoa(len(s.body)) + " bytes · rev " + strconv.Itoa(s.rev) +
180 " · written at height " + strconv.FormatInt(s.updated, 10 ) + "\n\n````\n" + body + "````\n"
181 }
182
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.