v0.31.0 bf0756b

index.md RawHistoryFolder

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"

Dmenu / Launcher

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
appearance.external_theme.enabled bool false Enable an external palette source
appearance.external_theme.provider string "noctalia" External provider. Only noctalia is supported
appearance.external_theme.format string "colors-json" External file format: colors-json or dumber-json
appearance.external_theme.path string $XDG_CONFIG_HOME/noctalia/colors.json for colors-json; built-in dumber-json template path for dumber-json Path to the external theme JSON file

Color Palettes

Light palette:

[appearance.light_palette]
background = "#fafafa"
surface = "#f4f4f5"
surface_variant = "#e4e4e7"
text = "#18181b"
muted = "#71717a"
accent = "#22c55e"
border = "#d4d4d8"

Dark palette:

[appearance.dark_palette]
background = "#0a0a0b"
surface = "#18181b"
surface_variant = "#27272a"
text = "#fafafa"
muted = "#a1a1aa"
accent = "#4ade80"
border = "#3f3f46"

External Theme: Noctalia

Dumber can integrate with Noctalia by reading the native Noctalia palette file directly. No user template is required for the default integration.

[appearance.external_theme]
enabled = true
provider = "noctalia"
format = "colors-json" # default
# Optional. Defaults to $XDG_CONFIG_HOME/noctalia/colors.json.
path = "~/.config/noctalia/colors.json"

The external file overrides palette colors only. Fonts, UI scale, pane-mode colors, and other appearance settings still come from Dumber config.

colors-json contract

colors-json reads Noctalia's active colors.json palette. Example file: docs/examples/noctalia-colors.json.

Dumber maps Noctalia roles like this:

Noctalia key Dumber palette field
mShadow background when present, otherwise mSurface
mSurface surface
mHover surface_variant when present, otherwise mSurfaceVariant
mOnSurface text
mOnSurfaceVariant muted
mPrimary accent
mOutline border

Every mapped value must be a CSS-safe 6-digit hex color such as #aabbcc.

mShadow and mHover are optional nuance roles. When present, they are validated and used to preserve Dumber's background/surface/input contrast.

Because Noctalia's native file contains the active palette only, Dumber applies that mapped palette to both resolved light and dark palettes so the app follows the file immediately.

Advanced: dumber-json user template

dumber-json remains supported for an explicit Dumber-specific Noctalia user template:

[appearance.external_theme]
enabled = true
provider = "noctalia"
format = "dumber-json"
# Optional for dumber-json. Defaults to $XDG_CONFIG_HOME/dumber/noctalia-theme.json.
path = "~/.config/dumber/noctalia-theme.json"

The file must be JSON with root light and dark objects. Optional source, name, and mode metadata fields are accepted; name or source is used only as source metadata. Dumber chooses the active palette from appearance.color_scheme and the system color-scheme preference.

Supported palette fields are:

  • background
  • surface
  • surface_variant
  • text
  • muted
  • accent
  • border

Each non-empty palette value must be a CSS-safe 6-digit hex color such as #AABBCC. Empty or omitted palette fields inherit from the configured Dumber palette for that mode.

Example rendered file: docs/examples/noctalia-dumber-theme.json. Example template: docs/examples/noctalia-dumber-theme.template.json.

When Noctalia rewrites the watched file, Dumber reapplies the same resolved theme path used by config reloads. A malformed or temporarily missing file keeps the last valid external palette when one exists; disabling appearance.external_theme.enabled clears that last-good external palette and returns to Dumber's configured palettes.

Debug Options

Key Type Default Description
debug.enable_devtools bool true Enable browser developer tools (F12, Inspect Element)
engine.cef.log_file string "" CEF runtime log path
engine.cef.log_severity int32 0 CEF log severity (0, 1, 2, 3, 4, 99)
engine.cef.trace_handlers bool false Log CEF handler dispatch details
engine.cef.enable_audio_handler bool true Enable the experimental CEF audio handler

Privacy

Key Type Default Valid Values Description
engine.cookie_policy string "always" always, no_third_party, never Cookie acceptance policy
engine.webkit.itp_enabled bool true - Enable WebKit fallback Intelligent Tracking Prevention

Rendering, UI Scale & Zoom

CEF is the default browser engine. WebKitGTK remains available as a fallback via engine.type = "webkit"; engine.webkit.* settings only affect that fallback engine.

Key Type Default Valid Values Description
engine.type string "cef" cef, webkit Browser engine selection; WebKitGTK is the fallback option
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 when windowless_frame_rate = 0
engine.cef.windowless_frame_rate int32 0 >= 0 Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled
engine.cef.windowless_frame_rate_max int32 240 >= 0 Hard cap for adaptive CEF OSR FPS; 0 uses the built-in cap
engine.cef.input.scroll_wheel_multiplier float 1.0 > 0 Mouse wheel scroll sensitivity multiplier
engine.cef.input.scroll_precise_multiplier float 2.5 > 0 Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels
engine.cef.input.scroll_horizontal_multiplier float 1.0 > 0 Horizontal scroll sensitivity multiplier
engine.cef.input.scroll_vertical_multiplier float 1.0 > 0 Vertical scroll sensitivity multiplier; combines with scroll_precise_multiplier for touchpads
engine.cef.input.scroll_max_delta int32 0 >= 0 Maximum absolute scroll delta after scaling; 0 disables clamping
engine.cef.input.touchpad_navigation_enabled bool true - Enable two-finger touchpad swipe back/forward navigation
engine.cef.input.touchpad_navigation_min_delta float 320.0 > 0 Minimum accumulated horizontal swipe delta required for navigation
engine.cef.input.touchpad_navigation_max_vertical_ratio float 0.5 > 0 Maximum vertical-to-horizontal delta ratio allowed for navigation swipes
engine.webkit.gsk_renderer string "auto" auto, opengl, vulkan, cairo WebKit fallback GTK renderer selection (GSK_RENDERER)
engine.webkit.disable_dmabuf_renderer bool false - Disable WebKit fallback DMA-BUF renderer
engine.webkit.force_compositing_mode bool false - Force WebKit fallback compositing mode (WEBKIT_FORCE_COMPOSITING_MODE)
engine.webkit.disable_compositing_mode bool false - Disable WebKit fallback compositing mode (WEBKIT_DISABLE_COMPOSITING_MODE)
engine.webkit.disable_mipmaps bool false - Disable GTK mipmaps for the WebKit fallback (GSK_GPU_DISABLE=mipmap)
engine.webkit.prefer_gl bool false - Prefer OpenGL over GLES for the WebKit fallback (GDK_DEBUG=gl-prefer-gl)
engine.webkit.draw_compositing_indicators bool false - Draw WebKit fallback compositing indicators (debug)
engine.webkit.show_fps bool false - Show WebKit fallback FPS counter (WEBKIT_SHOW_FPS)
engine.webkit.sample_memory bool false - Enable WebKit fallback memory sampling (WEBKIT_SAMPLE_MEMORY)
engine.webkit.debug_frames bool false - Enable GTK frame timing debug for the WebKit fallback (GDK_DEBUG=frames)
engine.webkit.force_vsync bool false - Force VSync for WebKit fallback video playback
engine.webkit.gl_rendering_mode string "auto" auto, gles2, gl3, none WebKit fallback GStreamer/OpenGL API selection
engine.webkit.gstreamer_debug_level int 0 0-5 WebKit fallback GStreamer debug verbosity
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%)

