defaults.go RawHistoryFolder

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

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close