refactor(shared): add git status styles and check helpers
Add to shared package: - Git status styles (dirty/ahead/behind/clean/conflict) - CheckMark() helper for presence indicators (✓/—) - Label() helper for key-value labels - CheckResult() helper for environment check output Update packages to use new shared utilities: - cmd/dev: use shared git styles for table cells - cmd/docs: use CheckMark() and Label() helpers - cmd/doctor: use CheckResult() and Success()/Error() helpers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1b76d4c145
commit
4fa0e0310b
5 changed files with 102 additions and 64 deletions
|
|
@ -45,22 +45,12 @@ var (
|
||||||
repoNameStyle = shared.RepoNameStyle
|
repoNameStyle = shared.RepoNameStyle
|
||||||
)
|
)
|
||||||
|
|
||||||
// Table styles for status display
|
// Table styles for status display (with padding for table cells)
|
||||||
var (
|
var (
|
||||||
cellStyle = lipgloss.NewStyle().
|
cellStyle = lipgloss.NewStyle().Padding(0, 1)
|
||||||
Padding(0, 1)
|
dirtyStyle = shared.GitDirtyStyle.Padding(0, 1)
|
||||||
|
aheadStyle = shared.GitAheadStyle.Padding(0, 1)
|
||||||
dirtyStyle = lipgloss.NewStyle().
|
cleanStyle = shared.GitCleanStyle.Padding(0, 1)
|
||||||
Foreground(lipgloss.Color("#ef4444")). // red-500
|
|
||||||
Padding(0, 1)
|
|
||||||
|
|
||||||
aheadStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#22c55e")). // green-500
|
|
||||||
Padding(0, 1)
|
|
||||||
|
|
||||||
cleanStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#6b7280")). // gray-500
|
|
||||||
Padding(0, 1)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'dev' command and all subcommands.
|
// AddCommands registers the 'dev' command and all subcommands.
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,21 @@
|
||||||
package docs
|
package docs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/charmbracelet/lipgloss"
|
|
||||||
"github.com/host-uk/core/cmd/shared"
|
"github.com/host-uk/core/cmd/shared"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Style and utility aliases from shared
|
// Style and utility aliases from shared
|
||||||
var (
|
var (
|
||||||
repoNameStyle = shared.RepoNameStyle
|
repoNameStyle = shared.RepoNameStyle
|
||||||
successStyle = shared.SuccessStyle
|
successStyle = shared.SuccessStyle
|
||||||
errorStyle = shared.ErrorStyle
|
errorStyle = shared.ErrorStyle
|
||||||
dimStyle = shared.DimStyle
|
dimStyle = shared.DimStyle
|
||||||
headerStyle = shared.HeaderStyle
|
headerStyle = shared.HeaderStyle
|
||||||
confirm = shared.Confirm
|
confirm = shared.Confirm
|
||||||
)
|
docsFoundStyle = shared.SuccessStyle
|
||||||
|
docsMissingStyle = shared.DimStyle
|
||||||
// Package-specific styles
|
docsFileStyle = shared.InfoStyle
|
||||||
var (
|
|
||||||
docsFoundStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#22c55e")) // green-500
|
|
||||||
|
|
||||||
docsMissingStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#6b7280")) // gray-500
|
|
||||||
|
|
||||||
docsFileStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#3b82f6")) // blue-500
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var docsCmd = &cobra.Command{
|
var docsCmd = &cobra.Command{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/host-uk/core/cmd/shared"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,22 +42,11 @@ func runDocsList(registryPath string) error {
|
||||||
for _, repo := range reg.List() {
|
for _, repo := range reg.List() {
|
||||||
info := scanRepoDocs(repo)
|
info := scanRepoDocs(repo)
|
||||||
|
|
||||||
readme := docsMissingStyle.Render("—")
|
readme := shared.CheckMark(info.Readme != "")
|
||||||
if info.Readme != "" {
|
claude := shared.CheckMark(info.ClaudeMd != "")
|
||||||
readme = docsFoundStyle.Render("✓")
|
changelog := shared.CheckMark(info.Changelog != "")
|
||||||
}
|
|
||||||
|
|
||||||
claude := docsMissingStyle.Render("—")
|
docsDir := shared.CheckMark(false)
|
||||||
if info.ClaudeMd != "" {
|
|
||||||
claude = docsFoundStyle.Render("✓")
|
|
||||||
}
|
|
||||||
|
|
||||||
changelog := docsMissingStyle.Render("—")
|
|
||||||
if info.Changelog != "" {
|
|
||||||
changelog = docsFoundStyle.Render("✓")
|
|
||||||
}
|
|
||||||
|
|
||||||
docsDir := docsMissingStyle.Render("—")
|
|
||||||
if len(info.DocsFiles) > 0 {
|
if len(info.DocsFiles) > 0 {
|
||||||
docsDir = docsFoundStyle.Render(fmt.Sprintf("%d files", len(info.DocsFiles)))
|
docsDir = docsFoundStyle.Render(fmt.Sprintf("%d files", len(info.DocsFiles)))
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +68,7 @@ func runDocsList(registryPath string) error {
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s %d with docs, %d without\n",
|
fmt.Printf("%s %d with docs, %d without\n",
|
||||||
dimStyle.Render("Coverage:"),
|
shared.Label("Coverage"),
|
||||||
withDocs,
|
withDocs,
|
||||||
withoutDocs,
|
withoutDocs,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,14 @@ func runDoctor(verbose bool) error {
|
||||||
for _, c := range requiredChecks {
|
for _, c := range requiredChecks {
|
||||||
ok, version := runCheck(c)
|
ok, version := runCheck(c)
|
||||||
if ok {
|
if ok {
|
||||||
if verbose && version != "" {
|
if verbose {
|
||||||
fmt.Printf(" %s %s %s\n", successStyle.Render("✓"), c.name, dimStyle.Render(version))
|
fmt.Println(shared.CheckResult(true, c.name, version))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s %s\n", successStyle.Render("✓"), c.name)
|
fmt.Println(shared.CheckResult(true, c.name, ""))
|
||||||
}
|
}
|
||||||
passed++
|
passed++
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s %s - %s\n", errorStyle.Render("✗"), c.name, c.description)
|
fmt.Printf(" %s %s - %s\n", errorStyle.Render(shared.SymbolCross), c.name, c.description)
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,14 +60,14 @@ func runDoctor(verbose bool) error {
|
||||||
for _, c := range optionalChecks {
|
for _, c := range optionalChecks {
|
||||||
ok, version := runCheck(c)
|
ok, version := runCheck(c)
|
||||||
if ok {
|
if ok {
|
||||||
if verbose && version != "" {
|
if verbose {
|
||||||
fmt.Printf(" %s %s %s\n", successStyle.Render("✓"), c.name, dimStyle.Render(version))
|
fmt.Println(shared.CheckResult(true, c.name, version))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s %s\n", successStyle.Render("✓"), c.name)
|
fmt.Println(shared.CheckResult(true, c.name, ""))
|
||||||
}
|
}
|
||||||
passed++
|
passed++
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s %s - %s\n", dimStyle.Render("○"), c.name, dimStyle.Render(c.description))
|
fmt.Printf(" %s %s - %s\n", dimStyle.Render(shared.SymbolSkip), c.name, dimStyle.Render(c.description))
|
||||||
optional++
|
optional++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,16 +75,16 @@ func runDoctor(verbose bool) error {
|
||||||
// Check GitHub access
|
// Check GitHub access
|
||||||
fmt.Println("\nGitHub Access:")
|
fmt.Println("\nGitHub Access:")
|
||||||
if checkGitHubSSH() {
|
if checkGitHubSSH() {
|
||||||
fmt.Printf(" %s SSH key found\n", successStyle.Render("✓"))
|
fmt.Println(shared.CheckResult(true, "SSH key found", ""))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s SSH key missing - run: ssh-keygen && gh ssh-key add\n", errorStyle.Render("✗"))
|
fmt.Printf(" %s SSH key missing - run: ssh-keygen && gh ssh-key add\n", errorStyle.Render(shared.SymbolCross))
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
|
|
||||||
if checkGitHubCLI() {
|
if checkGitHubCLI() {
|
||||||
fmt.Printf(" %s CLI authenticated\n", successStyle.Render("✓"))
|
fmt.Println(shared.CheckResult(true, "CLI authenticated", ""))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" %s CLI authentication - run: gh auth login\n", errorStyle.Render("✗"))
|
fmt.Printf(" %s CLI authentication - run: gh auth login\n", errorStyle.Render(shared.SymbolCross))
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,12 +95,12 @@ func runDoctor(verbose bool) error {
|
||||||
// Summary
|
// Summary
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
if failed > 0 {
|
if failed > 0 {
|
||||||
fmt.Printf("%s %d issues found\n", errorStyle.Render("Doctor:"), failed)
|
fmt.Println(shared.Error(fmt.Sprintf("Doctor: %d issues found", failed)))
|
||||||
fmt.Println("\nInstall missing tools:")
|
fmt.Println("\nInstall missing tools:")
|
||||||
printInstallInstructions()
|
printInstallInstructions()
|
||||||
return fmt.Errorf("%d required tools missing", failed)
|
return fmt.Errorf("%d required tools missing", failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s Environment ready\n", successStyle.Render("Doctor:"))
|
fmt.Println(shared.Success("Doctor: Environment ready"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,27 @@ var (
|
||||||
SeverityLowStyle = lipgloss.NewStyle().Foreground(ColourGray500)
|
SeverityLowStyle = lipgloss.NewStyle().Foreground(ColourGray500)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Git Status Styles (for repo state indicators)
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
var (
|
||||||
|
// GitDirtyStyle for uncommitted changes (red).
|
||||||
|
GitDirtyStyle = lipgloss.NewStyle().Foreground(ColourRed500)
|
||||||
|
|
||||||
|
// GitAheadStyle for unpushed commits (green).
|
||||||
|
GitAheadStyle = lipgloss.NewStyle().Foreground(ColourGreen500)
|
||||||
|
|
||||||
|
// GitBehindStyle for unpulled commits (amber).
|
||||||
|
GitBehindStyle = lipgloss.NewStyle().Foreground(ColourAmber500)
|
||||||
|
|
||||||
|
// GitCleanStyle for clean state (gray).
|
||||||
|
GitCleanStyle = lipgloss.NewStyle().Foreground(ColourGray500)
|
||||||
|
|
||||||
|
// GitConflictStyle for merge conflicts (red, bold).
|
||||||
|
GitConflictStyle = lipgloss.NewStyle().Bold(true).Foreground(ColourRed500)
|
||||||
|
)
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Box Styles (for bordered content)
|
// Box Styles (for bordered content)
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -332,6 +353,53 @@ func StatusText(text string, style lipgloss.Style) string {
|
||||||
return style.Render(text)
|
return style.Render(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckMark returns a styled checkmark (✓) or dash (—) based on presence.
|
||||||
|
// Useful for showing presence/absence in tables and lists.
|
||||||
|
func CheckMark(present bool) string {
|
||||||
|
if present {
|
||||||
|
return SuccessStyle.Render(SymbolCheck)
|
||||||
|
}
|
||||||
|
return DimStyle.Render("—")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMarkCustom returns a styled indicator with custom symbols and styles.
|
||||||
|
func CheckMarkCustom(present bool, presentStyle, absentStyle lipgloss.Style, presentSymbol, absentSymbol string) string {
|
||||||
|
if present {
|
||||||
|
return presentStyle.Render(presentSymbol)
|
||||||
|
}
|
||||||
|
return absentStyle.Render(absentSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label returns a styled label for key-value display.
|
||||||
|
// Example: Label("Status") -> "Status:" in dim gray
|
||||||
|
func Label(text string) string {
|
||||||
|
return KeyStyle.Render(text + ":")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelValue returns a styled "label: value" pair.
|
||||||
|
// Example: LabelValue("Branch", "main") -> "Branch: main"
|
||||||
|
func LabelValue(label, value string) string {
|
||||||
|
return fmt.Sprintf("%s %s", Label(label), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelValueStyled returns a styled "label: value" pair with custom value style.
|
||||||
|
func LabelValueStyled(label, value string, valueStyle lipgloss.Style) string {
|
||||||
|
return fmt.Sprintf("%s %s", Label(label), valueStyle.Render(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResult formats a check result with name and optional version.
|
||||||
|
// Used for environment checks like `✓ go 1.22.0` or `✗ docker`.
|
||||||
|
func CheckResult(ok bool, name string, version string) string {
|
||||||
|
symbol := ErrorStyle.Render(SymbolCross)
|
||||||
|
if ok {
|
||||||
|
symbol = SuccessStyle.Render(SymbolCheck)
|
||||||
|
}
|
||||||
|
if version != "" {
|
||||||
|
return fmt.Sprintf(" %s %s %s", symbol, name, DimStyle.Render(version))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(" %s %s", symbol, name)
|
||||||
|
}
|
||||||
|
|
||||||
// Bullet returns a bulleted item.
|
// Bullet returns a bulleted item.
|
||||||
func Bullet(text string) string {
|
func Bullet(text string) string {
|
||||||
return fmt.Sprintf(" %s %s", DimStyle.Render(SymbolBullet), text)
|
return fmt.Sprintf(" %s %s", DimStyle.Render(SymbolBullet), text)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue