package static import ( "bytes" "fmt" "io" "io/fs" "mime" "net/http" "path/filepath" "git.gabrielgio.me/cerrado/pkg/ext" "git.gabrielgio.me/cerrado/static" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/styles" ) func ServeStaticHandler() (ext.ErrorRequestHandler, error) { staticFs, err := fs.Sub(static.Static, ".") if err != nil { return nil, err } return func(w http.ResponseWriter, r *ext.Request) error { var ( f = r.PathValue("file") e = filepath.Ext(f) m = mime.TypeByExtension(e) ) ext.SetMIME(w, m) w.Header().Add("Cache-Control", "max-age=31536000") http.ServeFileFS(w, r.Request, staticFs, f) return nil }, nil } func ServeStaticCSSHandler(lightTheme, darkTheme string) (ext.ErrorRequestHandler, error) { var ( lightStyle = styles.Get(lightTheme) darkStyle = styles.Get(darkTheme) formatter = html.New( html.WithCSSComments(false), ) ) return func(w http.ResponseWriter, r *ext.Request) error { ext.SetMIME(w, "text/css") w.Header().Add("Cache-Control", "max-age=31536000") // use buffer so this function can fail before writing to http.ResponseWriter var buffer bytes.Buffer var style *chroma.Style style = darkStyle buffer.Write([]byte("[data-bs-theme=\"dark\"] {\n")) err := formatter.WriteCSS(&ws{&buffer}, style) if err != nil { return err } buffer.Write([]byte("}\n")) style = lightStyle buffer.Write([]byte("[data-bs-theme=\"light\"] {\n")) err = formatter.WriteCSS(&ws{&buffer}, style) if err != nil { return err } buffer.Write([]byte("}")) _, err = io.Copy(w, &buffer) if err != nil { return err } return nil }, nil } type ws struct { inner io.Writer } // This is very cursed, and rely on the fact that it writes every css rule at time. // it adds & to the begging so it can be nested by the ServeStaticCSSHandler. // This will allow the follow bootstrap data-bs-theme. func (w *ws) Write(p []byte) (n int, err error) { return fmt.Fprintf(w.inner, "& %s", string(p)) }