1package bootstrap
2
3import (
4 "maps"
5
6 "github.com/bnema/dumber/internal/application/port"
7 "github.com/bnema/dumber/internal/domain/entity"
8 "github.com/bnema/dumber/internal/infrastructure/config"
9)
10
11type configChangeManager interface {
12 Get() *config.Config
13 Watch() error
14 OnConfigChange(func(*config.Config))
15}
16
17type runtimeConfigProvider struct {
18 fallback entity.RuntimeConfigSnapshot
19 manager configChangeManager
20}
21
22func NewRuntimeConfigProvider(cfg *config.Config, manager configChangeManager) port.RuntimeConfigProvider {
23 return &runtimeConfigProvider{
24 fallback: RuntimeConfigSnapshotFromConfig(cfg),
25 manager: manager,
26 }
27}
28
29func (p *runtimeConfigProvider) Current() entity.RuntimeConfigSnapshot {
30 if p == nil {
31 return entity.RuntimeConfigSnapshot{}
32 }
33 if p.manager == nil {
34 return cloneRuntimeConfigSnapshot(p.fallback)
35 }
36 cfg := p.manager.Get()
37 if cfg == nil {
38 return cloneRuntimeConfigSnapshot(p.fallback)
39 }
40 return RuntimeConfigSnapshotFromConfig(cfg)
41}
42
43func (p *runtimeConfigProvider) Watch() error {
44 if p == nil || p.manager == nil {
45 return nil
46 }
47 return p.manager.Watch()
48}
49
50func (p *runtimeConfigProvider) OnChange(callback func(entity.RuntimeConfigSnapshot)) {
51 if p == nil || p.manager == nil || callback == nil {
52 return
53 }
54 p.manager.OnConfigChange(func(cfg *config.Config) {
55 if cfg == nil {
56 callback(cloneRuntimeConfigSnapshot(p.fallback))
57 return
58 }
59 callback(RuntimeConfigSnapshotFromConfig(cfg))
60 })
61}
62
63func EngineSettingsPayloadFromConfig(cfg *config.Config) entity.EngineSettingsPayload {
64 if cfg == nil {
65 return entity.EngineSettingsPayload{}
66 }
67 return entity.EngineSettingsPayload{
68 DefaultUIScale: cfg.DefaultUIScale,
69 WebContent: entity.EngineWebContentSettingsPayload{
70 SansFont: cfg.Appearance.SansFont,
71 SerifFont: cfg.Appearance.SerifFont,
72 MonospaceFont: cfg.Appearance.MonospaceFont,
73 DefaultFontSize: cfg.Appearance.DefaultFontSize,
74 EnableDevTools: cfg.Debug.EnableDevTools,
75 CaptureConsole: cfg.Logging.CaptureConsole,
76 DrawCompositingIndicators: cfg.Engine.WebKit.DrawCompositingIndicators,
77 HardwareDecoding: engineHardwareDecodingModeFromConfig(cfg.Media.HardwareDecodingMode),
78 AutoCopyOnSelection: cfg.Clipboard.AutoCopyOnSelection,
79 },
80 }
81}
82
83func RuntimeConfigSnapshotFromConfig(cfg *config.Config) entity.RuntimeConfigSnapshot {
84 if cfg == nil {
85 return entity.RuntimeConfigSnapshot{}
86 }
87 return entity.RuntimeConfigSnapshot{
88 EngineSettings: EngineSettingsPayloadFromConfig(cfg),
89 UI: entity.RuntimeUIConfig{
90 DefaultUIScale: cfg.DefaultUIScale,
91 SidebarWidth: cfg.SidebarWidth,
92 Appearance: cfg.Appearance,
93 Workspace: cloneWorkspaceConfig(cfg.Workspace),
94 Session: cloneSessionConfig(cfg.Session),
95 Clipboard: entity.RuntimeClipboardConfig{AutoCopyOnSelection: cfg.Clipboard.AutoCopyOnSelection},
96 SearchShortcuts: runtimeSearchShortcutsFromConfig(cfg.SearchShortcuts),
97 DefaultSearchEngine: cfg.DefaultSearchEngine,
98 Omnibox: entity.RuntimeOmniboxConfig{
99 InitialBehavior: cfg.Omnibox.InitialBehavior,
100 MostVisitedDays: cfg.Omnibox.MostVisitedDays,
101 AutoOpenOnNewPane: cfg.Omnibox.AutoOpenOnNewPane,
102 },
103 Update: entity.RuntimeUpdateConfig{
104 EnableOnStartup: cfg.Update.EnableOnStartup,
105 AutoDownload: cfg.Update.AutoDownload,
106 NotifyOnNewSettings: cfg.Update.NotifyOnNewSettings,
107 },
108 Downloads: entity.RuntimeDownloadsConfig{Path: cfg.Downloads.Path},
109 },
110 }
111}
112
113func runtimeSearchShortcutsFromConfig(in map[string]config.SearchShortcut) map[string]entity.RuntimeSearchShortcut {
114 out := make(map[string]entity.RuntimeSearchShortcut, len(in))
115 for key, shortcut := range in {
116 out[key] = entity.RuntimeSearchShortcut{
117 URL: shortcut.URL,
118 Description: shortcut.Description,
119 }
120 }
121 return out
122}
123
124func cloneRuntimeConfigSnapshot(snapshot entity.RuntimeConfigSnapshot) entity.RuntimeConfigSnapshot {
125 snapshot.UI.SearchShortcuts = cloneRuntimeSearchShortcuts(snapshot.UI.SearchShortcuts)
126 snapshot.UI.Workspace = cloneWorkspaceConfig(snapshot.UI.Workspace)
127 snapshot.UI.Session = cloneSessionConfig(snapshot.UI.Session)
128 return snapshot
129}
130
131func cloneRuntimeSearchShortcuts(in map[string]entity.RuntimeSearchShortcut) map[string]entity.RuntimeSearchShortcut {
132 if in == nil {
133 return nil
134 }
135 out := make(map[string]entity.RuntimeSearchShortcut, len(in))
136 maps.Copy(out, in)
137 return out
138}
139
140func cloneWorkspaceConfig(in entity.WorkspaceConfig) entity.WorkspaceConfig {
141 in.PaneMode.Actions = cloneActionBindings(in.PaneMode.Actions)
142 in.TabMode.Actions = cloneActionBindings(in.TabMode.Actions)
143 in.VimMode.Actions = cloneActionBindings(in.VimMode.Actions)
144 in.ResizeMode.Actions = cloneActionBindings(in.ResizeMode.Actions)
145 in.Shortcuts.Actions = cloneActionBindings(in.Shortcuts.Actions)
146 in.FloatingPane.Profiles = cloneFloatingPaneProfiles(in.FloatingPane.Profiles)
147 return in
148}
149
150func cloneSessionConfig(in entity.SessionConfig) entity.SessionConfig {
151 in.SessionMode.Actions = cloneActionBindings(in.SessionMode.Actions)
152 return in
153}
154
155func cloneActionBindings(in map[string]entity.ActionBinding) map[string]entity.ActionBinding {
156 if in == nil {
157 return nil
158 }
159 out := make(map[string]entity.ActionBinding, len(in))
160 for action, binding := range in {
161 binding.Keys = cloneStringSlice(binding.Keys)
162 out[action] = binding
163 }
164 return out
165}
166
167func cloneFloatingPaneProfiles(in map[string]entity.FloatingPaneProfile) map[string]entity.FloatingPaneProfile {
168 if in == nil {
169 return nil
170 }
171 out := make(map[string]entity.FloatingPaneProfile, len(in))
172 for name, profile := range in {
173 profile.Keys = cloneStringSlice(profile.Keys)
174 out[name] = profile
175 }
176 return out
177}
178
179func cloneStringSlice(in []string) []string {
180 if in == nil {
181 return nil
182 }
183 out := make([]string, len(in))
184 copy(out, in)
185 return out
186}
187
188func engineHardwareDecodingModeFromConfig(mode config.HardwareDecodingMode) entity.EngineHardwareDecodingMode {
189 switch mode {
190 case config.HardwareDecodingForce:
191 return entity.EngineHardwareDecodingForce
192 case config.HardwareDecodingDisable:
193 return entity.EngineHardwareDecodingDisable
194 default:
195 return entity.EngineHardwareDecodingAuto
196 }
197}