render.gno
6.89 Kb ยท 211 lines
1package nightsky
2
3import (
4 "strings"
5 "time"
6
7 "gno.land/p/jeronimoalbi/mdform"
8 "gno.land/p/moul/md"
9 "gno.land/p/nt/ufmt/v0"
10)
11
12// RenderNightsky generates the markdown output for the telescope realm.
13// Call this from your realm's Render(path string) string function.
14func (r *TelescopeRealm) RenderNightsky(path string) string {
15 switch path {
16 case "status":
17 return r.renderStatus()
18 case "commands":
19 return r.renderCommandQueue()
20 case "captures":
21 return r.renderCaptureHistory()
22 case "access":
23 return r.renderAccessRules()
24 case "commandData":
25 return r.renderCommandData()
26 case "control":
27 return r.renderControl()
28 default:
29 return r.renderMainPage()
30 }
31}
32
33func (r *TelescopeRealm) renderMainPage() string {
34 out := "# " + r.Config.Name + " ๐ญ\n\n"
35
36 statusIcon := "๐ด"
37 switch r.Config.Status {
38 case "online":
39 statusIcon = "๐ข"
40 case "busy":
41 statusIcon = "๐ก"
42 case "error":
43 statusIcon = "๐ด"
44 }
45
46 out += statusIcon + " **Status:** " + r.Config.Status + "\n\n"
47 out += "**Model:** " + r.Config.Model + "\n\n"
48 out += ufmt.Sprintf("**Position:** %.2fยฐ, %.2fยฐ\n\n", r.Config.Latitude, r.Config.Longitude)
49
50 out += "## Quick Stats\n\n"
51 out += ufmt.Sprintf("๐ Commands in queue: %d\n\n", len(r.CommandQueue))
52 out += ufmt.Sprintf("๐ธ Total captures: %d\n\n", r.Captures.Len())
53 out += ufmt.Sprintf("๐ฅ Authorized users: %d\n\n", r.AccessRules.Size())
54
55 out += "## Quick Links\n\n"
56 out += md.Link("๐ฎ Control Telescope", r.path(":control")) + " - Submit commands\n\n"
57 out += md.Link("๐ฅ Access Rules", r.path(":access")) + " - View and manage user access\n\n"
58 out += md.Link("๐ Command Queue", r.path(":commands")) + "\n\n"
59 out += md.Link("๐ธ Capture History", r.path(":captures")) + "\n\n"
60
61 if latest, ok := r.Captures.Latest(); ok {
62 out += "## Latest Capture\n\n"
63 out += "\n\n"
64 out += ufmt.Sprintf("_RA %.8fh ยท Dec %.8fยฐ ยท %ds_\n\n", latest.TargetRA, latest.TargetDec, latest.Exposure)
65 }
66
67 out += "---\n\n"
68 out += "## CLI Usage\n\n"
69 out += "```\n"
70 out += "gnokey maketx call -pkgpath \"gno.land" + r.Config.RealmPath + "\" \\\n"
71 out += " -func \"SubmitCommand\" \\\n"
72 out += " -args \"capture\" -args \"5.5\" -args \"22.5\" -args \"60\" \\\n"
73 out += " -gas-fee 1000000ugnot -gas-wanted 2000000 \\\n"
74 out += " -broadcast YOUR_ADDRESS\n"
75 out += "```\n\n"
76
77 out += md.Link("โ Back to NightSky Network", "/r/vik000/nightsky") + "\n\n"
78 return out
79}
80
81func (r *TelescopeRealm) renderControl() string {
82 out := "# ๐ฎ Control Telescope\n\n"
83 out += "**Note:** You must have access to submit commands.\n\n"
84 out += "---\n\n"
85
86 form := mdform.
87 New("exec", "SubmitCommand").
88 Select("commandType", "capture", "description", "Command type").
89 Select("commandType", "stop").
90 Input("targetRA", "placeholder", "0.0", "description", "Right Ascension (0โ24h) โ leave empty for stop").
91 Input("targetDec", "placeholder", "0.0", "description", "Declination (โ90โ90ยฐ) โ leave empty for stop").
92 Input("exposure", "placeholder", "60", "description", "Exposure time in seconds (1โ300) โ leave empty for stop")
93
94 out += form.String() + "\n\n"
95 out += "---\n\n"
96 out += md.Link("โ Back", r.path("")) + "\n\n"
97 return out
98}
99
100func (r *TelescopeRealm) renderCommandQueue() string {
101 out := "# Command Queue\n\n"
102
103 if len(r.CommandQueue) == 0 {
104 out += "No pending commands.\n\n"
105 } else {
106 out += ufmt.Sprintf("%d command(s) pending:\n\n", len(r.CommandQueue))
107 for i, cmd := range r.CommandQueue {
108 out += ufmt.Sprintf("### Command #%d\n\n", i+1)
109 out += "**Type:** " + cmd.CommandType + "\n\n"
110 if cmd.CommandType == "capture" {
111 out += ufmt.Sprintf("**Target:** RA %.8fh, Dec %.8fยฐ\n\n", cmd.TargetRA, cmd.TargetDec)
112 out += ufmt.Sprintf("**Exposure:** %d seconds\n\n", cmd.Exposure)
113 }
114 out += "**Requested by:** " + cmd.RequestedBy + "\n\n"
115 out += "---\n\n"
116 }
117 }
118
119 out += md.Link("โ Back", r.path("")) + "\n\n"
120 return out
121}
122
123func (r *TelescopeRealm) renderCaptureHistory() string {
124 out := "# Capture History\n\n"
125
126 if r.Captures.Len() == 0 {
127 out += "No captures yet.\n\n"
128 } else {
129 out += ufmt.Sprintf("Showing %d most recent captures:\n\n", r.Captures.Len())
130 i := 0
131 r.Captures.IterateNewest(func(capture CaptureResult) bool {
132 i++
133 out += ufmt.Sprintf("### Capture #%d\n\n", i)
134 out += ufmt.Sprintf("**Target:** RA %.8fh, Dec %.8fยฐ\n\n", capture.TargetRA, capture.TargetDec)
135 out += ufmt.Sprintf("**Exposure:** %d seconds\n\n", capture.Exposure)
136 out += "**Captured by:** " + capture.CapturedBy + "\n\n"
137 out += "**Image:** " + md.Link("View", capture.ImageURL) + "\n\n"
138 out += "---\n\n"
139 return false
140 })
141 }
142
143 out += md.Link("โ Back", r.path("")) + "\n\n"
144 return out
145}
146
147func (r *TelescopeRealm) renderAccessRules() string {
148 out := "# ๐ฅ Access Rules\n\n"
149
150 if r.AccessRules.Size() == 0 {
151 out += "No access rules configured. Only the owner can use the telescope.\n\n"
152 } else {
153 out += ufmt.Sprintf("%d user(s) have access:\n\n", r.AccessRules.Size())
154 r.AccessRules.Iterate("", "", func(_ string, value any) bool {
155 rule := value.(AccessRule)
156 out += "**User:** " + rule.AllowedAddress
157 if rule.ExpiresAt.IsZero() {
158 out += " (Expires: Never)\n\n"
159 } else {
160 out += " (Expires: " + rule.ExpiresAt.Format(time.RFC1123) + ")\n\n"
161 }
162 out += "---\n\n"
163 return false
164 })
165 }
166
167 out += "## Grant Access\n\n"
168 out += mdform.
169 New("exec", "GrantAccess").
170 Input("address", "placeholder", "g1...", "description", "Gno address to grant access to").
171 Input("durationDays", "placeholder", "0", "description", "Duration in days (0 = never expires)").
172 String() + "\n\n"
173
174 out += "## Revoke Access\n\n"
175 out += mdform.
176 New("exec", "RevokeAccess").
177 Input("address", "placeholder", "g1...", "description", "Gno address to revoke").
178 String() + "\n\n"
179
180 out += "---\n\n"
181 out += md.Link("โ Back", r.path("")) + "\n\n"
182 return out
183}
184
185// renderStatus returns a machine-readable status block for the telescope controller.
186func (r *TelescopeRealm) renderStatus() string {
187 out := ufmt.Sprintf("commands=%d\n", len(r.CommandQueue))
188 out += "status=" + r.Config.Status
189 return "```\n" + out + "\n```"
190}
191
192// renderCommandData returns the first queued command in a machine-readable block.
193func (r *TelescopeRealm) renderCommandData() string {
194 if len(r.CommandQueue) == 0 {
195 return "```\nno_commands\n```"
196 }
197 cmd := r.CommandQueue[0]
198 data := "type=" + cmd.CommandType + "\n"
199 data += ufmt.Sprintf("ra=%.8f\n", cmd.TargetRA)
200 data += ufmt.Sprintf("dec=%.8f\n", cmd.TargetDec)
201 data += ufmt.Sprintf("exposure=%d\n", cmd.Exposure)
202 data += "requester=" + cmd.RequestedBy
203 return "```\n" + data + "\n```"
204}
205
206// path builds an absolute URL path for this realm with an optional suffix,
207// using the pkgPath stored on the config at construction time. This avoids a
208// runtime realm lookup entirely (no chain/runtime/unsafe needed).
209func (r *TelescopeRealm) path(addon string) string {
210 return strings.TrimPrefix(r.Config.RealmPath, "gno.land") + addon
211}