From 6e1236d495a7bb419e33a0000a1f48027f0686c6 Mon Sep 17 00:00:00 2001 From: Simon Franken Date: Wed, 21 Oct 2026 16:18:17 +0200 Subject: [PATCH] Show a muted "in sync" for submodules without issues An empty status cell could be read as missing data. The muted word confirms the check ran without drawing attention. The word is defined once in the theme and shared with the detail pane. Co-Authored-By: Claude Opus 5.5 --- docs/requirements.md | 3 ++- docs/ux-guide.md | 6 ++++-- internal/tui/detail/detail.go | 2 +- internal/tui/theme/issues.go | 3 +++ internal/tui/tree/tree.go | 5 ++++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index c5c1a74..59c588f 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -42,7 +42,8 @@ commit hashes. It doesn't answer the questions users actually have: ### State per submodule - **F10** Each submodule shows at most **one** state word: the most severe - issue, plus `+n` if there are more. Submodules without issues show no word. + issue, plus `+n` if there are more. Submodules without issues show a + muted `in sync`. See [state-model.md](state-model.md) for the full rules. - **F11** Each submodule shows its pin as a ref (`origin/main`, a tag, or a short SHA as a fallback) and the distance to that ref (`↑n`, `↓n`, `↑n↓m`). diff --git a/docs/ux-guide.md b/docs/ux-guide.md index a6448b4..d0c1ade 100644 --- a/docs/ux-guide.md +++ b/docs/ux-guide.md @@ -47,13 +47,15 @@ indent ▸ name [n] status ref delta | indent | 2 per depth | | | toggle | 3 | `▸` collapsed, `▾` expanded, blank for leaves | | name | fits the widest visible name | name, `[n]` = direct children (collapsed only) | -| status | 14 | issue word, `+n`, `·k` hidden issues; spinner while loading | +| status | 14 | issue word, `+n`, `·k` hidden issues; muted `in sync` without issues; spinner while loading | | ref | widest ref, max 22 | resolved ref; muted short SHA if unresolved | | delta | 6 | `↑n`, `↓n`, `↑n↓m` | - The name column is only as wide as needed, so states stay close to names. - Directory headers (`src/`, `modules/`) are muted and not selectable. -- Rows without issues: name and ref muted (except the selected row). +- Rows without issues: name and ref muted (except the selected row), status + `in sync` in muted grey. An empty status cell would look like missing + data; the muted word confirms the check ran without drawing attention. ### Detail pane diff --git a/internal/tui/detail/detail.go b/internal/tui/detail/detail.go index ae44621..49fd382 100644 --- a/internal/tui/detail/detail.go +++ b/internal/tui/detail/detail.go @@ -74,7 +74,7 @@ func (m Model) lines(s *submodule.Submodule) []string { out = append(out, t.LevelStyle(is.Level, lipgloss.NewStyle()).Width(labelWidth).Render(theme.IssueWord(is.Kind))+text.Render(is.Detail)) } if len(issues) == 0 { - out = append(out, label("in sync")+muted.Render("nothing to do")) + out = append(out, label(theme.InSync)+muted.Render("nothing to do")) } if st.PinStaged { out = append(out, theme.Fg(t.Info).Width(labelWidth).Render("staged")+text.Render("pin change staged, not committed")) diff --git a/internal/tui/theme/issues.go b/internal/tui/theme/issues.go index 2aa0c7b..7287435 100644 --- a/internal/tui/theme/issues.go +++ b/internal/tui/theme/issues.go @@ -9,6 +9,9 @@ import ( "submarine/internal/submodule" ) +// InSync is the word for a submodule without issues. +const InSync = "in sync" + // IssueWord is the short label for an issue kind shown in the tree. func IssueWord(k submodule.IssueKind) string { switch k { diff --git a/internal/tui/tree/tree.go b/internal/tui/tree/tree.go index 92b2ded..7e81868 100644 --- a/internal/tui/tree/tree.go +++ b/internal/tui/tree/tree.go @@ -398,7 +398,8 @@ func (m Model) nodeLine(r row, nameColW, refW int) string { name := ansi.Truncate(s.Name, max(nameW-len(count), 1), "…") nameCell := seg(textColor, name) + seg(t.Muted, count) + pad(nameW-lipgloss.Width(name+count)) - // status: the most severe issue as a word, "+n" for further issues + // status: the most severe issue as a word, "+n" for further issues; + // a muted "in sync" without issues, so an empty cell never looks broken var status string switch { case s.Err != nil: @@ -410,6 +411,8 @@ func (m Model) nodeLine(r row, nameColW, refW int) string { if len(issues) > 1 { status += seg(t.Muted, fmt.Sprintf(" +%d", len(issues)-1)) } + default: + status = seg(t.Muted, theme.InSync) } if k, worst := s.HiddenIssues(); collapsed && k > 0 { if status != "" {