detached 84747b2

fix(ui): send complete engine settings update

commit
84747b2960fa71b189f34de0f4981ddcbc8988b4
author
brice · · 2026-06-23 13:45 UTC
committer
brice · · 2026-06-23 20:01 UTC
parent
5294200
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +63 -6
M cmd/dumber/main.go +5 -2
@@ -867,8 +867,11 @@ func buildUIDependencies(
867867 browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay)
868868
869869 uiDeps := &ui.Dependencies{
870- Ctx: ctx,
871- Config: cfg,
870+ Ctx: ctx,
871+ Config: cfg,
872+ EngineSettingsPayload: func() port.EngineSettingsPayload {
873+ return bootstrap.EngineSettingsPayloadFromConfig(cfg)
874+ },
872875 InitialURL: initialURL,
873876 RestoreSessionID: restoreSessionID,
874877 StartupCrashReports: startupCrashReports,
@@ -867,8 +867,11 @@ func buildUIDependencies(
867 browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay)867 browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay)
868 868
869 uiDeps := &ui.Dependencies{869 uiDeps := &ui.Dependencies{
870- Ctx: ctx,870+ Ctx: ctx,
871- Config: cfg,871+ Config: cfg,
872+ EngineSettingsPayload: func() port.EngineSettingsPayload {
873+ return bootstrap.EngineSettingsPayloadFromConfig(cfg)
874+ },
872 InitialURL: initialURL,875 InitialURL: initialURL,
873 RestoreSessionID: restoreSessionID,876 RestoreSessionID: restoreSessionID,
874 StartupCrashReports: startupCrashReports,877 StartupCrashReports: startupCrashReports,
M internal/ui/app.go +5 -2
@@ -5118,9 +5118,12 @@ func (a *App) applyAppearanceConfig(ctx context.Context) {
51185118 cfg := a.deps.Config
51195119
51205120 if a.engine != nil {
5121+ settings := port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale}
5122+ if a.deps.EngineSettingsPayload != nil {
5123+ settings = a.deps.EngineSettingsPayload()
5124+ }
51215125 if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{
5122- Settings: port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale},
5123- Raw: cfg,
5126+ Settings: settings,
51245127 }); err != nil {
51255128 log.Warn().Err(err).Msg("failed to apply engine settings update")
51265129 }
@@ -5118,9 +5118,12 @@ func (a *App) applyAppearanceConfig(ctx context.Context) {
5118 cfg := a.deps.Config5118 cfg := a.deps.Config
5119 5119
5120 if a.engine != nil {5120 if a.engine != nil {
5121+ settings := port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale}
5122+ if a.deps.EngineSettingsPayload != nil {
5123+ settings = a.deps.EngineSettingsPayload()
5124+ }
5121 if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{5125 if err := a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{
5122- Settings: port.EngineSettingsPayload{DefaultUIScale: cfg.DefaultUIScale},5126+ Settings: settings,
5123- Raw: cfg,
5124 }); err != nil {5127 }); err != nil {
5125 log.Warn().Err(err).Msg("failed to apply engine settings update")5128 log.Warn().Err(err).Msg("failed to apply engine settings update")
5126 }5129 }
M internal/ui/deps.go +3 -2
@@ -20,8 +20,9 @@ type Dependencies struct {
2020 // Core context and configuration
2121 Ctx context.Context
2222 Config *config.Config // TODO: replace with port interface in a later milestone
23- InitialURL string // URL to open on startup (optional)
24- RestoreSessionID string // Session ID to restore on startup (optional)
23+ EngineSettingsPayload func() port.EngineSettingsPayload
24+ InitialURL string // URL to open on startup (optional)
25+ RestoreSessionID string // Session ID to restore on startup (optional)
2526 StartupCrashReports []string
2627 OnFirstWebViewShown func(context.Context)
2728 OnSessionPersisted func() // Called by main after session is persisted to DB
@@ -20,8 +20,9 @@ type Dependencies struct {
20 // Core context and configuration20 // Core context and configuration
21 Ctx context.Context21 Ctx context.Context
22 Config *config.Config // TODO: replace with port interface in a later milestone22 Config *config.Config // TODO: replace with port interface in a later milestone
23- InitialURL string // URL to open on startup (optional)23+ EngineSettingsPayload func() port.EngineSettingsPayload
24- RestoreSessionID string // Session ID to restore on startup (optional)24+ InitialURL string // URL to open on startup (optional)
25+ RestoreSessionID string // Session ID to restore on startup (optional)
25 StartupCrashReports []string26 StartupCrashReports []string
26 OnFirstWebViewShown func(context.Context)27 OnFirstWebViewShown func(context.Context)
27 OnSessionPersisted func() // Called by main after session is persisted to DB28 OnSessionPersisted func() // Called by main after session is persisted to DB
M internal/ui/external_theme_reload_test.go +50 -0
@@ -8,6 +8,7 @@ import (
88 "github.com/stretchr/testify/mock"
99 "github.com/stretchr/testify/require"
1010
11+ "github.com/bnema/dumber/internal/application/port"
1112 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
1213 "github.com/bnema/dumber/internal/application/usecase"
1314 "github.com/bnema/dumber/internal/domain/entity"
@@ -68,6 +69,55 @@ func immediateDispatchForExternalThemeTest(label string, fn func()) syncdispatch
6869 return syncdispatch.SyncDispatchResult{Label: label, Status: syncdispatch.SyncDispatchCompleted}
6970 }
7071
72+func TestApplyAppearanceConfigSendsCompleteEngineSettingsPayload(t *testing.T) {
73+ ctx := context.Background()
74+ cfg := config.DefaultConfig()
75+ cfg.DefaultUIScale = 1.25
76+ cfg.Appearance.SansFont = "Inter"
77+ cfg.Appearance.SerifFont = "Literata"
78+ cfg.Appearance.MonospaceFont = "Fira Code"
79+ cfg.Appearance.DefaultFontSize = 17
80+ cfg.Debug.EnableDevTools = true
81+ cfg.Logging.CaptureConsole = true
82+ cfg.Engine.WebKit.DrawCompositingIndicators = true
83+ cfg.Media.HardwareDecodingMode = config.HardwareDecodingForce
84+ expected := port.EngineSettingsPayload{
85+ DefaultUIScale: cfg.DefaultUIScale,
86+ WebContent: port.EngineWebContentSettingsPayload{
87+ SansFont: cfg.Appearance.SansFont,
88+ SerifFont: cfg.Appearance.SerifFont,
89+ MonospaceFont: cfg.Appearance.MonospaceFont,
90+ DefaultFontSize: cfg.Appearance.DefaultFontSize,
91+ EnableDevTools: cfg.Debug.EnableDevTools,
92+ CaptureConsole: cfg.Logging.CaptureConsole,
93+ DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,
94+ HardwareDecoding: port.EngineHardwareDecodingForce,
95+ },
96+ }
97+ engine := portmocks.NewMockEngine(t)
98+ var captured port.EngineSettingsUpdate
99+ engine.EXPECT().UpdateSettings(mock.Anything, mock.Anything).
100+ Run(func(_ context.Context, update port.EngineSettingsUpdate) {
101+ captured = update
102+ }).
103+ Return(nil).
104+ Once()
105+ app := &App{
106+ deps: &Dependencies{
107+ Config: cfg,
108+ EngineSettingsPayload: func() port.EngineSettingsPayload {
109+ return expected
110+ },
111+ },
112+ engine: engine,
113+ }
114+
115+ app.applyAppearanceConfig(ctx)
116+
117+ require.Equal(t, expected, captured.Settings)
118+ require.Nil(t, captured.Raw)
119+}
120+
71121 func TestExternalThemeWatcherCallbackAppliesThemeThroughSharedPath(t *testing.T) {
72122 ctx := context.Background()
73123 cfg := config.DefaultConfig()
@@ -8,6 +8,7 @@ import (
8 "github.com/stretchr/testify/mock"8 "github.com/stretchr/testify/mock"
9 "github.com/stretchr/testify/require"9 "github.com/stretchr/testify/require"
10 10
11+ "github.com/bnema/dumber/internal/application/port"
11 portmocks "github.com/bnema/dumber/internal/application/port/mocks"12 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
12 "github.com/bnema/dumber/internal/application/usecase"13 "github.com/bnema/dumber/internal/application/usecase"
13 "github.com/bnema/dumber/internal/domain/entity"14 "github.com/bnema/dumber/internal/domain/entity"
@@ -68,6 +69,55 @@ func immediateDispatchForExternalThemeTest(label string, fn func()) syncdispatch
68 return syncdispatch.SyncDispatchResult{Label: label, Status: syncdispatch.SyncDispatchCompleted}69 return syncdispatch.SyncDispatchResult{Label: label, Status: syncdispatch.SyncDispatchCompleted}
69 }70 }
70 71
72+func TestApplyAppearanceConfigSendsCompleteEngineSettingsPayload(t *testing.T) {
73+ ctx := context.Background()
74+ cfg := config.DefaultConfig()
75+ cfg.DefaultUIScale = 1.25
76+ cfg.Appearance.SansFont = "Inter"
77+ cfg.Appearance.SerifFont = "Literata"
78+ cfg.Appearance.MonospaceFont = "Fira Code"
79+ cfg.Appearance.DefaultFontSize = 17
80+ cfg.Debug.EnableDevTools = true
81+ cfg.Logging.CaptureConsole = true
82+ cfg.Engine.WebKit.DrawCompositingIndicators = true
83+ cfg.Media.HardwareDecodingMode = config.HardwareDecodingForce
84+ expected := port.EngineSettingsPayload{
85+ DefaultUIScale: cfg.DefaultUIScale,
86+ WebContent: port.EngineWebContentSettingsPayload{
87+ SansFont: cfg.Appearance.SansFont,
88+ SerifFont: cfg.Appearance.SerifFont,
89+ MonospaceFont: cfg.Appearance.MonospaceFont,
90+ DefaultFontSize: cfg.Appearance.DefaultFontSize,
91+ EnableDevTools: cfg.Debug.EnableDevTools,
92+ CaptureConsole: cfg.Logging.CaptureConsole,
93+ DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,
94+ HardwareDecoding: port.EngineHardwareDecodingForce,
95+ },
96+ }
97+ engine := portmocks.NewMockEngine(t)
98+ var captured port.EngineSettingsUpdate
99+ engine.EXPECT().UpdateSettings(mock.Anything, mock.Anything).
100+ Run(func(_ context.Context, update port.EngineSettingsUpdate) {
101+ captured = update
102+ }).
103+ Return(nil).
104+ Once()
105+ app := &App{
106+ deps: &Dependencies{
107+ Config: cfg,
108+ EngineSettingsPayload: func() port.EngineSettingsPayload {
109+ return expected
110+ },
111+ },
112+ engine: engine,
113+ }
114+
115+ app.applyAppearanceConfig(ctx)
116+
117+ require.Equal(t, expected, captured.Settings)
118+ require.Nil(t, captured.Raw)
119+}
120+
71 func TestExternalThemeWatcherCallbackAppliesThemeThroughSharedPath(t *testing.T) {121 func TestExternalThemeWatcherCallbackAppliesThemeThroughSharedPath(t *testing.T) {
72 ctx := context.Background()122 ctx := context.Background()
73 cfg := config.DefaultConfig()123 cfg := config.DefaultConfig()

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