Skip to content

Configuration

locainin edited this page Apr 18, 2026 · 15 revisions

Configuration

UnixNotis loads config.toml from the XDG config directory and hot-reloads it on change.

Config file

Locations:

  • $XDG_CONFIG_HOME/unixnotis/config.toml
  • fallback: $HOME/.config/unixnotis/config.toml

If the file is missing, built-in defaults are used. Unknown keys are ignored and logged. Runtime sanitization clamps out-of-range values to safe limits. Some layout values are also clamped at runtime. For example, the panel keeps a minimum usable width even if config.toml asks for something smaller.

Reload behavior

  • The daemon, panel, and popup UIs watch for config changes.
  • Updates are applied without restarting the service.
  • Reload failures keep the previous valid configuration.

Minimal example

[general]
log_level = "info"
dnd_default = false

[inhibit]
mode = "no_popups"

[panel]
anchor = "right"
width = 420
# Vertical size as a percent of usable monitor height after margins
# and reserved work area
height = 84

# Exact pixel height override for advanced users
# height_override = 1487
empty_text = "NO NOTIFICATIONS"
empty_offset_top = 120

[popups]
anchor = "top-right"
width = 360

[theme]
base_css = "base.css"
popup_css = "popup.css"
panel_css = "panel.css"
widgets_css = "widgets.css"
media_css = "media.css"

Defaults snapshot (widgets)

The default panel includes:

  • Sliders: Volume, Brightness
  • Toggles: Wi-Fi, Bluetooth, Airplane, Night
  • Stats: CPU, RAM, Battery
  • Cards: Calendar, Weather (static unless configured)

See Widgets for the full schema and default command backends.

Section reference

[general]

  • log_level: tracing filter syntax (RUST_LOG compatible).
  • dnd_default: default DND state when no persisted state exists.

[inhibit]

  • mode: no_popups (store notifications, suppress popups) or drop_all.

[panel]

  • anchor: top-right, top-left, bottom-right, bottom-left, top, bottom, left, right.
  • width: requested width in logical pixels. On smaller displays, runtime caps this to a monitor-aware maximum so the panel remains usable. This is not a hard ceiling. GTK may keep the live panel wider when child widgets need more horizontal space than the requested width.
  • height: vertical size as a percent of usable monitor height. The usable area starts from monitor height and subtracts panel margins and Hyprland reserved work area when respect_work_area = true. This is a target height, not a forced clip height. Visible header, media, and widget sections still keep their natural minimum size, so very small percentages can look identical until the requested height grows past that content floor.
  • height_override: optional exact pixel height for advanced layouts. When this is set, height is ignored. This still does not force the panel smaller than the visible content minimum.
  • keyboard_interactivity: none, on-demand, exclusive.
  • output: optional Wayland output name.
  • close_on_blur: hide when the panel loses focus.
  • close_on_click_outside: hide on outside click (Hyprland only).
  • respect_work_area: respects compositor reserved area (Hyprland only).
  • empty_text: empty-state label text (supports \n for multi-line).
  • empty_offset_top: top offset when widgets are visible. When no widgets are visible, the empty state is centered in the list area.
  • [panel.margin]: margins in logical pixels.

Width behavior notes:

  • A smaller panel.width request does not force the panel narrower than its current natural width if widget content still needs more room
  • Fixed-column widget layouts are the most common cause: toggles render in 4 columns and stats/cards render in 2 columns
  • The media row now clamps itself to the panel body width, so it is less likely than before to become the main width driver unless theme CSS adds aggressive sizing
  • Theme CSS can also widen the live panel when controls use large min-width, padding, margins, or other horizontal sizing
  • noticenterctl css-check now warns when toggle/stat/card/media sizing looks likely to exceed the requested panel width
  • If a width change appears ignored, inspect widget CSS before assuming config reload is broken

Defaults:

  • empty_text = "NO NOTIFICATIONS"
  • empty_offset_top = 120
  • height = 84

