From 26c0cb0f43108b5da28644859239b1ea2b28959e Mon Sep 17 00:00:00 2001
From: Brittany Ellich
Date: Fri, 15 May 2026 11:14:54 -0700
Subject: [PATCH] feat(about,demo): port /about-atproto, /demo/clock, /healthz
Fill in three remaining routes from the old atmoquest app that were
missing after the feature-folded migration:
- features/about/{routes.go, pages/about.templ}: short "What's
ATProto?" explainer page linked from the guest landing.
- features/demo/routes.go: Datastar SSE clock endpoint that streams
the current UTC time into the statusbar #clock once a second.
- router/router.go: register the two new feature subrouters and add
the /healthz probe endpoint.
- features/index/pages/index.templ: restore the live clock by binding
#clock to data-on-load="@get('/demo/clock')" on both the authed
and guest statusbars (was a static --:--:-- placeholder).
Build clean, all tests pass, smoke-tested on :9090:
/healthz 200
/about-atproto 200 (2369 bytes)
/demo/clock SSE patch frames streaming
/ 200 (guest landing unchanged)
---
features/about/pages/about.templ | 43 +++++++++++
features/about/pages/about_templ.go | 77 +++++++++++++++++++
features/about/routes.go | 26 +++++++
features/demo/routes.go | 48 ++++++++++++
features/index/pages/index.templ | 4 +-
features/index/pages/index_templ.go | 114 +++++++++++++++++-----------
router/router.go | 9 +++
7 files changed, 275 insertions(+), 46 deletions(-)
create mode 100644 features/about/pages/about.templ
create mode 100644 features/about/pages/about_templ.go
create mode 100644 features/about/routes.go
create mode 100644 features/demo/routes.go
diff --git a/features/about/pages/about.templ b/features/about/pages/about.templ
new file mode 100644
index 0000000..64dffdb
--- /dev/null
+++ b/features/about/pages/about.templ
@@ -0,0 +1,43 @@
+package pages
+
+import "atmoquest/features/common/layouts"
+
+// AboutATProto is a short explainer page linked from the guest landing.
+// Intentionally tiny — its job is to give a curious visitor enough context
+// to understand what "your data goes to your repo, not ours" means before
+// they sign in.
+templ AboutATProto() {
+ @layouts.Base("What's ATProto? — atmo.quest", "ATProto is an open social protocol — your records live in your repo, not ours.") {
+
+ ATProto is an open social protocol. Instead of accounts living inside one company's database, your records live in your repo — a personal data store you can take with you.
+
+
+
+
+ atmo.quest writes connection, check-in, and badge records to your PDS. If atmo.quest disappears tomorrow, those records still exist in your repo and can be read by any other ATProto-aware app.
+
ATProto is an open social protocol. Instead of accounts living inside one company's database, your records live in your repo — a personal data store you can take with you.
atmo.quest writes connection, check-in, and badge records to your PDS. If atmo.quest disappears tomorrow, those records still exist in your repo and can be read by any other ATProto-aware app.
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+ templ_7745c5c3_Err = layouts.Base("What's ATProto? — atmo.quest", "ATProto is an open social protocol — your records live in your repo, not ours.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/features/about/routes.go b/features/about/routes.go
new file mode 100644
index 0000000..3f2d79b
--- /dev/null
+++ b/features/about/routes.go
@@ -0,0 +1,26 @@
+// Package about hosts the /about-atproto explainer page. It's intentionally
+// scope-minimal — a single GET that renders a static templ — but lives in
+// its own feature folder so the routes file in the router stays tidy and
+// future related pages (e.g. /about-collective) have an obvious home.
+package about
+
+import (
+ "log/slog"
+ "net/http"
+
+ "github.com/go-chi/chi/v5"
+
+ "atmoquest/features/about/pages"
+)
+
+// SetupRoutes registers the /about-atproto route.
+func SetupRoutes(router chi.Router) {
+ router.Get("/about-atproto", aboutATProto)
+}
+
+func aboutATProto(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ if err := pages.AboutATProto().Render(r.Context(), w); err != nil {
+ slog.Error("render about-atproto", "err", err)
+ }
+}
diff --git a/features/demo/routes.go b/features/demo/routes.go
new file mode 100644
index 0000000..edc7f45
--- /dev/null
+++ b/features/demo/routes.go
@@ -0,0 +1,48 @@
+// Package demo hosts small Datastar SSE sanity-check endpoints. Right now
+// that's just /demo/clock, which streams the current UTC time into the
+// statusbar #clock element so we can visually confirm the
+// Datastar Go SDK + chi + SSE plumbing all work end-to-end.
+//
+// The home page wires it up with:
+//
+//
--:--:--
+//
+// Delete this feature once a real interaction replaces the demo.
+package demo
+
+import (
+ "fmt"
+ "log/slog"
+ "net/http"
+ "time"
+
+ "github.com/go-chi/chi/v5"
+ "github.com/starfederation/datastar-go/datastar"
+)
+
+// SetupRoutes registers the demo SSE endpoints.
+func SetupRoutes(router chi.Router) {
+ router.Get("/demo/clock", clock)
+}
+
+// clock streams the current UTC time into #clock once per second. The
+// loop exits naturally when the client disconnects (r.Context().Done())
+// or when the SSE write fails.
+func clock(w http.ResponseWriter, r *http.Request) {
+ sse := datastar.NewSSE(w, r)
+ ticker := time.NewTicker(time.Second)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-r.Context().Done():
+ return
+ case t := <-ticker.C:
+ html := fmt.Sprintf(`
a person at a conference. you just met someone interesting and now their handle is gone.
init: v0.1 · CascadiaJS 2026
atmo.quest
An event companion ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "cascadiajs:~$whoami
a person at a conference. you just met someone interesting and now their handle is gone.
init: v0.1 · CascadiaJS 2026
atmo.quest
An event companion ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var19 string
- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs("for")
+ var templ_7745c5c3_Var20 string
+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs("for")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/index/pages/index.templ`, Line: 180, Col: 56}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " the open social web. Scan a QR, write a note, leave with a real follow-up list. Your data goes to your repo, not ours.
Side Quests ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " the open social web. Scan a QR, write a note, leave with a real follow-up list. Your data goes to your repo, not ours.