Skip to content

Configuration

locainin edited this page May 15, 2026 · 16 revisions

Configuration

UnixNotis loads config.toml from the XDG config directory and hot-reloads it on change. The default file is a working starting point, not a fixed product shape. Most panel widgets are meant to be edited in config: swap commands, point entries at scripts, change icons, hide sections, or add new toggles/stats/cards for local workflows.

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"

Widget starting point

The default panel includes:

  • Sliders: Volume, Brightness
  • Toggles: Wi-Fi, Bluetooth, Airplane, Night
  • Night uses shipped config-local helper scripts so backend fallback logic stays editable
  • Stats: CPU, RAM, Battery
  • Cards: Calendar, Weather (static unless configured)

Use this as a template. For custom controls, add or edit [[widgets.toggles]], [[widgets.stats]], and [[widgets.cards]] blocks. See Widgets for schemas, command fields, helper-script examples, 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.
  • title: main panel header text.
  • subtitle: optional secondary header text. Empty hides it.
  • search_placeholder: placeholder shown in the panel search entry.
  • search_visible: show the search entry by default instead of only after using the search action.
  • action_row_visible: show or hide the compact utility action row below the header.
  • notification_section_visible: wrap the notification list in a titled card-like section.
  • notification_list_expand: let the notification list consume remaining vertical panel space.
  • clear_button_placement: action-row, notification-header, or hidden.
  • quick_actions_label: heading above toggle buttons. Empty hides the heading.
  • system_status_label: heading above stat cards. Empty hides the heading.
  • recent_notifications_label: notification-list heading. Empty hides the heading.
  • clear_label: label for the clear-all action.
  • footer_label: optional passive footer text. Empty hides the footer. It is not clickable; use a custom toggle with toggle_cmd for user-defined actions.
  • widget_order: top-to-bottom section order using sliders, media, toggles, stats, cards.
  • [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
  • Widget grid columns are a common cause: defaults are toggles in 4 columns and stats/cards in 2 columns, but [widgets].toggle_columns, [widgets].stat_columns, and [widgets].card_columns can tune that without CSS hacks
  • The media row clamps itself to the panel body width, so it usually becomes a width driver only when media 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 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
  • title = "Notifications"
  • subtitle = ""
  • search_placeholder = "Search app, title, or message"
  • search_visible = false
  • action_row_visible = true
  • notification_section_visible = false
  • notification_list_expand = true
  • clear_button_placement = "action-row"
  • quick_actions_label = ""
  • system_status_label = ""
  • recent_notifications_label = "Notifications"
  • clear_label = "Clear"
  • footer_label = ""
  • widget_order = ["sliders", "media", "toggles", "stats", "cards"]

Section titles:

  • quick_actions_label titles the toggle section.
  • system_status_label titles the stat section.
  • recent_notifications_label titles the notification list.
  • Empty labels are hidden. This keeps compact layouts clean while still allowing descriptive headings in denser themes.

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 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 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.
  • Browser players with the same PID or same track metadata are collapsed into one entry.
  • 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
  • GTK runtimes with custom property support can use additive --unixnotis-* custom properties
  • those runtime values stay aligned with the [theme] config knobs instead of forcing theme authors to duplicate the same numbers by hand
  • shared hook classes are 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 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 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.
  • toggle_layout: horizontal or vertical. Controls icon/label placement inside toggle cards.
  • toggle_columns: fixed toggle grid column count. Default: 4.
  • stat_columns: fixed stat grid column count. Default: 2.
  • card_columns: fixed info-card grid column count. Default: 2.
  • 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