+20
-7
appview/pages/funcmap.go
+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
+2
appview/pages/pages.go
+1
-1
appview/pages/templates/layouts/repobase.html
+1
-1
appview/pages/templates/layouts/repobase.html
+35
-22
appview/pages/templates/repo/issues/fragments/commentList.html
+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
-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
+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
+1
-1
appview/pages/templates/repo/issues/fragments/putIssue.html
+3
-3
appview/pages/templates/repo/issues/fragments/replyIssueCommentPlaceholder.html
+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
+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
+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
+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
+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 {