1package styles
2
3import (
4 "encoding/json"
5 "fmt"
6 "strings"
7
8 "github.com/charmbracelet/lipgloss"
9
10 "github.com/bnema/dumber/internal/domain/entity"
11)
12
13// ConfigSchemaRenderer renders configuration schema information.
14type ConfigSchemaRenderer struct {
15 theme *Theme
16}
17
18// NewConfigSchemaRenderer creates a new ConfigSchemaRenderer.
19func NewConfigSchemaRenderer(theme *Theme) *ConfigSchemaRenderer {
20 return &ConfigSchemaRenderer{theme: theme}
21}
22
23// Render renders the configuration schema in styled format.
24func (r *ConfigSchemaRenderer) Render(keys []entity.ConfigKeyInfo) string {
25 if len(keys) == 0 {
26 return r.theme.Subtle.Render("No configuration keys found")
27 }
28
29 // Group keys by section
30 sections := groupBySection(keys)
31
32 // Build output
33 var parts []string
34
35 // Header
36 header := r.renderHeader()
37 parts = append(parts, header, "")
38
39 // Render each section
40 sectionOrder := []string{
41 "Appearance",
42 "Logging",
43 "History",
44 "Search",
45 "Dmenu",
46 "Workspace",
47 "Session",
48 "Omnibox",
49 "Content Filtering",
50 "Clipboard",
51 "Rendering",
52 "Media",
53 "Update",
54 "Downloads",
55 "Debug",
56 "Performance",
57 "Runtime",
58 "Database",
59 }
60
61 for _, section := range sectionOrder {
62 if sectionKeys, ok := sections[section]; ok {
63 parts = append(parts, r.renderSection(section, sectionKeys), "")
64 }
65 }
66
67 return strings.Join(parts, "\n")
68}
69
70// RenderJSON renders the configuration schema as JSON.
71func (*ConfigSchemaRenderer) RenderJSON(keys []entity.ConfigKeyInfo) (string, error) {
72 data, err := json.MarshalIndent(keys, "", " ")
73 if err != nil {
74 return "", fmt.Errorf("marshal schema: %w", err)
75 }
76 return string(data), nil
77}
78
79func (r *ConfigSchemaRenderer) renderHeader() string {
80 iconStyle := lipgloss.NewStyle().Foreground(r.theme.Accent)
81 title := fmt.Sprintf("%s %s", iconStyle.Render(IconConfig), r.theme.Title.Render("Config Schema Reference"))
82 return title
83}
84
85func groupBySection(keys []entity.ConfigKeyInfo) map[string][]entity.ConfigKeyInfo {
86 sections := make(map[string][]entity.ConfigKeyInfo)
87 for _, key := range keys {
88 sections[key.Section] = append(sections[key.Section], key)
89 }
90 return sections
91}
92
93func (r *ConfigSchemaRenderer) renderSection(name string, keys []entity.ConfigKeyInfo) string {
94 var lines []string
95
96 for _, key := range keys {
97 lines = append(lines, r.renderKey(key))
98 }
99
100 body := strings.Join(lines, "\n")
101
102 // Create section header (no extra padding/margin)
103 sectionHeader := r.theme.Highlight.Render(name)
104
105 // Build box content: header directly followed by keys
106 boxContent := sectionHeader + "\n" + body
107
108 // Use box style without top padding
109 boxStyle := r.theme.Box.PaddingTop(0)
110
111 return boxStyle.Render(boxContent)
112}
113
114func (r *ConfigSchemaRenderer) renderKey(key entity.ConfigKeyInfo) string {
115 keyStyle := r.theme.Normal.Bold(true)
116 typeStyle := r.theme.Subtle
117 defaultStyle := lipgloss.NewStyle().Foreground(r.theme.Accent)
118 descStyle := r.theme.Subtle
119 valuesStyle := r.theme.Normal
120
121 // Line 1: key name, type, default
122 line1 := fmt.Sprintf(
123 "%s %s %s",
124 keyStyle.Render(key.Key),
125 typeStyle.Render(key.Type),
126 defaultStyle.Render(key.Default),
127 )
128
129 // Line 2: description (indented)
130 line2 := fmt.Sprintf(" %s", descStyle.Render(key.Description))
131
132 result := line1 + "\n" + line2
133
134 // Line 3 (optional): values or range
135 if len(key.Values) > 0 {
136 valuesText := "Values: " + strings.Join(key.Values, ", ")
137 line3 := fmt.Sprintf(" %s", valuesStyle.Render(valuesText))
138 result += "\n" + line3
139 } else if key.Range != "" {
140 rangeText := "Range: " + key.Range
141 line3 := fmt.Sprintf(" %s", valuesStyle.Render(rangeText))
142 result += "\n" + line3
143 }
144
145 return result
146}