Height behavior notes:

  • The header row always reserves space for the title and action buttons
  • Visible widgets and media also reserve their own natural height and any configured min_height
  • Because of that, low height values such as 1 through 20 can appear almost the same on a widget-heavy panel
  • Hiding widgets lowers that floor, which is why the same height can look much smaller when the widget stack is collapsed
  • If the panel still looks taller than expected, check widget min_height values and theme CSS padding before assuming the percentage math is wrong

[popups]

  • anchor, width, spacing, max_visible.
  • width: requested width in logical pixels. On smaller displays, runtime caps this to a monitor-aware maximum so popup cards do not dominate the screen.
  • default_timeout_ms, critical_timeout_ms.
  • allow_click_through: disable input handling when true.
  • output: optional Wayland output name.
  • [popups.margin]: margins in logical pixels.

[history]

  • max_entries: history capacity.
  • max_active: active list capacity. Runtime clamps this to 12 so the panel and popup stack stay bounded under bursts.
  • transient_to_history: includes transient notifications in history.

Defaults:

  • max_entries = 200
  • max_active = 12
  • transient_to_history = false

[media]

  • enabled, include_browsers.
  • layout: structural media card preset.
    • carousel: nav buttons outside the card
    • inline: nav buttons folded into the card
    • stacked: vertical card with a lower control strip
    • showcase: wide card with a dedicated action rail
    • player: centered player shell with top art, a lower transport dock, and nav hidden by default
  • browser_tokens: case-insensitive browser-family tokens used to classify browser players. Matching is segment-based, not raw substring matching, so short tokens such as edge match names like microsoft-edge without also matching unrelated names like knowledge.
  • title_char_limit: marquee starts after this length.
  • show_source: show the source badge row above the title.
  • show_source_when_single_player: keep the source badge visible when only one player exists.
  • show_position: show the player counter.
  • show_position_when_single_player: keep the counter visible even when only one player exists.
  • show_title, show_artist, show_art, show_controls, show_navigation: hide or show each major media shell region without switching layouts.
  • title_fallback: fallback used when the active player has no title.
    • identity: show the player identity
    • artist: show the artist name
    • empty: leave the title lane blank
  • position_format: how the player counter is rendered.
    • fraction: current/total
    • current: just the current slot number
  • source_aliases: optional lowercase token map that rewrites noisy player names into friendlier labels.
  • allowlist / denylist: case-insensitive substrings for player identities.
  • art_position: artwork placement override layered on top of the preset.
    • auto, start, top, hidden
  • controls_position: transport placement override layered on top of the preset.
    • auto, inline, bottom, side, hidden
  • navigation_position: player-switch placement override layered on top of the preset.
    • auto, external, with_controls, hidden
  • art_size_px: preferred artwork edge length.
  • text_width_floor_px: minimum width reserved for the title lane.
  • card_height_px: optional exact media card height override.
  • content_spacing_px: spacing between major shell regions such as art, text, and rails.
  • control_spacing_px: spacing between prev, play, and next buttons.
  • navigation_spacing_px: spacing between player nav buttons and between nav and transport when they share one rail.
  • remote_art_policy: controls which players may fetch remote album art.
    • disabled: never fetch remote art
    • native_only: allow remote https art only for non-browser players
    • browsers_too: also allow browser media sessions to fetch remote https art

Defaults:

  • browser_tokens includes common desktop browsers (Firefox, Chromium, Chrome, Brave, Vivaldi, Edge, Opera, Epiphany, Midori, Zen, Librewolf, Waterfox, Floorp).
  • layout = "carousel".
  • title_char_limit = 32.
  • show_source = true.
  • show_position = true.
  • show_source_when_single_player = true.
  • show_position_when_single_player = false.
  • show_title = true.
  • show_artist = true.
  • show_art = true.
  • show_controls = true.
  • show_navigation = true.
  • title_fallback = "identity".
  • position_format = "fraction".
  • art_position = "auto".
  • controls_position = "auto".
  • navigation_position = "auto".
  • art_size_px = 50.
  • text_width_floor_px = 140.
  • card_height_px = none.
  • content_spacing_px = 10.
  • control_spacing_px = 6.
  • navigation_spacing_px = 6.
  • remote_art_policy = "native_only".
  • player preset defaults:
    • art_position = "top"
    • controls_position = "bottom"
    • navigation_position = "hidden"
    • card_height_px = 208 when no manual override is set

