1package config
2
3import (
4 "os"
5 "path/filepath"
6
7 "github.com/bnema/dumber/internal/domain/entity"
8)
9
10// Default configuration constants
11const (
12 // History defaults
13 defaultMaxHistoryEntries = 10000 // entries
14 defaultRetentionDays = 365 // 1 year
15
16 // Dmenu defaults
17 defaultMaxHistoryDays = 30 // days
18
19 // Logging defaults
20 defaultMaxLogAgeDays = 7 // days
21 defaultMaxLogFiles = 100 // session log files
22
23 // Appearance defaults
24 defaultSidebarWidth = 320 // px, clamped to [280, 380]
25 defaultFontSize = 16 // points
26 defaultExternalThemeProvider = "noctalia"
27 defaultExternalThemeFormat = "colors-json"
28 defaultExternalThemeColorsFilename = "colors.json"
29 defaultExternalThemeTemplateFormat = "dumber-json"
30 defaultExternalThemeTemplateFilename = "noctalia-theme.json"
31
32 // Workspace defaults
33 defaultNewPaneURL = "about:blank"
34
35 // Omnibox defaults
36 defaultOmniboxInitialBehavior = OmniboxInitialBehaviorRecent
37 defaultOmniboxMostVisitedDays = 30
38 defaultOmniboxAutoOpenOnNewPane = false
39
40 // Workspace defaults
41 defaultPaneActivationShortcut = "ctrl+p"
42 defaultPaneTimeoutMilliseconds = 3000
43 defaultTabActivationShortcut = "ctrl+t"
44 defaultTabTimeoutMilliseconds = 3000
45 defaultResizeActivationShortcut = "ctrl+n"
46 defaultResizeTimeoutMilliseconds = 3000
47 defaultPageActivationShortcut = "ctrl+y"
48 defaultPageTimeoutMilliseconds = 0
49 defaultPageSequenceTimeoutMilliseconds = 500
50 defaultResizeStepPercent = 5.0
51 defaultResizeMinPanePercent = 10.0
52 defaultTabBarPosition = "bottom"
53 defaultPopupPlacement = "right"
54 defaultExternalLinkBehavior = ExternalLinkBehaviorWindowed
55 defaultExternalLinkPlacement = ExternalLinkPlacementRight
56 defaultFloatingPaneWidthPct = 0.82
57 defaultFloatingPaneHeightPct = 0.72
58
59 // Session defaults
60 defaultSessionActivationShortcut = "ctrl+o"
61 defaultSessionTimeoutMilliseconds = 3000
62 defaultSnapshotIntervalMs = 5000
63 defaultMaxExitedSessions = 50
64 defaultMaxExitedSessionAgeDays = 7
65
66 // Workspace styling defaults
67 // Active pane border (overlay)
68 defaultBorderWidth = 1
69 defaultBorderColor = "@theme_selected_bg_color"
70
71 // Mode border width (consolidated for all modes)
72 defaultModeBorderWidth = 4
73
74 // Mode colors (used for both borders and toaster)
75 defaultPaneModeColor = "#4A90E2" // Blue for pane mode
76 defaultTabModeColor = "#FFA500" // Orange for tab mode
77 defaultSessionModeColor = "#9B59B6" // Purple for session mode
78 defaultResizeModeColor = "#00D4AA" // Cyan/teal for resize mode
79
80 // Mode indicator toaster
81 defaultModeIndicatorToasterEnabled = true
82
83 // Other styling
84 defaultTransitionDuration = 120
85 defaultUIScale = 1.0 // UI scale multiplier (1.0 = 100%, 1.2 = 120%)
86
87 // Performance defaults
88 defaultZoomCacheSize = 256 // domains to cache (~20KB memory)
89 defaultWebViewPoolPrewarmCount = 4 // WebViews to pre-create at startup
90 defaultCEFWindowlessFrameRate = 60 // static OSR frame rate when adaptive CEF pacing is disabled
91 defaultCEFWindowlessFrameRateMax = 240 // adaptive OSR frame-rate hard cap
92 defaultCEFScrollMultiplier = 1.0
93 defaultCEFScrollPreciseMultiplier = 2.5
94 defaultCEFScrollMaxDelta = 0
95 defaultCEFScrollTouchpadInertia = true
96 defaultCEFScrollWheelSmoothing = true
97 defaultCEFTouchpadNavigation = true
98 // Require a deliberate swipe before history navigation so ordinary
99 // horizontal scrolling is unlikely to cross the commit threshold.
100 defaultCEFTouchpadNavigationDelta = 320.0
101 defaultCEFTouchpadNavigationRatio = 0.5
102
103 // Skia threading defaults (0 = unset, -1 = unset for GPU threads)
104 defaultSkiaCPUPaintingThreads = 0
105 defaultSkiaGPUPaintingThreads = -1 // -1 means unset; 0 would disable GPU tile painting
106)
107
108func defaultBrowsingContextConfig() BrowsingContextConfig {
109 return BrowsingContextConfig{
110 Behavior: PopupBehaviorSplit,
111 Placement: defaultPopupPlacement,
112 OpenInNewPane: true,
113 FollowPaneContext: true,
114 BlankTargetBehavior: "stacked",
115 EnableSmartDetection: true,
116 OAuthAutoClose: true,
117 }
118}
119
120// getDefaultLogDir returns the default log directory, falls back to empty string on error
121func getDefaultLogDir() string {
122 logDir, err := GetLogDir()
123 if err != nil {
124 return ""
125 }
126 return logDir
127}
128
129// getDefaultExternalThemePath returns Noctalia's native colors.json path.
130func getDefaultExternalThemePath() string {
131 return getDefaultExternalThemePathForFormat(defaultExternalThemeFormat)
132}
133
134func getDefaultExternalThemePathForFormat(format string) string {
135 switch format {
136 case defaultExternalThemeTemplateFormat:
137 return getDefaultExternalThemeTemplatePath()
138 default:
139 return getDefaultNoctaliaColorsPath()
140 }
141}
142
143func getDefaultNoctaliaColorsPath() string {
144 configHome, err := os.UserConfigDir()
145 if err != nil {
146 return ""
147 }
148 return filepath.Join(configHome, defaultExternalThemeProvider, defaultExternalThemeColorsFilename)
149}
150
151func getDefaultExternalThemeTemplatePath() string {
152 configDir, err := GetConfigDir()
153 if err != nil {
154 return ""
155 }
156 return filepath.Join(configDir, defaultExternalThemeTemplateFilename)
157}
158
159// DefaultConfig returns the default configuration values for dumber.
160func DefaultConfig() *Config {
161 browsingContextDefaults := defaultBrowsingContextConfig()
162
163 return &Config{
164 Database: DatabaseConfig{
165 // Path is set dynamically in config.Load()
166 },
167 History: HistoryConfig{
168 MaxEntries: defaultMaxHistoryEntries,
169 RetentionPeriodDays: defaultRetentionDays, // 1 year
170 CleanupIntervalDays: 1, // daily cleanup
171 },
172 SearchShortcuts: GetDefaultSearchShortcuts(),
173 DefaultSearchEngine: "https://duckduckgo.com/?q=%s",
174 Dmenu: DmenuConfig{
175 MaxHistoryDays: defaultMaxHistoryDays,
176 ShowVisitCount: true,
177 ShowLastVisited: true,
178 HistoryPrefix: "🕒",
179 ShortcutPrefix: "🔍",
180 URLPrefix: "🌐",
181 DateFormat: "2006-01-02 15:04",
182 SortByVisitCount: true,
183 },
184 Logging: LoggingConfig{
185 Level: "info",
186 Format: "text", // text or json
187 MaxAge: defaultMaxLogAgeDays,
188 MaxFiles: defaultMaxLogFiles,
189 LogDir: getDefaultLogDir(),
190 EnableFileLog: true,
191 CaptureConsole: false, // Disabled by default
192 CaptureGTKLogs: false, // Disabled by default
193 },
194 Appearance: AppearanceConfig{
195 SansFont: "Fira Sans",
196 SerifFont: "Fira Sans",
197 MonospaceFont: "Fira Code",
198 GtkFont: "Adwaita Sans",
199 DefaultFontSize: defaultFontSize,
200 LightPalette: ColorPalette{
201 Background: "#fafafa",
202 Surface: "#f4f4f5",
203 SurfaceVariant: "#e4e4e7",
204 Text: "#18181b",
205 Muted: "#71717a",
206 Accent: "#22c55e", // Green-500 - vibrant primary
207 Border: "#d4d4d8",
208 },
209 DarkPalette: ColorPalette{
210 Background: "#0a0a0b",
211 Surface: "#18181b",
212 SurfaceVariant: "#27272a",
213 Text: "#fafafa",
214 Muted: "#a1a1aa",
215 Accent: "#4ade80", // Green-400 - vibrant primary
216 Border: "#3f3f46",
217 },
218 ColorScheme: "default", // default follows system theme
219 ExternalTheme: entity.ExternalThemeConfig{
220 Enabled: false,
221 Provider: defaultExternalThemeProvider,
222 Format: defaultExternalThemeFormat,
223 Path: getDefaultExternalThemePath(),
224 },
225 },
226 Debug: DebugConfig{
227 EnableDevTools: true,
228 },
229 Engine: EngineConfig{
230 Type: EngineTypeCEF,
231 Profile: ProfileDefault,
232 PoolPrewarmCount: defaultWebViewPoolPrewarmCount,
233 ZoomCacheSize: defaultZoomCacheSize,
234 // With ITP enabled, WebKit ignores ACCEPT_NO_THIRD_PARTY — ITP handles
235 // third-party cookie isolation more intelligently. Using Always + ITP
236 // matches Epiphany's model and avoids a misleading setting.
237 CookiePolicy: CookiePolicyAlways,
238 CEF: CEFEngineConfig{
239 RenderStack: CEFRenderStackVulkan,
240 AdaptiveWindowlessFrameRate: true,
241 WindowlessFrameRate: 0,
242 WindowlessFrameRateMax: defaultCEFWindowlessFrameRateMax,
243 LogFile: "",
244 EnableAudioHandler: true,
245 Input: CEFInputConfig{
246 ScrollWheelMultiplier: defaultCEFScrollMultiplier,
247 ScrollPreciseMultiplier: defaultCEFScrollPreciseMultiplier,
248 ScrollHorizontalMultiplier: defaultCEFScrollMultiplier,
249 ScrollVerticalMultiplier: defaultCEFScrollMultiplier,
250 ScrollMaxDelta: defaultCEFScrollMaxDelta,
251 ScrollTouchpadInertia: defaultCEFScrollTouchpadInertia,
252 ScrollWheelSmoothing: defaultCEFScrollWheelSmoothing,
253 TouchpadNavigationEnabled: defaultCEFTouchpadNavigation,
254 TouchpadNavigationMinDelta: defaultCEFTouchpadNavigationDelta,
255 TouchpadNavigationMaxVerticalRatio: defaultCEFTouchpadNavigationRatio,
256 },
257 },
258 WebKit: WebKitEngineConfig{
259 ITPEnabled: true,
260 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,
261 SkiaGPUPaintingThreads: defaultSkiaGPUPaintingThreads,
262 GSKRenderer: GSKRendererAuto,
263 GLRenderingMode: GLRenderingModeAuto,
264 },
265 },
266 DefaultWebpageZoom: 1.2, // 120% default zoom for better readability
267 DefaultUIScale: defaultUIScale, // 1.0 = 100%, 2.0 = 200%
268 SidebarWidth: defaultSidebarWidth, // 320px, clamped to [280, 380]
269 Workspace: WorkspaceConfig{
270 NewPaneURL: defaultNewPaneURL,
271 SwitchToTabOnMove: true,
272 PaneMode: PaneModeConfig{
273 ActivationShortcut: defaultPaneActivationShortcut,
274 TimeoutMilliseconds: defaultPaneTimeoutMilliseconds,
275 Actions: map[string]ActionBinding{
276 "split-right": {Keys: []string{"arrowright", "r"}, Desc: "Split pane to the right"},
277 "split-left": {Keys: []string{"arrowleft", "l"}, Desc: "Split pane to the left"},
278 "split-up": {Keys: []string{"arrowup", "u"}, Desc: "Split pane upward"},
279 "split-down": {Keys: []string{"arrowdown", "d"}, Desc: "Split pane downward"},
280 "stack-pane": {Keys: []string{"s"}, Desc: "Stack pane with sibling"},
281 "close-pane": {Keys: []string{"x"}, Desc: "Close current pane"},
282 "move-pane-to-tab": {Keys: []string{"m"}, Desc: "Move pane to different tab"},
283 "move-pane-to-next-tab": {Keys: []string{"M", "shift+m"}, Desc: "Move pane to next tab"},
284 "eject-pane-to-window": {Keys: []string{"w"}, Desc: "Eject active pane to a new window"},
285
286 "consume-or-expel-left": {Keys: []string{"["}, Desc: "Consume/expel pane left"},
287 "consume-or-expel-right": {Keys: []string{"]"}, Desc: "Consume/expel pane right"},
288 "consume-or-expel-up": {Keys: []string{"{"}, Desc: "Consume/expel pane up"},
289 "consume-or-expel-down": {Keys: []string{"}"}, Desc: "Consume/expel pane down"},
290
291 "focus-right": {Keys: []string{"shift+arrowright", "shift+l"}, Desc: "Focus pane to the right"},
292 "focus-left": {Keys: []string{"shift+arrowleft", "shift+h"}, Desc: "Focus pane to the left"},
293 "focus-up": {Keys: []string{"shift+arrowup", "shift+k"}, Desc: "Focus pane above"},
294 "focus-down": {Keys: []string{"shift+arrowdown", "shift+j"}, Desc: "Focus pane below"},
295 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
296 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
297 },
298 },
299 TabMode: TabModeConfig{
300 ActivationShortcut: defaultTabActivationShortcut,
301 TimeoutMilliseconds: defaultTabTimeoutMilliseconds,
302 Actions: map[string]ActionBinding{
303 "new-tab": {Keys: []string{"n", "c"}, Desc: "Create new tab"},
304 "close-tab": {Keys: []string{"x"}, Desc: "Close current tab"},
305 "next-tab": {Keys: []string{"l", "tab"}, Desc: "Switch to next tab"},
306 "previous-tab": {Keys: []string{"h", "shift+tab"}, Desc: "Switch to previous tab"},
307 "rename-tab": {Keys: []string{"r"}, Desc: "Rename current tab"},
308 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
309 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
310 },
311 },
312 VimMode: VimModeConfig{
313 ActivationShortcut: defaultPageActivationShortcut,
314 TimeoutMilliseconds: defaultPageTimeoutMilliseconds,
315 SequenceTimeoutMilliseconds: defaultPageSequenceTimeoutMilliseconds,
316 Actions: map[string]ActionBinding{
317 "vim-scroll-left": {Keys: []string{"h"}, Desc: "Scroll page left"},
318 "vim-scroll-down": {Keys: []string{"j"}, Desc: "Scroll page down"},
319 "vim-scroll-up": {Keys: []string{"k"}, Desc: "Scroll page up"},
320 "vim-scroll-right": {Keys: []string{"l"}, Desc: "Scroll page right"},
321 "vim-scroll-down-fast": {Keys: []string{"shift+j"}, Desc: "Scroll page down fast"},
322 "vim-scroll-up-fast": {Keys: []string{"shift+k"}, Desc: "Scroll page up fast"},
323 "confirm": {Keys: []string{"enter"}, Desc: "Open selected heading link and exit mode"},
324 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
325 "heading-next": {Keys: []string{"]]"}, Desc: "Jump to next heading"},
326 "heading-prev": {Keys: []string{"[["}, Desc: "Jump to previous heading"},
327 "focus-input": {Keys: []string{"gi"}, Desc: "Focus next page input"},
328 "code-next": {Keys: []string{"]c"}, Desc: "Jump to next code block"},
329 "code-prev": {Keys: []string{"[c"}, Desc: "Jump to previous code block"},
330 "table-next": {Keys: []string{"]t"}, Desc: "Jump to next table"},
331 "image-next": {Keys: []string{"]i"}, Desc: "Jump to next image"},
332 "list-next": {Keys: []string{"]l"}, Desc: "Jump to next list"},
333 "outline": {Keys: []string{"gO"}, Desc: "Show page outline"},
334 "yank-section": {Keys: []string{"yah"}, Desc: "Yank current section"},
335 "half-page-down": {Keys: []string{"<C-d>"}, Desc: "Scroll half page down"},
336 "half-page-up": {Keys: []string{"<C-u>"}, Desc: "Scroll half page up"},
337 },
338 },
339 ResizeMode: ResizeModeConfig{
340 ActivationShortcut: defaultResizeActivationShortcut,
341 TimeoutMilliseconds: defaultResizeTimeoutMilliseconds,
342 StepPercent: defaultResizeStepPercent,
343 MinPanePercent: defaultResizeMinPanePercent,
344 Actions: map[string]ActionBinding{
345 "resize-increase-left": {Keys: []string{"h", "arrowleft"}, Desc: "Increase size leftward"},
346 "resize-increase-down": {Keys: []string{"j", "arrowdown"}, Desc: "Increase size downward"},
347 "resize-increase-up": {Keys: []string{"k", "arrowup"}, Desc: "Increase size upward"},
348 "resize-increase-right": {Keys: []string{"l", "arrowright"}, Desc: "Increase size rightward"},
349 "resize-decrease-left": {Keys: []string{"H"}, Desc: "Decrease size leftward"},
350 "resize-decrease-down": {Keys: []string{"J"}, Desc: "Decrease size downward"},
351 "resize-decrease-up": {Keys: []string{"K"}, Desc: "Decrease size upward"},
352 "resize-decrease-right": {Keys: []string{"L"}, Desc: "Decrease size rightward"},
353 "resize-increase": {Keys: []string{"+", "="}, Desc: "Increase pane size"},
354 "resize-decrease": {Keys: []string{"-"}, Desc: "Decrease pane size"},
355 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
356 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
357 },
358 },
359 Shortcuts: GlobalShortcutsConfig{
360 Actions: map[string]ActionBinding{
361 "toggle-floating-pane": {Keys: []string{"alt+f"}, Desc: "Toggle floating pane"},
362 "toggle-history-systemview": {Keys: []string{"ctrl+h"}, Desc: "Toggle History in right split"},
363 "toggle-favorites-sidebar": {Keys: []string{"ctrl+b"}, Desc: "Toggle Favorites sidebar"},
364 "toggle-current-page-favorite": {Keys: []string{"ctrl+d"}, Desc: "Add/remove current page favorite"},
365 "toggle-config-systemview": {Keys: []string{}, Desc: "Toggle Config in right split"},
366 "close-pane": {Keys: []string{"ctrl+w"}, Desc: "Close active pane"},
367 "next-tab": {Keys: []string{"ctrl+tab"}, Desc: "Switch to next tab"},
368 "previous-tab": {Keys: []string{"ctrl+shift+tab"}, Desc: "Switch to previous tab"},
369 "consume-or-expel-left": {Keys: []string{"alt+["}, Desc: "Consume/expel pane left"},
370 "consume-or-expel-right": {Keys: []string{"alt+]"}, Desc: "Consume/expel pane right"},
371 "consume-or-expel-up": {Keys: []string{"alt+{"}, Desc: "Consume/expel pane up"},
372 "consume-or-expel-down": {Keys: []string{"alt+}"}, Desc: "Consume/expel pane down"},
373 },
374 },
375 FloatingPane: FloatingPaneConfig{
376 WidthPct: defaultFloatingPaneWidthPct,
377 HeightPct: defaultFloatingPaneHeightPct,
378 Profiles: map[string]FloatingPaneProfile{},
379 },
380 ExternalLinks: ExternalLinksConfig{
381 Behavior: defaultExternalLinkBehavior,
382 Placement: defaultExternalLinkPlacement,
383 },
384 TabBarPosition: defaultTabBarPosition,
385 HideTabBarWhenSingleTab: true,
386 BrowsingContexts: browsingContextDefaults,
387 Popups: browsingContextDefaults,
388 Styling: WorkspaceStylingConfig{
389 BorderWidth: defaultBorderWidth,
390 BorderColor: defaultBorderColor,
391 ModeBorderWidth: defaultModeBorderWidth,
392 PaneModeColor: defaultPaneModeColor,
393 TabModeColor: defaultTabModeColor,
394 SessionModeColor: defaultSessionModeColor,
395 ResizeModeColor: defaultResizeModeColor,
396 ModeIndicatorToasterEnabled: defaultModeIndicatorToasterEnabled,
397 TransitionDuration: defaultTransitionDuration,
398 },
399 },
400 ContentFiltering: ContentFilteringConfig{
401 Enabled: true, // Ad blocking enabled by default
402 AutoUpdate: true, // Auto-update filters from GitHub releases
403 },
404 Clipboard: ClipboardConfig{
405 AutoCopyOnSelection: true, // Enabled by default (zellij-style)
406 },
407 Omnibox: OmniboxConfig{
408 InitialBehavior: defaultOmniboxInitialBehavior,
409 MostVisitedDays: defaultOmniboxMostVisitedDays,
410 AutoOpenOnNewPane: defaultOmniboxAutoOpenOnNewPane,
411 },
412 Session: SessionConfig{
413 AutoRestore: false,
414 SnapshotIntervalMs: defaultSnapshotIntervalMs,
415 MaxExitedSessions: defaultMaxExitedSessions,
416 MaxExitedSessionAgeDays: defaultMaxExitedSessionAgeDays,
417 SessionMode: SessionModeConfig{
418 ActivationShortcut: defaultSessionActivationShortcut,
419 TimeoutMilliseconds: defaultSessionTimeoutMilliseconds,
420 Actions: map[string]ActionBinding{
421 "session-manager": {Keys: []string{"s", "w"}, Desc: "Open session manager"},
422 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
423 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
424 },
425 },
426 },
427 Media: MediaConfig{
428 HardwareDecodingMode: HardwareDecodingAuto, // auto allows sw fallback
429 PreferAV1: false, // Don't force codec preference, let site choose
430 ShowDiagnosticsOnStartup: false, // Disabled - diagnostics can be noisy
431 // GStreamer fields (ForceVSync, GLRenderingMode, GStreamerDebugLevel)
432 // moved to [engine.webkit] — zero values here prevent them from being
433 // written back when marshaling the Config struct.
434 },
435 Update: UpdateConfig{
436 EnableOnStartup: true, // Check for updates on startup by default
437 AutoDownload: false, // Conservative: don't auto-download by default
438 NotifyOnNewSettings: true, // Show toast when new config settings available
439 },
440 Downloads: DownloadsConfig{
441 Path: "", // Empty = use XDG_DOWNLOAD_DIR or ~/Downloads
442 },
443 }
444}
445
446// GetDefaultSearchShortcuts returns the default search shortcuts.
447func GetDefaultSearchShortcuts() map[string]SearchShortcut {
448 return map[string]SearchShortcut{
449 "ddg": {
450 URL: "https://duckduckgo.com/?q=%s",
451 Description: "DuckDuckGo search",
452 },
453 "g": {
454 URL: "https://www.google.com/search?q=%s",
455 Description: "Google search",
456 },
457 "gi": {
458 URL: "https://www.google.com/search?tbm=isch&q=%s",
459 Description: "Google Images search",
460 },
461 "gh": {
462 URL: "https://github.com/search?q=%s",
463 Description: "GitHub search",
464 },
465 "go": {
466 URL: "https://pkg.go.dev/search?q=%s",
467 Description: "Go package search",
468 },
469 "mdn": {
470 URL: "https://developer.mozilla.org/en-US/search?q=%s",
471 Description: "MDN Web Docs search",
472 },
473 "npm": {
474 URL: "https://www.npmjs.com/search?q=%s",
475 Description: "npm package search",
476 },
477 "r": {
478 URL: "https://www.reddit.com/search?q=%s",
479 Description: "Reddit search",
480 },
481 "so": {
482 URL: "https://stackoverflow.com/search?q=%s",
483 Description: "Stack Overflow search",
484 },
485 "w": {
486 URL: "https://en.wikipedia.org/wiki/%s",
487 Description: "Wikipedia search",
488 },
489 "yt": {
490 URL: "https://www.youtube.com/results?search_query=%s",
491 Description: "YouTube search",
492 },
493 }
494}