Monorepo for Tangled
1{{ define "repo/fragments/diff" }}
2 <style>
3 #filesToggle:checked ~ div label[for="filesToggle"] .show-text { display: none; }
4 #filesToggle:checked ~ div label[for="filesToggle"] .hide-text { display: inline; }
5 #filesToggle:not(:checked) ~ div label[for="filesToggle"] .hide-text { display: none; }
6 #filesToggle:checked ~ div div#files { width: fit-content; max-width: 15vw; }
7 #filesToggle:not(:checked) ~ div div#files { width: 0; display: none; margin-right: 0; }
8 #filesToggle:not(:checked) ~ div div#resize-files { display: none; }
9 </style>
10
11 {{ template "diffTopbar" . }}
12 {{ block "diffLayout" . }} {{ end }}
13 {{ template "fragments/resizable" }}
14{{ end }}
15
16{{ define "diffTopbar" }}
17 {{ $diff := index . 0 }}
18 {{ $opts := index . 1 }}
19 {{ $root := "" }}
20 {{ if gt (len .) 2 }}
21 {{ $root = index . 2 }}
22 {{ end }}
23
24 {{ block "filesCheckbox" $ }} {{ end }}
25 {{ block "subsCheckbox" $ }} {{ end }}
26
27 <!-- top bar -->
28 <div class="sticky top-0 z-30 bg-slate-100 dark:bg-gray-900 flex items-center gap-2 col-span-full h-12 p-2 {{ if $root }}mt-4{{ end }}">
29 <!-- left panel toggle -->
30 {{ template "filesToggle" . }}
31
32 <!-- stats -->
33 {{ $stat := $diff.Stats }}
34 {{ $count := len $diff.ChangedFiles }}
35 {{ template "repo/fragments/diffStatPill" $stat }}
36 <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>
37
38 {{ if $root }}
39 {{ if $root.IsInterdiff }}
40 <!-- interdiff indicator -->
41 <div class="flex items-center gap-2 before:content-['|'] before:text-gray-300 dark:before:text-gray-600 before:mr-2">
42 <span class="text-xs text-gray-600 dark:text-gray-400 uppercase tracking-wide">Interdiff</span>
43 <a
44 href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ sub $root.ActiveRound 1 }}"
45 class="px-2 py-0.5 bg-white dark:bg-gray-700 rounded font-mono text-xs hover:bg-gray-50 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-600"
46 >
47 #{{ sub $root.ActiveRound 1 }}
48 </a>
49 <span class="text-gray-400 text-xs">→</span>
50 <a
51 href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $root.ActiveRound }}"
52 class="px-2 py-0.5 bg-white dark:bg-gray-700 rounded font-mono text-xs hover:bg-gray-50 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-600"
53 >
54 #{{ $root.ActiveRound }}
55 </a>
56 </div>
57 {{ else if ne $root.ActiveRound nil }}
58 <!-- diff round indicator -->
59 <div class="flex items-center gap-2 before:content-['|'] before:text-gray-300 dark:before:text-gray-600 before:mr-2">
60 <span class="text-xs text-gray-600 dark:text-gray-400 uppercase tracking-wide">Diff</span>
61 <span class="px-2 py-0.5 bg-white dark:bg-gray-700 rounded font-mono text-xs border border-gray-300 dark:border-gray-600">
62 <span class="hidden md:inline">round </span>#{{ $root.ActiveRound }}
63 </span>
64 </div>
65 {{ end }}
66 {{ end }}
67
68 <!-- spacer -->
69 <div class="flex-grow"></div>
70
71 <!-- collapse diffs -->
72 {{ template "collapseToggle" }}
73
74 <!-- diff options -->
75 {{ template "repo/fragments/diffOpts" $opts }}
76
77 <!-- right panel toggle -->
78 {{ block "subsToggle" $ }} {{ end }}
79 </div>
80
81{{ end }}
82
83{{ define "resize-grip" }}
84 {{ $id := index . 0 }}
85 {{ $target := index . 1 }}
86 {{ $direction := index . 2 }}
87 <div id="{{ $id }}"
88 data-resizer="vertical"
89 data-target="{{ $target }}"
90 data-direction="{{ $direction }}"
91 class="resizer-vertical hidden md:flex w-4 sticky top-12 max-h-screen flex-col items-center justify-center group">
92 <div class="w-1 h-16 group-hover:h-24 group-[.resizing]:h-24 transition-all rounded-full bg-gray-400 dark:bg-gray-500 group-hover:bg-gray-500 group-hover:dark:bg-gray-400"></div>
93 </div>
94{{ end }}
95
96{{ define "diffLayout" }}
97 {{ $diff := index . 0 }}
98 {{ $opts := index . 1 }}
99
100 <div class="flex col-span-full flex-grow">
101 <!-- left panel -->
102 <div id="files" class="w-0 hidden md:block overflow-hidden sticky top-12 max-h-screen overflow-y-auto pb-12">
103 <section class="overflow-x-auto text-sm px-6 py-2 border border-gray-200 dark:border-gray-700 w-full mx-auto min-h-full rounded bg-white dark:bg-gray-800 drop-shadow-sm">
104 {{ template "repo/fragments/fileTree" $diff.FileTree }}
105 </section>
106 </div>
107
108 {{ template "resize-grip" (list "resize-files" "files" "before") }}
109
110 <!-- main content -->
111 <div id="diff-files" class="flex-1 min-w-0 sticky top-12 pb-12">
112 {{ template "diffFiles" (list $diff $opts) }}
113 </div>
114
115 </div>
116{{ end }}
117
118{{ define "diffFiles" }}
119 {{ $diff := index . 0 }}
120 {{ $opts := index . 1 }}
121 {{ $files := $diff.ChangedFiles }}
122 {{ $isSplit := $opts.Split }}
123 <div class="flex flex-col gap-4">
124 {{ if eq (len $files) 0 }}
125 <div class="text-center text-gray-500 dark:text-gray-400 py-8">
126 <p>No differences found between the selected revisions.</p>
127 </div>
128 {{ else }}
129 {{ range $idx, $file := $files }}
130 {{ template "diffFile" (list $idx $file $isSplit) }}
131 {{ end }}
132 {{ end }}
133 </div>
134{{ end }}
135
136{{ define "diffFile" }}
137 {{ $idx := index . 0 }}
138 {{ $file := index . 1 }}
139 {{ $isSplit := index . 2 }}
140 {{ with $file }}
141 <details open id="file-{{ .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 }}">
142 <summary class="list-none cursor-pointer sticky top-12 group-open:border-b border-gray-200 dark:border-gray-700">
143 <div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
144 <div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
145 <span class="group-open:hidden inline">{{ i "chevron-right" "w-4 h-4" }}</span>
146 <span class="hidden group-open:inline">{{ i "chevron-down" "w-4 h-4" }}</span>
147 {{ template "repo/fragments/diffStatPill" .Stats }}
148
149 <div class="flex gap-2 items-center overflow-x-auto">
150 {{ $n := .Names }}
151 {{ if and $n.New $n.Old (ne $n.New $n.Old)}}
152 {{ $n.Old }} {{ i "arrow-right" "w-4 h-4" }} {{ $n.New }}
153 {{ else if $n.New }}
154 {{ $n.New }}
155 {{ else }}
156 {{ $n.Old }}
157 {{ end }}
158 </div>
159 </div>
160 </div>
161 </summary>
162
163 <div class="transition-all duration-700 ease-in-out">
164 {{ $reason := .CanRender }}
165 {{ if $reason }}
166 <p class="text-center text-gray-400 dark:text-gray-500 p-4">{{ $reason }}</p>
167 {{ else }}
168 {{ if $isSplit }}
169 {{- template "repo/fragments/splitDiff" .Split -}}
170 {{ else }}
171 {{- template "repo/fragments/unifiedDiff" . -}}
172 {{ end }}
173 {{- end -}}
174 </div>
175 </details>
176 {{ end }}
177{{ end }}
178
179{{ define "filesCheckbox" }}
180 <input type="checkbox" id="filesToggle" class="peer/files hidden" checked/>
181{{ end }}
182
183{{ define "filesToggle" }}
184 <label title="Toggle filetree panel" for="filesToggle" class="hidden md:inline-flex items-center justify-center rounded cursor-pointer text-normal font-normal normalcase">
185 <span class="show-text">{{ i "panel-left-open" "size-4" }}</span>
186 <span class="hide-text">{{ i "panel-left-close" "size-4" }}</span>
187 </label>
188{{ end }}
189
190{{ define "collapseToggle" }}
191 <label
192 title="Expand/Collapse diffs"
193 for="collapseToggle"
194 class="btn font-normal normal-case p-2"
195 >
196 <input type="checkbox" id="collapseToggle" class="peer/collapse hidden" checked/>
197 <span class="peer-checked/collapse:hidden inline-flex items-center gap-2">
198 {{ i "fold-vertical" "w-4 h-4" }}
199 <span class="hidden md:inline">expand all</span>
200 </span>
201 <span class="peer-checked/collapse:inline-flex hidden flex items-center gap-2">
202 {{ i "unfold-vertical" "w-4 h-4" }}
203 <span class="hidden md:inline">collapse all</span>
204 </span>
205 </label>
206 <script>
207 document.addEventListener('DOMContentLoaded', function() {
208 const checkbox = document.getElementById('collapseToggle');
209 const details = document.querySelectorAll('details[id^="file-"]');
210
211 checkbox.addEventListener('change', function() {
212 details.forEach(detail => {
213 detail.open = checkbox.checked;
214 });
215 });
216
217 details.forEach(detail => {
218 detail.addEventListener('toggle', function() {
219 const allOpen = Array.from(details).every(d => d.open);
220 const allClosed = Array.from(details).every(d => !d.open);
221
222 if (allOpen) {
223 checkbox.checked = true;
224 } else if (allClosed) {
225 checkbox.checked = false;
226 }
227 });
228 });
229 });
230 </script>
231{{ end }}