v0.30.0 eb3068f

runtime_config.go RawHistoryFolder

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

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