CONFIGURATION.md

Configuration Guide

Paneru is configured via a TOML file or a Lua script — never both. By default, it looks for the TOML configuration in the following locations (in order):

  1. $PANERU_CONFIG (environment variable)
  2. $HOME/.paneru
  3. $HOME/.paneru.toml
  4. $XDG_CONFIG_HOME/paneru/paneru.toml

The configuration is automatically reloaded when the file is saved.

If an init.lua exists (see Lua Scripting Guide), it takes over completely and none of these TOML paths are read.


1. Global Options ([options])

General behavior settings for the window manager.

OptionTypeDefaultDescription
focus_follows_mouseBooleantrueIf enabled, the window under the mouse cursor will automatically gain focus.
mouse_follows_focusBooleantrueIf enabled, the mouse cursor will warp to the center of the focused window when focus changes via keyboard.
horizontal_mouse_warpInteger (-1, 1)OffIf enabled, the mouse will warp to another screen above or below, when touching the left or right edge. The direction depends on the direction - a negative value will cause the left edge to warp to a screen above and the right edge to a screen below. This allows having horizontal positioning of displays while having them aligned in a virtual layout in macOS settings. The cursor lands at the opposite edge of the target display (preserving cursor flow), with the source's relative Y position. Carries pre-warp horizontal velocity to avoid a "standing start", and skips the warp when the equivalent Y has no position on the target — matching macOS's native side-by-side behavior for displays of unequal height. (inspired by https://github.com/mogenson/WarpMouse.spoon)
horizontal_mouse_warp_offsetInteger (px)0Vertical pixel offset applied to the horizontal_mouse_warp landing position, signed by warp direction. Positive values shift the cursor lower when warping to a display below (in macOS arrangement) and higher when warping to one above. Use to compensate for physical desk arrangement differing from the macOS arrangement (e.g. portrait monitor sitting physically higher or lower than the laptop).
preset_column_widthsArray (Float)[0.25, 0.33, 0.5, 0.66, 0.75, 1.0, 1.5, 2.0]Ratios of the screen width used by the window_resize command and the menu bar width picker. Values above 1.0 create a horizontally scrollable oversized window.
preset_stack_heightsArray (Float)[0.25, 0.33, 0.5, 0.66, 0.75]Ratios of the viewport height used by the window_vertical_resize command. Only applies to windows inside a stack; every window in the column is kept at least 200px tall, so a ratio that would starve a neighbour is clamped.
animation_speedFloatNoneSpeed of window animations. Comfortable range is from 8 to 20. Unset or set to a very high value to effectively disable animations.
auto_centerBooleanfalseAutomatically center the focused window on the screen when switching focus.
sliver_heightFloat (0.1–1.0)1.0Vertical ratio of off-screen windows kept visible to prevent macOS from relocating them.
sliver_widthInteger (px)5Horizontal width of off-screen windows kept visible.
menubar_heightInteger (px)AutoManually override the detected macOS menubar height.
window_hidden_ratioFloat (0.0–1.0)0.0How much of a window can be hidden before it's forced into view on focus change. 0.0 = eager, 1.0 = lazy.
window_resize_cycleBooleantrueIf disabled, window_resize and window_shrink (and their window_vertical_* counterparts) stop at the largest/smallest preset instead of cycling back.
mouse_resize_modifierStringNoneIf enabled allows window resizing using mouse movement. For example cmd + shift will allow resizing of the window when holding those keys. Proximity of the pointer to left or right window edge determines which side will be adjusted.
reap_empty_workspacesStringfalseIf enabled, a virtual workspace without any windows will be removed.
disable_native_tabsBooleanfalseIf enabled, Paneru will not auto-merge a window into a tab group with an existing same-app sibling that shares its frame. Merging happens when the window is created, and again for a background tab that the window server stops showing while a sibling of the same app holds the same frame — an app that hides its old tab a moment late would otherwise leave a column nothing can appear in. Use this if you find unrelated windows being grouped together.
virtual_workspace_animationsBooleanfalseIf enabled, Paneru will animate virtual workspace swaps. Off by default, because people use virtual workspaces due to the slow animation of the native macOS workspaces.
insert_windows_mid_stripBooleanfalseWhen moving a window to another virtual workspace, insert it at the column matching its current on-screen position (keeping it where you see it and shifting the rest) instead of appending it to the end of the destination strip.
create_virtual_workspace_automaticallyBooleanfalseAutomatically creates a new virtual workspace when using window_virtual_south or Southward gesture controls.

2. Padding ([padding])

Sets the margins at the edges of the screen.

OptionTypeDefaultDescription
topInteger (px)0Padding at the top of the screen.
bottomInteger (px)0Padding at the bottom of the screen.
leftInteger (px)0Padding at the left edge.
rightInteger (px)0Padding at the right edge.

3. Swipe & Gestures ([swipe])

Configure trackpad gestures and scroll-wheel window sliding.

OptionTypeDefaultDescription
sensitivityFloat (0.1–2.0)0.35Multiplier for swipe distance.
decelerationFloat (1.0–10.0)4.0Rate at which inertia slows down after a swipe.
continuousBooleantrueIf true, the windows are allowed to fully move across the desktop, potentially exposing the empty desktop space. If false, the window strip will not move further than the left or right most window. This also affects the windows during keyboard focus - if false the left or right most windows will snap to the edge of display.

[swipe.gesture]

OptionTypeDefaultDescription
fingers_countIntegerNoneNumber of fingers for the swipe gesture. Set to 3 or more to enable.
directionString"Natural"Direction of movement: "Natural" or "Reversed".
verticalBooleantrueInterpret the vertical gestures with fingers_count or ignore them. Enabling this allows using vertical swipe gestures to change virtual desktops.
pagingBooleanfalseFocus one adjacent window for each horizontal gesture threshold instead of sliding the strip freely.
page_distance_cmPositive float2.0Approximate horizontal finger travel in centimeters required for each page when paging = true.
hapticsBooleantrueEmit a haptic pulse after each confirmed paging focus change. Set to false to disable pulses.

When fingers_count is omitted or set below 3, Paneru does not intercept native macOS gestures. If macOS uses three-finger horizontal swipes for Spaces, prefer [swipe.scroll] with a modifier or configure a different finger count.

Paging estimates physical distance from the trackpad's reported device size. The centimeter threshold may feel slightly different across devices; adjust page_distance_cm to suit your trackpad. Haptics require compatible hardware and may be suppressed by macOS when fingers are no longer touching it.

To move one window at a time with the existing focus animation:

toml
1[swipe.gesture]
2fingers_count = 3
3paging = true
4page_distance_cm = 2.0
5haptics = true

Each additional 2 cm of horizontal finger travel selects another window. Haptics are on by default in paging mode; set haptics = false to disable the pulses while keeping the same paging behavior.

[swipe.scroll]

OptionTypeDefaultDescription
modifierString"alt"Modifier key(s) required to slide windows with the scroll wheel: "alt", "rcmd", "ralt + cmd", "lctrl + lalt + cmd", etc.
window_stepBooleanfalseSelect one adjacent column per accepted wheel input. This uses the same focus commands as window_focus_west and window_focus_east.
vertical_modifierStringNoneAdditional modifier key that, when held together with modifier, switches virtual workspaces vertically instead of scrolling horizontally. For example, if modifier = "alt" and vertical_modifier = "shift", then alt + scroll slides windows horizontally and alt + shift + scroll switches virtual workspace rows.

To select windows with Alt + mouse wheel, enable window steps:

toml
1[swipe.scroll]
2modifier = "alt"
3window_step = true

Window steps use the current focus commands and scroll direction. Repeated inputs in the same direction are limited to one step every 180 milliseconds. Momentum is ignored. Trackpad gestures and vertical workspace scrolling keep their existing behavior.


4. Decorations ([decorations])

Visual styling for workspaces, active and inactive windows.

Virtual Workspace indicators

Toggles display of the currently active virtual workspace in the menubar or in a brief status popup window. Both are enabled by default. workspace_menu_status controls only the workspace indicator; the descriptor and menu remain available.

Example:

toml
1[decorations]
2# Both default to true
3workspace_menu_status = false
4workspace_popup_status = true

[decorations.menu]

Basic settings for the menubar display.

OptionTypeDefaultDescription
enabledBooleantrueShow the Paneru menu bar item unless a saved menu/command choice overrides it. Set to false to hide the entire item, including its descriptor, workspace indicator, and menu, freeing its menu bar space. Changes apply without restarting.
orientationString'default'Which of the menubar elements appears first, the descriptor 'default' or the indicator 'flipped'.
colors[String]['#FFFFFF']An array of hex colors defining a linear gradient (left to right) coloring the menubar display. Set a solid color by including only one element.
angleFloat90.0The angle (in degrees) of the gradient.

To hide Paneru from the menu bar:

toml
1[decorations.menu]
2enabled = false

Or in init.lua:

lua
1paneru.setup {
2 decorations = { menu = { enabled = false } },
3}

You can also click Hide Menu Bar in Paneru's menu. This choice is saved across restarts and overrides enabled. To show the item again:

sh
1paneru showmenu

This command shows the item in the running daemon and remembers the new choice. Lua scripts can use paneru.showmenu() and paneru.hidemenu(), or bind showmenu and hidemenu to keys. Choices are stored in $XDG_STATE_HOME/paneru/menu-bar.json (normally ~/.local/state/paneru/menu-bar.json); removing this file while Paneru is stopped restores the configuration's behavior.

The permission setup menu remains visible until the required macOS access is granted.

[decorations.menu.descriptor]

Settings for the descriptor component of the menubar display. The descriptor component is a visual marker for the Paneru menubar item.

OptionTypeDefaultDescription
styleString'symbol'Composition of the descriptor. Options are any SF Symbol 'symbol', any string of text 'text', both of those 'both', or nothing 'hidden'
textString'VW'Text shown if 'text' or 'both' style is selected.
symbolString'fish.fill'SF Symbol shown if 'symbol' or 'both' style is selected. SF Symbol names can be found in the SF Symbols App, in the Xcode library, or in a variety of places online.

[decorations.menu.indicator]

Settings for the indicator component of the menubar display. The indicator component shows your current (and optionally all active) virtual workspace(s).

Important Note: The 'unicode' and 'marked' formats only mean anything next to the workspaces they are not, so they are incompatible with the single-item 'mono' and 'paged' styles and will fall back to 'default'.

OptionTypeDefaultDescription
styleString'mono'Whether the indicator displays all virtual workspaces 'multi', only your current workspace 'mono', or your current workspace over how many there are 'paged' (2 / 4).
formatString'default'The format of the character(s) which represent your virtual workspace(s).
Options are regular numbers 'default', Roman numerals 'roman', any pair of Unicode characters 'unicode', or the inactive Unicode character with the active workspace shown as its number 'marked' (· 2 · ·).
font_sizeFloat (2.0 to 24.0)13.0Font size of the indicator in point.
active_characterChar'☉'The Unicode character which represents an active workspace in the 'unicode' format.
inactive_characterChar'○'The Unicode character which represents an inactive workspace in the 'unicode' format.

[decorations.inactive.dim] (Native macOS Dimming)

Paneru supports native macOS window dimming. To use this mode, only set opacity (and optionally opacity_night). Do not set a color.

OptionTypeDefaultDescription
opacityFloat (-1.0 to 1.0)0.0Dimming intensity. -1.0 is fully black, 1.0 is fully white.
opacity_nightFloat (-1.0 to 1.0)opacityDimming intensity used when macOS is in Dark Mode.

Example:

toml
1[decorations.inactive.dim]
2opacity = -0.15
3opacity_night = -0.25

5. Keybindings ([bindings])

Bindings map a key combination to an action. A binding can be a single string or an array of strings.

Format: "[modifiers-]key". For example alt + cmd - j and cmd - j.

Available modifiers are:

  • alt, lalt, ralt
  • ctrl, lctrl, rctrl
  • cmd, lcmd, rcmd
  • shift, lshift, rshift
  • fn

For a full list of parseable keys (i.e. leftarrow) check the source: https://github.com/karinushka/paneru/blob/3790b01f8d65df5d9000142db7cf25f9270dcccc/src/config.rs#L1466-L1601

Window commands

ActionDescription
window_focus_west / _eastFocus window to the left/right.
window_focus_north / _southFocus window above/below. If no window exists, switches focus to the display in that direction.
window_focus_first / _lastJump to the start/end of the strip.
window_focus_managedSwitch to a previously focused window on this workspace.
window_focus_unmanagedSwitch to a previously focused floating window on this workspace.
window_swap_west / _eastSwap current window with neighbor.
window_swap_north / _southSwap current window above/below. If no window exists, moves the window to the display in that direction.
window_swap_first / _lastMove current window to start/end of strip.
window_centerCenter the current window in the viewport.
window_resizeCycle through preset widths (Grow).
window_growAlias for window_resize.
window_shrinkCycle through preset widths (Shrink).
window_vertical_resizeCycle a stacked window through preset heights (Grow). No-op outside a stack.
window_vertical_growAlias for window_vertical_resize.
window_vertical_shrinkCycle a stacked window through preset heights (Shrink).
window_fullwidthToggle full-width mode.
window_manageToggle between tiled and floating state.
window_stackStack the current window into the column on the left.
window_unstackPull a window out of a stack into its own column.
window_tabbeddisplayToggle the focused stack between a normal split display and a tabbed display (one window visible at a time, sharing the full column). Cycle tabs with window_focus_north/window_focus_south.
window_equalizeMake all windows in a stack equal height.
window_balanceMake all columns in the strip the same width as the focused window.
window_nextdisplayMove focused window to the next monitor and follow it.
window_nextdisplaysendMove focused window to the next monitor but stay on current.
mouse_nextdisplayWarp mouse cursor to the next monitor.
window_snapSnap an overflowing window into the viewport.
window_raise_floatingMake the floating windows layer visible on the current workspace.
window_togglefloatlayerSelectively move the floating windows in front or behind of the workspace windows.
window_copyruleCopy a window rule template for the focused window to the clipboard.
showmenuShow the Paneru menu bar item and remember the choice across restarts.
hidemenuHide the entire Paneru menu bar item and remember the choice across restarts.
quitExit Paneru.
restartRestart the Paneru service (paneru restart).

Example:

toml
1[bindings]
2window_focus_west = "cmd - h"
3window_resize = ["alt - r", "ctrl - r"]

Virtual workspaces (Experimental)

Paneru allows having virtual spaces inside of the native macOS workspace. Logically it can be thought of several strips of windows (rows) stacked on top of each other within the single workspace. Similar to how Niri implements the movement between the vertical workspaces.

Shifting up or down goes to the previous or next strip of windows - wrapping around at the start or the end.

Moving the last window out of the virtual row, will "collapse it".

Virtual workspaces can also be navigated using trackpad gestures. If [swipe.gesture] is configured, a vertical 3/4-finger swipe will switch between virtual workspace rows, while horizontal swipes continue to scroll the strip as usual. For mouse users, see the vertical_modifier option under [swipe.scroll].

ActionDescription
window_virtual_north / _south / _first / _lastSwitch to the previous/next or first/last virtual workspace (row of windows), unconditionally. _east or _west are aliases for _north and _south.
window_virtualfocus_north / _southLike window_virtual_north/_south, but if the focused window is in a stack and has a neighbor above/below, focuses that neighbor instead of switching — the same within-column traversal as window_focus_north/_south. Only switches the virtual workspace once there's nothing left to focus that way. No _east/_west/_first/_last form (there's no "focus" reading to pair with those).
window_virtualnum_<number>Switch directly to the numbered virtual workspace.
window_virtualmove_north / _south / _first / _lastMove currently focused window to the previous/next or first/last virtual workspace and follow it. _east or _west are aliases for _north and _south.
window_virtualsend_north / _south / _first / _lastMove currently focused window to the previous/next or first/last virtual workspace but stay on the current one. _east or _west are aliases for _north and _south.
window_virtualmovenum_<number>Move currently focused window to the numbered virtual workspace and follow it.
window_virtualsendnum_<number>Move currently focused window to the numbered virtual workspace but stay on the current one.

