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