detached 18449b5

refactor(systemviews): render config with templ

commit
18449b5dbe6e034f682566aca06b92e844697c43
author
brice · · 2026-04-24 02:53 UTC
parent
a743a66
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +446 -115
A internal/ui/systemviews/config.templ +67 -0
@@ -0,0 +1,67 @@
1+package systemviews
2+
3+templ ConfigView(data configRenderData) {
4+ @Section("sv-config-values", "Config values") {
5+ @KV(flattenValues(data.Config))
6+ }
7+ @Section("sv-config-keybindings", "Keybindings") {
8+ @Meta(configKeybindingSummary(data.Keybindings))
9+ @ConfigKeybindings(data.Keybindings)
10+ }
11+}
12+
13+templ ConfigKeybindings(keybindings any) {
14+ {{ groups := configKeybindingGroups(keybindings) }}
15+ if groups == nil {
16+ @EmptyState("Keybindings unavailable")
17+ } else if len(groups) == 0 {
18+ @EmptyState("No keybindings configured")
19+ } else {
20+ for _, group := range groups {
21+ @ConfigKeybindingGroup(group)
22+ }
23+ }
24+}
25+
26+templ ConfigKeybindingGroup(group renderedKeybindingGroup) {
27+ <div class="sv-group">
28+ <div class="sv-group-header">
29+ <div>
30+ <h4>{ keybindingGroupTitle(group) }</h4>
31+ if keybindingGroupMeta(group) != "" {
32+ <p class="sv-meta">{ keybindingGroupMeta(group) }</p>
33+ }
34+ </div>
35+ </div>
36+ <div class="sv-group-body">
37+ if len(group.Bindings) == 0 {
38+ @EmptyState("No bindings in this group")
39+ } else {
40+ <ul class="sv-list">
41+ for _, binding := range group.Bindings {
42+ @ConfigKeybinding(binding)
43+ }
44+ </ul>
45+ }
46+ </div>
47+ </div>
48+}
49+
50+templ ConfigKeybinding(binding renderedKeybinding) {
51+ <li class="sv-list-row">
52+ <div class="sv-keybinding-row">
53+ <div>
54+ <strong>{ keybindingDescription(binding) }</strong>
55+ <p class="sv-meta">{ keybindingAction(binding) }</p>
56+ </div>
57+ <div class="sv-keybinding-keys">
58+ @KeyList(binding.Keys)
59+ <span class="sv-badge">{ keybindingStatus(binding) }</span>
60+ if keybindingIsCustom(binding) && len(binding.DefaultKeys) > 0 {
61+ <span class="sv-meta">default:</span>
62+ @KeyList(binding.DefaultKeys)
63+ }
64+ </div>
65+ </div>
66+ </li>
67+}
@@ -0,0 +1,67 @@
1+package systemviews
2+
3+templ ConfigView(data configRenderData) {
4+ @Section("sv-config-values", "Config values") {
5+ @KV(flattenValues(data.Config))
6+ }
7+ @Section("sv-config-keybindings", "Keybindings") {
8+ @Meta(configKeybindingSummary(data.Keybindings))
9+ @ConfigKeybindings(data.Keybindings)
10+ }
11+}
12+
13+templ ConfigKeybindings(keybindings any) {
14+ {{ groups := configKeybindingGroups(keybindings) }}
15+ if groups == nil {
16+ @EmptyState("Keybindings unavailable")
17+ } else if len(groups) == 0 {
18+ @EmptyState("No keybindings configured")
19+ } else {
20+ for _, group := range groups {
21+ @ConfigKeybindingGroup(group)
22+ }
23+ }
24+}
25+
26+templ ConfigKeybindingGroup(group renderedKeybindingGroup) {
27+ <div class="sv-group">
28+ <div class="sv-group-header">
29+ <div>
30+ <h4>{ keybindingGroupTitle(group) }</h4>
31+ if keybindingGroupMeta(group) != "" {
32+ <p class="sv-meta">{ keybindingGroupMeta(group) }</p>
33+ }
34+ </div>
35+ </div>
36+ <div class="sv-group-body">
37+ if len(group.Bindings) == 0 {
38+ @EmptyState("No bindings in this group")
39+ } else {
40+ <ul class="sv-list">
41+ for _, binding := range group.Bindings {
42+ @ConfigKeybinding(binding)
43+ }
44+ </ul>
45+ }
46+ </div>
47+ </div>
48+}
49+
50+templ ConfigKeybinding(binding renderedKeybinding) {
51+ <li class="sv-list-row">
52+ <div class="sv-keybinding-row">
53+ <div>
54+ <strong>{ keybindingDescription(binding) }</strong>
55+ <p class="sv-meta">{ keybindingAction(binding) }</p>
56+ </div>
57+ <div class="sv-keybinding-keys">
58+ @KeyList(binding.Keys)
59+ <span class="sv-badge">{ keybindingStatus(binding) }</span>
60+ if keybindingIsCustom(binding) && len(binding.DefaultKeys) > 0 {
61+ <span class="sv-meta">default:</span>
62+ @KeyList(binding.DefaultKeys)
63+ }
64+ </div>
65+ </div>
66+ </li>
67+}
A internal/ui/systemviews/config_templ.go +311 -0
@@ -0,0 +1,311 @@
1+// Code generated by templ - DO NOT EDIT.
2+
3+package systemviews
4+
5+//lint:file-ignore SA4006 This context is only used if a nested component is present.
6+
7+import "github.com/a-h/templ"
8+import templruntime "github.com/a-h/templ/runtime"
9+
10+func ConfigView(data configRenderData) templ.Component {
11+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
12+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
13+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
14+ return templ_7745c5c3_CtxErr
15+ }
16+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
17+ if !templ_7745c5c3_IsBuffer {
18+ defer func() {
19+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
20+ if templ_7745c5c3_Err == nil {
21+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
22+ }
23+ }()
24+ }
25+ ctx = templ.InitializeContext(ctx)
26+ templ_7745c5c3_Var1 := templ.GetChildren(ctx)
27+ if templ_7745c5c3_Var1 == nil {
28+ templ_7745c5c3_Var1 = templ.NopComponent
29+ }
30+ ctx = templ.ClearChildren(ctx)
31+ templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
32+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
33+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
34+ if !templ_7745c5c3_IsBuffer {
35+ defer func() {
36+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
37+ if templ_7745c5c3_Err == nil {
38+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
39+ }
40+ }()
41+ }
42+ ctx = templ.InitializeContext(ctx)
43+ templ_7745c5c3_Err = KV(flattenValues(data.Config)).Render(ctx, templ_7745c5c3_Buffer)
44+ if templ_7745c5c3_Err != nil {
45+ return templ_7745c5c3_Err
46+ }
47+ return nil
48+ })
49+ templ_7745c5c3_Err = Section("sv-config-values", "Config values").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
50+ if templ_7745c5c3_Err != nil {
51+ return templ_7745c5c3_Err
52+ }
53+ templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
54+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
55+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
56+ if !templ_7745c5c3_IsBuffer {
57+ defer func() {
58+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
59+ if templ_7745c5c3_Err == nil {
60+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
61+ }
62+ }()
63+ }
64+ ctx = templ.InitializeContext(ctx)
65+ templ_7745c5c3_Err = Meta(configKeybindingSummary(data.Keybindings)).Render(ctx, templ_7745c5c3_Buffer)
66+ if templ_7745c5c3_Err != nil {
67+ return templ_7745c5c3_Err
68+ }
69+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ")
70+ if templ_7745c5c3_Err != nil {
71+ return templ_7745c5c3_Err
72+ }
73+ templ_7745c5c3_Err = ConfigKeybindings(data.Keybindings).Render(ctx, templ_7745c5c3_Buffer)
74+ if templ_7745c5c3_Err != nil {
75+ return templ_7745c5c3_Err
76+ }
77+ return nil
78+ })
79+ templ_7745c5c3_Err = Section("sv-config-keybindings", "Keybindings").Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
80+ if templ_7745c5c3_Err != nil {
81+ return templ_7745c5c3_Err
82+ }
83+ return nil
84+ })
85+}
86+
87+func ConfigKeybindings(keybindings any) templ.Component {
88+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
89+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
90+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
91+ return templ_7745c5c3_CtxErr
92+ }
93+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
94+ if !templ_7745c5c3_IsBuffer {
95+ defer func() {
96+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
97+ if templ_7745c5c3_Err == nil {
98+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
99+ }
100+ }()
101+ }
102+ ctx = templ.InitializeContext(ctx)
103+ templ_7745c5c3_Var4 := templ.GetChildren(ctx)
104+ if templ_7745c5c3_Var4 == nil {
105+ templ_7745c5c3_Var4 = templ.NopComponent
106+ }
107+ ctx = templ.ClearChildren(ctx)
108+ groups := configKeybindingGroups(keybindings)
109+ if groups == nil {
110+ templ_7745c5c3_Err = EmptyState("Keybindings unavailable").Render(ctx, templ_7745c5c3_Buffer)
111+ if templ_7745c5c3_Err != nil {
112+ return templ_7745c5c3_Err
113+ }
114+ } else if len(groups) == 0 {
115+ templ_7745c5c3_Err = EmptyState("No keybindings configured").Render(ctx, templ_7745c5c3_Buffer)
116+ if templ_7745c5c3_Err != nil {
117+ return templ_7745c5c3_Err
118+ }
119+ } else {
120+ for _, group := range groups {
121+ templ_7745c5c3_Err = ConfigKeybindingGroup(group).Render(ctx, templ_7745c5c3_Buffer)
122+ if templ_7745c5c3_Err != nil {
123+ return templ_7745c5c3_Err
124+ }
125+ }
126+ }
127+ return nil
128+ })
129+}
130+
131+func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component {
132+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
133+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
134+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
135+ return templ_7745c5c3_CtxErr
136+ }
137+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
138+ if !templ_7745c5c3_IsBuffer {
139+ defer func() {
140+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
141+ if templ_7745c5c3_Err == nil {
142+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
143+ }
144+ }()
145+ }
146+ ctx = templ.InitializeContext(ctx)
147+ templ_7745c5c3_Var5 := templ.GetChildren(ctx)
148+ if templ_7745c5c3_Var5 == nil {
149+ templ_7745c5c3_Var5 = templ.NopComponent
150+ }
151+ ctx = templ.ClearChildren(ctx)
152+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"sv-group\"><div class=\"sv-group-header\"><div><h4>")
153+ if templ_7745c5c3_Err != nil {
154+ return templ_7745c5c3_Err
155+ }
156+ var templ_7745c5c3_Var6 string
157+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupTitle(group))
158+ if templ_7745c5c3_Err != nil {
159+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 30, Col: 37}
160+ }
161+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
162+ if templ_7745c5c3_Err != nil {
163+ return templ_7745c5c3_Err
164+ }
165+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h4>")
166+ if templ_7745c5c3_Err != nil {
167+ return templ_7745c5c3_Err
168+ }
169+ if keybindingGroupMeta(group) != "" {
170+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<p class=\"sv-meta\">")
171+ if templ_7745c5c3_Err != nil {
172+ return templ_7745c5c3_Err
173+ }
174+ var templ_7745c5c3_Var7 string
175+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupMeta(group))
176+ if templ_7745c5c3_Err != nil {
177+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 52}
178+ }
179+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
180+ if templ_7745c5c3_Err != nil {
181+ return templ_7745c5c3_Err
182+ }
183+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p>")
184+ if templ_7745c5c3_Err != nil {
185+ return templ_7745c5c3_Err
186+ }
187+ }
188+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></div><div class=\"sv-group-body\">")
189+ if templ_7745c5c3_Err != nil {
190+ return templ_7745c5c3_Err
191+ }
192+ if len(group.Bindings) == 0 {
193+ templ_7745c5c3_Err = EmptyState("No bindings in this group").Render(ctx, templ_7745c5c3_Buffer)
194+ if templ_7745c5c3_Err != nil {
195+ return templ_7745c5c3_Err
196+ }
197+ } else {
198+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<ul class=\"sv-list\">")
199+ if templ_7745c5c3_Err != nil {
200+ return templ_7745c5c3_Err
201+ }
202+ for _, binding := range group.Bindings {
203+ templ_7745c5c3_Err = ConfigKeybinding(binding).Render(ctx, templ_7745c5c3_Buffer)
204+ if templ_7745c5c3_Err != nil {
205+ return templ_7745c5c3_Err
206+ }
207+ }
208+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</ul>")
209+ if templ_7745c5c3_Err != nil {
210+ return templ_7745c5c3_Err
211+ }
212+ }
213+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div>")
214+ if templ_7745c5c3_Err != nil {
215+ return templ_7745c5c3_Err
216+ }
217+ return nil
218+ })
219+}
220+
221+func ConfigKeybinding(binding renderedKeybinding) templ.Component {
222+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
223+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
224+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
225+ return templ_7745c5c3_CtxErr
226+ }
227+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
228+ if !templ_7745c5c3_IsBuffer {
229+ defer func() {
230+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
231+ if templ_7745c5c3_Err == nil {
232+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
233+ }
234+ }()
235+ }
236+ ctx = templ.InitializeContext(ctx)
237+ templ_7745c5c3_Var8 := templ.GetChildren(ctx)
238+ if templ_7745c5c3_Var8 == nil {
239+ templ_7745c5c3_Var8 = templ.NopComponent
240+ }
241+ ctx = templ.ClearChildren(ctx)
242+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<li class=\"sv-list-row\"><div class=\"sv-keybinding-row\"><div><strong>")
243+ if templ_7745c5c3_Err != nil {
244+ return templ_7745c5c3_Err
245+ }
246+ var templ_7745c5c3_Var9 string
247+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingDescription(binding))
248+ if templ_7745c5c3_Err != nil {
249+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 44}
250+ }
251+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
252+ if templ_7745c5c3_Err != nil {
253+ return templ_7745c5c3_Err
254+ }
255+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</strong><p class=\"sv-meta\">")
256+ if templ_7745c5c3_Err != nil {
257+ return templ_7745c5c3_Err
258+ }
259+ var templ_7745c5c3_Var10 string
260+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingAction(binding))
261+ if templ_7745c5c3_Err != nil {
262+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 50}
263+ }
264+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
265+ if templ_7745c5c3_Err != nil {
266+ return templ_7745c5c3_Err
267+ }
268+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</p></div><div class=\"sv-keybinding-keys\">")
269+ if templ_7745c5c3_Err != nil {
270+ return templ_7745c5c3_Err
271+ }
272+ templ_7745c5c3_Err = KeyList(binding.Keys).Render(ctx, templ_7745c5c3_Buffer)
273+ if templ_7745c5c3_Err != nil {
274+ return templ_7745c5c3_Err
275+ }
276+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<span class=\"sv-badge\">")
277+ if templ_7745c5c3_Err != nil {
278+ return templ_7745c5c3_Err
279+ }
280+ var templ_7745c5c3_Var11 string
281+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingStatus(binding))
282+ if templ_7745c5c3_Err != nil {
283+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 54}
284+ }
285+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
286+ if templ_7745c5c3_Err != nil {
287+ return templ_7745c5c3_Err
288+ }
289+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</span> ")
290+ if templ_7745c5c3_Err != nil {
291+ return templ_7745c5c3_Err
292+ }
293+ if keybindingIsCustom(binding) && len(binding.DefaultKeys) > 0 {
294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<span class=\"sv-meta\">default:</span>")
295+ if templ_7745c5c3_Err != nil {
296+ return templ_7745c5c3_Err
297+ }
298+ templ_7745c5c3_Err = KeyList(binding.DefaultKeys).Render(ctx, templ_7745c5c3_Buffer)
299+ if templ_7745c5c3_Err != nil {
300+ return templ_7745c5c3_Err
301+ }
302+ }
303+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div></li>")
304+ if templ_7745c5c3_Err != nil {
305+ return templ_7745c5c3_Err
306+ }
307+ return nil
308+ })
309+}
310+
311+var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,311 @@
1+// Code generated by templ - DO NOT EDIT.
2+
3+package systemviews
4+
5+//lint:file-ignore SA4006 This context is only used if a nested component is present.
6+
7+import "github.com/a-h/templ"
8+import templruntime "github.com/a-h/templ/runtime"
9+
10+func ConfigView(data configRenderData) templ.Component {
11+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
12+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
13+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
14+ return templ_7745c5c3_CtxErr
15+ }
16+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
17+ if !templ_7745c5c3_IsBuffer {
18+ defer func() {
19+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
20+ if templ_7745c5c3_Err == nil {
21+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
22+ }
23+ }()
24+ }
25+ ctx = templ.InitializeContext(ctx)
26+ templ_7745c5c3_Var1 := templ.GetChildren(ctx)
27+ if templ_7745c5c3_Var1 == nil {
28+ templ_7745c5c3_Var1 = templ.NopComponent
29+ }
30+ ctx = templ.ClearChildren(ctx)
31+ templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
32+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
33+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
34+ if !templ_7745c5c3_IsBuffer {
35+ defer func() {
36+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
37+ if templ_7745c5c3_Err == nil {
38+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
39+ }
40+ }()
41+ }
42+ ctx = templ.InitializeContext(ctx)
43+ templ_7745c5c3_Err = KV(flattenValues(data.Config)).Render(ctx, templ_7745c5c3_Buffer)
44+ if templ_7745c5c3_Err != nil {
45+ return templ_7745c5c3_Err
46+ }
47+ return nil
48+ })
49+ templ_7745c5c3_Err = Section("sv-config-values", "Config values").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
50+ if templ_7745c5c3_Err != nil {
51+ return templ_7745c5c3_Err
52+ }
53+ templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
54+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
55+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
56+ if !templ_7745c5c3_IsBuffer {
57+ defer func() {
58+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
59+ if templ_7745c5c3_Err == nil {
60+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
61+ }
62+ }()
63+ }
64+ ctx = templ.InitializeContext(ctx)
65+ templ_7745c5c3_Err = Meta(configKeybindingSummary(data.Keybindings)).Render(ctx, templ_7745c5c3_Buffer)
66+ if templ_7745c5c3_Err != nil {
67+ return templ_7745c5c3_Err
68+ }
69+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ")
70+ if templ_7745c5c3_Err != nil {
71+ return templ_7745c5c3_Err
72+ }
73+ templ_7745c5c3_Err = ConfigKeybindings(data.Keybindings).Render(ctx, templ_7745c5c3_Buffer)
74+ if templ_7745c5c3_Err != nil {
75+ return templ_7745c5c3_Err
76+ }
77+ return nil
78+ })
79+ templ_7745c5c3_Err = Section("sv-config-keybindings", "Keybindings").Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
80+ if templ_7745c5c3_Err != nil {
81+ return templ_7745c5c3_Err
82+ }
83+ return nil
84+ })
85+}
86+
87+func ConfigKeybindings(keybindings any) templ.Component {
88+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
89+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
90+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
91+ return templ_7745c5c3_CtxErr
92+ }
93+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
94+ if !templ_7745c5c3_IsBuffer {
95+ defer func() {
96+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
97+ if templ_7745c5c3_Err == nil {
98+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
99+ }
100+ }()
101+ }
102+ ctx = templ.InitializeContext(ctx)
103+ templ_7745c5c3_Var4 := templ.GetChildren(ctx)
104+ if templ_7745c5c3_Var4 == nil {
105+ templ_7745c5c3_Var4 = templ.NopComponent
106+ }
107+ ctx = templ.ClearChildren(ctx)
108+ groups := configKeybindingGroups(keybindings)
109+ if groups == nil {
110+ templ_7745c5c3_Err = EmptyState("Keybindings unavailable").Render(ctx, templ_7745c5c3_Buffer)
111+ if templ_7745c5c3_Err != nil {
112+ return templ_7745c5c3_Err
113+ }
114+ } else if len(groups) == 0 {
115+ templ_7745c5c3_Err = EmptyState("No keybindings configured").Render(ctx, templ_7745c5c3_Buffer)
116+ if templ_7745c5c3_Err != nil {
117+ return templ_7745c5c3_Err
118+ }
119+ } else {
120+ for _, group := range groups {
121+ templ_7745c5c3_Err = ConfigKeybindingGroup(group).Render(ctx, templ_7745c5c3_Buffer)
122+ if templ_7745c5c3_Err != nil {
123+ return templ_7745c5c3_Err
124+ }
125+ }
126+ }
127+ return nil
128+ })
129+}
130+
131+func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component {
132+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
133+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
134+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
135+ return templ_7745c5c3_CtxErr
136+ }
137+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
138+ if !templ_7745c5c3_IsBuffer {
139+ defer func() {
140+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
141+ if templ_7745c5c3_Err == nil {
142+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
143+ }
144+ }()
145+ }
146+ ctx = templ.InitializeContext(ctx)
147+ templ_7745c5c3_Var5 := templ.GetChildren(ctx)
148+ if templ_7745c5c3_Var5 == nil {
149+ templ_7745c5c3_Var5 = templ.NopComponent
150+ }
151+ ctx = templ.ClearChildren(ctx)
152+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"sv-group\"><div class=\"sv-group-header\"><div><h4>")
153+ if templ_7745c5c3_Err != nil {
154+ return templ_7745c5c3_Err
155+ }
156+ var templ_7745c5c3_Var6 string
157+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupTitle(group))
158+ if templ_7745c5c3_Err != nil {
159+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 30, Col: 37}
160+ }
161+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
162+ if templ_7745c5c3_Err != nil {
163+ return templ_7745c5c3_Err
164+ }
165+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h4>")
166+ if templ_7745c5c3_Err != nil {
167+ return templ_7745c5c3_Err
168+ }
169+ if keybindingGroupMeta(group) != "" {
170+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<p class=\"sv-meta\">")
171+ if templ_7745c5c3_Err != nil {
172+ return templ_7745c5c3_Err
173+ }
174+ var templ_7745c5c3_Var7 string
175+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupMeta(group))
176+ if templ_7745c5c3_Err != nil {
177+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 52}
178+ }
179+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
180+ if templ_7745c5c3_Err != nil {
181+ return templ_7745c5c3_Err
182+ }
183+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p>")
184+ if templ_7745c5c3_Err != nil {
185+ return templ_7745c5c3_Err
186+ }
187+ }
188+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></div><div class=\"sv-group-body\">")
189+ if templ_7745c5c3_Err != nil {
190+ return templ_7745c5c3_Err
191+ }
192+ if len(group.Bindings) == 0 {
193+ templ_7745c5c3_Err = EmptyState("No bindings in this group").Render(ctx, templ_7745c5c3_Buffer)
194+ if templ_7745c5c3_Err != nil {
195+ return templ_7745c5c3_Err
196+ }
197+ } else {
198+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<ul class=\"sv-list\">")
199+ if templ_7745c5c3_Err != nil {
200+ return templ_7745c5c3_Err
201+ }
202+ for _, binding := range group.Bindings {
203+ templ_7745c5c3_Err = ConfigKeybinding(binding).Render(ctx, templ_7745c5c3_Buffer)
204+ if templ_7745c5c3_Err != nil {
205+ return templ_7745c5c3_Err
206+ }
207+ }
208+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</ul>")
209+ if templ_7745c5c3_Err != nil {
210+ return templ_7745c5c3_Err
211+ }
212+ }
213+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div></div>")
214+ if templ_7745c5c3_Err != nil {
215+ return templ_7745c5c3_Err
216+ }
217+ return nil
218+ })
219+}
220+
221+func ConfigKeybinding(binding renderedKeybinding) templ.Component {
222+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
223+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
224+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
225+ return templ_7745c5c3_CtxErr
226+ }
227+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
228+ if !templ_7745c5c3_IsBuffer {
229+ defer func() {
230+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
231+ if templ_7745c5c3_Err == nil {
232+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
233+ }
234+ }()
235+ }
236+ ctx = templ.InitializeContext(ctx)
237+ templ_7745c5c3_Var8 := templ.GetChildren(ctx)
238+ if templ_7745c5c3_Var8 == nil {
239+ templ_7745c5c3_Var8 = templ.NopComponent
240+ }
241+ ctx = templ.ClearChildren(ctx)
242+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<li class=\"sv-list-row\"><div class=\"sv-keybinding-row\"><div><strong>")
243+ if templ_7745c5c3_Err != nil {
244+ return templ_7745c5c3_Err
245+ }
246+ var templ_7745c5c3_Var9 string
247+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingDescription(binding))
248+ if templ_7745c5c3_Err != nil {
249+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 44}
250+ }
251+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
252+ if templ_7745c5c3_Err != nil {
253+ return templ_7745c5c3_Err
254+ }
255+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</strong><p class=\"sv-meta\">")
256+ if templ_7745c5c3_Err != nil {
257+ return templ_7745c5c3_Err
258+ }
259+ var templ_7745c5c3_Var10 string
260+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingAction(binding))
261+ if templ_7745c5c3_Err != nil {
262+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 50}
263+ }
264+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
265+ if templ_7745c5c3_Err != nil {
266+ return templ_7745c5c3_Err
267+ }
268+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</p></div><div class=\"sv-keybinding-keys\">")
269+ if templ_7745c5c3_Err != nil {
270+ return templ_7745c5c3_Err
271+ }
272+ templ_7745c5c3_Err = KeyList(binding.Keys).Render(ctx, templ_7745c5c3_Buffer)
273+ if templ_7745c5c3_Err != nil {
274+ return templ_7745c5c3_Err
275+ }
276+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<span class=\"sv-badge\">")
277+ if templ_7745c5c3_Err != nil {
278+ return templ_7745c5c3_Err
279+ }
280+ var templ_7745c5c3_Var11 string
281+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingStatus(binding))
282+ if templ_7745c5c3_Err != nil {
283+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 54}
284+ }
285+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
286+ if templ_7745c5c3_Err != nil {
287+ return templ_7745c5c3_Err
288+ }
289+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</span> ")
290+ if templ_7745c5c3_Err != nil {
291+ return templ_7745c5c3_Err
292+ }
293+ if keybindingIsCustom(binding) && len(binding.DefaultKeys) > 0 {
294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<span class=\"sv-meta\">default:</span>")
295+ if templ_7745c5c3_Err != nil {
296+ return templ_7745c5c3_Err
297+ }
298+ templ_7745c5c3_Err = KeyList(binding.DefaultKeys).Render(ctx, templ_7745c5c3_Buffer)
299+ if templ_7745c5c3_Err != nil {
300+ return templ_7745c5c3_Err
301+ }
302+ }
303+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div></li>")
304+ if templ_7745c5c3_Err != nil {
305+ return templ_7745c5c3_Err
306+ }
307+ return nil
308+ })
309+}
310+
311+var _ = templruntime.GeneratedTemplate
M internal/ui/systemviews/config_view.go +68 -115
@@ -2,7 +2,6 @@ package systemviews
22
33 import (
44 "fmt"
5- "html"
65 "reflect"
76 "sort"
87 "strings"
@@ -10,13 +9,22 @@ import (
109 "github.com/bnema/dumber/internal/application/port"
1110 )
1211
12+type configRenderData struct {
13+ Config port.SystemviewConfigPayload
14+ Keybindings any
15+}
16+
1317 func configHTML(cfg port.SystemviewConfigPayload, keybindings any) string {
18+ return mustRenderComponent(ConfigView(configRenderData{Config: cfg, Keybindings: keybindings}))
19+}
20+
21+func configKeybindingSummary(keybindings any) string {
1422 groups, bindings, loaded := summarizeKeybindings(keybindings)
1523 status := "not loaded"
1624 if loaded {
1725 status = "loaded"
1826 }
19- keybindingSummary := fmt.Sprintf(
27+ return fmt.Sprintf(
2028 "Keybindings %s (%d %s, %d %s)",
2129 status,
2230 groups,
@@ -24,13 +32,66 @@ func configHTML(cfg port.SystemviewConfigPayload, keybindings any) string {
2432 bindings,
2533 plural(bindings, "binding"),
2634 )
27- content := []string{
28- metaHTML(keybindingSummary),
29- "<h3>Config values</h3>" + kvHTML(flattenValues(cfg)),
30- "<h3>Keybindings</h3>" + renderKeybindingsSection(keybindings),
35+}
36+
37+func configKeybindingGroups(keybindings any) []renderedKeybindingGroup {
38+ groups, ok := collectKeybindingGroups(keybindings)
39+ if !ok {
40+ return nil
41+ }
42+ return groups
43+}
44+
45+func keybindingGroupTitle(group renderedKeybindingGroup) string {
46+ title := strings.TrimSpace(group.DisplayName)
47+ if title == "" {
48+ title = strings.TrimSpace(group.Mode)
49+ }
50+ if title == "" {
51+ return "Keybinding group"
52+ }
53+ return title
54+}
55+
56+func keybindingGroupMeta(group renderedKeybindingGroup) string {
57+ meta := make([]string, 0, 2)
58+ if strings.TrimSpace(group.Mode) != "" {
59+ meta = append(meta, "mode: "+group.Mode)
60+ }
61+ if strings.TrimSpace(group.Activation) != "" {
62+ meta = append(meta, "activation: "+group.Activation)
3163 }
64+ return strings.Join(meta, " • ")
65+}
3266
33- return sectionHTML("", "Config", strings.Join(content, ""))
67+func keybindingDescription(binding renderedKeybinding) string {
68+ description := strings.TrimSpace(binding.Description)
69+ if description != "" {
70+ return description
71+ }
72+ if strings.TrimSpace(binding.Action) != "" {
73+ return strings.TrimSpace(binding.Action)
74+ }
75+ return "Unnamed binding"
76+}
77+
78+func keybindingAction(binding renderedKeybinding) string {
79+ action := strings.TrimSpace(binding.Action)
80+ if action == "" {
81+ return "binding"
82+ }
83+ return action
84+}
85+
86+func keybindingIsCustom(binding renderedKeybinding) bool {
87+ return binding.IsCustom || !sameStringSlice(binding.Keys, binding.DefaultKeys)
88+}
89+
90+func keybindingStatus(binding renderedKeybinding) string {
91+ if keybindingIsCustom(binding) {
92+ return "custom"
93+ }
94+ return "default"
3495 }
3596
3697 func flattenValues(value any) []kvPair {
@@ -181,18 +242,6 @@ func summarizeKeybindings(keybindings any) (groups, bindings int, loaded bool) {
181242 return groups, bindings, true
182243 }
183244
184-func renderKeybindingsSection(keybindings any) string {
185- groups, ok := collectKeybindingGroups(keybindings)
186- if !ok {
187- return emptyStateHTML("Keybindings unavailable")
188- }
189- if len(groups) == 0 {
190- return emptyStateHTML("No keybindings configured")
191- }
192-
193- return renderKeybindingGroups(groups)
194-}
195-
196245 func collectKeybindingGroups(keybindings any) ([]renderedKeybindingGroup, bool) {
197246 switch v := keybindings.(type) {
198247 case nil:
@@ -351,14 +400,6 @@ func boolValue(value any) bool {
351400 return b
352401 }
353402
354-func renderKeybindingGroups(groups []renderedKeybindingGroup) string {
355- var rendered strings.Builder
356- for _, group := range groups {
357- rendered.WriteString(renderKeybindingGroup(group))
358- }
359- return rendered.String()
360-}
361-
362403 type renderedKeybindingGroup struct {
363404 DisplayName string
364405 Mode string
@@ -374,94 +415,6 @@ type renderedKeybinding struct {
374415 IsCustom bool
375416 }
376417
377-func renderKeybindingGroup(group renderedKeybindingGroup) string {
378- title := group.DisplayName
379- if strings.TrimSpace(title) == "" {
380- title = group.Mode
381- }
382- if strings.TrimSpace(title) == "" {
383- title = "Keybinding group"
384- }
385-
386- var body strings.Builder
387- body.WriteString(`<div class="sv-group">`)
388- body.WriteString(`<div class="sv-group-header">`)
389- body.WriteString(`<div>`)
390- body.WriteString(`<h4>` + html.EscapeString(title) + `</h4>`)
391-
392- meta := make([]string, 0, 2)
393- if strings.TrimSpace(group.Mode) != "" {
394- meta = append(meta, "mode: "+group.Mode)
395- }
396- if strings.TrimSpace(group.Activation) != "" {
397- meta = append(meta, "activation: "+group.Activation)
398- }
399- if len(meta) > 0 {
400- body.WriteString(`<p class="sv-meta">` + html.EscapeString(strings.Join(meta, " • ")) + `</p>`)
401- }
402- body.WriteString(`</div></div>`)
403- body.WriteString(`<div class="sv-group-body">`)
404- if len(group.Bindings) == 0 {
405- body.WriteString(emptyStateHTML("No bindings in this group"))
406- } else {
407- for _, binding := range group.Bindings {
408- body.WriteString(renderKeybinding(binding))
409- }
410- }
411- body.WriteString(`</div></div>`)
412-
413- return body.String()
414-}
415-
416-func renderKeybinding(binding renderedKeybinding) string {
417- description := strings.TrimSpace(binding.Description)
418- if description == "" {
419- description = strings.TrimSpace(binding.Action)
420- }
421- if description == "" {
422- description = "Unnamed binding"
423- }
424-
425- action := strings.TrimSpace(binding.Action)
426- if action == "" {
427- action = "binding"
428- }
429-
430- custom := binding.IsCustom || !sameStringSlice(binding.Keys, binding.DefaultKeys)
431- status := "default"
432- if custom {
433- status = "custom"
434- }
435-
436- var body strings.Builder
437- body.WriteString(`<div class="sv-binding">`)
438- body.WriteString(`<div class="sv-binding-header">`)
439- body.WriteString(`<div class="sv-binding-description">` + html.EscapeString(description) + `</div>`)
440- body.WriteString(`<div class="sv-binding-keys">` + keyListHTML(binding.Keys) + `</div>`)
441- body.WriteString(`</div>`)
442- body.WriteString(`<div class="sv-binding-meta">`)
443- body.WriteString(`<span>` + html.EscapeString("action: "+action) + `</span>`)
444- body.WriteString(`<span>` + html.EscapeString(status) + `</span>`)
445- if custom && len(binding.DefaultKeys) > 0 {
446- body.WriteString(`<span>default: ` + keyListHTML(binding.DefaultKeys) + `</span>`)
447- }
448- body.WriteString(`</div></div>`)
449-
450- return body.String()
451-}
452-
453-func keyListHTML(keys []string) string {
454- if len(keys) == 0 {
455- return `<span class="sv-key sv-key-empty">Unassigned</span>`
456- }
457-
458- parts := make([]string, 0, len(keys))
459- for _, key := range keys {
460- parts = append(parts, `<span class="sv-key">`+html.EscapeString(key)+`</span>`)
461- }
462- return strings.Join(parts, " ")
463-}
464-
465418 func sameStringSlice(a, b []string) bool {
466419 if len(a) != len(b) {
467420 return false
@@ -2,7 +2,6 @@ package systemviews
2 2
3 import (3 import (
4 "fmt"4 "fmt"
5- "html"
6 "reflect"5 "reflect"
7 "sort"6 "sort"
8 "strings"7 "strings"
@@ -10,13 +9,22 @@ import (
10 "github.com/bnema/dumber/internal/application/port"9 "github.com/bnema/dumber/internal/application/port"
11 )10 )
12 11
12+type configRenderData struct {
13+ Config port.SystemviewConfigPayload
14+ Keybindings any
15+}
16+
13 func configHTML(cfg port.SystemviewConfigPayload, keybindings any) string {17 func configHTML(cfg port.SystemviewConfigPayload, keybindings any) string {
18+ return mustRenderComponent(ConfigView(configRenderData{Config: cfg, Keybindings: keybindings}))
19+}
20+
21+func configKeybindingSummary(keybindings any) string {
14 groups, bindings, loaded := summarizeKeybindings(keybindings)22 groups, bindings, loaded := summarizeKeybindings(keybindings)
15 status := "not loaded"23 status := "not loaded"
16 if loaded {24 if loaded {
17 status = "loaded"25 status = "loaded"
18 }26 }
19- keybindingSummary := fmt.Sprintf(27+ return fmt.Sprintf(
20 "Keybindings %s (%d %s, %d %s)",28 "Keybindings %s (%d %s, %d %s)",
21 status,29 status,
22 groups,30 groups,
@@ -24,13 +32,66 @@ func configHTML(cfg port.SystemviewConfigPayload, keybindings any) string {
24 bindings,32 bindings,
25 plural(bindings, "binding"),33 plural(bindings, "binding"),
26 )34 )
27- content := []string{35+}
28- metaHTML(keybindingSummary),36+
29- "<h3>Config values</h3>" + kvHTML(flattenValues(cfg)),37+func configKeybindingGroups(keybindings any) []renderedKeybindingGroup {
30- "<h3>Keybindings</h3>" + renderKeybindingsSection(keybindings),38+ groups, ok := collectKeybindingGroups(keybindings)
39+ if !ok {
40+ return nil
41+ }
42+ return groups
43+}
44+
45+func keybindingGroupTitle(group renderedKeybindingGroup) string {
46+ title := strings.TrimSpace(group.DisplayName)
47+ if title == "" {
48+ title = strings.TrimSpace(group.Mode)
49+ }
50+ if title == "" {
51+ return "Keybinding group"
52+ }
53+ return title
54+}
55+
56+func keybindingGroupMeta(group renderedKeybindingGroup) string {
57+ meta := make([]string, 0, 2)
58+ if strings.TrimSpace(group.Mode) != "" {
59+ meta = append(meta, "mode: "+group.Mode)
60+ }
61+ if strings.TrimSpace(group.Activation) != "" {
62+ meta = append(meta, "activation: "+group.Activation)
31 }63 }
64+ return strings.Join(meta, " • ")
65+}
32 66
33- return sectionHTML("", "Config", strings.Join(content, ""))67+func keybindingDescription(binding renderedKeybinding) string {
68+ description := strings.TrimSpace(binding.Description)
69+ if description != "" {
70+ return description
71+ }
72+ if strings.TrimSpace(binding.Action) != "" {
73+ return strings.TrimSpace(binding.Action)
74+ }
75+ return "Unnamed binding"
76+}
77+
78+func keybindingAction(binding renderedKeybinding) string {
79+ action := strings.TrimSpace(binding.Action)
80+ if action == "" {
81+ return "binding"
82+ }
83+ return action
84+}
85+
86+func keybindingIsCustom(binding renderedKeybinding) bool {
87+ return binding.IsCustom || !sameStringSlice(binding.Keys, binding.DefaultKeys)
88+}
89+
90+func keybindingStatus(binding renderedKeybinding) string {
91+ if keybindingIsCustom(binding) {
92+ return "custom"
93+ }
94+ return "default"
34 }95 }
35 96
36 func flattenValues(value any) []kvPair {97 func flattenValues(value any) []kvPair {
@@ -181,18 +242,6 @@ func summarizeKeybindings(keybindings any) (groups, bindings int, loaded bool) {
181 return groups, bindings, true242 return groups, bindings, true
182 }243 }
183 244
184-func renderKeybindingsSection(keybindings any) string {
185- groups, ok := collectKeybindingGroups(keybindings)
186- if !ok {
187- return emptyStateHTML("Keybindings unavailable")
188- }
189- if len(groups) == 0 {
190- return emptyStateHTML("No keybindings configured")
191- }
192-
193- return renderKeybindingGroups(groups)
194-}
195-
196 func collectKeybindingGroups(keybindings any) ([]renderedKeybindingGroup, bool) {245 func collectKeybindingGroups(keybindings any) ([]renderedKeybindingGroup, bool) {
197 switch v := keybindings.(type) {246 switch v := keybindings.(type) {
198 case nil:247 case nil:
@@ -351,14 +400,6 @@ func boolValue(value any) bool {
351 return b400 return b
352 }401 }
353 402
354-func renderKeybindingGroups(groups []renderedKeybindingGroup) string {
355- var rendered strings.Builder
356- for _, group := range groups {
357- rendered.WriteString(renderKeybindingGroup(group))
358- }
359- return rendered.String()
360-}
361-
362 type renderedKeybindingGroup struct {403 type renderedKeybindingGroup struct {
363 DisplayName string404 DisplayName string
364 Mode string405 Mode string
@@ -374,94 +415,6 @@ type renderedKeybinding struct {
374 IsCustom bool415 IsCustom bool
375 }416 }
376 417
377-func renderKeybindingGroup(group renderedKeybindingGroup) string {
378- title := group.DisplayName
379- if strings.TrimSpace(title) == "" {
380- title = group.Mode
381- }
382- if strings.TrimSpace(title) == "" {
383- title = "Keybinding group"
384- }
385-
386- var body strings.Builder
387- body.WriteString(`<div class="sv-group">`)
388- body.WriteString(`<div class="sv-group-header">`)
389- body.WriteString(`<div>`)
390- body.WriteString(`<h4>` + html.EscapeString(title) + `</h4>`)
391-
392- meta := make([]string, 0, 2)
393- if strings.TrimSpace(group.Mode) != "" {
394- meta = append(meta, "mode: "+group.Mode)
395- }
396- if strings.TrimSpace(group.Activation) != "" {
397- meta = append(meta, "activation: "+group.Activation)
398- }
399- if len(meta) > 0 {
400- body.WriteString(`<p class="sv-meta">` + html.EscapeString(strings.Join(meta, " • ")) + `</p>`)
401- }
402- body.WriteString(`</div></div>`)
403- body.WriteString(`<div class="sv-group-body">`)
404- if len(group.Bindings) == 0 {
405- body.WriteString(emptyStateHTML("No bindings in this group"))
406- } else {
407- for _, binding := range group.Bindings {
408- body.WriteString(renderKeybinding(binding))
409- }
410- }
411- body.WriteString(`</div></div>`)
412-
413- return body.String()
414-}
415-
416-func renderKeybinding(binding renderedKeybinding) string {
417- description := strings.TrimSpace(binding.Description)
418- if description == "" {
419- description = strings.TrimSpace(binding.Action)
420- }
421- if description == "" {
422- description = "Unnamed binding"
423- }
424-
425- action := strings.TrimSpace(binding.Action)
426- if action == "" {
427- action = "binding"
428- }
429-
430- custom := binding.IsCustom || !sameStringSlice(binding.Keys, binding.DefaultKeys)
431- status := "default"
432- if custom {
433- status = "custom"
434- }
435-
436- var body strings.Builder
437- body.WriteString(`<div class="sv-binding">`)
438- body.WriteString(`<div class="sv-binding-header">`)
439- body.WriteString(`<div class="sv-binding-description">` + html.EscapeString(description) + `</div>`)
440- body.WriteString(`<div class="sv-binding-keys">` + keyListHTML(binding.Keys) + `</div>`)
441- body.WriteString(`</div>`)
442- body.WriteString(`<div class="sv-binding-meta">`)
443- body.WriteString(`<span>` + html.EscapeString("action: "+action) + `</span>`)
444- body.WriteString(`<span>` + html.EscapeString(status) + `</span>`)
445- if custom && len(binding.DefaultKeys) > 0 {
446- body.WriteString(`<span>default: ` + keyListHTML(binding.DefaultKeys) + `</span>`)
447- }
448- body.WriteString(`</div></div>`)
449-
450- return body.String()
451-}
452-
453-func keyListHTML(keys []string) string {
454- if len(keys) == 0 {
455- return `<span class="sv-key sv-key-empty">Unassigned</span>`
456- }
457-
458- parts := make([]string, 0, len(keys))
459- for _, key := range keys {
460- parts = append(parts, `<span class="sv-key">`+html.EscapeString(key)+`</span>`)
461- }
462- return strings.Join(parts, " ")
463-}
464-
465 func sameStringSlice(a, b []string) bool {418 func sameStringSlice(a, b []string) bool {
466 if len(a) != len(b) {419 if len(a) != len(b) {
467 return false420 return false

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