Example:

toml
1[bindings]
2window_virtual_north = "cmd + shift - k"
3window_virtual_south = "cmd + shift - j"
4window_virtualmove_north = "cmd + alt - k"
5window_virtualmove_south = "cmd + alt - j"
6window_virtualnum_1 = "cmd + alt - 1"
7window_virtualnum_2 = "cmd + alt - 2"
8window_virtualnum_3 = "cmd + alt - 3"
9window_virtualmovenum_1 = "cmd + alt + ctrl - 1"
10window_virtualmovenum_2 = "cmd + alt + ctrl - 2"
11window_virtualmovenum_3 = "cmd + alt + ctrl - 3"
12window_virtualsendnum_1 = "cmd + alt + shift - 1"
13window_virtualsendnum_2 = "cmd + alt + shift - 2"
14window_virtualsendnum_3 = "cmd + alt + shift - 3"

Example command line:

shell
1# Move to the previous virtual workspace.
2$ paneru send-cmd window virtual north
3# Move the current window to the next virtual workspace.
4$ paneru send-cmd window virtualmove south
5# Move directly to virtual workspace 3.
6$ paneru send-cmd window virtualnum 3
7# Move the current window to virtual workspace 3 and follow it.
8$ paneru send-cmd window virtualmovenum 3
9# Send the current window to virtual workspace 3 and stay here.
10$ paneru send-cmd window virtualsendnum 3

