de505df5 Andy McCall 1 month ago ↶
1
{% extends "base.html" %}
2
{% import "repo_tabs.html" as m %}
38833ed7 Andy McCall 1 month ago ↶
3
{% block title %}Proposal #{{ p.number }} {{ p.title }} · {{ repo.owner }}/{{ repo.name }}{% endblock %}
de505df5 Andy McCall 1 month ago ↶
4
{% block bodyattrs %} data-repo="{{ repo.url }}"{% endblock %}
5
{% block tabs %}{% call m::tabs(repo, "proposals") %}{% endblock %}
a1b98695 Andy McCall 1 month ago ↶
6
{% block mainclass %}{% if tab != "conversation" %} class="wide"{% endif %}{% endblock %}
de505df5 Andy McCall 1 month ago ↶
7
{% block content %}
ffb67a81 Andy McCall 21 days ago
8
{% include "repo_backdrop.html" %}
9
< div data-backdrop-avoid >
4f37afcc Andy McCall 1 month ago ↶
10
< div class = " head " > < h1 > {{ p.title }}{% if can_edit %} < a class = " head-pencil " href = " #edit" title = " Edit title, target and description" aria-label = " Edit title, target and description" > < svg viewBox = " 0 0 16 16" width = " 16" height = " 16" aria-hidden = " true" > < path fill = " currentColor" d = " M11.01 1.31a1.75 1.75 0 0 1 2.48 0l1.2 1.2a1.75 1.75 0 0 1 0 2.48L5.1 14.6a1.75 1.75 0 0 1-.8.46l-3.13.88a.75.75 0 0 1-.92-.92l.88-3.13a1.75 1.75 0 0 1 .46-.8Zm1.42 1.06a.25.25 0 0 0-.36 0L10.5 3.94 12.06 5.5l1.57-1.57a.25.25 0 0 0 0-.36ZM11 6.56 9.44 5 3.15 11.3a.25.25 0 0 0-.06.11l-.55 1.95 1.95-.55a.25.25 0 0 0 .11-.06Z" /> </ svg > </ a > {% endif %} < small class = " px kind-proposal " > proposal #{{ p.number }}</ small >
de505df5 Andy McCall 1 month ago ↶
11
< small >
12
< span class = " tag{% if p.state == " landed" %} head {% else if p.state = = "closed" %} bad {% endif %}"> {{ p.state }}</ span >
13
{{ p.author }} wants < span class = " lbl lbl-bm " > {{ source_label }}</ span > into < a class = " lbl lbl-bm " href = " {{ repo.url }}/tree/{{ p.target }}" > {{ p.target }}</ a >
14
· {{ changes.len()|plural("change", "changes") }} · opened < time title = " {{ p.created_ms|when_i }}" > {{ p.created_ms|ago_i }}</ time >
15
</ small >
16
</ h1 > </ div >
38833ed7 Andy McCall 1 month ago ↶
17
{% if !closes.is_empty() %}< p class = " chain " aria-label = " The chain of work" > {% for c in closes %}< a class = " chain-link kind-issue st-{{ c.state }} " href = " {{ c.url }}" title = " {{ c.title }}" > < span class = " chain-kind " > issue #{{ c.number }}</ span > {{ c.title }} < span class = " chain-state " > {{ c.state }}</ span > </ a > {% if !loop.last %} < span class = " chain-and " > +</ span > {% endif %}{% endfor %}
18
< span class = " chain-arrow " > →</ span > < span class = " chain-link kind-proposal st-{{ p.state }} " > < span class = " chain-kind " > proposal #{{ p.number }}</ span > < span class = " chain-state " > {{ p.state }}</ span > </ span >
19
< span class = " chain-arrow " > →</ span > < a class = " lbl lbl-bm " href = " {{ repo.url }}/tree/{{ p.target }}" > {{ p.target }}</ a > {% if p.state == "landed" %} < span class = " chain-state st-landed " > landed</ span > {% endif %}</ p > {% endif %}
de505df5 Andy McCall 1 month ago ↶
20
{% if let Some(n) = notice %}< p class = " note " > {{ n }}</ p > {% endif %}
21
{% if let Some(e) = error %}< p class = " error " > {{ e }}</ p > {% endif %}
217814c2 Andy McCall 8 days ago ↶
22
{% if conflicted %}< div class = " strip bad " > Landing would carry {{ conflicts.len()|plural("conflict", "conflicts") }}. Any collaborator can push a resolution change to the source; the proposal follows it. < a href = " ?tab=files#conflicts" > See them</ a > {% if can_resolve %}< form class = " inline " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/resolve" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" class = " small " title = " Brings {{ p.target }} into {{ p.source_bookmark }} here, as oxy merge would, then opens the resolver on the first conflicted file" > RESOLVE ON THE WEB</ button > </ form > {% endif %}</ div > {% endif %}
de505df5 Andy McCall 1 month ago ↶
23
{% if landed_already & & p.state != "landed" %}< div class = " strip warn " > Every change is already in {{ p.target }}.</ div > {% endif %}
668b2ad7 Andy McCall 9 days ago ↶
24
{% if p.state == "landed" & & !since_landing.is_empty() %}< div class = " strip warn " > < code > {{ p.source_bookmark }}</ code > has {{ since_landing.len()|plural("change", "changes") }} since this landed, not on < code > {{ p.target }}</ code > : {% for c in since_landing %}< a href = " {{ repo.url }}/change/{{ c.hash }}" > < code > {{ c.hash|short }}</ code > </ a > {{ c.title }}{% if !loop.last %}, {% endif %}{% endfor %}. < a href = " {{ repo.url }}/proposals/new?source={{ p.source_bookmark }}& amp; target={{ p.target }}" > Open a new proposal</ a > to take {% if since_landing.len() == 1 %}it{% else %}them{% endif %} there.</ div > {% endif %}
de505df5 Andy McCall 1 month ago ↶
25
< div class = " bar " >
26
< span class = " modes " >
27
< a href = " ?tab=conversation" {% if tab = = "conversation" %} class = " on " {% endif %}> conversation ({{ comments.len() + reviews.len() }})</ a >
28
< a href = " ?tab=changes" {% if tab = = "changes" %} class = " on " {% endif %}> changes ({{ changes.len() }})</ a >
29
< a href = " ?tab=files" {% if tab = = "files" %} class = " on " {% endif %}> files ({{ files.len() }})</ a >
30
</ span >
31
< span class = " small mute " > projected state < code title = " {{ state_hash }}" > {{ state_hash|short }}</ code > · {{ heads.len()|plural("head", "heads") }} · < a href = " {{ repo.url }}/tree/{% if let Some(h) = heads.first() %}{{ h }}{% endif %}" > browse</ a > · < a href = " {{ api_url }}" > json</ a > </ span >
32
</ div >
33
34
{% if tab == "changes" %}
35
{% if changes.is_empty() %}< p class = " empty " > Nothing to add: the target already has everything.</ p > {% endif %}
36
{% for c in changes %}{% call m::change_row(repo, c) %}{% endfor %}
37
38
{% else if tab == "files" %}
39
{% if !conflicts.is_empty() %}
40
< section class = " panel " id = " conflicts " > < h2 > Conflicts the landing would carry</ h2 >
41
{% for c in conflicts %}< p > < strong > {{ c.path }}</ strong > < span class = " tag bad " > {{ c.kind }}</ span > </ p > {% if let Some(t) = c.marked %}< pre class = " marked " > {{ t }}</ pre > {% endif %}{% endfor %}
42
</ section >
43
{% endif %}
9df855f2 Andy McCall 1 month ago ↶
44
{% if !files.is_empty() %}{% call m::diff_bar(ignore_ws, "tab=files& ") %}{% endif %}
de505df5 Andy McCall 1 month ago ↶
45
{% for f in files %}
9df855f2 Andy McCall 1 month ago ↶
46
< details class = " file-diff " id = " {{ f.anchor }} " {% if f.lines.len () <= 400 %} open {% endif %}>
47
< summary > < span class = " kind kind-{{ f.kind }} " > {{ f.kind }}</ span > < strong > {{ f.path }}</ strong > < span class = " counts " > < span class = " add " > +{{ f.added }}</ span > < span class = " del " > −{{ f.removed }}</ span > </ span > </ summary >
de505df5 Andy McCall 1 month ago ↶
48
{% if f.lines.is_empty() %}< p class = " empty " > No line changes.</ p > {% else %}
49
< table class = " code diff " >
50
{% for l in f.lines %}
51
< tr class = " {{ l.kind.class() }} " >
52
< td class = " n " > {% if let Some(n) = l.old_no %}{{ n }}{% endif %}</ td >
53
< td class = " n " > {% if let Some(n) = l.new_no %}{{ n }}{% endif %}</ td >
54
< td class = " s " > {{ l.kind.sign() }}</ td >
9df855f2 Andy McCall 1 month ago ↶
55
< td class = " t " > {% call m::line_text(l) %}{% if let Some(n) = l.new_no %}{% if shell.user.is_some() & & p.is_open() %}< details class = " linecomment " > < summary title = " Comment on this line" > +</ summary >
de505df5 Andy McCall 1 month ago ↶
56
< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/comment" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " path" value = " {{ f.path }}" > < input type = " hidden" name = " line" value = " {{ n }}" > < textarea name = " body" rows = " 3" required placeholder = " Anchored to this line's atom; it follows the line through rebases." > </ textarea > < button type = " submit" > COMMENT</ button > </ form > </ details > {% endif %}{% endif %}</ td >
57
</ tr >
58
{% if let Some(n) = l.new_no %}{% if let Some(idxs) = placed.get(& crate::web::proposals::key(& f.path, n)) %}
59
< tr class = " ctx " > < td colspan = " 4" class = " thread " >
60
{% for i in idxs %}{% let c = comments[*i] %}
61
< div class = " comment{% if c.row.resolved %} resolved{% endif %} " > < span class = " who " > {{ c.row.author }}</ span > < time class = " mute " title = " {{ c.row.created_ms|when_i }}" > {{ c.row.created_ms|ago_i }}</ time > {% if c.row.resolved %} < span class = " tag " > resolved</ span > {% endif %}
62
{% if shell.user.is_some() %}< form class = " tiny " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/comment/{{ c.row.id }}/resolve" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button > {% if c.row.resolved %}unresolve{% else %}resolve{% endif %}</ button > </ form > {% endif %}
63
< div class = " body " > {{ c.body_html|safe }}</ div > </ div >
64
{% endfor %}
65
</ td > </ tr >
66
{% endif %}{% endif %}
67
{% endfor %}
68
</ table >
69
{% endif %}
9df855f2 Andy McCall 1 month ago ↶
70
</ details >
de505df5 Andy McCall 1 month ago ↶
71
{% endfor %}
72
{% if !outdated.is_empty() %}
73
< section class = " panel " > < h2 > Comments on lines that are gone</ h2 >
74
{% for i in outdated %}{% let c = comments[*i] %}< div class = " comment " > < span class = " who " > {{ c.row.author }}</ span > on < code > {{ c.row.path }}</ code > < time class = " mute " > {{ c.row.created_ms|ago_i }}</ time > < div class = " body " > {{ c.body_html|safe }}</ div > </ div > {% endfor %}
75
</ section >
76
{% endif %}
77
78
{% else %}
9198231c Andy McCall 1 month ago ↶
79
< div class = " side-layout conversation " >
a1b98695 Andy McCall 1 month ago ↶
80
< div class = " timeline " >
81
< div class = " tl-item " >
82
< img class = " tl-avatar " src = " /avatar/{{ p.author }}" alt = " " width = " 36" height = " 36" >
83
< div class = " tl-card " >
4f37afcc Andy McCall 1 month ago ↶
84
< div class = " tl-head " id = " description " > < span > < a href = " /{{ p.author }}" > < strong > {{ p.author }}</ strong > </ a > opened this proposal < a class = " tl-when " href = " #description" > < time title = " {{ p.created_ms|when_i }}" > {{ p.created_ms|ago_i }}</ time > </ a > </ span >
85
< details class = " menu tl-menu " > < summary class = " dots " title = " More" aria-label = " More options" > ···</ summary >
86
< div class = " menu-pop menu-actions " >
87
< button type = " button" class = " menu-action " data-copy-link = " #description" > Copy link</ button >
88
< button type = " button" class = " menu-action " data-copy-text = " {{ p.body }}" > Copy Markdown</ button >
89
{% if can_edit %}< hr > < a class = " menu-action " href = " #edit" > Edit</ a > {% endif %}
90
</ div > </ details > </ div >
91
< div class = " tl-view " > {% if p.body.is_empty() %}< p class = " tl-empty mute " > No description.</ p > {% else %}< article class = " doc tl-doc " > {{ body_html|safe }}</ article > {% endif %}</ div >
92
{% if can_edit %}
7d2eb590 Andy McCall 1 month ago ↶
93
< form class = " tl-editform{% if draft.is_some() %} open{% endif %} " id = " edit " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/edit" >
4f37afcc Andy McCall 1 month ago ↶
94
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
95
< div class = " edit-row " > < label for = " etitle" > Title</ label > < input type = " text" id = " etitle " name = " title" value = " {{ p.title }}" > </ div >
96
< div class = " edit-row " > < label for = " etarget" > Into bookmark</ label > < input type = " text" id = " etarget " name = " target" value = " {{ p.target }}" > </ div >
7d2eb590 Andy McCall 1 month ago ↶
97
< textarea id = " ebody " name = " body" class = " md " rows = " 10" aria-label = " Description (Markdown)" > {% if let Some(d) = draft %}{{ d }}{% else %}{{ p.body }}{% endif %}</ textarea >
98
{% if draft.is_some() %}< p class = " small mute " > Drafted by an assistant; nothing is saved until you update the proposal.</ p > {% endif %}
119fd17d Andy McCall 18 days ago ↶
99
< div class = " tl-actions " > < a class = " go " href = " #description" > Cancel</ a > {% for a in assistants %}< button type = " submit" class = " agent-btn " formaction = " {{ repo.url }}/proposal/{{ p.number }}/draft" data-stream = " {{ repo.url }}/proposal/{{ p.number }}/draft/stream" formnovalidate name = " assistant" value = " {{ a }}" title = " {{ a }}, an AI assistant, drafts the description from the changes; you edit it before saving" > < img src = " /avatar/agent/{{ a }}" alt = " " width = " 16" height = " 16" > Draft with {{ a }}</ button > {% endfor %}< button type = " submit" class = " primary-btn " > Update proposal</ button > </ div >
4f37afcc Andy McCall 1 month ago ↶
100
</ form >
101
{% endif %}
a1b98695 Andy McCall 1 month ago ↶
102
</ div >
103
</ div >
b5a0a69b Andy McCall 20 days ago ↶
104
< div class = " tl-live " data-live = " {{ live_topic }}" data-fragment = " timeline" >
a1b98695 Andy McCall 1 month ago ↶
105
{% for t in timeline %}
106
{% if t.kind == "comment" %}{% let c = comments[t.index] %}
107
< div class = " tl-item " >
108
< img class = " tl-avatar " src = " /avatar/{{ c.row.author }}" alt = " " width = " 36" height = " 36" >
109
< div class = " tl-card " >
ffb67a81 Andy McCall 21 days ago
110
< div class = " tl-head " > < a href = " /{{ c.row.author }}" > < strong > {{ c.row.author }}</ strong > </ a > {% if !c.row.via.is_empty() %} < span class = " mute " aria-hidden = " true" > ↳</ span > < span class = " tag " title = " An AI assistant wrote this for {{ c.row.author }}, who asked it" > agent</ span > < img class = " avatar xs " src = " /avatar/agent/{{ c.row.via }}" alt = " " width = " 16" height = " 16" > < strong > {{ c.row.via }}</ strong > {% endif %} commented < time title = " {{ c.row.created_ms|when_i }}" > {{ c.row.created_ms|ago_i }}</ time > </ div >
a1b98695 Andy McCall 1 month ago ↶
111
< div class = " tl-text " > {{ c.body_html|safe }}</ div >
112
</ div >
113
</ div >
114
{% else if t.kind == "review" %}{% let r = reviews[t.index] %}
115
< div class = " tl-item review-{{ r.row.verdict }} " >
116
< img class = " tl-avatar " src = " /avatar/{{ r.row.author }}" alt = " " width = " 36" height = " 36" >
117
< div class = " tl-card " >
206c1fc1 Andy McCall 1 month ago ↶
118
< div class = " tl-head " > < a href = " /{{ r.row.author }}" > < strong > {{ r.row.author }}</ strong > </ a > {% if !r.row.via.is_empty() %} < span class = " mute " aria-hidden = " true" > ↳</ span > < span class = " tag " title = " An AI assistant answered for {{ r.row.author }}, who asked it" > agent</ span > < img class = " avatar xs " src = " /avatar/agent/{{ r.row.via }}" alt = " " width = " 16" height = " 16" > < strong > {{ r.row.via }}</ strong > {% endif %} {% if r.row.verdict == "approve" %}{% if r.sealed %}< span class = " sig sig-valid " > ◈</ span > approved and sealed{% else %}< span class = " sig sig-unsigned " > ○</ span > approved, seal pending{% endif %}{% else if r.row.verdict == "changes" %}< span class = " sig sig-invalid " > ✘</ span > requested changes{% else %}reviewed{% endif %} < time title = " {{ r.row.created_ms|when_i }}" > {{ r.row.created_ms|ago_i }}</ time > </ div >
a1b98695 Andy McCall 1 month ago ↶
119
{% if !r.row.body.is_empty() %}< div class = " tl-text " > {{ r.body_html|safe }}</ div > {% endif %}
120
</ div >
121
</ div >
122
{% else %}{% let e = events[t.index] %}
6ae3fa0a Andy McCall 22 days ago ↶
123
< p class = " tl-event " > < span class = " tl-dot " > </ span > < img class = " tl-mini " src = " /avatar/{{ e.actor }}" alt = " " width = " 18" height = " 18" > < strong > {{ e.actor }}</ strong > {{ e.event }}{% if e.event.ends_with("by change") & & !e.detail.is_empty() %}{% let hash = e.detail.split(' ').next().unwrap_or("") %} < a href = " {{ repo.url }}/change/{{ hash }}" > < code > {{ hash }}</ code > </ a > {{ e.detail[hash.len()..] }}{% else if !e.detail.is_empty() %}: < span title = " {{ e.detail }}" > {{ e.detail|state_label }}</ span > {% endif %} < time class = " mute " title = " {{ e.at_ms|when_i }}" > {{ e.at_ms|ago_i }}</ time > </ p >
a1b98695 Andy McCall 1 month ago ↶
124
{% endif %}
de505df5 Andy McCall 1 month ago ↶
125
{% endfor %}
4f37afcc Andy McCall 1 month ago ↶
126
{% if p.state == "landed" %}
127
< div class = " tl-item tl-outcome landed " >
128
< span class = " tl-badge " aria-hidden = " true" > < svg viewBox = " 0 0 16 16" width = " 18" height = " 18" > < path fill = " currentColor" d = " M5.45 5.15A3.5 3.5 0 0 0 8.75 7.5h1.94a2.25 2.25 0 1 1 0 1.5H8.75a5 5 0 0 1-3.5-1.43v3.04a2.25 2.25 0 1 1-1.5 0V5.39a2.25 2.25 0 1 1 1.7-.24ZM4.5 2.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25-4.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z" /> </ svg > </ span >
129
< div class = " tl-card outcome-card " >
130
< div > < strong > Proposal landed</ strong >
668b2ad7 Andy McCall 9 days ago ↶
131
< p class = " small " > {% if !p.landed_by.is_empty() %}< a href = " /{{ p.landed_by }}" > {{ p.landed_by }}</ a > landed{% else %}Landed{% endif %}{% if !changes.is_empty() %} {{ changes.len()|plural("change", "changes") }}{% endif %} into < a class = " lbl lbl-bm " href = " {{ repo.url }}/tree/{{ p.target }}" > {{ p.target }}</ a > {% if p.landed_ms > 0 %} < time title = " {{ p.landed_ms|when_i }}" > {{ p.landed_ms|ago_i }}</ time > {% endif %}.{% if let Some(b) = deletable_source %} The < code > {{ b }}</ code > bookmark can be safely deleted.{% endif %}</ p > </ div >
16626c0d Andy McCall 23 days ago ↶
132
{% if let Some(b) = deletable_source %}< form method = " post" action = " {{ repo.url }}/bookmarks/delete" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " bookmark" value = " {{ b }}" > < button type = " submit" data-busy = " Deleting" > Delete bookmark</ button > </ form > {% endif %}
4f37afcc Andy McCall 1 month ago ↶
133
</ div >
134
</ div >
135
{% else if p.state == "closed" %}
136
< div class = " tl-item tl-outcome closed " >
137
< span class = " tl-badge " aria-hidden = " true" > ✕</ span >
138
< div class = " tl-card outcome-card " > < div > < strong > Proposal closed</ strong > < p class = " small " > It was not landed{% if can_edit %}; reopen it below to carry on{% endif %}.</ p > </ div > </ div >
139
</ div >
140
{% endif %}
b5a0a69b Andy McCall 20 days ago ↶
141
{% for a in pending %}< p class = " tl-event tl-pending " role = " status" > < span class = " tl-dot " > </ span > < img class = " tl-mini " src = " /avatar/agent/{{ a }}" alt = " " width = " 18" height = " 18" > < strong > {{ a }}</ strong > is answering… < span class = " mute small " > this updates by itself</ span > </ p > {% endfor %}
142
</ div >
a1b98695 Andy McCall 1 month ago ↶
143
{% if let Some(me) = shell.user %}
4f37afcc Andy McCall 1 month ago ↶
144
{% if can_edit & & (p.state == "draft" || p.is_open() || p.state == "closed") %}< form id = " pstate " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/state" hidden > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > </ form > {% endif %}
a1b98695 Andy McCall 1 month ago ↶
145
< div class = " tl-item " >
146
< img class = " tl-avatar " src = " /avatar/{{ me }}" alt = " " width = " 36" height = " 36" >
4f37afcc Andy McCall 1 month ago ↶
147
< div class = " tl-card tl-compose " >
a1b98695 Andy McCall 1 month ago ↶
148
< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/comment" >
149
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
4f37afcc Andy McCall 1 month ago ↶
150
< p class = " compose-title " > Add a comment</ p >
151
< textarea id = " body " name = " body" class = " md " rows = " 4" required aria-label = " Comment (Markdown)" > </ textarea >
152
< div class = " compose-actions " >
153
{% if can_edit %}
154
{% if p.state == "draft" %}< button type = " submit" form = " pstate" name = " action" value = " ready" formnovalidate > Ready for review</ button > {% endif %}
155
{% if p.state == "open" %}< button type = " submit" form = " pstate" name = " action" value = " draft" formnovalidate > Back to draft</ button > {% endif %}
16626c0d Andy McCall 23 days ago ↶
156
{% if p.is_open() %}< button type = " submit" form = " pstate" name = " action" value = " close" class = " danger-btn " formnovalidate data-busy = " Closing" > Close proposal</ button > {% endif %}
157
{% if p.state == "closed" %}< button type = " submit" form = " pstate" name = " action" value = " reopen" formnovalidate data-busy = " Reopening" > Reopen proposal</ button > {% endif %}
4f37afcc Andy McCall 1 month ago ↶
158
{% endif %}
159
< button type = " submit" class = " primary-btn " > Comment</ button >
160
</ div >
a1b98695 Andy McCall 1 month ago ↶
161
</ form >
162
</ div >
163
</ div >
de505df5 Andy McCall 1 month ago ↶
164
{% if p.is_open() %}
4f37afcc Andy McCall 1 month ago ↶
165
< div class = " tl-item " >
166
< span class = " tl-badge review " aria-hidden = " true" > ◈</ span >
167
< form class = " tl-card tl-compose " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/review" >
a1b98695 Andy McCall 1 month ago ↶
168
< input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
4f37afcc Andy McCall 1 month ago ↶
169
< p class = " compose-title " > Review this proposal</ p >
170
< textarea id = " rbody " name = " body" rows = " 2" placeholder = " Leave a summary (optional)" aria-label = " Review summary" > </ textarea >
171
< div class = " compose-actions " >
172
< div class = " segmented " role = " radiogroup" aria-label = " Verdict" >
173
< label > < input type = " radio" name = " verdict" value = " comment" checked > < span > Comment</ span > </ label >
174
< label > < input type = " radio" name = " verdict" value = " approve" > < span > Approve</ span > </ label >
175
< label > < input type = " radio" name = " verdict" value = " changes" > < span > Request changes</ span > </ label >
176
</ div >
16626c0d Andy McCall 23 days ago ↶
177
< button type = " submit" class = " primary-btn " data-busy = " Submitting" > Submit review</ button >
4f37afcc Andy McCall 1 month ago ↶
178
</ div >
179
< p class = " compose-note " > An approval counts once you seal the changes from a client: < code > oxy seal --proposal {{ shell.base_url }}{{ repo.url }}/proposal/{{ p.number }}</ code > </ p >
a1b98695 Andy McCall 1 month ago ↶
180
</ form >
4f37afcc Andy McCall 1 month ago ↶
181
</ div >
182
{% endif %}
a1b98695 Andy McCall 1 month ago ↶
183
{% endif %}
de505df5 Andy McCall 1 month ago ↶
184
</ div >
8e96499f Andy McCall 1 month ago ↶
185
< aside class = " side side-sections " >
b5a0a69b Andy McCall 20 days ago ↶
186
< section class = " side-sec " data-live = " {{ live_topic }} {{ repo_topic }}" data-fragment = " landing" >
8e96499f Andy McCall 1 month ago ↶
187
< div class = " side-head " > < h3 > Landing</ h3 > </ div >
de505df5 Andy McCall 1 month ago ↶
188
{% if p.state == "landed" %}< p > < span class = " sig sig-valid " > ✔</ span > landed{% if !p.landed_state.is_empty() %} at < code > {{ p.landed_state|short }}</ code > {% endif %}</ p >
0b20f1ec Andy McCall 1 month ago ↶
189
{% if !p.landed_by.is_empty() %}< p class = " small mute " > by < a href = " /{{ p.landed_by }}" > {{ p.landed_by }}</ a > , {{ p.landed_via }}, {{ p.landed_ms|ago }}</ p > {% endif %}
de505df5 Andy McCall 1 month ago ↶
190
{% else if p.state == "closed" %}< p class = " mute " > closed</ p >
191
{% else %}
0b20f1ec Andy McCall 1 month ago ↶
192
{% if blockers.is_empty() %}< p > < span class = " sig sig-valid " > ✔</ span > ready to land</ p >
c7067c7b Andy McCall 15 days ago
193
{% if requirements.required_approvals == 0 & & approvals.is_empty() %}< p class = " small warn-text " > No approval yet, and this repository's < a href = " {{ repo.url }}/settings/landing" > landing rules</ a > require none.</ p > {% endif %}
0b20f1ec Andy McCall 1 month ago ↶
194
{% else %}< ul class = " plain small " > {% for b in blockers %}< li > < span class = " sig sig-invalid " > ✘</ span > {{ b }}</ li > {% endfor %}</ ul > {% endif %}
de505df5 Andy McCall 1 month ago ↶
195
{% if can_land %}
a7aa76ac Andy McCall 18 days ago
196
{% if hub_can_land %}< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/land" {% if approvals.is_empty () %} data-confirm = " Land #{{ p.number }} without any approval?" {% endif %}> < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" data-busy = " LANDING" > LAND</ button > </ form >
de505df5 Andy McCall 1 month ago ↶
197
{% else %}< p class = " small " > This repository has a trust policy, so land from a client with your key:</ p > < p class = " secret " > oxy proposal land {{ shell.base_url }}{{ repo.url }}/proposal/{{ p.number }}</ p > {% endif %}
198
{% endif %}
86a69b53 Andy McCall 17 days ago ↶
199
{% if !p.auto_land_by.is_empty() %}< p class = " small " > < span class = " sig sig-pending " aria-hidden = " true" > ◷</ span > lands when its checks pass (set by < a href = " /{{ p.auto_land_by }}" > {{ p.auto_land_by }}</ a > )</ p >
200
{% if can_auto_land %}< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/auto-land" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " on" value = " 0" > < button type = " submit" class = " small " > don't land it by itself</ button > </ form > {% endif %}
201
{% else if can_auto_land & & hub_can_land %}< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/auto-land" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " on" value = " 1" > < button type = " submit" title = " It lands, as you, once its required checks pass and nothing else blocks it" > LAND WHEN CHECKS PASS</ button > </ form > {% endif %}
2a8b6216 Andy McCall 17 days ago ↶
202
{% if can_land_over_checks & & hub_can_land %}< details class = " land-over " > < summary class = " small " > Land now, over the checks</ summary >
203
< form method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/land" data-confirm = " Land #{{ p.number }} before its required checks pass?" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
204
< label for = " over-checks" class = " small " > Why it can't wait for them (shown on the proposal, and audited)</ label >
205
< input type = " text" id = " over-checks " name = " over_checks" required maxlength = " 300" placeholder = " the runners are down; this is the fix" >
206
< button type = " submit" class = " danger-btn " data-busy = " LANDING" > LAND OVER CHECKS</ button >
207
</ form > </ details > {% endif %}
de505df5 Andy McCall 1 month ago ↶
208
{% endif %}
b5a0a69b Andy McCall 20 days ago ↶
209
</ section >
8e96499f Andy McCall 1 month ago ↶
210
{% if !closes.is_empty() %}< section class = " side-sec " > < div class = " side-head " > < h3 > Closes</ h3 > </ div > < ul class = " plain small side-list " > {% for c in closes %}< li > < a href = " {{ c.url }}" class = " kind-issue " > issue #{{ c.number }}</ a > {{ c.title }} < span class = " mute " > {{ c.state }}</ span > </ li > {% endfor %}</ ul > </ section > {% endif %}
211
< section class = " side-sec " >
212
< div class = " side-head " > < h3 > Reviewers</ h3 >
213
{% if can_request %}< details class = " menu " > < summary class = " gear " title = " Request reviews" aria-label = " Request reviews" > {% include "gear.html" %}</ summary >
214
< form class = " menu-pop " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/reviewers" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " action" value = " set" >
215
< p class = " menu-title " > Request reviews</ p >
216
< input class = " menu-filter " type = " search" placeholder = " Filter people" aria-label = " Filter people" data-menu-filter >
217
< div class = " menu-list " > {% for u in reviewable %}< label class = " menu-item " > < input type = " checkbox" name = " username" value = " {{ u }}" {% if requested.contains (u) %} checked {% endif %}> < img class = " avatar xs " src = " /avatar/{{ u }}" alt = " " loading = " lazy" > < span class = " menu-name " > {{ u }}</ span > </ label > {% endfor %}
218
{% if reviewable.is_empty() %}< p class = " menu-empty " > Nobody else with access to this repository yet.</ p > {% endif %}</ div >
219
< div class = " menu-foot " > < button type = " submit" class = " small primary-btn " > Apply</ button > </ div >
220
</ form > </ details > {% endif %}</ div >
221
{% if requested.is_empty() %}< p class = " side-empty " > No reviews requested</ p > {% else %}< ul class = " side-people " > {% for r in requested %}< li > < a href = " /{{ r }}" > < img class = " avatar xs " src = " /avatar/{{ r }}" alt = " " loading = " lazy" > {{ r }}</ a > < span class = " side-note-inline " > awaiting review</ span > </ li > {% endfor %}</ ul > {% endif %}
ffb67a81 Andy McCall 21 days ago
222
{% if !assistants.is_empty() %}< div class = " side-assist " > {% for a in assistants %}< form class = " tiny inline " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/assistant" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " assistant" value = " {{ a }}" > < button type = " submit" class = " agent-btn " title = " Ask {{ a }}, an AI assistant, to review; its answer is recorded as your review via {{ a }}" > < img src = " /avatar/agent/{{ a }}" alt = " " width = " 16" height = " 16" > Ask {{ a }}</ button > < button type = " submit" class = " agent-btn " name = " task" value = " summary" title = " {{ a }}, an AI assistant, summarises the proposal; its summary is posted as your comment via {{ a }}" > < img src = " /avatar/agent/{{ a }}" alt = " " width = " 16" height = " 16" > Summarise with {{ a }}</ button > </ form > {% endfor %}</ div > {% endif %}
8e96499f Andy McCall 1 month ago ↶
223
</ section >
224
< section class = " side-sec " >
225
< div class = " side-head " > < h3 > Labels</ h3 >
226
{% if can_triage %}< details class = " menu " > < summary class = " gear " title = " Edit labels" aria-label = " Edit labels" > {% include "gear.html" %}</ summary >
227
< form class = " menu-pop " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/labels" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" >
228
< p class = " menu-title " > Apply labels to this proposal</ p >
229
{% if !all_labels.is_empty() %}< input class = " menu-filter " type = " search" placeholder = " Filter labels" aria-label = " Filter labels" data-menu-filter > {% endif %}
230
< div class = " menu-list " > {% for l in all_labels %}< label class = " menu-item " > < input type = " checkbox" name = " label" value = " {{ l.id }}" {% if labels.contains (l) %} checked {% endif %}> < span class = " swatch " style = " -- c : {{ l.colour }}" > </ span > < span class = " menu-name " > {{ l.name }}{% if !l.description.is_empty() %}< small > {{ l.description }}</ small > {% endif %}</ span > </ label > {% endfor %}
231
{% if all_labels.is_empty() %}< p class = " menu-empty " > This repository has no labels yet.</ p > {% endif %}</ div >
232
< div class = " menu-foot " > < a class = " small " href = " {{ repo.url }}/labels" > Edit labels</ a > < button type = " submit" class = " small primary-btn " > Apply</ button > </ div >
233
</ form > </ details > {% endif %}</ div >
234
{% if labels.is_empty() %}< p class = " side-empty " > None yet</ p > {% else %}< p class = " side-labels " > {% for l in labels %}< span class = " label " style = " -- c : {{ l.colour }}" title = " {{ l.description }}" > {{ l.name }}</ span > {% endfor %}</ p > {% endif %}
235
</ section >
236
< section class = " side-sec " >
9e50caed Andy McCall 1 month ago ↶
237
< div class = " side-head " > < h3 > Projects</ h3 >
238
{% if let Some(choices) = project_choices %}{% if !choices.is_empty() %}< details class = " menu " > < summary class = " gear " title = " Add to a project" aria-label = " Add to a project" > {% include "gear.html" %}</ summary >
239
< form class = " menu-pop " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/project" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " action" value = " add" >
240
< p class = " menu-title " > Add to a project</ p >
241
{% if choices.len() > 6 %}< input class = " menu-filter " type = " search" placeholder = " Filter projects" aria-label = " Filter projects" data-menu-filter > {% endif %}
242
< div class = " menu-list " > {% for pr in choices %}< button type = " submit" name = " project" value = " {{ pr.number }}" class = " menu-item menu-pick " > < span class = " menu-name " > {{ pr.title }}< small > {{ pr.owner }}</ small > </ span > </ button > {% endfor %}</ div >
243
</ form > </ details > {% endif %}{% endif %}</ div >
991b5563 Andy McCall 1 month ago ↶
244
{% if projects.is_empty() %}< p class = " side-empty " > None yet</ p > {% else %}< ul class = " plain small side-list " > {% for (pr, col) in projects %}< li > < a href = " /projects/{{ pr.owner }}/{{ pr.number }}" > {{ pr.title }}</ a > < span class = " mute " > · {{ col }}</ span > {% if project_choices.is_some() %} < form class = " inline " method = " post" action = " {{ repo.url }}/proposal/{{ p.number }}/project" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < input type = " hidden" name = " project" value = " {{ pr.number }}" > < button name = " action" value = " remove" class = " linkish " title = " Take it off this project" > remove</ button > </ form > {% endif %}</ li > {% endfor %}</ ul > {% endif %}
9e50caed Andy McCall 1 month ago ↶
245
{% if let Some(choices) = project_choices %}{% if choices.is_empty() & & projects.is_empty() %}< p class = " side-note " > < a href = " /projects/{{ repo.owner }}" > {{ repo.owner }}'s projects</ a > </ p > {% endif %}{% endif %}
8e96499f Andy McCall 1 month ago ↶
246
</ section >
a2ef53c5 Andy McCall 19 days ago ↶
247
< section class = " side-sec " data-live = " {{ live_topic }} {{ repo_topic }}" data-fragment = " checks" >
8e96499f Andy McCall 1 month ago ↶
248
< div class = " side-head " > < h3 > Checks</ h3 > </ div >
b64e2537 Andy McCall 1 month ago ↶
249
< div id = " checks " >
8e96499f Andy McCall 1 month ago ↶
250
{% if checks.is_empty() %}< p class = " side-empty " > None reported on the projected state{% if !required_checks.is_empty() %}; required: {{ required_checks.join(", ") }}{% endif %}.</ p > {% else %}
a2ef53c5 Andy McCall 19 days ago ↶
251
< ul class = " plain " > {% for c in checks %}< li > < span class = " sig{% if c.status == " passed" %} sig-valid {% else if c.status = = "failed" %} sig-invalid {% else %} sig-unsigned {% endif %}"> {% if c.status == "passed" %}✔{% else if c.status == "failed" %}✘{% else if c.status == "running" %}…{% else %}○{% endif %}</ span > {% if c.url.is_empty() %}{{ c.name }}{% else %}< a href = " {{ c.url }}" > {{ c.name }}</ a > {% endif %} < span class = " mute small " > {{ c.status }}{% if !c.summary.is_empty() %} · {{ c.summary|addresses }}{% endif %}{% if !c.seal.is_empty() %} · sealed{% endif %}</ span > </ li > {% endfor %}</ ul > {% endif %}
b64e2537 Andy McCall 1 month ago ↶
252
</ div >
a2ef53c5 Andy McCall 19 days ago ↶
253
</ section >
8e96499f Andy McCall 1 month ago ↶
254
< section class = " side-sec " >
255
< div class = " side-head " > < h3 > Approvals</ h3 > </ div >
256
{% if approvals.is_empty() %}< p class = " side-empty " > None yet{% if requirements.required_approvals > 0 %}; {{ requirements.required_approvals }} sealed required{% endif %}.</ p > {% else %}
de505df5 Andy McCall 1 month ago ↶
257
< ul class = " plain " > {% for a in approvals %}< li > {% if a.sealed %}< span class = " sig sig-valid " > ◈</ span > {% else %}< span class = " sig sig-unsigned " > ○</ span > {% endif %} < a href = " /{{ a.account }}" > {{ a.account }}</ a > {% if !a.sealed %} < span class = " mute small " > seal pending</ span > {% endif %}</ li > {% endfor %}</ ul > {% endif %}
8e96499f Andy McCall 1 month ago ↶
258
</ section >
259
{% if !owners.is_empty() %}< section class = " side-sec " > < div class = " side-head " > < h3 > Code owners</ h3 > </ div > < ul class = " plain small " > {% for o in owners %}< li > {{ o }}</ li > {% endfor %}</ ul > </ section > {% endif %}
260
{% if !depends_on.is_empty() %}< section class = " side-sec " > < div class = " side-head " > < h3 > Depends on</ h3 > </ div > < ul class = " plain small " > {% for n in depends_on %}< li > < a href = " {{ repo.url }}/proposal/{{ n }}" > #{{ n }}</ a > </ li > {% endfor %}</ ul > </ section > {% endif %}
de505df5 Andy McCall 1 month ago ↶
261
</ aside >
262
</ div >
263
{% endif %}
ffb67a81 Andy McCall 21 days ago
264
</ div >
de505df5 Andy McCall 1 month ago ↶
265
{% endblock %}