{{define "content" -}}
<style>
/* Scoped beads styles — inlined because the scss bundle is not rebuilt in dev.
The look deliberately mimics todo.sr.ht (sourcehut's own issue tracker): flat
and square (no radius, no shadows, no gradients), hairline gray borders,
monospace ids, and squared bordered labels. Colours come from CSS variables
that mirror core.sr.ht's Bootstrap palette for the light default and the
prefers-color-scheme: dark variant sourcehut ships, so the view inherits the
host theme. The Mardi Gras lanes survive only as muted accent swatches and a
2px lane cap — never saturated fills — so it reads as sourcehut, not a
separate app. */
.beads {
--lane-rolling: #c9930a; --lane-linedup: #2f9e44;
--lane-stalled: #9c36b5; --lane-past: #868e96;
--bd-bg: #ffffff; --bd-panel: #f2f3f5; --bd-fg: #212529;
--bd-muted: #6c757d; --bd-border: #ced4da; --bd-stripe: rgba(0,0,0,.05);
--bd-danger: #b52a2a;
}
@media (prefers-color-scheme: dark) {
.beads {
--bd-bg: #212529; --bd-panel: #343a40; --bd-fg: #dee2e6;
--bd-muted: #adb5bd; --bd-border: #6c757d; --bd-stripe: rgba(255,255,255,.05);
--bd-danger: #ff6b6b;
}
}
.beads .rolling { --lane: var(--lane-rolling); }
.beads .lined-up { --lane: var(--lane-linedup); }
.beads .stalled { --lane: var(--lane-stalled); }
.beads .past-stand { --lane: var(--lane-past); }
.beads .total { --lane: var(--bd-muted); }
/* a small square colour chip standing in for the lane's Mardi Gras hue */
.beads .swatch {
display: inline-block; width: .7rem; height: .7rem; flex: 0 0 auto;
background: var(--lane); border: 1px solid rgba(0,0,0,.25); vertical-align: -1px;
}
/* summary: one flat hairline-bordered strip, cells divided by hairlines */
.beads-summary { display: flex; flex-wrap: wrap; border: 1px solid var(--bd-border); margin-bottom: 1rem; }
.beads-summary .stat {
flex: 1 1 7rem; min-width: 6.5rem; padding: .35rem .6rem;
border-left: 1px solid var(--bd-border);
}
.beads-summary .stat:first-child { border-left: none; }
.beads-summary .stat .n { font-size: 1.35rem; font-weight: 700; line-height: 1.1; }
.beads-summary .stat .l { display: flex; align-items: center; gap: .35rem; font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; color: var(--bd-muted); }
/* lanes: flat squared columns with a 2px accent cap */
.beads-lanes { display: flex; gap: .75rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.beads-lane { flex: 1 0 15rem; min-width: 15rem; }
.beads-lane .lane-head {
display: flex; align-items: center; gap: .4rem; padding: .3rem .5rem; font-weight: 700;
background: var(--bd-panel); color: var(--bd-fg);
border: 1px solid var(--bd-border); border-top: 2px solid var(--lane);
}
.beads-lane .lane-head .count { margin-left: auto; color: var(--bd-muted); font-weight: 400; font-size: .85rem; }
.beads-lane .lane-body { border: 1px solid var(--bd-border); border-top: none; }
/* issue rows: todo.sr.ht ticket-list feel — hairline separated, no radius */
.bead-row { display: block; padding: .3rem .5rem; text-decoration: none; color: var(--bd-fg); border-top: 1px solid var(--bd-border); }
.bead-row:first-child { border-top: none; }
.bead-row:hover { background: var(--bd-stripe); text-decoration: none; color: var(--bd-fg); }
.bead-row .r-id { font-family: monospace; font-size: .72rem; color: var(--bd-muted); }
.bead-row .r-title { display: block; margin: .05rem 0 .2rem; color: var(--bd-fg); }
.bead-row .r-meta { display: flex; flex-wrap: wrap; gap: .25rem; align-items: center; }
.beads-empty { color: var(--bd-muted); font-size: .8rem; font-style: italic; padding: .35rem .5rem; }
/* labels: sourcehut .label — squared, hairline-bordered, transparent */
.blabel {
display: inline-block; font-size: .7rem; line-height: 1.5; padding: 0 .3rem; white-space: nowrap;
border: 1px solid var(--bd-border); background: transparent; color: var(--bd-fg);
}
.blabel.prio { border-color: var(--lane-rolling); color: var(--bd-fg); font-weight: 700; }
.blabel.assignee { border-color: var(--lane-linedup); }
.blabel.block { border-color: var(--bd-danger); color: var(--bd-danger); }
.blabel.muted { color: var(--bd-muted); }
/* detail pane */
.bead-detail .ticket-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; margin: 0 0 .5rem; }
.bead-detail .ticket-title code { font-size: 1.1rem; }
.bead-detail .lane-label { border: 1px solid var(--lane); color: var(--bd-fg); padding: 0 .4rem; font-size: .75rem; }
.bead-detail .lane-label .swatch { margin-right: .3rem; }
.bead-detail .field-label { color: var(--bd-muted); font-weight: 600; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; }
.bead-detail table td.field-label { white-space: nowrap; width: 9rem; vertical-align: middle; }
.bead-detail pre.field-body {
white-space: pre-wrap; background: var(--bd-panel); border: 1px solid var(--bd-border);
color: var(--bd-fg); padding: .5rem; margin: .25rem 0 1rem;
}
.bead-deplist { list-style: none; padding-left: 0; margin-bottom: 0; }
.bead-deplist li { padding: .2rem 0; border-top: 1px solid var(--bd-border); }
.bead-deplist li:first-child { border-top: none; }
.bead-deplist code { font-size: .8rem; }
.bead-comment { border: 1px solid var(--bd-border); border-left: 2px solid var(--lane-linedup); padding: .4rem .6rem; margin-bottom: .5rem; }
.bead-comment .c-head { font-size: .8rem; color: var(--bd-muted); margin-bottom: .2rem; }
.bead-comment .c-body { white-space: pre-wrap; color: var(--bd-fg); }
</style>
<div class="beads">
<h2><a href="/~{{.Repo.OwnerName}}/{{.Repo.Name}}">~{{.Repo.OwnerName}}/{{.Repo.Name}}</a> · parade</h2>
{{template "viewtabs" (dict "Repo" .Repo "Views" .Views "Current" "beads" "Ref" .Ref)}}
{{if eq .Data.Mode "detail"}}
{{/* ---------------- detail pane ---------------- */}}
<p><a href="/~{{.Repo.OwnerName}}/{{.Repo.Name}}/view/beads">← Back to the parade</a></p>
{{with .Data.Issue}}
<div class="bead-detail">
<h2 class="ticket-title">
<code>{{.ID}}</code> <span>{{.Title}}</span>
<span class="lane-label" style="--lane: {{.Accent}};"><span class="swatch"></span>{{.Lane}}</span>
</h2>
<div class="r-meta mb-3">
<span class="blabel muted">status: {{.Status}}</span>
{{if .Priority}}<span class="blabel prio">P{{.Priority}}</span>{{end}}
{{if .IssueType}}<span class="blabel">{{.IssueType}}</span>{{end}}
{{if .Assignee}}<span class="blabel assignee">@{{.Assignee}}</span>{{end}}
{{range .Labels}}<span class="blabel muted">{{.}}</span>{{end}}
</div>
<table class="table table-sm">
<tbody>
{{if .CreatedBy}}<tr><td class="field-label">Created by</td><td>{{.CreatedBy}}</td></tr>{{end}}
{{if .Owner}}<tr><td class="field-label">Owner</td><td>{{.Owner}}</td></tr>{{end}}
{{if .CreatedAt}}<tr><td class="field-label">Created</td><td>{{.CreatedAt}}</td></tr>{{end}}
{{if .UpdatedAt}}<tr><td class="field-label">Updated</td><td>{{.UpdatedAt}}</td></tr>{{end}}
{{if .ClosedAt}}<tr><td class="field-label">Closed</td><td>{{.ClosedAt}}</td></tr>{{end}}
</tbody>
</table>
{{if .Description}}<div class="field-label">Description</div><pre class="field-body">{{.Description}}</pre>{{end}}
{{if .Design}}<div class="field-label">Design</div><pre class="field-body">{{.Design}}</pre>{{end}}
{{if .AcceptanceCriteria}}<div class="field-label">Acceptance criteria</div><pre class="field-body">{{.AcceptanceCriteria}}</pre>{{end}}
{{if .Notes}}<div class="field-label">Notes</div><pre class="field-body">{{.Notes}}</pre>{{end}}
</div>
<div class="row">
<div class="col-md-6">
<h4>Depends on</h4>
{{if $.Data.DependsOn}}
<ul class="bead-deplist">
{{range $.Data.DependsOn}}
<li>
<a href="/~{{$.Repo.OwnerName}}/{{$.Repo.Name}}/view/beads?issue={{.IssueID | urlquery}}"><code>{{.IssueID}}</code></a>
{{if .Title}}— {{.Title}}{{end}}
<span class="blabel muted">{{.Type}}</span>
{{if .Closed}}<span class="blabel muted">closed</span>{{else}}<span class="blabel block">{{.Status}}</span>{{end}}
</li>
{{end}}
</ul>
{{else}}<p class="beads-empty">No outgoing dependencies.</p>{{end}}
</div>
<div class="col-md-6">
<h4>Depended on by</h4>
{{if $.Data.DependedOnBy}}
<ul class="bead-deplist">
{{range $.Data.DependedOnBy}}
<li>
<a href="/~{{$.Repo.OwnerName}}/{{$.Repo.Name}}/view/beads?issue={{.IssueID | urlquery}}"><code>{{.IssueID}}</code></a>
{{if .Title}}— {{.Title}}{{end}}
<span class="blabel muted">{{.Type}}</span>
</li>
{{end}}
</ul>
{{else}}<p class="beads-empty">Nothing depends on this issue.</p>{{end}}
</div>
</div>
<h4>Comments</h4>
{{if $.Data.Comments}}
{{range $.Data.Comments}}
<div class="bead-comment">
<div class="c-head"><strong>{{.Author}}</strong> {{if .CreatedAt}}· {{.CreatedAt}}{{end}}</div>
<div class="c-body">{{.Text}}</div>
</div>
{{end}}
{{else}}<p class="beads-empty">No comments.</p>{{end}}
{{else}}
<div class="alert alert-warning">Issue not found.</div>
{{end}}
{{else}}
{{/* ---------------- board ---------------- */}}
{{if .Data.Truncated}}
<div class="alert alert-warning">Showing the first {{.Data.Total}} of {{.Data.ShownOf}} issues.</div>
{{end}}
<div class="beads-summary">
<div class="stat rolling"><div class="n">{{.Data.Counts.Rolling}}</div><div class="l"><span class="swatch"></span>Rolling</div></div>
<div class="stat lined-up"><div class="n">{{.Data.Counts.LinedUp}}</div><div class="l"><span class="swatch"></span>Lined Up</div></div>
<div class="stat stalled"><div class="n">{{.Data.Counts.Stalled}}</div><div class="l"><span class="swatch"></span>Stalled</div></div>
<div class="stat past-stand"><div class="n">{{.Data.Counts.PastStand}}</div><div class="l"><span class="swatch"></span>Past Stand</div></div>
<div class="stat total"><div class="n">{{.Data.Counts.Total}}</div><div class="l">Total</div></div>
</div>
<div class="beads-lanes">
{{range .Data.Lanes}}
<div class="beads-lane {{.Slug}}">
<div class="lane-head">
<span class="swatch"></span>{{.Name}}<span class="count">{{len .Issues}}</span>
</div>
<div class="lane-body">
{{range .Issues}}
<a class="bead-row" href="/~{{$.Repo.OwnerName}}/{{$.Repo.Name}}/view/beads?issue={{.ID | urlquery}}">
<span class="r-id">{{.ID}}</span>
<span class="r-title">{{.Title}}</span>
<span class="r-meta">
{{if .PriorityLabel}}<span class="blabel prio">{{.PriorityLabel}}</span>{{end}}
{{if .Type}}<span class="blabel">{{.Type}}</span>{{end}}
{{if .Assignee}}<span class="blabel assignee">@{{.Assignee}}</span>{{end}}
{{range .Labels}}<span class="blabel muted">{{.}}</span>{{end}}
{{if .BlockedBy}}<span class="blabel block">🚧 {{.BlockedBy}}</span>{{end}}
{{if .Blocks}}<span class="blabel muted">blocks {{.Blocks}}</span>{{end}}
</span>
</a>
{{else}}
<p class="beads-empty">Empty lane.</p>
{{end}}
</div>
</div>
{{end}}
</div>
{{end}}
</div>
{{- end}}