+18
-11
appview/db/profile.go
+18
-11
appview/db/profile.go
···
20
timeline := models.ProfileTimeline{
21
ByMonth: make([]models.ByMonth, TimeframeMonths),
22
}
23
-
currentMonth := time.Now().Month()
24
timeframe := fmt.Sprintf("-%d months", TimeframeMonths)
25
26
pulls, err := GetPullsByOwnerDid(e, forDid, timeframe)
···
30
31
// group pulls by month
32
for _, pull := range pulls {
33
-
pullMonth := pull.Created.Month()
34
35
-
if currentMonth-pullMonth >= TimeframeMonths {
36
// shouldn't happen; but times are weird
37
continue
38
}
39
40
-
idx := currentMonth - pullMonth
41
items := &timeline.ByMonth[idx].PullEvents.Items
42
43
*items = append(*items, &pull)
···
53
}
54
55
for _, issue := range issues {
56
-
issueMonth := issue.Created.Month()
57
58
-
if currentMonth-issueMonth >= TimeframeMonths {
59
// shouldn't happen; but times are weird
60
continue
61
}
62
63
-
idx := currentMonth - issueMonth
64
items := &timeline.ByMonth[idx].IssueEvents.Items
65
66
*items = append(*items, &issue)
···
77
if repo.Source != "" {
78
sourceRepo, err = GetRepoByAtUri(e, repo.Source)
79
if err != nil {
80
-
return nil, err
81
}
82
}
83
84
-
repoMonth := repo.Created.Month()
85
86
-
if currentMonth-repoMonth >= TimeframeMonths {
87
// shouldn't happen; but times are weird
88
continue
89
}
90
91
-
idx := currentMonth - repoMonth
92
93
items := &timeline.ByMonth[idx].RepoEvents
94
*items = append(*items, models.RepoEvent{
···
98
}
99
100
return &timeline, nil
101
}
102
103
func UpsertProfile(tx *sql.Tx, profile *models.Profile) error {
···
20
timeline := models.ProfileTimeline{
21
ByMonth: make([]models.ByMonth, TimeframeMonths),
22
}
23
+
now := time.Now()
24
timeframe := fmt.Sprintf("-%d months", TimeframeMonths)
25
26
pulls, err := GetPullsByOwnerDid(e, forDid, timeframe)
···
30
31
// group pulls by month
32
for _, pull := range pulls {
33
+
monthsAgo := monthsBetween(pull.Created, now)
34
35
+
if monthsAgo >= TimeframeMonths {
36
// shouldn't happen; but times are weird
37
continue
38
}
39
40
+
idx := monthsAgo
41
items := &timeline.ByMonth[idx].PullEvents.Items
42
43
*items = append(*items, &pull)
···
53
}
54
55
for _, issue := range issues {
56
+
monthsAgo := monthsBetween(issue.Created, now)
57
58
+
if monthsAgo >= TimeframeMonths {
59
// shouldn't happen; but times are weird
60
continue
61
}
62
63
+
idx := monthsAgo
64
items := &timeline.ByMonth[idx].IssueEvents.Items
65
66
*items = append(*items, &issue)
···
77
if repo.Source != "" {
78
sourceRepo, err = GetRepoByAtUri(e, repo.Source)
79
if err != nil {
80
+
// the source repo was not found, skip this bit
81
+
log.Println("profile", "err", err)
82
}
83
}
84
85
+
monthsAgo := monthsBetween(repo.Created, now)
86
87
+
if monthsAgo >= TimeframeMonths {
88
// shouldn't happen; but times are weird
89
continue
90
}
91
92
+
idx := monthsAgo
93
94
items := &timeline.ByMonth[idx].RepoEvents
95
*items = append(*items, models.RepoEvent{
···
99
}
100
101
return &timeline, nil
102
+
}
103
+
104
+
func monthsBetween(from, to time.Time) int {
105
+
years := to.Year() - from.Year()
106
+
months := int(to.Month() - from.Month())
107
+
return years*12 + months
108
}
109
110
func UpsertProfile(tx *sql.Tx, profile *models.Profile) error {
+1
-1
appview/db/punchcard.go
+1
-1
appview/db/punchcard.go
-5
appview/knots/knots.go
-5
appview/knots/knots.go
···
666
k.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
667
return
668
}
669
-
if memberId.Handle.IsInvalidHandle() {
670
-
l.Error("failed to resolve member identity to handle")
671
-
k.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
672
-
return
673
-
}
674
675
// remove from enforcer
676
err = k.Enforcer.RemoveKnotMember(domain, memberId.DID.String())
+4
appview/middleware/middleware.go
+4
appview/middleware/middleware.go
···
223
)
224
if err != nil {
225
log.Println("failed to resolve repo", "err", err)
226
mw.pages.ErrorKnot404(w)
227
return
228
}
···
240
f, err := mw.repoResolver.Resolve(r)
241
if err != nil {
242
log.Println("failed to fully resolve repo", err)
243
mw.pages.ErrorKnot404(w)
244
return
245
}
···
288
f, err := mw.repoResolver.Resolve(r)
289
if err != nil {
290
log.Println("failed to fully resolve repo", err)
291
mw.pages.ErrorKnot404(w)
292
return
293
}
···
324
f, err := mw.repoResolver.Resolve(r)
325
if err != nil {
326
log.Println("failed to fully resolve repo", err)
327
mw.pages.ErrorKnot404(w)
328
return
329
}
···
223
)
224
if err != nil {
225
log.Println("failed to resolve repo", "err", err)
226
+
w.WriteHeader(http.StatusNotFound)
227
mw.pages.ErrorKnot404(w)
228
return
229
}
···
241
f, err := mw.repoResolver.Resolve(r)
242
if err != nil {
243
log.Println("failed to fully resolve repo", err)
244
+
w.WriteHeader(http.StatusNotFound)
245
mw.pages.ErrorKnot404(w)
246
return
247
}
···
290
f, err := mw.repoResolver.Resolve(r)
291
if err != nil {
292
log.Println("failed to fully resolve repo", err)
293
+
w.WriteHeader(http.StatusNotFound)
294
mw.pages.ErrorKnot404(w)
295
return
296
}
···
327
f, err := mw.repoResolver.Resolve(r)
328
if err != nil {
329
log.Println("failed to fully resolve repo", err)
330
+
w.WriteHeader(http.StatusNotFound)
331
mw.pages.ErrorKnot404(w)
332
return
333
}
+35
-35
appview/pages/templates/repo/fragments/splitDiff.html
+35
-35
appview/pages/templates/repo/fragments/splitDiff.html
···
3
{{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right bg-white dark:bg-gray-800" -}}
4
{{- $linkStyle := "text-gray-400 dark:text-gray-500 hover:underline" -}}
5
{{- $lineNrSepStyle := "pr-2 border-r border-gray-200 dark:border-gray-700" -}}
6
-
{{- $containerStyle := "flex min-w-full items-center target:border target:rounded-sm target:border-yellow-200 target:dark:border-yellow-700 scroll-mt-20" -}}
7
{{- $emptyStyle := "bg-gray-200/30 dark:bg-gray-700/30" -}}
8
{{- $addStyle := "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400" -}}
9
{{- $delStyle := "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400 " -}}
10
{{- $ctxStyle := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}}
11
{{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}}
12
<div class="grid grid-cols-2 divide-x divide-gray-200 dark:divide-gray-700">
13
-
<pre class="overflow-x-auto col-span-1"><div class="overflow-x-auto"><div class="min-w-full inline-block">{{- range .TextFragments -}}<div class="bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</div>
14
{{- range .LeftLines -}}
15
{{- if .IsEmpty -}}
16
-
<div class="{{ $emptyStyle }} {{ $containerStyle }}">
17
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><span aria-hidden="true" class="invisible">{{.LineNumber}}</span></div>
18
-
<div class="{{ $opStyle }}"><span aria-hidden="true" class="invisible">{{ .Op.String }}</span></div>
19
-
<div class="px-2 invisible" aria-hidden="true">{{ .Content }}</div>
20
-
</div>
21
{{- else if eq .Op.String "-" -}}
22
-
<div class="{{ $delStyle }} {{ $containerStyle }}" id="{{$name}}-O{{.LineNumber}}">
23
-
<div class="{{ $lineNrStyle }} {{ $lineNrSepStyle }}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{.LineNumber}}">{{ .LineNumber }}</a></div>
24
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
25
-
<div class="px-2">{{ .Content }}</div>
26
-
</div>
27
{{- else if eq .Op.String " " -}}
28
-
<div class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-O{{.LineNumber}}">
29
-
<div class="{{ $lineNrStyle }} {{ $lineNrSepStyle }}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{.LineNumber}}">{{ .LineNumber }}</a></div>
30
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
31
-
<div class="px-2">{{ .Content }}</div>
32
-
</div>
33
{{- end -}}
34
{{- end -}}
35
-
{{- end -}}</div></div></pre>
36
37
-
<pre class="overflow-x-auto col-span-1"><div class="overflow-x-auto"><div class="min-w-full inline-block">{{- range .TextFragments -}}<div class="bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</div>
38
{{- range .RightLines -}}
39
{{- if .IsEmpty -}}
40
-
<div class="{{ $emptyStyle }} {{ $containerStyle }}">
41
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><span aria-hidden="true" class="invisible">{{.LineNumber}}</span></div>
42
-
<div class="{{ $opStyle }}"><span aria-hidden="true" class="invisible">{{ .Op.String }}</span></div>
43
-
<div class="px-2 invisible" aria-hidden="true">{{ .Content }}</div>
44
-
</div>
45
{{- else if eq .Op.String "+" -}}
46
-
<div class="{{ $addStyle }} {{ $containerStyle }}" id="{{$name}}-N{{.LineNumber}}">
47
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{.LineNumber}}">{{ .LineNumber }}</a></div>
48
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
49
-
<div class="px-2" >{{ .Content }}</div>
50
-
</div>
51
{{- else if eq .Op.String " " -}}
52
-
<div class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-N{{.LineNumber}}">
53
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{.LineNumber}}">{{ .LineNumber }}</a></div>
54
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
55
-
<div class="px-2">{{ .Content }}</div>
56
-
</div>
57
{{- end -}}
58
{{- end -}}
59
-
{{- end -}}</div></div></pre>
60
</div>
61
{{ end }}
···
3
{{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right bg-white dark:bg-gray-800" -}}
4
{{- $linkStyle := "text-gray-400 dark:text-gray-500 hover:underline" -}}
5
{{- $lineNrSepStyle := "pr-2 border-r border-gray-200 dark:border-gray-700" -}}
6
+
{{- $containerStyle := "inline-flex w-full items-center target:border target:rounded-sm target:border-yellow-200 target:dark:border-yellow-700 scroll-mt-20" -}}
7
{{- $emptyStyle := "bg-gray-200/30 dark:bg-gray-700/30" -}}
8
{{- $addStyle := "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400" -}}
9
{{- $delStyle := "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400 " -}}
10
{{- $ctxStyle := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}}
11
{{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}}
12
<div class="grid grid-cols-2 divide-x divide-gray-200 dark:divide-gray-700">
13
+
<div class="overflow-x-auto col-span-1 font-mono leading-normal"><div class="overflow-x-auto"><div class="inline-flex flex-col min-w-full">{{- range .TextFragments -}}<span class="block bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</span>
14
{{- range .LeftLines -}}
15
{{- if .IsEmpty -}}
16
+
<span class="{{ $emptyStyle }} {{ $containerStyle }}">
17
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><span aria-hidden="true" class="invisible">{{.LineNumber}}</span></span>
18
+
<span class="{{ $opStyle }}"><span aria-hidden="true" class="invisible">{{ .Op.String }}</span></span>
19
+
<span class="px-2 invisible" aria-hidden="true">{{ .Content }}</span>
20
+
</span>
21
{{- else if eq .Op.String "-" -}}
22
+
<span class="{{ $delStyle }} {{ $containerStyle }}" id="{{$name}}-O{{.LineNumber}}">
23
+
<span class="{{ $lineNrStyle }} {{ $lineNrSepStyle }}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{.LineNumber}}">{{ .LineNumber }}</a></span>
24
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
25
+
<span class="px-2 whitespace-pre">{{ .Content }}</span>
26
+
</span>
27
{{- else if eq .Op.String " " -}}
28
+
<span class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-O{{.LineNumber}}">
29
+
<span class="{{ $lineNrStyle }} {{ $lineNrSepStyle }}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{.LineNumber}}">{{ .LineNumber }}</a></span>
30
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
31
+
<span class="px-2 whitespace-pre">{{ .Content }}</span>
32
+
</span>
33
{{- end -}}
34
{{- end -}}
35
+
{{- end -}}</div></div></div>
36
37
+
<div class="overflow-x-auto col-span-1 font-mono leading-normal"><div class="overflow-x-auto"><div class="inline-flex flex-col min-w-full">{{- range .TextFragments -}}<span class="block bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</span>
38
{{- range .RightLines -}}
39
{{- if .IsEmpty -}}
40
+
<span class="{{ $emptyStyle }} {{ $containerStyle }}">
41
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><span aria-hidden="true" class="invisible">{{.LineNumber}}</span></span>
42
+
<span class="{{ $opStyle }}"><span aria-hidden="true" class="invisible">{{ .Op.String }}</span></span>
43
+
<span class="px-2 invisible" aria-hidden="true">{{ .Content }}</span>
44
+
</span>
45
{{- else if eq .Op.String "+" -}}
46
+
<span class="{{ $addStyle }} {{ $containerStyle }}" id="{{$name}}-N{{.LineNumber}}">
47
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{.LineNumber}}">{{ .LineNumber }}</a></span>
48
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
49
+
<span class="px-2 whitespace-pre">{{ .Content }}</span>
50
+
</span>
51
{{- else if eq .Op.String " " -}}
52
+
<span class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-N{{.LineNumber}}">
53
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{.LineNumber}}">{{ .LineNumber }}</a> </span>
54
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
55
+
<span class="px-2 whitespace-pre">{{ .Content }}</span>
56
+
</span>
57
{{- end -}}
58
{{- end -}}
59
+
{{- end -}}</div></div></div>
60
</div>
61
{{ end }}
+21
-22
appview/pages/templates/repo/fragments/unifiedDiff.html
+21
-22
appview/pages/templates/repo/fragments/unifiedDiff.html
···
1
{{ define "repo/fragments/unifiedDiff" }}
2
{{ $name := .Id }}
3
-
<pre class="overflow-x-auto"><div class="overflow-x-auto"><div class="min-w-full inline-block">{{- range .TextFragments -}}<div class="bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</div>
4
{{- $oldStart := .OldPosition -}}
5
{{- $newStart := .NewPosition -}}
6
{{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right bg-white dark:bg-gray-800 target:bg-yellow-200 target:dark:bg-yellow-600" -}}
7
{{- $linkStyle := "text-gray-400 dark:text-gray-500 hover:underline" -}}
8
{{- $lineNrSepStyle1 := "" -}}
9
{{- $lineNrSepStyle2 := "pr-2 border-r border-gray-200 dark:border-gray-700" -}}
10
-
{{- $containerStyle := "flex min-w-full items-center target:border target:rounded-sm target:border-yellow-200 target:dark:border-yellow-700 scroll-mt-20" -}}
11
{{- $addStyle := "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400 " -}}
12
{{- $delStyle := "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400 " -}}
13
{{- $ctxStyle := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}}
14
{{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}}
15
{{- range .Lines -}}
16
{{- if eq .Op.String "+" -}}
17
-
<div class="{{ $addStyle }} {{ $containerStyle }}" id="{{$name}}-N{{$newStart}}">
18
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><span aria-hidden="true" class="invisible">{{$newStart}}</span></div>
19
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{$newStart}}">{{ $newStart }}</a></div>
20
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
21
-
<div class="px-2">{{ .Line }}</div>
22
-
</div>
23
{{- $newStart = add64 $newStart 1 -}}
24
{{- end -}}
25
{{- if eq .Op.String "-" -}}
26
-
<div class="{{ $delStyle }} {{ $containerStyle }}" id="{{$name}}-O{{$oldStart}}">
27
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}">{{ $oldStart }}</a></div>
28
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><span aria-hidden="true" class="invisible">{{$oldStart}}</span></div>
29
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
30
-
<div class="px-2">{{ .Line }}</div>
31
-
</div>
32
{{- $oldStart = add64 $oldStart 1 -}}
33
{{- end -}}
34
{{- if eq .Op.String " " -}}
35
-
<div class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-O{{$oldStart}}-N{{$newStart}}">
36
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}-N{{$newStart}}">{{ $oldStart }}</a></div>
37
-
<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}-N{{$newStart}}">{{ $newStart }}</a></div>
38
-
<div class="{{ $opStyle }}">{{ .Op.String }}</div>
39
-
<div class="px-2">{{ .Line }}</div>
40
-
</div>
41
{{- $newStart = add64 $newStart 1 -}}
42
{{- $oldStart = add64 $oldStart 1 -}}
43
{{- end -}}
44
{{- end -}}
45
-
{{- end -}}</div></div></pre>
46
{{ end }}
47
-
···
1
{{ define "repo/fragments/unifiedDiff" }}
2
{{ $name := .Id }}
3
+
<div class="overflow-x-auto font-mono leading-normal"><div class="overflow-x-auto"><div class="inline-flex flex-col min-w-full">{{- range .TextFragments -}}<span class="block bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</span>
4
{{- $oldStart := .OldPosition -}}
5
{{- $newStart := .NewPosition -}}
6
{{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right bg-white dark:bg-gray-800 target:bg-yellow-200 target:dark:bg-yellow-600" -}}
7
{{- $linkStyle := "text-gray-400 dark:text-gray-500 hover:underline" -}}
8
{{- $lineNrSepStyle1 := "" -}}
9
{{- $lineNrSepStyle2 := "pr-2 border-r border-gray-200 dark:border-gray-700" -}}
10
+
{{- $containerStyle := "inline-flex w-full items-center target:border target:rounded-sm target:border-yellow-200 target:dark:border-yellow-700 scroll-mt-20" -}}
11
{{- $addStyle := "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400 " -}}
12
{{- $delStyle := "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400 " -}}
13
{{- $ctxStyle := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}}
14
{{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}}
15
{{- range .Lines -}}
16
{{- if eq .Op.String "+" -}}
17
+
<span class="{{ $addStyle }} {{ $containerStyle }}" id="{{$name}}-N{{$newStart}}">
18
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><span aria-hidden="true" class="invisible">{{$newStart}}</span></span>
19
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{$newStart}}">{{ $newStart }}</a></span>
20
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
21
+
<span class="px-2 whitespace-pre">{{ .Line }}</span>
22
+
</span>
23
{{- $newStart = add64 $newStart 1 -}}
24
{{- end -}}
25
{{- if eq .Op.String "-" -}}
26
+
<span class="{{ $delStyle }} {{ $containerStyle }}" id="{{$name}}-O{{$oldStart}}">
27
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}">{{ $oldStart }}</a></span>
28
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><span aria-hidden="true" class="invisible">{{$oldStart}}</span></span>
29
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
30
+
<span class="px-2 whitespace-pre">{{ .Line }}</span>
31
+
</span>
32
{{- $oldStart = add64 $oldStart 1 -}}
33
{{- end -}}
34
{{- if eq .Op.String " " -}}
35
+
<span class="{{ $ctxStyle }} {{ $containerStyle }}" id="{{$name}}-O{{$oldStart}}-N{{$newStart}}">
36
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}-N{{$newStart}}">{{ $oldStart }}</a></span>
37
+
<span class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}-N{{$newStart}}">{{ $newStart }}</a></span>
38
+
<span class="{{ $opStyle }}">{{ .Op.String }}</span>
39
+
<span class="px-2 whitespace-pre">{{ .Line }}</span>
40
+
</span>
41
{{- $newStart = add64 $newStart 1 -}}
42
{{- $oldStart = add64 $oldStart 1 -}}
43
{{- end -}}
44
{{- end -}}
45
+
{{- end -}}</div></div></div>
46
{{ end }}
+1
appview/repo/archive.go
+1
appview/repo/archive.go
-5
appview/spindles/spindles.go
-5
appview/spindles/spindles.go
···
653
s.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
654
return
655
}
656
-
if memberId.Handle.IsInvalidHandle() {
657
-
l.Error("failed to resolve member identity to handle")
658
-
s.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
659
-
return
660
-
}
661
662
tx, err := s.Db.Begin()
663
if err != nil {
+6
-4
appview/state/profile.go
+6
-4
appview/state/profile.go
···
163
}
164
165
// populate commit counts in the timeline, using the punchcard
166
-
currentMonth := time.Now().Month()
167
for _, p := range profile.Punchcard.Punches {
168
-
idx := currentMonth - p.Date.Month()
169
-
if int(idx) < len(timeline.ByMonth) {
170
-
timeline.ByMonth[idx].Commits += p.Count
171
}
172
}
173
···
163
}
164
165
// populate commit counts in the timeline, using the punchcard
166
+
now := time.Now()
167
for _, p := range profile.Punchcard.Punches {
168
+
years := now.Year() - p.Date.Year()
169
+
months := int(now.Month() - p.Date.Month())
170
+
monthsAgo := years*12 + months
171
+
if monthsAgo >= 0 && monthsAgo < len(timeline.ByMonth) {
172
+
timeline.ByMonth[monthsAgo].Commits += p.Count
173
}
174
}
175
+2
appview/state/router.go
+2
appview/state/router.go
···
109
})
110
111
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
112
+
w.WriteHeader(http.StatusNotFound)
113
s.pages.Error404(w)
114
})
115
···
183
r.Get("/brand", s.Brand)
184
185
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
186
+
w.WriteHeader(http.StatusNotFound)
187
s.pages.Error404(w)
188
})
189
return r
+32
-7
docs/template.html
+32
-7
docs/template.html
···
43
$endfor$
44
45
$if(toc)$
46
-
<!-- mobile topbar toc -->
47
-
<details id="mobile-$idprefix$TOC" role="doc-toc" class="md:hidden bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 z-50 space-y-4 group px-6 py-4">
48
-
<summary class="cursor-pointer list-none text-sm font-semibold select-none flex gap-2 justify-between items-center dark:text-white">
49
$if(toc-title)$$toc-title$$else$Table of Contents$endif$
50
-
<span class="group-open:hidden inline">${ menu.svg() }</span>
51
-
<span class="hidden group-open:inline">${ x.svg() }</span>
52
-
</summary>
53
${ table-of-contents:toc.html() }
54
-
</details>
55
<!-- desktop sidebar toc -->
56
<nav id="$idprefix$TOC" role="doc-toc" class="hidden md:block fixed left-0 top-0 w-80 h-screen bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 overflow-y-auto p-4 z-50">
57
$if(toc-title)$
···
43
$endfor$
44
45
$if(toc)$
46
+
<!-- mobile TOC trigger -->
47
+
<div class="md:hidden px-6 py-4 border-b border-gray-200 dark:border-gray-700">
48
+
<button
49
+
type="button"
50
+
popovertarget="mobile-toc-popover"
51
+
popovertargetaction="toggle"
52
+
class="w-full flex gap-2 items-center text-sm font-semibold dark:text-white"
53
+
>
54
+
${ menu.svg() }
55
+
$if(toc-title)$$toc-title$$else$Table of Contents$endif$
56
+
</button>
57
+
</div>
58
+
59
+
<div
60
+
id="mobile-toc-popover"
61
+
popover
62
+
class="mobile-toc-popover
63
+
bg-white dark:bg-gray-800
64
+
border-b border-gray-200 dark:border-gray-700
65
+
h-full overflow-y-auto
66
+
px-6 py-4 fixed inset-x-0 top-0 w-fit max-w-4/5 m-0"
67
+
>
68
+
<button
69
+
type="button"
70
+
popovertarget="mobile-toc-popover"
71
+
popovertargetaction="toggle"
72
+
class="w-full flex gap-2 items-center text-sm font-semibold dark:text-white mb-4">
73
+
${ x.svg() }
74
$if(toc-title)$$toc-title$$else$Table of Contents$endif$
75
+
</button>
76
${ table-of-contents:toc.html() }
77
+
</div>
78
+
79
+
80
<!-- desktop sidebar toc -->
81
<nav id="$idprefix$TOC" role="doc-toc" class="hidden md:block fixed left-0 top-0 w-80 h-screen bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 overflow-y-auto p-4 z-50">
82
$if(toc-title)$
+1
-1
flake.nix
+1
-1
flake.nix
+1
input.css
+1
input.css
+21
-3
spindle/server.go
+21
-3
spindle/server.go
···
8
"log/slog"
9
"maps"
10
"net/http"
11
12
"github.com/go-chi/chi/v5"
13
"tangled.org/core/api/tangled"
···
30
)
31
32
//go:embed motd
33
-
var motd []byte
34
35
const (
36
rbacDomain = "thisserver"
···
47
cfg *config.Config
48
ks *eventconsumer.Consumer
49
res *idresolver.Resolver
50
-
vault secrets.Manager
51
}
52
53
// New creates a new Spindle server with the provided configuration and engines.
···
128
cfg: cfg,
129
res: resolver,
130
vault: vault,
131
}
132
133
err = e.AddSpindle(rbacDomain)
···
201
return s.e
202
}
203
204
// Start starts the Spindle server (blocking).
205
func (s *Spindle) Start(ctx context.Context) error {
206
// starts a job queue runner in the background
···
246
mux := chi.NewRouter()
247
248
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
249
-
w.Write(motd)
250
})
251
mux.HandleFunc("/events", s.Events)
252
mux.HandleFunc("/logs/{knot}/{rkey}/{name}", s.Logs)
···
8
"log/slog"
9
"maps"
10
"net/http"
11
+
"sync"
12
13
"github.com/go-chi/chi/v5"
14
"tangled.org/core/api/tangled"
···
31
)
32
33
//go:embed motd
34
+
var defaultMotd []byte
35
36
const (
37
rbacDomain = "thisserver"
···
48
cfg *config.Config
49
ks *eventconsumer.Consumer
50
res *idresolver.Resolver
51
+
vault secrets.Manager
52
+
motd []byte
53
+
motdMu sync.RWMutex
54
}
55
56
// New creates a new Spindle server with the provided configuration and engines.
···
131
cfg: cfg,
132
res: resolver,
133
vault: vault,
134
+
motd: defaultMotd,
135
}
136
137
err = e.AddSpindle(rbacDomain)
···
205
return s.e
206
}
207
208
+
// SetMotdContent sets custom MOTD content, replacing the embedded default.
209
+
func (s *Spindle) SetMotdContent(content []byte) {
210
+
s.motdMu.Lock()
211
+
defer s.motdMu.Unlock()
212
+
s.motd = content
213
+
}
214
+
215
+
// GetMotdContent returns the current MOTD content.
216
+
func (s *Spindle) GetMotdContent() []byte {
217
+
s.motdMu.RLock()
218
+
defer s.motdMu.RUnlock()
219
+
return s.motd
220
+
}
221
+
222
// Start starts the Spindle server (blocking).
223
func (s *Spindle) Start(ctx context.Context) error {
224
// starts a job queue runner in the background
···
264
mux := chi.NewRouter()
265
266
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
267
+
w.Write(s.GetMotdContent())
268
})
269
mux.HandleFunc("/events", s.Events)
270
mux.HandleFunc("/logs/{knot}/{rkey}/{name}", s.Logs)