1{# A review's discussion: approvals, then comments oldest first. A line2 comment quotes its line as it was when written; comments from an older3 revision are outdated. Written with forge comment and forge approve, or4 from this page signed in: the form at the end stays hidden until5 discuss.ts finds who is signed in, and posts the tip the page shows. #}6{% import "ui.njk" as ui %}7{% set u = p.repo.urls %}8{% css %}9ul.approvals { list-style: none; margin: 0; padding: var(--space-2) var(--space-3); border-bottom: var(--hairline) solid var(--border); display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-5); font-size: var(--text-sm); }10ul.approvals .tick { color: var(--pass); }11ul.approvals .stale { color: var(--muted); }12ul.approvals .stale .tick { color: var(--warn); }13ul.approvals .date { font: var(--fw-regular) var(--text-xs) var(--mono); color: var(--muted); }14ul.approvals .date time { font-size: inherit; }15.comment { padding: 0 0 var(--space-1); }16.comment:target { box-shadow: inset var(--bar) 0 0 var(--accent); }17.comment-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-half) var(--space-3); padding: var(--space-3) var(--space-3) 0; font-size: var(--text-sm); }18.comment-head .date { font: var(--fw-regular) var(--text-xs) var(--mono); color: var(--muted); }19.comment-head .date time { font-size: inherit; }20.comment-head .rev { font: var(--fw-regular) var(--text-xs) var(--mono); color: var(--muted); }21.comment .tag { font-size: var(--text-sm); font-weight: var(--fw-medium); color: var(--warn); }22/* From forge critique: the model in the mono, blocking in the fail colour. */23.comment .agent { font: var(--fw-regular) var(--text-xs) var(--mono); color: var(--muted); }24.comment .tag.blocking { color: var(--fail); }25.comment .quote { margin: var(--space-2) var(--space-3) 0; border: var(--hairline) solid var(--border); background: var(--tint); overflow: hidden; }26.comment .quote .where { padding: var(--space-1) var(--space-3); font: var(--fw-regular) var(--text-xs)/1.6 var(--mono); border-bottom: var(--hairline) solid var(--border); overflow-wrap: anywhere; }27.comment .quote pre { margin: 0; padding: var(--space-1) var(--space-3); font: var(--fw-regular) var(--text-code)/1.55 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 4; }28.comment.outdated .quote pre { color: var(--muted); }29.comment .markdown { padding: var(--space-2) var(--space-3); font-size: var(--text-md); line-height: 1.6; }30.comment .markdown > :last-child { margin-bottom: 0; }31/* Sent from this page, until the rebuilt page shows it. */32.comment.pending { border-bottom-style: dashed; }33.comment.pending .text { padding: var(--space-2) var(--space-3); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--muted); }34.discussion .panel-body { border-top: var(--hairline) solid var(--border); }35.discussion > .panel-head + .panel-body { border-top: 0; }36#no-comments { margin: 0; padding: var(--space-3); }37.reply-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-2); font-size: var(--text-sm); }38.reply-bar .grow { flex: 1; }39.reply-status.bad { color: var(--fail); white-space: pre-line; }40{% endcss %}41{% set head %}<b>discussion</b><span class="grow">{{ rv.comments.length | plural('comment') }}{% if rv.approvals.length %}, {{ rv.approvals.length | plural('approval') }}{% endif %}</span>{% endset %}42{% call ui.panel(head, 'discussion', 'id="discussion"') %}43{% if rv.approvals.length %}44<ul class="approvals">45{% for a in rv.approvals %}46<li class="{{ 'current' if a.current else 'stale' }}" data-user="{{ a.user }}"><span class="tick">✔</span> <b>{{ a.user }}</b> approved{% if a.rev %} r{{ a.rev }}{% endif %} <span class="date">({{ a.ts | ago }})</span>{% if not a.current %}, changed since{% endif %}</li>47{% endfor %}48</ul>49{% endif %}50{% if rv.comments.length %}51<ol class="rows comments">52{% for c in rv.comments %}53<li class="comment{% if c.outdated %} outdated{% endif %}" id="comment-{{ c.n }}">54<div class="comment-head"><b>{{ c.user }}</b>{% if c.agent %} <span class="agent" title="written by an agent review (forge critique)">agent {{ c.agent }}</span>{% endif %}{% if c.severity == 'blocking' %} <span class="tag blocking">blocking</span>{% endif %} <a class="date" href="#comment-{{ c.n }}">{{ c.ts | ago }}</a>{% if c.rev %} <span class="rev">r{{ c.rev }}</span>{% endif %}{% if c.outdated %} <span class="tag">outdated</span>{% endif %}{% if c.addressed %} <span class="tag jev-addressed" title="According to Jev, the change since this comment addresses it">likely addressed in r{{ c.addressed }}</span>{% endif %}</div>55{% if c.path %}56{% if c.inDiff %}{% set href = u.reviewDiff(rv.id).href ~ '#comment-' ~ c.n %}{% elif not c.outdated and rv.live %}{% set href = u.blob(rv.branch, c.path).href ~ '#l' ~ c.line %}{% else %}{% set href = '' %}{% endif %}57<div class="quote"><div class="where">{% if href %}<a href="{{ href }}">{{ c.path }}:{{ c.line }}</a>{% else %}{{ c.path }}:{{ c.line }}{% endif %}</div><pre>{{ c.text or '' }}</pre></div>58{% endif %}59<div class="markdown">{{ c.body | reviewMarkdown(p.repo, rv) | safe }}</div>60</li>61{% endfor %}62</ol>63{% else %}64<p class="desc" id="no-comments">No comments yet: <code>forge comment {{ rv.id }}</code> adds one.</p>65{% endif %}66<form class="reply panel-body" id="reply" data-post="{{ u.review(rv.id).href }}" data-at="{{ rv.tip }}" data-author="{{ rv.author or '' }}" hidden>67{{ ui.textarea('comment on #' + rv.id, 'Comment on #' + rv.id + '. Markdown is fine.', 'name="body" maxlength="32768"') }}68<div class="reply-bar"><span class="desc who"></span><span class="grow"></span><span class="reply-status" role="status"></span>{% if rv.live %}{{ ui.button('approve', false, 'data-approve title="approve this tree of #' + rv.id + ', as forge approve ' + rv.id + ' does"') }}{% endif %}{{ ui.button('comment', true, '', 'submit') }}</div>69</form>70{% endcall %}