Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2

appview/pages: rework the PR page entirely

the new 3-panel layout puts the diff upfront, and the review panel off
to the right. on mobile devices, the review panel is a collapsible
bottom-sheet, and on desktop, it is a collapsible side-panel. it is now
possible to comment on a PR while viewing its diff.

all the JS on the page is entirely optional and simply added for
quality-of-life (such as auto-collapsing the bottomsheet on mobile
etc.).

in the review panel, submissions are listed with a top-level entry, and
comments on each submission are "reply" entries. the top-level
submission header includes the following information:

- commit messages and bodies (if available, on patch PRs this is
omitted)
- pipeline status (if avaiable, only for PRs that have triggered CI)
- mergability (if available, this is calculated only for the
latest submission)

the actual merge status (merged/closed/deleted) of the PR is listed
above the pull-action bar. previous designs combined the mergability
check and the merge-status into one component.

Signed-off-by: oppiliappan <me@oppi.li>

authored by oppi.li and committed by tangled.org 0f6d3ee0 a83ffa10

+454 -238
+454 -238
appview/pages/templates/repo/pulls/pull.html
··· 6 6 {{ template "repo/pulls/fragments/og" (dict "RepoInfo" .RepoInfo "Pull" .Pull) }} 7 7 {{ end }} 8 8 9 - {{ define "repoContentLayout" }} 10 - <div class="grid grid-cols-1 md:grid-cols-10 gap-4 w-full"> 11 - <div class="col-span-1 md:col-span-8"> 12 - <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white"> 13 - {{ block "repoContent" . }}{{ end }} 14 - </section> 15 - {{ block "repoAfter" . }}{{ end }} 9 + {{ define "mainLayout" }} 10 + <div class="px-1 flex-grow flex flex-col gap-4"> 11 + <div class="max-w-screen-lg mx-auto"> 12 + {{ block "contentLayout" . }} 13 + {{ block "content" . }}{{ end }} 14 + {{ end }} 16 15 </div> 17 - <div class="col-span-1 md:col-span-2 flex flex-col gap-6"> 16 + {{ block "contentAfterLayout" . }} 17 + <main> 18 + {{ block "contentAfter" . }}{{ end }} 19 + </main> 20 + {{ end }} 21 + </div> 22 + <script> 23 + (function() { 24 + const details = document.getElementById('bottomSheet'); 25 + const isDesktop = () => window.matchMedia('(min-width: 768px)').matches; 26 + 27 + // close on mobile initially 28 + if (!isDesktop()) { 29 + details.open = false; 30 + } 31 + 32 + // prevent closing on desktop 33 + details.addEventListener('toggle', function(e) { 34 + if (isDesktop() && !this.open) { 35 + this.open = true; 36 + } 37 + }); 38 + 39 + const mediaQuery = window.matchMedia('(min-width: 768px)'); 40 + mediaQuery.addEventListener('change', function(e) { 41 + if (e.matches) { 42 + // switched to desktop - keep open 43 + details.open = true; 44 + } else { 45 + // switched to mobile - close 46 + details.open = false; 47 + } 48 + }); 49 + })(); 50 + </script> 51 + {{ end }} 52 + 53 + {{ define "repoContentLayout" }} 54 + <div class="grid grid-cols-1 md:grid-cols-10 gap-4"> 55 + <section class="bg-white col-span-1 md:col-span-8 dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white h-full flex-shrink"> 56 + {{ block "repoContent" . }}{{ end }} 57 + </section> 58 + <div class="flex flex-col gap-6 col-span-1 md:col-span-2"> 18 59 {{ template "repo/fragments/labelPanel" 19 60 (dict "RepoInfo" $.RepoInfo 20 61 "Defs" $.LabelDefs ··· 70 29 </div> 71 30 {{ end }} 72 31 32 + {{ define "contentAfter" }} 33 + {{ template "repo/fragments/diff" (list .Diff .DiffOpts $) }} 34 + {{ end }} 35 + 73 36 {{ define "repoContent" }} 74 37 {{ template "repo/pulls/fragments/pullHeader" . }} 75 - 76 38 {{ if .Pull.IsStacked }} 77 39 <div class="mt-8"> 78 40 {{ template "repo/pulls/fragments/pullStack" . }} ··· 83 39 {{ end }} 84 40 {{ end }} 85 41 86 - {{ define "repoAfter" }} 87 - <section id="submissions" class="mt-4"> 88 - <div class="flex flex-col gap-4"> 89 - {{ block "submissions" . }} {{ end }} 90 - </div> 91 - </section> 42 + {{ define "diffLayout" }} 43 + {{ $diff := index . 0 }} 44 + {{ $opts := index . 1 }} 45 + {{ $root := index . 2 }} 92 46 93 - <div id="pull-close"></div> 94 - <div id="pull-reopen"></div> 47 + <div class="flex col-span-full"> 48 + <!-- left panel --> 49 + <div id="files" class="w-0 hidden md:block overflow-hidden sticky top-12 max-h-screen overflow-y-auto pb-12"> 50 + <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"> 51 + {{ template "repo/fragments/fileTree" $diff.FileTree }} 52 + </section> 53 + </div> 54 + 55 + <!-- main content --> 56 + <div class="flex-1 min-w-0 sticky top-12 pb-12"> 57 + {{ template "diffFiles" (list $diff $opts) }} 58 + </div> 59 + 60 + <!-- right panel --> 61 + {{ template "subsPanel" $ }} 62 + </div> 95 63 {{ end }} 64 + 65 + {{ define "subsPanel" }} 66 + {{ $root := index . 2 }} 67 + {{ $pull := $root.Pull }} 68 + 69 + <!-- backdrop overlay - only visible on mobile when open --> 70 + <div class=" 71 + fixed inset-0 bg-black/50 z-50 md:hidden opacity-0 72 + pointer-events-none transition-opacity duration-300 73 + has-[~#subs_details[open]]:opacity-100 has-[~#subs_details[open]]:pointer-events-auto"> 74 + </div> 75 + <!-- right panel - bottom sheet on mobile, side panel on desktop --> 76 + <div id="subs" class="fixed bottom-0 left-0 right-0 z-50 w-full md:static md:z-auto md:max-h-screen md:sticky md:top-12 overflow-hidden"> 77 + <details open id="bottomSheet" class="group rounded-t-2xl md:rounded-t-sm drop-shadow-lg md:drop-shadow-none"> 78 + <summary class=" 79 + flex gap-4 items-center justify-between 80 + rounded-t-2xl md:rounded-t-sm cursor-pointer list-none p-4 md:h-12 81 + text-white md:text-black md:dark:text-white 82 + bg-green-600 dark:bg-green-600 83 + md:bg-white md:dark:bg-gray-800 84 + drop-shadow-sm 85 + md:border-b md:border-x border-gray-200 dark:border-gray-700"> 86 + <h2 class="">Review Panel </h2> 87 + {{ template "subsPanelSummary" $ }} 88 + </summary> 89 + <div class="max-h-[85vh] md:max-h-[calc(100vh-3rem-3rem)] w-full flex flex-col-reverse gap-4 overflow-y-auto bg-slate-100 dark:bg-gray-900 md:bg-transparent"> 90 + {{ template "submissions" $root }} 91 + </div> 92 + </details> 93 + </div> 94 + {{ end }} 95 + 96 + {{ define "subsPanelSummary" }} 97 + {{ $root := index . 2 }} 98 + {{ $pull := $root.Pull }} 99 + {{ $latest := $pull.LastRoundNumber }} 100 + <div class="flex items-center gap-2 text-sm"> 101 + {{ if $root.IsInterdiff }} 102 + <span> 103 + viewing interdiff of 104 + <span class="font-mono">#{{ $root.ActiveRound }}</span> 105 + and 106 + <span class="font-mono">#{{ sub $root.ActiveRound 1 }}</span> 107 + </span> 108 + {{ else }} 109 + <span> 110 + viewing round 111 + <span class="font-mono">#{{ $root.ActiveRound }}</span> 112 + </span> 113 + {{ if ne $root.ActiveRound $latest }} 114 + <span>(outdated)</span> 115 + <span class="before:content-['·']"></span> 116 + <a class="underline" href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $latest }}?{{ safeUrl $root.DiffOpts.Encode }}"> 117 + view latest 118 + </a> 119 + {{ end }} 120 + {{ end }} 121 + <span class="md:hidden inline"> 122 + <span class="inline group-open:hidden">{{ i "chevron-up" "size-4" }}</span> 123 + <span class="hidden group-open:inline">{{ i "chevron-down" "size-4" }}</span> 124 + </span> 125 + </div> 126 + {{ end }} 127 + 128 + {{ define "subsCheckbox" }} 129 + <input type="checkbox" id="subsToggle" class="peer/subs hidden" checked/> 130 + {{ end }} 131 + 132 + {{ define "subsToggle" }} 133 + <style> 134 + /* Mobile: full width */ 135 + #subsToggle:checked ~ div div#subs { 136 + width: 100%; 137 + margin-left: 0; 138 + } 139 + #subsToggle:checked ~ div label[for="subsToggle"] .show-toggle { display: none; } 140 + #subsToggle:checked ~ div label[for="subsToggle"] .hide-toggle { display: flex; } 141 + #subsToggle:not(:checked) ~ div label[for="subsToggle"] .hide-toggle { display: none; } 142 + 143 + /* Desktop: 25vw with left margin */ 144 + @media (min-width: 768px) { 145 + #subsToggle:checked ~ div div#subs { 146 + width: 25vw; 147 + margin-left: 1rem; 148 + } 149 + /* Unchecked state */ 150 + #subsToggle:not(:checked) ~ div div#subs { 151 + width: 0; 152 + display: none; 153 + margin-left: 0; 154 + } 155 + } 156 + </style> 157 + <label title="Toggle review panel" for="subsToggle" class="hidden md:flex items-center justify-end rounded cursor-pointer"> 158 + <span class="show-toggle">{{ i "message-square-more" "size-4" }}</span> 159 + <span class="hide-toggle w-[25vw] flex justify-end">{{ i "message-square" "size-4" }}</span> 160 + </label> 161 + {{ end }} 162 + 96 163 97 164 {{ define "submissions" }} 98 165 {{ $lastIdx := sub (len .Pull.Submissions) 1 }} 99 - {{ $targetBranch := .Pull.TargetBranch }} 100 - {{ $repoName := .RepoInfo.FullName }} 101 - {{ range $idx, $item := .Pull.Submissions }} 102 - {{ with $item }} 103 - <details {{ if eq $idx $lastIdx }}open{{ end }}> 104 - <summary id="round-#{{ .RoundNumber }}" class="list-none cursor-pointer"> 105 - <div class="flex flex-wrap gap-2 items-stretch"> 106 - <!-- round number --> 107 - <div class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-3 py-2 dark:text-white"> 108 - <span class="flex items-center">{{ i "hash" "w-4 h-4" }}{{ .RoundNumber }}</span> 109 - </div> 110 - <!-- round summary --> 111 - <div class="flex-1 rounded drop-shadow-sm bg-white dark:bg-gray-800 p-2 text-gray-500 dark:text-gray-400"> 112 - <span class="gap-1 flex items-center"> 113 - {{ $owner := resolve $.Pull.OwnerDid }} 114 - {{ $re := "re" }} 115 - {{ if eq .RoundNumber 0 }} 116 - {{ $re = "" }} 117 - {{ end }} 118 - <span class="hidden md:inline">{{$re}}submitted</span> 119 - by {{ template "user/fragments/picHandleLink" $.Pull.OwnerDid }} 120 - <span class="select-none before:content-['\00B7']"></span> 121 - <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ .RoundNumber }}">{{ template "repo/fragments/shortTime" .Created }}</a> 122 - <span class="select-none before:content-['·']"></span> 123 - {{ $s := "s" }} 124 - {{ if eq (len .Comments) 1 }} 125 - {{ $s = "" }} 126 - {{ end }} 127 - {{ len .Comments }} comment{{$s}} 128 - </span> 129 - </div> 166 + {{ range $ridx, $item := reverse .Pull.Submissions }} 167 + {{ $idx := sub $lastIdx $ridx }} 168 + {{ template "submission" (list $item $idx $lastIdx $) }} 169 + {{ end }} 170 + {{ end }} 130 171 131 - <a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group" 132 - hx-boost="true" 133 - href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}"> 134 - {{ i "file-diff" "w-4 h-4" }} 135 - <span class="hidden md:inline">diff</span> 136 - {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 137 - </a> 138 - {{ if ne $idx 0 }} 139 - <a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group" 140 - hx-boost="true" 141 - href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff"> 142 - {{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }} 143 - <span class="hidden md:inline">interdiff</span> 144 - {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 145 - </a> 146 - {{ end }} 147 - <span id="interdiff-error-{{.RoundNumber}}"></span> 148 - </div> 149 - </summary> 172 + {{ define "submission" }} 173 + {{ $item := index . 0 }} 174 + {{ $idx := index . 1 }} 175 + {{ $lastIdx := index . 2 }} 176 + {{ $root := index . 3 }} 177 + <div class="rounded border border-gray-200 dark:border-gray-700 w-full shadow-sm bg-gray-50 dark:bg-gray-800/50"> 178 + {{ template "submissionHeader" $ }} 179 + {{ template "submissionComments" $ }} 150 180 151 - {{ if .IsFormatPatch }} 152 - {{ $patches := .AsFormatPatch }} 153 - {{ $round := .RoundNumber }} 154 - <details class="group py-2 md:ml-[3.5rem] text-gray-500 dark:text-gray-400 flex flex-col gap-2 relative text-sm"> 155 - <summary class="py-1 list-none cursor-pointer hover:text-gray-500 hover:dark:text-gray-400"> 156 - {{ $s := "s" }} 157 - {{ if eq (len $patches) 1 }} 158 - {{ $s = "" }} 159 - {{ end }} 160 - <div class="group-open:hidden flex items-center gap-2 ml-2"> 161 - {{ i "chevrons-up-down" "w-4 h-4" }} expand {{ len $patches }} commit{{$s}} 162 - </div> 163 - <div class="hidden group-open:flex items-center gap-2 ml-2"> 164 - {{ i "chevrons-down-up" "w-4 h-4" }} hide {{ len $patches }} commit{{$s}} 165 - </div> 166 - </summary> 167 - {{ range $patches }} 168 - <div id="commit-{{.SHA}}" class="py-1 px-2 relative w-full md:max-w-3/5 md:w-fit flex flex-col"> 169 - <div class="flex items-center gap-2"> 170 - {{ i "git-commit-horizontal" "w-4 h-4" }} 171 - <div class="text-sm text-gray-500 dark:text-gray-400"> 172 - <!-- attempt to resolve $fullRepo: this is possible only on non-deleted forks and branches --> 173 - {{ $fullRepo := "" }} 174 - {{ if and $.Pull.IsForkBased $.Pull.PullSource.Repo }} 175 - {{ $fullRepo = printf "%s/%s" $owner $.Pull.PullSource.Repo.Name }} 176 - {{ else if $.Pull.IsBranchBased }} 177 - {{ $fullRepo = $.RepoInfo.FullName }} 178 - {{ end }} 181 + {{ if eq $lastIdx $item.RoundNumber }} 182 + {{ block "mergeStatus" $root }} {{ end }} 183 + {{ block "resubmitStatus" $root }} {{ end }} 184 + {{ end }} 179 185 180 - <!-- if $fullRepo was resolved, link to it, otherwise just span without a link --> 181 - {{ if $fullRepo }} 182 - <a href="/{{ $fullRepo }}/commit/{{ .SHA }}" class="font-mono text-gray-500 dark:text-gray-400">{{ slice .SHA 0 8 }}</a> 183 - {{ else }} 184 - <span class="font-mono">{{ slice .SHA 0 8 }}</span> 185 - {{ end }} 186 - </div> 187 - <div class="flex items-center"> 188 - <span>{{ .Title | description }}</span> 189 - {{ if gt (len .Body) 0 }} 190 - <button 191 - class="py-1/2 px-1 mx-2 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600" 192 - hx-on:click="document.getElementById('body-{{$round}}-{{.SHA}}').classList.toggle('hidden')" 193 - > 194 - {{ i "ellipsis" "w-3 h-3" }} 195 - </button> 196 - {{ end }} 197 - </div> 198 - </div> 199 - {{ if gt (len .Body) 0 }} 200 - <p id="body-{{$round}}-{{.SHA}}" class="hidden mt-1 text-sm pb-2"> 201 - {{ nl2br .Body }} 202 - </p> 203 - {{ end }} 204 - </div> 186 + {{ if $root.LoggedInUser }} 187 + {{ template "repo/pulls/fragments/pullActions" 188 + (dict 189 + "LoggedInUser" $root.LoggedInUser 190 + "Pull" $root.Pull 191 + "RepoInfo" $root.RepoInfo 192 + "RoundNumber" $item.RoundNumber 193 + "MergeCheck" $root.MergeCheck 194 + "ResubmitCheck" $root.ResubmitCheck 195 + "BranchDeleteStatus" $root.BranchDeleteStatus 196 + "Stack" $root.Stack) }} 197 + {{ else }} 198 + {{ template "loginPrompt" $ }} 199 + {{ end }} 200 + </div> 201 + {{ end }} 202 + 203 + {{ define "submissionHeader" }} 204 + {{ $item := index . 0 }} 205 + {{ $lastIdx := index . 2 }} 206 + {{ $root := index . 3 }} 207 + {{ $round := $item.RoundNumber }} 208 + <div class="rounded px-6 py-4 pr-2 pt-2 bg-white dark:bg-gray-800 flex gap-2 sticky top-0 z-20 border-b border-gray-200 dark:border-gray-700"> 209 + <!-- left column: just profile picture --> 210 + <div class="flex-shrink-0 pt-2"> 211 + <img 212 + src="{{ tinyAvatar $root.Pull.OwnerDid }}" 213 + alt="" 214 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 215 + /> 216 + </div> 217 + <!-- right column --> 218 + <div class="flex-1 min-w-0 flex flex-col gap-1"> 219 + {{ template "submissionInfo" $ }} 220 + {{ template "submissionCommits" $ }} 221 + {{ template "submissionPipeline" $ }} 222 + {{ if eq $lastIdx $round }} 223 + {{ block "mergeCheck" $root }} {{ end }} 224 + {{ end }} 225 + </div> 226 + </div> 227 + {{ end }} 228 + 229 + {{ define "submissionInfo" }} 230 + {{ $item := index . 0 }} 231 + {{ $idx := index . 1 }} 232 + {{ $root := index . 3 }} 233 + {{ $round := $item.RoundNumber }} 234 + <div class="flex gap-2 items-center justify-between mb-1"> 235 + <span class="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 pt-2"> 236 + {{ resolve $root.Pull.OwnerDid }} submitted v{{ $round }} 237 + <span class="select-none before:content-['\00B7']"></span> 238 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ $round }}"> 239 + {{ template "repo/fragments/shortTimeAgo" $item.Created }} 240 + </a> 241 + </span> 242 + <div class="flex gap-2 items-center"> 243 + {{ if ne $root.ActiveRound $round }} 244 + <a class="btn-flat flex items-center gap-2 no-underline hover:no-underline text-sm" 245 + href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $round }}?{{ safeUrl $root.DiffOpts.Encode }}"> 246 + {{ i "diff" "w-4 h-4" }} 247 + diff 248 + </a> 249 + {{ end }} 250 + {{ if ne $idx 0 }} 251 + <a class="btn-flat flex items-center gap-2 no-underline hover:no-underline text-sm" 252 + href="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $round }}/interdiff?{{ safeUrl $root.DiffOpts.Encode }}"> 253 + {{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }} 254 + interdiff 255 + </a> 256 + {{ end }} 257 + </div> 258 + </div> 259 + {{ end }} 260 + 261 + {{ define "submissionCommits" }} 262 + {{ $item := index . 0 }} 263 + {{ $root := index . 3 }} 264 + {{ $round := $item.RoundNumber }} 265 + {{ $patches := $item.AsFormatPatch }} 266 + {{ if $patches }} 267 + <details class="group/commit"> 268 + <summary class="list-none cursor-pointer flex items-center gap-2"> 269 + <span>{{ i "git-commit-horizontal" "w-4 h-4" }}</span> 270 + {{ len $patches }} commit{{ if ne (len $patches) 1 }}s{{ end }} 271 + <div class="text-sm text-gray-500 dark:text-gray-400"> 272 + <span class="group-open/commit:hidden inline">expand</span> 273 + <span class="hidden group-open/commit:inline">collapse</span> 274 + </div> 275 + </summary> 276 + {{ range $patches }} 277 + {{ template "submissionCommit" (list . $item $root) }} 278 + {{ end }} 279 + </details> 280 + {{ end }} 281 + {{ end }} 282 + 283 + {{ define "submissionCommit" }} 284 + {{ $patch := index . 0 }} 285 + {{ $item := index . 1 }} 286 + {{ $root := index . 2 }} 287 + {{ $round := $item.RoundNumber }} 288 + {{ with $patch }} 289 + <div id="commit-{{.SHA}}" class="py-1 relative w-full md:max-w-3/5 md:w-fit flex flex-col text-gray-600 dark:text-gray-300"> 290 + <div class="flex items-baseline gap-2"> 291 + <div class="text-xs"> 292 + <!-- attempt to resolve $fullRepo: this is possible only on non-deleted forks and branches --> 293 + {{ $fullRepo := "" }} 294 + {{ if and $root.Pull.IsForkBased $root.Pull.PullSource.Repo }} 295 + {{ $fullRepo = printf "%s/%s" $root.Pull.OwnerDid $root.Pull.PullSource.Repo.Name }} 296 + {{ else if $root.Pull.IsBranchBased }} 297 + {{ $fullRepo = $root.RepoInfo.FullName }} 205 298 {{ end }} 206 - </details> 207 - {{ end }} 208 299 209 - 210 - <div class="md:pl-[3.5rem] flex flex-col gap-2 mt-2 relative"> 211 - {{ range $cidx, $c := .Comments }} 212 - <div id="comment-{{$c.ID}}" class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-2 px-4 relative w-full"> 213 - {{ if gt $cidx 0 }} 214 - <div class="absolute left-8 -top-2 w-px h-2 bg-gray-300 dark:bg-gray-600"></div> 215 - {{ end }} 216 - <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 217 - {{ template "user/fragments/picHandleLink" $c.OwnerDid }} 218 - <span class="before:content-['·']"></span> 219 - <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}">{{ template "repo/fragments/time" $c.Created }}</a> 220 - </div> 221 - <div class="prose dark:prose-invert"> 222 - {{ $c.Body | markdown }} 223 - </div> 224 - </div> 225 - {{ end }} 226 - 227 - {{ block "pipelineStatus" (list $ .) }} {{ end }} 228 - 229 - {{ if eq $lastIdx .RoundNumber }} 230 - {{ block "mergeStatus" $ }} {{ end }} 231 - {{ block "resubmitStatus" $ }} {{ end }} 232 - {{ end }} 233 - 234 - {{ if $.LoggedInUser }} 235 - {{ template "repo/pulls/fragments/pullActions" 236 - (dict 237 - "LoggedInUser" $.LoggedInUser 238 - "Pull" $.Pull 239 - "RepoInfo" $.RepoInfo 240 - "RoundNumber" .RoundNumber 241 - "MergeCheck" $.MergeCheck 242 - "ResubmitCheck" $.ResubmitCheck 243 - "BranchDeleteStatus" $.BranchDeleteStatus 244 - "Stack" $.Stack) }} 300 + <!-- if $fullRepo was resolved, link to it, otherwise just span without a link --> 301 + {{ if $fullRepo }} 302 + <a href="/{{ $fullRepo }}/commit/{{ .SHA }}" class="font-mono text-gray-600 dark:text-gray-300">{{ slice .SHA 0 8 }}</a> 245 303 {{ else }} 246 - <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm p-2 relative flex gap-2 items-center w-fit"> 247 - <a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2"> 248 - sign up 249 - </a> 250 - <span class="text-gray-500 dark:text-gray-400">or</span> 251 - <a href="/login" class="underline">login</a> 252 - to add to the discussion 253 - </div> 304 + <span class="font-mono">{{ slice .SHA 0 8 }}</span> 254 305 {{ end }} 255 306 </div> 307 + 308 + <div> 309 + <span>{{ .Title | description }}</span> 310 + {{ if gt (len .Body) 0 }} 311 + <button 312 + class="py-1/2 px-1 mx-2 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600" 313 + hx-on:click="document.getElementById('body-{{$round}}-{{.SHA}}').classList.toggle('hidden')" 314 + > 315 + {{ i "ellipsis" "w-3 h-3" }} 316 + </button> 317 + {{ end }} 318 + {{ if gt (len .Body) 0 }} 319 + <p id="body-{{$round}}-{{.SHA}}" class="hidden mt-1 pb-2">{{ nl2br .Body }}</p> 320 + {{ end }} 321 + </div> 322 + </div> 323 + </div> 324 + {{ end }} 325 + {{ end }} 326 + 327 + {{ define "mergeCheck" }} 328 + {{ $isOpen := .Pull.State.IsOpen }} 329 + {{ if and $isOpen .MergeCheck .MergeCheck.Error }} 330 + <div class="flex items-center gap-2"> 331 + {{ i "triangle-alert" "w-4 h-4 text-red-600 dark:text-red-500" }} 332 + {{ .MergeCheck.Error }} 333 + </div> 334 + {{ else if and $isOpen .MergeCheck .MergeCheck.IsConflicted }} 335 + <details class="group/conflict"> 336 + <summary class="flex items-center justify-between cursor-pointer list-none"> 337 + <div class="flex items-center gap-2 "> 338 + {{ i "triangle-alert" "text-red-600 dark:text-red-500 w-4 h-4" }} 339 + <span class="font-medium">merge conflicts detected</span> 340 + <div class="text-sm text-gray-500 dark:text-gray-400"> 341 + <span class="group-open/conflict:hidden inline">expand</span> 342 + <span class="hidden group-open/conflict:inline">collapse</span> 343 + </div> 344 + </div> 345 + </summary> 346 + {{ if gt (len .MergeCheck.Conflicts) 0 }} 347 + <ul class="space-y-1 mt-2 overflow-x-auto"> 348 + {{ range .MergeCheck.Conflicts }} 349 + {{ if .Filename }} 350 + <li class="flex items-center whitespace-nowrap"> 351 + {{ i "file-warning" "inline-flex w-4 h-4 mr-1.5 text-red-600 dark:text-red-500 flex-shrink-0" }} 352 + <span class="font-mono">{{ .Filename }}</span> 353 + </li> 354 + {{ else if .Reason }} 355 + <li class="flex items-center whitespace-nowrap"> 356 + {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-600 dark:text-red-500 " }} 357 + <span>{{.Reason}}</span> 358 + </li> 359 + {{ end }} 360 + {{ end }} 361 + </ul> 362 + {{ end }} 256 363 </details> 257 - {{ end }} 364 + {{ else if and $isOpen .MergeCheck }} 365 + <div class="flex items-center gap-2"> 366 + {{ i "check" "w-4 h-4 text-green-600 dark:text-green-500" }} 367 + <span>no conflicts, ready to merge</span> 368 + </div> 258 369 {{ end }} 259 370 {{ end }} 260 371 261 372 {{ define "mergeStatus" }} 262 373 {{ if .Pull.State.IsClosed }} 263 - <div class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 374 + <div class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded drop-shadow-sm px-6 py-2 relative"> 264 375 <div class="flex items-center gap-2 text-black dark:text-white"> 265 376 {{ i "ban" "w-4 h-4" }} 266 377 <span class="font-medium">closed without merging</span ··· 423 224 </div> 424 225 </div> 425 226 {{ else if .Pull.State.IsMerged }} 426 - <div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 227 + <div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative"> 427 228 <div class="flex items-center gap-2 text-purple-500 dark:text-purple-300"> 428 229 {{ i "git-merge" "w-4 h-4" }} 429 230 <span class="font-medium">pull request successfully merged</span ··· 431 232 </div> 432 233 </div> 433 234 {{ else if .Pull.State.IsDeleted }} 434 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 235 + <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative"> 435 236 <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 436 237 {{ i "git-pull-request-closed" "w-4 h-4" }} 437 238 <span class="font-medium">This pull has been deleted (possibly by jj abandon or jj squash)</span> 438 - </div> 439 - </div> 440 - {{ else if and .MergeCheck .MergeCheck.Error }} 441 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 442 - <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 443 - {{ i "triangle-alert" "w-4 h-4" }} 444 - <span class="font-medium">{{ .MergeCheck.Error }}</span> 445 - </div> 446 - </div> 447 - {{ else if and .MergeCheck .MergeCheck.IsConflicted }} 448 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 449 - <div class="flex flex-col gap-2 text-red-500 dark:text-red-300"> 450 - <div class="flex items-center gap-2"> 451 - {{ i "triangle-alert" "w-4 h-4" }} 452 - <span class="font-medium">merge conflicts detected</span> 453 - </div> 454 - {{ if gt (len .MergeCheck.Conflicts) 0 }} 455 - <ul class="space-y-1"> 456 - {{ range .MergeCheck.Conflicts }} 457 - {{ if .Filename }} 458 - <li class="flex items-center"> 459 - {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} 460 - <span class="font-mono">{{ .Filename }}</span> 461 - </li> 462 - {{ else if .Reason }} 463 - <li class="flex items-center"> 464 - {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} 465 - <span>{{.Reason}}</span> 466 - </li> 467 - {{ end }} 468 - {{ end }} 469 - </ul> 470 - {{ end }} 471 - </div> 472 - </div> 473 - {{ else if .MergeCheck }} 474 - <div class="bg-green-50 dark:bg-green-900 border border-green-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 475 - <div class="flex items-center gap-2 text-green-500 dark:text-green-300"> 476 - {{ i "circle-check-big" "w-4 h-4" }} 477 - <span class="font-medium">no conflicts, ready to merge</span> 478 239 </div> 479 240 </div> 480 241 {{ end }} ··· 442 283 443 284 {{ define "resubmitStatus" }} 444 285 {{ if .ResubmitCheck.Yes }} 445 - <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 286 + <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative"> 446 287 <div class="flex items-center gap-2 text-amber-500 dark:text-amber-300"> 447 288 {{ i "triangle-alert" "w-4 h-4" }} 448 289 <span class="font-medium">this branch has been updated, consider resubmitting</span> ··· 451 292 {{ end }} 452 293 {{ end }} 453 294 454 - {{ define "pipelineStatus" }} 455 - {{ $root := index . 0 }} 456 - {{ $submission := index . 1 }} 457 - {{ $pipeline := index $root.Pipelines $submission.SourceRev }} 295 + {{ define "submissionPipeline" }} 296 + {{ $item := index . 0 }} 297 + {{ $root := index . 3 }} 298 + {{ $pipeline := index $root.Pipelines $item.SourceRev }} 458 299 {{ with $pipeline }} 459 300 {{ $id := .Id }} 460 301 {{ if .Statuses }} 461 - <div class="max-w-80 grid grid-cols-1 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 462 - {{ range $name, $all := .Statuses }} 463 - <a href="/{{ $root.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 464 - <div 465 - class="flex gap-2 items-center justify-between p-2"> 466 - {{ $lastStatus := $all.Latest }} 467 - {{ $kind := $lastStatus.Status.String }} 468 - 469 - <div id="left" class="flex items-center gap-2 flex-shrink-0"> 470 - {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 471 - {{ $name }} 472 - </div> 473 - <div id="right" class="flex items-center gap-2 flex-shrink-0"> 474 - <span class="font-bold">{{ $kind }}</span> 475 - {{ if .TimeTaken }} 476 - {{ template "repo/fragments/duration" .TimeTaken }} 477 - {{ else }} 478 - {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 479 - {{ end }} 480 - </div> 302 + <details class="group/pipeline"> 303 + <summary class="cursor-pointer list-none flex items-center gap-2"> 304 + {{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" false) }} 305 + <div class="text-sm text-gray-500 dark:text-gray-400"> 306 + <span class="group-open/pipeline:hidden inline">expand</span> 307 + <span class="hidden group-open/pipeline:inline">collapse</span> 481 308 </div> 482 - </a> 483 - {{ end }} 484 - </div> 309 + </summary> 310 + <div class="my-2 grid grid-cols-1 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 311 + {{ range $name, $all := .Statuses }} 312 + <a href="/{{ $root.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 313 + <div 314 + class="flex gap-2 items-center justify-between p-2"> 315 + {{ $lastStatus := $all.Latest }} 316 + {{ $kind := $lastStatus.Status.String }} 317 + 318 + <div id="left" class="flex items-center gap-2 flex-shrink-0"> 319 + {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 320 + {{ $name }} 321 + </div> 322 + <div id="right" class="flex items-center gap-2 flex-shrink-0"> 323 + <span class="font-bold">{{ $kind }}</span> 324 + {{ if .TimeTaken }} 325 + {{ template "repo/fragments/duration" .TimeTaken }} 326 + {{ else }} 327 + {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 328 + {{ end }} 329 + </div> 330 + </div> 331 + </a> 332 + {{ end }} 333 + </div> 334 + </details> 485 335 {{ end }} 486 336 {{ end }} 337 + {{ end }} 338 + 339 + {{ define "submissionComments" }} 340 + {{ $item := index . 0 }} 341 + <div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700"> 342 + {{ range $item.Comments }} 343 + {{ template "submissionComment" . }} 344 + {{ end }} 345 + </div> 346 + {{ end }} 347 + 348 + {{ define "submissionComment" }} 349 + <div id="comment-{{.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 350 + <!-- left column: profile picture --> 351 + <div class="flex-shrink-0"> 352 + <img 353 + src="{{ tinyAvatar .OwnerDid }}" 354 + alt="" 355 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 356 + /> 357 + </div> 358 + <!-- right column: name and body in two rows --> 359 + <div class="flex-1 min-w-0"> 360 + <!-- Row 1: Author and timestamp --> 361 + <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 362 + <span>{{ resolve .OwnerDid }}</span> 363 + <span class="before:content-['·']"></span> 364 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}"> 365 + {{ template "repo/fragments/time" .Created }} 366 + </a> 367 + </div> 368 + <!-- Row 2: Body text --> 369 + <div class="prose dark:prose-invert mt-1"> 370 + {{ .Body | markdown }} 371 + </div> 372 + </div> 373 + </div> 374 + {{ end }} 375 + 376 + {{ define "loginPrompt" }} 377 + <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm p-2 relative flex gap-2 items-center"> 378 + <a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2"> 379 + sign up 380 + </a> 381 + <span class="text-gray-500 dark:text-gray-400">or</span> 382 + <a href="/login" class="underline">login</a> 383 + to add to the discussion 384 + </div> 487 385 {{ end }}