detached 0b331d7

refactor(config): tighten systemview reader wiring

commit
0b331d7affc238daa61672b185519d5f763420de
author
brice · · 2026-04-11 18:04 UTC
committer
brice · · 2026-04-12 04:52 UTC
parent
86449d3
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +45 -40
M internal/bootstrap/engine.go +0 -13
@@ -54,19 +54,6 @@ func BuildEngine(input EngineInput) (port.Engine, error) {
5454 CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy),
5555 }
5656 wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit)
57- systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor())
58- systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader)
59- buildConfigPayload := func(read func(context.Context) (port.SystemviewConfigPayload, error)) func() ([]byte, error) {
60- return func() ([]byte, error) {
61- payload, err := read(input.Ctx)
62- if err != nil {
63- return nil, err
64- }
65- return json.Marshal(payload)
66- }
67- }
68- currentConfigPayload := buildConfigPayload(systemviewUC.Current)
69- defaultConfigPayload := buildConfigPayload(systemviewUC.Default)
7057
7158 return webkit.NewEngine(
7259 input.Ctx, cfg, opts, wkCfg,
@@ -54,19 +54,6 @@ func BuildEngine(input EngineInput) (port.Engine, error) {
54 CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy),54 CookiePolicy: port.CookiePolicy(cfg.Engine.CookiePolicy),
55 }55 }
56 wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit)56 wkCfg := webkit.EngineConfigFromConfig(cfg.Engine.WebKit)
57- systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor())
58- systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader)
59- buildConfigPayload := func(read func(context.Context) (port.SystemviewConfigPayload, error)) func() ([]byte, error) {
60- return func() ([]byte, error) {
61- payload, err := read(input.Ctx)
62- if err != nil {
63- return nil, err
64- }
65- return json.Marshal(payload)
66- }
67- }
68- currentConfigPayload := buildConfigPayload(systemviewUC.Current)
69- defaultConfigPayload := buildConfigPayload(systemviewUC.Default)
70 57
71 return webkit.NewEngine(58 return webkit.NewEngine(
72 input.Ctx, cfg, opts, wkCfg,59 input.Ctx, cfg, opts, wkCfg,
M internal/infrastructure/config/webui_payload.go +30 -27
@@ -5,40 +5,43 @@ import "github.com/bnema/dumber/internal/application/port"
55 // BuildSystemviewConfigPayload projects the full config into the stable DTO used by
66 // the settings page across engines.
77 func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.SystemviewConfigPayload {
8+ payload := port.SystemviewConfigPayload{
9+ SearchShortcuts: map[string]port.SearchShortcut{},
10+ }
811 if cfg == nil {
9- return port.SystemviewConfigPayload{}
12+ return payload
1013 }
1114
1215 perfCfg := PerformanceConfigFromEngine(&cfg.Engine)
1316 resolved := ResolvePerformanceProfile(&perfCfg, hw)
1417
15- return port.SystemviewConfigPayload{
16- Appearance: buildSystemviewAppearancePayload(cfg.Appearance),
17- DefaultUIScale: cfg.DefaultUIScale,
18- DefaultSearchEngine: cfg.DefaultSearchEngine,
19- SearchShortcuts: buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts),
20- EngineType: cfg.Engine.ResolveEngineType(),
21- Performance: port.SystemviewPerformancePayload{
22- Profile: string(cfg.Engine.Profile),
23- Custom: port.SystemviewCustomPerformancePayload{
24- SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,
25- SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,
26- WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,
27- NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,
28- WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,
29- },
30- Resolved: port.SystemviewResolvedPerformancePayload{
31- SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,
32- SkiaGPUThreads: resolved.SkiaGPUPaintingThreads,
33- WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB,
34- NetworkProcessMemoryMB: resolved.NetworkProcessMemoryLimitMB,
35- WebViewPoolPrewarm: resolved.WebViewPoolPrewarmCount,
36- ConservativeThreshold: resolved.WebProcessMemoryConservativeThreshold,
37- StrictThreshold: resolved.WebProcessMemoryStrictThreshold,
38- },
39- Hardware: buildSystemviewHardwarePayload(hw),
18+ payload.Appearance = buildSystemviewAppearancePayload(cfg.Appearance)
19+ payload.DefaultUIScale = cfg.DefaultUIScale
20+ payload.DefaultSearchEngine = cfg.DefaultSearchEngine
21+ payload.SearchShortcuts = buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts)
22+ payload.EngineType = cfg.Engine.ResolveEngineType()
23+ payload.Performance = port.SystemviewPerformancePayload{
24+ Profile: string(cfg.Engine.Profile),
25+ Custom: port.SystemviewCustomPerformancePayload{
26+ SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,
27+ SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,
28+ WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,
29+ NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,
30+ WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,
31+ },
32+ Resolved: port.SystemviewResolvedPerformancePayload{
33+ SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,
34+ SkiaGPUThreads: resolved.SkiaGPUPaintingThreads,
35+ WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB,
36+ NetworkProcessMemoryMB: resolved.NetworkProcessMemoryLimitMB,
37+ WebViewPoolPrewarm: resolved.WebViewPoolPrewarmCount,
38+ ConservativeThreshold: resolved.WebProcessMemoryConservativeThreshold,
39+ StrictThreshold: resolved.WebProcessMemoryStrictThreshold,
4040 },
41+ Hardware: buildSystemviewHardwarePayload(hw),
4142 }
43+
44+ return payload
4245 }
4346
4447 func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIAppearanceConfig {
@@ -71,7 +74,7 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIApp
7174
7275 func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]port.SearchShortcut {
7376 if len(shortcuts) == 0 {
74- return nil
77+ return map[string]port.SearchShortcut{}
7578 }
7679
7780 result := make(map[string]port.SearchShortcut, len(shortcuts))
@@ -5,40 +5,43 @@ import "github.com/bnema/dumber/internal/application/port"
5 // BuildSystemviewConfigPayload projects the full config into the stable DTO used by5 // BuildSystemviewConfigPayload projects the full config into the stable DTO used by
6 // the settings page across engines.6 // the settings page across engines.
7 func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.SystemviewConfigPayload {7 func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.SystemviewConfigPayload {
8+ payload := port.SystemviewConfigPayload{
9+ SearchShortcuts: map[string]port.SearchShortcut{},
10+ }
8 if cfg == nil {11 if cfg == nil {
9- return port.SystemviewConfigPayload{}12+ return payload
10 }13 }
11 14
12 perfCfg := PerformanceConfigFromEngine(&cfg.Engine)15 perfCfg := PerformanceConfigFromEngine(&cfg.Engine)
13 resolved := ResolvePerformanceProfile(&perfCfg, hw)16 resolved := ResolvePerformanceProfile(&perfCfg, hw)
14 17
15- return port.SystemviewConfigPayload{18+ payload.Appearance = buildSystemviewAppearancePayload(cfg.Appearance)
16- Appearance: buildSystemviewAppearancePayload(cfg.Appearance),19+ payload.DefaultUIScale = cfg.DefaultUIScale
17- DefaultUIScale: cfg.DefaultUIScale,20+ payload.DefaultSearchEngine = cfg.DefaultSearchEngine
18- DefaultSearchEngine: cfg.DefaultSearchEngine,21+ payload.SearchShortcuts = buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts)
19- SearchShortcuts: buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts),22+ payload.EngineType = cfg.Engine.ResolveEngineType()
20- EngineType: cfg.Engine.ResolveEngineType(),23+ payload.Performance = port.SystemviewPerformancePayload{
21- Performance: port.SystemviewPerformancePayload{24+ Profile: string(cfg.Engine.Profile),
22- Profile: string(cfg.Engine.Profile),25+ Custom: port.SystemviewCustomPerformancePayload{
23- Custom: port.SystemviewCustomPerformancePayload{26+ SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,
24- SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads,27+ SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,
25- SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads,28+ WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,
26- WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB,29+ NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,
27- NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB,30+ WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,
28- WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount,31+ },
29- },32+ Resolved: port.SystemviewResolvedPerformancePayload{
30- Resolved: port.SystemviewResolvedPerformancePayload{33+ SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,
31- SkiaCPUThreads: resolved.SkiaCPUPaintingThreads,34+ SkiaGPUThreads: resolved.SkiaGPUPaintingThreads,
32- SkiaGPUThreads: resolved.SkiaGPUPaintingThreads,35+ WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB,
33- WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB,36+ NetworkProcessMemoryMB: resolved.NetworkProcessMemoryLimitMB,
34- NetworkProcessMemoryMB: resolved.NetworkProcessMemoryLimitMB,37+ WebViewPoolPrewarm: resolved.WebViewPoolPrewarmCount,
35- WebViewPoolPrewarm: resolved.WebViewPoolPrewarmCount,38+ ConservativeThreshold: resolved.WebProcessMemoryConservativeThreshold,
36- ConservativeThreshold: resolved.WebProcessMemoryConservativeThreshold,39+ StrictThreshold: resolved.WebProcessMemoryStrictThreshold,
37- StrictThreshold: resolved.WebProcessMemoryStrictThreshold,
38- },
39- Hardware: buildSystemviewHardwarePayload(hw),
40 },40 },
41+ Hardware: buildSystemviewHardwarePayload(hw),
41 }42 }
43+
44+ return payload
42 }45 }
43 46
44 func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIAppearanceConfig {47 func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIAppearanceConfig {
@@ -71,7 +74,7 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIApp
71 74
72 func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]port.SearchShortcut {75 func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]port.SearchShortcut {
73 if len(shortcuts) == 0 {76 if len(shortcuts) == 0 {
74- return nil77+ return map[string]port.SearchShortcut{}
75 }78 }
76 79
77 result := make(map[string]port.SearchShortcut, len(shortcuts))80 result := make(map[string]port.SearchShortcut, len(shortcuts))
A internal/infrastructure/config/webui_payload_test.go +15 -0
@@ -0,0 +1,15 @@
1+package config
2+
3+import (
4+ "testing"
5+
6+ "github.com/stretchr/testify/require"
7+)
8+
9+func TestBuildSystemviewConfigPayload_EmptySearchShortcutsUsesEmptyMap(t *testing.T) {
10+ cfg := DefaultConfig()
11+ cfg.SearchShortcuts = map[string]SearchShortcut{}
12+ payload := BuildSystemviewConfigPayload(cfg, nil)
13+ require.NotNil(t, payload.SearchShortcuts)
14+ require.Empty(t, payload.SearchShortcuts)
15+}
@@ -0,0 +1,15 @@
1+package config
2+
3+import (
4+ "testing"
5+
6+ "github.com/stretchr/testify/require"
7+)
8+
9+func TestBuildSystemviewConfigPayload_EmptySearchShortcutsUsesEmptyMap(t *testing.T) {
10+ cfg := DefaultConfig()
11+ cfg.SearchShortcuts = map[string]SearchShortcut{}
12+ payload := BuildSystemviewConfigPayload(cfg, nil)
13+ require.NotNil(t, payload.SearchShortcuts)
14+ require.Empty(t, payload.SearchShortcuts)
15+}

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