See QUERY_AND_SUBSCRIBE_FORMAT.md for the structured paneru query responses and paneru subscribe event stream.


6. Window Rules ([windows])

Define specific behaviors for applications based on their Title, Bundle ID, or accessibility role/subrole.

OptionTypeDescription
titleRegex(Required) Regex pattern to match the window title.
bundle_idStringOptional Bundle ID to match (e.g., com.apple.Terminal).
roleStringOptional accessibility role to match exactly (e.g., AXWindow). A rule with role never matches a window that reports none.
subroleStringOptional accessibility subrole to match exactly (e.g., AXStandardWindow, AXDialog, AXFloatingWindow). A rule with subrole never matches a window that reports none.
floatingBooleanForce the window to be floating/unmanaged.
manageBooleanForce Paneru to manage this app/window even if macOS reports the app as unobservable or the window has a non-standard role/subrole.
indexIntegerPreferred position in the strip when spawned.
dont_focusBooleanPrevent the window from taking focus when spawned.
widthPositive Float or "complement_focused"Initial width ratio for the window. Values above 1.0 create an oversized, horizontally scrollable window. "complement_focused" uses 1.0 minus the focused window's width ratio; if there is no focused window or it is at least 95% wide, the new window starts at full width.
gridStringplacement for floating windows: "cols:rows:x:y:w:h".
horizontal_paddingIntegerGaps to the left/right of this window.
vertical_paddingIntegerGaps to the top/bottom of this window.
bindings_passthroughArray (String)Keys that should bypass Paneru and go directly to the app.

