chore(config): splitted for easier parsing and doc preps

This commit is contained in:
Lemmy
2026-04-19 22:14:27 -04:00
parent 59b1ff49b4
commit 7a0245d2af
10 changed files with 1419 additions and 1358 deletions
+16 -1358
View File
File diff suppressed because it is too large Load Diff
+149
View File
@@ -0,0 +1,149 @@
# Bar
Bars are defined as named subtables under `[bar.*]`. Each bar is spawned on every connected output, then per-monitor overrides are applied.
```toml
[bar.main]
position = "top" # top | bottom | left | right
enabled = true
auto_hide = false # slide out after pointer leaves; reveal from edge trigger strip
reserve_space = false # keep exclusive zone even when auto-hidden
thickness = 34 # bar cross-axis size in pixels (height for horizontal, width for vertical)
background_opacity = 1.0 # 0.0 (transparent) to 1.0 (opaque)
background_blur = true # request compositor blur via ext-background-effect-v1 (niri)
radius = 12 # global corner radius fallback
radius_top_left = 12
radius_top_right = 12
radius_bottom_left = 12
radius_bottom_right = 12
margin_h = 180 # horizontal gap between bar and screen edge
margin_v = 10 # vertical gap between bar and screen edge
padding = 14 # main-axis padding from bar edges to start/end widget sections
widget_spacing = 6 # gap between widgets within a section
scale = 1.0 # content scale multiplier for icons and text
shadow_blur = 12 # blur radius in pixels (0 = no shadow)
shadow_offset_x = 0
shadow_offset_y = 6 # positive = down
# Default capsule style for all widgets on this bar (see Widget Capsule section)
capsule = false
capsule_fill = "surface_variant"
capsule_opacity = 1.0
# capsule_border = "outline" # omit this key for no border by default
start = ["launcher", "wallpaper", "workspaces"]
center = ["clock"]
end = ["media", "tray", "notifications", "network", "bluetooth", "volume", "brightness", "battery", "session"]
```
Radius precedence: `radius` is the global fallback; per-corner values override it when provided.
Multiple `[bar.*]` entries are supported — each is independently configured and rendered on all outputs.
---
## Per-monitor overrides
Inside a bar, add named monitor subtables under `[bar.<name>.monitor.*]`. **First match wins**, in file order.
```toml
[bar.main.monitor.dp1]
match = "DP-1" # connector name or description substring
enabled = true
thickness = 44
background_opacity = 0.9
radius = 0
radius_top_left = 12
radius_top_right = 12
radius_bottom_left = 0
radius_bottom_right = 0
padding = 20
widget_spacing = 6
start = []
center = ["workspaces"]
end = ["volume", "clock"]
```
**`match` resolution** — compared against:
1. Exact connector name (`eDP-1`, `DP-1`, `HDMI-A-1`, …)
2. Any substring of the monitor description string (`"LG"`, `"4K"`, `"DELL"`, …)
`match` defaults to the subtable key name when omitted, so `[bar.main.monitor."DP-1"]` without a `match` field works too.
Only the fields you specify are overridden; everything else falls through to the `[bar.*]` defaults. Supported override fields: all bar fields, plus `auto_hide`, `reserve_space`, `scale`, `background_opacity`, `color`, and all `capsule_*` keys.
---
## Auto-hide
When `auto_hide = true`, the bar:
- Does **not** reserve compositor exclusive zone (windows are not pushed away).
- Slides out once the pointer leaves the bar.
- Slides back in when the pointer reaches the matching screen edge trigger strip.
Set `reserve_space = true` to keep the exclusive zone while auto-hidden.
---
## Widget Capsule
Each widget can have a capsule (pill-shaped background + optional border). Settings cascade from bar defaults down to per-widget overrides.
### Bar-level defaults
Set under `[bar.<name>]` or `[bar.<name>.monitor.*]`:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `capsule` | bool | `false` | `true` gives every widget a capsule unless `[widget.*]` sets `capsule = false`. |
| `color` | string | *(unset)* | Default icon + label color for every widget on this bar. |
| `capsule_fill` | string | `surface_variant` | Default capsule background. Theme role or `#` hex; hex alpha is ignored — use `capsule_opacity`. |
| `capsule_color` | string | — | Synonym for `capsule_fill` when that key is absent. |
| `capsule_foreground` | string | *(unset)* | Default icon + label color for capped widgets. `capsule_ink` is accepted as a deprecated alias. |
| `capsule_padding` | number | `6` | Inner padding in logical pixels before `scale` is applied (clamped 048). |
| `capsule_opacity` | number | `1.0` | Capsule background opacity (0.01.0). |
| `capsule_border` | string | *(omitted)* | If omitted, no border by default. If present (even `""`), per-widget border rules apply. |
### Per-widget overrides
Set under `[widget.<name>]`:
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `capsule` | bool | *(from bar)* | Omit to inherit bar flag; `false` disables; `true` forces on. |
| `capsule_fill` | string | *(from bar)* | Capsule background color. |
| `capsule_color` | string | *(from bar)* | Synonym for `capsule_fill` when that key is absent. |
| `capsule_foreground` | string | *(from bar)* | Icon + label color when capsule is visible. `color` takes priority over this. |
| `capsule_padding` | number | *(from bar)* | Per-widget inner padding (048). |
| `capsule_opacity` | number | *(from bar)* | Per-widget capsule background opacity. |
| `capsule_border` | string | *(from bar)* | Omit to inherit bar policy. Present but empty/whitespace-only = no border. |
| `color` | string | *(unset)* | Icon + label color with or without capsule. Resolution order: `color``capsule_foreground` → built-in defaults. |
Theme role names use **snake_case** (e.g. `on_surface`, `surface_variant`, `secondary`). Hyphens are accepted and normalized to underscores.
The capsule is hidden automatically when a widget reports no visible ink (empty tray, absent battery, invisible root). Subclasses may override `Widget::shouldShowBarCapsule()`.
```toml
[bar.main]
capsule = true
capsule_fill = "surface_secondary"
capsule_opacity = 0.9
capsule_border = "outline"
# Accent bar: primary fill + matching text
[bar.accent]
capsule = true
capsule_color = "primary"
capsule_foreground = "on_primary"
capsule_padding = 10
[widget.volume]
capsule_fill = "#2a2a33"
capsule_border = "" # no border on this widget
[widget.spacer]
type = "spacer"
capsule = false
```
+85
View File
@@ -0,0 +1,85 @@
# Desktop Widgets
Desktop widgets are enabled from `config.toml`. Widget instances and edit-mode grid settings are stored in a separate state file.
- Config toggle: `[desktop_widgets]` in `~/.config/noctalia/config.toml`
- State file: `$XDG_STATE_HOME/noctalia/desktop_widgets.toml` (falls back to `~/.local/state/noctalia/desktop_widgets.toml`)
```toml
[desktop_widgets]
enabled = true
```
When enabled, Noctalia renders each desktop widget as its own tightly-sized layer-shell surface on the `Bottom` layer. The current implementation ships `clock` and `audio_visualizer` widget types plus an interactive edit mode.
---
## Edit mode
### IPC
```sh
noctalia msg edit-desktop-widgets
noctalia msg exit-desktop-widgets
noctalia msg toggle-desktop-widgets-edit
```
### Controls
| Action | Effect |
|--------|--------|
| Drag widget body | Move |
| Drag outer selection ring | Rotate |
| Drag bottom-right handle | Scale uniformly |
| Drag toolbar handle | Reposition the editor toolbar on that output |
| `G` | Toggle snap grid |
| `Shift` + drag | Temporarily disable snapping |
| `Delete` / `Backspace` | Remove selected widget |
| `Escape` / click `Done` | Exit edit mode |
---
## State file format
Widget definitions are not read from `config.toml` — edit mode writes them to the state file so positions and transforms can be changed interactively.
```toml
schema_version = 1
[grid]
visible = true
cell_size = 16
major_interval = 4
[[widget]]
id = "desktop-widget-0000000000000001"
type = "clock"
output = "DP-1"
cx = 960.0
cy = 540.0
scale = 1.5
rotation = 0.0
[widget.settings]
format = "{:%H:%M}"
```
Desktop audio visualizers store an `aspect_ratio` setting for shape while `scale` controls overall size. They also accept optional `mirrored`, `low_color`, and `high_color` settings; both colors default to `primary`.
```toml
[[widget]]
id = "desktop-widget-0000000000000002"
type = "audio_visualizer"
output = "DP-1"
cx = 1040.0
cy = 620.0
scale = 1.25
rotation = 0.0
[widget.settings]
bands = 32
aspect_ratio = 2.5
mirrored = true
low_color = "primary"
high_color = "secondary"
```
+79
View File
@@ -0,0 +1,79 @@
# Dock
A standalone application dock that displays pinned and running apps. Disabled by default.
```toml
[dock]
enabled = false # set true to activate
position = "bottom" # top | bottom | left | right
active_monitor_only = false # when true, only show apps/windows from the active monitor
icon_size = 48
padding = 8 # inner padding around the icon row (all sides)
item_spacing = 6 # gap between items in pixels
background_opacity = 0.88
background_blur = true # request compositor blur via ext-background-effect-v1 (niri)
radius = 16
margin_h = 0 # horizontal compositor margin
margin_v = 8 # vertical gap between dock and screen edge
shadow_blur = 12
shadow_offset_x = 0
shadow_offset_y = 4
show_running = true # also show running apps not in the pinned list
auto_hide = false # fade out when pointer leaves; fade in on approach
reserve_space = false # keep exclusive zone even when auto-hidden
active_scale = 1.0 # icon scale for the focused app (clamped 0.11.75)
inactive_scale = 0.85 # icon scale for non-focused apps (clamped 0.11.0)
active_opacity = 1.0
inactive_opacity = 0.85
show_instance_count = true # badge with window count when an app has 2+ windows
# Desktop entry IDs, StartupWMClass, or human-readable names
pinned = ["firefox", "code", "kitty"]
```
---
## `pinned` matching
Each entry in `pinned` is matched against desktop entries using these rules in order:
1. Desktop entry ID stem (`"firefox"` matches `firefox.desktop` and `org.mozilla.Firefox.desktop`)
2. `StartupWMClass` field of the desktop entry
3. App `Name` field (case-insensitive)
4. Full desktop entry path
If no match is found, a placeholder slot is reserved so the dock position is preserved.
---
## Active app emphasis
- The focused app uses `active_scale` and `active_opacity`.
- All other apps use `inactive_scale` and `inactive_opacity`.
- Focus changes animate smoothly between the two states.
---
## Auto-hide
When `auto_hide = true`, the dock:
- Does **not** reserve compositor exclusive zone.
- Fades out after the pointer leaves (slow ease-in animation).
- Fades back in when the pointer enters the thin edge trigger strip.
Set `reserve_space = true` to keep the exclusive zone while auto-hidden.
---
## IPC
```sh
noctalia msg show-dock # re-display all instances
noctalia msg hide-dock # close all instances until next reload
noctalia msg toggle-dock # toggle dock visibility
noctalia msg reload-dock # reload dock configuration
```
+172
View File
@@ -0,0 +1,172 @@
# Services
- [Audio](#audio)
- [Brightness](#brightness)
- [Night Light](#night-light)
- [Weather](#weather)
- [Idle](#idle)
- [Notifications](#notifications)
---
## Audio
```toml
[audio]
enable_overdrive = false # allow volume sliders above 100% (up to 150%)
```
When `enable_overdrive = false`, the Control Center output and microphone sliders clamp to 100%. When `true`, they allow up to 150%.
---
## Brightness
Brightness control uses the kernel backlight interface by default. `ddcutil` support is opt-in for external monitors that expose DDC/CI brightness.
```toml
[brightness]
enable_ddcutil = false
ignore_mmids = [] # e.g. ["ACI-ROG_PG279Q-10220"] — skip in all ddcutil commands
[brightness.monitor.eDP-1]
backend = "backlight" # auto | none | backlight | ddcutil
[brightness.monitor.DP-1]
backend = "ddcutil"
```
Notes:
- `enable_ddcutil = true` only enables DDC/CI discovery — it does not force every monitor to `ddcutil`.
- `ignore_mmids` passes `--ignore-mmid` to every `ddcutil` invocation. Run `ddcutil --verbose detect` to find monitor model id strings.
- Per-monitor overrides use the same connector/description matching rules as bar monitor overrides.
- `backend = "auto"` prefers kernel backlight when available and falls back to `ddcutil`.
- `backend = "none"` hides brightness control for the matched display.
- `ddcutil` is treated as best-effort — repeated DDC failures cool down that display to avoid hammering the monitor bus.
### IPC
```sh
noctalia msg set-brightness 65 # current display
noctalia msg set-brightness DP-1 0.65
noctalia msg set-brightness * 40% # all displays
noctalia msg raise-brightness # current display, default 5% step
noctalia msg raise-brightness DP-1 10
noctalia msg lower-brightness * 5% # all displays
```
Targets: `current`, `all`/`*`, a display id (`eDP-1`, `DP-1`), or a monitor selector token (same matching rules as monitor overrides). `current` resolves from the active/focused output, falling back to the last interactive output.
Values and steps accept normalized (`0.0``1.0`) or percentage-style (`65`, `65%`, `5%`) values. `raise-brightness` / `lower-brightness` target `current` with a 5% step when no arguments are given.
---
## Night Light
Uses `wlsunset` to apply color temperature shifts.
```toml
[nightlight]
enabled = false
force = false # force night mode from startup
use_weather_location = true # use weather coordinates when no manual schedule or location is set
temperature_day = 6500 # Kelvin
temperature_night = 4000 # Kelvin
# Option A: explicit schedule
start_time = "20:30" # HH:MM — sunset / night starts
stop_time = "07:30" # HH:MM — sunrise / day starts
# Option B: geolocation schedule (used when start/stop are missing)
# latitude = 52.5200
# longitude = 13.4050
```
Priority: `start_time` + `stop_time` > explicit `latitude`/`longitude` > WeatherService coordinates. If only one of latitude/longitude is provided, Night Light refuses to start.
### IPC
```sh
noctalia msg enable-nightlight
noctalia msg disable-nightlight
noctalia msg toggle-nightlight
noctalia msg toggle-force-nightlight
```
`enable-nightlight` / `disable-nightlight` / `toggle-nightlight` control schedule enable state. `toggle-force-nightlight` toggles forced-on mode regardless of schedule.
---
## Weather
```toml
[weather]
enabled = false
auto_locate = false # resolve coordinates from IP address when true
address = "Toronto, ON" # geocoded when auto_locate = false
refresh_minutes = 30
unit = "celsius" # celsius | fahrenheit
```
When `auto_locate = false`, Noctalia geocodes `address` to latitude/longitude and fetches current weather plus a 6-day forecast. When `auto_locate = true`, the `address` field is ignored and location is resolved via IP.
Enabling weather adds a `Weather` tab to the control center. The bar `weather` widget type also requires weather to be enabled.
---
## Idle
Idle behaviors are named entries under `[idle.behavior.*]`. When no `config.toml` exists, Noctalia uses a built-in default with the lock behavior disabled.
```toml
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
enabled = false # explicitly disabled in the default config
[idle.behavior.screen-off]
timeout = 32
command = "noctalia:dpms-off"
[idle.behavior.custom]
timeout = 48
command = "notify-send 'Idle' 'Going idle'"
```
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `enabled` | bool | `true` | Enable or disable this behavior |
| `timeout` | int | `0` | Seconds before the behavior triggers |
| `command` | string | `""` | Shell command or `noctalia:` IPC subcommand |
### `noctalia:` commands in idle
The `noctalia:` prefix runs the rest of the string through the IPC command registry — the same as `noctalia msg <subcommand>`. Examples:
```
noctalia:lock
noctalia:dpms-off
noctalia:dpms-on
noctalia:enable-idle-inhibitor
noctalia:disable-idle-inhibitor
noctalia:toggle-idle-inhibitor
noctalia:panel-toggle launcher
noctalia:panel-toggle session
noctalia:panel-toggle clipboard
noctalia:panel-toggle wallpaper
noctalia:panel-toggle control-center
```
Idle behavior uses the Wayland `ext_idle_notifier_v1` protocol and respects active idle inhibitors.
---
## Notifications
```toml
[notification]
background_opacity = 0.97 # toast card background alpha; lower values let compositor blur show through
background_blur = true # request compositor background blur behind toasts
```
+123
View File
@@ -0,0 +1,123 @@
# Shell
- [Shell settings](#shell-settings)
- [OSD](#osd)
- [Keybinds](#keybinds)
- [Hooks](#hooks)
---
## Shell settings
Global UI settings that apply across all shell surfaces.
```toml
[shell]
ui_scale = 1.0 # content scale for panels and non-bar shell UI
font_family = "sans-serif" # Pango family string; Fontconfig handles fallback
lang = "en" # override language detection
notifications_dbus = true # when false, don't claim org.freedesktop.Notifications
polkit_agent = false # register Noctalia's native polkit authentication agent
password_style = "default" # default | random
avatar_path = "~/Pictures/avatar.png"
clipboard_auto_paste = "auto" # off | auto | ctrl_v | ctrl_shift_v | shift_insert
[shell.animation]
enabled = true
speed = 1.0 # 1.0 = normal, 0.5 = 2× slower, 2.0 = 2× faster
```
Notes:
- `ui_scale` is completely separate from `bar.scale`: `bar.scale` only affects bar widget content; `ui_scale` covers the control center, launcher, clipboard, and other non-bar surfaces. Neither changes Wayland output / HiDPI buffer scale.
- `font_family` sets the primary Pango family for all shell text. Can be a concrete family like `Inter` or a generic like `sans-serif`.
- `notifications_dbus` only controls the external D-Bus daemon. Internal notifications still appear in popups, history, and widgets when disabled.
- `polkit_agent` controls registration on `org.freedesktop.PolicyKit1`. Keep disabled if another desktop agent handles auth prompts.
- `password_style`: `default` uses `circle-filled`; `random` cycles through multiple filled glyph shapes on polkit and lock screen inputs.
- `clipboard_auto_paste`: `auto` = image entries use `Ctrl+V`, text entries use `Ctrl+Shift+V`; `off` = copy only, no automatic paste.
- `shell.animation.enabled` disables all animated transitions globally. `speed` scales durations globally.
---
## OSD
```toml
[osd]
position = "top_right" # top_right | top_left | top_center | bottom_right | bottom_left | bottom_center
```
The OSD powers the volume HUD and defaults to `top_right`.
---
## Keybinds
Centralized keyboard actions for shell panels (`launcher`, `session`, `clipboard`, `wallpaper`) and panel close/cancel.
```toml
[keybinds]
validate = ["return", "kp_enter"]
cancel = ["escape"]
left = ["left"]
right = ["right"]
up = ["up"]
down = ["down"]
```
Each action accepts a single string chord or an array of chords.
**Chord format:** `key`, `modifier+key`, or `modifier+modifier+key`
**Supported modifiers:** `ctrl`, `shift`, `alt`
**`super` bindings are rejected** (`super`, `win`, `windows`, `logo`, `meta`, `mod4`) and produce a config parse error.
**Supported actions:** `validate`, `cancel`, `left`, `right`, `up`, `down`
---
## Hooks
Optional shell hooks run commands when specific events happen. Define them under `[hooks]` in `config.toml`. Each event is a string (one command) or an array of strings (run in order). The same `noctalia:` prefix rules apply as in [idle behaviors](services.md#idle).
| Key | When it fires |
|-----|---------------|
| `started` | Once after Noctalia finishes startup (IPC ready). |
| `wallpaper_changed` | After a persisted wallpaper path change is applied. |
| `colors_changed` | After the theme palette is resolved and terminal templates are updated. |
| `session_locked` | When the compositor confirms the session lock. |
| `session_unlocked` | When the session leaves the locked state. |
| `logging_out` | Immediately before the session panel runs the logout sequence. |
| `rebooting` | Immediately before the session panel runs reboot. |
| `shutting_down` | Immediately before the session panel runs shutdown. |
| `wifi_enabled` / `wifi_disabled` | When NetworkManager's Wi-Fi radio toggles (not on first snapshot). |
| `bluetooth_enabled` / `bluetooth_disabled` | When the default adapter's powered state toggles (not on first snapshot). |
| `battery_state_changed` | When UPower's battery state enum changes (charging, discharging, etc.). |
| `battery_under_threshold` | When charge **crosses from above to at or below** `battery_low_percent_threshold`. |
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `battery_low_percent_threshold` | int | `0` | Percent (0100). `0` disables `battery_under_threshold`. Set to e.g. `15` to activate. |
```toml
[hooks]
started = "notify-send 'Noctalia' 'Shell started'"
wallpaper_changed = ["touch /tmp/noctalia-wallpaper"]
colors_changed = "notify-send 'Theme' 'Palette updated'"
session_locked = "notify-send 'Session' 'Locked'"
session_unlocked = "notify-send 'Session' 'Unlocked'"
logging_out = "echo logging out >> /tmp/noctalia-hooks.log"
rebooting = "notify-send 'System' 'Rebooting'"
shutting_down = "notify-send 'System' 'Shutting down'"
wifi_enabled = "notify-send 'Network' 'Wi-Fi on'"
wifi_disabled = "notify-send 'Network' 'Wi-Fi off'"
bluetooth_enabled = "notify-send 'BT' 'Bluetooth on'"
bluetooth_disabled = "notify-send 'BT' 'Bluetooth off'"
battery_low_percent_threshold = 15
battery_state_changed = "notify-send 'Power' 'Battery state changed'"
battery_under_threshold = "notify-send 'Power' 'Low battery'"
```
+105
View File
@@ -0,0 +1,105 @@
# Theme
Controls the colors used across the shell — bars, panels, widgets, OSD, overview, and notifications.
```toml
[theme]
mode = "dark" # dark | light | auto
source = "builtin" # builtin | wallpaper | community
builtin = "Noctalia" # bundled scheme name
community_palette = "Noctalia" # community palette name when source = "community"
wallpaper_scheme = "m3-content" # generator used when source = "wallpaper"
```
Theme changes apply live when you edit `config.toml`. Wallpaper-derived themes re-resolve automatically when the default wallpaper changes.
---
## `source`
| Value | Description |
|-------|-------------|
| `builtin` | Use one of the schemes compiled into the binary. |
| `wallpaper` | Generate a palette from the current default wallpaper each time it changes. |
| `community` | Fetch a palette by name from `https://api.noctalia.dev/palette/<name>`. |
---
## `builtin`
Names which bundled scheme to load when `source = "builtin"`. Unknown names fall back to `Noctalia`.
Available schemes: `Ayu`, `Catppuccin`, `Dracula`, `Eldritch`, `Gruvbox`, `Kanagawa`, `Noctalia`, `Nord`, `Rosé Pine`, `Tokyo-Night`
---
## `community_palette`
Names a palette served by `https://api.noctalia.dev/palette/<name>`. The shell downloads the palette on first use and caches it in `~/.cache/noctalia/community-palettes/` (honoring `XDG_CACHE_HOME`). Subsequent launches load the cached file and never touch the network, so community palettes work offline.
While the initial download is in flight — or if it fails — the shell falls back to `Noctalia` built-in and cross-fades once the download arrives.
To refresh a palette, delete the corresponding file under `~/.cache/noctalia/community-palettes/`.
---
## `wallpaper_scheme`
Picks the generator when `source = "wallpaper"`:
| Value | Description |
|-------|-------------|
| `m3-tonal-spot` | M3 default — balanced tones anchored on the seed color |
| `m3-content` | M3 for content-forward UIs — higher chroma |
| `m3-fruit-salad` | M3 with playful cross-hue accents |
| `m3-rainbow` | M3 spanning the full wheel for multi-accent layouts |
| `m3-monochrome` | M3 collapsed to a single hue |
| `vibrant` | Custom — saturated and high-contrast |
| `faithful` | Custom — stays close to the source image |
| `dysfunctional` | Custom — deliberately off-kilter |
| `muted` | Custom — low-saturation |
---
## `mode`
`dark` or `light` selects the palette variant. `auto` is treated as `dark` until system light/dark tracking lands.
### IPC
```sh
noctalia msg toggle-theme-mode
```
---
## External App Templates
Noctalia can apply generated colors to external app config files whenever the resolved theme changes.
```toml
[theme.templates]
enable_builtins = true
builtin_ids = [] # opt-in; run: noctalia theme --list-builtins
enable_user_templates = false
user_config = "~/.config/noctalia/user-templates.toml"
```
- `enable_builtins` enables the shipped built-in template catalog.
- `builtin_ids` selects which built-in templates run. Empty array = nothing applied until you opt in.
- `enable_user_templates` enables loading your own template file.
- `user_config` points at that extra file. When enabled, Noctalia creates a stub if it does not already exist.
```toml
[theme.templates]
enable_builtins = true
builtin_ids = ["foot", "walker", "gtk3", "gtk4"]
enable_user_templates = true
user_config = "~/.config/noctalia/user-templates.toml"
```
List available built-in template ids:
```sh
noctalia theme --list-builtins
```
+52
View File
@@ -0,0 +1,52 @@
# Wallpaper
```toml
[wallpaper]
enabled = true
fill_mode = "crop" # center | crop | fit | stretch | repeat
transition = ["fade", "wipe", "disc", "stripes", "zoom", "honeycomb"]
# array of effects picked at random each transition
# omit to use all effects
transition_duration = 1500 # milliseconds
edge_smoothness = 0.3 # 0.0 1.0
# Directory browsed by the wallpaper picker panel
directory = "/home/user/Wallpapers"
# Optional per-mode directories (parsed but not yet consumed by the renderer)
directory_light = "/home/user/Wallpapers/Light"
directory_dark = "/home/user/Wallpapers/Dark"
# Per-monitor overrides — same match rules as bar monitor overrides
[wallpaper.monitor.DP-2]
enabled = false
directory = "/home/user/Wallpapers/Vertical"
directory_light = "/home/user/Wallpapers/Vertical/Light"
directory_dark = "/home/user/Wallpapers/Vertical/Dark"
```
The wallpaper picker panel lists images in `directory` as a grid of thumbnails. Selecting a monitor in the panel toolbar switches to that monitor's override directory (falling back to the base `directory`). Clicking a tile writes the path to `state.toml` and applies it immediately. Picking a wallpaper while **ALL** is selected applies it to every connected output.
---
# Overview
Renders a blurred and tinted copy of the current wallpaper as a layer-shell backdrop for compositor overview modes (e.g. niri's overview). Disabled by default.
To make the surface visible during niri overview, add a layer-rule to your niri config:
```kdl
layer-rule {
match namespace="^noctalia-overview"
place-within-backdrop true
}
```
```toml
[overview]
enabled = false
unload_when_not_in_use = true # release resources while closed; false keeps them warm in VRAM
blur_intensity = 0.5 # 0.0 = no blur, 1.0 = maximum blur
tint_intensity = 0.3 # 0.0 = no tint, 1.0 = fully opaque tint
```
The tint color is `palette.surface` and is not currently configurable.
+428
View File
@@ -0,0 +1,428 @@
# Widgets
## Widget definitions
Widget definitions are optional. When a name in a widget list matches a `[widget.<name>]` entry, that entry's `type` and settings are used. When there is no match, the name itself is treated as the widget type with default settings.
```toml
[widget.<name>]
type = "<widget-type>" # defaults to <name> when omitted
# ...widget-specific settings
```
This allows multiple instances of the same widget type:
```toml
[widget.clock-seconds]
type = "clock"
format = "{:%H:%M:%S}"
[bar.main]
end = ["clock", "clock-seconds"] # two clock widgets, different formats
```
---
## `clock`
Displays the current time.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `format` | string | `{:%H:%M}` | `std::format`-style chrono format string (horizontal bars and vertical fallback) |
| `vertical_format` | string | `""` | Format used when the bar is vertical. When empty, falls back to `format` with `:` replaced by line breaks. |
```toml
[widget.clock]
format = "{:%H:%M}"
vertical_format = "{:%H\n%M}"
[widget.clock-seconds]
type = "clock"
format = "{:%H:%M:%S}"
vertical_format = "{:%H\n%M\n%S}"
```
---
## `spacer`
Empty space between widgets.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `length` | number | `8` | Spacer length in screen pixels |
```toml
[widget.gap]
type = "spacer"
length = 24
```
---
## `workspaces`
Workspace switcher with solid dots/pills and optional labels.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `display` | string | `"id"` | Label mode: `"none"`, `"id"`, or `"name"` |
```toml
[widget.workspaces]
display = "id" # none | id | name
```
---
## `sysmon`
System resource monitor. Shows an icon and value for one configurable stat. Multiple instances with different stats can coexist on the same bar.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `stat` | string | `"cpu_usage"` | Which stat to display (see table below) |
| `path` | string | `"/"` | Mount path for `disk_pct` (ignored otherwise) |
| `display` | string | `"gauge"` | `"gauge"` = icon + vertical fill bar; `"graph"` = icon + sparkline + value; `"text"` = icon + value |
**`stat` values:**
| Value | Display | Description |
|-------|---------|-------------|
| `cpu_usage` | `75%` | CPU utilisation (all cores) |
| `cpu_temp` | `65°C` | CPU package temperature |
| `ram_used` | `4.2G` | RAM used (human-readable) |
| `ram_pct` | `26%` | RAM used % |
| `swap_pct` | `12%` | Swap used % |
| `disk_pct` | `45%` | Disk used % for `path` |
```toml
[widget.cpu]
type = "sysmon"
stat = "cpu_usage"
[widget.temp]
type = "sysmon"
stat = "cpu_temp"
[widget.ram]
type = "sysmon"
stat = "ram_used"
[widget.disk]
type = "sysmon"
stat = "disk_pct"
path = "/"
```
---
## `volume`
Shows the default audio sink volume and mute state via PipeWire. No configurable settings.
### IPC
```sh
# Output (speaker)
noctalia msg set-volume 65
noctalia msg raise-volume
noctalia msg raise-volume 10
noctalia msg lower-volume
noctalia msg mute
# Input (microphone)
noctalia msg set-mic-volume 0.5
noctalia msg raise-mic-volume
noctalia msg raise-mic-volume 5%
noctalia msg lower-mic-volume
noctalia msg mute-mic
```
Volume and mic IPC use a default step of 5% when no explicit step is provided. Values and steps accept normalized (`0.0``1.0`) or percentage-style values (`65`, `65%`, `5%`). Rule of thumb: if you include a decimal and the value is `<= 1.0`, it is treated as normalized; otherwise it is treated as a percentage.
Output and mic volume are clamped to 0100% by default. If `[audio] enable_overdrive = true`, the maximum is raised to 150%.
---
## `audio_visualizer`
Horizontal audio spectrum using the current PipeWire monitor stream.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `width` | number | `56` | Widget width in screen pixels |
| `height` | number | `16` | Widget height in screen pixels |
| `bands` | number | `16` | Number of spectrum bands |
| `mirrored` | bool | `false` | Mirror the spectrum around the center line |
| `low_color` | string | `"primary"` | Color for the first bars (theme role or `#` hex) |
| `high_color` | string | `"primary"` | Color for the last bars (theme role or `#` hex) |
```toml
[widget.audio-vis]
type = "audio_visualizer"
width = 64
height = 16
bands = 20
mirrored = true
low_color = "primary"
high_color = "secondary"
```
---
## `media`
Shows the current media artwork and track title from MPRIS.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `max_length` | number | `220` | Maximum length for the title text |
| `art_size` | number | `16` | Artwork size in pixels before scale |
```toml
[widget.media]
max_length = 220
art_size = 24
```
---
## `battery`
Shows battery charge level and state via UPower. Hides itself when no battery is present — safe to include on desktops.
No configurable settings.
---
## `brightness`
Shows the current display brightness level and adjusts it via scroll wheel (±5% per step). Hides itself when no controllable display is found. Click opens the display tab in the control center.
No configurable settings. See [`[brightness]`](services.md#brightness) for backend configuration.
---
## `bluetooth`
Shows the Bluetooth adapter state and connected device. Click opens the Bluetooth tab in the control center.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `show_label` | bool | `false` | Show the connected device name next to the icon |
```toml
[widget.bluetooth]
show_label = true
```
---
## `network`
Shows the current network connection (Wi-Fi SSID and signal, or wired) via NetworkManager. Dims and shows a `wifi-off` glyph when disconnected.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `show_label` | bool | `true` | Show the SSID or interface name next to the glyph |
```toml
[widget.network]
show_label = false
```
---
## `keyboard_layout`
Shows the current keyboard layout indicator from the active XKB state.
Left-click cycles layouts using the compositor backend when supported. Override with `cycle_command` for a custom shell command.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `display` | string | `"short"` | `"short"` = compact code; `"full"` = full layout name |
| `cycle_command` | string | `""` | Optional override command run on left click instead of compositor backend |
```toml
[widget.keyboard_layout]
display = "short" # short | full
```
---
## `lock_keys`
Shows Caps Lock / Num Lock / Scroll Lock state from the active XKB keyboard state.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `display` | string | `"short"` | `"short"` (`C N S`) or `"full"` (`Caps Num Scroll`) |
| `show_caps_lock` | bool | `true` | Show the Caps Lock indicator |
| `show_num_lock` | bool | `true` | Show the Num Lock indicator |
| `show_scroll_lock` | bool | `false` | Show the Scroll Lock indicator |
| `hide_when_off` | bool | `false` | Hide each indicator when it is off |
```toml
[widget.lock_keys]
display = "short"
show_caps_lock = true
show_num_lock = true
show_scroll_lock = false
hide_when_off = false
```
---
## `launcher`
Opens the launcher panel on click.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `icon` | string | `"search"` | Bar glyph id (Tabler-backed aliases) |
```toml
[widget.launcher]
icon = "menu"
```
---
## `weather`
Shows the current weather in the bar and opens the Weather control-center tab on click. Requires `[weather]` to be configured.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `max_length` | number | `160` | Maximum length for the weather text |
| `show_condition` | bool | `true` | Show condition text like `Overcast` next to temperature |
```toml
[widget.weather]
max_length = 180
show_condition = false
```
---
## `notifications`
Shows the pending notification count. Right-clicking toggles transient Do Not Disturb (DND) mode.
No configurable settings.
### IPC
```sh
noctalia msg toggle-notification-dnd
noctalia msg notification-dnd-status
```
When DND is enabled, incoming notifications are stored in history/control center but toast popups are suppressed.
---
## `tray`
System tray (StatusNotifierItem).
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `hidden` | array of string | `[]` | Tray items to hide by id/name/bus/path token |
```toml
[widget.tray]
hidden = ["nm-applet", "blueman", "org.kde.StatusNotifierItem-1-1"]
```
---
## `power_profiles`
Shows the current power profile using a glyph and cycles to the next available profile on click.
No configurable settings.
---
## `idle_inhibitor`
Shows a keep-awake glyph and toggles the compositor idle inhibitor on click. Uses the standard Wayland `zwp_idle_inhibit_manager_v1` protocol.
No configurable settings.
### IPC
```sh
noctalia msg enable-idle-inhibitor
noctalia msg disable-idle-inhibitor
noctalia msg toggle-idle-inhibitor
```
---
## `nightlight`
Cycles through three night light states on click:
| State | Icon | Meaning |
|-------|------|---------|
| Off | moon-off | Night light disabled |
| On | moon | Scheduled — follows `start_time`/`stop_time` or location |
| Forced | moon-stars | Always-on override — ignores schedule |
No configurable settings.
### IPC
```sh
noctalia msg toggle-nightlight
noctalia msg toggle-force-nightlight
```
---
## `theme_mode`
Toggles between dark (moon glyph) and light (sun glyph) theme mode on click.
No configurable settings.
### IPC
```sh
noctalia msg toggle-theme-mode
```
---
## `session`
Shows a power glyph and opens the session menu panel on click.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `icon` | string | `"shutdown"` | Bar glyph id (Tabler-backed aliases) |
```toml
[widget.session]
icon = "lock"
```
---
## `wallpaper`
Shows a glyph and opens the wallpaper picker panel on click.
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `icon` | string | `"wallpaper-selector"` | Bar glyph id (Tabler-backed aliases) |
```toml
[widget.wallpaper]
icon = "photo"
```
+210
View File
@@ -0,0 +1,210 @@
# Noctalia configuration — all settings at their defaults.
# Copy to ~/.config/noctalia/config.toml and edit what you need.
# Changes are hot-reloaded via inotify — no restart required.
# ── Bar ───────────────────────────────────────────────────────────────────────
[bar.main]
position = "top" # top | bottom | left | right
thickness = 34
background_opacity = 1.0
background_blur = true # ext-background-effect-v1 (niri); inert on other compositors
radius = 12
margin_h = 180
margin_v = 10
padding = 14
widget_spacing = 6
scale = 1.0
shadow_blur = 12
shadow_offset_x = 0
shadow_offset_y = 6
auto_hide = false
reserve_space = false
capsule = false
# capsule_fill = "surface_variant"
# capsule_opacity = 1.0
# capsule_border = "outline" # uncomment for a border on all widgets
start = ["launcher", "wallpaper", "workspaces"]
center = ["clock"]
end = ["media", "tray", "notifications", "network", "bluetooth", "volume", "brightness", "battery", "session"]
# Per-monitor override example — only the fields you list are overridden:
# [bar.main.monitor.dp1]
# match = "DP-1"
# thickness = 44
# ── Dock ──────────────────────────────────────────────────────────────────────
[dock]
enabled = false # set true to activate
position = "bottom" # top | bottom | left | right
icon_size = 48
padding = 8
item_spacing = 6
background_opacity = 0.88
background_blur = true
radius = 16
margin_h = 0
margin_v = 8
shadow_blur = 12
shadow_offset_x = 0
shadow_offset_y = 4
show_running = true
auto_hide = false
reserve_space = false
active_scale = 1.0
inactive_scale = 0.85
active_opacity = 1.0
inactive_opacity = 0.85
show_instance_count = true
active_monitor_only = false
pinned = [] # e.g. ["firefox", "code", "kitty"]
# ── Desktop Widgets ───────────────────────────────────────────────────────────
[desktop_widgets]
enabled = false
# ── Wallpaper ─────────────────────────────────────────────────────────────────
[wallpaper]
enabled = true
fill_mode = "crop" # center | crop | fit | stretch | repeat
transition = ["fade", "wipe", "disc", "stripes", "zoom", "honeycomb"]
transition_duration = 1500 # milliseconds
edge_smoothness = 0.3
directory = "~/Pictures/Wallpapers"
# ── Overview ──────────────────────────────────────────────────────────────────
[overview]
enabled = false
unload_when_not_in_use = true
blur_intensity = 0.5 # 0.0 = no blur, 1.0 = maximum
tint_intensity = 0.3 # 0.0 = no tint, 1.0 = opaque
# ── Theme ─────────────────────────────────────────────────────────────────────
[theme]
mode = "dark" # dark | light | auto
source = "builtin" # builtin | wallpaper | community
builtin = "Noctalia" # Ayu | Catppuccin | Dracula | Eldritch | Gruvbox |
# Kanagawa | Noctalia | Nord | Rosé Pine | Tokyo-Night
# community_palette = "Noctalia" # fetched from api.noctalia.dev, cached locally
# wallpaper_scheme = "m3-content" # m3-tonal-spot | m3-content | m3-fruit-salad |
# m3-rainbow | m3-monochrome | vibrant | faithful |
# dysfunctional | muted
[theme.templates]
enable_builtins = true
builtin_ids = [] # opt-in; list ids with: noctalia theme --list-builtins
enable_user_templates = false
user_config = "~/.config/noctalia/user-templates.toml"
# ── Audio ─────────────────────────────────────────────────────────────────────
[audio]
enable_overdrive = false # allow volume above 100% (up to 150%)
# ── Brightness ────────────────────────────────────────────────────────────────
[brightness]
enable_ddcutil = false
# ignore_mmids = [] # skip these monitors from ddcutil; run: ddcutil --verbose detect
# Per-monitor backend override:
# [brightness.monitor.eDP-1]
# backend = "backlight" # auto | none | backlight | ddcutil
# [brightness.monitor.DP-1]
# backend = "ddcutil"
# ── Night Light ───────────────────────────────────────────────────────────────
[nightlight]
enabled = false
force = false
use_weather_location = true
temperature_day = 6500 # Kelvin
temperature_night = 4000 # Kelvin
# start_time = "20:30" # HH:MM — explicit schedule takes priority over geolocation
# stop_time = "07:30"
# latitude = 52.5200
# longitude = 13.4050
# ── Weather ───────────────────────────────────────────────────────────────────
[weather]
enabled = false
auto_locate = false # resolve location from IP when true
address = "Toronto, ON" # geocoded when auto_locate = false
refresh_minutes = 30
unit = "celsius" # celsius | fahrenheit
# ── Idle ──────────────────────────────────────────────────────────────────────
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
enabled = false
# [idle.behavior.screen-off]
# timeout = 32
# command = "noctalia:dpms-off"
# ── Notifications ─────────────────────────────────────────────────────────────
[notification]
background_opacity = 0.97
background_blur = true
# ── OSD ───────────────────────────────────────────────────────────────────────
[osd]
position = "top_right" # top_right | top_left | top_center | bottom_right | bottom_left | bottom_center
# ── Shell ─────────────────────────────────────────────────────────────────────
[shell]
ui_scale = 1.0
font_family = "sans-serif"
notifications_dbus = true
polkit_agent = false
password_style = "default" # default | random
clipboard_auto_paste = "auto" # off | auto | ctrl_v | ctrl_shift_v | shift_insert
# avatar_path = "~/Pictures/avatar.png"
# lang = "en"
[shell.animation]
enabled = true
speed = 1.0 # 0.5 = 2× slower, 2.0 = 2× faster
# ── Keybinds ──────────────────────────────────────────────────────────────────
[keybinds]
validate = ["return", "kp_enter"]
cancel = ["escape"]
left = ["left"]
right = ["right"]
up = ["up"]
down = ["down"]
# ── Hooks ─────────────────────────────────────────────────────────────────────
[hooks]
battery_low_percent_threshold = 0 # set to e.g. 15 to enable battery_under_threshold hook
# started = "notify-send 'Noctalia' 'Shell started'"
# wallpaper_changed = []
# colors_changed = []
# session_locked = []
# session_unlocked = []
# logging_out = []
# rebooting = []
# shutting_down = []
# wifi_enabled = []
# wifi_disabled = []
# bluetooth_enabled = []
# bluetooth_disabled = []
# battery_state_changed = []
# battery_under_threshold = []