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:
@@ -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.
|
||||
|
||||
144
cmd/bd/main.go
144
cmd/bd/main.go
@@ -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
57
docs/UI_PHILOSOPHY.md
Normal 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 |
|
||||
Reference in New Issue
Block a user