For example, use width = "complement_focused" in a window rule to give each new window the width remaining beside the currently focused window. A new window opened from a full-width window also starts at full width. This width is chosen during initial placement, before the window_spawned Lua hook runs.

Example:

toml
1[windows.terminal]
2title = ".*"
3bundle_id = "com.apple.Terminal"
4horizontal_padding = 5
5bindings_passthrough = ["ctrl-h", "ctrl-l"]

Windows whose subrole isn't AXStandardWindow or AXFloatingWindow (e.g. AXDialog) are ignored by Paneru unless a rule with manage = true matches them, so a role/subrole rule for such windows only takes effect for apps that are also force-managed.

Example:

toml
1[windows.xcode]
2title = ".*"
3bundle_id = "com.apple.dt.Xcode"
4manage = true
5
6[windows.dialogs]
7title = ".*"
8subrole = "AXDialog"
9floating = true
10
11[windows.floating_window]
12title = ".*"
13subrole = "AXFloatingWindow"
14floating = true

Copying a window rule

Neither the bundle ID nor the exact window title is visible anywhere in the UI, so the Paneru menu bar has a Copy Window Rule entry. You can also trigger it via a keybinding (e.g., window_copyrule = "ctrl + alt - c") or run it from the command line (paneru window copyrule). It puts a rule for the currently focused window on the clipboard, ready to paste:

