refactor(ui): standardize on lipgloss semantic color system
Replace all fatih/color usages with internal/ui package that provides: - Semantic color tokens (Pass, Warn, Fail, Accent, Muted) - Adaptive light/dark mode support via Lipgloss AdaptiveColor - Ayu theme colors for consistent, accessible output - Tufte-inspired data-ink ratio principles Files migrated: 35 command files in cmd/bd/ Add docs/ui-philosophy.md documenting: - Semantic token usage guidelines - Light/dark terminal optimization rationale - Tufte and perceptual UI/UX theory application - When to use (and not use) color in CLI output
This commit is contained in:
@@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/beads/internal/config"
|
||||
"github.com/steveyegge/beads/internal/rpc"
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
"github.com/steveyegge/beads/internal/ui"
|
||||
"github.com/steveyegge/beads/internal/util"
|
||||
)
|
||||
var readyCmd = &cobra.Command{
|
||||
@@ -105,20 +105,20 @@ var readyCmd = &cobra.Command{
|
||||
hasOpenIssues = stats.OpenIssues > 0 || stats.InProgressIssues > 0
|
||||
}
|
||||
}
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
if hasOpenIssues {
|
||||
fmt.Printf("\n%s No ready work found (all issues have blocking dependencies)\n\n",
|
||||
yellow("✨"))
|
||||
ui.RenderWarn("✨"))
|
||||
} else {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("\n%s No open issues\n\n", green("✨"))
|
||||
fmt.Printf("\n%s No open issues\n\n", ui.RenderPass("✨"))
|
||||
}
|
||||
return
|
||||
}
|
||||
cyan := color.New(color.FgCyan).SprintFunc()
|
||||
fmt.Printf("\n%s Ready work (%d issues with no blockers):\n\n", cyan("📋"), len(issues))
|
||||
fmt.Printf("\n%s Ready work (%d issues with no blockers):\n\n", ui.RenderAccent("📋"), len(issues))
|
||||
for i, issue := range issues {
|
||||
fmt.Printf("%d. [P%d] %s: %s\n", i+1, issue.Priority, issue.ID, issue.Title)
|
||||
fmt.Printf("%d. [%s] [%s] %s: %s\n", i+1,
|
||||
ui.RenderPriority(issue.Priority),
|
||||
ui.RenderType(string(issue.IssueType)),
|
||||
ui.RenderID(issue.ID), issue.Title)
|
||||
if issue.EstimatedMinutes != nil {
|
||||
fmt.Printf(" Estimate: %d min\n", *issue.EstimatedMinutes)
|
||||
}
|
||||
@@ -175,21 +175,21 @@ var readyCmd = &cobra.Command{
|
||||
hasOpenIssues = stats.OpenIssues > 0 || stats.InProgressIssues > 0
|
||||
}
|
||||
if hasOpenIssues {
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Printf("\n%s No ready work found (all issues have blocking dependencies)\n\n",
|
||||
yellow("✨"))
|
||||
ui.RenderWarn("✨"))
|
||||
} else {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("\n%s No open issues\n\n", green("✨"))
|
||||
fmt.Printf("\n%s No open issues\n\n", ui.RenderPass("✨"))
|
||||
}
|
||||
// Show tip even when no ready work found
|
||||
maybeShowTip(store)
|
||||
return
|
||||
}
|
||||
cyan := color.New(color.FgCyan).SprintFunc()
|
||||
fmt.Printf("\n%s Ready work (%d issues with no blockers):\n\n", cyan("📋"), len(issues))
|
||||
fmt.Printf("\n%s Ready work (%d issues with no blockers):\n\n", ui.RenderAccent("📋"), len(issues))
|
||||
for i, issue := range issues {
|
||||
fmt.Printf("%d. [P%d] %s: %s\n", i+1, issue.Priority, issue.ID, issue.Title)
|
||||
fmt.Printf("%d. [%s] [%s] %s: %s\n", i+1,
|
||||
ui.RenderPriority(issue.Priority),
|
||||
ui.RenderType(string(issue.IssueType)),
|
||||
ui.RenderID(issue.ID), issue.Title)
|
||||
if issue.EstimatedMinutes != nil {
|
||||
fmt.Printf(" Estimate: %d min\n", *issue.EstimatedMinutes)
|
||||
}
|
||||
@@ -233,14 +233,14 @@ var blockedCmd = &cobra.Command{
|
||||
return
|
||||
}
|
||||
if len(blocked) == 0 {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("\n%s No blocked issues\n\n", green("✨"))
|
||||
fmt.Printf("\n%s No blocked issues\n\n", ui.RenderPass("✨"))
|
||||
return
|
||||
}
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("\n%s Blocked issues (%d):\n\n", red("🚫"), len(blocked))
|
||||
fmt.Printf("\n%s Blocked issues (%d):\n\n", ui.RenderFail("🚫"), len(blocked))
|
||||
for _, issue := range blocked {
|
||||
fmt.Printf("[P%d] %s: %s\n", issue.Priority, issue.ID, issue.Title)
|
||||
fmt.Printf("[%s] %s: %s\n",
|
||||
ui.RenderPriority(issue.Priority),
|
||||
ui.RenderID(issue.ID), issue.Title)
|
||||
blockedBy := issue.BlockedBy
|
||||
if blockedBy == nil {
|
||||
blockedBy = []string{}
|
||||
@@ -272,16 +272,13 @@ var statsCmd = &cobra.Command{
|
||||
outputJSON(stats)
|
||||
return
|
||||
}
|
||||
cyan := color.New(color.FgCyan).SprintFunc()
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Printf("\n%s Beads Statistics:\n\n", cyan("📊"))
|
||||
fmt.Printf("\n%s Beads Statistics:\n\n", ui.RenderAccent("📊"))
|
||||
fmt.Printf("Total Issues: %d\n", stats.TotalIssues)
|
||||
fmt.Printf("Open: %s\n", green(fmt.Sprintf("%d", stats.OpenIssues)))
|
||||
fmt.Printf("In Progress: %s\n", yellow(fmt.Sprintf("%d", stats.InProgressIssues)))
|
||||
fmt.Printf("Open: %s\n", ui.RenderPass(fmt.Sprintf("%d", stats.OpenIssues)))
|
||||
fmt.Printf("In Progress: %s\n", ui.RenderWarn(fmt.Sprintf("%d", stats.InProgressIssues)))
|
||||
fmt.Printf("Closed: %d\n", stats.ClosedIssues)
|
||||
fmt.Printf("Blocked: %d\n", stats.BlockedIssues)
|
||||
fmt.Printf("Ready: %s\n", green(fmt.Sprintf("%d", stats.ReadyIssues)))
|
||||
fmt.Printf("Blocked: %s\n", ui.RenderFail(fmt.Sprintf("%d", stats.BlockedIssues)))
|
||||
fmt.Printf("Ready: %s\n", ui.RenderPass(fmt.Sprintf("%d", stats.ReadyIssues)))
|
||||
if stats.TombstoneIssues > 0 {
|
||||
fmt.Printf("Deleted: %d (tombstones)\n", stats.TombstoneIssues)
|
||||
}
|
||||
@@ -316,16 +313,13 @@ var statsCmd = &cobra.Command{
|
||||
outputJSON(stats)
|
||||
return
|
||||
}
|
||||
cyan := color.New(color.FgCyan).SprintFunc()
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Printf("\n%s Beads Statistics:\n\n", cyan("📊"))
|
||||
fmt.Printf("\n%s Beads Statistics:\n\n", ui.RenderAccent("📊"))
|
||||
fmt.Printf("Total Issues: %d\n", stats.TotalIssues)
|
||||
fmt.Printf("Open: %s\n", green(fmt.Sprintf("%d", stats.OpenIssues)))
|
||||
fmt.Printf("In Progress: %s\n", yellow(fmt.Sprintf("%d", stats.InProgressIssues)))
|
||||
fmt.Printf("Open: %s\n", ui.RenderPass(fmt.Sprintf("%d", stats.OpenIssues)))
|
||||
fmt.Printf("In Progress: %s\n", ui.RenderWarn(fmt.Sprintf("%d", stats.InProgressIssues)))
|
||||
fmt.Printf("Closed: %d\n", stats.ClosedIssues)
|
||||
fmt.Printf("Blocked: %d\n", stats.BlockedIssues)
|
||||
fmt.Printf("Ready: %s\n", green(fmt.Sprintf("%d", stats.ReadyIssues)))
|
||||
fmt.Printf("Blocked: %s\n", ui.RenderFail(fmt.Sprintf("%d", stats.BlockedIssues)))
|
||||
fmt.Printf("Ready: %s\n", ui.RenderPass(fmt.Sprintf("%d", stats.ReadyIssues)))
|
||||
if stats.TombstoneIssues > 0 {
|
||||
fmt.Printf("Deleted: %d (tombstones)\n", stats.TombstoneIssues)
|
||||
}
|
||||
@@ -333,7 +327,7 @@ var statsCmd = &cobra.Command{
|
||||
fmt.Printf("Pinned: %d\n", stats.PinnedIssues)
|
||||
}
|
||||
if stats.EpicsEligibleForClosure > 0 {
|
||||
fmt.Printf("Epics Ready to Close: %s\n", green(fmt.Sprintf("%d", stats.EpicsEligibleForClosure)))
|
||||
fmt.Printf("Epics Ready to Close: %s\n", ui.RenderPass(fmt.Sprintf("%d", stats.EpicsEligibleForClosure)))
|
||||
}
|
||||
if stats.AverageLeadTime > 0 {
|
||||
fmt.Printf("Avg Lead Time: %.1f hours\n", stats.AverageLeadTime)
|
||||
|
||||
Reference in New Issue
Block a user