engine.cef.input.scroll_precise_multiplier controls touchpad/high-resolution wheel scroll speed, and engine.cef.input.scroll_vertical_multiplier applies an additional vertical-only scale. engine.cef.input.touchpad_navigation_max_vertical_ratio only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed.

engine.cef.input.touchpad_navigation_min_delta uses raw GTK touchpad surface units for back/forward gestures. The default 320.0 matches WebKit-style commit distance to reduce accidental navigation; raise or lower it in config.toml to tune gesture sensitivity.

Legacy key migration

Existing configs using older keys are migrated to the current engine shape. New configs should use the canonical keys above.

Legacy key Current key
privacy.cookie_policy engine.cookie_policy
privacy.itp_enabled engine.webkit.itp_enabled
rendering.disable_dmabuf_renderer engine.webkit.disable_dmabuf_renderer
rendering.force_compositing_mode engine.webkit.force_compositing_mode
rendering.disable_compositing_mode engine.webkit.disable_compositing_mode
rendering.gsk_renderer engine.webkit.gsk_renderer
rendering.disable_mipmaps engine.webkit.disable_mipmaps
rendering.prefer_gl engine.webkit.prefer_gl
rendering.draw_compositing_indicators engine.webkit.draw_compositing_indicators
rendering.show_fps engine.webkit.show_fps
rendering.sample_memory engine.webkit.sample_memory
rendering.debug_frames engine.webkit.debug_frames
media.force_vsync engine.webkit.force_vsync
media.gl_rendering_mode engine.webkit.gl_rendering_mode
media.gstreamer_debug_level engine.webkit.gstreamer_debug_level
runtime.prefix engine.webkit.prefix
performance.* engine.profile, engine.pool_prewarm_count, engine.zoom_cache_size, or matching engine.webkit.* custom tuning keys
rendering.mode Dropped; no current setting

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"]
eject-pane-to-window = ["w"]

# 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.

Vim Mode

Key Type Default Description
workspace.vim_mode.activation_shortcut string "ctrl+y" Vim mode activation key
workspace.vim_mode.timeout_ms int 0 Vim mode timeout in milliseconds (0 disables auto-timeout)
workspace.vim_mode.sequence_timeout_ms int 500 Timeout for ambiguous multi-key Vim sequences
workspace.vim_mode.preload_accessibility bool false Pre-enable browser accessibility when a WebView is shown
workspace.vim_mode.actions map See below Action→binding mappings

Default vim mode actions:

[workspace.vim_mode.actions.vim-scroll-left]
keys = ["h"]
desc = "Scroll page left"

[workspace.vim_mode.actions.vim-scroll-down]
keys = ["j"]
desc = "Scroll page down"

[workspace.vim_mode.actions.vim-scroll-up]
keys = ["k"]
desc = "Scroll page up"

[workspace.vim_mode.actions.vim-scroll-right]
keys = ["l"]
desc = "Scroll page right"

[workspace.vim_mode.actions.vim-scroll-down-fast]
keys = ["shift+j"]
desc = "Scroll page down fast"

[workspace.vim_mode.actions.vim-scroll-up-fast]
keys = ["shift+k"]
desc = "Scroll page up fast"

[workspace.vim_mode.actions.focus-input]
keys = ["gi"]
desc = "Focus next page input"

[workspace.vim_mode.actions.heading-next]
keys = ["]]"]
desc = "Jump to next heading"

[workspace.vim_mode.actions.heading-prev]
keys = ["[["]
desc = "Jump to previous heading"

[workspace.vim_mode.actions.confirm]
keys = ["enter"]
desc = "Confirm action"

[workspace.vim_mode.actions.cancel]
keys = ["escape"]
desc = "Cancel/exit mode"

