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