+ New

fierj

Public

Tiny personal git forge

e37b87df9af786adf6267e0a5232f821fec9df97
diff --git a/handlers.go b/handlers.go
index 7d90030..6733fb9 100644
--- a/handlers.go
+++ b/handlers.go
@@ -1,12 +1,80 @@
 package main
 
 import (
+	"embed"
+	"fmt"
 	"log/slog"
 	"net/http"
 	"sort"
+	"strings"
 	"text/template"
+	"time"
 )
 
+type BreadcrumbItem struct {
+	Name string
+	URL  string
+}
+
+//go:embed templates/*.html
+var templateFS embed.FS
+
+var funcMap = template.FuncMap{
+	"pathJoin": func(parts ...string) string {
+		var nonEmpty []string
+		for _, p := range parts {
+			if p != "" {
+				nonEmpty = append(nonEmpty, p)
+			}
+		}
+		return strings.Join(nonEmpty, "/")
+	},
+	"shortHash": func(h string) string {
+		if len(h) > 8 {
+			return h[:8]
+		}
+		return h
+	},
+	"hasSuffix": strings.HasSuffix,
+	"timeAgo": func(ts string) string {
+		t, err := time.Parse(time.RFC3339, ts)
+		if err != nil {
+			return ts
+		}
+		d := time.Since(t)
+		switch {
+		case d < time.Minute:
+			return "just now"
+		case d < time.Hour:
+			m := int(d.Minutes())
+			if m == 1 {
+				return "1 minute ago"
+			}
+			return fmt.Sprintf("%d minutes ago", m)
+		case d < 24*time.Hour:
+			h := int(d.Hours())
+			if h == 1 {
+				return "1 hour ago"
+			}
+			return fmt.Sprintf("%d hours ago", h)
+		case d < 30*24*time.Hour:
+			days := int(d.Hours() / 24)
+			if days == 1 {
+				return "1 day ago"
+			}
+			return fmt.Sprintf("%d days ago", days)
+		case d < 365*24*time.Hour:
+			months := int(d.Hours() / 24 / 30)
+			if months == 1 {
+				return "1 month ago"
+			}
+			return fmt.Sprintf("%d months ago", months)
+		default:
+			return ts[:10]
+		}
+	},
+}
+
 func renderError(w http.ResponseWriter, tmpl *template.Template, code int, message string) {
 	w.WriteHeader(code)
 	tmpl.ExecuteTemplate(w, "error.html", map[string]any{
@@ -15,6 +83,23 @@ func renderError(w http.ResponseWriter, tmpl *template.Template, code int, messa
 	})
 }
 
+func Breadcrumb(path string) []BreadcrumbItem {
+	if path == "" {
+		return nil
+	}
+	parts := strings.Split(path, "/")
+	items := make([]BreadcrumbItem, len(parts))
+	cumulative := ""
+	for i, p := range parts {
+		if i > 0 {
+			cumulative += "/"
+		}
+		cumulative += p
+		items[i] = BreadcrumbItem{Name: p, URL: cumulative}
+	}
+	return items
+}
+
 func Repos(cfg Config, tmpl *template.Template) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		repos, err := ListRepos(cfg)
@@ -79,11 +164,11 @@ func Tree(cfg Config, tmpl *template.Template) http.HandlerFunc {
 		}
 		readme := git.Readme(ref, treePath)
 		tmpl.ExecuteTemplate(w, "tree.html", map[string]any{
-			"Repo": git.Repo(),
-			"Ref":  ref,
-			"Path": treePath,
-			// "Breadcrumb": makeBreadcrumb(treePath),
-			"Entries": fileEntries,
+			"Repo":       git.Repo(),
+			"Ref":        ref,
+			"Path":       treePath,
+			"Breadcrumb": Breadcrumb(treePath),
+			"Entries":    fileEntries,
 			// "CloneURL":    fmt.Sprintf("%s@%s:%s.git", cfg.SSHUser, cfg.Host, repoName),
 			"Description": git.Description(),
 			"Branches":    git.Branches(),
@@ -110,10 +195,10 @@ func Blob(cfg Config, tmpl *template.Template) http.HandlerFunc {
 			return
 		}
 		tmpl.ExecuteTemplate(w, "blob.html", map[string]any{
-			"Repo": git.Repo(),
-			"Ref":  ref,
-			"Path": filePath,
-			// "Breadcrumb":  makeBreadcrumb(filePath),
+			"Repo":        git.Repo(),
+			"Ref":         ref,
+			"Path":        filePath,
+			"Breadcrumb":  Breadcrumb(filePath),
 			"Content":     content,
 			"Description": git.Description(),
 			"Branches":    git.Branches(),
diff --git a/main.go b/main.go
index f6615a8..047568e 100644
--- a/main.go
+++ b/main.go
@@ -2,77 +2,15 @@ package main
 
 import (
 	"context"
-	"embed"
-	"fmt"
 	"log/slog"
 	"net/http"
 	"os"
 	"os/signal"
-	"strings"
 	"syscall"
 	"text/template"
 	"time"
 )
 
-//go:embed templates/*.html
-var templateFS embed.FS
-
-var funcMap = template.FuncMap{
-	"pathJoin": func(parts ...string) string {
-		var nonEmpty []string
-		for _, p := range parts {
-			if p != "" {
-				nonEmpty = append(nonEmpty, p)
-			}
-		}
-		return strings.Join(nonEmpty, "/")
-	},
-	"shortHash": func(h string) string {
-		if len(h) > 8 {
-			return h[:8]
-		}
-		return h
-	},
-	"hasSuffix": strings.HasSuffix,
-	"timeAgo": func(ts string) string {
-		t, err := time.Parse(time.RFC3339, ts)
-		if err != nil {
-			return ts
-		}
-		d := time.Since(t)
-		switch {
-		case d < time.Minute:
-			return "just now"
-		case d < time.Hour:
-			m := int(d.Minutes())
-			if m == 1 {
-				return "1 minute ago"
-			}
-			return fmt.Sprintf("%d minutes ago", m)
-		case d < 24*time.Hour:
-			h := int(d.Hours())
-			if h == 1 {
-				return "1 hour ago"
-			}
-			return fmt.Sprintf("%d hours ago", h)
-		case d < 30*24*time.Hour:
-			days := int(d.Hours() / 24)
-			if days == 1 {
-				return "1 day ago"
-			}
-			return fmt.Sprintf("%d days ago", days)
-		case d < 365*24*time.Hour:
-			months := int(d.Hours() / 24 / 30)
-			if months == 1 {
-				return "1 month ago"
-			}
-			return fmt.Sprintf("%d months ago", months)
-		default:
-			return ts[:10]
-		}
-	},
-}
-
 func main() {
 	cfgPath := "config.json"
 	if len(os.Args) > 1 {