1package config
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/bnema/dumber/internal/application/dto"
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 dto.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.GtkFont = cfg.Appearance.GtkFont
47 current.Appearance.DefaultFontSize = cfg.Appearance.DefaultFontSize
48 current.Appearance.ColorScheme = cfg.Appearance.ColorScheme
49 current.Appearance.LightPalette = ColorPalette{
50 Background: cfg.Appearance.LightPalette.Background,
51 Surface: cfg.Appearance.LightPalette.Surface,
52 SurfaceVariant: cfg.Appearance.LightPalette.SurfaceVariant,
53 Text: cfg.Appearance.LightPalette.Text,
54 Muted: cfg.Appearance.LightPalette.Muted,
55 Accent: cfg.Appearance.LightPalette.Accent,
56 Border: cfg.Appearance.LightPalette.Border,
57 }
58 current.Appearance.DarkPalette = ColorPalette{
59 Background: cfg.Appearance.DarkPalette.Background,
60 Surface: cfg.Appearance.DarkPalette.Surface,
61 SurfaceVariant: cfg.Appearance.DarkPalette.SurfaceVariant,
62 Text: cfg.Appearance.DarkPalette.Text,
63 Muted: cfg.Appearance.DarkPalette.Muted,
64 Accent: cfg.Appearance.DarkPalette.Accent,
65 Border: cfg.Appearance.DarkPalette.Border,
66 }
67 current.DefaultUIScale = cfg.DefaultUIScale
68 current.DefaultSearchEngine = cfg.DefaultSearchEngine
69 if len(cfg.SearchShortcuts) == 0 {
70 current.SearchShortcuts = nil
71 } else {
72 shortcuts := make(map[string]SearchShortcut, len(cfg.SearchShortcuts))
73 for key, shortcut := range cfg.SearchShortcuts {
74 shortcuts[key] = SearchShortcut{
75 URL: shortcut.URL,
76 Description: shortcut.Description,
77 }
78 }
79 current.SearchShortcuts = shortcuts
80 }
81
82 // Performance profile (requires restart to take effect)
83 if cfg.Performance.Profile != "" {
84 current.Engine.Profile = PerformanceProfile(cfg.Performance.Profile)
85 }
86
87 // Custom performance fields (only used when profile is "custom")
88 if cfg.Performance.Profile == string(ProfileCustom) {
89 current.Engine.WebKit.SkiaCPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaCPUThreads, 0, maxSkiaCPUThreads)
90 current.Engine.WebKit.SkiaGPUPaintingThreads = clampInt(cfg.Performance.Custom.SkiaGPUThreads, -1, maxSkiaGPUThreads)
91 current.Engine.WebKit.WebProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.WebProcessMemoryMB, 0, maxWebProcessMemoryMB)
92 current.Engine.WebKit.NetworkProcessMemoryLimitMB = clampInt(cfg.Performance.Custom.NetworkProcessMemoryMB, 0, maxNetworkProcessMemoryMB)
93 current.Engine.PoolPrewarmCount = clampInt(cfg.Performance.Custom.WebViewPoolPrewarm, 0, maxWebViewPoolPrewarm)
94 }
95
96 return g.mgr.Save(current)
97}