v0.24.0 a261e52

config_schema.go RawHistoryFolder

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		"Rendering",
51		"Media",
52		"Update",
53		"Debug",
54		"Performance",
55		"Runtime",
56		"Database",
57	}
58
59	for _, section := range sectionOrder {
60		if sectionKeys, ok := sections[section]; ok {
61			parts = append(parts, r.renderSection(section, sectionKeys), "")
62		}
63	}
64
65	return strings.Join(parts, "\n")
66}
67
68// RenderJSON renders the configuration schema as JSON.
69func (*ConfigSchemaRenderer) RenderJSON(keys []entity.ConfigKeyInfo) (string, error) {
70	data, err := json.MarshalIndent(keys, "", "  ")
71	if err != nil {
72		return "", fmt.Errorf("marshal schema: %w", err)
73	}
74	return string(data), nil
75}
76
77func (r *ConfigSchemaRenderer) renderHeader() string {
78	iconStyle := lipgloss.NewStyle().Foreground(r.theme.Accent)
79	title := fmt.Sprintf("%s %s", iconStyle.Render(IconConfig), r.theme.Title.Render("Config Schema Reference"))
80	return title
81}
82
83func groupBySection(keys []entity.ConfigKeyInfo) map[string][]entity.ConfigKeyInfo {
84	sections := make(map[string][]entity.ConfigKeyInfo)
85	for _, key := range keys {
86		sections[key.Section] = append(sections[key.Section], key)
87	}
88	return sections
89}
90
91func (r *ConfigSchemaRenderer) renderSection(name string, keys []entity.ConfigKeyInfo) string {
92	var lines []string
93
94	for _, key := range keys {
95		lines = append(lines, r.renderKey(key))
96	}
97
98	body := strings.Join(lines, "\n")
99
100	// Create section header (no extra padding/margin)
101	sectionHeader := r.theme.Highlight.Render(name)
102
103	// Build box content: header directly followed by keys
104	boxContent := sectionHeader + "\n" + body
105
106	// Use box style without top padding
107	boxStyle := r.theme.Box.PaddingTop(0)
108
109	return boxStyle.Render(boxContent)
110}
111
112func (r *ConfigSchemaRenderer) renderKey(key entity.ConfigKeyInfo) string {
113	keyStyle := r.theme.Normal.Bold(true)
114	typeStyle := r.theme.Subtle
115	defaultStyle := lipgloss.NewStyle().Foreground(r.theme.Accent)
116	descStyle := r.theme.Subtle
117	valuesStyle := r.theme.Normal
118
119	// Line 1: key name, type, default
120	line1 := fmt.Sprintf(
121		"%s  %s  %s",
122		keyStyle.Render(key.Key),
123		typeStyle.Render(key.Type),
124		defaultStyle.Render(key.Default),
125	)
126
127	// Line 2: description (indented)
128	line2 := fmt.Sprintf("  %s", descStyle.Render(key.Description))
129
130	result := line1 + "\n" + line2
131
132	// Line 3 (optional): values or range
133	if len(key.Values) > 0 {
134		valuesText := "Values: " + strings.Join(key.Values, ", ")
135		line3 := fmt.Sprintf("  %s", valuesStyle.Render(valuesText))
136		result += "\n" + line3
137	} else if key.Range != "" {
138		rangeText := "Range: " + key.Range
139		line3 := fmt.Sprintf("  %s", valuesStyle.Render(rangeText))
140		result += "\n" + line3
141	}
142
143	return result
144}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close