golang.org/x/build@v0.0.0-20240506185731-218518f32b70/devapp/templates/reviews.tmpl (about) 1 <!DOCTYPE html> 2 <html lang="en"> 3 <meta charset="utf-8"> 4 <meta name="viewport" content="width=device-width, initial-scale=1"> 5 <title>Open Go Code Reviews</title> 6 <style> 7 * { 8 box-sizing: border-box; 9 margin: 0; 10 padding: 0; 11 } 12 body { 13 font: 13px system-ui, sans-serif; 14 padding: 1rem; 15 } 16 h2 { 17 margin: 1em 0 .35em; 18 } 19 h3:first-of-type { 20 padding: 0 21 } 22 a:link, 23 a:visited { 24 color: #00c; 25 } 26 header { 27 border-bottom: 1px solid #666; 28 margin-bottom: 10px; 29 padding-bottom: 10px; 30 } 31 .header-subtitle { 32 color: #666; 33 font-size: .9em; 34 } 35 .filters { 36 display: block; 37 margin: 1em 0; 38 } 39 .filter-input { 40 font: inherit; 41 width: 30em; 42 } 43 .how-to { 44 cursor: pointer; 45 margin-top: 1em; 46 max-width: 40em; 47 } 48 .how-to-container { 49 padding: 0 1em; 50 } 51 .how-to ul { 52 list-style: none; 53 margin-top: .5em; 54 } 55 .how-to li { 56 margin: .25em 1em .75em; 57 } 58 .examples li { 59 margin: .25em 1em; 60 } 61 .row { 62 border-bottom: 1px solid #f1f2f3; 63 display: flex; 64 padding: .5em 0; 65 white-space: nowrap; 66 } 67 .date { 68 min-width: 6rem; 69 } 70 .owner { 71 min-width: 10rem; 72 max-width: 10rem; 73 overflow: hidden; 74 text-overflow: ellipsis; 75 padding-right: 1em; 76 } 77 .icons, 78 .number { 79 flex-shrink: 0; 80 } 81 .icons { 82 height: 20px; 83 margin-right: 1.5em; 84 text-align: right; 85 width: 12em; 86 } 87 .number { 88 margin-right: 1.5em; 89 text-align: right; 90 width: 6ch; 91 } 92 .subject { 93 overflow: hidden; 94 text-overflow: ellipsis; 95 } 96 .pill, 97 .pill:link, 98 .pill:visited { 99 color: #000; 100 display: inline-block; 101 border-radius: 2px; 102 padding: 2px 5px; 103 text-decoration: none; 104 } 105 .plus-two:link, 106 .plus-two:visited, 107 .minus-two:link, 108 .minus-two:visited { 109 color: #fff; 110 } 111 .plus-two { 112 background-color: #2e7d32; 113 } 114 .plus-one { 115 background-color: #dcedc8; 116 } 117 .minus-one { 118 background-color: #fdaeb7; 119 } 120 .minus-two { 121 background-color: #b71c1c; 122 } 123 .no-human-comments { 124 background-color: #e0e0e0; 125 } 126 .release-milestone { 127 background-color: #a0e8ff; 128 } 129 [hidden] { 130 display: none; 131 } 132 </style> 133 <header> 134 <strong>{{.TotalChanges}} open changes</strong> 135 <div class="header-subtitle"> 136 Excluding those marked WIP, having hashtags "wait-author", "wait-release", "wait-issue", or description containing "DO NOT REVIEW". 137 </div> 138 <div class="header-subtitle"> 139 <a href="https://go.googlesource.com/build/+/master/devapp/reviews.go">Source code</a> 140 </div> 141 </header> 142 <div class="filters"> 143 <label class="filter-label">Filter: <input class="filter-input js-filter-input" type="text"></label> 144 <details class="how-to"> 145 <summary>Filters How-to</summary> 146 <div class="how-to-container"> 147 <ul> 148 <li>You can filter on any displayed field below, along with a few other fields not shown, such as reviewer. 149 <li>Exclude terms by prefixing them with a <code>-</code>. 150 <li>The <code>t:</code> operator is short for "tag" and coincides with the colored tags you see in the rows below. 151 <li>Supported tag values include "Changes without a human comment" (<code>t:attn</code>) and <code>Code-Review</code> 152 label values (<code>t:-2</code>, <code>t:+1</code>, and <code>t:+2</code>). 153 <li>All terms use substring matching, meaning that you can type <code>reviewer:iant</code> instead of <code>reviewer:iant@golang.org</code> 154 <li>The following operators are supported: <code>t:, repo:, reviewer:, cc:, involves:, and owner:</code> 155 </ul> 156 Examples: 157 <ul> 158 <li><a href="?q=reviewer%3Abradfitz+repo%3Anet">http2 changes in the net repo where brad is a reviewer</a> 159 <li><a href="?q=-involves%3Aaustin+runtime%3A">runtime changes that don't involve austin</a> 160 </ul> 161 </div> 162 </details> 163 </div> 164 {{range $p := .Projects}} 165 {{if .Changes}} 166 <section hidden> 167 <h2>{{.Project}}</h2> 168 {{range .Changes}} 169 <div class="row" data-terms="{{.SearchTerms}}"> 170 <span class="date">{{.FormattedLastUpdate}}</span> 171 <span class="owner">{{.Owner.Name}}</span> 172 <span class="icons"> 173 {{if .NoHumanComments}}<a class="pill no-human-comments" href="?q=t%3Aattn" title="Has no human comments">→</a>{{end}} 174 {{if .HasMinusTwo}}️<a class="pill minus-two" href="?q=t%3A-2" title="Code-Review: -2">-2</a>{{end}} 175 {{if .HasMinusOne}}<a class="pill minus-one" href="?q=t%3A-1" title="Code-Review: -1">-1</a>{{end}} 176 {{if .HasPlusOne}}<a class="pill plus-one" href="?q=t%3A%2B1" title="Code-Review: +1">+1</a>{{end}} 177 {{if .HasPlusTwo}}<a class="pill plus-two" href="?q=t%3A%2B2" title="Code-Review: +2">+2</a>{{end}} 178 {{if .TryBotMinusOne}}<a class="pill minus-one" href="?q=trybot%3A-1" title="Run-TryBot: -1">🤖-1</a>{{end}} 179 {{if .TryBotPlusOne}}<a class="pill plus-one" href="?q=trybot%3A%2B1" title="Run-TryBot: +1">🤖+1</a>{{end}} 180 {{if .ReleaseMilestone}}<a class="pill release-milestone" href="?q=release%3A{{.ReleaseMilestone}}" title="References an issue in Go{{.ReleaseMilestone}}">{{.ReleaseMilestone}}</a>{{end}} 181 </span> 182 <a class="number" href="https://{{$p.ReviewServer}}/{{.Number}}" target="_blank">{{.Number}}</a> 183 <span class="subject"> 184 {{.Subject}} 185 </span> 186 </div> 187 {{end}} 188 </section> 189 {{end}} 190 {{end}} 191 <script> 192 function filter(query) { 193 console.time('filter'); 194 query = query.toLowerCase(); 195 document.querySelectorAll('.row').forEach(el => { 196 const terms = el.dataset.terms; 197 const queryTerms = query.split(' '); 198 let match = true; 199 for (let i = 0; i < queryTerms.length; i++) { 200 let q = queryTerms[i].trim(); 201 if (q.length === 0 || q === '-') { continue; } 202 if (q.startsWith('-')) { 203 match = match && !terms.includes(q.substr(1)); 204 } else { 205 match = match && terms.includes(q); 206 } 207 } 208 el.hidden = !match; 209 }); 210 document.querySelectorAll('section').forEach(el => { 211 el.hidden = el.querySelectorAll('.row:not([hidden])').length === 0; 212 }); 213 console.timeEnd('filter'); 214 } 215 216 let debounceTimerId = null; 217 const filterInputEl = document.querySelector('.js-filter-input') 218 filterInputEl.addEventListener('keyup', e => { 219 const q = e.target.value.trim(); 220 let url = new URL(window.location.href); 221 if (!!q) { 222 url.searchParams.set('q', q); 223 } else { 224 url.searchParams.delete('q'); 225 } 226 window.history.replaceState({}, null, url.toString()); 227 228 if (debounceTimerId) { 229 window.clearTimeout(debounceTimerId); 230 debounceTimerId = null; 231 } 232 debounceTimerId = window.setTimeout(() => { filter(q); }, 200); 233 }); 234 235 const initialQuery = (new URL(window.location.href)).searchParams.get('q') || ''; 236 filterInputEl.value = initialQuery; 237 filter(initialQuery); 238 </script>