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