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

appview: repo/index: add ref selector dropdown

nix: builds for repoguard and keyfetch

anirudh.fi 5e314cec 32c6cef1

verified
Changed files
+307 -172
appview
pages
templates
repo
state
knotserver
types
+2
appview/pages/pages.go
···
LoggedInUser *auth.User
RepoInfo RepoInfo
Active string
+
Branches []types.Branch
+
Tags []*types.TagReference
types.RepoIndexResponse
}
+124 -67
appview/pages/templates/repo/index.html
···
-
{{define "repoContent"}}
+
{{ define "repoContent" }}
+
<main>
+
{{- if .IsEmpty }}
+
this repo is empty
+
{{ else }}
+
<div class="flex gap-4">
+
<div id="file-tree" class="w-1/2">
+
{{ $containerstyle := "py-1" }}
+
{{ $linkstyle := "no-underline hover:underline" }}
-
<main>
-
{{- if .IsEmpty }}
-
this repo is empty
-
{{ else }}
-
<div class="flex gap-4">
-
<div id="file-tree" class="w-1/2">
-
{{ $containerstyle := "py-1" }}
-
{{ $linkstyle := "no-underline hover:underline" }}
-
{{ range .Files }}
-
{{ if not .IsFile }}
-
<div class="{{ $containerstyle }}">
-
<a href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}" class="{{ $linkstyle }}">
-
<div class="flex items-center gap-2">
-
<i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }}/
-
</div>
-
</a>
-
</div>
-
{{ end }}
-
{{ end }}
+
<div class="flex justify-end">
+
<select
+
hx-get="/{{ .RepoInfo.FullName }}/tree/"
+
hx-on::config-request = "event.detail.path += this.value"
+
hx-trigger="change"
+
hx-target="#repo-content"
+
hx-select="#repo-content"
+
hx-push-url="true"
+
class="p-1 border border-gray-500 bg-white"
+
>
+
<optgroup label="branches" class="font-semibold">
+
{{ range .Branches }}
+
<option
+
value="{{ .Reference.Name }}"
+
class="py-1"
+
>
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</optgroup>
+
<optgroup label="tags" class="font-semibold">
+
{{ range .Tags }}
+
<option
+
value="{{ .Reference.Name }}"
+
class="py-1"
+
>
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</optgroup>
+
</select>
+
</div>
-
{{ range .Files }}
-
{{ if .IsFile }}
-
<div class="{{ $containerstyle }}">
-
<a href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}" class="{{ $linkstyle }}">
-
<div class="flex items-center gap-2">
-
<i class="w-3 h-3" data-lucide="file"></i>{{ .Name }}
-
</div>
-
</a>
-
</div>
-
{{ end }}
-
{{ end }}
-
</div>
-
<div id="commit-log" class="flex-1">
-
{{ range .Commits }}
-
<div class=
-
"relative
+
<section id="repo-content">
+
{{ range .Files }}
+
{{ if not .IsFile }}
+
<div class="{{ $containerstyle }}">
+
<a
+
href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}"
+
class="{{ $linkstyle }}"
+
>
+
<div class="flex items-center gap-2">
+
<i
+
class="w-3 h-3 fill-current"
+
data-lucide="folder"
+
></i
+
>{{ .Name }}/
+
</div>
+
</a>
+
</div>
+
{{ end }}
+
{{ end }}
+
+
{{ range .Files }}
+
{{ if .IsFile }}
+
<div class="{{ $containerstyle }}">
+
<a
+
href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}"
+
class="{{ $linkstyle }}"
+
>
+
<div class="flex items-center gap-2">
+
<i
+
class="w-3 h-3"
+
data-lucide="file"
+
></i
+
>{{ .Name }}
+
</div>
+
</a>
+
</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:content-['']
+
before:absolute
before:w-1
before:h-1
before:bg-black
···
</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>
-
&nbsp;·&nbsp;
-
<span>
-
<a href="mailto:{{ .Author.Email }}" class="text-gray-500 no-underline hover:underline">{{ .Author.Name }}</a>
-
</span>
-
&nbsp;·&nbsp;
-
<span>{{ timeFmt .Author.When }}</span>
-
</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>
+
&nbsp;·&nbsp;
+
<span>
+
<a
+
href="mailto:{{ .Author.Email }}"
+
class="text-gray-500 no-underline hover:underline"
+
>{{ .Author.Name }}</a
+
>
+
</span>
+
&nbsp;·&nbsp;
+
<span>{{ timeFmt .Author.When }}</span>
+
</div>
+
</div>
+
{{ end }}
+
</div>
+
</div>
+
</section>
+
{{- if .Readme }}
+
<article class="readme">
+
{{- .Readme -}}
+
</article>
+
{{- end -}}
+
{{- end -}}
-
</div>
-
{{ end }}
-
</div>
-
</div>
-
{{- if .Readme }}
-
<article class="readme">
-
{{- .Readme -}}
-
</article>
-
{{- end -}}
-
{{- end -}}
-
<div class="clone-url">
-
<strong>clone</strong>
-
<pre>
+
<div class="clone-url">
+
<strong>clone</strong>
+
<pre>
git clone https://tangled.sh/{{ .RepoInfo.OwnerWithAt }}/{{ .RepoInfo.Name }}
-
</pre>
-
</div>
-
</main>
-
{{end}}
-
+
</pre
+
>
+
</div>
+
</main>
+
{{ end }}
+50 -1
appview/state/repo.go
···
)
func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) {
+
ref := chi.URLParam(r, "ref")
f, err := fullyResolvedRepo(r)
if err != nil {
log.Println("failed to fully resolve repo", err)
return
}
+
var reqUrl string
+
if ref != "" {
+
reqUrl = fmt.Sprintf("http://%s/%s/%s/tree/%s", f.Knot, f.OwnerDid(), f.RepoName, ref)
+
} else {
+
reqUrl = fmt.Sprintf("http://%s/%s/%s", f.Knot, f.OwnerDid(), f.RepoName)
+
}
-
resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s", f.Knot, f.OwnerDid(), f.RepoName))
+
resp, err := http.Get(reqUrl)
if err != nil {
s.pages.Error503(w)
log.Println("failed to reach knotserver", err)
···
return
}
+
branchesResp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/branches", f.Knot, f.OwnerDid(), f.RepoName))
+
if err != nil {
+
log.Println("failed to reach knotserver for branches", err)
+
return
+
}
+
defer branchesResp.Body.Close()
+
+
tagsResp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/tags", f.Knot, f.OwnerDid(), f.RepoName))
+
if err != nil {
+
log.Println("failed to reach knotserver for tags", err)
+
return
+
}
+
defer tagsResp.Body.Close()
+
+
branchesBody, err := io.ReadAll(branchesResp.Body)
+
if err != nil {
+
log.Println("failed to read branches response", err)
+
return
+
}
+
+
tagsBody, err := io.ReadAll(tagsResp.Body)
+
if err != nil {
+
log.Println("failed to read tags response", err)
+
return
+
}
+
+
var branchesResult types.RepoBranchesResponse
+
err = json.Unmarshal(branchesBody, &branchesResult)
+
if err != nil {
+
log.Println("failed to parse branches response", err)
+
return
+
}
+
+
var tagsResult types.RepoTagsResponse
+
err = json.Unmarshal(tagsBody, &tagsResult)
+
if err != nil {
+
log.Println("failed to parse tags response", err)
+
return
+
}
+
log.Println(resp.Status, result)
user := s.auth.GetUser(r)
···
SettingsAllowed: settingsAllowed(s, user, f),
},
RepoIndexResponse: result,
+
Branches: branchesResult.Branches,
+
Tags: tagsResult.Tags,
})
return
+1
appview/state/state.go
···
r.Get("/", s.RepoIndex)
r.Get("/log/{ref}", s.RepoLog)
r.Route("/tree/{ref}", func(r chi.Router) {
+
r.Get("/", s.RepoIndex)
r.Get("/*", s.RepoTree)
})
r.Get("/commit/{ref}", s.RepoCommit)
+122 -97
flake.nix
···
flake = false;
};
lucide-src = {
-
url = "https://unpkg.com/lucide@latest";
-
flake = false;
+
url = "https://unpkg.com/lucide@latest";
+
flake = false;
};
gitignore = {
url = "github:hercules-ci/gitignore.nix";
···
};
};
-
outputs = {
-
self,
-
nixpkgs,
-
indigo,
-
htmx-src,
-
lucide-src,
-
gitignore,
-
}: let
-
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
-
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
-
nixpkgsFor = forAllSystems (system:
-
import nixpkgs {
-
inherit system;
-
overlays = [self.overlays.default];
-
});
-
inherit (gitignore.lib) gitignoreSource;
-
in {
-
overlays.default = final: prev: {
-
indigo-lexgen = with final;
-
final.buildGoModule {
-
pname = "indigo-lexgen";
-
version = "0.1.0";
-
src = indigo;
-
subPackages = ["cmd/lexgen"];
-
vendorHash = "sha256-pGc29fgJFq8LP7n/pY1cv6ExZl88PAeFqIbFEhB3xXs=";
-
doCheck = false;
-
};
+
outputs =
+
{ self
+
, nixpkgs
+
, indigo
+
, htmx-src
+
, lucide-src
+
, gitignore
+
,
+
}:
+
let
+
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
+
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
+
nixpkgsFor = forAllSystems (system:
+
import nixpkgs {
+
inherit system;
+
overlays = [ self.overlays.default ];
+
});
+
inherit (gitignore.lib) gitignoreSource;
+
in
+
{
+
overlays.default = final: prev: {
+
indigo-lexgen = with final;
+
final.buildGoModule {
+
pname = "indigo-lexgen";
+
version = "0.1.0";
+
src = indigo;
+
subPackages = [ "cmd/lexgen" ];
+
vendorHash = "sha256-pGc29fgJFq8LP7n/pY1cv6ExZl88PAeFqIbFEhB3xXs=";
+
doCheck = false;
+
};
-
appview = with final;
-
final.pkgsStatic.buildGoModule {
-
pname = "appview";
-
version = "0.1.0";
-
src = gitignoreSource ./.;
-
postConfigureHook = ''
-
cp -f ${htmx-src} appview/pages/static/htmx.min.js
-
cp -f ${lucide-src} appview/pages/static/lucide.min.js
-
${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o appview/pages/static/tw.css
-
'';
-
subPackages = ["cmd/appview"];
-
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
-
env.CGO_ENABLED = 1;
-
stdenv = pkgsStatic.stdenv;
-
};
-
knotserver = with final;
+
appview = with final;
+
final.pkgsStatic.buildGoModule {
+
pname = "appview";
+
version = "0.1.0";
+
src = gitignoreSource ./.;
+
postConfigureHook = ''
+
cp -f ${htmx-src} appview/pages/static/htmx.min.js
+
cp -f ${lucide-src} appview/pages/static/lucide.min.js
+
${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o appview/pages/static/tw.css
+
'';
+
subPackages = [ "cmd/appview" ];
+
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
+
env.CGO_ENABLED = 1;
+
stdenv = pkgsStatic.stdenv;
+
};
+
knotserver = with final;
+
final.pkgsStatic.buildGoModule {
+
pname = "knotserver";
+
version = "0.1.0";
+
src = gitignoreSource ./.;
+
subPackages = [ "cmd/knotserver" ];
+
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
+
env.CGO_ENABLED = 1;
+
};
+
repoguard = with final;
+
final.pkgsStatic.buildGoModule {
+
pname = "repoguard";
+
version = "0.1.0";
+
src = gitignoreSource ./.;
+
subPackages = [ "cmd/repoguard" ];
+
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
+
env.CGO_ENABLED = 0;
+
};
+
keyfetch = with final;
final.pkgsStatic.buildGoModule {
-
pname = "knotserver";
-
version = "0.1.0";
-
src = gitignoreSource ./.;
-
subPackages = ["cmd/knotserver"];
-
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
-
env.CGO_ENABLED = 1;
-
};
+
pname = "keyfetch";
+
version = "0.1.0";
+
src = gitignoreSource ./.;
+
subPackages = [ "cmd/keyfetch" ];
+
vendorHash = "sha256-t7lWrCyFWCI7zjUcC6XNWzCrUPSCFnFu9gTmRTsYrz0=";
+
env.CGO_ENABLED = 0;
+
};
+
};
+
packages = forAllSystems (system: {
+
inherit (nixpkgsFor."${system}") indigo-lexgen appview knotserver repoguard keyfetch;
+
});
+
defaultPackage = forAllSystems (system: nixpkgsFor.${system}.appview);
+
formatter = forAllSystems (system: nixpkgsFor."${system}".alejandra);
+
devShells = forAllSystems (system:
+
let
+
pkgs = nixpkgsFor.${system};
+
staticShell = pkgs.mkShell.override {
+
stdenv = pkgs.pkgsStatic.stdenv;
+
};
+
in
+
{
+
default = staticShell {
+
nativeBuildInputs = [
+
pkgs.go
+
pkgs.air
+
pkgs.gopls
+
pkgs.httpie
+
pkgs.indigo-lexgen
+
pkgs.litecli
+
pkgs.websocat
+
pkgs.tailwindcss
+
];
+
};
+
});
+
apps = forAllSystems (system:
+
let
+
pkgs = nixpkgsFor."${system}";
+
air-watcher = name:
+
pkgs.writeShellScriptBin "run"
+
''
+
${pkgs.air}/bin/air -c /dev/null \
+
-build.cmd "${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o ./appview/pages/static/tw.css && ${pkgs.go}/bin/go build -o ./out/${name}.out ./cmd/${name}/main.go" \
+
-build.bin "./out/${name}.out" \
+
-build.include_ext "go,html,css"
+
'';
+
in
+
{
+
watch-appview = {
+
type = "app";
+
program = ''${air-watcher "appview"}/bin/run'';
+
};
+
watch-knotserver = {
+
type = "app";
+
program = ''${air-watcher "knotserver"}/bin/run'';
+
};
+
});
};
-
packages = forAllSystems (system: {
-
inherit (nixpkgsFor."${system}") indigo-lexgen appview knotserver;
-
});
-
defaultPackage = forAllSystems (system: nixpkgsFor.${system}.appview);
-
formatter = forAllSystems (system: nixpkgsFor."${system}".alejandra);
-
devShells = forAllSystems (system: let
-
pkgs = nixpkgsFor.${system};
-
staticShell = pkgs.mkShell.override {
-
stdenv = pkgs.pkgsStatic.stdenv;
-
};
-
in {
-
default = staticShell {
-
nativeBuildInputs = [
-
pkgs.go
-
pkgs.air
-
pkgs.gopls
-
pkgs.httpie
-
pkgs.indigo-lexgen
-
pkgs.litecli
-
pkgs.websocat
-
pkgs.tailwindcss
-
];
-
};
-
});
-
apps = forAllSystems (system: let
-
pkgs = nixpkgsFor."${system}";
-
air-watcher = name:
-
pkgs.writeShellScriptBin "run"
-
''
-
${pkgs.air}/bin/air -c /dev/null \
-
-build.cmd "${pkgs.tailwindcss}/bin/tailwindcss -i input.css -o ./appview/pages/static/tw.css && ${pkgs.go}/bin/go build -o ./out/${name}.out ./cmd/${name}/main.go" \
-
-build.bin "./out/${name}.out" \
-
-build.include_ext "go,html,css"
-
'';
-
in {
-
watch-appview = {
-
type = "app";
-
program = ''${air-watcher "appview"}/bin/run'';
-
};
-
watch-knotserver = {
-
type = "app";
-
program = ''${air-watcher "knotserver"}/bin/run'';
-
};
-
});
-
};
}
+5 -4
knotserver/routes.go
···
rtags := []*types.TagReference{}
for _, tag := range tags {
tr := types.TagReference{
-
Ref: types.Reference{
-
Name: tag.Name(),
-
Hash: tag.Hash().String(),
-
},
Tag: tag.TagObject(),
+
}
+
+
tr.Reference = types.Reference{
+
Name: tag.Name(),
+
Hash: tag.Hash().String(),
}
if tag.Message() != "" {
+3 -3
types/repo.go
···
}
type TagReference struct {
-
Ref Reference `json:"ref,omitempty"`
-
Tag *object.Tag `json:"tag,omitempty"`
-
Message string `json:"message,omitempty"`
+
Reference `json:"ref,omitempty"`
+
Tag *object.Tag `json:"tag,omitempty"`
+
Message string `json:"message,omitempty"`
}
type Reference struct {