// Package views renders the server-side HTML with gomponents.
// Every function returns a g.Node. Handlers pass the result to Render.
package views
import (
"net/http"
"hearthforge/internal/highlight"
"hearthforge/internal/config"
"hearthforge/internal/db"
"hearthforge/internal/util"
g "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
// Page carries the data the page shell needs.
type Page struct {
Title string
User *db.SessionUser
Cfg *config.Config
}
// Layout renders the full HTML document around the body nodes.
func Layout(p Page, body ...g.Node) g.Node {
pageTitle := "Hearthforge"
if p.Title != "" {
pageTitle = p.Title + " — Hearthforge"
}
owner := ""
if p.Cfg != nil {
owner = p.Cfg.OwnerDisplayName
}
return HTML(Lang("en"),
Head(
Meta(Charset("UTF-8")),
Meta(Name("viewport"), Content("width=device-width, initial-scale=1")),
TitleEl(g.Text(pageTitle)),
Script(Src("/assets/theme.js")),
Link(Rel("icon"), Type("image/svg+xml"), Href("/assets/favicon.svg")),
Link(Rel("stylesheet"), Href("/assets/css/main.css")),
Link(Rel("stylesheet"), Href("/assets/code-theme.css?v="+highlight.CSSVersion())),
Script(Src("/assets/app.js"), Defer()),
),
Body(
Header(Class("site-header"),
Nav(Class("site-nav"),
A(Href("/"), Class("site-logo"),
Img(Src("/assets/favicon.svg"), Class("logo-icon"), Alt(""), Aria("hidden", "true")),
Span(Class("logo-text"), g.Text("Hearthforge")),
),
Div(Class("nav-links"),
g.Iff(p.User != nil, func() g.Node {
return g.Group{
A(Href("/settings"), Class("nav-user"),
Avatar(&p.User.ID, &p.User.AvatarVersion, 24),
Span(g.Text(util.DisplayName(p.User.Username, owner))),
),
Form(Method("POST"), Action("/logout"), Class("inline-form"),
Button(Type("submit"), Class("btn btn-sm"), g.Text("Sign out")),
),
}
}),
g.If(p.User == nil, g.Group{
A(Href("/login"), Class("btn btn-sm"), g.Text("Sign in")),
A(Href("/register"), Class("btn btn-sm btn-primary"), g.Text("Register")),
}),
),
),
),
Main(Class("site-main"), g.Group(body)),
Footer(Class("site-footer"),
P(g.Text("Hearthforge — hosted by "+owner)),
),
),
)
}
// Render writes a full HTML document to the response.
func Render(w http.ResponseWriter, status int, node g.Node) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
w.WriteHeader(status)
_ = Doctype(node).Render(w)
}