feat(ux): visual improvements for list tree, graph, and show commands

bd list --tree:
- Use actual parent-child dependencies instead of dotted ID hierarchy
- Treat epic dependencies as parent-child relationships
- Sort children by priority (P0 first)
- Fix tree display in daemon mode with read-only store access

bd graph:
- Add --all flag to show dependency graph of all open issues
- Add --compact flag for tree-style rendering (reduces 44+ lines to 13)
- Fix "needs:N" cognitive noise by using semantic colors
- Add blocks:N indicator with semantic red coloring

bd show:
- Tufte-aligned header with status icon, priority, and type badges
- Add glamour markdown rendering with auto light/dark mode detection
- Cap markdown line width at 100 chars for readability
- Mute entire row for closed dependencies (work done, no attention needed)

Design system:
- Add shared status icons (○ ◐ ● ✓ ❄) with semantic colors
- Implement priority colors: P0 red, P1 orange, P2 muted gold, P3-P4 neutral
- Add TrueColor profile for distinct hex color rendering
- Type badges for epic (purple) and bug (red)

Design principles:
- Semantic colors only for actionable items
- Closed items fade (muted gray)
- Icons > text labels for better scanability

Co-Authored-By: SageOx <ox@sageox.ai>
This commit is contained in:
Ryan Snodgrass
2026-01-08 20:49:09 -08:00
parent 7e70de1f6d
commit cfd1f39e1e
11 changed files with 1064 additions and 319 deletions

View File

@@ -57,16 +57,17 @@ func TestRenderStatusAndPriority(t *testing.T) {
}
}
// RenderPriority now includes the priority icon (●)
priorityCases := []struct {
priority int
want string
}{
{0, PriorityP0Style.Render("P0")},
{1, PriorityP1Style.Render("P1")},
{2, PriorityP2Style.Render("P2")},
{3, PriorityP3Style.Render("P3")},
{4, PriorityP4Style.Render("P4")},
{5, "P5"},
{0, PriorityP0Style.Render(PriorityIcon + " P0")},
{1, PriorityP1Style.Render(PriorityIcon + " P1")},
{2, PriorityP2Style.Render(PriorityIcon + " P2")},
{3, PriorityP3Style.Render(PriorityIcon + " P3")},
{4, PriorityP4Style.Render(PriorityIcon + " P4")},
{5, PriorityIcon + " P5"},
}
for _, tc := range priorityCases {
if got := RenderPriority(tc.priority); got != tc.want {
@@ -74,6 +75,11 @@ func TestRenderStatusAndPriority(t *testing.T) {
}
}
// RenderPriorityCompact returns just "P0" without icon
if got := RenderPriorityCompact(0); !strings.Contains(got, "P0") {
t.Fatalf("compact priority should contain P0, got %q", got)
}
if got := RenderPriorityForStatus(0, "closed"); got != "P0" {
t.Fatalf("closed priority should be plain text, got %q", got)
}