Make the superproject selectable via the header

The header stands for the root repository: it is selected on startup,
shows the root's own state word, and the detail pane shows its state
like a submodule's (HEAD vs. upstream, changes, unpushed, leftover
submodule clones as untracked repositories). Submodules are ignored in
the root's changes; their rows show them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 11:54:40 +02:00
co-authored by Claude Sonnet 5.5
parent f9838c0c99
commit 72c060882b
18 changed files with 688 additions and 92 deletions
+25
View File
@@ -32,6 +32,31 @@ func IssueWord(k submodule.IssueKind) string {
}
}
// StatusWord renders the state of s as one word: a spinner while loading,
// `error`, the most severe issue with "+n" for further ones, `staged`, or a
// muted "in sync". base is applied to every segment, e.g. to keep a row
// background.
func (t Theme) StatusWord(s *submodule.Submodule, base lipgloss.Style, spinner string) string {
seg := func(c lipgloss.TerminalColor, text string) string { return base.Foreground(c).Render(text) }
switch {
case s.Err != nil:
return t.LevelStyle(submodule.LevelDanger, base).Render("error")
case s.Status == nil:
return seg(t.Primary, spinner)
}
if issues := s.Status.Issues(); len(issues) > 0 {
out := t.LevelStyle(issues[0].Level, base).Render(IssueWord(issues[0].Kind))
if len(issues) > 1 {
out += seg(t.Muted, fmt.Sprintf(" +%d", len(issues)-1))
}
return out
}
if s.Status.PinStaged {
return seg(t.Info, Staged)
}
return seg(t.Muted, InSync)
}
// ChangeLetter is the one-letter label of a changed file.
func ChangeLetter(k submodule.ChangeKind) string {
switch k {