Notes:

  • Layout presets change the real GTK shell, not just the CSS classes.
  • The preset now acts as a default shell, and the position toggles can override it. For example, layout = "showcase" can still use controls_position = "bottom" and art_position = "top".
  • The centered player presentation is now an explicit preset, not a side effect of art_position = "top". Other top-art shells keep their own alignment rules unless the preset or manual overrides say otherwise.
  • show_art = false, show_controls = false, or show_navigation = false remove those regions from the shell entirely and lower width pressure.
  • Local file artwork from native players such as mpv and smplayer is still allowed.
  • Remote art is limited to validated https URLs and bounded fetch sizes.
  • Browser remote art stays off by default because webpage-controlled metadata can choose the art URL.
  • The source label and player counter still respect the single-player flags, so those labels can be hidden on single-player setups without changing multi-player behavior.

Example:

[media]
layout = "player"
show_source = false
show_position = false
title_fallback = "artist"
art_size_px = 56
text_width_floor_px = 220
card_height_px = 176
content_spacing_px = 4
control_spacing_px = 8

[media.source_aliases]
spotify = "Spotify"
firefox = "Firefox"

Enable browser remote art only when that tradeoff is acceptable:

[media]
remote_art_policy = "browsers_too"

[sound]

  • enabled.
  • default_name: freedesktop sound theme name.
  • default_file: sound file path (relative to config dir when not absolute).
  • default_dir: directory containing sound files.

[theme]

Theme CSS file names resolve relative to the config directory. Alpha values are clamped to 0.0..=1.0 and dimensions are clamped to safe ranges.

  • base_css, panel_css, popup_css, widgets_css: theme file paths loaded by the live UI
  • media_css: media-widget-only theme layer loaded above widgets.css Relative paths resolve from the config directory. Absolute paths are also allowed
  • Active theme files do not have to end in .css, but using the standard names keeps output and troubleshooting easier to follow
  • border_width: card/control border thickness.
  • card_radius: card corner radius.
  • surface_alpha, surface_strong_alpha, card_alpha.
  • shadow_soft_alpha, shadow_strong_alpha.

Theme behavior notes:

  • existing themes still work through the legacy GTK color path
  • newer GTK runtimes can also use additive --unixnotis-* custom properties
  • those runtime values now stay aligned with the [theme] config knobs instead of forcing theme authors to duplicate the same numbers by hand
  • shared hook classes are now available on panel cards, popup cards, and media cards for more targeted styling

Theme path notes:

  • noticenterctl css-check follows the active theme paths from config.toml instead of blindly scanning every .css file under the config tree
  • The active path list now includes [theme].media_css
  • It warns when a configured theme target is missing
  • It warns when multiple theme slots resolve to the same file
  • It prints the active resolved theme file list before validation so it is clear which files were actually checked
  • It now accepts valid modern GTK CSS such as supported var(...) and calc(...) usage instead of treating them like blanket bad input

[widgets]

  • toggle_tooltips: enables GTK hover tooltips for toggle buttons. Default: false.
  • refresh_interval_ms: base refresh interval for widget polling.
  • refresh_interval_slow_ms: slower interval used during stable periods.
  • [[widgets.stats]].plugin: optional external plugin for stat values.
  • [[widgets.cards]].plugin: optional external plugin for card content.

Plugin block fields:

  • api_version: plugin contract version (currently 1).
  • command: simple executable command (no shell metacharacters).
  • timeout_ms: plugin runtime timeout in milliseconds.
  • max_output_bytes: maximum accepted plugin stdout payload size.

See Widgets for the widget schema and command examples.

Rules match notification fields and apply overrides. See Rules for details and examples.

Clone this wiki locally