Commit d4d1ff98e9

d4d1ff98e92b3ee021a17e319dcbc553319989dd

parent: 6bbced84d6

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-23 23:16 UTC

landing: the signup-to-release recording

Autoplays muted with controls above the merge request picture. The static image route serves gif, webm and mp4 by extension through ServeContent, which answers Range; Safari will not play video without it.

Ref #253
internal/httpd/fonts_test.go +13 −3
@@ -47,7 +47,7 @@ func TestStylesheetFontsAreServed(t *testing.T) {
4747}
4848
4949// TestLandingImagesAreServed: every file under static/img has a route
50// that answers 200 with an image type and the stylesheet's cache policy.
50// that answers 200 with an image or video type, and a video answers Range.
5151func TestLandingImagesAreServed(t *testing.T) {
5252 s := New(config.Default(), nil)
5353 byPattern := map[string]http.HandlerFunc{}
@@ -72,8 +72,18 @@ func TestLandingImagesAreServed(t *testing.T) {
7272 }
7373 rec := httptest.NewRecorder()
7474 h(rec, httptest.NewRequest("GET", u, nil))
75 if rec.Code != http.StatusOK || !strings.HasPrefix(rec.Header().Get("Content-Type"), "image/") {
76 t.Errorf("%s: %d %s", e.Name(), rec.Code, rec.Header().Get("Content-Type"))
75 ct := rec.Header().Get("Content-Type")
76 if rec.Code != http.StatusOK || !(strings.HasPrefix(ct, "image/") || strings.HasPrefix(ct, "video/")) {
77 t.Errorf("%s: %d %q", e.Name(), rec.Code, ct)
78 }
79 if strings.HasPrefix(ct, "video/") {
80 rec := httptest.NewRecorder()
81 req := httptest.NewRequest("GET", u, nil)
82 req.Header.Set("Range", "bytes=0-1")
83 h(rec, req)
84 if rec.Code != http.StatusPartialContent || rec.Body.Len() != 2 {
85 t.Errorf("%s: Range answered %d with %d bytes", e.Name(), rec.Code, rec.Body.Len())
86 }
7787 }
7888 }
7989}
internal/httpd/web.go +13 −4
@@ -117,16 +117,25 @@ func (s *Server) font(w http.ResponseWriter, r *http.Request) {
117117 w.Write(data)
118118}
119119
120// image serves the embedded landing pictures with the font cache policy.
120var staticTypes = map[string]string{
121 ".png": "image/png",
122 ".gif": "image/gif",
123 ".webm": "video/webm",
124 ".mp4": "video/mp4",
125}
126
127// image serves the embedded landing pictures and recording with the font
128// cache policy. ServeContent answers Range, which Safari needs to play video.
121129func (s *Server) image(w http.ResponseWriter, r *http.Request) {
122 data, err := web.ImageFS.ReadFile("static" + r.URL.Path[len("/static"):])
130 name := "static" + r.URL.Path[len("/static"):]
131 data, err := web.ImageFS.ReadFile(name)
123132 if err != nil {
124133 http.NotFound(w, r)
125134 return
126135 }
127 w.Header().Set("Content-Type", "image/png")
136 w.Header().Set("Content-Type", staticTypes[path.Ext(name)])
128137 w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
129 w.Write(data)
138 http.ServeContent(w, r, name, time.Time{}, bytes.NewReader(data))
130139}
131140
132141// notFound renders the designed 404 page with a 404 status. Falls back to
internal/web/static/img/demo.gif added

Binary file not shown.

internal/web/static/img/demo.mp4 added

Binary file not shown.

internal/web/static/img/demo.webm added

Binary file not shown.

internal/web/static/style.css +1
@@ -1704,6 +1704,7 @@ details.refmenu .refdrop a.allrefs {
17041704pre.quickstart { margin: 0 0 var(--sp-4); }
17051705.shot { border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; margin: var(--sp-5) 0; }
17061706.shot img { display: block; width: 100%; height: auto; max-height: 420px; object-fit: cover; object-position: top; }
1707.shot video { display: block; width: 100%; height: auto; }
17071708.shot figcaption {
17081709 border-top: 1px solid var(--line);
17091710 background: var(--surface);
internal/web/templates/landing.html +6
@@ -7,6 +7,12 @@
77<pre class="quickstart" tabindex="0">ssh git@{{.Host}} help # every command, no client to install
88git clone ssh://git@{{.Host}}/owner/repo.git</pre>
99<div class="routes"><a class="button primary" href="/explore">Explore repositories</a>{{if .Signup}}<a class="button btn" href="/register">Create an account</a>{{end}}</div>
10<figure class="shot"><video autoplay muted loop playsinline controls width="1248" height="688" aria-describedby="demo-caption">
11 <source src="/static/img/demo.webm" type="video/webm">
12 <source src="/static/img/demo.mp4" type="video/mp4">
13</video>
14<figcaption id="demo-caption">Signup to release on gitbay.org from a terminal: register an SSH key, verify the mailed code, create a repository and push, file an issue, open and merge a request that closes it, then tag and publish a release.</figcaption>
15</figure>
1016<figure class="shot"><picture>
1117 <source srcset="/static/img/mr-dark.png" media="(prefers-color-scheme: dark)">
1218 <img src="/static/img/mr-light.png" width="1280" height="900" alt="A merged merge request: the description on the left, and on the right two CI checks reporting success with how long each ran, an approval, and the source and target branches.">
internal/web/web.go +1 −1
@@ -29,7 +29,7 @@ var FaviconSVG []byte
2929//go:embed static/fonts/*.woff2
3030var FontFS embed.FS
3131
32//go:embed static/img/*.png
32//go:embed static/img/*.png static/img/*.gif static/img/*.webm static/img/*.mp4
3333var ImageFS embed.FS
3434
3535// version returns the short VCS revision baked into the binary, or "" when