appview: commits page
Restyle commits in repo index and implement a separate commits page with pagination.
Anirudh Oppiliappan 1 week ago 14 files (+364, -252)
Changed files
MODIFIED
appview/pages/pages.go
MODIFIED
appview/pages/pages.go
@@ -293,9 +293,11 @@ type RepoLogParams struct {LoggedInUser *auth.UserRepoInfo RepoInfotypes.RepoLogResponse+ Active string}func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {+ params.Active = "overview"return p.execute("repo/log", w, params)}
@@ -3,46 +3,57 @@{{ 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 }}
@@ -1,6 +1,6 @@{{ 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">@@ -17,7 +17,7 @@ <details class="relative inline-block text-left"><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>
@@ -63,12 +63,12 @@{{ $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>
@@ -1,49 +1,54 @@{{ 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 }}">@@ -94,81 +99,72 @@ </div>{{ 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 }}@@ -179,7 +175,7 @@ {{ 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>@@ -187,7 +183,7 @@ </section>{{- 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
@@ -1,8 +1,8 @@-{{ define "title" }}issues | {{ .RepoInfo.FullName }}{{ end }}+{{ define "title" }}Issues · {{ .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><ahref="/{{ .RepoInfo.FullName }}/issues/new"@@ -15,7 +15,7 @@ </div><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>
@@ -1,23 +1,106 @@-{{define "title"}} log | {{ .RepoInfo.FullName }} {{end}}+{{ define "title" }}Commits · {{ .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 }}
@@ -1,19 +1,23 @@+{{ define "title" }}Settings · {{ .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,4 +1,4 @@-{{ define "title" }}timeline{{ end }}+{{ define "title" }}Timeline{{ end }}{{ define "content" }}<h1>Timeline</h1>
MODIFIED
appview/state/repo.go
MODIFIED
appview/state/repo.go
@@ -83,8 +83,16 @@ log.Println("failed to fully resolve repo", err)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@@ -92,12 +100,12 @@ }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@@ -112,7 +120,7 @@ OwnerHandle: f.OwnerHandle(),Name: f.RepoName,SettingsAllowed: settingsAllowed(s, user, f),},- RepoLogResponse: result,+ RepoLogResponse: repolog,})return}
MODIFIED
appview/state/state.go
MODIFIED
appview/state/state.go
@@ -793,7 +793,7 @@ r.With(ResolveIdent(s)).Route("/{user}", func(r chi.Router) {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)
MODIFIED
input.css
MODIFIED
input.css
@@ -3,62 +3,67 @@ @tailwind components;@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 {@@ -81,8 +86,8 @@ @apply bg-opacity-30;}html {- letter-spacing: -0.01em;- word-spacing: -0.07em;+ letter-spacing: -0.01em;+ word-spacing: -0.07em;}a {@@ -106,22 +111,22 @@ .btn {@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-centerjustify-center bg-transparent px-2 pb-[0.2rem] text-basetext-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-500active: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;}}}
MODIFIED
knotserver/routes.go
MODIFIED
knotserver/routes.go
@@ -51,6 +51,7 @@ }}commits, err := gr.Commits()+ total := len(commits)if err != nil {writeError(w, err.Error(), http.StatusInternalServerError)l.Error("fetching commits", "error", err.Error())@@ -144,14 +145,15 @@ ref = mainBranch}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)
MODIFIED
types/repo.go
MODIFIED
types/repo.go
@@ -7,14 +7,15 @@ "github.com/go-git/go-git/v5/plumbing/object")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 {