Monorepo for Tangled tangled.org

appview/pages: rework the PR page entirely #973

merged opened by oppi.li targeting master from op/vyrymqtwolsn

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

Labels

None yet.

assignee

None yet.

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

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
appview/pages: rework the PR page entirely
3/3 success
expand
expand 0 comments
pull request successfully merged
oppi.li submitted #0
1 commit
expand
appview/pages: rework the PR page entirely
3/3 success
expand
expand 0 comments