▍ humdrum codex / glint v1.0.2
license AGPL-3.0

feat: status bar shows repo name (or parent folder) + filename (TASK-010)

0b2e184dc6993c3b4e665958d97fa7c9e256a860
Kevin Kortum <kevinkortum@me.com> · 2026-06-29 16:11

parent 466af178

feat: status bar shows repo name (or parent folder) + filename (TASK-010)

The left segment now resolves the file's git repository root name (the folder
holding .git), falling back to the immediate parent folder when not in a repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KjNrdAWUdkaxFyGdrPHaBj

2 files changed

internal/app/app.go +23 −7
@@ -725,17 +725,15 @@ 	dirty := ""
 	if a.editor.Dirty {
 		dirty = " ●"
 	}
-	// Left: a transient message takes over; otherwise dir (+ filename).
+	// Left: a transient message takes over; otherwise the repo (or parent
+	// folder) and filename.
 	var left string
 	if a.status != "" {
 		left = a.status + dirty
+	} else if a.path != "" {
+		left = repoOrParent(a.path) + "/" + filepath.Base(a.path) + dirty
 	} else {
-		dir := filepath.Base(a.currentDir())
-		if a.path != "" {
-			left = dir + "/" + filepath.Base(a.path) + dirty
-		} else {
-			left = dir + "/" + dirty
-		}
+		left = filepath.Base(a.currentDir()) + "/" + dirty
 	}
 	// Right: [sel stats ·] Ln r:c · N words · theme · ?  (high→low priority).
 	pos := a.editor.Cursor
@@ -783,6 +781,24 @@ 	for len(r) > 0 && lipgloss.Width(string(r))+1 > width {
 		r = r[:len(r)-1]
 	}
 	return string(r) + "…"
+}
+
+// repoOrParent returns the name of the git repository containing path (the
+// folder holding .git), or the file's immediate parent folder when path is not
+// inside a repo.
+func repoOrParent(path string) string {
+	parent := filepath.Dir(path)
+	for dir := parent; ; {
+		if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
+			return filepath.Base(dir)
+		}
+		up := filepath.Dir(dir)
+		if up == dir { // reached the filesystem root
+			break
+		}
+		dir = up
+	}
+	return filepath.Base(parent)
 }
 
 func maxInt(a, b int) int {
internal/app/status_test.go +30 −0
@@ -1,6 +1,8 @@
 package app
 
 import (
+	"os"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -56,6 +58,34 @@ 	}
 	got := a.statusBar()
 	if !strings.Contains(got, "7 chars") || !strings.Contains(got, "2 words selected") {
 		t.Fatalf("status bar missing selection stats:\n%s", got)
+	}
+}
+
+func TestRepoOrParentReturnsRepoName(t *testing.T) {
+	root := t.TempDir()
+	repo := filepath.Join(root, "myrepo")
+	sub := filepath.Join(repo, "notes")
+	if err := os.MkdirAll(filepath.Join(repo, ".git"), 0o755); err != nil {
+		t.Fatal(err)
+	}
+	if err := os.MkdirAll(sub, 0o755); err != nil {
+		t.Fatal(err)
+	}
+	got := repoOrParent(filepath.Join(sub, "n.md"))
+	if got != "myrepo" {
+		t.Fatalf("repoOrParent = %q, want %q", got, "myrepo")
+	}
+}
+
+func TestRepoOrParentFallsBackToParent(t *testing.T) {
+	root := t.TempDir()
+	dir := filepath.Join(root, "plainfolder")
+	if err := os.MkdirAll(dir, 0o755); err != nil {
+		t.Fatal(err)
+	}
+	got := repoOrParent(filepath.Join(dir, "n.md"))
+	if got != "plainfolder" {
+		t.Fatalf("repoOrParent = %q, want %q", got, "plainfolder")
 	}
 }