{{define "content" -}}
<style>
/* Self-contained milestone styles, inlined in the same flat todo.sr.ht idiom as
the beads view (the scss bundle is not rebuilt in dev). Theme variables mirror
core.sr.ht's palette and follow the host's light/dark preference. */
.milestones {
--accent: #2f9e44;
--bd-bg: #ffffff; --bd-panel: #f2f3f5; --bd-fg: #212529;
--bd-muted: #6c757d; --bd-border: #ced4da; --bd-danger: #b52a2a;
}
@media (prefers-color-scheme: dark) {
.milestones {
--bd-bg: #212529; --bd-panel: #343a40; --bd-fg: #dee2e6;
--bd-muted: #adb5bd; --bd-border: #6c757d; --bd-danger: #ff6b6b;
}
}
.milestones .ms { border: 1px solid var(--bd-border); margin-bottom: 1rem; }
.milestones .ms-head { display: flex; align-items: center; gap: .7rem; padding: .5rem .7rem; background: var(--bd-panel); border-bottom: 1px solid var(--bd-border); }
.milestones .ms-title { font-weight: 700; font-size: 1rem; margin: 0; flex: 0 0 auto; }
.milestones .ms-bar { flex: 1 1 auto; height: 8px; background: var(--bd-bg); border: 1px solid var(--bd-border); min-width: 6rem; }
.milestones .ms-fill { display: block; height: 100%; background: var(--accent); }
.milestones .ms-counts { flex: 0 0 auto; font-size: .8rem; color: var(--bd-muted); white-space: nowrap; }
.milestones .ms-counts b { color: var(--bd-fg); }
.milestones .ms-issue { display: flex; align-items: baseline; gap: .5rem; padding: .3rem .7rem; border-top: 1px solid var(--bd-border); text-decoration: none; color: var(--bd-fg); }
.milestones .ms-issue:first-of-type { border-top: none; }
.milestones .ms-issue:hover { background: var(--bd-panel); }
.milestones .ms-issue .r-id { font-family: monospace; font-size: .8rem; color: var(--bd-muted); flex: 0 0 auto; }
.milestones .ms-issue .r-title { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.milestones .ms-issue.done .r-title { text-decoration: line-through; color: var(--bd-muted); }
.milestones .ms-issue .r-meta { flex: 0 0 auto; display: flex; gap: .3rem; }
.milestones .ms-issue.head { border-left: 3px solid var(--accent); background: var(--bd-panel); }
.milestones .ms-issue.head .r-title { font-weight: 700; }
.milestones .ms-issue.epic .r-title { font-weight: 600; }
.milestones .ms-issue.epic .r-sub { flex: 0 0 auto; font-size: .78rem; color: var(--bd-muted); }
.milestones .ms-issue.child { padding-left: 2.4rem; }
.milestones .blabel {
display: inline-block; font-size: .72rem; line-height: 1.4; padding: 0 .35rem;
border: 1px solid var(--bd-border); color: var(--bd-fg); white-space: nowrap;
}
.milestones .blabel.prio { border-color: var(--bd-danger); color: var(--bd-danger); }
.milestones .blabel.muted { color: var(--bd-muted); }
.milestones .blabel.assignee { border-color: var(--accent); }
/* freshness line (the shared "beadsHead" partial), in the same muted small type
the beads view gives it */
.milestones .beads-freshness { font-size: .78rem; color: var(--bd-muted); margin: -.35rem 0 .7rem; }
.milestones .beads-freshness a { color: var(--bd-muted); }
.milestones .beads-freshness code { font-size: .72rem; color: inherit; }
.milestones .ms-empty { color: var(--bd-muted); font-style: italic; }
.milestones .ms-foot { color: var(--bd-muted); font-size: .82rem; margin-top: .5rem; }
</style>
<div class="milestones">
<h2><a href="/~{{.Repo.OwnerName}}/{{.Repo.Name}}">~{{.Repo.OwnerName}}/{{.Repo.Name}}</a> · milestones</h2>
{{template "beadsHead" (dict "Repo" .Repo "Ref" .Ref "Head" .Head)}}
{{template "viewtabs" (dict "Repo" .Repo "Views" .Views "Current" "milestones" "Ref" .Ref)}}
{{/* Every number below this line is a rollup — "1/4 done", "N of M carry no
milestone label" — and arithmetic over the rows that were read renders
exactly like arithmetic over the tracker. A milestone can lose members
outright here, not merely undercount them: membership comes from labels, and
a clip in either table takes an issue off its milestone silently. The
wording and the markup are the detail pane's, because it is the same fact
about the same read; a second phrasing for it would be a second style. It
sits above the rollup rather than under it, and outside the
milestones/no-milestones branch, because "No milestones." over a clipped
read is the reading most easily mistaken for a fact about the tracker. */}}
{{if .Data.Truncated}}
<div class="alert alert-warning">This read was clipped, so what is below may be short.{{if .Data.IssuesClipped}} The tracker holds {{.Data.ShownOf}} issues and only the first of them were read.{{end}}</div>
{{end}}
{{if .Data.Milestones}}
{{range .Data.Milestones}}
<div class="ms">
<div class="ms-head">
<h3 class="ms-title">{{.Name}}</h3>
<span class="ms-bar" title="{{.Pct}}% complete"><span class="ms-fill" style="width: {{.Pct}}%"></span></span>
<span class="ms-counts"><b>{{.Done}}</b>/{{.Total}} done · {{.Open}} open{{if .InProgress}} · {{.InProgress}} in progress{{end}}</span>
</div>
{{range .Heads}}{{template "ms-row" (dict "Repo" $.Repo "Card" . "Class" "head")}}{{end}}
{{range .Epics}}
<a class="ms-issue epic{{if eq .Card.Category "closed"}} done{{end}}" href="/~{{$.Repo.OwnerName}}/{{$.Repo.Name}}/view/beads?issue={{.Card.ID | urlquery}}">
<span class="r-id">{{.Card.ID}}</span>
<span class="r-title">{{.Card.Title}}</span>
{{if .Total}}<span class="r-sub">{{.Done}}/{{.Total}}</span>{{end}}
<span class="r-meta">
{{if .Card.PriorityLabel}}<span class="blabel prio">{{.Card.PriorityLabel}}</span>{{end}}
<span class="blabel">epic</span>
{{if .Card.Assignee}}<span class="blabel assignee">@{{.Card.Assignee}}</span>{{end}}
<span class="blabel muted">{{.Card.Category}}</span>
</span>
</a>
{{range .Children}}{{template "ms-row" (dict "Repo" $.Repo "Card" . "Class" "child")}}{{end}}
{{end}}
{{range .Loose}}{{template "ms-row" (dict "Repo" $.Repo "Card" . "Class" "")}}{{end}}
</div>
{{end}}
{{if .Data.Unlabeled}}<p class="ms-foot">{{.Data.Unlabeled}} of {{.Data.Total}} issues carry no milestone label.</p>{{end}}
{{else}}
<p class="ms-empty">No milestones. Tag issues with a <code>milestone:<name></code> label to group them here.</p>
{{end}}
</div>
{{- end}}
{{define "ms-row" -}}
<a class="ms-issue{{with .Class}} {{.}}{{end}}{{if eq .Card.Category "closed"}} done{{end}}" href="/~{{.Repo.OwnerName}}/{{.Repo.Name}}/view/beads?issue={{.Card.ID | urlquery}}">
<span class="r-id">{{.Card.ID}}</span>
<span class="r-title">{{.Card.Title}}</span>
<span class="r-meta">
{{if .Card.PriorityLabel}}<span class="blabel prio">{{.Card.PriorityLabel}}</span>{{end}}
{{if .Card.Type}}<span class="blabel">{{.Card.Type}}</span>{{end}}
{{if .Card.Assignee}}<span class="blabel assignee">@{{.Card.Assignee}}</span>{{end}}
<span class="blabel muted">{{.Card.Category}}</span>
</span>
</a>
{{- end}}