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: "appearance.gtk_font",
143 Type: "string",
144 Default: defaults.Appearance.GtkFont,
145 Description: "Native GTK UI font family for app chrome",
146 Section: SectionAppearance,
147 },
148 {
149 Key: "default_webpage_zoom",
150 Type: "float64",
151 Default: fmt.Sprintf("%.1f", defaults.DefaultWebpageZoom),
152 Description: "Default zoom level for web pages (1.0 = 100%%)",
153 Range: "0.1-5.0",
154 Section: SectionAppearance,
155 },
156 {
157 Key: "default_ui_scale",
158 Type: "float64",
159 Default: fmt.Sprintf("%.1f", defaults.DefaultUIScale),
160 Description: "UI scale multiplier for GTK widgets",
161 Range: "0.5-3.0",
162 Section: SectionAppearance,
163 },
164 {
165 Key: "appearance.light_palette.*",
166 Type: "string",
167 Default: "(hex colors)",
168 Description: "Light theme color palette (background, surface, surface_variant, text, muted, accent, border)",
169 Section: SectionAppearance,
170 },
171 {
172 Key: "appearance.dark_palette.*",
173 Type: "string",
174 Default: "(hex colors)",
175 Description: "Dark theme color palette (background, surface, surface_variant, text, muted, accent, border)",
176 Section: SectionAppearance,
177 },
178 }
179}
180
181func (*SchemaProvider) getLoggingKeys(defaults *Config) []entity.ConfigKeyInfo {
182 return []entity.ConfigKeyInfo{
183 {
184 Key: "logging.level",
185 Type: "string",
186 Default: defaults.Logging.Level,
187 Description: "Log verbosity level",
188 Values: []string{"trace", "debug", "info", "warn", "error", "fatal"},
189 Section: SectionLogging,
190 },
191 {
192 Key: "logging.format",
193 Type: "string",
194 Default: defaults.Logging.Format,
195 Description: "Log output format",
196 Values: []string{"text", "json", "console"},
197 Section: SectionLogging,
198 },
199 {
200 Key: "logging.max_age",
201 Type: "int",
202 Default: fmt.Sprintf("%d", defaults.Logging.MaxAge),
203 Description: "Maximum age of log files in days",
204 Range: ">=0",
205 Section: SectionLogging,
206 },
207 {
208 Key: "logging.max_files",
209 Type: "int",
210 Default: fmt.Sprintf("%d", defaults.Logging.MaxFiles),
211 Description: "Maximum number of session log files to keep (0 disables count-based cleanup)",
212 Range: ">=0",
213 Section: SectionLogging,
214 },
215 {
216 Key: "logging.log_dir",
217 Type: "string",
218 Default: defaults.Logging.LogDir,
219 Description: "Directory for log files",
220 Section: SectionLogging,
221 },
222 {
223 Key: "logging.enable_file_log",
224 Type: "bool",
225 Default: fmt.Sprintf("%t", defaults.Logging.EnableFileLog),
226 Description: "Enable logging to file",
227 Section: SectionLogging,
228 },
229 {
230 Key: "logging.capture_console",
231 Type: "bool",
232 Default: fmt.Sprintf("%t", defaults.Logging.CaptureConsole),
233 Description: "Capture browser console messages to log",
234 Section: SectionLogging,
235 },
236 }
237}
238
239func (*SchemaProvider) getHistoryKeys(defaults *Config) []entity.ConfigKeyInfo {
240 return []entity.ConfigKeyInfo{
241 {
242 Key: "history.max_entries",
243 Type: "int",
244 Default: fmt.Sprintf("%d", defaults.History.MaxEntries),
245 Description: "Maximum number of history entries to keep",
246 Range: ">=0",
247 Section: SectionHistory,
248 },
249 {
250 Key: "history.retention_period_days",
251 Type: "int",
252 Default: fmt.Sprintf("%d", defaults.History.RetentionPeriodDays),
253 Description: "Number of days to retain history",
254 Range: ">=0",
255 Section: SectionHistory,
256 },
257 {
258 Key: "history.cleanup_interval_days",
259 Type: "int",
260 Default: fmt.Sprintf("%d", defaults.History.CleanupIntervalDays),
261 Description: "Days between automatic history cleanup",
262 Range: ">=0",
263 Section: SectionHistory,
264 },
265 }
266}
267
268func (*SchemaProvider) getSearchKeys(defaults *Config) []entity.ConfigKeyInfo {
269 return []entity.ConfigKeyInfo{
270 {
271 Key: "default_search_engine",
272 Type: "string",
273 Default: defaults.DefaultSearchEngine,
274 Description: "Default search engine URL (must contain %s placeholder)",
275 Section: SectionSearch,
276 },
277 {
278 Key: "search_shortcuts.<key>",
279 Type: "object",
280 Default: "(see defaults)",
281 Description: "Search shortcuts map with url and description fields",
282 Section: SectionSearch,
283 },
284 }
285}
286
287func (*SchemaProvider) getDmenuKeys(defaults *Config) []entity.ConfigKeyInfo {
288 return []entity.ConfigKeyInfo{
289 {
290 Key: "dmenu.max_history_days",
291 Type: "int",
292 Default: fmt.Sprintf("%d", defaults.Dmenu.MaxHistoryDays),
293 Description: "Number of days of history to show in dmenu (0 = all)",
294 Range: ">=0",
295 Section: SectionDmenu,
296 },
297 {
298 Key: "dmenu.show_visit_count",
299 Type: "bool",
300 Default: fmt.Sprintf("%t", defaults.Dmenu.ShowVisitCount),
301 Description: "Show visit count in dmenu entries",
302 Section: SectionDmenu,
303 },
304 {
305 Key: "dmenu.show_last_visited",
306 Type: "bool",
307 Default: fmt.Sprintf("%t", defaults.Dmenu.ShowLastVisited),
308 Description: "Show last visited date in dmenu entries",
309 Section: SectionDmenu,
310 },
311 {
312 Key: "dmenu.history_prefix",
313 Type: "string",
314 Default: defaults.Dmenu.HistoryPrefix,
315 Description: "Prefix for history entries in dmenu",
316 Section: SectionDmenu,
317 },
318 {
319 Key: "dmenu.shortcut_prefix",
320 Type: "string",
321 Default: defaults.Dmenu.ShortcutPrefix,
322 Description: "Prefix for search shortcuts in dmenu",
323 Section: SectionDmenu,
324 },
325 {
326 Key: "dmenu.url_prefix",
327 Type: "string",
328 Default: defaults.Dmenu.URLPrefix,
329 Description: "Prefix for URL entries in dmenu",
330 Section: SectionDmenu,
331 },
332 {
333 Key: "dmenu.date_format",
334 Type: "string",
335 Default: defaults.Dmenu.DateFormat,
336 Description: "Go time format for dates in dmenu",
337 Section: SectionDmenu,
338 },
339 {
340 Key: "dmenu.sort_by_visit_count",
341 Type: "bool",
342 Default: fmt.Sprintf("%t", defaults.Dmenu.SortByVisitCount),
343 Description: "Sort dmenu entries by visit count",
344 Section: SectionDmenu,
345 },
346 }
347}
348
349func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo {
350 return []entity.ConfigKeyInfo{
351 {
352 Key: "workspace.new_pane_url",
353 Type: "string",
354 Default: defaults.Workspace.NewPaneURL,
355 Description: "URL loaded when creating a new pane",
356 Section: SectionWorkspace,
357 },
358 {
359 Key: "workspace.tab_bar_position",
360 Type: "string",
361 Default: defaults.Workspace.TabBarPosition,
362 Description: "Position of the tab bar",
363 Values: []string{"top", "bottom"},
364 Section: SectionWorkspace,
365 },
366 {
367 Key: "workspace.hide_tab_bar_when_single_tab",
368 Type: "bool",
369 Default: fmt.Sprintf("%t", defaults.Workspace.HideTabBarWhenSingleTab),
370 Description: "Hide tab bar when only one tab exists",
371 Section: SectionWorkspace,
372 },
373 {
374 Key: "workspace.switch_to_tab_on_move",
375 Type: "bool",
376 Default: fmt.Sprintf("%t", defaults.Workspace.SwitchToTabOnMove),
377 Description: "Switch focus to tab when moving pane to it",
378 Section: SectionWorkspace,
379 },
380 // Pane mode
381 {
382 Key: "workspace.pane_mode.activation_shortcut",
383 Type: "string",
384 Default: defaults.Workspace.PaneMode.ActivationShortcut,
385 Description: "Shortcut to enter pane mode",
386 Section: SectionWorkspace,
387 },
388 {
389 Key: "workspace.pane_mode.timeout_ms",
390 Type: "int",
391 Default: fmt.Sprintf("%d", defaults.Workspace.PaneMode.TimeoutMilliseconds),
392 Description: "Pane mode timeout in milliseconds (0 = no timeout)",
393 Range: ">=0",
394 Section: SectionWorkspace,
395 },
396 {
397 Key: "workspace.pane_mode.actions.<action>",
398 Type: "[]string",
399 Default: "(see defaults)",
400 Description: "Key bindings for pane mode actions",
401 Section: SectionWorkspace,
402 },
403 // Tab mode
404 {
405 Key: "workspace.tab_mode.activation_shortcut",
406 Type: "string",
407 Default: defaults.Workspace.TabMode.ActivationShortcut,
408 Description: "Shortcut to enter tab mode",
409 Section: SectionWorkspace,
410 },
411 {
412 Key: "workspace.tab_mode.timeout_ms",
413 Type: "int",
414 Default: fmt.Sprintf("%d", defaults.Workspace.TabMode.TimeoutMilliseconds),
415 Description: "Tab mode timeout in milliseconds (0 = no timeout)",
416 Range: ">=0",
417 Section: SectionWorkspace,
418 },
419 {
420 Key: "workspace.tab_mode.actions.<action>",
421 Type: "[]string",
422 Default: "(see defaults)",
423 Description: "Key bindings for tab mode actions",
424 Section: SectionWorkspace,
425 },
426 // Resize mode
427 {
428 Key: "workspace.resize_mode.activation_shortcut",
429 Type: "string",
430 Default: defaults.Workspace.ResizeMode.ActivationShortcut,
431 Description: "Shortcut to enter resize mode",
432 Section: SectionWorkspace,
433 },
434 {
435 Key: "workspace.resize_mode.timeout_ms",
436 Type: "int",
437 Default: fmt.Sprintf("%d", defaults.Workspace.ResizeMode.TimeoutMilliseconds),
438 Description: "Resize mode timeout in milliseconds (0 = no timeout)",
439 Range: ">=0",
440 Section: SectionWorkspace,
441 },
442 {
443 Key: "workspace.resize_mode.step_percent",
444 Type: "float64",
445 Default: fmt.Sprintf("%.1f", defaults.Workspace.ResizeMode.StepPercent),
446 Description: "Percentage to resize panes per step",
447 Section: SectionWorkspace,
448 },
449 {
450 Key: "workspace.resize_mode.min_pane_percent",
451 Type: "float64",
452 Default: fmt.Sprintf("%.1f", defaults.Workspace.ResizeMode.MinPanePercent),
453 Description: "Minimum pane size as percentage",
454 Section: SectionWorkspace,
455 },
456 {
457 Key: "workspace.resize_mode.actions.<action>",
458 Type: "[]string",
459 Default: "(see defaults)",
460 Description: "Key bindings for resize mode actions",
461 Section: SectionWorkspace,
462 },
463 // Global shortcuts - managed via dedicated keybindings UI
464 {
465 Key: "workspace.shortcuts.actions",
466 Type: "object",
467 Default: "(see defaults)",
468 Description: "Global keyboard shortcuts (use Keybindings tab to edit)",
469 Section: SectionWorkspace,
470 },
471 // Floating pane
472 {
473 Key: "workspace.floating_pane.width_pct",
474 Type: "float64",
475 Default: fmt.Sprintf("%.2f", defaults.Workspace.FloatingPane.WidthPct),
476 Description: "Floating pane width as a fraction of workspace width",
477 Range: "(0,1]",
478 Section: SectionWorkspace,
479 },
480 {
481 Key: "workspace.floating_pane.height_pct",
482 Type: "float64",
483 Default: fmt.Sprintf("%.2f", defaults.Workspace.FloatingPane.HeightPct),
484 Description: "Floating pane height as a fraction of workspace height",
485 Range: "(0,1]",
486 Section: SectionWorkspace,
487 },
488 {
489 Key: "workspace.floating_pane.profiles.<name>",
490 Type: "object",
491 Default: "(empty)",
492 Description: "Named floating pane URL profiles with keys, url, and optional desc",
493 Section: SectionWorkspace,
494 },
495 // Browsing contexts
496 {
497 Key: "workspace.browsing_contexts.behavior",
498 Type: "string",
499 Default: string(defaults.Workspace.BrowsingContexts.Behavior),
500 Description: "How to handle JavaScript popup windows",
501 Values: []string{"split", "stacked", "tabbed", "windowed"},
502 Section: SectionWorkspace,
503 },
504 {
505 Key: "workspace.browsing_contexts.placement",
506 Type: "string",
507 Default: defaults.Workspace.BrowsingContexts.Placement,
508 Description: "Placement direction for split popups",
509 Values: []string{"right", "left", "top", "bottom"},
510 Section: SectionWorkspace,
511 },
512 {
513 Key: "workspace.browsing_contexts.blank_target_behavior",
514 Type: "string",
515 Default: defaults.Workspace.BrowsingContexts.BlankTargetBehavior,
516 Description: "How to handle target=\"_blank\" links",
517 Values: []string{"split", "stacked", "tabbed"},
518 Section: SectionWorkspace,
519 },
520 {
521 Key: "workspace.browsing_contexts.open_in_new_pane",
522 Type: "bool",
523 Default: fmt.Sprintf("%t", defaults.Workspace.BrowsingContexts.OpenInNewPane),
524 Description: "Open popups in workspace instead of blocking",
525 Section: SectionWorkspace,
526 },
527 {
528 Key: "workspace.browsing_contexts.follow_pane_context",
529 Type: "bool",
530 Default: fmt.Sprintf("%t", defaults.Workspace.BrowsingContexts.FollowPaneContext),
531 Description: "Popup placement follows parent pane context",
532 Section: SectionWorkspace,
533 },
534 {
535 Key: "workspace.browsing_contexts.enable_smart_detection",
536 Type: "bool",
537 Default: fmt.Sprintf("%t", defaults.Workspace.BrowsingContexts.EnableSmartDetection),
538 Description: "Use window properties to detect popup intent",
539 Section: SectionWorkspace,
540 },
541 {
542 Key: "workspace.browsing_contexts.oauth_auto_close",
543 Type: "bool",
544 Default: fmt.Sprintf("%t", defaults.Workspace.BrowsingContexts.OAuthAutoClose),
545 Description: "Auto-close OAuth popups after success",
546 Section: SectionWorkspace,
547 },
548 // Styling
549 {
550 Key: "workspace.styling.border_width",
551 Type: "int",
552 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.BorderWidth),
553 Description: "Active pane border width in pixels",
554 Range: ">=0",
555 Section: SectionWorkspace,
556 },
557 {
558 Key: "workspace.styling.border_color",
559 Type: "string",
560 Default: defaults.Workspace.Styling.BorderColor,
561 Description: "Active pane border color (CSS or @theme variable)",
562 Section: SectionWorkspace,
563 },
564 {
565 Key: "workspace.styling.mode_border_width",
566 Type: "int",
567 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.ModeBorderWidth),
568 Description: "Border width for modal mode indicators",
569 Range: ">=0",
570 Section: SectionWorkspace,
571 },
572 {
573 Key: "workspace.styling.pane_mode_color",
574 Type: "string",
575 Default: defaults.Workspace.Styling.PaneModeColor,
576 Description: "Color for pane mode indicator",
577 Section: SectionWorkspace,
578 },
579 {
580 Key: "workspace.styling.tab_mode_color",
581 Type: "string",
582 Default: defaults.Workspace.Styling.TabModeColor,
583 Description: "Color for tab mode indicator",
584 Section: SectionWorkspace,
585 },
586 {
587 Key: "workspace.styling.session_mode_color",
588 Type: "string",
589 Default: defaults.Workspace.Styling.SessionModeColor,
590 Description: "Color for session mode indicator",
591 Section: SectionWorkspace,
592 },
593 {
594 Key: "workspace.styling.resize_mode_color",
595 Type: "string",
596 Default: defaults.Workspace.Styling.ResizeModeColor,
597 Description: "Color for resize mode indicator",
598 Section: SectionWorkspace,
599 },
600 {
601 Key: "workspace.styling.mode_indicator_toaster_enabled",
602 Type: "bool",
603 Default: fmt.Sprintf("%t", defaults.Workspace.Styling.ModeIndicatorToasterEnabled),
604 Description: "Show toaster notification for modal modes",
605 Section: SectionWorkspace,
606 },
607 {
608 Key: "workspace.styling.transition_duration",
609 Type: "int",
610 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.TransitionDuration),
611 Description: "Border animation duration in milliseconds",
612 Range: ">=0",
613 Section: SectionWorkspace,
614 },
615 }
616}
617
618func (*SchemaProvider) getSessionKeys(defaults *Config) []entity.ConfigKeyInfo {
619 return []entity.ConfigKeyInfo{
620 {
621 Key: "session.auto_restore",
622 Type: "bool",
623 Default: fmt.Sprintf("%t", defaults.Session.AutoRestore),
624 Description: "Automatically restore last session on startup",
625 Section: SectionSession,
626 },
627 {
628 Key: "session.snapshot_interval_ms",
629 Type: "int",
630 Default: fmt.Sprintf("%d", defaults.Session.SnapshotIntervalMs),
631 Description: "Minimum interval between session snapshots",
632 Range: ">=0",
633 Section: SectionSession,
634 },
635 {
636 Key: "session.max_exited_sessions",
637 Type: "int",
638 Default: fmt.Sprintf("%d", defaults.Session.MaxExitedSessions),
639 Description: "Maximum number of exited sessions to keep",
640 Range: ">=0",
641 Section: SectionSession,
642 },
643 {
644 Key: "session.max_exited_session_age_days",
645 Type: "int",
646 Default: fmt.Sprintf("%d", defaults.Session.MaxExitedSessionAgeDays),
647 Description: "Maximum age of exited sessions in days",
648 Range: ">=0",
649 Section: SectionSession,
650 },
651 {
652 Key: "session.session_mode.activation_shortcut",
653 Type: "string",
654 Default: defaults.Session.SessionMode.ActivationShortcut,
655 Description: "Shortcut to enter session mode",
656 Section: SectionSession,
657 },
658 {
659 Key: "session.session_mode.timeout_ms",
660 Type: "int",
661 Default: fmt.Sprintf("%d", defaults.Session.SessionMode.TimeoutMilliseconds),
662 Description: "Session mode timeout in milliseconds",
663 Range: ">=0",
664 Section: SectionSession,
665 },
666 {
667 Key: "session.session_mode.actions.<action>",
668 Type: "[]string",
669 Default: "(see defaults)",
670 Description: "Key bindings for session mode actions",
671 Section: SectionSession,
672 },
673 }
674}
675
676func (*SchemaProvider) getOmniboxKeys(defaults *Config) []entity.ConfigKeyInfo {
677 return []entity.ConfigKeyInfo{
678 {
679 Key: "omnibox.initial_behavior",
680 Type: "string",
681 Default: string(defaults.Omnibox.InitialBehavior),
682 Description: "What to show when omnibox opens with empty input",
683 Values: []string{
684 string(OmniboxInitialBehaviorRecent),
685 string(OmniboxInitialBehaviorMostVisited),
686 string(OmniboxInitialBehaviorNone),
687 },
688 Section: SectionOmnibox,
689 },
690 {
691 Key: "omnibox.most_visited_days",
692 Type: "int",
693 Default: fmt.Sprintf("%d", defaults.Omnibox.MostVisitedDays),
694 Description: "Days of history to consider for most_visited (0 = all history)",
695 Range: ">=0",
696 Section: SectionOmnibox,
697 },
698 {
699 Key: "omnibox.auto_open_on_new_pane",
700 Type: "bool",
701 Default: fmt.Sprintf("%t", defaults.Omnibox.AutoOpenOnNewPane),
702 Description: "Auto-open omnibox when creating new pane",
703 Section: SectionOmnibox,
704 },
705 }
706}
707
708func (*SchemaProvider) getContentFilteringKeys(defaults *Config) []entity.ConfigKeyInfo {
709 return []entity.ConfigKeyInfo{
710 {
711 Key: "content_filtering.enabled",
712 Type: "bool",
713 Default: fmt.Sprintf("%t", defaults.ContentFiltering.Enabled),
714 Description: "Enable ad blocking and content filtering",
715 Section: SectionContentFiltering,
716 },
717 {
718 Key: "content_filtering.auto_update",
719 Type: "bool",
720 Default: fmt.Sprintf("%t", defaults.ContentFiltering.AutoUpdate),
721 Description: "Auto-update filter lists from GitHub",
722 Section: SectionContentFiltering,
723 },
724 }
725}
726
727func (*SchemaProvider) getClipboardKeys(defaults *Config) []entity.ConfigKeyInfo {
728 return []entity.ConfigKeyInfo{
729 {
730 Key: "clipboard.auto_copy_on_selection",
731 Type: "bool",
732 Default: fmt.Sprintf("%t", defaults.Clipboard.AutoCopyOnSelection),
733 Description: "Auto-copy selected text to clipboard (zellij-style)",
734 Section: SectionClipboard,
735 },
736 }
737}
738
739func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {
740 return []entity.ConfigKeyInfo{
741 {
742 Key: "engine.webkit.gsk_renderer",
743 Type: "string",
744 Default: string(defaults.Engine.WebKit.GSKRenderer),
745 Description: "GTK Scene Kit renderer selection",
746 Values: []string{"auto", "opengl", "vulkan", "cairo"},
747 Section: SectionRendering,
748 },
749 {
750 Key: "engine.webkit.disable_dmabuf_renderer",
751 Type: "bool",
752 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableDMABufRenderer),
753 Description: "Disable DMA-BUF accelerated renderer",
754 Section: SectionRendering,
755 },
756 {
757 Key: "engine.webkit.force_compositing_mode",
758 Type: "bool",
759 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ForceCompositingMode),
760 Description: "Force accelerated compositing",
761 Section: SectionRendering,
762 },
763 {
764 Key: "engine.webkit.disable_compositing_mode",
765 Type: "bool",
766 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableCompositingMode),
767 Description: "Disable accelerated compositing",
768 Section: SectionRendering,
769 },
770 {
771 Key: "engine.webkit.disable_mipmaps",
772 Type: "bool",
773 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DisableMipmaps),
774 Description: "Disable mipmap generation",
775 Section: SectionRendering,
776 },
777 {
778 Key: "engine.webkit.prefer_gl",
779 Type: "bool",
780 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.PreferGL),
781 Description: "Force OpenGL over GLES",
782 Section: SectionRendering,
783 },
784 {
785 Key: "engine.webkit.draw_compositing_indicators",
786 Type: "bool",
787 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DrawCompositingIndicators),
788 Description: "Show compositing layer borders",
789 Section: SectionRendering,
790 },
791 {
792 Key: "engine.webkit.show_fps",
793 Type: "bool",
794 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.ShowFPS),
795 Description: "Display WebKit FPS counter",
796 Section: SectionRendering,
797 },
798 {
799 Key: "engine.webkit.sample_memory",
800 Type: "bool",
801 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SampleMemory),
802 Description: "Enable memory sampling",
803 Section: SectionRendering,
804 },
805 {
806 Key: "engine.webkit.debug_frames",
807 Type: "bool",
808 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.DebugFrames),
809 Description: "Enable frame timing debug output",
810 Section: SectionRendering,
811 },
812 }
813}
814
815func (*SchemaProvider) getMediaKeys(defaults *Config) []entity.ConfigKeyInfo {
816 return []entity.ConfigKeyInfo{
817 {
818 Key: "media.hardware_decoding",
819 Type: "string",
820 Default: string(defaults.Media.HardwareDecodingMode),
821 Description: "Hardware vs software video decoding",
822 Values: []string{"auto", "force", "disable"},
823 Section: SectionMedia,
824 },
825 {
826 Key: "media.prefer_av1",
827 Type: "bool",
828 Default: fmt.Sprintf("%t", defaults.Media.PreferAV1),
829 Description: "Prioritize AV1 codec when available",
830 Section: SectionMedia,
831 },
832 {
833 Key: "media.show_diagnostics",
834 Type: "bool",
835 Default: fmt.Sprintf("%t", defaults.Media.ShowDiagnosticsOnStartup),
836 Description: "Show media capability warnings at startup",
837 Section: SectionMedia,
838 },
839 }
840}
841
842func (*SchemaProvider) getUpdateKeys(defaults *Config) []entity.ConfigKeyInfo {
843 return []entity.ConfigKeyInfo{
844 {
845 Key: "update.enable_on_startup",
846 Type: "bool",
847 Default: fmt.Sprintf("%t", defaults.Update.EnableOnStartup),
848 Description: "Check for updates on browser startup",
849 Section: SectionUpdate,
850 },
851 {
852 Key: "update.auto_download",
853 Type: "bool",
854 Default: fmt.Sprintf("%t", defaults.Update.AutoDownload),
855 Description: "Auto-download updates in background",
856 Section: SectionUpdate,
857 },
858 {
859 Key: "update.notify_on_new_settings",
860 Type: "bool",
861 Default: fmt.Sprintf("%t", defaults.Update.NotifyOnNewSettings),
862 Description: "Show toast when new config settings available",
863 Section: SectionUpdate,
864 },
865 }
866}
867
868func (*SchemaProvider) getDebugKeys(defaults *Config) []entity.ConfigKeyInfo {
869 return []entity.ConfigKeyInfo{
870 {
871 Key: "debug.enable_devtools",
872 Type: "bool",
873 Default: fmt.Sprintf("%t", defaults.Debug.EnableDevTools),
874 Description: "Enable browser developer tools (F12)",
875 Section: SectionDebug,
876 },
877 {
878 Key: "engine.cef.log_file",
879 Type: "string",
880 Default: defaults.Engine.CEF.LogFile,
881 Description: "Path to the CEF runtime log file written during startup and browser process execution",
882 Section: SectionDebug,
883 },
884 {
885 Key: "engine.cef.log_severity",
886 Type: "int32",
887 Default: fmt.Sprintf("%d", defaults.Engine.CEF.LogSeverity),
888 Description: "CEF internal log verbosity (0=default, 1=verbose, 2=info, 3=warning, 4=error, 99=disable)",
889 Section: SectionDebug,
890 },
891 {
892 Key: "engine.cef.trace_handlers",
893 Type: "bool",
894 Default: fmt.Sprintf("%t", defaults.Engine.CEF.TraceHandlers),
895 Description: "Trace purego-cef handler wrapping and refcount activity",
896 Section: SectionDebug,
897 },
898 {
899 Key: "engine.cef.enable_audio_handler",
900 Type: "bool",
901 Default: fmt.Sprintf("%t", defaults.Engine.CEF.EnableAudioHandler),
902 Description: "Enable the experimental CEF audio handler bridge",
903 Section: SectionDebug,
904 },
905 }
906}
907
908func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {
909 return []entity.ConfigKeyInfo{
910 {
911 Key: "engine.profile",
912 Type: "string",
913 Default: string(defaults.Engine.Profile),
914 Description: "Performance profile preset (custom enables manual tuning)",
915 Values: []string{"default", "lite", "balanced", "max", "custom"},
916 Section: SectionPerformance,
917 },
918 {
919 Key: "engine.zoom_cache_size",
920 Type: "int",
921 Default: fmt.Sprintf("%d", defaults.Engine.ZoomCacheSize),
922 Description: "Number of domain zoom levels to cache",
923 Range: ">=0",
924 Section: SectionPerformance,
925 },
926 {
927 Key: "engine.pool_prewarm_count",
928 Type: "int",
929 Default: fmt.Sprintf("%d", defaults.Engine.PoolPrewarmCount),
930 Description: "WebViews to pre-create at startup",
931 Range: ">=0",
932 Section: SectionPerformance,
933 },
934 {
935 Key: "engine.cef.render_stack",
936 Type: "string",
937 Default: string(defaults.Engine.CEF.CEFRenderStack()),
938 Description: "CEF GPU render stack",
939 Values: []string{"vulkan", "egl"},
940 Section: SectionPerformance,
941 },
942 {
943 Key: "engine.cef.adaptive_windowless_frame_rate",
944 Type: "bool",
945 Default: fmt.Sprintf("%t", defaults.Engine.CEF.AdaptiveWindowlessFrameRate),
946 Description: "Adapt CEF OSR frame rate to the active Wayland monitor refresh rate when no explicit windowless_frame_rate is set",
947 Section: SectionPerformance,
948 },
949 {
950 Key: "engine.cef.windowless_frame_rate",
951 Type: "int32",
952 Default: fmt.Sprintf("%d", defaults.Engine.CEF.WindowlessFrameRate),
953 Description: "Explicit maximum off-screen frame rate for the CEF engine; 0 enables adaptive mode when configured",
954 Range: ">=0",
955 Section: SectionPerformance,
956 },
957 {
958 Key: "engine.cef.windowless_frame_rate_max",
959 Type: "int32",
960 Default: fmt.Sprintf("%d", defaults.Engine.CEF.CEFWindowlessFrameRateMax()),
961 Description: "Hard cap for adaptive CEF OSR frame rate; 0 uses the built-in default cap",
962 Range: ">=0",
963 Section: SectionPerformance,
964 },
965 {
966 Key: "engine.webkit.skia_cpu_painting_threads",
967 Type: "int",
968 Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaCPUPaintingThreads),
969 Description: "Skia CPU rendering threads (0=unset, custom profile only)",
970 Range: ">=0",
971 Section: SectionPerformance,
972 },
973 {
974 Key: "engine.webkit.skia_gpu_painting_threads",
975 Type: "int",
976 Default: fmt.Sprintf("%d", defaults.Engine.WebKit.SkiaGPUPaintingThreads),
977 Description: "Skia GPU rendering threads (-1=unset, 0=disable, custom profile only)",
978 Range: ">=-1",
979 Section: SectionPerformance,
980 },
981 {
982 Key: "engine.webkit.skia_enable_cpu_rendering",
983 Type: "bool",
984 Default: fmt.Sprintf("%t", defaults.Engine.WebKit.SkiaEnableCPURendering),
985 Description: "Force CPU rendering (custom profile only)",
986 Section: SectionPerformance,
987 },
988 {
989 Key: "engine.webkit.web_process_memory_limit_mb",
990 Type: "int",
991 Default: fmt.Sprintf("%d", defaults.Engine.WebKit.WebProcessMemoryLimitMB),
992 Description: "Web process memory limit in MB (0=unset, custom profile only)",
993 Range: ">=0",
994 Section: SectionPerformance,
995 },
996 {
997 Key: "engine.webkit.web_process_memory_poll_interval_sec",
998 Type: "float64",
999 Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.WebProcessMemoryPollIntervalSec),
1000 Description: "Memory check interval in seconds (0=default 30s, custom profile only)",
1001 Range: ">=0",
1002 Section: SectionPerformance,
1003 },
1004 {
1005 Key: "engine.webkit.web_process_memory_conservative_threshold",
1006 Type: "float64",
1007 Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryConservativeThreshold),
1008 Description: "Conservative memory cleanup threshold (0-1, custom profile only)",
1009 Range: "0-1",
1010 Section: SectionPerformance,
1011 },
1012 {
1013 Key: "engine.webkit.web_process_memory_strict_threshold",
1014 Type: "float64",
1015 Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.WebProcessMemoryStrictThreshold),
1016 Description: "Strict memory cleanup threshold (0-1, custom profile only)",
1017 Range: "0-1",
1018 Section: SectionPerformance,
1019 },
1020 {
1021 Key: "engine.webkit.network_process_memory_limit_mb",
1022 Type: "int",
1023 Default: fmt.Sprintf("%d", defaults.Engine.WebKit.NetworkProcessMemoryLimitMB),
1024 Description: "Network process memory limit in MB (0=unset, custom profile only)",
1025 Range: ">=0",
1026 Section: SectionPerformance,
1027 },
1028 {
1029 Key: "engine.webkit.network_process_memory_poll_interval_sec",
1030 Type: "float64",
1031 Default: fmt.Sprintf("%.1f", defaults.Engine.WebKit.NetworkProcessMemoryPollIntervalSec),
1032 Description: "Network memory check interval in seconds (custom profile only)",
1033 Range: ">=0",
1034 Section: SectionPerformance,
1035 },
1036 {
1037 Key: "engine.webkit.network_process_memory_conservative_threshold",
1038 Type: "float64",
1039 Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryConservativeThreshold),
1040 Description: "Network conservative memory threshold (0-1, custom profile only)",
1041 Range: "0-1",
1042 Section: SectionPerformance,
1043 },
1044 {
1045 Key: "engine.webkit.network_process_memory_strict_threshold",
1046 Type: "float64",
1047 Default: fmt.Sprintf("%.2f", defaults.Engine.WebKit.NetworkProcessMemoryStrictThreshold),
1048 Description: "Network strict memory threshold (0-1, custom profile only)",
1049 Range: "0-1",
1050 Section: SectionPerformance,
1051 },
1052 }
1053}
1054
1055func (*SchemaProvider) getRuntimeKeys(defaults *Config) []entity.ConfigKeyInfo {
1056 return []entity.ConfigKeyInfo{
1057 {
1058 Key: "engine.cef.cef_dir",
1059 Type: "string",
1060 Default: defaults.Engine.CEF.CEFDir,
1061 Description: "Optional CEF runtime directory containing libcef.so and Resources; overrides the default " +
1062 "runtime discovery order (CEF_DIR env, /usr/lib/cef, ~/.local/share/cef)",
1063 Section: SectionRuntime,
1064 },
1065 {
1066 Key: "engine.webkit.prefix",
1067 Type: "string",
1068 Default: defaults.Engine.WebKit.Prefix,
1069 Description: "Custom runtime prefix for WebKitGTK/GTK",
1070 Section: SectionRuntime,
1071 },
1072 }
1073}
1074
1075func (*SchemaProvider) getDatabaseKeys() []entity.ConfigKeyInfo {
1076 // Get actual default path for display
1077 dbPath := "$XDG_DATA_HOME/" + appName + "/" + databaseName
1078 if path, err := GetDatabaseFile(); err == nil {
1079 dbPath = path
1080 }
1081
1082 return []entity.ConfigKeyInfo{
1083 {
1084 Key: "database.path",
1085 Type: "string",
1086 Default: dbPath,
1087 Description: "Path to SQLite database file",
1088 Section: SectionDatabase,
1089 },
1090 }
1091}
1092
1093func (*SchemaProvider) getDownloadsKeys(_ *Config) []entity.ConfigKeyInfo {
1094 return []entity.ConfigKeyInfo{
1095 {
1096 Key: "downloads.path",
1097 Type: "string",
1098 Default: "(empty = $XDG_DOWNLOAD_DIR or ~/Downloads)",
1099 Description: "Directory where downloads are saved",
1100 Section: SectionDownloads,
1101 },
1102 }
1103}