Archived
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 <noreply@anthropic.com>
114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
package theme
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"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 {
|
|
case submodule.IssueMissing:
|
|
return "missing"
|
|
case submodule.IssueUnpushed:
|
|
return "unpushed"
|
|
case submodule.IssueConflict:
|
|
return "conflict"
|
|
case submodule.IssueOffPin:
|
|
return "off pin"
|
|
default:
|
|
return "changed"
|
|
}
|
|
}
|
|
|
|
// ChangeLetter is the one-letter label of a changed file.
|
|
func ChangeLetter(k submodule.ChangeKind) string {
|
|
switch k {
|
|
case submodule.ChangeConflicted:
|
|
return "!"
|
|
case submodule.ChangeModified:
|
|
return "M"
|
|
case submodule.ChangeAdded:
|
|
return "A"
|
|
case submodule.ChangeRemoved:
|
|
return "D"
|
|
case submodule.ChangeRenamed:
|
|
return "R"
|
|
default:
|
|
return "?"
|
|
}
|
|
}
|
|
|
|
// ChangeStyle returns base with the color of a change kind.
|
|
func (t Theme) ChangeStyle(k submodule.ChangeKind, base lipgloss.Style) lipgloss.Style {
|
|
switch k {
|
|
case submodule.ChangeConflicted:
|
|
return base.Foreground(t.Danger).Bold(true)
|
|
case submodule.ChangeModified:
|
|
return base.Foreground(t.Warn)
|
|
case submodule.ChangeAdded:
|
|
return base.Foreground(t.Added)
|
|
case submodule.ChangeRemoved:
|
|
return base.Foreground(t.Danger)
|
|
case submodule.ChangeRenamed:
|
|
return base.Foreground(t.Primary)
|
|
default:
|
|
return base.Foreground(t.Muted)
|
|
}
|
|
}
|
|
|
|
// LevelStyle returns base with the text color (and weight) for a level.
|
|
func (t Theme) LevelStyle(l submodule.Level, base lipgloss.Style) lipgloss.Style {
|
|
switch l {
|
|
case submodule.LevelDanger:
|
|
return base.Foreground(t.Danger).Bold(true)
|
|
case submodule.LevelWarn:
|
|
return base.Foreground(t.Warn)
|
|
default:
|
|
return base.Foreground(t.Muted)
|
|
}
|
|
}
|
|
|
|
// Delta renders ahead/behind counts like "↑1↓2", muted unless the pin is
|
|
// unpushed. Empty if both are zero. base is applied to every segment, e.g.
|
|
// to keep a row background.
|
|
func (t Theme) Delta(p submodule.PinInfo, base lipgloss.Style) string {
|
|
var s string
|
|
if p.Ahead > 0 {
|
|
c := t.Muted
|
|
if !p.Pushed {
|
|
c = t.Danger
|
|
}
|
|
s += base.Foreground(c).Render(fmt.Sprintf("%s%d", t.Glyphs.Ahead, p.Ahead))
|
|
}
|
|
if p.Behind > 0 {
|
|
s += base.Foreground(t.Muted).Render(fmt.Sprintf("%s%d", t.Glyphs.Behind, p.Behind))
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Ago formats a time relative to now, e.g. "5m ago"; the zero time is "never".
|
|
func Ago(t time.Time) string {
|
|
if t.IsZero() {
|
|
return "never"
|
|
}
|
|
d := time.Since(t)
|
|
switch {
|
|
case d < time.Minute:
|
|
return "just now"
|
|
case d < time.Hour:
|
|
return fmt.Sprintf("%dm ago", int(d.Minutes()))
|
|
case d < 24*time.Hour:
|
|
return fmt.Sprintf("%dh ago", int(d.Hours()))
|
|
default:
|
|
return fmt.Sprintf("%dd ago", int(d.Hours()/24))
|
|
}
|
|
}
|