This repository has no description
1{{ define "repo/pulls/fragments/pullStepReview" }}
2 <section class="flex flex-col gap-3">
3 {{ if not .Comparison }}
4 <div class="p-4 border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-800/30 text-sm text-gray-600 dark:text-gray-400">
5 {{ if eq .Source "patch" }}
6 Paste a patch above to see a comparison.
7 {{ else }}
8 Pick a source and target above to see a comparison.
9 {{ end }}
10 </div>
11 {{ else }}
12 {{ $commits := .Comparison.FormatPatch }}
13 {{ if $commits }}
14 <div class="flex flex-col gap-2">
15 <div class="flex items-center justify-between gap-3 min-w-0 text-sm text-gray-500 dark:text-gray-400">
16 {{ if .IsStacked }}
17 <span class="inline-flex items-center gap-2 flex-shrink-0 text-gray-900 dark:text-gray-100">
18 {{ i "chevrons-down-up" "w-4 h-4" }}
19 <span>Stack</span>
20 <span class="text-gray-500 dark:text-gray-400">{{ len $commits }} pull request{{ if ne (len $commits) 1 }}s{{ end }}</span>
21 </span>
22 {{ else }}
23 <span class="flex-shrink-0">{{ len $commits }} commit{{ if ne (len $commits) 1 }}s{{ end }}</span>
24 {{ end }}
25 {{ if and .SourceBranch .TargetBranch }}
26 <span class="inline-flex items-center gap-2 font-mono text-gray-600 dark:text-gray-300 truncate">
27 <span>{{ .TargetBranch }}</span>
28 {{ i "arrow-left-right" "w-4 h-4 flex-shrink-0" }}
29 <span>{{ .SourceBranch }}</span>
30 </span>
31 {{ end }}
32 </div>
33 {{ if .IsStacked }}
34 {{ template "pullReviewStackedCommits" . }}
35 {{ else }}
36 {{ template "pullReviewFlatCommits" . }}
37 {{ end }}
38 </div>
39 {{ else if ne .Source "patch" }}
40 <div class="p-4 border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-800/30 text-sm text-gray-600 dark:text-gray-400">
41 {{ if and .SourceBranch .TargetBranch (eq .SourceBranch .TargetBranch) }}
42 Source and target are the same branch, nothing to merge.
43 {{ else }}
44 No commits between target and source. Make sure your source branch has commits not on the target.
45 {{ end }}
46 </div>
47 {{ end }}
48
49 {{ if and .Diff (not .IsStacked) }}
50 {{ template "pullComposeFlatDiff" (list .Diff .DiffOpts) }}
51 {{ end }}
52
53 {{ if and .IsStacked $commits }}
54 {{ template "pullSubmitRow" . }}
55 {{ template "pullStackApplyAllScript" }}
56 {{ end }}
57 {{ end }}
58 </section>
59{{ end }}
60
61{{ define "pullStackApplyAllScript" }}
62 <script>
63 (() => {
64 const checkbox = document.getElementById('stack-apply-all');
65 if (!checkbox || checkbox.dataset.applyAllWired === '1') return;
66 checkbox.dataset.applyAllWired = '1';
67
68 const update = () => {
69 const panels = document.querySelectorAll('[data-stack-labels]');
70 panels.forEach((panel, idx) => {
71 if (idx === 0) return;
72 panel.classList.toggle('hidden', checkbox.checked);
73 });
74 };
75 checkbox.addEventListener('change', update);
76 update();
77 })();
78 </script>
79{{ end }}
80
81{{ define "pullReviewFlatCommits" }}
82 {{ $commits := .Comparison.FormatPatch }}
83 <ul class="flex flex-col gap-2">
84 {{ range $commits }}
85 <li class="border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800 px-3 py-2 flex items-center gap-3">
86 <span class="text-gray-700 dark:text-gray-300 flex-shrink-0">
87 {{ i "git-pull-request-create" "w-4 h-4" }}
88 </span>
89 <span class="flex-1 min-w-0 truncate text-sm dark:text-gray-200">{{ .Title }}</span>
90 {{ template "pullReviewCommitTimestamp" (dict "Patch" .) }}
91 <span class="-my-2 self-stretch w-px bg-gray-200 dark:bg-gray-700"></span>
92 {{ template "pullReviewCommitActions" (dict "Patch" . "RepoInfo" $.RepoInfo) }}
93 </li>
94 {{ end }}
95 </ul>
96{{ end }}
97
98{{ define "pullReviewStackedCommits" }}
99 {{ $root := . }}
100 {{ $commits := .Comparison.FormatPatch }}
101 {{ $previewUrl := printf "/%s/pulls/new/preview" .RepoInfo.FullName }}
102 {{ $hasSidePanel := and $root.LabelDefs $root.RepoInfo.Roles.IsPushAllowed }}
103 <ul class="flex flex-col gap-2">
104 {{ range $idx, $p := $commits }}
105 {{ $cid := $p.ChangeIdOrEmpty }}
106 {{ $titleOverride := index $root.StackTitles $cid }}
107 {{ $bodyOverride := index $root.StackBodies $cid }}
108 {{ $displayTitle := $p.Title }}
109 {{ if $titleOverride }}{{ $displayTitle = $titleOverride }}{{ end }}
110 {{ $bodyValue := $p.Body }}
111 {{ if $bodyOverride }}{{ $bodyValue = $bodyOverride }}{{ end }}
112 {{ $perDiff := "" }}
113 {{ $perOpts := dict }}
114 {{ if lt $idx (len $root.StackedDiffs) }}
115 {{ $sd := index $root.StackedDiffs $idx }}
116 {{ $perDiff = $sd.Diff }}
117 {{ $perOpts = $sd.Opts }}
118 {{ end }}
119 <li>
120 <details class="group/stacked border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800">
121 <summary class="p-3 cursor-pointer flex items-center gap-3 list-none">
122 <span class="text-gray-700 dark:text-gray-300 flex-shrink-0">
123 {{ i "chevron-right" "w-4 h-4 group-open/stacked:hidden inline" }}
124 {{ i "chevron-down" "w-4 h-4 hidden group-open/stacked:inline" }}
125 </span>
126 <span class="-my-3 self-stretch w-px bg-gray-200 dark:bg-gray-700"></span>
127 <span class="text-gray-700 dark:text-gray-300 flex-shrink-0">
128 {{ i "git-pull-request-create" "w-4 h-4" }}
129 </span>
130 <span class="flex-1 min-w-0 truncate text-sm dark:text-gray-200">{{ $displayTitle }}</span>
131 {{ template "pullReviewCommitTimestamp" (dict "Patch" $p) }}
132 <span class="-my-3 self-stretch w-px bg-gray-200 dark:bg-gray-700"></span>
133 {{ template "pullReviewCommitActions" (dict "Patch" $p "RepoInfo" $root.RepoInfo) }}
134 </summary>
135 {{ if $cid }}
136 <div class="px-3 pb-3 pt-1 flex flex-col gap-3 border-t border-gray-100 dark:border-gray-700">
137 {{ $titleName := printf "stackTitle[%s]" $cid }}
138 {{ $bodyName := printf "stackBody[%s]" $cid }}
139 <div class="flex flex-col md:flex-row gap-6">
140 <div class="flex-1 min-w-0 flex flex-col gap-3">
141 <div class="flex flex-col gap-1">
142 <label class="text-xs uppercase tracking-wide text-gray-800 dark:text-gray-200">title</label>
143 <input
144 type="text"
145 name="{{ $titleName }}"
146 value="{{ $displayTitle }}"
147 class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600"
148 placeholder="{{ $p.Title }}"
149 />
150 </div>
151 {{ template "markdownEditor" (dict
152 "Id" (printf "stack-body-%s" $cid)
153 "Name" $bodyName
154 "Value" $bodyValue
155 "Rows" 4
156 "Placeholder" "Describe this pull request. Markdown is supported."
157 "LabelText" "description"
158 "PreviewUrl" $previewUrl
159 ) }}
160 </div>
161 {{ if $hasSidePanel }}
162 <aside data-stack-labels="{{ $cid }}" class="w-full md:w-72 md:flex-shrink-0 flex flex-col gap-4">
163 {{ $labelState := $root.LabelState }}
164 {{ if $root.StackLabelStates }}
165 {{ $perCid := index $root.StackLabelStates $cid }}
166 {{ if $perCid }}{{ $labelState = $perCid }}{{ end }}
167 {{ end }}
168 {{ $labelCtx := dict "Defs" $root.LabelDefs "State" $labelState "RepoInfo" $root.RepoInfo "Subject" "" "LoggedInUser" $root.LoggedInUser "Prefix" (printf "stackLabel[%s]" $cid) }}
169 {{ template "editBasicLabels" $labelCtx }}
170 {{ template "editKvLabels" $labelCtx }}
171 {{ if eq $idx 0 }}
172 <label class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 self-start">
173 <input type="checkbox" id="stack-apply-all" name="applyLabelsToAll" autocomplete="off" />
174 Apply labels and assignees to all PRs in stack
175 </label>
176 {{ end }}
177 </aside>
178 {{ end }}
179 </div>
180 {{ if $perDiff }}
181 <hr class="border-gray-200 dark:border-gray-700 my-1" />
182 <div id="stack-diff-{{ $cid }}">
183 <input type="hidden" name="stackSplit[{{ $cid }}]" value="{{ if $perOpts.Split }}split{{ else }}unified{{ end }}" />
184 {{ template "pullStackedDiffArea" (dict "Diff" $perDiff "DiffOpts" $perOpts "Cid" $cid) }}
185 </div>
186 {{ end }}
187 </div>
188 {{ else }}
189 <div class="px-3 pb-3 pt-1 text-sm text-yellow-700 dark:text-yellow-300 border-t border-gray-100 dark:border-gray-700">
190 This commit has no <span class="font-mono">Change-Id</span> header and can't be stacked. Set one on the commit and re-push.
191 </div>
192 {{ end }}
193 </details>
194 </li>
195 {{ end }}
196 </ul>
197{{ end }}
198
199{{ define "pullReviewCommitTimestamp" }}
200 {{ $p := .Patch }}
201 {{ if or (not $p.AuthorDate.IsZero) $p.SHA }}
202 <span class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 flex-shrink-0">
203 {{ if not $p.AuthorDate.IsZero }}
204 {{ template "repo/fragments/shortTimeAgo" $p.AuthorDate }}
205 {{ end }}
206 {{ if $p.SHA }}
207 <span class="font-mono bg-gray-100 dark:bg-gray-900 text-gray-700 dark:text-gray-300 px-2 py-0.5 rounded">{{ slice $p.SHA 0 8 }}</span>
208 {{ end }}
209 </span>
210 {{ end }}
211{{ end }}
212
213{{ define "pullReviewCommitActions" }}
214 {{ $p := .Patch }}
215 {{ $repoInfo := .RepoInfo }}
216 {{ if $p.SHA }}
217 <span class="flex items-center gap-1 text-gray-700 dark:text-gray-300 flex-shrink-0">
218 <button type="button"
219 class="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
220 title="Copy SHA"
221 onclick="event.preventDefault(); event.stopPropagation(); navigator.clipboard.writeText('{{ $p.SHA }}'); this.innerHTML=`{{ i "copy-check" "w-4 h-4" }}`; setTimeout(() => this.innerHTML=`{{ i "copy" "w-4 h-4" }}`, 1500)">
222 {{ i "copy" "w-4 h-4" }}
223 </button>
224 <a href="/{{ $repoInfo.FullName }}/tree/{{ $p.SHA }}"
225 onclick="event.stopPropagation()"
226 class="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
227 title="Browse repository at this commit">
228 {{ i "folder-code" "w-4 h-4" }}
229 </a>
230 </span>
231 {{ end }}
232{{ end }}
233
234{{ define "pullStackedDiffArea" }}
235 {{ $diff := .Diff }}
236 {{ $opts := .DiffOpts }}
237 {{ $cid := .Cid }}
238 {{ $togId := printf "stack-%s-filesToggle" $cid }}
239 {{ $colId := printf "stack-%s-collapseToggle" $cid }}
240 {{ $filesId := printf "stack-%s-files" $cid }}
241 {{ $diffAreaId := printf "stack-%s-diff-area" $cid }}
242 {{ $filePrefix := printf "stack-%s-file-" $cid }}
243 {{ $stat := $diff.Stats }}
244 {{ $count := len $diff.ChangedFiles }}
245
246 <style>
247 #{{ $togId }}:checked ~ * label[for="{{ $togId }}"] .show-text { display: none; }
248 #{{ $togId }}:checked ~ * label[for="{{ $togId }}"] .hide-text { display: inline; }
249 #{{ $togId }}:not(:checked) ~ * label[for="{{ $togId }}"] .hide-text { display: none; }
250 #{{ $togId }}:checked ~ * div#{{ $filesId }} { width: fit-content; max-width: 15vw; }
251 #{{ $togId }}:not(:checked) ~ * div#{{ $filesId }} { width: 0; display: none; margin-right: 0; }
252 </style>
253
254 <input type="checkbox" id="{{ $togId }}" class="hidden"/>
255
256 <div id="{{ $diffAreaId }}">
257 <div class="bg-slate-100 dark:bg-gray-900 flex items-center gap-2 h-12 p-2 rounded-t-md border border-b-0 border-gray-200 dark:border-gray-700">
258 <label title="Toggle filetree panel" for="{{ $togId }}" class="hidden md:inline-flex items-center justify-center rounded cursor-pointer text-normal font-normal normal-case">
259 <span class="show-text">{{ i "panel-left-open" "size-4" }}</span>
260 <span class="hide-text">{{ i "panel-left-close" "size-4" }}</span>
261 </label>
262
263 {{ template "repo/fragments/diffStatPill" $stat }}
264 <span class="text-xs text-gray-600 dark:text-gray-400 hidden md:inline-flex">{{ $count }} changed file{{ if ne $count 1 }}s{{ end }}</span>
265
266 <div class="flex-grow"></div>
267
268 <label title="Expand/Collapse diffs" for="{{ $colId }}" class="btn font-normal normal-case p-2">
269 <input type="checkbox" id="{{ $colId }}" class="peer/collapse hidden"/>
270 <span class="peer-checked/collapse:hidden inline-flex items-center gap-2">
271 {{ i "unfold-vertical" "w-4 h-4" }}
272 <span class="hidden md:inline">Expand all</span>
273 </span>
274 <span class="peer-checked/collapse:inline-flex hidden flex items-center gap-2">
275 {{ i "fold-vertical" "w-4 h-4" }}
276 <span class="hidden md:inline">Collapse all</span>
277 </span>
278 </label>
279
280 {{ template "repo/fragments/diffOpts" $opts }}
281 </div>
282
283 <div class="flex border border-gray-200 dark:border-gray-700 rounded-b-md">
284 <div id="{{ $filesId }}" class="hidden md:block overflow-hidden max-h-[60vh] overflow-y-auto border-r border-gray-200 dark:border-gray-700">
285 <section class="overflow-x-auto text-sm px-3 py-2 w-full mx-auto">
286 {{ template "repo/fragments/fileTreePrefixed" (dict "Tree" $diff.FileTree "Prefix" $filePrefix) }}
287 </section>
288 </div>
289
290 <div class="flex-1 min-w-0 p-2">
291 <div class="flex flex-col gap-2">
292 {{ if eq $count 0 }}
293 <div class="text-center text-gray-500 dark:text-gray-400 py-8">
294 <p>No differences found.</p>
295 </div>
296 {{ else if le $count 5 }}
297 {{ range $idx, $file := $diff.ChangedFiles }}
298 {{ template "stackedDiffFile" (dict "Idx" $idx "File" $file "IsSplit" $opts.Split "Prefix" $filePrefix) }}
299 {{ end }}
300 {{ else }}
301 {{ range $idx, $file := slice $diff.ChangedFiles 0 5 }}
302 {{ template "stackedDiffFile" (dict "Idx" $idx "File" $file "IsSplit" $opts.Split "Prefix" $filePrefix) }}
303 {{ end }}
304 {{ $remaining := sub $count 5 }}
305 <details class="group/showmore">
306 <summary class="cursor-pointer text-sm text-gray-700 dark:text-gray-300 py-2 px-1 flex items-center gap-2 select-none hover:text-gray-900 dark:hover:text-gray-100 list-none">
307 <span class="group-open/showmore:hidden inline-flex items-center gap-2">
308 {{ i "chevron-right" "w-4 h-4" }}
309 Show {{ $remaining }} more file{{ if ne $remaining 1 }}s{{ end }}
310 </span>
311 <span class="hidden group-open/showmore:inline-flex items-center gap-2">
312 {{ i "chevron-down" "w-4 h-4" }}
313 Hide {{ $remaining }} file{{ if ne $remaining 1 }}s{{ end }}
314 </span>
315 </summary>
316 <div class="flex flex-col gap-2 mt-2">
317 {{ range $idx, $file := slice $diff.ChangedFiles 5 }}
318 {{ template "stackedDiffFile" (dict "Idx" (add $idx 5) "File" $file "IsSplit" $opts.Split "Prefix" $filePrefix) }}
319 {{ end }}
320 </div>
321 </details>
322 {{ end }}
323 </div>
324 </div>
325 </div>
326 </div>
327
328 <script>
329 (() => {
330 const cb = document.getElementById('{{ $colId }}');
331 const area = document.getElementById('{{ $diffAreaId }}');
332 if (!cb || !area) return;
333 const all = () => area.querySelectorAll('details[id^="{{ $filePrefix }}"]');
334 cb.addEventListener('change', () => {
335 all().forEach(d => { d.open = cb.checked; });
336 });
337 area.addEventListener('toggle', (e) => {
338 if (!e.target.matches('details[id^="{{ $filePrefix }}"]')) return;
339 const dets = Array.from(all());
340 const allOpen = dets.every(d => d.open);
341 const allClosed = dets.every(d => !d.open);
342 if (allOpen) cb.checked = true;
343 else if (allClosed) cb.checked = false;
344 }, true);
345 })();
346 </script>
347{{ end }}
348
349{{ define "stackedDiffFile" }}
350 {{ $idx := .Idx }}
351 {{ $file := .File }}
352 {{ $isSplit := .IsSplit }}
353 {{ $prefix := .Prefix }}
354 {{ $isGenerated := false }}
355 {{ $isDeleted := false }}
356 {{ with $file }}
357 {{ $n := .Names }}
358 {{ $isDeleted = and (eq $n.New "") (ne $n.Old "") }}
359 {{ if $n.New }}
360 {{ $isGenerated = isGenerated $n.New }}
361 {{ else if $n.Old }}
362 {{ $isGenerated = isGenerated $n.Old }}
363 {{ end }}
364 <details id="{{ $prefix }}{{ .Id }}" class="group border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm" tabindex="{{ add $idx 1 }}">
365 <summary class="list-none cursor-pointer group-open:border-b border-gray-200 dark:border-gray-700">
366 <div class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
367 <div class="p-2 flex gap-2 items-center overflow-x-auto">
368 <span class="group-open:hidden inline">{{ i "chevron-right" "w-4 h-4" }}</span>
369 <span class="hidden group-open:inline">{{ i "chevron-down" "w-4 h-4" }}</span>
370 {{ template "repo/fragments/diffStatPill" .Stats }}
371 <div class="flex gap-2 items-center overflow-x-auto">
372 {{ if and $n.New $n.Old (ne $n.New $n.Old)}}
373 {{ $n.Old }} {{ i "arrow-right" "w-4 h-4" }} {{ $n.New }}
374 {{ else if $n.New }}
375 {{ $n.New }}
376 {{ else }}
377 {{ $n.Old }}
378 {{ end }}
379 {{ if $isDeleted }}
380 <span class="text-gray-400 dark:text-gray-500" title="Deleted files are collapsed by default">
381 {{ i "circle-question-mark" "size-4" }}
382 </span>
383 {{ else if $isGenerated }}
384 <span class="text-gray-400 dark:text-gray-500" title="Generated files are collapsed by default">
385 {{ i "circle-question-mark" "size-4" }}
386 </span>
387 {{ end }}
388 </div>
389 </div>
390 </div>
391 </summary>
392
393 <div class="transition-all duration-700 ease-in-out">
394 {{ $reason := .CanRender }}
395 {{ if $reason }}
396 <p class="text-center text-gray-400 dark:text-gray-500 p-4">{{ $reason }}</p>
397 {{ else }}
398 {{ if $isSplit }}
399 {{- template "repo/fragments/splitDiff" .Split -}}
400 {{ else }}
401 {{- template "repo/fragments/unifiedDiff" . -}}
402 {{ end }}
403 {{- end -}}
404 </div>
405 </details>
406 {{ end }}
407{{ end }}
408
409{{ define "pullComposeFlatDiff" }}
410 {{ $diff := index . 0 }}
411 {{ $opts := index . 1 }}
412 {{ $files := $diff.ChangedFiles }}
413 {{ $isSplit := $opts.Split }}
414 {{ $count := len $files }}
415 {{ $stat := $diff.Stats }}
416
417 <style>
418 #composeFilesToggle:checked ~ * label[for="composeFilesToggle"] .show-text { display: none; }
419 #composeFilesToggle:checked ~ * label[for="composeFilesToggle"] .hide-text { display: inline; }
420 #composeFilesToggle:not(:checked) ~ * label[for="composeFilesToggle"] .hide-text { display: none; }
421 #composeFilesToggle:checked ~ * div#composeFiles { width: fit-content; max-width: 15vw; }
422 #composeFilesToggle:not(:checked) ~ * div#composeFiles { width: 0; display: none; margin-right: 0; }
423 </style>
424
425 <input type="checkbox" id="composeFilesToggle" class="hidden"/>
426
427 <div id="diff-area">
428 <div class="bg-slate-100 dark:bg-gray-900 flex items-center gap-2 h-12 p-2 rounded-t-md border border-b-0 border-gray-200 dark:border-gray-700">
429 <label title="Toggle filetree panel" for="composeFilesToggle" class="hidden md:inline-flex items-center justify-center rounded cursor-pointer text-normal font-normal normal-case">
430 <span class="show-text">{{ i "panel-left-open" "size-4" }}</span>
431 <span class="hide-text">{{ i "panel-left-close" "size-4" }}</span>
432 </label>
433
434 {{ template "repo/fragments/diffStatPill" $stat }}
435 <span class="text-xs text-gray-600 dark:text-gray-400 hidden md:inline-flex">{{ $count }} changed file{{ if ne $count 1 }}s{{ end }}</span>
436
437 <div class="flex-grow"></div>
438
439 <label title="Expand/Collapse diffs" for="composeCollapseToggle" class="btn font-normal normal-case p-2">
440 <input type="checkbox" id="composeCollapseToggle" class="peer/collapse hidden"/>
441 <span class="peer-checked/collapse:hidden inline-flex items-center gap-2">
442 {{ i "unfold-vertical" "w-4 h-4" }}
443 <span class="hidden md:inline">Expand all</span>
444 </span>
445 <span class="peer-checked/collapse:inline-flex hidden flex items-center gap-2">
446 {{ i "fold-vertical" "w-4 h-4" }}
447 <span class="hidden md:inline">Collapse all</span>
448 </span>
449 </label>
450
451 {{ template "repo/fragments/diffOpts" $opts }}
452 </div>
453
454 <div class="flex border border-gray-200 dark:border-gray-700 rounded-b-md">
455 <div id="composeFiles" class="hidden md:block overflow-hidden max-h-[60vh] overflow-y-auto border-r border-gray-200 dark:border-gray-700">
456 <section class="overflow-x-auto text-sm px-3 py-2 w-full mx-auto">
457 {{ template "repo/fragments/fileTree" $diff.FileTree }}
458 </section>
459 </div>
460
461 <div class="flex-1 min-w-0 p-2">
462 <div class="flex flex-col gap-2">
463 {{ if eq $count 0 }}
464 <div class="text-center text-gray-500 dark:text-gray-400 py-8">
465 <p>No differences found.</p>
466 </div>
467 {{ else if le $count 5 }}
468 {{ range $idx, $file := $files }}
469 {{ template "diffFile" (list $idx $file $isSplit true) }}
470 {{ end }}
471 {{ else }}
472 {{ range $idx, $file := slice $files 0 5 }}
473 {{ template "diffFile" (list $idx $file $isSplit true) }}
474 {{ end }}
475 {{ $remaining := sub $count 5 }}
476 <details class="group/showmore">
477 <summary class="cursor-pointer text-sm text-gray-700 dark:text-gray-300 py-2 px-1 flex items-center gap-2 select-none hover:text-gray-900 dark:hover:text-gray-100 list-none">
478 <span class="group-open/showmore:hidden inline-flex items-center gap-2">
479 {{ i "chevron-right" "w-4 h-4" }}
480 Show {{ $remaining }} more file{{ if ne $remaining 1 }}s{{ end }}
481 </span>
482 <span class="hidden group-open/showmore:inline-flex items-center gap-2">
483 {{ i "chevron-down" "w-4 h-4" }}
484 Hide {{ $remaining }} file{{ if ne $remaining 1 }}s{{ end }}
485 </span>
486 </summary>
487 <div class="flex flex-col gap-2 mt-2">
488 {{ range $idx, $file := slice $files 5 }}
489 {{ template "diffFile" (list (add $idx 5) $file $isSplit true) }}
490 {{ end }}
491 </div>
492 </details>
493 {{ end }}
494 </div>
495 </div>
496 </div>
497 </div>
498
499 <script>
500 (() => {
501 const checkbox = document.getElementById('composeCollapseToggle');
502 const diffArea = document.getElementById('diff-area');
503 if (!checkbox || !diffArea) return;
504 const all = () => diffArea.querySelectorAll('details[id^="file-"]');
505 checkbox.addEventListener('change', () => {
506 all().forEach(d => { d.open = checkbox.checked; });
507 });
508 diffArea.addEventListener('toggle', (e) => {
509 if (!e.target.matches('details[id^="file-"]')) return;
510 const dets = Array.from(all());
511 const allOpen = dets.every(d => d.open);
512 const allClosed = dets.every(d => !d.open);
513 if (allOpen) checkbox.checked = true;
514 else if (allClosed) checkbox.checked = false;
515 }, true);
516 })();
517 </script>
518{{ end }}