web: mobile Explore, registration key help, empty search !459

merged merged by cmc on 2026-09-22 05:34 UTC · krz/gitbay:web-review-followups into main

8 files changed, +90 −10

Layout: unified · split

CHANGELOG.org +15
@@ -4,6 +4,21 @@ Versioning follows semver from v0.1.0. Database migrations run
44automatically on daemon start; upgrade notes appear per release when
55anything beyond "replace the binary and restart" is needed.
66
7* v1.34.1 — 2026-09-21
8
9Three findings from an external review of the web UI (#248).
10
11- Explore is a rail square at every width. It was =.railopt=, so it
12 dropped below 34rem into the More menu, which exists only for a
13 signed-in viewer: a signed-out phone had no route to the listing from
14 any page. Six squares and the mark are 308px, inside a 320px phone.
15- =/register= answers "no key yet?" in place, with the =ssh-keygen= and
16 =cat= lines behind a disclosure, instead of linking out to the wiki
17 over a half-filled form. The link also hardcoded =gitbay.org=.
18- An empty =/search= says what to do next — fewer words, the same query
19 across every kind, or the listing — rather than repeating the count
20 line's "no matches".
21
722* v1.34.0 — 2026-09-21
823
924The profile's tabs, reworked.
e2e/design_test.go +5 −2
@@ -200,8 +200,11 @@ func TestLandingRoutes(t *testing.T) {
200200 t.Error("landing still calls repo create the whole onboarding")
201201 }
202202 _, reg := inst.get(t, "/register")
203 if !strings.Contains(reg, "Paste the contents of your public key file") || !strings.Contains(reg, "/krz/gitbay/wiki/SSH-keys") {
204 t.Error("register page lacks the key hint or the wiki link")
203 if !strings.Contains(reg, "Paste the contents of your public key file") || !strings.Contains(reg, "ssh-keygen -t ed25519") {
204 t.Error("register page lacks the key hint or the disclosure that makes a key")
205 }
206 if strings.Contains(reg, "gitbay.org/krz/gitbay/wiki") {
207 t.Error("register page links out to this instance's wiki")
205208 }
206209}
207210
e2e/searchweb_test.go +11
@@ -56,6 +56,17 @@ func TestGlobalSearchAndNotificationsWeb(t *testing.T) {
5656 t.Fatal("short query not refused")
5757 }
5858
59 // A query that matches nothing says what to do next instead of
60 // repeating the count line's "no matches" under it (#248).
61 _, body = inst.get(t, "/search?q=zzznothing&kind=issue")
62 if !strings.Contains(body, "0 results") || !strings.Contains(body, "Try fewer words") ||
63 !strings.Contains(body, "search everything") {
64 t.Fatalf("empty search lacks its recovery line:\n%s", body)
65 }
66 if strings.Contains(body, "no matches") {
67 t.Fatalf("empty search repeats the count line:\n%s", body)
68 }
69
5970 // Alice sees her private repository and its issue in the same page.
6071 browser := inst.login(t, aliceKey)
6172 status, body = browserGet(t, browser, inst.base()+"/search?q=widget")
internal/httpd/railanon_test.go added +39
@@ -0,0 +1,39 @@
1package httpd
2
3import (
4 "strings"
5 "testing"
6
7 "gitbay.org/gitbay/internal/web"
8)
9
10// Below 34rem the rail hides every .railopt square and the More menu
11// holds them instead — but that menu is rendered only for a signed-in
12// viewer. A square a signed-out visitor can use therefore cannot be
13// .railopt: it would hide with nothing to hold it. Explore was, so a
14// signed-out phone had no route to the listing from any page (#248).
15func TestSignedOutRailDropsNothing(t *testing.T) {
16 var sb strings.Builder
17 err := web.Render(&sb, "explore.html", struct {
18 basePage
19 Tab string
20 Query string
21 Facets []facetGroup
22 Repos []describedRepo
23 }{basePage{Site: "gitbay"}, "explore", "", nil, nil})
24 if err != nil {
25 t.Fatalf("render: %v", err)
26 }
27 out := sb.String()
28 end := strings.Index(out, "</nav>")
29 if end < 0 {
30 t.Fatalf("no rail in the rendered page:\n%s", out)
31 }
32 rail := out[:end]
33 if strings.Contains(rail, "railopt") {
34 t.Errorf("a signed-out rail marks a square railopt, with no More menu to hold it:\n%s", rail)
35 }
36 if !strings.Contains(rail, `href="/explore"`) {
37 t.Errorf("a signed-out rail carries no Explore square:\n%s", rail)
38 }
39}
internal/web/static/style.css +7 −4
@@ -1861,10 +1861,13 @@ svg.icon { vertical-align: -0.125em; }
18611861}
18621862
18631863/* Nine 44px squares and the mark are 440px, so below 34rem the rail
1864 keeps five — dashboard, search, notifications, More, your avatar — and
1865 the More menu holds the rest. Shrinking the squares instead is what
1866 this replaces: 32px was under the target every phone guideline asks
1867 for, and the count only grows. */
1864 keeps six — dashboard, explore, search, notifications, More, your
1865 avatar — and the More menu holds the rest. Seven squares at 44px are
1866 308px, inside a 320px phone. Explore is one of the six because the
1867 More menu only exists for a signed-in viewer: dropping it left a
1868 signed-out phone with no route to the listing at all. Shrinking the
1869 squares instead is what this replaces: 32px was under the target
1870 every phone guideline asks for, and the count only grows. */
18681871@media (max-width: 34rem) {
18691872 .railopt { display: none; }
18701873 .railmore { display: block; }
internal/web/templates/globalsearch.html +3 −1
@@ -40,7 +40,9 @@
4040</li>
4141{{end}}
4242</ul>
43{{else}}<p class="empty-note">no matches for “{{.Query}}”</p>{{end}}
43{{/* The count line above already says nothing matched, so this one
44 carries the way out instead of repeating it. */}}
45{{else}}<p class="empty-note">Try fewer words{{if .Kind}}, <a href="?q={{.Query}}">search everything</a>,{{end}} or <a href="/explore">browse the repositories</a>.</p>{{end}}
4446{{else}}
4547<p class="empty-note">Repository names, descriptions and topics, and the title and body of every issue and merge request you can read. File contents are searched per repository, from a repository's Code tab.</p>
4648{{end}}
internal/web/templates/layout.html +1 −2
@@ -19,7 +19,7 @@
1919 are kept in step by hand: add a square there and add it here. */}}
2020 <ul class="raillist">
2121 {{if .Viewer}}<li>{{template "raillink" dict "Href" "/" "Icon" "home" "Name" "Dashboard" "Current" (eq (str . "Tab") "dashboard")}}</li>{{end}}
22 <li class="railopt">{{template "raillink" dict "Href" "/explore" "Icon" "compass" "Name" "Explore" "Current" (eq (str . "Tab") "explore")}}</li>
22 <li>{{template "raillink" dict "Href" "/explore" "Icon" "compass" "Name" "Explore" "Current" (eq (str . "Tab") "explore")}}</li>
2323 <li>{{template "raillink" dict "Href" "/search" "Icon" "search" "Name" "Search" "Current" (eq (str . "Tab") "sitesearch")}}</li>
2424 {{if .Viewer}}<li>{{template "raillink" dict "Href" "/notifications" "Icon" "bell" "Name" "Notifications" "Current" (eq (str . "Tab") "notifications") "Count" .Rail.Unread}}</li>
2525 <li class="railopt">{{template "raillink" dict "Href" "/new" "Icon" "plus" "Name" "New repository"}}</li>{{end}}
@@ -33,7 +33,6 @@
3333 {{if .Viewer}}<details class="railmore">
3434 <summary class="railicon" aria-label="More" title="More">{{template "icon" "ellipsis"}}<span class="vh">More</span></summary>
3535 <div class="raildrop">
36 <a href="/explore">{{template "icon" "compass"}} Explore</a>
3736 <a href="/new">{{template "icon" "plus"}} New repository</a>
3837 <a href="/settings">{{template "icon" "gear"}} Settings</a>
3938 {{if .Admin}}<a href="/admin">{{template "icon" "shield"}} Admin</a>{{end}}
internal/web/templates/register.html +9 −1
@@ -10,7 +10,15 @@
1010{{if eq .Mode "invite"}}<div class="field"><label for="invite">Invite code</label><input type="text" id="invite" name="invite" required></div>
1111{{else}}<div class="field"><label for="email">Email</label><input type="text" id="email" name="email" required></div>{{end}}
1212<div class="field"><label for="key">SSH public key</label>
13<p class="hint">Paste the contents of your public key file, usually <code>~/.ssh/id_ed25519.pub</code>. It starts with <code>ssh-ed25519</code> or <code>ssh-rsa</code>. No key yet? <a href="https://gitbay.org/krz/gitbay/wiki/SSH-keys">Make one</a>.</p>
13<p class="hint">Paste the contents of your public key file, usually <code>~/.ssh/id_ed25519.pub</code>. It starts with <code>ssh-ed25519</code> or <code>ssh-rsa</code>.</p>
14{{/* The help is a disclosure rather than a link out: a wiki page is on
15 another origin, opens over the half-filled form, and has no way
16 back to it. */}}
17<details class="editbox"><summary>No key yet?</summary>
18<p class="meta">Run this, take the default path, and paste what the second line prints.</p>
19<pre class="code" tabindex="0">ssh-keygen -t ed25519
20cat ~/.ssh/id_ed25519.pub</pre>
21</details>
1422<textarea id="key" name="key" rows="3" required placeholder="ssh-ed25519 AAAA... you@host"></textarea></div>
1523<p><button type="submit">Create account</button></p>
1624</form>