package templates import ( "embed" "html/template" "io" "io/fs" "net/http" "time" ) //go:embed all:templates var templateFS embed.FS //go:embed all:static var staticFS embed.FS // Renderer renders HTML templates with shared layout and functions. type Renderer struct { templates *template.Template } // New creates a template renderer. func New() (*Renderer, error) { funcs := template.FuncMap{ "formatDate": formatDate, "draftBadge": draftBadgeClass, } tmpl, err := template.New("").Funcs(funcs).ParseFS(templateFS, "templates/*.html") if err != nil { return nil, err } return &Renderer{templates: tmpl}, nil } // Render executes a named template with data. func (r *Renderer) Render(w io.Writer, name string, data any) error { return r.templates.ExecuteTemplate(w, name, data) } // StaticHandler serves embedded static assets. func StaticHandler() http.Handler { sub, err := fs.Sub(staticFS, "static") if err != nil { panic(err) } return http.FileServer(http.FS(sub)) } func formatDate(t any) string { switch v := t.(type) { case time.Time: if v.IsZero() { return "—" } return v.Format("Jan 2, 2006") case string: return v default: return "—" } } func draftBadgeClass(draft bool) string { if draft { return "badge-draft" } return "badge-published" }