v0.24.0 a261e52

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

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