Notes:

  • Vim Mode scrolls and navigates the active webpage inside the active pane only.
  • gi focuses the next visible editable page input, starting at the first when no input is active and wrapping after the last.
  • ]] and [[ move through visible h1–h6 headings and use the current Dumber theme accent for the selected-heading outline.
  • The default timeout_ms = 0 means Vim Mode stays active until you exit it or focus moves into an editable/browser UI context. Ctrl+Y can still activate it when a page input is already focused.
  • Arrow keys still use the browser engine's native page-navigation path while Vim Mode is active.
  • Tab and Shift+Tab keep focus traversal inside the page while a page input is focused.
  • Live input, heading, and page-focus navigation currently use the CEF engine path; WebKit fallback supports the Vim scroll commands while equivalent semantic navigation is pending.
  • Other app-level shortcuts stay suspended until Vim Mode exits, except for the Vim Mode toggle itself.
  • workspace.styling.pane_mode_color, workspace.styling.transition_duration, and workspace.styling.mode_indicator_toaster_enabled control the pane-local Vim Mode visuals.
  • Scroll execution: CEF and WebKit both use the shared BuildPageScrollByJS resolver when the page is ready. Each step starts under the viewport center, walks up through ancestors that can move in the requested direction, and hands scrolling to the document when a nested container reaches its boundary. The application repeater owns held-key cadence; each engine executes one immediate scroll step per tick. Cross-origin frame contents remain best-effort. CEF may use native precision-wheel input only as a pre-frame fallback before the browser/main frame is ready.

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); also reused for Vim mode pane-local accent, indicator, and pulse
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 bottom-left mode toaster when modal modes are active; Vim Mode keeps a persistent VIM MODE indicator until exit or toggle off
workspace.styling.transition_duration int 120 Border and pulse 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 and Vim mode local accent
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

Media

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

WebKit fallback GStreamer tuning is configured under engine.webkit.force_vsync, engine.webkit.gl_rendering_mode, and engine.webkit.gstreamer_debug_level.

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

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

WebKit fallback GStreamer diagnostics:

[engine.webkit]
gstreamer_debug_level = 3

Set media.show_diagnostics = false and engine.webkit.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
engine.cef.cef_dir string "" Optional CEF runtime directory containing libcef.so and resources
engine.webkit.prefix string "" Optional WebKitGTK fallback runtime prefix (e.g., /opt/webkitgtk)

engine.cef.cef_dir overrides CEF runtime discovery. For the WebKitGTK fallback, engine.webkit.prefix 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:

[engine.cef]
cef_dir = "/opt/cef"

[engine.webkit]
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

Performance profiles tune engine resource behavior. CEF is the default engine; the engine.webkit.* custom fields below apply only when using the WebKitGTK fallback.

Note: Performance settings are applied at browser startup. Changes require a restart to take effect.

Key Type Default Valid Values Description
engine.profile string "default" default, lite, balanced, max, custom Performance profile selection
engine.pool_prewarm_count int 4 >= 0 WebViews to pre-create at startup
engine.zoom_cache_size int 256 >= 0 Domain zoom levels to cache

Profiles

Profile Description Use Case
default No tuning, uses engine defaults Compatibility baseline, troubleshooting
lite Reduced resource usage Low-RAM systems (< 4GB), battery saving
balanced Moderate tuning General use
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

Example:

[engine]
profile = "default"
pool_prewarm_count = 4
zoom_cache_size = 256

WebKit fallback custom profile settings

When engine.profile = "custom" and engine.type = "webkit", you can configure individual WebKit tuning options:

Key Type Default Description
engine.webkit.skia_cpu_painting_threads int 0 Skia CPU rendering threads (0=unset)
engine.webkit.skia_gpu_painting_threads int -1 Skia GPU rendering threads (-1=unset, 0=disable)
engine.webkit.skia_enable_cpu_rendering bool false Force CPU rendering
engine.webkit.web_process_memory_limit_mb int 0 Web process memory limit in MB (0=unset)
engine.webkit.web_process_memory_poll_interval_sec float 0 Memory check interval (0=WebKit default: 30s)
engine.webkit.web_process_memory_conservative_threshold float 0 Conservative cleanup threshold (0=unset)
engine.webkit.web_process_memory_strict_threshold float 0 Strict cleanup threshold (0=unset)
engine.webkit.network_process_memory_limit_mb int 0 Network process memory limit in MB
engine.webkit.network_process_memory_poll_interval_sec float 0 Network memory check interval
engine.webkit.network_process_memory_conservative_threshold float 0 Network conservative threshold
engine.webkit.network_process_memory_strict_threshold float 0 Network strict threshold

Custom WebKit fallback example:

[engine]
type = "webkit"
profile = "custom"
pool_prewarm_count = 6
zoom_cache_size = 256

[engine.webkit]
skia_cpu_painting_threads = 4
skia_gpu_painting_threads = 2
web_process_memory_limit_mb = 1024
web_process_memory_conservative_threshold = 0.4
web_process_memory_strict_threshold = 0.6

Important: Individual engine.webkit.* tuning fields are ignored unless engine.profile = "custom".

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

# Engine/rendering (examples)
DUMBER_ENGINE_TYPE=cef
# CEF defaults to the GPU-first Vulkan DMABUF stack.
# Use EGL/OpenGL for driver compatibility:
# DUMBER_ENGINE_CEF_RENDER_STACK=egl
# WebKit fallback rendering overrides:
# DUMBER_ENGINE_WEBKIT_DISABLE_DMABUF_RENDERER=true
# DUMBER_ENGINE_WEBKIT_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| `appearance.external_theme.enabled` | bool | `false` | Enable an external palette source |
145| `appearance.external_theme.provider` | string | `"noctalia"` | External provider. Only `noctalia` is supported |
146| `appearance.external_theme.format` | string | `"colors-json"` | External file format: `colors-json` or `dumber-json` |
147| `appearance.external_theme.path` | string | `$XDG_CONFIG_HOME/noctalia/colors.json` for `colors-json`; built-in dumber-json template path for `dumber-json` | Path to the external theme JSON file |
148
149### Color Palettes
150
151**Light palette:**
152
153```toml
154[appearance.light_palette]
155background = "#fafafa"
156surface = "#f4f4f5"
157surface_variant = "#e4e4e7"
158text = "#18181b"
159muted = "#71717a"
160accent = "#22c55e"
161border = "#d4d4d8"
162```
163
164**Dark palette:**
165
166```toml
167[appearance.dark_palette]
168background = "#0a0a0b"
169surface = "#18181b"
170surface_variant = "#27272a"
171text = "#fafafa"
172muted = "#a1a1aa"
173accent = "#4ade80"
174border = "#3f3f46"
175```
176
177### External Theme: Noctalia
178
179Dumber can integrate with Noctalia by reading the native Noctalia palette file directly. No user template is required for the default integration.
180
181```toml
182[appearance.external_theme]
183enabled = true
184provider = "noctalia"
185format = "colors-json" # default
186# Optional. Defaults to $XDG_CONFIG_HOME/noctalia/colors.json.
187path = "~/.config/noctalia/colors.json"
188```
189
190The external file overrides palette colors only. Fonts, UI scale, pane-mode colors, and other appearance settings still come from Dumber config.
191
192#### `colors-json` contract
193
194`colors-json` reads Noctalia's active `colors.json` palette. Example file: [`docs/examples/noctalia-colors.json`](../examples/noctalia-colors.json).
195
196Dumber maps Noctalia roles like this:
197
198| Noctalia key | Dumber palette field |
199|--------------|----------------------|
200| `mShadow` | `background` when present, otherwise `mSurface` |
201| `mSurface` | `surface` |
202| `mHover` | `surface_variant` when present, otherwise `mSurfaceVariant` |
203| `mOnSurface` | `text` |
204| `mOnSurfaceVariant` | `muted` |
205| `mPrimary` | `accent` |
206| `mOutline` | `border` |
207
208Every mapped value must be a CSS-safe 6-digit hex color such as `#aabbcc`.
209
210`mShadow` and `mHover` are optional nuance roles. When present, they are validated and used to preserve Dumber's background/surface/input contrast.
211
212Because Noctalia's native file contains the active palette only, Dumber applies that mapped palette to both resolved light and dark palettes so the app follows the file immediately.
213
214#### Advanced: `dumber-json` user template
215
216`dumber-json` remains supported for an explicit Dumber-specific Noctalia user template:
217
218```toml
219[appearance.external_theme]
220enabled = true
221provider = "noctalia"
222format = "dumber-json"
223# Optional for dumber-json. Defaults to $XDG_CONFIG_HOME/dumber/noctalia-theme.json.
224path = "~/.config/dumber/noctalia-theme.json"
225```
226
227The file must be JSON with root `light` and `dark` objects. Optional `source`, `name`, and `mode` metadata fields are accepted; `name` or `source` is used only as source metadata. Dumber chooses the active palette from `appearance.color_scheme` and the system color-scheme preference.
228
229Supported palette fields are:
230
231- `background`
232- `surface`
233- `surface_variant`
234- `text`
235- `muted`
236- `accent`
237- `border`
238
239Each non-empty palette value must be a CSS-safe 6-digit hex color such as `#AABBCC`. Empty or omitted palette fields inherit from the configured Dumber palette for that mode.
240
241Example rendered file: [`docs/examples/noctalia-dumber-theme.json`](../examples/noctalia-dumber-theme.json). Example template: [`docs/examples/noctalia-dumber-theme.template.json`](../examples/noctalia-dumber-theme.template.json).
242
243When Noctalia rewrites the watched file, Dumber reapplies the same resolved theme path used by config reloads. A malformed or temporarily missing file keeps the last valid external palette when one exists; disabling `appearance.external_theme.enabled` clears that last-good external palette and returns to Dumber's configured palettes.
244
245## Debug Options
246
247| Key | Type | Default | Description |
248|-----|------|---------|-------------|
249| `debug.enable_devtools` | bool | `true` | Enable browser developer tools (F12, Inspect Element) |
250| `engine.cef.log_file` | string | `""` | CEF runtime log path |
251| `engine.cef.log_severity` | int32 | `0` | CEF log severity (`0`, `1`, `2`, `3`, `4`, `99`) |
252| `engine.cef.trace_handlers` | bool | `false` | Log CEF handler dispatch details |
253| `engine.cef.enable_audio_handler` | bool | `true` | Enable the experimental CEF audio handler |
254
255## Privacy
256
257| Key | Type | Default | Valid Values | Description |
258|-----|------|---------|--------------|-------------|
259| `engine.cookie_policy` | string | `"always"` | `always`, `no_third_party`, `never` | Cookie acceptance policy |
260| `engine.webkit.itp_enabled` | bool | `true` | - | Enable WebKit fallback Intelligent Tracking Prevention |
261
262
263## Rendering, UI Scale & Zoom
264
265CEF is the default browser engine. WebKitGTK remains available as a fallback via `engine.type = "webkit"`; `engine.webkit.*` settings only affect that fallback engine.
266
267| Key | Type | Default | Valid Values | Description |
268|-----|------|---------|--------------|-------------|
269| `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine selection; WebKitGTK is the fallback option |
270| `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack |
271| `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling when `windowless_frame_rate = 0` |
272| `engine.cef.windowless_frame_rate` | int32 | `0` | `>= 0` | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled |
273| `engine.cef.windowless_frame_rate_max` | int32 | `240` | `>= 0` | Hard cap for adaptive CEF OSR FPS; 0 uses the built-in cap |
274| `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | `> 0` | Mouse wheel scroll sensitivity multiplier |
275| `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | `> 0` | Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels |
276| `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | `> 0` | Horizontal scroll sensitivity multiplier |
277| `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | `> 0` | Vertical scroll sensitivity multiplier; combines with `scroll_precise_multiplier` for touchpads |
278| `engine.cef.input.scroll_max_delta` | int32 | `0` | `>= 0` | Maximum absolute scroll delta after scaling; 0 disables clamping |
279| `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation |
280| `engine.cef.input.touchpad_navigation_min_delta` | float | `320.0` | `> 0` | Minimum accumulated horizontal swipe delta required for navigation |
281| `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | `> 0` | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes |
282| `engine.webkit.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | WebKit fallback GTK renderer selection (`GSK_RENDERER`) |
283| `engine.webkit.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit fallback DMA-BUF renderer |
284| `engine.webkit.force_compositing_mode` | bool | `false` | - | Force WebKit fallback compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) |
285| `engine.webkit.disable_compositing_mode` | bool | `false` | - | Disable WebKit fallback compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) |
286| `engine.webkit.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps for the WebKit fallback (`GSK_GPU_DISABLE=mipmap`) |
287| `engine.webkit.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES for the WebKit fallback (`GDK_DEBUG=gl-prefer-gl`) |
288| `engine.webkit.draw_compositing_indicators` | bool | `false` | - | Draw WebKit fallback compositing indicators (debug) |
289| `engine.webkit.show_fps` | bool | `false` | - | Show WebKit fallback FPS counter (`WEBKIT_SHOW_FPS`) |
290| `engine.webkit.sample_memory` | bool | `false` | - | Enable WebKit fallback memory sampling (`WEBKIT_SAMPLE_MEMORY`) |
291| `engine.webkit.debug_frames` | bool | `false` | - | Enable GTK frame timing debug for the WebKit fallback (`GDK_DEBUG=frames`) |
292| `engine.webkit.force_vsync` | bool | `false` | - | Force VSync for WebKit fallback video playback |
293| `engine.webkit.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | WebKit fallback GStreamer/OpenGL API selection |
294| `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` | WebKit fallback GStreamer debug verbosity |
295| `default_ui_scale` | float | `1.0` | `> 0` | GTK widget UI scale (1.0=100%, 2.0=200%) |
296| `default_webpage_zoom` | float | `1.2` | `> 0` | Default page zoom (1.0=100%, 1.2=120%) |
297
298`engine.cef.input.scroll_precise_multiplier` controls touchpad/high-resolution wheel scroll speed, and `engine.cef.input.scroll_vertical_multiplier` applies an additional vertical-only scale. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed.
299
300`engine.cef.input.touchpad_navigation_min_delta` uses raw GTK touchpad surface units for back/forward gestures. The default `320.0` matches WebKit-style commit distance to reduce accidental navigation; raise or lower it in `config.toml` to tune gesture sensitivity.
301
302### Legacy key migration
303
304Existing configs using older keys are migrated to the current engine shape. New configs should use the canonical keys above.
305
306| Legacy key | Current key |
307|------------|-------------|
308| `privacy.cookie_policy` | `engine.cookie_policy` |
309| `privacy.itp_enabled` | `engine.webkit.itp_enabled` |
310| `rendering.disable_dmabuf_renderer` | `engine.webkit.disable_dmabuf_renderer` |
311| `rendering.force_compositing_mode` | `engine.webkit.force_compositing_mode` |
312| `rendering.disable_compositing_mode` | `engine.webkit.disable_compositing_mode` |
313| `rendering.gsk_renderer` | `engine.webkit.gsk_renderer` |
314| `rendering.disable_mipmaps` | `engine.webkit.disable_mipmaps` |
315| `rendering.prefer_gl` | `engine.webkit.prefer_gl` |
316| `rendering.draw_compositing_indicators` | `engine.webkit.draw_compositing_indicators` |
317| `rendering.show_fps` | `engine.webkit.show_fps` |
318| `rendering.sample_memory` | `engine.webkit.sample_memory` |
319| `rendering.debug_frames` | `engine.webkit.debug_frames` |
320| `media.force_vsync` | `engine.webkit.force_vsync` |
321| `media.gl_rendering_mode` | `engine.webkit.gl_rendering_mode` |
322| `media.gstreamer_debug_level` | `engine.webkit.gstreamer_debug_level` |
323| `runtime.prefix` | `engine.webkit.prefix` |
324| `performance.*` | `engine.profile`, `engine.pool_prewarm_count`, `engine.zoom_cache_size`, or matching `engine.webkit.*` custom tuning keys |
325| `rendering.mode` | Dropped; no current setting |
326
327## Workspace Configuration
328
329### General
330
331| Key | Type | Default | Description |
332|-----|------|---------|-------------|
333| `workspace.new_pane_url` | string | `"about:blank"` | URL loaded for new panes/tabs (supports `http(s)://`, `dumb://`, `file://`, `about:`) |
334| `workspace.switch_to_tab_on_move` | bool | `true` | When moving a pane to another tab, automatically switch to the destination tab |
335
336**Example:**
337```toml
338[workspace]
339new_pane_url = "dumb://history"
340```
341
342### Pane Mode
343
344| Key | Type | Default | Description |
345|-----|------|---------|-------------|
346| `workspace.pane_mode.activation_shortcut` | string | `"ctrl+p"` | Pane mode activation key |
347| `workspace.pane_mode.timeout_ms` | int | `3000` | Pane mode timeout (ms) |
348| `workspace.pane_mode.actions` | map | See below | Action→keys mappings |
349
350**Default pane mode actions:**
351```toml
352[workspace.pane_mode.actions]
353split-right = ["arrowright", "r"]
354split-left = ["arrowleft", "l"]
355split-up = ["arrowup", "u"]
356split-down = ["arrowdown", "d"]
357stack-pane = ["s"]
358close-pane = ["x"]
359move-pane-to-tab = ["m"]
360move-pane-to-next-tab = ["M", "shift+m"]
361eject-pane-to-window = ["w"]
362
363# Consume-or-expel (niri-style) - very alpha
364consume-or-expel-left = ["["]
365consume-or-expel-right = ["]"]
366consume-or-expel-up = ["{"]
367consume-or-expel-down = ["}"]
368
369focus-right = ["shift+arrowright", "shift+l"]
370focus-left = ["shift+arrowleft", "shift+h"]
371focus-up = ["shift+arrowup", "shift+k"]
372focus-down = ["shift+arrowdown", "shift+j"]
373confirm = ["enter"]
374cancel = ["escape"]
375```
376
377> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
378
379### Tab Mode
380
381| Key | Type | Default | Description |
382|-----|------|---------|-------------|
383| `workspace.tab_mode.activation_shortcut` | string | `"ctrl+t"` | Tab mode activation key |
384| `workspace.tab_mode.timeout_ms` | int | `3000` | Tab mode timeout (ms) |
385| `workspace.tab_mode.actions` | map | See below | Action→keys mappings |
386
387**Default tab mode actions:**
388```toml
389[workspace.tab_mode.actions]
390new-tab = ["n", "c"]
391close-tab = ["x"]
392next-tab = ["l", "tab"]
393previous-tab = ["h", "shift+tab"]
394rename-tab = ["r"]
395confirm = ["enter"]
396cancel = ["escape"]
397```
398
399> **Note:** Actions are inverted to key→action map in memory for O(1) lookup performance during navigation.
400
401### Vim Mode
402
403| Key | Type | Default | Description |
404|-----|------|---------|-------------|
405| `workspace.vim_mode.activation_shortcut` | string | `"ctrl+y"` | Vim mode activation key |
406| `workspace.vim_mode.timeout_ms` | int | `0` | Vim mode timeout in milliseconds (`0` disables auto-timeout) |
407| `workspace.vim_mode.sequence_timeout_ms` | int | `500` | Timeout for ambiguous multi-key Vim sequences |
408| `workspace.vim_mode.preload_accessibility` | bool | `false` | Pre-enable browser accessibility when a WebView is shown |
409| `workspace.vim_mode.actions` | map | See below | Action→binding mappings |
410
411**Default vim mode actions:**
412
413```toml
414[workspace.vim_mode.actions.vim-scroll-left]
415keys = ["h"]
416desc = "Scroll page left"
417
418[workspace.vim_mode.actions.vim-scroll-down]
419keys = ["j"]
420desc = "Scroll page down"
421
422[workspace.vim_mode.actions.vim-scroll-up]
423keys = ["k"]
424desc = "Scroll page up"
425
426[workspace.vim_mode.actions.vim-scroll-right]
427keys = ["l"]
428desc = "Scroll page right"
429
430[workspace.vim_mode.actions.vim-scroll-down-fast]
431keys = ["shift+j"]
432desc = "Scroll page down fast"
433
434[workspace.vim_mode.actions.vim-scroll-up-fast]
435keys = ["shift+k"]
436desc = "Scroll page up fast"
437
438[workspace.vim_mode.actions.focus-input]
439keys = ["gi"]
440desc = "Focus next page input"
441
442[workspace.vim_mode.actions.heading-next]
443keys = ["]]"]
444desc = "Jump to next heading"
445
446[workspace.vim_mode.actions.heading-prev]
447keys = ["[["]
448desc = "Jump to previous heading"
449
450[workspace.vim_mode.actions.confirm]
451keys = ["enter"]
452desc = "Confirm action"
453
454[workspace.vim_mode.actions.cancel]
455keys = ["escape"]
456desc = "Cancel/exit mode"
457```
458
459Notes:
460- Vim Mode scrolls and navigates the active webpage inside the active pane only.
461- `gi` focuses the next visible editable page input, starting at the first when no input is active and wrapping after the last.
462- `]]` and `[[` move through visible `h1`–`h6` headings and use the current Dumber theme accent for the selected-heading outline.
463- The default `timeout_ms = 0` means Vim Mode stays active until you exit it or focus moves into an editable/browser UI context. `Ctrl+Y` can still activate it when a page input is already focused.
464- Arrow keys still use the browser engine's native page-navigation path while Vim Mode is active.
465- `Tab` and `Shift+Tab` keep focus traversal inside the page while a page input is focused.
466- Live input, heading, and page-focus navigation currently use the CEF engine path; WebKit fallback supports the Vim scroll commands while equivalent semantic navigation is pending.
467- Other app-level shortcuts stay suspended until Vim Mode exits, except for the Vim Mode toggle itself.
468- `workspace.styling.pane_mode_color`, `workspace.styling.transition_duration`, and `workspace.styling.mode_indicator_toaster_enabled` control the pane-local Vim Mode visuals.
469- Scroll execution: CEF and WebKit both use the shared `BuildPageScrollByJS` resolver when the page is ready. Each step starts under the viewport center, walks up through ancestors that can move in the requested direction, and hands scrolling to the document when a nested container reaches its boundary. The application repeater owns held-key cadence; each engine executes one immediate scroll step per tick. Cross-origin frame contents remain best-effort. CEF may use native precision-wheel input only as a pre-frame fallback before the browser/main frame is ready.
470
471### Resize Mode
472
473| Key | Type | Default | Description |
474|-----|------|---------|-------------|
475| `workspace.resize_mode.activation_shortcut` | string | `"ctrl+n"` | Resize mode activation key |
476| `workspace.resize_mode.timeout_ms` | int | `3000` | Resize mode timeout (ms) |
477| `workspace.resize_mode.step_percent` | float | `5.0` | Split ratio step per keystroke (percent) |
478| `workspace.resize_mode.min_pane_percent` | float | `10.0` | Minimum pane size (percent) |
479| `workspace.resize_mode.actions` | map | See below | Action→keys mappings |
480
481**Default resize mode actions:**
482```toml
483[workspace.resize_mode.actions]
484resize-increase-left = ["h", "arrowleft"]
485resize-increase-down = ["j", "arrowdown"]
486resize-increase-up = ["k", "arrowup"]
487resize-increase-right = ["l", "arrowright"]
488resize-decrease-left = ["H"]
489resize-decrease-down = ["J"]
490resize-decrease-up = ["K"]
491resize-decrease-right = ["L"]
492resize-increase = ["+", "="]
493resize-decrease = ["-"]
494confirm = ["enter"]
495cancel = ["escape"]
496```
497
498Notes:
499- Directional actions (`resize-increase-*/resize-decrease-*`) move the split divider.
500- Smart actions (`resize-increase` / `resize-decrease`) grow/shrink the active pane (best-effort) by picking a direction automatically.
501- Timeout is refreshed on each resize keypress so you can keep adjusting without re-entering the mode.
502
503### Global Shortcuts
504
505Global shortcuts are configured under `workspace.shortcuts.actions` using the same `ActionBinding` structure as modal keybindings:
506
507| Action | Default Keys | Description |
508|--------|--------------|-------------|
509| `toggle_floating_pane` | `alt+f` | Toggle the workspace floating pane (persistent session) |
510| `close_pane` | `ctrl+w` | Close active pane (or release floating pane when floating is active) |
511| `next_tab` | `ctrl+tab` | Switch to next tab |
512| `previous_tab` | `ctrl+shift+tab` | Switch to previous tab |
513| `consume_or_expel_left` | `alt+[` | Consume into left sibling stack, or expel left if stacked |
514| `consume_or_expel_right` | `alt+]` | Consume into right sibling stack, or expel right if stacked |
515| `consume_or_expel_up` | `alt+{` | Consume into upper sibling stack, or expel up if stacked |
516| `consume_or_expel_down` | `alt+}` | Consume into lower sibling stack, or expel down if stacked |
517
518**Example:**
519```toml
520[workspace.shortcuts.actions.close_pane]
521  keys = ["ctrl+w"]
522  desc = "Close active pane"
523
524[workspace.shortcuts.actions.next_tab]
525  keys = ["ctrl+tab", "alt+l"]
526  desc = "Switch to next tab"
527
528[workspace.shortcuts.actions.toggle_floating_pane]
529  keys = ["alt+f"]
530  desc = "Toggle floating pane"
531```
532
533> **Tip:** Use `dumb://config` → Keybindings tab to edit shortcuts visually.
534>
535> **Note:** New tab creation uses modal tab mode (Ctrl+T then n/c). This follows the Zellij-style modal keyboard interface.
536>
537> **Note:** Consume-or-expel is experimental and may change behavior between releases.
538>
539> **Warning:** Some `Alt+<key>` shortcuts may conflict with WebKit defaults, website handlers, or your desktop environment.
540> If a binding does not fire, rebind it to a different key.
541
542### Floating Pane
543
544The floating workspace pane is a persistent overlay session: hiding it does not destroy its web content.
545
546- Default behavior includes exactly one floating shortcut: `Alt+F` (`toggle_floating_pane`).
547- URL shortcuts (for example `Alt+G`) are user-defined via `workspace.floating_pane.profiles` and are empty by default.
548- `Alt+F` toggles visibility only and preserves floating state.
549- `Ctrl+W` on an active floating pane closes and releases that floating session for a fresh next open.
550- Floating profile keybindings support modifier combos with `ctrl`, `shift`, and `alt` (for example `ctrl+shift+y` or `ctrl+alt+m`).
551
552| Key | Type | Default | Description |
553|-----|------|---------|-------------|
554| `workspace.floating_pane.width_pct` | float | `0.82` | Floating pane width as fraction of workspace width (`(0,1]`) |
555| `workspace.floating_pane.height_pct` | float | `0.72` | Floating pane height as fraction of workspace height (`(0,1]`) |
556| `workspace.floating_pane.profiles` | map | `{}` | Named URL profiles, each with `keys[]`, `url`, optional `desc` |
557
558**Example:**
559
560```toml
561[workspace.shortcuts.actions.toggle_floating_pane]
562keys = ["alt+f"]
563desc = "Toggle floating pane"
564
565[workspace.floating_pane]
566width_pct = 0.82
567height_pct = 0.72
568
569[workspace.floating_pane.profiles]
570# Empty by default. Add as many entries as needed.
571
572[workspace.floating_pane.profiles.google]
573keys = ["alt+g"]
574url = "https://google.com"
575desc = "Open floating pane on Google"
576
577[workspace.floating_pane.profiles.github]
578keys = ["alt+h"]
579url = "https://github.com"
580desc = "Open floating pane on GitHub"
581```
582
583### Browsing Context Behavior
584
585| Key | Type | Default | Valid Values | Description |
586|-----|------|---------|--------------|-------------|
587| `workspace.browsing_contexts.behavior` | string | `"split"` | `split`, `stacked`, `tabbed`, `windowed` | Placement mode for script-opened browsing contexts |
588| `workspace.browsing_contexts.placement` | string | `"right"` | `right`, `left`, `top`, `bottom` | Split direction when behavior is `split` |
589| `workspace.browsing_contexts.open_in_new_pane` | bool | `true` | - | Allow new browsing contexts to open in the workspace |
590| `workspace.browsing_contexts.follow_pane_context` | bool | `true` | - | Keep new browsing contexts aligned with the parent pane context |
591| `workspace.browsing_contexts.blank_target_behavior` | string | `"stacked"` | `split`, `stacked`, `tabbed` | Placement mode for `_blank` / new-page link contexts |
592| `workspace.browsing_contexts.enable_smart_detection` | bool | `true` | - | Use window properties to refine browsing-context classification |
593| `workspace.browsing_contexts.oauth_auto_close` | bool | `true` | - | Auto-close OAuth browsing contexts after success |
594
595### Workspace Styling
596
597| Key | Type | Default | Description |
598|-----|------|---------|-------------|
599| `workspace.styling.border_width` | int | `1` | Active pane border width (px) - overlay |
600| `workspace.styling.border_color` | string | `"@theme_selected_bg_color"` | Active pane border color |
601| `workspace.styling.mode_border_width` | int | `4` | Modal mode border width (px) - applies to all modes |
602| `workspace.styling.pane_mode_color` | string | `"#4A90E2"` | Pane mode color (blue); also reused for Vim mode pane-local accent, indicator, and pulse |
603| `workspace.styling.tab_mode_color` | string | `"#FFA500"` | Tab mode color (orange) - used for border and toaster |
604| `workspace.styling.session_mode_color` | string | `"#9B59B6"` | Session mode color (purple) - used for border and toaster |
605| `workspace.styling.resize_mode_color` | string | `"#00D4AA"` | Resize mode color (teal) - used for border and toaster |
606| `workspace.styling.mode_indicator_toaster_enabled` | bool | `true` | Show bottom-left mode toaster when modal modes are active; Vim Mode keeps a persistent `VIM MODE` indicator until exit or toggle off |
607| `workspace.styling.transition_duration` | int | `120` | Border and pulse transition duration (ms) |
608
609**Example:**
610
611```toml
612[workspace.styling]
613border_width = 1
614border_color = "@theme_selected_bg_color"
615mode_border_width = 4
616pane_mode_color = "#4A90E2"      # Blue for pane mode and Vim mode local accent
617tab_mode_color = "#FFA500"       # Orange for tab mode
618session_mode_color = "#9B59B6"   # Purple for session mode
619resize_mode_color = "#00D4AA"    # Teal for resize mode
620mode_indicator_toaster_enabled = true
621transition_duration = 120
622```
623
624## Session
625
626| Key | Type | Default | Description |
627|-----|------|---------|-------------|
628| `session.auto_restore` | bool | `false` | Automatically restore the last session on startup |
629| `session.snapshot_interval_ms` | int | `5000` | Minimum interval between snapshots in milliseconds |
630| `session.max_exited_sessions` | int | `50` | Maximum number of exited sessions to keep |
631| `session.max_exited_session_age_days` | int | `7` | Maximum age in days for exited sessions (auto-deleted on startup) |
632
633### Session Mode
634
635| Key | Type | Default | Description |
636|-----|------|---------|-------------|
637| `session.session_mode.activation_shortcut` | string | `"ctrl+o"` | Session mode activation key |
638| `session.session_mode.timeout_ms` | int | `3000` | Session mode timeout (ms) |
639| `session.session_mode.actions` | map | See below | Action to keys mappings |
640
641**Default session mode actions:**
642```toml
643[session.session_mode.actions]
644session-manager = ["s", "w"]
645confirm = ["enter"]
646cancel = ["escape"]
647```
648
649**Example:**
650```toml
651[session]
652auto_restore = false              # Don't auto-restore on startup
653snapshot_interval_ms = 5000       # Save state every 5 seconds (debounced)
654max_exited_sessions = 50          # Keep last 50 exited sessions
655max_exited_session_age_days = 7   # Delete sessions older than 7 days on startup
656```
657
658## Media
659
660| Key | Type | Default | Valid Values | Description |
661|-----|------|---------|--------------|-------------|
662| `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode |
663| `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available |
664| `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup |
665
666WebKit fallback GStreamer tuning is configured under `engine.webkit.force_vsync`, `engine.webkit.gl_rendering_mode`, and `engine.webkit.gstreamer_debug_level`.
667
668**Hardware decoding modes:**
669- `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000
670- `force`: Hardware only - fails if unavailable
671- `disable`: Software only - higher CPU usage
672
673**GPU auto-detection:**
674Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings:
675- **AMD**: Uses `radeonsi` driver
676- **Intel**: Uses `iHD` driver (modern, for Broadwell+)
677- **NVIDIA**: Uses `nvidia` driver with EGL platform
678
679**Example:**
680
681```toml
682[media]
683hardware_decoding = "auto"    # HW preferred, SW fallback
684prefer_av1 = false            # Let site choose codec
685show_diagnostics = false      # Keep off for daily use
686```
687
688**WebKit fallback GStreamer diagnostics:**
689
690```toml
691[engine.webkit]
692gstreamer_debug_level = 3
693```
694
695Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode.
696
697**Diagnostics CLI:**
698
699```bash
700# Check GStreamer plugins and VA-API status
701dumber doctor --media
702```
703
704## Runtime
705
706| Key | Type | Default | Description |
707|-----|------|---------|-------------|
708| `engine.cef.cef_dir` | string | `""` | Optional CEF runtime directory containing `libcef.so` and resources |
709| `engine.webkit.prefix` | string | `""` | Optional WebKitGTK fallback runtime prefix (e.g., `/opt/webkitgtk`) |
710
711`engine.cef.cef_dir` overrides CEF runtime discovery. For the WebKitGTK fallback, `engine.webkit.prefix` prepends prefix-derived paths to:
712- `PKG_CONFIG_PATH` (for version checks)
713- `LD_LIBRARY_PATH` (for runtime library loading)
714- `GI_TYPELIB_PATH` (GObject introspection)
715- `XDG_DATA_DIRS` (schemas/resources)
716
717**Example:**
718
719```toml
720[engine.cef]
721cef_dir = "/opt/cef"
722
723[engine.webkit]
724prefix = "/opt/webkitgtk"
725```
726
727## Clipboard
728
729| Key | Type | Default | Description |
730|-----|------|---------|-------------|
731| `clipboard.auto_copy_on_selection` | bool | `true` | Automatically copy selected text to clipboard (zellij/tmux-style) |
732
733When 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.
734
735**Example:**
736
737```toml
738[clipboard]
739auto_copy_on_selection = true  # Enabled by default
740```
741
742## Content Filtering
743
744| Key | Type | Default | Description |
745|-----|------|---------|-------------|
746| `content_filtering.enabled` | bool | `true` | Enable ad blocking |
747| `content_filtering.auto_update` | bool | `true` | Automatically update filters |
748
749Notes:
750- Filter data is downloaded from `bnema/ublock-webkit-filters` GitHub releases.
751- Domain whitelist is managed via the database (`content_whitelist` table), not the config file.
752
753## Update
754
755| Key | Type | Default | Description |
756|-----|------|---------|-------------|
757| `update.enable_on_startup` | bool | `true` | Check for updates when browser starts |
758| `update.auto_download` | bool | `false` | Automatically download updates in background |
759| `update.notify_on_new_settings` | bool | `true` | Show toast notification when new config settings are available |
760
761**Example:**
762
763```toml
764[update]
765enable_on_startup = true       # Check for updates on startup
766auto_download = false          # Don't auto-download (prompt instead)
767notify_on_new_settings = true  # Show toast when config migration available
768```
769
770**CLI Commands:**
771
772```bash
773# Check config status and available migrations
774dumber config status
775
776# Add missing settings with default values
777dumber config migrate
778
779# Skip confirmation prompt
780dumber config migrate --yes
781```
782
783## Performance Profiles
784
785Performance profiles tune engine resource behavior. CEF is the default engine; the `engine.webkit.*` custom fields below apply only when using the WebKitGTK fallback.
786
787> **Note:** Performance settings are applied at browser startup. Changes require a restart to take effect.
788
789| Key | Type | Default | Valid Values | Description |
790|-----|------|---------|--------------|-------------|
791| `engine.profile` | string | `"default"` | `default`, `lite`, `balanced`, `max`, `custom` | Performance profile selection |
792| `engine.pool_prewarm_count` | int | `4` | `>= 0` | WebViews to pre-create at startup |
793| `engine.zoom_cache_size` | int | `256` | `>= 0` | Domain zoom levels to cache |
794
795### Profiles
796
797| Profile | Description | Use Case |
798|---------|-------------|----------|
799| `default` | No tuning, uses engine defaults | Compatibility baseline, troubleshooting |
800| `lite` | Reduced resource usage | Low-RAM systems (< 4GB), battery saving |
801| `balanced` | Moderate tuning | General use |
802| `max` | Maximum responsiveness | Heavy pages (GitHub PRs, complex SPAs), high-end systems |
803| `custom` | Manual control over all settings | Advanced users who want fine-grained control |
804
805**Example:**
806
807```toml
808[engine]
809profile = "default"
810pool_prewarm_count = 4
811zoom_cache_size = 256
812```
813
814### WebKit fallback custom profile settings
815
816When `engine.profile = "custom"` and `engine.type = "webkit"`, you can configure individual WebKit tuning options:
817
818| Key | Type | Default | Description |
819|-----|------|---------|-------------|
820| `engine.webkit.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) |
821| `engine.webkit.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) |
822| `engine.webkit.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering |
823| `engine.webkit.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) |
824| `engine.webkit.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) |
825| `engine.webkit.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) |
826| `engine.webkit.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) |
827| `engine.webkit.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB |
828| `engine.webkit.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval |
829| `engine.webkit.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold |
830| `engine.webkit.network_process_memory_strict_threshold` | float | `0` | Network strict threshold |
831
832**Custom WebKit fallback example:**
833
834```toml
835[engine]
836type = "webkit"
837profile = "custom"
838pool_prewarm_count = 6
839zoom_cache_size = 256
840
841[engine.webkit]
842skia_cpu_painting_threads = 4
843skia_gpu_painting_threads = 2
844web_process_memory_limit_mb = 1024
845web_process_memory_conservative_threshold = 0.4
846web_process_memory_strict_threshold = 0.6
847```
848
849> **Important:** Individual `engine.webkit.*` tuning fields are ignored unless `engine.profile = "custom"`.
850
851## Downloads
852
853| Key | Type | Default | Description |
854|-----|------|---------|-------------|
855| `downloads.path` | string | `""` | Download directory path (empty = `$XDG_DOWNLOAD_DIR` or `~/Downloads`) |
856
857Downloads are saved to the configured directory with toast notifications for download started, completed, and failed events.
858
859**Example:**
860```toml
861[downloads]
862path = ""  # Use system default ($XDG_DOWNLOAD_DIR or ~/Downloads)
863
864# Or specify a custom directory:
865# path = "/home/user/my-downloads"
866```
867
868## Environment Variables
869
870All config values can be overridden via environment variables with the prefix `DUMBER_`:
871
872```bash
873# Database
874DUMBER_DATABASE_PATH=/custom/path/db.sqlite
875
876# Engine/rendering (examples)
877DUMBER_ENGINE_TYPE=cef
878# CEF defaults to the GPU-first Vulkan DMABUF stack.
879# Use EGL/OpenGL for driver compatibility:
880# DUMBER_ENGINE_CEF_RENDER_STACK=egl
881# WebKit fallback rendering overrides:
882# DUMBER_ENGINE_WEBKIT_DISABLE_DMABUF_RENDERER=true
883# DUMBER_ENGINE_WEBKIT_GSK_RENDERER=opengl
884DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
885
886# Logging
887DUMBER_LOG_LEVEL=debug
888DUMBER_LOG_FORMAT=json
889```

Keyboard shortcuts

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

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

Branches 8

Tags 42

↑↓ moveenter openesc close