Mirror of https://git.jolheiser.com/ugit

Compare changes

Choose any two refs to compare.

+24
.air.toml
··· 1 + root = "." 2 + testdata_dir = "testdata" 3 + tmp_dir = "tmp" 4 + 5 + [build] 6 + bin = "./ugitd" 7 + cmd = "go build ./cmd/ugitd" 8 + delay = 1000 9 + exclude_file = ["internal/html/tailwind.go"] 10 + exclude_regex = ["_test.go"] 11 + exclude_unchanged = true 12 + include_ext = ["go"] 13 + pre_cmd = ["go generate ./..."] 14 + 15 + [misc] 16 + clean_on_exit = true 17 + 18 + [proxy] 19 + app_port = 8449 20 + enabled = true 21 + proxy_port = 8450 22 + 23 + [screen] 24 + clear_on_rebuild = true
+2
.gitignore
··· 1 1 /ugit* 2 2 .ssh/ 3 3 .ugit/ 4 + .tsnet/ 5 + *.qcow2
-5
.helix/languages.toml
··· 1 - [[language]] 2 - name = "templ" 3 - language-id = "html" 4 - language-servers = ["templ", "vscode-html-language-server", "tailwindcss-ls"] 5 -
+6 -5
README.md
··· 2 2 <picture> 3 3 <img alt="ugit logo" width="250" src="./assets/ugit.svg" /> 4 4 </picture> 5 - <h3 align="center">ugit</h3> 5 + <h3 align="center">ยตgit</h3> 6 6 </p> 7 7 8 8 Minimal git server 9 9 10 - ugit allows cloning via HTTPS/SSH, but can only be pushed to via SSH. 10 + ยตgit allows cloning via HTTPS/SSH, but can only be pushed to via SSH. 11 11 12 - There are no plans to directly support issues or PR workflows, although webhooks are planned and auxillary software may be created to facilitate these things. 13 - For now, if you wish to collaborate, please send me patches at [ugit@jolheiser.com](mailto:git@jolheiser.com). 12 + There are no plans to directly support issues or PR workflows. 13 + If you wish to collaborate, please send me patches via [github](https://github.com/jolheiser/ugit) or [tangled](https://tangled.org/@jolheiser.com/ugit). 14 + For issue/bug-tracking, I recommend [git-bug](https://github.com/git-bug/git-bug). 14 15 15 - Currently all HTML is allowed in markdown, ugit is intended to be run by/for a trusted user. 16 + Currently all HTML is allowed in markdown, ยตgit is intended to be run by/for a trusted user. 16 17 17 18 ## Getting your public SSH keys from another forge 18 19
+1 -1
assets/ugit.svg
··· 1 1 <?xml version="1.0" encoding="UTF-8"?> 2 2 <svg viewBox="0 0 24 24" stroke="#de4c36" fill="#de4c36" stroke-width="1" xmlns="http://www.w3.org/2000/svg"> 3 - <title>ugit icon</title> 3 + <title>ยตgit icon</title> 4 4 <rect fill="none" x="1" y="1" rx="1" ry="1" width="22" height="22"></rect> 5 5 <ellipse cx="6" cy="6" rx="2" ry="2"></ellipse> 6 6 <ellipse cx="18" cy="6" rx="2" ry="2"></ellipse>
+29 -12
cmd/ugitd/args.go
··· 3 3 import ( 4 4 "flag" 5 5 "fmt" 6 + "log/slog" 6 7 "strings" 7 8 8 - "github.com/charmbracelet/log" 9 9 "github.com/peterbourgon/ff/v3" 10 10 "github.com/peterbourgon/ff/v3/ffyaml" 11 11 ) 12 12 13 13 type cliArgs struct { 14 - RepoDir string 15 - SSH sshArgs 16 - HTTP httpArgs 17 - Meta metaArgs 18 - Profile profileArgs 19 - Log logArgs 14 + RepoDir string 15 + SSH sshArgs 16 + HTTP httpArgs 17 + Meta metaArgs 18 + Profile profileArgs 19 + Log logArgs 20 + ShowPrivate bool 20 21 } 21 22 22 23 type sshArgs struct { 24 + Enable bool 23 25 AuthorizedKeys string 24 26 CloneURL string 25 27 Port int ··· 27 29 } 28 30 29 31 type httpArgs struct { 32 + Enable bool 30 33 CloneURL string 31 34 Port int 32 35 } ··· 48 51 } 49 52 50 53 type logArgs struct { 51 - Level log.Level 54 + Level slog.Level 52 55 JSON bool 53 56 } 54 57 ··· 59 62 c = cliArgs{ 60 63 RepoDir: ".ugit", 61 64 SSH: sshArgs{ 65 + Enable: true, 62 66 AuthorizedKeys: ".ssh/authorized_keys", 63 67 CloneURL: "ssh://localhost:8448", 64 68 Port: 8448, 65 69 HostKey: ".ssh/ugit_ed25519", 66 70 }, 67 71 HTTP: httpArgs{ 72 + Enable: true, 68 73 CloneURL: "http://localhost:8449", 69 74 Port: 8449, 70 75 }, ··· 73 78 Description: "Minimal git server", 74 79 }, 75 80 Log: logArgs{ 76 - Level: log.InfoLevel, 81 + Level: slog.LevelError, 77 82 }, 78 83 } 79 84 80 85 fs.Func("log.level", "Logging level", func(s string) error { 81 - lvl, err := log.ParseLevel(s) 82 - if err != nil { 83 - return err 86 + var lvl slog.Level 87 + switch strings.ToLower(s) { 88 + case "debug": 89 + lvl = slog.LevelDebug 90 + case "info": 91 + lvl = slog.LevelInfo 92 + case "warn", "warning": 93 + lvl = slog.LevelWarn 94 + case "error": 95 + lvl = slog.LevelError 96 + default: 97 + return fmt.Errorf("unknown log level %q: options are [debug, info, warn, error]", s) 84 98 } 85 99 c.Log.Level = lvl 86 100 return nil 87 101 }) 88 102 fs.BoolVar(&c.Log.JSON, "log.json", c.Log.JSON, "Print logs in JSON(L) format") 89 103 fs.StringVar(&c.RepoDir, "repo-dir", c.RepoDir, "Path to directory containing repositories") 104 + fs.BoolVar(&c.ShowPrivate, "show-private", c.ShowPrivate, "Show private repos in web interface") 105 + fs.BoolVar(&c.SSH.Enable, "ssh.enable", c.SSH.Enable, "Enable SSH server") 90 106 fs.StringVar(&c.SSH.AuthorizedKeys, "ssh.authorized-keys", c.SSH.AuthorizedKeys, "Path to authorized_keys") 91 107 fs.StringVar(&c.SSH.CloneURL, "ssh.clone-url", c.SSH.CloneURL, "SSH clone URL base") 92 108 fs.IntVar(&c.SSH.Port, "ssh.port", c.SSH.Port, "SSH port") 93 109 fs.StringVar(&c.SSH.HostKey, "ssh.host-key", c.SSH.HostKey, "SSH host key (created if it doesn't exist)") 110 + fs.BoolVar(&c.HTTP.Enable, "http.enable", c.HTTP.Enable, "Enable HTTP server") 94 111 fs.StringVar(&c.HTTP.CloneURL, "http.clone-url", c.HTTP.CloneURL, "HTTP clone URL base") 95 112 fs.IntVar(&c.HTTP.Port, "http.port", c.HTTP.Port, "HTTP port") 96 113 fs.StringVar(&c.Meta.Title, "meta.title", c.Meta.Title, "App title")
+46 -31
cmd/ugitd/main.go
··· 4 4 "errors" 5 5 "flag" 6 6 "fmt" 7 + "log" 7 8 "log/slog" 8 9 "os" 9 10 "os/signal" 10 11 "path/filepath" 11 12 "strconv" 12 13 "strings" 14 + "syscall" 13 15 14 - "github.com/charmbracelet/log" 15 16 "github.com/go-chi/chi/v5/middleware" 16 17 "github.com/go-chi/httplog/v2" 17 18 "github.com/go-git/go-git/v5/plumbing/protocol/packp" ··· 39 40 panic(err) 40 41 } 41 42 42 - log.SetLevel(args.Log.Level) 43 + slog.SetLogLoggerLevel(args.Log.Level) 43 44 middleware.DefaultLogger = httplog.RequestLogger(httplog.NewLogger("ugit", httplog.Options{ 44 45 JSON: args.Log.JSON, 45 46 LogLevel: slog.Level(args.Log.Level), 46 - Concise: args.Log.Level != log.DebugLevel, 47 + Concise: args.Log.Level != slog.LevelDebug, 47 48 })) 48 49 49 - if args.Log.Level == log.DebugLevel { 50 + if args.Log.Level == slog.LevelDebug { 50 51 trace.SetTarget(trace.Packet) 51 52 } else { 52 53 middleware.DefaultLogger = http.NoopLogger ··· 54 55 } 55 56 56 57 if args.Log.JSON { 57 - log.SetFormatter(log.JSONFormatter) 58 + logger := slog.New(slog.NewJSONHandler(os.Stderr, nil)) 59 + slog.SetDefault(logger) 58 60 } 59 61 60 62 if err := requiredFS(args.RepoDir); err != nil { 61 63 panic(err) 62 64 } 63 65 64 - sshSettings := ssh.Settings{ 65 - AuthorizedKeys: args.SSH.AuthorizedKeys, 66 - CloneURL: args.SSH.CloneURL, 67 - Port: args.SSH.Port, 68 - HostKey: args.SSH.HostKey, 69 - RepoDir: args.RepoDir, 70 - } 71 - sshSrv, err := ssh.New(sshSettings) 72 - if err != nil { 73 - panic(err) 74 - } 75 - go func() { 76 - log.Debugf("SSH listening on ssh://localhost:%d\n", sshSettings.Port) 77 - if err := sshSrv.ListenAndServe(); err != nil { 66 + if args.SSH.Enable { 67 + sshSettings := ssh.Settings{ 68 + AuthorizedKeys: args.SSH.AuthorizedKeys, 69 + CloneURL: args.SSH.CloneURL, 70 + Port: args.SSH.Port, 71 + HostKey: args.SSH.HostKey, 72 + RepoDir: args.RepoDir, 73 + } 74 + sshSrv, err := ssh.New(sshSettings) 75 + if err != nil { 78 76 panic(err) 79 77 } 80 - }() 78 + go func() { 79 + log.Printf("SSH listening on ssh://localhost:%d\n", sshSettings.Port) 80 + if err := sshSrv.ListenAndServe(); err != nil { 81 + panic(err) 82 + } 83 + }() 84 + } 81 85 82 86 httpSettings := http.Settings{ 83 87 Title: args.Meta.Title, ··· 89 93 Username: args.Profile.Username, 90 94 Email: args.Profile.Email, 91 95 }, 96 + ShowPrivate: args.ShowPrivate, 92 97 } 93 98 for _, link := range args.Profile.Links { 94 99 httpSettings.Profile.Links = append(httpSettings.Profile.Links, http.Link{ ··· 96 101 URL: link.URL, 97 102 }) 98 103 } 99 - httpSrv := http.New(httpSettings) 100 - go func() { 101 - log.Debugf("HTTP listening on http://localhost:%d\n", httpSettings.Port) 102 - if err := httpSrv.ListenAndServe(); err != nil { 103 - panic(err) 104 - } 105 - }() 104 + if args.HTTP.Enable { 105 + httpSrv := http.New(httpSettings) 106 + go func() { 107 + log.Printf("HTTP listening on http://localhost:%d\n", httpSettings.Port) 108 + if err := httpSrv.ListenAndServe(); err != nil { 109 + panic(err) 110 + } 111 + }() 112 + } 106 113 107 114 ch := make(chan os.Signal, 1) 108 - signal.Notify(ch, os.Kill, os.Interrupt) 115 + signal.Notify(ch, syscall.SIGTERM, os.Interrupt) 109 116 <-ch 110 117 } 111 118 ··· 128 135 } 129 136 fp = filepath.Join(fp, "pre-receive") 130 137 138 + if err := os.MkdirAll(fp+".d", os.ModePerm); err != nil { 139 + return err 140 + } 141 + 131 142 fi, err := os.Create(fp) 132 143 if err != nil { 133 144 return err 134 145 } 135 - fi.WriteString("#!/usr/bin/env bash\n") 136 - fi.WriteString(fmt.Sprintf("%s pre-receive-hook\n", bin)) 146 + fmt.Fprintln(fi, "#!/usr/bin/env bash") 147 + fmt.Fprintf(fi, "%s pre-receive-hook\n", bin) 148 + fmt.Fprintf(fi, `for hook in %s.d/*; do 149 + test -x "${hook}" && test -f "${hook}" || continue 150 + "${hook}" 151 + done`, fp) 137 152 fi.Close() 138 153 139 154 return os.Chmod(fp, 0o755) ··· 147 162 148 163 opts := make([]*packp.Option, 0) 149 164 if pushCount, err := strconv.Atoi(os.Getenv("GIT_PUSH_OPTION_COUNT")); err == nil { 150 - for idx := 0; idx < pushCount; idx++ { 165 + for idx := range pushCount { 151 166 opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx)) 152 167 kv := strings.SplitN(opt, "=", 2) 153 168 if len(kv) == 2 {
+3 -9
contrib/layout.kdl
··· 5 5 command "nix" 6 6 args "develop" 7 7 size "90%" 8 - start_suspended true 9 8 } 10 9 pane split_direction="vertical" { 11 10 pane { 12 - name "run" 13 - command "go" 14 - args "run" "./cmd/ugitd" 15 - start_suspended true 16 - } 17 - pane { 18 - name "watch" 11 + name "air" 19 12 command "nix" 20 - args "develop" "--command" "nu" "-c" "watch --glob *.templ ./internal/html/ {|| go generate ./...}" 13 + args "develop" "--command" "air" 14 + start_suspended true 21 15 } 22 16 } 23 17 }
+7 -62
flake.lock
··· 1 1 { 2 2 "nodes": { 3 - "flake-utils": { 4 - "inputs": { 5 - "systems": "systems" 6 - }, 7 - "locked": { 8 - "lastModified": 1694529238, 9 - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", 10 - "owner": "numtide", 11 - "repo": "flake-utils", 12 - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", 13 - "type": "github" 14 - }, 15 - "original": { 16 - "owner": "numtide", 17 - "repo": "flake-utils", 18 - "type": "github" 19 - } 20 - }, 21 - "gomod2nix": { 22 - "inputs": { 23 - "flake-utils": "flake-utils", 24 - "nixpkgs": [ 25 - "nixpkgs" 26 - ] 27 - }, 28 - "locked": { 29 - "lastModified": 1717050755, 30 - "narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=", 31 - "owner": "nix-community", 32 - "repo": "gomod2nix", 33 - "rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1", 34 - "type": "github" 35 - }, 36 - "original": { 37 - "owner": "nix-community", 38 - "repo": "gomod2nix", 39 - "type": "github" 40 - } 41 - }, 42 3 "nixpkgs": { 43 4 "locked": { 44 - "lastModified": 1719379843, 45 - "narHash": "sha256-u+D+IOAMMl70+CJ9NKB+RMrASjInuIWMHzjLWQjPZ6c=", 5 + "lastModified": 1755205935, 6 + "narHash": "sha256-EQ6qHuJWguaoBZyxdqsgJqyEdS77k+2CnlKrfFxlkRY=", 46 7 "owner": "nixos", 47 8 "repo": "nixpkgs", 48 - "rev": "b3f3c1b13fb08f3828442ee86630362e81136bbc", 9 + "rev": "c5e2e42c112de623adfd662b3e51f0805bf9ff83", 49 10 "type": "github" 50 11 }, 51 12 "original": { ··· 57 18 }, 58 19 "root": { 59 20 "inputs": { 60 - "gomod2nix": "gomod2nix", 61 21 "nixpkgs": "nixpkgs", 62 22 "tailwind-ctp": "tailwind-ctp", 63 23 "tailwind-ctp-lsp": "tailwind-ctp-lsp" 64 24 } 65 25 }, 66 - "systems": { 67 - "locked": { 68 - "lastModified": 1681028828, 69 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 70 - "owner": "nix-systems", 71 - "repo": "default", 72 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 73 - "type": "github" 74 - }, 75 - "original": { 76 - "owner": "nix-systems", 77 - "repo": "default", 78 - "type": "github" 79 - } 80 - }, 81 26 "tailwind-ctp": { 82 27 "inputs": { 83 28 "nixpkgs": [ ··· 85 30 ] 86 31 }, 87 32 "locked": { 88 - "lastModified": 1695841587, 89 - "narHash": "sha256-fgiZd5AV+hi8Ne0bJ8SyAx5nppseW4aXJQEIDSr0VNA=", 33 + "lastModified": 1753803577, 34 + "narHash": "sha256-sE5IgsHacXmfsWJjVVPvb7Aa6oHElt+nfP6+3Pqa414=", 90 35 "ref": "refs/heads/main", 91 - "rev": "afca060674b20e0ccecde2d6fe88c887790219a5", 92 - "revCount": 1, 36 + "rev": "4ce9431cbb4e9e5dceadfbc413fbd34e7305fe8e", 37 + "revCount": 3, 93 38 "type": "git", 94 39 "url": "https://git.jolheiser.com/tailwind-ctp" 95 40 },
+67 -153
flake.nix
··· 3 3 4 4 inputs = { 5 5 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 - gomod2nix = { 7 - url = "github:nix-community/gomod2nix"; 8 - inputs.nixpkgs.follows = "nixpkgs"; 9 - }; 10 6 tailwind-ctp = { 11 7 url = "git+https://git.jolheiser.com/tailwind-ctp"; 12 8 inputs.nixpkgs.follows = "nixpkgs"; ··· 17 13 }; 18 14 }; 19 15 20 - outputs = { 21 - self, 22 - nixpkgs, 23 - gomod2nix, 24 - tailwind-ctp, 25 - tailwind-ctp-lsp, 26 - } @ inputs: let 27 - system = "x86_64-linux"; 28 - pkgs = nixpkgs.legacyPackages.${system}; 29 - tailwind-ctp = inputs.tailwind-ctp.packages.${system}.default; 30 - tailwind-ctp-lsp = inputs.tailwind-ctp-lsp.packages.${system}.default; 31 - ugit = gomod2nix.legacyPackages.${system}.buildGoApplication rec { 32 - name = "ugitd"; 33 - src = pkgs.nix-gitignore.gitignoreSource [] (builtins.path { 34 - inherit name; 35 - path = ./.; 36 - }); 37 - pwd = ./.; 38 - subPackages = ["cmd/ugitd"]; 39 - CGO_ENABLED = 0; 40 - flags = [ 41 - "-trimpath" 42 - ]; 43 - ldflags = [ 44 - "-s" 45 - "-w" 46 - "-extldflags -static" 47 - ]; 48 - meta = with pkgs.lib; { 49 - description = "Minimal git server"; 50 - homepage = "https://git.jolheiser.com/ugit"; 51 - maintainers = with maintainers; [jolheiser]; 52 - mainProgram = "ugitd"; 53 - }; 54 - }; 55 - in { 56 - packages.${system}.default = ugit; 57 - devShells.${system}.default = pkgs.mkShell { 58 - nativeBuildInputs = with pkgs; [ 59 - go 60 - gopls 61 - gomod2nix.legacyPackages.${system}.gomod2nix 62 - templ 63 - tailwind-ctp 64 - tailwind-ctp-lsp 65 - vscode-langservers-extracted 16 + outputs = 17 + { 18 + self, 19 + nixpkgs, 20 + tailwind-ctp, 21 + tailwind-ctp-lsp, 22 + }: 23 + let 24 + systems = [ 25 + "x86_64-linux" 26 + "i686-linux" 27 + "x86_64-darwin" 28 + "aarch64-linux" 29 + "armv6l-linux" 30 + "armv7l-linux" 66 31 ]; 67 - }; 68 - nixosModules.default = { 69 - pkgs, 70 - lib, 71 - config, 72 - ... 73 - }: let 74 - cfg = config.services.ugit; 75 - yamlFormat = pkgs.formats.yaml {}; 76 - configFile = pkgs.writeText "ugit.yaml" (builtins.readFile (yamlFormat.generate "ugit-yaml" cfg.config)); 77 - authorizedKeysFile = pkgs.writeText "ugit_keys" (builtins.concatStringsSep "\n" cfg.authorizedKeys); 78 - in { 79 - options = with lib; { 80 - services.ugit = { 81 - enable = mkEnableOption "Enable ugit"; 82 - 83 - package = mkOption { 84 - type = types.package; 85 - description = "ugit package to use"; 86 - default = ugit; 87 - }; 88 - 89 - repoDir = mkOption { 90 - type = types.str; 91 - description = "where ugit stores repositories"; 92 - default = "/var/lib/ugit/repos"; 93 - }; 94 - 95 - authorizedKeys = mkOption { 96 - type = types.listOf types.str; 97 - description = "list of keys to use for authorized_keys"; 98 - default = []; 99 - }; 100 - 101 - authorizedKeysFile = mkOption { 102 - type = types.str; 103 - description = "path to authorized_keys file ugit uses for auth"; 104 - default = "/var/lib/ugit/authorized_keys"; 105 - }; 106 - 107 - hostKeyFile = mkOption { 108 - type = types.str; 109 - description = "path to host key file (will be created if it doesn't exist)"; 110 - default = "/var/lib/ugit/ugit_ed25519"; 111 - }; 112 - 113 - config = mkOption { 114 - type = types.attrs; 115 - default = {}; 116 - description = "config.yaml contents"; 32 + forAllSystems = f: nixpkgs.lib.genAttrs systems f; 33 + tctp = forAllSystems (system: tailwind-ctp.packages.${system}.default); 34 + tctpl = forAllSystems (system: tailwind-ctp-lsp.packages.${system}.default); 35 + in 36 + { 37 + packages = forAllSystems (system: import ./nix { pkgs = nixpkgs.legacyPackages.${system}; }); 38 + devShells = forAllSystems ( 39 + system: 40 + let 41 + pkgs = nixpkgs.legacyPackages.${system}; 42 + in 43 + { 44 + default = pkgs.mkShell { 45 + nativeBuildInputs = with pkgs; [ 46 + go 47 + gopls 48 + air 49 + tctp.${system} 50 + #tctpl.${system} 51 + #vscode-langservers-extracted 52 + ]; 117 53 }; 118 - 119 - user = mkOption { 120 - type = types.str; 121 - default = "ugit"; 122 - description = "User account under which ugit runs"; 123 - }; 124 - 125 - group = mkOption { 126 - type = types.str; 127 - default = "ugit"; 128 - description = "Group account under which ugit runs"; 129 - }; 130 - 131 - openFirewall = mkOption { 132 - type = types.bool; 133 - default = false; 134 - }; 135 - }; 54 + } 55 + ); 56 + nixosModules.default = import ./nix/module.nix; 57 + nixosConfigurations.ugitVM = nixpkgs.lib.nixosSystem { 58 + system = "x86_64-linux"; 59 + modules = [ 60 + ./nix/vm.nix 61 + { 62 + virtualisation.vmVariant.virtualisation = { 63 + cores = 2; 64 + memorySize = 2048; 65 + graphics = false; 66 + }; 67 + system.stateVersion = "23.11"; 68 + } 69 + ]; 136 70 }; 137 - config = lib.mkIf cfg.enable { 138 - users.users."${cfg.user}" = { 139 - home = "/var/lib/ugit"; 140 - createHome = true; 141 - group = "${cfg.group}"; 142 - isSystemUser = true; 143 - isNormalUser = false; 144 - description = "user for ugit service"; 145 - }; 146 - users.groups."${cfg.group}" = {}; 147 - networking.firewall = lib.mkIf cfg.openFirewall { 148 - allowedTCPPorts = [8448 8449]; 149 - }; 150 - 151 - systemd.services.ugit = { 152 - enable = true; 153 - script = let 154 - authorizedKeysPath = 155 - if (builtins.length cfg.authorizedKeys) > 0 156 - then authorizedKeysFile 157 - else cfg.authorizedKeysFile; 158 - args = ["--config=${configFile}" "--repo-dir=${cfg.repoDir}" "--ssh.authorized-keys=${authorizedKeysPath}" "--ssh.host-key=${cfg.hostKeyFile}"]; 159 - in "${cfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}"; 160 - wantedBy = ["multi-user.target"]; 161 - after = ["network.target"]; 162 - path = [cfg.package pkgs.git pkgs.bash]; 163 - serviceConfig = { 164 - User = cfg.user; 165 - Group = cfg.group; 166 - Restart = "always"; 167 - RestartSec = "15"; 168 - WorkingDirectory = "/var/lib/ugit"; 71 + apps = forAllSystems ( 72 + system: 73 + let 74 + pkgs = import nixpkgs { inherit system; }; 75 + in 76 + { 77 + vm = { 78 + type = "app"; 79 + program = "${pkgs.writeShellScript "vm" '' 80 + nixos-rebuild build-vm --flake .#ugitVM 81 + ./result/bin/run-nixos-vm 82 + rm nixos.qcow2 83 + ''}"; 169 84 }; 170 - }; 171 - }; 85 + } 86 + ); 172 87 }; 173 - }; 174 88 }
+73 -56
go.mod
··· 1 1 module go.jolheiser.com/ugit 2 2 3 - go 1.21 4 - 5 - toolchain go1.21.5 3 + go 1.24.0 6 4 7 5 require ( 8 - github.com/a-h/templ v0.2.543 9 - github.com/alecthomas/chroma/v2 v2.12.0 10 - github.com/charmbracelet/log v0.3.1 11 - github.com/charmbracelet/ssh v0.0.0-20240201134204-3f297de25560 12 - github.com/charmbracelet/wish v1.3.0 6 + github.com/a-h/templ v0.3.960 7 + github.com/alecthomas/assert/v2 v2.11.0 8 + github.com/alecthomas/chroma/v2 v2.20.0 9 + github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309 10 + github.com/charmbracelet/wish v1.4.7 13 11 github.com/dustin/go-humanize v1.0.1 14 - github.com/go-chi/chi/v5 v5.0.11 12 + github.com/go-chi/chi/v5 v5.2.3 15 13 github.com/go-chi/httplog/v2 v2.1.1 16 - github.com/go-git/go-billy/v5 v5.5.0 17 - github.com/go-git/go-git/v5 v5.11.0 14 + github.com/go-git/go-billy/v5 v5.6.2 15 + github.com/go-git/go-git/v5 v5.16.3 18 16 github.com/peterbourgon/ff/v3 v3.4.0 19 - github.com/yuin/goldmark v1.6.0 20 - github.com/yuin/goldmark-emoji v1.0.2 17 + github.com/yuin/goldmark v1.7.13 18 + github.com/yuin/goldmark-emoji v1.0.6 21 19 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc 22 - golang.org/x/net v0.25.0 20 + golang.org/x/net v0.46.0 23 21 ) 24 22 25 23 require ( 26 - dario.cat/mergo v1.0.0 // indirect 27 - github.com/Microsoft/go-winio v0.6.1 // indirect 28 - github.com/ProtonMail/go-crypto v1.0.0 // indirect 24 + dario.cat/mergo v1.0.2 // indirect 25 + github.com/Microsoft/go-winio v0.6.2 // indirect 26 + github.com/ProtonMail/go-crypto v1.3.0 // indirect 27 + github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect 28 + github.com/alecthomas/repr v0.5.1 // indirect 29 + github.com/andybalholm/brotli v1.1.0 // indirect 29 30 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 30 - github.com/atotto/clipboard v0.1.4 // indirect 31 31 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 32 - github.com/catppuccin/go v0.2.0 // indirect 33 - github.com/charmbracelet/bubbles v0.18.0 // indirect 34 - github.com/charmbracelet/bubbletea v0.26.4 // indirect 35 - github.com/charmbracelet/huh v0.5.1 // indirect 36 - github.com/charmbracelet/keygen v0.5.0 // indirect 37 - github.com/charmbracelet/lipgloss v0.11.0 // indirect 38 - github.com/charmbracelet/x/ansi v0.1.2 // indirect 39 - github.com/charmbracelet/x/errors v0.0.0-20240130180102-bafe6fbaee60 // indirect 40 - github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1 // indirect 41 - github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a // indirect 42 - github.com/charmbracelet/x/input v0.1.2 // indirect 43 - github.com/charmbracelet/x/term v0.1.1 // indirect 44 - github.com/charmbracelet/x/windows v0.1.2 // indirect 45 - github.com/cloudflare/circl v1.3.7 // indirect 46 - github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5 // indirect 47 - github.com/creack/pty v1.1.21 // indirect 48 - github.com/cyphar/filepath-securejoin v0.2.4 // indirect 49 - github.com/dlclark/regexp2 v1.10.0 // indirect 32 + github.com/cenkalti/backoff/v4 v4.3.0 // indirect 33 + github.com/charmbracelet/bubbletea v1.3.10 // indirect 34 + github.com/charmbracelet/colorprofile v0.3.2 // indirect 35 + github.com/charmbracelet/keygen v0.5.4 // indirect 36 + github.com/charmbracelet/lipgloss v1.1.0 // indirect 37 + github.com/charmbracelet/log v0.4.2 // indirect 38 + github.com/charmbracelet/x/ansi v0.10.2 // indirect 39 + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect 40 + github.com/charmbracelet/x/conpty v0.1.1 // indirect 41 + github.com/charmbracelet/x/errors v0.0.0-20251030181443-0cf22f8402df // indirect 42 + github.com/charmbracelet/x/term v0.2.1 // indirect 43 + github.com/charmbracelet/x/termios v0.1.1 // indirect 44 + github.com/cli/browser v1.3.0 // indirect 45 + github.com/clipperhouse/stringish v0.1.1 // indirect 46 + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect 47 + github.com/cloudflare/circl v1.6.1 // indirect 48 + github.com/creack/pty v1.1.24 // indirect 49 + github.com/cyphar/filepath-securejoin v0.5.0 // indirect 50 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 51 + github.com/dlclark/regexp2 v1.11.5 // indirect 50 52 github.com/emirpasic/gods v1.18.1 // indirect 51 53 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect 54 + github.com/fatih/color v1.16.0 // indirect 55 + github.com/fsnotify/fsnotify v1.7.0 // indirect 52 56 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 53 - github.com/go-logfmt/logfmt v0.6.0 // indirect 54 - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 57 + github.com/go-logfmt/logfmt v0.6.1 // indirect 58 + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 59 + github.com/hexops/gotextdiff v1.0.3 // indirect 55 60 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 56 - github.com/kevinburke/ssh_config v1.2.0 // indirect 57 - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 61 + github.com/kevinburke/ssh_config v1.4.0 // indirect 62 + github.com/klauspost/cpuid/v2 v2.3.0 // indirect 63 + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect 64 + github.com/mattn/go-colorable v0.1.13 // indirect 58 65 github.com/mattn/go-isatty v0.0.20 // indirect 59 66 github.com/mattn/go-localereader v0.0.1 // indirect 60 - github.com/mattn/go-runewidth v0.0.15 // indirect 61 - github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect 67 + github.com/mattn/go-runewidth v0.0.19 // indirect 62 68 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect 63 69 github.com/muesli/cancelreader v0.2.2 // indirect 64 - github.com/muesli/reflow v0.3.0 // indirect 65 - github.com/muesli/termenv v0.15.2 // indirect 66 - github.com/pjbgf/sha1cd v0.3.0 // indirect 70 + github.com/muesli/termenv v0.16.0 // indirect 71 + github.com/natefinch/atomic v1.0.1 // indirect 72 + github.com/pjbgf/sha1cd v0.5.0 // indirect 73 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 67 74 github.com/rivo/uniseg v0.4.7 // indirect 68 - github.com/sergi/go-diff v1.3.1 // indirect 69 - github.com/skeema/knownhosts v1.2.1 // indirect 70 - github.com/u-root/u-root v0.12.0 // indirect 75 + github.com/sergi/go-diff v1.4.0 // indirect 76 + github.com/skeema/knownhosts v1.3.2 // indirect 71 77 github.com/xanzy/ssh-agent v0.3.3 // indirect 72 78 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 73 - golang.org/x/crypto v0.23.0 // indirect 74 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect 75 - golang.org/x/mod v0.17.0 // indirect 76 - golang.org/x/sync v0.7.0 // indirect 77 - golang.org/x/sys v0.21.0 // indirect 78 - golang.org/x/term v0.20.0 // indirect 79 - golang.org/x/text v0.16.0 // indirect 80 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 79 + golang.org/x/crypto v0.43.0 // indirect 80 + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect 81 + golang.org/x/mod v0.29.0 // indirect 82 + golang.org/x/sync v0.17.0 // indirect 83 + golang.org/x/sys v0.37.0 // indirect 84 + golang.org/x/text v0.30.0 // indirect 85 + golang.org/x/tools v0.38.0 // indirect 81 86 gopkg.in/warnings.v0 v0.1.2 // indirect 82 87 gopkg.in/yaml.v2 v2.4.0 // indirect 83 88 ) 89 + 90 + tool ( 91 + github.com/a-h/templ/cmd/templ 92 + go.jolheiser.com/ugit/assets 93 + go.jolheiser.com/ugit/cmd/ugitd 94 + go.jolheiser.com/ugit/internal/git 95 + go.jolheiser.com/ugit/internal/html 96 + go.jolheiser.com/ugit/internal/html/markup 97 + go.jolheiser.com/ugit/internal/http 98 + go.jolheiser.com/ugit/internal/http/httperr 99 + go.jolheiser.com/ugit/internal/ssh 100 + )
+1 -1
go.mod.sri
··· 1 - sha256-8kI94hcJupAUye6cEAmIlN+CrtYSXlgoAlmpyXArfF8= 1 + sha256-OPdD57zqvRzzk8o7xyxGoqUCT+KNtBI/+1isQXkhWOw=
+134 -193
go.sum
··· 1 - dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 - dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 1 + dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= 2 + dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= 3 3 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 4 - github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 5 - github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 6 - github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= 7 - github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 8 - github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= 9 - github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= 10 - github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= 11 - github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= 4 + github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 5 + github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 6 + github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= 7 + github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= 8 + github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo= 9 + github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ= 10 + github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM= 11 + github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= 12 + github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= 13 + github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= 12 14 github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= 13 - github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= 14 - github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= 15 + github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= 16 + github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= 15 17 github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= 16 - github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= 17 - github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 18 + github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= 19 + github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 20 + github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 21 + github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 18 22 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 19 23 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 20 24 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 21 25 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 22 - github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 23 - github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 24 26 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 25 27 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 26 - github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 27 - github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= 28 - github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= 29 - github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= 30 - github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= 31 - github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= 32 - github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= 33 - github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40= 34 - github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0= 35 - github.com/charmbracelet/huh v0.5.1 h1:t5j6g9sMjAE2a9AQuc4lNL7pf/0X4WdHiiMGkL8v/aM= 36 - github.com/charmbracelet/huh v0.5.1/go.mod h1:gs7b2brpzXkY0PBWUqJrlzvOowTCL0vNAR6OTItc+kA= 37 - github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= 38 - github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8= 39 - github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= 40 - github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= 41 - github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g= 42 - github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8= 43 - github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw= 44 - github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g= 45 - github.com/charmbracelet/ssh v0.0.0-20240201134204-3f297de25560 h1:kUYiDRF04AsyJvWi4HrklXXSU7+S7qjExusfDnYbVGg= 46 - github.com/charmbracelet/ssh v0.0.0-20240201134204-3f297de25560/go.mod h1:IHy7o73i1MrQ5lmyJjjJ0g7y4+V+g69cm+Y7JCiZWPo= 47 - github.com/charmbracelet/wish v1.3.0 h1:SYV5TIlzDb6WaxjkkYXxv2WZsTu/QZGwfGVc0UB5M48= 48 - github.com/charmbracelet/wish v1.3.0/go.mod h1:1U/bI7zX+IE26ThD5gxtLgeRzctVhSrTpjucPqw4Pos= 49 - github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= 50 - github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= 51 - github.com/charmbracelet/x/errors v0.0.0-20240130180102-bafe6fbaee60 h1:u0XFhTN81zvoGwyQWOYjjkrimEQj5L2DPECCX5cBsRw= 52 - github.com/charmbracelet/x/errors v0.0.0-20240130180102-bafe6fbaee60/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= 53 - github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1 h1:VZIQzjwFE0EamzG2v8HfemeisB8X02Tl0BZBnJ0PeU8= 54 - github.com/charmbracelet/x/exp/strings v0.0.0-20240617190524-788ec55faed1/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= 55 - github.com/charmbracelet/x/exp/term v0.0.0-20240130180102-bafe6fbaee60 h1:IV19YKUZVf6ATrhiPSCirZ4Bs7EsenYwOWcUHngV+q0= 56 - github.com/charmbracelet/x/exp/term v0.0.0-20240130180102-bafe6fbaee60/go.mod h1:kOOxxyxgAFQVcR5yQJWTuLjzt5dR2pcgwy3WaLEudjE= 57 - github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a h1:k/s6UoOSVynWiw7PlclyGO2VdVs5ZLbMIHiGp4shFZE= 58 - github.com/charmbracelet/x/exp/term v0.0.0-20240524151031-ff83003bf67a/go.mod h1:YBotIGhfoWhHDlnUpJMkjebGV2pdGRCn1Y4/Nk/vVcU= 59 - github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= 60 - github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA= 61 - github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= 62 - github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= 63 - github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= 64 - github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= 65 - github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 66 - github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 67 - github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 68 - github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5 h1:Ig+OPkE3XQrrl+SKsOqAjlkrBN/zrr+Qpw7rCuDjRCE= 69 - github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= 70 - github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= 71 - github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 72 - github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= 73 - github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 28 + github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 29 + github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 30 + github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= 31 + github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= 32 + github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI= 33 + github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI= 34 + github.com/charmbracelet/keygen v0.5.4 h1:XQYgf6UEaTGgQSSmiPpIQ78WfseNQp4Pz8N/c1OsrdA= 35 + github.com/charmbracelet/keygen v0.5.4/go.mod h1:t4oBRr41bvK7FaJsAaAQhhkUuHslzFXVjOBwA55CZNM= 36 + github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= 37 + github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= 38 + github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= 39 + github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= 40 + github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309 h1:dCVbCRRtg9+tsfiTXTp0WupDlHruAXyp+YoxGVofHHc= 41 + github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309/go.mod h1:R9cISUs5kAH4Cq/rguNbSwcR+slE5Dfm8FEs//uoIGE= 42 + github.com/charmbracelet/wish v1.4.7 h1:O+jdLac3s6GaqkOHHSwezejNK04vl6VjO1A+hl8J8Yc= 43 + github.com/charmbracelet/wish v1.4.7/go.mod h1:OBZ8vC62JC5cvbxJLh+bIWtG7Ctmct+ewziuUWK+G14= 44 + github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw= 45 + github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8= 46 + github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= 47 + github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 48 + github.com/charmbracelet/x/conpty v0.1.1 h1:s1bUxjoi7EpqiXysVtC+a8RrvPPNcNvAjfi4jxsAuEs= 49 + github.com/charmbracelet/x/conpty v0.1.1/go.mod h1:OmtR77VODEFbiTzGE9G1XiRJAga6011PIm4u5fTNZpk= 50 + github.com/charmbracelet/x/errors v0.0.0-20251030181443-0cf22f8402df h1:6zTS1yAzaSDINNXDQRtayOHL4Zde4LVmnbjRPdxV84g= 51 + github.com/charmbracelet/x/errors v0.0.0-20251030181443-0cf22f8402df/go.mod h1:O2BTD/aMVQDmrvqroIO3fB6zXUuU07ZpVt21QTmZjRg= 52 + github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= 53 + github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 54 + github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= 55 + github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= 56 + github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= 57 + github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= 58 + github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= 59 + github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= 60 + github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= 61 + github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= 62 + github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 63 + github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 64 + github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= 65 + github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 66 + github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw= 67 + github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 74 68 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 75 - github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 76 69 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 70 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 71 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 77 72 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 78 73 github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 79 - github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= 80 - github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 74 + github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= 75 + github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 81 76 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 82 77 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 83 - github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= 84 - github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 78 + github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= 79 + github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 85 80 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 86 81 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 87 82 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= 88 83 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= 89 - github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= 90 - github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= 91 - github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= 92 - github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 84 + github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 85 + github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 86 + github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 87 + github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 88 + github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= 89 + github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= 90 + github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= 91 + github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= 93 92 github.com/go-chi/httplog/v2 v2.1.1 h1:ojojiu4PIaoeJ/qAO4GWUxJqvYUTobeo7zmuHQJAxRk= 94 93 github.com/go-chi/httplog/v2 v2.1.1/go.mod h1:/XXdxicJsp4BA5fapgIC3VuTD+z0Z/VzukoB3VDc1YE= 95 94 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 96 95 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 97 - github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= 98 - github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= 96 + github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= 97 + github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= 99 98 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 100 99 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 101 - github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= 102 - github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= 103 - github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 104 - github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 105 - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 106 - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 107 - github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 108 - github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 100 + github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= 101 + github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= 102 + github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= 103 + github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= 104 + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 105 + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 106 + github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 107 + github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 109 108 github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 110 109 github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 111 - github.com/hugelgupf/vmtest v0.0.0-20240102225328-693afabdd27f h1:ov45/OzrJG8EKbGjn7jJZQJTN7Z1t73sFYNIRd64YlI= 112 - github.com/hugelgupf/vmtest v0.0.0-20240102225328-693afabdd27f/go.mod h1:JoDrYMZpDPYo6uH9/f6Peqms3zNNWT2XiGgioMOIGuI= 113 110 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 114 111 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 115 - github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 116 - github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 112 + github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= 113 + github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= 114 + github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= 115 + github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 117 116 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 118 117 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 119 118 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= ··· 121 120 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 122 121 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 123 122 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 124 - github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 125 - github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 123 + github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= 124 + github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 125 + github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 126 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 127 + github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 126 128 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 127 129 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 128 130 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= 129 131 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= 130 - github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 131 - github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 132 - github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 133 - github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= 134 - github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= 132 + github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= 133 + github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= 135 134 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= 136 135 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 137 136 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= 138 137 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 139 - github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 140 - github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 141 - github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 142 - github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 143 - github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 144 - github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 138 + github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 139 + github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 140 + github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= 141 + github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= 142 + github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 143 + github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 145 144 github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= 146 145 github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= 147 - github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= 148 - github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= 146 + github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= 147 + github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= 149 148 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 150 149 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 151 - github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 152 150 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 153 - github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 154 - github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 155 - github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= 156 - github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 151 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 152 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 157 153 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 158 154 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 159 - github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 160 - github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 161 - github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= 162 - github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= 155 + github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 156 + github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 157 + github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= 158 + github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 163 159 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 164 - github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= 165 - github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= 160 + github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= 161 + github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= 166 162 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 167 163 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 168 164 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 169 165 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 170 - github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 171 - github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 172 - github.com/u-root/gobusybox/src v0.0.0-20231228173702-b69f654846aa h1:unMPGGK/CRzfg923allsikmvk2l7beBeFPUNC4RVX/8= 173 - github.com/u-root/gobusybox/src v0.0.0-20231228173702-b69f654846aa/go.mod h1:Zj4Tt22fJVn/nz/y6Ergm1SahR9dio1Zm/D2/S0TmXM= 174 - github.com/u-root/u-root v0.12.0 h1:K0AuBFriwr0w/PGS3HawiAw89e3+MU7ks80GpghAsNs= 175 - github.com/u-root/u-root v0.12.0/go.mod h1:FYjTOh4IkIZHhjsd17lb8nYW6udgXdJhG1c0r6u0arI= 166 + github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 167 + github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 176 168 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 177 169 github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 178 170 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 179 171 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 180 - github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 181 - github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 182 172 github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 183 - github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= 184 - github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 185 - github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= 186 - github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= 173 + github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= 174 + github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= 175 + github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= 176 + github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= 187 177 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= 188 178 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= 189 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 190 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 191 179 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 192 - golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 193 - golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 194 - golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= 195 - golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= 196 - golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 197 - golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 198 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= 199 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 200 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 201 - golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 202 - golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 203 - golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 204 - golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 205 - golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 206 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 180 + golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= 181 + golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= 182 + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= 183 + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= 184 + golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= 185 + golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 207 186 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 208 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 209 - golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 210 - golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 211 - golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 212 - golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 213 - golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 214 - golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 215 - golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 216 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 217 - golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 218 - golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 219 - golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 220 - golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 221 - golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 222 - golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 187 + golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= 188 + golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= 189 + golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= 190 + golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 223 191 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 224 192 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 225 193 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 194 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 195 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 228 196 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 229 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 230 197 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 231 - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 232 - golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 233 - golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 234 - golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 235 - golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 198 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 236 199 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 237 - golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 238 - golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 239 - golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 240 - golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 200 + golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= 201 + golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 241 202 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 242 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 243 - golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 244 - golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 245 - golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 246 - golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= 247 - golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 248 - golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= 249 - golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 250 - golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 251 - golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 203 + golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= 204 + golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= 252 205 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 253 - golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 254 - golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 255 - golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 256 - golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 257 - golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 258 - golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 259 - golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 260 - golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 206 + golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= 207 + golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= 261 208 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 262 - golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 263 - golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 264 - golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 265 - golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= 266 - golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 267 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 268 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 269 - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 209 + golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= 210 + golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 270 211 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 271 212 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 272 213 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-180
gomod2nix.toml
··· 1 - schema = 3 2 - 3 - [mod] 4 - [mod."dario.cat/mergo"] 5 - version = "v1.0.0" 6 - hash = "sha256-jlpc8dDj+DmiOU4gEawBu8poJJj9My0s9Mvuk9oS8ww=" 7 - [mod."github.com/Microsoft/go-winio"] 8 - version = "v0.6.1" 9 - hash = "sha256-BL0BVaHtmPKQts/711W59AbHXjGKqFS4ZTal0RYnR9I=" 10 - [mod."github.com/ProtonMail/go-crypto"] 11 - version = "v1.0.0" 12 - hash = "sha256-Gflazvyv+457FpUTtPafJ+SdolYSalpsU0tragTxNi8=" 13 - [mod."github.com/a-h/templ"] 14 - version = "v0.2.543" 15 - hash = "sha256-1BvIj9UPZJp8SOXMPIGdHyZLIvjORHg2UY3pRZJM01s=" 16 - [mod."github.com/alecthomas/chroma/v2"] 17 - version = "v2.12.0" 18 - hash = "sha256-w3gKGPwsoayknuU4ifPaF0JOMNqnKjIEutbIkR9c2Ag=" 19 - [mod."github.com/anmitsu/go-shlex"] 20 - version = "v0.0.0-20200514113438-38f4b401e2be" 21 - hash = "sha256-L3Ak4X2z7WXq7vMKuiHCOJ29nlpajUQ08Sfb9T0yP54=" 22 - [mod."github.com/aymanbagabas/go-osc52/v2"] 23 - version = "v2.0.1" 24 - hash = "sha256-6Bp0jBZ6npvsYcKZGHHIUSVSTAMEyieweAX2YAKDjjg=" 25 - [mod."github.com/charmbracelet/bubbletea"] 26 - version = "v0.25.0" 27 - hash = "sha256-A0WjFRFAUhwO3m7uvCOeefPPIM8ReU+xTtIRxG0aH+Y=" 28 - [mod."github.com/charmbracelet/keygen"] 29 - version = "v0.5.0" 30 - hash = "sha256-JFD2SdFL7tq3oVhnBEgiBTrJvjqdUtIuodAJuSFcJoA=" 31 - [mod."github.com/charmbracelet/lipgloss"] 32 - version = "v0.9.1" 33 - hash = "sha256-AHbabOymgDRIXsMBgJHS25/GgBWT54oGbd15EBWKeZc=" 34 - [mod."github.com/charmbracelet/log"] 35 - version = "v0.3.1" 36 - hash = "sha256-Er60POPID2eNrRZnBHxoI4yHn0mIKnXYftGKSslbXx0=" 37 - [mod."github.com/charmbracelet/ssh"] 38 - version = "v0.0.0-20240201134204-3f297de25560" 39 - hash = "sha256-r4h4bym47rs3C2us+sCgVfwAl4TCbm3bDCTsXKYREz8=" 40 - [mod."github.com/charmbracelet/wish"] 41 - version = "v1.3.0" 42 - hash = "sha256-3Uq1PDu5DMoWgJykFx/roGk20x8jdb7o5JFPpmEtX/c=" 43 - [mod."github.com/charmbracelet/x/errors"] 44 - version = "v0.0.0-20240130180102-bafe6fbaee60" 45 - hash = "sha256-GO8hf0lhVtl00C+xoTzvBtPU2cO0PymSLc2szBRUNtE=" 46 - [mod."github.com/charmbracelet/x/exp/term"] 47 - version = "v0.0.0-20240130180102-bafe6fbaee60" 48 - hash = "sha256-hEj/Gj1U1ahk5EFVZVAL52yrdBNO47yXykpiehJICbc=" 49 - [mod."github.com/cloudflare/circl"] 50 - version = "v1.3.7" 51 - hash = "sha256-AkOpcZ+evLxLJStvvr01+TLeWDqcLxY3e/AhGggzh40=" 52 - [mod."github.com/containerd/console"] 53 - version = "v1.0.4-0.20230706203907-8f6c4e4faef5" 54 - hash = "sha256-mxRERsgS6TmI5I0UYblhzl2FZlbtkJhUkfF1x6mZINw=" 55 - [mod."github.com/creack/pty"] 56 - version = "v1.1.21" 57 - hash = "sha256-pjGw6wQlrVhN65XaIxZueNJqnXThGu00u24rKOLzxS0=" 58 - [mod."github.com/cyphar/filepath-securejoin"] 59 - version = "v0.2.4" 60 - hash = "sha256-heCD0xMxlwnHCHcRBgTjVexHOLyWI2zRW3E8NFKoLzk=" 61 - [mod."github.com/dlclark/regexp2"] 62 - version = "v1.10.0" 63 - hash = "sha256-Jxzj/O/Q9tIWBOOgCkCibhrgJBzzfVIxYDsabt7O8ow=" 64 - [mod."github.com/dustin/go-humanize"] 65 - version = "v1.0.1" 66 - hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc=" 67 - [mod."github.com/emirpasic/gods"] 68 - version = "v1.18.1" 69 - hash = "sha256-hGDKddjLj+5dn2woHtXKUdd49/3xdsqnhx7VEdCu1m4=" 70 - [mod."github.com/go-chi/chi/v5"] 71 - version = "v5.0.11" 72 - hash = "sha256-95LKg/OVzhik2HUz6cirHH3eAT4qbHSg52bSvkc+XOY=" 73 - [mod."github.com/go-git/gcfg"] 74 - version = "v1.5.1-0.20230307220236-3a3c6141e376" 75 - hash = "sha256-f4k0gSYuo0/q3WOoTxl2eFaj7WZpdz29ih6CKc8Ude8=" 76 - [mod."github.com/go-git/go-billy/v5"] 77 - version = "v5.5.0" 78 - hash = "sha256-4XUoD2bOCMCdu83egb/y8kY/Fm0s1rWgPMtiahh38OQ=" 79 - [mod."github.com/go-git/go-git/v5"] 80 - version = "v5.11.0" 81 - hash = "sha256-2yUM/FlV+nYxacVynJCnDZeMub4Iu8JL2WBHmlnwOkE=" 82 - [mod."github.com/go-logfmt/logfmt"] 83 - version = "v0.6.0" 84 - hash = "sha256-RtIG2qARd5sT10WQ7F3LR8YJhS8exs+KiuUiVf75bWg=" 85 - [mod."github.com/golang/groupcache"] 86 - version = "v0.0.0-20210331224755-41bb18bfe9da" 87 - hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" 88 - [mod."github.com/jbenet/go-context"] 89 - version = "v0.0.0-20150711004518-d14ea06fba99" 90 - hash = "sha256-VANNCWNNpARH/ILQV9sCQsBWgyL2iFT+4AHZREpxIWE=" 91 - [mod."github.com/kevinburke/ssh_config"] 92 - version = "v1.2.0" 93 - hash = "sha256-Ta7ZOmyX8gG5tzWbY2oES70EJPfI90U7CIJS9EAce0s=" 94 - [mod."github.com/lucasb-eyer/go-colorful"] 95 - version = "v1.2.0" 96 - hash = "sha256-Gg9dDJFCTaHrKHRR1SrJgZ8fWieJkybljybkI9x0gyE=" 97 - [mod."github.com/mattn/go-isatty"] 98 - version = "v0.0.20" 99 - hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=" 100 - [mod."github.com/mattn/go-localereader"] 101 - version = "v0.0.1" 102 - hash = "sha256-JlWckeGaWG+bXK8l8WEdZqmSiTwCA8b1qbmBKa/Fj3E=" 103 - [mod."github.com/mattn/go-runewidth"] 104 - version = "v0.0.15" 105 - hash = "sha256-WP39EU2UrQbByYfnwrkBDoKN7xzXsBssDq3pNryBGm0=" 106 - [mod."github.com/muesli/ansi"] 107 - version = "v0.0.0-20230316100256-276c6243b2f6" 108 - hash = "sha256-qRKn0Bh2yvP0QxeEMeZe11Vz0BPFIkVcleKsPeybKMs=" 109 - [mod."github.com/muesli/cancelreader"] 110 - version = "v0.2.2" 111 - hash = "sha256-uEPpzwRJBJsQWBw6M71FDfgJuR7n55d/7IV8MO+rpwQ=" 112 - [mod."github.com/muesli/reflow"] 113 - version = "v0.3.0" 114 - hash = "sha256-Pou2ybE9SFSZG6YfZLVV1Eyfm+X4FuVpDPLxhpn47Cc=" 115 - [mod."github.com/muesli/termenv"] 116 - version = "v0.15.2" 117 - hash = "sha256-Eum/SpyytcNIchANPkG4bYGBgcezLgej7j/+6IhqoMU=" 118 - [mod."github.com/peterbourgon/ff/v3"] 119 - version = "v3.4.0" 120 - hash = "sha256-rmRl4GSmc2atnMbw6hTs6jwxW5lO4ivYuF2VN3jacZM=" 121 - [mod."github.com/pjbgf/sha1cd"] 122 - version = "v0.3.0" 123 - hash = "sha256-kX9BdLh2dxtGNaDvc24NORO+C0AZ7JzbrXrtecCdB7w=" 124 - [mod."github.com/rivo/uniseg"] 125 - version = "v0.4.6" 126 - hash = "sha256-zGfzO8FWj03POzo47SzrK1B4yLMKJ7iic6ium76ZLzI=" 127 - [mod."github.com/sergi/go-diff"] 128 - version = "v1.3.1" 129 - hash = "sha256-XLA/BLIPuUU76yikXqIeRSXr7T7A3Uz6I27+mDxGj7w=" 130 - [mod."github.com/skeema/knownhosts"] 131 - version = "v1.2.1" 132 - hash = "sha256-u0jB6ahTdGa+SvcIvPNRLnbSHvgmW9X/ThRq0nWQrJs=" 133 - [mod."github.com/u-root/u-root"] 134 - version = "v0.12.0" 135 - hash = "sha256-B9Qoq1S0l0W6twET54uxiWjh2ulxN/zMLAeWJX4cXW0=" 136 - [mod."github.com/xanzy/ssh-agent"] 137 - version = "v0.3.3" 138 - hash = "sha256-l3pGB6IdzcPA/HLk93sSN6NM2pKPy+bVOoacR5RC2+c=" 139 - [mod."github.com/yuin/goldmark"] 140 - version = "v1.6.0" 141 - hash = "sha256-0PeGjGxxM7lUSx2dn8yPUBpilPQzEN9nkgf3s+5zGTY=" 142 - [mod."github.com/yuin/goldmark-emoji"] 143 - version = "v1.0.2" 144 - hash = "sha256-RvzhNXlF98fu9SD/Rve9JMtR4bcRh7rN56Twhh/kmt4=" 145 - [mod."github.com/yuin/goldmark-highlighting/v2"] 146 - version = "v2.0.0-20230729083705-37449abec8cc" 147 - hash = "sha256-HpiwU7jIeDUAg2zOpTIiviQir8dpRPuXYh2nqFFccpg=" 148 - [mod."golang.org/x/crypto"] 149 - version = "v0.18.0" 150 - hash = "sha256-BuMVUxOIyfLo8MOhqYt+uQ8NDN6P2KdblKyfPxINzQ4=" 151 - [mod."golang.org/x/exp"] 152 - version = "v0.0.0-20240119083558-1b970713d09a" 153 - hash = "sha256-JQ3JLywTjgboNhs12blhOkS3ty7m8sUa/zaWv1k/X28=" 154 - [mod."golang.org/x/mod"] 155 - version = "v0.14.0" 156 - hash = "sha256-sx3hWp5l99DBfIrn821ohfoBwvaITSHMWbzPvX0btLM=" 157 - [mod."golang.org/x/net"] 158 - version = "v0.20.0" 159 - hash = "sha256-PCttIsWSBQd6fDXL49jepszUAMLnAGAKR//5EDO3XDk=" 160 - [mod."golang.org/x/sync"] 161 - version = "v0.6.0" 162 - hash = "sha256-LLims/wjDZtIqlYCVHREewcUOX4hwRwplEuZKPOJ/HI=" 163 - [mod."golang.org/x/sys"] 164 - version = "v0.16.0" 165 - hash = "sha256-ZkGclbp2S7NQYhbuGji6XokCn2Qi1BJy8dwyAOTV8sY=" 166 - [mod."golang.org/x/term"] 167 - version = "v0.16.0" 168 - hash = "sha256-9qlHcsCI1sa7ZI4Q+fJbOp3mG5Y+uV16e+pGmG+MQe0=" 169 - [mod."golang.org/x/text"] 170 - version = "v0.14.0" 171 - hash = "sha256-yh3B0tom1RfzQBf1RNmfdNWF1PtiqxV41jW1GVS6JAg=" 172 - [mod."golang.org/x/tools"] 173 - version = "v0.17.0" 174 - hash = "sha256-CxuHfKKtUkn3VjA7D9WQjzvV1EUbyI/xMNhb5CxO6IQ=" 175 - [mod."gopkg.in/warnings.v0"] 176 - version = "v0.1.2" 177 - hash = "sha256-ATVL9yEmgYbkJ1DkltDGRn/auGAjqGOfjQyBYyUo8s8=" 178 - [mod."gopkg.in/yaml.v2"] 179 - version = "v2.4.0" 180 - hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0="
+20 -2
internal/git/git.go
··· 98 98 } 99 99 } 100 100 101 - fis := make([]FileInfo, 0) 101 + fis := make([]FileInfo, 0, len(t.Entries)) 102 102 for _, entry := range t.Entries { 103 103 fm, err := entry.Mode.ToOSFileMode() 104 104 if err != nil { ··· 118 118 sort.Slice(fis, func(i, j int) bool { 119 119 fi1 := fis[i] 120 120 fi2 := fis[j] 121 - return (fi1.IsDir && !fi2.IsDir) || fi1.Name() < fi2.Name() 121 + if fi1.IsDir != fi2.IsDir { 122 + return fi1.IsDir 123 + } 124 + return fi1.Name() < fi2.Name() 122 125 }) 123 126 124 127 return fis, nil 128 + } 129 + 130 + // GetCommitFromRef returns the commit object for a given ref 131 + func (r Repo) GetCommitFromRef(ref string) (*object.Commit, error) { 132 + g, err := r.Git() 133 + if err != nil { 134 + return nil, err 135 + } 136 + 137 + hash, err := g.ResolveRevision(plumbing.Revision(ref)) 138 + if err != nil { 139 + return nil, err 140 + } 141 + 142 + return g.CommitObject(*hash) 125 143 } 126 144 127 145 // FileContent returns the content of a file in the git tree at a given ref/rev
+276
internal/git/git_test.go
··· 1 + package git_test 2 + 3 + import ( 4 + "path/filepath" 5 + "testing" 6 + "time" 7 + 8 + "github.com/alecthomas/assert/v2" 9 + "github.com/go-git/go-git/v5/plumbing/protocol/packp" 10 + "go.jolheiser.com/ugit/internal/git" 11 + ) 12 + 13 + func TestEnsureRepo(t *testing.T) { 14 + tmp := t.TempDir() 15 + 16 + ok, err := git.PathExists(filepath.Join(tmp, "test")) 17 + assert.False(t, ok, "repo should not exist yet") 18 + assert.NoError(t, err, "PathExists should not error when repo doesn't exist") 19 + 20 + err = git.EnsureRepo(tmp, "test") 21 + assert.NoError(t, err, "repo should be created") 22 + 23 + ok, err = git.PathExists(filepath.Join(tmp, "test")) 24 + assert.True(t, ok, "repo should exist") 25 + assert.NoError(t, err, "EnsureRepo should not error when path exists") 26 + 27 + err = git.EnsureRepo(tmp, "test") 28 + assert.NoError(t, err, "repo should already exist") 29 + } 30 + 31 + func TestRepo(t *testing.T) { 32 + tmp := t.TempDir() 33 + err := git.EnsureRepo(tmp, "test.git") 34 + assert.NoError(t, err, "should create repo") 35 + 36 + repo, err := git.NewRepo(tmp, "test") 37 + assert.NoError(t, err, "should init new repo") 38 + assert.True(t, repo.Meta.Private, "repo should default to private") 39 + 40 + repo.Meta.Private = false 41 + err = repo.SaveMeta() 42 + assert.NoError(t, err, "should save repo meta") 43 + 44 + repo, err = git.NewRepo(tmp, "test") 45 + assert.NoError(t, err, "should not error when getting existing repo") 46 + assert.False(t, repo.Meta.Private, "repo should be public after saving meta") 47 + } 48 + 49 + func TestPathExists(t *testing.T) { 50 + tmp := t.TempDir() 51 + exists, err := git.PathExists(tmp) 52 + assert.NoError(t, err) 53 + assert.True(t, exists) 54 + 55 + doesNotExist := filepath.Join(tmp, "does-not-exist") 56 + exists, err = git.PathExists(doesNotExist) 57 + assert.NoError(t, err) 58 + assert.False(t, exists) 59 + } 60 + 61 + func TestRepoMetaUpdate(t *testing.T) { 62 + original := git.RepoMeta{ 63 + Description: "Original description", 64 + Private: true, 65 + Tags: git.TagSet{"tag1": struct{}{}, "tag2": struct{}{}}, 66 + } 67 + 68 + update := git.RepoMeta{ 69 + Description: "Updated description", 70 + Private: false, 71 + Tags: git.TagSet{"tag3": struct{}{}}, 72 + } 73 + 74 + err := original.Update(update) 75 + assert.NoError(t, err) 76 + 77 + assert.Equal(t, "Updated description", original.Description) 78 + assert.False(t, original.Private) 79 + assert.Equal(t, []string{"tag1", "tag2", "tag3"}, original.Tags.Slice()) 80 + } 81 + 82 + func TestFileInfoName(t *testing.T) { 83 + testCases := []struct { 84 + path string 85 + expected string 86 + }{ 87 + {path: "file.txt", expected: "file.txt"}, 88 + {path: "dir/file.txt", expected: "file.txt"}, 89 + {path: "nested/path/to/file.go", expected: "file.go"}, 90 + {path: "README.md", expected: "README.md"}, 91 + } 92 + 93 + for _, tc := range testCases { 94 + t.Run(tc.path, func(t *testing.T) { 95 + fi := git.FileInfo{Path: tc.path} 96 + assert.Equal(t, tc.expected, fi.Name()) 97 + }) 98 + } 99 + } 100 + 101 + func TestCommitSummaryAndDetails(t *testing.T) { 102 + testCases := []struct { 103 + message string 104 + expectedSummary string 105 + expectedDetails string 106 + }{ 107 + { 108 + message: "Simple commit message", 109 + expectedSummary: "Simple commit message", 110 + expectedDetails: "", 111 + }, 112 + { 113 + message: "Add feature X\n\nThis commit adds feature X\nWith multiple details\nAcross multiple lines", 114 + expectedSummary: "Add feature X", 115 + expectedDetails: "\nThis commit adds feature X\nWith multiple details\nAcross multiple lines", 116 + }, 117 + { 118 + message: "Fix bug\n\nDetailed explanation", 119 + expectedSummary: "Fix bug", 120 + expectedDetails: "\nDetailed explanation", 121 + }, 122 + } 123 + 124 + for _, tc := range testCases { 125 + t.Run(tc.message, func(t *testing.T) { 126 + commit := git.Commit{ 127 + SHA: "abcdef1234567890", 128 + Message: tc.message, 129 + Signature: "", 130 + Author: "Test User", 131 + Email: "test@example.com", 132 + When: time.Now(), 133 + } 134 + 135 + assert.Equal(t, tc.expectedSummary, commit.Summary()) 136 + assert.Equal(t, tc.expectedDetails, commit.Details()) 137 + }) 138 + } 139 + } 140 + 141 + func TestCommitShort(t *testing.T) { 142 + commit := git.Commit{ 143 + SHA: "abcdef1234567890abcdef1234567890", 144 + } 145 + 146 + assert.Equal(t, "abcdef12", commit.Short()) 147 + } 148 + 149 + func TestCommitFilePath(t *testing.T) { 150 + testCases := []struct { 151 + name string 152 + fromPath string 153 + toPath string 154 + expected string 155 + }{ 156 + { 157 + name: "to path preferred", 158 + fromPath: "old/path.txt", 159 + toPath: "new/path.txt", 160 + expected: "new/path.txt", 161 + }, 162 + { 163 + name: "fallback to from path", 164 + fromPath: "deleted/file.txt", 165 + toPath: "", 166 + expected: "deleted/file.txt", 167 + }, 168 + { 169 + name: "both paths empty", 170 + fromPath: "", 171 + toPath: "", 172 + expected: "", 173 + }, 174 + } 175 + 176 + for _, tc := range testCases { 177 + t.Run(tc.name, func(t *testing.T) { 178 + cf := git.CommitFile{ 179 + From: git.CommitFileEntry{Path: tc.fromPath}, 180 + To: git.CommitFileEntry{Path: tc.toPath}, 181 + } 182 + assert.Equal(t, tc.expected, cf.Path()) 183 + }) 184 + } 185 + } 186 + 187 + func TestRepoName(t *testing.T) { 188 + tmp := t.TempDir() 189 + 190 + repoName := "testrepo" 191 + err := git.EnsureRepo(tmp, repoName+".git") 192 + assert.NoError(t, err) 193 + 194 + repo, err := git.NewRepo(tmp, repoName) 195 + assert.NoError(t, err) 196 + assert.Equal(t, repoName, repo.Name()) 197 + 198 + repoName2 := "test-repo-with-hyphens" 199 + err = git.EnsureRepo(tmp, repoName2+".git") 200 + assert.NoError(t, err) 201 + 202 + repo2, err := git.NewRepo(tmp, repoName2) 203 + assert.NoError(t, err) 204 + assert.Equal(t, repoName2, repo2.Name()) 205 + } 206 + 207 + func TestHandlePushOptions(t *testing.T) { 208 + tmp := t.TempDir() 209 + err := git.EnsureRepo(tmp, "test.git") 210 + assert.NoError(t, err) 211 + 212 + repo, err := git.NewRepo(tmp, "test") 213 + assert.NoError(t, err) 214 + 215 + opts := []*packp.Option{ 216 + {Key: "description", Value: "New description"}, 217 + } 218 + err = git.HandlePushOptions(repo, opts) 219 + assert.NoError(t, err) 220 + assert.Equal(t, "New description", repo.Meta.Description) 221 + 222 + opts = []*packp.Option{ 223 + {Key: "private", Value: "false"}, 224 + } 225 + err = git.HandlePushOptions(repo, opts) 226 + assert.NoError(t, err) 227 + assert.False(t, repo.Meta.Private) 228 + 229 + repo.Meta.Private = true 230 + opts = []*packp.Option{ 231 + {Key: "private", Value: "invalid"}, 232 + } 233 + err = git.HandlePushOptions(repo, opts) 234 + assert.NoError(t, err) 235 + assert.True(t, repo.Meta.Private) 236 + 237 + opts = []*packp.Option{ 238 + {Key: "tags", Value: "tag1,tag2"}, 239 + } 240 + err = git.HandlePushOptions(repo, opts) 241 + assert.NoError(t, err) 242 + 243 + opts = []*packp.Option{ 244 + {Key: "description", Value: "Combined update"}, 245 + {Key: "private", Value: "true"}, 246 + } 247 + err = git.HandlePushOptions(repo, opts) 248 + assert.NoError(t, err) 249 + assert.Equal(t, "Combined update", repo.Meta.Description) 250 + assert.True(t, repo.Meta.Private) 251 + } 252 + 253 + func TestRepoPath(t *testing.T) { 254 + tmp := t.TempDir() 255 + err := git.EnsureRepo(tmp, "test.git") 256 + assert.NoError(t, err) 257 + 258 + repo, err := git.NewRepo(tmp, "test") 259 + assert.NoError(t, err) 260 + 261 + expected := filepath.Join(tmp, "test.git") 262 + assert.Equal(t, expected, repo.Path()) 263 + } 264 + 265 + func TestEnsureJSONFile(t *testing.T) { 266 + tmp := t.TempDir() 267 + err := git.EnsureRepo(tmp, "test.git") 268 + assert.NoError(t, err) 269 + 270 + repo, err := git.NewRepo(tmp, "test") 271 + assert.NoError(t, err) 272 + 273 + assert.True(t, repo.Meta.Private, "default repo should be private") 274 + assert.Equal(t, "", repo.Meta.Description, "default description should be empty") 275 + assert.Equal(t, 0, len(repo.Meta.Tags), "default tags should be empty") 276 + }
+4 -2
internal/git/grep.go
··· 18 18 19 19 // Grep performs a naive "code search" via git grep 20 20 func (r Repo) Grep(search string) ([]GrepResult, error) { 21 - // Plain-text search only 22 - re, err := regexp.Compile(regexp.QuoteMeta(search)) 21 + if after, ok := strings.CutPrefix(search, "="); ok { 22 + search = regexp.QuoteMeta(after) 23 + } 24 + re, err := regexp.Compile(search) 23 25 if err != nil { 24 26 return nil, err 25 27 }
+66 -4
internal/git/meta.go
··· 3 3 import ( 4 4 "encoding/json" 5 5 "errors" 6 + "fmt" 6 7 "io/fs" 7 8 "os" 8 9 "path/filepath" 10 + "slices" 9 11 ) 10 12 11 13 // RepoMeta is the meta information a Repo can have 12 14 type RepoMeta struct { 13 - Description string `json:"description"` 14 - Private bool `json:"private"` 15 - Tags []string `json:"tags"` 15 + Description string `json:"description"` 16 + Private bool `json:"private"` 17 + Tags TagSet `json:"tags"` 18 + } 19 + 20 + // TagSet is a Set of tags 21 + type TagSet map[string]struct{} 22 + 23 + // Add adds a tag to the set 24 + func (t TagSet) Add(tag string) { 25 + t[tag] = struct{}{} 26 + } 27 + 28 + // Remove removes a tag from the set 29 + func (t TagSet) Remove(tag string) { 30 + delete(t, tag) 31 + } 32 + 33 + // Contains checks if a tag is in the set 34 + func (t TagSet) Contains(tag string) bool { 35 + _, ok := t[tag] 36 + return ok 37 + } 38 + 39 + // Slice returns the set as a (sorted) slice 40 + func (t TagSet) Slice() []string { 41 + s := make([]string, 0, len(t)) 42 + for k := range t { 43 + s = append(s, k) 44 + } 45 + slices.Sort(s) 46 + return s 47 + } 48 + 49 + // MarshalJSON implements [json.Marshaler] 50 + func (t TagSet) MarshalJSON() ([]byte, error) { 51 + return json.Marshal(t.Slice()) 52 + } 53 + 54 + // UnmarshalJSON implements [json.Unmarshaler] 55 + func (t *TagSet) UnmarshalJSON(b []byte) error { 56 + if *t == nil { 57 + ts := make(TagSet) 58 + t = &ts 59 + } 60 + var s []string 61 + if err := json.Unmarshal(b, &s); err != nil { 62 + return err 63 + } 64 + for _, ss := range s { 65 + t.Add(ss) 66 + } 67 + return nil 16 68 } 17 69 18 70 // Update updates meta given another RepoMeta ··· 46 98 return json.NewEncoder(fi).Encode(r.Meta) 47 99 } 48 100 101 + var defaultMeta = func() []byte { 102 + b, err := json.Marshal(RepoMeta{ 103 + Private: true, 104 + }) 105 + if err != nil { 106 + panic(fmt.Sprintf("could not init default meta: %v", err)) 107 + } 108 + return b 109 + }() 110 + 49 111 func ensureJSONFile(path string) error { 50 112 _, err := os.Stat(path) 51 113 if err == nil { ··· 59 121 return err 60 122 } 61 123 defer fi.Close() 62 - if _, err := fi.WriteString(`{"private":true}`); err != nil { 124 + if _, err := fi.Write(defaultMeta); err != nil { 63 125 return err 64 126 } 65 127 return nil
+53
internal/git/meta_test.go
··· 1 + package git 2 + 3 + import ( 4 + "encoding/json" 5 + "testing" 6 + 7 + "github.com/alecthomas/assert/v2" 8 + ) 9 + 10 + func TestTagSet(t *testing.T) { 11 + set := make(TagSet) 12 + assert.Equal(t, 0, len(set)) 13 + assert.Equal(t, 0, len(set.Slice())) 14 + 15 + set.Add("foo") 16 + assert.Equal(t, 1, len(set)) 17 + assert.Equal(t, 1, len(set.Slice())) 18 + assert.True(t, set.Contains("foo")) 19 + 20 + set.Add("bar") 21 + assert.Equal(t, 2, len(set)) 22 + assert.Equal(t, 2, len(set.Slice())) 23 + assert.True(t, set.Contains("foo")) 24 + assert.True(t, set.Contains("bar")) 25 + 26 + set.Add("bar") 27 + assert.Equal(t, 2, len(set)) 28 + assert.Equal(t, 2, len(set.Slice())) 29 + assert.True(t, set.Contains("foo")) 30 + assert.True(t, set.Contains("bar")) 31 + 32 + set.Remove("foo") 33 + assert.Equal(t, 1, len(set)) 34 + assert.Equal(t, 1, len(set.Slice())) 35 + assert.False(t, set.Contains("foo")) 36 + assert.True(t, set.Contains("bar")) 37 + 38 + set.Add("foo") 39 + set.Add("baz") 40 + j, err := json.Marshal(set) 41 + assert.NoError(t, err) 42 + assert.Equal(t, `["bar","baz","foo"]`, string(j)) 43 + 44 + set = make(TagSet) 45 + b := []byte(`["foo","bar","baz"]`) 46 + err = json.Unmarshal(b, &set) 47 + assert.NoError(t, err) 48 + assert.Equal(t, 3, len(set)) 49 + assert.Equal(t, 3, len(set.Slice())) 50 + assert.True(t, set.Contains("foo")) 51 + assert.True(t, set.Contains("bar")) 52 + assert.True(t, set.Contains("baz")) 53 + }
+5 -9
internal/git/protocol.go
··· 58 58 remove = true 59 59 tagValue = strings.TrimPrefix(tagValue, "-") 60 60 } 61 - for idx, tag := range repo.Meta.Tags { 62 - if strings.EqualFold(tag, tagValue) { 63 - if remove { 64 - repo.Meta.Tags = append(repo.Meta.Tags[:idx], repo.Meta.Tags[idx+1:]...) 65 - } else { 66 - repo.Meta.Tags = append(repo.Meta.Tags, strings.ToLower(tagValue)) 67 - } 68 - break 69 - } 61 + tagValue = strings.ToLower(tagValue) 62 + if remove { 63 + repo.Meta.Tags.Remove(tagValue) 64 + } else { 65 + repo.Meta.Tags.Add(tagValue) 70 66 } 71 67 } 72 68 }
-1
internal/git/protocol_git.go
··· 58 58 cmd.Env = append(os.Environ(), fmt.Sprintf("UGIT_REPODIR=%s", repoDir), "GIT_PROTOCOL=version=2") 59 59 cmd.Stdin = ctx 60 60 cmd.Stdout = ctx 61 - fmt.Println(cmd.Env, cmd.String()) 62 61 63 62 return cmd.Run() 64 63 }
+11
internal/git/repo.go
··· 57 57 if err := json.NewDecoder(fi).Decode(&r.Meta); err != nil { 58 58 return nil, err 59 59 } 60 + if r.Meta.Tags == nil { 61 + r.Meta.Tags = make(TagSet) 62 + } 60 63 61 64 return r, nil 62 65 } ··· 132 135 type CommitFileEntry struct { 133 136 Path string 134 137 Commit string 138 + } 139 + 140 + // Path returns either the To or From path, in order of preference 141 + func (c CommitFile) Path() string { 142 + if c.To.Path != "" { 143 + return c.To.Path 144 + } 145 + return c.From.Path 135 146 } 136 147 137 148 // Short returns the first eight characters of the SHA
+3 -1
internal/html/base.templ
··· 18 18 <meta property="og:description" content={ bc.Description }/> 19 19 </head> 20 20 <body class="latte dark:mocha bg-base/50 dark:bg-base/95 max-w-7xl mx-5 sm:mx-auto my-10"> 21 - <h2 class="text-text text-xl mb-3"><a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href="/">Home</a></h2> 21 + <h2 class="text-text text-xl mb-3"> 22 + <a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href="/">Home</a> 23 + </h2> 22 24 { children... } 23 25 </body> 24 26 </html>
+22 -17
internal/html/base_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 type BaseContext struct { 14 12 Title string ··· 16 14 } 17 15 18 16 func base(bc BaseContext) templ.Component { 19 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 20 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 17 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 18 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 19 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 20 + return templ_7745c5c3_CtxErr 21 + } 22 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 21 23 if !templ_7745c5c3_IsBuffer { 22 - templ_7745c5c3_Buffer = templ.GetBuffer() 23 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 24 + defer func() { 25 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 26 + if templ_7745c5c3_Err == nil { 27 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 28 + } 29 + }() 24 30 } 25 31 ctx = templ.InitializeContext(ctx) 26 32 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 28 34 templ_7745c5c3_Var1 = templ.NopComponent 29 35 } 30 36 ctx = templ.ClearChildren(ctx) 31 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>") 37 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>") 32 38 if templ_7745c5c3_Err != nil { 33 39 return templ_7745c5c3_Err 34 40 } ··· 41 47 if templ_7745c5c3_Err != nil { 42 48 return templ_7745c5c3_Err 43 49 } 44 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title><link rel=\"icon\" href=\"/_/favicon.svg\"><link rel=\"stylesheet\" href=\"/_/tailwind.css\"><meta property=\"og:title\" content=\"") 50 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title><link rel=\"icon\" href=\"/_/favicon.svg\"><link rel=\"stylesheet\" href=\"/_/tailwind.css\"><meta property=\"og:title\" content=\"") 45 51 if templ_7745c5c3_Err != nil { 46 52 return templ_7745c5c3_Err 47 53 } ··· 54 60 if templ_7745c5c3_Err != nil { 55 61 return templ_7745c5c3_Err 56 62 } 57 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta property=\"og:description\" content=\"") 63 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><meta property=\"og:description\" content=\"") 58 64 if templ_7745c5c3_Err != nil { 59 65 return templ_7745c5c3_Err 60 66 } ··· 67 73 if templ_7745c5c3_Err != nil { 68 74 return templ_7745c5c3_Err 69 75 } 70 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></head><body class=\"latte dark:mocha bg-base/50 dark:bg-base/95 max-w-7xl mx-5 sm:mx-auto my-10\"><h2 class=\"text-text text-xl mb-3\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"/\">Home</a></h2>") 76 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"></head><body class=\"latte dark:mocha bg-base/50 dark:bg-base/95 max-w-7xl mx-5 sm:mx-auto my-10\"><h2 class=\"text-text text-xl mb-3\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"/\">Home</a></h2>") 71 77 if templ_7745c5c3_Err != nil { 72 78 return templ_7745c5c3_Err 73 79 } ··· 75 81 if templ_7745c5c3_Err != nil { 76 82 return templ_7745c5c3_Err 77 83 } 78 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>") 84 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</body></html>") 79 85 if templ_7745c5c3_Err != nil { 80 86 return templ_7745c5c3_Err 81 87 } 82 - if !templ_7745c5c3_IsBuffer { 83 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 84 - } 85 - return templ_7745c5c3_Err 88 + return nil 86 89 }) 87 90 } 91 + 92 + var _ = templruntime.GeneratedTemplate
-4
internal/html/generate.css
··· 46 46 47 47 .code>.chroma { 48 48 @apply text-sm p-3 rounded overflow-scroll; 49 - } 50 - 51 - .chroma .line { 52 - @apply overflow-scroll 53 49 }
+7 -5
internal/html/generate.go
··· 12 12 13 13 "go.jolheiser.com/ugit/internal/html/markup" 14 14 15 + "github.com/alecthomas/chroma/v2/formatters/html" 15 16 "github.com/alecthomas/chroma/v2/styles" 16 17 ) 17 18 ··· 25 26 otherCSS string 26 27 ) 27 28 28 - //go:generate templ generate 29 + //go:generate go tool templ generate 29 30 //go:generate go run generate.go 30 31 func main() { 31 32 if err := tailwind(); err != nil { ··· 33 34 } 34 35 } 35 36 36 - // Generate tailwind code from templ templates and combine with other misc CSS 37 + // Generate tailwind code from templates and combine with other misc CSS 37 38 func tailwind() error { 38 39 fmt.Println("generating tailwind...") 39 40 ··· 47 48 } 48 49 49 50 fmt.Println("generating chroma styles...") 51 + formatter := html.New(markup.Options("")...) 50 52 51 53 latte := styles.Get("catppuccin-latte") 52 - if err := markup.Formatter.WriteCSS(tmp, latte); err != nil { 54 + if err := formatter.WriteCSS(tmp, latte); err != nil { 53 55 return err 54 56 } 55 57 56 58 tmp.WriteString("@media (prefers-color-scheme: dark) {") 57 59 mocha := styles.Get("catppuccin-mocha") 58 - if err := markup.Formatter.WriteCSS(tmp, mocha); err != nil { 60 + if err := formatter.WriteCSS(tmp, mocha); err != nil { 59 61 return err 60 62 } 61 63 tmp.WriteString("}") ··· 71 73 defer styles.Close() 72 74 73 75 var buf bytes.Buffer 74 - cmd := exec.Command("tailwind-ctp", "-i", tmp.Name(), "--minify") 76 + cmd := exec.Command("tailwind-ctp_3", "-i", tmp.Name(), "--minify") 75 77 cmd.Stdout = &buf 76 78 if err := cmd.Run(); err != nil { 77 79 return err
+28 -8
internal/html/index.templ
··· 1 1 package html 2 2 3 - import "go.jolheiser.com/ugit/internal/git" 4 - import "github.com/dustin/go-humanize" 5 - import "go.jolheiser.com/ugit/assets" 3 + import ( 4 + "fmt" 5 + "github.com/dustin/go-humanize" 6 + "go.jolheiser.com/ugit/assets" 7 + "go.jolheiser.com/ugit/internal/git" 8 + ) 6 9 7 10 type IndexContext struct { 8 11 BaseContext ··· 21 24 URL string 22 25 } 23 26 24 - func lastCommit(repo *git.Repo, human bool) string { 27 + func lastCommitTime(repo *git.Repo, human bool) string { 25 28 c, err := repo.LastCommit() 26 29 if err != nil { 27 30 return "" ··· 32 35 return c.When.Format("01/02/2006 03:04:05 PM") 33 36 } 34 37 38 + func lastCommit(repo *git.Repo) *git.Commit { 39 + c, err := repo.LastCommit() 40 + if err != nil { 41 + return nil 42 + } 43 + return &c 44 + } 45 + 35 46 templ Index(ic IndexContext) { 36 47 @base(ic.BaseContext) { 37 48 <header> ··· 62 73 </div> 63 74 } 64 75 </div> 65 - <div class="grid sm:grid-cols-8 gap-2 mt-5"> 76 + <div class="grid sm:grid-cols-10 gap-2 mt-5"> 66 77 for _, repo := range ic.Repos { 78 + {{ commit := lastCommit(repo) }} 67 79 <div class="sm:col-span-2 text-blue dark:text-lavender"><a class="underline decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid" href={ templ.URL("/" + repo.Name()) }>{ repo.Name() }</a></div> 68 - <div class="sm:col-span-4 text-subtext0">{ repo.Meta.Description }</div> 80 + <div class="sm:col-span-3 text-subtext0">{ repo.Meta.Description }</div> 81 + <div class="sm:col-span-3 text-subtext0"> 82 + if commit != nil { 83 + <div title={ commit.Message }> 84 + <a class="underline text-blue dark:text-lavender decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid" href={ fmt.Sprintf("/%s/commit/%s", repo.Name(), commit.SHA) }>{ commit.Short() }</a> 85 + { ": " + commit.Summary() } 86 + </div> 87 + } 88 + </div> 69 89 <div class="sm:col-span-1 text-subtext0"> 70 - for _, tag := range repo.Meta.Tags { 90 + for _, tag := range repo.Meta.Tags.Slice() { 71 91 <a href={ templ.SafeURL("?tag=" + tag) } class="rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block">{ tag }</a> 72 92 } 73 93 </div> 74 - <div class="sm:col-span-1 text-text/80 mb-4 sm:mb-0" title={ lastCommit(repo, false) }>{ lastCommit(repo, true) }</div> 94 + <div class="sm:col-span-1 text-text/80 mb-4 sm:mb-0" title={ lastCommitTime(repo, false) }>{ lastCommitTime(repo, true) }</div> 75 95 } 76 96 </div> 77 97 </main>
+175 -77
internal/html/index_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 - import "go.jolheiser.com/ugit/internal/git" 14 - import "github.com/dustin/go-humanize" 15 - import "go.jolheiser.com/ugit/assets" 11 + import ( 12 + "fmt" 13 + "github.com/dustin/go-humanize" 14 + "go.jolheiser.com/ugit/assets" 15 + "go.jolheiser.com/ugit/internal/git" 16 + ) 16 17 17 18 type IndexContext struct { 18 19 BaseContext ··· 31 32 URL string 32 33 } 33 34 34 - func lastCommit(repo *git.Repo, human bool) string { 35 + func lastCommitTime(repo *git.Repo, human bool) string { 35 36 c, err := repo.LastCommit() 36 37 if err != nil { 37 38 return "" ··· 42 43 return c.When.Format("01/02/2006 03:04:05 PM") 43 44 } 44 45 46 + func lastCommit(repo *git.Repo) *git.Commit { 47 + c, err := repo.LastCommit() 48 + if err != nil { 49 + return nil 50 + } 51 + return &c 52 + } 53 + 45 54 func Index(ic IndexContext) templ.Component { 46 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 47 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 55 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 56 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 57 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 58 + return templ_7745c5c3_CtxErr 59 + } 60 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 48 61 if !templ_7745c5c3_IsBuffer { 49 - templ_7745c5c3_Buffer = templ.GetBuffer() 50 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 62 + defer func() { 63 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 64 + if templ_7745c5c3_Err == nil { 65 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 66 + } 67 + }() 51 68 } 52 69 ctx = templ.InitializeContext(ctx) 53 70 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 55 72 templ_7745c5c3_Var1 = templ.NopComponent 56 73 } 57 74 ctx = templ.ClearChildren(ctx) 58 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 59 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 75 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 76 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 77 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 60 78 if !templ_7745c5c3_IsBuffer { 61 - templ_7745c5c3_Buffer = templ.GetBuffer() 62 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 79 + defer func() { 80 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 81 + if templ_7745c5c3_Err == nil { 82 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 83 + } 84 + }() 63 85 } 64 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<header><h1 class=\"text-text text-xl font-bold\">") 86 + ctx = templ.InitializeContext(ctx) 87 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<header><h1 class=\"text-text text-xl font-bold\">") 65 88 if templ_7745c5c3_Err != nil { 66 89 return templ_7745c5c3_Err 67 90 } 68 91 var templ_7745c5c3_Var3 string 69 92 templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(ic.Title) 70 93 if templ_7745c5c3_Err != nil { 71 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 38, Col: 53} 94 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 49, Col: 53} 72 95 } 73 96 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 74 97 if templ_7745c5c3_Err != nil { 75 98 return templ_7745c5c3_Err 76 99 } 77 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><h2 class=\"text-subtext1 text-lg\">") 100 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</h1><h2 class=\"text-subtext1 text-lg\">") 78 101 if templ_7745c5c3_Err != nil { 79 102 return templ_7745c5c3_Err 80 103 } 81 104 var templ_7745c5c3_Var4 string 82 105 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(ic.Description) 83 106 if templ_7745c5c3_Err != nil { 84 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 39, Col: 53} 107 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 50, Col: 53} 85 108 } 86 109 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 87 110 if templ_7745c5c3_Err != nil { 88 111 return templ_7745c5c3_Err 89 112 } 90 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2></header><main class=\"mt-5\"><div class=\"grid grid-cols-1 sm:grid-cols-8\">") 113 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h2></header><main class=\"mt-5\"><div class=\"grid grid-cols-1 sm:grid-cols-8\">") 91 114 if templ_7745c5c3_Err != nil { 92 115 return templ_7745c5c3_Err 93 116 } 94 117 if ic.Profile.Username != "" { 95 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-mauve\">") 118 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"text-mauve\">") 96 119 if templ_7745c5c3_Err != nil { 97 120 return templ_7745c5c3_Err 98 121 } 99 122 var templ_7745c5c3_Var5 string 100 123 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(`@` + ic.Profile.Username) 101 124 if templ_7745c5c3_Err != nil { 102 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 44, Col: 56} 125 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 55, Col: 56} 103 126 } 104 127 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 105 128 if templ_7745c5c3_Err != nil { 106 129 return templ_7745c5c3_Err 107 130 } 108 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 131 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div>") 109 132 if templ_7745c5c3_Err != nil { 110 133 return templ_7745c5c3_Err 111 134 } 112 135 } 113 136 if ic.Profile.Email != "" { 114 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-mauve col-span-2\"><div class=\"w-5 h-5 stroke-mauve inline-block mr-1 align-middle\">") 137 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"text-mauve col-span-2\"><div class=\"w-5 h-5 stroke-mauve inline-block mr-1 align-middle\">") 115 138 if templ_7745c5c3_Err != nil { 116 139 return templ_7745c5c3_Err 117 140 } ··· 119 142 if templ_7745c5c3_Err != nil { 120 143 return templ_7745c5c3_Err 121 144 } 122 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><a class=\"underline decoration-mauve/50 decoration-dashed hover:decoration-solid\" href=\"") 145 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div><a class=\"underline decoration-mauve/50 decoration-dashed hover:decoration-solid\" href=\"") 123 146 if templ_7745c5c3_Err != nil { 124 147 return templ_7745c5c3_Err 125 148 } 126 - var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL("mailto:" + ic.Profile.Email) 127 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6))) 149 + var templ_7745c5c3_Var6 templ.SafeURL 150 + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("mailto:" + ic.Profile.Email)) 151 + if templ_7745c5c3_Err != nil { 152 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 62, Col: 138} 153 + } 154 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) 128 155 if templ_7745c5c3_Err != nil { 129 156 return templ_7745c5c3_Err 130 157 } 131 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 158 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">") 132 159 if templ_7745c5c3_Err != nil { 133 160 return templ_7745c5c3_Err 134 161 } 135 162 var templ_7745c5c3_Var7 string 136 163 templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(ic.Profile.Email) 137 164 if templ_7745c5c3_Err != nil { 138 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 51, Col: 159} 165 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 62, Col: 159} 139 166 } 140 167 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) 141 168 if templ_7745c5c3_Err != nil { 142 169 return templ_7745c5c3_Err 143 170 } 144 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div>") 171 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</a></div>") 145 172 if templ_7745c5c3_Err != nil { 146 173 return templ_7745c5c3_Err 147 174 } 148 175 } 149 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"grid grid-cols-1 sm:grid-cols-8\">") 176 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div><div class=\"grid grid-cols-1 sm:grid-cols-8\">") 150 177 if templ_7745c5c3_Err != nil { 151 178 return templ_7745c5c3_Err 152 179 } 153 180 for _, link := range ic.Profile.Links { 154 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-mauve\"><div class=\"w-5 h-5 stroke-mauve inline-block mr-1 align-middle\">") 181 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"text-mauve\"><div class=\"w-5 h-5 stroke-mauve inline-block mr-1 align-middle\">") 155 182 if templ_7745c5c3_Err != nil { 156 183 return templ_7745c5c3_Err 157 184 } ··· 159 186 if templ_7745c5c3_Err != nil { 160 187 return templ_7745c5c3_Err 161 188 } 162 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><a class=\"underline decoration-mauve/50 decoration-dashed hover:decoration-solid\" rel=\"me\" href=\"") 189 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div><a class=\"underline decoration-mauve/50 decoration-dashed hover:decoration-solid\" rel=\"me\" href=\"") 163 190 if templ_7745c5c3_Err != nil { 164 191 return templ_7745c5c3_Err 165 192 } 166 - var templ_7745c5c3_Var8 templ.SafeURL = templ.SafeURL(link.URL) 167 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8))) 193 + var templ_7745c5c3_Var8 templ.SafeURL 194 + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(link.URL)) 195 + if templ_7745c5c3_Err != nil { 196 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 72, Col: 127} 197 + } 198 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 168 199 if templ_7745c5c3_Err != nil { 169 200 return templ_7745c5c3_Err 170 201 } 171 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 202 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">") 172 203 if templ_7745c5c3_Err != nil { 173 204 return templ_7745c5c3_Err 174 205 } 175 206 var templ_7745c5c3_Var9 string 176 207 templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(link.Name) 177 208 if templ_7745c5c3_Err != nil { 178 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 61, Col: 141} 209 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 72, Col: 141} 179 210 } 180 211 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) 181 212 if templ_7745c5c3_Err != nil { 182 213 return templ_7745c5c3_Err 183 214 } 184 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div>") 215 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</a></div>") 185 216 if templ_7745c5c3_Err != nil { 186 217 return templ_7745c5c3_Err 187 218 } 188 219 } 189 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"grid sm:grid-cols-8 gap-2 mt-5\">") 220 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div><div class=\"grid sm:grid-cols-10 gap-2 mt-5\">") 190 221 if templ_7745c5c3_Err != nil { 191 222 return templ_7745c5c3_Err 192 223 } 193 224 for _, repo := range ic.Repos { 194 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"sm:col-span-2 text-blue dark:text-lavender\"><a class=\"underline decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid\" href=\"") 225 + commit := lastCommit(repo) 226 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div class=\"sm:col-span-2 text-blue dark:text-lavender\"><a class=\"underline decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid\" href=\"") 195 227 if templ_7745c5c3_Err != nil { 196 228 return templ_7745c5c3_Err 197 229 } 198 - var templ_7745c5c3_Var10 templ.SafeURL = templ.URL("/" + repo.Name()) 199 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var10))) 230 + var templ_7745c5c3_Var10 templ.SafeURL 231 + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(templ.URL("/" + repo.Name())) 232 + if templ_7745c5c3_Err != nil { 233 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 79, Col: 205} 234 + } 235 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) 200 236 if templ_7745c5c3_Err != nil { 201 237 return templ_7745c5c3_Err 202 238 } 203 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 239 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\">") 204 240 if templ_7745c5c3_Err != nil { 205 241 return templ_7745c5c3_Err 206 242 } 207 243 var templ_7745c5c3_Var11 string 208 244 templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(repo.Name()) 209 245 if templ_7745c5c3_Err != nil { 210 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 67, Col: 221} 246 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 79, Col: 221} 211 247 } 212 248 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) 213 249 if templ_7745c5c3_Err != nil { 214 250 return templ_7745c5c3_Err 215 251 } 216 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"sm:col-span-4 text-subtext0\">") 252 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</a></div><div class=\"sm:col-span-3 text-subtext0\">") 217 253 if templ_7745c5c3_Err != nil { 218 254 return templ_7745c5c3_Err 219 255 } 220 256 var templ_7745c5c3_Var12 string 221 257 templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(repo.Meta.Description) 222 258 if templ_7745c5c3_Err != nil { 223 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 68, Col: 69} 259 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 80, Col: 69} 224 260 } 225 261 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) 226 262 if templ_7745c5c3_Err != nil { 227 263 return templ_7745c5c3_Err 228 264 } 229 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"sm:col-span-1 text-subtext0\">") 265 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div><div class=\"sm:col-span-3 text-subtext0\">") 230 266 if templ_7745c5c3_Err != nil { 231 267 return templ_7745c5c3_Err 232 268 } 233 - for _, tag := range repo.Meta.Tags { 234 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"") 269 + if commit != nil { 270 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div title=\"") 235 271 if templ_7745c5c3_Err != nil { 236 272 return templ_7745c5c3_Err 237 273 } 238 - var templ_7745c5c3_Var13 templ.SafeURL = templ.SafeURL("?tag=" + tag) 239 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var13))) 274 + var templ_7745c5c3_Var13 string 275 + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(commit.Message) 276 + if templ_7745c5c3_Err != nil { 277 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 83, Col: 34} 278 + } 279 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) 240 280 if templ_7745c5c3_Err != nil { 241 281 return templ_7745c5c3_Err 242 282 } 243 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block\">") 283 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"><a class=\"underline text-blue dark:text-lavender decoration-blue/50 dark:decoration-lavender/50 decoration-dashed hover:decoration-solid\" href=\"") 244 284 if templ_7745c5c3_Err != nil { 245 285 return templ_7745c5c3_Err 246 286 } 247 - var templ_7745c5c3_Var14 string 248 - templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(tag) 287 + var templ_7745c5c3_Var14 templ.SafeURL 288 + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/%s/commit/%s", repo.Name(), commit.SHA)) 249 289 if templ_7745c5c3_Err != nil { 250 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 71, Col: 141} 290 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 84, Col: 206} 251 291 } 252 292 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) 253 293 if templ_7745c5c3_Err != nil { 254 294 return templ_7745c5c3_Err 255 295 } 256 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>") 296 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">") 297 + if templ_7745c5c3_Err != nil { 298 + return templ_7745c5c3_Err 299 + } 300 + var templ_7745c5c3_Var15 string 301 + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(commit.Short()) 302 + if templ_7745c5c3_Err != nil { 303 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 84, Col: 225} 304 + } 305 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) 306 + if templ_7745c5c3_Err != nil { 307 + return templ_7745c5c3_Err 308 + } 309 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</a> ") 310 + if templ_7745c5c3_Err != nil { 311 + return templ_7745c5c3_Err 312 + } 313 + var templ_7745c5c3_Var16 string 314 + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(": " + commit.Summary()) 315 + if templ_7745c5c3_Err != nil { 316 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 85, Col: 33} 317 + } 318 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) 319 + if templ_7745c5c3_Err != nil { 320 + return templ_7745c5c3_Err 321 + } 322 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div>") 257 323 if templ_7745c5c3_Err != nil { 258 324 return templ_7745c5c3_Err 259 325 } 260 326 } 261 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"sm:col-span-1 text-text/80 mb-4 sm:mb-0\" title=\"") 327 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div><div class=\"sm:col-span-1 text-subtext0\">") 262 328 if templ_7745c5c3_Err != nil { 263 329 return templ_7745c5c3_Err 264 330 } 265 - var templ_7745c5c3_Var15 string 266 - templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(lastCommit(repo, false)) 331 + for _, tag := range repo.Meta.Tags.Slice() { 332 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a href=\"") 333 + if templ_7745c5c3_Err != nil { 334 + return templ_7745c5c3_Err 335 + } 336 + var templ_7745c5c3_Var17 templ.SafeURL 337 + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("?tag=" + tag)) 338 + if templ_7745c5c3_Err != nil { 339 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 91, Col: 45} 340 + } 341 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) 342 + if templ_7745c5c3_Err != nil { 343 + return templ_7745c5c3_Err 344 + } 345 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block\">") 346 + if templ_7745c5c3_Err != nil { 347 + return templ_7745c5c3_Err 348 + } 349 + var templ_7745c5c3_Var18 string 350 + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(tag) 351 + if templ_7745c5c3_Err != nil { 352 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 91, Col: 141} 353 + } 354 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) 355 + if templ_7745c5c3_Err != nil { 356 + return templ_7745c5c3_Err 357 + } 358 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</a>") 359 + if templ_7745c5c3_Err != nil { 360 + return templ_7745c5c3_Err 361 + } 362 + } 363 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div><div class=\"sm:col-span-1 text-text/80 mb-4 sm:mb-0\" title=\"") 267 364 if templ_7745c5c3_Err != nil { 268 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 74, Col: 89} 365 + return templ_7745c5c3_Err 366 + } 367 + var templ_7745c5c3_Var19 string 368 + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(lastCommitTime(repo, false)) 369 + if templ_7745c5c3_Err != nil { 370 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 94, Col: 93} 269 371 } 270 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) 372 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) 271 373 if templ_7745c5c3_Err != nil { 272 374 return templ_7745c5c3_Err 273 375 } 274 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 376 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\">") 275 377 if templ_7745c5c3_Err != nil { 276 378 return templ_7745c5c3_Err 277 379 } 278 - var templ_7745c5c3_Var16 string 279 - templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(lastCommit(repo, true)) 380 + var templ_7745c5c3_Var20 string 381 + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(lastCommitTime(repo, true)) 280 382 if templ_7745c5c3_Err != nil { 281 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 74, Col: 116} 383 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 94, Col: 124} 282 384 } 283 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) 385 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) 284 386 if templ_7745c5c3_Err != nil { 285 387 return templ_7745c5c3_Err 286 388 } 287 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 389 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</div>") 288 390 if templ_7745c5c3_Err != nil { 289 391 return templ_7745c5c3_Err 290 392 } 291 393 } 292 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></main>") 394 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div></main>") 293 395 if templ_7745c5c3_Err != nil { 294 396 return templ_7745c5c3_Err 295 397 } 296 - if !templ_7745c5c3_IsBuffer { 297 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 298 - } 299 - return templ_7745c5c3_Err 398 + return nil 300 399 }) 301 400 templ_7745c5c3_Err = base(ic.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 302 401 if templ_7745c5c3_Err != nil { 303 402 return templ_7745c5c3_Err 304 403 } 305 - if !templ_7745c5c3_IsBuffer { 306 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 307 - } 308 - return templ_7745c5c3_Err 404 + return nil 309 405 }) 310 406 } 407 + 408 + var _ = templruntime.GeneratedTemplate
+16 -24
internal/html/markup/chroma.go
··· 2 2 3 3 import ( 4 4 "io" 5 + "path/filepath" 5 6 6 7 "github.com/alecthomas/chroma/v2" 7 8 "github.com/alecthomas/chroma/v2/formatters/html" ··· 9 10 "github.com/alecthomas/chroma/v2/styles" 10 11 ) 11 12 12 - var ( 13 - // Formatter is the default formatter 14 - Formatter = html.New( 13 + var customReg = map[string]string{ 14 + ".hujson": "json", 15 + } 16 + 17 + // Options are the default set of formatting options 18 + func Options(linePrefix string) []html.Option { 19 + return []html.Option{ 15 20 html.WithLineNumbers(true), 16 - html.WithLinkableLineNumbers(true, "L"), 21 + html.WithLinkableLineNumbers(true, linePrefix), 17 22 html.WithClasses(true), 18 23 html.LineNumbersInTable(true), 19 - ) 20 - basicFormatter = html.New( 21 - html.WithClasses(true), 22 - ) 23 - // Code is the entrypoint for formatting 24 - Code = code{} 25 - ) 26 - 27 - type code struct{} 24 + } 25 + } 28 26 29 27 func setup(source []byte, fileName string) (chroma.Iterator, *chroma.Style, error) { 30 28 lexer := lexers.Match(fileName) 31 29 if lexer == nil { 32 30 lexer = lexers.Fallback 31 + if name, ok := customReg[filepath.Ext(fileName)]; ok { 32 + lexer = lexers.Get(name) 33 + } 33 34 } 34 35 lexer = chroma.Coalesce(lexer) 35 36 ··· 46 47 return iter, style, nil 47 48 } 48 49 49 - // Basic formats code without any extras 50 - func (c code) Basic(source []byte, fileName string, writer io.Writer) error { 51 - iter, style, err := setup(source, fileName) 52 - if err != nil { 53 - return err 54 - } 55 - return basicFormatter.Format(writer, style, iter) 56 - } 57 - 58 50 // Convert formats code with line numbers, links, etc. 59 - func (c code) Convert(source []byte, fileName string, writer io.Writer) error { 51 + func Convert(source []byte, fileName, linePrefix string, writer io.Writer) error { 60 52 iter, style, err := setup(source, fileName) 61 53 if err != nil { 62 54 return err 63 55 } 64 - return Formatter.Format(writer, style, iter) 56 + return html.New(Options(linePrefix)...).Format(writer, style, iter) 65 57 } 66 58 67 59 // Snippet formats code with line numbers starting at a specific line
+1
internal/html/markup/markdown.go
··· 112 112 case *ast.Image: 113 113 link := v.Destination 114 114 if len(link) > 0 && !bytes.HasPrefix(link, []byte("http")) { 115 + v.SetAttributeString("style", []byte("max-width:100%;")) 115 116 v.Destination = []byte(resolveLink(ctx.repo, ctx.ref, ctx.path, string(link)) + "?raw&pretty") 116 117 } 117 118
+4 -3
internal/html/readme.templ
··· 1 1 package html 2 2 3 3 type ReadmeComponentContext struct { 4 - Markdown string 4 + Markdown string 5 5 } 6 6 7 7 templ readmeComponent(rcc ReadmeComponentContext) { 8 8 if rcc.Markdown != "" { 9 - <div class="bg-base dark:bg-base/50 p-5 mt-5 rounded markdown">@templ.Raw(rcc.Markdown)</div> 9 + <div class="bg-base dark:bg-base/50 p-5 mt-5 rounded markdown"> 10 + @templ.Raw(rcc.Markdown) 11 + </div> 10 12 } 11 13 } 12 -
+19 -14
internal/html/readme_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 type ReadmeComponentContext struct { 14 12 Markdown string 15 13 } 16 14 17 15 func readmeComponent(rcc ReadmeComponentContext) templ.Component { 18 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 19 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 16 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 17 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 18 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 19 + return templ_7745c5c3_CtxErr 20 + } 21 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 20 22 if !templ_7745c5c3_IsBuffer { 21 - templ_7745c5c3_Buffer = templ.GetBuffer() 22 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 23 + defer func() { 24 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 25 + if templ_7745c5c3_Err == nil { 26 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 27 + } 28 + }() 23 29 } 24 30 ctx = templ.InitializeContext(ctx) 25 31 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 28 34 } 29 35 ctx = templ.ClearChildren(ctx) 30 36 if rcc.Markdown != "" { 31 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"bg-base dark:bg-base/50 p-5 mt-5 rounded markdown\">") 37 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"bg-base dark:bg-base/50 p-5 mt-5 rounded markdown\">") 32 38 if templ_7745c5c3_Err != nil { 33 39 return templ_7745c5c3_Err 34 40 } ··· 36 42 if templ_7745c5c3_Err != nil { 37 43 return templ_7745c5c3_Err 38 44 } 39 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 45 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div>") 40 46 if templ_7745c5c3_Err != nil { 41 47 return templ_7745c5c3_Err 42 48 } 43 49 } 44 - if !templ_7745c5c3_IsBuffer { 45 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 46 - } 47 - return templ_7745c5c3_Err 50 + return nil 48 51 }) 49 52 } 53 + 54 + var _ = templruntime.GeneratedTemplate
+1 -1
internal/html/repo_breadcrumb.templ
··· 2 2 3 3 import ( 4 4 "fmt" 5 - "strings" 6 5 "path" 6 + "strings" 7 7 ) 8 8 9 9 type RepoBreadcrumbComponentContext struct {
+30 -21
internal/html/repo_breadcrumb_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import ( 14 12 "fmt" ··· 47 45 } 48 46 49 47 func repoBreadcrumbComponent(rbcc RepoBreadcrumbComponentContext) templ.Component { 50 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 51 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 48 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 49 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 50 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 51 + return templ_7745c5c3_CtxErr 52 + } 53 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 52 54 if !templ_7745c5c3_IsBuffer { 53 - templ_7745c5c3_Buffer = templ.GetBuffer() 54 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 55 + defer func() { 56 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 57 + if templ_7745c5c3_Err == nil { 58 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 59 + } 60 + }() 55 61 } 56 62 ctx = templ.InitializeContext(ctx) 57 63 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 60 66 } 61 67 ctx = templ.ClearChildren(ctx) 62 68 if rbcc.Path != "" { 63 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"inline-block text-text\">") 69 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"inline-block text-text\">") 64 70 if templ_7745c5c3_Err != nil { 65 71 return templ_7745c5c3_Err 66 72 } 67 73 for _, crumb := range rbcc.crumbs() { 68 74 if crumb.end { 69 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span>") 75 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<span>") 70 76 if templ_7745c5c3_Err != nil { 71 77 return templ_7745c5c3_Err 72 78 } ··· 79 85 if templ_7745c5c3_Err != nil { 80 86 return templ_7745c5c3_Err 81 87 } 82 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>") 88 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</span>") 83 89 if templ_7745c5c3_Err != nil { 84 90 return templ_7745c5c3_Err 85 91 } 86 92 } else { 87 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 93 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 88 94 if templ_7745c5c3_Err != nil { 89 95 return templ_7745c5c3_Err 90 96 } 91 - var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(crumb.href) 92 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3))) 97 + var templ_7745c5c3_Var3 templ.SafeURL 98 + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(crumb.href)) 99 + if templ_7745c5c3_Err != nil { 100 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_breadcrumb.templ`, Line: 46, Col: 118} 101 + } 102 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 93 103 if templ_7745c5c3_Err != nil { 94 104 return templ_7745c5c3_Err 95 105 } 96 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 106 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">") 97 107 if templ_7745c5c3_Err != nil { 98 108 return templ_7745c5c3_Err 99 109 } ··· 106 116 if templ_7745c5c3_Err != nil { 107 117 return templ_7745c5c3_Err 108 118 } 109 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ") 119 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</a> ") 110 120 if templ_7745c5c3_Err != nil { 111 121 return templ_7745c5c3_Err 112 122 } ··· 121 131 } 122 132 } 123 133 } 124 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 134 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>") 125 135 if templ_7745c5c3_Err != nil { 126 136 return templ_7745c5c3_Err 127 137 } 128 138 } 129 - if !templ_7745c5c3_IsBuffer { 130 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 131 - } 132 - return templ_7745c5c3_Err 139 + return nil 133 140 }) 134 141 } 142 + 143 + var _ = templruntime.GeneratedTemplate
+16 -8
internal/html/repo_commit.templ
··· 4 4 import "github.com/dustin/go-humanize" 5 5 import "go.jolheiser.com/ugit/internal/git" 6 6 7 - type RepoCommitContext struct{ 8 - BaseContext 9 - RepoHeaderComponentContext 10 - Commit git.Commit 7 + type RepoCommitContext struct { 8 + BaseContext 9 + RepoHeaderComponentContext 10 + Commit git.Commit 11 11 } 12 12 13 13 templ RepoCommit(rcc RepoCommitContext) { ··· 25 25 <div>{ rcc.Commit.Author }{ " " }<a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("mailto:%s", rcc.Commit.Email)) }>{ fmt.Sprintf("<%s>", rcc.Commit.Email) }</a></div> 26 26 <div title={ rcc.Commit.When.Format("01/02/2006 03:04:05 PM") }>{ humanize.Time(rcc.Commit.When) }</div> 27 27 </div> 28 - <div class="text-text mt-5">{ fmt.Sprintf("%d changed files, %d additions(+), %d deletions(-)", rcc.Commit.Stats.Changed, rcc.Commit.Stats.Additions, rcc.Commit.Stats.Deletions) }</div> 28 + <details class="text-text mt-5"> 29 + <summary class="cursor-pointer">{ fmt.Sprintf("%d changed files, %d additions(+), %d deletions(-)", rcc.Commit.Stats.Changed, rcc.Commit.Stats.Additions, rcc.Commit.Stats.Deletions) }</summary> 30 + <div class="p-3 bg-base rounded"> 31 + for _, file := range rcc.Commit.Files { 32 + <a class="block underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ "#" + file.Path() }>{ file.Path() }</a> 33 + } 34 + </div> 35 + </details> 29 36 for _, file := range rcc.Commit.Files { 30 - <div class="text-text mt-5"> 37 + <div class="text-text mt-5" id={ file.Path() }> 31 38 <span class="text-text/80" title={ file.Action }>{ string(file.Action[0]) }</span> 32 39 { " " } 33 40 if file.From.Path != "" { ··· 40 47 <a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path)) }>{ file.To.Path }</a> 41 48 } 42 49 </div> 43 - <div class="whitespace-pre code">@templ.Raw(file.Patch)</div> 50 + <div class="code"> 51 + @templ.Raw(file.Patch) 52 + </div> 44 53 } 45 54 } 46 55 } 47 -
+162 -81
internal/html/repo_commit_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import "fmt" 14 12 import "github.com/dustin/go-humanize" ··· 21 19 } 22 20 23 21 func RepoCommit(rcc RepoCommitContext) templ.Component { 24 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 25 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 22 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 23 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 24 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 25 + return templ_7745c5c3_CtxErr 26 + } 27 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 26 28 if !templ_7745c5c3_IsBuffer { 27 - templ_7745c5c3_Buffer = templ.GetBuffer() 28 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 29 + defer func() { 30 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 31 + if templ_7745c5c3_Err == nil { 32 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 33 + } 34 + }() 29 35 } 30 36 ctx = templ.InitializeContext(ctx) 31 37 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 33 39 templ_7745c5c3_Var1 = templ.NopComponent 34 40 } 35 41 ctx = templ.ClearChildren(ctx) 36 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 37 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 42 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 43 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 44 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 38 45 if !templ_7745c5c3_IsBuffer { 39 - templ_7745c5c3_Buffer = templ.GetBuffer() 40 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 46 + defer func() { 47 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 48 + if templ_7745c5c3_Err == nil { 49 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 50 + } 51 + }() 41 52 } 53 + ctx = templ.InitializeContext(ctx) 42 54 templ_7745c5c3_Err = repoHeaderComponent(rcc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 43 55 if templ_7745c5c3_Err != nil { 44 56 return templ_7745c5c3_Err 45 57 } 46 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 58 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 47 59 if templ_7745c5c3_Err != nil { 48 60 return templ_7745c5c3_Err 49 61 } 50 - var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA)) 51 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3))) 62 + var templ_7745c5c3_Var3 templ.SafeURL 63 + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA))) 64 + if templ_7745c5c3_Err != nil { 65 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 16, Col: 213} 66 + } 67 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 52 68 if templ_7745c5c3_Err != nil { 53 69 return templ_7745c5c3_Err 54 70 } 55 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">tree</a>") 71 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">tree</a>") 56 72 if templ_7745c5c3_Err != nil { 57 73 return templ_7745c5c3_Err 58 74 } ··· 65 81 if templ_7745c5c3_Err != nil { 66 82 return templ_7745c5c3_Err 67 83 } 68 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 84 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 69 85 if templ_7745c5c3_Err != nil { 70 86 return templ_7745c5c3_Err 71 87 } 72 - var templ_7745c5c3_Var5 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/log/%s", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA)) 73 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5))) 88 + var templ_7745c5c3_Var5 templ.SafeURL 89 + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/log/%s", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA))) 90 + if templ_7745c5c3_Err != nil { 91 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 16, Col: 412} 92 + } 93 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 74 94 if templ_7745c5c3_Err != nil { 75 95 return templ_7745c5c3_Err 76 96 } 77 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">log</a>") 97 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">log</a>") 78 98 if templ_7745c5c3_Err != nil { 79 99 return templ_7745c5c3_Err 80 100 } ··· 87 107 if templ_7745c5c3_Err != nil { 88 108 return templ_7745c5c3_Err 89 109 } 90 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 110 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 91 111 if templ_7745c5c3_Err != nil { 92 112 return templ_7745c5c3_Err 93 113 } 94 - var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/commit/%s.patch", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA)) 95 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7))) 114 + var templ_7745c5c3_Var7 templ.SafeURL 115 + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/commit/%s.patch", rcc.RepoHeaderComponentContext.Name, rcc.Commit.SHA))) 116 + if templ_7745c5c3_Err != nil { 117 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 16, Col: 619} 118 + } 119 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) 96 120 if templ_7745c5c3_Err != nil { 97 121 return templ_7745c5c3_Err 98 122 } 99 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">patch</a></div><div class=\"text-text whitespace-pre mt-5 p-3 bg-base rounded\">") 123 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">patch</a></div><div class=\"text-text whitespace-pre mt-5 p-3 bg-base rounded\">") 100 124 if templ_7745c5c3_Err != nil { 101 125 return templ_7745c5c3_Err 102 126 } ··· 109 133 if templ_7745c5c3_Err != nil { 110 134 return templ_7745c5c3_Err 111 135 } 112 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 136 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>") 113 137 if templ_7745c5c3_Err != nil { 114 138 return templ_7745c5c3_Err 115 139 } 116 140 if rcc.Commit.Signature != "" { 117 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<details class=\"text-text whitespace-pre\"><summary class=\"cursor-pointer\">Signature</summary><div class=\"p-3 bg-base rounded\"><code>") 141 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<details class=\"text-text whitespace-pre\"><summary class=\"cursor-pointer\">Signature</summary><div class=\"p-3 bg-base rounded\"><code>") 118 142 if templ_7745c5c3_Err != nil { 119 143 return templ_7745c5c3_Err 120 144 } ··· 127 151 if templ_7745c5c3_Err != nil { 128 152 return templ_7745c5c3_Err 129 153 } 130 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</code></div></details>") 154 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</code></div></details>") 131 155 if templ_7745c5c3_Err != nil { 132 156 return templ_7745c5c3_Err 133 157 } 134 158 } 135 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"text-text mt-3\"><div>") 159 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " <div class=\"text-text mt-3\"><div>") 136 160 if templ_7745c5c3_Err != nil { 137 161 return templ_7745c5c3_Err 138 162 } ··· 154 178 if templ_7745c5c3_Err != nil { 155 179 return templ_7745c5c3_Err 156 180 } 157 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 181 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 158 182 if templ_7745c5c3_Err != nil { 159 183 return templ_7745c5c3_Err 160 184 } 161 - var templ_7745c5c3_Var12 templ.SafeURL = templ.SafeURL(fmt.Sprintf("mailto:%s", rcc.Commit.Email)) 162 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var12))) 185 + var templ_7745c5c3_Var12 templ.SafeURL 186 + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("mailto:%s", rcc.Commit.Email))) 187 + if templ_7745c5c3_Err != nil { 188 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 25, Col: 181} 189 + } 190 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) 163 191 if templ_7745c5c3_Err != nil { 164 192 return templ_7745c5c3_Err 165 193 } 166 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 194 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\">") 167 195 if templ_7745c5c3_Err != nil { 168 196 return templ_7745c5c3_Err 169 197 } ··· 176 204 if templ_7745c5c3_Err != nil { 177 205 return templ_7745c5c3_Err 178 206 } 179 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div title=\"") 207 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</a></div><div title=\"") 180 208 if templ_7745c5c3_Err != nil { 181 209 return templ_7745c5c3_Err 182 210 } ··· 189 217 if templ_7745c5c3_Err != nil { 190 218 return templ_7745c5c3_Err 191 219 } 192 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 220 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">") 193 221 if templ_7745c5c3_Err != nil { 194 222 return templ_7745c5c3_Err 195 223 } ··· 202 230 if templ_7745c5c3_Err != nil { 203 231 return templ_7745c5c3_Err 204 232 } 205 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><div class=\"text-text mt-5\">") 233 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></div><details class=\"text-text mt-5\"><summary class=\"cursor-pointer\">") 206 234 if templ_7745c5c3_Err != nil { 207 235 return templ_7745c5c3_Err 208 236 } 209 237 var templ_7745c5c3_Var16 string 210 238 templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d changed files, %d additions(+), %d deletions(-)", rcc.Commit.Stats.Changed, rcc.Commit.Stats.Additions, rcc.Commit.Stats.Deletions)) 211 239 if templ_7745c5c3_Err != nil { 212 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 28, Col: 179} 240 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 29, Col: 184} 213 241 } 214 242 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) 215 243 if templ_7745c5c3_Err != nil { 216 244 return templ_7745c5c3_Err 217 245 } 218 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 246 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</summary><div class=\"p-3 bg-base rounded\">") 219 247 if templ_7745c5c3_Err != nil { 220 248 return templ_7745c5c3_Err 221 249 } 222 250 for _, file := range rcc.Commit.Files { 223 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-text mt-5\"><span class=\"text-text/80\" title=\"") 251 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<a class=\"block underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 224 252 if templ_7745c5c3_Err != nil { 225 253 return templ_7745c5c3_Err 226 254 } 227 - var templ_7745c5c3_Var17 string 228 - templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(file.Action) 255 + var templ_7745c5c3_Var17 templ.SafeURL 256 + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs("#" + file.Path()) 229 257 if templ_7745c5c3_Err != nil { 230 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 31, Col: 50} 258 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 32, Col: 116} 231 259 } 232 260 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) 233 261 if templ_7745c5c3_Err != nil { 234 262 return templ_7745c5c3_Err 235 263 } 236 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 264 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">") 237 265 if templ_7745c5c3_Err != nil { 238 266 return templ_7745c5c3_Err 239 267 } 240 268 var templ_7745c5c3_Var18 string 241 - templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(string(file.Action[0])) 269 + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(file.Path()) 242 270 if templ_7745c5c3_Err != nil { 243 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 31, Col: 77} 271 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 32, Col: 132} 244 272 } 245 273 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) 246 274 if templ_7745c5c3_Err != nil { 247 275 return templ_7745c5c3_Err 248 276 } 249 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> ") 277 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</a>") 278 + if templ_7745c5c3_Err != nil { 279 + return templ_7745c5c3_Err 280 + } 281 + } 282 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div></details> ") 283 + if templ_7745c5c3_Err != nil { 284 + return templ_7745c5c3_Err 285 + } 286 + for _, file := range rcc.Commit.Files { 287 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"text-text mt-5\" id=\"") 250 288 if templ_7745c5c3_Err != nil { 251 289 return templ_7745c5c3_Err 252 290 } 253 291 var templ_7745c5c3_Var19 string 254 - templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(" ") 292 + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(file.Path()) 255 293 if templ_7745c5c3_Err != nil { 256 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 32, Col: 9} 294 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 37, Col: 47} 257 295 } 258 296 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) 259 297 if templ_7745c5c3_Err != nil { 260 298 return templ_7745c5c3_Err 261 299 } 262 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 300 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"><span class=\"text-text/80\" title=\"") 301 + if templ_7745c5c3_Err != nil { 302 + return templ_7745c5c3_Err 303 + } 304 + var templ_7745c5c3_Var20 string 305 + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(file.Action) 306 + if templ_7745c5c3_Err != nil { 307 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 38, Col: 50} 308 + } 309 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) 310 + if templ_7745c5c3_Err != nil { 311 + return templ_7745c5c3_Err 312 + } 313 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\">") 314 + if templ_7745c5c3_Err != nil { 315 + return templ_7745c5c3_Err 316 + } 317 + var templ_7745c5c3_Var21 string 318 + templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(string(file.Action[0])) 319 + if templ_7745c5c3_Err != nil { 320 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 38, Col: 77} 321 + } 322 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) 323 + if templ_7745c5c3_Err != nil { 324 + return templ_7745c5c3_Err 325 + } 326 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</span> ") 327 + if templ_7745c5c3_Err != nil { 328 + return templ_7745c5c3_Err 329 + } 330 + var templ_7745c5c3_Var22 string 331 + templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(" ") 332 + if templ_7745c5c3_Err != nil { 333 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 39, Col: 9} 334 + } 335 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) 336 + if templ_7745c5c3_Err != nil { 337 + return templ_7745c5c3_Err 338 + } 339 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ") 263 340 if templ_7745c5c3_Err != nil { 264 341 return templ_7745c5c3_Err 265 342 } 266 343 if file.From.Path != "" { 267 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 344 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 268 345 if templ_7745c5c3_Err != nil { 269 346 return templ_7745c5c3_Err 270 347 } 271 - var templ_7745c5c3_Var20 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.From.Commit, file.From.Path)) 272 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var20))) 348 + var templ_7745c5c3_Var23 templ.SafeURL 349 + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.From.Commit, file.From.Path))) 350 + if templ_7745c5c3_Err != nil { 351 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 41, Col: 208} 352 + } 353 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) 273 354 if templ_7745c5c3_Err != nil { 274 355 return templ_7745c5c3_Err 275 356 } 276 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 357 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\">") 277 358 if templ_7745c5c3_Err != nil { 278 359 return templ_7745c5c3_Err 279 360 } 280 - var templ_7745c5c3_Var21 string 281 - templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(file.From.Path) 361 + var templ_7745c5c3_Var24 string 362 + templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(file.From.Path) 282 363 if templ_7745c5c3_Err != nil { 283 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 34, Col: 227} 364 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 41, Col: 227} 284 365 } 285 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) 366 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) 286 367 if templ_7745c5c3_Err != nil { 287 368 return templ_7745c5c3_Err 288 369 } 289 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ") 370 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</a> ") 290 371 if templ_7745c5c3_Err != nil { 291 372 return templ_7745c5c3_Err 292 373 } 293 374 } 294 375 if file.From.Path != "" && file.To.Path != "" { 295 - var templ_7745c5c3_Var22 string 296 - templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(" -> ") 376 + var templ_7745c5c3_Var25 string 377 + templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(" -> ") 297 378 if templ_7745c5c3_Err != nil { 298 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 37, Col: 13} 379 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 44, Col: 13} 299 380 } 300 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) 381 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) 301 382 if templ_7745c5c3_Err != nil { 302 383 return templ_7745c5c3_Err 303 384 } 304 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 385 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " ") 305 386 if templ_7745c5c3_Err != nil { 306 387 return templ_7745c5c3_Err 307 388 } 308 389 } 309 390 if file.To.Path != "" { 310 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 391 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 311 392 if templ_7745c5c3_Err != nil { 312 393 return templ_7745c5c3_Err 313 394 } 314 - var templ_7745c5c3_Var23 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path)) 315 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var23))) 395 + var templ_7745c5c3_Var26 templ.SafeURL 396 + templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path))) 397 + if templ_7745c5c3_Err != nil { 398 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 47, Col: 204} 399 + } 400 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) 316 401 if templ_7745c5c3_Err != nil { 317 402 return templ_7745c5c3_Err 318 403 } 319 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 404 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">") 320 405 if templ_7745c5c3_Err != nil { 321 406 return templ_7745c5c3_Err 322 407 } 323 - var templ_7745c5c3_Var24 string 324 - templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(file.To.Path) 408 + var templ_7745c5c3_Var27 string 409 + templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(file.To.Path) 325 410 if templ_7745c5c3_Err != nil { 326 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 40, Col: 221} 411 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_commit.templ`, Line: 47, Col: 221} 327 412 } 328 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) 413 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) 329 414 if templ_7745c5c3_Err != nil { 330 415 return templ_7745c5c3_Err 331 416 } 332 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>") 417 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</a>") 333 418 if templ_7745c5c3_Err != nil { 334 419 return templ_7745c5c3_Err 335 420 } 336 421 } 337 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"whitespace-pre code\">") 422 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div><div class=\"code\">") 338 423 if templ_7745c5c3_Err != nil { 339 424 return templ_7745c5c3_Err 340 425 } ··· 342 427 if templ_7745c5c3_Err != nil { 343 428 return templ_7745c5c3_Err 344 429 } 345 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 430 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div>") 346 431 if templ_7745c5c3_Err != nil { 347 432 return templ_7745c5c3_Err 348 433 } 349 434 } 350 - if !templ_7745c5c3_IsBuffer { 351 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 352 - } 353 - return templ_7745c5c3_Err 435 + return nil 354 436 }) 355 437 templ_7745c5c3_Err = base(rcc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 356 438 if templ_7745c5c3_Err != nil { 357 439 return templ_7745c5c3_Err 358 440 } 359 - if !templ_7745c5c3_IsBuffer { 360 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 361 - } 362 - return templ_7745c5c3_Err 441 + return nil 363 442 }) 364 443 } 444 + 445 + var _ = templruntime.GeneratedTemplate
+42 -5
internal/html/repo_file.templ
··· 1 1 package html 2 2 3 + import "fmt" 4 + 3 5 type RepoFileContext struct { 4 6 BaseContext 5 7 RepoHeaderComponentContext 6 8 RepoBreadcrumbComponentContext 7 - Code string 9 + Code string 10 + Commit string 11 + Path string 12 + } 13 + 14 + func (rfc RepoFileContext) Permalink() string { 15 + return fmt.Sprintf("/%s/tree/%s/%s", rfc.RepoBreadcrumbComponentContext.Repo, rfc.Commit, rfc.Path) 8 16 } 9 17 10 18 templ RepoFile(rfc RepoFileContext) { ··· 14 22 @repoBreadcrumbComponent(rfc.RepoBreadcrumbComponentContext) 15 23 { " - " } 16 24 <a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" href="?raw">raw</a> 17 - <div class="code"> 25 + { " - " } 26 + <a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" id="permalink" data-permalink={ rfc.Permalink() } href={ rfc.Permalink() }>permalink</a> 27 + <div class="code relative"> 18 28 @templ.Raw(rfc.Code) 29 + <button id="copy" class="absolute top-0 right-0 rounded bg-base hover:bg-surface0"></button> 19 30 </div> 20 31 </div> 21 32 } ··· 23 34 const lineRe = /#L(\d+)(?:-L(\d+))?/g 24 35 const $lineLines = document.querySelectorAll(".chroma .lntable .lnt"); 25 36 const $codeLines = document.querySelectorAll(".chroma .lntable .line"); 37 + const $copyButton = document.getElementById('copy'); 38 + const $permalink = document.getElementById('permalink'); 39 + const $copyIcon = "๐Ÿ“‹"; 40 + const $copiedIcon = "โœ…"; 41 + let $code = "" 42 + for (let codeLine of $codeLines) $code += codeLine.innerText; 26 43 let start = 0; 27 44 let end = 0; 28 45 ··· 31 48 start = results[0][1] !== undefined ? parseInt(results[0][1]) : 0; 32 49 end = results[0][2] !== undefined ? parseInt(results[0][2]) : 0; 33 50 } 34 - if (start != 0) { 51 + if (start !== 0) { 35 52 deactivateLines(); 36 53 activateLines(start, end); 54 + let anchor = `#${start}`; 55 + if (end !== 0) anchor += `-${end}`; 56 + if (anchor !== "") $permalink.href = $permalink.dataset.permalink + anchor; 57 + $lineLines[start-1].scrollIntoView(true); 37 58 } 38 59 39 60 for (let line of $lineLines) { ··· 45 66 if (event.shiftKey) { 46 67 end = n; 47 68 anchor = `#L${start}-L${end}`; 69 + } else if (start === n) { 70 + start = 0; 71 + end = 0; 48 72 } else { 49 73 start = n; 50 74 end = 0; 51 75 anchor = `#L${start}`; 52 76 } 53 - history.replaceState(null, null, anchor); 54 - activateLines(start, end); 77 + history.replaceState(null, null, window.location.pathname + anchor); 78 + $permalink.href = $permalink.dataset.permalink + anchor; 79 + if (start !== 0) activateLines(start, end); 55 80 }); 56 81 } 82 + 83 + if (navigator.clipboard && navigator.clipboard.writeText) { 84 + $copyButton.innerText = $copyIcon; 85 + $copyButton.classList.remove("hidden"); 86 + } 87 + $copyButton.addEventListener("click", () => { 88 + navigator.clipboard.writeText($code); 89 + $copyButton.innerText = $copiedIcon; 90 + setTimeout(() => { 91 + $copyButton.innerText = $copyIcon; 92 + }, 1000); 93 + }); 57 94 58 95 function activateLines(start, end) { 59 96 if (end < start) end = start;
+81 -26
internal/html/repo_file_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 10 + 11 + import "fmt" 12 12 13 13 type RepoFileContext struct { 14 14 BaseContext 15 15 RepoHeaderComponentContext 16 16 RepoBreadcrumbComponentContext 17 - Code string 17 + Code string 18 + Commit string 19 + Path string 20 + } 21 + 22 + func (rfc RepoFileContext) Permalink() string { 23 + return fmt.Sprintf("/%s/tree/%s/%s", rfc.RepoBreadcrumbComponentContext.Repo, rfc.Commit, rfc.Path) 18 24 } 19 25 20 26 func RepoFile(rfc RepoFileContext) templ.Component { 21 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 22 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 27 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 28 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 29 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 30 + return templ_7745c5c3_CtxErr 31 + } 32 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 23 33 if !templ_7745c5c3_IsBuffer { 24 - templ_7745c5c3_Buffer = templ.GetBuffer() 25 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 34 + defer func() { 35 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 36 + if templ_7745c5c3_Err == nil { 37 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 38 + } 39 + }() 26 40 } 27 41 ctx = templ.InitializeContext(ctx) 28 42 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 30 44 templ_7745c5c3_Var1 = templ.NopComponent 31 45 } 32 46 ctx = templ.ClearChildren(ctx) 33 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 34 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 47 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 48 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 49 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 35 50 if !templ_7745c5c3_IsBuffer { 36 - templ_7745c5c3_Buffer = templ.GetBuffer() 37 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 51 + defer func() { 52 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 53 + if templ_7745c5c3_Err == nil { 54 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 55 + } 56 + }() 38 57 } 58 + ctx = templ.InitializeContext(ctx) 39 59 templ_7745c5c3_Err = repoHeaderComponent(rfc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 40 60 if templ_7745c5c3_Err != nil { 41 61 return templ_7745c5c3_Err 42 62 } 43 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"mt-2 text-text\">") 63 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <div class=\"mt-2 text-text\">") 44 64 if templ_7745c5c3_Err != nil { 45 65 return templ_7745c5c3_Err 46 66 } ··· 51 71 var templ_7745c5c3_Var3 string 52 72 templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(" - ") 53 73 if templ_7745c5c3_Err != nil { 54 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 15, Col: 10} 74 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 23, Col: 10} 55 75 } 56 76 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 57 77 if templ_7745c5c3_Err != nil { 58 78 return templ_7745c5c3_Err 59 79 } 60 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"text-text underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"?raw\">raw</a><div class=\"code\">") 80 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <a class=\"text-text underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"?raw\">raw</a> ") 81 + if templ_7745c5c3_Err != nil { 82 + return templ_7745c5c3_Err 83 + } 84 + var templ_7745c5c3_Var4 string 85 + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" - ") 86 + if templ_7745c5c3_Err != nil { 87 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 25, Col: 10} 88 + } 89 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 90 + if templ_7745c5c3_Err != nil { 91 + return templ_7745c5c3_Err 92 + } 93 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " <a class=\"text-text underline decoration-text/50 decoration-dashed hover:decoration-solid\" id=\"permalink\" data-permalink=\"") 94 + if templ_7745c5c3_Err != nil { 95 + return templ_7745c5c3_Err 96 + } 97 + var templ_7745c5c3_Var5 string 98 + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(rfc.Permalink()) 99 + if templ_7745c5c3_Err != nil { 100 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 26, Col: 141} 101 + } 102 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 103 + if templ_7745c5c3_Err != nil { 104 + return templ_7745c5c3_Err 105 + } 106 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" href=\"") 107 + if templ_7745c5c3_Err != nil { 108 + return templ_7745c5c3_Err 109 + } 110 + var templ_7745c5c3_Var6 templ.SafeURL 111 + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(rfc.Permalink()) 112 + if templ_7745c5c3_Err != nil { 113 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 26, Col: 166} 114 + } 115 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) 61 116 if templ_7745c5c3_Err != nil { 62 117 return templ_7745c5c3_Err 63 118 } 64 - templ_7745c5c3_Err = templ.Raw(rfc.Code).Render(ctx, templ_7745c5c3_Buffer) 119 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">permalink</a><div class=\"code relative\">") 65 120 if templ_7745c5c3_Err != nil { 66 121 return templ_7745c5c3_Err 67 122 } 68 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>") 123 + templ_7745c5c3_Err = templ.Raw(rfc.Code).Render(ctx, templ_7745c5c3_Buffer) 69 124 if templ_7745c5c3_Err != nil { 70 125 return templ_7745c5c3_Err 71 126 } 72 - if !templ_7745c5c3_IsBuffer { 73 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 127 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<button id=\"copy\" class=\"absolute top-0 right-0 rounded bg-base hover:bg-surface0\"></button></div></div>") 128 + if templ_7745c5c3_Err != nil { 129 + return templ_7745c5c3_Err 74 130 } 75 - return templ_7745c5c3_Err 131 + return nil 76 132 }) 77 133 templ_7745c5c3_Err = base(rfc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 78 134 if templ_7745c5c3_Err != nil { 79 135 return templ_7745c5c3_Err 80 136 } 81 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script>\n\t\tconst lineRe = /#L(\\d+)(?:-L(\\d+))?/g\n\t\tconst $lineLines = document.querySelectorAll(\".chroma .lntable .lnt\");\n\t\tconst $codeLines = document.querySelectorAll(\".chroma .lntable .line\");\n\t\tlet start = 0;\n\t\tlet end = 0;\n\n\t\tconst results = [...location.hash.matchAll(lineRe)];\t\t\n\t\tif (0 in results) {\n\t\t\tstart = results[0][1] !== undefined ? parseInt(results[0][1]) : 0;\n\t\t\tend = results[0][2] !== undefined ? parseInt(results[0][2]) : 0;\n\t\t}\n\t\tif (start != 0) {\n\t\t\tdeactivateLines();\n\t\t\tactivateLines(start, end);\n\t\t}\n\n\t\tfor (let line of $lineLines) {\n\t\t\tline.addEventListener(\"click\", (event) => {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tdeactivateLines();\n\t\t\t\tconst n = parseInt(line.id.substring(1));\n\t\t\t\tlet anchor = \"\";\n\t\t\t\tif (event.shiftKey) {\n\t\t\t\t\tend = n;\n\t\t\t\t\tanchor = `#L${start}-L${end}`;\n\t\t\t\t} else {\n\t\t\t\t\tstart = n;\n\t\t\t\t\tend = 0;\n\t\t\t\t\tanchor = `#L${start}`;\n\t\t\t\t}\n\t\t\t\thistory.replaceState(null, null, anchor);\n\t\t\t\tactivateLines(start, end);\n\t\t\t});\n\t\t}\n\n\t\tfunction activateLines(start, end) {\n\t\t\tif (end < start) end = start;\n\t\t\tfor (let idx = start - 1; idx < end; idx++) {\n\t\t\t\t$codeLines[idx].classList.add(\"active\");\n\t\t\t}\n\t\t}\n\n\t\tfunction deactivateLines() {\n\t\t\tfor (let code of $codeLines) {\n\t\t\t\tcode.classList.remove(\"active\");\n\t\t\t}\n\t\t}\n\n\t\t\n\t\t\n\t</script>") 137 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<script>\n\t\tconst lineRe = /#L(\\d+)(?:-L(\\d+))?/g\n\t\tconst $lineLines = document.querySelectorAll(\".chroma .lntable .lnt\");\n\t\tconst $codeLines = document.querySelectorAll(\".chroma .lntable .line\");\n\t\tconst $copyButton = document.getElementById('copy');\n\t\tconst $permalink = document.getElementById('permalink');\n\t\tconst $copyIcon = \"๐Ÿ“‹\";\n\t\tconst $copiedIcon = \"โœ…\";\n\t\tlet $code = \"\"\n\t\tfor (let codeLine of $codeLines) $code += codeLine.innerText;\n\t\tlet start = 0;\n\t\tlet end = 0;\n\n\t\tconst results = [...location.hash.matchAll(lineRe)];\t\t\n\t\tif (0 in results) {\n\t\t\tstart = results[0][1] !== undefined ? parseInt(results[0][1]) : 0;\n\t\t\tend = results[0][2] !== undefined ? parseInt(results[0][2]) : 0;\n\t\t}\n\t\tif (start !== 0) {\n\t\t\tdeactivateLines();\n\t\t\tactivateLines(start, end);\n\t\t\tlet anchor = `#${start}`;\n if (end !== 0) anchor += `-${end}`;\n if (anchor !== \"\") $permalink.href = $permalink.dataset.permalink + anchor;\n\t\t\t$lineLines[start-1].scrollIntoView(true);\n\t\t}\n\n\t\tfor (let line of $lineLines) {\n\t\t\tline.addEventListener(\"click\", (event) => {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tdeactivateLines();\n\t\t\t\tconst n = parseInt(line.id.substring(1));\n\t\t\t\tlet anchor = \"\";\n\t\t\t\tif (event.shiftKey) {\n\t\t\t\t\tend = n;\n\t\t\t\t\tanchor = `#L${start}-L${end}`;\n\t\t\t\t} else if (start === n) {\n\t\t\t\t\tstart = 0;\n\t\t\t\t\tend = 0;\n\t\t\t\t} else {\n\t\t\t\t\tstart = n;\n\t\t\t\t\tend = 0;\n\t\t\t\t\tanchor = `#L${start}`;\n\t\t\t\t}\n\t\t\t\thistory.replaceState(null, null, window.location.pathname + anchor);\n\t\t\t\t$permalink.href = $permalink.dataset.permalink + anchor;\n\t\t\t\tif (start !== 0) activateLines(start, end);\n\t\t\t});\n\t\t}\n\n\t\tif (navigator.clipboard && navigator.clipboard.writeText) {\n\t\t\t$copyButton.innerText = $copyIcon;\n\t\t\t$copyButton.classList.remove(\"hidden\");\n }\n\t\t$copyButton.addEventListener(\"click\", () => {\n navigator.clipboard.writeText($code);\n\t\t\t$copyButton.innerText = $copiedIcon;\n\t\t\tsetTimeout(() => {\n\t\t\t\t$copyButton.innerText = $copyIcon;\n\t\t\t}, 1000);\n });\n\n\t\tfunction activateLines(start, end) {\n\t\t\tif (end < start) end = start;\n\t\t\tfor (let idx = start - 1; idx < end; idx++) {\n\t\t\t\t$codeLines[idx].classList.add(\"active\");\n\t\t\t}\n\t\t}\n\n\t\tfunction deactivateLines() {\n\t\t\tfor (let code of $codeLines) {\n\t\t\t\tcode.classList.remove(\"active\");\n\t\t\t}\n\t\t}\n\n\t\t\n\t\t\n\t</script>") 82 138 if templ_7745c5c3_Err != nil { 83 139 return templ_7745c5c3_Err 84 140 } 85 - if !templ_7745c5c3_IsBuffer { 86 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 87 - } 88 - return templ_7745c5c3_Err 141 + return nil 89 142 }) 90 143 } 144 + 145 + var _ = templruntime.GeneratedTemplate
+3 -4
internal/html/repo_log.templ
··· 5 5 import "go.jolheiser.com/ugit/internal/git" 6 6 7 7 type RepoLogContext struct { 8 - BaseContext 9 - RepoHeaderComponentContext 10 - Commits []git.Commit 8 + BaseContext 9 + RepoHeaderComponentContext 10 + Commits []git.Commit 11 11 } 12 12 13 13 templ RepoLog(rlc RepoLogContext) { ··· 36 36 </div> 37 37 } 38 38 } 39 -
+54 -38
internal/html/repo_log_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import "fmt" 14 12 import "github.com/dustin/go-humanize" ··· 21 19 } 22 20 23 21 func RepoLog(rlc RepoLogContext) templ.Component { 24 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 25 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 22 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 23 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 24 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 25 + return templ_7745c5c3_CtxErr 26 + } 27 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 26 28 if !templ_7745c5c3_IsBuffer { 27 - templ_7745c5c3_Buffer = templ.GetBuffer() 28 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 29 + defer func() { 30 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 31 + if templ_7745c5c3_Err == nil { 32 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 33 + } 34 + }() 29 35 } 30 36 ctx = templ.InitializeContext(ctx) 31 37 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 33 39 templ_7745c5c3_Var1 = templ.NopComponent 34 40 } 35 41 ctx = templ.ClearChildren(ctx) 36 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 37 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 42 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 43 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 44 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 38 45 if !templ_7745c5c3_IsBuffer { 39 - templ_7745c5c3_Buffer = templ.GetBuffer() 40 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 46 + defer func() { 47 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 48 + if templ_7745c5c3_Err == nil { 49 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 50 + } 51 + }() 41 52 } 53 + ctx = templ.InitializeContext(ctx) 42 54 templ_7745c5c3_Err = repoHeaderComponent(rlc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 43 55 if templ_7745c5c3_Err != nil { 44 56 return templ_7745c5c3_Err 45 57 } 46 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"grid sm:grid-cols-8 gap-1 text-text mt-5\">") 58 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <div class=\"grid sm:grid-cols-8 gap-1 text-text mt-5\">") 47 59 if templ_7745c5c3_Err != nil { 48 60 return templ_7745c5c3_Err 49 61 } 50 62 for _, commit := range rlc.Commits { 51 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"sm:col-span-5\"><div><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 63 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"sm:col-span-5\"><div><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 52 64 if templ_7745c5c3_Err != nil { 53 65 return templ_7745c5c3_Err 54 66 } 55 - var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/commit/%s", rlc.RepoHeaderComponentContext.Name, commit.SHA)) 56 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3))) 67 + var templ_7745c5c3_Var3 templ.SafeURL 68 + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/commit/%s", rlc.RepoHeaderComponentContext.Name, commit.SHA))) 69 + if templ_7745c5c3_Err != nil { 70 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_log.templ`, Line: 19, Col: 190} 71 + } 72 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 57 73 if templ_7745c5c3_Err != nil { 58 74 return templ_7745c5c3_Err 59 75 } 60 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 76 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">") 61 77 if templ_7745c5c3_Err != nil { 62 78 return templ_7745c5c3_Err 63 79 } ··· 70 86 if templ_7745c5c3_Err != nil { 71 87 return templ_7745c5c3_Err 72 88 } 73 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"whitespace-pre\">") 89 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</a></div><div class=\"whitespace-pre\">") 74 90 if templ_7745c5c3_Err != nil { 75 91 return templ_7745c5c3_Err 76 92 } 77 93 if commit.Details() != "" { 78 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<details><summary class=\"cursor-pointer\">") 94 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<details><summary class=\"cursor-pointer\">") 79 95 if templ_7745c5c3_Err != nil { 80 96 return templ_7745c5c3_Err 81 97 } ··· 88 104 if templ_7745c5c3_Err != nil { 89 105 return templ_7745c5c3_Err 90 106 } 91 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</summary><div class=\"p-3 bg-base rounded\">") 107 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</summary><div class=\"p-3 bg-base rounded\">") 92 108 if templ_7745c5c3_Err != nil { 93 109 return templ_7745c5c3_Err 94 110 } ··· 101 117 if templ_7745c5c3_Err != nil { 102 118 return templ_7745c5c3_Err 103 119 } 104 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></details>") 120 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></details>") 105 121 if templ_7745c5c3_Err != nil { 106 122 return templ_7745c5c3_Err 107 123 } ··· 116 132 return templ_7745c5c3_Err 117 133 } 118 134 } 119 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><div class=\"sm:col-span-3 mb-4\"><div>") 135 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div></div><div class=\"sm:col-span-3 mb-4\"><div>") 120 136 if templ_7745c5c3_Err != nil { 121 137 return templ_7745c5c3_Err 122 138 } ··· 138 154 if templ_7745c5c3_Err != nil { 139 155 return templ_7745c5c3_Err 140 156 } 141 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 157 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 142 158 if templ_7745c5c3_Err != nil { 143 159 return templ_7745c5c3_Err 144 160 } 145 - var templ_7745c5c3_Var10 templ.SafeURL = templ.SafeURL(fmt.Sprintf("mailto:%s", commit.Email)) 146 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var10))) 161 + var templ_7745c5c3_Var10 templ.SafeURL 162 + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("mailto:%s", commit.Email))) 163 + if templ_7745c5c3_Err != nil { 164 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_log.templ`, Line: 32, Col: 175} 165 + } 166 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) 147 167 if templ_7745c5c3_Err != nil { 148 168 return templ_7745c5c3_Err 149 169 } 150 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 170 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">") 151 171 if templ_7745c5c3_Err != nil { 152 172 return templ_7745c5c3_Err 153 173 } ··· 160 180 if templ_7745c5c3_Err != nil { 161 181 return templ_7745c5c3_Err 162 182 } 163 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div title=\"") 183 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</a></div><div title=\"") 164 184 if templ_7745c5c3_Err != nil { 165 185 return templ_7745c5c3_Err 166 186 } ··· 173 193 if templ_7745c5c3_Err != nil { 174 194 return templ_7745c5c3_Err 175 195 } 176 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 196 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\">") 177 197 if templ_7745c5c3_Err != nil { 178 198 return templ_7745c5c3_Err 179 199 } ··· 186 206 if templ_7745c5c3_Err != nil { 187 207 return templ_7745c5c3_Err 188 208 } 189 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>") 209 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div></div>") 190 210 if templ_7745c5c3_Err != nil { 191 211 return templ_7745c5c3_Err 192 212 } 193 213 } 194 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 214 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div>") 195 215 if templ_7745c5c3_Err != nil { 196 216 return templ_7745c5c3_Err 197 217 } 198 - if !templ_7745c5c3_IsBuffer { 199 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 200 - } 201 - return templ_7745c5c3_Err 218 + return nil 202 219 }) 203 220 templ_7745c5c3_Err = base(rlc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 204 221 if templ_7745c5c3_Err != nil { 205 222 return templ_7745c5c3_Err 206 223 } 207 - if !templ_7745c5c3_IsBuffer { 208 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 209 - } 210 - return templ_7745c5c3_Err 224 + return nil 211 225 }) 212 226 } 227 + 228 + var _ = templruntime.GeneratedTemplate
+4 -5
internal/html/repo_refs.templ
··· 4 4 import "go.jolheiser.com/ugit/internal/git" 5 5 6 6 type RepoRefsContext struct { 7 - BaseContext 8 - RepoHeaderComponentContext 9 - Branches []string 10 - Tags []git.Tag 7 + BaseContext 8 + RepoHeaderComponentContext 9 + Branches []string 10 + Tags []git.Tag 11 11 } 12 12 13 13 templ RepoRefs(rrc RepoRefsContext) { ··· 39 39 } 40 40 } 41 41 } 42 -
+73 -49
internal/html/repo_refs_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import "fmt" 14 12 import "go.jolheiser.com/ugit/internal/git" ··· 21 19 } 22 20 23 21 func RepoRefs(rrc RepoRefsContext) templ.Component { 24 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 25 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 22 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 23 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 24 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 25 + return templ_7745c5c3_CtxErr 26 + } 27 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 26 28 if !templ_7745c5c3_IsBuffer { 27 - templ_7745c5c3_Buffer = templ.GetBuffer() 28 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 29 + defer func() { 30 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 31 + if templ_7745c5c3_Err == nil { 32 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 33 + } 34 + }() 29 35 } 30 36 ctx = templ.InitializeContext(ctx) 31 37 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 33 39 templ_7745c5c3_Var1 = templ.NopComponent 34 40 } 35 41 ctx = templ.ClearChildren(ctx) 36 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 37 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 42 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 43 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 44 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 38 45 if !templ_7745c5c3_IsBuffer { 39 - templ_7745c5c3_Buffer = templ.GetBuffer() 40 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 46 + defer func() { 47 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 48 + if templ_7745c5c3_Err == nil { 49 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 50 + } 51 + }() 41 52 } 53 + ctx = templ.InitializeContext(ctx) 42 54 templ_7745c5c3_Err = repoHeaderComponent(rrc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 43 55 if templ_7745c5c3_Err != nil { 44 56 return templ_7745c5c3_Err 45 57 } 46 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 58 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ") 47 59 if templ_7745c5c3_Err != nil { 48 60 return templ_7745c5c3_Err 49 61 } 50 62 if len(rrc.Branches) > 0 { 51 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h3 class=\"text-text text-lg mt-5\">Branches</h3><div class=\"text-text grid grid-cols-4 sm:grid-cols-8\">") 63 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<h3 class=\"text-text text-lg mt-5\">Branches</h3><div class=\"text-text grid grid-cols-4 sm:grid-cols-8\">") 52 64 if templ_7745c5c3_Err != nil { 53 65 return templ_7745c5c3_Err 54 66 } 55 67 for _, branch := range rrc.Branches { 56 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col-span-2 sm:col-span-1 font-bold\">") 68 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"col-span-2 sm:col-span-1 font-bold\">") 57 69 if templ_7745c5c3_Err != nil { 58 70 return templ_7745c5c3_Err 59 71 } ··· 66 78 if templ_7745c5c3_Err != nil { 67 79 return templ_7745c5c3_Err 68 80 } 69 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"col-span-2 sm:col-span-7\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 81 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div><div class=\"col-span-2 sm:col-span-7\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 70 82 if templ_7745c5c3_Err != nil { 71 83 return templ_7745c5c3_Err 72 84 } 73 - var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rrc.RepoHeaderComponentContext.Name, branch)) 74 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) 85 + var templ_7745c5c3_Var4 templ.SafeURL 86 + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rrc.RepoHeaderComponentContext.Name, branch))) 87 + if templ_7745c5c3_Err != nil { 88 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_refs.templ`, Line: 21, Col: 218} 89 + } 90 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 75 91 if templ_7745c5c3_Err != nil { 76 92 return templ_7745c5c3_Err 77 93 } 78 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">tree</a>") 94 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">tree</a>") 79 95 if templ_7745c5c3_Err != nil { 80 96 return templ_7745c5c3_Err 81 97 } ··· 88 104 if templ_7745c5c3_Err != nil { 89 105 return templ_7745c5c3_Err 90 106 } 91 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 107 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 92 108 if templ_7745c5c3_Err != nil { 93 109 return templ_7745c5c3_Err 94 110 } 95 - var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/log/%s", rrc.RepoHeaderComponentContext.Name, branch)) 96 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6))) 111 + var templ_7745c5c3_Var6 templ.SafeURL 112 + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/log/%s", rrc.RepoHeaderComponentContext.Name, branch))) 113 + if templ_7745c5c3_Err != nil { 114 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_refs.templ`, Line: 21, Col: 409} 115 + } 116 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) 97 117 if templ_7745c5c3_Err != nil { 98 118 return templ_7745c5c3_Err 99 119 } 100 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">log</a></div>") 120 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">log</a></div>") 101 121 if templ_7745c5c3_Err != nil { 102 122 return templ_7745c5c3_Err 103 123 } 104 124 } 105 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 125 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div>") 106 126 if templ_7745c5c3_Err != nil { 107 127 return templ_7745c5c3_Err 108 128 } 109 129 } 110 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 130 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ") 111 131 if templ_7745c5c3_Err != nil { 112 132 return templ_7745c5c3_Err 113 133 } 114 134 if len(rrc.Tags) > 0 { 115 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h3 class=\"text-text text-lg mt-5\">Tags</h3><div class=\"text-text grid grid-cols-8\">") 135 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<h3 class=\"text-text text-lg mt-5\">Tags</h3><div class=\"text-text grid grid-cols-8\">") 116 136 if templ_7745c5c3_Err != nil { 117 137 return templ_7745c5c3_Err 118 138 } 119 139 for _, tag := range rrc.Tags { 120 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col-span-1 font-bold\">") 140 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"col-span-1 font-bold\">") 121 141 if templ_7745c5c3_Err != nil { 122 142 return templ_7745c5c3_Err 123 143 } ··· 130 150 if templ_7745c5c3_Err != nil { 131 151 return templ_7745c5c3_Err 132 152 } 133 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"col-span-7\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 153 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div><div class=\"col-span-7\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 134 154 if templ_7745c5c3_Err != nil { 135 155 return templ_7745c5c3_Err 136 156 } 137 - var templ_7745c5c3_Var8 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rrc.RepoHeaderComponentContext.Name, tag.Name)) 138 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8))) 157 + var templ_7745c5c3_Var8 templ.SafeURL 158 + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rrc.RepoHeaderComponentContext.Name, tag.Name))) 159 + if templ_7745c5c3_Err != nil { 160 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_refs.templ`, Line: 30, Col: 206} 161 + } 162 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 139 163 if templ_7745c5c3_Err != nil { 140 164 return templ_7745c5c3_Err 141 165 } 142 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">tree</a>") 166 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">tree</a>") 143 167 if templ_7745c5c3_Err != nil { 144 168 return templ_7745c5c3_Err 145 169 } ··· 152 176 if templ_7745c5c3_Err != nil { 153 177 return templ_7745c5c3_Err 154 178 } 155 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 179 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 156 180 if templ_7745c5c3_Err != nil { 157 181 return templ_7745c5c3_Err 158 182 } 159 - var templ_7745c5c3_Var10 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/log/%s", rrc.RepoHeaderComponentContext.Name, tag.Name)) 160 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var10))) 183 + var templ_7745c5c3_Var10 templ.SafeURL 184 + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/log/%s", rrc.RepoHeaderComponentContext.Name, tag.Name))) 185 + if templ_7745c5c3_Err != nil { 186 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_refs.templ`, Line: 30, Col: 399} 187 + } 188 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) 161 189 if templ_7745c5c3_Err != nil { 162 190 return templ_7745c5c3_Err 163 191 } 164 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">log</a></div>") 192 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\">log</a></div>") 165 193 if templ_7745c5c3_Err != nil { 166 194 return templ_7745c5c3_Err 167 195 } 168 196 if tag.Signature != "" { 169 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<details class=\"col-span-8 whitespace-pre\"><summary class=\"cursor-pointer\">Signature</summary><code>") 197 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<details class=\"col-span-8 whitespace-pre\"><summary class=\"cursor-pointer\">Signature</summary><code>") 170 198 if templ_7745c5c3_Err != nil { 171 199 return templ_7745c5c3_Err 172 200 } ··· 179 207 if templ_7745c5c3_Err != nil { 180 208 return templ_7745c5c3_Err 181 209 } 182 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</code></details>") 210 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</code></details>") 183 211 if templ_7745c5c3_Err != nil { 184 212 return templ_7745c5c3_Err 185 213 } 186 214 } 187 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 215 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " ") 188 216 if templ_7745c5c3_Err != nil { 189 217 return templ_7745c5c3_Err 190 218 } 191 219 if tag.Annotation != "" { 192 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col-span-8 mb-3\">") 220 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<div class=\"col-span-8 mb-3\">") 193 221 if templ_7745c5c3_Err != nil { 194 222 return templ_7745c5c3_Err 195 223 } ··· 202 230 if templ_7745c5c3_Err != nil { 203 231 return templ_7745c5c3_Err 204 232 } 205 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 233 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div>") 206 234 if templ_7745c5c3_Err != nil { 207 235 return templ_7745c5c3_Err 208 236 } 209 237 } 210 238 } 211 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 239 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div>") 212 240 if templ_7745c5c3_Err != nil { 213 241 return templ_7745c5c3_Err 214 242 } 215 243 } 216 - if !templ_7745c5c3_IsBuffer { 217 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 218 - } 219 - return templ_7745c5c3_Err 244 + return nil 220 245 }) 221 246 templ_7745c5c3_Err = base(rrc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 222 247 if templ_7745c5c3_Err != nil { 223 248 return templ_7745c5c3_Err 224 249 } 225 - if !templ_7745c5c3_IsBuffer { 226 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 227 - } 228 - return templ_7745c5c3_Err 250 + return nil 229 251 }) 230 252 } 253 + 254 + var _ = templruntime.GeneratedTemplate
+6 -8
internal/html/repo_search.templ
··· 11 11 12 12 func (s SearchContext) DedupeResults() [][]git.GrepResult { 13 13 var ( 14 - results [][]git.GrepResult 15 - currentFile string 16 - currentResults []git.GrepResult 14 + results [][]git.GrepResult 15 + currentFile string 17 16 ) 17 + var idx int 18 18 for _, result := range s.Results { 19 19 if result.File == currentFile { 20 - currentResults = append(currentResults, result) 20 + results[idx-1] = append(results[idx-1], result) 21 21 continue 22 22 } 23 - if currentFile != "" { 24 - results = append(results, currentResults) 25 - } 23 + results = append(results, []git.GrepResult{result}) 26 24 currentFile = result.File 27 - currentResults = []git.GrepResult{result} 25 + idx++ 28 26 } 29 27 30 28 return results
+76 -57
internal/html/repo_search_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import "fmt" 14 12 import "go.jolheiser.com/ugit/internal/git" ··· 21 19 22 20 func (s SearchContext) DedupeResults() [][]git.GrepResult { 23 21 var ( 24 - results [][]git.GrepResult 25 - currentFile string 26 - currentResults []git.GrepResult 22 + results [][]git.GrepResult 23 + currentFile string 27 24 ) 25 + var idx int 28 26 for _, result := range s.Results { 29 27 if result.File == currentFile { 30 - currentResults = append(currentResults, result) 28 + results[idx-1] = append(results[idx-1], result) 31 29 continue 32 30 } 33 - if currentFile != "" { 34 - results = append(results, currentResults) 35 - } 31 + results = append(results, []git.GrepResult{result}) 36 32 currentFile = result.File 37 - currentResults = []git.GrepResult{result} 33 + idx++ 38 34 } 39 35 40 36 return results 41 37 } 42 38 43 39 func RepoSearch(sc SearchContext) templ.Component { 44 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 45 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 40 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 41 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 42 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 43 + return templ_7745c5c3_CtxErr 44 + } 45 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 46 46 if !templ_7745c5c3_IsBuffer { 47 - templ_7745c5c3_Buffer = templ.GetBuffer() 48 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 47 + defer func() { 48 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 49 + if templ_7745c5c3_Err == nil { 50 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 51 + } 52 + }() 49 53 } 50 54 ctx = templ.InitializeContext(ctx) 51 55 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 53 57 templ_7745c5c3_Var1 = templ.NopComponent 54 58 } 55 59 ctx = templ.ClearChildren(ctx) 56 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 57 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 60 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 61 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 62 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 58 63 if !templ_7745c5c3_IsBuffer { 59 - templ_7745c5c3_Buffer = templ.GetBuffer() 60 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 64 + defer func() { 65 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 66 + if templ_7745c5c3_Err == nil { 67 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 68 + } 69 + }() 61 70 } 71 + ctx = templ.InitializeContext(ctx) 62 72 templ_7745c5c3_Err = repoHeaderComponent(sc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 63 73 if templ_7745c5c3_Err != nil { 64 74 return templ_7745c5c3_Err ··· 69 79 return templ_7745c5c3_Err 70 80 } 71 81 } 72 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 82 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ") 73 83 if templ_7745c5c3_Err != nil { 74 84 return templ_7745c5c3_Err 75 85 } 76 86 if len(sc.DedupeResults()) == 0 { 77 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"text-text mt-5 text-lg\">No results</p>") 87 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<p class=\"text-text mt-5 text-lg\">No results</p>") 78 88 if templ_7745c5c3_Err != nil { 79 89 return templ_7745c5c3_Err 80 90 } 81 91 } 82 - if !templ_7745c5c3_IsBuffer { 83 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 84 - } 85 - return templ_7745c5c3_Err 92 + return nil 86 93 }) 87 94 templ_7745c5c3_Err = base(sc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 88 95 if templ_7745c5c3_Err != nil { 89 96 return templ_7745c5c3_Err 90 97 } 91 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script>\n\t\tconst search = new URLSearchParams(window.location.search).get(\"q\");\n\t\tif (search !== \"\") document.querySelector(\"#search\").value = search;\n\t</script>") 98 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<script>\n\t\tconst search = new URLSearchParams(window.location.search).get(\"q\");\n\t\tif (search !== \"\") document.querySelector(\"#search\").value = search;\n\t</script>") 92 99 if templ_7745c5c3_Err != nil { 93 100 return templ_7745c5c3_Err 94 101 } 95 - if !templ_7745c5c3_IsBuffer { 96 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 97 - } 98 - return templ_7745c5c3_Err 102 + return nil 99 103 }) 100 104 } 101 105 102 106 func repoSearchResult(repo, ref string, results []git.GrepResult) templ.Component { 103 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 104 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 107 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 108 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 109 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 110 + return templ_7745c5c3_CtxErr 111 + } 112 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 105 113 if !templ_7745c5c3_IsBuffer { 106 - templ_7745c5c3_Buffer = templ.GetBuffer() 107 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 114 + defer func() { 115 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 116 + if templ_7745c5c3_Err == nil { 117 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 118 + } 119 + }() 108 120 } 109 121 ctx = templ.InitializeContext(ctx) 110 122 templ_7745c5c3_Var3 := templ.GetChildren(ctx) ··· 112 124 templ_7745c5c3_Var3 = templ.NopComponent 113 125 } 114 126 ctx = templ.ClearChildren(ctx) 115 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 127 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 116 128 if templ_7745c5c3_Err != nil { 117 129 return templ_7745c5c3_Err 118 130 } 119 - var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", repo, ref, results[0].File, results[0].Line)) 120 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) 131 + var templ_7745c5c3_Var4 templ.SafeURL 132 + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", repo, ref, results[0].File, results[0].Line))) 133 + if templ_7745c5c3_Err != nil { 134 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 48, Col: 210} 135 + } 136 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 121 137 if templ_7745c5c3_Err != nil { 122 138 return templ_7745c5c3_Err 123 139 } 124 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 140 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">") 125 141 if templ_7745c5c3_Err != nil { 126 142 return templ_7745c5c3_Err 127 143 } 128 144 var templ_7745c5c3_Var5 string 129 145 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(results[0].File) 130 146 if templ_7745c5c3_Err != nil { 131 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 50, Col: 230} 147 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 48, Col: 230} 132 148 } 133 149 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 134 150 if templ_7745c5c3_Err != nil { 135 151 return templ_7745c5c3_Err 136 152 } 137 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"code\">") 153 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</a></div><div class=\"code\">") 138 154 if templ_7745c5c3_Err != nil { 139 155 return templ_7745c5c3_Err 140 156 } ··· 142 158 if templ_7745c5c3_Err != nil { 143 159 return templ_7745c5c3_Err 144 160 } 145 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 161 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div>") 146 162 if templ_7745c5c3_Err != nil { 147 163 return templ_7745c5c3_Err 148 164 } 149 165 if len(results) > 1 { 150 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<details class=\"text-text cursor-pointer\"><summary>") 166 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<details class=\"text-text cursor-pointer\"><summary>") 151 167 if templ_7745c5c3_Err != nil { 152 168 return templ_7745c5c3_Err 153 169 } 154 170 var templ_7745c5c3_Var6 string 155 171 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d ", len(results[1:]))) 156 172 if templ_7745c5c3_Err != nil { 157 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 56, Col: 50} 173 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 54, Col: 50} 158 174 } 159 175 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) 160 176 if templ_7745c5c3_Err != nil { 161 177 return templ_7745c5c3_Err 162 178 } 163 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("more</summary> ") 179 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "more</summary> ") 164 180 if templ_7745c5c3_Err != nil { 165 181 return templ_7745c5c3_Err 166 182 } 167 183 for _, result := range results[1:] { 168 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-text mt-5 ml-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 184 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"text-text mt-5 ml-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 169 185 if templ_7745c5c3_Err != nil { 170 186 return templ_7745c5c3_Err 171 187 } 172 - var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", repo, ref, result.File, result.Line)) 173 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7))) 188 + var templ_7745c5c3_Var7 templ.SafeURL 189 + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", repo, ref, result.File, result.Line))) 190 + if templ_7745c5c3_Err != nil { 191 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 56, Col: 210} 192 + } 193 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) 174 194 if templ_7745c5c3_Err != nil { 175 195 return templ_7745c5c3_Err 176 196 } 177 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 197 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">") 178 198 if templ_7745c5c3_Err != nil { 179 199 return templ_7745c5c3_Err 180 200 } 181 201 var templ_7745c5c3_Var8 string 182 202 templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(results[0].File) 183 203 if templ_7745c5c3_Err != nil { 184 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 58, Col: 230} 204 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 56, Col: 230} 185 205 } 186 206 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 187 207 if templ_7745c5c3_Err != nil { 188 208 return templ_7745c5c3_Err 189 209 } 190 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"code ml-5\">") 210 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</a></div><div class=\"code ml-5\">") 191 211 if templ_7745c5c3_Err != nil { 192 212 return templ_7745c5c3_Err 193 213 } ··· 195 215 if templ_7745c5c3_Err != nil { 196 216 return templ_7745c5c3_Err 197 217 } 198 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 218 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div>") 199 219 if templ_7745c5c3_Err != nil { 200 220 return templ_7745c5c3_Err 201 221 } 202 222 } 203 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</details>") 223 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</details>") 204 224 if templ_7745c5c3_Err != nil { 205 225 return templ_7745c5c3_Err 206 226 } 207 227 } 208 - if !templ_7745c5c3_IsBuffer { 209 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 210 - } 211 - return templ_7745c5c3_Err 228 + return nil 212 229 }) 213 230 } 231 + 232 + var _ = templruntime.GeneratedTemplate
+65 -40
internal/html/repo_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import "fmt" 14 12 ··· 21 19 } 22 20 23 21 func repoHeaderComponent(rhcc RepoHeaderComponentContext) templ.Component { 24 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 25 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 22 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 23 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 24 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 25 + return templ_7745c5c3_CtxErr 26 + } 27 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 26 28 if !templ_7745c5c3_IsBuffer { 27 - templ_7745c5c3_Buffer = templ.GetBuffer() 28 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 29 + defer func() { 30 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 31 + if templ_7745c5c3_Err == nil { 32 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 33 + } 34 + }() 29 35 } 30 36 ctx = templ.InitializeContext(ctx) 31 37 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 33 39 templ_7745c5c3_Var1 = templ.NopComponent 34 40 } 35 41 ctx = templ.ClearChildren(ctx) 36 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mb-1 text-text\"><a class=\"text-lg underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 42 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"mb-1 text-text\"><a class=\"text-lg underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 37 43 if templ_7745c5c3_Err != nil { 38 44 return templ_7745c5c3_Err 39 45 } 40 - var templ_7745c5c3_Var2 templ.SafeURL = templ.SafeURL("/" + rhcc.Name) 41 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2))) 46 + var templ_7745c5c3_Var2 templ.SafeURL 47 + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/" + rhcc.Name)) 48 + if templ_7745c5c3_Err != nil { 49 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 15, Col: 128} 50 + } 51 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) 42 52 if templ_7745c5c3_Err != nil { 43 53 return templ_7745c5c3_Err 44 54 } 45 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 55 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">") 46 56 if templ_7745c5c3_Err != nil { 47 57 return templ_7745c5c3_Err 48 58 } ··· 55 65 if templ_7745c5c3_Err != nil { 56 66 return templ_7745c5c3_Err 57 67 } 58 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ") 68 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</a> ") 59 69 if templ_7745c5c3_Err != nil { 60 70 return templ_7745c5c3_Err 61 71 } ··· 69 79 if templ_7745c5c3_Err != nil { 70 80 return templ_7745c5c3_Err 71 81 } 72 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"text-text/80 text-sm underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 82 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " <a class=\"text-text/80 text-sm underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 73 83 if templ_7745c5c3_Err != nil { 74 84 return templ_7745c5c3_Err 75 85 } 76 - var templ_7745c5c3_Var5 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rhcc.Name, rhcc.Ref)) 77 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5))) 86 + var templ_7745c5c3_Var5 templ.SafeURL 87 + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/", rhcc.Name, rhcc.Ref))) 88 + if templ_7745c5c3_Err != nil { 89 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 18, Col: 175} 90 + } 91 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 78 92 if templ_7745c5c3_Err != nil { 79 93 return templ_7745c5c3_Err 80 94 } 81 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 95 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\">") 82 96 if templ_7745c5c3_Err != nil { 83 97 return templ_7745c5c3_Err 84 98 } ··· 91 105 if templ_7745c5c3_Err != nil { 92 106 return templ_7745c5c3_Err 93 107 } 94 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ") 108 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</a> ") 95 109 if templ_7745c5c3_Err != nil { 96 110 return templ_7745c5c3_Err 97 111 } ··· 105 119 if templ_7745c5c3_Err != nil { 106 120 return templ_7745c5c3_Err 107 121 } 108 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 122 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 109 123 if templ_7745c5c3_Err != nil { 110 124 return templ_7745c5c3_Err 111 125 } 112 - var templ_7745c5c3_Var8 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/refs", rhcc.Name)) 113 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8))) 126 + var templ_7745c5c3_Var8 templ.SafeURL 127 + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/refs", rhcc.Name))) 128 + if templ_7745c5c3_Err != nil { 129 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 21, Col: 139} 130 + } 131 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 114 132 if templ_7745c5c3_Err != nil { 115 133 return templ_7745c5c3_Err 116 134 } 117 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">refs</a> ") 135 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">refs</a> ") 118 136 if templ_7745c5c3_Err != nil { 119 137 return templ_7745c5c3_Err 120 138 } ··· 127 145 if templ_7745c5c3_Err != nil { 128 146 return templ_7745c5c3_Err 129 147 } 130 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 148 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 131 149 if templ_7745c5c3_Err != nil { 132 150 return templ_7745c5c3_Err 133 151 } 134 - var templ_7745c5c3_Var10 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/log/%s", rhcc.Name, rhcc.Ref)) 135 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var10))) 152 + var templ_7745c5c3_Var10 templ.SafeURL 153 + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/log/%s", rhcc.Name, rhcc.Ref))) 154 + if templ_7745c5c3_Err != nil { 155 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 23, Col: 151} 156 + } 157 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) 136 158 if templ_7745c5c3_Err != nil { 137 159 return templ_7745c5c3_Err 138 160 } 139 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">log</a> ") 161 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">log</a> ") 140 162 if templ_7745c5c3_Err != nil { 141 163 return templ_7745c5c3_Err 142 164 } ··· 149 171 if templ_7745c5c3_Err != nil { 150 172 return templ_7745c5c3_Err 151 173 } 152 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form class=\"inline-block\" action=\"") 174 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<form class=\"inline-block\" action=\"") 153 175 if templ_7745c5c3_Err != nil { 154 176 return templ_7745c5c3_Err 155 177 } 156 - var templ_7745c5c3_Var12 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name)) 157 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var12))) 178 + var templ_7745c5c3_Var12 templ.SafeURL 179 + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name))) 180 + if templ_7745c5c3_Err != nil { 181 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 25, Col: 89} 182 + } 183 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) 158 184 if templ_7745c5c3_Err != nil { 159 185 return templ_7745c5c3_Err 160 186 } 161 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" method=\"get\"><input class=\"rounded p-1 bg-mantle focus:border-lavender focus:outline-none focus:ring-0\" id=\"search\" type=\"text\" name=\"q\" placeholder=\"search\"></form>") 187 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" method=\"get\"><input class=\"rounded p-1 bg-mantle focus:border-lavender focus:outline-none focus:ring-0\" id=\"search\" type=\"text\" name=\"q\" placeholder=\"search\"></form>") 162 188 if templ_7745c5c3_Err != nil { 163 189 return templ_7745c5c3_Err 164 190 } ··· 171 197 if templ_7745c5c3_Err != nil { 172 198 return templ_7745c5c3_Err 173 199 } 174 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<pre class=\"text-text inline select-all bg-base dark:bg-base/50 p-1 rounded\">") 200 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<pre class=\"text-text inline select-all bg-base dark:bg-base/50 p-1 rounded\">") 175 201 if templ_7745c5c3_Err != nil { 176 202 return templ_7745c5c3_Err 177 203 } ··· 184 210 if templ_7745c5c3_Err != nil { 185 211 return templ_7745c5c3_Err 186 212 } 187 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</pre></div><div class=\"text-subtext0 mb-1\">") 213 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</pre></div><div class=\"text-subtext0 mb-1\">") 188 214 if templ_7745c5c3_Err != nil { 189 215 return templ_7745c5c3_Err 190 216 } 191 217 for _, tag := range rhcc.Tags { 192 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block\">") 218 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<span class=\"rounded border-rosewater border-solid border pb-0.5 px-1 mr-1 mb-1 inline-block\">") 193 219 if templ_7745c5c3_Err != nil { 194 220 return templ_7745c5c3_Err 195 221 } ··· 202 228 if templ_7745c5c3_Err != nil { 203 229 return templ_7745c5c3_Err 204 230 } 205 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>") 231 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span>") 206 232 if templ_7745c5c3_Err != nil { 207 233 return templ_7745c5c3_Err 208 234 } 209 235 } 210 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"text-text/80 mb-1\">") 236 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</div><div class=\"text-text/80 mb-1\">") 211 237 if templ_7745c5c3_Err != nil { 212 238 return templ_7745c5c3_Err 213 239 } ··· 220 246 if templ_7745c5c3_Err != nil { 221 247 return templ_7745c5c3_Err 222 248 } 223 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 249 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div>") 224 250 if templ_7745c5c3_Err != nil { 225 251 return templ_7745c5c3_Err 226 252 } 227 - if !templ_7745c5c3_IsBuffer { 228 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 229 - } 230 - return templ_7745c5c3_Err 253 + return nil 231 254 }) 232 255 } 256 + 257 + var _ = templruntime.GeneratedTemplate
-1
internal/html/repo_tree.templ
··· 11 11 RepoBreadcrumbComponentContext 12 12 RepoTreeComponentContext 13 13 ReadmeComponentContext 14 - Description string 15 14 } 16 15 17 16 templ RepoTree(rtc RepoTreeContext) {
+68 -48
internal/html/repo_tree_templ.go
··· 1 1 // Code generated by templ - DO NOT EDIT. 2 2 3 - // templ: version: v0.2.707 3 + // templ: version: v0.3.924 4 4 package html 5 5 6 6 //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 7 8 8 import "github.com/a-h/templ" 9 - import "context" 10 - import "io" 11 - import "bytes" 9 + import templruntime "github.com/a-h/templ/runtime" 12 10 13 11 import ( 14 12 "fmt" ··· 21 19 RepoBreadcrumbComponentContext 22 20 RepoTreeComponentContext 23 21 ReadmeComponentContext 24 - Description string 25 22 } 26 23 27 24 func RepoTree(rtc RepoTreeContext) templ.Component { 28 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 29 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 25 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 26 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 27 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 28 + return templ_7745c5c3_CtxErr 29 + } 30 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 30 31 if !templ_7745c5c3_IsBuffer { 31 - templ_7745c5c3_Buffer = templ.GetBuffer() 32 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 32 + defer func() { 33 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 34 + if templ_7745c5c3_Err == nil { 35 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 36 + } 37 + }() 33 38 } 34 39 ctx = templ.InitializeContext(ctx) 35 40 templ_7745c5c3_Var1 := templ.GetChildren(ctx) ··· 37 42 templ_7745c5c3_Var1 = templ.NopComponent 38 43 } 39 44 ctx = templ.ClearChildren(ctx) 40 - templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 41 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 45 + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 46 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 47 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 42 48 if !templ_7745c5c3_IsBuffer { 43 - templ_7745c5c3_Buffer = templ.GetBuffer() 44 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 49 + defer func() { 50 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 51 + if templ_7745c5c3_Err == nil { 52 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 53 + } 54 + }() 45 55 } 56 + ctx = templ.InitializeContext(ctx) 46 57 templ_7745c5c3_Err = repoHeaderComponent(rtc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 47 58 if templ_7745c5c3_Err != nil { 48 59 return templ_7745c5c3_Err 49 60 } 50 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 61 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ") 51 62 if templ_7745c5c3_Err != nil { 52 63 return templ_7745c5c3_Err 53 64 } ··· 55 66 if templ_7745c5c3_Err != nil { 56 67 return templ_7745c5c3_Err 57 68 } 58 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 69 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ") 59 70 if templ_7745c5c3_Err != nil { 60 71 return templ_7745c5c3_Err 61 72 } ··· 63 74 if templ_7745c5c3_Err != nil { 64 75 return templ_7745c5c3_Err 65 76 } 66 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") 77 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " ") 67 78 if templ_7745c5c3_Err != nil { 68 79 return templ_7745c5c3_Err 69 80 } ··· 71 82 if templ_7745c5c3_Err != nil { 72 83 return templ_7745c5c3_Err 73 84 } 74 - if !templ_7745c5c3_IsBuffer { 75 - _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 76 - } 77 - return templ_7745c5c3_Err 85 + return nil 78 86 }) 79 87 templ_7745c5c3_Err = base(rtc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 80 88 if templ_7745c5c3_Err != nil { 81 89 return templ_7745c5c3_Err 82 90 } 83 - if !templ_7745c5c3_IsBuffer { 84 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 85 - } 86 - return templ_7745c5c3_Err 91 + return nil 87 92 }) 88 93 } 89 94 ··· 102 107 } 103 108 104 109 func repoTreeComponent(rtcc RepoTreeComponentContext) templ.Component { 105 - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 106 - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 110 + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { 111 + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context 112 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 113 + return templ_7745c5c3_CtxErr 114 + } 115 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) 107 116 if !templ_7745c5c3_IsBuffer { 108 - templ_7745c5c3_Buffer = templ.GetBuffer() 109 - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 117 + defer func() { 118 + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) 119 + if templ_7745c5c3_Err == nil { 120 + templ_7745c5c3_Err = templ_7745c5c3_BufErr 121 + } 122 + }() 110 123 } 111 124 ctx = templ.InitializeContext(ctx) 112 125 templ_7745c5c3_Var3 := templ.GetChildren(ctx) ··· 114 127 templ_7745c5c3_Var3 = templ.NopComponent 115 128 } 116 129 ctx = templ.ClearChildren(ctx) 117 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid grid-cols-3 sm:grid-cols-8 text-text py-5 rounded px-5 gap-x-3 gap-y-1 bg-base dark:bg-base/50\">") 130 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"grid grid-cols-3 sm:grid-cols-8 text-text py-5 rounded px-5 gap-x-3 gap-y-1 bg-base dark:bg-base/50\">") 118 131 if templ_7745c5c3_Err != nil { 119 132 return templ_7745c5c3_Err 120 133 } 121 134 if rtcc.Back != "" { 122 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col-span-2\"></div><div class=\"sm:col-span-6\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 135 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"col-span-2\"></div><div class=\"sm:col-span-6\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 123 136 if templ_7745c5c3_Err != nil { 124 137 return templ_7745c5c3_Err 125 138 } 126 - var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rtcc.Repo, rtcc.Ref, rtcc.Back)) 127 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) 139 + var templ_7745c5c3_Var4 templ.SafeURL 140 + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rtcc.Repo, rtcc.Ref, rtcc.Back))) 141 + if templ_7745c5c3_Err != nil { 142 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 43, Col: 194} 143 + } 144 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 128 145 if templ_7745c5c3_Err != nil { 129 146 return templ_7745c5c3_Err 130 147 } 131 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">..</a></div>") 148 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">..</a></div>") 132 149 if templ_7745c5c3_Err != nil { 133 150 return templ_7745c5c3_Err 134 151 } 135 152 } 136 153 for _, fi := range rtcc.Tree { 137 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"sm:col-span-1 break-keep\">") 154 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"sm:col-span-1 break-keep\">") 138 155 if templ_7745c5c3_Err != nil { 139 156 return templ_7745c5c3_Err 140 157 } 141 158 var templ_7745c5c3_Var5 string 142 159 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fi.Mode) 143 160 if templ_7745c5c3_Err != nil { 144 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 47, Col: 50} 161 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 46, Col: 50} 145 162 } 146 163 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 147 164 if templ_7745c5c3_Err != nil { 148 165 return templ_7745c5c3_Err 149 166 } 150 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"sm:col-span-1 text-right\">") 167 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><div class=\"sm:col-span-1 text-right\">") 151 168 if templ_7745c5c3_Err != nil { 152 169 return templ_7745c5c3_Err 153 170 } 154 171 var templ_7745c5c3_Var6 string 155 172 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fi.Size) 156 173 if templ_7745c5c3_Err != nil { 157 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 48, Col: 50} 174 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 47, Col: 50} 158 175 } 159 176 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) 160 177 if templ_7745c5c3_Err != nil { 161 178 return templ_7745c5c3_Err 162 179 } 163 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"sm:col-span-6 overflow-hidden text-ellipsis\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 180 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div><div class=\"sm:col-span-6 overflow-hidden text-ellipsis\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 164 181 if templ_7745c5c3_Err != nil { 165 182 return templ_7745c5c3_Err 166 183 } 167 - var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rtcc.Repo, rtcc.Ref, fi.Path)) 168 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7))) 184 + var templ_7745c5c3_Var7 templ.SafeURL 185 + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rtcc.Repo, rtcc.Ref, fi.Path))) 186 + if templ_7745c5c3_Err != nil { 187 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 48, Col: 222} 188 + } 189 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) 169 190 if templ_7745c5c3_Err != nil { 170 191 return templ_7745c5c3_Err 171 192 } 172 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 193 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\">") 173 194 if templ_7745c5c3_Err != nil { 174 195 return templ_7745c5c3_Err 175 196 } 176 197 var templ_7745c5c3_Var8 string 177 198 templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(slashDir(fi.Name(), fi.IsDir)) 178 199 if templ_7745c5c3_Err != nil { 179 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 49, Col: 256} 200 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 48, Col: 256} 180 201 } 181 202 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 182 203 if templ_7745c5c3_Err != nil { 183 204 return templ_7745c5c3_Err 184 205 } 185 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div>") 206 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</a></div>") 186 207 if templ_7745c5c3_Err != nil { 187 208 return templ_7745c5c3_Err 188 209 } 189 210 } 190 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 211 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div>") 191 212 if templ_7745c5c3_Err != nil { 192 213 return templ_7745c5c3_Err 193 214 } 194 - if !templ_7745c5c3_IsBuffer { 195 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 196 - } 197 - return templ_7745c5c3_Err 215 + return nil 198 216 }) 199 217 } 218 + 219 + var _ = templruntime.GeneratedTemplate
+1 -1
internal/html/tailwind.config.js
··· 1 1 /** @type {import('tailwindcss').Config} */ 2 2 module.exports = { 3 - content: ["./**/*.templ"], 3 + content: ["./*.go"], 4 4 plugins: [require("@catppuccin/tailwindcss")], 5 5 } 6 6
+1 -1
internal/html/tailwind.go
··· 5 5 6 6 func TailwindHandler(w http.ResponseWriter, r *http.Request) { 7 7 w.Header().Set("Content-Type", "text/css") 8 - w.Write([]byte("/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.latte{--ctp-rosewater:220,138,120;--ctp-flamingo:221,120,120;--ctp-pink:234,118,203;--ctp-mauve:136,57,239;--ctp-red:210,15,57;--ctp-maroon:230,69,83;--ctp-peach:254,100,11;--ctp-yellow:223,142,29;--ctp-green:64,160,43;--ctp-teal:23,146,153;--ctp-sky:4,165,229;--ctp-sapphire:32,159,181;--ctp-blue:30,102,245;--ctp-lavender:114,135,253;--ctp-text:76,79,105;--ctp-subtext1:92,95,119;--ctp-subtext0:108,111,133;--ctp-overlay2:124,127,147;--ctp-overlay1:140,143,161;--ctp-overlay0:156,160,176;--ctp-surface2:172,176,190;--ctp-surface1:188,192,204;--ctp-surface0:204,208,218;--ctp-base:239,241,245;--ctp-mantle:230,233,239;--ctp-crust:220,224,232}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-5{margin-left:1.25rem}.mr-1{margin-right:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.inline-block{display:inline-block}.inline{display:inline}.grid{display:grid}.h-5{height:1.25rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-1{row-gap:.25rem}.overflow-hidden{overflow:hidden}.text-ellipsis{text-overflow:ellipsis}.whitespace-pre{white-space:pre}.break-keep{word-break:keep-all}.rounded{border-radius:.25rem}.border{border-width:1px}.border-solid{border-style:solid}.border-rosewater{--tw-border-opacity:1;border-color:rgba(var(--ctp-rosewater),var(--tw-border-opacity))}.bg-base{--tw-bg-opacity:1;background-color:rgba(var(--ctp-base),var(--tw-bg-opacity))}.bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.bg-mantle{--tw-bg-opacity:1;background-color:rgba(var(--ctp-mantle),var(--tw-bg-opacity))}.stroke-mauve{stroke:rgb(var(--ctp-mauve))}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pb-0{padding-bottom:0}.pb-0\\.5{padding-bottom:.125rem}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-blue{--tw-text-opacity:1;color:rgba(var(--ctp-blue),var(--tw-text-opacity))}.text-mauve{--tw-text-opacity:1;color:rgba(var(--ctp-mauve),var(--tw-text-opacity))}.text-subtext0{--tw-text-opacity:1;color:rgba(var(--ctp-subtext0),var(--tw-text-opacity))}.text-subtext1{--tw-text-opacity:1;color:rgba(var(--ctp-subtext1),var(--tw-text-opacity))}.text-text{--tw-text-opacity:1;color:rgba(var(--ctp-text),var(--tw-text-opacity))}.text-text\\/80{color:rgba(var(--ctp-text),.8)}.underline{text-decoration-line:underline}.decoration-blue\\/50{text-decoration-color:rgba(var(--ctp-blue),.5)}.decoration-mauve\\/50{text-decoration-color:rgba(var(--ctp-mauve),.5)}.decoration-text\\/50{text-decoration-color:rgba(var(--ctp-text),.5)}.decoration-dashed{text-decoration-style:dashed}.markdown *{all:revert-layer;color:rgb(var(--ctp-text))}.markdown code,.markdown pre{background-color:rgb(var(--ctp-base))}.markdown a{color:rgb(var(--ctp-blue));text-decoration-line:underline;text-decoration-style:dashed}.markdown a:hover{text-decoration-style:solid}.markdown .chroma{border-radius:.25rem;padding:.75rem}.chroma *{background-color:rgb(var(--ctp-base))!important}.chroma table{border-spacing:5px 0!important}.chroma .lnt{color:rgb(var(--ctp-subtext1))!important}.chroma .lnt:focus,.chroma .lnt:target{color:rgb(var(--ctp-subtext0))!important}.chroma .line.active,.chroma .line.active *{background:rgb(var(--ctp-surface0))!important}.code>.chroma{overflow:scroll;border-radius:.25rem;padding:.75rem;font-size:.875rem;line-height:1.25rem}.chroma .line{overflow:scroll}.bg,.chroma{color:#4c4f69;background-color:#eff1f5}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#bcc0cc;background-color:#eff1f5}.chroma .err{color:#d20f39}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#bcc0cc}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8c8fa1}.chroma .line{display:flex}.chroma .k{color:#8839ef}.chroma .kc{color:#fe640b}.chroma .kd{color:#d20f39}.chroma .kn{color:#179299}.chroma .kp,.chroma .kr{color:#8839ef}.chroma .kt{color:#d20f39}.chroma .na{color:#1e66f5}.chroma .bp,.chroma .nb{color:#04a5e5}.chroma .nc,.chroma .no{color:#df8e1d}.chroma .nd{color:#1e66f5;font-weight:700}.chroma .ni{color:#179299}.chroma .ne{color:#fe640b}.chroma .fm,.chroma .nf{color:#1e66f5}.chroma .nl{color:#04a5e5}.chroma .nn,.chroma .py{color:#fe640b}.chroma .nt{color:#8839ef}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#dc8a78}.chroma .s{color:#40a02b}.chroma .sa{color:#d20f39}.chroma .sb,.chroma .sc{color:#40a02b}.chroma .dl{color:#1e66f5}.chroma .sd{color:#9ca0b0}.chroma .s2{color:#40a02b}.chroma .se{color:#1e66f5}.chroma .sh{color:#9ca0b0}.chroma .si,.chroma .sx{color:#40a02b}.chroma .sr{color:#179299}.chroma .s1,.chroma .ss{color:#40a02b}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fe640b}.chroma .o,.chroma .ow{color:#04a5e5;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#9ca0b0;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#d20f39;background-color:#ccd0da}.chroma .ge{font-style:italic}.chroma .gr{color:#d20f39}.chroma .gh{color:#fe640b;font-weight:700}.chroma .gi{color:#40a02b;background-color:#ccd0da}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fe640b}.chroma .gt{color:#d20f39}.chroma .gl{text-decoration:underline}@media (prefers-color-scheme:dark){.bg,.chroma{color:#cdd6f4;background-color:#1e1e2e}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#45475a;background-color:#1e1e2e}.chroma .err{color:#f38ba8}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#45475a}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f849c}.chroma .line{display:flex}.chroma .k{color:#cba6f7}.chroma .kc{color:#fab387}.chroma .kd{color:#f38ba8}.chroma .kn{color:#94e2d5}.chroma .kp,.chroma .kr{color:#cba6f7}.chroma .kt{color:#f38ba8}.chroma .na{color:#89b4fa}.chroma .bp,.chroma .nb{color:#89dceb}.chroma .nc,.chroma .no{color:#f9e2af}.chroma .nd{color:#89b4fa;font-weight:700}.chroma .ni{color:#94e2d5}.chroma .ne{color:#fab387}.chroma .fm,.chroma .nf{color:#89b4fa}.chroma .nl{color:#89dceb}.chroma .nn,.chroma .py{color:#fab387}.chroma .nt{color:#cba6f7}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#f5e0dc}.chroma .s{color:#a6e3a1}.chroma .sa{color:#f38ba8}.chroma .sb,.chroma .sc{color:#a6e3a1}.chroma .dl{color:#89b4fa}.chroma .sd{color:#6c7086}.chroma .s2{color:#a6e3a1}.chroma .se{color:#89b4fa}.chroma .sh{color:#6c7086}.chroma .si,.chroma .sx{color:#a6e3a1}.chroma .sr{color:#94e2d5}.chroma .s1,.chroma .ss{color:#a6e3a1}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fab387}.chroma .o,.chroma .ow{color:#89dceb;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#6c7086;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#f38ba8;background-color:#313244}.chroma .ge{font-style:italic}.chroma .gr{color:#f38ba8}.chroma .gh{color:#fab387;font-weight:700}.chroma .gi{color:#a6e3a1;background-color:#313244}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fab387}.chroma .gt{color:#f38ba8}.chroma .gl{text-decoration:underline}.dark\\:mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}}.hover\\:decoration-solid:hover{text-decoration-style:solid}.focus\\:border-lavender:focus{--tw-border-opacity:1;border-color:rgba(var(--ctp-lavender),var(--tw-border-opacity))}.focus\\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}@media (prefers-color-scheme:dark){.dark\\:bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.dark\\:bg-base\\/95{background-color:rgba(var(--ctp-base),.95)}.dark\\:text-lavender{--tw-text-opacity:1;color:rgba(var(--ctp-lavender),var(--tw-text-opacity))}.dark\\:decoration-lavender\\/50{text-decoration-color:rgba(var(--ctp-lavender),.5)}}@media (min-width:640px){.sm\\:col-span-1{grid-column:span 1/span 1}.sm\\:col-span-2{grid-column:span 2/span 2}.sm\\:col-span-3{grid-column:span 3/span 3}.sm\\:col-span-4{grid-column:span 4/span 4}.sm\\:col-span-5{grid-column:span 5/span 5}.sm\\:col-span-6{grid-column:span 6/span 6}.sm\\:col-span-7{grid-column:span 7/span 7}.sm\\:mx-auto{margin-left:auto;margin-right:auto}.sm\\:mb-0{margin-bottom:0}.sm\\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}}")) 8 + w.Write([]byte("/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.latte{--ctp-rosewater:220,138,120;--ctp-flamingo:221,120,120;--ctp-pink:234,118,203;--ctp-mauve:136,57,239;--ctp-red:210,15,57;--ctp-maroon:230,69,83;--ctp-peach:254,100,11;--ctp-yellow:223,142,29;--ctp-green:64,160,43;--ctp-teal:23,146,153;--ctp-sky:4,165,229;--ctp-sapphire:32,159,181;--ctp-blue:30,102,245;--ctp-lavender:114,135,253;--ctp-text:76,79,105;--ctp-subtext1:92,95,119;--ctp-subtext0:108,111,133;--ctp-overlay2:124,127,147;--ctp-overlay1:140,143,161;--ctp-overlay0:156,160,176;--ctp-surface2:172,176,190;--ctp-surface1:188,192,204;--ctp-surface0:204,208,218;--ctp-base:239,241,245;--ctp-mantle:230,233,239;--ctp-crust:220,224,232}.mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.absolute{position:absolute}.relative{position:relative}.right-0{right:0}.start-1{inset-inline-start:.25rem}.top-0{top:0}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-5{margin-left:1.25rem}.mr-1{margin-right:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-1{row-gap:.25rem}.overflow-hidden{overflow:hidden}.text-ellipsis{text-overflow:ellipsis}.whitespace-pre{white-space:pre}.break-keep{word-break:keep-all}.rounded{border-radius:.25rem}.border{border-width:1px}.border-solid{border-style:solid}.border-rosewater{--tw-border-opacity:1;border-color:rgba(var(--ctp-rosewater),var(--tw-border-opacity))}.bg-base{--tw-bg-opacity:1;background-color:rgba(var(--ctp-base),var(--tw-bg-opacity))}.bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.bg-mantle{--tw-bg-opacity:1;background-color:rgba(var(--ctp-mantle),var(--tw-bg-opacity))}.stroke-mauve{stroke:rgb(var(--ctp-mauve))}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pb-0{padding-bottom:0}.pb-0\\.5{padding-bottom:.125rem}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-blue{--tw-text-opacity:1;color:rgba(var(--ctp-blue),var(--tw-text-opacity))}.text-mauve{--tw-text-opacity:1;color:rgba(var(--ctp-mauve),var(--tw-text-opacity))}.text-subtext0{--tw-text-opacity:1;color:rgba(var(--ctp-subtext0),var(--tw-text-opacity))}.text-subtext1{--tw-text-opacity:1;color:rgba(var(--ctp-subtext1),var(--tw-text-opacity))}.text-text{--tw-text-opacity:1;color:rgba(var(--ctp-text),var(--tw-text-opacity))}.text-text\\/80{color:rgba(var(--ctp-text),.8)}.underline{text-decoration-line:underline}.decoration-blue\\/50{text-decoration-color:rgba(var(--ctp-blue),.5)}.decoration-mauve\\/50{text-decoration-color:rgba(var(--ctp-mauve),.5)}.decoration-text\\/50{text-decoration-color:rgba(var(--ctp-text),.5)}.decoration-dashed{text-decoration-style:dashed}.markdown *{all:revert-layer;color:rgb(var(--ctp-text))}.markdown code,.markdown pre{background-color:rgb(var(--ctp-base))}.markdown a{color:rgb(var(--ctp-blue));text-decoration-line:underline;text-decoration-style:dashed}.markdown a:hover{text-decoration-style:solid}.markdown .chroma{border-radius:.25rem;padding:.75rem}.chroma *{background-color:rgb(var(--ctp-base))!important}.chroma table{border-spacing:5px 0!important}.chroma .lnt{color:rgb(var(--ctp-subtext1))!important}.chroma .lnt:focus,.chroma .lnt:target{color:rgb(var(--ctp-subtext0))!important}.chroma .line.active,.chroma .line.active *{background:rgb(var(--ctp-surface0))!important}.code>.chroma{overflow:scroll;border-radius:.25rem;padding:.75rem;font-size:.875rem;line-height:1.25rem}.bg,.chroma{color:#4c4f69;background-color:#eff1f5}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#4c4f69;background-color:#bcc0cc}.chroma .err{color:#d20f39}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{background-color:#bcc0cc}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8c8fa1}.chroma .line{display:flex}.chroma .k{color:#8839ef}.chroma .kc{color:#fe640b}.chroma .kd{color:#d20f39}.chroma .kn{color:#179299}.chroma .kp,.chroma .kr{color:#8839ef}.chroma .kt{color:#d20f39}.chroma .na{color:#1e66f5}.chroma .bp,.chroma .nb{color:#04a5e5}.chroma .nc,.chroma .no{color:#df8e1d}.chroma .nd{color:#1e66f5;font-weight:700}.chroma .ni{color:#179299}.chroma .ne{color:#fe640b}.chroma .fm,.chroma .nf{color:#1e66f5}.chroma .nl{color:#04a5e5}.chroma .nn,.chroma .py{color:#fe640b}.chroma .nt{color:#8839ef}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#dc8a78}.chroma .s{color:#40a02b}.chroma .sa{color:#d20f39}.chroma .sb,.chroma .sc{color:#40a02b}.chroma .dl{color:#1e66f5}.chroma .sd{color:#9ca0b0}.chroma .s2{color:#40a02b}.chroma .se{color:#1e66f5}.chroma .sh{color:#9ca0b0}.chroma .si,.chroma .sx{color:#40a02b}.chroma .sr{color:#179299}.chroma .s1,.chroma .ss{color:#40a02b}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fe640b}.chroma .o,.chroma .ow{color:#04a5e5;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#9ca0b0;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#d20f39;background-color:#ccd0da}.chroma .ge{font-style:italic}.chroma .gr{color:#d20f39}.chroma .gh{color:#fe640b;font-weight:700}.chroma .gi{color:#40a02b;background-color:#ccd0da}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fe640b}.chroma .gt{color:#d20f39}.chroma .gl{text-decoration:underline}@media (prefers-color-scheme:dark){.bg,.chroma{color:#cdd6f4;background-color:#1e1e2e}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#cdd6f4;background-color:#45475a}.chroma .err{color:#f38ba8}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{background-color:#45475a}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f849c}.chroma .line{display:flex}.chroma .k{color:#cba6f7}.chroma .kc{color:#fab387}.chroma .kd{color:#f38ba8}.chroma .kn{color:#94e2d5}.chroma .kp,.chroma .kr{color:#cba6f7}.chroma .kt{color:#f38ba8}.chroma .na{color:#89b4fa}.chroma .bp,.chroma .nb{color:#89dceb}.chroma .nc,.chroma .no{color:#f9e2af}.chroma .nd{color:#89b4fa;font-weight:700}.chroma .ni{color:#94e2d5}.chroma .ne{color:#fab387}.chroma .fm,.chroma .nf{color:#89b4fa}.chroma .nl{color:#89dceb}.chroma .nn,.chroma .py{color:#fab387}.chroma .nt{color:#cba6f7}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#f5e0dc}.chroma .s{color:#a6e3a1}.chroma .sa{color:#f38ba8}.chroma .sb,.chroma .sc{color:#a6e3a1}.chroma .dl{color:#89b4fa}.chroma .sd{color:#6c7086}.chroma .s2{color:#a6e3a1}.chroma .se{color:#89b4fa}.chroma .sh{color:#6c7086}.chroma .si,.chroma .sx{color:#a6e3a1}.chroma .sr{color:#94e2d5}.chroma .s1,.chroma .ss{color:#a6e3a1}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fab387}.chroma .o,.chroma .ow{color:#89dceb;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#6c7086;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#f38ba8;background-color:#313244}.chroma .ge{font-style:italic}.chroma .gr{color:#f38ba8}.chroma .gh{color:#fab387;font-weight:700}.chroma .gi{color:#a6e3a1;background-color:#313244}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fab387}.chroma .gt{color:#f38ba8}.chroma .gl{text-decoration:underline}.dark\\:mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}}.hover\\:bg-surface0:hover{--tw-bg-opacity:1;background-color:rgba(var(--ctp-surface0),var(--tw-bg-opacity))}.hover\\:decoration-solid:hover{text-decoration-style:solid}.focus\\:border-lavender:focus{--tw-border-opacity:1;border-color:rgba(var(--ctp-lavender),var(--tw-border-opacity))}.focus\\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}@media (prefers-color-scheme:dark){.dark\\:bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.dark\\:bg-base\\/95{background-color:rgba(var(--ctp-base),.95)}.dark\\:text-lavender{--tw-text-opacity:1;color:rgba(var(--ctp-lavender),var(--tw-text-opacity))}.dark\\:decoration-lavender\\/50{text-decoration-color:rgba(var(--ctp-lavender),.5)}}@media (min-width:640px){.sm\\:col-span-1{grid-column:span 1/span 1}.sm\\:col-span-2{grid-column:span 2/span 2}.sm\\:col-span-3{grid-column:span 3/span 3}.sm\\:col-span-5{grid-column:span 5/span 5}.sm\\:col-span-6{grid-column:span 6/span 6}.sm\\:col-span-7{grid-column:span 7/span 7}.sm\\:mx-auto{margin-left:auto;margin-right:auto}.sm\\:mb-0{margin-bottom:0}.sm\\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}}")) 9 9 }
+12 -4
internal/http/http.go
··· 18 18 // Server is the container struct for the HTTP server 19 19 type Server struct { 20 20 port int 21 - mux *chi.Mux 21 + Mux *chi.Mux 22 22 } 23 23 24 24 // ListenAndServe simply wraps http.ListenAndServe to contain the functionality here 25 25 func (s Server) ListenAndServe() error { 26 - return http.ListenAndServe(fmt.Sprintf("localhost:%d", s.port), s.mux) 26 + return http.ListenAndServe(fmt.Sprintf("localhost:%d", s.port), s.Mux) 27 27 } 28 28 29 29 // Settings is the configuration for the HTTP server ··· 34 34 Port int 35 35 RepoDir string 36 36 Profile Profile 37 + ShowPrivate bool 37 38 } 38 39 39 40 // Profile is the index profile ··· 101 102 r.Get("/tailwind.css", html.TailwindHandler) 102 103 }) 103 104 104 - return Server{mux: mux, port: settings.Port} 105 + return Server{Mux: mux, port: settings.Port} 105 106 } 106 107 107 108 type repoHandler struct { ··· 115 116 } 116 117 } 117 118 119 + func (rh repoHandler) repoBaseContext(repo *git.Repo) html.BaseContext { 120 + return html.BaseContext{ 121 + Title: repo.Name(), 122 + Description: repo.Meta.Description, 123 + } 124 + } 125 + 118 126 func (rh repoHandler) repoHeaderContext(repo *git.Repo, r *http.Request) html.RepoHeaderComponentContext { 119 127 ref := chi.URLParam(r, "ref") 120 128 if ref == "" { ··· 125 133 Name: chi.URLParam(r, "repo"), 126 134 Ref: ref, 127 135 CloneURL: rh.s.CloneURL, 128 - Tags: repo.Meta.Tags, 136 + Tags: repo.Meta.Tags.Slice(), 129 137 } 130 138 } 131 139
+2 -3
internal/http/httperr/httperr.go
··· 2 2 3 3 import ( 4 4 "errors" 5 + "log/slog" 5 6 "net/http" 6 - 7 - "github.com/charmbracelet/log" 8 7 ) 9 8 10 9 type httpError struct { ··· 41 40 status = httpErr.status 42 41 } 43 42 44 - log.Error(err) 43 + slog.Error("httperr Handler error", "error", err) 45 44 http.Error(w, http.StatusText(status), status) 46 45 } 47 46 }
+122
internal/http/httperr/httperr_test.go
··· 1 + package httperr_test 2 + 3 + import ( 4 + "errors" 5 + "net/http" 6 + "net/http/httptest" 7 + "testing" 8 + 9 + "github.com/alecthomas/assert/v2" 10 + "go.jolheiser.com/ugit/internal/http/httperr" 11 + ) 12 + 13 + func successHandler(w http.ResponseWriter, r *http.Request) error { 14 + w.WriteHeader(http.StatusOK) 15 + return nil 16 + } 17 + 18 + func errorHandler(w http.ResponseWriter, r *http.Request) error { 19 + return errors.New("test error") 20 + } 21 + 22 + func statusErrorHandler(status int) func(w http.ResponseWriter, r *http.Request) error { 23 + return func(w http.ResponseWriter, r *http.Request) error { 24 + return httperr.Status(errors.New("test error"), status) 25 + } 26 + } 27 + 28 + func TestHandler_Success(t *testing.T) { 29 + handler := httperr.Handler(successHandler) 30 + 31 + req := httptest.NewRequest("GET", "/", nil) 32 + recorder := httptest.NewRecorder() 33 + 34 + handler.ServeHTTP(recorder, req) 35 + 36 + assert.Equal(t, http.StatusOK, recorder.Code) 37 + } 38 + 39 + func TestHandler_Error(t *testing.T) { 40 + handler := httperr.Handler(errorHandler) 41 + 42 + req := httptest.NewRequest("GET", "/", nil) 43 + recorder := httptest.NewRecorder() 44 + 45 + handler.ServeHTTP(recorder, req) 46 + 47 + assert.Equal(t, http.StatusInternalServerError, recorder.Code) 48 + } 49 + 50 + func TestHandler_StatusError(t *testing.T) { 51 + testCases := []struct { 52 + name string 53 + status int 54 + expectedStatus int 55 + }{ 56 + { 57 + name: "not found", 58 + status: http.StatusNotFound, 59 + expectedStatus: http.StatusNotFound, 60 + }, 61 + { 62 + name: "bad request", 63 + status: http.StatusBadRequest, 64 + expectedStatus: http.StatusBadRequest, 65 + }, 66 + { 67 + name: "unauthorized", 68 + status: http.StatusUnauthorized, 69 + expectedStatus: http.StatusUnauthorized, 70 + }, 71 + } 72 + 73 + for _, tc := range testCases { 74 + t.Run(tc.name, func(t *testing.T) { 75 + handler := httperr.Handler(statusErrorHandler(tc.status)) 76 + 77 + req := httptest.NewRequest("GET", "/", nil) 78 + recorder := httptest.NewRecorder() 79 + 80 + handler.ServeHTTP(recorder, req) 81 + 82 + assert.Equal(t, tc.expectedStatus, recorder.Code) 83 + }) 84 + } 85 + } 86 + 87 + type unwrapper interface { 88 + Unwrap() error 89 + } 90 + 91 + func TestError(t *testing.T) { 92 + originalErr := errors.New("original error") 93 + httpErr := httperr.Error(originalErr) 94 + 95 + assert.Equal(t, originalErr.Error(), httpErr.Error()) 96 + 97 + unwrapper, ok := any(httpErr).(unwrapper) 98 + assert.True(t, ok) 99 + assert.Equal(t, originalErr, unwrapper.Unwrap()) 100 + } 101 + 102 + func TestStatus(t *testing.T) { 103 + originalErr := errors.New("original error") 104 + httpErr := httperr.Status(originalErr, http.StatusNotFound) 105 + 106 + assert.Equal(t, originalErr.Error(), httpErr.Error()) 107 + 108 + unwrapper, ok := any(httpErr).(unwrapper) 109 + assert.True(t, ok) 110 + assert.Equal(t, originalErr, unwrapper.Unwrap()) 111 + 112 + handler := httperr.Handler(func(w http.ResponseWriter, r *http.Request) error { 113 + return httpErr 114 + }) 115 + 116 + req := httptest.NewRequest("GET", "/", nil) 117 + recorder := httptest.NewRecorder() 118 + 119 + handler.ServeHTTP(recorder, req) 120 + 121 + assert.Equal(t, http.StatusNotFound, recorder.Code) 122 + }
+6 -3
internal/http/index.go
··· 3 3 import ( 4 4 "net/http" 5 5 "os" 6 - "slices" 7 6 "sort" 8 7 "strings" 9 8 "time" ··· 31 30 return httperr.Error(err) 32 31 } 33 32 if repo.Meta.Private { 34 - continue 33 + if !rh.s.ShowPrivate { 34 + continue 35 + } 36 + repo.Meta.Tags.Add("private") 35 37 } 36 - if tagFilter != "" && !slices.Contains(repo.Meta.Tags, strings.ToLower(tagFilter)) { 38 + 39 + if tagFilter != "" && !repo.Meta.Tags.Contains(strings.ToLower(tagFilter)) { 37 40 continue 38 41 } 39 42 repos = append(repos, repo)
+4 -1
internal/http/middleware.go
··· 27 27 return httperr.Status(err, httpErr) 28 28 } 29 29 if repo.Meta.Private { 30 - return httperr.Status(errors.New("could not get git repo"), http.StatusNotFound) 30 + if !rh.s.ShowPrivate { 31 + return httperr.Status(errors.New("could not get git repo"), http.StatusNotFound) 32 + } 33 + repo.Meta.Tags.Add("private") 31 34 } 32 35 r = r.WithContext(context.WithValue(r.Context(), repoCtxKey, repo)) 33 36 next.ServeHTTP(w, r)
+18 -10
internal/http/repo.go
··· 48 48 back = filepath.Dir(path) 49 49 } 50 50 if err := html.RepoTree(html.RepoTreeContext{ 51 - Description: repo.Meta.Description, 52 - BaseContext: rh.baseContext(), 51 + BaseContext: rh.repoBaseContext(repo), 53 52 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 54 53 RepoBreadcrumbComponentContext: rh.repoBreadcrumbContext(repo, r, path), 55 54 RepoTreeComponentContext: html.RepoTreeComponentContext{ ··· 88 87 } 89 88 90 89 var buf bytes.Buffer 91 - if err := markup.Code.Convert([]byte(content), filepath.Base(path), &buf); err != nil { 90 + if err := markup.Convert([]byte(content), filepath.Base(path), "L", &buf); err != nil { 92 91 return httperr.Error(err) 93 92 } 94 93 94 + commit := ref 95 + if len(ref) < 40 { 96 + commitObj, err := repo.GetCommitFromRef(ref) 97 + if err == nil { 98 + commit = commitObj.Hash.String() 99 + } 100 + } 101 + 95 102 if err := html.RepoFile(html.RepoFileContext{ 96 - BaseContext: rh.baseContext(), 103 + BaseContext: rh.repoBaseContext(repo), 97 104 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 98 105 RepoBreadcrumbComponentContext: rh.repoBreadcrumbContext(repo, r, path), 99 106 Code: buf.String(), 107 + Commit: commit, 108 + Path: path, 100 109 }).Render(r.Context(), w); err != nil { 101 110 return httperr.Error(err) 102 111 } ··· 118 127 } 119 128 120 129 if err := html.RepoRefs(html.RepoRefsContext{ 121 - BaseContext: rh.baseContext(), 130 + BaseContext: rh.repoBaseContext(repo), 122 131 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 123 132 Branches: branches, 124 133 Tags: tags, ··· 138 147 } 139 148 140 149 if err := html.RepoLog(html.RepoLogContext{ 141 - BaseContext: rh.baseContext(), 150 + BaseContext: rh.repoBaseContext(repo), 142 151 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 143 152 Commits: commits, 144 153 }).Render(r.Context(), w); err != nil { ··· 158 167 159 168 for idx, p := range commit.Files { 160 169 var patch bytes.Buffer 161 - if err := markup.Code.Basic([]byte(p.Patch), "commit.patch", &patch); err != nil { 170 + if err := markup.Convert([]byte(p.Patch), "commit.patch", p.Path()+"-L", &patch); err != nil { 162 171 return httperr.Error(err) 163 172 } 164 173 commit.Files[idx].Patch = patch.String() 165 174 } 166 175 167 176 if err := html.RepoCommit(html.RepoCommitContext{ 168 - BaseContext: rh.baseContext(), 177 + BaseContext: rh.repoBaseContext(repo), 169 178 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 170 179 Commit: commit, 171 180 }).Render(r.Context(), w); err != nil { ··· 206 215 } 207 216 results[idx].Content = buf.String() 208 217 } 209 - 210 218 } 211 219 212 220 if err := html.RepoSearch(html.SearchContext{ 213 - BaseContext: rh.baseContext(), 221 + BaseContext: rh.repoBaseContext(repo), 214 222 RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 215 223 Results: results, 216 224 }).Render(r.Context(), w); err != nil {
-37
internal/ssh/form.go
··· 1 - package ssh 2 - 3 - import ( 4 - "strings" 5 - 6 - "github.com/charmbracelet/huh" 7 - "go.jolheiser.com/ugit/internal/git" 8 - ) 9 - 10 - type stringSliceAccessor []string 11 - 12 - func (s stringSliceAccessor) Get() string { 13 - return strings.Join(s, "\n") 14 - } 15 - 16 - func (s stringSliceAccessor) Set(value string) { 17 - s = strings.Split(value, "\n") 18 - } 19 - 20 - func newForm(meta git.RepoMeta) *huh.Form { 21 - return huh.NewForm( 22 - huh.NewGroup( 23 - huh.NewText(). 24 - Title("Description"). 25 - Value(&meta.Description), 26 - huh.NewConfirm(). 27 - Title("Visibility"). 28 - Affirmative("Public"). 29 - Negative("Private"). 30 - Value(&meta.Private), 31 - huh.NewText(). 32 - Title("Tags"). 33 - Description("One per line"). 34 - Accessor(stringSliceAccessor(meta.Tags)), 35 - ), 36 - ) 37 - }
+3 -3
internal/ssh/ssh.go
··· 2 2 3 3 import ( 4 4 "fmt" 5 + "log" 5 6 6 - "github.com/charmbracelet/log" 7 7 "github.com/charmbracelet/ssh" 8 8 "github.com/charmbracelet/wish" 9 9 "github.com/charmbracelet/wish/logging" ··· 42 42 func (a hooks) Fetch(_ string, _ ssh.PublicKey) {} 43 43 44 44 var ( 45 - DefaultLogger logging.Logger = log.StandardLog() 45 + DefaultLogger logging.Logger = log.Default() 46 46 NoopLogger logging.Logger = noopLogger{} 47 47 ) 48 48 49 49 type noopLogger struct{} 50 50 51 - func (n noopLogger) Printf(format string, v ...interface{}) {} 51 + func (n noopLogger) Printf(format string, v ...any) {}
+4 -14
internal/ssh/wish.go
··· 5 5 "errors" 6 6 "fmt" 7 7 "io/fs" 8 + "log/slog" 8 9 "os" 9 10 "path/filepath" 10 11 "strings" ··· 12 13 13 14 "go.jolheiser.com/ugit/internal/git" 14 15 15 - "github.com/charmbracelet/log" 16 16 "github.com/charmbracelet/ssh" 17 17 "github.com/charmbracelet/wish" 18 18 ) ··· 91 91 if errors.Is(err, ErrInvalidRepo) { 92 92 Fatal(s, ErrInvalidRepo) 93 93 } 94 - log.Error("unknown git error", "error", err) 94 + slog.Error("unknown git error", "error", err) 95 95 Fatal(s, ErrSystemMalfunction) 96 96 } 97 97 gh.Fetch(repo, pk) ··· 103 103 if len(cmd) == 0 { 104 104 des, err := os.ReadDir(repoDir) 105 105 if err != nil && err != fs.ErrNotExist { 106 - log.Error("invalid repository", "error", err) 106 + slog.Error("invalid repository", "error", err) 107 107 } 108 108 tw := tabwriter.NewWriter(s, 0, 0, 1, ' ', 0) 109 109 for _, de := range des { ··· 122 122 } 123 123 tw.Flush() 124 124 } 125 - 126 - // Edit repo 127 - if len(cmd) == 1 { 128 - repo, err := git.NewRepo(cmd[0]) 129 - if err != nil { 130 - Fatal(s, ErrInvalidRepo) 131 - } 132 - 133 - } 134 - 135 125 sh(s) 136 126 } 137 127 } ··· 178 168 } 179 169 180 170 // Fatal prints to the session's STDOUT as a git response and exit 1. 181 - func Fatal(s ssh.Session, v ...interface{}) { 171 + func Fatal(s ssh.Session, v ...any) { 182 172 msg := fmt.Sprint(v...) 183 173 // hex length includes 4 byte length prefix and ending newline 184 174 pktLine := fmt.Sprintf("%04x%s\n", len(msg)+5, msg)
+10
nix/default.nix
··· 1 + { 2 + pkgs ? import <nixpkgs> { }, 3 + }: 4 + let 5 + pkg = pkgs.callPackage ./pkg.nix { inherit pkgs; }; 6 + in 7 + { 8 + ugit = pkg; 9 + default = pkg; 10 + }
+228
nix/module.nix
··· 1 + { 2 + pkgs, 3 + lib, 4 + config, 5 + ... 6 + }: 7 + let 8 + cfg = config.services.ugit; 9 + pkg = pkgs.callPackage ./pkg.nix { inherit pkgs; }; 10 + yamlFormat = pkgs.formats.yaml { }; 11 + instanceOptions = 12 + { name, config, ... }: 13 + let 14 + inherit (lib) mkEnableOption mkOption types; 15 + baseDir = "/var/lib/ugit-${name}"; 16 + in 17 + { 18 + options = { 19 + enable = mkEnableOption "Enable ugit"; 20 + 21 + package = mkOption { 22 + type = types.package; 23 + description = "ugit package to use"; 24 + default = pkg; 25 + }; 26 + 27 + homeDir = mkOption { 28 + type = types.str; 29 + description = "ugit home directory"; 30 + default = baseDir; 31 + }; 32 + 33 + repoDir = mkOption { 34 + type = types.str; 35 + description = "where ugit stores repositories"; 36 + default = "${baseDir}/repos"; 37 + }; 38 + 39 + authorizedKeys = mkOption { 40 + type = types.listOf types.str; 41 + description = "list of keys to use for authorized_keys"; 42 + default = [ ]; 43 + }; 44 + 45 + authorizedKeysFile = mkOption { 46 + type = types.str; 47 + description = "path to authorized_keys file ugit uses for auth"; 48 + default = "${baseDir}/authorized_keys"; 49 + }; 50 + 51 + hostKeyFile = mkOption { 52 + type = types.str; 53 + description = "path to host key file (will be created if it doesn't exist)"; 54 + default = "${baseDir}/ugit_ed25519"; 55 + }; 56 + 57 + config = mkOption { 58 + type = types.attrs; 59 + default = { }; 60 + description = "config.yaml contents"; 61 + }; 62 + 63 + user = mkOption { 64 + type = types.str; 65 + default = "ugit-${name}"; 66 + description = "User account under which ugit runs"; 67 + }; 68 + 69 + group = mkOption { 70 + type = types.str; 71 + default = "ugit-${name}"; 72 + description = "Group account under which ugit runs"; 73 + }; 74 + 75 + hooks = mkOption { 76 + type = types.listOf ( 77 + types.submodule { 78 + options = { 79 + name = mkOption { 80 + type = types.str; 81 + description = "Hook name"; 82 + }; 83 + content = mkOption { 84 + type = types.str; 85 + description = "Hook contents"; 86 + }; 87 + }; 88 + } 89 + ); 90 + description = "A list of pre-receive hooks to run"; 91 + default = [ ]; 92 + }; 93 + }; 94 + }; 95 + in 96 + { 97 + options = { 98 + services.ugit = lib.mkOption { 99 + type = lib.types.attrsOf (lib.types.submodule instanceOptions); 100 + default = { }; 101 + description = "Attribute set of ugit instances"; 102 + }; 103 + }; 104 + config = lib.mkIf (cfg != { }) { 105 + users.users = lib.mapAttrs' ( 106 + name: instanceCfg: 107 + lib.nameValuePair instanceCfg.user { 108 + home = instanceCfg.homeDir; 109 + createHome = true; 110 + group = instanceCfg.group; 111 + isSystemUser = true; 112 + isNormalUser = false; 113 + description = "user for ugit ${name} service"; 114 + } 115 + ) (lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg); 116 + 117 + users.groups = lib.mapAttrs' (name: instanceCfg: lib.nameValuePair instanceCfg.group { }) ( 118 + lib.filterAttrs (name: instanceCfg: instanceCfg.enable) cfg 119 + ); 120 + 121 + systemd.services = lib.foldl' ( 122 + acc: name: 123 + let 124 + instanceCfg = cfg.${name}; 125 + in 126 + lib.recursiveUpdate acc ( 127 + lib.optionalAttrs instanceCfg.enable { 128 + "ugit-${name}" = { 129 + enable = true; 130 + description = "ugit instance ${name}"; 131 + wantedBy = [ "multi-user.target" ]; 132 + after = [ "network.target" ]; 133 + path = [ 134 + instanceCfg.package 135 + pkgs.git 136 + pkgs.bash 137 + ]; 138 + serviceConfig = { 139 + User = instanceCfg.user; 140 + Group = instanceCfg.group; 141 + Restart = "always"; 142 + RestartSec = "15"; 143 + WorkingDirectory = instanceCfg.homeDir; 144 + ReadWritePaths = [ instanceCfg.homeDir ]; 145 + CapabilityBoundingSet = ""; 146 + NoNewPrivileges = true; 147 + ProtectSystem = "strict"; 148 + ProtectHome = true; 149 + PrivateTmp = true; 150 + PrivateDevices = true; 151 + PrivateUsers = true; 152 + ProtectHostname = true; 153 + ProtectClock = true; 154 + ProtectKernelTunables = true; 155 + ProtectKernelModules = true; 156 + ProtectKernelLogs = true; 157 + ProtectControlGroups = true; 158 + RestrictAddressFamilies = [ 159 + "AF_UNIX" 160 + "AF_INET" 161 + "AF_INET6" 162 + ]; 163 + RestrictNamespaces = true; 164 + LockPersonality = true; 165 + MemoryDenyWriteExecute = true; 166 + RestrictRealtime = true; 167 + RestrictSUIDSGID = true; 168 + RemoveIPC = true; 169 + PrivateMounts = true; 170 + SystemCallArchitectures = "native"; 171 + ExecStart = 172 + let 173 + configFile = pkgs.writeText "ugit-${name}.yaml" ( 174 + builtins.readFile (yamlFormat.generate "ugit-${name}-yaml" instanceCfg.config) 175 + ); 176 + authorizedKeysFile = pkgs.writeText "ugit_${name}_keys" ( 177 + builtins.concatStringsSep "\n" instanceCfg.authorizedKeys 178 + ); 179 + 180 + authorizedKeysPath = 181 + if (builtins.length instanceCfg.authorizedKeys) > 0 then 182 + authorizedKeysFile 183 + else 184 + instanceCfg.authorizedKeysFile; 185 + args = [ 186 + "--config=${configFile}" 187 + "--repo-dir=${instanceCfg.repoDir}" 188 + "--ssh.authorized-keys=${authorizedKeysPath}" 189 + "--ssh.host-key=${instanceCfg.hostKeyFile}" 190 + ]; 191 + in 192 + "${instanceCfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}"; 193 + }; 194 + }; 195 + 196 + "ugit-${name}-hooks" = { 197 + description = "Setup hooks for ugit instance ${name}"; 198 + wantedBy = [ "multi-user.target" ]; 199 + after = [ "ugit-${name}.service" ]; 200 + requires = [ "ugit-${name}.service" ]; 201 + serviceConfig = { 202 + Type = "oneshot"; 203 + RemainAfterExit = true; 204 + User = instanceCfg.user; 205 + Group = instanceCfg.group; 206 + ExecStart = 207 + let 208 + hookDir = "${instanceCfg.repoDir}/hooks/pre-receive.d"; 209 + mkHookScript = 210 + hook: 211 + let 212 + script = pkgs.writeShellScript "ugit-${name}-${hook.name}" hook.content; 213 + in 214 + '' 215 + mkdir -p ${hookDir} 216 + ln -sf ${script} ${hookDir}/${hook.name} 217 + ''; 218 + in 219 + pkgs.writeShellScript "ugit-${name}-hooks-setup" '' 220 + ${builtins.concatStringsSep "\n" (map mkHookScript instanceCfg.hooks)} 221 + ''; 222 + }; 223 + }; 224 + } 225 + ) 226 + ) { } (builtins.attrNames cfg); 227 + }; 228 + }
+32
nix/pkg.nix
··· 1 + { 2 + pkgs ? import <nixpkgs> { }, 3 + }: 4 + let 5 + name = "ugitd"; 6 + in 7 + pkgs.buildGoModule { 8 + pname = name; 9 + version = "main"; 10 + src = pkgs.nix-gitignore.gitignoreSource [ ] ( 11 + builtins.path { 12 + inherit name; 13 + path = ../.; 14 + } 15 + ); 16 + subPackages = [ 17 + "cmd/ugitd" 18 + ]; 19 + vendorHash = pkgs.lib.fileContents ../go.mod.sri; 20 + env.CGO_ENABLED = 0; 21 + flags = [ "-trimpath" ]; 22 + ldflags = [ 23 + "-s" 24 + "-w" 25 + "-extldflags -static" 26 + ]; 27 + meta = { 28 + description = "Minimal git server"; 29 + homepage = "https://git.jolheiser.com/ugit"; 30 + mainProgram = "ugitd"; 31 + }; 32 + }
+84
nix/vm.nix
··· 1 + { pkgs, ... }: 2 + let 3 + privKey = '' 4 + -----BEGIN OPENSSH PRIVATE KEY----- 5 + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 6 + QyNTUxOQAAACBIpmLtcHhECei1ls6s0kKUehjpRCP9yel/c5YCIb5DpQAAAIgAYtkzAGLZ 7 + MwAAAAtzc2gtZWQyNTUxOQAAACBIpmLtcHhECei1ls6s0kKUehjpRCP9yel/c5YCIb5DpQ 8 + AAAEDFY3M69VfnFbyE67r3l4lDcf5eht5qgNemE9xtMhRkBkimYu1weEQJ6LWWzqzSQpR6 9 + GOlEI/3J6X9zlgIhvkOlAAAAAAECAwQF 10 + -----END OPENSSH PRIVATE KEY----- 11 + ''; 12 + pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEimYu1weEQJ6LWWzqzSQpR6GOlEI/3J6X9zlgIhvkOl"; 13 + sshConfig = '' 14 + Host ugit 15 + HostName localhost 16 + Port 8448 17 + User ugit 18 + IdentityFile ~/.ssh/vm 19 + IdentitiesOnly yes 20 + ''; 21 + in 22 + { 23 + imports = [ ./module.nix ]; 24 + environment.systemPackages = with pkgs; [ git ]; 25 + services.getty.autologinUser = "root"; 26 + services.openssh.enable = true; 27 + services.ugit.vm = { 28 + enable = true; 29 + authorizedKeys = [ pubKey ]; 30 + hooks = [ 31 + { 32 + name = "pre-receive"; 33 + content = '' 34 + echo "Pre-receive hook executed" 35 + ''; 36 + } 37 + ]; 38 + }; 39 + systemd.services."setup-vm" = { 40 + wantedBy = [ "multi-user.target" ]; 41 + after = [ "ugit-vm.service" ]; 42 + path = with pkgs; [ 43 + git 44 + ]; 45 + serviceConfig = { 46 + Type = "oneshot"; 47 + RemainAfterExit = true; 48 + User = "root"; 49 + Group = "root"; 50 + ExecStart = 51 + let 52 + privSSH = pkgs.writeText "vm-privkey" privKey; 53 + sshConfigFile = pkgs.writeText "vm-sshconfig" sshConfig; 54 + in 55 + pkgs.writeShellScript "setup-vm-script" '' 56 + # Hack to let ugit start up and generate its SSH keypair 57 + sleep 3 58 + 59 + # Set up git 60 + git config --global user.name "NixUser" 61 + git config --global user.email "nixuser@example.com" 62 + git config --global init.defaultBranch main 63 + git config --global push.autoSetupRemote true 64 + 65 + # Set up SSH files 66 + mkdir ~/.ssh 67 + ln -sf ${sshConfigFile} ~/.ssh/config 68 + cp ${privSSH} ~/.ssh/vm 69 + chmod 600 ~/.ssh/vm 70 + echo "[localhost]:8448 $(cat /var/lib/ugit-vm/ugit_ed25519.pub)" > ~/.ssh/known_hosts 71 + 72 + # Stage some git activity 73 + mkdir ~/repo 74 + cd ~/repo 75 + git init 76 + git remote add origin ugit:repo.git 77 + touch README.md 78 + git add README.md 79 + git commit -m "Test" 80 + ''; 81 + }; 82 + }; 83 + 84 + }