this repo has no description

wip: rework PR interface

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

+20 -7
appview/pages/funcmap.go
··· 334 }, 335 "deref": func(v any) any { 336 val := reflect.ValueOf(v) 337 - if val.Kind() == reflect.Ptr && !val.IsNil() { 338 return val.Elem().Interface() 339 } 340 return nil ··· 366 return p.AvatarUrl(handle, "") 367 }, 368 "langColor": enry.GetColor, 369 - "layoutSide": func() string { 370 - return "col-span-1 md:col-span-2 lg:col-span-3" 371 - }, 372 - "layoutCenter": func() string { 373 - return "col-span-1 md:col-span-8 lg:col-span-6" 374 - }, 375 376 "normalizeForHtmlId": func(s string) string { 377 normalized := strings.ReplaceAll(s, ":", "_") 378 normalized = strings.ReplaceAll(normalized, ".", "_")
··· 334 }, 335 "deref": func(v any) any { 336 val := reflect.ValueOf(v) 337 + if val.Kind() == reflect.Pointer && !val.IsNil() { 338 return val.Elem().Interface() 339 } 340 return nil ··· 366 return p.AvatarUrl(handle, "") 367 }, 368 "langColor": enry.GetColor, 369 + "reverse": func(s any) any { 370 + if s == nil { 371 + return nil 372 + } 373 + 374 + v := reflect.ValueOf(s) 375 + 376 + if v.Kind() != reflect.Slice { 377 + return s 378 + } 379 + 380 + length := v.Len() 381 + reversed := reflect.MakeSlice(v.Type(), length, length) 382 383 + for i := range length { 384 + reversed.Index(i).Set(v.Index(length - 1 - i)) 385 + } 386 + 387 + return reversed.Interface() 388 + }, 389 "normalizeForHtmlId": func(s string) string { 390 normalized := strings.ReplaceAll(s, ":", "_") 391 normalized = strings.ReplaceAll(normalized, ".", "_")
+2
appview/pages/pages.go
··· 1103 MergeCheck types.MergeCheckResponse 1104 ResubmitCheck ResubmitResult 1105 Pipelines map[string]models.Pipeline 1106 1107 OrderedReactionKinds []models.ReactionKind 1108 Reactions map[models.ReactionKind]models.ReactionDisplayData
··· 1103 MergeCheck types.MergeCheckResponse 1104 ResubmitCheck ResubmitResult 1105 Pipelines map[string]models.Pipeline 1106 + Diff *types.NiceDiff 1107 + DiffOpts types.DiffOpts 1108 1109 OrderedReactionKinds []models.ReactionKind 1110 Reactions map[models.ReactionKind]models.ReactionDisplayData
+1 -1
appview/pages/templates/layouts/repobase.html
··· 1 {{ define "title" }}{{ .RepoInfo.FullName }}{{ end }} 2 3 {{ define "content" }} 4 - <section id="repo-header" class="mb-4 p-2 dark:text-white"> 5 <div class="text-lg flex flex-col sm:flex-row items-start gap-4 justify-between"> 6 <!-- left items --> 7 <div class="flex flex-col gap-2">
··· 1 {{ define "title" }}{{ .RepoInfo.FullName }}{{ end }} 2 3 {{ define "content" }} 4 + <section id="repo-header" class="mb-2 py-2 px-4 dark:text-white"> 5 <div class="text-lg flex flex-col sm:flex-row items-start gap-4 justify-between"> 6 <!-- left items --> 7 <div class="flex flex-col gap-2">
+35 -22
appview/pages/templates/repo/issues/fragments/commentList.html
··· 1 {{ define "repo/issues/fragments/commentList" }} 2 - <div class="flex flex-col gap-8"> 3 {{ range $item := .CommentList }} 4 {{ template "commentListing" (list $ .) }} 5 {{ end }} ··· 19 <div class="rounded border border-gray-200 dark:border-gray-700 w-full overflow-hidden shadow-sm bg-gray-50 dark:bg-gray-800/50"> 20 {{ template "topLevelComment" $params }} 21 22 - <div class="relative ml-4 border-l-2 border-gray-200 dark:border-gray-700"> 23 {{ range $index, $reply := $comment.Replies }} 24 - <div class="relative "> 25 - <!-- Horizontal connector --> 26 - <div class="absolute left-0 top-6 w-4 h-1 bg-gray-200 dark:bg-gray-700"></div> 27 - 28 - <div class="pl-2"> 29 - {{ 30 - template "replyComment" 31 - (dict 32 - "RepoInfo" $root.RepoInfo 33 - "LoggedInUser" $root.LoggedInUser 34 - "Issue" $root.Issue 35 - "Comment" $reply) 36 - }} 37 - </div> 38 </div> 39 {{ end }} 40 </div> ··· 44 {{ end }} 45 46 {{ define "topLevelComment" }} 47 - <div class="rounded px-6 py-4 bg-white dark:bg-gray-800"> 48 - {{ template "repo/issues/fragments/issueCommentHeader" . }} 49 - {{ template "repo/issues/fragments/issueCommentBody" . }} 50 </div> 51 {{ end }} 52 53 {{ define "replyComment" }} 54 - <div class="p-4 w-full mx-auto overflow-hidden"> 55 - {{ template "repo/issues/fragments/issueCommentHeader" . }} 56 - {{ template "repo/issues/fragments/issueCommentBody" . }} 57 </div> 58 {{ end }}
··· 1 {{ define "repo/issues/fragments/commentList" }} 2 + <div class="flex flex-col gap-4"> 3 {{ range $item := .CommentList }} 4 {{ template "commentListing" (list $ .) }} 5 {{ end }} ··· 19 <div class="rounded border border-gray-200 dark:border-gray-700 w-full overflow-hidden shadow-sm bg-gray-50 dark:bg-gray-800/50"> 20 {{ template "topLevelComment" $params }} 21 22 + <div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700"> 23 {{ range $index, $reply := $comment.Replies }} 24 + <div class="-ml-4"> 25 + {{ 26 + template "replyComment" 27 + (dict 28 + "RepoInfo" $root.RepoInfo 29 + "LoggedInUser" $root.LoggedInUser 30 + "Issue" $root.Issue 31 + "Comment" $reply) 32 + }} 33 </div> 34 {{ end }} 35 </div> ··· 39 {{ end }} 40 41 {{ define "topLevelComment" }} 42 + <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2 "> 43 + <div class="flex-shrink-0"> 44 + <img 45 + src="{{ tinyAvatar .Comment.Did }}" 46 + alt="" 47 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 48 + /> 49 + </div> 50 + <div class="flex-1 min-w-0"> 51 + {{ template "repo/issues/fragments/issueCommentHeader" . }} 52 + {{ template "repo/issues/fragments/issueCommentBody" . }} 53 + </div> 54 </div> 55 {{ end }} 56 57 {{ define "replyComment" }} 58 + <div class="py-4 pr-4 w-full mx-auto overflow-hidden flex gap-2 "> 59 + <div class="flex-shrink-0"> 60 + <img 61 + src="{{ tinyAvatar .Comment.Did }}" 62 + alt="" 63 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 64 + /> 65 + </div> 66 + <div class="flex-1 min-w-0"> 67 + {{ template "repo/issues/fragments/issueCommentHeader" . }} 68 + {{ template "repo/issues/fragments/issueCommentBody" . }} 69 + </div> 70 </div> 71 {{ end }}
-63
appview/pages/templates/repo/issues/fragments/globalIssueListing.html
··· 1 - {{ define "repo/issues/fragments/globalIssueListing" }} 2 - <div class="flex flex-col gap-2"> 3 - {{ range .Issues }} 4 - <div class="rounded drop-shadow-sm bg-white px-6 py-4 dark:bg-gray-800 dark:border-gray-700"> 5 - <div class="pb-2 mb-3"> 6 - <div class="flex items-center gap-3 mb-2"> 7 - <a 8 - href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}" 9 - class="text-blue-600 dark:text-blue-400 font-medium hover:underline text-sm" 10 - > 11 - {{ resolve .Repo.Did }}/{{ .Repo.Name }} 12 - </a> 13 - </div> 14 - <a 15 - href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}/issues/{{ .IssueId }}" 16 - class="no-underline hover:underline" 17 - > 18 - {{ .Title | description }} 19 - <span class="text-gray-500">#{{ .IssueId }}</span> 20 - </a> 21 - </div> 22 - <div class="text-sm text-gray-500 dark:text-gray-400 flex flex-wrap items-center gap-1"> 23 - {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 24 - {{ $icon := "ban" }} 25 - {{ $state := "closed" }} 26 - {{ if .Open }} 27 - {{ $bgColor = "bg-green-600 dark:bg-green-700" }} 28 - {{ $icon = "circle-dot" }} 29 - {{ $state = "open" }} 30 - {{ end }} 31 - 32 - <span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm"> 33 - {{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }} 34 - <span class="text-white dark:text-white">{{ $state }}</span> 35 - </span> 36 - 37 - <span class="ml-1"> 38 - {{ template "user/fragments/picHandleLink" .Did }} 39 - </span> 40 - 41 - <span class="before:content-['·']"> 42 - {{ template "repo/fragments/time" .Created }} 43 - </span> 44 - 45 - <span class="before:content-['·']"> 46 - {{ $s := "s" }} 47 - {{ if eq (len .Comments) 1 }} 48 - {{ $s = "" }} 49 - {{ end }} 50 - <a href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}/issues/{{ .IssueId }}" class="text-gray-500 dark:text-gray-400">{{ len .Comments }} comment{{$s}}</a> 51 - </span> 52 - 53 - {{ $state := .Labels }} 54 - {{ range $k, $d := $.LabelDefs }} 55 - {{ range $v, $s := $state.GetValSet $d.AtUri.String }} 56 - {{ template "labels/fragments/label" (dict "def" $d "val" $v "withPrefix" true) }} 57 - {{ end }} 58 - {{ end }} 59 - </div> 60 - </div> 61 - {{ end }} 62 - </div> 63 - {{ end }}
···
+2 -1
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
··· 1 {{ define "repo/issues/fragments/issueCommentHeader" }} 2 <div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 "> 3 - {{ template "user/fragments/picHandleLink" .Comment.Did }} 4 {{ template "hats" $ }} 5 {{ template "timestamp" . }} 6 {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 7 {{ if and $isCommentOwner (not .Comment.Deleted) }}
··· 1 {{ define "repo/issues/fragments/issueCommentHeader" }} 2 <div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 "> 3 + {{ resolve .Comment.Did }} 4 {{ template "hats" $ }} 5 + <span class="before:content-['·']"></span> 6 {{ template "timestamp" . }} 7 {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 8 {{ if and $isCommentOwner (not .Comment.Deleted) }}
+1 -1
appview/pages/templates/repo/issues/fragments/putIssue.html
··· 18 <textarea 19 name="body" 20 id="body" 21 - rows="6" 22 class="w-full resize-y" 23 placeholder="Describe your issue. Markdown is supported." 24 >{{ if .Issue }}{{ .Issue.Body }}{{ end }}</textarea>
··· 18 <textarea 19 name="body" 20 id="body" 21 + rows="15" 22 class="w-full resize-y" 23 placeholder="Describe your issue. Markdown is supported." 24 >{{ if .Issue }}{{ .Issue.Body }}{{ end }}</textarea>
+3 -3
appview/pages/templates/repo/issues/fragments/replyIssueCommentPlaceholder.html
··· 1 {{ define "repo/issues/fragments/replyIssueCommentPlaceholder" }} 2 - <div class="p-2 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 3 {{ if .LoggedInUser }} 4 <img 5 src="{{ tinyAvatar .LoggedInUser.Did }}" 6 alt="" 7 - class="rounded-full h-6 w-6 mr-1 border border-gray-300 dark:border-gray-700" 8 /> 9 {{ end }} 10 <input 11 - class="w-full py-2 border-none focus:outline-none" 12 placeholder="Leave a reply..." 13 hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/reply" 14 hx-trigger="focus"
··· 1 {{ define "repo/issues/fragments/replyIssueCommentPlaceholder" }} 2 + <div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 3 {{ if .LoggedInUser }} 4 <img 5 src="{{ tinyAvatar .LoggedInUser.Did }}" 6 alt="" 7 + class="rounded-full size-8 mr-1 border-2 border-gray-300 dark:border-gray-700" 8 /> 9 {{ end }} 10 <input 11 + class="w-full p-0 border-none focus:outline-none" 12 placeholder="Leave a reply..." 13 hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/reply" 14 hx-trigger="focus"
+1 -1
appview/pages/templates/repo/pulls/fragments/pullActions.html
··· 22 {{ $isLastRound := eq $roundNumber $lastIdx }} 23 {{ $isSameRepoBranch := .Pull.IsBranchBased }} 24 {{ $isUpToDate := .ResubmitCheck.No }} 25 - <div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative"> 26 <button 27 hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ $roundNumber }}/comment" 28 hx-target="#actions-{{$roundNumber}}"
··· 22 {{ $isLastRound := eq $roundNumber $lastIdx }} 23 {{ $isSameRepoBranch := .Pull.IsBranchBased }} 24 {{ $isUpToDate := .ResubmitCheck.No }} 25 + <div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative py-2 px-4"> 26 <button 27 hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ $roundNumber }}/comment" 28 hx-target="#actions-{{$roundNumber}}"
+20
appview/pages/templates/repo/pulls/fragments/replyPullCommentPlaceholder.html
···
··· 1 + {{ define "repo/pulls/fragments/replyPullCommentPlaceholder" }} 2 + <div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 3 + {{ if .LoggedInUser }} 4 + <img 5 + src="{{ tinyAvatar .LoggedInUser.Did }}" 6 + alt="" 7 + class="rounded-full size-8 mr-1 border-2 border-gray-300 dark:border-gray-700" 8 + /> 9 + {{ end }} 10 + <input 11 + class="w-full p-0 border-none focus:outline-none" 12 + placeholder="Leave a reply..." 13 + hx-get="/{{ .Submission.ID }}/reply" 14 + hx-trigger="focus" 15 + hx-target="closest div" 16 + hx-swap="outerHTML" 17 + > 18 + </input> 19 + </div> 20 + {{ end }}
+229 -27
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 ··· 26 "Backlinks" $.Backlinks) }} 27 {{ template "repo/fragments/externalLinkPanel" $.Pull.AtUri }} 28 </div> 29 </div> 30 {{ end }} 31 ··· 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" }} ··· 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" }} ··· 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> ··· 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> ··· 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
··· 6 {{ template "repo/pulls/fragments/og" (dict "RepoInfo" .RepoInfo "Pull" .Pull) }} 7 {{ end }} 8 9 + {{ define "mainLayout" }} 10 + <div class="px-1 col-span-full flex-grow flex flex-col gap-4"> 11 + {{ block "contentLayout" . }} 12 + {{ block "content" . }}{{ end }} 13 + {{ end }} 14 + </div> 15 + {{ end }} 16 + 17 {{ define "repoContentLayout" }} 18 <div class="grid grid-cols-1 md:grid-cols-10 gap-4 w-full"> 19 + <div class="col-span-1 md:col-span-7"> 20 <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white"> 21 {{ block "repoContent" . }}{{ end }} 22 </section> 23 {{ block "repoAfter" . }}{{ end }} 24 </div> 25 + <div class="col-span-1 md:col-span-3 flex flex-col gap-6"> 26 {{ template "repo/fragments/labelPanel" 27 (dict "RepoInfo" $.RepoInfo 28 "Defs" $.LabelDefs ··· 34 "Backlinks" $.Backlinks) }} 35 {{ template "repo/fragments/externalLinkPanel" $.Pull.AtUri }} 36 </div> 37 + 38 + <div class="col-span-1 md:col-span-7"> 39 + {{ template "repo/fragments/diff" (list .Diff .DiffOpts) }} 40 + </div> 41 + <div class="col-span-1 md:col-span-3 flex flex-col max-h-dvh sticky top-0"> 42 + <div class="z-20 sticky top-0 rounded-t p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700"> 43 + <h2 class="font-bold uppercase">history</h2> 44 + </div> 45 + <div class="flex flex-col-reverse gap-4 overflow-y-auto"> 46 + {{ template "submissions2" . }} 47 + </div> 48 + </div> 49 </div> 50 {{ end }} 51 ··· 60 {{ end }} 61 62 {{ define "repoAfter" }} 63 + <div id="pull-close"></div> 64 + <div id="pull-reopen"></div> 65 + {{ end }} 66 + 67 + {{ define "submissions2" }} 68 + {{ $lastIdx := sub (len .Pull.Submissions) 1 }} 69 + {{ range $ridx, $item := reverse .Pull.Submissions }} 70 + {{ $idx := sub $lastIdx $ridx }} 71 + <div class="rounded border border-gray-200 dark:border-gray-700 w-full shadow-sm bg-gray-50 dark:bg-gray-800/50"> 72 + {{ with $item }} 73 + {{ $patches := .AsFormatPatch }} 74 + {{ $round := .RoundNumber }} 75 + <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2"> 76 + <div class="flex-shrink-0"> 77 + <img 78 + src="{{ tinyAvatar $.Pull.OwnerDid }}" 79 + alt="" 80 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 81 + /> 82 + </div> 83 + <!-- right column: name and body in two rows --> 84 + <div class="flex-1 min-w-0 flex flex-col gap-2"> 85 + <div class="flex gap-2 items-center justify-between mb-1"> 86 + <span class="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-500"> 87 + {{ resolve $.Pull.OwnerDid }} submitted v{{ $round }} 88 + <span class="select-none before:content-['\00B7']"></span> 89 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ $round }}">{{ template "repo/fragments/shortTimeAgo" .Created }}</a> 90 + </span> 91 + {{ if ne $idx 0 }} 92 + <a class="flex items-center gap-2 no-underline hover:no-underline text-sm" 93 + hx-boost="true" 94 + href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{$round}}/interdiff"> 95 + {{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }} 96 + <span class="hidden md:inline">interdiff</span> 97 + </a> 98 + {{ end }} 99 + </div> 100 + <div> 101 + {{ if eq 1 (len $patches) }} 102 + <!-- only one commit, just inline the message into the round header --> 103 + {{ $commit := index $patches 0 }} 104 + <span>{{ $commit.Title | description }}</span> 105 + {{ if gt (len $commit.Body) 0 }} 106 + <p id="body-{{$round}}-{{$commit.SHA}}" class="mt-1 pb-2"> 107 + {{ nl2br $commit.Body }} 108 + </p> 109 + {{ end }} 110 + {{ else }} 111 + <span>Commits:</span> 112 + {{ range $patches }} 113 + <div id="commit-{{.SHA}}" class="py-1 px-2 relative w-full md:max-w-3/5 md:w-fit flex flex-col"> 114 + <div class="flex items-center gap-2"> 115 + {{ i "git-commit-horizontal" "w-4 h-4 flex-shrink-0" }} 116 + <div class="flex items-center"> 117 + <span>{{ .Title | description }}</span> 118 + {{ if gt (len .Body) 0 }} 119 + <button 120 + 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" 121 + hx-on:click="document.getElementById('body-{{$round}}-{{.SHA}}').classList.toggle('hidden')" 122 + > 123 + {{ i "ellipsis" "w-3 h-3" }} 124 + </button> 125 + {{ end }} 126 + </div> 127 + </div> 128 + {{ if gt (len .Body) 0 }} 129 + <p id="body-{{$round}}-{{.SHA}}" class="hidden mt-1 text-sm pb-2"> 130 + {{ nl2br .Body }} 131 + </p> 132 + {{ end }} 133 + </div> 134 + {{ end }} 135 + {{ end }} 136 + </div> 137 + <div> 138 + {{ block "pipelineStatus" (list $ .) }} {{ end }} 139 + </div> 140 + </div> 141 + </div> 142 + <div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700"> 143 + {{ range $cidx, $c := .Comments }} 144 + <div id="comment-{{$c.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 145 + <!-- left column: profile picture --> 146 + <div class="flex-shrink-0"> 147 + <img 148 + src="{{ tinyAvatar $c.OwnerDid }}" 149 + alt="" 150 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 151 + /> 152 + </div> 153 + <!-- right column: name and body in two rows --> 154 + <div class="flex-1 min-w-0"> 155 + <!-- Row 1: Author and timestamp --> 156 + <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 157 + <span>{{ resolve $c.OwnerDid }}</span> 158 + <span class="before:content-['·']"></span> 159 + <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> 160 + </div> 161 + <!-- Row 2: Body text --> 162 + <div class="prose dark:prose-invert mt-1"> 163 + {{ $c.Body | markdown }} 164 + </div> 165 + </div> 166 + </div> 167 + {{ end }} 168 + </div> 169 + {{ end }} 170 + {{ if eq $lastIdx .RoundNumber }} 171 + {{ block "mergeStatus" $ }} {{ end }} 172 + {{ block "resubmitStatus" $ }} {{ end }} 173 + {{ end }} 174 + {{ if $.LoggedInUser }} 175 + {{ if eq $lastIdx .RoundNumber }} 176 + {{ template "newComment" (list $ .) }} 177 + {{ else }} 178 + {{ template "repo/pulls/fragments/replyPullCommentPlaceholder" (dict "LoggedInUser" $.LoggedInUser "Submission" .) }} 179 + {{ end }} 180 + {{ else }} 181 + <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"> 182 + <a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2"> 183 + sign up 184 + </a> 185 + <span class="text-gray-500 dark:text-gray-400">or</span> 186 + <a href="/login" class="underline">login</a> 187 + to add to the discussion 188 + </div> 189 + {{ end }} 190 + </div> 191 + {{ end }} 192 + {{ end }} 193 + 194 + {{ define "newComment" }} 195 + {{ $root := index . 0 }} 196 + {{ $submission := index . 1 }} 197 + <form 198 + id="comment-form" 199 + hx-post="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $submission.RoundNumber }}/comment" 200 + hx-on::after-request="if(event.detail.successful) this.reset()" 201 + > 202 + <div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-full"> 203 + <div class="text-sm pb-2 text-gray-500 dark:text-gray-400"> 204 + {{ template "user/fragments/picHandleLink" $root.LoggedInUser.Did }} 205 </div> 206 + <textarea 207 + id="comment-textarea" 208 + name="body" 209 + class="w-full p-2 rounded border border-gray-200 dark:border-gray-700" 210 + placeholder="Add to the discussion" 211 + rows="8" 212 + ></textarea> 213 + <div id="pull-comment"></div> 214 + </div> 215 + {{ template "replyActions" . }} 216 + </form> 217 + {{ end }} 218 219 + {{ define "replyActions" }} 220 + <div class="flex flex-wrap items-stretch justify-end gap-2 text-gray-500 dark:text-gray-400 text-sm"> 221 + {{ template "cancel" . }} 222 + {{ template "reply" . }} 223 + </div> 224 + {{ end }} 225 + 226 + {{ define "cancel" }} 227 + <button 228 + class="btn text-red-500 dark:text-red-400 flex gap-2 items-center group" 229 + hx-get="TODO" 230 + hx-target="TODO" 231 + hx-swap="outerHTML"> 232 + {{ i "x" "size-4" }} 233 + cancel 234 + </button> 235 + {{ end }} 236 + 237 + {{ define "reply" }} 238 + <button 239 + id="TODO" 240 + type="submit" 241 + class="btn-create flex items-center gap-2 no-underline hover:no-underline"> 242 + {{ i "reply" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 243 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 244 + reply 245 + </button> 246 {{ end }} 247 248 {{ define "submissions" }} ··· 411 412 {{ define "mergeStatus" }} 413 {{ if .Pull.State.IsClosed }} 414 + <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"> 415 <div class="flex items-center gap-2 text-black dark:text-white"> 416 {{ i "ban" "w-4 h-4" }} 417 <span class="font-medium">closed without merging</span ··· 419 </div> 420 </div> 421 {{ else if .Pull.State.IsMerged }} 422 + <div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative"> 423 <div class="flex items-center gap-2 text-purple-500 dark:text-purple-300"> 424 {{ i "git-merge" "w-4 h-4" }} 425 <span class="font-medium">pull request successfully merged</span ··· 427 </div> 428 </div> 429 {{ else if .Pull.State.IsDeleted }} 430 + <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative"> 431 <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 432 {{ i "git-pull-request-closed" "w-4 h-4" }} 433 <span class="font-medium">This pull has been deleted (possibly by jj abandon or jj squash)</span> 434 </div> 435 </div> 436 {{ else if and .MergeCheck .MergeCheck.Error }} 437 + <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative"> 438 <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 439 {{ i "triangle-alert" "w-4 h-4" }} 440 <span class="font-medium">{{ .MergeCheck.Error }}</span> 441 </div> 442 </div> 443 {{ else if and .MergeCheck .MergeCheck.IsConflicted }} 444 + <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative"> 445 + <details class="text-red-500 dark:text-red-300 group"> 446 + <summary class="flex items-center justify-between cursor-pointer list-none"> 447 + <div class="flex items-center gap-2 "> 448 + {{ i "triangle-alert" "w-4 h-4" }} 449 + <span class="font-medium">merge conflicts detected</span> 450 + </div> 451 + <div> 452 + <span class="group-open:hidden inline">{{ i "chevrons-up-down" "w-4 h-4" }}</span> 453 + <span class="hidden group-open:inline">{{ i "chevrons-down-up" "w-4 h-4" }}</span> 454 + </div> 455 + </summary> 456 {{ if gt (len .MergeCheck.Conflicts) 0 }} 457 + <ul class="space-y-1 mt-2"> 458 {{ range .MergeCheck.Conflicts }} 459 {{ if .Filename }} 460 + <li class="flex items-center"> 461 + {{ i "file-warning" "inline-flex w-4 h-4 mr-1.5 text-red-500 dark:text-red-300 flex-shrink-0" }} 462 + <span class="font-mono" style="word-break: keep-all; overflow-wrap: break-word;">{{ .Filename }}</span> 463 + </li> 464 {{ else if .Reason }} 465 <li class="flex items-center"> 466 {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} ··· 470 {{ end }} 471 </ul> 472 {{ end }} 473 + </details> 474 </div> 475 {{ else if .MergeCheck }} 476 + <div class="bg-green-50 dark:bg-green-900 border border-green-500 rounded drop-shadow-sm px-6 py-2 relative"> 477 <div class="flex items-center gap-2 text-green-500 dark:text-green-300"> 478 {{ i "circle-check-big" "w-4 h-4" }} 479 <span class="font-medium">no conflicts, ready to merge</span> ··· 484 485 {{ define "resubmitStatus" }} 486 {{ if .ResubmitCheck.Yes }} 487 + <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative"> 488 <div class="flex items-center gap-2 text-amber-500 dark:text-amber-300"> 489 {{ i "triangle-alert" "w-4 h-4" }} 490 <span class="font-medium">this branch has been updated, consider resubmitting</span> ··· 500 {{ with $pipeline }} 501 {{ $id := .Id }} 502 {{ if .Statuses }} 503 + <span>Workflows:</span> 504 + <div class=" 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"> 505 {{ range $name, $all := .Statuses }} 506 <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"> 507 <div
+11 -2
appview/pulls/pulls.go
··· 232 defs[l.AtUri().String()] = &l 233 } 234 235 - s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 236 LoggedInUser: user, 237 RepoInfo: s.repoResolver.GetRepoInfo(r, user), 238 Pull: pull, ··· 243 MergeCheck: mergeCheckResponse, 244 ResubmitCheck: resubmitResult, 245 Pipelines: m, 246 247 OrderedReactionKinds: models.OrderedReactionKinds, 248 Reactions: reactionMap, 249 UserReacted: userReactions, 250 251 LabelDefs: defs, 252 - }) 253 } 254 255 func (s *Pulls) mergeCheck(r *http.Request, f *models.Repo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {
··· 232 defs[l.AtUri().String()] = &l 233 } 234 235 + patch := pull.LatestSubmission().CombinedPatch() 236 + diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 237 + var diffOpts types.DiffOpts 238 + if d := r.URL.Query().Get("diff"); d == "split" { 239 + diffOpts.Split = true 240 + } 241 + 242 + log.Println(s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 243 LoggedInUser: user, 244 RepoInfo: s.repoResolver.GetRepoInfo(r, user), 245 Pull: pull, ··· 250 MergeCheck: mergeCheckResponse, 251 ResubmitCheck: resubmitResult, 252 Pipelines: m, 253 + Diff: &diff, 254 + DiffOpts: diffOpts, 255 256 OrderedReactionKinds: models.OrderedReactionKinds, 257 Reactions: reactionMap, 258 UserReacted: userReactions, 259 260 LabelDefs: defs, 261 + })) 262 } 263 264 func (s *Pulls) mergeCheck(r *http.Request, f *models.Repo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {