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 Style: cfg.Omnibox.Style,
100 InitialBehavior: cfg.Omnibox.InitialBehavior,
101 MaxHistoryDays: cfg.Omnibox.MaxHistoryDays,
102 AutoOpenOnNewPane: cfg.Omnibox.AutoOpenOnNewPane,
103 },
104 Update: entity.RuntimeUpdateConfig{
105 EnableOnStartup: cfg.Update.EnableOnStartup,
106 AutoDownload: cfg.Update.AutoDownload,
107 NotifyOnNewSettings: cfg.Update.NotifyOnNewSettings,
108 },
109 Downloads: entity.RuntimeDownloadsConfig{Path: cfg.Downloads.Path},
110 },
111 }
112}
113
114func runtimeSearchShortcutsFromConfig(in map[string]config.SearchShortcut) map[string]entity.RuntimeSearchShortcut {
115 out := make(map[string]entity.RuntimeSearchShortcut, len(in))
116 for key, shortcut := range in {
117 out[key] = entity.RuntimeSearchShortcut{
118 URL: shortcut.URL,
119 Description: shortcut.Description,
120 }
121 }
122 return out
123}
124
125func cloneRuntimeConfigSnapshot(snapshot entity.RuntimeConfigSnapshot) entity.RuntimeConfigSnapshot {
126 snapshot.UI.SearchShortcuts = cloneRuntimeSearchShortcuts(snapshot.UI.SearchShortcuts)
127 snapshot.UI.Workspace = cloneWorkspaceConfig(snapshot.UI.Workspace)
128 snapshot.UI.Session = cloneSessionConfig(snapshot.UI.Session)
129 return snapshot
130}
131
132func cloneRuntimeSearchShortcuts(in map[string]entity.RuntimeSearchShortcut) map[string]entity.RuntimeSearchShortcut {
133 if in == nil {
134 return nil
135 }
136 out := make(map[string]entity.RuntimeSearchShortcut, len(in))
137 maps.Copy(out, in)
138 return out
139}
140
141func cloneWorkspaceConfig(in entity.WorkspaceConfig) entity.WorkspaceConfig {
142 in.PrefixMode.Actions = cloneActionBindings(in.PrefixMode.Actions)
143 in.PaneMode.Actions = cloneActionBindings(in.PaneMode.Actions)
144 in.TabMode.Actions = cloneActionBindings(in.TabMode.Actions)
145 in.VimMode.Actions = cloneActionBindings(in.VimMode.Actions)
146 in.ResizeMode.Actions = cloneActionBindings(in.ResizeMode.Actions)
147 in.Shortcuts.Actions = cloneActionBindings(in.Shortcuts.Actions)
148 in.FloatingPane.Profiles = cloneFloatingPaneProfiles(in.FloatingPane.Profiles)
149 return in
150}
151
152func cloneSessionConfig(in entity.SessionConfig) entity.SessionConfig {
153 in.SessionMode.Actions = cloneActionBindings(in.SessionMode.Actions)
154 return in
155}
156
157func cloneActionBindings(in map[string]entity.ActionBinding) map[string]entity.ActionBinding {
158 if in == nil {
159 return nil
160 }
161 out := make(map[string]entity.ActionBinding, len(in))
162 for action, binding := range in {
163 binding.Keys = cloneStringSlice(binding.Keys)
164 out[action] = binding
165 }
166 return out
167}
168
169func cloneFloatingPaneProfiles(in map[string]entity.FloatingPaneProfile) map[string]entity.FloatingPaneProfile {
170 if in == nil {
171 return nil
172 }
173 out := make(map[string]entity.FloatingPaneProfile, len(in))
174 for name, profile := range in {
175 profile.Keys = cloneStringSlice(profile.Keys)
176 out[name] = profile
177 }
178 return out
179}
180
181func cloneStringSlice(in []string) []string {
182 if in == nil {
183 return nil
184 }
185 out := make([]string, len(in))
186 copy(out, in)
187 return out
188}
189
190func engineHardwareDecodingModeFromConfig(mode config.HardwareDecodingMode) entity.EngineHardwareDecodingMode {
191 switch mode {
192 case config.HardwareDecodingForce:
193 return entity.EngineHardwareDecodingForce
194 case config.HardwareDecodingDisable:
195 return entity.EngineHardwareDecodingDisable
196 default:
197 return entity.EngineHardwareDecodingAuto
198 }
199}