Configuration Reference
Primary format: TOML (recommended)
Location: ~/.config/dumber/config.toml
Also supports: JSON (.json) and YAML (.yaml)
JSON Schema: ~/.config/dumber/config.schema.json (auto-generated for JSON format)
Why TOML?
TOML is the recommended format for dumber configuration because:
- Clear structure: Explicit sections make complex configs easy to read and modify
- No ambiguity: No whitespace sensitivity or type coercion issues
- Comments supported: Add documentation directly in your config
- Industry standard: Used by Cargo, Hugo, and many modern tools
You can still use JSON or YAML if you prefer - dumber automatically detects the format.
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_items |
int |
20 |
Max items shown in launcher |
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 popularity |
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 max items via CLI flag
dumber dmenu --max 50
Omnibox
| Key |
Type |
Default |
Valid Values |
Description |
omnibox.initial_behavior |
string |
"recent" |
recent, most_visited, none |
Initial history display behavior |
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
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" |
debug, info, warn, error |
Log level |
logging.format |
string |
"text" |
text, json |
Log format |
logging.max_age |
int |
7 |
>= 0 |
Days to keep logs |
logging.log_dir |
string |
~/.local/state/dumber/logs/ |
any |
Log directory |
logging.enable_file_log |
bool |
true |
- |
Enable file logging |
logging.capture_console |
bool |
false |
- |
Capture browser console to logs |
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) |
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:) |
Example:
[workspace]
new_pane_url = "dumb://home"
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"]
# Consume-or-expel (niri-style) - very alpha
consume-or-expel-left = ["bracketleft", "["]
consume-or-expel-right = ["bracketright", "]"]
consume-or-expel-up = ["shift+bracketleft", "braceleft", "{"]
consume-or-expel-down = ["shift+bracketright", "braceright", "}"]
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
| Key |
Type |
Default |
Description |
workspace.shortcuts.close_pane |
string |
"ctrl+w" |
Close active pane (closes tab if last pane) |
workspace.shortcuts.next_tab |
string |
"ctrl+tab" |
Next tab shortcut |
workspace.shortcuts.previous_tab |
string |
"ctrl+shift+tab" |
Previous tab shortcut |
workspace.shortcuts.consume_or_expel_left |
string |
"alt+bracketleft" |
Consume into left sibling stack, or expel left if stacked (very alpha) |
workspace.shortcuts.consume_or_expel_right |
string |
"alt+bracketright" |
Consume into right sibling stack, or expel right if stacked (very alpha) |
workspace.shortcuts.consume_or_expel_up |
string |
"alt+shift+bracketleft" |
Consume into upper sibling stack, or expel up if stacked (very alpha) |
workspace.shortcuts.consume_or_expel_down |
string |
"alt+shift+bracketright" |
Consume into lower sibling stack, or expel down if stacked (very alpha) |
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.
| Key |
Type |
Default |
Valid Values |
Description |
workspace.popups.behavior |
string |
"split" |
split, stacked, tabbed, windowed |
Popup opening mode |
workspace.popups.placement |
string |
"right" |
right, left, top, bottom |
Split direction |
workspace.popups.open_in_new_pane |
bool |
true |
- |
Open popups in workspace |
workspace.popups.follow_pane_context |
bool |
true |
- |
Follow parent pane context |
workspace.popups.blank_target_behavior |
string |
"pane" |
pane, tab |
_blank target handling |
workspace.popups.enable_smart_detection |
bool |
true |
- |
Use WindowProperties for detection |
workspace.popups.oauth_auto_close |
bool |
true |
- |
Auto-close OAuth popups |
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.pane_mode_border_width |
int |
4 |
Pane mode border width (px) - Ctrl+P N overlay |
workspace.styling.pane_mode_border_color |
string |
"#4A90E2" |
Pane mode border color (blue) |
workspace.styling.tab_mode_border_width |
int |
4 |
Tab mode border width (px) - Ctrl+P T overlay |
workspace.styling.tab_mode_border_color |
string |
"#FFA500" |
Tab mode border color (orange) |
workspace.styling.session_mode_border_width |
int |
4 |
Session mode border width (px) - Ctrl+O overlay |
workspace.styling.session_mode_border_color |
string |
"#9B59B6" |
Session mode border color (purple) |
workspace.styling.resize_mode_border_width |
int |
4 |
Resize mode border width (px) |
workspace.styling.resize_mode_border_color |
string |
"#00D4AA" |
Resize mode border color (teal) |
workspace.styling.transition_duration |
int |
120 |
Border transition duration (ms) |
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 # Enable for debugging
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
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"
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.
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)
# Defaults: rendering.disable_dmabuf_renderer=false, rendering.gsk_renderer="auto"
DUMBER_RENDERING_MODE=cpu
# 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 Reference
2
3**Primary format**: TOML (recommended)
4**Location**: `~/.config/dumber/config.toml`
5**Also supports**: JSON (`.json`) and YAML (`.yaml`)
6**JSON Schema**: `~/.config/dumber/config.schema.json` (auto-generated for JSON format)
7
8## Why TOML?
9
10TOML is the recommended format for dumber configuration because:
11- **Clear structure**: Explicit sections make complex configs easy to read and modify
12- **No ambiguity**: No whitespace sensitivity or type coercion issues
13- **Comments supported**: Add documentation directly in your config
14- **Industry standard**: Used by Cargo, Hugo, and many modern tools
15
16You can still use JSON or YAML if you prefer - dumber automatically detects the format.
17
18## Database
19
20| Key | Type | Default | Description |
21|-----|------|---------|-------------|
22| `database.path` | string | `~/.local/share/dumber/dumber.db` | Database file path |
23
24## History
25
26| Key | Type | Default | Valid Values | Description |
27|-----|------|---------|--------------|-------------|
28| `history.max_entries` | int | `10000` | > 0 | Maximum number of history entries |
29| `history.retention_period_days` | int | `365` | > 0 | Days to keep history (1 year) |
30| `history.cleanup_interval_days` | int | `1` | > 0 | How often to run cleanup |
31
32## Search Configuration
33
34| Key | Type | Default | Description |
35|-----|------|---------|-------------|
36| `default_search_engine` | string | `"https://duckduckgo.com/?q=%s"` | Default search engine URL template (must contain `%s` placeholder) |
37| `search_shortcuts` | map | See defaults | Map of shortcut aliases to URLs |
38
39**Example:**
40```toml
41# Default search engine - used when no shortcut is specified
42default_search_engine = "https://duckduckgo.com/?q=%s"
43
44# Alternative search engines:
45# default_search_engine = "https://www.google.com/search?q=%s"
46# default_search_engine = "https://www.startpage.com/search?q=%s"
47# default_search_engine = "https://search.brave.com/search?q=%s"
48```
49
50**Default shortcuts (usage: `!shortcut query`):**
51```toml
52# Examples:
53# !ddg golang → DuckDuckGo search for "golang"
54# !g rust tutorial → Google search for "rust tutorial"
55# !gi cats → Google Images search for "cats"
56# !gh opencode → GitHub search for "opencode"
57# !yt music video → YouTube search for "music video"
58
59[search_shortcuts.ddg]
60url = "https://duckduckgo.com/?q=%s"
61description = "DuckDuckGo search"
62
63[search_shortcuts.g]
64url = "https://google.com/search?q=%s"
65description = "Google Search"
66
67[search_shortcuts.gi]
68url = "https://google.com/search?tbm=isch&q=%s"
69description = "Google Images"
70
71[search_shortcuts.gh]
72url = "https://github.com/search?q=%s"
73description = "GitHub"
74
75[search_shortcuts.yt]
76url = "https://youtube.com/results?search_query=%s"
77description = "YouTube"
78```
79
80## Dmenu / Launcher
81
82These settings control the `dumber dmenu` CLI command for rofi/fuzzel integration.
83
84| Key | Type | Default | Description |
85|-----|------|---------|-------------|
86| `dmenu.max_history_items` | int | `20` | Max items shown in launcher |
87| `dmenu.show_visit_count` | bool | `true` | Show visit counts in output |
88| `dmenu.show_last_visited` | bool | `true` | Show last visited dates in output |
89| `dmenu.history_prefix` | string | `"🕒"` | Prefix for history items |
90| `dmenu.shortcut_prefix` | string | `"🔍"` | Prefix for shortcuts |
91| `dmenu.url_prefix` | string | `"🌐"` | Prefix for URLs |
92| `dmenu.date_format` | string | `"2006-01-02 15:04"` | Go time format string |
93| `dmenu.sort_by_visit_count` | bool | `true` | Sort by popularity |
94
95**CLI Usage:**
96```bash
97# Pipe mode (default) - outputs history for rofi/fuzzel with favicons
98dumber dmenu | rofi -dmenu -show-icons -p "Browse: " | dumber dmenu --select
99dumber dmenu | fuzzel --dmenu -p "Browse: " | dumber dmenu --select
100
101# Interactive TUI mode
102dumber dmenu --interactive
103
104# Override max items via CLI flag
105dumber dmenu --max 50
106```
107
108## Omnibox
109
110| Key | Type | Default | Valid Values | Description |
111|-----|------|---------|--------------|-------------|
112| `omnibox.initial_behavior` | string | `"recent"` | `recent`, `most_visited`, `none` | Initial history display behavior |
113| `omnibox.auto_open_on_new_pane` | bool | `false` | - | Automatically open the omnibox after creating a new pane |
114
115**Example:**
116```toml
117[omnibox]
118initial_behavior = "recent" # Show recent history when omnibox opens
119auto_open_on_new_pane = false
120
121# Alternative options:
122# initial_behavior = "most_visited" # Show most visited sites
123# initial_behavior = "none" # Show no initial suggestions
124```
125
126## Logging
127
128| Key | Type | Default | Valid Values | Description |
129|-----|------|---------|--------------|-------------|
130| `logging.level` | string | `"info"` | `debug`, `info`, `warn`, `error` | Log level |
131| `logging.format` | string | `"text"` | `text`, `json` | Log format |
132| `logging.max_age` | int | `7` | >= 0 | Days to keep logs |
133| `logging.log_dir` | string | `~/.local/state/dumber/logs/` | any | Log directory |
134| `logging.enable_file_log` | bool | `true` | - | Enable file logging |
135| `logging.capture_console` | bool | `false` | - | Capture browser console to logs |
136
137## Appearance
138
139| Key | Type | Default | Description |
140|-----|------|---------|-------------|
141| `appearance.sans_font` | string | `"Fira Sans"` | Sans-serif font |
142| `appearance.serif_font` | string | `"Fira Sans"` | Serif font |
143| `appearance.monospace_font` | string | `"Fira Code"` | Monospace font |
144| `appearance.default_font_size` | int | `16` | Font size in points |
145| `appearance.color_scheme` | string | `"default"` | `prefer-dark`, `prefer-light`, `default` |
146
147### Color Palettes
148
149**Light palette:**
150```toml
151[appearance.light_palette]
152background = "#f8f8f8"
153surface = "#f2f2f2"
154surface_variant = "#ececec"
155text = "#1a1a1a"
156muted = "#6e6e6e"
157accent = "#404040"
158border = "#d2d2d2"
159```
160
161**Dark palette:**
162```toml
163[appearance.dark_palette]
164background = "#0e0e0e"
165surface = "#1a1a1a"
166surface_variant = "#141414"
167text = "#e4e4e4"
168muted = "#848484"
169accent = "#a8a8a8"
170border = "#363636"
171```
172
173## Debug Options
174
175| Key | Type | Default | Description |
176|-----|------|---------|-------------|
177| `debug.enable_devtools` | bool | `true` | Enable browser developer tools (F12, Inspect Element) |
178
179## Rendering, UI Scale & Zoom
180
181| Key | Type | Default | Valid Values | Description |
182|-----|------|---------|--------------|-------------|
183| `rendering.mode` | string | `"gpu"` | `auto`, `gpu`, `cpu` | WebKit hardware acceleration policy |
184| `rendering.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit DMA-BUF renderer (may fix flicker on Wayland; slower) |
185| `rendering.force_compositing_mode` | bool | `false` | - | Force WebKit compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) |
186| `rendering.disable_compositing_mode` | bool | `false` | - | Disable WebKit compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) |
187| `rendering.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | GTK renderer selection (`GSK_RENDERER`) |
188| `rendering.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps (`GSK_GPU_DISABLE=mipmap`) |
189| `rendering.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES (`GDK_DEBUG=gl-prefer-gl`) |
190| `rendering.draw_compositing_indicators` | bool | `false` | - | Draw WebKit compositing indicators (debug) |
191| `rendering.show_fps` | bool | `false` | - | Show WebKit FPS counter (`WEBKIT_SHOW_FPS`) |
192| `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) |
193| `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) |
194| `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) |
195| `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) |
196
197## Workspace Configuration
198
199### General
200
201| Key | Type | Default | Description |
202|-----|------|---------|-------------|
203| `workspace.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) |
204
205**Example:**
206```toml
207[workspace]
208new_pane_url = "dumb://home"
209```
210
211### Pane Mode
212
213| Key | Type | Default | Description |
214|-----|------|---------|-------------|
215| `workspace.pane_mode.activation_shortcut` | string | `"ctrl+p"` | Pane mode activation key |
216| `workspace.pane_mode.timeout_ms` | int | `3000` | Pane mode timeout (ms) |
217| `workspace.pane_mode.actions` | map | See below | Action→keys mappings |
218
219**Default pane mode actions:**
220```toml
221[workspace.pane_mode.actions]
222split-right = ["arrowright", "r"]
223split-left = ["arrowleft", "l"]
224split-up = ["arrowup", "u"]
225split-down = ["arrowdown", "d"]
226stack-pane = ["s"]
227close-pane = ["x"]
228
229# Consume-or-expel (niri-style) - very alpha
230consume-or-expel-left = ["bracketleft", "["]
231consume-or-expel-right = ["bracketright", "]"]
232consume-or-expel-up = ["shift+bracketleft", "braceleft", "{"]
233consume-or-expel-down = ["shift+bracketright", "braceright", "}"]
234
235focus-right = ["shift+arrowright", "shift+l"]
236focus-left = ["shift+arrowleft", "shift+h"]
237focus-up = ["shift+arrowup", "shift+k"]
238focus-down = ["shift+arrowdown", "shift+j"]
239confirm = ["enter"]
240cancel = ["escape"]
241```
242
243> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
244
245### Tab Mode
246
247| Key | Type | Default | Description |
248|-----|------|---------|-------------|
249| `workspace.tab_mode.activation_shortcut` | string | `"ctrl+t"` | Tab mode activation key |
250| `workspace.tab_mode.timeout_ms` | int | `3000` | Tab mode timeout (ms) |
251| `workspace.tab_mode.actions` | map | See below | Action→keys mappings |
252
253**Default tab mode actions:**
254```toml
255[workspace.tab_mode.actions]
256new-tab = ["n", "c"]
257close-tab = ["x"]
258next-tab = ["l", "tab"]
259previous-tab = ["h", "shift+tab"]
260rename-tab = ["r"]
261confirm = ["enter"]
262cancel = ["escape"]
263```
264
265> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
266
267### Resize Mode
268
269| Key | Type | Default | Description |
270|-----|------|---------|-------------|
271| `workspace.resize_mode.activation_shortcut` | string | `"ctrl+n"` | Resize mode activation key |
272| `workspace.resize_mode.timeout_ms` | int | `3000` | Resize mode timeout (ms) |
273| `workspace.resize_mode.step_percent` | float | `5.0` | Split ratio step per keystroke (percent) |
274| `workspace.resize_mode.min_pane_percent` | float | `10.0` | Minimum pane size (percent) |
275| `workspace.resize_mode.actions` | map | See below | Action→keys mappings |
276
277**Default resize mode actions:**
278```toml
279[workspace.resize_mode.actions]
280resize-increase-left = ["h", "arrowleft"]
281resize-increase-down = ["j", "arrowdown"]
282resize-increase-up = ["k", "arrowup"]
283resize-increase-right = ["l", "arrowright"]
284resize-decrease-left = ["H"]
285resize-decrease-down = ["J"]
286resize-decrease-up = ["K"]
287resize-decrease-right = ["L"]
288resize-increase = ["+", "="]
289resize-decrease = ["-"]
290confirm = ["enter"]
291cancel = ["escape"]
292```
293
294Notes:
295- Directional actions (`resize-increase-*/resize-decrease-*`) move the split divider.
296- Smart actions (`resize-increase` / `resize-decrease`) grow/shrink the active pane (best-effort) by picking a direction automatically.
297- Timeout is refreshed on each resize keypress so you can keep adjusting without re-entering the mode.
298
299### Global Shortcuts
300
301| Key | Type | Default | Description |
302|-----|------|---------|-------------|
303| `workspace.shortcuts.close_pane` | string | `"ctrl+w"` | Close active pane (closes tab if last pane) |
304| `workspace.shortcuts.next_tab` | string | `"ctrl+tab"` | Next tab shortcut |
305| `workspace.shortcuts.previous_tab` | string | `"ctrl+shift+tab"` | Previous tab shortcut |
306| `workspace.shortcuts.consume_or_expel_left` | string | `"alt+bracketleft"` | Consume into left sibling stack, or expel left if stacked (very alpha) |
307| `workspace.shortcuts.consume_or_expel_right` | string | `"alt+bracketright"` | Consume into right sibling stack, or expel right if stacked (very alpha) |
308| `workspace.shortcuts.consume_or_expel_up` | string | `"alt+shift+bracketleft"` | Consume into upper sibling stack, or expel up if stacked (very alpha) |
309| `workspace.shortcuts.consume_or_expel_down` | string | `"alt+shift+bracketright"` | Consume into lower sibling stack, or expel down if stacked (very alpha) |
310
311> **Note:** New tab creation uses modal tab mode (Ctrl+T then n/c). This follows the Zellij-style modal keyboard interface.
312>
313> **Note:** Consume-or-expel is experimental and may change behavior between releases.
314
315### Popup Behavior
316
317| Key | Type | Default | Valid Values | Description |
318|-----|------|---------|--------------|-------------|
319| `workspace.popups.behavior` | string | `"split"` | `split`, `stacked`, `tabbed`, `windowed` | Popup opening mode |
320| `workspace.popups.placement` | string | `"right"` | `right`, `left`, `top`, `bottom` | Split direction |
321| `workspace.popups.open_in_new_pane` | bool | `true` | - | Open popups in workspace |
322| `workspace.popups.follow_pane_context` | bool | `true` | - | Follow parent pane context |
323| `workspace.popups.blank_target_behavior` | string | `"pane"` | `pane`, `tab` | `_blank` target handling |
324| `workspace.popups.enable_smart_detection` | bool | `true` | - | Use WindowProperties for detection |
325| `workspace.popups.oauth_auto_close` | bool | `true` | - | Auto-close OAuth popups |
326
327### Workspace Styling
328
329| Key | Type | Default | Description |
330|-----|------|---------|-------------|
331| `workspace.styling.border_width` | int | `1` | Active pane border width (px) - overlay |
332| `workspace.styling.border_color` | string | `"@theme_selected_bg_color"` | Active pane border color |
333| `workspace.styling.pane_mode_border_width` | int | `4` | Pane mode border width (px) - Ctrl+P N overlay |
334| `workspace.styling.pane_mode_border_color` | string | `"#4A90E2"` | Pane mode border color (blue) |
335| `workspace.styling.tab_mode_border_width` | int | `4` | Tab mode border width (px) - Ctrl+P T overlay |
336| `workspace.styling.tab_mode_border_color` | string | `"#FFA500"` | Tab mode border color (orange) |
337| `workspace.styling.session_mode_border_width` | int | `4` | Session mode border width (px) - Ctrl+O overlay |
338| `workspace.styling.session_mode_border_color` | string | `"#9B59B6"` | Session mode border color (purple) |
339| `workspace.styling.resize_mode_border_width` | int | `4` | Resize mode border width (px) |
340| `workspace.styling.resize_mode_border_color` | string | `"#00D4AA"` | Resize mode border color (teal) |
341| `workspace.styling.transition_duration` | int | `120` | Border transition duration (ms) |
342
343## Session
344
345| Key | Type | Default | Description |
346|-----|------|---------|-------------|
347| `session.auto_restore` | bool | `false` | Automatically restore the last session on startup |
348| `session.snapshot_interval_ms` | int | `5000` | Minimum interval between snapshots in milliseconds |
349| `session.max_exited_sessions` | int | `50` | Maximum number of exited sessions to keep |
350| `session.max_exited_session_age_days` | int | `7` | Maximum age in days for exited sessions (auto-deleted on startup) |
351
352### Session Mode
353
354| Key | Type | Default | Description |
355|-----|------|---------|-------------|
356| `session.session_mode.activation_shortcut` | string | `"ctrl+o"` | Session mode activation key |
357| `session.session_mode.timeout_ms` | int | `3000` | Session mode timeout (ms) |
358| `session.session_mode.actions` | map | See below | Action to keys mappings |
359
360**Default session mode actions:**
361```toml
362[session.session_mode.actions]
363session-manager = ["s", "w"]
364confirm = ["enter"]
365cancel = ["escape"]
366```
367
368**Example:**
369```toml
370[session]
371auto_restore = false # Don't auto-restore on startup
372snapshot_interval_ms = 5000 # Save state every 5 seconds (debounced)
373max_exited_sessions = 50 # Keep last 50 exited sessions
374max_exited_session_age_days = 7 # Delete sessions older than 7 days on startup
375```
376
377## Media
378
379| Key | Type | Default | Valid Values | Description |
380|-----|------|---------|--------------|-------------|
381| `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode |
382| `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available |
383| `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup |
384| `media.force_vsync` | bool | `false` | - | Force VSync for video playback (may help with tearing) |
385| `media.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | OpenGL API selection for video rendering |
386| `media.gstreamer_debug_level` | int | `0` | `0-5` | GStreamer debug verbosity (0=off) |
387
388**Hardware decoding modes:**
389- `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000
390- `force`: Hardware only - fails if unavailable
391- `disable`: Software only - higher CPU usage
392
393**GL rendering modes:**
394- `auto` (default): Let GStreamer choose the best OpenGL API
395- `gles2`: Force GLES2 (better for some mobile/embedded GPUs)
396- `gl3`: Force OpenGL 3.x desktop
397- `none`: Disable GL-based rendering
398
399**GPU auto-detection:**
400Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings:
401- **AMD**: Uses `radeonsi` driver
402- **Intel**: Uses `iHD` driver (modern, for Broadwell+)
403- **NVIDIA**: Uses `nvidia` driver with EGL platform
404
405**Example:**
406```toml
407[media]
408hardware_decoding = "auto" # HW preferred, SW fallback
409prefer_av1 = false # Let site choose codec
410show_diagnostics = false # Enable for debugging
411force_vsync = false # Let compositor handle VSync
412gl_rendering_mode = "auto" # GStreamer picks best GL API
413gstreamer_debug_level = 0 # Increase to 3-5 for debugging
414```
415
416**Diagnostics CLI:**
417```bash
418# Check GStreamer plugins and VA-API status
419dumber doctor --media
420```
421
422## Runtime
423
424| Key | Type | Default | Description |
425|-----|------|---------|-------------|
426| `runtime.prefix` | string | `""` | Optional runtime prefix (e.g., `/opt/webkitgtk`) used to locate newer GTK/WebKitGTK installs |
427
428If set, dumber prepends prefix-derived paths to:
429- `PKG_CONFIG_PATH` (for version checks)
430- `LD_LIBRARY_PATH` (for runtime library loading)
431- `GI_TYPELIB_PATH` (GObject introspection)
432- `XDG_DATA_DIRS` (schemas/resources)
433
434**Example:**
435```toml
436[runtime]
437prefix = "/opt/webkitgtk"
438```
439
440## Content Filtering
441
442| Key | Type | Default | Description |
443|-----|------|---------|-------------|
444| `content_filtering.enabled` | bool | `true` | Enable ad blocking |
445| `content_filtering.auto_update` | bool | `true` | Automatically update filters |
446
447Notes:
448- Filter data is downloaded from `bnema/ublock-webkit-filters` GitHub releases.
449- Domain whitelist is managed via the database (`content_whitelist` table), not the config file.
450
451## Environment Variables
452
453All config values can be overridden via environment variables with the prefix `DUMBER_`:
454
455```bash
456# Database
457DUMBER_DATABASE_PATH=/custom/path/db.sqlite
458
459# Rendering (examples)
460# Defaults: rendering.disable_dmabuf_renderer=false, rendering.gsk_renderer="auto"
461DUMBER_RENDERING_MODE=cpu
462# DUMBER_RENDERING_DISABLE_DMABUF_RENDERER=true
463# DUMBER_RENDERING_GSK_RENDERER=opengl
464DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
465
466# Logging
467DUMBER_LOG_LEVEL=debug
468DUMBER_LOG_FORMAT=json
469```