detached 838fc2f

Merge 302b124894f5046544f45426778f17d06092d41c into 993fa25853254a30231614da8a874281357ce9a0

commit
838fc2f2e0b677ca8f279391fa9737454ceb82c5
author
ermite · · 2026-10-22 00:49 UTC
merge of
993fa25 + 302b124 · diff against the first parent
signature
unsigned
50 files · +897 -368
M docs/issues.md +3 -1
@@ -50,7 +50,9 @@ Limits: the 100 most recent commits of the merge request, 16 KiB of each text, 1
5050
5151 The signed-in owner has an Inbox at `/-/inbox`, reached from the **Inbox** button at the top left of the home page, which shows the number of open items. It lists the open merge requests and the open issues of every repository, most recently updated first. The counters at the top (`N merge requests · M issues`) always count everything; `?repo=<name>` restricts the two lists to one repository, and the repository links under the counters set it. Repositories with issues off contribute no issues.
5252
53-The page updates live: merge request and issue events of any repository refresh the counters and the lists. Anyone who is not the signed-in owner, including a token, gets the same 404 as for an unknown page, and the same answer for the `owner` event stream it follows.
53+The **Running** section, after **Awaiting moderation**, lists queued and in-progress CI runs newest first, with links to each run, its repository, ref, and start time. The `N running` counter counts every repository; the repository filter also applies to this section. **Shift+I** (`I`) opens the inbox from any page for the signed-in owner, unless single-key shortcuts are off; lowercase `i` still opens the issues tab.
54+
55+The page updates live: run status changes, merge request and issue events of any repository refresh the counters and the lists. Anyone who is not the signed-in owner, including a token, gets the same 404 as for an unknown page, and the same answer for the `owner` event stream it follows.
5456
5557 ## Public form and moderation
5658
@@ -50,7 +50,9 @@ Limits: the 100 most recent commits of the merge request, 16 KiB of each text, 1
50 50
51 The signed-in owner has an Inbox at `/-/inbox`, reached from the **Inbox** button at the top left of the home page, which shows the number of open items. It lists the open merge requests and the open issues of every repository, most recently updated first. The counters at the top (`N merge requests · M issues`) always count everything; `?repo=<name>` restricts the two lists to one repository, and the repository links under the counters set it. Repositories with issues off contribute no issues.51 The signed-in owner has an Inbox at `/-/inbox`, reached from the **Inbox** button at the top left of the home page, which shows the number of open items. It lists the open merge requests and the open issues of every repository, most recently updated first. The counters at the top (`N merge requests · M issues`) always count everything; `?repo=<name>` restricts the two lists to one repository, and the repository links under the counters set it. Repositories with issues off contribute no issues.
52 52
53-The page updates live: merge request and issue events of any repository refresh the counters and the lists. Anyone who is not the signed-in owner, including a token, gets the same 404 as for an unknown page, and the same answer for the `owner` event stream it follows.53+The **Running** section, after **Awaiting moderation**, lists queued and in-progress CI runs newest first, with links to each run, its repository, ref, and start time. The `N running` counter counts every repository; the repository filter also applies to this section. **Shift+I** (`I`) opens the inbox from any page for the signed-in owner, unless single-key shortcuts are off; lowercase `i` still opens the issues tab.
54+
55+The page updates live: run status changes, merge request and issue events of any repository refresh the counters and the lists. Anyone who is not the signed-in owner, including a token, gets the same 404 as for an unknown page, and the same answer for the `owner` event stream it follows.
54 56
55 ## Public form and moderation57 ## Public form and moderation
56 58
M internal/adapters/web/inbox.go +25 -3
@@ -67,8 +67,17 @@ type InboxRepoLink struct {
6767 Current bool
6868 }
6969
70-// InboxPage is the inbox: the counters, the filter and the two lists.
70+// InboxRunRow adds repository and start time to the Actions run display.
71+type InboxRunRow struct {
72+ RunRow
73+ Repo, RepoHref, When, WhenAt string
74+}
75+
76+// InboxPage is the inbox: counters, repository filter and item lists.
7177 type InboxPage struct {
78+ Running int
79+ RunsEnabled bool
80+ Runs []InboxRunRow
7281 // MergeRequests and Issues count every open item, whatever the filter.
7382 MergeRequests, Issues int
7483 // PendingCount counts every issue awaiting moderation.
@@ -106,6 +115,19 @@ func inboxFilter(r *http.Request) (domain.RepoName, error) {
106115 func (s *Server) inboxPage(ov ports.InboxOverview, filter domain.RepoName) InboxPage {
107116 now := s.cfg.Now()
108117 p := InboxPage{MergeRequests: ov.Counts.MergeRequests, Issues: ov.Counts.Issues, PendingCount: ov.Counts.Pending, Filter: filter.String()}
118+ p.Running, p.RunsEnabled = ov.Counts.Running, ov.RunsEnabled
119+ for _, run := range ov.Runs {
120+ r := InboxRunRow{RunRow: s.runRow(run.Repo.String(), run, now), Repo: run.Repo.String(), RepoHref: repoHref(run.Repo.String())}
121+ if run.Status == domain.CIInProgress {
122+ r.State = "in progress"
123+ }
124+ ts := run.StartedAt
125+ if ts.IsZero() {
126+ ts = run.CreatedAt
127+ }
128+ r.When, r.WhenAt = whenOf(now, ts)
129+ p.Runs = append(p.Runs, r)
130+ }
109131 for _, pi := range ov.Pending {
110132 p.Pending = append(p.Pending, s.pendingRow(now, pi, filter))
111133 }
@@ -266,7 +288,7 @@ func (l *liveStream) inbox(ctx context.Context, we ports.WatchEvent) ([]fragment
266288 switch we.Event.(type) {
267289 case domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved,
268290 domain.IssueOpened, domain.IssueClosed, domain.IssueReopened, domain.IssueCommented, domain.IssueSubmitted, domain.IssuePendingRemoved,
269- domain.RepoUpdated, domain.RepoDeleted:
291+ domain.RepoUpdated, domain.RepoDeleted, domain.RunUpdated:
270292 default:
271293 return nil, nil
272294 }
@@ -283,7 +305,7 @@ func (l *liveStream) inbox(ctx context.Context, we ports.WatchEvent) ([]fragment
283305 }
284306 p := l.s.inboxPage(ov, filter)
285307 var out []fragment
286- for _, c := range []templ.Component{InboxCounters(p), InboxRepos(p), InboxPending(p), InboxMRs(p), InboxIssues(p)} {
308+ for _, c := range []templ.Component{InboxCounters(p), InboxRepos(p), InboxPending(p), InboxRuns(p), InboxMRs(p), InboxIssues(p)} {
287309 f, err := renderFragment(ctx, inboxEvent, c)
288310 if err != nil {
289311 return nil, err
@@ -67,8 +67,17 @@ type InboxRepoLink struct {
67 Current bool67 Current bool
68 }68 }
69 69
70-// InboxPage is the inbox: the counters, the filter and the two lists.70+// InboxRunRow adds repository and start time to the Actions run display.
71+type InboxRunRow struct {
72+ RunRow
73+ Repo, RepoHref, When, WhenAt string
74+}
75+
76+// InboxPage is the inbox: counters, repository filter and item lists.
71 type InboxPage struct {77 type InboxPage struct {
78+ Running int
79+ RunsEnabled bool
80+ Runs []InboxRunRow
72 // MergeRequests and Issues count every open item, whatever the filter.81 // MergeRequests and Issues count every open item, whatever the filter.
73 MergeRequests, Issues int82 MergeRequests, Issues int
74 // PendingCount counts every issue awaiting moderation.83 // PendingCount counts every issue awaiting moderation.
@@ -106,6 +115,19 @@ func inboxFilter(r *http.Request) (domain.RepoName, error) {
106 func (s *Server) inboxPage(ov ports.InboxOverview, filter domain.RepoName) InboxPage {115 func (s *Server) inboxPage(ov ports.InboxOverview, filter domain.RepoName) InboxPage {
107 now := s.cfg.Now()116 now := s.cfg.Now()
108 p := InboxPage{MergeRequests: ov.Counts.MergeRequests, Issues: ov.Counts.Issues, PendingCount: ov.Counts.Pending, Filter: filter.String()}117 p := InboxPage{MergeRequests: ov.Counts.MergeRequests, Issues: ov.Counts.Issues, PendingCount: ov.Counts.Pending, Filter: filter.String()}
118+ p.Running, p.RunsEnabled = ov.Counts.Running, ov.RunsEnabled
119+ for _, run := range ov.Runs {
120+ r := InboxRunRow{RunRow: s.runRow(run.Repo.String(), run, now), Repo: run.Repo.String(), RepoHref: repoHref(run.Repo.String())}
121+ if run.Status == domain.CIInProgress {
122+ r.State = "in progress"
123+ }
124+ ts := run.StartedAt
125+ if ts.IsZero() {
126+ ts = run.CreatedAt
127+ }
128+ r.When, r.WhenAt = whenOf(now, ts)
129+ p.Runs = append(p.Runs, r)
130+ }
109 for _, pi := range ov.Pending {131 for _, pi := range ov.Pending {
110 p.Pending = append(p.Pending, s.pendingRow(now, pi, filter))132 p.Pending = append(p.Pending, s.pendingRow(now, pi, filter))
111 }133 }
@@ -266,7 +288,7 @@ func (l *liveStream) inbox(ctx context.Context, we ports.WatchEvent) ([]fragment
266 switch we.Event.(type) {288 switch we.Event.(type) {
267 case domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved,289 case domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed, domain.MergeRequestReady, domain.CommentAdded, domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved,
268 domain.IssueOpened, domain.IssueClosed, domain.IssueReopened, domain.IssueCommented, domain.IssueSubmitted, domain.IssuePendingRemoved,290 domain.IssueOpened, domain.IssueClosed, domain.IssueReopened, domain.IssueCommented, domain.IssueSubmitted, domain.IssuePendingRemoved,
269- domain.RepoUpdated, domain.RepoDeleted:291+ domain.RepoUpdated, domain.RepoDeleted, domain.RunUpdated:
270 default:292 default:
271 return nil, nil293 return nil, nil
272 }294 }
@@ -283,7 +305,7 @@ func (l *liveStream) inbox(ctx context.Context, we ports.WatchEvent) ([]fragment
283 }305 }
284 p := l.s.inboxPage(ov, filter)306 p := l.s.inboxPage(ov, filter)
285 var out []fragment307 var out []fragment
286- for _, c := range []templ.Component{InboxCounters(p), InboxRepos(p), InboxPending(p), InboxMRs(p), InboxIssues(p)} {308+ for _, c := range []templ.Component{InboxCounters(p), InboxRepos(p), InboxPending(p), InboxRuns(p), InboxMRs(p), InboxIssues(p)} {
287 f, err := renderFragment(ctx, inboxEvent, c)309 f, err := renderFragment(ctx, inboxEvent, c)
288 if err != nil {310 if err != nil {
289 return nil, err311 return nil, err
M internal/adapters/web/inbox.templ +34 -1
@@ -12,6 +12,7 @@ templ Inbox(l Layout, p InboxPage) {
1212 @InboxCounters(p)
1313 @InboxRepos(p)
1414 @InboxPending(p)
15+ @InboxRuns(p)
1516 @InboxMRs(p)
1617 @InboxIssues(p)
1718 </div>
@@ -20,7 +21,7 @@ templ Inbox(l Layout, p InboxPage) {
2021
2122 templ InboxCounters(p InboxPage) {
2223 <p id="inbox-counts" class="inbox-counts">
23- <strong>{ itoa(p.MergeRequests) }</strong> merge requests · <strong>{ itoa(p.Issues) }</strong> issues · <strong>{ itoa(p.PendingCount) }</strong> awaiting moderation
24+ <strong>{ itoa(p.MergeRequests) }</strong> merge requests · <strong>{ itoa(p.Issues) }</strong> issues · <strong>{ itoa(p.PendingCount) }</strong> awaiting moderation · <strong>{ itoa(p.Running) }</strong> running
2425 </p>
2526 }
2627
@@ -113,3 +114,35 @@ templ inboxRows(rows []InboxRow, label string) {
113114 </table>
114115 }
115116 }
117+
118+// InboxRuns stays as an empty fragment when CI is disabled.
119+templ InboxRuns(p InboxPage) {
120+ <section id="inbox-runs" hidden?={ !p.RunsEnabled }
121+ if p.RunsEnabled {
122+ aria-labelledby="inbox-runs-h"
123+ }
124+ >
125+ if p.RunsEnabled {
126+ <h2 id="inbox-runs-h" class="rule-h"><span>Running</span><span class="count">{ itoa(len(p.Runs)) }</span></h2>
127+ if len(p.Runs) == 0 {
128+ <p class="lede dim">no runs in progress.</p>
129+ } else {
130+ <table class="list mrs" aria-label="Running">
131+ <tbody data-rows>
132+ for _, r := range p.Runs {
133+ <tr>
134+ <td class="c-st">@CheckIcon(r.Kind, r.State)<span class="dim">{ r.State }</span></td>
135+ <td class="c-subject"><a href={ templ.SafeURL(r.Href) }>{ r.Workflow }{ " " }{ r.Number }</a> <a class="mute" href={ templ.SafeURL(r.RepoHref) }>{ r.Repo }</a> <span class="dim">{ r.Ref }{ " " }{ r.Event }</span></td>
136+ <td class="c-when nowrap dim">
137+ if r.WhenAt != "" {
138+ <time datetime={ r.WhenAt } title={ r.WhenAt }>{ r.When }</time>
139+ }
140+ </td>
141+ </tr>
142+ }
143+ </tbody>
144+ </table>
145+ }
146+ }
147+ </section>
148+}
@@ -12,6 +12,7 @@ templ Inbox(l Layout, p InboxPage) {
12 @InboxCounters(p)12 @InboxCounters(p)
13 @InboxRepos(p)13 @InboxRepos(p)
14 @InboxPending(p)14 @InboxPending(p)
15+ @InboxRuns(p)
15 @InboxMRs(p)16 @InboxMRs(p)
16 @InboxIssues(p)17 @InboxIssues(p)
17 </div>18 </div>
@@ -20,7 +21,7 @@ templ Inbox(l Layout, p InboxPage) {
20 21
21 templ InboxCounters(p InboxPage) {22 templ InboxCounters(p InboxPage) {
22 <p id="inbox-counts" class="inbox-counts">23 <p id="inbox-counts" class="inbox-counts">
23- <strong>{ itoa(p.MergeRequests) }</strong> merge requests · <strong>{ itoa(p.Issues) }</strong> issues · <strong>{ itoa(p.PendingCount) }</strong> awaiting moderation24+ <strong>{ itoa(p.MergeRequests) }</strong> merge requests · <strong>{ itoa(p.Issues) }</strong> issues · <strong>{ itoa(p.PendingCount) }</strong> awaiting moderation · <strong>{ itoa(p.Running) }</strong> running
24 </p>25 </p>
25 }26 }
26 27
@@ -113,3 +114,35 @@ templ inboxRows(rows []InboxRow, label string) {
113 </table>114 </table>
114 }115 }
115 }116 }
117+
118+// InboxRuns stays as an empty fragment when CI is disabled.
119+templ InboxRuns(p InboxPage) {
120+ <section id="inbox-runs" hidden?={ !p.RunsEnabled }
121+ if p.RunsEnabled {
122+ aria-labelledby="inbox-runs-h"
123+ }
124+ >
125+ if p.RunsEnabled {
126+ <h2 id="inbox-runs-h" class="rule-h"><span>Running</span><span class="count">{ itoa(len(p.Runs)) }</span></h2>
127+ if len(p.Runs) == 0 {
128+ <p class="lede dim">no runs in progress.</p>
129+ } else {
130+ <table class="list mrs" aria-label="Running">
131+ <tbody data-rows>
132+ for _, r := range p.Runs {
133+ <tr>
134+ <td class="c-st">@CheckIcon(r.Kind, r.State)<span class="dim">{ r.State }</span></td>
135+ <td class="c-subject"><a href={ templ.SafeURL(r.Href) }>{ r.Workflow }{ " " }{ r.Number }</a> <a class="mute" href={ templ.SafeURL(r.RepoHref) }>{ r.Repo }</a> <span class="dim">{ r.Ref }{ " " }{ r.Event }</span></td>
136+ <td class="c-when nowrap dim">
137+ if r.WhenAt != "" {
138+ <time datetime={ r.WhenAt } title={ r.WhenAt }>{ r.When }</time>
139+ }
140+ </td>
141+ </tr>
142+ }
143+ </tbody>
144+ </table>
145+ }
146+ }
147+ </section>
148+}
M internal/adapters/web/inbox_templ.go +451 -173
@@ -62,6 +62,10 @@ func Inbox(l Layout, p InboxPage) templ.Component {
6262 if templ_7745c5c3_Err != nil {
6363 return templ_7745c5c3_Err
6464 }
65+ templ_7745c5c3_Err = InboxRuns(p).Render(ctx, templ_7745c5c3_Buffer)
66+ if templ_7745c5c3_Err != nil {
67+ return templ_7745c5c3_Err
68+ }
6569 templ_7745c5c3_Err = InboxMRs(p).Render(ctx, templ_7745c5c3_Buffer)
6670 if templ_7745c5c3_Err != nil {
6771 return templ_7745c5c3_Err
@@ -112,7 +116,7 @@ func InboxCounters(p InboxPage) templ.Component {
112116 var templ_7745c5c3_Var4 string
113117 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.MergeRequests))
114118 if templ_7745c5c3_Err != nil {
115- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 33}
119+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 33}
116120 }
117121 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
118122 if templ_7745c5c3_Err != nil {
@@ -125,7 +129,7 @@ func InboxCounters(p InboxPage) templ.Component {
125129 var templ_7745c5c3_Var5 string
126130 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Issues))
127131 if templ_7745c5c3_Err != nil {
128- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 87}
132+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 87}
129133 }
130134 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
131135 if templ_7745c5c3_Err != nil {
@@ -138,13 +142,26 @@ func InboxCounters(p InboxPage) templ.Component {
138142 var templ_7745c5c3_Var6 string
139143 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))
140144 if templ_7745c5c3_Err != nil {
141- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 139}
145+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 139}
142146 }
143147 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
144148 if templ_7745c5c3_Err != nil {
145149 return templ_7745c5c3_Err
146150 }
147- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</strong> awaiting moderation</p>")
151+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</strong> awaiting moderation · <strong>")
152+ if templ_7745c5c3_Err != nil {
153+ return templ_7745c5c3_Err
154+ }
155+ var templ_7745c5c3_Var7 string
156+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Running))
157+ if templ_7745c5c3_Err != nil {
158+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 199}
159+ }
160+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
161+ if templ_7745c5c3_Err != nil {
162+ return templ_7745c5c3_Err
163+ }
164+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</strong> running</p>")
148165 if templ_7745c5c3_Err != nil {
149166 return templ_7745c5c3_Err
150167 }
@@ -168,58 +185,58 @@ func InboxRepos(p InboxPage) templ.Component {
168185 }()
169186 }
170187 ctx = templ.InitializeContext(ctx)
171- templ_7745c5c3_Var7 := templ.GetChildren(ctx)
172- if templ_7745c5c3_Var7 == nil {
173- templ_7745c5c3_Var7 = templ.NopComponent
188+ templ_7745c5c3_Var8 := templ.GetChildren(ctx)
189+ if templ_7745c5c3_Var8 == nil {
190+ templ_7745c5c3_Var8 = templ.NopComponent
174191 }
175192 ctx = templ.ClearChildren(ctx)
176- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<nav id=\"inbox-repos\" class=\"since\" aria-label=\"Repository\">")
193+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<nav id=\"inbox-repos\" class=\"since\" aria-label=\"Repository\">")
177194 if templ_7745c5c3_Err != nil {
178195 return templ_7745c5c3_Err
179196 }
180197 for _, r := range p.Repos {
181- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<a href=\"")
198+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<a href=\"")
182199 if templ_7745c5c3_Err != nil {
183200 return templ_7745c5c3_Err
184201 }
185- var templ_7745c5c3_Var8 templ.SafeURL
186- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
202+ var templ_7745c5c3_Var9 templ.SafeURL
203+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
187204 if templ_7745c5c3_Err != nil {
188- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 31, Col: 32}
205+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 32, Col: 32}
189206 }
190- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
207+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
191208 if templ_7745c5c3_Err != nil {
192209 return templ_7745c5c3_Err
193210 }
194- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"")
211+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"")
195212 if templ_7745c5c3_Err != nil {
196213 return templ_7745c5c3_Err
197214 }
198215 if r.Current {
199- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " aria-current=\"page\"")
216+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " aria-current=\"page\"")
200217 if templ_7745c5c3_Err != nil {
201218 return templ_7745c5c3_Err
202219 }
203220 }
204- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">")
221+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, ">")
205222 if templ_7745c5c3_Err != nil {
206223 return templ_7745c5c3_Err
207224 }
208- var templ_7745c5c3_Var9 string
209- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(r.Label)
225+ var templ_7745c5c3_Var10 string
226+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(r.Label)
210227 if templ_7745c5c3_Err != nil {
211- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 35, Col: 13}
228+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 36, Col: 13}
212229 }
213- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
230+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
214231 if templ_7745c5c3_Err != nil {
215232 return templ_7745c5c3_Err
216233 }
217- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</a>")
234+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</a>")
218235 if templ_7745c5c3_Err != nil {
219236 return templ_7745c5c3_Err
220237 }
221238 }
222- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</nav>")
239+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</nav>")
223240 if templ_7745c5c3_Err != nil {
224241 return templ_7745c5c3_Err
225242 }
@@ -246,219 +263,219 @@ func InboxPending(p InboxPage) templ.Component {
246263 }()
247264 }
248265 ctx = templ.InitializeContext(ctx)
249- templ_7745c5c3_Var10 := templ.GetChildren(ctx)
250- if templ_7745c5c3_Var10 == nil {
251- templ_7745c5c3_Var10 = templ.NopComponent
266+ templ_7745c5c3_Var11 := templ.GetChildren(ctx)
267+ if templ_7745c5c3_Var11 == nil {
268+ templ_7745c5c3_Var11 = templ.NopComponent
252269 }
253270 ctx = templ.ClearChildren(ctx)
254- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<section id=\"inbox-pending\" aria-labelledby=\"inbox-pending-h\"><h2 id=\"inbox-pending-h\" class=\"rule-h\"><span>Awaiting moderation</span><span class=\"count\">")
271+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<section id=\"inbox-pending\" aria-labelledby=\"inbox-pending-h\"><h2 id=\"inbox-pending-h\" class=\"rule-h\"><span>Awaiting moderation</span><span class=\"count\">")
255272 if templ_7745c5c3_Err != nil {
256273 return templ_7745c5c3_Err
257274 }
258- var templ_7745c5c3_Var11 string
259- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))
275+ var templ_7745c5c3_Var12 string
276+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))
260277 if templ_7745c5c3_Err != nil {
261- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 45, Col: 116}
278+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 46, Col: 116}
262279 }
263- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
280+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
264281 if templ_7745c5c3_Err != nil {
265282 return templ_7745c5c3_Err
266283 }
267- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span></h2>")
284+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></h2>")
268285 if templ_7745c5c3_Err != nil {
269286 return templ_7745c5c3_Err
270287 }
271288 if len(p.Pending) > 0 {
272- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<ul class=\"pending-list\">")
289+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<ul class=\"pending-list\">")
273290 if templ_7745c5c3_Err != nil {
274291 return templ_7745c5c3_Err
275292 }
276293 for _, m := range p.Pending {
277- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<li class=\"pending\"><div class=\"p-head\"><a href=\"")
294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<li class=\"pending\"><div class=\"p-head\"><a href=\"")
278295 if templ_7745c5c3_Err != nil {
279296 return templ_7745c5c3_Err
280297 }
281- var templ_7745c5c3_Var12 templ.SafeURL
282- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
298+ var templ_7745c5c3_Var13 templ.SafeURL
299+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
283300 if templ_7745c5c3_Err != nil {
284- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 51, Col: 42}
301+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 42}
285302 }
286- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
303+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
287304 if templ_7745c5c3_Err != nil {
288305 return templ_7745c5c3_Err
289306 }
290- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
307+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">")
291308 if templ_7745c5c3_Err != nil {
292309 return templ_7745c5c3_Err
293310 }
294- var templ_7745c5c3_Var13 string
295- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
311+ var templ_7745c5c3_Var14 string
312+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
296313 if templ_7745c5c3_Err != nil {
297- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 51, Col: 53}
314+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 53}
298315 }
299- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
316+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
300317 if templ_7745c5c3_Err != nil {
301318 return templ_7745c5c3_Err
302319 }
303- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</a> <span>")
320+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</a> <span>")
304321 if templ_7745c5c3_Err != nil {
305322 return templ_7745c5c3_Err
306323 }
307- var templ_7745c5c3_Var14 string
308- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author)
324+ var templ_7745c5c3_Var15 string
325+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author)
309326 if templ_7745c5c3_Err != nil {
310- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 23}
327+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 53, Col: 23}
311328 }
312- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
329+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
313330 if templ_7745c5c3_Err != nil {
314331 return templ_7745c5c3_Err
315332 }
316- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span> ")
333+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span> ")
317334 if templ_7745c5c3_Err != nil {
318335 return templ_7745c5c3_Err
319336 }
320337 if m.WhenAt != "" {
321- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<time datetime=\"")
338+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<time datetime=\"")
322339 if templ_7745c5c3_Err != nil {
323340 return templ_7745c5c3_Err
324341 }
325- var templ_7745c5c3_Var15 string
326- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
342+ var templ_7745c5c3_Var16 string
343+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
327344 if templ_7745c5c3_Err != nil {
328- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 33}
345+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 33}
329346 }
330- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
347+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
331348 if templ_7745c5c3_Err != nil {
332349 return templ_7745c5c3_Err
333350 }
334- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" title=\"")
351+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" title=\"")
335352 if templ_7745c5c3_Err != nil {
336353 return templ_7745c5c3_Err
337354 }
338- var templ_7745c5c3_Var16 string
339- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
355+ var templ_7745c5c3_Var17 string
356+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
340357 if templ_7745c5c3_Err != nil {
341- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 52}
358+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 52}
342359 }
343- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
360+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
344361 if templ_7745c5c3_Err != nil {
345362 return templ_7745c5c3_Err
346363 }
347- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\">")
364+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
348365 if templ_7745c5c3_Err != nil {
349366 return templ_7745c5c3_Err
350367 }
351- var templ_7745c5c3_Var17 string
352- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
368+ var templ_7745c5c3_Var18 string
369+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
353370 if templ_7745c5c3_Err != nil {
354- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 63}
371+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 63}
355372 }
356- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
373+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
357374 if templ_7745c5c3_Err != nil {
358375 return templ_7745c5c3_Err
359376 }
360- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</time>")
377+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</time>")
361378 if templ_7745c5c3_Err != nil {
362379 return templ_7745c5c3_Err
363380 }
364381 }
365- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div><p class=\"p-title\">")
382+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><p class=\"p-title\">")
366383 if templ_7745c5c3_Err != nil {
367384 return templ_7745c5c3_Err
368385 }
369- var templ_7745c5c3_Var18 string
370- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
386+ var templ_7745c5c3_Var19 string
387+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
371388 if templ_7745c5c3_Err != nil {
372- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 57, Col: 34}
389+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 58, Col: 34}
373390 }
374- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
391+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
375392 if templ_7745c5c3_Err != nil {
376393 return templ_7745c5c3_Err
377394 }
378- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</p>")
395+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</p>")
379396 if templ_7745c5c3_Err != nil {
380397 return templ_7745c5c3_Err
381398 }
382399 if m.Preview != "" {
383- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<p class=\"p-body\">")
400+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<p class=\"p-body\">")
384401 if templ_7745c5c3_Err != nil {
385402 return templ_7745c5c3_Err
386403 }
387- var templ_7745c5c3_Var19 string
388- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.Preview)
404+ var templ_7745c5c3_Var20 string
405+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(m.Preview)
389406 if templ_7745c5c3_Err != nil {
390- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 59, Col: 36}
407+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 60, Col: 36}
391408 }
392- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
409+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
393410 if templ_7745c5c3_Err != nil {
394411 return templ_7745c5c3_Err
395412 }
396- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</p>")
413+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</p>")
397414 if templ_7745c5c3_Err != nil {
398415 return templ_7745c5c3_Err
399416 }
400417 }
401418 if m.Approve != "" {
402- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"p-btns\"><form method=\"post\" action=\"")
419+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"p-btns\"><form method=\"post\" action=\"")
403420 if templ_7745c5c3_Err != nil {
404421 return templ_7745c5c3_Err
405422 }
406- var templ_7745c5c3_Var20 templ.SafeURL
407- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Approve))
423+ var templ_7745c5c3_Var21 templ.SafeURL
424+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Approve))
408425 if templ_7745c5c3_Err != nil {
409- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 63, Col: 61}
426+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 64, Col: 61}
410427 }
411- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
428+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
412429 if templ_7745c5c3_Err != nil {
413430 return templ_7745c5c3_Err
414431 }
415- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><button type=\"submit\" class=\"btn primary\">Approve</button></form><form method=\"post\" action=\"")
432+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><button type=\"submit\" class=\"btn primary\">Approve</button></form><form method=\"post\" action=\"")
416433 if templ_7745c5c3_Err != nil {
417434 return templ_7745c5c3_Err
418435 }
419- var templ_7745c5c3_Var21 templ.SafeURL
420- templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Reject))
436+ var templ_7745c5c3_Var22 templ.SafeURL
437+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Reject))
421438 if templ_7745c5c3_Err != nil {
422- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 64, Col: 60}
439+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 65, Col: 60}
423440 }
424- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
441+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
425442 if templ_7745c5c3_Err != nil {
426443 return templ_7745c5c3_Err
427444 }
428- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><button type=\"submit\" class=\"btn\">Reject</button></form><form method=\"post\" action=\"")
445+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><button type=\"submit\" class=\"btn\">Reject</button></form><form method=\"post\" action=\"")
429446 if templ_7745c5c3_Err != nil {
430447 return templ_7745c5c3_Err
431448 }
432- var templ_7745c5c3_Var22 templ.SafeURL
433- templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Ban))
449+ var templ_7745c5c3_Var23 templ.SafeURL
450+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Ban))
434451 if templ_7745c5c3_Err != nil {
435- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 65, Col: 57}
452+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 66, Col: 57}
436453 }
437- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
454+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
438455 if templ_7745c5c3_Err != nil {
439456 return templ_7745c5c3_Err
440457 }
441- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><button type=\"submit\" class=\"btn danger\">Reject and ban</button></form></div>")
458+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"><button type=\"submit\" class=\"btn danger\">Reject and ban</button></form></div>")
442459 if templ_7745c5c3_Err != nil {
443460 return templ_7745c5c3_Err
444461 }
445462 }
446- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</li>")
463+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</li>")
447464 if templ_7745c5c3_Err != nil {
448465 return templ_7745c5c3_Err
449466 }
450467 }
451- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</ul>")
468+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</ul>")
452469 if templ_7745c5c3_Err != nil {
453470 return templ_7745c5c3_Err
454471 }
455472 } else {
456- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<p class=\"lede dim\">nothing awaits moderation.</p>")
473+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<p class=\"lede dim\">nothing awaits moderation.</p>")
457474 if templ_7745c5c3_Err != nil {
458475 return templ_7745c5c3_Err
459476 }
460477 }
461- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")
478+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</section>")
462479 if templ_7745c5c3_Err != nil {
463480 return templ_7745c5c3_Err
464481 }
@@ -482,25 +499,25 @@ func InboxMRs(p InboxPage) templ.Component {
482499 }()
483500 }
484501 ctx = templ.InitializeContext(ctx)
485- templ_7745c5c3_Var23 := templ.GetChildren(ctx)
486- if templ_7745c5c3_Var23 == nil {
487- templ_7745c5c3_Var23 = templ.NopComponent
502+ templ_7745c5c3_Var24 := templ.GetChildren(ctx)
503+ if templ_7745c5c3_Var24 == nil {
504+ templ_7745c5c3_Var24 = templ.NopComponent
488505 }
489506 ctx = templ.ClearChildren(ctx)
490- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<section id=\"inbox-mrs\" aria-labelledby=\"inbox-mrs-h\"><h2 id=\"inbox-mrs-h\" class=\"rule-h\"><span>Open merge requests</span><span class=\"count\">")
507+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<section id=\"inbox-mrs\" aria-labelledby=\"inbox-mrs-h\"><h2 id=\"inbox-mrs-h\" class=\"rule-h\"><span>Open merge requests</span><span class=\"count\">")
491508 if templ_7745c5c3_Err != nil {
492509 return templ_7745c5c3_Err
493510 }
494- var templ_7745c5c3_Var24 string
495- templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.MRs)))
511+ var templ_7745c5c3_Var25 string
512+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.MRs)))
496513 if templ_7745c5c3_Err != nil {
497- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 79, Col: 108}
514+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 80, Col: 108}
498515 }
499- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
516+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
500517 if templ_7745c5c3_Err != nil {
501518 return templ_7745c5c3_Err
502519 }
503- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></h2>")
520+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</span></h2>")
504521 if templ_7745c5c3_Err != nil {
505522 return templ_7745c5c3_Err
506523 }
@@ -509,12 +526,12 @@ func InboxMRs(p InboxPage) templ.Component {
509526 return templ_7745c5c3_Err
510527 }
511528 if len(p.MRs) == 0 {
512- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p class=\"lede dim\">no open merge requests.</p>")
529+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<p class=\"lede dim\">no open merge requests.</p>")
513530 if templ_7745c5c3_Err != nil {
514531 return templ_7745c5c3_Err
515532 }
516533 }
517- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</section>")
534+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</section>")
518535 if templ_7745c5c3_Err != nil {
519536 return templ_7745c5c3_Err
520537 }
@@ -538,25 +555,25 @@ func InboxIssues(p InboxPage) templ.Component {
538555 }()
539556 }
540557 ctx = templ.InitializeContext(ctx)
541- templ_7745c5c3_Var25 := templ.GetChildren(ctx)
542- if templ_7745c5c3_Var25 == nil {
543- templ_7745c5c3_Var25 = templ.NopComponent
558+ templ_7745c5c3_Var26 := templ.GetChildren(ctx)
559+ if templ_7745c5c3_Var26 == nil {
560+ templ_7745c5c3_Var26 = templ.NopComponent
544561 }
545562 ctx = templ.ClearChildren(ctx)
546- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<section id=\"inbox-issues\" aria-labelledby=\"inbox-issues-h\"><h2 id=\"inbox-issues-h\" class=\"rule-h\"><span>Open issues</span><span class=\"count\">")
563+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<section id=\"inbox-issues\" aria-labelledby=\"inbox-issues-h\"><h2 id=\"inbox-issues-h\" class=\"rule-h\"><span>Open issues</span><span class=\"count\">")
547564 if templ_7745c5c3_Err != nil {
548565 return templ_7745c5c3_Err
549566 }
550- var templ_7745c5c3_Var26 string
551- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.IssueRows)))
567+ var templ_7745c5c3_Var27 string
568+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.IssueRows)))
552569 if templ_7745c5c3_Err != nil {
553- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 89, Col: 109}
570+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 90, Col: 109}
554571 }
555- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
572+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
556573 if templ_7745c5c3_Err != nil {
557574 return templ_7745c5c3_Err
558575 }
559- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</span></h2>")
576+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</span></h2>")
560577 if templ_7745c5c3_Err != nil {
561578 return templ_7745c5c3_Err
562579 }
@@ -565,12 +582,12 @@ func InboxIssues(p InboxPage) templ.Component {
565582 return templ_7745c5c3_Err
566583 }
567584 if len(p.IssueRows) == 0 {
568- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<p class=\"lede dim\">no open issues.</p>")
585+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<p class=\"lede dim\">no open issues.</p>")
569586 if templ_7745c5c3_Err != nil {
570587 return templ_7745c5c3_Err
571588 }
572589 }
573- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</section>")
590+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</section>")
574591 if templ_7745c5c3_Err != nil {
575592 return templ_7745c5c3_Err
576593 }
@@ -594,160 +611,160 @@ func inboxRows(rows []InboxRow, label string) templ.Component {
594611 }()
595612 }
596613 ctx = templ.InitializeContext(ctx)
597- templ_7745c5c3_Var27 := templ.GetChildren(ctx)
598- if templ_7745c5c3_Var27 == nil {
599- templ_7745c5c3_Var27 = templ.NopComponent
614+ templ_7745c5c3_Var28 := templ.GetChildren(ctx)
615+ if templ_7745c5c3_Var28 == nil {
616+ templ_7745c5c3_Var28 = templ.NopComponent
600617 }
601618 ctx = templ.ClearChildren(ctx)
602619 if len(rows) > 0 {
603- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<table class=\"list mrs\" aria-label=\"")
620+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<table class=\"list mrs\" aria-label=\"")
604621 if templ_7745c5c3_Err != nil {
605622 return templ_7745c5c3_Err
606623 }
607- var templ_7745c5c3_Var28 string
608- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(label)
624+ var templ_7745c5c3_Var29 string
625+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(label)
609626 if templ_7745c5c3_Err != nil {
610- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 99, Col: 44}
627+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 100, Col: 44}
611628 }
612- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
629+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
613630 if templ_7745c5c3_Err != nil {
614631 return templ_7745c5c3_Err
615632 }
616- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"><tbody data-rows>")
633+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\"><tbody data-rows>")
617634 if templ_7745c5c3_Err != nil {
618635 return templ_7745c5c3_Err
619636 }
620637 for _, m := range rows {
621- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<tr")
638+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<tr")
622639 if templ_7745c5c3_Err != nil {
623640 return templ_7745c5c3_Err
624641 }
625642 if m.Cursor {
626- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, " data-cursor")
643+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, " data-cursor")
627644 if templ_7745c5c3_Err != nil {
628645 return templ_7745c5c3_Err
629646 }
630647 }
631- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, " data-state=\"open\"><td class=\"c-n mute\">")
648+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " data-state=\"open\"><td class=\"c-n mute\">")
632649 if templ_7745c5c3_Err != nil {
633650 return templ_7745c5c3_Err
634651 }
635- var templ_7745c5c3_Var29 string
636- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(m.Number)
652+ var templ_7745c5c3_Var30 string
653+ templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(m.Number)
637654 if templ_7745c5c3_Err != nil {
638- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 103, Col: 37}
655+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 37}
639656 }
640- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
657+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
641658 if templ_7745c5c3_Err != nil {
642659 return templ_7745c5c3_Err
643660 }
644- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</td><td class=\"c-subject\"><a href=\"")
661+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</td><td class=\"c-subject\"><a href=\"")
645662 if templ_7745c5c3_Err != nil {
646663 return templ_7745c5c3_Err
647664 }
648- var templ_7745c5c3_Var30 templ.SafeURL
649- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Href))
665+ var templ_7745c5c3_Var31 templ.SafeURL
666+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Href))
650667 if templ_7745c5c3_Err != nil {
651- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 59}
668+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 59}
652669 }
653- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
670+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
654671 if templ_7745c5c3_Err != nil {
655672 return templ_7745c5c3_Err
656673 }
657- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\">")
674+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
658675 if templ_7745c5c3_Err != nil {
659676 return templ_7745c5c3_Err
660677 }
661- var templ_7745c5c3_Var31 string
662- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
678+ var templ_7745c5c3_Var32 string
679+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
663680 if templ_7745c5c3_Err != nil {
664- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 71}
681+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 71}
665682 }
666- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
683+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
667684 if templ_7745c5c3_Err != nil {
668685 return templ_7745c5c3_Err
669686 }
670- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</a> <a class=\"mute\" href=\"")
687+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</a> <a class=\"mute\" href=\"")
671688 if templ_7745c5c3_Err != nil {
672689 return templ_7745c5c3_Err
673690 }
674- var templ_7745c5c3_Var32 templ.SafeURL
675- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
691+ var templ_7745c5c3_Var33 templ.SafeURL
692+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
676693 if templ_7745c5c3_Err != nil {
677- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 126}
694+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 126}
678695 }
679- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
696+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
680697 if templ_7745c5c3_Err != nil {
681698 return templ_7745c5c3_Err
682699 }
683- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\">")
700+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\">")
684701 if templ_7745c5c3_Err != nil {
685702 return templ_7745c5c3_Err
686703 }
687- var templ_7745c5c3_Var33 string
688- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
704+ var templ_7745c5c3_Var34 string
705+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
689706 if templ_7745c5c3_Err != nil {
690- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 137}
707+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 137}
691708 }
692- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
709+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
693710 if templ_7745c5c3_Err != nil {
694711 return templ_7745c5c3_Err
695712 }
696- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</a></td><td class=\"c-when nowrap dim\">")
713+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</a></td><td class=\"c-when nowrap dim\">")
697714 if templ_7745c5c3_Err != nil {
698715 return templ_7745c5c3_Err
699716 }
700717 if m.WhenAt != "" {
701- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<time datetime=\"")
718+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<time datetime=\"")
702719 if templ_7745c5c3_Err != nil {
703720 return templ_7745c5c3_Err
704721 }
705- var templ_7745c5c3_Var34 string
706- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
722+ var templ_7745c5c3_Var35 string
723+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
707724 if templ_7745c5c3_Err != nil {
708- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 33}
725+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 33}
709726 }
710- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
727+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
711728 if templ_7745c5c3_Err != nil {
712729 return templ_7745c5c3_Err
713730 }
714- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" title=\"")
731+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" title=\"")
715732 if templ_7745c5c3_Err != nil {
716733 return templ_7745c5c3_Err
717734 }
718- var templ_7745c5c3_Var35 string
719- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
735+ var templ_7745c5c3_Var36 string
736+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
720737 if templ_7745c5c3_Err != nil {
721- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 52}
738+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 52}
722739 }
723- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
740+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
724741 if templ_7745c5c3_Err != nil {
725742 return templ_7745c5c3_Err
726743 }
727- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\">")
744+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">")
728745 if templ_7745c5c3_Err != nil {
729746 return templ_7745c5c3_Err
730747 }
731- var templ_7745c5c3_Var36 string
732- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
748+ var templ_7745c5c3_Var37 string
749+ templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
733750 if templ_7745c5c3_Err != nil {
734- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 63}
751+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 63}
735752 }
736- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
753+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
737754 if templ_7745c5c3_Err != nil {
738755 return templ_7745c5c3_Err
739756 }
740- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</time>")
757+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</time>")
741758 if templ_7745c5c3_Err != nil {
742759 return templ_7745c5c3_Err
743760 }
744761 }
745- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</td></tr>")
762+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</td></tr>")
746763 if templ_7745c5c3_Err != nil {
747764 return templ_7745c5c3_Err
748765 }
749766 }
750- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</tbody></table>")
767+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</tbody></table>")
751768 if templ_7745c5c3_Err != nil {
752769 return templ_7745c5c3_Err
753770 }
@@ -756,4 +773,265 @@ func inboxRows(rows []InboxRow, label string) templ.Component {
756773 })
757774 }
758775
776+// InboxRuns stays as an empty fragment when CI is disabled.
777+func InboxRuns(p InboxPage) templ.Component {
778+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
779+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
780+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
781+ return templ_7745c5c3_CtxErr
782+ }
783+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
784+ if !templ_7745c5c3_IsBuffer {
785+ defer func() {
786+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
787+ if templ_7745c5c3_Err == nil {
788+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
789+ }
790+ }()
791+ }
792+ ctx = templ.InitializeContext(ctx)
793+ templ_7745c5c3_Var38 := templ.GetChildren(ctx)
794+ if templ_7745c5c3_Var38 == nil {
795+ templ_7745c5c3_Var38 = templ.NopComponent
796+ }
797+ ctx = templ.ClearChildren(ctx)
798+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<section id=\"inbox-runs\"")
799+ if templ_7745c5c3_Err != nil {
800+ return templ_7745c5c3_Err
801+ }
802+ if !p.RunsEnabled {
803+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " hidden")
804+ if templ_7745c5c3_Err != nil {
805+ return templ_7745c5c3_Err
806+ }
807+ }
808+ if p.RunsEnabled {
809+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, " aria-labelledby=\"inbox-runs-h\"")
810+ if templ_7745c5c3_Err != nil {
811+ return templ_7745c5c3_Err
812+ }
813+ }
814+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, ">")
815+ if templ_7745c5c3_Err != nil {
816+ return templ_7745c5c3_Err
817+ }
818+ if p.RunsEnabled {
819+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<h2 id=\"inbox-runs-h\" class=\"rule-h\"><span>Running</span><span class=\"count\">")
820+ if templ_7745c5c3_Err != nil {
821+ return templ_7745c5c3_Err
822+ }
823+ var templ_7745c5c3_Var39 string
824+ templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Runs)))
825+ if templ_7745c5c3_Err != nil {
826+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 126, Col: 99}
827+ }
828+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
829+ if templ_7745c5c3_Err != nil {
830+ return templ_7745c5c3_Err
831+ }
832+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</span></h2>")
833+ if templ_7745c5c3_Err != nil {
834+ return templ_7745c5c3_Err
835+ }
836+ if len(p.Runs) == 0 {
837+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<p class=\"lede dim\">no runs in progress.</p>")
838+ if templ_7745c5c3_Err != nil {
839+ return templ_7745c5c3_Err
840+ }
841+ } else {
842+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<table class=\"list mrs\" aria-label=\"Running\"><tbody data-rows>")
843+ if templ_7745c5c3_Err != nil {
844+ return templ_7745c5c3_Err
845+ }
846+ for _, r := range p.Runs {
847+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<tr><td class=\"c-st\">")
848+ if templ_7745c5c3_Err != nil {
849+ return templ_7745c5c3_Err
850+ }
851+ templ_7745c5c3_Err = CheckIcon(r.Kind, r.State).Render(ctx, templ_7745c5c3_Buffer)
852+ if templ_7745c5c3_Err != nil {
853+ return templ_7745c5c3_Err
854+ }
855+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<span class=\"dim\">")
856+ if templ_7745c5c3_Err != nil {
857+ return templ_7745c5c3_Err
858+ }
859+ var templ_7745c5c3_Var40 string
860+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(r.State)
861+ if templ_7745c5c3_Err != nil {
862+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 134, Col: 79}
863+ }
864+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
865+ if templ_7745c5c3_Err != nil {
866+ return templ_7745c5c3_Err
867+ }
868+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</span></td><td class=\"c-subject\"><a href=\"")
869+ if templ_7745c5c3_Err != nil {
870+ return templ_7745c5c3_Err
871+ }
872+ var templ_7745c5c3_Var41 templ.SafeURL
873+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
874+ if templ_7745c5c3_Err != nil {
875+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 61}
876+ }
877+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
878+ if templ_7745c5c3_Err != nil {
879+ return templ_7745c5c3_Err
880+ }
881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\">")
882+ if templ_7745c5c3_Err != nil {
883+ return templ_7745c5c3_Err
884+ }
885+ var templ_7745c5c3_Var42 string
886+ templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(r.Workflow)
887+ if templ_7745c5c3_Err != nil {
888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 76}
889+ }
890+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
891+ if templ_7745c5c3_Err != nil {
892+ return templ_7745c5c3_Err
893+ }
894+ var templ_7745c5c3_Var43 string
895+ templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
896+ if templ_7745c5c3_Err != nil {
897+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 83}
898+ }
899+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
900+ if templ_7745c5c3_Err != nil {
901+ return templ_7745c5c3_Err
902+ }
903+ var templ_7745c5c3_Var44 string
904+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(r.Number)
905+ if templ_7745c5c3_Err != nil {
906+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 95}
907+ }
908+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
909+ if templ_7745c5c3_Err != nil {
910+ return templ_7745c5c3_Err
911+ }
912+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</a> <a class=\"mute\" href=\"")
913+ if templ_7745c5c3_Err != nil {
914+ return templ_7745c5c3_Err
915+ }
916+ var templ_7745c5c3_Var45 templ.SafeURL
917+ templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.RepoHref))
918+ if templ_7745c5c3_Err != nil {
919+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 150}
920+ }
921+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
922+ if templ_7745c5c3_Err != nil {
923+ return templ_7745c5c3_Err
924+ }
925+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\">")
926+ if templ_7745c5c3_Err != nil {
927+ return templ_7745c5c3_Err
928+ }
929+ var templ_7745c5c3_Var46 string
930+ templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(r.Repo)
931+ if templ_7745c5c3_Err != nil {
932+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 161}
933+ }
934+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
935+ if templ_7745c5c3_Err != nil {
936+ return templ_7745c5c3_Err
937+ }
938+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</a> <span class=\"dim\">")
939+ if templ_7745c5c3_Err != nil {
940+ return templ_7745c5c3_Err
941+ }
942+ var templ_7745c5c3_Var47 string
943+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(r.Ref)
944+ if templ_7745c5c3_Err != nil {
945+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 193}
946+ }
947+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
948+ if templ_7745c5c3_Err != nil {
949+ return templ_7745c5c3_Err
950+ }
951+ var templ_7745c5c3_Var48 string
952+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
953+ if templ_7745c5c3_Err != nil {
954+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 200}
955+ }
956+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
957+ if templ_7745c5c3_Err != nil {
958+ return templ_7745c5c3_Err
959+ }
960+ var templ_7745c5c3_Var49 string
961+ templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(r.Event)
962+ if templ_7745c5c3_Err != nil {
963+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 211}
964+ }
965+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
966+ if templ_7745c5c3_Err != nil {
967+ return templ_7745c5c3_Err
968+ }
969+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</span></td><td class=\"c-when nowrap dim\">")
970+ if templ_7745c5c3_Err != nil {
971+ return templ_7745c5c3_Err
972+ }
973+ if r.WhenAt != "" {
974+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "<time datetime=\"")
975+ if templ_7745c5c3_Err != nil {
976+ return templ_7745c5c3_Err
977+ }
978+ var templ_7745c5c3_Var50 string
979+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
980+ if templ_7745c5c3_Err != nil {
981+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 35}
982+ }
983+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
984+ if templ_7745c5c3_Err != nil {
985+ return templ_7745c5c3_Err
986+ }
987+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "\" title=\"")
988+ if templ_7745c5c3_Err != nil {
989+ return templ_7745c5c3_Err
990+ }
991+ var templ_7745c5c3_Var51 string
992+ templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
993+ if templ_7745c5c3_Err != nil {
994+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 54}
995+ }
996+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51)
997+ if templ_7745c5c3_Err != nil {
998+ return templ_7745c5c3_Err
999+ }
1000+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\">")
1001+ if templ_7745c5c3_Err != nil {
1002+ return templ_7745c5c3_Err
1003+ }
1004+ var templ_7745c5c3_Var52 string
1005+ templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)
1006+ if templ_7745c5c3_Err != nil {
1007+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 65}
1008+ }
1009+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
1010+ if templ_7745c5c3_Err != nil {
1011+ return templ_7745c5c3_Err
1012+ }
1013+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</time>")
1014+ if templ_7745c5c3_Err != nil {
1015+ return templ_7745c5c3_Err
1016+ }
1017+ }
1018+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</td></tr>")
1019+ if templ_7745c5c3_Err != nil {
1020+ return templ_7745c5c3_Err
1021+ }
1022+ }
1023+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</tbody></table>")
1024+ if templ_7745c5c3_Err != nil {
1025+ return templ_7745c5c3_Err
1026+ }
1027+ }
1028+ }
1029+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</section>")
1030+ if templ_7745c5c3_Err != nil {
1031+ return templ_7745c5c3_Err
1032+ }
1033+ return nil
1034+ })
1035+}
1036+
7591037 var _ = templruntime.GeneratedTemplate
@@ -62,6 +62,10 @@ func Inbox(l Layout, p InboxPage) templ.Component {
62 if templ_7745c5c3_Err != nil {62 if templ_7745c5c3_Err != nil {
63 return templ_7745c5c3_Err63 return templ_7745c5c3_Err
64 }64 }
65+ templ_7745c5c3_Err = InboxRuns(p).Render(ctx, templ_7745c5c3_Buffer)
66+ if templ_7745c5c3_Err != nil {
67+ return templ_7745c5c3_Err
68+ }
65 templ_7745c5c3_Err = InboxMRs(p).Render(ctx, templ_7745c5c3_Buffer)69 templ_7745c5c3_Err = InboxMRs(p).Render(ctx, templ_7745c5c3_Buffer)
66 if templ_7745c5c3_Err != nil {70 if templ_7745c5c3_Err != nil {
67 return templ_7745c5c3_Err71 return templ_7745c5c3_Err
@@ -112,7 +116,7 @@ func InboxCounters(p InboxPage) templ.Component {
112 var templ_7745c5c3_Var4 string116 var templ_7745c5c3_Var4 string
113 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.MergeRequests))117 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.MergeRequests))
114 if templ_7745c5c3_Err != nil {118 if templ_7745c5c3_Err != nil {
115- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 33}119+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 33}
116 }120 }
117 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))121 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
118 if templ_7745c5c3_Err != nil {122 if templ_7745c5c3_Err != nil {
@@ -125,7 +129,7 @@ func InboxCounters(p InboxPage) templ.Component {
125 var templ_7745c5c3_Var5 string129 var templ_7745c5c3_Var5 string
126 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Issues))130 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Issues))
127 if templ_7745c5c3_Err != nil {131 if templ_7745c5c3_Err != nil {
128- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 87}132+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 87}
129 }133 }
130 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))134 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
131 if templ_7745c5c3_Err != nil {135 if templ_7745c5c3_Err != nil {
@@ -138,13 +142,26 @@ func InboxCounters(p InboxPage) templ.Component {
138 var templ_7745c5c3_Var6 string142 var templ_7745c5c3_Var6 string
139 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))143 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))
140 if templ_7745c5c3_Err != nil {144 if templ_7745c5c3_Err != nil {
141- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 23, Col: 139}145+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 139}
142 }146 }
143 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))147 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
144 if templ_7745c5c3_Err != nil {148 if templ_7745c5c3_Err != nil {
145 return templ_7745c5c3_Err149 return templ_7745c5c3_Err
146 }150 }
147- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</strong> awaiting moderation</p>")151+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</strong> awaiting moderation · <strong>")
152+ if templ_7745c5c3_Err != nil {
153+ return templ_7745c5c3_Err
154+ }
155+ var templ_7745c5c3_Var7 string
156+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Running))
157+ if templ_7745c5c3_Err != nil {
158+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 24, Col: 199}
159+ }
160+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
161+ if templ_7745c5c3_Err != nil {
162+ return templ_7745c5c3_Err
163+ }
164+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</strong> running</p>")
148 if templ_7745c5c3_Err != nil {165 if templ_7745c5c3_Err != nil {
149 return templ_7745c5c3_Err166 return templ_7745c5c3_Err
150 }167 }
@@ -168,58 +185,58 @@ func InboxRepos(p InboxPage) templ.Component {
168 }()185 }()
169 }186 }
170 ctx = templ.InitializeContext(ctx)187 ctx = templ.InitializeContext(ctx)
171- templ_7745c5c3_Var7 := templ.GetChildren(ctx)188+ templ_7745c5c3_Var8 := templ.GetChildren(ctx)
172- if templ_7745c5c3_Var7 == nil {189+ if templ_7745c5c3_Var8 == nil {
173- templ_7745c5c3_Var7 = templ.NopComponent190+ templ_7745c5c3_Var8 = templ.NopComponent
174 }191 }
175 ctx = templ.ClearChildren(ctx)192 ctx = templ.ClearChildren(ctx)
176- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<nav id=\"inbox-repos\" class=\"since\" aria-label=\"Repository\">")193+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<nav id=\"inbox-repos\" class=\"since\" aria-label=\"Repository\">")
177 if templ_7745c5c3_Err != nil {194 if templ_7745c5c3_Err != nil {
178 return templ_7745c5c3_Err195 return templ_7745c5c3_Err
179 }196 }
180 for _, r := range p.Repos {197 for _, r := range p.Repos {
181- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<a href=\"")198+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<a href=\"")
182 if templ_7745c5c3_Err != nil {199 if templ_7745c5c3_Err != nil {
183 return templ_7745c5c3_Err200 return templ_7745c5c3_Err
184 }201 }
185- var templ_7745c5c3_Var8 templ.SafeURL202+ var templ_7745c5c3_Var9 templ.SafeURL
186- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))203+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
187 if templ_7745c5c3_Err != nil {204 if templ_7745c5c3_Err != nil {
188- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 31, Col: 32}205+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 32, Col: 32}
189 }206 }
190- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))207+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
191 if templ_7745c5c3_Err != nil {208 if templ_7745c5c3_Err != nil {
192 return templ_7745c5c3_Err209 return templ_7745c5c3_Err
193 }210 }
194- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"")211+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"")
195 if templ_7745c5c3_Err != nil {212 if templ_7745c5c3_Err != nil {
196 return templ_7745c5c3_Err213 return templ_7745c5c3_Err
197 }214 }
198 if r.Current {215 if r.Current {
199- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " aria-current=\"page\"")216+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " aria-current=\"page\"")
200 if templ_7745c5c3_Err != nil {217 if templ_7745c5c3_Err != nil {
201 return templ_7745c5c3_Err218 return templ_7745c5c3_Err
202 }219 }
203 }220 }
204- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">")221+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, ">")
205 if templ_7745c5c3_Err != nil {222 if templ_7745c5c3_Err != nil {
206 return templ_7745c5c3_Err223 return templ_7745c5c3_Err
207 }224 }
208- var templ_7745c5c3_Var9 string225+ var templ_7745c5c3_Var10 string
209- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(r.Label)226+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(r.Label)
210 if templ_7745c5c3_Err != nil {227 if templ_7745c5c3_Err != nil {
211- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 35, Col: 13}228+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 36, Col: 13}
212 }229 }
213- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))230+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
214 if templ_7745c5c3_Err != nil {231 if templ_7745c5c3_Err != nil {
215 return templ_7745c5c3_Err232 return templ_7745c5c3_Err
216 }233 }
217- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</a>")234+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</a>")
218 if templ_7745c5c3_Err != nil {235 if templ_7745c5c3_Err != nil {
219 return templ_7745c5c3_Err236 return templ_7745c5c3_Err
220 }237 }
221 }238 }
222- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</nav>")239+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</nav>")
223 if templ_7745c5c3_Err != nil {240 if templ_7745c5c3_Err != nil {
224 return templ_7745c5c3_Err241 return templ_7745c5c3_Err
225 }242 }
@@ -246,219 +263,219 @@ func InboxPending(p InboxPage) templ.Component {
246 }()263 }()
247 }264 }
248 ctx = templ.InitializeContext(ctx)265 ctx = templ.InitializeContext(ctx)
249- templ_7745c5c3_Var10 := templ.GetChildren(ctx)266+ templ_7745c5c3_Var11 := templ.GetChildren(ctx)
250- if templ_7745c5c3_Var10 == nil {267+ if templ_7745c5c3_Var11 == nil {
251- templ_7745c5c3_Var10 = templ.NopComponent268+ templ_7745c5c3_Var11 = templ.NopComponent
252 }269 }
253 ctx = templ.ClearChildren(ctx)270 ctx = templ.ClearChildren(ctx)
254- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<section id=\"inbox-pending\" aria-labelledby=\"inbox-pending-h\"><h2 id=\"inbox-pending-h\" class=\"rule-h\"><span>Awaiting moderation</span><span class=\"count\">")271+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<section id=\"inbox-pending\" aria-labelledby=\"inbox-pending-h\"><h2 id=\"inbox-pending-h\" class=\"rule-h\"><span>Awaiting moderation</span><span class=\"count\">")
255 if templ_7745c5c3_Err != nil {272 if templ_7745c5c3_Err != nil {
256 return templ_7745c5c3_Err273 return templ_7745c5c3_Err
257 }274 }
258- var templ_7745c5c3_Var11 string275+ var templ_7745c5c3_Var12 string
259- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))276+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.PendingCount))
260 if templ_7745c5c3_Err != nil {277 if templ_7745c5c3_Err != nil {
261- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 45, Col: 116}278+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 46, Col: 116}
262 }279 }
263- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))280+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
264 if templ_7745c5c3_Err != nil {281 if templ_7745c5c3_Err != nil {
265 return templ_7745c5c3_Err282 return templ_7745c5c3_Err
266 }283 }
267- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span></h2>")284+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></h2>")
268 if templ_7745c5c3_Err != nil {285 if templ_7745c5c3_Err != nil {
269 return templ_7745c5c3_Err286 return templ_7745c5c3_Err
270 }287 }
271 if len(p.Pending) > 0 {288 if len(p.Pending) > 0 {
272- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<ul class=\"pending-list\">")289+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<ul class=\"pending-list\">")
273 if templ_7745c5c3_Err != nil {290 if templ_7745c5c3_Err != nil {
274 return templ_7745c5c3_Err291 return templ_7745c5c3_Err
275 }292 }
276 for _, m := range p.Pending {293 for _, m := range p.Pending {
277- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<li class=\"pending\"><div class=\"p-head\"><a href=\"")294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<li class=\"pending\"><div class=\"p-head\"><a href=\"")
278 if templ_7745c5c3_Err != nil {295 if templ_7745c5c3_Err != nil {
279 return templ_7745c5c3_Err296 return templ_7745c5c3_Err
280 }297 }
281- var templ_7745c5c3_Var12 templ.SafeURL298+ var templ_7745c5c3_Var13 templ.SafeURL
282- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))299+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
283 if templ_7745c5c3_Err != nil {300 if templ_7745c5c3_Err != nil {
284- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 51, Col: 42}301+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 42}
285 }302 }
286- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))303+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
287 if templ_7745c5c3_Err != nil {304 if templ_7745c5c3_Err != nil {
288 return templ_7745c5c3_Err305 return templ_7745c5c3_Err
289 }306 }
290- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")307+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">")
291 if templ_7745c5c3_Err != nil {308 if templ_7745c5c3_Err != nil {
292 return templ_7745c5c3_Err309 return templ_7745c5c3_Err
293 }310 }
294- var templ_7745c5c3_Var13 string311+ var templ_7745c5c3_Var14 string
295- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)312+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
296 if templ_7745c5c3_Err != nil {313 if templ_7745c5c3_Err != nil {
297- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 51, Col: 53}314+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 53}
298 }315 }
299- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))316+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
300 if templ_7745c5c3_Err != nil {317 if templ_7745c5c3_Err != nil {
301 return templ_7745c5c3_Err318 return templ_7745c5c3_Err
302 }319 }
303- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</a> <span>")320+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</a> <span>")
304 if templ_7745c5c3_Err != nil {321 if templ_7745c5c3_Err != nil {
305 return templ_7745c5c3_Err322 return templ_7745c5c3_Err
306 }323 }
307- var templ_7745c5c3_Var14 string324+ var templ_7745c5c3_Var15 string
308- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author)325+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(m.Author)
309 if templ_7745c5c3_Err != nil {326 if templ_7745c5c3_Err != nil {
310- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 52, Col: 23}327+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 53, Col: 23}
311 }328 }
312- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))329+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
313 if templ_7745c5c3_Err != nil {330 if templ_7745c5c3_Err != nil {
314 return templ_7745c5c3_Err331 return templ_7745c5c3_Err
315 }332 }
316- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span> ")333+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span> ")
317 if templ_7745c5c3_Err != nil {334 if templ_7745c5c3_Err != nil {
318 return templ_7745c5c3_Err335 return templ_7745c5c3_Err
319 }336 }
320 if m.WhenAt != "" {337 if m.WhenAt != "" {
321- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<time datetime=\"")338+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<time datetime=\"")
322 if templ_7745c5c3_Err != nil {339 if templ_7745c5c3_Err != nil {
323 return templ_7745c5c3_Err340 return templ_7745c5c3_Err
324 }341 }
325- var templ_7745c5c3_Var15 string342+ var templ_7745c5c3_Var16 string
326- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)343+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
327 if templ_7745c5c3_Err != nil {344 if templ_7745c5c3_Err != nil {
328- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 33}345+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 33}
329 }346 }
330- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)347+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
331 if templ_7745c5c3_Err != nil {348 if templ_7745c5c3_Err != nil {
332 return templ_7745c5c3_Err349 return templ_7745c5c3_Err
333 }350 }
334- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" title=\"")351+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" title=\"")
335 if templ_7745c5c3_Err != nil {352 if templ_7745c5c3_Err != nil {
336 return templ_7745c5c3_Err353 return templ_7745c5c3_Err
337 }354 }
338- var templ_7745c5c3_Var16 string355+ var templ_7745c5c3_Var17 string
339- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)356+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
340 if templ_7745c5c3_Err != nil {357 if templ_7745c5c3_Err != nil {
341- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 52}358+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 52}
342 }359 }
343- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)360+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
344 if templ_7745c5c3_Err != nil {361 if templ_7745c5c3_Err != nil {
345 return templ_7745c5c3_Err362 return templ_7745c5c3_Err
346 }363 }
347- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\">")364+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
348 if templ_7745c5c3_Err != nil {365 if templ_7745c5c3_Err != nil {
349 return templ_7745c5c3_Err366 return templ_7745c5c3_Err
350 }367 }
351- var templ_7745c5c3_Var17 string368+ var templ_7745c5c3_Var18 string
352- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)369+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
353 if templ_7745c5c3_Err != nil {370 if templ_7745c5c3_Err != nil {
354- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 54, Col: 63}371+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 55, Col: 63}
355 }372 }
356- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))373+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
357 if templ_7745c5c3_Err != nil {374 if templ_7745c5c3_Err != nil {
358 return templ_7745c5c3_Err375 return templ_7745c5c3_Err
359 }376 }
360- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</time>")377+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</time>")
361 if templ_7745c5c3_Err != nil {378 if templ_7745c5c3_Err != nil {
362 return templ_7745c5c3_Err379 return templ_7745c5c3_Err
363 }380 }
364 }381 }
365- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div><p class=\"p-title\">")382+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><p class=\"p-title\">")
366 if templ_7745c5c3_Err != nil {383 if templ_7745c5c3_Err != nil {
367 return templ_7745c5c3_Err384 return templ_7745c5c3_Err
368 }385 }
369- var templ_7745c5c3_Var18 string386+ var templ_7745c5c3_Var19 string
370- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)387+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
371 if templ_7745c5c3_Err != nil {388 if templ_7745c5c3_Err != nil {
372- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 57, Col: 34}389+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 58, Col: 34}
373 }390 }
374- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))391+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
375 if templ_7745c5c3_Err != nil {392 if templ_7745c5c3_Err != nil {
376 return templ_7745c5c3_Err393 return templ_7745c5c3_Err
377 }394 }
378- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</p>")395+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</p>")
379 if templ_7745c5c3_Err != nil {396 if templ_7745c5c3_Err != nil {
380 return templ_7745c5c3_Err397 return templ_7745c5c3_Err
381 }398 }
382 if m.Preview != "" {399 if m.Preview != "" {
383- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<p class=\"p-body\">")400+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<p class=\"p-body\">")
384 if templ_7745c5c3_Err != nil {401 if templ_7745c5c3_Err != nil {
385 return templ_7745c5c3_Err402 return templ_7745c5c3_Err
386 }403 }
387- var templ_7745c5c3_Var19 string404+ var templ_7745c5c3_Var20 string
388- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(m.Preview)405+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(m.Preview)
389 if templ_7745c5c3_Err != nil {406 if templ_7745c5c3_Err != nil {
390- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 59, Col: 36}407+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 60, Col: 36}
391 }408 }
392- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))409+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
393 if templ_7745c5c3_Err != nil {410 if templ_7745c5c3_Err != nil {
394 return templ_7745c5c3_Err411 return templ_7745c5c3_Err
395 }412 }
396- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</p>")413+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</p>")
397 if templ_7745c5c3_Err != nil {414 if templ_7745c5c3_Err != nil {
398 return templ_7745c5c3_Err415 return templ_7745c5c3_Err
399 }416 }
400 }417 }
401 if m.Approve != "" {418 if m.Approve != "" {
402- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"p-btns\"><form method=\"post\" action=\"")419+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"p-btns\"><form method=\"post\" action=\"")
403 if templ_7745c5c3_Err != nil {420 if templ_7745c5c3_Err != nil {
404 return templ_7745c5c3_Err421 return templ_7745c5c3_Err
405 }422 }
406- var templ_7745c5c3_Var20 templ.SafeURL423+ var templ_7745c5c3_Var21 templ.SafeURL
407- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Approve))424+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Approve))
408 if templ_7745c5c3_Err != nil {425 if templ_7745c5c3_Err != nil {
409- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 63, Col: 61}426+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 64, Col: 61}
410 }427 }
411- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))428+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
412 if templ_7745c5c3_Err != nil {429 if templ_7745c5c3_Err != nil {
413 return templ_7745c5c3_Err430 return templ_7745c5c3_Err
414 }431 }
415- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><button type=\"submit\" class=\"btn primary\">Approve</button></form><form method=\"post\" action=\"")432+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><button type=\"submit\" class=\"btn primary\">Approve</button></form><form method=\"post\" action=\"")
416 if templ_7745c5c3_Err != nil {433 if templ_7745c5c3_Err != nil {
417 return templ_7745c5c3_Err434 return templ_7745c5c3_Err
418 }435 }
419- var templ_7745c5c3_Var21 templ.SafeURL436+ var templ_7745c5c3_Var22 templ.SafeURL
420- templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Reject))437+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Reject))
421 if templ_7745c5c3_Err != nil {438 if templ_7745c5c3_Err != nil {
422- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 64, Col: 60}439+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 65, Col: 60}
423 }440 }
424- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))441+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
425 if templ_7745c5c3_Err != nil {442 if templ_7745c5c3_Err != nil {
426 return templ_7745c5c3_Err443 return templ_7745c5c3_Err
427 }444 }
428- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><button type=\"submit\" class=\"btn\">Reject</button></form><form method=\"post\" action=\"")445+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><button type=\"submit\" class=\"btn\">Reject</button></form><form method=\"post\" action=\"")
429 if templ_7745c5c3_Err != nil {446 if templ_7745c5c3_Err != nil {
430 return templ_7745c5c3_Err447 return templ_7745c5c3_Err
431 }448 }
432- var templ_7745c5c3_Var22 templ.SafeURL449+ var templ_7745c5c3_Var23 templ.SafeURL
433- templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Ban))450+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Ban))
434 if templ_7745c5c3_Err != nil {451 if templ_7745c5c3_Err != nil {
435- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 65, Col: 57}452+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 66, Col: 57}
436 }453 }
437- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))454+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
438 if templ_7745c5c3_Err != nil {455 if templ_7745c5c3_Err != nil {
439 return templ_7745c5c3_Err456 return templ_7745c5c3_Err
440 }457 }
441- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"><button type=\"submit\" class=\"btn danger\">Reject and ban</button></form></div>")458+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"><button type=\"submit\" class=\"btn danger\">Reject and ban</button></form></div>")
442 if templ_7745c5c3_Err != nil {459 if templ_7745c5c3_Err != nil {
443 return templ_7745c5c3_Err460 return templ_7745c5c3_Err
444 }461 }
445 }462 }
446- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</li>")463+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</li>")
447 if templ_7745c5c3_Err != nil {464 if templ_7745c5c3_Err != nil {
448 return templ_7745c5c3_Err465 return templ_7745c5c3_Err
449 }466 }
450 }467 }
451- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</ul>")468+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</ul>")
452 if templ_7745c5c3_Err != nil {469 if templ_7745c5c3_Err != nil {
453 return templ_7745c5c3_Err470 return templ_7745c5c3_Err
454 }471 }
455 } else {472 } else {
456- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<p class=\"lede dim\">nothing awaits moderation.</p>")473+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<p class=\"lede dim\">nothing awaits moderation.</p>")
457 if templ_7745c5c3_Err != nil {474 if templ_7745c5c3_Err != nil {
458 return templ_7745c5c3_Err475 return templ_7745c5c3_Err
459 }476 }
460 }477 }
461- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")478+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</section>")
462 if templ_7745c5c3_Err != nil {479 if templ_7745c5c3_Err != nil {
463 return templ_7745c5c3_Err480 return templ_7745c5c3_Err
464 }481 }
@@ -482,25 +499,25 @@ func InboxMRs(p InboxPage) templ.Component {
482 }()499 }()
483 }500 }
484 ctx = templ.InitializeContext(ctx)501 ctx = templ.InitializeContext(ctx)
485- templ_7745c5c3_Var23 := templ.GetChildren(ctx)502+ templ_7745c5c3_Var24 := templ.GetChildren(ctx)
486- if templ_7745c5c3_Var23 == nil {503+ if templ_7745c5c3_Var24 == nil {
487- templ_7745c5c3_Var23 = templ.NopComponent504+ templ_7745c5c3_Var24 = templ.NopComponent
488 }505 }
489 ctx = templ.ClearChildren(ctx)506 ctx = templ.ClearChildren(ctx)
490- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<section id=\"inbox-mrs\" aria-labelledby=\"inbox-mrs-h\"><h2 id=\"inbox-mrs-h\" class=\"rule-h\"><span>Open merge requests</span><span class=\"count\">")507+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<section id=\"inbox-mrs\" aria-labelledby=\"inbox-mrs-h\"><h2 id=\"inbox-mrs-h\" class=\"rule-h\"><span>Open merge requests</span><span class=\"count\">")
491 if templ_7745c5c3_Err != nil {508 if templ_7745c5c3_Err != nil {
492 return templ_7745c5c3_Err509 return templ_7745c5c3_Err
493 }510 }
494- var templ_7745c5c3_Var24 string511+ var templ_7745c5c3_Var25 string
495- templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.MRs)))512+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.MRs)))
496 if templ_7745c5c3_Err != nil {513 if templ_7745c5c3_Err != nil {
497- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 79, Col: 108}514+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 80, Col: 108}
498 }515 }
499- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))516+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
500 if templ_7745c5c3_Err != nil {517 if templ_7745c5c3_Err != nil {
501 return templ_7745c5c3_Err518 return templ_7745c5c3_Err
502 }519 }
503- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></h2>")520+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</span></h2>")
504 if templ_7745c5c3_Err != nil {521 if templ_7745c5c3_Err != nil {
505 return templ_7745c5c3_Err522 return templ_7745c5c3_Err
506 }523 }
@@ -509,12 +526,12 @@ func InboxMRs(p InboxPage) templ.Component {
509 return templ_7745c5c3_Err526 return templ_7745c5c3_Err
510 }527 }
511 if len(p.MRs) == 0 {528 if len(p.MRs) == 0 {
512- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p class=\"lede dim\">no open merge requests.</p>")529+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<p class=\"lede dim\">no open merge requests.</p>")
513 if templ_7745c5c3_Err != nil {530 if templ_7745c5c3_Err != nil {
514 return templ_7745c5c3_Err531 return templ_7745c5c3_Err
515 }532 }
516 }533 }
517- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</section>")534+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</section>")
518 if templ_7745c5c3_Err != nil {535 if templ_7745c5c3_Err != nil {
519 return templ_7745c5c3_Err536 return templ_7745c5c3_Err
520 }537 }
@@ -538,25 +555,25 @@ func InboxIssues(p InboxPage) templ.Component {
538 }()555 }()
539 }556 }
540 ctx = templ.InitializeContext(ctx)557 ctx = templ.InitializeContext(ctx)
541- templ_7745c5c3_Var25 := templ.GetChildren(ctx)558+ templ_7745c5c3_Var26 := templ.GetChildren(ctx)
542- if templ_7745c5c3_Var25 == nil {559+ if templ_7745c5c3_Var26 == nil {
543- templ_7745c5c3_Var25 = templ.NopComponent560+ templ_7745c5c3_Var26 = templ.NopComponent
544 }561 }
545 ctx = templ.ClearChildren(ctx)562 ctx = templ.ClearChildren(ctx)
546- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<section id=\"inbox-issues\" aria-labelledby=\"inbox-issues-h\"><h2 id=\"inbox-issues-h\" class=\"rule-h\"><span>Open issues</span><span class=\"count\">")563+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<section id=\"inbox-issues\" aria-labelledby=\"inbox-issues-h\"><h2 id=\"inbox-issues-h\" class=\"rule-h\"><span>Open issues</span><span class=\"count\">")
547 if templ_7745c5c3_Err != nil {564 if templ_7745c5c3_Err != nil {
548 return templ_7745c5c3_Err565 return templ_7745c5c3_Err
549 }566 }
550- var templ_7745c5c3_Var26 string567+ var templ_7745c5c3_Var27 string
551- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.IssueRows)))568+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.IssueRows)))
552 if templ_7745c5c3_Err != nil {569 if templ_7745c5c3_Err != nil {
553- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 89, Col: 109}570+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 90, Col: 109}
554 }571 }
555- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))572+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
556 if templ_7745c5c3_Err != nil {573 if templ_7745c5c3_Err != nil {
557 return templ_7745c5c3_Err574 return templ_7745c5c3_Err
558 }575 }
559- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</span></h2>")576+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</span></h2>")
560 if templ_7745c5c3_Err != nil {577 if templ_7745c5c3_Err != nil {
561 return templ_7745c5c3_Err578 return templ_7745c5c3_Err
562 }579 }
@@ -565,12 +582,12 @@ func InboxIssues(p InboxPage) templ.Component {
565 return templ_7745c5c3_Err582 return templ_7745c5c3_Err
566 }583 }
567 if len(p.IssueRows) == 0 {584 if len(p.IssueRows) == 0 {
568- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<p class=\"lede dim\">no open issues.</p>")585+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<p class=\"lede dim\">no open issues.</p>")
569 if templ_7745c5c3_Err != nil {586 if templ_7745c5c3_Err != nil {
570 return templ_7745c5c3_Err587 return templ_7745c5c3_Err
571 }588 }
572 }589 }
573- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</section>")590+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</section>")
574 if templ_7745c5c3_Err != nil {591 if templ_7745c5c3_Err != nil {
575 return templ_7745c5c3_Err592 return templ_7745c5c3_Err
576 }593 }
@@ -594,160 +611,160 @@ func inboxRows(rows []InboxRow, label string) templ.Component {
594 }()611 }()
595 }612 }
596 ctx = templ.InitializeContext(ctx)613 ctx = templ.InitializeContext(ctx)
597- templ_7745c5c3_Var27 := templ.GetChildren(ctx)614+ templ_7745c5c3_Var28 := templ.GetChildren(ctx)
598- if templ_7745c5c3_Var27 == nil {615+ if templ_7745c5c3_Var28 == nil {
599- templ_7745c5c3_Var27 = templ.NopComponent616+ templ_7745c5c3_Var28 = templ.NopComponent
600 }617 }
601 ctx = templ.ClearChildren(ctx)618 ctx = templ.ClearChildren(ctx)
602 if len(rows) > 0 {619 if len(rows) > 0 {
603- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<table class=\"list mrs\" aria-label=\"")620+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<table class=\"list mrs\" aria-label=\"")
604 if templ_7745c5c3_Err != nil {621 if templ_7745c5c3_Err != nil {
605 return templ_7745c5c3_Err622 return templ_7745c5c3_Err
606 }623 }
607- var templ_7745c5c3_Var28 string624+ var templ_7745c5c3_Var29 string
608- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(label)625+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(label)
609 if templ_7745c5c3_Err != nil {626 if templ_7745c5c3_Err != nil {
610- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 99, Col: 44}627+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 100, Col: 44}
611 }628 }
612- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)629+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
613 if templ_7745c5c3_Err != nil {630 if templ_7745c5c3_Err != nil {
614 return templ_7745c5c3_Err631 return templ_7745c5c3_Err
615 }632 }
616- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"><tbody data-rows>")633+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\"><tbody data-rows>")
617 if templ_7745c5c3_Err != nil {634 if templ_7745c5c3_Err != nil {
618 return templ_7745c5c3_Err635 return templ_7745c5c3_Err
619 }636 }
620 for _, m := range rows {637 for _, m := range rows {
621- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<tr")638+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<tr")
622 if templ_7745c5c3_Err != nil {639 if templ_7745c5c3_Err != nil {
623 return templ_7745c5c3_Err640 return templ_7745c5c3_Err
624 }641 }
625 if m.Cursor {642 if m.Cursor {
626- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, " data-cursor")643+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, " data-cursor")
627 if templ_7745c5c3_Err != nil {644 if templ_7745c5c3_Err != nil {
628 return templ_7745c5c3_Err645 return templ_7745c5c3_Err
629 }646 }
630 }647 }
631- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, " data-state=\"open\"><td class=\"c-n mute\">")648+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, " data-state=\"open\"><td class=\"c-n mute\">")
632 if templ_7745c5c3_Err != nil {649 if templ_7745c5c3_Err != nil {
633 return templ_7745c5c3_Err650 return templ_7745c5c3_Err
634 }651 }
635- var templ_7745c5c3_Var29 string652+ var templ_7745c5c3_Var30 string
636- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(m.Number)653+ templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(m.Number)
637 if templ_7745c5c3_Err != nil {654 if templ_7745c5c3_Err != nil {
638- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 103, Col: 37}655+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 37}
639 }656 }
640- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))657+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
641 if templ_7745c5c3_Err != nil {658 if templ_7745c5c3_Err != nil {
642 return templ_7745c5c3_Err659 return templ_7745c5c3_Err
643 }660 }
644- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</td><td class=\"c-subject\"><a href=\"")661+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</td><td class=\"c-subject\"><a href=\"")
645 if templ_7745c5c3_Err != nil {662 if templ_7745c5c3_Err != nil {
646 return templ_7745c5c3_Err663 return templ_7745c5c3_Err
647 }664 }
648- var templ_7745c5c3_Var30 templ.SafeURL665+ var templ_7745c5c3_Var31 templ.SafeURL
649- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Href))666+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.Href))
650 if templ_7745c5c3_Err != nil {667 if templ_7745c5c3_Err != nil {
651- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 59}668+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 59}
652 }669 }
653- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))670+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
654 if templ_7745c5c3_Err != nil {671 if templ_7745c5c3_Err != nil {
655 return templ_7745c5c3_Err672 return templ_7745c5c3_Err
656 }673 }
657- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\">")674+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
658 if templ_7745c5c3_Err != nil {675 if templ_7745c5c3_Err != nil {
659 return templ_7745c5c3_Err676 return templ_7745c5c3_Err
660 }677 }
661- var templ_7745c5c3_Var31 string678+ var templ_7745c5c3_Var32 string
662- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)679+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title)
663 if templ_7745c5c3_Err != nil {680 if templ_7745c5c3_Err != nil {
664- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 71}681+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 71}
665 }682 }
666- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))683+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
667 if templ_7745c5c3_Err != nil {684 if templ_7745c5c3_Err != nil {
668 return templ_7745c5c3_Err685 return templ_7745c5c3_Err
669 }686 }
670- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</a> <a class=\"mute\" href=\"")687+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</a> <a class=\"mute\" href=\"")
671 if templ_7745c5c3_Err != nil {688 if templ_7745c5c3_Err != nil {
672 return templ_7745c5c3_Err689 return templ_7745c5c3_Err
673 }690 }
674- var templ_7745c5c3_Var32 templ.SafeURL691+ var templ_7745c5c3_Var33 templ.SafeURL
675- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))692+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(m.RepoHref))
676 if templ_7745c5c3_Err != nil {693 if templ_7745c5c3_Err != nil {
677- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 126}694+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 126}
678 }695 }
679- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))696+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
680 if templ_7745c5c3_Err != nil {697 if templ_7745c5c3_Err != nil {
681 return templ_7745c5c3_Err698 return templ_7745c5c3_Err
682 }699 }
683- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\">")700+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\">")
684 if templ_7745c5c3_Err != nil {701 if templ_7745c5c3_Err != nil {
685 return templ_7745c5c3_Err702 return templ_7745c5c3_Err
686 }703 }
687- var templ_7745c5c3_Var33 string704+ var templ_7745c5c3_Var34 string
688- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)705+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(m.Repo)
689 if templ_7745c5c3_Err != nil {706 if templ_7745c5c3_Err != nil {
690- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 104, Col: 137}707+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 105, Col: 137}
691 }708 }
692- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))709+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
693 if templ_7745c5c3_Err != nil {710 if templ_7745c5c3_Err != nil {
694 return templ_7745c5c3_Err711 return templ_7745c5c3_Err
695 }712 }
696- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</a></td><td class=\"c-when nowrap dim\">")713+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</a></td><td class=\"c-when nowrap dim\">")
697 if templ_7745c5c3_Err != nil {714 if templ_7745c5c3_Err != nil {
698 return templ_7745c5c3_Err715 return templ_7745c5c3_Err
699 }716 }
700 if m.WhenAt != "" {717 if m.WhenAt != "" {
701- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<time datetime=\"")718+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<time datetime=\"")
702 if templ_7745c5c3_Err != nil {719 if templ_7745c5c3_Err != nil {
703 return templ_7745c5c3_Err720 return templ_7745c5c3_Err
704 }721 }
705- var templ_7745c5c3_Var34 string722+ var templ_7745c5c3_Var35 string
706- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)723+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
707 if templ_7745c5c3_Err != nil {724 if templ_7745c5c3_Err != nil {
708- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 33}725+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 33}
709 }726 }
710- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)727+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
711 if templ_7745c5c3_Err != nil {728 if templ_7745c5c3_Err != nil {
712 return templ_7745c5c3_Err729 return templ_7745c5c3_Err
713 }730 }
714- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" title=\"")731+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" title=\"")
715 if templ_7745c5c3_Err != nil {732 if templ_7745c5c3_Err != nil {
716 return templ_7745c5c3_Err733 return templ_7745c5c3_Err
717 }734 }
718- var templ_7745c5c3_Var35 string735+ var templ_7745c5c3_Var36 string
719- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)736+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(m.WhenAt)
720 if templ_7745c5c3_Err != nil {737 if templ_7745c5c3_Err != nil {
721- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 52}738+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 52}
722 }739 }
723- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)740+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
724 if templ_7745c5c3_Err != nil {741 if templ_7745c5c3_Err != nil {
725 return templ_7745c5c3_Err742 return templ_7745c5c3_Err
726 }743 }
727- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\">")744+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">")
728 if templ_7745c5c3_Err != nil {745 if templ_7745c5c3_Err != nil {
729 return templ_7745c5c3_Err746 return templ_7745c5c3_Err
730 }747 }
731- var templ_7745c5c3_Var36 string748+ var templ_7745c5c3_Var37 string
732- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)749+ templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(m.When)
733 if templ_7745c5c3_Err != nil {750 if templ_7745c5c3_Err != nil {
734- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 107, Col: 63}751+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 108, Col: 63}
735 }752 }
736- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))753+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
737 if templ_7745c5c3_Err != nil {754 if templ_7745c5c3_Err != nil {
738 return templ_7745c5c3_Err755 return templ_7745c5c3_Err
739 }756 }
740- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</time>")757+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</time>")
741 if templ_7745c5c3_Err != nil {758 if templ_7745c5c3_Err != nil {
742 return templ_7745c5c3_Err759 return templ_7745c5c3_Err
743 }760 }
744 }761 }
745- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</td></tr>")762+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</td></tr>")
746 if templ_7745c5c3_Err != nil {763 if templ_7745c5c3_Err != nil {
747 return templ_7745c5c3_Err764 return templ_7745c5c3_Err
748 }765 }
749 }766 }
750- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</tbody></table>")767+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</tbody></table>")
751 if templ_7745c5c3_Err != nil {768 if templ_7745c5c3_Err != nil {
752 return templ_7745c5c3_Err769 return templ_7745c5c3_Err
753 }770 }
@@ -756,4 +773,265 @@ func inboxRows(rows []InboxRow, label string) templ.Component {
756 })773 })
757 }774 }
758 775
776+// InboxRuns stays as an empty fragment when CI is disabled.
777+func InboxRuns(p InboxPage) templ.Component {
778+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
779+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
780+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
781+ return templ_7745c5c3_CtxErr
782+ }
783+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
784+ if !templ_7745c5c3_IsBuffer {
785+ defer func() {
786+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
787+ if templ_7745c5c3_Err == nil {
788+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
789+ }
790+ }()
791+ }
792+ ctx = templ.InitializeContext(ctx)
793+ templ_7745c5c3_Var38 := templ.GetChildren(ctx)
794+ if templ_7745c5c3_Var38 == nil {
795+ templ_7745c5c3_Var38 = templ.NopComponent
796+ }
797+ ctx = templ.ClearChildren(ctx)
798+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<section id=\"inbox-runs\"")
799+ if templ_7745c5c3_Err != nil {
800+ return templ_7745c5c3_Err
801+ }
802+ if !p.RunsEnabled {
803+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " hidden")
804+ if templ_7745c5c3_Err != nil {
805+ return templ_7745c5c3_Err
806+ }
807+ }
808+ if p.RunsEnabled {
809+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, " aria-labelledby=\"inbox-runs-h\"")
810+ if templ_7745c5c3_Err != nil {
811+ return templ_7745c5c3_Err
812+ }
813+ }
814+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, ">")
815+ if templ_7745c5c3_Err != nil {
816+ return templ_7745c5c3_Err
817+ }
818+ if p.RunsEnabled {
819+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<h2 id=\"inbox-runs-h\" class=\"rule-h\"><span>Running</span><span class=\"count\">")
820+ if templ_7745c5c3_Err != nil {
821+ return templ_7745c5c3_Err
822+ }
823+ var templ_7745c5c3_Var39 string
824+ templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Runs)))
825+ if templ_7745c5c3_Err != nil {
826+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 126, Col: 99}
827+ }
828+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
829+ if templ_7745c5c3_Err != nil {
830+ return templ_7745c5c3_Err
831+ }
832+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</span></h2>")
833+ if templ_7745c5c3_Err != nil {
834+ return templ_7745c5c3_Err
835+ }
836+ if len(p.Runs) == 0 {
837+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<p class=\"lede dim\">no runs in progress.</p>")
838+ if templ_7745c5c3_Err != nil {
839+ return templ_7745c5c3_Err
840+ }
841+ } else {
842+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<table class=\"list mrs\" aria-label=\"Running\"><tbody data-rows>")
843+ if templ_7745c5c3_Err != nil {
844+ return templ_7745c5c3_Err
845+ }
846+ for _, r := range p.Runs {
847+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<tr><td class=\"c-st\">")
848+ if templ_7745c5c3_Err != nil {
849+ return templ_7745c5c3_Err
850+ }
851+ templ_7745c5c3_Err = CheckIcon(r.Kind, r.State).Render(ctx, templ_7745c5c3_Buffer)
852+ if templ_7745c5c3_Err != nil {
853+ return templ_7745c5c3_Err
854+ }
855+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<span class=\"dim\">")
856+ if templ_7745c5c3_Err != nil {
857+ return templ_7745c5c3_Err
858+ }
859+ var templ_7745c5c3_Var40 string
860+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(r.State)
861+ if templ_7745c5c3_Err != nil {
862+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 134, Col: 79}
863+ }
864+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
865+ if templ_7745c5c3_Err != nil {
866+ return templ_7745c5c3_Err
867+ }
868+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</span></td><td class=\"c-subject\"><a href=\"")
869+ if templ_7745c5c3_Err != nil {
870+ return templ_7745c5c3_Err
871+ }
872+ var templ_7745c5c3_Var41 templ.SafeURL
873+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
874+ if templ_7745c5c3_Err != nil {
875+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 61}
876+ }
877+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
878+ if templ_7745c5c3_Err != nil {
879+ return templ_7745c5c3_Err
880+ }
881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\">")
882+ if templ_7745c5c3_Err != nil {
883+ return templ_7745c5c3_Err
884+ }
885+ var templ_7745c5c3_Var42 string
886+ templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(r.Workflow)
887+ if templ_7745c5c3_Err != nil {
888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 76}
889+ }
890+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
891+ if templ_7745c5c3_Err != nil {
892+ return templ_7745c5c3_Err
893+ }
894+ var templ_7745c5c3_Var43 string
895+ templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
896+ if templ_7745c5c3_Err != nil {
897+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 83}
898+ }
899+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
900+ if templ_7745c5c3_Err != nil {
901+ return templ_7745c5c3_Err
902+ }
903+ var templ_7745c5c3_Var44 string
904+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(r.Number)
905+ if templ_7745c5c3_Err != nil {
906+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 95}
907+ }
908+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
909+ if templ_7745c5c3_Err != nil {
910+ return templ_7745c5c3_Err
911+ }
912+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</a> <a class=\"mute\" href=\"")
913+ if templ_7745c5c3_Err != nil {
914+ return templ_7745c5c3_Err
915+ }
916+ var templ_7745c5c3_Var45 templ.SafeURL
917+ templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.RepoHref))
918+ if templ_7745c5c3_Err != nil {
919+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 150}
920+ }
921+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
922+ if templ_7745c5c3_Err != nil {
923+ return templ_7745c5c3_Err
924+ }
925+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\">")
926+ if templ_7745c5c3_Err != nil {
927+ return templ_7745c5c3_Err
928+ }
929+ var templ_7745c5c3_Var46 string
930+ templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(r.Repo)
931+ if templ_7745c5c3_Err != nil {
932+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 161}
933+ }
934+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
935+ if templ_7745c5c3_Err != nil {
936+ return templ_7745c5c3_Err
937+ }
938+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</a> <span class=\"dim\">")
939+ if templ_7745c5c3_Err != nil {
940+ return templ_7745c5c3_Err
941+ }
942+ var templ_7745c5c3_Var47 string
943+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(r.Ref)
944+ if templ_7745c5c3_Err != nil {
945+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 193}
946+ }
947+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
948+ if templ_7745c5c3_Err != nil {
949+ return templ_7745c5c3_Err
950+ }
951+ var templ_7745c5c3_Var48 string
952+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
953+ if templ_7745c5c3_Err != nil {
954+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 200}
955+ }
956+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
957+ if templ_7745c5c3_Err != nil {
958+ return templ_7745c5c3_Err
959+ }
960+ var templ_7745c5c3_Var49 string
961+ templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(r.Event)
962+ if templ_7745c5c3_Err != nil {
963+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 135, Col: 211}
964+ }
965+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
966+ if templ_7745c5c3_Err != nil {
967+ return templ_7745c5c3_Err
968+ }
969+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</span></td><td class=\"c-when nowrap dim\">")
970+ if templ_7745c5c3_Err != nil {
971+ return templ_7745c5c3_Err
972+ }
973+ if r.WhenAt != "" {
974+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "<time datetime=\"")
975+ if templ_7745c5c3_Err != nil {
976+ return templ_7745c5c3_Err
977+ }
978+ var templ_7745c5c3_Var50 string
979+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
980+ if templ_7745c5c3_Err != nil {
981+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 35}
982+ }
983+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
984+ if templ_7745c5c3_Err != nil {
985+ return templ_7745c5c3_Err
986+ }
987+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "\" title=\"")
988+ if templ_7745c5c3_Err != nil {
989+ return templ_7745c5c3_Err
990+ }
991+ var templ_7745c5c3_Var51 string
992+ templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
993+ if templ_7745c5c3_Err != nil {
994+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 54}
995+ }
996+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51)
997+ if templ_7745c5c3_Err != nil {
998+ return templ_7745c5c3_Err
999+ }
1000+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\">")
1001+ if templ_7745c5c3_Err != nil {
1002+ return templ_7745c5c3_Err
1003+ }
1004+ var templ_7745c5c3_Var52 string
1005+ templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)
1006+ if templ_7745c5c3_Err != nil {
1007+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/inbox.templ`, Line: 138, Col: 65}
1008+ }
1009+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
1010+ if templ_7745c5c3_Err != nil {
1011+ return templ_7745c5c3_Err
1012+ }
1013+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</time>")
1014+ if templ_7745c5c3_Err != nil {
1015+ return templ_7745c5c3_Err
1016+ }
1017+ }
1018+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</td></tr>")
1019+ if templ_7745c5c3_Err != nil {
1020+ return templ_7745c5c3_Err
1021+ }
1022+ }
1023+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</tbody></table>")
1024+ if templ_7745c5c3_Err != nil {
1025+ return templ_7745c5c3_Err
1026+ }
1027+ }
1028+ }
1029+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</section>")
1030+ if templ_7745c5c3_Err != nil {
1031+ return templ_7745c5c3_Err
1032+ }
1033+ return nil
1034+ })
1035+}
1036+
759 var _ = templruntime.GeneratedTemplate1037 var _ = templruntime.GeneratedTemplate
M internal/adapters/web/inbox_test.go +6 -4
@@ -180,12 +180,13 @@ func TestInboxLiveFragments(t *testing.T) {
180180 ls := &liveStream{s: srv, view: "inbox"}
181181 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: domain.IssueOpened{Issue: domain.Issue{Repo: repoName(t, "beta"), Number: 7}}})
182182 require.NoError(t, err)
183- require.Len(t, frags, 5)
183+ require.Len(t, frags, 6)
184184 assert.Contains(t, frags[0].html, `id="inbox-counts"`)
185185 assert.Contains(t, frags[1].html, `id="inbox-repos"`)
186186 assert.Contains(t, frags[2].html, `id="inbox-pending"`)
187- assert.Contains(t, frags[3].html, `id="inbox-mrs"`)
188- assert.Contains(t, frags[4].html, `id="inbox-issues"`)
187+ assert.Contains(t, frags[3].html, `id="inbox-runs"`)
188+ assert.Contains(t, frags[4].html, `id="inbox-mrs"`)
189+ assert.Contains(t, frags[5].html, `id="inbox-issues"`)
189190
190191 none, err := ls.render(withOwner(t.Context()), ports.WatchEvent{Event: domain.PushEvent{Repo: repoName(t, "beta")}})
191192 require.NoError(t, err)
@@ -286,6 +287,7 @@ func TestInboxRefreshesOnRepoEvents(t *testing.T) {
286287 t.Parallel()
287288 for name, ev := range map[string]domain.Event{
288289 "issues turned off": domain.RepoUpdated{Repo: repoName(t, "beta")},
290+ "run updated": domain.RunUpdated{},
289291 "repo deleted": domain.RepoDeleted{Repo: repoName(t, "beta")},
290292 } {
291293 t.Run(name, func(t *testing.T) {
@@ -297,7 +299,7 @@ func TestInboxRefreshesOnRepoEvents(t *testing.T) {
297299 ls := &liveStream{s: srv, view: "inbox"}
298300 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: ev})
299301 require.NoError(t, err)
300- require.Len(t, frags, 5)
302+ require.Len(t, frags, 6)
301303 all := ""
302304 for _, f := range frags {
303305 all += f.html
@@ -180,12 +180,13 @@ func TestInboxLiveFragments(t *testing.T) {
180 ls := &liveStream{s: srv, view: "inbox"}180 ls := &liveStream{s: srv, view: "inbox"}
181 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: domain.IssueOpened{Issue: domain.Issue{Repo: repoName(t, "beta"), Number: 7}}})181 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: domain.IssueOpened{Issue: domain.Issue{Repo: repoName(t, "beta"), Number: 7}}})
182 require.NoError(t, err)182 require.NoError(t, err)
183- require.Len(t, frags, 5)183+ require.Len(t, frags, 6)
184 assert.Contains(t, frags[0].html, `id="inbox-counts"`)184 assert.Contains(t, frags[0].html, `id="inbox-counts"`)
185 assert.Contains(t, frags[1].html, `id="inbox-repos"`)185 assert.Contains(t, frags[1].html, `id="inbox-repos"`)
186 assert.Contains(t, frags[2].html, `id="inbox-pending"`)186 assert.Contains(t, frags[2].html, `id="inbox-pending"`)
187- assert.Contains(t, frags[3].html, `id="inbox-mrs"`)187+ assert.Contains(t, frags[3].html, `id="inbox-runs"`)
188- assert.Contains(t, frags[4].html, `id="inbox-issues"`)188+ assert.Contains(t, frags[4].html, `id="inbox-mrs"`)
189+ assert.Contains(t, frags[5].html, `id="inbox-issues"`)
189 190
190 none, err := ls.render(withOwner(t.Context()), ports.WatchEvent{Event: domain.PushEvent{Repo: repoName(t, "beta")}})191 none, err := ls.render(withOwner(t.Context()), ports.WatchEvent{Event: domain.PushEvent{Repo: repoName(t, "beta")}})
191 require.NoError(t, err)192 require.NoError(t, err)
@@ -286,6 +287,7 @@ func TestInboxRefreshesOnRepoEvents(t *testing.T) {
286 t.Parallel()287 t.Parallel()
287 for name, ev := range map[string]domain.Event{288 for name, ev := range map[string]domain.Event{
288 "issues turned off": domain.RepoUpdated{Repo: repoName(t, "beta")},289 "issues turned off": domain.RepoUpdated{Repo: repoName(t, "beta")},
290+ "run updated": domain.RunUpdated{},
289 "repo deleted": domain.RepoDeleted{Repo: repoName(t, "beta")},291 "repo deleted": domain.RepoDeleted{Repo: repoName(t, "beta")},
290 } {292 } {
291 t.Run(name, func(t *testing.T) {293 t.Run(name, func(t *testing.T) {
@@ -297,7 +299,7 @@ func TestInboxRefreshesOnRepoEvents(t *testing.T) {
297 ls := &liveStream{s: srv, view: "inbox"}299 ls := &liveStream{s: srv, view: "inbox"}
298 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: ev})300 frags, err := ls.render(withOwner(t.Context()), ports.WatchEvent{ID: "b-1", Event: ev})
299 require.NoError(t, err)301 require.NoError(t, err)
300- require.Len(t, frags, 5)302+ require.Len(t, frags, 6)
301 all := ""303 all := ""
302 for _, f := range frags {304 for _, f := range frags {
303 all += f.html305 all += f.html
A internal/adapters/web/jstest/keys.test.mjs +29 -0
@@ -0,0 +1,29 @@
1+// Exercise the production keydown handler without loading the rest of the page.
2+import assert from 'node:assert/strict';
3+import { readFileSync } from 'node:fs';
4+import { test } from 'node:test';
5+const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8');
6+const block = src.slice(src.indexOf(' // 1. Keys.'), src.indexOf(' // 2. A [data-fire]'));
7+
8+function page({ owner = true, off = false, typing = false, dialog = false } = {}) {
9+ let handler;
10+ const fired = [];
11+ const window = { ermiteKeys: { blocks: () => off } };
12+ const $ = (s) => s === 'dialog[open]' ? dialog : s === '[data-inbox]' ? owner : null;
13+ new Function('window', '$', 'ermite', 'addEventListener', block)(window, $, { fire: (n) => fired.push(n) }, (_, f) => { handler = f; });
14+ return (key) => {
15+ const e = { key, shiftKey: key === 'I', preventDefault() {}, target: { closest: (s) => s.startsWith('input') && typing } };
16+ handler(e);
17+ return fired;
18+ };
19+}
20+test('Shift+I opens inbox while lowercase i retains the issues tab', () => {
21+ const press = page();
22+ assert.deepEqual(press('I'), ['inbox']);
23+ assert.deepEqual(press('i'), ['inbox', 'i']);
24+});
25+test('inbox shortcut respects preference, fields, dialogs, and owner target', () => {
26+ for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) {
27+ assert.deepEqual(page(options)('I'), [], JSON.stringify(options));
28+ }
29+});
@@ -0,0 +1,29 @@
1+// Exercise the production keydown handler without loading the rest of the page.
2+import assert from 'node:assert/strict';
3+import { readFileSync } from 'node:fs';
4+import { test } from 'node:test';
5+const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8');
6+const block = src.slice(src.indexOf(' // 1. Keys.'), src.indexOf(' // 2. A [data-fire]'));
7+
8+function page({ owner = true, off = false, typing = false, dialog = false } = {}) {
9+ let handler;
10+ const fired = [];
11+ const window = { ermiteKeys: { blocks: () => off } };
12+ const $ = (s) => s === 'dialog[open]' ? dialog : s === '[data-inbox]' ? owner : null;
13+ new Function('window', '$', 'ermite', 'addEventListener', block)(window, $, { fire: (n) => fired.push(n) }, (_, f) => { handler = f; });
14+ return (key) => {
15+ const e = { key, shiftKey: key === 'I', preventDefault() {}, target: { closest: (s) => s.startsWith('input') && typing } };
16+ handler(e);
17+ return fired;
18+ };
19+}
20+test('Shift+I opens inbox while lowercase i retains the issues tab', () => {
21+ const press = page();
22+ assert.deepEqual(press('I'), ['inbox']);
23+ assert.deepEqual(press('i'), ['inbox', 'i']);
24+});
25+test('inbox shortcut respects preference, fields, dialogs, and owner target', () => {
26+ for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) {
27+ assert.deepEqual(page(options)('I'), [], JSON.stringify(options));
28+ }
29+});
M internal/adapters/web/keys.templ +1 -0
@@ -28,6 +28,7 @@ templ Keys() {
2828 k-r from:body -> ermite.tab('r');
2929 k-m from:body -> ermite.tab('m');
3030 k-i from:body -> ermite.tab('i');
31+ k-inbox from:body -> ermite.go('[data-inbox]');
3132 k-next from:body -> ermite.nextNote();
3233 k-split from:body -> ermite.layout('split');
3334 k-unified from:body -> ermite.layout('unified');
@@ -28,6 +28,7 @@ templ Keys() {
28 k-r from:body -> ermite.tab('r');28 k-r from:body -> ermite.tab('r');
29 k-m from:body -> ermite.tab('m');29 k-m from:body -> ermite.tab('m');
30 k-i from:body -> ermite.tab('i');30 k-i from:body -> ermite.tab('i');
31+ k-inbox from:body -> ermite.go('[data-inbox]');
31 k-next from:body -> ermite.nextNote();32 k-next from:body -> ermite.nextNote();
32 k-split from:body -> ermite.layout('split');33 k-split from:body -> ermite.layout('split');
33 k-unified from:body -> ermite.layout('unified');34 k-unified from:body -> ermite.layout('unified');
M internal/adapters/web/keys_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/overlays.templ +3 -0
@@ -27,6 +27,9 @@ templ OverlayHelp(mr bool) {
2727 <div><dt><kbd>/</kbd></dt><dd>filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)</dd></div>
2828 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>
2929 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>
30+ if SignedIn(ctx) {
31+ <div><dt><kbd>I</kbd></dt><dd>inbox</dd></div>
32+ }
3033 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>
3134 <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>
3235 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>
@@ -27,6 +27,9 @@ templ OverlayHelp(mr bool) {
27 <div><dt><kbd>/</kbd></dt><dd>filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)</dd></div>27 <div><dt><kbd>/</kbd></dt><dd>filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)</dd></div>
28 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>28 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>
29 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>29 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd></div>
30+ if SignedIn(ctx) {
31+ <div><dt><kbd>I</kbd></dt><dd>inbox</dd></div>
32+ }
30 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>33 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>
31 <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>34 <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>
32 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>35 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>
M internal/adapters/web/overlays_templ.go +49 -39 generated file: collapsed
M internal/adapters/web/shell.templ +5 -1
@@ -67,6 +67,9 @@ templ Shell(l Layout, rail templ.Component) {
6767 </div>
6868 @Footer(l)
6969 @Hint(l)
70+ if SignedIn(ctx) {
71+ <a hidden data-inbox href={ inboxPath }>Inbox</a>
72+ }
7073 @OverlayHelp(l.MR)
7174 @OverlayRefs(l)
7275 @OverlayCmd(l.Cmd)
@@ -87,7 +90,8 @@ templ Header(l Layout) {
8790 <a
8891 class="btn inbox-btn"
8992 href={ templ.SafeURL(l.Inbox.Href) }
90- title="inbox"
93+ title="inbox (I)"
94+ aria-keyshortcuts="Shift+I"
9195 >
9296 <span class="ico i-mailbox" aria-hidden="true"></span>
9397 Inbox
@@ -67,6 +67,9 @@ templ Shell(l Layout, rail templ.Component) {
67 </div>67 </div>
68 @Footer(l)68 @Footer(l)
69 @Hint(l)69 @Hint(l)
70+ if SignedIn(ctx) {
71+ <a hidden data-inbox href={ inboxPath }>Inbox</a>
72+ }
70 @OverlayHelp(l.MR)73 @OverlayHelp(l.MR)
71 @OverlayRefs(l)74 @OverlayRefs(l)
72 @OverlayCmd(l.Cmd)75 @OverlayCmd(l.Cmd)
@@ -87,7 +90,8 @@ templ Header(l Layout) {
87 <a90 <a
88 class="btn inbox-btn"91 class="btn inbox-btn"
89 href={ templ.SafeURL(l.Inbox.Href) }92 href={ templ.SafeURL(l.Inbox.Href) }
90- title="inbox"93+ title="inbox (I)"
94+ aria-keyshortcuts="Shift+I"
91 >95 >
92 <span class="ico i-mailbox" aria-hidden="true"></span>96 <span class="ico i-mailbox" aria-hidden="true"></span>
93 Inbox97 Inbox
M internal/adapters/web/shell_templ.go +152 -133 generated file: collapsed
M internal/adapters/web/static/js/ermite.js +2 -2
@@ -612,7 +612,7 @@
612612
613613 // 1. Keys.
614614 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',
615- s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
615+ s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
616616 // the merge request page (mr.js): the merge menu, mark read
617617 M: 'merge', R: 'read' };
618618 // "g t" is the key "t": the tabs have one event each.
@@ -654,7 +654,7 @@
654654 // page-level keys, as on the other lists.
655655 if (!e.shiftKey && e.target.closest?.('#tree-rows') && window.ermiteTree?.arrow(e.key)) { e.preventDefault(); return; }
656656 const name = single[e.key];
657- if (!name) return;
657+ if (!name || (name === 'inbox' && !$('[data-inbox]'))) return;
658658 if (e.key !== 'Enter' || !$('dialog[open]')) e.preventDefault();
659659 ermite.fire(name);
660660 });
@@ -612,7 +612,7 @@
612 612
613 // 1. Keys.613 // 1. Keys.
614 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',614 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',
615- s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',615+ s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
616 // the merge request page (mr.js): the merge menu, mark read616 // the merge request page (mr.js): the merge menu, mark read
617 M: 'merge', R: 'read' };617 M: 'merge', R: 'read' };
618 // "g t" is the key "t": the tabs have one event each.618 // "g t" is the key "t": the tabs have one event each.
@@ -654,7 +654,7 @@
654 // page-level keys, as on the other lists.654 // page-level keys, as on the other lists.
655 if (!e.shiftKey && e.target.closest?.('#tree-rows') && window.ermiteTree?.arrow(e.key)) { e.preventDefault(); return; }655 if (!e.shiftKey && e.target.closest?.('#tree-rows') && window.ermiteTree?.arrow(e.key)) { e.preventDefault(); return; }
656 const name = single[e.key];656 const name = single[e.key];
657- if (!name) return;657+ if (!name || (name === 'inbox' && !$('[data-inbox]'))) return;
658 if (e.key !== 'Enter' || !$('dialog[open]')) e.preventDefault();658 if (e.key !== 'Enter' || !$('dialog[open]')) e.preventDefault();
659 ermite.fire(name);659 ermite.fire(name);
660 });660 });
M internal/app/forge.go +1 -1
@@ -412,7 +412,7 @@ func newForge(ctx context.Context, cfg config.Config, opts options) (_ *forge, e
412412 BaseDifficulty: opts.issuePoW, MaxDifficulty: opts.issuePoW, MinFillTime: fastFill(opts.issuePoW),
413413 })
414414 f.Inbox = usecase.NewInbox(usecase.InboxConfig{
415- Meta: store, MergeRequests: store.MergeRequests(), Issues: store.Issues(), Pending: store.PendingIssues(),
415+ Meta: store, MergeRequests: store.MergeRequests(), Issues: store.Issues(), Pending: store.PendingIssues(), CI: store.CI(),
416416 })
417417 f.CI.SetPullGate(f.MergeRequests)
418418 f.AutoMerge.Start()
@@ -412,7 +412,7 @@ func newForge(ctx context.Context, cfg config.Config, opts options) (_ *forge, e
412 BaseDifficulty: opts.issuePoW, MaxDifficulty: opts.issuePoW, MinFillTime: fastFill(opts.issuePoW),412 BaseDifficulty: opts.issuePoW, MaxDifficulty: opts.issuePoW, MinFillTime: fastFill(opts.issuePoW),
413 })413 })
414 f.Inbox = usecase.NewInbox(usecase.InboxConfig{414 f.Inbox = usecase.NewInbox(usecase.InboxConfig{
415- Meta: store, MergeRequests: store.MergeRequests(), Issues: store.Issues(), Pending: store.PendingIssues(),415+ Meta: store, MergeRequests: store.MergeRequests(), Issues: store.Issues(), Pending: store.PendingIssues(), CI: store.CI(),
416 })416 })
417 f.CI.SetPullGate(f.MergeRequests)417 f.CI.SetPullGate(f.MergeRequests)
418 f.AutoMerge.Start()418 f.AutoMerge.Start()
M internal/app/testdata/web/blob-binary.html +1 -0
@@ -428,6 +428,7 @@
428428 k-r from:body -> ermite.tab('r');
429429 k-m from:body -> ermite.tab('m');
430430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431432 k-next from:body -> ermite.nextNote();
432433 k-split from:body -> ermite.layout('split');
433434 k-unified from:body -> ermite.layout('unified');
@@ -428,6 +428,7 @@
428 k-r from:body -> ermite.tab('r');428 k-r from:body -> ermite.tab('r');
429 k-m from:body -> ermite.tab('m');429 k-m from:body -> ermite.tab('m');
430 k-i from:body -> ermite.tab('i');430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431 k-next from:body -> ermite.nextNote();432 k-next from:body -> ermite.nextNote();
432 k-split from:body -> ermite.layout('split');433 k-split from:body -> ermite.layout('split');
433 k-unified from:body -> ermite.layout('unified');434 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-code.html +1 -0
@@ -498,6 +498,7 @@
498498 k-r from:body -> ermite.tab('r');
499499 k-m from:body -> ermite.tab('m');
500500 k-i from:body -> ermite.tab('i');
501+ k-inbox from:body -> ermite.go('[data-inbox]');
501502 k-next from:body -> ermite.nextNote();
502503 k-split from:body -> ermite.layout('split');
503504 k-unified from:body -> ermite.layout('unified');
@@ -498,6 +498,7 @@
498 k-r from:body -> ermite.tab('r');498 k-r from:body -> ermite.tab('r');
499 k-m from:body -> ermite.tab('m');499 k-m from:body -> ermite.tab('m');
500 k-i from:body -> ermite.tab('i');500 k-i from:body -> ermite.tab('i');
501+ k-inbox from:body -> ermite.go('[data-inbox]');
501 k-next from:body -> ermite.nextNote();502 k-next from:body -> ermite.nextNote();
502 k-split from:body -> ermite.layout('split');503 k-split from:body -> ermite.layout('split');
503 k-unified from:body -> ermite.layout('unified');504 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-empty.html +1 -0
@@ -428,6 +428,7 @@
428428 k-r from:body -> ermite.tab('r');
429429 k-m from:body -> ermite.tab('m');
430430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431432 k-next from:body -> ermite.nextNote();
432433 k-split from:body -> ermite.layout('split');
433434 k-unified from:body -> ermite.layout('unified');
@@ -428,6 +428,7 @@
428 k-r from:body -> ermite.tab('r');428 k-r from:body -> ermite.tab('r');
429 k-m from:body -> ermite.tab('m');429 k-m from:body -> ermite.tab('m');
430 k-i from:body -> ermite.tab('i');430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431 k-next from:body -> ermite.nextNote();432 k-next from:body -> ermite.nextNote();
432 k-split from:body -> ermite.layout('split');433 k-split from:body -> ermite.layout('split');
433 k-unified from:body -> ermite.layout('unified');434 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-image.html +1 -0
@@ -430,6 +430,7 @@
430430 k-r from:body -> ermite.tab('r');
431431 k-m from:body -> ermite.tab('m');
432432 k-i from:body -> ermite.tab('i');
433+ k-inbox from:body -> ermite.go('[data-inbox]');
433434 k-next from:body -> ermite.nextNote();
434435 k-split from:body -> ermite.layout('split');
435436 k-unified from:body -> ermite.layout('unified');
@@ -430,6 +430,7 @@
430 k-r from:body -> ermite.tab('r');430 k-r from:body -> ermite.tab('r');
431 k-m from:body -> ermite.tab('m');431 k-m from:body -> ermite.tab('m');
432 k-i from:body -> ermite.tab('i');432 k-i from:body -> ermite.tab('i');
433+ k-inbox from:body -> ermite.go('[data-inbox]');
433 k-next from:body -> ermite.nextNote();434 k-next from:body -> ermite.nextNote();
434 k-split from:body -> ermite.layout('split');435 k-split from:body -> ermite.layout('split');
435 k-unified from:body -> ermite.layout('unified');436 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-markdown.html +1 -0
@@ -495,6 +495,7 @@
495495 k-r from:body -> ermite.tab('r');
496496 k-m from:body -> ermite.tab('m');
497497 k-i from:body -> ermite.tab('i');
498+ k-inbox from:body -> ermite.go('[data-inbox]');
498499 k-next from:body -> ermite.nextNote();
499500 k-split from:body -> ermite.layout('split');
500501 k-unified from:body -> ermite.layout('unified');
@@ -495,6 +495,7 @@
495 k-r from:body -> ermite.tab('r');495 k-r from:body -> ermite.tab('r');
496 k-m from:body -> ermite.tab('m');496 k-m from:body -> ermite.tab('m');
497 k-i from:body -> ermite.tab('i');497 k-i from:body -> ermite.tab('i');
498+ k-inbox from:body -> ermite.go('[data-inbox]');
498 k-next from:body -> ermite.nextNote();499 k-next from:body -> ermite.nextNote();
499 k-split from:body -> ermite.layout('split');500 k-split from:body -> ermite.layout('split');
500 k-unified from:body -> ermite.layout('unified');501 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-svg.html +1 -0
@@ -453,6 +453,7 @@
453453 k-r from:body -> ermite.tab('r');
454454 k-m from:body -> ermite.tab('m');
455455 k-i from:body -> ermite.tab('i');
456+ k-inbox from:body -> ermite.go('[data-inbox]');
456457 k-next from:body -> ermite.nextNote();
457458 k-split from:body -> ermite.layout('split');
458459 k-unified from:body -> ermite.layout('unified');
@@ -453,6 +453,7 @@
453 k-r from:body -> ermite.tab('r');453 k-r from:body -> ermite.tab('r');
454 k-m from:body -> ermite.tab('m');454 k-m from:body -> ermite.tab('m');
455 k-i from:body -> ermite.tab('i');455 k-i from:body -> ermite.tab('i');
456+ k-inbox from:body -> ermite.go('[data-inbox]');
456 k-next from:body -> ermite.nextNote();457 k-next from:body -> ermite.nextNote();
457 k-split from:body -> ermite.layout('split');458 k-split from:body -> ermite.layout('split');
458 k-unified from:body -> ermite.layout('unified');459 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-text.html +1 -0
@@ -448,6 +448,7 @@
448448 k-r from:body -> ermite.tab('r');
449449 k-m from:body -> ermite.tab('m');
450450 k-i from:body -> ermite.tab('i');
451+ k-inbox from:body -> ermite.go('[data-inbox]');
451452 k-next from:body -> ermite.nextNote();
452453 k-split from:body -> ermite.layout('split');
453454 k-unified from:body -> ermite.layout('unified');
@@ -448,6 +448,7 @@
448 k-r from:body -> ermite.tab('r');448 k-r from:body -> ermite.tab('r');
449 k-m from:body -> ermite.tab('m');449 k-m from:body -> ermite.tab('m');
450 k-i from:body -> ermite.tab('i');450 k-i from:body -> ermite.tab('i');
451+ k-inbox from:body -> ermite.go('[data-inbox]');
451 k-next from:body -> ermite.nextNote();452 k-next from:body -> ermite.nextNote();
452 k-split from:body -> ermite.layout('split');453 k-split from:body -> ermite.layout('split');
453 k-unified from:body -> ermite.layout('unified');454 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-too-large.html +1 -0
@@ -428,6 +428,7 @@
428428 k-r from:body -> ermite.tab('r');
429429 k-m from:body -> ermite.tab('m');
430430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431432 k-next from:body -> ermite.nextNote();
432433 k-split from:body -> ermite.layout('split');
433434 k-unified from:body -> ermite.layout('unified');
@@ -428,6 +428,7 @@
428 k-r from:body -> ermite.tab('r');428 k-r from:body -> ermite.tab('r');
429 k-m from:body -> ermite.tab('m');429 k-m from:body -> ermite.tab('m');
430 k-i from:body -> ermite.tab('i');430 k-i from:body -> ermite.tab('i');
431+ k-inbox from:body -> ermite.go('[data-inbox]');
431 k-next from:body -> ermite.nextNote();432 k-next from:body -> ermite.nextNote();
432 k-split from:body -> ermite.layout('split');433 k-split from:body -> ermite.layout('split');
433 k-unified from:body -> ermite.layout('unified');434 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/blob-trojan.html +1 -0
@@ -495,6 +495,7 @@
495495 k-r from:body -> ermite.tab('r');
496496 k-m from:body -> ermite.tab('m');
497497 k-i from:body -> ermite.tab('i');
498+ k-inbox from:body -> ermite.go('[data-inbox]');
498499 k-next from:body -> ermite.nextNote();
499500 k-split from:body -> ermite.layout('split');
500501 k-unified from:body -> ermite.layout('unified');
@@ -495,6 +495,7 @@
495 k-r from:body -> ermite.tab('r');495 k-r from:body -> ermite.tab('r');
496 k-m from:body -> ermite.tab('m');496 k-m from:body -> ermite.tab('m');
497 k-i from:body -> ermite.tab('i');497 k-i from:body -> ermite.tab('i');
498+ k-inbox from:body -> ermite.go('[data-inbox]');
498 k-next from:body -> ermite.nextNote();499 k-next from:body -> ermite.nextNote();
499 k-split from:body -> ermite.layout('split');500 k-split from:body -> ermite.layout('split');
500 k-unified from:body -> ermite.layout('unified');501 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-big.html +1 -0
@@ -480,6 +480,7 @@
480480 k-r from:body -> ermite.tab('r');
481481 k-m from:body -> ermite.tab('m');
482482 k-i from:body -> ermite.tab('i');
483+ k-inbox from:body -> ermite.go('[data-inbox]');
483484 k-next from:body -> ermite.nextNote();
484485 k-split from:body -> ermite.layout('split');
485486 k-unified from:body -> ermite.layout('unified');
@@ -480,6 +480,7 @@
480 k-r from:body -> ermite.tab('r');480 k-r from:body -> ermite.tab('r');
481 k-m from:body -> ermite.tab('m');481 k-m from:body -> ermite.tab('m');
482 k-i from:body -> ermite.tab('i');482 k-i from:body -> ermite.tab('i');
483+ k-inbox from:body -> ermite.go('[data-inbox]');
483 k-next from:body -> ermite.nextNote();484 k-next from:body -> ermite.nextNote();
484 k-split from:body -> ermite.layout('split');485 k-split from:body -> ermite.layout('split');
485 k-unified from:body -> ermite.layout('unified');486 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-binary.html +1 -0
@@ -471,6 +471,7 @@
471471 k-r from:body -> ermite.tab('r');
472472 k-m from:body -> ermite.tab('m');
473473 k-i from:body -> ermite.tab('i');
474+ k-inbox from:body -> ermite.go('[data-inbox]');
474475 k-next from:body -> ermite.nextNote();
475476 k-split from:body -> ermite.layout('split');
476477 k-unified from:body -> ermite.layout('unified');
@@ -471,6 +471,7 @@
471 k-r from:body -> ermite.tab('r');471 k-r from:body -> ermite.tab('r');
472 k-m from:body -> ermite.tab('m');472 k-m from:body -> ermite.tab('m');
473 k-i from:body -> ermite.tab('i');473 k-i from:body -> ermite.tab('i');
474+ k-inbox from:body -> ermite.go('[data-inbox]');
474 k-next from:body -> ermite.nextNote();475 k-next from:body -> ermite.nextNote();
475 k-split from:body -> ermite.layout('split');476 k-split from:body -> ermite.layout('split');
476 k-unified from:body -> ermite.layout('unified');477 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-merge.html +1 -0
@@ -513,6 +513,7 @@
513513 k-r from:body -> ermite.tab('r');
514514 k-m from:body -> ermite.tab('m');
515515 k-i from:body -> ermite.tab('i');
516+ k-inbox from:body -> ermite.go('[data-inbox]');
516517 k-next from:body -> ermite.nextNote();
517518 k-split from:body -> ermite.layout('split');
518519 k-unified from:body -> ermite.layout('unified');
@@ -513,6 +513,7 @@
513 k-r from:body -> ermite.tab('r');513 k-r from:body -> ermite.tab('r');
514 k-m from:body -> ermite.tab('m');514 k-m from:body -> ermite.tab('m');
515 k-i from:body -> ermite.tab('i');515 k-i from:body -> ermite.tab('i');
516+ k-inbox from:body -> ermite.go('[data-inbox]');
516 k-next from:body -> ermite.nextNote();517 k-next from:body -> ermite.nextNote();
517 k-split from:body -> ermite.layout('split');518 k-split from:body -> ermite.layout('split');
518 k-unified from:body -> ermite.layout('unified');519 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-modify-split.html +1 -0
@@ -636,6 +636,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
636636 k-r from:body -> ermite.tab('r');
637637 k-m from:body -> ermite.tab('m');
638638 k-i from:body -> ermite.tab('i');
639+ k-inbox from:body -> ermite.go('[data-inbox]');
639640 k-next from:body -> ermite.nextNote();
640641 k-split from:body -> ermite.layout('split');
641642 k-unified from:body -> ermite.layout('unified');
@@ -636,6 +636,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
636 k-r from:body -> ermite.tab('r');636 k-r from:body -> ermite.tab('r');
637 k-m from:body -> ermite.tab('m');637 k-m from:body -> ermite.tab('m');
638 k-i from:body -> ermite.tab('i');638 k-i from:body -> ermite.tab('i');
639+ k-inbox from:body -> ermite.go('[data-inbox]');
639 k-next from:body -> ermite.nextNote();640 k-next from:body -> ermite.nextNote();
640 k-split from:body -> ermite.layout('split');641 k-split from:body -> ermite.layout('split');
641 k-unified from:body -> ermite.layout('unified');642 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-modify.html +1 -0
@@ -636,6 +636,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
636636 k-r from:body -> ermite.tab('r');
637637 k-m from:body -> ermite.tab('m');
638638 k-i from:body -> ermite.tab('i');
639+ k-inbox from:body -> ermite.go('[data-inbox]');
639640 k-next from:body -> ermite.nextNote();
640641 k-split from:body -> ermite.layout('split');
641642 k-unified from:body -> ermite.layout('unified');
@@ -636,6 +636,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
636 k-r from:body -> ermite.tab('r');636 k-r from:body -> ermite.tab('r');
637 k-m from:body -> ermite.tab('m');637 k-m from:body -> ermite.tab('m');
638 k-i from:body -> ermite.tab('i');638 k-i from:body -> ermite.tab('i');
639+ k-inbox from:body -> ermite.go('[data-inbox]');
639 k-next from:body -> ermite.nextNote();640 k-next from:body -> ermite.nextNote();
640 k-split from:body -> ermite.layout('split');641 k-split from:body -> ermite.layout('split');
641 k-unified from:body -> ermite.layout('unified');642 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-rename.html +1 -0
@@ -540,6 +540,7 @@
540540 k-r from:body -> ermite.tab('r');
541541 k-m from:body -> ermite.tab('m');
542542 k-i from:body -> ermite.tab('i');
543+ k-inbox from:body -> ermite.go('[data-inbox]');
543544 k-next from:body -> ermite.nextNote();
544545 k-split from:body -> ermite.layout('split');
545546 k-unified from:body -> ermite.layout('unified');
@@ -540,6 +540,7 @@
540 k-r from:body -> ermite.tab('r');540 k-r from:body -> ermite.tab('r');
541 k-m from:body -> ermite.tab('m');541 k-m from:body -> ermite.tab('m');
542 k-i from:body -> ermite.tab('i');542 k-i from:body -> ermite.tab('i');
543+ k-inbox from:body -> ermite.go('[data-inbox]');
543 k-next from:body -> ermite.nextNote();544 k-next from:body -> ermite.nextNote();
544 k-split from:body -> ermite.layout('split');545 k-split from:body -> ermite.layout('split');
545 k-unified from:body -> ermite.layout('unified');546 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-root.html +1 -0
@@ -859,6 +859,7 @@
859859 k-r from:body -> ermite.tab('r');
860860 k-m from:body -> ermite.tab('m');
861861 k-i from:body -> ermite.tab('i');
862+ k-inbox from:body -> ermite.go('[data-inbox]');
862863 k-next from:body -> ermite.nextNote();
863864 k-split from:body -> ermite.layout('split');
864865 k-unified from:body -> ermite.layout('unified');
@@ -859,6 +859,7 @@
859 k-r from:body -> ermite.tab('r');859 k-r from:body -> ermite.tab('r');
860 k-m from:body -> ermite.tab('m');860 k-m from:body -> ermite.tab('m');
861 k-i from:body -> ermite.tab('i');861 k-i from:body -> ermite.tab('i');
862+ k-inbox from:body -> ermite.go('[data-inbox]');
862 k-next from:body -> ermite.nextNote();863 k-next from:body -> ermite.nextNote();
863 k-split from:body -> ermite.layout('split');864 k-split from:body -> ermite.layout('split');
864 k-unified from:body -> ermite.layout('unified');865 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commit-trojan.html +1 -0
@@ -688,6 +688,7 @@
688688 k-r from:body -> ermite.tab('r');
689689 k-m from:body -> ermite.tab('m');
690690 k-i from:body -> ermite.tab('i');
691+ k-inbox from:body -> ermite.go('[data-inbox]');
691692 k-next from:body -> ermite.nextNote();
692693 k-split from:body -> ermite.layout('split');
693694 k-unified from:body -> ermite.layout('unified');
@@ -688,6 +688,7 @@
688 k-r from:body -> ermite.tab('r');688 k-r from:body -> ermite.tab('r');
689 k-m from:body -> ermite.tab('m');689 k-m from:body -> ermite.tab('m');
690 k-i from:body -> ermite.tab('i');690 k-i from:body -> ermite.tab('i');
691+ k-inbox from:body -> ermite.go('[data-inbox]');
691 k-next from:body -> ermite.nextNote();692 k-next from:body -> ermite.nextNote();
692 k-split from:body -> ermite.layout('split');693 k-split from:body -> ermite.layout('split');
693 k-unified from:body -> ermite.layout('unified');694 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commits-page2.html +1 -0
@@ -441,6 +441,7 @@
441441 k-r from:body -> ermite.tab('r');
442442 k-m from:body -> ermite.tab('m');
443443 k-i from:body -> ermite.tab('i');
444+ k-inbox from:body -> ermite.go('[data-inbox]');
444445 k-next from:body -> ermite.nextNote();
445446 k-split from:body -> ermite.layout('split');
446447 k-unified from:body -> ermite.layout('unified');
@@ -441,6 +441,7 @@
441 k-r from:body -> ermite.tab('r');441 k-r from:body -> ermite.tab('r');
442 k-m from:body -> ermite.tab('m');442 k-m from:body -> ermite.tab('m');
443 k-i from:body -> ermite.tab('i');443 k-i from:body -> ermite.tab('i');
444+ k-inbox from:body -> ermite.go('[data-inbox]');
444 k-next from:body -> ermite.nextNote();445 k-next from:body -> ermite.nextNote();
445 k-split from:body -> ermite.layout('split');446 k-split from:body -> ermite.layout('split');
446 k-unified from:body -> ermite.layout('unified');447 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commits-path.html +1 -0
@@ -456,6 +456,7 @@
456456 k-r from:body -> ermite.tab('r');
457457 k-m from:body -> ermite.tab('m');
458458 k-i from:body -> ermite.tab('i');
459+ k-inbox from:body -> ermite.go('[data-inbox]');
459460 k-next from:body -> ermite.nextNote();
460461 k-split from:body -> ermite.layout('split');
461462 k-unified from:body -> ermite.layout('unified');
@@ -456,6 +456,7 @@
456 k-r from:body -> ermite.tab('r');456 k-r from:body -> ermite.tab('r');
457 k-m from:body -> ermite.tab('m');457 k-m from:body -> ermite.tab('m');
458 k-i from:body -> ermite.tab('i');458 k-i from:body -> ermite.tab('i');
459+ k-inbox from:body -> ermite.go('[data-inbox]');
459 k-next from:body -> ermite.nextNote();460 k-next from:body -> ermite.nextNote();
460 k-split from:body -> ermite.layout('split');461 k-split from:body -> ermite.layout('split');
461 k-unified from:body -> ermite.layout('unified');462 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commits-root.html +1 -0
@@ -1246,6 +1246,7 @@
12461246 k-r from:body -> ermite.tab('r');
12471247 k-m from:body -> ermite.tab('m');
12481248 k-i from:body -> ermite.tab('i');
1249+ k-inbox from:body -> ermite.go('[data-inbox]');
12491250 k-next from:body -> ermite.nextNote();
12501251 k-split from:body -> ermite.layout('split');
12511252 k-unified from:body -> ermite.layout('unified');
@@ -1246,6 +1246,7 @@
1246 k-r from:body -> ermite.tab('r');1246 k-r from:body -> ermite.tab('r');
1247 k-m from:body -> ermite.tab('m');1247 k-m from:body -> ermite.tab('m');
1248 k-i from:body -> ermite.tab('i');1248 k-i from:body -> ermite.tab('i');
1249+ k-inbox from:body -> ermite.go('[data-inbox]');
1249 k-next from:body -> ermite.nextNote();1250 k-next from:body -> ermite.nextNote();
1250 k-split from:body -> ermite.layout('split');1251 k-split from:body -> ermite.layout('split');
1251 k-unified from:body -> ermite.layout('unified');1252 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/commits-small.html +1 -0
@@ -611,6 +611,7 @@
611611 k-r from:body -> ermite.tab('r');
612612 k-m from:body -> ermite.tab('m');
613613 k-i from:body -> ermite.tab('i');
614+ k-inbox from:body -> ermite.go('[data-inbox]');
614615 k-next from:body -> ermite.nextNote();
615616 k-split from:body -> ermite.layout('split');
616617 k-unified from:body -> ermite.layout('unified');
@@ -611,6 +611,7 @@
611 k-r from:body -> ermite.tab('r');611 k-r from:body -> ermite.tab('r');
612 k-m from:body -> ermite.tab('m');612 k-m from:body -> ermite.tab('m');
613 k-i from:body -> ermite.tab('i');613 k-i from:body -> ermite.tab('i');
614+ k-inbox from:body -> ermite.go('[data-inbox]');
614 k-next from:body -> ermite.nextNote();615 k-next from:body -> ermite.nextNote();
615 k-split from:body -> ermite.layout('split');616 k-split from:body -> ermite.layout('split');
616 k-unified from:body -> ermite.layout('unified');617 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/empty.html +1 -0
@@ -284,6 +284,7 @@
284284 k-r from:body -> ermite.tab('r');
285285 k-m from:body -> ermite.tab('m');
286286 k-i from:body -> ermite.tab('i');
287+ k-inbox from:body -> ermite.go('[data-inbox]');
287288 k-next from:body -> ermite.nextNote();
288289 k-split from:body -> ermite.layout('split');
289290 k-unified from:body -> ermite.layout('unified');
@@ -284,6 +284,7 @@
284 k-r from:body -> ermite.tab('r');284 k-r from:body -> ermite.tab('r');
285 k-m from:body -> ermite.tab('m');285 k-m from:body -> ermite.tab('m');
286 k-i from:body -> ermite.tab('i');286 k-i from:body -> ermite.tab('i');
287+ k-inbox from:body -> ermite.go('[data-inbox]');
287 k-next from:body -> ermite.nextNote();288 k-next from:body -> ermite.nextNote();
288 k-split from:body -> ermite.layout('split');289 k-split from:body -> ermite.layout('split');
289 k-unified from:body -> ermite.layout('unified');290 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/home.html +1 -0
@@ -394,6 +394,7 @@
394394 k-r from:body -> ermite.tab('r');
395395 k-m from:body -> ermite.tab('m');
396396 k-i from:body -> ermite.tab('i');
397+ k-inbox from:body -> ermite.go('[data-inbox]');
397398 k-next from:body -> ermite.nextNote();
398399 k-split from:body -> ermite.layout('split');
399400 k-unified from:body -> ermite.layout('unified');
@@ -394,6 +394,7 @@
394 k-r from:body -> ermite.tab('r');394 k-r from:body -> ermite.tab('r');
395 k-m from:body -> ermite.tab('m');395 k-m from:body -> ermite.tab('m');
396 k-i from:body -> ermite.tab('i');396 k-i from:body -> ermite.tab('i');
397+ k-inbox from:body -> ermite.go('[data-inbox]');
397 k-next from:body -> ermite.nextNote();398 k-next from:body -> ermite.nextNote();
398 k-split from:body -> ermite.layout('split');399 k-split from:body -> ermite.layout('split');
399 k-unified from:body -> ermite.layout('unified');400 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/not-found-page.html +1 -0
@@ -267,6 +267,7 @@
267267 k-r from:body -> ermite.tab('r');
268268 k-m from:body -> ermite.tab('m');
269269 k-i from:body -> ermite.tab('i');
270+ k-inbox from:body -> ermite.go('[data-inbox]');
270271 k-next from:body -> ermite.nextNote();
271272 k-split from:body -> ermite.layout('split');
272273 k-unified from:body -> ermite.layout('unified');
@@ -267,6 +267,7 @@
267 k-r from:body -> ermite.tab('r');267 k-r from:body -> ermite.tab('r');
268 k-m from:body -> ermite.tab('m');268 k-m from:body -> ermite.tab('m');
269 k-i from:body -> ermite.tab('i');269 k-i from:body -> ermite.tab('i');
270+ k-inbox from:body -> ermite.go('[data-inbox]');
270 k-next from:body -> ermite.nextNote();271 k-next from:body -> ermite.nextNote();
271 k-split from:body -> ermite.layout('split');272 k-split from:body -> ermite.layout('split');
272 k-unified from:body -> ermite.layout('unified');273 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/not-found.html +1 -0
@@ -267,6 +267,7 @@
267267 k-r from:body -> ermite.tab('r');
268268 k-m from:body -> ermite.tab('m');
269269 k-i from:body -> ermite.tab('i');
270+ k-inbox from:body -> ermite.go('[data-inbox]');
270271 k-next from:body -> ermite.nextNote();
271272 k-split from:body -> ermite.layout('split');
272273 k-unified from:body -> ermite.layout('unified');
@@ -267,6 +267,7 @@
267 k-r from:body -> ermite.tab('r');267 k-r from:body -> ermite.tab('r');
268 k-m from:body -> ermite.tab('m');268 k-m from:body -> ermite.tab('m');
269 k-i from:body -> ermite.tab('i');269 k-i from:body -> ermite.tab('i');
270+ k-inbox from:body -> ermite.go('[data-inbox]');
270 k-next from:body -> ermite.nextNote();271 k-next from:body -> ermite.nextNote();
271 k-split from:body -> ermite.layout('split');272 k-split from:body -> ermite.layout('split');
272 k-unified from:body -> ermite.layout('unified');273 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/refs.html +1 -0
@@ -503,6 +503,7 @@
503503 k-r from:body -> ermite.tab('r');
504504 k-m from:body -> ermite.tab('m');
505505 k-i from:body -> ermite.tab('i');
506+ k-inbox from:body -> ermite.go('[data-inbox]');
506507 k-next from:body -> ermite.nextNote();
507508 k-split from:body -> ermite.layout('split');
508509 k-unified from:body -> ermite.layout('unified');
@@ -503,6 +503,7 @@
503 k-r from:body -> ermite.tab('r');503 k-r from:body -> ermite.tab('r');
504 k-m from:body -> ermite.tab('m');504 k-m from:body -> ermite.tab('m');
505 k-i from:body -> ermite.tab('i');505 k-i from:body -> ermite.tab('i');
506+ k-inbox from:body -> ermite.go('[data-inbox]');
506 k-next from:body -> ermite.nextNote();507 k-next from:body -> ermite.nextNote();
507 k-split from:body -> ermite.layout('split');508 k-split from:body -> ermite.layout('split');
508 k-unified from:body -> ermite.layout('unified');509 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/summary.html +1 -0
@@ -529,6 +529,7 @@
529529 k-r from:body -> ermite.tab('r');
530530 k-m from:body -> ermite.tab('m');
531531 k-i from:body -> ermite.tab('i');
532+ k-inbox from:body -> ermite.go('[data-inbox]');
532533 k-next from:body -> ermite.nextNote();
533534 k-split from:body -> ermite.layout('split');
534535 k-unified from:body -> ermite.layout('unified');
@@ -529,6 +529,7 @@
529 k-r from:body -> ermite.tab('r');529 k-r from:body -> ermite.tab('r');
530 k-m from:body -> ermite.tab('m');530 k-m from:body -> ermite.tab('m');
531 k-i from:body -> ermite.tab('i');531 k-i from:body -> ermite.tab('i');
532+ k-inbox from:body -> ermite.go('[data-inbox]');
532 k-next from:body -> ermite.nextNote();533 k-next from:body -> ermite.nextNote();
533 k-split from:body -> ermite.layout('split');534 k-split from:body -> ermite.layout('split');
534 k-unified from:body -> ermite.layout('unified');535 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/tree-nested.html +1 -0
@@ -443,6 +443,7 @@
443443 k-r from:body -> ermite.tab('r');
444444 k-m from:body -> ermite.tab('m');
445445 k-i from:body -> ermite.tab('i');
446+ k-inbox from:body -> ermite.go('[data-inbox]');
446447 k-next from:body -> ermite.nextNote();
447448 k-split from:body -> ermite.layout('split');
448449 k-unified from:body -> ermite.layout('unified');
@@ -443,6 +443,7 @@
443 k-r from:body -> ermite.tab('r');443 k-r from:body -> ermite.tab('r');
444 k-m from:body -> ermite.tab('m');444 k-m from:body -> ermite.tab('m');
445 k-i from:body -> ermite.tab('i');445 k-i from:body -> ermite.tab('i');
446+ k-inbox from:body -> ermite.go('[data-inbox]');
446 k-next from:body -> ermite.nextNote();447 k-next from:body -> ermite.nextNote();
447 k-split from:body -> ermite.layout('split');448 k-split from:body -> ermite.layout('split');
448 k-unified from:body -> ermite.layout('unified');449 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/tree-root.html +1 -0
@@ -664,6 +664,7 @@
664664 k-r from:body -> ermite.tab('r');
665665 k-m from:body -> ermite.tab('m');
666666 k-i from:body -> ermite.tab('i');
667+ k-inbox from:body -> ermite.go('[data-inbox]');
667668 k-next from:body -> ermite.nextNote();
668669 k-split from:body -> ermite.layout('split');
669670 k-unified from:body -> ermite.layout('unified');
@@ -664,6 +664,7 @@
664 k-r from:body -> ermite.tab('r');664 k-r from:body -> ermite.tab('r');
665 k-m from:body -> ermite.tab('m');665 k-m from:body -> ermite.tab('m');
666 k-i from:body -> ermite.tab('i');666 k-i from:body -> ermite.tab('i');
667+ k-inbox from:body -> ermite.go('[data-inbox]');
667 k-next from:body -> ermite.nextNote();668 k-next from:body -> ermite.nextNote();
668 k-split from:body -> ermite.layout('split');669 k-split from:body -> ermite.layout('split');
669 k-unified from:body -> ermite.layout('unified');670 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/tree-slash-ref.html +1 -0
@@ -440,6 +440,7 @@
440440 k-r from:body -> ermite.tab('r');
441441 k-m from:body -> ermite.tab('m');
442442 k-i from:body -> ermite.tab('i');
443+ k-inbox from:body -> ermite.go('[data-inbox]');
443444 k-next from:body -> ermite.nextNote();
444445 k-split from:body -> ermite.layout('split');
445446 k-unified from:body -> ermite.layout('unified');
@@ -440,6 +440,7 @@
440 k-r from:body -> ermite.tab('r');440 k-r from:body -> ermite.tab('r');
441 k-m from:body -> ermite.tab('m');441 k-m from:body -> ermite.tab('m');
442 k-i from:body -> ermite.tab('i');442 k-i from:body -> ermite.tab('i');
443+ k-inbox from:body -> ermite.go('[data-inbox]');
443 k-next from:body -> ermite.nextNote();444 k-next from:body -> ermite.nextNote();
444 k-split from:body -> ermite.layout('split');445 k-split from:body -> ermite.layout('split');
445 k-unified from:body -> ermite.layout('unified');446 k-unified from:body -> ermite.layout('unified');
M internal/app/testdata/web/tree-subdir.html +1 -0
@@ -456,6 +456,7 @@
456456 k-r from:body -> ermite.tab('r');
457457 k-m from:body -> ermite.tab('m');
458458 k-i from:body -> ermite.tab('i');
459+ k-inbox from:body -> ermite.go('[data-inbox]');
459460 k-next from:body -> ermite.nextNote();
460461 k-split from:body -> ermite.layout('split');
461462 k-unified from:body -> ermite.layout('unified');
@@ -456,6 +456,7 @@
456 k-r from:body -> ermite.tab('r');456 k-r from:body -> ermite.tab('r');
457 k-m from:body -> ermite.tab('m');457 k-m from:body -> ermite.tab('m');
458 k-i from:body -> ermite.tab('i');458 k-i from:body -> ermite.tab('i');
459+ k-inbox from:body -> ermite.go('[data-inbox]');
459 k-next from:body -> ermite.nextNote();460 k-next from:body -> ermite.nextNote();
460 k-split from:body -> ermite.layout('split');461 k-split from:body -> ermite.layout('split');
461 k-unified from:body -> ermite.layout('unified');462 k-unified from:body -> ermite.layout('unified');
M internal/app/web_inbox_test.go +32 -2
@@ -8,6 +8,7 @@ import (
88 "net/http"
99 "strings"
1010 "testing"
11+ "time"
1112
1213 "github.com/stretchr/testify/assert"
1314 "github.com/stretchr/testify/require"
@@ -42,22 +43,51 @@ func TestWebInboxEndToEnd(t *testing.T) {
4243 assert.Contains(t, empty.body, "no open merge requests.")
4344 assert.Contains(t, empty.body, "no open issues.")
4445
46+ // A queued run is visible and its run-level events refresh the owner stream.
47+ draft, err := r.forge.store.CI().CreateRun(r.ctx, r.name("proj"), ".github/workflows/build.yml", func(id, number int64) ports.CIRunDraft {
48+ return ports.CIRunDraft{Run: domain.CIRun{Repo: r.name("proj"), ID: id, Number: number, Attempt: 1, WorkflowName: "Inbox build", Status: domain.CIQueued, Ref: "refs/heads/main", Event: "push", CreatedAt: time.Now()}}
49+ })
50+ require.NoError(t, err)
51+ runs := b.get("/-/inbox", as(token))
52+ assert.Contains(t, runs.body, "Inbox build")
53+ assert.Contains(t, runs.body, "<strong>1</strong> running")
54+ assert.Contains(t, runs.body, `data-inbox href="/-/inbox"`)
55+
4556 // The owner stream sees both repositories.
4657 stream := ownerStream(t, r.harness, token)
4758
59+ r.forge.Bus.Publish(r.ctx, domain.RunUpdated{Run: draft.Run, At: time.Now(), Visibility: domain.VisibilityPublic, CreatedAt: time.Now()})
60+ var runFragments strings.Builder
61+ for range 6 {
62+ runFragments.WriteString(nextFragment(t, stream).data)
63+ }
64+ assert.Contains(t, runFragments.String(), "Inbox build")
65+ // Completion removes the row and updates the total in the same live fragments.
66+ draft.Run.Status, draft.Run.Conclusion = domain.CICompleted, domain.CISuccess
67+ _, err = r.forge.store.CI().Apply(r.ctx, ports.CIWrite{Runs: []domain.CIRun{draft.Run}})
68+ require.NoError(t, err)
69+ r.forge.Bus.Publish(r.ctx, domain.RunUpdated{Run: draft.Run, At: time.Now(), Visibility: domain.VisibilityPublic, CreatedAt: time.Now()})
70+ runFragments.Reset()
71+ for range 6 {
72+ runFragments.WriteString(nextFragment(t, stream).data)
73+ }
74+ assert.NotContains(t, runFragments.String(), "Inbox build")
75+ assert.Contains(t, runFragments.String(), "no runs in progress.")
76+ assert.Contains(t, runFragments.String(), "<strong>0</strong> running")
77+
4878 root := domain.Root()
4979 _, err = r.forge.MergeRequests.Create(r.ctx, root, r.name("proj"), ports.CreateMergeRequestParams{Title: "add search", Source: "feat/search", Target: "main"})
5080 require.NoError(t, err)
5181 ev := nextFragment(t, stream)
5282 assert.Equal(t, "inbox", ev.name)
5383 assert.Contains(t, ev.data, `id="inbox-counts"`)
54- for range 4 { // the other fragments of this event
84+ for range 5 { // the other fragments of this event
5585 nextFragment(t, stream)
5686 }
5787 _, err = r.forge.Issues.Create(r.ctx, root, r.name("other"), ports.CreateIssueParams{Title: "search is slow"})
5888 require.NoError(t, err)
5989 var seen strings.Builder
60- for range 5 { // the five fragments of this event
90+ for range 6 { // the six fragments of this event
6191 seen.WriteString(nextFragment(t, stream).data)
6292 }
6393 assert.Contains(t, seen.String(), "search is slow", "an event of the second repository")
@@ -8,6 +8,7 @@ import (
8 "net/http"8 "net/http"
9 "strings"9 "strings"
10 "testing"10 "testing"
11+ "time"
11 12
12 "github.com/stretchr/testify/assert"13 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/require"14 "github.com/stretchr/testify/require"
@@ -42,22 +43,51 @@ func TestWebInboxEndToEnd(t *testing.T) {
42 assert.Contains(t, empty.body, "no open merge requests.")43 assert.Contains(t, empty.body, "no open merge requests.")
43 assert.Contains(t, empty.body, "no open issues.")44 assert.Contains(t, empty.body, "no open issues.")
44 45
46+ // A queued run is visible and its run-level events refresh the owner stream.
47+ draft, err := r.forge.store.CI().CreateRun(r.ctx, r.name("proj"), ".github/workflows/build.yml", func(id, number int64) ports.CIRunDraft {
48+ return ports.CIRunDraft{Run: domain.CIRun{Repo: r.name("proj"), ID: id, Number: number, Attempt: 1, WorkflowName: "Inbox build", Status: domain.CIQueued, Ref: "refs/heads/main", Event: "push", CreatedAt: time.Now()}}
49+ })
50+ require.NoError(t, err)
51+ runs := b.get("/-/inbox", as(token))
52+ assert.Contains(t, runs.body, "Inbox build")
53+ assert.Contains(t, runs.body, "<strong>1</strong> running")
54+ assert.Contains(t, runs.body, `data-inbox href="/-/inbox"`)
55+
45 // The owner stream sees both repositories.56 // The owner stream sees both repositories.
46 stream := ownerStream(t, r.harness, token)57 stream := ownerStream(t, r.harness, token)
47 58
59+ r.forge.Bus.Publish(r.ctx, domain.RunUpdated{Run: draft.Run, At: time.Now(), Visibility: domain.VisibilityPublic, CreatedAt: time.Now()})
60+ var runFragments strings.Builder
61+ for range 6 {
62+ runFragments.WriteString(nextFragment(t, stream).data)
63+ }
64+ assert.Contains(t, runFragments.String(), "Inbox build")
65+ // Completion removes the row and updates the total in the same live fragments.
66+ draft.Run.Status, draft.Run.Conclusion = domain.CICompleted, domain.CISuccess
67+ _, err = r.forge.store.CI().Apply(r.ctx, ports.CIWrite{Runs: []domain.CIRun{draft.Run}})
68+ require.NoError(t, err)
69+ r.forge.Bus.Publish(r.ctx, domain.RunUpdated{Run: draft.Run, At: time.Now(), Visibility: domain.VisibilityPublic, CreatedAt: time.Now()})
70+ runFragments.Reset()
71+ for range 6 {
72+ runFragments.WriteString(nextFragment(t, stream).data)
73+ }
74+ assert.NotContains(t, runFragments.String(), "Inbox build")
75+ assert.Contains(t, runFragments.String(), "no runs in progress.")
76+ assert.Contains(t, runFragments.String(), "<strong>0</strong> running")
77+
48 root := domain.Root()78 root := domain.Root()
49 _, err = r.forge.MergeRequests.Create(r.ctx, root, r.name("proj"), ports.CreateMergeRequestParams{Title: "add search", Source: "feat/search", Target: "main"})79 _, err = r.forge.MergeRequests.Create(r.ctx, root, r.name("proj"), ports.CreateMergeRequestParams{Title: "add search", Source: "feat/search", Target: "main"})
50 require.NoError(t, err)80 require.NoError(t, err)
51 ev := nextFragment(t, stream)81 ev := nextFragment(t, stream)
52 assert.Equal(t, "inbox", ev.name)82 assert.Equal(t, "inbox", ev.name)
53 assert.Contains(t, ev.data, `id="inbox-counts"`)83 assert.Contains(t, ev.data, `id="inbox-counts"`)
54- for range 4 { // the other fragments of this event84+ for range 5 { // the other fragments of this event
55 nextFragment(t, stream)85 nextFragment(t, stream)
56 }86 }
57 _, err = r.forge.Issues.Create(r.ctx, root, r.name("other"), ports.CreateIssueParams{Title: "search is slow"})87 _, err = r.forge.Issues.Create(r.ctx, root, r.name("other"), ports.CreateIssueParams{Title: "search is slow"})
58 require.NoError(t, err)88 require.NoError(t, err)
59 var seen strings.Builder89 var seen strings.Builder
60- for range 5 { // the five fragments of this event90+ for range 6 { // the six fragments of this event
61 seen.WriteString(nextFragment(t, stream).data)91 seen.WriteString(nextFragment(t, stream).data)
62 }92 }
63 assert.Contains(t, seen.String(), "search is slow", "an event of the second repository")93 assert.Contains(t, seen.String(), "search is slow", "an event of the second repository")
M internal/ports/dto.go +6 -3
@@ -408,13 +408,16 @@ type LoginLink struct {
408408
409409 // InboxCounts are the numbers of open items across every repository, whatever
410410 // the filter.
411-type InboxCounts struct{ MergeRequests, Issues, Pending int }
411+type InboxCounts struct{ MergeRequests, Issues, Pending, Running int }
412412
413413 // InboxOverview is what the owner has to look at across every repository: the
414-// open merge requests and the open issues, most recently updated first. A
415-// section for the items awaiting moderation can be added next to them.
414+// open merge requests and issues, ongoing CI runs, and issues awaiting moderation.
416415 type InboxOverview struct {
417416 Counts InboxCounts
417+ // Runs are queued or in progress, newest first, respecting the filter.
418+ Runs []domain.CIRun
419+ // RunsEnabled distinguishes disabled CI from an empty queue.
420+ RunsEnabled bool
418421 // MergeRequests and Issues are those of the filtered repository, or of
419422 // every repository when there is no filter. Counts always counts all.
420423 MergeRequests []domain.MergeRequest
@@ -408,13 +408,16 @@ type LoginLink struct {
408 408
409 // InboxCounts are the numbers of open items across every repository, whatever409 // InboxCounts are the numbers of open items across every repository, whatever
410 // the filter.410 // the filter.
411-type InboxCounts struct{ MergeRequests, Issues, Pending int }411+type InboxCounts struct{ MergeRequests, Issues, Pending, Running int }
412 412
413 // InboxOverview is what the owner has to look at across every repository: the413 // InboxOverview is what the owner has to look at across every repository: the
414-// open merge requests and the open issues, most recently updated first. A414+// open merge requests and issues, ongoing CI runs, and issues awaiting moderation.
415-// section for the items awaiting moderation can be added next to them.
416 type InboxOverview struct {415 type InboxOverview struct {
417 Counts InboxCounts416 Counts InboxCounts
417+ // Runs are queued or in progress, newest first, respecting the filter.
418+ Runs []domain.CIRun
419+ // RunsEnabled distinguishes disabled CI from an empty queue.
420+ RunsEnabled bool
418 // MergeRequests and Issues are those of the filtered repository, or of421 // MergeRequests and Issues are those of the filtered repository, or of
419 // every repository when there is no filter. Counts always counts all.422 // every repository when there is no filter. Counts always counts all.
420 MergeRequests []domain.MergeRequest423 MergeRequests []domain.MergeRequest
M internal/usecase/events.go +2 -2
@@ -690,12 +690,12 @@ func isRepoLifecycleEvent(ev domain.Event) bool {
690690 return false
691691 }
692692
693-// isItemEvent reports whether ev is about a merge request or an issue.
693+// isItemEvent reports whether ev changes an inbox item, including CI runs.
694694 func isItemEvent(ev domain.Event) bool {
695695 switch ev.(type) {
696696 case domain.IssueReferenced:
697697 return false // back-reference refreshes do not change the inbox
698- case domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed,
698+ case domain.RunUpdated, domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed,
699699 domain.MergeRequestAutoMergeEnabled, domain.MergeRequestAutoMergeCancelled,
700700 domain.MergeRequestReady, domain.MergeRequestMergeability, domain.CommentAdded,
701701 domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved:
@@ -690,12 +690,12 @@ func isRepoLifecycleEvent(ev domain.Event) bool {
690 return false690 return false
691 }691 }
692 692
693-// isItemEvent reports whether ev is about a merge request or an issue.693+// isItemEvent reports whether ev changes an inbox item, including CI runs.
694 func isItemEvent(ev domain.Event) bool {694 func isItemEvent(ev domain.Event) bool {
695 switch ev.(type) {695 switch ev.(type) {
696 case domain.IssueReferenced:696 case domain.IssueReferenced:
697 return false // back-reference refreshes do not change the inbox697 return false // back-reference refreshes do not change the inbox
698- case domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed,698+ case domain.RunUpdated, domain.MergeRequestOpened, domain.MergeRequestMerged, domain.MergeRequestClosed,
699 domain.MergeRequestAutoMergeEnabled, domain.MergeRequestAutoMergeCancelled,699 domain.MergeRequestAutoMergeEnabled, domain.MergeRequestAutoMergeCancelled,
700 domain.MergeRequestReady, domain.MergeRequestMergeability, domain.CommentAdded,700 domain.MergeRequestReady, domain.MergeRequestMergeability, domain.CommentAdded,
701 domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved:701 domain.CommentEdited, domain.CommentDeleted, domain.CommentResolved:
M internal/usecase/inbox.go +31 -2
@@ -24,11 +24,12 @@ type Inbox struct {
2424 issues ports.IssueStore
2525 // pending is nil when the public form is not wired.
2626 pending ports.PendingIssueStore
27+ ci ports.CIStore
2728 }
2829
2930 var _ ports.InboxService = (*Inbox)(nil)
3031
31-// InboxConfig holds what the inbox needs. Every field is required.
32+// InboxConfig holds what the inbox needs; Pending and CI are optional.
3233 type InboxConfig struct {
3334 Meta ports.RepoMetaStore
3435 MergeRequests ports.MergeRequestStore
@@ -36,11 +37,12 @@ type InboxConfig struct {
3637 // Pending lists the issues awaiting moderation. Optional: without it the
3738 // inbox has no pending section.
3839 Pending ports.PendingIssueStore
40+ CI ports.CIStore
3941 }
4042
4143 // NewInbox builds the inbox use case.
4244 func NewInbox(cfg InboxConfig) *Inbox {
43- return &Inbox{meta: cfg.Meta, mrs: cfg.MergeRequests, issues: cfg.Issues, pending: cfg.Pending}
45+ return &Inbox{meta: cfg.Meta, mrs: cfg.MergeRequests, issues: cfg.Issues, pending: cfg.Pending, ci: cfg.CI}
4446 }
4547
4648 // Overview lists what is open, through the indexes of open items. The counts
@@ -126,6 +128,33 @@ func (in *Inbox) Overview(ctx context.Context, p domain.Principal, filter domain
126128 out.Counts.Pending = len(out.Pending)
127129 }
128130
131+ if in.ci != nil {
132+ out.RunsEnabled = true
133+ runs, err := in.ci.ListOpenRuns(ctx)
134+ if err != nil {
135+ return out, fmt.Errorf("list open runs: %w", err)
136+ }
137+ for _, run := range runs {
138+ if run.Status != domain.CIQueued && run.Status != domain.CIInProgress {
139+ continue
140+ }
141+ if err := look(run.Repo); err != nil {
142+ return out, err
143+ }
144+ if !exists[run.Repo] {
145+ continue
146+ }
147+ repos[run.Repo] = true
148+ out.Counts.Running++
149+ if filter.IsZero() || run.Repo == filter {
150+ out.Runs = append(out.Runs, run)
151+ }
152+ }
153+ slices.SortStableFunc(out.Runs, func(a, b domain.CIRun) int {
154+ return compareRecent(a.CreatedAt, a.CreatedAt, a.Repo, a.ID, b.CreatedAt, b.CreatedAt, b.Repo, b.ID)
155+ })
156+ }
157+
129158 slices.SortStableFunc(out.MergeRequests, func(a, b domain.MergeRequest) int {
130159 return compareRecent(a.UpdatedAt, a.CreatedAt, a.Repo, a.Number, b.UpdatedAt, b.CreatedAt, b.Repo, b.Number)
131160 })
@@ -24,11 +24,12 @@ type Inbox struct {
24 issues ports.IssueStore24 issues ports.IssueStore
25 // pending is nil when the public form is not wired.25 // pending is nil when the public form is not wired.
26 pending ports.PendingIssueStore26 pending ports.PendingIssueStore
27+ ci ports.CIStore
27 }28 }
28 29
29 var _ ports.InboxService = (*Inbox)(nil)30 var _ ports.InboxService = (*Inbox)(nil)
30 31
31-// InboxConfig holds what the inbox needs. Every field is required.32+// InboxConfig holds what the inbox needs; Pending and CI are optional.
32 type InboxConfig struct {33 type InboxConfig struct {
33 Meta ports.RepoMetaStore34 Meta ports.RepoMetaStore
34 MergeRequests ports.MergeRequestStore35 MergeRequests ports.MergeRequestStore
@@ -36,11 +37,12 @@ type InboxConfig struct {
36 // Pending lists the issues awaiting moderation. Optional: without it the37 // Pending lists the issues awaiting moderation. Optional: without it the
37 // inbox has no pending section.38 // inbox has no pending section.
38 Pending ports.PendingIssueStore39 Pending ports.PendingIssueStore
40+ CI ports.CIStore
39 }41 }
40 42
41 // NewInbox builds the inbox use case.43 // NewInbox builds the inbox use case.
42 func NewInbox(cfg InboxConfig) *Inbox {44 func NewInbox(cfg InboxConfig) *Inbox {
43- return &Inbox{meta: cfg.Meta, mrs: cfg.MergeRequests, issues: cfg.Issues, pending: cfg.Pending}45+ return &Inbox{meta: cfg.Meta, mrs: cfg.MergeRequests, issues: cfg.Issues, pending: cfg.Pending, ci: cfg.CI}
44 }46 }
45 47
46 // Overview lists what is open, through the indexes of open items. The counts48 // Overview lists what is open, through the indexes of open items. The counts
@@ -126,6 +128,33 @@ func (in *Inbox) Overview(ctx context.Context, p domain.Principal, filter domain
126 out.Counts.Pending = len(out.Pending)128 out.Counts.Pending = len(out.Pending)
127 }129 }
128 130
131+ if in.ci != nil {
132+ out.RunsEnabled = true
133+ runs, err := in.ci.ListOpenRuns(ctx)
134+ if err != nil {
135+ return out, fmt.Errorf("list open runs: %w", err)
136+ }
137+ for _, run := range runs {
138+ if run.Status != domain.CIQueued && run.Status != domain.CIInProgress {
139+ continue
140+ }
141+ if err := look(run.Repo); err != nil {
142+ return out, err
143+ }
144+ if !exists[run.Repo] {
145+ continue
146+ }
147+ repos[run.Repo] = true
148+ out.Counts.Running++
149+ if filter.IsZero() || run.Repo == filter {
150+ out.Runs = append(out.Runs, run)
151+ }
152+ }
153+ slices.SortStableFunc(out.Runs, func(a, b domain.CIRun) int {
154+ return compareRecent(a.CreatedAt, a.CreatedAt, a.Repo, a.ID, b.CreatedAt, b.CreatedAt, b.Repo, b.ID)
155+ })
156+ }
157+
129 slices.SortStableFunc(out.MergeRequests, func(a, b domain.MergeRequest) int {158 slices.SortStableFunc(out.MergeRequests, func(a, b domain.MergeRequest) int {
130 return compareRecent(a.UpdatedAt, a.CreatedAt, a.Repo, a.Number, b.UpdatedAt, b.CreatedAt, b.Repo, b.Number)159 return compareRecent(a.UpdatedAt, a.CreatedAt, a.Repo, a.Number, b.UpdatedAt, b.CreatedAt, b.Repo, b.Number)
131 })160 })
M internal/usecase/inbox_test.go +33 -0
@@ -108,3 +108,36 @@ func TestInboxListsPendingIssuesWithoutTheirHash(t *testing.T) {
108108 assert.Nil(t, ov.Pending[0].IPHash)
109109 assert.Equal(t, []domain.RepoName{name(t, "on")}, ov.Repos)
110110 }
111+
112+func TestInboxRuns(t *testing.T) {
113+ t.Parallel()
114+ mrs, issues, meta, ci := mocks.NewMergeRequestStore(t), mocks.NewIssueStore(t), mocks.NewRepoMetaStore(t), mocks.NewCIStore(t)
115+ mrs.EXPECT().ListOpenAll(mock.Anything).Return(nil, nil)
116+ issues.EXPECT().ListOpenAll(mock.Anything).Return(nil, nil)
117+ now := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
118+ alpha, beta := name(t, "alpha"), name(t, "beta")
119+ ci.EXPECT().ListOpenRuns(mock.Anything).Return([]domain.CIRun{
120+ {Repo: alpha, ID: 1, Status: domain.CIQueued, CreatedAt: now},
121+ {Repo: beta, ID: 2, Status: domain.CIInProgress, CreatedAt: now.Add(time.Hour)},
122+ {Repo: alpha, ID: 3, Status: domain.CICompleted, CreatedAt: now.Add(2 * time.Hour)},
123+ {Repo: name(t, "gone"), ID: 4, Status: domain.CIQueued, CreatedAt: now},
124+ }, nil)
125+ for _, n := range []string{"alpha", "beta"} {
126+ meta.EXPECT().Get(mock.Anything, name(t, n)).Return(repoWithIssues(t, n, domain.IssuesOff), nil)
127+ }
128+ meta.EXPECT().Get(mock.Anything, name(t, "gone")).Return(domain.Repo{}, domain.ErrNotFound)
129+ in := usecase.NewInbox(usecase.InboxConfig{Meta: meta, MergeRequests: mrs, Issues: issues, CI: ci})
130+ all, err := in.Overview(t.Context(), domain.Owner(), domain.RepoName{})
131+ require.NoError(t, err)
132+ assert.True(t, all.RunsEnabled)
133+ assert.Equal(t, 2, all.Counts.Running)
134+ require.Len(t, all.Runs, 2)
135+ assert.Equal(t, int64(2), all.Runs[0].ID)
136+ assert.Equal(t, int64(1), all.Runs[1].ID)
137+ assert.Equal(t, []domain.RepoName{alpha, beta}, all.Repos)
138+ filtered, err := in.Overview(t.Context(), domain.Owner(), alpha)
139+ require.NoError(t, err)
140+ assert.Equal(t, 2, filtered.Counts.Running)
141+ require.Len(t, filtered.Runs, 1)
142+ assert.Equal(t, alpha, filtered.Runs[0].Repo)
143+}
@@ -108,3 +108,36 @@ func TestInboxListsPendingIssuesWithoutTheirHash(t *testing.T) {
108 assert.Nil(t, ov.Pending[0].IPHash)108 assert.Nil(t, ov.Pending[0].IPHash)
109 assert.Equal(t, []domain.RepoName{name(t, "on")}, ov.Repos)109 assert.Equal(t, []domain.RepoName{name(t, "on")}, ov.Repos)
110 }110 }
111+
112+func TestInboxRuns(t *testing.T) {
113+ t.Parallel()
114+ mrs, issues, meta, ci := mocks.NewMergeRequestStore(t), mocks.NewIssueStore(t), mocks.NewRepoMetaStore(t), mocks.NewCIStore(t)
115+ mrs.EXPECT().ListOpenAll(mock.Anything).Return(nil, nil)
116+ issues.EXPECT().ListOpenAll(mock.Anything).Return(nil, nil)
117+ now := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
118+ alpha, beta := name(t, "alpha"), name(t, "beta")
119+ ci.EXPECT().ListOpenRuns(mock.Anything).Return([]domain.CIRun{
120+ {Repo: alpha, ID: 1, Status: domain.CIQueued, CreatedAt: now},
121+ {Repo: beta, ID: 2, Status: domain.CIInProgress, CreatedAt: now.Add(time.Hour)},
122+ {Repo: alpha, ID: 3, Status: domain.CICompleted, CreatedAt: now.Add(2 * time.Hour)},
123+ {Repo: name(t, "gone"), ID: 4, Status: domain.CIQueued, CreatedAt: now},
124+ }, nil)
125+ for _, n := range []string{"alpha", "beta"} {
126+ meta.EXPECT().Get(mock.Anything, name(t, n)).Return(repoWithIssues(t, n, domain.IssuesOff), nil)
127+ }
128+ meta.EXPECT().Get(mock.Anything, name(t, "gone")).Return(domain.Repo{}, domain.ErrNotFound)
129+ in := usecase.NewInbox(usecase.InboxConfig{Meta: meta, MergeRequests: mrs, Issues: issues, CI: ci})
130+ all, err := in.Overview(t.Context(), domain.Owner(), domain.RepoName{})
131+ require.NoError(t, err)
132+ assert.True(t, all.RunsEnabled)
133+ assert.Equal(t, 2, all.Counts.Running)
134+ require.Len(t, all.Runs, 2)
135+ assert.Equal(t, int64(2), all.Runs[0].ID)
136+ assert.Equal(t, int64(1), all.Runs[1].ID)
137+ assert.Equal(t, []domain.RepoName{alpha, beta}, all.Repos)
138+ filtered, err := in.Overview(t.Context(), domain.Owner(), alpha)
139+ require.NoError(t, err)
140+ assert.Equal(t, 2, filtered.Counts.Running)
141+ require.Len(t, filtered.Runs, 1)
142+ assert.Equal(t, alpha, filtered.Runs[0].Repo)
143+}

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 42

↑↓ moveenter openesc close