This repository has no description
1{{ define "title" }}
2 {{ .Pull.Title }} · Pull #{{ .Pull.PullId }} · {{ .RepoInfo.FullName }} · Tangled
3{{ end }}
4
5{{ define "mainLayout" }}
6{{ template "fragments/resizable" }}
7<input type="hidden" id="pull-id" value="{{ .Pull.PullId }}" />
8<input type="hidden" id="pull-target-repo-did" value="{{ .Pull.RepoDid }}" />
9<input type="hidden" id="pull-active-version-id" value="{{ .ActiveVersionId }}" />
10<div class="flex px-1 group/pr-layout">
11 <div class="flex-1 min-w-0">
12 <div class="max-w-screen-lg mx-auto">
13 <!-- repo header -->
14 <section id="repo-header" class="mb-2 py-2 px-4 dark:text-white">
15 <div class="flex flex-col sm:flex-row items-start gap-4 justify-between mb-2">
16 <div class="flex flex-col gap-2">
17 {{ template "repoOwnerAndName" . }}
18 {{ template "repoForkInfo" . }}
19 </div>
20 <div class="hidden sm:block sm:flex-shrink-0">
21 {{ template "repoActions" . }}
22 </div>
23 </div>
24 {{ template "repoMetadata" . }}
25
26 <div class="block sm:hidden mt-4">
27 {{ template "repoActions" . }}
28 </div>
29 </section>
30 <!-- repo nav -->
31 <nav class="w-full pl-4 overflow-auto">
32 <div class="flex z-60">
33 {{ range $item := .RepoInfo.GetTabs }}
34 {{ $key := index $item 0 }}
35 {{ $value := index $item 1 }}
36 {{ $icon := index $item 2 }}
37 {{ $meta := index $.RepoInfo.TabMetadata $key }}
38 {{/* no hx-boost here because PR page can be super large */}}
39 <a
40 href="/{{ $.RepoInfo.FullName }}{{ $value }}"
41 class="relative -mr-px group"
42 >
43 <div
44 class='px-4 py-1 mr-1 text-black dark:text-white min-w-[80px] text-center relative rounded-t whitespace-nowrap
45 {{ if eq "pulls" $key }}
46 -mb-px bg-white dark:bg-gray-800
47 {{ else }}
48 group-hover:bg-gray-100/25 group-hover:dark:bg-gray-700/25
49 {{ end }}
50 '
51 >
52 <span class="flex items-center justify-center">
53 {{ i $icon "size-4 mr-2" }}
54 {{ $key | capitalize }}
55 {{ if $meta }}
56 <span class="bg-gray-200 dark:bg-gray-700 rounded py-1/2 px-1 text-sm ml-1">{{ scaleFmt $meta }}</span>
57 {{ end }}
58 </span>
59 </div>
60 </a>
61 {{ end }}
62 </div>
63 </nav>
64 <div class="grid grid-cols-1 lg:grid-cols-[4fr_1fr] gap-4 mb-4">
65 <div class="min-w-0">
66 <section class="bg-white dark:bg-gray-800 p-6 rounded">
67 <!-- PR header -->
68 <header class="pb-2">
69 <h1 class="text-2xl">
70 {{ .Pull.Title | description }}
71 <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span>
72 </h1>
73 </header>
74 <div class="flex items-center gap-2">
75 {{ template "repo/pulls/fragments/pullState" .Pull.State }}
76 <span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1">
77 opened by
78 {{ template "user/fragments/picLink" (list .Pull.OwnerDid.String "size-6" (index .VouchRelationships .Pull.OwnerDid)) }}
79 <a href="/{{ resolve .Pull.OwnerDid.String }}">{{ resolve .Pull.OwnerDid.String }}</a>
80 <span class="select-none before:content-['\00B7']"></span>
81 {{ template "repo/fragments/time" .Pull.Created }}
82 <span class="select-none before:content-['\00B7']"></span>
83 targeting
84 <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">
85 <a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}">{{ .Pull.TargetBranch }}</a>
86 </span>
87
88 {{ if .Pull.SourceBranch }}
89 from
90 <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">
91 {{ if not .Pull.IsForkBased }}
92 {{ $repoPath := .RepoInfo.FullName }}
93 <a href="/{{ $repoPath }}/tree/{{ pathEscape .Pull.SourceBranch }}">{{ .Pull.SourceBranch }}</a>
94 {{ else }}
95 <a href="/{{ .Pull.SourceRepo }}">fork</a>:
96 <a href="/{{ .Pull.SourceRepo }}/tree/{{ pathEscape .Pull.SourceBranch }}">{{ .Pull.SourceBranch }}</a>
97 {{ end }}
98 </span>
99 {{ end }}
100 </span>
101 </div>
102 <article class="mt-4 prose dark:prose-invert">
103 {{ if .Pull.Body }}
104 {{ .Pull.Body | markdown }}
105 {{ else }}
106 <span class="italic">No description provided</span>
107 {{ end }}
108 </article>
109 <div class="mt-4">
110 {{ $aturi := .Pull.AtUri }}
111 {{ template "repo/fragments/reactions"
112 (dict "Reactions" (index .Reactions $aturi)
113 "UserReacted" (index .UserReacted $aturi)
114 "ThreadAt" $aturi) }}
115 </div>
116 </section>
117 </div>
118 <div class="lg:row-start-1 lg:row-end-3 lg:col-start-2 min-w-0">
119 {{ template "repo/fragments/labelPanel"
120 (dict "RepoInfo" $.RepoInfo
121 "Defs" $.LabelDefs
122 "Subject" $.Pull.AtUri
123 "State" $.Pull.Labels) }}
124 {{ template "repo/fragments/participants" $.Pull.Participants }}
125 {{ if $.LoggedInUser }}
126 {{ template "repo/pulls/fragments/subscribeButton"
127 (dict "RepoInfo" $.RepoInfo
128 "PullId" $.Pull.PullId
129 "IsSubscribed" $.IsSubscribed) }}
130 {{ end }}
131 {{ template "repo/fragments/backlinks"
132 (dict "RepoInfo" $.RepoInfo
133 "Backlinks" $.Backlinks) }}
134 {{ template "repo/fragments/externalLinkPanel" $.Pull.AtUri }}
135 </div>
136 <div class="lg:row-start-2 lg:col-start-1">
137 <h2 id="commits">Commits</h2>
138 {{ if .ErrorMsg }}
139 <div class="flex items-center justify-center w-full min-h-32 p-4 rounded text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-900/30 border border-red-300 dark:border-red-700">
140 <span>{{ .ErrorMsg }}</span>
141 </div>
142 {{ else }}
143 <div class="grid grid-cols-1 bg-white dark:bg-gray-800 rounded shadow-sm border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700">
144 {{ $seeAll := false }}
145 {{ if .IsInterdiff }}
146 {{ $seeAll = not .ChangeId }}
147 {{ else }}
148 {{ $version := index .Pull.Versions .VersionId }}
149 {{ $seeAll = and .IsDiffBase .IsDiffHead }}
150 {{ end }}
151 <div class="text-sm px-2 {{ if $seeAll }}bg-gray-100/50 dark:bg-gray-700/50{{ end }}">
152 <div class="relative">
153 {{ if $seeAll }}
154 <div class="flex-shrink-0 absolute top-3 left-0">
155 {{ i "arrow-right" "size-4" }}
156 </div>
157 {{ end }}
158 <div class="py-2 ml-6">
159 {{ if .IsInterdiff }}
160 <a href="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/{{ .Version1 }}..{{ .Version2 }}/all">See all changes</a>
161 {{ else }}
162 <a href="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/{{ .VersionId }}">See all changes</a>
163 {{ end }}
164 </div>
165 </div>
166 </div>
167 {{ range .Commits }}
168 {{ $messageParts := splitN .Message "\n\n" 2 }}
169 {{ $active := and (not $seeAll) (eq (shortId $.ActiveCommitId) (shortId .Hash.String)) }}
170 {{ $toggleId := printf "commit-message-toggle-%s" .Hash }}
171 {{ $pipeline := index $.Pipelines .Hash.String }}
172 <div class="text-sm px-2 {{ if $active }}bg-gray-100/50 dark:bg-gray-700/50{{ end }}">
173 <input id="{{ $toggleId }}" type="checkbox" class="peer hidden" />
174 <div class="relative flex items-center justify-between">
175 {{ if $active }}
176 <div class="flex-shrink-0 absolute left-0">
177 {{ i "arrow-right" "size-4" }}
178 </div>
179 {{ end }}
180 <div class="py-2 ml-6">
181 <code class="py-0.5 px-1">{{ shortId .Hash.String }}</code>
182 {{ if .ChangeId }}
183 <code class="py-0.5 px-1">{{ shortId .ChangeId }}</code>
184 {{ end }}
185 {{ if $.IsInterdiff }}
186 <a href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/{{ $.Version1 }}..{{ $.Version2 }}/{{ .ChangeId }}">{{ index $messageParts 0 }}</a>
187 {{ else }}
188 <a href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/{{ $.VersionId }}/{{ shortId .FirstParentHash.String }}..{{ shortId .Hash.String }}">{{ index $messageParts 0 }}</a>
189 {{ end }}
190 <label
191 for="{{ $toggleId }}"
192 class="cursor-pointer inline-flex py-0.5 px-1 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"
193 >
194 {{ i "ellipsis" "size-3" }}
195 </label>
196 </div>
197 {{ if and $pipeline $pipeline.Id }}
198 <div>
199 {{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }}
200 </div>
201 {{ end }}
202 </div>
203 {{ if gt (len $messageParts) 1 }}
204 <pre class="ml-6 mt-1 mb-2 hidden peer-checked:block">{{ index $messageParts 1 }}</pre>
205 {{ end }}
206 </div>
207 {{ end }}
208 </div>
209 {{ end }}
210 </div>
211 </div>
212 </div>
213 <!-- diff -->
214 <div hx-include="this" class="min-h-[50vh] group/diff-layout">
215 {{ $diffUrl := "" }}
216 {{ if not .IsInterdiff }}
217 {{ $diffUrl = (printf "/%s/pulls/%d/_/diff?view=unified" .Pull.RepoDid .Pull.PullId) }}
218 {{ $diff := .DiffParams }}
219 <input type="hidden" name="baseRepo" value="{{ .Pull.RepoDid }}">
220 <input type="hidden" name="base" value="{{ $diff.Base }}">
221 <input type="hidden" name="headRepo" value="{{ .Pull.SourceRepo }}">
222 <input type="hidden" name="head" value="{{ $diff.Head }}">
223 {{ else if .DiffParams.Diff }}
224 {{ $diffUrl = (printf "/%s/pulls/%d/_/diff?view=unified" .Pull.RepoDid .Pull.PullId) }}
225 {{ $diff := .DiffParams.Diff }}
226 <input type="hidden" name="baseRepo" value="{{ .Pull.SourceRepo }}">
227 <input type="hidden" name="base" value="{{ $diff.Base }}">
228 <input type="hidden" name="headRepo" value="{{ .Pull.SourceRepo }}">
229 <input type="hidden" name="head" value="{{ $diff.Head }}">
230 {{ else }}
231 {{ $diffUrl = (printf "/%s/pulls/%d/_/interdiff?view=unified" .Pull.RepoDid .Pull.PullId) }}
232 {{ $diff := .DiffParams.Interdiff }}
233 <input type="hidden" name="repo" value="{{ .Pull.SourceRepo }}">
234 <input type="hidden" name="base1" value="{{ $diff.From.Base }}">
235 <input type="hidden" name="head1" value="{{ $diff.From.Head }}">
236 <input type="hidden" name="base2" value="{{ $diff.To.Base }}">
237 <input type="hidden" name="head2" value="{{ $diff.To.Head }}">
238 {{ end }}
239 <!-- diff header -->
240 <header class="bg-slate-100 dark:bg-gray-900 sticky px-1 top-0 z-20 flex flex-col md:flex-row md:justify-between">
241 <!-- left -->
242 <div class="h-12 flex items-center gap-2">
243 <div class="md:block hidden">
244 <label for="diff-tree-toggle" title="Toggle filetree panel" class="btn-flat">
245 <span class="group-has-[#diff-tree-toggle:checked]/diff-layout:hidden inline">{{ i "panel-left-open" "size-4" }}</span>
246 <span class="hidden group-has-[#diff-tree-toggle:checked]/diff-layout:inline">{{ i "panel-left-close" "size-4" }}</span>
247 </label>
248 </div>
249 <div id="diff-stats">
250 <div class="flex items-center font-mono text-sm">
251 <span class="rounded-l p-1 select-none bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400">+{{ i "loader-circle" "size-3 inline-flex animate-spin" }}</span>
252 <span class="rounded-r p-1 select-none bg-red-100 text-red-700 dark:bg-red-800/50 dark:text-red-400">-{{ i "loader-circle" "size-3 inline-flex animate-spin" }}</span>
253 </div>
254 </div>
255 {{ if .IsInterdiff }}
256 <span class="text-sm">
257 Interdiff
258 <span class="ml-1 text-xs after:content-['|'] after:ml-1 after:text-gray-300 dark:after:text-gray-600">
259 <code class="inline-block box-content min-h-[1lh] py-0.5 px-1 border border-gray-300 dark:border-gray-600">v{{ .Version1 }}</code>
260 {{ i "arrow-right" "size-3 inline-flex" }}
261 <code class="inline-block box-content min-h-[1lh] py-0.5 px-1 border border-gray-300 dark:border-gray-600">v{{ .Version2 }}</code>
262 </span>
263 <a href="#commits" class="btn-flat text-xs hover:bg-gray-50 dark:hover:bg-gray-800">
264 {{ with .ChangeId }}
265 <code class="py-0.5 px-1 border border-gray-300 dark:border-gray-600">{{ shortId . }}</code>
266 {{ else }}
267 <span class="px-1">All changes</span>
268 {{ end }}
269 </a>
270 </span>
271 {{ else }}
272 <span class="text-sm">
273 Diff
274 <span class="ml-1 text-xs after:content-['|'] after:ml-1 after:text-gray-300 dark:after:text-gray-600">
275 <code class="py-0.5 px-1 border border-gray-300 dark:border-gray-600">v{{ .VersionId }}</code>
276 </span>
277 <a href="#commits" class="btn-flat text-xs hover:bg-gray-50 dark:hover:bg-gray-800">
278 <code class="inline-block box-content min-h-[1lh] py-0.5 px-1 border border-gray-300 dark:border-gray-600">{{ .DisplayDiffBase }}</code>
279 {{ i "arrow-right" "size-3 inline-flex" }}
280 <code class="inline-block box-content min-h-[1lh] py-0.5 px-1 border border-gray-300 dark:border-gray-600">{{ .DisplayDiffHead }}</code>
281 </a>
282 </span>
283 {{ end }}
284 </div>
285 <!-- right -->
286 <div class="h-12 flex items-center gap-2">
287 <label title="Expand/Collapse diffs" class="btn font-normal normal-case">
288 <input type="checkbox" id="diff-collapse-toggle" class="peer/collapse hidden" checked/>
289 <span class="peer-checked/collapse:hidden inline-flex items-center gap-2">
290 {{ i "unfold-vertical" "size-4" }}
291 <span class="hidden md:inline">Expand all</span>
292 </span>
293 <span class="peer-checked/collapse:inline-flex hidden items-center gap-2">
294 {{ i "fold-vertical" "size-4" }}
295 <span class="hidden md:inline">Collapse all</span>
296 </span>
297 </label>
298
299 <div class="md:hidden flex-grow"></div>
300
301 <!-- diff settings -->
302 <div id="diff-settings">
303 {{ template "repo/pulls/fragments/diffSettings" (dict "DiffUrl" $diffUrl "Unified" true) }}
304 </div>
305
306 {{ $isInterdiff := .IsInterdiff }}
307 {{ $canInterdiff := gt .ActiveVersionId 0 }}
308 <div class="btn-group">
309 <button
310 type="button"
311 role="link"
312 onclick="window.location.href='/{{ .Pull.RepoDid }}/pulls/{{ .Pull.PullId }}/{{ .ActiveVersionId }}';"
313 class="btn-group-item {{ if not $isInterdiff }}active{{ end }}"
314 >
315 {{ i "diff" "size-4" }}
316 Diff
317 </button>
318 <button
319 type="button"
320 role="link"
321 onclick="window.location.href='/{{ .Pull.RepoDid }}/pulls/{{ .Pull.PullId }}/{{ sub .ActiveVersionId 1 }}..{{ .ActiveVersionId }}';"
322 class="btn-group-item {{ if $isInterdiff }}active{{ end }}"
323 {{ if not $canInterdiff }}disabled{{ end }}
324 >
325 {{ i "chevrons-left-right-ellipsis" "size-4 rotate-90" }}
326 Interdiff
327 </button>
328 </div>
329 <label
330 for="discussion-toggle"
331 title="Toggle discussion panel"
332 class="btn-flat hidden lg:inline-flex group-has-[#discussion-toggle:checked]/pr-layout:hidden"
333 >
334 {{ i "message-square-more" "size-4" }}
335 </label>
336 </div>
337 </header>
338 <div class="flex">
339 <input type="checkbox" id="diff-tree-toggle" class="peer hidden" checked>
340 <aside id="diff-files" class="hidden peer-checked:md:block w-[20%] max-w-[40%]">
341 <div class="sticky top-12 bg-white dark:bg-gray-800 rounded shadow-sm border border-gray-200 dark:border-gray-700">
342 <div class="
343 max-h-[calc(100vh-3rem-0.5rem)] pb-2
344 overflow-y-auto
345 ">
346 <section class="text-sm px-6 py-2 w-full">
347 <div class="mb-8">
348 <div id="diff-files-content">
349 {{ i "loader-circle" "size-4 inline-flex animate-spin" }} loading...
350 </div>
351 </div>
352 </section>
353 </div>
354 </div>
355 </aside>
356
357 {{ template "resize-grip" (list "resize-diff-files" "diff-files" "before" "hidden peer-checked:md:flex") }}
358
359 <div id="diff-list-container" class="flex-1 min-w-0">
360 <div
361 id="diff-list"
362 hx-get="{{ $diffUrl }}"
363 hx-swap="outerHTML"
364 hx-trigger="load"
365 >
366 {{ i "loader-circle" "size-4 inline-flex animate-spin" }} loading...
367 </div>
368 </div>
369 <script>
370 (() => {
371 const checkbox = document.getElementById('diff-collapse-toggle');
372 const diffList = document.getElementById('diff-list-container');
373
374 checkbox.addEventListener('change', () => {
375 console.debug("checked", checkbox.checked);
376 diffList.querySelectorAll('details[id^="file-"]').forEach(detail => {
377 detail.open = checkbox.checked;
378 });
379 });
380
381 if (window.__collapseToggleHandler) {
382 diffList.removeEventListener('toggle', window.__collapseToggleHandler, true);
383 }
384
385 const handler = (e) => {
386 if (!e.target.matches('details[id^="file-"]')) return;
387 const details = document.querySelectorAll('details[id^="file-"]');
388 const allOpen = Array.from(details).every(d => d.open);
389 const allClosed = Array.from(details).every(d => !d.open);
390
391 if (allOpen) checkbox.checked = true;
392 else if (allClosed) checkbox.checked = false;
393 };
394
395 window.__collapseToggleHandler = handler;
396 diffList.addEventListener('toggle', handler, true);
397 })();
398 </script>
399 </div>
400 </div>
401 </div>
402
403 <div id="bottom-sheet-backdrop" class="fixed inset-0 bg-black/50 lg:hidden opacity-0 pointer-events-none transition-opacity duration-300 z-20"></div>
404
405 <input type="checkbox" id="discussion-toggle" class="peer hidden" checked>
406 {{ template "resize-grip" (list "resize-discussion" "discussion" "after" "hidden peer-checked:lg:flex") }}
407
408 <div id="discussion" class="
409 fixed z-30 bottom-0 right-0 w-full max-h-[calc(100vh-10rem)] rounded-t-2xl
410 bg-slate-100 dark:bg-gray-900
411 lg:sticky lg:top-0 lg:hidden peer-checked:lg:block lg:w-[25vw] lg:max-w-[50vw] lg:max-h-screen
412 lg:bg-transparent
413 ">
414 <details open id="bottom-sheet" class="
415 flex flex-col group/history
416 {{ if .Pull.State.IsOpen -}} *:border-green-600 *:dark:border-green-900
417 {{ else if .Pull.State.IsMerged -}} *:border-purple-600 *:dark:border-purple-900
418 {{ else if .Pull.State.IsAbandoned -}} *:border-red-600 *:dark:border-red-900
419 {{ else -}} *:border-gray-600 *:dark:border-gray-900
420 {{ end }}
421 ">
422 {{ $versions := len .Pull.Versions }}
423 {{ $comments := .Pull.TotalComments }}
424 <summary class="
425 list-none z-20 flex items-center justify-between
426 pl-3 pr-4 py-2 rounded-t-2xl
427 relative
428 border-t-4 border-x-4
429 group-open/history:bg-transparent
430 lg:border-0
431 lg:h-12 lg:px-1 lg:pointer-events-none
432 lg:bg-transparent
433 ">
434 <div class="flex items-center gap-2">
435 <span class="lg:hidden">
436 {{ template "repo/pulls/fragments/pullState" .Pull.State }}
437 </span>
438 <h2>History</h2>
439 </div>
440 <div class="flex items-center gap-2 text-sm">
441 <span>
442 {{ $versions }} version{{ if ne $versions 1 }}s{{ end }}
443 </span>
444 <span class="select-none before:content-['\00B7']"></span>
445 <span>
446 {{ $comments }} comment{{ if ne $comments 1 }}s{{ end }}
447 </span>
448 <label for="discussion-toggle" title="Toggle discussion panel" class="hidden lg:inline-flex btn-flat pointer-events-auto">
449 {{ i "message-square" "size-4" }}
450 </label>
451 <span class="lg:hidden">
452 <span class="group-open/history:hidden inline">{{ i "chevron-up" "size-4" }}</span>
453 <span class="group-open/history:inline hidden">{{ i "chevron-down" "size-4" }}</span>
454 </span>
455 </div>
456 </summary>
457 <div class="
458 max-h-[calc(100vh-3rem-0.5rem)] pb-32 overflow-y-auto space-y-4
459 px-1 border-x-4
460 lg:px-0 lg:border-0
461 ">
462 {{ range .Pull.Versions }}
463 {{ $active := eq .ID $.ActiveVersionId }}
464 <div
465 id="version-{{ .ID }}"
466 class="
467 {{ if $active }}active{{ end }}
468 w-full shadow-sm rounded overflow-clip bg-gray-50 dark:bg-gray-900
469 "
470 >
471 <!-- header -->
472 <header class="sticky top-0 z-20 bg-slate-100 dark:bg-gray-900">
473 <div class="pt-2 pr-2 pb-4 pl-4 lg:pl-6 flex gap-2
474 rounded-t border-t border-x border-gray-200 dark:border-gray-700
475 {{ if $active }}
476 bg-blue-50 dark:bg-blue-950
477 {{ else }}
478 bg-white dark:bg-gray-800
479 {{ end }}
480 ">
481 <!-- left column: just profile picture -->
482 <div class="flex-shrink-0 pt-2">
483 {{ template "user/fragments/picLink" (list $.Pull.OwnerDid.String "size-8" (index $.VouchRelationships $.Pull.OwnerDid)) }}
484 </div>
485 <!-- right column -->
486 <div class="flex-1 min-w-0 flex flex-col gap-1">
487 <!-- submission info -->
488 {{ $handle := resolve $.Pull.OwnerDid.String }}
489 <div class="flex gap-2 items-center justify-between mb-1">
490 <span class="inline-flex items-center gap-2 text-sm pt-2
491 {{ if $active }}
492 text-gray-600 dark:text-gray-300
493 {{ else }}
494 text-gray-500 dark:text-gray-400
495 {{ end }}"
496 >
497 <a href="/{{ $handle }}">{{ $handle }}</a>
498 submitted
499 <span class="
500 px-2 py-0.5 rounded font-mono text-xs border
501 {{ if $active }}
502 text-blue-800 dark:text-white bg-blue-100 dark:bg-blue-600 border-blue-200 dark:border-blue-500
503 {{ else }}
504 text-black dark:text-white bg-gray-100 dark:bg-gray-700 border-gray-300 dark:border-gray-600
505 {{ end }}
506 ">#{{ .ID }}</span>
507 <span class="select-none before:content-['\00B7']"></span>
508 <a href="/{{ $.Pull.RepoDid }}/pulls/{{ $.Pull.PullId }}/{{ .ID }}">{{ template "repo/fragments/shortTime" .Created }}</a>
509 </span>
510 <a class="btn-flat flex items-center gap-2 no-underline hover:no-underline text-sm"
511 href="/{{ $.Pull.RepoDid }}/pulls/{{ $.Pull.PullId }}/{{ .ID }}">
512 {{ i "diff" "size-4" }}
513 Diff
514 </a>
515 </div>
516 {{ if eq .ID $.Pull.LatestVersionNumber }}
517 <div id="mergecheck-banner">
518 {{ if $.Pull.State.IsOpen }}
519 <div class="flex items-center gap-2 text-gray-500 dark:text-gray-400">
520 {{ i "loader-circle" "size-4 animate-spin" }}
521 <span>Checking mergeability…</span>
522 </div>
523 {{ end }}
524 </div>
525 {{ end }}
526 </div>
527 </div>
528 </header>
529 <!-- comments -->
530 {{ $count := len .Comments }}
531 <details
532 class="relative pl-8 lg:pl-10 rounded-b border-x border-b border-gray-200 dark:border-gray-700 group/comments group/collapse"
533 {{ if or $active (eq $count 0) }}open{{ end }}
534 >
535 <summary class="cursor-pointer list-none">
536 <div class="collapse-trigger hidden group-open/comments:block absolute left-2 top-0 bottom-0 w-12 lg:w-16 transition-colors flex items-center justify-center group/border z-4">
537 <div class="absolute left-1/2 -translate-x-1/2 top-0 bottom-0 w-0.5 group-open/comments:bg-gray-200 dark:group-open/comments:bg-gray-700 group-has-[.collapse-trigger:hover]/collapse:bg-gray-400 dark:group-has-[.collapse-trigger:hover]/collapse:bg-gray-500 transition-colors"> </div>
538 </div>
539 <div class="group-open/comments:hidden block relative group/summary py-4">
540 <div class="absolute -left-8 top-0 bottom-0 w-16 transition-colors flex items-center justify-center z-4">
541 <div class="absolute left-1/2 -translate-x-1/2 h-1/3 top-0 bottom-0 w-0.5 bg-gray-200 dark:bg-gray-700 group-hover/summary:bg-gray-400 dark:group-hover/summary:bg-gray-500 transition-colors"></div>
542 </div>
543 <span class="text-gray-500 dark:text-gray-400 text-sm group-hover/summary:text-gray-600 dark:group-hover/summary:text-gray-300 transition-colors flex items-center gap-2 -ml-2 relative">
544 {{ i "circle-plus" "size-4 z-5" }}
545 Expand {{ $count }} comment{{ if ne $count 1 }}s{{ end }}
546 </span>
547 </div>
548 </summary>
549 <div id="pull-comments-{{ .ID }}">
550 {{ range .Comments }}
551 {{ template "fragments/comment/pullComment"
552 (dict "LoggedInUser" $.LoggedInUser
553 "Reactions" (index (asReactionMapMap $.Reactions) .FeedCommentAtUri)
554 "UserReacted" (index (asReactionStatusMapMap $.UserReacted) .FeedCommentAtUri)
555 "Comment" .) }}
556 {{ end }}
557 </div>
558 {{ if gt $count 0}}
559 <button
560 class="collapse-trigger flex items-center gap-2 -ml-2 relative cursor-pointer text-sm text-gray-500 dark:text-gray-400 group-has-[.collapse-trigger:hover]/collapse:text-gray-600 dark:group-has-[.collapse-trigger:hover]/collapse:text-gray-300 mt-4 pb-4 transition-colors"
561 hx-on:click="this.closest('details').open = false"
562 >
563 <span class="bg-gray-50 dark:bg-slate-900 dark:rounded-full">{{ i "circle-chevron-up" "size-4 z-5" }}</span> Collapse comment{{ if ne $count 1 }}s{{ end }}
564 </button>
565 {{ end }}
566
567 <div hx-include="this" class="relative -ml-8 lg:-ml-10 rounded-b bg-gray-50 dark:bg-gray-900">
568 {{ if eq .ID $.Pull.LatestVersionNumber }}
569 {{ block "mergeStatus" $ }} {{ end }}
570 <div id="pull-action-error" class="error"></div>
571 {{ end }}
572 <input name="subject-uri" type="hidden" value="{{ $.Pull.AtUri }}">
573 <input name="pull-round-idx" type="hidden" value="{{ .ID }}">
574 {{ template "repo/pulls/fragments/pullActions"
575 (dict
576 "LoggedInUser" $.LoggedInUser
577 "Pull" $.Pull
578 "RepoInfo" $.RepoInfo
579 "RoundNumber" .ID
580 "MergeCheck" $.MergeCheck
581 "ResubmitCheck" $.ResubmitCheck
582 "BranchDeleteStatus" $.BranchDeleteStatus
583 "Loading" (eq .ID $.Pull.LatestVersionNumber)) }}
584 </div>
585 </details>
586 </div>
587 {{ end }}
588 </div>
589 <div class="hidden sticky bottom-0 h-12">
590 <!-- TODO: bottom PR actions -->
591 </div>
592 </details>
593 </div>
594 <script>
595 (function() {
596 const details = document.getElementById('bottom-sheet');
597 const backdrop = document.getElementById('bottom-sheet-backdrop');
598 const isDesktop = () => window.matchMedia('(min-width: 1024px)').matches;
599
600 // function to update backdrop
601 const updateBackdrop = () => {
602 if (backdrop) {
603 if (details.open && !isDesktop()) {
604 backdrop.classList.remove('opacity-0', 'pointer-events-none');
605 backdrop.classList.add('opacity-100', 'pointer-events-auto');
606 document.body.style.overflow = 'hidden';
607 } else {
608 backdrop.classList.remove('opacity-100', 'pointer-events-auto');
609 backdrop.classList.add('opacity-0', 'pointer-events-none');
610 document.body.style.overflow = '';
611 }
612 }
613 };
614
615 // close on mobile initially
616 if (!isDesktop()) {
617 details.open = false;
618 }
619 updateBackdrop(); // initialize backdrop
620
621 // prevent closing on desktop
622 details.addEventListener('toggle', function(e) {
623 if (isDesktop() && !this.open) {
624 this.open = true;
625 }
626 updateBackdrop();
627 });
628
629 const mediaQuery = window.matchMedia('(min-width: 1024px)');
630 mediaQuery.addEventListener('change', function(e) {
631 if (e.matches) {
632 // switched to desktop - keep open
633 details.open = true;
634 } else {
635 // switched to mobile - close
636 details.open = false;
637 }
638 updateBackdrop();
639 });
640
641 // close when clicking backdrop
642 if (backdrop) {
643 backdrop.addEventListener('click', () => {
644 if (!isDesktop()) {
645 details.open = false;
646 }
647 });
648 }
649 })();
650 </script>
651</div>
652{{ end }}