feat(ux): reorganize command groups and add Tufte-inspired help styling

- Add semantic coloring to help output (section headers, flags, types)
- Reorganize commands into clearer categories:
  - "Views & Reports" for readonly queries (ready, stats, blocked, etc.)
  - "Maintenance" for health/repair tasks (doctor, validate, migrate, etc.)
  - Merge "Issue Metadata" back into "Working With Issues"
- Highlight "doctor" as primary maintenance entry point with "(start here)"
- Create docs/UI_PHILOSOPHY.md documenting Tufte-inspired design principles
- Add guidance for PR to not include .beads/issues.jsonl
This commit is contained in:
Ryan Snodgrass
2025-12-20 12:57:16 -08:00
parent f6392efd07
commit fb1dff4f56
3 changed files with 206 additions and 0 deletions

View File

@@ -857,3 +857,8 @@ For more details, see README.md and docs/QUICKSTART.md.
- NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
## Pull Requests (PR)
- Make sure that .beads/issues.jsonl is never submitted during PRs; revert the changes to .beads/issues.jsonl on the branch.

View File

@@ -6,6 +6,7 @@ import (
"os"
"os/signal"
"path/filepath"
"regexp"
"runtime/pprof"
"runtime/trace"
"slices"
@@ -24,6 +25,7 @@ import (
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/storage/memory"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/ui"
"github.com/steveyegge/beads/internal/utils"
)
@@ -140,6 +142,148 @@ func init() {
// Add --version flag to root command (same behavior as version subcommand)
rootCmd.Flags().BoolP("version", "V", false, "Print version information")
// Command groups for organized help output (Tufte-inspired)
rootCmd.AddGroup(&cobra.Group{ID: "issues", Title: "Working With Issues:"})
rootCmd.AddGroup(&cobra.Group{ID: "views", Title: "Views & Reports:"})
rootCmd.AddGroup(&cobra.Group{ID: "deps", Title: "Dependencies & Structure:"})
rootCmd.AddGroup(&cobra.Group{ID: "sync", Title: "Sync & Data:"})
rootCmd.AddGroup(&cobra.Group{ID: "setup", Title: "Setup & Configuration:"})
// NOTE: Many maintenance commands (clean, cleanup, compact, validate, repair-deps)
// should eventually be consolidated into 'bd doctor' and 'bd doctor --fix' to simplify
// the user experience. The doctor command can detect issues and offer fixes interactively.
rootCmd.AddGroup(&cobra.Group{ID: "maint", Title: "Maintenance:"})
rootCmd.AddGroup(&cobra.Group{ID: "advanced", Title: "Integrations & Advanced:"})
// Custom help function with semantic coloring (Tufte-inspired)
// Note: Usage output (shown on errors) is not styled to avoid recursion issues
rootCmd.SetHelpFunc(colorizedHelpFunc)
}
// colorizedHelpFunc wraps Cobra's default help with semantic coloring
// Applies subtle accent color to group headers for visual hierarchy
func colorizedHelpFunc(cmd *cobra.Command, args []string) {
// Build full help output: Long description + Usage
var output strings.Builder
// Include Long description first (like Cobra's default help)
if cmd.Long != "" {
output.WriteString(cmd.Long)
output.WriteString("\n\n")
} else if cmd.Short != "" {
output.WriteString(cmd.Short)
output.WriteString("\n\n")
}
// Add the usage string which contains commands, flags, etc.
output.WriteString(cmd.UsageString())
// Apply semantic coloring
result := colorizeHelpOutput(output.String())
fmt.Print(result)
}
// colorizeHelpOutput applies semantic colors to help text
// - Group headers get accent color for visual hierarchy
// - Section headers (Examples:, Flags:) get accent color
// - Command names get subtle styling for scanability
// - Flag names get bold styling, types get muted
// - Default values get muted styling
func colorizeHelpOutput(help string) string {
// Match group header lines (e.g., "Working With Issues:")
// These are standalone lines ending with ":" and followed by commands
groupHeaderRE := regexp.MustCompile(`(?m)^([A-Z][A-Za-z &]+:)\s*$`)
result := groupHeaderRE.ReplaceAllStringFunc(help, func(match string) string {
// Trim whitespace, colorize, then restore
trimmed := strings.TrimSpace(match)
return ui.RenderAccent(trimmed)
})
// Match section headers in subcommand help (Examples:, Flags:, etc.)
sectionHeaderRE := regexp.MustCompile(`(?m)^(Examples|Flags|Usage|Global Flags|Aliases|Available Commands):`)
result = sectionHeaderRE.ReplaceAllStringFunc(result, func(match string) string {
return ui.RenderAccent(match)
})
// Match command lines: " command Description text"
// Commands are indented with 2 spaces, followed by spaces, then description
// Pattern matches: indent + command-name (with hyphens) + spacing + description
cmdLineRE := regexp.MustCompile(`(?m)^( )([a-z][a-z0-9]*(?:-[a-z0-9]+)*)(\s{2,})(.*)$`)
result = cmdLineRE.ReplaceAllStringFunc(result, func(match string) string {
parts := cmdLineRE.FindStringSubmatch(match)
if len(parts) != 5 {
return match
}
indent := parts[1]
cmdName := parts[2]
spacing := parts[3]
description := parts[4]
// Colorize command references in description (e.g., 'comments add')
description = colorizeCommandRefs(description)
// Highlight entry point hints (e.g., "(start here)")
description = highlightEntryPoints(description)
// Subtle styling on command name for scanability
return indent + ui.RenderCommand(cmdName) + spacing + description
})
// Match flag lines: " -f, --file string Description"
// Pattern: indent + flags + spacing + optional type + description
flagLineRE := regexp.MustCompile(`(?m)^(\s+)(-\w,\s+--[\w-]+|--[\w-]+)(\s+)(string|int|duration|bool)?(\s*.*)$`)
result = flagLineRE.ReplaceAllStringFunc(result, func(match string) string {
parts := flagLineRE.FindStringSubmatch(match)
if len(parts) < 6 {
return match
}
indent := parts[1]
flags := parts[2]
spacing := parts[3]
typeStr := parts[4]
desc := parts[5]
// Mute default values in description
desc = muteDefaults(desc)
if typeStr != "" {
return indent + ui.RenderCommand(flags) + spacing + ui.RenderMuted(typeStr) + desc
}
return indent + ui.RenderCommand(flags) + spacing + desc
})
return result
}
// muteDefaults applies muted styling to default value annotations
func muteDefaults(text string) string {
defaultRE := regexp.MustCompile(`(\(default[^)]*\))`)
return defaultRE.ReplaceAllStringFunc(text, func(match string) string {
return ui.RenderMuted(match)
})
}
// highlightEntryPoints applies accent styling to entry point hints like "(start here)"
func highlightEntryPoints(text string) string {
entryRE := regexp.MustCompile(`(\(start here\))`)
return entryRE.ReplaceAllStringFunc(text, func(match string) string {
return ui.RenderAccent(match)
})
}
// colorizeCommandRefs applies command styling to references in text
// Matches patterns like 'command name' or 'bd command'
func colorizeCommandRefs(text string) string {
// Match 'command words' in single quotes (e.g., 'comments add')
cmdRefRE := regexp.MustCompile(`'([a-z][a-z0-9 -]+)'`)
return cmdRefRE.ReplaceAllStringFunc(text, func(match string) string {
// Extract the command name without quotes
inner := match[1 : len(match)-1]
return "'" + ui.RenderCommand(inner) + "'"
})
}
var rootCmd = &cobra.Command{

57
docs/UI_PHILOSOPHY.md Normal file
View File

@@ -0,0 +1,57 @@
# UI/UX Philosophy
Beads CLI follows Tufte-inspired design principles for terminal output.
## Core Principles
1. **Maximize data-ink ratio**: Only color what demands attention
2. **Respect cognitive load**: Let whitespace and position do most of the work
3. **Create scannable hierarchy**: Headers mark territory, bold creates scan targets
## Color Usage
| Purpose | Style | When to Use |
|---------|-------|-------------|
| Navigation landmarks | Accent (blue) | Section headers, group titles |
| Scan targets | Bold | Command names, flag names |
| De-emphasized | Muted (gray) | Types, defaults, closed items |
| Semantic states | Pass/Warn/Fail | P0/P1 priority, bugs, blocked |
| Standard text | Plain | Descriptions, prose, examples |
## Anti-Patterns
- Don't highlight everything (defeats the purpose)
- Don't use color for decoration
- Don't style closed/completed items (they're done, users don't care)
- Keep examples plain (copy-paste friendly)
## Help Output Styling
### Main Help (`bd help`)
- **Group headers** (Working With Issues:, Views & Reports:, etc.): Accent color
- **Command names** in listings: Bold
- **Command references** in descriptions ('comments add'): Bold
### Subcommand Help (`bd <cmd> --help`)
- **Section headers** (Usage:, Flags:, Examples:): Accent color
- **Flag names** (-f, --file): Bold
- **Type annotations** (string, int, duration): Muted
- **Default values** (default: ...): Muted
- **Descriptions**: Plain
- **Examples**: Plain (copy-paste friendly)
## Ayu Theme
All colors use the Ayu theme with adaptive light/dark mode support.
See `internal/ui/styles.go` for implementation.
| Color | Light Mode | Dark Mode | Usage |
|-------|------------|-----------|-------|
| Accent | #399ee6 | #59c2ff | Headers, links |
| Command | Bold white | Bold white | Command/flag names |
| Muted | #828c99 | #6c7680 | Types, defaults |
| Pass | #6cbf43 | #7fd962 | Success states |
| Warn | #e6ba7e | #ffb454 | Warnings, P1 |
| Fail | #f07171 | #f26d78 | Errors, bugs, P0 |