+2
appview/pages/pages.go
+2
appview/pages/pages.go
···
293
293
LoggedInUser *auth.User
294
294
RepoInfo RepoInfo
295
295
types.RepoLogResponse
296
+
Active string
296
297
}
297
298
298
299
func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {
300
+
params.Active = "overview"
299
301
return p.execute("repo/log", w, params)
300
302
}
301
303
+46
-35
appview/pages/templates/layouts/repobase.html
+46
-35
appview/pages/templates/layouts/repobase.html
···
3
3
{{ define "content" }}
4
4
<section id="repo-header" class="mb-4">
5
5
<p class="text-xl">
6
-
<a href="/{{ .RepoInfo.OwnerWithAt }}" class="no-underline hover:underline">{{ .RepoInfo.OwnerWithAt }}</a>
7
-
/
8
-
<a href="/{{ .RepoInfo.FullName }}" class="no-underline hover:underline">{{ .RepoInfo.Name }}</a>
6
+
<a
7
+
href="/{{ .RepoInfo.OwnerWithAt }}"
8
+
class="no-underline hover:underline"
9
+
>{{ .RepoInfo.OwnerWithAt }}</a
10
+
>
11
+
/
12
+
<a
13
+
href="/{{ .RepoInfo.FullName }}"
14
+
class="no-underline hover:underline"
15
+
>{{ .RepoInfo.Name }}</a
16
+
>
9
17
</p>
10
18
<span>
11
-
{{ if .RepoInfo.Description }}
12
-
{{ .RepoInfo.Description }}
13
-
{{ else }}
14
-
<span class="italic">this repo has no description</span>
15
-
{{ end }}
19
+
{{ if .RepoInfo.Description }}
20
+
{{ .RepoInfo.Description }}
21
+
{{ else }}
22
+
<span class="italic">this repo has no description</span>
23
+
{{ end }}
16
24
</span>
17
25
</section>
18
26
<section id="repo-links" class="min-h-screen flex flex-col">
19
-
<nav class="w-full mx-auto">
20
-
<div class="flex z-60 border-black border-b">
21
-
{{ $activeTabStyles := "border-black border-l border-r border-t border-b-0 -mb-px bg-white" }}
22
-
{{ $tabs := .RepoInfo.GetTabs }}
23
-
{{ range $item := $tabs }}
24
-
{{ $key := index $item 0 }}
25
-
{{ $value := index $item 1 }}
26
-
<a
27
-
href="/{{ $.RepoInfo.FullName }}{{ $value }}"
28
-
class="relative -mr-px group no-underline"
29
-
hx-boost="true"
30
-
>
31
-
<div
32
-
class="px-4 py-2 mr-1 text-black min-w-[80px] text-center relative group-hover:bg-gray-200
33
-
{{ if eq $.Active $key }}{{ $activeTabStyles }}{{ end }}"
34
-
>
35
-
{{ $key }}
36
-
</div>
37
-
</a>
38
-
{{ end}}
39
-
</div>
40
-
</nav>
41
-
<section
42
-
class="bg-white p-6 min-h-[200px] border-l border-r border-b border-black relative z-20 w-full mx-auto">
43
-
{{ block "repoContent" . }}{{ end }}
44
-
</section>
45
-
{{ block "repoAfter" . }} {{ end }}
27
+
<nav class="w-full mx-auto">
28
+
<div class="flex z-60 border-gray-200 border-b">
29
+
{{ $activeTabStyles := "border-gray-200 border-l border-r border-t border-b-0 -mb-px bg-white" }}
30
+
{{ $tabs := .RepoInfo.GetTabs }}
31
+
{{ range $item := $tabs }}
32
+
{{ $key := index $item 0 }}
33
+
{{ $value := index $item 1 }}
34
+
<a
35
+
href="/{{ $.RepoInfo.FullName }}{{ $value }}"
36
+
class="relative -mr-px group no-underline"
37
+
hx-boost="true"
38
+
>
39
+
<div
40
+
class="px-4 py-2 mr-1 text-black min-w-[80px] text-center relative group-hover:bg-gray-200
41
+
{{ if eq $.Active $key }}
42
+
{{ $activeTabStyles }}
43
+
{{ end }}"
44
+
>
45
+
{{ $key }}
46
+
</div>
47
+
</a>
48
+
{{ end }}
49
+
</div>
50
+
</nav>
51
+
<section
52
+
class="bg-white p-6 min-h-[200px] border-l border-r border-b border-gray-200 relative z-20 w-full mx-auto"
53
+
>
54
+
{{ block "repoContent" . }}{{ end }}
55
+
</section>
56
+
{{ block "repoAfter" . }}{{ end }}
46
57
</section>
47
58
{{ end }}
48
59
+2
-2
appview/pages/templates/layouts/topbar.html
+2
-2
appview/pages/templates/layouts/topbar.html
···
1
1
{{ define "layouts/topbar" }}
2
2
{{ $linkstyle := "text-gray-400 hover:text-gray-900 no-underline" }}
3
-
<nav class="space-x-4 mb-4 py-2 border-b border-black">
3
+
<nav class="space-x-4 mb-4 py-2 border-b border-gray-200">
4
4
<div class="container flex justify-between p-0">
5
5
<div id="left-items">
6
6
<a href="/" hx-boost="true" class="{{ $linkstyle }} flex gap-2">
···
17
17
<summary class="{{ $linkstyle }} cursor-pointer list-none">
18
18
{{ didOrHandle .Did .Handle }}
19
19
</summary>
20
-
<div class="absolute flex flex-col right-0 mt-4 p-2 w-48 bg-white border border-black z-50">
20
+
<div class="absolute flex flex-col right-0 mt-4 p-2 w-48 bg-white border border-gray-200 z-50">
21
21
<a href="/{{ didOrHandle .Did .Handle }}"class="{{ $linkstyle }}">profile</a>
22
22
<a href="/knots"class="{{ $linkstyle }}">knots</a>
23
23
<a href="/settings"class="{{ $linkstyle }}">settings</a>
+2
-2
appview/pages/templates/repo/commit.html
+2
-2
appview/pages/templates/repo/commit.html
···
63
63
{{ $last := sub (len $diff) 1 }}
64
64
{{ range $idx, $hunk := $diff }}
65
65
{{ with $hunk }}
66
-
<section class="mt-4 border border-black w-full mx-auto">
66
+
<section class="mt-4 border border-gray-200 w-full mx-auto">
67
67
<div id="file-{{ .Name.New }}">
68
68
<div id="diff-file">
69
69
<details open>
70
70
<summary class="list-none cursor-pointer sticky top-0">
71
-
<div id="diff-file-header" class="border-b cursor-pointer bg-white border-black flex justify-between">
71
+
<div id="diff-file-header" class="border-b cursor-pointer bg-white border-gray-200 flex justify-between">
72
72
<div id="left-side-items" class="p-2">
73
73
{{ if .IsNew }}
74
74
<span class="diff-type p-1 mr-1 font-mono text-sm bg-green-100 rounded text-green-700 select-none">A</span>
+103
-107
appview/pages/templates/repo/index.html
+103
-107
appview/pages/templates/repo/index.html
···
1
1
{{ define "repoContent" }}
2
2
<main>
3
+
<div class="flex justify-between pb-5">
4
+
<select
5
+
onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + this.value"
6
+
class="p-1 border border-gray-200 bg-white"
7
+
>
8
+
<optgroup label="branches" class="bold text-sm">
9
+
{{ range .Branches }}
10
+
<option
11
+
value="{{ .Reference.Name }}"
12
+
class="py-1"
13
+
{{ if eq .Reference.Name $.Ref }}
14
+
selected
15
+
{{ end }}
16
+
>
17
+
{{ .Reference.Name }}
18
+
</option>
19
+
{{ end }}
20
+
</optgroup>
21
+
<optgroup label="tags" class="bold text-sm">
22
+
{{ range .Tags }}
23
+
<option
24
+
value="{{ .Reference.Name }}"
25
+
class="py-1"
26
+
{{ if eq .Reference.Name $.Ref }}
27
+
selected
28
+
{{ end }}
29
+
>
30
+
{{ .Reference.Name }}
31
+
</option>
32
+
{{ else }}
33
+
<option class="py-1" disabled>no tags found</option>
34
+
{{ end }}
35
+
</optgroup>
36
+
</select>
37
+
<a
38
+
href="/{{ .RepoInfo.FullName }}/commits/{{ .Ref }}"
39
+
class="btn ml-2 no-underline flex items-center gap-2"
40
+
>
41
+
<i class="w-4 h-4" data-lucide="logs"></i>
42
+
{{ .TotalCommits }}
43
+
{{ if eq .TotalCommits 1 }}commit{{ else }}commits{{ end }}
44
+
</a>
45
+
</div>
46
+
3
47
<div class="flex gap-4">
4
-
<div id="file-tree" class="w-3/5">
48
+
<div id="file-tree" class="w-3/5 pr-2 border-r border-gray-200">
5
49
{{ $containerstyle := "py-1" }}
6
50
{{ $linkstyle := "no-underline hover:underline" }}
7
51
8
-
9
-
<div class="flex justify-end">
10
-
<select
11
-
onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + this.value"
12
-
class="p-1 border border-gray-500 bg-white"
13
-
>
14
-
<optgroup label="branches" class="bold text-sm">
15
-
{{ range .Branches }}
16
-
<option
17
-
value="{{ .Reference.Name }}"
18
-
class="py-1"
19
-
{{ if eq .Reference.Name $.Ref }}
20
-
selected
21
-
{{ end }}
22
-
>
23
-
{{ .Reference.Name }}
24
-
</option>
25
-
{{ end }}
26
-
</optgroup>
27
-
<optgroup label="tags" class="bold text-sm">
28
-
{{ range .Tags }}
29
-
<option
30
-
value="{{ .Reference.Name }}"
31
-
class="py-1"
32
-
{{ if eq .Reference.Name $.Ref }}
33
-
selected
34
-
{{ end }}
35
-
>
36
-
{{ .Reference.Name }}
37
-
</option>
38
-
{{ else }}
39
-
<option class="py-1" disabled>
40
-
no tags found
41
-
</option>
42
-
{{ end }}
43
-
</optgroup>
44
-
</select>
45
-
</div>
46
-
47
52
{{ range .Files }}
48
53
{{ if not .IsFile }}
49
54
<div class="{{ $containerstyle }}">
···
94
99
{{ end }}
95
100
{{ end }}
96
101
</div>
102
+
97
103
<div id="commit-log" class="flex-1">
98
104
{{ range .Commits }}
99
-
<div
100
-
class="relative
101
-
px-4
102
-
py-4
103
-
border-l
104
-
border-black
105
-
before:content-['']
106
-
before:absolute
107
-
before:w-1
108
-
before:h-1
109
-
before:bg-black
110
-
before:rounded-full
111
-
before:left-[-2.2px]
112
-
before:top-1/2
113
-
before:-translate-y-1/2
114
-
"
115
-
>
116
-
<div id="commit-message">
117
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
118
-
<div class="text-base cursor-pointer">
119
-
<div>
105
+
<div class="flex flex-row items-center">
106
+
<i
107
+
class="w-5 h-5 text-gray-400 align-top"
108
+
data-lucide="git-commit-horizontal"
109
+
></i>
110
+
<div class="relative px-4 py-4">
111
+
<div id="commit-message">
112
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
113
+
<div class="text-base cursor-pointer">
120
114
<div>
121
-
<a
122
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
123
-
class="inline no-underline hover:underline"
124
-
>{{ index $messageParts 0 }}</a
125
-
>
115
+
<div>
116
+
<a
117
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
118
+
class="inline no-underline hover:underline"
119
+
>{{ index $messageParts 0 }}</a
120
+
>
121
+
{{ if gt (len $messageParts) 1 }}
122
+
123
+
<button
124
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
125
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
126
+
>
127
+
<i
128
+
class="w-3 h-3"
129
+
data-lucide="ellipsis"
130
+
></i>
131
+
</button>
132
+
{{ end }}
133
+
</div>
126
134
{{ if gt (len $messageParts) 1 }}
127
-
128
-
<button
129
-
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
130
-
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
135
+
<p
136
+
class="hidden mt-1 text-sm cursor-text pb-2"
131
137
>
132
-
<i
133
-
class="w-3 h-3"
134
-
data-lucide="ellipsis"
135
-
></i>
136
-
</button>
138
+
{{ nl2br (unwrapText (index $messageParts 1)) }}
139
+
</p>
137
140
{{ end }}
138
141
</div>
139
-
{{ if gt (len $messageParts) 1 }}
140
-
<p
141
-
class="hidden mt-1 text-sm cursor-text pb-2"
142
-
>
143
-
{{ nl2br (unwrapText (index $messageParts 1)) }}
144
-
</p>
145
-
{{ end }}
146
142
</div>
147
143
</div>
148
-
</div>
149
144
150
-
<div class="text-xs text-gray-500">
151
-
<span class="font-mono">
152
-
<a
153
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
154
-
class="text-gray-500 no-underline hover:underline"
155
-
>{{ slice .Hash.String 0 8 }}</a
156
-
>
157
-
</span>
158
-
<span
159
-
class="mx-2 before:content-['·'] before:select-none"
160
-
></span>
161
-
<span>
162
-
<a
163
-
href="mailto:{{ .Author.Email }}"
164
-
class="text-gray-500 no-underline hover:underline"
165
-
>{{ .Author.Name }}</a
166
-
>
167
-
</span>
168
-
<div
169
-
class="inline-block px-1 select-none after:content-['·']"
170
-
></div>
171
-
<span>{{ timeFmt .Author.When }}</span>
145
+
<div class="text-xs text-gray-500">
146
+
<span class="font-mono">
147
+
<a
148
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
149
+
class="text-gray-500 no-underline hover:underline"
150
+
>{{ slice .Hash.String 0 8 }}</a
151
+
>
152
+
</span>
153
+
<span
154
+
class="mx-2 before:content-['·'] before:select-none"
155
+
></span>
156
+
<span>
157
+
<a
158
+
href="mailto:{{ .Author.Email }}"
159
+
class="text-gray-500 no-underline hover:underline"
160
+
>{{ .Author.Name }}</a
161
+
>
162
+
</span>
163
+
<div
164
+
class="inline-block px-1 select-none after:content-['·']"
165
+
></div>
166
+
<span>{{ timeFmt .Author.When }}</span>
167
+
</div>
172
168
</div>
173
169
</div>
174
170
{{ end }}
···
179
175
180
176
{{ define "repoAfter" }}
181
177
{{- if .Readme }}
182
-
<section class="mt-4 p-6 border border-black w-full mx-auto">
178
+
<section class="mt-4 p-6 border border-gray-200 w-full mx-auto">
183
179
<article class="readme">
184
180
{{- .Readme -}}
185
181
</article>
···
187
183
{{- end -}}
188
184
189
185
190
-
<section class="mt-4 p-6 border border-black w-full mx-auto">
186
+
<section class="mt-4 p-6 border border-gray-200 w-full mx-auto">
191
187
<strong>clone</strong>
192
188
<pre>
193
189
git clone https://tangled.sh/{{ .RepoInfo.OwnerWithAt }}/{{ .RepoInfo.Name }} </pre
+3
-3
appview/pages/templates/repo/issues/issues.html
+3
-3
appview/pages/templates/repo/issues/issues.html
···
1
-
{{ define "title" }}issues | {{ .RepoInfo.FullName }}{{ end }}
1
+
{{ define "title" }}Issues · {{ .RepoInfo.FullName }}{{ end }}
2
2
3
3
{{ define "repoContent" }}
4
4
<div class="flex justify-between items-center">
5
-
<h1 class="m-0">issues</h1>
5
+
<h1 class="m-0">Issues</h1>
6
6
<div class="error" id="issues"></div>
7
7
<a
8
8
href="/{{ .RepoInfo.FullName }}/issues/new"
···
15
15
16
16
<section id="issues" class="mt-8 space-y-4">
17
17
{{ range .Issues }}
18
-
<div class="border border-gray-200 p-4">
18
+
<div class="border border-gray-200 p-4 mx-4 hover:bg-gray-50">
19
19
<time class="float-right text-sm">
20
20
{{ .Created | timeFmt }}
21
21
</time>
+100
-17
appview/pages/templates/repo/log.html
+100
-17
appview/pages/templates/repo/log.html
···
1
-
{{define "title"}} log | {{ .RepoInfo.FullName }} {{end}}
1
+
{{ define "title" }}Commits · {{ .RepoInfo.FullName }}{{ end }}
2
2
3
-
{{define "content"}}
3
+
{{ define "repoContent" }}
4
4
5
-
<h1>
6
-
log | {{ .RepoInfo.FullName }}
7
-
</h1>
5
+
<h1>Commits</h1>
8
6
<main>
9
-
<div class="log">
10
-
{{ range .Commits }}
11
-
<div>
12
-
<div><a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" class="commit-hash">{{ slice .Hash.String 0 8 }}</a></div>
13
-
<pre>{{ .Message }}</pre>
7
+
<div id="commit-log" class="flex-1">
8
+
{{ range .Commits }}
9
+
<div class="flex flex-row justify-between items-center">
10
+
<i
11
+
class="w-5 h-5 mt-5 text-gray-400 align-middle"
12
+
data-lucide="git-commit-horizontal"
13
+
></i>
14
+
<div
15
+
class="relative w-full px-4 py-4 mt-5 mx-3 hover:bg-gray-50 border border-gray-200"
16
+
>
17
+
<div id="commit-message">
18
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
19
+
<div class="text-base cursor-pointer">
20
+
<div>
21
+
<div>
22
+
<a
23
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
24
+
class="inline no-underline hover:underline"
25
+
>{{ index $messageParts 0 }}</a
26
+
>
27
+
{{ if gt (len $messageParts) 1 }}
28
+
29
+
<button
30
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
31
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
32
+
>
33
+
<i
34
+
class="w-3 h-3"
35
+
data-lucide="ellipsis"
36
+
></i>
37
+
</button>
38
+
{{ end }}
39
+
</div>
40
+
{{ if gt (len $messageParts) 1 }}
41
+
<p
42
+
class="hidden mt-1 text-sm cursor-text pb-2"
43
+
>
44
+
{{ nl2br (unwrapText (index $messageParts 1)) }}
45
+
</p>
46
+
{{ end }}
47
+
</div>
48
+
</div>
49
+
</div>
50
+
51
+
<div class="text-xs text-gray-500">
52
+
<span class="font-mono">
53
+
<a
54
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
55
+
class="text-gray-500 no-underline hover:underline"
56
+
>{{ slice .Hash.String 0 8 }}</a
57
+
>
58
+
</span>
59
+
<span
60
+
class="mx-2 before:content-['·'] before:select-none"
61
+
></span>
62
+
<span>
63
+
<a
64
+
href="mailto:{{ .Author.Email }}"
65
+
class="text-gray-500 no-underline hover:underline"
66
+
>{{ .Author.Name }}</a
67
+
>
68
+
</span>
69
+
<div
70
+
class="inline-block px-1 select-none after:content-['·']"
71
+
></div>
72
+
<span>{{ timeFmt .Author.When }}</span>
73
+
</div>
74
+
</div>
75
+
</div>
76
+
{{ end }}
14
77
</div>
15
-
<div class="commit-info">
16
-
{{ .Author.Name }} <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a>
17
-
<div>{{ .Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</div>
78
+
79
+
{{ $commits_len := len .Commits }}
80
+
<div class="flex justify-between mt-4 px-10">
81
+
{{ if gt .Page 1 }}
82
+
<a
83
+
class="btn flex items-center gap-2 no-underline"
84
+
hx-boost="true"
85
+
onclick="window.location.href = window.location.pathname + '?page={{ sub .Page 1 }}'"
86
+
>
87
+
<i data-lucide="chevron-left" class="w-4 h-4"></i>
88
+
previous
89
+
</a>
90
+
{{ else }}
91
+
<div></div>
92
+
{{ end }}
93
+
94
+
{{ if eq $commits_len 30 }}
95
+
<a
96
+
class="btn flex items-center gap-2 no-underline"
97
+
hx-boost="true"
98
+
onclick="window.location.href = window.location.pathname + '?page={{ add .Page 1 }}'"
99
+
>
100
+
next
101
+
<i data-lucide="chevron-right" class="w-4 h-4"></i>
102
+
</a>
103
+
{{ end }}
18
104
</div>
19
-
<hr />
20
-
{{ end }}
21
-
</div>
22
105
</main>
23
-
{{end}}
106
+
{{ end }}
+16
-12
appview/pages/templates/repo/settings.html
+16
-12
appview/pages/templates/repo/settings.html
···
1
+
{{ define "title" }}Settings · {{ .RepoInfo.FullName }}{{ end }}
1
2
{{ define "repoContent" }}
2
3
<header class="font-bold text-sm mb-4">COLLABORATORS</header>
3
4
4
5
<div id="collaborator-list" class="flex flex-col gap-2 mb-2">
5
-
{{ range .Collaborators }}
6
-
<div id="collaborator" class="mb-2">
7
-
<a href="/{{ didOrHandle .Did .Handle }}" class="no-underline hover:underline text-black">
8
-
{{ didOrHandle .Did .Handle }}
9
-
</a>
10
-
<div>
11
-
<span class="text-sm text-gray-500">
12
-
{{ .Role }}
13
-
</span>
14
-
</div>
15
-
</div>
16
-
{{ end }}
6
+
{{ range .Collaborators }}
7
+
<div id="collaborator" class="mb-2">
8
+
<a
9
+
href="/{{ didOrHandle .Did .Handle }}"
10
+
class="no-underline hover:underline text-black"
11
+
>
12
+
{{ didOrHandle .Did .Handle }}
13
+
</a>
14
+
<div>
15
+
<span class="text-sm text-gray-500">
16
+
{{ .Role }}
17
+
</span>
18
+
</div>
19
+
</div>
20
+
{{ end }}
17
21
</div>
18
22
19
23
{{ if .IsCollaboratorInviteAllowed }}
+1
-1
appview/pages/templates/timeline.html
+1
-1
appview/pages/templates/timeline.html
+13
-5
appview/state/repo.go
+13
-5
appview/state/repo.go
···
83
83
return
84
84
}
85
85
86
+
page := 1
87
+
if r.URL.Query().Get("page") != "" {
88
+
page, err = strconv.Atoi(r.URL.Query().Get("page"))
89
+
if err != nil {
90
+
page = 1
91
+
}
92
+
}
93
+
86
94
ref := chi.URLParam(r, "ref")
87
-
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/log/%s", f.Knot, f.OwnerDid(), f.RepoName, ref))
95
+
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/log/%s?page=%d&per_page=30", f.Knot, f.OwnerDid(), f.RepoName, ref, page))
88
96
if err != nil {
89
97
log.Println("failed to reach knotserver", err)
90
98
return
···
92
100
93
101
body, err := io.ReadAll(resp.Body)
94
102
if err != nil {
95
-
log.Fatalf("Error reading response body: %v", err)
103
+
log.Printf("error reading response body: %v", err)
96
104
return
97
105
}
98
106
99
-
var result types.RepoLogResponse
100
-
err = json.Unmarshal(body, &result)
107
+
var repolog types.RepoLogResponse
108
+
err = json.Unmarshal(body, &repolog)
101
109
if err != nil {
102
110
log.Println("failed to parse json response", err)
103
111
return
···
112
120
Name: f.RepoName,
113
121
SettingsAllowed: settingsAllowed(s, user, f),
114
122
},
115
-
RepoLogResponse: result,
123
+
RepoLogResponse: repolog,
116
124
})
117
125
return
118
126
}
+1
-1
appview/state/state.go
+1
-1
appview/state/state.go
···
793
793
r.Get("/", s.ProfilePage)
794
794
r.With(ResolveRepoKnot(s)).Route("/{repo}", func(r chi.Router) {
795
795
r.Get("/", s.RepoIndex)
796
-
r.Get("/log/{ref}", s.RepoLog)
796
+
r.Get("/commits/{ref}", s.RepoLog)
797
797
r.Route("/tree/{ref}", func(r chi.Router) {
798
798
r.Get("/", s.RepoIndex)
799
799
r.Get("/*", s.RepoTree)
+56
-51
input.css
+56
-51
input.css
···
3
3
@tailwind utilities;
4
4
@layer base {
5
5
@font-face {
6
-
font-family: 'iA Writer Quattro S';
7
-
src: url('/static/fonts/iAWriterQuattroS-Regular.ttf') format('truetype');
8
-
font-weight: normal;
9
-
font-style: normal;
10
-
font-display: swap;
11
-
font-feature-settings: "calt" 1, "kern" 1;
6
+
font-family: "iA Writer Quattro S";
7
+
src: url("/static/fonts/iAWriterQuattroS-Regular.ttf")
8
+
format("truetype");
9
+
font-weight: normal;
10
+
font-style: normal;
11
+
font-display: swap;
12
+
font-feature-settings:
13
+
"calt" 1,
14
+
"kern" 1;
12
15
}
13
16
@font-face {
14
-
font-family: 'iA Writer Quattro S';
15
-
src: url('/static/fonts/iAWriterQuattroS-Bold.ttf') format('truetype');
16
-
font-weight: bold;
17
-
font-style: normal;
18
-
font-display: swap;
17
+
font-family: "iA Writer Quattro S";
18
+
src: url("/static/fonts/iAWriterQuattroS-Bold.ttf") format("truetype");
19
+
font-weight: bold;
20
+
font-style: normal;
21
+
font-display: swap;
19
22
}
20
23
@font-face {
21
-
font-family: 'iA Writer Quattro S';
22
-
src: url('/static/fonts/iAWriterQuattroS-Italic.ttf') format('truetype');
23
-
font-weight: normal;
24
-
font-style: italic;
25
-
font-display: swap;
24
+
font-family: "iA Writer Quattro S";
25
+
src: url("/static/fonts/iAWriterQuattroS-Italic.ttf") format("truetype");
26
+
font-weight: normal;
27
+
font-style: italic;
28
+
font-display: swap;
26
29
}
27
30
@font-face {
28
-
font-family: 'iA Writer Quattro S';
29
-
src: url('/static/fonts/iAWriterQuattroS-BoldItalic.ttf') format('truetype');
30
-
font-weight: bold;
31
-
font-style: italic;
32
-
font-display: swap;
31
+
font-family: "iA Writer Quattro S";
32
+
src: url("/static/fonts/iAWriterQuattroS-BoldItalic.ttf")
33
+
format("truetype");
34
+
font-weight: bold;
35
+
font-style: italic;
36
+
font-display: swap;
33
37
}
34
38
35
39
@font-face {
36
-
font-family: 'iA Writer Mono S';
37
-
src: url('/static/fonts/iAWriterMonoS-Regular.ttf') format('truetype');
38
-
font-weight: normal;
39
-
font-style: normal;
40
-
font-display: swap;
40
+
font-family: "iA Writer Mono S";
41
+
src: url("/static/fonts/iAWriterMonoS-Regular.ttf") format("truetype");
42
+
font-weight: normal;
43
+
font-style: normal;
44
+
font-display: swap;
41
45
}
42
46
@font-face {
43
-
font-family: 'iA Writer Mono S';
44
-
src: url('/static/fonts/iAWriterMonoS-Bold.ttf') format('truetype');
45
-
font-weight: bold;
46
-
font-style: normal;
47
-
font-display: swap;
47
+
font-family: "iA Writer Mono S";
48
+
src: url("/static/fonts/iAWriterMonoS-Bold.ttf") format("truetype");
49
+
font-weight: bold;
50
+
font-style: normal;
51
+
font-display: swap;
48
52
}
49
53
@font-face {
50
-
font-family: 'iA Writer Mono S';
51
-
src: url('/static/fonts/iAWriterMonoS-Italic.ttf') format('truetype');
52
-
font-weight: normal;
53
-
font-style: italic;
54
-
font-display: swap;
54
+
font-family: "iA Writer Mono S";
55
+
src: url("/static/fonts/iAWriterMonoS-Italic.ttf") format("truetype");
56
+
font-weight: normal;
57
+
font-style: italic;
58
+
font-display: swap;
55
59
}
56
60
@font-face {
57
-
font-family: 'iA Writer Mono S';
58
-
src: url('/static/fonts/iAWriterMonoS-BoldItalic.ttf') format('truetype');
59
-
font-weight: bold;
60
-
font-style: italic;
61
-
font-display: swap;
61
+
font-family: "iA Writer Mono S";
62
+
src: url("/static/fonts/iAWriterMonoS-BoldItalic.ttf")
63
+
format("truetype");
64
+
font-weight: bold;
65
+
font-style: italic;
66
+
font-display: swap;
62
67
}
63
68
64
69
@font-face {
···
81
86
}
82
87
83
88
html {
84
-
letter-spacing: -0.01em;
85
-
word-spacing: -0.07em;
89
+
letter-spacing: -0.01em;
90
+
word-spacing: -0.07em;
86
91
}
87
92
88
93
a {
···
106
111
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center
107
112
justify-center bg-transparent px-2 pb-[0.2rem] text-base
108
113
text-gray-900 before:absolute before:inset-0 before:-z-10
109
-
before:block before:rounded-sm before:border before:border-blue-200
110
-
before:bg-white before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
111
-
before:content-[''] hover:before:border-blue-300
112
-
hover:before:bg-blue-50
113
-
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
114
+
before:block before:rounded-sm before:border before:border-gray-200
115
+
before:bg-white before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#f5f5f5]
116
+
before:content-[''] hover:before:border-gray-300
117
+
hover:before:bg-gray-50
118
+
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#f5f5f5]
114
119
focus:outline-none focus-visible:before:outline
115
-
focus-visible:before:outline-4 focus-visible:before:outline-blue-500
120
+
focus-visible:before:outline-4 focus-visible:before:outline-gray-500
116
121
active:before:shadow-[inset_0_2px_2px_0_rgba(20,20,96,0.1)];
117
122
}
118
123
}
119
124
@layer utilities {
120
125
.error {
121
-
@apply py-1 border-black text-black;
126
+
@apply py-1 text-red-400;
122
127
}
123
128
.success {
124
-
@apply py-1 border-black text-black;
129
+
@apply py-1 text-black;
125
130
}
126
131
}
127
132
}
+10
-8
knotserver/routes.go
+10
-8
knotserver/routes.go
···
51
51
}
52
52
53
53
commits, err := gr.Commits()
54
+
total := len(commits)
54
55
if err != nil {
55
56
writeError(w, err.Error(), http.StatusInternalServerError)
56
57
l.Error("fetching commits", "error", err.Error())
···
144
145
}
145
146
146
147
resp := types.RepoIndexResponse{
147
-
IsEmpty: false,
148
-
Ref: ref,
149
-
Commits: commits,
150
-
Description: getDescription(path),
151
-
Readme: readmeContent,
152
-
Files: files,
153
-
Branches: bs,
154
-
Tags: rtags,
148
+
IsEmpty: false,
149
+
Ref: ref,
150
+
Commits: commits,
151
+
Description: getDescription(path),
152
+
Readme: readmeContent,
153
+
Files: files,
154
+
Branches: bs,
155
+
Tags: rtags,
156
+
TotalCommits: total,
155
157
}
156
158
157
159
writeJSON(w, resp)
+9
-8
types/repo.go
+9
-8
types/repo.go
···
7
7
)
8
8
9
9
type RepoIndexResponse struct {
10
-
IsEmpty bool `json:"is_empty"`
11
-
Ref string `json:"ref,omitempty"`
12
-
Readme template.HTML `json:"readme,omitempty"`
13
-
Commits []*object.Commit `json:"commits,omitempty"`
14
-
Description string `json:"description,omitempty"`
15
-
Files []NiceTree `json:"files,omitempty"`
16
-
Branches []Branch `json:"branches,omitempty"`
17
-
Tags []*TagReference `json:"tags,omitempty"`
10
+
IsEmpty bool `json:"is_empty"`
11
+
Ref string `json:"ref,omitempty"`
12
+
Readme template.HTML `json:"readme,omitempty"`
13
+
Commits []*object.Commit `json:"commits,omitempty"`
14
+
Description string `json:"description,omitempty"`
15
+
Files []NiceTree `json:"files,omitempty"`
16
+
Branches []Branch `json:"branches,omitempty"`
17
+
Tags []*TagReference `json:"tags,omitempty"`
18
+
TotalCommits int `json:"total_commits,omitempty"`
18
19
}
19
20
20
21
type RepoLogResponse struct {