v0.25.0 2e6c485

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

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