Commit 794702c58d
794702c58d07759975699235f4408486305f8008
parent: 471902dd14
Verified · cmc
cmc <hello@cleberg.net> · 2026-08-25T15:07:24Z
Fix mobile layout overflows and link mirror targets
- The repo-header mirror line links to the mirror URL.
- Hide the tree mode header on mobile along with its cells; the
leftover th kept a phantom third column that broke row borders.
- Wiki pages with wide code blocks overflowed the viewport on mobile:
column-direction .wikilayout inherited align-items:flex-start, so
.wikipage took its content's intrinsic width. Stretch pins it to the
container; text-size-adjust stops the iOS font inflation the
overflow triggered. #10
e2e/mirror_test.go
+1 −1
| @@ -119,7 +119,7 @@ func TestMirrors(t *testing.T) { |
| 119 | 119 | if status, _ := browserGet(t, browser, inst.base()+loginPath); status != 200 { |
| 120 | 120 | t.Fatalf("login: %d", status) |
| 121 | 121 | } |
| 122 | | if _, body := browserGet(t, browser, inst.base()+"/alice/app"); !strings.Contains(body, "mirrors to") { |
| 122 | if _, body := browserGet(t, browser, inst.base()+"/alice/app"); !strings.Contains(body, "mirrors to <a href=\""+remoteURL) { |
| 123 | 123 | t.Fatalf("admin repo page missing mirror line:\n%s", body) |
| 124 | 124 | } |
| 125 | 125 | if _, body := browserGet(t, newBrowser(t), inst.base()+"/alice/app"); strings.Contains(body, "mirrors to") { |
internal/httpd/web.go
+4 −2
| @@ -222,10 +222,11 @@ type repoPage struct { |
| 222 | 222 | } |
| 223 | 223 | |
| 224 | 224 | // mirrorLine is the admin-only mirror status shown in the repo header. |
| 225 | | // It carries no credentials: URL host/path only, sync time, and error. |
| 225 | // It carries no credentials: the stored URL is credential-free. |
| 226 | 226 | type mirrorLine struct { |
| 227 | 227 | Direction string |
| 228 | | Target string // URL without the scheme |
| 228 | URL string |
| 229 | Target string // URL without the scheme, for display |
| 229 | 230 | Synced string |
| 230 | 231 | Error string |
| 231 | 232 | } |
| @@ -276,6 +277,7 @@ func (s *Server) repoFor(w http.ResponseWriter, r *http.Request, ref string) (re |
| 276 | 277 | for _, m := range ms { |
| 277 | 278 | mirrors = append(mirrors, mirrorLine{ |
| 278 | 279 | Direction: m.Direction, |
| 280 | URL: m.URL, |
| 279 | 281 | Target: strings.TrimPrefix(strings.TrimPrefix(m.URL, "https://"), "http://"), |
| 280 | 282 | Synced: syncedAt(m.LastSync), |
| 281 | 283 | Error: m.LastError, |
internal/web/static/style.css
+6 −3
| @@ -68,7 +68,7 @@ |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | * { box-sizing: border-box; } |
| 71 | | html { font-size: 15px; } |
| 71 | html { font-size: 15px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } |
| 72 | 72 | body { |
| 73 | 73 | margin: 0; |
| 74 | 74 | background: var(--bg); |
| @@ -765,7 +765,10 @@ pre.matchline mark { |
| 765 | 765 | .wikinav li a:hover { color: var(--accent); text-decoration: none; } |
| 766 | 766 | .wikinav li a.active { color: var(--accent); font-weight: 550; } |
| 767 | 767 | @media (max-width: 40rem) { |
| 768 | | .wikilayout { flex-direction: column; } |
| 768 | /* Column direction + the desktop align-items:flex-start lets .wikipage |
| 769 | take its content's intrinsic width, overflowing the viewport (and |
| 770 | triggering iOS font inflation). Stretch pins it to the container. */ |
| 771 | .wikilayout { flex-direction: column; align-items: stretch; } |
| 769 | 772 | .wikinav { width: auto; padding-top: 0; } |
| 770 | 773 | } |
| 771 | 774 | |
| @@ -819,7 +822,7 @@ pre.matchline mark { |
| 819 | 822 | main.container { padding-top: var(--sp-2); } |
| 820 | 823 | .repogrid { grid-template-columns: 1fr; } |
| 821 | 824 | .thread { margin-left: var(--sp-3); } |
| 822 | | table.tree td.mode { display: none; } |
| 825 | table.tree td.mode, table.tree th.mode { display: none; } |
| 823 | 826 | .readme, .code { padding: var(--sp-3); } |
| 824 | 827 | .blamehunk { flex-direction: column; gap: 0; } |
| 825 | 828 | .blameinfo { width: auto; } |
internal/web/templates/layout.html
+1 −1
| @@ -32,7 +32,7 @@ |
| 32 | 32 | <h1 class="repotitle"><a class="owner" href="/{{.Repo.OwnerName}}">{{.Repo.OwnerName}}</a><span class="sep">/</span><a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}">{{.Repo.Name}}</a>{{if eq .Repo.Visibility "private"}} <span class="chip chip-neutral">private</span>{{end}}{{if .Repo.Settings.Archived}} <span class="chip chip-stale">archived</span>{{end}}{{if .Viewer}}<form method="post" action="/{{.Repo.OwnerName}}/{{.Repo.Name}}/pin" class="inline pinform"><button type="submit" class="pinbtn{{if .Pinned}} pinned{{end}}" title="{{if .Pinned}}unpin from dashboard{{else}}pin to dashboard{{end}}">{{if .Pinned}}★ pinned{{else}}☆ pin{{end}}</button></form>{{end}}</h1> |
| 33 | 33 | {{if .Desc}}<p class="desc">{{.Desc}}</p>{{end}} |
| 34 | 34 | {{if .Topics}}<p class="topics">{{range .Topics}}<span class="chip topic">{{.}}</span> {{end}}</p>{{end}} |
| 35 | | {{range .Mirrors}}<p class="meta mirrorline">{{if eq .Direction "push"}}mirrors to{{else}}mirrors from{{end}} {{.Target}}{{if .Error}} · <span class="mirrorerr">sync error: {{.Error}}</span>{{else if .Synced}} · synced {{.Synced}}{{end}}</p> |
| 35 | {{range .Mirrors}}<p class="meta mirrorline">{{if eq .Direction "push"}}mirrors to{{else}}mirrors from{{end}} <a href="{{.URL}}" rel="nofollow">{{.Target}}</a>{{if .Error}} · <span class="mirrorerr">sync error: {{.Error}}</span>{{else if .Synced}} · synced {{.Synced}}{{end}}</p> |
| 36 | 36 | {{end}}<nav class="tabs"> |
| 37 | 37 | <a {{if eq .Tab "files"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}">files</a> |
| 38 | 38 | <a {{if eq .Tab "log"}}class="active" {{end}}href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/log">log</a> |