pages.templ RawHistoryFolder
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5// Home is the repository list.
6templ Home(l Layout, p HomePage) {
7 @Shell(l, nil) {
8 <div class="filter">
9 <label for="home-filter" class="sr">filter</label>
10 <input
11 id="home-filter"
12 type="search"
13 data-filter
14 placeholder="Filter repositories…"
15 autocomplete="off"
16 hx-on:input="let t = this.value.toLowerCase(); q('#repo-rows > tr').arr().forEach(r => r.hidden = !r.textContent.toLowerCase().includes(t)); take('data-cursor', '#repo-rows > tr:not([hidden])')"
17 />
18 </div>
19 <table class="list home" aria-label="Repositories">
20 <thead>
21 <tr>
22 <th class="cur"></th>
23 for _, c := range p.Cols {
24 <th class={ c.Class } aria-sort={ c.Dir }>
25 <a href={ templ.SafeURL(c.Href) }>
26 { c.Label }
27 if c.Mark != "" {
28 <span aria-hidden="true">{ c.Mark }</span>
29 }
30 </a>
31 </th>
32 }
33 </tr>
34 </thead>
35 <tbody id="repo-rows" data-rows>
36 for _, r := range p.Rows {
37 @HomeRowView(r)
38 }
39 </tbody>
40 </table>
41 if len(p.Rows) == 0 {
42 <p class="lede dim">no repositories yet.</p>
43 }
44 }
45}
46
47// Summary is the summary of a repository: the README reads in the main
48// column at the reading measure; what one acts on sits in the rail.
49templ Summary(l Layout, p SummaryPage) {
50 @Shell(l, summaryRail(p)) {
51 if p.Readme != nil {
52 @ReadmeCard(*p.Readme)
53 }
54 }
55}
56
57templ summaryRail(p SummaryPage) {
58 <section aria-labelledby="r-clone"><h2 id="r-clone">Clone</h2>@CloneBlock(p.Clone)
59</section>
60 <section aria-labelledby="r-facts"><h2 id="r-facts">About</h2>@Facts(p.Facts)
61</section>
62 if p.Latest != nil {
63 <section aria-labelledby="r-tag">
64 <h2 id="r-tag">Tags <span class="count">{ itoa(p.Latest.Count) }</span> <a href={ templ.SafeURL(p.Latest.RefsHref) }>View all</a></h2>
65 if p.Latest.Name != "" {
66 <div class="latest-tag">
67 <a class="tag-name" href={ templ.SafeURL(p.Latest.Href) }><span class="ico i-tag" aria-hidden="true"></span>{ p.Latest.Name }</a>
68 <span class="pill">latest</span>
69 <time class="dim" datetime={ p.Latest.WhenAt } title={ p.Latest.WhenAt }>{ p.Latest.When }</time>
70 if p.Latest.Subject != "" {
71 <p class="tag-sub dim">{ p.Latest.Subject }</p>
72 }
73 </div>
74 } else {
75 <p class="dim">{ plural(p.Latest.Count, "tag", "tags") }</p>
76 }
77 </section>
78 }
79 if len(p.Recent) > 0 {
80 <section aria-labelledby="r-recent"><h2 id="r-recent">Recent commits <a href={ templ.SafeURL(p.AllHref) }>View all</a></h2>@CommitRows(p.Recent)
81</section>
82 }
83 if len(p.Outline) > 0 && p.Readme != nil {
84 <section aria-labelledby="r-outline"><h2 id="r-outline">On this page</h2>@Outline(p.Outline)
85</section>
86 }
87}
88
89// Refs lists the branches and tags of a repository.
90templ Refs(l Layout, p RefsPage) {
91 @Shell(l, refsRail(p)) {
92 <div class="filter">
93 <label for="ref-filter" class="sr">filter</label>
94 <input
95 id="ref-filter"
96 type="search"
97 data-filter
98 placeholder="Filter branches and tags…"
99 autocomplete="off"
100 hx-on:input="let t = this.value.toLowerCase(); q('#ref-rows > tr').arr().forEach(r => r.hidden = !r.textContent.toLowerCase().includes(t));
101 let first = q('#ref-rows > tr:not([hidden])').arr()[0]; first && q(first).take('data-cursor', '#ref-rows > tr')"
102 />
103 </div>
104 <table class="list refs" aria-label="Branches and tags">
105 <thead>
106 <tr><th class="cur"></th><th class="c-kind">kind</th><th class="c-name">name</th><th class="c-sha">commit</th><th class="c-sub">subject</th><th class="c-when">date</th></tr>
107 </thead>
108 @RefsBody(p)
109 </table>
110 if p.Note != "" {
111 <p class="note">{ p.Note }</p>
112 }
113 }
114}
115
116templ refRow(kind string, r RefRow) {
117 <tr data-cursor?={ r.Cursor }>
118 <td class="cur" aria-hidden="true"></td>
119 <td class="c-kind mute">{ kind }</td>
120 <td class="c-name">
121 <a href={ templ.SafeURL(r.Href) }>{ r.Name }</a>
122 if r.Default {
123 <span class="pill">default</span>
124 }
125 </td>
126 <td class="c-sha">
127 if r.CHref != "" {
128 <a href={ templ.SafeURL(r.CHref) }>{ r.Short }</a>
129 } else {
130 { r.Short }
131 }
132 </td>
133 <td class="c-sub">{ r.Subject }</td>
134 <td class="c-when dim">
135 if r.WhenAt != "" {
136 <time datetime={ r.WhenAt } title={ r.WhenAt }>{ r.When }</time>
137 }
138 </td>
139 </tr>
140}
141
142templ refsRail(p RefsPage) {
143 <section aria-labelledby="r-refs">
144 <h2 id="r-refs">Overview</h2>
145 @RefsKV(p)
146 </section>
147}
148
149// State is a 404, 500 or empty-repository page inside the normal shell.
150templ State(l Layout, p StatePage) {
151 @Shell(l, nil) {
152 <section class="state" aria-labelledby="state-title">
153 <p class="eyebrow">{ p.Code }</p>
154 <h1 id="state-title">{ p.Title }</h1>
155 for _, line := range p.Lines {
156 <p class="dim">{ line }</p>
157 }
158 if p.Clone != nil {
159 @CloneBlock(*p.Clone)
160 }
161 <p class="state-back">
162 for _, b := range p.Back {
163 <a href={ templ.SafeURL(b.Href) }>{ b.Label }</a>
164 }
165 </p>
166 </section>
167 }
168}
169
170// Login asks the owner to confirm a login link. Opening the link consumes
171// nothing; only this form does. It is a state page with a button.
172templ Login(l Layout, p LoginPage) {
173 @Shell(l, nil) {
174 <section class="state" aria-labelledby="state-title">
175 <h1 id="state-title">{ p.Title }</h1>
176 for _, line := range p.Lines {
177 <p class="dim">{ line }</p>
178 }
179 <form class="state-act" method="post" action={ templ.SafeURL(p.Action) }>
180 <button type="submit" class="btn primary">{ p.Button }</button>
181 </form>
182 <p class="state-back"><a href="/">Back to home</a></p>
183 </section>
184 }
185}