Fix the logo's pop-in with a traced vector logo

The logo was an 880 KB SVG wrapping a 1254px PNG, downloaded again on
every page load. Keep that PNG as the master (artwork/logo.png) and have
scripts/logo.py trace it with potrace into a 6 KB logo.svg (favicon,
README) and logo.js, which brand.js draws inline in the header and on the
login page so the logo arrives with the page. Remove the unused
logo-white.svg.

Static files now carry content ETags, so reloads get 304s instead of
downloading the UI again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-08 18:01:22 +00:00
1 parent cf49df0346
commit 0f04dbb4b1
15 files changed
+215 -23049

No files matched your search

+31 -1
View File
@@ -1,10 +1,14 @@
package server
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"io/fs"
"net/http"
"strconv"
"strings"
"time"
"jarl/internal/api"
"jarl/internal/auth"
@@ -120,6 +124,7 @@ func (s *Server) routes() {
// Web UI
if s.web != nil {
files := http.FileServer(http.FS(s.web))
etags := contentETags(s.web)
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.Header().Set("Allow", "GET, HEAD")
@@ -134,11 +139,17 @@ func (s *Server) routes() {
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
w.Write(b)
w.Header().Set("ETag", etags["index.html"])
http.ServeContent(w, r, "index.html", time.Time{}, bytes.NewReader(b))
return
}
if strings.HasPrefix(r.URL.Path, "/assets/") {
// no-cache: the browser asks again each time, but with
// the ETag an unchanged file costs a 304 and no body.
w.Header().Set("Cache-Control", "no-cache")
if tag, ok := etags[strings.TrimPrefix(r.URL.Path, "/")]; ok {
w.Header().Set("ETag", tag)
}
files.ServeHTTP(w, r)
return
}
@@ -233,3 +244,22 @@ func require(p *auth.Principal, a auth.Action) error {
}
return nil
}
// contentETags returns an ETag for every file of the embedded web UI,
// from its content (embedded files have no modification time).
func contentETags(fsys fs.FS) map[string]string {
tags := map[string]string{}
fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
b, err := fs.ReadFile(fsys, path)
if err != nil {
return nil
}
sum := sha256.Sum256(b)
tags[path] = `"` + hex.EncodeToString(sum[:12]) + `"`
return nil
})
return tags
}