detached 221e8de

fix(web): put the repo description above content and rail

commit
221e8de8c404161819df1de5ae6748dd7188af98
author
brice · · 2026-10-11 12:28 UTC
parent
c8e4042
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
9 files · +294 -292
M cmd/ermite/testdata/web/summary.html +1 -1
@@ -75,8 +75,8 @@
7575 </nav>
7676 </header>
7777 <div class="work has-rail">
78+<p class="lede work-lede">A demo repository</p>
7879 <main id="main" tabindex="-1">
79-<p class="lede">A demo repository</p>
8080 <section class="card readme" aria-labelledby="readme-h">
8181 <h2 class="card-h" id="readme-h">
8282 <span class="ico i-file" aria-hidden="true">
@@ -75,8 +75,8 @@
75 </nav>75 </nav>
76 </header>76 </header>
77 <div class="work has-rail">77 <div class="work has-rail">
78+<p class="lede work-lede">A demo repository</p>
78 <main id="main" tabindex="-1">79 <main id="main" tabindex="-1">
79-<p class="lede">A demo repository</p>
80 <section class="card readme" aria-labelledby="readme-h">80 <section class="card readme" aria-labelledby="readme-h">
81 <h2 class="card-h" id="readme-h">81 <h2 class="card-h" id="readme-h">
82 <span class="ico i-file" aria-hidden="true">82 <span class="ico i-file" aria-hidden="true">
M internal/adapters/web/assets.go +1 -1
@@ -4,9 +4,9 @@ package web
44
55 import (
66 "crypto/sha256"
7- "errors"
87 "embed"
98 "encoding/hex"
9+ "errors"
1010 "io/fs"
1111 "net/http"
1212 "path"
@@ -4,9 +4,9 @@ package web
4 4
5 import (5 import (
6 "crypto/sha256"6 "crypto/sha256"
7- "errors"
8 "embed"7 "embed"
9 "encoding/hex"8 "encoding/hex"
9+ "errors"
10 "io/fs"10 "io/fs"
11 "net/http"11 "net/http"
12 "path"12 "path"
M internal/adapters/web/pages.templ +0 -3
@@ -48,9 +48,6 @@ templ Home(l Layout, p HomePage) {
4848 // column at the reading measure; what one acts on sits in the rail.
4949 templ Summary(l Layout, p SummaryPage) {
5050 @Shell(l, summaryRail(p)) {
51- if p.Desc != "" {
52- <p class="lede">{ p.Desc }</p>
53- }
5451 if p.Readme != nil {
5552 @ReadmeCard(*p.Readme)
5653 }
@@ -48,9 +48,6 @@ templ Home(l Layout, p HomePage) {
48 // column at the reading measure; what one acts on sits in the rail.48 // column at the reading measure; what one acts on sits in the rail.
49 templ Summary(l Layout, p SummaryPage) {49 templ Summary(l Layout, p SummaryPage) {
50 @Shell(l, summaryRail(p)) {50 @Shell(l, summaryRail(p)) {
51- if p.Desc != "" {
52- <p class="lede">{ p.Desc }</p>
53- }
54 if p.Readme != nil {51 if p.Readme != nil {
55 @ReadmeCard(*p.Readme)52 @ReadmeCard(*p.Readme)
56 }53 }
M internal/adapters/web/pages_templ.go +157 -180 generated file: collapsed
M internal/adapters/web/shell.templ +3 -0
@@ -25,6 +25,9 @@ templ Shell(l Layout, rail templ.Component) {
2525 <a class="skip" href="#main">skip to content</a>
2626 @Header(l)
2727 <div class={ "work", templ.KV("has-rail", l.HasRail && rail != nil) }>
28+ if l.Lede != "" {
29+ <p class="lede work-lede">{ l.Lede }</p>
30+ }
2831 <main id="main" tabindex="-1">
2932 { children... }
3033 </main>
@@ -25,6 +25,9 @@ templ Shell(l Layout, rail templ.Component) {
25 <a class="skip" href="#main">skip to content</a>25 <a class="skip" href="#main">skip to content</a>
26 @Header(l)26 @Header(l)
27 <div class={ "work", templ.KV("has-rail", l.HasRail && rail != nil) }>27 <div class={ "work", templ.KV("has-rail", l.HasRail && rail != nil) }>
28+ if l.Lede != "" {
29+ <p class="lede work-lede">{ l.Lede }</p>
30+ }
28 <main id="main" tabindex="-1">31 <main id="main" tabindex="-1">
29 { children... }32 { children... }
30 </main>33 </main>
M internal/adapters/web/shell_templ.go +128 -105 generated file: collapsed
M internal/adapters/web/static/css/20-shell.css +1 -0
@@ -108,6 +108,7 @@
108108 }
109109 .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); }
110110 .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
111+ .work-lede { grid-column: 1 / -1; margin-bottom: calc(-1 * var(--s2)); }
111112 main > * + * { margin-top: var(--s4); }
112113
113114 /* blocks: a surface with a header; rail sections are blocks too */
@@ -108,6 +108,7 @@
108 }108 }
109 .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); }109 .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); }
110 .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }110 .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
111+ .work-lede { grid-column: 1 / -1; margin-bottom: calc(-1 * var(--s2)); }
111 main > * + * { margin-top: var(--s4); }112 main > * + * { margin-top: var(--s4); }
112 113
113 /* blocks: a surface with a header; rail sections are blocks too */114 /* blocks: a surface with a header; rail sections are blocks too */
M internal/adapters/web/summary.go +1 -1
@@ -44,7 +44,6 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
4444 now := s.cfg.Now()
4545
4646 p := SummaryPage{
47- Desc: d.repo.GetDescription(),
4847 AllHref: repoHref(d.name(), "commits", ref),
4948 Clone: Clone{ID: "clone-" + d.name(), Lines: []string{"git clone " + s.cloneURL(d.name())}},
5049 }
@@ -74,6 +73,7 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
7473 l.Title = d.name() + " · " + s.instance
7574 l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=summary")
7675 l.HasRail = true
76+ l.Lede = d.repo.GetDescription()
7777 return Summary(l, p), http.StatusOK, nil
7878 }
7979
@@ -44,7 +44,6 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
44 now := s.cfg.Now()44 now := s.cfg.Now()
45 45
46 p := SummaryPage{46 p := SummaryPage{
47- Desc: d.repo.GetDescription(),
48 AllHref: repoHref(d.name(), "commits", ref),47 AllHref: repoHref(d.name(), "commits", ref),
49 Clone: Clone{ID: "clone-" + d.name(), Lines: []string{"git clone " + s.cloneURL(d.name())}},48 Clone: Clone{ID: "clone-" + d.name(), Lines: []string{"git clone " + s.cloneURL(d.name())}},
50 }49 }
@@ -74,6 +73,7 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
74 l.Title = d.name() + " · " + s.instance73 l.Title = d.name() + " · " + s.instance
75 l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=summary")74 l.Live, l.LiveURL = true, liveURL("repo:"+d.name(), "view=summary")
76 l.HasRail = true75 l.HasRail = true
76+ l.Lede = d.repo.GetDescription()
77 return Summary(l, p), http.StatusOK, nil77 return Summary(l, p), http.StatusOK, nil
78 }78 }
79 79
M internal/adapters/web/view.go +2 -1
@@ -50,6 +50,8 @@ type Layout struct {
5050 Tags []RefItem
5151 Cmd []CmdItem
5252 HasRail bool
53+ // Lede is a line above the content and the rail, so both start level.
54+ Lede string
5355 // Live connects the page to the event stream (/-/events).
5456 Live bool
5557 // LiveURL is the stream the page follows, with its query.
@@ -133,7 +135,6 @@ type CommitRow struct {
133135
134136 // SummaryPage is the repository summary.
135137 type SummaryPage struct {
136- Desc string
137138 Facts []Fact
138139 Clone Clone
139140 Recent []CommitRow
@@ -50,6 +50,8 @@ type Layout struct {
50 Tags []RefItem50 Tags []RefItem
51 Cmd []CmdItem51 Cmd []CmdItem
52 HasRail bool52 HasRail bool
53+ // Lede is a line above the content and the rail, so both start level.
54+ Lede string
53 // Live connects the page to the event stream (/-/events).55 // Live connects the page to the event stream (/-/events).
54 Live bool56 Live bool
55 // LiveURL is the stream the page follows, with its query.57 // LiveURL is the stream the page follows, with its query.
@@ -133,7 +135,6 @@ type CommitRow struct {
133 135
134 // SummaryPage is the repository summary.136 // SummaryPage is the repository summary.
135 type SummaryPage struct {137 type SummaryPage struct {
136- Desc string
137 Facts []Fact138 Facts []Fact
138 Clone Clone139 Clone Clone
139 Recent []CommitRow140 Recent []CommitRow

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close