Commit 0ed36f2a40

0ed36f2a409e00e0c52a4830e7c07517040f59ce

parent: 9854108a6b

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-26 18:55 UTC

Render preload imagesrcset as a whole attribute

html/template treats unknown attributes containing "src" as a single URL
and percent-encoded the spaces and commas of the srcset list.

Ref #2

Layout: unified · split

internal/web/funcs.go +15 −7
@@ -12,13 +12,14 @@ import (
1212)
1313
1414var funcs = template.FuncMap{
15 "albumURL": albumURL,
16 "photoURL": photoURL,
17 "imgURL": imgURL,
18 "srcset": srcset,
19 "ar": aspect,
20 "exif": exifLine,
21 "add": func(a, b int) int { return a + b },
15 "albumURL": albumURL,
16 "photoURL": photoURL,
17 "imgURL": imgURL,
18 "srcset": srcset,
19 "preloadSrcset": preloadSrcset,
20 "ar": aspect,
21 "exif": exifLine,
22 "add": func(a, b int) int { return a + b },
2223}
2324
2425func albumURL(a *library.Album) string {
@@ -50,6 +51,13 @@ func srcset(it *library.Item) string {
5051 return strings.Join(parts, ", ")
5152}
5253
54// preloadSrcset renders the whole imagesrcset attribute: html/template treats
55// any unknown attribute containing "src" as a single URL and would escape the
56// spaces and commas a srcset list needs.
57func preloadSrcset(it *library.Item) template.HTMLAttr {
58 return template.HTMLAttr(`imagesrcset="` + template.HTMLEscapeString(srcset(it)) + `"`)
59}
60
5361func aspect(it *library.Item) string {
5462 if it.Meta.Height == 0 {
5563 return "1"
internal/web/server_test.go +1 −1
@@ -96,7 +96,7 @@ func TestPages(t *testing.T) {
9696 }{
9797 {"/", 200, []string{`href="/tokyo/"`, `data-ar="1.5000"`, "Tokyo", "2025", `href="/info/"`}, nil},
9898 {"/tokyo/", 200, []string{`href="/tokyo/a.jpg/"`, `href="/tokyo/b.jpg/"`, `/img/tokyo/a.jpg/960.jpg?v=`, `480w`, `class="on"`, `alt="a.jpg"`}, []string{`alt=""`}},
99 {"/tokyo/a.jpg/", 200, []string{"a.jpg — 1 / 2", "X-T5 · f/2 · 1/125 · ISO 1600 · 2025-03-14", `rel="next" href="/tokyo/b.jpg/"`, `id="up" href="/tokyo/"`, `alt="a.jpg"`, `rel="preload" as="image" imagesrcset="/img/tokyo/b.jpg/480.jpg`, `imagesizes="100vw"`}, []string{`rel="prev"`, `alt=""`}},
99 {"/tokyo/a.jpg/", 200, []string{"a.jpg — 1 / 2", "X-T5 · f/2 · 1/125 · ISO 1600 · 2025-03-14", `rel="next" href="/tokyo/b.jpg/"`, `id="up" href="/tokyo/"`, `alt="a.jpg"`, `rel="preload" as="image" imagesrcset="/img/tokyo/b.jpg/480.jpg?v=`, ` 480w, /img/tokyo/b.jpg/960.jpg?v=`, `imagesizes="100vw"`}, []string{`rel="prev"`, `alt=""`}},
100100 {"/tokyo/b.jpg/", 200, []string{`rel="prev" href="/tokyo/a.jpg/"`, "2 / 2"}, []string{`rel="next"`}},
101101 {"/info/", 200, []string{"<em>there</em>"}, nil},
102102 {"/nope/", 404, []string{"404"}, nil},
internal/web/templates/photo.html +2 −2
@@ -11,6 +11,6 @@
1111 <span>{{exif .Item}}</span>
1212</footer>
1313<a id="up" href="{{albumURL .Album}}" hidden></a>
14{{with .Prev}}<link rel="preload" as="image" imagesrcset="{{srcset .}}" imagesizes="100vw">{{end}}
15{{with .Next}}<link rel="preload" as="image" imagesrcset="{{srcset .}}" imagesizes="100vw">{{end}}
14{{with .Prev}}<link rel="preload" as="image" {{preloadSrcset .}} imagesizes="100vw">{{end}}
15{{with .Next}}<link rel="preload" as="image" {{preloadSrcset .}} imagesizes="100vw">{{end}}
1616{{end}}