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 - managed via dedicated keybindings UI
449 {
450 Key: "workspace.shortcuts.actions",
451 Type: "object",
452 Default: "(see defaults)",
453 Description: "Global keyboard shortcuts (use Keybindings tab to edit)",
454 Section: SectionWorkspace,
455 },
456 // Popups
457 {
458 Key: "workspace.popups.behavior",
459 Type: "string",
460 Default: string(defaults.Workspace.Popups.Behavior),
461 Description: "How to handle JavaScript popup windows",
462 Values: []string{"split", "stacked", "tabbed", "windowed"},
463 Section: SectionWorkspace,
464 },
465 {
466 Key: "workspace.popups.placement",
467 Type: "string",
468 Default: defaults.Workspace.Popups.Placement,
469 Description: "Placement direction for split popups",
470 Values: []string{"right", "left", "top", "bottom"},
471 Section: SectionWorkspace,
472 },
473 {
474 Key: "workspace.popups.blank_target_behavior",
475 Type: "string",
476 Default: defaults.Workspace.Popups.BlankTargetBehavior,
477 Description: "How to handle target=\"_blank\" links",
478 Values: []string{"split", "stacked", "tabbed"},
479 Section: SectionWorkspace,
480 },
481 {
482 Key: "workspace.popups.open_in_new_pane",
483 Type: "bool",
484 Default: fmt.Sprintf("%t", defaults.Workspace.Popups.OpenInNewPane),
485 Description: "Open popups in workspace instead of blocking",
486 Section: SectionWorkspace,
487 },
488 {
489 Key: "workspace.popups.follow_pane_context",
490 Type: "bool",
491 Default: fmt.Sprintf("%t", defaults.Workspace.Popups.FollowPaneContext),
492 Description: "Popup placement follows parent pane context",
493 Section: SectionWorkspace,
494 },
495 {
496 Key: "workspace.popups.enable_smart_detection",
497 Type: "bool",
498 Default: fmt.Sprintf("%t", defaults.Workspace.Popups.EnableSmartDetection),
499 Description: "Use window properties to detect popup intent",
500 Section: SectionWorkspace,
501 },
502 {
503 Key: "workspace.popups.oauth_auto_close",
504 Type: "bool",
505 Default: fmt.Sprintf("%t", defaults.Workspace.Popups.OAuthAutoClose),
506 Description: "Auto-close OAuth popups after success",
507 Section: SectionWorkspace,
508 },
509 // Styling
510 {
511 Key: "workspace.styling.border_width",
512 Type: "int",
513 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.BorderWidth),
514 Description: "Active pane border width in pixels",
515 Range: ">=0",
516 Section: SectionWorkspace,
517 },
518 {
519 Key: "workspace.styling.border_color",
520 Type: "string",
521 Default: defaults.Workspace.Styling.BorderColor,
522 Description: "Active pane border color (CSS or @theme variable)",
523 Section: SectionWorkspace,
524 },
525 {
526 Key: "workspace.styling.mode_border_width",
527 Type: "int",
528 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.ModeBorderWidth),
529 Description: "Border width for modal mode indicators",
530 Range: ">=0",
531 Section: SectionWorkspace,
532 },
533 {
534 Key: "workspace.styling.pane_mode_color",
535 Type: "string",
536 Default: defaults.Workspace.Styling.PaneModeColor,
537 Description: "Color for pane mode indicator",
538 Section: SectionWorkspace,
539 },
540 {
541 Key: "workspace.styling.tab_mode_color",
542 Type: "string",
543 Default: defaults.Workspace.Styling.TabModeColor,
544 Description: "Color for tab mode indicator",
545 Section: SectionWorkspace,
546 },
547 {
548 Key: "workspace.styling.session_mode_color",
549 Type: "string",
550 Default: defaults.Workspace.Styling.SessionModeColor,
551 Description: "Color for session mode indicator",
552 Section: SectionWorkspace,
553 },
554 {
555 Key: "workspace.styling.resize_mode_color",
556 Type: "string",
557 Default: defaults.Workspace.Styling.ResizeModeColor,
558 Description: "Color for resize mode indicator",
559 Section: SectionWorkspace,
560 },
561 {
562 Key: "workspace.styling.mode_indicator_toaster_enabled",
563 Type: "bool",
564 Default: fmt.Sprintf("%t", defaults.Workspace.Styling.ModeIndicatorToasterEnabled),
565 Description: "Show toaster notification for modal modes",
566 Section: SectionWorkspace,
567 },
568 {
569 Key: "workspace.styling.transition_duration",
570 Type: "int",
571 Default: fmt.Sprintf("%d", defaults.Workspace.Styling.TransitionDuration),
572 Description: "Border animation duration in milliseconds",
573 Range: ">=0",
574 Section: SectionWorkspace,
575 },
576 }
577}
578
579func (*SchemaProvider) getSessionKeys(defaults *Config) []entity.ConfigKeyInfo {
580 return []entity.ConfigKeyInfo{
581 {
582 Key: "session.auto_restore",
583 Type: "bool",
584 Default: fmt.Sprintf("%t", defaults.Session.AutoRestore),
585 Description: "Automatically restore last session on startup",
586 Section: SectionSession,
587 },
588 {
589 Key: "session.snapshot_interval_ms",
590 Type: "int",
591 Default: fmt.Sprintf("%d", defaults.Session.SnapshotIntervalMs),
592 Description: "Minimum interval between session snapshots",
593 Range: ">=0",
594 Section: SectionSession,
595 },
596 {
597 Key: "session.max_exited_sessions",
598 Type: "int",
599 Default: fmt.Sprintf("%d", defaults.Session.MaxExitedSessions),
600 Description: "Maximum number of exited sessions to keep",
601 Range: ">=0",
602 Section: SectionSession,
603 },
604 {
605 Key: "session.max_exited_session_age_days",
606 Type: "int",
607 Default: fmt.Sprintf("%d", defaults.Session.MaxExitedSessionAgeDays),
608 Description: "Maximum age of exited sessions in days",
609 Range: ">=0",
610 Section: SectionSession,
611 },
612 {
613 Key: "session.session_mode.activation_shortcut",
614 Type: "string",
615 Default: defaults.Session.SessionMode.ActivationShortcut,
616 Description: "Shortcut to enter session mode",
617 Section: SectionSession,
618 },
619 {
620 Key: "session.session_mode.timeout_ms",
621 Type: "int",
622 Default: fmt.Sprintf("%d", defaults.Session.SessionMode.TimeoutMilliseconds),
623 Description: "Session mode timeout in milliseconds",
624 Range: ">=0",
625 Section: SectionSession,
626 },
627 {
628 Key: "session.session_mode.actions.<action>",
629 Type: "[]string",
630 Default: "(see defaults)",
631 Description: "Key bindings for session mode actions",
632 Section: SectionSession,
633 },
634 }
635}
636
637func (*SchemaProvider) getOmniboxKeys(defaults *Config) []entity.ConfigKeyInfo {
638 return []entity.ConfigKeyInfo{
639 {
640 Key: "omnibox.initial_behavior",
641 Type: "string",
642 Default: defaults.Omnibox.InitialBehavior,
643 Description: "What to show when omnibox opens with empty input",
644 Values: []string{"recent", "most_visited", "none"},
645 Section: SectionOmnibox,
646 },
647 {
648 Key: "omnibox.auto_open_on_new_pane",
649 Type: "bool",
650 Default: fmt.Sprintf("%t", defaults.Omnibox.AutoOpenOnNewPane),
651 Description: "Auto-open omnibox when creating new pane",
652 Section: SectionOmnibox,
653 },
654 }
655}
656
657func (*SchemaProvider) getContentFilteringKeys(defaults *Config) []entity.ConfigKeyInfo {
658 return []entity.ConfigKeyInfo{
659 {
660 Key: "content_filtering.enabled",
661 Type: "bool",
662 Default: fmt.Sprintf("%t", defaults.ContentFiltering.Enabled),
663 Description: "Enable ad blocking and content filtering",
664 Section: SectionContentFiltering,
665 },
666 {
667 Key: "content_filtering.auto_update",
668 Type: "bool",
669 Default: fmt.Sprintf("%t", defaults.ContentFiltering.AutoUpdate),
670 Description: "Auto-update filter lists from GitHub",
671 Section: SectionContentFiltering,
672 },
673 }
674}
675
676func (*SchemaProvider) getClipboardKeys(defaults *Config) []entity.ConfigKeyInfo {
677 return []entity.ConfigKeyInfo{
678 {
679 Key: "clipboard.auto_copy_on_selection",
680 Type: "bool",
681 Default: fmt.Sprintf("%t", defaults.Clipboard.AutoCopyOnSelection),
682 Description: "Auto-copy selected text to clipboard (zellij-style)",
683 Section: SectionClipboard,
684 },
685 }
686}
687
688func (*SchemaProvider) getRenderingKeys(defaults *Config) []entity.ConfigKeyInfo {
689 return []entity.ConfigKeyInfo{
690 {
691 Key: "rendering.mode",
692 Type: "string",
693 Default: string(defaults.Rendering.Mode),
694 Description: "GPU/CPU rendering selection for WebKit",
695 Values: []string{"auto", "gpu", "cpu"},
696 Section: SectionRendering,
697 },
698 {
699 Key: "rendering.gsk_renderer",
700 Type: "string",
701 Default: string(defaults.Rendering.GSKRenderer),
702 Description: "GTK Scene Kit renderer selection",
703 Values: []string{"auto", "opengl", "vulkan", "cairo"},
704 Section: SectionRendering,
705 },
706 {
707 Key: "rendering.disable_dmabuf_renderer",
708 Type: "bool",
709 Default: fmt.Sprintf("%t", defaults.Rendering.DisableDMABufRenderer),
710 Description: "Disable DMA-BUF accelerated renderer",
711 Section: SectionRendering,
712 },
713 {
714 Key: "rendering.force_compositing_mode",
715 Type: "bool",
716 Default: fmt.Sprintf("%t", defaults.Rendering.ForceCompositingMode),
717 Description: "Force accelerated compositing",
718 Section: SectionRendering,
719 },
720 {
721 Key: "rendering.disable_compositing_mode",
722 Type: "bool",
723 Default: fmt.Sprintf("%t", defaults.Rendering.DisableCompositingMode),
724 Description: "Disable accelerated compositing",
725 Section: SectionRendering,
726 },
727 {
728 Key: "rendering.disable_mipmaps",
729 Type: "bool",
730 Default: fmt.Sprintf("%t", defaults.Rendering.DisableMipmaps),
731 Description: "Disable mipmap generation",
732 Section: SectionRendering,
733 },
734 {
735 Key: "rendering.prefer_gl",
736 Type: "bool",
737 Default: fmt.Sprintf("%t", defaults.Rendering.PreferGL),
738 Description: "Force OpenGL over GLES",
739 Section: SectionRendering,
740 },
741 {
742 Key: "rendering.draw_compositing_indicators",
743 Type: "bool",
744 Default: fmt.Sprintf("%t", defaults.Rendering.DrawCompositingIndicators),
745 Description: "Show compositing layer borders",
746 Section: SectionRendering,
747 },
748 {
749 Key: "rendering.show_fps",
750 Type: "bool",
751 Default: fmt.Sprintf("%t", defaults.Rendering.ShowFPS),
752 Description: "Display WebKit FPS counter",
753 Section: SectionRendering,
754 },
755 {
756 Key: "rendering.sample_memory",
757 Type: "bool",
758 Default: fmt.Sprintf("%t", defaults.Rendering.SampleMemory),
759 Description: "Enable memory sampling",
760 Section: SectionRendering,
761 },
762 {
763 Key: "rendering.debug_frames",
764 Type: "bool",
765 Default: fmt.Sprintf("%t", defaults.Rendering.DebugFrames),
766 Description: "Enable frame timing debug output",
767 Section: SectionRendering,
768 },
769 }
770}
771
772func (*SchemaProvider) getMediaKeys(defaults *Config) []entity.ConfigKeyInfo {
773 return []entity.ConfigKeyInfo{
774 {
775 Key: "media.hardware_decoding",
776 Type: "string",
777 Default: string(defaults.Media.HardwareDecodingMode),
778 Description: "Hardware vs software video decoding",
779 Values: []string{"auto", "force", "disable"},
780 Section: SectionMedia,
781 },
782 {
783 Key: "media.gl_rendering_mode",
784 Type: "string",
785 Default: string(defaults.Media.GLRenderingMode),
786 Description: "OpenGL API selection for video rendering",
787 Values: []string{"auto", "gles2", "gl3", "none"},
788 Section: SectionMedia,
789 },
790 {
791 Key: "media.prefer_av1",
792 Type: "bool",
793 Default: fmt.Sprintf("%t", defaults.Media.PreferAV1),
794 Description: "Prioritize AV1 codec when available",
795 Section: SectionMedia,
796 },
797 {
798 Key: "media.show_diagnostics",
799 Type: "bool",
800 Default: fmt.Sprintf("%t", defaults.Media.ShowDiagnosticsOnStartup),
801 Description: "Show media capability warnings at startup",
802 Section: SectionMedia,
803 },
804 {
805 Key: "media.force_vsync",
806 Type: "bool",
807 Default: fmt.Sprintf("%t", defaults.Media.ForceVSync),
808 Description: "Force vertical sync for video playback",
809 Section: SectionMedia,
810 },
811 {
812 Key: "media.gstreamer_debug_level",
813 Type: "int",
814 Default: fmt.Sprintf("%d", defaults.Media.GStreamerDebugLevel),
815 Description: "GStreamer debug verbosity (0=off, 1-5)",
816 Range: "0-5",
817 Section: SectionMedia,
818 },
819 }
820}
821
822func (*SchemaProvider) getUpdateKeys(defaults *Config) []entity.ConfigKeyInfo {
823 return []entity.ConfigKeyInfo{
824 {
825 Key: "update.enable_on_startup",
826 Type: "bool",
827 Default: fmt.Sprintf("%t", defaults.Update.EnableOnStartup),
828 Description: "Check for updates on browser startup",
829 Section: SectionUpdate,
830 },
831 {
832 Key: "update.auto_download",
833 Type: "bool",
834 Default: fmt.Sprintf("%t", defaults.Update.AutoDownload),
835 Description: "Auto-download updates in background",
836 Section: SectionUpdate,
837 },
838 {
839 Key: "update.notify_on_new_settings",
840 Type: "bool",
841 Default: fmt.Sprintf("%t", defaults.Update.NotifyOnNewSettings),
842 Description: "Show toast when new config settings available",
843 Section: SectionUpdate,
844 },
845 }
846}
847
848func (*SchemaProvider) getDebugKeys(defaults *Config) []entity.ConfigKeyInfo {
849 return []entity.ConfigKeyInfo{
850 {
851 Key: "debug.enable_devtools",
852 Type: "bool",
853 Default: fmt.Sprintf("%t", defaults.Debug.EnableDevTools),
854 Description: "Enable browser developer tools (F12)",
855 Section: SectionDebug,
856 },
857 }
858}
859
860func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyInfo {
861 return []entity.ConfigKeyInfo{
862 {
863 Key: "performance.profile",
864 Type: "string",
865 Default: string(defaults.Performance.Profile),
866 Description: "Performance profile preset (custom enables manual tuning)",
867 Values: []string{"default", "lite", "max", "custom"},
868 Section: SectionPerformance,
869 },
870 {
871 Key: "performance.zoom_cache_size",
872 Type: "int",
873 Default: fmt.Sprintf("%d", defaults.Performance.ZoomCacheSize),
874 Description: "Number of domain zoom levels to cache",
875 Range: ">=0",
876 Section: SectionPerformance,
877 },
878 {
879 Key: "performance.webview_pool_prewarm_count",
880 Type: "int",
881 Default: fmt.Sprintf("%d", defaults.Performance.WebViewPoolPrewarmCount),
882 Description: "WebViews to pre-create at startup",
883 Range: ">=0",
884 Section: SectionPerformance,
885 },
886 {
887 Key: "performance.skia_cpu_painting_threads",
888 Type: "int",
889 Default: fmt.Sprintf("%d", defaults.Performance.SkiaCPUPaintingThreads),
890 Description: "Skia CPU rendering threads (0=unset, custom profile only)",
891 Range: ">=0",
892 Section: SectionPerformance,
893 },
894 {
895 Key: "performance.skia_gpu_painting_threads",
896 Type: "int",
897 Default: fmt.Sprintf("%d", defaults.Performance.SkiaGPUPaintingThreads),
898 Description: "Skia GPU rendering threads (-1=unset, 0=disable, custom profile only)",
899 Range: ">=-1",
900 Section: SectionPerformance,
901 },
902 {
903 Key: "performance.skia_enable_cpu_rendering",
904 Type: "bool",
905 Default: fmt.Sprintf("%t", defaults.Performance.SkiaEnableCPURendering),
906 Description: "Force CPU rendering (custom profile only)",
907 Section: SectionPerformance,
908 },
909 {
910 Key: "performance.web_process_memory_limit_mb",
911 Type: "int",
912 Default: fmt.Sprintf("%d", defaults.Performance.WebProcessMemoryLimitMB),
913 Description: "Web process memory limit in MB (0=unset, custom profile only)",
914 Range: ">=0",
915 Section: SectionPerformance,
916 },
917 {
918 Key: "performance.web_process_memory_poll_interval_sec",
919 Type: "float64",
920 Default: fmt.Sprintf("%.1f", defaults.Performance.WebProcessMemoryPollIntervalSec),
921 Description: "Memory check interval in seconds (0=default 30s, custom profile only)",
922 Range: ">=0",
923 Section: SectionPerformance,
924 },
925 {
926 Key: "performance.web_process_memory_conservative_threshold",
927 Type: "float64",
928 Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryConservativeThreshold),
929 Description: "Conservative memory cleanup threshold (0-1, custom profile only)",
930 Range: "0-1",
931 Section: SectionPerformance,
932 },
933 {
934 Key: "performance.web_process_memory_strict_threshold",
935 Type: "float64",
936 Default: fmt.Sprintf("%.2f", defaults.Performance.WebProcessMemoryStrictThreshold),
937 Description: "Strict memory cleanup threshold (0-1, custom profile only)",
938 Range: "0-1",
939 Section: SectionPerformance,
940 },
941 {
942 Key: "performance.network_process_memory_limit_mb",
943 Type: "int",
944 Default: fmt.Sprintf("%d", defaults.Performance.NetworkProcessMemoryLimitMB),
945 Description: "Network process memory limit in MB (0=unset, custom profile only)",
946 Range: ">=0",
947 Section: SectionPerformance,
948 },
949 {
950 Key: "performance.network_process_memory_poll_interval_sec",
951 Type: "float64",
952 Default: fmt.Sprintf("%.1f", defaults.Performance.NetworkProcessMemoryPollIntervalSec),
953 Description: "Network memory check interval in seconds (custom profile only)",
954 Range: ">=0",
955 Section: SectionPerformance,
956 },
957 {
958 Key: "performance.network_process_memory_conservative_threshold",
959 Type: "float64",
960 Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryConservativeThreshold),
961 Description: "Network conservative memory threshold (0-1, custom profile only)",
962 Range: "0-1",
963 Section: SectionPerformance,
964 },
965 {
966 Key: "performance.network_process_memory_strict_threshold",
967 Type: "float64",
968 Default: fmt.Sprintf("%.2f", defaults.Performance.NetworkProcessMemoryStrictThreshold),
969 Description: "Network strict memory threshold (0-1, custom profile only)",
970 Range: "0-1",
971 Section: SectionPerformance,
972 },
973 }
974}
975
976func (*SchemaProvider) getRuntimeKeys(_ *Config) []entity.ConfigKeyInfo {
977 return []entity.ConfigKeyInfo{
978 {
979 Key: "runtime.prefix",
980 Type: "string",
981 Default: "(empty)",
982 Description: "Custom runtime prefix for WebKitGTK/GTK",
983 Section: SectionRuntime,
984 },
985 }
986}
987
988func (*SchemaProvider) getDatabaseKeys() []entity.ConfigKeyInfo {
989 // Get actual default path for display
990 dbPath := "$XDG_DATA_HOME/" + appName + "/" + databaseName
991 if path, err := GetDatabaseFile(); err == nil {
992 dbPath = path
993 }
994
995 return []entity.ConfigKeyInfo{
996 {
997 Key: "database.path",
998 Type: "string",
999 Default: dbPath,
1000 Description: "Path to SQLite database file",
1001 Section: SectionDatabase,
1002 },
1003 }
1004}
1005
1006func (*SchemaProvider) getDownloadsKeys(_ *Config) []entity.ConfigKeyInfo {
1007 return []entity.ConfigKeyInfo{
1008 {
1009 Key: "downloads.path",
1010 Type: "string",
1011 Default: "(empty = $XDG_DOWNLOAD_DIR or ~/Downloads)",
1012 Description: "Directory where downloads are saved",
1013 Section: SectionDownloads,
1014 },
1015 }
1016}