+39
appview/models/pipeline.go
+39
appview/models/pipeline.go
···
1
1
package models
2
2
3
3
import (
4
+
"fmt"
4
5
"slices"
6
+
"strings"
5
7
"time"
6
8
7
9
"github.com/bluesky-social/indigo/atproto/syntax"
···
50
52
}
51
53
52
54
return 0
55
+
}
56
+
57
+
// produces short summary of successes:
58
+
// - "0/4" when zero successes of 4 workflows
59
+
// - "4/4" when all successes of 4 workflows
60
+
// - "0/0" when no workflows run in this pipeline
61
+
func (p Pipeline) ShortStatusSummary() string {
62
+
counts := make(map[spindle.StatusKind]int)
63
+
for _, w := range p.Statuses {
64
+
counts[w.Latest().Status] += 1
65
+
}
66
+
67
+
total := len(p.Statuses)
68
+
successes := counts[spindle.StatusKindSuccess]
69
+
70
+
return fmt.Sprintf("%d/%d", successes, total)
71
+
}
72
+
73
+
// produces a string of the form "3/4 success, 2/4 failed, 1/4 pending"
74
+
func (p Pipeline) LongStatusSummary() string {
75
+
counts := make(map[spindle.StatusKind]int)
76
+
for _, w := range p.Statuses {
77
+
counts[w.Latest().Status] += 1
78
+
}
79
+
80
+
total := len(p.Statuses)
81
+
82
+
var result []string
83
+
// finish states first, followed by start states
84
+
states := append(spindle.FinishStates[:], spindle.StartStates[:]...)
85
+
for _, state := range states {
86
+
if count, ok := counts[state]; ok {
87
+
result = append(result, fmt.Sprintf("%d/%d %s", count, total, state.String()))
88
+
}
89
+
}
90
+
91
+
return strings.Join(result, ", ")
53
92
}
54
93
55
94
func (p Pipeline) Counts() map[string]int {
+20
-7
appview/pages/funcmap.go
+20
-7
appview/pages/funcmap.go
···
334
334
},
335
335
"deref": func(v any) any {
336
336
val := reflect.ValueOf(v)
337
-
if val.Kind() == reflect.Ptr && !val.IsNil() {
337
+
if val.Kind() == reflect.Pointer && !val.IsNil() {
338
338
return val.Elem().Interface()
339
339
}
340
340
return nil
···
366
366
return p.AvatarUrl(handle, "")
367
367
},
368
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
-
},
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)
375
382
383
+
for i := range length {
384
+
reversed.Index(i).Set(v.Index(length - 1 - i))
385
+
}
386
+
387
+
return reversed.Interface()
388
+
},
376
389
"normalizeForHtmlId": func(s string) string {
377
390
normalized := strings.ReplaceAll(s, ":", "_")
378
391
normalized = strings.ReplaceAll(normalized, ".", "_")
+2
appview/pages/pages.go
+2
appview/pages/pages.go
···
1103
1103
MergeCheck types.MergeCheckResponse
1104
1104
ResubmitCheck ResubmitResult
1105
1105
Pipelines map[string]models.Pipeline
1106
+
Diff *types.NiceDiff
1107
+
DiffOpts types.DiffOpts
1106
1108
1107
1109
OrderedReactionKinds []models.ReactionKind
1108
1110
Reactions map[models.ReactionKind]models.ReactionDisplayData
+1
appview/pages/templates/fragments/tabSelector.html
+1
appview/pages/templates/fragments/tabSelector.html
+1
-1
appview/pages/templates/layouts/repobase.html
+1
-1
appview/pages/templates/layouts/repobase.html
···
1
1
{{ define "title" }}{{ .RepoInfo.FullName }}{{ end }}
2
2
3
3
{{ define "content" }}
4
-
<section id="repo-header" class="mb-4 p-2 dark:text-white">
4
+
<section id="repo-header" class="mb-2 py-2 px-4 dark:text-white">
5
5
<div class="text-lg flex flex-col sm:flex-row items-start gap-4 justify-between">
6
6
<!-- left items -->
7
7
<div class="flex flex-col gap-2">
+1
-1
appview/pages/templates/repo/fragments/diff.html
+1
-1
appview/pages/templates/repo/fragments/diff.html
···
18
18
{{ range $idx, $hunk := $diff }}
19
19
{{ with $hunk }}
20
20
<details open id="file-{{ .Id }}" class="group border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm" tabindex="{{ add $idx 1 }}">
21
-
<summary class="list-none cursor-pointer sticky top-0">
21
+
<summary class="list-none cursor-pointer sticky top-12">
22
22
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
23
23
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
24
24
<span class="group-open:hidden inline">{{ i "chevron-right" "w-4 h-4" }}</span>
+1
-8
appview/pages/templates/repo/fragments/diffChangedFiles.html
+1
-8
appview/pages/templates/repo/fragments/diffChangedFiles.html
···
1
1
{{ define "repo/fragments/diffChangedFiles" }}
2
-
{{ $stat := .Stat }}
3
2
{{ $fileTree := fileTree .ChangedFiles }}
4
3
<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">
5
-
<div class="diff-stat">
6
-
<div class="flex gap-2 items-center">
7
-
<strong class="text-sm uppercase dark:text-gray-200">Changed files</strong>
8
-
{{ template "repo/fragments/diffStatPill" $stat }}
9
-
</div>
10
-
{{ template "repo/fragments/fileTree" $fileTree }}
11
-
</div>
4
+
{{ template "repo/fragments/fileTree" $fileTree }}
12
5
</section>
13
6
{{ end }}
+22
-25
appview/pages/templates/repo/fragments/diffOpts.html
+22
-25
appview/pages/templates/repo/fragments/diffOpts.html
···
1
1
{{ define "repo/fragments/diffOpts" }}
2
-
<section class="flex flex-col gap-2 overflow-x-auto text-sm px-6 py-2 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm">
3
-
<strong class="text-sm uppercase dark:text-gray-200">options</strong>
4
-
{{ $active := "unified" }}
5
-
{{ if .Split }}
6
-
{{ $active = "split" }}
7
-
{{ end }}
2
+
{{ $active := "unified" }}
3
+
{{ if .Split }}
4
+
{{ $active = "split" }}
5
+
{{ end }}
8
6
9
-
{{ $unified :=
10
-
(dict
11
-
"Key" "unified"
12
-
"Value" "unified"
13
-
"Icon" "square-split-vertical"
14
-
"Meta" "") }}
15
-
{{ $split :=
16
-
(dict
17
-
"Key" "split"
18
-
"Value" "split"
19
-
"Icon" "square-split-horizontal"
20
-
"Meta" "") }}
21
-
{{ $values := list $unified $split }}
7
+
{{ $unified :=
8
+
(dict
9
+
"Key" "unified"
10
+
"Value" "unified"
11
+
"Icon" "square-split-vertical"
12
+
"Meta" "") }}
13
+
{{ $split :=
14
+
(dict
15
+
"Key" "split"
16
+
"Value" "split"
17
+
"Icon" "square-split-horizontal"
18
+
"Meta" "") }}
19
+
{{ $values := list $unified $split }}
22
20
23
-
{{ template "fragments/tabSelector"
24
-
(dict
25
-
"Name" "diff"
26
-
"Values" $values
27
-
"Active" $active) }}
28
-
</section>
21
+
{{ template "fragments/tabSelector"
22
+
(dict
23
+
"Name" "diff"
24
+
"Values" $values
25
+
"Active" $active) }}
29
26
{{ end }}
30
27
+35
-22
appview/pages/templates/repo/issues/fragments/commentList.html
+35
-22
appview/pages/templates/repo/issues/fragments/commentList.html
···
1
1
{{ define "repo/issues/fragments/commentList" }}
2
-
<div class="flex flex-col gap-8">
2
+
<div class="flex flex-col gap-4">
3
3
{{ range $item := .CommentList }}
4
4
{{ template "commentListing" (list $ .) }}
5
5
{{ end }}
···
19
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
20
{{ template "topLevelComment" $params }}
21
21
22
-
<div class="relative ml-4 border-l-2 border-gray-200 dark:border-gray-700">
22
+
<div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700">
23
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>
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
+
}}
38
33
</div>
39
34
{{ end }}
40
35
</div>
···
44
39
{{ end }}
45
40
46
41
{{ 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" . }}
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>
50
54
</div>
51
55
{{ end }}
52
56
53
57
{{ 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" . }}
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>
57
70
</div>
58
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
1
{{ define "repo/issues/fragments/issueCommentHeader" }}
2
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 }}
3
+
{{ resolve .Comment.Did }}
4
4
{{ template "hats" $ }}
5
+
<span class="before:content-['ยท']"></span>
5
6
{{ template "timestamp" . }}
6
7
{{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }}
7
8
{{ if and $isCommentOwner (not .Comment.Deleted) }}
+2
-2
appview/pages/templates/repo/issues/fragments/issueListing.html
+2
-2
appview/pages/templates/repo/issues/fragments/issueListing.html
···
21
21
{{ $state = "open" }}
22
22
{{ end }}
23
23
24
-
<span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm">
24
+
<span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }}">
25
25
{{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }}
26
-
<span class="text-white dark:text-white">{{ $state }}</span>
26
+
<span class="text-white dark:text-white text-sm">{{ $state }}</span>
27
27
</span>
28
28
29
29
<span class="ml-1">
+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
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">
2
+
<div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700">
3
3
{{ if .LoggedInUser }}
4
4
<img
5
5
src="{{ tinyAvatar .LoggedInUser.Did }}"
6
6
alt=""
7
-
class="rounded-full h-6 w-6 mr-1 border border-gray-300 dark:border-gray-700"
7
+
class="rounded-full size-8 mr-1 border-2 border-gray-300 dark:border-gray-700"
8
8
/>
9
9
{{ end }}
10
10
<input
11
-
class="w-full py-2 border-none focus:outline-none"
11
+
class="w-full p-0 border-none focus:outline-none"
12
12
placeholder="Leave a reply..."
13
13
hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/reply"
14
14
hx-trigger="focus"
+5
-5
appview/pages/templates/repo/issues/issue.html
+5
-5
appview/pages/templates/repo/issues/issue.html
···
58
58
{{ $icon = "circle-dot" }}
59
59
{{ end }}
60
60
<div class="inline-flex items-center gap-2">
61
-
<div id="state"
62
-
class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}">
63
-
{{ i $icon "w-4 h-4 mr-1.5 text-white" }}
64
-
<span class="text-white">{{ .Issue.State }}</span>
65
-
</div>
61
+
<span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }}">
62
+
{{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }}
63
+
<span class="text-white dark:text-white text-sm">{{ .Issue.State }}</span>
64
+
</span>
65
+
66
66
<span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1">
67
67
opened by
68
68
{{ template "user/fragments/picHandleLink" .Issue.Did }}
+60
-69
appview/pages/templates/repo/pipelines/fragments/pipelineSymbol.html
+60
-69
appview/pages/templates/repo/pipelines/fragments/pipelineSymbol.html
···
1
1
{{ define "repo/pipelines/fragments/pipelineSymbol" }}
2
-
<div class="cursor-pointer">
3
-
{{ $c := .Counts }}
4
-
{{ $statuses := .Statuses }}
5
-
{{ $total := len $statuses }}
6
-
{{ $success := index $c "success" }}
7
-
{{ $fail := index $c "failed" }}
8
-
{{ $timeout := index $c "timeout" }}
9
-
{{ $empty := eq $total 0 }}
10
-
{{ $allPass := eq $success $total }}
11
-
{{ $allFail := eq $fail $total }}
12
-
{{ $allTimeout := eq $timeout $total }}
13
-
14
-
{{ if $empty }}
15
-
<div class="flex gap-1 items-center">
16
-
{{ i "hourglass" "size-4 text-gray-600 dark:text-gray-400 " }}
17
-
<span>0/{{ $total }}</span>
18
-
</div>
19
-
{{ else if $allPass }}
20
-
<div class="flex gap-1 items-center">
21
-
{{ i "check" "size-4 text-green-600" }}
22
-
<span>{{ $total }}/{{ $total }}</span>
23
-
</div>
24
-
{{ else if $allFail }}
25
-
<div class="flex gap-1 items-center">
26
-
{{ i "x" "size-4 text-red-500" }}
27
-
<span>0/{{ $total }}</span>
28
-
</div>
29
-
{{ else if $allTimeout }}
30
-
<div class="flex gap-1 items-center">
31
-
{{ i "clock-alert" "size-4 text-orange-500" }}
32
-
<span>0/{{ $total }}</span>
33
-
</div>
2
+
<div class="cursor-pointer flex gap-2 items-center">
3
+
{{ template "symbol" .Pipeline }}
4
+
{{ if .ShortSummary }}
5
+
{{ .Pipeline.ShortStatusSummary }}
34
6
{{ else }}
35
-
{{ $radius := f64 8 }}
36
-
{{ $circumference := mulf64 2.0 (mulf64 3.1416 $radius) }}
37
-
{{ $offset := 0.0 }}
38
-
<div class="flex gap-1 items-center">
39
-
<svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 20 20">
40
-
<circle cx="10" cy="10" r="{{ $radius }}" fill="none" stroke="#f3f4f633" stroke-width="2"/>
7
+
{{ .Pipeline.LongStatusSummary }}
8
+
{{ end }}
9
+
</div>
10
+
{{ end }}
41
11
42
-
{{ range $kind, $count := $c }}
43
-
{{ $color := "" }}
44
-
{{ if or (eq $kind "pending") (eq $kind "running") }}
45
-
{{ $color = "#eab308" }} {{/* amber-500 */}}
46
-
{{ else if eq $kind "success" }}
47
-
{{ $color = "#10b981" }} {{/* green-500 */}}
48
-
{{ else if eq $kind "cancelled" }}
49
-
{{ $color = "#6b7280" }} {{/* gray-500 */}}
50
-
{{ else if eq $kind "timeout" }}
51
-
{{ $color = "#fb923c" }} {{/* orange-400 */}}
52
-
{{ else }}
53
-
{{ $color = "#ef4444" }} {{/* red-500 for failed or unknown */}}
54
-
{{ end }}
12
+
{{ define "symbol" }}
13
+
{{ $c := .Counts }}
14
+
{{ $statuses := .Statuses }}
15
+
{{ $total := len $statuses }}
16
+
{{ $success := index $c "success" }}
17
+
{{ $fail := index $c "failed" }}
18
+
{{ $timeout := index $c "timeout" }}
19
+
{{ $empty := eq $total 0 }}
20
+
{{ $allPass := eq $success $total }}
21
+
{{ $allFail := eq $fail $total }}
22
+
{{ $allTimeout := eq $timeout $total }}
55
23
56
-
{{ $percent := divf64 (f64 $count) (f64 $total) }}
57
-
{{ $length := mulf64 $percent $circumference }}
58
-
59
-
<circle
60
-
cx="10" cy="10" r="{{ $radius }}"
61
-
fill="none"
62
-
stroke="{{ $color }}"
63
-
stroke-width="2"
64
-
stroke-dasharray="{{ printf "%.2f %.2f" $length (subf64 $circumference $length) }}"
65
-
stroke-dashoffset="{{ printf "%.2f" (negf64 $offset) }}"
66
-
/>
67
-
{{ $offset = addf64 $offset $length }}
68
-
{{ end }}
69
-
</svg>
70
-
<span>{{ $success }}/{{ $total }}</span>
71
-
</div>
72
-
{{ end }}
73
-
</div>
24
+
{{ if $empty }}
25
+
{{ i "hourglass" "size-4 text-gray-600 dark:text-gray-400 " }}
26
+
{{ else if $allPass }}
27
+
{{ i "check" "size-4 text-green-600 dark:text-green-500" }}
28
+
{{ else if $allFail }}
29
+
{{ i "x" "size-4 text-red-600 dark:text-red-500" }}
30
+
{{ else if $allTimeout }}
31
+
{{ i "clock-alert" "size-4 text-orange-500" }}
32
+
{{ else }}
33
+
{{ $radius := f64 8 }}
34
+
{{ $circumference := mulf64 2.0 (mulf64 3.1416 $radius) }}
35
+
{{ $offset := 0.0 }}
36
+
<svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 20 20">
37
+
<circle cx="10" cy="10" r="{{ $radius }}" fill="none" class="stroke-gray-200 dark:stroke-gray-700" stroke-width="2"/>
38
+
{{ range $kind, $count := $c }}
39
+
{{ $colorClass := "" }}
40
+
{{ if or (eq $kind "pending") (eq $kind "running") }}
41
+
{{ $colorClass = "stroke-yellow-600 dark:stroke-yellow-500" }}
42
+
{{ else if eq $kind "success" }}
43
+
{{ $colorClass = "stroke-green-600 dark:stroke-green-500" }}
44
+
{{ else if eq $kind "cancelled" }}
45
+
{{ $colorClass = "stroke-gray-600 dark:stroke-gray-500" }}
46
+
{{ else if eq $kind "timeout" }}
47
+
{{ $colorClass = "stroke-orange-600 dark:stroke-orange-500" }}
48
+
{{ else }}
49
+
{{ $colorClass = "stroke-red-600 dark:stroke-red-500" }}
50
+
{{ end }}
51
+
{{ $percent := divf64 (f64 $count) (f64 $total) }}
52
+
{{ $length := mulf64 $percent $circumference }}
53
+
<circle
54
+
cx="10" cy="10" r="{{ $radius }}"
55
+
fill="none"
56
+
class="{{ $colorClass }}"
57
+
stroke-width="2"
58
+
stroke-dasharray="{{ printf "%.2f %.2f" $length (subf64 $circumference $length) }}"
59
+
stroke-dashoffset="{{ printf "%.2f" (negf64 $offset) }}"
60
+
/>
61
+
{{ $offset = addf64 $offset $length }}
62
+
{{ end }}
63
+
</svg>
64
+
{{ end }}
74
65
{{ end }}
+1
-1
appview/pages/templates/repo/pipelines/fragments/pipelineSymbolLong.html
+1
-1
appview/pages/templates/repo/pipelines/fragments/pipelineSymbolLong.html
···
4
4
<div class="relative inline-block">
5
5
<details class="relative">
6
6
<summary class="cursor-pointer list-none">
7
-
{{ template "repo/pipelines/fragments/pipelineSymbol" .Pipeline }}
7
+
{{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }}
8
8
</summary>
9
9
{{ template "repo/pipelines/fragments/tooltip" $ }}
10
10
</details>
+17
-17
appview/pages/templates/repo/pulls/fragments/pullActions.html
+17
-17
appview/pages/templates/repo/pulls/fragments/pullActions.html
···
22
22
{{ $isLastRound := eq $roundNumber $lastIdx }}
23
23
{{ $isSameRepoBranch := .Pull.IsBranchBased }}
24
24
{{ $isUpToDate := .ResubmitCheck.No }}
25
-
<div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative">
25
+
<div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative p-2">
26
26
<button
27
27
hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ $roundNumber }}/comment"
28
28
hx-target="#actions-{{$roundNumber}}"
29
29
hx-swap="outerHtml"
30
-
class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group">
31
-
{{ i "message-square-plus" "w-4 h-4" }}
32
-
<span>comment</span>
30
+
class="btn-flat p-2 flex items-center gap-2 no-underline hover:no-underline group">
31
+
{{ i "message-square-plus" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
33
32
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
33
+
comment
34
34
</button>
35
35
{{ if .BranchDeleteStatus }}
36
36
<button
37
37
hx-delete="/{{ .BranchDeleteStatus.Repo.Did }}/{{ .BranchDeleteStatus.Repo.Name }}/branches"
38
38
hx-vals='{"branch": "{{ .BranchDeleteStatus.Branch }}" }'
39
39
hx-swap="none"
40
-
class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">
40
+
class="btn-flat p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">
41
41
{{ i "git-branch" "w-4 h-4" }}
42
42
<span>delete branch</span>
43
43
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
···
52
52
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/merge"
53
53
hx-swap="none"
54
54
hx-confirm="Are you sure you want to merge pull #{{ .Pull.PullId }} into the `{{ .Pull.TargetBranch }}` branch?"
55
-
class="btn p-2 flex items-center gap-2 group" {{ $disabled }}>
56
-
{{ i "git-merge" "w-4 h-4" }}
57
-
<span>merge{{if $stackCount}} {{$stackCount}}{{end}}</span>
55
+
class="btn-flat p-2 flex items-center gap-2 group" {{ $disabled }}>
56
+
{{ i "git-merge" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
58
57
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
58
+
merge{{if $stackCount}} {{$stackCount}}{{end}}
59
59
</button>
60
60
{{ end }}
61
61
···
74
74
{{ end }}
75
75
76
76
hx-disabled-elt="#resubmitBtn"
77
-
class="btn p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }}
77
+
class="btn-flat p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }}
78
78
79
79
{{ if $disabled }}
80
80
title="Update this branch to resubmit this pull request"
···
82
82
title="Resubmit this pull request"
83
83
{{ end }}
84
84
>
85
-
{{ i "rotate-ccw" "w-4 h-4" }}
86
-
<span>resubmit</span>
85
+
{{ i "rotate-ccw" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
87
86
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
87
+
resubmit
88
88
</button>
89
89
{{ end }}
90
90
···
92
92
<button
93
93
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/close"
94
94
hx-swap="none"
95
-
class="btn p-2 flex items-center gap-2 group">
96
-
{{ i "ban" "w-4 h-4" }}
97
-
<span>close</span>
95
+
class="btn-flat p-2 flex items-center gap-2 group">
96
+
{{ i "ban" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
98
97
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
98
+
close
99
99
</button>
100
100
{{ end }}
101
101
···
103
103
<button
104
104
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/reopen"
105
105
hx-swap="none"
106
-
class="btn p-2 flex items-center gap-2 group">
107
-
{{ i "refresh-ccw-dot" "w-4 h-4" }}
108
-
<span>reopen</span>
106
+
class="btn-flat p-2 flex items-center gap-2 group">
107
+
{{ i "refresh-ccw-dot" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
109
108
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
109
+
reopen
110
110
</button>
111
111
{{ end }}
112
112
</div>
+6
-7
appview/pages/templates/repo/pulls/fragments/pullHeader.html
+6
-7
appview/pages/templates/repo/pulls/fragments/pullHeader.html
···
1
1
{{ define "repo/pulls/fragments/pullHeader" }}
2
-
<header class="pb-4">
2
+
<header class="pb-2">
3
3
<h1 class="text-2xl dark:text-white">
4
4
{{ .Pull.Title | description }}
5
5
<span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span>
···
17
17
{{ $icon = "git-merge" }}
18
18
{{ end }}
19
19
20
-
<section class="mt-2">
20
+
<section>
21
21
<div class="flex items-center gap-2">
22
-
<div
23
-
id="state"
24
-
class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}"
22
+
<span
23
+
class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm"
25
24
>
26
-
{{ i $icon "w-4 h-4 mr-1.5 text-white" }}
25
+
{{ i $icon "w-3 h-3 mr-1.5 text-white" }}
27
26
<span class="text-white">{{ .Pull.State.String }}</span>
28
-
</div>
27
+
</span>
29
28
<span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1">
30
29
opened by
31
30
{{ template "user/fragments/picHandleLink" .Pull.OwnerDid }}
+39
-24
appview/pages/templates/repo/pulls/fragments/pullNewComment.html
+39
-24
appview/pages/templates/repo/pulls/fragments/pullNewComment.html
···
1
1
{{ define "repo/pulls/fragments/pullNewComment" }}
2
2
<div
3
3
id="pull-comment-card-{{ .RoundNumber }}"
4
-
class="bg-white dark:bg-gray-800 rounded drop-shadow-sm p-4 relative w-full flex flex-col gap-2">
5
-
<div class="text-sm text-gray-500 dark:text-gray-400">
6
-
{{ resolve .LoggedInUser.Did }}
7
-
</div>
4
+
class="w-full flex flex-col gap-2">
5
+
{{ template "user/fragments/picHandleLink" .LoggedInUser.Did }}
8
6
<form
9
7
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/comment"
10
-
hx-indicator="#create-comment-spinner"
11
8
hx-swap="none"
12
-
class="w-full flex flex-wrap gap-2"
9
+
hx-on::after-request="if(event.detail.successful) this.reset()"
10
+
hx-disabled-elt="#reply-{{ .RoundNumber }}"
11
+
class="w-full flex flex-wrap gap-2 group"
13
12
>
14
13
<textarea
15
14
name="body"
16
15
class="w-full p-2 rounded border border-gray-200"
16
+
rows=8
17
17
placeholder="Add to the discussion..."></textarea
18
18
>
19
-
<button type="submit" class="btn flex items-center gap-2">
20
-
{{ i "message-square" "w-4 h-4" }}
21
-
<span>comment</span>
22
-
<span id="create-comment-spinner" class="group">
23
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
24
-
</span>
25
-
</button>
26
-
<button
27
-
type="button"
28
-
class="btn flex items-center gap-2 group"
29
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/actions"
30
-
hx-swap="outerHTML"
31
-
hx-target="#pull-comment-card-{{ .RoundNumber }}"
32
-
>
33
-
{{ i "x" "w-4 h-4" }}
34
-
<span>cancel</span>
35
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
36
-
</button>
19
+
{{ template "replyActions" . }}
37
20
<div id="pull-comment"></div>
38
21
</form>
39
22
</div>
40
23
{{ end }}
24
+
25
+
{{ define "replyActions" }}
26
+
<div class="flex flex-wrap items-stretch justify-end gap-2 text-gray-500 dark:text-gray-400 text-sm w-full">
27
+
{{ template "cancel" . }}
28
+
{{ template "reply" . }}
29
+
</div>
30
+
{{ end }}
31
+
32
+
{{ define "cancel" }}
33
+
<button
34
+
type="button"
35
+
class="btn text-red-500 dark:text-red-400 flex gap-2 items-center group"
36
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/actions"
37
+
hx-swap="outerHTML"
38
+
hx-target="#actions-{{.RoundNumber}}"
39
+
>
40
+
{{ i "x" "w-4 h-4" }}
41
+
<span>cancel</span>
42
+
</button>
43
+
{{ end }}
44
+
45
+
{{ define "reply" }}
46
+
<button
47
+
type="submit"
48
+
id="reply-{{ .RoundNumber }}"
49
+
class="btn-create flex items-center gap-2">
50
+
{{ i "reply" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
51
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
52
+
reply
53
+
</button>
54
+
{{ end }}
55
+
+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 }}
+1
-1
appview/pages/templates/repo/pulls/fragments/summarizedPullHeader.html
+1
-1
appview/pages/templates/repo/pulls/fragments/summarizedPullHeader.html
···
18
18
{{ $lastSubmission := index .Submissions $latestRound }}
19
19
{{ $commentCount := len $lastSubmission.Comments }}
20
20
{{ if and $pipeline $pipeline.Id }}
21
-
{{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }}
21
+
{{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }}
22
22
<span class="before:content-['ยท'] before:select-none text-gray-500 dark:text-gray-400"></span>
23
23
{{ end }}
24
24
<span>
+334
-77
appview/pages/templates/repo/pulls/pull.html
+334
-77
appview/pages/templates/repo/pulls/pull.html
···
6
6
{{ template "repo/pulls/fragments/og" (dict "RepoInfo" .RepoInfo "Pull" .Pull) }}
7
7
{{ end }}
8
8
9
+
{{ define "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
+
9
17
{{ 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">
18
+
<div class="grid grid-cols-1 md:grid-cols-10 gap-y-2 gap-x-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 h-full">
13
21
{{ block "repoContent" . }}{{ end }}
14
22
</section>
15
23
{{ block "repoAfter" . }}{{ end }}
16
24
</div>
17
-
<div class="col-span-1 md:col-span-2 flex flex-col gap-6">
25
+
<div class="col-span-1 md:col-span-3 flex flex-col gap-6">
18
26
{{ template "repo/fragments/labelPanel"
19
27
(dict "RepoInfo" $.RepoInfo
20
28
"Defs" $.LabelDefs
···
26
34
"Backlinks" $.Backlinks) }}
27
35
{{ template "repo/fragments/externalLinkPanel" $.Pull.AtUri }}
28
36
</div>
37
+
38
+
<style>
39
+
#filesToggle:checked ~ div label[for="filesToggle"] .show-text { display: none; }
40
+
#filesToggle:checked ~ div label[for="filesToggle"] .hide-text { display: inline; }
41
+
#filesToggle:not(:checked) ~ div label[for="filesToggle"] .hide-text { display: none; }
42
+
43
+
#filesToggle:checked ~ div div#files { width: 10vw; margin-right: 1rem; }
44
+
#filesToggle:not(:checked) ~ div div#files { width: 0; display: hidden; margin-right: 0; }
45
+
46
+
#subsToggle:checked ~ div div#subs { width: 25vw; margin-left: 1rem; }
47
+
#subsToggle:not(:checked) ~ div div#subs { width: 0; display: hidden; margin-left: 0; }
48
+
</style>
49
+
50
+
<!-- Checkboxes must come first as siblings -->
51
+
<input type="checkbox" id="filesToggle" class="peer/files hidden" checked/>
52
+
<input type="checkbox" id="subsToggle" class="peer/subs hidden" checked/>
53
+
54
+
<!-- Top bar with controls -->
55
+
<div class="sticky top-0 z-30 bg-slate-100 dark:bg-gray-900 flex items-center gap-2 col-span-full h-12">
56
+
<label for="filesToggle" class="inline-flex items-center justify-center rounded cursor-pointer p-2 text-normal font-normal normalcase">
57
+
<span class="show-text">{{ i "panel-left-open" "size-5" }}</span>
58
+
<span class="hide-text">{{ i "panel-left-close" "size-5" }}</span>
59
+
</label>
60
+
{{ template "repo/fragments/diffStatPill" .Diff.Stat }}
61
+
{{ .Diff.Stat.FilesChanged }} changed file{{ if ne .Diff.Stat.FilesChanged 1 }}s{{ end }}
62
+
<div class="flex-grow"></div>
63
+
{{ template "repo/fragments/diffOpts" .DiffOpts }}
64
+
<label for="subsToggle" class="inline-flex items-center justify-center rounded cursor-pointer p-2">
65
+
{{ i "message-square-more" "size-5" }}
66
+
</label>
67
+
</div>
68
+
69
+
<div class="flex col-span-full">
70
+
<!-- left panel -->
71
+
<div id="files" class="w-0 overflow-hidden sticky top-12 max-h-screen overflow-y-auto pb-12">
72
+
{{ template "repo/fragments/diffChangedFiles" .Diff }}
73
+
</div>
74
+
75
+
<!-- main content -->
76
+
<div class="flex-1 min-w-0 sticky top-12 pb-12">
77
+
{{ template "repo/fragments/diff" (list .Diff .DiffOpts) }}
78
+
</div>
79
+
80
+
<!-- right panel -->
81
+
<div id="subs" class="w-0 overflow-hidden max-h-screen flex flex-col sticky top-12 pb-12">
82
+
<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">
83
+
<h2 class="font-bold uppercase">history</h2>
84
+
</div>
85
+
<div class="flex flex-col-reverse gap-4 overflow-y-auto">
86
+
{{ template "submissions2" . }}
87
+
</div>
88
+
</div>
89
+
</div>
29
90
</div>
30
91
{{ end }}
31
92
32
93
{{ define "repoContent" }}
33
94
{{ template "repo/pulls/fragments/pullHeader" . }}
34
-
35
95
{{ if .Pull.IsStacked }}
36
96
<div class="mt-8">
37
97
{{ template "repo/pulls/fragments/pullStack" . }}
···
40
100
{{ end }}
41
101
42
102
{{ define "repoAfter" }}
43
-
<section id="submissions" class="mt-4">
44
-
<div class="flex flex-col gap-4">
45
-
{{ block "submissions" . }} {{ end }}
103
+
<div id="pull-close"></div>
104
+
<div id="pull-reopen"></div>
105
+
{{ end }}
106
+
107
+
{{ define "submissions2" }}
108
+
{{ $lastIdx := sub (len .Pull.Submissions) 1 }}
109
+
{{ range $ridx, $item := reverse .Pull.Submissions }}
110
+
{{ $idx := sub $lastIdx $ridx }}
111
+
<div class="rounded border border-gray-200 dark:border-gray-700 w-full shadow-sm bg-gray-50 dark:bg-gray-800/50">
112
+
{{ with $item }}
113
+
{{ $patches := .AsFormatPatch }}
114
+
{{ $round := .RoundNumber }}
115
+
<div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2">
116
+
<div class="flex-shrink-0">
117
+
<img
118
+
src="{{ tinyAvatar $.Pull.OwnerDid }}"
119
+
alt=""
120
+
class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900"
121
+
/>
122
+
</div>
123
+
<!-- right column: name and body in two rows -->
124
+
<div class="flex-1 min-w-0 flex flex-col gap-1">
125
+
<div class="flex gap-2 items-center justify-between mb-1">
126
+
<span class="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
127
+
{{ resolve $.Pull.OwnerDid }} submitted v{{ $round }}
128
+
<span class="select-none before:content-['\00B7']"></span>
129
+
<a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ $round }}">{{ template "repo/fragments/shortTimeAgo" .Created }}</a>
130
+
</span>
131
+
{{ if ne $idx 0 }}
132
+
<a class="flex items-center gap-2 no-underline hover:no-underline text-sm"
133
+
hx-boost="true"
134
+
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{$round}}/interdiff">
135
+
{{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }}
136
+
<span class="hidden md:inline">interdiff</span>
137
+
</a>
138
+
{{ end }}
139
+
</div>
140
+
<details class="group">
141
+
<summary class="list-none cursor-pointer flex items-center gap-2">
142
+
<span>{{ i "git-commit-horizontal" "w-4 h-4" }}</span>
143
+
{{ len $patches }} commit{{ if ne (len $patches) 1 }}s{{ end }}
144
+
</summary>
145
+
{{ range $patches }}
146
+
<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">
147
+
<div class="flex items-baseline gap-2">
148
+
<div>
149
+
<!-- attempt to resolve $fullRepo: this is possible only on non-deleted forks and branches -->
150
+
{{ $fullRepo := "" }}
151
+
{{ if and $.Pull.IsForkBased $.Pull.PullSource.Repo }}
152
+
{{ $fullRepo = printf "%s/%s" $.Pull.OwnerDid $.Pull.PullSource.Repo.Name }}
153
+
{{ else if $.Pull.IsBranchBased }}
154
+
{{ $fullRepo = $.RepoInfo.FullName }}
155
+
{{ end }}
156
+
157
+
<!-- if $fullRepo was resolved, link to it, otherwise just span without a link -->
158
+
{{ if $fullRepo }}
159
+
<a href="/{{ $fullRepo }}/commit/{{ .SHA }}" class="font-mono text-gray-600 dark:text-gray-300">{{ slice .SHA 0 8 }}</a>
160
+
{{ else }}
161
+
<span class="font-mono">{{ slice .SHA 0 8 }}</span>
162
+
{{ end }}
163
+
</div>
164
+
165
+
<div>
166
+
<span>{{ .Title | description }}</span>
167
+
{{ if gt (len .Body) 0 }}
168
+
<button
169
+
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"
170
+
hx-on:click="document.getElementById('body-{{$round}}-{{.SHA}}').classList.toggle('hidden')"
171
+
>
172
+
{{ i "ellipsis" "w-3 h-3" }}
173
+
</button>
174
+
{{ end }}
175
+
{{ if gt (len .Body) 0 }}
176
+
<p id="body-{{$round}}-{{.SHA}}" class="hidden mt-1 text-sm pb-2">{{ nl2br .Body }}</p>
177
+
{{ end }}
178
+
</div>
179
+
</div>
180
+
</div>
181
+
{{ end }}
182
+
</details>
183
+
<div>
184
+
{{ block "pipelineStatus" (list $ .) }} {{ end }}
185
+
</div>
186
+
{{ if eq $lastIdx .RoundNumber }}
187
+
{{ block "mergeCheck" $ }} {{ end }}
188
+
{{ end }}
189
+
</div>
190
+
</div>
191
+
<div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700">
192
+
{{ range $cidx, $c := .Comments }}
193
+
<div id="comment-{{$c.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto">
194
+
<!-- left column: profile picture -->
195
+
<div class="flex-shrink-0">
196
+
<img
197
+
src="{{ tinyAvatar $c.OwnerDid }}"
198
+
alt=""
199
+
class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900"
200
+
/>
201
+
</div>
202
+
<!-- right column: name and body in two rows -->
203
+
<div class="flex-1 min-w-0">
204
+
<!-- Row 1: Author and timestamp -->
205
+
<div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1">
206
+
<span>{{ resolve $c.OwnerDid }}</span>
207
+
<span class="before:content-['ยท']"></span>
208
+
<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>
209
+
</div>
210
+
<!-- Row 2: Body text -->
211
+
<div class="prose dark:prose-invert mt-1">
212
+
{{ $c.Body | markdown }}
213
+
</div>
214
+
</div>
215
+
</div>
216
+
{{ end }}
217
+
</div>
218
+
{{ end }}
219
+
{{ if eq $lastIdx .RoundNumber }}
220
+
{{ block "mergeStatus" $ }} {{ end }}
221
+
{{ block "resubmitStatus" $ }} {{ end }}
222
+
{{ end }}
223
+
{{ if $.LoggedInUser }}
224
+
{{ template "repo/pulls/fragments/pullActions"
225
+
(dict
226
+
"LoggedInUser" $.LoggedInUser
227
+
"Pull" $.Pull
228
+
"RepoInfo" $.RepoInfo
229
+
"RoundNumber" .RoundNumber
230
+
"MergeCheck" $.MergeCheck
231
+
"ResubmitCheck" $.ResubmitCheck
232
+
"BranchDeleteStatus" $.BranchDeleteStatus
233
+
"Stack" $.Stack) }}
234
+
{{ else }}
235
+
<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">
236
+
<a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2">
237
+
sign up
238
+
</a>
239
+
<span class="text-gray-500 dark:text-gray-400">or</span>
240
+
<a href="/login" class="underline">login</a>
241
+
to add to the discussion
242
+
</div>
243
+
{{ end }}
244
+
</div>
245
+
{{ end }}
246
+
{{ end }}
247
+
248
+
{{ define "newComment" }}
249
+
{{ $root := index . 0 }}
250
+
{{ $submission := index . 1 }}
251
+
<form
252
+
id="comment-form"
253
+
hx-post="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $submission.RoundNumber }}/comment"
254
+
hx-on::after-request="if(event.detail.successful) this.reset()"
255
+
>
256
+
<div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-full">
257
+
<div class="text-sm pb-2 text-gray-500 dark:text-gray-400">
258
+
{{ template "user/fragments/picHandleLink" $root.LoggedInUser.Did }}
46
259
</div>
47
-
</section>
260
+
<textarea
261
+
id="comment-textarea"
262
+
name="body"
263
+
class="w-full p-2 rounded border border-gray-200 dark:border-gray-700"
264
+
placeholder="Add to the discussion"
265
+
rows="8"
266
+
></textarea>
267
+
<div id="pull-comment"></div>
268
+
</div>
269
+
{{ template "replyActions" . }}
270
+
</form>
271
+
{{ end }}
48
272
49
-
<div id="pull-close"></div>
50
-
<div id="pull-reopen"></div>
273
+
{{ define "replyActions" }}
274
+
<div class="flex flex-wrap items-stretch justify-end gap-2 text-gray-500 dark:text-gray-400 text-sm">
275
+
{{ template "cancel" . }}
276
+
{{ template "reply" . }}
277
+
</div>
278
+
{{ end }}
279
+
280
+
{{ define "cancel" }}
281
+
<button
282
+
class="btn text-red-500 dark:text-red-400 flex gap-2 items-center group"
283
+
hx-get="TODO"
284
+
hx-target="TODO"
285
+
hx-swap="outerHTML">
286
+
{{ i "x" "size-4" }}
287
+
cancel
288
+
</button>
289
+
{{ end }}
290
+
291
+
{{ define "reply" }}
292
+
<button
293
+
id="TODO"
294
+
type="submit"
295
+
class="btn-create flex items-center gap-2 no-underline hover:no-underline">
296
+
{{ i "reply" "w-4 h-4 inline group-[.htmx-request]:hidden" }}
297
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
298
+
reply
299
+
</button>
51
300
{{ end }}
52
301
53
302
{{ define "submissions" }}
···
214
463
{{ end }}
215
464
{{ end }}
216
465
466
+
{{ define "mergeCheck" }}
467
+
{{ $isOpen := .Pull.State.IsOpen }}
468
+
{{ if and $isOpen .MergeCheck .MergeCheck.Error }}
469
+
<div class="flex items-center gap-2">
470
+
{{ i "triangle-alert" "w-4 h-4 text-red-500 dark:text-red-300" }}
471
+
{{ .MergeCheck.Error }}
472
+
</div>
473
+
{{ else if and $isOpen .MergeCheck .MergeCheck.IsConflicted }}
474
+
<details class="group">
475
+
<summary class="flex items-center justify-between cursor-pointer list-none">
476
+
<div class="flex items-center gap-2 ">
477
+
{{ i "triangle-alert" "w-4 h-4" }}
478
+
<span class="font-medium">merge conflicts detected</span>
479
+
</div>
480
+
<div>
481
+
<span class="group-open:hidden inline">{{ i "chevrons-up-down" "w-4 h-4" }}</span>
482
+
<span class="hidden group-open:inline">{{ i "chevrons-down-up" "w-4 h-4" }}</span>
483
+
</div>
484
+
</summary>
485
+
{{ if gt (len .MergeCheck.Conflicts) 0 }}
486
+
<ul class="space-y-1 mt-2">
487
+
{{ range .MergeCheck.Conflicts }}
488
+
{{ if .Filename }}
489
+
<li class="flex items-center">
490
+
{{ i "file-warning" "inline-flex w-4 h-4 mr-1.5 text-red-500 dark:text-red-300 flex-shrink-0" }}
491
+
<span class="font-mono" style="word-break: keep-all; overflow-wrap: break-word;">{{ .Filename }}</span>
492
+
</li>
493
+
{{ else if .Reason }}
494
+
<li class="flex items-center">
495
+
{{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }}
496
+
<span>{{.Reason}}</span>
497
+
</li>
498
+
{{ end }}
499
+
{{ end }}
500
+
</ul>
501
+
{{ end }}
502
+
</details>
503
+
{{ else if and $isOpen .MergeCheck }}
504
+
<div class="flex items-center gap-2">
505
+
{{ i "check" "w-4 h-4 text-green-600 dark:text-green-500" }}
506
+
<span>no conflicts, ready to merge</span>
507
+
</div>
508
+
{{ end }}
509
+
{{ end }}
510
+
217
511
{{ define "mergeStatus" }}
218
512
{{ 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">
513
+
<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">
220
514
<div class="flex items-center gap-2 text-black dark:text-white">
221
515
{{ i "ban" "w-4 h-4" }}
222
516
<span class="font-medium">closed without merging</span
···
224
518
</div>
225
519
</div>
226
520
{{ 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">
521
+
<div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative">
228
522
<div class="flex items-center gap-2 text-purple-500 dark:text-purple-300">
229
523
{{ i "git-merge" "w-4 h-4" }}
230
524
<span class="font-medium">pull request successfully merged</span
···
232
526
</div>
233
527
</div>
234
528
{{ 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">
529
+
<div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative">
236
530
<div class="flex items-center gap-2 text-red-500 dark:text-red-300">
237
531
{{ i "git-pull-request-closed" "w-4 h-4" }}
238
532
<span class="font-medium">This pull has been deleted (possibly by jj abandon or jj squash)</span>
239
533
</div>
240
534
</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
535
{{ end }}
282
536
{{ end }}
283
537
284
538
{{ define "resubmitStatus" }}
285
539
{{ 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">
540
+
<div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative">
287
541
<div class="flex items-center gap-2 text-amber-500 dark:text-amber-300">
288
542
{{ i "triangle-alert" "w-4 h-4" }}
289
543
<span class="font-medium">this branch has been updated, consider resubmitting</span>
···
299
553
{{ with $pipeline }}
300
554
{{ $id := .Id }}
301
555
{{ 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 }}
556
+
<details>
557
+
<summary class="cursor-pointer list-none">{{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" false) }}</summary>
558
+
<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">
559
+
{{ range $name, $all := .Statuses }}
560
+
<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">
561
+
<div
562
+
class="flex gap-2 items-center justify-between p-2">
563
+
{{ $lastStatus := $all.Latest }}
564
+
{{ $kind := $lastStatus.Status.String }}
309
565
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>
566
+
<div id="left" class="flex items-center gap-2 flex-shrink-0">
567
+
{{ template "repo/pipelines/fragments/workflowSymbol" $all }}
568
+
{{ $name }}
569
+
</div>
570
+
<div id="right" class="flex items-center gap-2 flex-shrink-0">
571
+
<span class="font-bold">{{ $kind }}</span>
572
+
{{ if .TimeTaken }}
573
+
{{ template "repo/fragments/duration" .TimeTaken }}
574
+
{{ else }}
575
+
{{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }}
576
+
{{ end }}
577
+
</div>
578
+
</div>
579
+
</a>
580
+
{{ end }}
322
581
</div>
323
-
</a>
324
-
{{ end }}
325
-
</div>
582
+
</details>
326
583
{{ end }}
327
584
{{ end }}
328
585
{{ end }}
+1
-1
appview/pages/templates/repo/pulls/pulls.html
+1
-1
appview/pages/templates/repo/pulls/pulls.html
···
136
136
{{ $pipeline := index $.Pipelines .LatestSha }}
137
137
{{ if and $pipeline $pipeline.Id }}
138
138
<span class="before:content-['ยท']"></span>
139
-
{{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }}
139
+
{{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }}
140
140
{{ end }}
141
141
142
142
{{ $state := .Labels }}
+11
-2
appview/pulls/pulls.go
+11
-2
appview/pulls/pulls.go
···
232
232
defs[l.AtUri().String()] = &l
233
233
}
234
234
235
-
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
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{
236
243
LoggedInUser: user,
237
244
RepoInfo: s.repoResolver.GetRepoInfo(r, user),
238
245
Pull: pull,
···
243
250
MergeCheck: mergeCheckResponse,
244
251
ResubmitCheck: resubmitResult,
245
252
Pipelines: m,
253
+
Diff: &diff,
254
+
DiffOpts: diffOpts,
246
255
247
256
OrderedReactionKinds: models.OrderedReactionKinds,
248
257
Reactions: reactionMap,
249
258
UserReacted: userReactions,
250
259
251
260
LabelDefs: defs,
252
-
})
261
+
}))
253
262
}
254
263
255
264
func (s *Pulls) mergeCheck(r *http.Request, f *models.Repo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {
+29
appview/state/manifest.go
+29
appview/state/manifest.go
···
1
+
package state
2
+
3
+
import (
4
+
"encoding/json"
5
+
"net/http"
6
+
)
7
+
8
+
// https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest
9
+
// https://www.w3.org/TR/appmanifest/
10
+
var manifestData = map[string]any{
11
+
"name": "tangled",
12
+
"description": "tightly-knit social coding.",
13
+
"icons": []map[string]string{
14
+
{
15
+
"src": "/static/logos/dolly.svg",
16
+
"sizes": "144x144",
17
+
},
18
+
},
19
+
"start_url": "/",
20
+
"id": "https://tangled.org",
21
+
"display": "standalone",
22
+
"background_color": "#111827",
23
+
"theme_color": "#111827",
24
+
}
25
+
26
+
func (p *State) WebAppManifest(w http.ResponseWriter, r *http.Request) {
27
+
w.Header().Set("Content-Type", "application/manifest+json")
28
+
json.NewEncoder(w).Encode(manifestData)
29
+
}
+1
-1
appview/state/router.go
+1
-1
appview/state/router.go
-23
appview/state/state.go
-23
appview/state/state.go
···
212
212
w.Write([]byte(robotsTxt))
213
213
}
214
214
215
-
// https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest
216
-
const manifestJson = `{
217
-
"name": "tangled",
218
-
"description": "tightly-knit social coding.",
219
-
"icons": [
220
-
{
221
-
"src": "/favicon.svg",
222
-
"sizes": "144x144"
223
-
}
224
-
],
225
-
"start_url": "/",
226
-
"id": "org.tangled",
227
-
228
-
"display": "standalone",
229
-
"background_color": "#111827",
230
-
"theme_color": "#111827"
231
-
}`
232
-
233
-
func (p *State) PWAManifest(w http.ResponseWriter, r *http.Request) {
234
-
w.Header().Set("Content-Type", "application/json")
235
-
w.Write([]byte(manifestJson))
236
-
}
237
-
238
215
func (s *State) TermsOfService(w http.ResponseWriter, r *http.Request) {
239
216
user := s.oauth.GetUser(r)
240
217
s.pages.TermsOfService(w, pages.TermsOfServiceParams{
+1
-1
cmd/dolly/main.go
+1
-1
cmd/dolly/main.go
+6
docs/logo.html
+6
docs/logo.html
+2
docs/template.html
+2
docs/template.html
···
74
74
${ x.svg() }
75
75
$if(toc-title)$$toc-title$$else$Table of Contents$endif$
76
76
</button>
77
+
${ logo.html() }
77
78
${ search.html() }
78
79
${ table-of-contents:toc.html() }
79
80
</div>
···
88
89
class="hidden md:flex md:flex-col gap-4 fixed left-0 top-0 w-80 h-screen
89
90
bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700
90
91
p-4 z-50 overflow-y-auto">
92
+
${ logo.html() }
91
93
${ search.html() }
92
94
<div class="flex-1">
93
95
$if(toc-title)$
+13
input.css
+13
input.css
···
124
124
dark:text-gray-100 dark:before:bg-gray-800 dark:before:border-gray-700;
125
125
}
126
126
127
+
.btn-flat {
128
+
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center justify-center
129
+
bg-transparent px-2 pb-[0.2rem] text-sm text-gray-900
130
+
before:absolute before:inset-0 before:-z-10 before:block before:rounded
131
+
before:border before:border-gray-200 before:bg-white
132
+
before:content-[''] before:transition-all before:duration-150 before:ease-in-out
133
+
hover:before:bg-gray-50
134
+
dark:hover:before:bg-gray-700
135
+
focus:outline-none focus-visible:before:outline focus-visible:before:outline-2 focus-visible:before:outline-gray-400
136
+
disabled:cursor-not-allowed disabled:opacity-50
137
+
dark:text-gray-100 dark:before:bg-gray-800 dark:before:border-gray-700;
138
+
}
139
+
127
140
.btn-create {
128
141
@apply btn text-white
129
142
before:bg-green-600 hover:before:bg-green-700
+1
-1
nix/pkgs/appview-static-files.nix
+1
-1
nix/pkgs/appview-static-files.nix
···
30
30
31
31
${dolly}/bin/dolly -output logos/dolly.png -size 180x180
32
32
${dolly}/bin/dolly -output logos/dolly.ico -size 48x48
33
-
${dolly}/bin/dolly -output logos/dolly.svg
33
+
${dolly}/bin/dolly -output logos/dolly.svg -color currentColor
34
34
# tailwindcss -c $src/tailwind.config.js -i $src/input.css -o tw.css won't work
35
35
# for whatever reason (produces broken css), so we are doing this instead
36
36
cd ${src} && ${tailwindcss}/bin/tailwindcss -i input.css -o $out/tw.css
+4
nix/pkgs/docs.nix
+4
nix/pkgs/docs.nix
···
5
5
inter-fonts-src,
6
6
ibm-plex-mono-src,
7
7
lucide-src,
8
+
dolly,
8
9
src,
9
10
}:
10
11
runCommandLocal "docs" {} ''
···
17
18
18
19
# icons
19
20
cp -rf ${lucide-src}/*.svg working/
21
+
22
+
# logo
23
+
${dolly}/bin/dolly -output working/dolly.svg -color currentColor
20
24
21
25
# content - chunked
22
26
${pandoc}/bin/pandoc ${src}/docs/DOCS.md \
+2
-2
spindle/models/models.go
+2
-2
spindle/models/models.go
+4
-2
types/diff.go
+4
-2
types/diff.go
···
27
27
}
28
28
29
29
type DiffStat struct {
30
-
Insertions int64
31
-
Deletions int64
30
+
Insertions int64
31
+
Deletions int64
32
+
FilesChanged int
32
33
}
33
34
34
35
func (d *Diff) Stats() DiffStat {
···
37
38
stats.Insertions += f.LinesAdded
38
39
stats.Deletions += f.LinesDeleted
39
40
}
41
+
stats.FilesChanged = len(d.TextFragments)
40
42
return stats
41
43
}
42
44