package web import ( "embed" "html/template" "log/slog" "net/http" "strings" "go.bigb.es/auxilia/scribe" "sourcecraft.dev/bigbes/sr-ht-ecore/chrome" "sourcecraft.dev/bigbes/sr-ht-ecore/pages" ) // tmplFS holds the page templates. sr-ht-ecore's pages.Load discovers them: // layout.html is the chrome every page is executed through, a file whose name // starts with "_" is a partial parsed into every set, and everything else is a // page that must define "content". // // The glob and not the bare directory, because Go's embed excludes names // starting with '_' when it walks a directory and _threads.html is exactly such // a name. // //go:embed templates/*.html var tmplFS embed.FS // staticFS holds the built assets: the hashed stylesheet produced by `make css` // and the logo. It is compiled into the binary, which is why `make css` alone // does not restyle a running daemon — see the package doc. // //go:embed static var staticFS embed.FS // funcMap holds this service's own template helpers. // // It carries only what is ours: pages.Load merges it over chrome.Funcs — the // generic helpers every custom service on this instance was carrying its own // copy of, `shortsha` among them — in that order, so a name may be shadowed // deliberately rather than by accident of map ordering. Nothing shadows one // today, and a helper that diverged from the shared spelling of the same name // would be the drift ecore exists to prevent. var funcMap = template.FuncMap{ // indent renders a tree depth as non-breaking space, so the space view's // hierarchy reads as a hierarchy without a nested-list template recursion. // A negative depth (a level-1 heading, once decremented) indents nothing. "indent": func(depth int) template.HTML { if depth <= 0 { return "" } return template.HTML(strings.Repeat("    ", depth)) }, // dec turns a 1-based heading level into a 0-based indent depth. "dec": func(n int) int { return n - 1 }, } // blockThreadsTmpl is the per-block comment markup, parsed on its own so the // diff renderer — which builds its HTML in Go and cannot reach a page template // through the usual {{template}} call — and the proposal page cannot drift into // two spellings of a thread. It is the same file the page set parses as a // partial, so there is one spelling and not two. // // A missing define is a build-time mistake in this package, so it panics at // init the way template.Must does, rather than yielding a page with the // comments silently absent. var blockThreadsTmpl = func() *template.Template { t := template.Must(template.New("_threads.html"). Funcs(chrome.Funcs()).Funcs(funcMap). ParseFS(tmplFS, "templates/_threads.html")) blocks := t.Lookup("blockthreads") if blocks == nil { panic(`web: templates/_threads.html does not define "blockthreads"`) } return blocks }() // renderError renders the chrome-wrapped error page: this service's view struct // around ecore's shared error body. // // An empty message takes the standard sentence for the status, so a refusal // with nothing of its own to add says what every other service on this instance // says. It never recurses on failure — pages.Render answers the response itself // and hands back only a line for the log. func (s *Server) renderError(w http.ResponseWriter, r *http.Request, status int, message string) { vd := s.view(r, http.StatusText(status)) vd.Data = pages.Error(status, message) if err := s.pages.Render(w, status, pages.ErrorPage, vd); err != nil { slog.ErrorContext(r.Context(), "rendering the error page failed after it was answered", "status", status, "method", r.Method, "path", r.URL.Path, scribe.Err(err)) } }