v0.23.1 6c872d5

schema_provider.go RawHistoryFolder

1package config
2
3import (
4	"fmt"
5
6	"github.com/bnema/dumber/internal/domain/entity"
7)
8
9// Section names for grouping config keys.
10const (
11	SectionAppearance       = "Appearance"
12	SectionLogging          = "Logging"
13	SectionHistory          = "History"
14	SectionWorkspace        = "Workspace"
15	SectionSession          = "Session"
16	SectionOmnibox          = "Omnibox"
17	SectionContentFiltering = "Content Filtering"
18	SectionRendering        = "Rendering"
19	SectionMedia            = "Media"
20	SectionUpdate           = "Update"
21	SectionDmenu            = "Dmenu"
22	SectionDebug            = "Debug"
23	SectionPerformance      = "Performance"
24	SectionRuntime          = "Runtime"
25	SectionDatabase         = "Database"
26	SectionSearch           = "Search"
27)
28
29// SchemaProvider implements port.ConfigSchemaProvider.
30type SchemaProvider struct{}
31
32// NewSchemaProvider creates a new SchemaProvider.
33func NewSchemaProvider() *SchemaProvider {
34	return &SchemaProvider{}
35}
36
37// GetSchema returns all configuration keys with their metadata.
38func (p *SchemaProvider) GetSchema() []entity.ConfigKeyInfo {
39	defaults := DefaultConfig()
40
41	keys := make([]entity.ConfigKeyInfo, 0, 100)
42
43	// Appearance section
44	keys = append(keys, p.getAppearanceKeys(defaults)...)
45
46	// Logging section
47	keys = append(keys, p.getLoggingKeys(defaults)...)
48
49	// History section
50	keys = append(keys, p.getHistoryKeys(defaults)...)
51
52	// Search section
53	keys = append(keys, p.getSearchKeys(defaults)...)
54
55	// Dmenu section
56	keys = append(keys, p.getDmenuKeys(defaults)...)
57
58	// Workspace section
59	keys = append(keys, p.getWorkspaceKeys(defaults)...)
60
61	// Session section
62	keys = append(keys, p.getSessionKeys(defaults)...)
63
64	// Omnibox section
65	keys = append(keys, p.getOmniboxKeys(defaults)...)
66
67	// Content Filtering section
68	keys = append(keys, p.getContentFilteringKeys(defaults)...)
69
70	// Rendering section
71	keys = append(keys, p.getRenderingKeys(defaults)...)
72
73	// Media section
74	keys = append(keys, p.getMediaKeys(defaults)...)
75
76	// Update section
77	keys = append(keys, p.getUpdateKeys(defaults)...)
78
79	// Debug section
80	keys = append(keys, p.getDebugKeys(defaults)...)
81
82	// Performance section
83	keys = append(keys, p.getPerformanceKeys(defaults)...)
84
85	// Runtime section
86	keys = append(keys, p.getRuntimeKeys(defaults)...)
87
88	// Database section
89	keys = append(keys, p.getDatabaseKeys()...)
90
91	return keys
92}
93
94func (*SchemaProvider) getAppearanceKeys(defaults *Config) []entity.ConfigKeyInfo {
95	return []entity.ConfigKeyInfo{
96		{
97			Key:         "appearance.color_scheme",
98			Type:        "string",
99			Default:     defaults.Appearance.ColorScheme,
100			Description: "Theme preference (follows system by default)",
101			Values:      []string{"default", "prefer-dark", "prefer-light"},
102			Section:     SectionAppearance,
103		},
104		{
105			Key:         "appearance.default_font_size",
106			Type:        "int",
107			Default:     fmt.Sprintf("%d", defaults.Appearance.DefaultFontSize),
108			Description: "Default font size in CSS pixels",
109			Range:       "1-72",
110			Section:     SectionAppearance,
111		},
112		{
113			Key:         "appearance.sans_font",
114			Type:        "string",
115			Default:     defaults.Appearance.SansFont,
116			Description: "Sans-serif font family for web pages",
117			Section:     SectionAppearance,
118		},
119		{
120			Key:         "appearance.serif_font",
121			Type:        "string",
122			Default:     defaults.Appearance.SerifFont,
123			Description: "Serif font family for web pages",
124			Section:     SectionAppearance,
125		},
126		{
127			Key:         "appearance.monospace_font",
128			Type:        "string",
129			Default:     defaults.Appearance.MonospaceFont,
130			Description: "Monospace font family for code",
131			Section:     SectionAppearance,
132		},
133		{
134			Key:         "default_webpage_zoom",
135			Type:        "float64",
136			Default:     fmt.Sprintf("%.1f", defaults.DefaultWebpageZoom),
137			Description: "Default zoom level for web pages (1.0 = 100%%)",
138			Range:       "0.1-5.0",
139			Section:     SectionAppearance,
140		},
141		{
142			Key:         "default_ui_scale",
143			Type:        "float64",
144			Default:     fmt.Sprintf("%.1f", defaults.DefaultUIScale),
145			Description: "UI scale multiplier for GTK widgets",
146			Range:       "0.5-3.0",
147			Section:     SectionAppearance,
148		},
149		{
150			Key:         "appearance.light_palette.*",
151			Type:        "string",
152			Default:     "(hex colors)",
153			Description: "Light theme color palette (background, surface, surface_variant, text, muted, accent, border)",
154			Section:     SectionAppearance,
155		},
156		{
157			Key:         "appearance.dark_palette.*",
158			Type:        "string",
159			Default:     "(hex colors)",
160			Description: "Dark theme color palette (background, surface, surface_variant, text, muted, accent, border)",
161			Section:     SectionAppearance,
162		},
163	}
164}
165
166func (*SchemaProvider) getLoggingKeys(defaults *Config) []entity.ConfigKeyInfo {
167	return []entity.ConfigKeyInfo{
168		{
169			Key:         "logging.level",
170			Type:        "string",
171			Default:     defaults.Logging.Level,
172			Description: "Log verbosity level",
173			Values:      []string{"trace", "debug", "info", "warn", "error", "fatal"},
174			Section:     SectionLogging,
175		},
176		{
177			Key:         "logging.format",
178			Type:        "string",
179			Default:     defaults.Logging.Format,
180			Description: "Log output format",
181			Values:      []string{"text", "json", "console"},
182			Section:     SectionLogging,
183		},
184		{
185			Key:         "logging.max_age",
186			Type:        "int",
187			Default:     fmt.Sprintf("%d", defaults.Logging.MaxAge),
188			Description: "Maximum age of log files in days",
189			Range:       ">=0",
190			Section:     SectionLogging,
191		},
192		{
193			Key:         "logging.log_dir",
194			Type:        "string",
195			Default:     defaults.Logging.LogDir,
196			Description: "Directory for log files",
197			Section:     SectionLogging,
198		},
199		{
200			Key:         "logging.enable_file_log",
201			Type:        "bool",
202			Default:     fmt.Sprintf("%t", defaults.Logging.EnableFileLog),
203			Description: "Enable logging to file",
204			Section:     SectionLogging,
205		},
206		{
207			Key:         "logging.capture_console",
208			Type:        "bool",
209			Default:     fmt.Sprintf("%t", defaults.Logging.CaptureConsole),
210			Description: "Capture browser console messages to log",
211			Section:     SectionLogging,
212		},
213	}
214}
215
216func (*SchemaProvider) getHistoryKeys(defaults *Config) []entity.ConfigKeyInfo {
217	return []entity.ConfigKeyInfo{
218		{
219			Key:         "history.max_entries",
220			Type:        "int",
221			Default:     fmt.Sprintf("%d", defaults.History.MaxEntries),
222			Description: "Maximum number of history entries to keep",
223			Range:       ">=0",
224			Section:     SectionHistory,
225		},
226		{
227			Key:         "history.retention_period_days",
228			Type:        "int",
229			Default:     fmt.Sprintf("%d", defaults.History.RetentionPeriodDays),
230			Description: "Number of days to retain history",
231			Range:       ">=0",
232			Section:     SectionHistory,
233		},
234		{
235			Key:         "history.cleanup_interval_days",
236			Type:        "int",
237			Default:     fmt.Sprintf("%d", defaults.History.CleanupIntervalDays),
238			Description: "Days between automatic history cleanup",
239			Range:       ">=0",
240			Section:     SectionHistory,
241		},
242	}
243}
244
245func (*SchemaProvider) getSearchKeys(defaults *Config) []entity.ConfigKeyInfo {
246	return []entity.ConfigKeyInfo{
247		{
248			Key:         "default_search_engine",
249			Type:        "string",
250			Default:     defaults.DefaultSearchEngine,
251			Description: "Default search engine URL (must contain %s placeholder)",
252			Section:     SectionSearch,
253		},
254		{
255			Key:         "search_shortcuts.<key>",
256			Type:        "object",
257			Default:     "(see defaults)",
258			Description: "Search shortcuts map with url and description fields",
259			Section:     SectionSearch,
260		},
261	}
262}
263
264func (*SchemaProvider) getDmenuKeys(defaults *Config) []entity.ConfigKeyInfo {
265	return []entity.ConfigKeyInfo{
266		{
267			Key:         "dmenu.max_history_days",
268			Type:        "int",
269			Default:     fmt.Sprintf("%d", defaults.Dmenu.MaxHistoryDays),
270			Description: "Number of days of history to show in dmenu (0 = all)",
271			Range:       ">=0",
272			Section:     SectionDmenu,
273		},
274		{
275			Key:         "dmenu.show_visit_count",
276			Type:        "bool",
277			Default:     fmt.Sprintf("%t", defaults.Dmenu.ShowVisitCount),
278			Description: "Show visit count in dmenu entries",
279			Section:     SectionDmenu,
280		},
281		{
282			Key:         "dmenu.show_last_visited",
283			Type:        "bool",
284			Default:     fmt.Sprintf("%t", defaults.Dmenu.ShowLastVisited),
285			Description: "Show last visited date in dmenu entries",
286			Section:     SectionDmenu,
287		},
288		{
289			Key:         "dmenu.history_prefix",
290			Type:        "string",
291			Default:     defaults.Dmenu.HistoryPrefix,
292			Description: "Prefix for history entries in dmenu",
293			Section:     SectionDmenu,
294		},
295		{
296			Key:         "dmenu.shortcut_prefix",
297			Type:        "string",
298			Default:     defaults.Dmenu.ShortcutPrefix,
299			Description: "Prefix for search shortcuts in dmenu",
300			Section:     SectionDmenu,
301		},
302		{
303			Key:         "dmenu.url_prefix",
304			Type:        "string",
305			Default:     defaults.Dmenu.URLPrefix,
306			Description: "Prefix for URL entries in dmenu",
307			Section:     SectionDmenu,
308		},
309		{
310			Key:         "dmenu.date_format",
311			Type:        "string",
312			Default:     defaults.Dmenu.DateFormat,
313			Description: "Go time format for dates in dmenu",
314			Section:     SectionDmenu,
315		},
316		{
317			Key:         "dmenu.sort_by_visit_count",
318			Type:        "bool",
319			Default:     fmt.Sprintf("%t", defaults.Dmenu.SortByVisitCount),
320			Description: "Sort dmenu entries by visit count",
321			Section:     SectionDmenu,
322		},
323	}
324}
325
326func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo {
327	return []entity.ConfigKeyInfo{
328		{
329			Key:         "workspace.new_pane_url",
330			Type:        "string",
331			Default:     defaults.Workspace.NewPaneURL,
332			Description: "URL loaded when creating a new pane",
333			Section:     SectionWorkspace,
334		},
335		{
336			Key:         "workspace.tab_bar_position",
337			Type:        "string",
338			Default:     defaults.Workspace.TabBarPosition,
339			Description: "Position of the tab bar",
340			Values:      []string{"top", "bottom"},
341			Section:     SectionWorkspace,
342		},
343		{
344			Key:         "workspace.hide_tab_bar_when_single_tab",
345			Type:        "bool",
346			Default:     fmt.Sprintf("%t", defaults.Workspace.HideTabBarWhenSingleTab),
347			Description: "Hide tab bar when only one tab exists",
348			Section:     SectionWorkspace,
349		},
350		{
351			Key:         "workspace.switch_to_tab_on_move",
352			Type:        "bool",
353			Default:     fmt.Sprintf("%t", defaults.Workspace.SwitchToTabOnMove),
354			Description: "Switch focus to tab when moving pane to it",
355			Section:     SectionWorkspace,
356		},
357		// Pane mode
358		{
359			Key:         "workspace.pane_mode.activation_shortcut",
360			Type:        "string",
361			Default:     defaults.Workspace.PaneMode.ActivationShortcut,
362			Description: "Shortcut to enter pane mode",
363			Section:     SectionWorkspace,
364		},
365		{
366			Key:         "workspace.pane_mode.timeout_ms",
367			Type:        "int",
368			Default:     fmt.Sprintf("%d", defaults.Workspace.PaneMode.TimeoutMilliseconds),
369			Description: "Pane mode timeout in milliseconds (0 = no timeout)",
370			Range:       ">=0",
371			Section:     SectionWorkspace,
372		},
373		{
374			Key:         "workspace.pane_mode.actions.<action>",
375			Type:        "[]string",
376			Default:     "(see defaults)",
377			Description: "Key bindings for pane mode actions",
378			Section:     SectionWorkspace,
379		},
380		// Tab mode
381		{
382			Key:         "workspace.tab_mode.activation_shortcut",
383			Type:        "string",
384			Default:     defaults.Workspace.TabMode.ActivationShortcut,
385			Description: "Shortcut to enter tab mode",
386			Section:     SectionWorkspace,
387		},
388		{
389			Key:         "workspace.tab_mode.timeout_ms",
390			Type:        "int",
391			Default:     fmt.Sprintf("%d", defaults.Workspace.TabMode.TimeoutMilliseconds),
392			Description: "Tab mode timeout in milliseconds (0 = no timeout)",
393			Range:       ">=0",
394			Section:     SectionWorkspace,
395		},
396		{
397			Key:         "workspace.tab_mode.actions.<action>",
398			Type:        "[]string",
399			Default:     "(see defaults)",
400			Description: "Key bindings for tab mode actions",
401			Section:     SectionWorkspace,
402		},
403		// Resize mode
404		{
405			Key:         "workspace.resize_mode.activation_shortcut",
406			Type:        "string",
407			Default:     defaults.Workspace.ResizeMode.ActivationShortcut,
408			Description: "Shortcut to enter resize mode",
409			Section:     SectionWorkspace,
410		},
411		{
412			Key:         "workspace.resize_mode.timeout_ms",
413			Type:        "int",
414			Default:     fmt.Sprintf("%d", defaults.Workspace.ResizeMode.TimeoutMilliseconds),
415			Description: "Resize mode timeout in milliseconds (0 = no timeout)",
416			Range:       ">=0",
417			Section:     SectionWorkspace,
418		},
419		{
420			Key:         "workspace.resize_mode.step_percent",
421			Type:        "float64",
422			Default:     fmt.Sprintf("%.1f", defaults.Workspace.ResizeMode.StepPercent),
423			Description: "Percentage to resize panes per step",
424			Section:     SectionWorkspace,
425		},
426		{
427			Key:         "workspace.resize_mode.min_pane_percent",
428			Type:        "float64",
429			Default:     fmt.Sprintf("%.1f", defaults.Workspace.ResizeMode.MinPanePercent),
430			Description: "Minimum pane size as percentage",
431			Section:     SectionWorkspace,
432		},
433		{
434			Key:         "workspace.resize_mode.actions.<action>",
435			Type:        "[]string",
436			Default:     "(see defaults)",
437			Description: "Key bindings for resize mode actions",
438			Section:     SectionWorkspace,
439		},
440		// Global shortcuts
441		{
442			Key:         "workspace.shortcuts.close_pane",
443			Type:        "string",
444			Default:     defaults.Workspace.Shortcuts.ClosePane,
445			Description: "Global shortcut to close current pane",
446			Section:     SectionWorkspace,
447		},
448		{
449			Key:         "workspace.shortcuts.next_tab",
450			Type:        "string",
451			Default:     defaults.Workspace.Shortcuts.NextTab,
452			Description: "Global shortcut to switch to next tab",
453			Section:     SectionWorkspace,
454		},
455		{
456			Key:         "workspace.shortcuts.previous_tab",
457			Type:        "string",
458			Default:     defaults.Workspace.Shortcuts.PreviousTab,
459			Description: "Global shortcut to switch to previous tab",
460			Section:     SectionWorkspace,
461		},
462		// Popups
463		{
464			Key:         "workspace.popups.behavior",
465			Type:        "string",
466			Default:     string(defaults.Workspace.Popups.Behavior),
467			Description: "How to handle JavaScript popup windows",
468			Values:      []string{"split", "stacked", "tabbed", "windowed"},
469			Section:     SectionWorkspace,
470		},
471		{
472			Key:         "workspace.popups.placement",
473			Type:        "string",
474			Default:     defaults.Workspace.Popups.Placement,
475			Description: "Placement direction for split popups",
476			Values:      []string{"right", "left", "top", "bottom"},
477			Section:     SectionWorkspace,
478		},
479		{
480			Key:         "workspace.popups.blank_target_behavior",
481			Type:        "string",
482			Default:     defaults.Workspace.Popups.BlankTargetBehavior,
483			Description: "How to handle target=\"_blank\" links",
484			Values:      []string{"split", "stacked", "tabbed"},
485			Section:     SectionWorkspace,
486		},
487		{
488			Key:         "workspace.popups.open_in_new_pane",
489			Type:        "bool",
490			Default:     fmt.Sprintf("%t", defaults.Workspace.Popups.OpenInNewPane),
491			Description: "Open popups in workspace instead of blocking",
492			Section:     SectionWorkspace,
493		},
494		{
495			Key:         "workspace.popups.follow_pane_context",
496			Type:        "bool",
497			Default:     fmt.Sprintf("%t", defaults.Workspace.Popups.FollowPaneContext),
498			Description: "Popup placement follows parent pane context",
499			Section:     SectionWorkspace,
500		},
501		{
502			Key:         "workspace.popups.enable_smart_detection",
503			Type:        "bool",
504			Default:     fmt.Sprintf("%t", defaults.Workspace.Popups.EnableSmartDetection),
505			Description: "Use window properties to detect popup intent",
506			Section:     SectionWorkspace,
507		},
508		{
509			Key:         "workspace.popups.oauth_auto_close",
510			Type:        "bool",
511			Default:     fmt.Sprintf("%t", defaults.Workspace.Popups.OAuthAutoClose),
512			Description: "Auto-close OAuth popups after success",
513			Section:     SectionWorkspace,
514		},
515		// Styling
516		{
517			Key:         "workspace.styling.border_width",
518			Type:        "int",
519			Default:     fmt.Sprintf("%d", defaults.Workspace.Styling.BorderWidth),
520			Description: "Active pane border width in pixels",
521			Range:       ">=0",
522			Section:     SectionWorkspace,
523		},
524		{
525			Key:         "workspace.styling.border_color",
526			Type:        "string",
527			Default:     defaults.Workspace.Styling.BorderColor,
528			Description: "Active pane border color (CSS or @theme variable)",
529			Section:     SectionWorkspace,
530		},
531		{
532			Key:         "workspace.styling.mode_border_width",
533			Type:        "int",
534			Default:     fmt.Sprintf("%d", defaults.Workspace.Styling.ModeBorderWidth),
535			Description: "Border width for modal mode indicators",
536			Range:       ">=0",
537			Section:     SectionWorkspace,
538		},
539		{
540			Key:         "workspace.styling.pane_mode_color",
541			Type:        "string",
542			Default:     defaults.Workspace.Styling.PaneModeColor,
543			Description: "Color for pane mode indicator",
544			Section:     SectionWorkspace,
545		},
546		{
547			Key:         "workspace.styling.tab_mode_color",
548			Type:        "string",
549			Default:     defaults.Workspace.Styling.TabModeColor,
550			Description: "Color for tab mode indicator",
551			Section:     SectionWorkspace,
552		},
553		{
554			Key:         "workspace.styling.session_mode_color",
555			Type:        "string",
556			Default:     defaults.Workspace.Styling.SessionModeColor,
557			Description: "Color for session mode indicator",
558			Section:     SectionWorkspace,
559		},
560		{
561			Key:         "workspace.styling.resize_mode_color",
562			Type:        "string",
563			Default:     defaults.Workspace.Styling.ResizeModeColor,
564			Description: "Color for resize mode indicator",
565			Section:     SectionWorkspace,
566		},
567		{
568			Key:         "workspace.styling.mode_indicator_toaster_enabled",
569			Type:        "bool",
570			Default:     fmt.Sprintf("%t", defaults.Workspace.Styling.ModeIndicatorToasterEnabled),
571			Description: "Show toaster notification for modal modes",
572			Section:     SectionWorkspace,
573		},
574		{
575			Key:         "workspace.styling.transition_duration",
576			Type:        "int",
577			Default:     fmt.Sprintf("%d", defaults.Workspace.Styling.TransitionDuration),
578			Description: "Border animation duration in milliseconds",
579			Range:       ">=0",
580			Section:     SectionWorkspace,
581		},
582	}
583}
584
585func (*SchemaProvider) getSessionKeys(defaults *Config) []entity.ConfigKeyInfo {
586	return []entity.ConfigKeyInfo{
587		{
588			Key:         "session.auto_restore",
589			Type:        "bool",
590			Default:     fmt.Sprintf("%t", defaults.Session.AutoRestore),
591			Description: "Automatically restore last session on startup",
592			Section:     SectionSession,
593		},
594		{
595			Key:         "session.snapshot_interval_ms",
596			Type:        "int",
597			Default:     fmt.Sprintf("%d", defaults.Session.SnapshotIntervalMs),
598			Description: "Minimum interval between session snapshots",
599			Range:       ">=0",
600			Section:     SectionSession,
601		},
602		{
603			Key:         "session.max_exited_sessions",
604			Type:        "int",
605			Default:     fmt.Sprintf("%d", defaults.Session.MaxExitedSessions),
606			Description: "Maximum number of exited sessions to keep",
607			Range:       ">=0",
608			Section:     SectionSession,
609		},
610		{
611			Key:         "session.max_exited_session_age_days",
612			Type:        "int",
613			Default:     fmt.Sprintf("%d", defaults.Session.MaxExitedSessionAgeDays),
614			Description: "Maximum age of exited sessions in days",
615			Range:       ">=0",
616			Section:     SectionSession,
617		},
618		{
619			Key:         "session.session_mode.activation_shortcut",
620			Type:        "string",
621			Default:     defaults.Session.SessionMode.ActivationShortcut,
622			Description: "Shortcut to enter session mode",
623			Section:     SectionSession,
624		},
625		{
626			Key:         "session.session_mode.timeout_ms",
627			Type:        "int",
628			Default:     fmt.Sprintf("%d", defaults.Session.SessionMode.TimeoutMilliseconds),
629			Description: "Session mode timeout in milliseconds",
630			Range:       ">=0",
631			Section:     SectionSession,
632		},
633		{
634			Key:         "session.session_mode.actions.<action>",
635			Type:        "[]string",
636			Default:     "(see defaults)",
637			Description: "Key bindings for session mode actions",
638			Section:     SectionSession,
639		},
640	}
641}
642
643func (*SchemaProvider) getOmniboxKeys(defaults *Config) []entity.ConfigKeyInfo {
644	return []entity.ConfigKeyInfo{
645		{
646			Key:         "omnibox.initial_behavior",
647			Type:        "string",
648			Default:     defaults.Omnibox.InitialBehavior,
649			Description: "What to show when omnibox opens with empty input",
650			Values:      []string{"recent", "most_visited", "none"},
651			Section:     SectionOmnibox,
652		},
653		{
654			Key:         "omnibox.auto_open_on_new_pane",
655			Type:        "bool",
656			Default:     fmt.Sprintf("%t", defaults.Omnibox.AutoOpenOnNewPane),
657			Description: "Auto-open omnibox when creating new pane",
658			Section:     SectionOmnibox,
659		},
660	}
661}
662
663func (*SchemaProvider) getContentFilteringKeys(defaults *Config) []entity.ConfigKeyInfo {
664	return []entity.ConfigKeyInfo{
665		{
666			Key:         "content_filtering.enabled",
667			Type:        "bool",
668			Default:     fmt.Sprintf("%t", defaults.ContentFiltering.Enabled),
669			Description: "Enable ad blocking and content filtering",
670			Section:     SectionContentFiltering,
671		},
672		{
673			Key:         "content_filtering.auto_update",
674			Type:        "bool",
675			Default:     fmt.Sprintf("%t", defaults.ContentFiltering.AutoUpdate),
676			Description: "Auto-update filter lists from GitHub",
677			Section:     SectionContentFiltering,
678		},
679	}
680}
681
682func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {
683	return []entity.ConfigKeyInfo{
684		{
685			Key:         "rendering.mode",
686			Type:        "string",
687			Default:     string(defaults.Rendering.Mode),
688			Description: "GPU/CPU rendering selection for WebKit",
689			Values:      []string{"auto", "gpu", "cpu"},
690			Section:     SectionRendering,
691		},
692		{
693			Key:         "rendering.gsk_renderer",
694			Type:        "string",
695			Default:     string(defaults.Rendering.GSKRenderer),
696			Description: "GTK Scene Kit renderer selection",
697			Values:      []string{"auto", "opengl", "vulkan", "cairo"},
698			Section:     SectionRendering,
699		},
700		{
701			Key:         "rendering.disable_dmabuf_renderer",
702			Type:        "bool",
703			Default:     fmt.Sprintf("%t", defaults.Rendering.DisableDMABufRenderer),
704			Description: "Disable DMA-BUF accelerated renderer",
705			Section:     SectionRendering,
706		},
707		{
708			Key:         "rendering.force_compositing_mode",
709			Type:        "bool",
710			Default:     fmt.Sprintf("%t", defaults.Rendering.ForceCompositingMode),
711			Description: "Force accelerated compositing",
712			Section:     SectionRendering,
713		},
714		{
715			Key:         "rendering.disable_compositing_mode",
716			Type:        "bool",
717			Default:     fmt.Sprintf("%t", defaults.Rendering.DisableCompositingMode),
718			Description: "Disable accelerated compositing",
719			Section:     SectionRendering,
720		},
721		{
722			Key:         "rendering.disable_mipmaps",
723			Type:        "bool",
724			Default:     fmt.Sprintf("%t", defaults.Rendering.DisableMipmaps),
725			Description: "Disable mipmap generation",
726			Section:     SectionRendering,
727		},
728		{
729			Key:         "rendering.prefer_gl",
730			Type:        "bool",
731			Default:     fmt.Sprintf("%t", defaults.Rendering.PreferGL),
732			Description: "Force OpenGL over GLES",
733			Section:     SectionRendering,
734		},
735		{
736			Key:         "rendering.draw_compositing_indicators",
737			Type:        "bool",
738			Default:     fmt.Sprintf("%t", defaults.Rendering.DrawCompositingIndicators),
739			Description: "Show compositing layer borders",
740			Section:     SectionRendering,
741		},
742		{
743			Key:         "rendering.show_fps",
744			Type:        "bool",
745			Default:     fmt.Sprintf("%t", defaults.Rendering.ShowFPS),
746			Description: "Display WebKit FPS counter",
747			Section:     SectionRendering,
748		},
749		{
750			Key:         "rendering.sample_memory",
751			Type:        "bool",
752			Default:     fmt.Sprintf("%t", defaults.Rendering.SampleMemory),
753			Description: "Enable memory sampling",
754			Section:     SectionRendering,
755		},
756		{
757			Key:         "rendering.debug_frames",
758			Type:        "bool",
759			Default:     fmt.Sprintf("%t", defaults.Rendering.DebugFrames),
760			Description: "Enable frame timing debug output",
761			Section:     SectionRendering,
762		},
763	}
764}
765
766func (*SchemaProvider) getMediaKeys(defaults *Config) []entity.ConfigKeyInfo {
767	return []entity.ConfigKeyInfo{
768		{
769			Key:         "media.hardware_decoding",
770			Type:        "string",
771			Default:     string(defaults.Media.HardwareDecodingMode),
772			Description: "Hardware vs software video decoding",
773			Values:      []string{"auto", "force", "disable"},
774			Section:     SectionMedia,
775		},
776		{
777			Key:         "media.gl_rendering_mode",
778			Type:        "string",
779			Default:     string(defaults.Media.GLRenderingMode),
780			Description: "OpenGL API selection for video rendering",
781			Values:      []string{"auto", "gles2", "gl3", "none"},
782			Section:     SectionMedia,
783		},
784		{
785			Key:         "media.prefer_av1",
786			Type:        "bool",
787			Default:     fmt.Sprintf("%t", defaults.Media.PreferAV1),
788			Description: "Prioritize AV1 codec when available",
789			Section:     SectionMedia,
790		},
791		{
792			Key:         "media.show_diagnostics",
793			Type:        "bool",
794			Default:     fmt.Sprintf("%t", defaults.Media.ShowDiagnosticsOnStartup),
795			Description: "Show media capability warnings at startup",
796			Section:     SectionMedia,
797		},
798		{
799			Key:         "media.force_vsync",
800			Type:        "bool",
801			Default:     fmt.Sprintf("%t", defaults.Media.ForceVSync),
802			Description: "Force vertical sync for video playback",
803			Section:     SectionMedia,
804		},
805		{
806			Key:         "media.gstreamer_debug_level",
807			Type:        "int",
808			Default:     fmt.Sprintf("%d", defaults.Media.GStreamerDebugLevel),
809			Description: "GStreamer debug verbosity (0=off, 1-5)",
810			Range:       "0-5",
811			Section:     SectionMedia,
812		},
813	}
814}
815
816func (*SchemaProvider) getUpdateKeys(defaults *Config) []entity.ConfigKeyInfo {
817	return []entity.ConfigKeyInfo{
818		{
819			Key:         "update.enable_on_startup",
820			Type:        "bool",
821			Default:     fmt.Sprintf("%t", defaults.Update.EnableOnStartup),
822			Description: "Check for updates on browser startup",
823			Section:     SectionUpdate,
824		},
825		{
826			Key:         "update.auto_download",
827			Type:        "bool",
828			Default:     fmt.Sprintf("%t", defaults.Update.AutoDownload),
829			Description: "Auto-download updates in background",
830			Section:     SectionUpdate,
831		},
832		{
833			Key:         "update.notify_on_new_settings",
834			Type:        "bool",
835			Default:     fmt.Sprintf("%t", defaults.Update.NotifyOnNewSettings),
836			Description: "Show toast when new config settings available",
837			Section:     SectionUpdate,
838		},
839	}
840}
841
842func (*SchemaProvider) getDebugKeys(defaults *Config) []entity.ConfigKeyInfo {
843	return []entity.ConfigKeyInfo{
844		{
845			Key:         "debug.enable_devtools",
846			Type:        "bool",
847			Default:     fmt.Sprintf("%t", defaults.Debug.EnableDevTools),
848			Description: "Enable browser developer tools (F12)",
849			Section:     SectionDebug,
850		},
851	}
852}
853
854func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {
855	return []entity.ConfigKeyInfo{
856		{
857			Key:         "performance.zoom_cache_size",
858			Type:        "int",
859			Default:     fmt.Sprintf("%d", defaults.Performance.ZoomCacheSize),
860			Description: "Number of domain zoom levels to cache",
861			Range:       ">=0",
862			Section:     SectionPerformance,
863		},
864		{
865			Key:         "performance.webview_pool_prewarm_count",
866			Type:        "int",
867			Default:     fmt.Sprintf("%d", defaults.Performance.WebViewPoolPrewarmCount),
868			Description: "WebViews to pre-create at startup",
869			Range:       ">=0",
870			Section:     SectionPerformance,
871		},
872	}
873}
874
875func (*SchemaProvider) getRuntimeKeys(_ *Config) []entity.ConfigKeyInfo {
876	return []entity.ConfigKeyInfo{
877		{
878			Key:         "runtime.prefix",
879			Type:        "string",
880			Default:     "(empty)",
881			Description: "Custom runtime prefix for WebKitGTK/GTK",
882			Section:     SectionRuntime,
883		},
884	}
885}
886
887func (*SchemaProvider) getDatabaseKeys() []entity.ConfigKeyInfo {
888	// Get actual default path for display
889	dbPath := "$XDG_DATA_HOME/" + appName + "/" + databaseName
890	if path, err := GetDatabaseFile(); err == nil {
891		dbPath = path
892	}
893
894	return []entity.ConfigKeyInfo{
895		{
896			Key:         "database.path",
897			Type:        "string",
898			Default:     dbPath,
899			Description: "Path to SQLite database file",
900			Section:     SectionDatabase,
901		},
902	}
903}

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