forked from tangled.org/core
this repo has no description

appview: commits page

Restyle commits in repo index and implement a separate commits page with pagination.

Changed files
+364 -252
appview
knotserver
types
+2
appview/pages/pages.go
···
LoggedInUser *auth.User
RepoInfo RepoInfo
types.RepoLogResponse
+
Active string
}
func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {
+
params.Active = "overview"
return p.execute("repo/log", w, params)
}
+46 -35
appview/pages/templates/layouts/repobase.html
···
{{ define "content" }}
<section id="repo-header" class="mb-4">
<p class="text-xl">
-
<a href="/{{ .RepoInfo.OwnerWithAt }}" class="no-underline hover:underline">{{ .RepoInfo.OwnerWithAt }}</a>
-
/
-
<a href="/{{ .RepoInfo.FullName }}" class="no-underline hover:underline">{{ .RepoInfo.Name }}</a>
+
<a
+
href="/{{ .RepoInfo.OwnerWithAt }}"
+
class="no-underline hover:underline"
+
>{{ .RepoInfo.OwnerWithAt }}</a
+
>
+
/
+
<a
+
href="/{{ .RepoInfo.FullName }}"
+
class="no-underline hover:underline"
+
>{{ .RepoInfo.Name }}</a
+
>
</p>
<span>
-
{{ if .RepoInfo.Description }}
-
{{ .RepoInfo.Description }}
-
{{ else }}
-
<span class="italic">this repo has no description</span>
-
{{ end }}
+
{{ if .RepoInfo.Description }}
+
{{ .RepoInfo.Description }}
+
{{ else }}
+
<span class="italic">this repo has no description</span>
+
{{ end }}
</span>
</section>
<section id="repo-links" class="min-h-screen flex flex-col">
-
<nav class="w-full mx-auto">
-
<div class="flex z-60 border-black border-b">
-
{{ $activeTabStyles := "border-black border-l border-r border-t border-b-0 -mb-px bg-white" }}
-
{{ $tabs := .RepoInfo.GetTabs }}
-
{{ range $item := $tabs }}
-
{{ $key := index $item 0 }}
-
{{ $value := index $item 1 }}
-
<a
-
href="/{{ $.RepoInfo.FullName }}{{ $value }}"
-
class="relative -mr-px group no-underline"
-
hx-boost="true"
-
>
-
<div
-
class="px-4 py-2 mr-1 text-black min-w-[80px] text-center relative group-hover:bg-gray-200
-
{{ if eq $.Active $key }}{{ $activeTabStyles }}{{ end }}"
-
>
-
{{ $key }}
-
</div>
-
</a>
-
{{ end}}
-
</div>
-
</nav>
-
<section
-
class="bg-white p-6 min-h-[200px] border-l border-r border-b border-black relative z-20 w-full mx-auto">
-
{{ block "repoContent" . }}{{ end }}
-
</section>
-
{{ block "repoAfter" . }} {{ end }}
+
<nav class="w-full mx-auto">
+
<div class="flex z-60 border-gray-200 border-b">
+
{{ $activeTabStyles := "border-gray-200 border-l border-r border-t border-b-0 -mb-px bg-white" }}
+
{{ $tabs := .RepoInfo.GetTabs }}
+
{{ range $item := $tabs }}
+
{{ $key := index $item 0 }}
+
{{ $value := index $item 1 }}
+
<a
+
href="/{{ $.RepoInfo.FullName }}{{ $value }}"
+
class="relative -mr-px group no-underline"
+
hx-boost="true"
+
>
+
<div
+
class="px-4 py-2 mr-1 text-black min-w-[80px] text-center relative group-hover:bg-gray-200
+
{{ if eq $.Active $key }}
+
{{ $activeTabStyles }}
+
{{ end }}"
+
>
+
{{ $key }}
+
</div>
+
</a>
+
{{ end }}
+
</div>
+
</nav>
+
<section
+
class="bg-white p-6 min-h-[200px] border-l border-r border-b border-gray-200 relative z-20 w-full mx-auto"
+
>
+
{{ block "repoContent" . }}{{ end }}
+
</section>
+
{{ block "repoAfter" . }}{{ end }}
</section>
{{ end }}
+2 -2
appview/pages/templates/layouts/topbar.html
···
{{ define "layouts/topbar" }}
{{ $linkstyle := "text-gray-400 hover:text-gray-900 no-underline" }}
-
<nav class="space-x-4 mb-4 py-2 border-b border-black">
+
<nav class="space-x-4 mb-4 py-2 border-b border-gray-200">
<div class="container flex justify-between p-0">
<div id="left-items">
<a href="/" hx-boost="true" class="{{ $linkstyle }} flex gap-2">
···
<summary class="{{ $linkstyle }} cursor-pointer list-none">
{{ didOrHandle .Did .Handle }}
</summary>
-
<div class="absolute flex flex-col right-0 mt-4 p-2 w-48 bg-white border border-black z-50">
+
<div class="absolute flex flex-col right-0 mt-4 p-2 w-48 bg-white border border-gray-200 z-50">
<a href="/{{ didOrHandle .Did .Handle }}"class="{{ $linkstyle }}">profile</a>
<a href="/knots"class="{{ $linkstyle }}">knots</a>
<a href="/settings"class="{{ $linkstyle }}">settings</a>
+2 -2
appview/pages/templates/repo/commit.html
···
{{ $last := sub (len $diff) 1 }}
{{ range $idx, $hunk := $diff }}
{{ with $hunk }}
-
<section class="mt-4 border border-black w-full mx-auto">
+
<section class="mt-4 border border-gray-200 w-full mx-auto">
<div id="file-{{ .Name.New }}">
<div id="diff-file">
<details open>
<summary class="list-none cursor-pointer sticky top-0">
-
<div id="diff-file-header" class="border-b cursor-pointer bg-white border-black flex justify-between">
+
<div id="diff-file-header" class="border-b cursor-pointer bg-white border-gray-200 flex justify-between">
<div id="left-side-items" class="p-2">
{{ if .IsNew }}
<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
···
{{ define "repoContent" }}
<main>
+
<div class="flex justify-between pb-5">
+
<select
+
onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + this.value"
+
class="p-1 border border-gray-200 bg-white"
+
>
+
<optgroup label="branches" class="bold text-sm">
+
{{ range .Branches }}
+
<option
+
value="{{ .Reference.Name }}"
+
class="py-1"
+
{{ if eq .Reference.Name $.Ref }}
+
selected
+
{{ end }}
+
>
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</optgroup>
+
<optgroup label="tags" class="bold text-sm">
+
{{ range .Tags }}
+
<option
+
value="{{ .Reference.Name }}"
+
class="py-1"
+
{{ if eq .Reference.Name $.Ref }}
+
selected
+
{{ end }}
+
>
+
{{ .Reference.Name }}
+
</option>
+
{{ else }}
+
<option class="py-1" disabled>no tags found</option>
+
{{ end }}
+
</optgroup>
+
</select>
+
<a
+
href="/{{ .RepoInfo.FullName }}/commits/{{ .Ref }}"
+
class="btn ml-2 no-underline flex items-center gap-2"
+
>
+
<i class="w-4 h-4" data-lucide="logs"></i>
+
{{ .TotalCommits }}
+
{{ if eq .TotalCommits 1 }}commit{{ else }}commits{{ end }}
+
</a>
+
</div>
+
<div class="flex gap-4">
-
<div id="file-tree" class="w-3/5">
+
<div id="file-tree" class="w-3/5 pr-2 border-r border-gray-200">
{{ $containerstyle := "py-1" }}
{{ $linkstyle := "no-underline hover:underline" }}
-
-
<div class="flex justify-end">
-
<select
-
onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + this.value"
-
class="p-1 border border-gray-500 bg-white"
-
>
-
<optgroup label="branches" class="bold text-sm">
-
{{ range .Branches }}
-
<option
-
value="{{ .Reference.Name }}"
-
class="py-1"
-
{{ if eq .Reference.Name $.Ref }}
-
selected
-
{{ end }}
-
>
-
{{ .Reference.Name }}
-
</option>
-
{{ end }}
-
</optgroup>
-
<optgroup label="tags" class="bold text-sm">
-
{{ range .Tags }}
-
<option
-
value="{{ .Reference.Name }}"
-
class="py-1"
-
{{ if eq .Reference.Name $.Ref }}
-
selected
-
{{ end }}
-
>
-
{{ .Reference.Name }}
-
</option>
-
{{ else }}
-
<option class="py-1" disabled>
-
no tags found
-
</option>
-
{{ end }}
-
</optgroup>
-
</select>
-
</div>
-
{{ range .Files }}
{{ if not .IsFile }}
<div class="{{ $containerstyle }}">
···
{{ end }}
{{ end }}
</div>
+
<div id="commit-log" class="flex-1">
{{ range .Commits }}
-
<div
-
class="relative
-
px-4
-
py-4
-
border-l
-
border-black
-
before:content-['']
-
before:absolute
-
before:w-1
-
before:h-1
-
before:bg-black
-
before:rounded-full
-
before:left-[-2.2px]
-
before:top-1/2
-
before:-translate-y-1/2
-
"
-
>
-
<div id="commit-message">
-
{{ $messageParts := splitN .Message "\n\n" 2 }}
-
<div class="text-base cursor-pointer">
-
<div>
+
<div class="flex flex-row items-center">
+
<i
+
class="w-5 h-5 text-gray-400 align-top"
+
data-lucide="git-commit-horizontal"
+
></i>
+
<div class="relative px-4 py-4">
+
<div id="commit-message">
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
+
<div class="text-base cursor-pointer">
<div>
-
<a
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
-
class="inline no-underline hover:underline"
-
>{{ index $messageParts 0 }}</a
-
>
+
<div>
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="inline no-underline hover:underline"
+
>{{ index $messageParts 0 }}</a
+
>
+
{{ if gt (len $messageParts) 1 }}
+
+
<button
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
+
>
+
<i
+
class="w-3 h-3"
+
data-lucide="ellipsis"
+
></i>
+
</button>
+
{{ end }}
+
</div>
{{ if gt (len $messageParts) 1 }}
-
-
<button
-
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
-
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
+
<p
+
class="hidden mt-1 text-sm cursor-text pb-2"
>
-
<i
-
class="w-3 h-3"
-
data-lucide="ellipsis"
-
></i>
-
</button>
+
{{ nl2br (unwrapText (index $messageParts 1)) }}
+
</p>
{{ end }}
</div>
-
{{ if gt (len $messageParts) 1 }}
-
<p
-
class="hidden mt-1 text-sm cursor-text pb-2"
-
>
-
{{ nl2br (unwrapText (index $messageParts 1)) }}
-
</p>
-
{{ end }}
</div>
</div>
-
</div>
-
<div class="text-xs text-gray-500">
-
<span class="font-mono">
-
<a
-
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
-
class="text-gray-500 no-underline hover:underline"
-
>{{ slice .Hash.String 0 8 }}</a
-
>
-
</span>
-
<span
-
class="mx-2 before:content-['·'] before:select-none"
-
></span>
-
<span>
-
<a
-
href="mailto:{{ .Author.Email }}"
-
class="text-gray-500 no-underline hover:underline"
-
>{{ .Author.Name }}</a
-
>
-
</span>
-
<div
-
class="inline-block px-1 select-none after:content-['·']"
-
></div>
-
<span>{{ timeFmt .Author.When }}</span>
+
<div class="text-xs text-gray-500">
+
<span class="font-mono">
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ slice .Hash.String 0 8 }}</a
+
>
+
</span>
+
<span
+
class="mx-2 before:content-['·'] before:select-none"
+
></span>
+
<span>
+
<a
+
href="mailto:{{ .Author.Email }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ .Author.Name }}</a
+
>
+
</span>
+
<div
+
class="inline-block px-1 select-none after:content-['·']"
+
></div>
+
<span>{{ timeFmt .Author.When }}</span>
+
</div>
</div>
</div>
{{ end }}
···
{{ define "repoAfter" }}
{{- if .Readme }}
-
<section class="mt-4 p-6 border border-black w-full mx-auto">
+
<section class="mt-4 p-6 border border-gray-200 w-full mx-auto">
<article class="readme">
{{- .Readme -}}
</article>
···
{{- end -}}
-
<section class="mt-4 p-6 border border-black w-full mx-auto">
+
<section class="mt-4 p-6 border border-gray-200 w-full mx-auto">
<strong>clone</strong>
<pre>
git clone https://tangled.sh/{{ .RepoInfo.OwnerWithAt }}/{{ .RepoInfo.Name }} </pre
+3 -3
appview/pages/templates/repo/issues/issues.html
···
-
{{ define "title" }}issues | {{ .RepoInfo.FullName }}{{ end }}
+
{{ define "title" }}Issues &middot; {{ .RepoInfo.FullName }}{{ end }}
{{ define "repoContent" }}
<div class="flex justify-between items-center">
-
<h1 class="m-0">issues</h1>
+
<h1 class="m-0">Issues</h1>
<div class="error" id="issues"></div>
<a
href="/{{ .RepoInfo.FullName }}/issues/new"
···
<section id="issues" class="mt-8 space-y-4">
{{ range .Issues }}
-
<div class="border border-gray-200 p-4">
+
<div class="border border-gray-200 p-4 mx-4 hover:bg-gray-50">
<time class="float-right text-sm">
{{ .Created | timeFmt }}
</time>
+100 -17
appview/pages/templates/repo/log.html
···
-
{{define "title"}} log | {{ .RepoInfo.FullName }} {{end}}
+
{{ define "title" }}Commits &middot; {{ .RepoInfo.FullName }}{{ end }}
-
{{define "content"}}
+
{{ define "repoContent" }}
-
<h1>
-
log | {{ .RepoInfo.FullName }}
-
</h1>
+
<h1>Commits</h1>
<main>
-
<div class="log">
-
{{ range .Commits }}
-
<div>
-
<div><a href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}" class="commit-hash">{{ slice .Hash.String 0 8 }}</a></div>
-
<pre>{{ .Message }}</pre>
+
<div id="commit-log" class="flex-1">
+
{{ range .Commits }}
+
<div class="flex flex-row justify-between items-center">
+
<i
+
class="w-5 h-5 mt-5 text-gray-400 align-middle"
+
data-lucide="git-commit-horizontal"
+
></i>
+
<div
+
class="relative w-full px-4 py-4 mt-5 mx-3 hover:bg-gray-50 border border-gray-200"
+
>
+
<div id="commit-message">
+
{{ $messageParts := splitN .Message "\n\n" 2 }}
+
<div class="text-base cursor-pointer">
+
<div>
+
<div>
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="inline no-underline hover:underline"
+
>{{ index $messageParts 0 }}</a
+
>
+
{{ if gt (len $messageParts) 1 }}
+
+
<button
+
class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded"
+
hx-on:click="this.parentElement.nextElementSibling.classList.toggle('hidden')"
+
>
+
<i
+
class="w-3 h-3"
+
data-lucide="ellipsis"
+
></i>
+
</button>
+
{{ end }}
+
</div>
+
{{ if gt (len $messageParts) 1 }}
+
<p
+
class="hidden mt-1 text-sm cursor-text pb-2"
+
>
+
{{ nl2br (unwrapText (index $messageParts 1)) }}
+
</p>
+
{{ end }}
+
</div>
+
</div>
+
</div>
+
+
<div class="text-xs text-gray-500">
+
<span class="font-mono">
+
<a
+
href="/{{ $.RepoInfo.FullName }}/commit/{{ .Hash.String }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ slice .Hash.String 0 8 }}</a
+
>
+
</span>
+
<span
+
class="mx-2 before:content-['·'] before:select-none"
+
></span>
+
<span>
+
<a
+
href="mailto:{{ .Author.Email }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ .Author.Name }}</a
+
>
+
</span>
+
<div
+
class="inline-block px-1 select-none after:content-['·']"
+
></div>
+
<span>{{ timeFmt .Author.When }}</span>
+
</div>
+
</div>
+
</div>
+
{{ end }}
</div>
-
<div class="commit-info">
-
{{ .Author.Name }} <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a>
-
<div>{{ .Author.When.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</div>
+
+
{{ $commits_len := len .Commits }}
+
<div class="flex justify-between mt-4 px-10">
+
{{ if gt .Page 1 }}
+
<a
+
class="btn flex items-center gap-2 no-underline"
+
hx-boost="true"
+
onclick="window.location.href = window.location.pathname + '?page={{ sub .Page 1 }}'"
+
>
+
<i data-lucide="chevron-left" class="w-4 h-4"></i>
+
previous
+
</a>
+
{{ else }}
+
<div></div>
+
{{ end }}
+
+
{{ if eq $commits_len 30 }}
+
<a
+
class="btn flex items-center gap-2 no-underline"
+
hx-boost="true"
+
onclick="window.location.href = window.location.pathname + '?page={{ add .Page 1 }}'"
+
>
+
next
+
<i data-lucide="chevron-right" class="w-4 h-4"></i>
+
</a>
+
{{ end }}
</div>
-
<hr />
-
{{ end }}
-
</div>
</main>
-
{{end}}
+
{{ end }}
+16 -12
appview/pages/templates/repo/settings.html
···
+
{{ define "title" }}Settings &middot; {{ .RepoInfo.FullName }}{{ end }}
{{ define "repoContent" }}
<header class="font-bold text-sm mb-4">COLLABORATORS</header>
<div id="collaborator-list" class="flex flex-col gap-2 mb-2">
-
{{ range .Collaborators }}
-
<div id="collaborator" class="mb-2">
-
<a href="/{{ didOrHandle .Did .Handle }}" class="no-underline hover:underline text-black">
-
{{ didOrHandle .Did .Handle }}
-
</a>
-
<div>
-
<span class="text-sm text-gray-500">
-
{{ .Role }}
-
</span>
-
</div>
-
</div>
-
{{ end }}
+
{{ range .Collaborators }}
+
<div id="collaborator" class="mb-2">
+
<a
+
href="/{{ didOrHandle .Did .Handle }}"
+
class="no-underline hover:underline text-black"
+
>
+
{{ didOrHandle .Did .Handle }}
+
</a>
+
<div>
+
<span class="text-sm text-gray-500">
+
{{ .Role }}
+
</span>
+
</div>
+
</div>
+
{{ end }}
</div>
{{ if .IsCollaboratorInviteAllowed }}
+1 -1
appview/pages/templates/timeline.html
···
-
{{ define "title" }}timeline{{ end }}
+
{{ define "title" }}Timeline{{ end }}
{{ define "content" }}
<h1>Timeline</h1>
+13 -5
appview/state/repo.go
···
return
}
+
page := 1
+
if r.URL.Query().Get("page") != "" {
+
page, err = strconv.Atoi(r.URL.Query().Get("page"))
+
if err != nil {
+
page = 1
+
}
+
}
+
ref := chi.URLParam(r, "ref")
-
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/log/%s", f.Knot, f.OwnerDid(), f.RepoName, ref))
+
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))
if err != nil {
log.Println("failed to reach knotserver", err)
return
···
body, err := io.ReadAll(resp.Body)
if err != nil {
-
log.Fatalf("Error reading response body: %v", err)
+
log.Printf("error reading response body: %v", err)
return
}
-
var result types.RepoLogResponse
-
err = json.Unmarshal(body, &result)
+
var repolog types.RepoLogResponse
+
err = json.Unmarshal(body, &repolog)
if err != nil {
log.Println("failed to parse json response", err)
return
···
Name: f.RepoName,
SettingsAllowed: settingsAllowed(s, user, f),
},
-
RepoLogResponse: result,
+
RepoLogResponse: repolog,
})
return
}
+1 -1
appview/state/state.go
···
r.Get("/", s.ProfilePage)
r.With(ResolveRepoKnot(s)).Route("/{repo}", func(r chi.Router) {
r.Get("/", s.RepoIndex)
-
r.Get("/log/{ref}", s.RepoLog)
+
r.Get("/commits/{ref}", s.RepoLog)
r.Route("/tree/{ref}", func(r chi.Router) {
r.Get("/", s.RepoIndex)
r.Get("/*", s.RepoTree)
+56 -51
input.css
···
@tailwind utilities;
@layer base {
@font-face {
-
font-family: 'iA Writer Quattro S';
-
src: url('/static/fonts/iAWriterQuattroS-Regular.ttf') format('truetype');
-
font-weight: normal;
-
font-style: normal;
-
font-display: swap;
-
font-feature-settings: "calt" 1, "kern" 1;
+
font-family: "iA Writer Quattro S";
+
src: url("/static/fonts/iAWriterQuattroS-Regular.ttf")
+
format("truetype");
+
font-weight: normal;
+
font-style: normal;
+
font-display: swap;
+
font-feature-settings:
+
"calt" 1,
+
"kern" 1;
}
@font-face {
-
font-family: 'iA Writer Quattro S';
-
src: url('/static/fonts/iAWriterQuattroS-Bold.ttf') format('truetype');
-
font-weight: bold;
-
font-style: normal;
-
font-display: swap;
+
font-family: "iA Writer Quattro S";
+
src: url("/static/fonts/iAWriterQuattroS-Bold.ttf") format("truetype");
+
font-weight: bold;
+
font-style: normal;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Quattro S';
-
src: url('/static/fonts/iAWriterQuattroS-Italic.ttf') format('truetype');
-
font-weight: normal;
-
font-style: italic;
-
font-display: swap;
+
font-family: "iA Writer Quattro S";
+
src: url("/static/fonts/iAWriterQuattroS-Italic.ttf") format("truetype");
+
font-weight: normal;
+
font-style: italic;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Quattro S';
-
src: url('/static/fonts/iAWriterQuattroS-BoldItalic.ttf') format('truetype');
-
font-weight: bold;
-
font-style: italic;
-
font-display: swap;
+
font-family: "iA Writer Quattro S";
+
src: url("/static/fonts/iAWriterQuattroS-BoldItalic.ttf")
+
format("truetype");
+
font-weight: bold;
+
font-style: italic;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Mono S';
-
src: url('/static/fonts/iAWriterMonoS-Regular.ttf') format('truetype');
-
font-weight: normal;
-
font-style: normal;
-
font-display: swap;
+
font-family: "iA Writer Mono S";
+
src: url("/static/fonts/iAWriterMonoS-Regular.ttf") format("truetype");
+
font-weight: normal;
+
font-style: normal;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Mono S';
-
src: url('/static/fonts/iAWriterMonoS-Bold.ttf') format('truetype');
-
font-weight: bold;
-
font-style: normal;
-
font-display: swap;
+
font-family: "iA Writer Mono S";
+
src: url("/static/fonts/iAWriterMonoS-Bold.ttf") format("truetype");
+
font-weight: bold;
+
font-style: normal;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Mono S';
-
src: url('/static/fonts/iAWriterMonoS-Italic.ttf') format('truetype');
-
font-weight: normal;
-
font-style: italic;
-
font-display: swap;
+
font-family: "iA Writer Mono S";
+
src: url("/static/fonts/iAWriterMonoS-Italic.ttf") format("truetype");
+
font-weight: normal;
+
font-style: italic;
+
font-display: swap;
}
@font-face {
-
font-family: 'iA Writer Mono S';
-
src: url('/static/fonts/iAWriterMonoS-BoldItalic.ttf') format('truetype');
-
font-weight: bold;
-
font-style: italic;
-
font-display: swap;
+
font-family: "iA Writer Mono S";
+
src: url("/static/fonts/iAWriterMonoS-BoldItalic.ttf")
+
format("truetype");
+
font-weight: bold;
+
font-style: italic;
+
font-display: swap;
}
@font-face {
···
}
html {
-
letter-spacing: -0.01em;
-
word-spacing: -0.07em;
+
letter-spacing: -0.01em;
+
word-spacing: -0.07em;
}
a {
···
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center
justify-center bg-transparent px-2 pb-[0.2rem] text-base
text-gray-900 before:absolute before:inset-0 before:-z-10
-
before:block before:rounded-sm before:border before:border-blue-200
-
before:bg-white before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
-
before:content-[''] hover:before:border-blue-300
-
hover:before:bg-blue-50
-
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
+
before:block before:rounded-sm before:border before:border-gray-200
+
before:bg-white before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#f5f5f5]
+
before:content-[''] hover:before:border-gray-300
+
hover:before:bg-gray-50
+
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#f5f5f5]
focus:outline-none focus-visible:before:outline
-
focus-visible:before:outline-4 focus-visible:before:outline-blue-500
+
focus-visible:before:outline-4 focus-visible:before:outline-gray-500
active:before:shadow-[inset_0_2px_2px_0_rgba(20,20,96,0.1)];
}
}
@layer utilities {
.error {
-
@apply py-1 border-black text-black;
+
@apply py-1 text-red-400;
}
.success {
-
@apply py-1 border-black text-black;
+
@apply py-1 text-black;
}
}
}
+10 -8
knotserver/routes.go
···
}
commits, err := gr.Commits()
+
total := len(commits)
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
l.Error("fetching commits", "error", err.Error())
···
}
resp := types.RepoIndexResponse{
-
IsEmpty: false,
-
Ref: ref,
-
Commits: commits,
-
Description: getDescription(path),
-
Readme: readmeContent,
-
Files: files,
-
Branches: bs,
-
Tags: rtags,
+
IsEmpty: false,
+
Ref: ref,
+
Commits: commits,
+
Description: getDescription(path),
+
Readme: readmeContent,
+
Files: files,
+
Branches: bs,
+
Tags: rtags,
+
TotalCommits: total,
}
writeJSON(w, resp)
+9 -8
types/repo.go
···
)
type RepoIndexResponse struct {
-
IsEmpty bool `json:"is_empty"`
-
Ref string `json:"ref,omitempty"`
-
Readme template.HTML `json:"readme,omitempty"`
-
Commits []*object.Commit `json:"commits,omitempty"`
-
Description string `json:"description,omitempty"`
-
Files []NiceTree `json:"files,omitempty"`
-
Branches []Branch `json:"branches,omitempty"`
-
Tags []*TagReference `json:"tags,omitempty"`
+
IsEmpty bool `json:"is_empty"`
+
Ref string `json:"ref,omitempty"`
+
Readme template.HTML `json:"readme,omitempty"`
+
Commits []*object.Commit `json:"commits,omitempty"`
+
Description string `json:"description,omitempty"`
+
Files []NiceTree `json:"files,omitempty"`
+
Branches []Branch `json:"branches,omitempty"`
+
Tags []*TagReference `json:"tags,omitempty"`
+
TotalCommits int `json:"total_commits,omitempty"`
}
type RepoLogResponse struct {