Configuration Guide
Location: ~/.config/dumber/config.toml
Formats: TOML (recommended), JSON, YAML
Database
| Key |
Type |
Default |
Description |
database.path |
string |
~/.local/share/dumber/dumber.db |
Database file path |
History
| Key |
Type |
Default |
Valid Values |
Description |
history.max_entries |
int |
10000 |
> 0 |
Maximum number of history entries |
history.retention_period_days |
int |
365 |
> 0 |
Days to keep history (1 year) |
history.cleanup_interval_days |
int |
1 |
> 0 |
How often to run cleanup |
Search Configuration
| Key |
Type |
Default |
Description |
default_search_engine |
string |
"https://duckduckgo.com/?q=%s" |
Default search engine URL template (must contain %s placeholder) |
search_shortcuts |
map |
See defaults |
Map of shortcut aliases to URLs |
Example:
# Default search engine - used when no shortcut is specified
default_search_engine = "https://duckduckgo.com/?q=%s"
# Alternative search engines:
# default_search_engine = "https://www.google.com/search?q=%s"
# default_search_engine = "https://www.startpage.com/search?q=%s"
# default_search_engine = "https://search.brave.com/search?q=%s"
Default shortcuts (usage: !shortcut query):
# Examples:
# !ddg golang → DuckDuckGo search for "golang"
# !g rust tutorial → Google search for "rust tutorial"
# !gi cats → Google Images search for "cats"
# !gh opencode → GitHub search for "opencode"
# !yt music video → YouTube search for "music video"
[search_shortcuts.ddg]
url = "https://duckduckgo.com/?q=%s"
description = "DuckDuckGo search"
[search_shortcuts.g]
url = "https://google.com/search?q=%s"
description = "Google Search"
[search_shortcuts.gi]
url = "https://google.com/search?tbm=isch&q=%s"
description = "Google Images"
[search_shortcuts.gh]
url = "https://github.com/search?q=%s"
description = "GitHub"
[search_shortcuts.yt]
url = "https://youtube.com/results?search_query=%s"
description = "YouTube"
These settings control the dumber dmenu CLI command for rofi/fuzzel integration.
| Key |
Type |
Default |
Description |
dmenu.max_history_days |
int |
30 |
Number of days of history to show (0 = all) |
dmenu.show_visit_count |
bool |
true |
Show visit counts in output |
dmenu.show_last_visited |
bool |
true |
Show last visited dates in output |
dmenu.history_prefix |
string |
"🕒" |
Prefix for history items |
dmenu.shortcut_prefix |
string |
"🔍" |
Prefix for shortcuts |
dmenu.url_prefix |
string |
"🌐" |
Prefix for URLs |
dmenu.date_format |
string |
"2006-01-02 15:04" |
Go time format string |
dmenu.sort_by_visit_count |
bool |
true |
Sort by visit count instead of recency |
CLI Usage:
# Pipe mode (default) - outputs history for rofi/fuzzel with favicons
dumber dmenu | rofi -dmenu -show-icons -p "Browse: " | dumber dmenu --select
dumber dmenu | fuzzel --dmenu -p "Browse: " | dumber dmenu --select
# Interactive TUI mode
dumber dmenu --interactive
# Override history days via CLI flag (show last 7 days)
dumber dmenu --days 7
# Sort by most visited instead of recency
dumber dmenu --most-visited
# Combined: most visited from last 14 days
dumber dmenu --days 14 --most-visited
Omnibox
| Key |
Type |
Default |
Valid Values |
Description |
omnibox.initial_behavior |
string |
"recent" |
recent, most_visited, none |
Initial history display behavior |
omnibox.most_visited_days |
int |
30 |
>= 0 |
Days of history to consider when initial_behavior = "most_visited" (0 = all history) |
omnibox.auto_open_on_new_pane |
bool |
false |
- |
Automatically open the omnibox after creating a new pane |
Example:
[omnibox]
initial_behavior = "recent" # Show recent history when omnibox opens
most_visited_days = 30 # Days of history used for most_visited
auto_open_on_new_pane = false
# Alternative options:
# initial_behavior = "most_visited" # Show most visited sites
# initial_behavior = "none" # Show no initial suggestions
Logging
| Key |
Type |
Default |
Valid Values |
Description |
logging.level |
string |
"info" |
trace, debug, info, warn, error, fatal |
Log level |
logging.format |
string |
"text" |
text, json, console |
Log format |
logging.max_age |
int |
7 |
>= 0 |
Days to keep logs |
logging.max_files |
int |
100 |
>= 0 |
Session log files to keep (0 disables count cleanup) |
logging.log_dir |
string |
~/.local/state/dumber/logs |
any path |
Log directory |
logging.enable_file_log |
bool |
true |
- |
Enable file logging |
logging.capture_console |
bool |
false |
- |
Capture browser console to logs |
logging.capture_gtk_logs |
bool |
false |
- |
Capture GTK and WebKit logs for debugging. |
Appearance
| Key |
Type |
Default |
Description |
appearance.sans_font |
string |
"Fira Sans" |
Sans-serif font |
appearance.serif_font |
string |
"Fira Sans" |
Serif font |
appearance.monospace_font |
string |
"Fira Code" |
Monospace font |
appearance.default_font_size |
int |
16 |
Font size in points |
appearance.color_scheme |
string |
"default" |
prefer-dark, prefer-light, default |
Color Palettes
Light palette:
[appearance.light_palette]
background = "#f8f8f8"
surface = "#f2f2f2"
surface_variant = "#ececec"
text = "#1a1a1a"
muted = "#6e6e6e"
accent = "#404040"
border = "#d2d2d2"
Dark palette:
[appearance.dark_palette]
background = "#0e0e0e"
surface = "#1a1a1a"
surface_variant = "#141414"
text = "#e4e4e4"
muted = "#848484"
accent = "#a8a8a8"
border = "#363636"
Debug Options
| Key |
Type |
Default |
Description |
debug.enable_devtools |
bool |
true |
Enable browser developer tools (F12, Inspect Element) |
Rendering, UI Scale & Zoom
| Key |
Type |
Default |
Valid Values |
Description |
rendering.mode |
string |
"gpu" |
auto, gpu, cpu |
WebKit hardware acceleration policy |
rendering.disable_dmabuf_renderer |
bool |
false |
- |
Disable WebKit DMA-BUF renderer (may fix flicker on Wayland; slower) |
rendering.force_compositing_mode |
bool |
false |
- |
Force WebKit compositing mode (WEBKIT_FORCE_COMPOSITING_MODE) |
rendering.disable_compositing_mode |
bool |
false |
- |
Disable WebKit compositing mode (WEBKIT_DISABLE_COMPOSITING_MODE) |
rendering.gsk_renderer |
string |
"auto" |
auto, opengl, vulkan, cairo |
GTK renderer selection (GSK_RENDERER) |
rendering.disable_mipmaps |
bool |
false |
- |
Disable GTK mipmaps (GSK_GPU_DISABLE=mipmap) |
rendering.prefer_gl |
bool |
false |
- |
Prefer OpenGL over GLES (GDK_DEBUG=gl-prefer-gl) |
rendering.draw_compositing_indicators |
bool |
false |
- |
Draw WebKit compositing indicators (debug) |
rendering.show_fps |
bool |
false |
- |
Show WebKit FPS counter (WEBKIT_SHOW_FPS) |
rendering.sample_memory |
bool |
false |
- |
Enable WebKit memory sampling (WEBKIT_SAMPLE_MEMORY) |
rendering.debug_frames |
bool |
false |
- |
Enable GTK frame timing debug (GDK_DEBUG=frames) |
engine.type |
string |
"cef" |
cef, webkit |
Browser engine. WebKit remains available as a fallback. |
engine.cef.render_stack |
string |
"vulkan" |
vulkan, egl |
CEF GPU render stack |
engine.cef.adaptive_windowless_frame_rate |
bool |
true |
- |
Enable adaptive CEF OSR FPS polling (only applies when windowless_frame_rate = 0) |
engine.cef.windowless_frame_rate |
int32 |
0 |
>= 0 |
Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default |
engine.cef.windowless_frame_rate_max |
int32 |
240 |
>= 0 |
Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF |
default_ui_scale |
float |
1.0 |
> 0 |
GTK widget UI scale (1.0=100%, 2.0=200%) |
default_webpage_zoom |
float |
1.2 |
> 0 |
Default page zoom (1.0=100%, 1.2=120%) |
Workspace Configuration
General
| Key |
Type |
Default |
Description |
workspace.new_pane_url |
string |
"about:blank" |
URL loaded for new panes/tabs (supports http(s)://, dumb://, file://, about:) |
workspace.switch_to_tab_on_move |
bool |
true |
When moving a pane to another tab, automatically switch to the destination tab |
Example:
[workspace]
new_pane_url = "dumb://history"
Pane Mode
| Key |
Type |
Default |
Description |
workspace.pane_mode.activation_shortcut |
string |
"ctrl+p" |
Pane mode activation key |
workspace.pane_mode.timeout_ms |
int |
3000 |
Pane mode timeout (ms) |
workspace.pane_mode.actions |
map |
See below |
Action→keys mappings |
Default pane mode actions:
[workspace.pane_mode.actions]
split-right = ["arrowright", "r"]
split-left = ["arrowleft", "l"]
split-up = ["arrowup", "u"]
split-down = ["arrowdown", "d"]
stack-pane = ["s"]
close-pane = ["x"]
move-pane-to-tab = ["m"]
move-pane-to-next-tab = ["M", "shift+m"]
# Consume-or-expel (niri-style) - very alpha
consume-or-expel-left = ["["]
consume-or-expel-right = ["]"]
consume-or-expel-up = ["{"]
consume-or-expel-down = ["}"]
focus-right = ["shift+arrowright", "shift+l"]
focus-left = ["shift+arrowleft", "shift+h"]
focus-up = ["shift+arrowup", "shift+k"]
focus-down = ["shift+arrowdown", "shift+j"]
confirm = ["enter"]
cancel = ["escape"]
Note: Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
Tab Mode
| Key |
Type |
Default |
Description |
workspace.tab_mode.activation_shortcut |
string |
"ctrl+t" |
Tab mode activation key |
workspace.tab_mode.timeout_ms |
int |
3000 |
Tab mode timeout (ms) |
workspace.tab_mode.actions |
map |
See below |
Action→keys mappings |
Default tab mode actions:
[workspace.tab_mode.actions]
new-tab = ["n", "c"]
close-tab = ["x"]
next-tab = ["l", "tab"]
previous-tab = ["h", "shift+tab"]
rename-tab = ["r"]
confirm = ["enter"]
cancel = ["escape"]
Note: Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
Resize Mode
| Key |
Type |
Default |
Description |
workspace.resize_mode.activation_shortcut |
string |
"ctrl+n" |
Resize mode activation key |
workspace.resize_mode.timeout_ms |
int |
3000 |
Resize mode timeout (ms) |
workspace.resize_mode.step_percent |
float |
5.0 |
Split ratio step per keystroke (percent) |
workspace.resize_mode.min_pane_percent |
float |
10.0 |
Minimum pane size (percent) |
workspace.resize_mode.actions |
map |
See below |
Action→keys mappings |
Default resize mode actions:
[workspace.resize_mode.actions]
resize-increase-left = ["h", "arrowleft"]
resize-increase-down = ["j", "arrowdown"]
resize-increase-up = ["k", "arrowup"]
resize-increase-right = ["l", "arrowright"]
resize-decrease-left = ["H"]
resize-decrease-down = ["J"]
resize-decrease-up = ["K"]
resize-decrease-right = ["L"]
resize-increase = ["+", "="]
resize-decrease = ["-"]
confirm = ["enter"]
cancel = ["escape"]
Notes:
- Directional actions (
resize-increase-*/resize-decrease-*) move the split divider.
- Smart actions (
resize-increase / resize-decrease) grow/shrink the active pane (best-effort) by picking a direction automatically.
- Timeout is refreshed on each resize keypress so you can keep adjusting without re-entering the mode.
Global Shortcuts
Global shortcuts are configured under workspace.shortcuts.actions using the same ActionBinding structure as modal keybindings:
| Action |
Default Keys |
Description |
toggle_floating_pane |
alt+f |
Toggle the workspace floating pane (persistent session) |
close_pane |
ctrl+w |
Close active pane (or release floating pane when floating is active) |
next_tab |
ctrl+tab |
Switch to next tab |
previous_tab |
ctrl+shift+tab |
Switch to previous tab |
consume_or_expel_left |
alt+[ |
Consume into left sibling stack, or expel left if stacked |
consume_or_expel_right |
alt+] |
Consume into right sibling stack, or expel right if stacked |
consume_or_expel_up |
alt+{ |
Consume into upper sibling stack, or expel up if stacked |
consume_or_expel_down |
alt+} |
Consume into lower sibling stack, or expel down if stacked |
Example:
[workspace.shortcuts.actions.close_pane]
keys = ["ctrl+w"]
desc = "Close active pane"
[workspace.shortcuts.actions.next_tab]
keys = ["ctrl+tab", "alt+l"]
desc = "Switch to next tab"
[workspace.shortcuts.actions.toggle_floating_pane]
keys = ["alt+f"]
desc = "Toggle floating pane"
Tip: Use dumb://config → Keybindings tab to edit shortcuts visually.
Note: New tab creation uses modal tab mode (Ctrl+T then n/c). This follows the Zellij-style modal keyboard interface.
Note: Consume-or-expel is experimental and may change behavior between releases.
Warning: Some Alt+<key> shortcuts may conflict with WebKit defaults, website handlers, or your desktop environment.
If a binding does not fire, rebind it to a different key.
Floating Pane
The floating workspace pane is a persistent overlay session: hiding it does not destroy its web content.
- Default behavior includes exactly one floating shortcut:
Alt+F (toggle_floating_pane).
- URL shortcuts (for example
Alt+G) are user-defined via workspace.floating_pane.profiles and are empty by default.
Alt+F toggles visibility only and preserves floating state.
Ctrl+W on an active floating pane closes and releases that floating session for a fresh next open.
- Floating profile keybindings support modifier combos with
ctrl, shift, and alt (for example ctrl+shift+y or ctrl+alt+m).
| Key |
Type |
Default |
Description |
workspace.floating_pane.width_pct |
float |
0.82 |
Floating pane width as fraction of workspace width ((0,1]) |
workspace.floating_pane.height_pct |
float |
0.72 |
Floating pane height as fraction of workspace height ((0,1]) |
workspace.floating_pane.profiles |
map |
{} |
Named URL profiles, each with keys[], url, optional desc |
Example:
[workspace.shortcuts.actions.toggle_floating_pane]
keys = ["alt+f"]
desc = "Toggle floating pane"
[workspace.floating_pane]
width_pct = 0.82
height_pct = 0.72
[workspace.floating_pane.profiles]
# Empty by default. Add as many entries as needed.
[workspace.floating_pane.profiles.google]
keys = ["alt+g"]
url = "https://google.com"
desc = "Open floating pane on Google"
[workspace.floating_pane.profiles.github]
keys = ["alt+h"]
url = "https://github.com"
desc = "Open floating pane on GitHub"
Browsing Context Behavior
| Key |
Type |
Default |
Valid Values |
Description |
workspace.browsing_contexts.behavior |
string |
"split" |
split, stacked, tabbed, windowed |
Placement mode for script-opened browsing contexts |
workspace.browsing_contexts.placement |
string |
"right" |
right, left, top, bottom |
Split direction when behavior is split |
workspace.browsing_contexts.open_in_new_pane |
bool |
true |
- |
Allow new browsing contexts to open in the workspace |
workspace.browsing_contexts.follow_pane_context |
bool |
true |
- |
Keep new browsing contexts aligned with the parent pane context |
workspace.browsing_contexts.blank_target_behavior |
string |
"stacked" |
split, stacked, tabbed |
Placement mode for _blank / new-page link contexts |
workspace.browsing_contexts.enable_smart_detection |
bool |
true |
- |
Use window properties to refine browsing-context classification |
workspace.browsing_contexts.oauth_auto_close |
bool |
true |
- |
Auto-close OAuth browsing contexts after success |
Workspace Styling
| Key |
Type |
Default |
Description |
workspace.styling.border_width |
int |
1 |
Active pane border width (px) - overlay |
workspace.styling.border_color |
string |
"@theme_selected_bg_color" |
Active pane border color |
workspace.styling.mode_border_width |
int |
4 |
Modal mode border width (px) - applies to all modes |
workspace.styling.pane_mode_color |
string |
"#4A90E2" |
Pane mode color (blue) - used for border and toaster |
workspace.styling.tab_mode_color |
string |
"#FFA500" |
Tab mode color (orange) - used for border and toaster |
workspace.styling.session_mode_color |
string |
"#9B59B6" |
Session mode color (purple) - used for border and toaster |
workspace.styling.resize_mode_color |
string |
"#00D4AA" |
Resize mode color (teal) - used for border and toaster |
workspace.styling.mode_indicator_toaster_enabled |
bool |
true |
Show toaster notification when modal modes are active |
workspace.styling.transition_duration |
int |
120 |
Border transition duration (ms) |
Example:
[workspace.styling]
border_width = 1
border_color = "@theme_selected_bg_color"
mode_border_width = 4
pane_mode_color = "#4A90E2" # Blue for pane mode
tab_mode_color = "#FFA500" # Orange for tab mode
session_mode_color = "#9B59B6" # Purple for session mode
resize_mode_color = "#00D4AA" # Teal for resize mode
mode_indicator_toaster_enabled = true
transition_duration = 120
Session
| Key |
Type |
Default |
Description |
session.auto_restore |
bool |
false |
Automatically restore the last session on startup |
session.snapshot_interval_ms |
int |
5000 |
Minimum interval between snapshots in milliseconds |
session.max_exited_sessions |
int |
50 |
Maximum number of exited sessions to keep |
session.max_exited_session_age_days |
int |
7 |
Maximum age in days for exited sessions (auto-deleted on startup) |
Session Mode
| Key |
Type |
Default |
Description |
session.session_mode.activation_shortcut |
string |
"ctrl+o" |
Session mode activation key |
session.session_mode.timeout_ms |
int |
3000 |
Session mode timeout (ms) |
session.session_mode.actions |
map |
See below |
Action to keys mappings |
Default session mode actions:
[session.session_mode.actions]
session-manager = ["s", "w"]
confirm = ["enter"]
cancel = ["escape"]
Example:
[session]
auto_restore = false # Don't auto-restore on startup
snapshot_interval_ms = 5000 # Save state every 5 seconds (debounced)
max_exited_sessions = 50 # Keep last 50 exited sessions
max_exited_session_age_days = 7 # Delete sessions older than 7 days on startup
| Key |
Type |
Default |
Valid Values |
Description |
media.hardware_decoding |
string |
"auto" |
auto, force, disable |
Hardware video decoding mode |
media.prefer_av1 |
bool |
false |
- |
Prefer AV1 codec when available |
media.show_diagnostics |
bool |
false |
- |
Show media diagnostics warnings at startup |
media.force_vsync |
bool |
false |
- |
Force VSync for video playback (may help with tearing) |
media.gl_rendering_mode |
string |
"auto" |
auto, gles2, gl3, none |
OpenGL API selection for video rendering |
media.gstreamer_debug_level |
int |
0 |
0-5 |
GStreamer debug verbosity (0=off) |
Hardware decoding modes:
auto (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000
force: Hardware only - fails if unavailable
disable: Software only - higher CPU usage
GL rendering modes:
auto (default): Let GStreamer choose the best OpenGL API
gles2: Force GLES2 (better for some mobile/embedded GPUs)
gl3: Force OpenGL 3.x desktop
none: Disable GL-based rendering
GPU auto-detection:
Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings:
- AMD: Uses
radeonsi driver
- Intel: Uses
iHD driver (modern, for Broadwell+)
- NVIDIA: Uses
nvidia driver with EGL platform
Example:
[media]
hardware_decoding = "auto" # HW preferred, SW fallback
prefer_av1 = false # Let site choose codec
show_diagnostics = false # Keep off for daily use
force_vsync = false # Let compositor handle VSync
gl_rendering_mode = "auto" # GStreamer picks best GL API
gstreamer_debug_level = 0 # Increase to 3-5 for debugging
Diagnostic mode toggle path:
# ~/.config/dumber/config.toml
[media]
show_diagnostics = true
gstreamer_debug_level = 3
Set show_diagnostics = false and gstreamer_debug_level = 0 to return to normal mode.
Diagnostics CLI:
# Check GStreamer plugins and VA-API status
dumber doctor --media
Runtime
| Key |
Type |
Default |
Description |
runtime.prefix |
string |
"" |
Optional runtime prefix (e.g., /opt/webkitgtk) used to locate newer GTK/WebKitGTK installs |
If set, dumber prepends prefix-derived paths to:
PKG_CONFIG_PATH (for version checks)
LD_LIBRARY_PATH (for runtime library loading)
GI_TYPELIB_PATH (GObject introspection)
XDG_DATA_DIRS (schemas/resources)
Example:
[runtime]
prefix = "/opt/webkitgtk"
Clipboard
| Key |
Type |
Default |
Description |
clipboard.auto_copy_on_selection |
bool |
true |
Automatically copy selected text to clipboard (zellij/tmux-style) |
When enabled, selecting text in a web page immediately copies it to the clipboard with a brief toast notification. Does not apply to text selection in input fields or textareas.
Example:
[clipboard]
auto_copy_on_selection = true # Enabled by default
Content Filtering
| Key |
Type |
Default |
Description |
content_filtering.enabled |
bool |
true |
Enable ad blocking |
content_filtering.auto_update |
bool |
true |
Automatically update filters |
Notes:
- Filter data is downloaded from
bnema/ublock-webkit-filters GitHub releases.
- Domain whitelist is managed via the database (
content_whitelist table), not the config file.
Update
| Key |
Type |
Default |
Description |
update.enable_on_startup |
bool |
true |
Check for updates when browser starts |
update.auto_download |
bool |
false |
Automatically download updates in background |
update.notify_on_new_settings |
bool |
true |
Show toast notification when new config settings are available |
Example:
[update]
enable_on_startup = true # Check for updates on startup
auto_download = false # Don't auto-download (prompt instead)
notify_on_new_settings = true # Show toast when config migration available
CLI Commands:
# Check config status and available migrations
dumber config status
# Add missing settings with default values
dumber config migrate
# Skip confirmation prompt
dumber config migrate --yes
Performance profiles provide preset configurations for WebKitGTK tuning. These settings affect Skia rendering threads, memory pressure handling, and WebView pool behavior.
Note: Performance settings are applied at browser startup. Changes require a restart to take effect.
| Key |
Type |
Default |
Valid Values |
Description |
performance.profile |
string |
"default" |
default, lite, max, custom |
Performance profile selection |
Profiles
| Profile |
Description |
Use Case |
default |
No tuning, uses WebKit defaults |
Compatibility baseline, troubleshooting |
lite |
Reduced resource usage |
Low-RAM systems (< 4GB), battery saving |
max |
Maximum responsiveness |
Heavy pages (GitHub PRs, complex SPAs), high-end systems |
custom |
Manual control over all settings |
Advanced users who want fine-grained control |
Profile Settings Matrix
| Setting |
default |
lite |
max |
| Skia CPU threads |
unset |
2 |
NumCPU()/2 (min 4) |
| Skia GPU threads |
unset |
unset |
scales with VRAM |
| Web process memory (MB) |
unset |
768 |
unset |
| Network process memory (MB) |
unset |
384 |
unset |
| Conservative threshold |
unset |
0.25 |
unset |
| Strict threshold |
unset |
0.4 |
unset |
| WebView pool prewarm |
4 |
2 |
scales with RAM |
Example:
[performance]
profile = "default" # WebKit baseline default
# For WebKit baseline behavior:
# profile = "default"
# For heavy pages (GitHub PRs, complex web apps):
# profile = "max"
# For manual control:
# profile = "custom"
Custom Profile Settings
When profile = "custom", you can configure individual tuning options:
| Key |
Type |
Default |
Description |
performance.skia_cpu_painting_threads |
int |
0 |
Skia CPU rendering threads (0=unset) |
performance.skia_gpu_painting_threads |
int |
-1 |
Skia GPU rendering threads (-1=unset, 0=disable) |
performance.skia_enable_cpu_rendering |
bool |
false |
Force CPU rendering |
performance.web_process_memory_limit_mb |
int |
0 |
Web process memory limit in MB (0=unset) |
performance.web_process_memory_poll_interval_sec |
float |
0 |
Memory check interval (0=WebKit default: 30s) |
performance.web_process_memory_conservative_threshold |
float |
0 |
Conservative cleanup threshold (0=unset) |
performance.web_process_memory_strict_threshold |
float |
0 |
Strict cleanup threshold (0=unset) |
performance.network_process_memory_limit_mb |
int |
0 |
Network process memory limit in MB |
performance.network_process_memory_poll_interval_sec |
float |
0 |
Network memory check interval |
performance.network_process_memory_conservative_threshold |
float |
0 |
Network conservative threshold |
performance.network_process_memory_strict_threshold |
float |
0 |
Network strict threshold |
performance.webview_pool_prewarm_count |
int |
4 |
WebViews to pre-create at startup |
performance.zoom_cache_size |
int |
256 |
Domain zoom levels to cache |
Custom profile example:
[performance]
profile = "custom"
# Skia threading - tune for your CPU
skia_cpu_painting_threads = 4
skia_gpu_painting_threads = 2
# Web process memory pressure
web_process_memory_limit_mb = 1024
web_process_memory_conservative_threshold = 0.4
web_process_memory_strict_threshold = 0.6
# WebView pool
webview_pool_prewarm_count = 6
Important: Individual tuning fields are ignored unless profile = "custom". Setting individual fields with any other profile will produce a validation warning.
Downloads
| Key |
Type |
Default |
Description |
downloads.path |
string |
"" |
Download directory path (empty = $XDG_DOWNLOAD_DIR or ~/Downloads) |
Downloads are saved to the configured directory with toast notifications for download started, completed, and failed events.
Example:
[downloads]
path = "" # Use system default ($XDG_DOWNLOAD_DIR or ~/Downloads)
# Or specify a custom directory:
# path = "/home/user/my-downloads"
Environment Variables
All config values can be overridden via environment variables with the prefix DUMBER_:
# Database
DUMBER_DATABASE_PATH=/custom/path/db.sqlite
# Rendering (examples)
# CEF defaults to the GPU-first Vulkan DMABUF stack.
# Use EGL/OpenGL for driver compatibility:
# DUMBER_ENGINE_CEF_RENDER_STACK=egl
DUMBER_RENDERING_MODE=cpu
# WebKit-specific rendering overrides:
# DUMBER_RENDERING_DISABLE_DMABUF_RENDERER=true
# DUMBER_RENDERING_GSK_RENDERER=opengl
DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
# Logging
DUMBER_LOG_LEVEL=debug
DUMBER_LOG_FORMAT=json
source
1# Configuration Guide
2
3**Location**: `~/.config/dumber/config.toml`
4**Formats**: TOML (recommended), JSON, YAML
5
6## Database
7
8| Key | Type | Default | Description |
9|-----|------|---------|-------------|
10| `database.path` | string | `~/.local/share/dumber/dumber.db` | Database file path |
11
12## History
13
14| Key | Type | Default | Valid Values | Description |
15|-----|------|---------|--------------|-------------|
16| `history.max_entries` | int | `10000` | > 0 | Maximum number of history entries |
17| `history.retention_period_days` | int | `365` | > 0 | Days to keep history (1 year) |
18| `history.cleanup_interval_days` | int | `1` | > 0 | How often to run cleanup |
19
20## Search Configuration
21
22| Key | Type | Default | Description |
23|-----|------|---------|-------------|
24| `default_search_engine` | string | `"https://duckduckgo.com/?q=%s"` | Default search engine URL template (must contain `%s` placeholder) |
25| `search_shortcuts` | map | See defaults | Map of shortcut aliases to URLs |
26
27**Example:**
28```toml
29# Default search engine - used when no shortcut is specified
30default_search_engine = "https://duckduckgo.com/?q=%s"
31
32# Alternative search engines:
33# default_search_engine = "https://www.google.com/search?q=%s"
34# default_search_engine = "https://www.startpage.com/search?q=%s"
35# default_search_engine = "https://search.brave.com/search?q=%s"
36```
37
38**Default shortcuts (usage: `!shortcut query`):**
39```toml
40# Examples:
41# !ddg golang → DuckDuckGo search for "golang"
42# !g rust tutorial → Google search for "rust tutorial"
43# !gi cats → Google Images search for "cats"
44# !gh opencode → GitHub search for "opencode"
45# !yt music video → YouTube search for "music video"
46
47[search_shortcuts.ddg]
48url = "https://duckduckgo.com/?q=%s"
49description = "DuckDuckGo search"
50
51[search_shortcuts.g]
52url = "https://google.com/search?q=%s"
53description = "Google Search"
54
55[search_shortcuts.gi]
56url = "https://google.com/search?tbm=isch&q=%s"
57description = "Google Images"
58
59[search_shortcuts.gh]
60url = "https://github.com/search?q=%s"
61description = "GitHub"
62
63[search_shortcuts.yt]
64url = "https://youtube.com/results?search_query=%s"
65description = "YouTube"
66```
67
68## Dmenu / Launcher
69
70These settings control the `dumber dmenu` CLI command for rofi/fuzzel integration.
71
72| Key | Type | Default | Description |
73|-----|------|---------|-------------|
74| `dmenu.max_history_days` | int | `30` | Number of days of history to show (0 = all) |
75| `dmenu.show_visit_count` | bool | `true` | Show visit counts in output |
76| `dmenu.show_last_visited` | bool | `true` | Show last visited dates in output |
77| `dmenu.history_prefix` | string | `"🕒"` | Prefix for history items |
78| `dmenu.shortcut_prefix` | string | `"🔍"` | Prefix for shortcuts |
79| `dmenu.url_prefix` | string | `"🌐"` | Prefix for URLs |
80| `dmenu.date_format` | string | `"2006-01-02 15:04"` | Go time format string |
81| `dmenu.sort_by_visit_count` | bool | `true` | Sort by visit count instead of recency |
82
83**CLI Usage:**
84```bash
85# Pipe mode (default) - outputs history for rofi/fuzzel with favicons
86dumber dmenu | rofi -dmenu -show-icons -p "Browse: " | dumber dmenu --select
87dumber dmenu | fuzzel --dmenu -p "Browse: " | dumber dmenu --select
88
89# Interactive TUI mode
90dumber dmenu --interactive
91
92# Override history days via CLI flag (show last 7 days)
93dumber dmenu --days 7
94
95# Sort by most visited instead of recency
96dumber dmenu --most-visited
97
98# Combined: most visited from last 14 days
99dumber dmenu --days 14 --most-visited
100```
101
102## Omnibox
103
104| Key | Type | Default | Valid Values | Description |
105|-----|------|---------|--------------|-------------|
106| `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior |
107| `omnibox.most_visited_days` | int | `30` | `>= 0` | Days of history to consider when `initial_behavior = "most_visited"` (`0` = all history) |
108| `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane |
109
110**Example:**
111```toml
112[omnibox]
113initial_behavior = "recent" # Show recent history when omnibox opens
114most_visited_days = 30 # Days of history used for most_visited
115auto_open_on_new_pane = false
116
117# Alternative options:
118# initial_behavior = "most_visited" # Show most visited sites
119# initial_behavior = "none" # Show no initial suggestions
120```
121
122## Logging
123
124| Key | Type | Default | Valid Values | Description |
125|-----|------|---------|--------------|-------------|
126| `logging.level` | string | `"info"` | `trace`, `debug`, `info`, `warn`, `error`, `fatal` | Log level |
127| `logging.format` | string | `"text"` | `text`, `json`, `console` | Log format |
128| `logging.max_age` | int | `7` | `>= 0` | Days to keep logs |
129| `logging.max_files` | int | `100` | `>= 0` | Session log files to keep (`0` disables count cleanup) |
130| `logging.log_dir` | string | `~/.local/state/dumber/logs` | any path | Log directory |
131| `logging.enable_file_log` | bool | `true` | - | Enable file logging |
132| `logging.capture_console` | bool | `false` | - | Capture browser console to logs |
133| `logging.capture_gtk_logs` | bool | `false` | - | Capture GTK and WebKit logs for debugging. |
134
135## Appearance
136
137| Key | Type | Default | Description |
138|-----|------|---------|-------------|
139| `appearance.sans_font` | string | `"Fira Sans"` | Sans-serif font |
140| `appearance.serif_font` | string | `"Fira Sans"` | Serif font |
141| `appearance.monospace_font` | string | `"Fira Code"` | Monospace font |
142| `appearance.default_font_size` | int | `16` | Font size in points |
143| `appearance.color_scheme` | string | `"default"` | `prefer-dark`, `prefer-light`, `default` |
144
145### Color Palettes
146
147**Light palette:**
148```toml
149[appearance.light_palette]
150background = "#f8f8f8"
151surface = "#f2f2f2"
152surface_variant = "#ececec"
153text = "#1a1a1a"
154muted = "#6e6e6e"
155accent = "#404040"
156border = "#d2d2d2"
157```
158
159**Dark palette:**
160```toml
161[appearance.dark_palette]
162background = "#0e0e0e"
163surface = "#1a1a1a"
164surface_variant = "#141414"
165text = "#e4e4e4"
166muted = "#848484"
167accent = "#a8a8a8"
168border = "#363636"
169```
170
171## Debug Options
172
173| Key | Type | Default | Description |
174|-----|------|---------|-------------|
175| `debug.enable_devtools` | bool | `true` | Enable browser developer tools (F12, Inspect Element) |
176
177## Rendering, UI Scale & Zoom
178
179| Key | Type | Default | Valid Values | Description |
180|-----|------|---------|--------------|-------------|
181| `rendering.mode` | string | `"gpu"` | `auto`, `gpu`, `cpu` | WebKit hardware acceleration policy |
182| `rendering.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit DMA-BUF renderer (may fix flicker on Wayland; slower) |
183| `rendering.force_compositing_mode` | bool | `false` | - | Force WebKit compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) |
184| `rendering.disable_compositing_mode` | bool | `false` | - | Disable WebKit compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) |
185| `rendering.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | GTK renderer selection (`GSK_RENDERER`) |
186| `rendering.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps (`GSK_GPU_DISABLE=mipmap`) |
187| `rendering.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES (`GDK_DEBUG=gl-prefer-gl`) |
188| `rendering.draw_compositing_indicators` | bool | `false` | - | Draw WebKit compositing indicators (debug) |
189| `rendering.show_fps` | bool | `false` | - | Show WebKit FPS counter (`WEBKIT_SHOW_FPS`) |
190| `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) |
191| `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) |
192| `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine. WebKit remains available as a fallback. |
193| `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack |
194| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling (only applies when `windowless_frame_rate = 0`) |
195| `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default |
196| `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF |
197| `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) |
198| `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) |
199
200## Workspace Configuration
201
202### General
203
204| Key | Type | Default | Description |
205|-----|------|---------|-------------|
206| `workspace.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) |
207| `workspace.switch_to_tab_on_move` | bool | `true` | When moving a pane to another tab, automatically switch to the destination tab |
208
209**Example:**
210```toml
211[workspace]
212new_pane_url = "dumb://history"
213```
214
215### Pane Mode
216
217| Key | Type | Default | Description |
218|-----|------|---------|-------------|
219| `workspace.pane_mode.activation_shortcut` | string | `"ctrl+p"` | Pane mode activation key |
220| `workspace.pane_mode.timeout_ms` | int | `3000` | Pane mode timeout (ms) |
221| `workspace.pane_mode.actions` | map | See below | Action→keys mappings |
222
223**Default pane mode actions:**
224```toml
225[workspace.pane_mode.actions]
226split-right = ["arrowright", "r"]
227split-left = ["arrowleft", "l"]
228split-up = ["arrowup", "u"]
229split-down = ["arrowdown", "d"]
230stack-pane = ["s"]
231close-pane = ["x"]
232move-pane-to-tab = ["m"]
233move-pane-to-next-tab = ["M", "shift+m"]
234
235# Consume-or-expel (niri-style) - very alpha
236consume-or-expel-left = ["["]
237consume-or-expel-right = ["]"]
238consume-or-expel-up = ["{"]
239consume-or-expel-down = ["}"]
240
241focus-right = ["shift+arrowright", "shift+l"]
242focus-left = ["shift+arrowleft", "shift+h"]
243focus-up = ["shift+arrowup", "shift+k"]
244focus-down = ["shift+arrowdown", "shift+j"]
245confirm = ["enter"]
246cancel = ["escape"]
247```
248
249> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
250
251### Tab Mode
252
253| Key | Type | Default | Description |
254|-----|------|---------|-------------|
255| `workspace.tab_mode.activation_shortcut` | string | `"ctrl+t"` | Tab mode activation key |
256| `workspace.tab_mode.timeout_ms` | int | `3000` | Tab mode timeout (ms) |
257| `workspace.tab_mode.actions` | map | See below | Action→keys mappings |
258
259**Default tab mode actions:**
260```toml
261[workspace.tab_mode.actions]
262new-tab = ["n", "c"]
263close-tab = ["x"]
264next-tab = ["l", "tab"]
265previous-tab = ["h", "shift+tab"]
266rename-tab = ["r"]
267confirm = ["enter"]
268cancel = ["escape"]
269```
270
271> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
272
273### Resize Mode
274
275| Key | Type | Default | Description |
276|-----|------|---------|-------------|
277| `workspace.resize_mode.activation_shortcut` | string | `"ctrl+n"` | Resize mode activation key |
278| `workspace.resize_mode.timeout_ms` | int | `3000` | Resize mode timeout (ms) |
279| `workspace.resize_mode.step_percent` | float | `5.0` | Split ratio step per keystroke (percent) |
280| `workspace.resize_mode.min_pane_percent` | float | `10.0` | Minimum pane size (percent) |
281| `workspace.resize_mode.actions` | map | See below | Action→keys mappings |
282
283**Default resize mode actions:**
284```toml
285[workspace.resize_mode.actions]
286resize-increase-left = ["h", "arrowleft"]
287resize-increase-down = ["j", "arrowdown"]
288resize-increase-up = ["k", "arrowup"]
289resize-increase-right = ["l", "arrowright"]
290resize-decrease-left = ["H"]
291resize-decrease-down = ["J"]
292resize-decrease-up = ["K"]
293resize-decrease-right = ["L"]
294resize-increase = ["+", "="]
295resize-decrease = ["-"]
296confirm = ["enter"]
297cancel = ["escape"]
298```
299
300Notes:
301- Directional actions (`resize-increase-*/resize-decrease-*`) move the split divider.
302- Smart actions (`resize-increase` / `resize-decrease`) grow/shrink the active pane (best-effort) by picking a direction automatically.
303- Timeout is refreshed on each resize keypress so you can keep adjusting without re-entering the mode.
304
305### Global Shortcuts
306
307Global shortcuts are configured under `workspace.shortcuts.actions` using the same `ActionBinding` structure as modal keybindings:
308
309| Action | Default Keys | Description |
310|--------|--------------|-------------|
311| `toggle_floating_pane` | `alt+f` | Toggle the workspace floating pane (persistent session) |
312| `close_pane` | `ctrl+w` | Close active pane (or release floating pane when floating is active) |
313| `next_tab` | `ctrl+tab` | Switch to next tab |
314| `previous_tab` | `ctrl+shift+tab` | Switch to previous tab |
315| `consume_or_expel_left` | `alt+[` | Consume into left sibling stack, or expel left if stacked |
316| `consume_or_expel_right` | `alt+]` | Consume into right sibling stack, or expel right if stacked |
317| `consume_or_expel_up` | `alt+{` | Consume into upper sibling stack, or expel up if stacked |
318| `consume_or_expel_down` | `alt+}` | Consume into lower sibling stack, or expel down if stacked |
319
320**Example:**
321```toml
322[workspace.shortcuts.actions.close_pane]
323 keys = ["ctrl+w"]
324 desc = "Close active pane"
325
326[workspace.shortcuts.actions.next_tab]
327 keys = ["ctrl+tab", "alt+l"]
328 desc = "Switch to next tab"
329
330[workspace.shortcuts.actions.toggle_floating_pane]
331 keys = ["alt+f"]
332 desc = "Toggle floating pane"
333```
334
335> **Tip:** Use `dumb://config` → Keybindings tab to edit shortcuts visually.
336>
337> **Note:** New tab creation uses modal tab mode (Ctrl+T then n/c). This follows the Zellij-style modal keyboard interface.
338>
339> **Note:** Consume-or-expel is experimental and may change behavior between releases.
340>
341> **Warning:** Some `Alt+<key>` shortcuts may conflict with WebKit defaults, website handlers, or your desktop environment.
342> If a binding does not fire, rebind it to a different key.
343
344### Floating Pane
345
346The floating workspace pane is a persistent overlay session: hiding it does not destroy its web content.
347
348- Default behavior includes exactly one floating shortcut: `Alt+F` (`toggle_floating_pane`).
349- URL shortcuts (for example `Alt+G`) are user-defined via `workspace.floating_pane.profiles` and are empty by default.
350- `Alt+F` toggles visibility only and preserves floating state.
351- `Ctrl+W` on an active floating pane closes and releases that floating session for a fresh next open.
352- Floating profile keybindings support modifier combos with `ctrl`, `shift`, and `alt` (for example `ctrl+shift+y` or `ctrl+alt+m`).
353
354| Key | Type | Default | Description |
355|-----|------|---------|-------------|
356| `workspace.floating_pane.width_pct` | float | `0.82` | Floating pane width as fraction of workspace width (`(0,1]`) |
357| `workspace.floating_pane.height_pct` | float | `0.72` | Floating pane height as fraction of workspace height (`(0,1]`) |
358| `workspace.floating_pane.profiles` | map | `{}` | Named URL profiles, each with `keys[]`, `url`, optional `desc` |
359
360**Example:**
361
362```toml
363[workspace.shortcuts.actions.toggle_floating_pane]
364keys = ["alt+f"]
365desc = "Toggle floating pane"
366
367[workspace.floating_pane]
368width_pct = 0.82
369height_pct = 0.72
370
371[workspace.floating_pane.profiles]
372# Empty by default. Add as many entries as needed.
373
374[workspace.floating_pane.profiles.google]
375keys = ["alt+g"]
376url = "https://google.com"
377desc = "Open floating pane on Google"
378
379[workspace.floating_pane.profiles.github]
380keys = ["alt+h"]
381url = "https://github.com"
382desc = "Open floating pane on GitHub"
383```
384
385### Browsing Context Behavior
386
387| Key | Type | Default | Valid Values | Description |
388|-----|------|---------|--------------|-------------|
389| `workspace.browsing_contexts.behavior` | string | `"split"` | `split`, `stacked`, `tabbed`, `windowed` | Placement mode for script-opened browsing contexts |
390| `workspace.browsing_contexts.placement` | string | `"right"` | `right`, `left`, `top`, `bottom` | Split direction when behavior is `split` |
391| `workspace.browsing_contexts.open_in_new_pane` | bool | `true` | - | Allow new browsing contexts to open in the workspace |
392| `workspace.browsing_contexts.follow_pane_context` | bool | `true` | - | Keep new browsing contexts aligned with the parent pane context |
393| `workspace.browsing_contexts.blank_target_behavior` | string | `"stacked"` | `split`, `stacked`, `tabbed` | Placement mode for `_blank` / new-page link contexts |
394| `workspace.browsing_contexts.enable_smart_detection` | bool | `true` | - | Use window properties to refine browsing-context classification |
395| `workspace.browsing_contexts.oauth_auto_close` | bool | `true` | - | Auto-close OAuth browsing contexts after success |
396
397### Workspace Styling
398
399| Key | Type | Default | Description |
400|-----|------|---------|-------------|
401| `workspace.styling.border_width` | int | `1` | Active pane border width (px) - overlay |
402| `workspace.styling.border_color` | string | `"@theme_selected_bg_color"` | Active pane border color |
403| `workspace.styling.mode_border_width` | int | `4` | Modal mode border width (px) - applies to all modes |
404| `workspace.styling.pane_mode_color` | string | `"#4A90E2"` | Pane mode color (blue) - used for border and toaster |
405| `workspace.styling.tab_mode_color` | string | `"#FFA500"` | Tab mode color (orange) - used for border and toaster |
406| `workspace.styling.session_mode_color` | string | `"#9B59B6"` | Session mode color (purple) - used for border and toaster |
407| `workspace.styling.resize_mode_color` | string | `"#00D4AA"` | Resize mode color (teal) - used for border and toaster |
408| `workspace.styling.mode_indicator_toaster_enabled` | bool | `true` | Show toaster notification when modal modes are active |
409| `workspace.styling.transition_duration` | int | `120` | Border transition duration (ms) |
410
411**Example:**
412```toml
413[workspace.styling]
414border_width = 1
415border_color = "@theme_selected_bg_color"
416mode_border_width = 4
417pane_mode_color = "#4A90E2" # Blue for pane mode
418tab_mode_color = "#FFA500" # Orange for tab mode
419session_mode_color = "#9B59B6" # Purple for session mode
420resize_mode_color = "#00D4AA" # Teal for resize mode
421mode_indicator_toaster_enabled = true
422transition_duration = 120
423```
424
425## Session
426
427| Key | Type | Default | Description |
428|-----|------|---------|-------------|
429| `session.auto_restore` | bool | `false` | Automatically restore the last session on startup |
430| `session.snapshot_interval_ms` | int | `5000` | Minimum interval between snapshots in milliseconds |
431| `session.max_exited_sessions` | int | `50` | Maximum number of exited sessions to keep |
432| `session.max_exited_session_age_days` | int | `7` | Maximum age in days for exited sessions (auto-deleted on startup) |
433
434### Session Mode
435
436| Key | Type | Default | Description |
437|-----|------|---------|-------------|
438| `session.session_mode.activation_shortcut` | string | `"ctrl+o"` | Session mode activation key |
439| `session.session_mode.timeout_ms` | int | `3000` | Session mode timeout (ms) |
440| `session.session_mode.actions` | map | See below | Action to keys mappings |
441
442**Default session mode actions:**
443```toml
444[session.session_mode.actions]
445session-manager = ["s", "w"]
446confirm = ["enter"]
447cancel = ["escape"]
448```
449
450**Example:**
451```toml
452[session]
453auto_restore = false # Don't auto-restore on startup
454snapshot_interval_ms = 5000 # Save state every 5 seconds (debounced)
455max_exited_sessions = 50 # Keep last 50 exited sessions
456max_exited_session_age_days = 7 # Delete sessions older than 7 days on startup
457```
458
459## Media
460
461| Key | Type | Default | Valid Values | Description |
462|-----|------|---------|--------------|-------------|
463| `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode |
464| `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available |
465| `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup |
466| `media.force_vsync` | bool | `false` | - | Force VSync for video playback (may help with tearing) |
467| `media.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | OpenGL API selection for video rendering |
468| `media.gstreamer_debug_level` | int | `0` | `0-5` | GStreamer debug verbosity (0=off) |
469
470**Hardware decoding modes:**
471- `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000
472- `force`: Hardware only - fails if unavailable
473- `disable`: Software only - higher CPU usage
474
475**GL rendering modes:**
476- `auto` (default): Let GStreamer choose the best OpenGL API
477- `gles2`: Force GLES2 (better for some mobile/embedded GPUs)
478- `gl3`: Force OpenGL 3.x desktop
479- `none`: Disable GL-based rendering
480
481**GPU auto-detection:**
482Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings:
483- **AMD**: Uses `radeonsi` driver
484- **Intel**: Uses `iHD` driver (modern, for Broadwell+)
485- **NVIDIA**: Uses `nvidia` driver with EGL platform
486
487**Example:**
488```toml
489[media]
490hardware_decoding = "auto" # HW preferred, SW fallback
491prefer_av1 = false # Let site choose codec
492show_diagnostics = false # Keep off for daily use
493force_vsync = false # Let compositor handle VSync
494gl_rendering_mode = "auto" # GStreamer picks best GL API
495gstreamer_debug_level = 0 # Increase to 3-5 for debugging
496```
497
498**Diagnostic mode toggle path:**
499
500```toml
501# ~/.config/dumber/config.toml
502[media]
503show_diagnostics = true
504gstreamer_debug_level = 3
505```
506
507Set `show_diagnostics = false` and `gstreamer_debug_level = 0` to return to normal mode.
508
509**Diagnostics CLI:**
510```bash
511# Check GStreamer plugins and VA-API status
512dumber doctor --media
513```
514
515## Runtime
516
517| Key | Type | Default | Description |
518|-----|------|---------|-------------|
519| `runtime.prefix` | string | `""` | Optional runtime prefix (e.g., `/opt/webkitgtk`) used to locate newer GTK/WebKitGTK installs |
520
521If set, dumber prepends prefix-derived paths to:
522- `PKG_CONFIG_PATH` (for version checks)
523- `LD_LIBRARY_PATH` (for runtime library loading)
524- `GI_TYPELIB_PATH` (GObject introspection)
525- `XDG_DATA_DIRS` (schemas/resources)
526
527**Example:**
528```toml
529[runtime]
530prefix = "/opt/webkitgtk"
531```
532
533## Clipboard
534
535| Key | Type | Default | Description |
536|-----|------|---------|-------------|
537| `clipboard.auto_copy_on_selection` | bool | `true` | Automatically copy selected text to clipboard (zellij/tmux-style) |
538
539When enabled, selecting text in a web page immediately copies it to the clipboard with a brief toast notification. Does not apply to text selection in input fields or textareas.
540
541**Example:**
542```toml
543[clipboard]
544auto_copy_on_selection = true # Enabled by default
545```
546
547## Content Filtering
548
549| Key | Type | Default | Description |
550|-----|------|---------|-------------|
551| `content_filtering.enabled` | bool | `true` | Enable ad blocking |
552| `content_filtering.auto_update` | bool | `true` | Automatically update filters |
553
554Notes:
555- Filter data is downloaded from `bnema/ublock-webkit-filters` GitHub releases.
556- Domain whitelist is managed via the database (`content_whitelist` table), not the config file.
557
558## Update
559
560| Key | Type | Default | Description |
561|-----|------|---------|-------------|
562| `update.enable_on_startup` | bool | `true` | Check for updates when browser starts |
563| `update.auto_download` | bool | `false` | Automatically download updates in background |
564| `update.notify_on_new_settings` | bool | `true` | Show toast notification when new config settings are available |
565
566**Example:**
567```toml
568[update]
569enable_on_startup = true # Check for updates on startup
570auto_download = false # Don't auto-download (prompt instead)
571notify_on_new_settings = true # Show toast when config migration available
572```
573
574**CLI Commands:**
575```bash
576# Check config status and available migrations
577dumber config status
578
579# Add missing settings with default values
580dumber config migrate
581
582# Skip confirmation prompt
583dumber config migrate --yes
584```
585
586## Performance Profiles
587
588Performance profiles provide preset configurations for WebKitGTK tuning. These settings affect Skia rendering threads, memory pressure handling, and WebView pool behavior.
589
590> **Note:** Performance settings are applied at browser startup. Changes require a restart to take effect.
591
592| Key | Type | Default | Valid Values | Description |
593|-----|------|---------|--------------|-------------|
594| `performance.profile` | string | `"default"` | `default`, `lite`, `max`, `custom` | Performance profile selection |
595
596### Profiles
597
598| Profile | Description | Use Case |
599|---------|-------------|----------|
600| `default` | No tuning, uses WebKit defaults | Compatibility baseline, troubleshooting |
601| `lite` | Reduced resource usage | Low-RAM systems (< 4GB), battery saving |
602| `max` | Maximum responsiveness | Heavy pages (GitHub PRs, complex SPAs), high-end systems |
603| `custom` | Manual control over all settings | Advanced users who want fine-grained control |
604
605### Profile Settings Matrix
606
607| Setting | default | lite | max |
608|---------|---------|------|-----|
609| Skia CPU threads | unset | 2 | `NumCPU()/2` (min 4) |
610| Skia GPU threads | unset | unset | scales with VRAM |
611| Web process memory (MB) | unset | 768 | unset |
612| Network process memory (MB) | unset | 384 | unset |
613| Conservative threshold | unset | 0.25 | unset |
614| Strict threshold | unset | 0.4 | unset |
615| WebView pool prewarm | 4 | 2 | scales with RAM |
616
617**Example:**
618```toml
619[performance]
620profile = "default" # WebKit baseline default
621
622# For WebKit baseline behavior:
623# profile = "default"
624
625# For heavy pages (GitHub PRs, complex web apps):
626# profile = "max"
627
628# For manual control:
629# profile = "custom"
630```
631
632### Custom Profile Settings
633
634When `profile = "custom"`, you can configure individual tuning options:
635
636| Key | Type | Default | Description |
637|-----|------|---------|-------------|
638| `performance.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) |
639| `performance.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) |
640| `performance.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering |
641| `performance.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) |
642| `performance.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) |
643| `performance.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) |
644| `performance.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) |
645| `performance.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB |
646| `performance.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval |
647| `performance.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold |
648| `performance.network_process_memory_strict_threshold` | float | `0` | Network strict threshold |
649| `performance.webview_pool_prewarm_count` | int | `4` | WebViews to pre-create at startup |
650| `performance.zoom_cache_size` | int | `256` | Domain zoom levels to cache |
651
652**Custom profile example:**
653```toml
654[performance]
655profile = "custom"
656
657# Skia threading - tune for your CPU
658skia_cpu_painting_threads = 4
659skia_gpu_painting_threads = 2
660
661# Web process memory pressure
662web_process_memory_limit_mb = 1024
663web_process_memory_conservative_threshold = 0.4
664web_process_memory_strict_threshold = 0.6
665
666# WebView pool
667webview_pool_prewarm_count = 6
668```
669
670> **Important:** Individual tuning fields are ignored unless `profile = "custom"`. Setting individual fields with any other profile will produce a validation warning.
671
672## Downloads
673
674| Key | Type | Default | Description |
675|-----|------|---------|-------------|
676| `downloads.path` | string | `""` | Download directory path (empty = `$XDG_DOWNLOAD_DIR` or `~/Downloads`) |
677
678Downloads are saved to the configured directory with toast notifications for download started, completed, and failed events.
679
680**Example:**
681```toml
682[downloads]
683path = "" # Use system default ($XDG_DOWNLOAD_DIR or ~/Downloads)
684
685# Or specify a custom directory:
686# path = "/home/user/my-downloads"
687```
688
689## Environment Variables
690
691All config values can be overridden via environment variables with the prefix `DUMBER_`:
692
693```bash
694# Database
695DUMBER_DATABASE_PATH=/custom/path/db.sqlite
696
697# Rendering (examples)
698# CEF defaults to the GPU-first Vulkan DMABUF stack.
699# Use EGL/OpenGL for driver compatibility:
700# DUMBER_ENGINE_CEF_RENDER_STACK=egl
701DUMBER_RENDERING_MODE=cpu
702# WebKit-specific rendering overrides:
703# DUMBER_RENDERING_DISABLE_DMABUF_RENDERER=true
704# DUMBER_RENDERING_GSK_RENDERER=opengl
705DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
706
707# Logging
708DUMBER_LOG_LEVEL=debug
709DUMBER_LOG_FORMAT=json
710```