Search Apps Documentation Source Content File Folder Download Copy Actions Download

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 += "![Latest capture](" + latest.ImageURL + ")\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}