toml
1[windows.ghostty]
2bundle_id = "com.mitchellh.ghostty"
3title = "^paneru — zsh$"
4# title = ".*" # all windows of this app
5# role = "AXWindow"
6# subrole = "AXStandardWindow"
7# app: Ghostty role: AXWindow subrole: AXStandardWindow
8# floating = true
9# manage = true
10# width = 0.5

The two matchers are live; everything else is a comment for you to uncomment. title is anchored and regex-escaped so it matches only this window — swap in the commented .* to cover every window of the app instead. The app, role and subrole line is there to tell one pasted rule from the next. role and subrole are also offered as commented-out matchers above it (omitted when the window doesn't report one); uncomment them to target, say, only an app's dialogs (with manage = true, see above). When an init.lua is in charge, the snippet is written as a Lua table instead of TOML.

Forcing management of LSUIElement or non-standard windows

Some applications (e.g., BetterTouchTool, ProtonVPN) are flagged as background apps (LSUIElement) or expose windows with unusual accessibility roles such as AXTable or AXTextField. Paneru normally ignores these processes and windows. Use manage = true to opt in and forcibly manage the matching windows.

toml
1[windows.btt_main]
2bundle_id = "com.hegenberg.BetterTouchTool"
3title = "BetterTouchTool"
4manage = true
5
6[windows.btt_screenshot]
7bundle_id = "com.hegenberg.BetterTouchTool"
8title = "Screenshot.*"
9floating = true

Session Restore

Paneru saves its managed window layout and can restore it the next time it starts. Restore is a startup-only phase: Paneru loads the saved session, applies it after initial window discovery, keeps matching open for a short grace period, then stops consulting the saved state until the next Paneru process start.

The saved session includes:

  • native workspace ids
  • virtual workspace rows and the selected row per native workspace
  • layout structure: singles, stacks, tabs, and fullscreen strips
  • display/screen association
  • window identity for matching across restarts

Matched startup windows use the saved session before static [windows] rules. That means saved layout, virtual workspace, display, and managed/floating state win over configured index, floating, width, and grid rules during restore. Unmatched startup windows, and all windows created after the restore grace period ends, keep normal [windows] behavior.

OptionTypeDefaultDescription
enabledBooleantrueEnables session restore on startup.
startup_grace_msInteger (ms)2000How long Paneru keeps restore matching active after startup. This gives apps a chance to create windows shortly after Paneru starts.
missing_windowsString"ignore"Behavior when a saved window is not present during restore. Currently only "ignore" is supported, which drops the missing window and compacts the restored layout.

Example:

toml
1[restore]
2enabled = true
3startup_grace_ms = 2000
4missing_windows = "ignore"

Restore matches windows first by stable startup identity:

  • window id
  • process id
  • bundle id

If an application has restarted and those ids changed, Paneru can use a conservative fallback match:

  • bundle id
  • non-empty window title
  • window identifier
  • accessibility role
  • accessibility subrole

Fallback matching is only used when it is unambiguous. If multiple current windows could match the same saved window, Paneru skips that saved window rather than moving the wrong one.

If a saved app or window is missing, the default "ignore" policy simply drops it from the restored layout. Empty stacks, tab groups, columns, and virtual rows are removed. If the previously selected virtual row is removed because all of its windows are missing, Paneru selects the nearest remaining restored row for that native workspace. If no restored row remains, the normal startup workspace selection is kept.

For screens, Paneru prefers the current macOS workspace-to-display mapping when the workspace is already present on a display. Otherwise it restores to the saved display id when that screen is still connected, then falls back to the current active display, then the first available display by id. Paneru does not create placeholder displays or off-screen state for disconnected monitors.


7. Experimental Features

Warning

These features rely on undocumented macOS window-server APIs and have known issues. For example, overlay windows (like YouTube Picture-in-Picture) may be partially shaded, and layer ordering can behave unexpectedly. Both features are disabled by default.

Disabling System Integrity Protection (SIP) is not required, but without it Paneru has limited control over window layering, which is the root cause of most visual edge-cases. Enable these only if you are comfortable with occasional glitches.

Inactive Window Overlay Dimming

Another dimming option that draws a translucent overlay on every inactive window to visually emphasize the focused one.

Activation: This mode is enabled by setting both opacity and color under [decorations.inactive.dim]. In this mode, opacity ranges from 0.0 to 1.0.

OptionTypeDefaultDescription
opacityFloat (0.0 to 1.0)0.0Opacity of the dim overlay. 0.0 is transparent, 1.0 is opaque.
colorString (Hex)"#000000"Hex color for the dim overlay (default: black).

Example:

toml
1[decorations.inactive.dim]
2opacity = 0.3
3color = "#000000"

Active Window Border

Draws a colored border around the currently focused window.

OptionTypeDefaultDescription
enabledBooleanfalseEnable the active window border.
colorString (Hex)"#FFFFFF"Hex color for the active window border.
color_nightString (Hex)colorHex color for the active window border when macOS is in Dark Mode.
opacityFloat (0.0–1.0)1.0Opacity of the active window border.
widthFloat (px)2.0Width of the border in pixels.
radiusNumber/String"auto"Corner radius in pixels or "auto" to match system.

Example:

toml
1[decorations.active.border]
2enabled = true
3color = "#89b4fa"
4color_night = "#cba6f7"
5width = 2.0
6radius = 12.0

In Dark Mode, the border uses color_night. If color_night is not set, the border uses color, then the legacy border_color option, then white. In Light Mode, the border ignores color_night. The border changes color immediately when the macOS appearance changes.

Tip: You can override the border_radius for specific applications in the [windows] section. See Window Rules.

8. Lua Scripting

Paneru embeds a Lua runtime that allows full configuration via init.lua, replacing paneru.toml entirely. When a Lua configuration or script exists ($PANERU_LUA, $HOME/.paneru.lua, or $XDG_CONFIG_HOME/paneru/init.lua), it takes over completely and no TOML config is read.

All options, padding, gesture settings, window rules, and keybindings documented in sections 1–7 above are available under identical names via paneru.setup{...}.

In addition to static configuration, Lua scripting allows:

  • Event Hooks (paneru.on): React to window creation (window_spawned), focus changes, or space switches with optional filter specs or regex matchers.
  • Keybinding Callbacks (paneru.bind): Map hotkeys to custom Lua callbacks or commands.
  • State Queries (paneru.query_*): Read real-time window, workspace, and display layout state without round-trip shell executions.
  • Persistent State (paneru.state): Store and mutate data across reloads and daemon restarts.
  • Programmatic Layout Transformations (ws): Pure layout operations (ws:focus, ws:swap, ws:float, ws:shift, ws:view, etc.) for custom workflows like named scratchpads.

For complete documentation, event specifications, API reference, and examples, see the Lua Scripting Guide.