1package config
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/bnema/dumber/internal/application/port"
8)
9
10type WebUIConfigGateway struct {
11 mgr *Manager
12}
13
14func NewWebUIConfigGateway(mgr *Manager) *WebUIConfigGateway {
15 return &WebUIConfigGateway{mgr: mgr}
16}
17
18// Validation limits for custom performance fields.
19const (
20 maxWebProcessMemoryMB = 16384
21 maxNetworkProcessMemoryMB = 4096
22 maxWebViewPoolPrewarm = 20
23)
24
25// clampInt returns v clamped to [lo, hi].
26func clampInt(v, lo, hi int) int {
27 if v < lo {
28 return lo
29 }
30 if v > hi {
31 return hi
32 }
33 return v
34}
35
36func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUIConfig) error {
37 _ = ctx
38 if g == nil || g.mgr == nil {
39 return fmt.Errorf("config manager not initialized")
40 }
41
42 current := Get()
43 current.Appearance.SansFont = cfg.Appearance.SansFont
44 current.Appearance.SerifFont = cfg.Appearance.SerifFont
45 current.Appearance.MonospaceFont = cfg.Appearance.MonospaceFont
46 current.Appearance.DefaultFontSize = cfg.Appearance.DefaultFontSize
47 current.Appearance.ColorScheme = cfg.Appearance.ColorScheme
48 current.Appearance.LightPalette = ColorPalette{
49 Background: cfg.Appearance.LightPalette.Background,
50 Surface: cfg.Appearance.LightPalette.Surface,
51 SurfaceVariant: cfg.Appearance.LightPalette.SurfaceVariant,
52 Text: cfg.Appearance.LightPalette.Text,
53 Muted: cfg.Appearance.LightPalette.Muted,
54 Accent: cfg.Appearance.LightPalette.Accent,
55 Border: cfg.Appearance.LightPalette.Border,
56 }
57 current.Appearance.DarkPalette = ColorPalette{
58 Background: cfg.Appearance.DarkPalette.Background,
59 Surface: cfg.Appearance.DarkPalette.Surface,
60 SurfaceVariant: cfg.Appearance.DarkPalette.SurfaceVariant,
61 Text: cfg.Appearance.DarkPalette.Text,
62 Muted: cfg.Appearance.DarkPalette.Muted,
63 Accent: cfg.Appearance.DarkPalette.Accent,
64 Border: cfg.Appearance.DarkPalette.Border,
65 }
66 current.DefaultUIScale = cfg.DefaultUIScale
67 current.DefaultSearchEngine = cfg.DefaultSearchEngine
68 if len(cfg.SearchShortcuts) == 0 {
69 current.SearchShortcuts = nil
70 } else {
71 shortcuts := make(map[string]SearchShortcut, len(cfg.SearchShortcuts))
72 for key, shortcut := range cfg.SearchShortcuts {
73 shortcuts[key] = SearchShortcut{
74 URL: shortcut.URL,
75 Description: shortcut.Description,
76 }
77 }
78 current.SearchShortcuts = shortcuts
79 }
80
81 // Performance profile (requires restart to take effect)
82 if cfg.Performance.Profile != "" {
83 current.Engine.Profile = PerformanceProfile(cfg.Performance.Profile)
84 }
85
86 // Custom performance fields (only used when profile is "custom")
87 if cfg.Performance.Profile == string(ProfileCustom) {
88 current.Engine.WebKit.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)
89 current.Engine.WebKit.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)
90 current.Engine.WebKit.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)
91 current.Engine.WebKit.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)
92 current.Engine.PoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)
93 }
94
95 return g.mgr.Save(current)
96}