Oatbar - desktop bar for X11 and Wayland
Supported platforms: Native X11 and Wayland. First-class support for sway and hyprland.
The motivation for creation of oatbar was to extend on the idea of Unix-way for toolbars.
Inspired by i3bar which consumes a JSON stream that controls it’s appearance, we take this
idea much further without becoming a DIY widget toolkit.
JSON or plain text streams can be turned into text panels, progress bars or even images without any significant coding effort. It also ships with first-class support for LLMs (Large Language Models) to process your data.
Example
[[bar]]
height=32
blocks_left=["workspace"]
blocks_right=["clock"]
[[command]]
name="clock"
command="date '+%a %b %e %H:%M:%S'"
interval=1
[[command]]
name="desktop"
command="oatbar-desktop"
[[block]]
name = 'workspace'
type = 'enum'
active = '${desktop:workspace.active}'
variants = '${desktop:workspace.variants}'
on_mouse_left = "oatbar-desktop $BLOCK_INDEX"
[[block]]
name = 'clock'
type = 'text'
value = '${clock:value}'
Here clock command sends plain text, but desktop streams
structured data in JSON. Each is connected to text and enum selector
widgets respectively. oatbar-desktop ships with oatbar, but it is an external tool
to a bar, as can be replaced your own script.
Feel free to run oatbar-desktop and investigate it’s output. oatbar consumes
multiple text formats and this data can be
displayed with minimal configuration on widgets called blocks.
Ideas
I truly aspire to build something unique with oatbar, something what other status bars lack. Do you have a cool or unconventional feature you’d like to see in oatbar?
Join the disussion and brainstorm!
Next Steps
-
Partial; e.g. no
polybarformatting, but many scripts work. ↩
Installation
Installation
Please install cargo via the package manager or rustup.rs.
Supported Platforms
oatbar supports both X11 and Wayland compositors, including:
- Wayland: sway, hyprland
- X11: i3, bspwm, and other X11 window managers
Note
Wayland support uses a pure-Rust implementation and does not require any additional system libraries. The
libxcb/x11-xcbdependency is only required for X11 support.
Dependencies
ArchLinux
# For both X11 and Wayland (default)
pacman -Sy pango cairo libxcb pkgconf
# For Wayland only (build with --no-default-features -F wayland)
pacman -Sy pango cairo pkgconf
Ubuntu/Debian
# For both X11 and Wayland (default)
apt-get install -y build-essential pkg-config \
libssl-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libx11-xcb-dev
# For Wayland only (build with --no-default-features -F wayland)
apt-get install -y build-essential pkg-config libssl-dev libxkbcommon-dev \
libcairo2-dev libpango1.0-dev
Other
Install the development packages for the following libraries:
- SSL
- libxkbcommon
- Cairo
- Pango
- x11-xcb (only required for X11 support)
Install
cargo install oatbar
During the first launch the bar will create a default config at
~/.config/oatbar/config.toml that should work on most machines. Run:
oatbar
And you should see:

NetBSD
On NetBSD, a package is available from the official repositories. To install it, simply run:
pkgin install oatbar
Next
Overview
The configuration for the oatbar is located at ~/.config/oatbar/config.toml. If you do
not have this file, it would be generated with reasonable defaults.

Proceed to concepts to learn basic building blocks
of oatbar configuration.
Proceed to cookbook if you are familiar with concepts and you are looking for a recipe to solve a particular problem, proceed to the particular problem.
You can also use an AI assistant with the MCP server to configure the bar interactively — it can read and edit your config, inspect live variables, and restart oatbar without any manual steps.
The configuration is unstable.
Until 1.0 release, the configuration change is unstable and can change between minor releases. Community feedback is needed to make sure that when configuration stabilizes, it is the best possible.
Concepts
oatbar configuration is built around four main concepts that work together to display information.
- Bar (
[[bar]]): The top-level window and layout container. - Block (
[[block]]): Visual widgets (text, images, graphs) displayed on the bar. - Command (
[[command]]): External programs that fetch data. - Variable (
[[var]]): Named data holders populated by commands and used by blocks.
Data Flow
- Command runs (e.g.,
date) and outputs text. oatbarparses the output and updates a Variable (e.g.,${clock:value}).- Block references the variable in its
valueproperty (e.g.,value="Time: ${clock:value}"). - Bar re-renders the block with the new text.
Variable Interpolation
String properties in blocks support variable interpolation using the ${...} syntax.
Syntax
- Basic:
${command_name:variable_name} - With Property:
${command_name:variable_name.property}(for complex data like i3bar JSON). - Filters:
${variable|filter:arg}(e.g.,${cpu|align:>3}).
Example
[[command]]
name="clock"
command="date +%H:%M"
interval=60
[[block]]
name="my_clock"
type="text"
value="Time: ${clock:value}"
Debugging
Use oatctl to inspect the current state of variables and blocks.
# List all active variables and their values
oatctl var ls
Bar
Bar is a single panel that is positioned at the top or the bottom of a screen.
Here are all the properties it can have:
| Property | Type | Default | Description |
|---|---|---|---|
height | int | 32 | Height of the bar in pixels. |
position | string | bottom | Position on screen: top, bottom or center. |
monitor | string | primary | Monitor name (from xrandr) to display the bar on. |
blocks_left | list | [] | List of block names to align to the left. |
blocks_center | list | [] | List of block names to align to the center. |
blocks_right | list | [] | List of block names to align to the right. |
margin | int/object | 0 | Margin around the bar. Can be a single number or {top=0, bottom=0, left=0, right=0}. |
background | color | transparent | Background color of the entire bar. |
popup | bool | false | If true, the bar is hidden until triggered by a block (see Block) or mouse at edge. |
popup_at_edge | bool | false | If true, showing the mouse at the screen edge triggers the popup. |
show_if_matches | list | [] | List of [expression, regex] pairs. Bar is visible only if all regexes match. |
Example
[[bar]]
height=32
position="top"
monitor="eDP-1"
background="#1e1e2e"
margin={left=10, right=10, top=5, bottom=0}
blocks_left=["workspace"]
blocks_center=["window_title"]
blocks_right=["clock", "sys_info"]
popup=false
popup_at_edge=true
show_if_matches=[["${desktop:workspace.active}", "1"]]
Command
Command is an external program that provides data to oatbar.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | Required | Unique identifier for the command. Used in variable namespaces (e.g., ${name:value}). |
command | string | Required | Shell command to execute (run via sh -c). |
interval | int | 10 | Execution interval in seconds. |
once | bool | false | If true, run only once at startup. |
format | string | auto | Output format: plain, i3bar or auto. |
line_names | list | [] | Names for variables when command outputs multiple lines (e.g., ["first", "second"] maps to ${name:first}, ${name:second}). |
Example
# Runs periodically
[[command]]
name="disk_free"
command="df -h / | tail -1 | awk '{print $5}'"
interval=60
format="plain"
# Runs once
[[command]]
name="uname"
command="uname -a"
once=true
# Streams continuously (e.g., oatbar-desktop)
[[command]]
name="desktop"
command="oatbar-desktop"
format="i3bar"
# Multi-line output
[[command]]
name="user_info"
command="echo $USER; echo $HOST"
line_names=["user", "host"]
oatbar will run each command as sh -c "command" to support basic shell
substitutions.
Formats
Formats are usually auto-detected and there is no need to set format explicitly.
plain
Plain text format is just text printed to stdout.
name="hello"
command="echo Hello world"
This will set ${hello:value} variable to be used
by blocks. If the command outputs multiple lines, each print
will set this variable to a new value. If the command runs indefinitely, the
pauses between prints can be used to only update the variable when necessary.
When the command is complete, it will be restarted after interval
seconds (the default is 10).
If line_names are set, then the output is expected in groups of
multiple lines, each will set it’s own variable, like ${hello:first_name} and
${hello:last_name} in the following example:
name="hello"
line_names=["first_name", "last_name"]
Many polybar scripts can be
used via the plain format, as soon as they don’t use polybar specific
formatting.
i3blocks raw format plugins
can be consumed too by means of the line_names set to standard names for
i3blocks.
i3bar
i3bar format is the richest supported format.
It supports multiple streams of data across multiple “instances” of these streams.
In i3wm this format fully controls the display of i3bar, where
for oatbar it is a yet another data source that needs to be explicitly
connected to properties of the blocks. For example instead of coloring
the block, you can choose to color the entire bar. Or you can use color red coming
from an i3bar plugin as a signal to show a hidden block.
Plugins that oatbar ships with use this format.
[[command]]
name="desktop"
command="oatbar-desktop"
The command output-desktop outputs:
{"version":1}
[
[{"full_text":"workspace: 1","name":"workspace","active":0,"value":"1","variants":"1,2,3"},
{"full_text":"window: Alacritty","name":"window_title","value":"Alacritty"}],
...
This command is named desktop in the config.
Each entry is groups variables under a different name that
represents a purpose of the data stream, in this case: workspace
and window_title. Multiple entries with the same name, but different
instance field to represent further breakdown (e.g. names of
network interfaces from a network plugin).
The output from above will set
the following variables, run oatbar and see them in real-time
$ oatctl var ls
desktop:workspace.active=0
desktop:workspace.value=1
desktop:workspace.variants=1,2,3
desktop:workspace.full_text=workspace: 1
desktop:window_title.value=Alacritty
desktop:window_title.full_text=window: Alacritty
If instance is present in the entry, then the name of the variable is
command_name:name.instance.variable.
Block
Blocks are the widgets displaying pieces of information on the bar.
oatbar provides a lot of hidden power via these widgets, as they can provide
more than they initially seem. Consider that the most of the string properties
of blocks support variable substitution, directly controlled by your scripts.
The reference below explains properties of these blocks and the Cookbook shows how to use them in a very clever way.
- Example
- Configuration Inheritance
- Text block
- Number block
- Enum block
- Image block
- Popups and Visibility
All blocks share the following properties.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | Required | Unique identifier for the block. |
type | string | text | Block type: text, number, enum, image. |
value | string | "" | The content to display. Supports variables (e.g., ${cmd:var}). |
show_if_matches | list | [] | List of [expression, regex] pairs. Block is visible only if all regexes match. |
replace | list | [] | List of [regex, replacement] pairs applied to value. |
replace_first_match | bool | true | If false, applies all replacements; if true, stops after first match. |
output_format | string | None | Final formatting string (e.g., CPU: ${value}). |
pango_markup | bool | true | Enable Pango Markup parsing. |
font | string | inherit | Font definition (e.g., Monospace 12). |
background | color | transparent | Background color (Hex #RRGGBB or #RRGGBBAA). |
foreground | color | inherit | Text color. |
line_width | float | 1.1 | Width of border lines. |
overline_color | color | None | Color of the top border line. |
underline_color | color | None | Color of the bottom border line. |
edgeline_color | color | None | Color of the side border lines (left/right). |
hover_background | color | None | Background color on hover. |
hover_foreground | color | None | Text color on hover. |
hover_line_width | float | None | Width of border lines on hover. |
hover_overline_color | color | None | Color of the top border line on hover. |
hover_underline_color | color | None | Color of the bottom border line on hover. |
hover_edgeline_color | color | None | Color of the side border lines (left/right) on hover. |
margin | float | 0.0 | Space outside the block. |
padding | float | 8.0 | Space inside the block (around text). |
separator_type | string | None | Separator style: left, right, gap. |
separator_radius | float | 0.0 | Radius for rounded separators. |
popup | string | None | Popup behavior: block, partial_bar, bar. |
popup_value | string | None | Variable that triggers the popup on change. |
on_mouse_left | string | None | Command to run on left click. |
on_mouse_middle | string | None | Command to run on middle click. |
on_mouse_right | string | None | Command to run on right click. |
on_scroll_up | string | None | Command to run on scroll up. |
on_scroll_down | string | None | Command to run on scroll down. |
Example
[[block]]
name="clock"
type="text"
value="<span weight='bold'>${clock:value}</span>"
background="#1e1e2e"
foreground="#cdd6f4"
padding=8
margin=4
line_width=2.0
underline_color="#fab387"
on_mouse_left="calendar"
show_if_matches=[["${clock:value}", ".+"]]
Configuration Inheritance
To avoid repetition, you can use default_block to define common properties. oatbar uses a cascading configuration system:
- Global Default: A
[[default_block]]without anameapplies to all blocks. - Named Default: A
[[default_block]]with anameinherits from the Global Default. - Block: A
[[block]]inherits from a Named Default (ifinheritis set) or directly from the Global Default.
Global Default Example
This sets a default background for every block in the bar.
[[default_block]]
background="#202020"
padding=5
Named Default Example
You can define specific styles (e.g., “active” vs “inactive”) and apply them to specific blocks.
[[default_block]]
name="active_style"
background="#fab387"
foreground="#1e1e2e"
[[block]]
name="my_block"
inherit="active_style"
value="I am active!"
In this case, my_block will have:
padding=5(from Global Default)background="#fab387"(from Named Default “active_style”, overriding Global)foreground="#1e1e2e"(from Named Default “active_style”)value="I am active!"(from Block)
Text block
[[block]]
type="text"
Text blocks include all common properties, which should be enough to show basic text or icons using Pango markup, icon fonts such as Font Awesome, Nerd Fonts, IcoMoon or emojis.
In addition, text blocks are used as separators to create partial bars. They are smaller bars within a bar that groups multiple blocks together.

[[bar]]
blocks_right=["L", "music", "R", "E", "L", "layout", "S", "clock", "R"]
[[block]]
name="music"
...
show_if_matches = [['${player:now_playing.full_text}', '.+']]
popup = "partial_bar"
[[block]]
name="S"
type = "text"
separator_type = "gap"
value = "|"
[[block]]
name="E"
type = "text"
separator_type = "gap"
value = " "
background = "#00000000"
[[block]]
name="L"
type = "text"
separator_type = "left"
separator_radius = 8.0
[[block]]
name="R"
type = "text"
separator_type = "right"
separator_radius = 8.0
separator_type gives a hint on where partial bars are located.
This helps when popup="partial_bar". It also helps to collapse
unnecessary separators when normal blocks around them are hidden.
Number block
[[block]]
type="number"
Number can be displayed as text on the text block. But the real value comes when the bar understands that the data is a number.
In addition to common properties, the number blocks support unit conversions and alternative forms of display, such as progress bars.
# Min/max values are used in progress bars.
# They are set as string because they support
# variable substituion and can be specified in units.
min_value="0"
max_value="1000"
# A number type that input represents.
# - number - a number from min to max
# - percent - a number from 0 to 100, '%' is ommitted from the input when parsing.
# - bytes - a number that supports byte unit suffixes, e.g. "GB", "kb",
# - See https://docs.rs/bytesize/latest/bytesize/
number_type="percent"
# A sorted list of ramp formats. If set, prior to wrapping with `output_format`,
# wrap to the format from the entry larger than `value`.
ramp = [
["80%", "<span foreground='yellow'>${value}</span>"],
["90%", "<span foreground='red'>${value}</span>"],
]
number_display can be used to select the widget that is going to display your
number on the block.
Number as text
You can display the number as text as you would have with a text block, but there
is a benefit of additional functionality, such as unit conversions and ramp
functionality.
[[block]]
type="number"
name="cpu"
number_type="percent"
...
number_display="text"
Progress bar
[[block]]
type="number"
name="cpu"
number_type="percent"
...
number_display="progress_bar"
# How many characters to use for the progress bar.
progress_bar_size=10
# Progress bar characters. In this example would render: "━━━━雷 "
empty=" "
fill="━"
indicator="雷"
# Each of the above can be a ramp
# fill = [
# ["", "━"],
# ["60%", "<span foreground='yellow'>━</span>"],
# ["90%", "<span foreground='red'>━</span>"],
# ]
Enum block
[[block]]
type="enum"
Enum is different from text block as it renders multiple child text blocks called
variants, only one of which is active. Example: keyboard layout switch.
Almost every common property related to block display has an active_ counterpart to configure the active variant.
| Property | Description |
|---|---|
active_font | Font for the active variant. |
active_background | Background color for the active variant. |
active_foreground | Text color for the active variant. |
active_line_width | Border line width for the active variant. |
active_overline_color | Top border color for the active variant. |
active_underline_color | Bottom border color for the active variant. |
active_edgeline_color | Side border color for the active variant. |
active_margin | Margin for the active variant. |
active_padding | Padding for the active variant. |
active_output_format | Format string for the active variant. |
active_pango_markup | Enable/disable Pango markup for the active variant. |
active_hover_... | All hover_ properties are also available with active_ prefix (e.g. active_hover_background). |
# A separated list of variants, e.g. "ua,se,us".
variants = '${keyboard:layout.variants}'
# An index of the item that is to be active starting from 0.
active = '${keyboard:layout.active}'
# A separator for the variants list. Default: ",".
enum_separator="|"
Text processing via replace is done per item of the variants separately,
not together. If an variant becomes empty as a result of processing, it will
not be displayed, but it won’t impact the meaning of active index.
BLOCK_INDEX environment variable set for on_mouse_left command is set to
the index of the variant that was clicked on.
Image block
[[block]]
type="image"
In image blocks, the value property is interpreted as and image file name to be
rendered. Supported formats: BMP, ICO, JPEG, PNG, SVG, WEBP.
# If set, can shrink the image smaller than automatically determined size.
max_image_height=20
# If this value is set and changed, then image caching gets disabled and
# image is reloaded from the filesystem even if the filename stayed the same.
# It can be used by a command to generate dynamic images under the same filename.
updater_value="${image_generator:timestamp}"
images or download them from the Internet on flight in the command that generates a filename.
Popups and Visibility
oatbar allows blocks to be hidden by default and “pop up” only when important information needs to be shown.
Popup Settings
popup: Defines what pops up.bar: The entire bar is shown (if[[bar]] popup=true).partial_bar: Only the section of the bar containing this block is shown (useful for “toast” notifications).block: Only this specific block is shown.
popup_value: Defines when it pops up.- If set, the popup triggers only when this variable changes.
- If not set, the popup triggers on every update to the block.
Why popup_value?
Without popup_value, a block that updates every second (like a clock) would keep the popup open indefinitely. By binding popup_value to a specific event variable (e.g., ${volume:value}), you ensure the popup only appears when that specific value changes, even if other parts of the block update.
Example: Media Player
Consider a media player block that shows the song title and the current playback time.
[[block]]
name="media"
type="text"
# Updates every second due to ${player:position}
value="${player:title} - ${player:position}"
popup="partial_bar"
# Only popup when the song title changes, ignoring position updates
popup_value="${player:title}"
Without popup_value, the bar would pop up every second as the playback time updates. With popup_value, it only appears when the song changes.
Variable
Variables are at length described in the Command section where they are produced and in the Block section where they are consumed.
If oatbar is running and you have added a few commands, you can see all available variables
using oatctl command:
$ oatctl var ls
clock:value=Mon Jul 8 00:40:19
desktop:window_title.full_text=window: Alacritty
desktop:window_title.value=Alacritty
desktop:workspace.active=0
desktop:workspace.full_text=workspace: 1
desktop:workspace.value=1
desktop:workspace.variants=1,2
...
More info on how to get and set variables programmatically:
$ oatctl var help
Standalone variables
You can declare your additional variables that do not come from commands. This is useful to
pre-process data with replace and replace_first_match to be used in multiple blocks.
Supported properties:
name: Unique name of the variable.value: The value of the variable, supports placeholders.replace: List of regex replacements.replace_first_match: Whether to stop after the first replacement (default:false).
[[var]]
name="clock_color_attr"
value = '${clock:color}'
replace = [["(.+)","foreground='$1'"]]
[[block]]
value = "<span ${clock_color_attr}>${clock:value}</span>"
Standalone variables can use each other only in the order they are declared in the file, otherwise the result is undefined.
Switch-Case Logic
replace_first_match=true allows implementing a switch-case logic where you map a set of known values to other values, optionally providing a default at the end. Since processing stops after the first match, order matters.
[[var]]
name="status_icon"
value="${player:status}"
replace_first_match=true
replace=[
["^playing$", "▶"],
["^paused$", "⏸"],
["^stopped$", "⏹"],
[".*", ""] # Default/Catch-all
]
Filters
Filters are additional functions you can apply to values inside of the ${...} expressions. Example:
value = '${desktop:window_title.value|def:(no selected window)|max:100}'
Supported filters:
defsets the default value if the input variable is emptymaxlimits the length of the input. If it is larger, it is shortened with ellipsis (...)alignaligns the text to occupy a fixed width.- Syntax:
align:[filler][alignment][width] alignmentmust be one of:<(left),^(center), or>(right).filleris an optional character (defaults to space).widthis the target width.- Examples:
align:<10-> Left align, space padding (e.g.hello)align:->10-> Right align,-padding (e.g.-----hello)align:_^10-> Center align,_padding (e.g.__hello___)
- Syntax:
Cookbook
oatbar tries to keep pre-packaged plugins or modules to a minimum.
Instead it offers easy ways of achieving the same by integrating
external data sources, tools or modules from ecosystems of other bars.
Next
- Learn where to get data to show it on the bar.
- If you have the data, proceed learning how to display it.
- If you know the basic, see some advanced applications of
oatbarconcepts. - Let AI assistants read and write your bar live with MCP.
Data
This chapter contains examples of common sources and methods of ingesting data for your blocks.
Common Blocks
App Launcher
[[block]]
name='browser'
type = 'text'
value = "<span font='Font Awesome 6 Free 22'></span> "
on_mouse_left = 'chrome'
Clock
[[command]]
name="clock"
command="date '+%a %b %e %H:%M:%S'"
interval=1
[[block]]
name = 'clock'
type = 'text'
value = '${clock:value}'
If you do not need to show seconds, you can make interval smaller.
Keyboard
oatbar ships with keyboard status utility that streams keyboard layouts
and indicator values in i3bar format.
If you run it, you will see
❯ oatbar-keyboard
{"version":1}
[
[{"full_text":"layout: us","name":"layout","active":0,"value":"us","variants":"us,ua"},
{"full_text":"caps_lock:off","name":"indicator","instance":"caps_lock","value":"off"},
...],
Enable it with
[[command]]
name="keyboard"
command="oatbar-keyboard"
Layout
oatbar-keyboard is designed to work with setxkbmap.
For example you set up your layouts on each WM start like this:
setxkbmap -layout us,ua -option grp:alt_space_toggle
Enable oatbar-keyboard and use an enum block:
[[block]]
name = 'layout'
type = 'enum'
active = '${keyboard:layout.active}'
variants = '${keyboard:layout.variants}'
on_mouse_left = "oatbar-keyboard layout set $BLOCK_INDEX"
Indicators
Show indicators, such as caps_lock, scroll_lock and num_lock
as follows:
[[block]]
name = 'caps_lock'
type = 'text'
value = '${keyboard:indicator.caps_lock.full_text}'
Active workspaces and windows
oatbar-desktop talks to your WM via EWMH protocol to obtain
the information about active workspaces and windows.
❯ oatbar-desktop
{"version":1}
[
[{"full_text":"workspace: 1","name":"workspace","active":0,"value":"1","variants":"1,2,3"},
{"full_text":"window: Alacritty","name":"window_title","value":"Alacritty"}],
[[command]]
name="desktop"
command="oatbar-desktop"
[[block]]
name = 'workspace'
type = 'enum'
active = '${desktop:workspace.active}'
variants = '${desktop:workspace.variants}'
# Optional replacement with icons
replace = [
["1",""],
["2",""],
["3",""]
]
font="Font Awesome 6 Free 13"
on_mouse_left = "oatbar-desktop $BLOCK_INDEX"
[[block]]
name='window'
type = 'text'
value = '${desktop:window_title.value|max:100}'
pango_markup = false # Window title can happen to have HTML.
System stats
oatbar ships with a oatbar-stats utility that streams system stats in the i3bar
format:
- CPU
- Memory
- Network
- Interface names
- Running status
- Address
- Download and upload rates
- Battery
- Charge percentage
- State (Charging, Discharging, Full, Empty, Unknown)
There is a lot of data you can display on your
blocks. Enableoatbar-statslike this:
[[command]]
name="stats"
command="oatbar-stats"
Restart oatbar and examine the new variables.
oatctl var ls | grep '^stats:'
The example output below.
stats:cpu.full_text=cpu: 2%
stats:cpu.percent=2
stats:memory.free=8744980480
stats:memory.full_text=mem: 73% 32.9 GB
stats:memory.percent=73
stats:memory.total=32915705856
stats:memory.used=24170725376
stats:net.igc0.full_text=igc0: 192.168.0.160
stats:net.igc0.ipv4_0_addr=192.168.0.160
stats:net.igc0.ipv4_0_broadcast=192.168.0.255
stats:net.igc0.ipv4_0_run=true
stats:net.igc0.ipv4_0_up=true
stats:net.igc0.mac_0_addr=48:21:0b:35:ca:08
stats:net.igc0.mac_0_run=true
stats:net.igc0.mac_0_up=true
stats:net.igc0.rx_per_sec=1704
stats:net.igc0.tx_per_sec=1110
...
Entries with full_text are a good start to display directly,
if you do not need more fine grained customizations.
[[block]]
name="ethernet"
type="text"
value="${stats:net.igc0.full_text}"
Battery
To display battery status, you can use the instance index (usually 0 for the primary battery) along with the variables exported by oatbar-stats.
[[block]]
name="battery"
type="text"
value="<b>batt:</b> ${stats:battery.0.charge}%"
show_if_matches=[["${stats:battery.0.charge}", ".+"]]
Disk space
Example for /home directory partition:
[[command]]
name="home_free"
command="df -h /home | tail -1 | awk '{print $5}'"
interval=60
[[block]]
name='home_free'
type = 'text'
value = '<b>/home</b> ${home_free:value}'
LLM
oatbar ships with oatbar-llm, a powerful utility that uses LLMs to process data and return it in i3bar format.
Due to the power and flexibility of this feature, it has its own dedicated section in the cookbook.
See LLM Cookbook for examples and LLM Configuration for full reference.
Third-party sources
Existing bar ecosystems already can provide large mount of useful information.
oatbar by-design focuses on making it possible to adapt third-party data sources.
i3status
i3status is a great cross-platform source of information about the system. It supports:
- CPU
- Memory
- Network
- Battery
- Volume
i3status is designed to be used by i3bar, but oatbar supports this format natively.
Enable it in ~/.i3status.conf or in ~/.config/i3status/config:
general {
output_format = "i3bar"
}
Add you plugin as described in the i3status documentation.
Prefer simple output format, as you can format it on the oatbar side. Example:
order += cpu_usage
cpu_usage {
format = "%usage"
}
If you run i3status you will now see
❯ i3status
{"version":1}
[
[{"name":"cpu_usage","markup":"none","full_text":"00%"}]
,[{"name":"cpu_usage","markup":"none","full_text":"02%"}]
In oatbar config:
[[block]]
name='cpu'
type = 'number'
value = "${i3status:cpu_usage.full_text}"
number_type = "percent"
output_format="<b>CPU:</b>${value}"
number_display="text"
If you prefer a progress bar:
number_display="progress_bar"
conky
As i3status, conky can also be a great source of system data.
conky can print it’s variables as plain text
and oatbar can consume it as multi-line plain text. Example ~/.oatconkyrc:
conky.config = {
out_to_console = true,
out_to_x = false,
update_interval = 1.0,
}
conky.text = [[
$memperc%
$cpu%
]]
If you run conky -c ~/.oatconkyrc you will see repeating groups of numbers:
2%
10%
5%
10%
In oatbar config:
[[command]]
name="conky"
command="conky -c ~/.oatconkyrc"
line_names=["mem","cpu"]
[[block]]
name='cpu'
type = 'number'
value = "${conky:cpu}"
number_type = "percent"
[block.number_display]
type="text"
output_format="<b>CPU:</b>${value}"
[[block]]
name='mem'
type = 'number'
value = "${conky:mem}"
number_type = "percent"
[block.number_display]
type="text"
output_format="<b>MEM:</b>${value}"
i3blocks
i3blocks in a drop-in replacement for i3status to be used in
i3bar. If you have existingi i3blocks configs, feel free to plug it
directly into oatbar:
[[command]]
name="i3blocks"
command="i3blocks"
You can check which oatbar variables it makes available by running
i3blocks in your console.
The indirection between the script, i3blocks and oatbar is not required.
You can connect any plugin from the i3block-contrib
excellent collection directly into oatbar.
For example:
$ git clone https://github.com/vivien/i3blocks-contrib
$ cd ./i3blocks-contrib/cpu_usage2
$ make
$ ./cpu_usage2 -l "cpu: "
cpu: <span> 39.79%</span>
cpu: <span> 47.06%</span>
As you can see, it inputs only one line of data each interval,
so setting line_names is not necessary, however always check for it.
[[command]]
name="cpu_usage2"
command="/path/to/cpu_usage2 -l 'cpu: '"
[[block]]
name="cpu_usage2"
type="text"
value="${cpu_usage2:value}"
HTTP APIs
HTTP JSON APIs that do not require complicated login are extremely
easy to integrate using curl and jq.
Explore your JSON first
$ curl 'https://api.ipify.org?format=json'
{"ip":"1.2.3.4"}
jq -r let’s you extract a value from a JSON object. Add the command
to oatbar config, but make sure to set a sufficient interval not to get
banned.
[[command]]
name="ip"
command="curl 'https://api.ipify.org?format=json | jq -r .ip"
interval=1800
[[block]]
name="ip"
type="text"
value="my ip: ${ip:value}"
File
You can use file watching utils to output file contents on any file change.
For example for Linux you can use fswatch.
[[command]]
command="cat ./file; fswatch --event Updated ./file | xargs -I {} cat {}"
Socket
Use socat to read from sockets. TCP socket:
[[command]]
command="socat TCP:localhost:7777 -"
SSL socket:
[[command]]
command="socat OPENSSL:localhost:7777 -"
For Unix socket:
[[command]]
command="socat UNIX-CONNECT:/path/to/socket -"
LLM
oatbar ships with oatbar-llm, a powerful utility that uses LLMs to process data and return it in i3bar format.
It can summarize logs, explain errors, fetch news, or just generate cool content.
How it works
oatbar-llm runs the configured [[command]]s before invoking the LLM. The output of these commands is then fed into the LLM prompt as context.
This means the LLM does not execute tools or commands itself. You have full control over what data is sent to the model for processing.
See LLM Configuration for full reference.
Examples
These examples are for illustrative purposes. Due to the non-deterministic nature of LLMs, you may need to tune the prompts (questions) to get the exact output format or content you desire for your specific model and use case.
System Health Check
Use conky to generate a detailed, one-shot system report and have the LLM analyze it for potential bottlenecks or issues.
1. Create ~/.config/oatbar/conky.conf
conky.config = {
out_to_console = true,
out_to_x = false,
background = false,
update_interval = 1,
total_run_times = 1,
use_spacer = 'none',
}
conky.text = [[
System Health Report
--------------------
Uptime: ${uptime}
Load Avg: ${loadavg}
Processes: ${running_processes} running of ${processes} total
CPU: ${cpu}%
RAM: ${memperc}% (${mem}/${memmax})
Swap: ${swapperc}% (${swap}/${swapmax})
Disk Usage:
/ : ${fs_used /}/${fs_size /} (${fs_used_perc /}%)
/home : ${fs_used /home}/${fs_size /home} (${fs_used_perc /home}%)
Network (eth0):
Up: ${upspeed eth0} (Total: ${totalup eth0})
Down: ${downspeed eth0} (Total: ${totaldown eth0})
Top CPU:
1. ${top name 1}: ${top cpu 1}%
2. ${top name 2}: ${top cpu 2}%
3. ${top name 3}: ${top cpu 3}%
4. ${top name 4}: ${top cpu 4}%
5. ${top name 5}: ${top cpu 5}%
Top Mem:
1. ${top_mem name 1}: ${top_mem mem 1}%
2. ${top_mem name 2}: ${top_mem mem 2}%
3. ${top_mem name 3}: ${top_mem mem 3}%
4. ${top_mem name 4}: ${top_mem mem 4}%
5. ${top_mem name 5}: ${top_mem mem 5}%
]]
2. Configure ~/.config/oatbar/llm.toml
[[command]]
name = "conky_report"
command = "conky -c ~/.config/oatbar/conky.conf"
interval = 1800
[[variable]]
name = "health_check"
type = "string"
question = "Analyze this system report and summarize the health status. Highlight any resource hogs. Up to 5 words"
[[variable]]
name = "health_report"
type = "string"
question = "Generate a detailed bulleted report of the system health based on the data."
write_to = "/tmp/health_report.md"
3. Configure oatbar
[[command]]
name="conky_ai"
command="oatbar-llm"
interval=1800
[[block]]
name="health"
type="text"
value="${conky_ai:health_check.value}"
on_mouse_left="xdg-open /tmp/health_report.md"
Git Repository Status
Summarize uncommitted changes in your current project to keep you focused.
1. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
[[command]]
name="git_status"
command="cd ~/Projects/my-project && git status -s && git diff --stat"
[[variable]]
name="git_summary"
type="string"
question="Summarize the uncommitted changes in 3-5 words. If clean, say 'Clean'."
2. Configure oatbar
[[command]]
name="git_ai"
command="oatbar-llm"
interval=600
[[block]]
name="git_status"
type="text"
value="Git: ${git_ai:git_summary.value}"
Security Monitor
Monitor open ports and recent authentication failures for a quick security overview.
1. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
[[command]]
name="ports"
command="ss -tuln"
[[command]]
name="auth_logs"
command="journalctl -u sshd -n 20 --no-pager"
[[variable]]
name="security_alert"
type="string"
question="Analyze open ports and sshd logs. Is there any suspicious activity?"
allowed_answers=["Safe", "Suspicious"]
2. Configure oatbar
[[command]]
name="security_ai"
command="oatbar-llm"
interval=3600
[[block]]
name="security"
type="text"
value="Sec: ${security_ai:security_alert.value}"
Weather & Outfit Advisor
Get clothing suggestions based on the current weather.
1. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
[[command]]
name="weather"
command="curl -s 'https://api.open-meteo.com/v1/forecast?latitude=40.76&longitude=-73.99¤t=temperature_2m,weather_code'"
[[variable]]
name="outfit"
type="string"
question="Based on this weather JSON (temperature in Celsius), suggest a simple outfit (e.g., 'T-shirt & Shorts', 'Coat & Scarf'). Keep it under 5 words."
2. Configure oatbar
[[command]]
name="outfit_ai"
command="oatbar-llm"
interval=7200
[[block]]
name="outfit"
type="text"
value="Wear: ${outfit_ai:outfit.value}"
Standup Meeting Helper
Summarize your work from the last 24 hours to prepare for your daily standup meeting.
1. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
[[command]]
name="my_commits"
command="cd ~/Projects/my-project && git log --author='My Name' --since='24 hours ago' --oneline"
[[variable]]
name="standup_notes"
type="string"
question="Create a bulleted list of my completed tasks for a standup meeting."
write_to="/tmp/standup_notes.md"
2. Configure oatbar
[[command]]
name="standup_ai"
command="oatbar-llm"
interval=3600
[[block]]
name="standup"
type="text"
value="Standup Prep"
on_mouse_left="xdg-open /tmp/standup_notes.md"
Knowledge Base Examples
These examples demonstrate how to use the knowledge_base feature to provide static context to the LLM, allowing it to act as a specialized assistant.
Code Review Helper (Style Guide Enforcer)
Check your code against your team’s style guide.
1. Create ~/.config/oatbar/style_guide.md
# Team Style Guide
- Prefer `unwrap_or_else` over `unwrap`.
- Use `tracing` for logging, not `println!`.
- All public functions must have documentation.
- Variable names should be descriptive (no `x`, `y`, `temp`).
2. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
knowledge_base="/home/user/.config/oatbar/style_guide.md"
[[command]]
name="git_diff"
command="cd ~/Projects/my-project && git diff --cached"
[[variable]]
name="style_review"
type="string"
question="Review the git diff against the style guide. Point out any violations concisely."
Focus & Schedule Assistant
Get reminders based on your personal schedule and priorities.
1. Create ~/.config/oatbar/schedule.md
# My Schedule & Priorities
- **Mornings (8am-12pm):** Deep work (Coding, Writing). No meetings.
- **Lunch:** 12pm-1pm.
- **Afternoons (1pm-5pm):** Meetings, Emails, Code Reviews.
- **Evenings:** Learning Rust, Gym.
**Current Focus:** Shipping the LLM module for Oatbar.
2. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
knowledge_base="/home/user/.config/oatbar/schedule.md"
[[command]]
name="current_time"
command="date +%H:%M"
[[variable]]
name="focus_tip"
type="string"
question="Based on the current time and my schedule, what should I be focusing on right now? Keep it short."
Error Log Analyzer
Suggest next steps when system errors occur, based on a runbook.
1. Create ~/.config/oatbar/runbook.md
# Incident Runbook
- **High CPU:** Check `top`, identify process. If `cargo`, ignore. If unknown, kill.
- **Disk Full:** Clean `/tmp` and `~/.cache`. Check `docker system df`.
- **SSH Failures:** Check `auth.log` for repeated IPs. Ban with `fail2ban`.
- **OOM:** Check kernel logs. Restart service.
2. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
knowledge_base="/home/user/.config/oatbar/runbook.md"
[[command]]
name="sys_errors"
command="journalctl -p err -n 10 --no-pager"
[[variable]]
name="incident_action"
type="string"
question="Analyze the recent system errors. Based on the runbook, what is the recommended action?"
Hacker News RSS Summary
Fetch the latest news and get a concise summary on your bar.
1. Configure ~/.config/oatbar/llm.toml
[llm]
provider="google"
name="gemini-2.5-flash"
knowledge_base="/home/user/.config/oatbar/hn_preferences.md"
Create ~/.config/oatbar/hn_preferences.md:
I am interested in:
- Rust, Go, C++
- System programming, Linux, Kernel
- AI, LLMs, Machine Learning
- Security, Cryptography
I am NOT interested in:
- Web frameworks (React, Vue, etc.)
[[command]]
name="hn_rss"
command="curl -s https://news.ycombinator.com/rss"
[[variable]]
name="top_stories"
type="string"
question="Extract the top 3 most interesting headlines from this RSS feed and combine them into a single, short sentence separated by pipes."
2. Configure oatbar
[[command]]
name="news"
command="oatbar-llm"
interval=10800 # Every 3 hours
[[block]]
name="news_feed"
type="text"
value="HN: ${news:top_stories.value}"
Tips & Best Practices
Debugging Prompts
Before connecting oatbar-llm to oatbar, run it manually in your terminal to verify the output. Use oatbar-llm --mode=debug to see the raw response from the LLM, which is helpful for troubleshooting prompts.
Prompt Engineering
LLMs are sensitive to how you ask questions.
- Be Specific: Instead of “What’s the status?”, ask “Summarize the system status in 3 words based on these logs.”
- Define Output: Explicitly state the desired format (e.g., “Format: …”).
- Iterate: Use the debug mode to tweak your prompt until you get consistent results.
Quota Management
LLM API calls can be expensive or rate-limited.
- Watch your usage: Monitor your provider’s dashboard.
- Increase Intervals: For non-critical data (like weather or news), set the
intervalinoatbarto a higher value (e.g.,3600for 1 hour,10800for 3 hours).
Consolidating Queries
To save on API calls and context window usage, combine related tasks into a single oatbar-llm configuration.
Instead of having one config for “CPU” and another for “Memory”, fetch both metrics in the [[command]] section and ask for a combined summary populating multiple [[variable]]s.
Appearance
This section focuses on recipes on how to display the data from your sources.
Separators
Simple separator

Separator is just a text block.
[[bar]]
blocks_left=["foo", "S", "bar", "S", "baz"]
[[block]]
name="S"
type = 'text'
separator_type = 'gap'
value = '|'
foreground = "#53e2ae"
This approach offers maximum flexibility:
- Multiple separator types and styles
- Dynamically separators based on conditions
- Disappearing separators via
show_if_matches
Specifying separator_type = "gap" is recommended. It gives oatbar a hint that the block is
a separator. For example multiple separators in a row do not make sense and
they will collapse if real blocks between them become hidden.
Empty space around bar and Rounded Corners
By default oatbar looks more traditional.

You can apply margins, transparent backgrounds, and edge blocks with separator_radius to achieve empty space and rounded corners. To achieve this:
- Make the
[[bar]]background entirely transparent (e.g.,#00000000) and applymargin. - Make the standard blocks opaque via
[[default_block]]. - Flank your layout arrays with specific left (
L) and right (R) endcap blocks utilizingseparator_radiusto form the rounded edges.

[[bar]]
blocks_right=["L", "layout", "S", "clock", "R"]
# `top` may need a value or must be zero depening on your WM.
margin={left=8, right=8, top=0, bottom=8}
# Alpha channels is zero, so the bar is transparent unless there is has a block.
background="#00000000"
[[default_block]]
# The actual block color.
background="#191919e6"
[[block]]
name='L'
type = 'text'
separator_type = 'left'
separator_radius = 8.0
[[block]]
name='R'
type = 'text'
separator_type = 'right'
separator_radius = 8.0
Border lines

# Or per [[block]] separately.
[[default_block]]
edgeline_color = "#53e2ae"
overline_color = "#53e2ae"
underline_color = "#53e2ae"
# edgeline applies to `left` and `right` blocks.
[[block]]
name='L'
type = 'text'
separator_type = 'left'
separator_radius = 8.0
[[block]]
name='R'
type = 'text'
separator_type = 'right'
separator_radius = 8.0
Partial bar and Subpanes
Bars of oatbar can be further separated into smaller disconnected partial bars (or subpanes). You can achieve this by chaining together R (Right), E (Empty gap), and L (Left) blocks.
This combination creates pill-like separation:
Rblock: Closes the preceding subpane with a rounded right edge.Eblock: Acts as a transparent gap (must havebackground = "#00000000"and stripped of edge lines).Lblock: Starts the next subpane with a rounded left edge.

[[bar]]
blocks_left=["L", "workspace", "R", "E", "L", "active_window", "R"]
[[block]]
name="E"
type = 'text'
separator_type = 'gap'
value = ' '
background = "#00000000"
# If you have set these in [[default_block]], reset them back.
edgeline_color = ""
overline_color = ""
underline_color = ""
Setting separator_type correctly for all separators will make partial panel
disappearing if all real blocks are hidden via show_if_matches.
Blocks
Pango markup
oatbar supports full Pango Markup
as a main tool to format the block content. Command can emit Pango markup too, controlling
the appearance of the blocks.

[[block]]
name='pango'
type = 'text'
value = "<span text_transform='capitalize' color='yellow'>h<i>ell</i>o</span> <span color='lawngreen'>World</span>"
Font names to be used in Pango can be looked up via the fc-list command.
Icons
Use icon fonts such as Font Awesome, Nerd Fonts, IcoMoon or emojis.
![]()
[[block]]
name='pango'
type = 'text'
value = "<span ${green_icon}></span> Symbolico - I'm free"
[[var]]
name="green_icon"
value="font='Font Awesome 6 Free 13' foreground='#53e2ae'"
Some icon fonts use ligatures instead of emojis, replacing words with icons like this:
value = "<span ${green_icon}>music</span> Symbolico - I'm free"
If your icon does not perfectly vertically align with your text, experiment with font size and rise Pango parameter.
Visibility
show_if_matches combined with a powerful tool to build dynamic bars.
Here it is used to only show the block if the value is not empty.
[block]
value = '${desktop:window_title.value}'
show_if_matches = [['${desktop:window_title.value}', '.+']]
Custom variables, not only those coming from commands can be used. They can be
set via oatctl var set, opening a huge number of possibilities. See some examples in
the Advanced chapter.
If you are not an expert in regular expressions, here are some useful ones:
| Regex | Meaning |
|---|---|
foo | Contains foo |
^foo | Starts with foo |
foo$ | Ends with foo |
^foo$ | Exactly foo |
^$ | Empty string |
.+ | Non empty string |
(foo|bar|baz) | Contains one of those words |
^(foo|bar|baz)$ | Exactly one of those words |
In the examples above foo works because it only contains alpha-numeric characters,
but be careful with including characters that have special meaning in regular expressions.
For more info read regex crate documentation.
Popup bars
A bar can be hidden and appear when certain conditions are met.
[[bar]]
popup=true
Popup bars appear on the top of the windows, unlike normal bars that allocate dedicated space on the screen.

Popup at cursor
Popup bar can be shown when the cursor approaches the screen edge where the bar is located.
[[bar]]
popup=true
popup_at_edge=true
Temporary popup on block change
When any property of the block changes you can make it appear. Depending on a popup
value you can show enclosing partial or entire bar.
[[bar]]
popup="bar"
#popup="partial_bar"
#popup="block"
If you won’t want to popup on any property change, you can limit it to one expression.
[[bar]]
popup_value="${foo}"
Example layout switcher that appears in the middle of the screen when you change your layout.
[[bar]]
blocks_center=["L", "layout_enum_center", "R"]
background="#00000000"
popup=true
position="center"
[[block]]
name = 'layout_enum_center'
type = 'enum'
active = '${keyboard:layout.active}'
variants = '${keyboard:layout.variants}'
popup = "block"
Tray
oatbar supports modern System Trays via the standard Status Notifier Item (SNI) protocol. Because it is built on SNI, trays work seamlessly across both X11 and Wayland environments.

Legacy xembed support:
oatbardoes not support the older X11xembedtray protocol. If you use legacy applications that requirexembed, run the third-partyxembedsniproxydaemon to bridge them to the SNI protocol.
How it Works
Unlike traditional desktop bars, oatbar doesn’t have a hardcoded “system tray area”. Trays are treated identically to regular blocks and variables.
When a tray application launches, the oatbar-sni protocol server extracts its pixel buffers and actions into standard variables. This gives you full control:
- Place trays anywhere: Insert tray icons individually anywhere in your layout.
- Split them up: Move different trays to completely different blocks or bars.
- Filter by monitor: Conditionally show specific apps only on certain monitors.
- Custom icons & text: Ignore the app’s native
pixmapand substitute your own images, font icons, or text. - Programmatic menu actions: Create dedicated buttons that click specific context menu items by name — no interactive menu needed.
Configuration Guide
1. Enable the SNI Protocol Server
Add the oatbar-sni command to your ~/.config/oatbar/config.toml:
[[command]]
name="sni"
command="oatbar-sni"
2. Discover Tray App IDs
Each tray block maps to one application. You need to find the identifier that the app registers under.
Launch the application you want to configure (e.g., vlc), then query the active variables:
oatctl var ls | grep sni | grep visible
You should see output like:
sni.vlc.visible
sni.Pidgin.visible
sni.chrome_status_icon_1.visible
sni.nm-tray.visible
The string after sni. is the app identifier (e.g., vlc, Pidgin). Note that some apps like Chrome use dynamic identifiers such as chrome_status_icon_1.
3. Add a Tray Block
Create an Image block binding to the app’s variables. Hook all on_mouse_* events to oatbar-sni activate to forward clicks to the tray application:
[[block]]
name="tray_vlc"
type="image"
pixmap="${sni:sni.vlc.pixmap}"
show_if_matches=[['${sni:sni.vlc.visible}', '1']]
max_image_height=20
on_mouse_left="oatbar-sni activate ${sni:sni.vlc.dbus} left $ABS_X $ABS_Y"
on_mouse_middle="oatbar-sni activate ${sni:sni.vlc.dbus} middle $ABS_X $ABS_Y"
on_mouse_right="oatbar-sni activate ${sni:sni.vlc.dbus} right $ABS_X $ABS_Y"
Then add your tray blocks to the bar layout:
[[bar]]
blocks_right=["L", "tray_vlc", "clock_pill", "R"]
Repeat for every tray app you use, replacing the identifier from step 2.
Recipes
oatbardoesn’t treat tray blocks specially — they are regular blocks. Any property can be omitted, overridden, or replaced with your own values.
Replace Tray Icon with Custom Text
Since a tray block is just a block, you can use type="text" instead of type="image" and ignore the pixmap entirely. Only the visible variable and click handlers reference SNI:
[[block]]
name="tray_vlc"
type="text"
value="<span foreground='#89b4fa'>🎵 VLC</span>"
show_if_matches=[['${sni:sni.vlc.visible}', '1']]
on_mouse_left="oatbar-sni activate ${sni:sni.vlc.dbus} left $ABS_X $ABS_Y"
on_mouse_right="oatbar-sni activate ${sni:sni.vlc.dbus} right $ABS_X $ABS_Y"
Replace Tray Icon with a Custom Image
Use a static image file instead of the app-provided pixmap:
[[block]]
name="tray_discord"
type="image"
value="/home/user/.config/oatbar/icons/discord.png"
show_if_matches=[['${sni:sni.chrome_status_icon_1.visible}', '1']]
max_image_height=20
on_mouse_left="oatbar-sni activate ${sni:sni.chrome_status_icon_1.dbus} left $ABS_X $ABS_Y"
Split Trays Across Different Bars
Place specific trays on different bars or monitors. Each tray block is independent:
# Primary monitor bar
[[bar]]
monitor="eDP-1"
blocks_right=["L", "tray_vlc", "tray_pidgin", "clock_pill", "R"]
# External monitor bar — only show network manager
[[bar]]
monitor="HDMI-A-1"
blocks_right=["L", "tray_nm", "clock_pill", "R"]
Customizing Context Menus
Some tray applications export their right-click context menus over DBus, relying on the bar to display them.
oatbar defaults to rendering these menus using rofi. Delegating to external search launchers like this provides a unique advantage: you can instantly use your keyboard to search, filter, and select through complex nested tray menus.

You can replace the default with any dmenu-compatible tool via ~/.config/oatbar/sni.toml:
Wayland (Wofi)
# ~/.config/oatbar/sni.toml
dbusmenu_display_cmd = "wofi --dmenu"
X11 (dmenu)
# ~/.config/oatbar/sni.toml
dbusmenu_display_cmd = "dmenu -l 100"
Blocks to click menu items
Since oatbar-sni can click menu items directly via dbusmenu item-click, you can create dedicated bar buttons that trigger specific menu actions — no right-click menu needed. This works for applications that export their menus over the DBusMenu protocol.
For example, create a “Mute” button that only appears when VLC is running:
[[block]]
name="vlc_mute"
type="text"
value="🔇 Mute"
show_if_matches=[['${sni:sni.vlc.visible}', '1']]
on_mouse_left="oatbar-sni dbusmenu item-click ${sni:sni.vlc.dbus} --regex 'Mute'"
This is a regular text block that:
- Uses
sni.vlc.visibleto appear only while VLC is running — you don’t even need a separate VLC tray icon block. - Clicks a specific menu item by label regex on left-click, skipping the interactive menu entirely.
Use oatbar-sni dbusmenu print to discover available menu labels:
oatbar-sni dbusmenu print $(oatctl var get sni:sni.vlc.dbus)
See the Reference for full dbusmenu command details.
Keyboard shortcuts for tray menus
Because oatbar exposes variables via oatctl, you can bind keyboard shortcuts in your window manager to interact with tray applications directly. By combining oatctl var get to fetch the dynamic DBus path with oatbar-sni,
and rofi you can invoke menus or click specific items without using the mouse.
Here is an example using i3wm/sway configuration syntax to control VLC:
# ~/.config/i3/config
# Press Super+V to display VLC's right-click context menu (via rofi/dmenu)
bindsym $mod+v exec oatbar-sni activate $(oatctl var get sni:sni.vlc.dbus) right 0 0
# Press Super+M to directly click "Mute" in VLC's context menu, skipping the UI
bindsym $mod+m exec oatbar-sni dbusmenu item-click $(oatctl var get sni:sni.vlc.dbus) --regex 'Mute'
This works identically to the on-click actions in bar blocks, but relies on your window manager for the trigger.
Media (MPRIS)
oatbar supports controlling and displaying track information from any media player that implements the MPRIS D-Bus interface (e.g., Spotify, VLC, mpv, Firefox). To control mpd, which lacks native MPRIS support, we recommend using the mpdris daemon bridge.
The oatbar-mpris command provides both a continuous stream of media variables and subcommands for controlling playback. For a full list of variables and commands, see the MPRIS Reference.
Enabling the command
Add the oatbar-mpris command to your ~/.config/oatbar/config.toml:
[[command]]
name="mpris"
command="oatbar-mpris"
Media Player Block Example
Here is a block that displays the title and artist, and uses click handlers to control playback:
[[block]]
name="media_player"
type="text"
value="${mpris:mpris.track}"
show_if_matches=[['${mpris:mpris.playback_status}', 'Playing']]
on_mouse_left="oatbar-mpris play-pause"
on_mouse_right="oatbar-mpris next"
on_mouse_middle="oatbar-mpris previous"
Displaying Progress
The oatbar-mpris command polls the current position periodically and handles track seeks automatically. You can use this to display a progress bar.
[[block]]
name="media_progress"
type="number"
value="${mpris:mpris.position}"
min_value="0"
max_value="${mpris:mpris.length}"
number_display="progress_bar"
progress_bar_size=20
output_format="${mpris:mpris.position_str} ${value} ${mpris:mpris.length_str}"
show_if_matches=[['${mpris:mpris.playback_status}', 'Playing']]
MCP
oatbar-mcp is a Model Context Protocol server that gives
AI assistants live access to oatbar’s variable system. They can read bar state, write new content,
trigger data refreshes, edit your config, and restart oatbar — all in real time.
See MCP Reference for setup instructions and the full configuration reference.
- Bar Configurator
- Live Activity Indicator
- Inject Custom Content
- Read Bar State for Context
- Force-Refresh Stale Data
- Privacy: Hiding Variables
- Multiple Concurrent Clients
Bar Configurator
The most powerful use of oatbar-mcp is letting an AI assistant act as a configuration
assistant for your bar.
The assistant can:
- Read your
~/.config/oatbar/config.tomland explain what each block does - Add, remove, or rearrange blocks
- Tune colors, fonts, padding, and separators
- Diagnose why a block isn’t showing up by inspecting live variables via
list_vars - Apply changes immediately by calling
restart_oatbar— no terminal needed
Getting started: Connect oatbar-mcp to your MCP client, then ask:
- “Show me what variables are available on the bar.”
- “Add a block showing disk usage on the right side.”
- “Why is my clock block not visible?”
- “Change the background of the workspace block to #2d2d2d.”
The assistant inspects current state, edits the config file, and restarts oatbar — all in one conversation.
See also: Configuration Overview, Block Reference
Live Activity Indicator
Show what the MCP client is doing right now as it happens. The assistant reports short status
messages ("Building...", "Searching...", "Editing config.toml") via report_status as it
works. The status clears automatically when activity stops.
Add this block to your config to display it:
[[block]]
name = "mcp_status"
type = "text"
value = "${mcp:value}"
show_if_matches = [["${mcp:recent}", "1"]]
Inject Custom Content
Ask the assistant to put anything on your bar — a reminder, a count of open issues, a quote:
- “Put ‘Focus: finish the parser’ on my bar.”
The assistant calls set_var with a name like mcp:message and you display it:
[[block]]
name = "ai_message"
type = "text"
value = "${mcp:message}"
show_if_matches = [["${mcp:message}", ".+"]]
Blocks support Pango markup in their value
field, so if the assistant writes a plain text value like "Focus: finish the parser",
the block can still render it with styling defined in the block config itself.
Read Bar State for Context
The assistant can call list_vars or get_var to read what oatbar currently knows — focused
workspace, media track, system stats — and use that as context.
Example: Ask the assistant to summarize system status. It reads variables like stats:cpu
and stats:memory and returns a human-readable summary — no shell commands needed.
Example: Ask which workspace is focused. It reads desktop:workspace.value and can
suggest or take action based on the current context.
Force-Refresh Stale Data
If bar data looks outdated, ask the assistant to call poke to trigger an immediate re-run:
- “My clock block looks stuck — refresh it.”
The assistant calls poke with command = "clock" to restart that command’s interval
immediately, or with no command to refresh everything at once.
Privacy: Hiding Variables
Some variables you may not want an MCP client to read — for example, the currently focused
window title may reveal what you are working on. Use hidden_variables in
~/.config/oatbar/mcp.toml to block access:
# ~/.config/oatbar/mcp.toml
hidden_variables = ["desktop:window\\..*"]
The assistant will receive an access-denied error if it tries to read or write matching variable names.
Multiple Concurrent Clients
If you run more than one MCP client simultaneously, set mcp_name per client so their
status variables don’t collide:
# ~/.config/oatbar/mcp.toml
mcp_name = "assistant"
Then display ${mcp:assistant.value} on the bar, and use ${mcp:assistant.recent} in
show_if_matches. Each client gets its own namespace.
Advanced examples
Let’s build something complex with what oatbar has to offer.
Combination of variables, visibility control and programmatic access to variables via oatctl var provides
tremendous power.
In these examples oatctl var is often called from on_mouse_left handlers, but you
can use it in your WM keybindings too.
Workspace customizations
If you have enabled oatbar-desktop command, you should have access to the ${desktop:workspace.value}
variable.
[[command]]
name="desktop"
command="oatbar-desktop"
See which values it can have via oatctl var ls | grep desktop when running oatbar. You can use
this to set any properties of your block, including appearance and visibility.
Appearance
In this example, the bar on workspace two is a bit more red than usual.
[[var]]
name="default_block_bg"
value="${desktop:workspace.value}"
replace_first_match=true
replace=[
["^two$","#301919e6"],
[".*", "#191919e6"],
]
[[default_block]]
background="${default_block_bg}"
Visibility
This block shown only on workspace three.
[[block]]
show_if_matches=[["${desktop:workspace.value}", "^three$"]]
Menu
oatbar does not know anything about menus, but let’s build one.


[[bar]]
blocks_left=["L", "menu", "launch_chrome", "launch_terminal", "R"]
[[default_block]]
background="#191919e6"
[[default_block]]
name="menu_child"
background="#111111e6"
line_width=3
overline_color="#191919e6"
underline_color="#191919e6"
show_if_matches=[["${show_menu}","show"]]
[[block]]
name='menu'
type = 'text'
value = "${show_menu}"
replace = [
["^$", "circle-right"],
["show", "circle-left"],
["(.+)","<span font='IcoMoon-Free 12' weight='bold' color='#53e2ae'>$1</span>"],
]
on_mouse_left = "oatctl var rotate show_menu right '' show"
[[block]]
name='launch_chrome'
type = 'text'
inherit="menu_child"
value = "<span font='IcoMoon-Free 12'></span> "
on_mouse_left = "oatctl var set show_menu ''; chrome"
[[block]]
name='launch_terminal'
type = 'text'
inherit="menu_child"
value = "<span font='IcoMoon-Free 12'></span> "
on_mouse_left = "oatctl var set show_menu ''; alacritty"
Let’s take a closer look:
- We create a
show_menuvariable that can be empty or set toshow - In
menublock all regexes apply in sequence. - The first two replace it with icon names.
- The last one wraps the icon name into the final Pango markup.
- The
on_mouse_leftrotates the values ofshow_menubetween empty andshow, effectively toggling it. - Blocks are only displayed if
show_menuis set. - Blocks clear
show_menubefore launching the app to hide the menu. - A small cosmetic effect is achieved by inheriting a
default_blockwith a different style.
This example can be extended to build more layers of nesting by introducing additional variables.
Rotating menu
It sometimes useful to always show the main panel, but have an occasional access to additional information. A great idea would be to build a rotating menu.

[[bar]]
blocks_left=["L", "rotate_left", "panel_0", "panel_1", "panel_2", "rotate_right", "R"]
[[block]]
name='rotate_left'
type = 'text'
value = "<span font='IcoMoon-Free 12' color='#53e2ae'>circle-left</span>"
on_mouse_left = "oatctl var rotate rotation_idx left '' 1 2"
[[block]]
name='rotate_right'
type = 'text'
value = "<span font='IcoMoon-Free 12' color='#53e2ae'>circle-right</span>"
on_mouse_left = "oatctl var rotate rotation_idx right '' 1 2"
[[block]]
name='panel_0'
type = 'text'
value = "<span color='yellow'>Panel 0</span>"
show_if_matches=[["${rotation_idx}", "^$"]]
[[block]]
name='panel_1'
type = 'text'
value = "<span color='lime'>Panel 1</span>"
show_if_matches=[["${rotation_idx}", "1"]]
[[block]]
name='panel_2'
type = 'text'
value = "<span color='deeppink'>Panel 2</span>"
show_if_matches=[["${rotation_idx}", "2"]]
Dynamic image block

This looks like a normal clock, but it is actually loaded from the PNG, that was generated by a custom command. You can display arbitrary graphics!
A program to generate an image:
import datetime
import cairo
import os
import time
import sys
while True:
WIDTH, HEIGHT = 270, 28
sfc = cairo.ImageSurface(cairo.Format.ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context(sfc)
ctx.set_font_size(16)
ctx.select_font_face("Courier", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
ctx.move_to(3, 20)
ctx.set_source_rgb(1.0, 1.0, 1.0)
time_str="%s" % datetime.datetime.now()
ctx.show_text(time_str)
ctx.fill()
output_filename = '/tmp/custom-clock.png'
sfc.write_to_png(output_filename)
print(output_filename)
print(time_str)
sys.stdout.flush()
time.sleep(1)
If run, it would write /tmp/custom-clock.png and print text like this
$ python3 ./book/src/configuration/cookbook/custom-clock.py
/tmp/custom-clock.png
2024-06-30 21:09:22.165792
/tmp/custom-clock.png
2024-06-30 21:09:23.185004
/tmp/custom-clock.png
2024-06-30 21:09:24.195003
[[command]]
name="img-clock"
command="python3 /home/user/Project/oatbar/clock.py"
line_names=["file_name", "ts"]
[[block]]
name = 'image'
type = 'image'
value = '${img-clock:file_name}'
updater_value = '${img-clock:ts}'
The value of img-clock:ts is not important, but because it
changes, the image is not cached. It get’s reloaded from
the disk on each update.
This is not the most efficient way to build custom widgets.
It involves writing/reading files from the
disk on each update. Building animations is possible, but
not efficient, which can matter for you if you are on
laptop battery. You can use tmpfs to save on disk writes,
but not so much on CPU cycles.
Reference
This section contains detailed reference documentation for various oatbar modules.
- LLM —
oatbar-llmconfiguration - Tray — SNI system tray protocol
- MPRIS — Media player integration
- MCP —
oatbar-mcpModel Context Protocol server
LLM Configuration
oatbar-llm is configured via ~/.config/oatbar/llm.toml.
Structure
The configuration file consists of three main sections:
[llm]: Global LLM provider settings.[[command]]: External commands to gather context.[[variable]]: Variables to extract or generate using the LLM.
[llm] Section
Configures the LLM provider and global behavior.
| Field | Type | Default | Description |
|---|---|---|---|
provider | string | Required | The LLM provider. Supported: google, openai, anthropic, mistral, xai, ollama. |
name | string | Required | The model name (e.g., gemini-2.5-flash, gpt-4o). |
role | string | Default system prompt | Custom system prompt to define the AI’s persona and goal. |
temperature | float | 0.6 | Controls randomness (0.0 = deterministic, 1.0 = creative). |
max_tokens | int | 3000 | Maximum number of tokens in the response. |
url | string | None | Custom API URL (useful for local LLMs or proxies). |
knowledge_base | path | None | Path to a text file containing static context/preferences to include in the prompt. Must be an absolute path (no ~). |
output_format_prompt | string | None | Custom instruction for output format (required if using Custom output mode). |
retries | int | 5 | Number of retries for failed API calls. |
back_off | duration | 1s | Initial backoff duration for retries. |
[[command]] Section
Defines shell commands to run. Their output is fed to the LLM as context.
| Field | Type | Default | Description |
|---|---|---|---|
command | string | Required | The shell command to execute. |
name | string | command string | A unique name to refer to this command’s output in the prompt context. |
[[variable]] Section
Defines the questions to ask the LLM and how to handle the answers.
| Field | Type | Default | Description |
|---|---|---|---|
name | string | Required | The key for the variable in the output JSON. |
question | string | Required | The prompt/question for the LLM to answer to populate this variable. |
type | string | string | The expected data type: string, number, boolean. |
allowed_answers | list | None | A list of valid string values (enum) to restrict the output. |
max_length | int | None | Maximum length of the string response. |
write_to | path | None | If set, the variable’s value will be written to this file. Must be an absolute path (no ~). |
Output Modes
oatbar-llm supports different output modes via the --mode CLI flag:
json(default): Outputs a JSON object suitable foroatbar(i3bar format).debug: Prints the full prompt and raw response for debugging.custom: Outputs raw text based onoutput_format_prompt. Useful for generating reports or files.
Configuring Keys
API keys are not stored in the configuration file. Instead, oatbar-llm reads them from specific files in the configuration directory (~/.config/oatbar/).
| Provider | Key File Path |
|---|---|
~/.config/oatbar/google_api_key | |
| OpenAI | ~/.config/oatbar/openai_api_key |
| Anthropic | ~/.config/oatbar/anthropic_api_key |
| Mistral | ~/.config/oatbar/mistral_api_key |
| xAI | ~/.config/oatbar/xai_api_key |
| Ollama | Not required |
Ensure these files contain only the API key (no newlines or extra spaces preferred, though whitespace is trimmed).
Ollama Configuration
Ollama does not require an API key. However, you may need to specify the URL if it’s not running on the default port.
[llm]
provider="ollama"
name="llama3"
url="http://localhost:11434" # Optional, defaults to this value
CLI Options
--config <FILE>: Path to a custom config file (default:~/.config/oatbar/llm.toml).--mode <MODE>: Output mode (json,debug,custom).
Tray Reference
This page documents how oatbar interacts with the Status Notifier Item (SNI) protocol.
The oatbar-sni backend implements a cross-platform System Tray endpoint that works on both Wayland and X11.
Variables
When an application registers a tray item on DBus, oatbar-sni exports the following variables. All variables are prefixed with the app’s namespace ID (AppID):
| Variable | Description |
|---|---|
sni.AppID.visible | 1 if the tray item is active, empty string if disconnected. Use with show_if_matches. |
sni.AppID.dbus | The DBus name required by oatbar-sni activate to route mouse interactions. |
sni.AppID.pixmap | JSON array of pixel data: [width, height, byte0, byte1, ...] in ARGB format. Bind to the block’s pixmap field. |
sni.AppID.icon_name | Named icon from the system icon theme (alternative to pixmap). Requires gtk4_icons feature (disabled by default). |
sni.AppID.icon_theme_path | Additional icon theme search path for resolving icon_name. Requires gtk4_icons feature (disabled by default). |
Mouse Event Handling
Forward mouse clicks to the tray application using oatbar-sni activate:
oatbar-sni activate <dbus_name> <button> <x> <y>
| Argument | Description |
|---|---|
dbus_name | The DBus name from sni.AppID.dbus. |
button | One of left, middle, right. |
x, y | Screen coordinates of the click (use $ABS_X, $ABS_Y). |
Usage pattern:
on_mouse_left="oatbar-sni activate ${sni:sni.App.dbus} left $ABS_X $ABS_Y"
Context Menus (DBusMenu Protocol)
The SNI specification supports two approaches for context menus:
- Application-managed: The application draws its own context menu window.
oatbarsimply forwards the activation request. - Bar-managed (DBusMenu): The application exports menu items over DBus using the DBusMenu protocol. The bar is responsible for displaying them.
For bar-managed menus, oatbar-sni pipes the menu entries into an external launcher process via stdin and reads the user’s selection from stdout.
Default command:
rofi -dmenu -i -no-sort -hover-select -me-select-entry '' -me-accept-entry MousePrimary
Customizing the Menu Renderer
Override the menu display command by creating ~/.config/oatbar/sni.toml:
dbusmenu_display_cmd = "wofi --dmenu"
The configured command receives menu entries on stdin (one per line) and must print the selected entry to stdout. See the Cookbook for examples.
DBusMenu CLI Commands
dbusmenu print
Dumps the full menu tree of a tray application to the console:
oatbar-sni dbusmenu print <dbus_address>
Each line shows the menu item ID, indentation for hierarchy, and the label. Useful for discovering item IDs and labels for use with item-click. Example:
20 Hide VLC media player in taskbar
19 ---
18 Play
13 ---
12 Speed:
3 Faster (fine)
2 Normal Speed
1 Slower (fine)
11 ---
9 Increase Volume
8 Decrease Volume
7 Mute
6 ---
5 Open Media
4 Quit
dbusmenu item-click
Programmatically clicks a menu item without user interaction:
oatbar-sni dbusmenu item-click <dbus_address> --id <item_id>
oatbar-sni dbusmenu item-click <dbus_address> --regex <pattern>
| Option | Description |
|---|---|
--id <item_id> | Click the menu item with this exact numeric ID (from dbusmenu print). |
--regex <pattern> | Click the menu item whose label matches this regex. Fails if zero or more than one item matches. |
Exactly one of --id or --regex must be provided.
MPRIS Reference
This page documents the variables and CLI commands exposed by oatbar-mpris.
Variables
The oatbar-mpris daemon continuously outputs the state of the active MPRIS media player. All variables are available under the mpris.mpris.* namespace (assuming your command is named mpris).
| Variable | Description |
|---|---|
mpris.mpris.full_text | A formatted string combining artist and title with a music: prefix (e.g. music: Artist - Title). |
mpris.mpris.track | Identical to the formatting of full_text but without the music: prefix. |
mpris.mpris.title | The track title. |
mpris.mpris.artist | The track artist. |
mpris.mpris.album | The album name. |
mpris.mpris.playback_status | Current playback state: Playing, Paused, or Stopped. |
mpris.mpris.player | Short name of the active player (e.g. spotify, vlc). |
mpris.mpris.volume | Player volume level (0-100). |
mpris.mpris.length | Track duration in seconds. |
mpris.mpris.length_str | Track duration formatted as MM:SS or HH:MM:SS. |
mpris.mpris.position | Current playback position in seconds. |
mpris.mpris.position_str | Current playback position formatted as MM:SS or HH:MM:SS. |
mpris.mpris.position_ts | The Unix timestamp (in seconds) of the last position sample. Useful for manual interpolation. |
mpris.mpris.rate | Current playback rate (multiplier, typically 1.0). |
CLI Commands
The oatbar-mpris binary also acts as a CLI client to control the active media player.
oatbar-mpris [COMMAND]
Output Mode
Running without any command acts as a long-running daemon that streams i3bar JSON to standard output. It is intended to be run by oatbar as an external block configuration command.
Media Controls
These commands interact with the currently active media player:
play— Start or resume playback.pause— Pause playback.play-pause— Toggle between play and pause.next— Skip to the next track.previous— Go to the previous track.stop— Stop playback entirely.seek <PCT>— Seek to a specific percentage of the track (0-100).
Example:
oatbar-mpris seek 50
(Seeks strictly to the midpoint of the current track)
MCP Server Configuration
oatbar-mcp is a Model Context Protocol server that exposes
oatbar’s internal variable system to AI assistants. It lets MCP-compatible clients read bar state,
inject content, trigger data refreshes, and restart oatbar — all in real time.
See MCP Cookbook for practical usage ideas.
Building
oatbar-mcp requires the mcp feature flag:
cargo build --release --features mcp --bin oatbar-mcp
CLI Options
| Flag | Default | Description |
|---|---|---|
--instance-name <NAME> | oatbar | Unique name of the running oatbar instance to connect to. Must match the instance oatbar was started with. |
Configuration File
oatbar-mcp reads an optional config from ~/.config/oatbar/mcp.toml. All fields have defaults
and the file may be omitted entirely.
# ~/.config/oatbar/mcp.toml
# Regex patterns for variables that MCP clients cannot read or write.
# Useful for hiding variables you prefer the assistant not to see,
# such as window titles that reveal what you are working on.
hidden_variables = ["desktop:window\\..*"]
# Seconds that mcp:recent (or mcp:<name>.recent) stays "1" after a
# status report before automatically resetting to "". Default: 5.
recent_timeout_seconds = 5
# Optional name suffix for mcp variables. When unset, variables are
# named `mcp:value` and `mcp:recent`. When set to e.g. "assistant",
# they become `mcp:assistant.value` and `mcp:assistant.recent`.
# Useful when running multiple MCP clients simultaneously.
mcp_name = "assistant"
Fields
| Field | Type | Default | Description |
|---|---|---|---|
hidden_variables | list of regex strings | [] | Variable name patterns to hide from MCP clients. Matched variables cannot be read or written. |
recent_timeout_seconds | integer | 5 | How long (in seconds) mcp:recent stays "1" after a status update before auto-resetting to "". |
mcp_name | string | None | Optional name suffix. Changes MCP variable names from mcp:* to mcp:<name>.*. |
MCP Variables
When report_status is called, oatbar-mcp writes two variables into oatbar’s variable system:
| Variable | Value | Description |
|---|---|---|
mcp:value | status string | The last status message reported by the MCP client. |
mcp:recent | "1" or "" | Set to "1" on status update, cleared after recent_timeout_seconds. Use with show_if_matches to show the block only while active. |
When mcp_name is set to e.g. "assistant", these become mcp:assistant.value and
mcp:assistant.recent.
Displaying MCP Status on the Bar
Use show_if_matches to show the block only while the assistant is actively reporting:
[[block]]
name = "mcp_status"
type = "text"
value = "${mcp:value}"
show_if_matches = [["${mcp:recent}", "1"]]
Tools
list_vars
Lists all oatbar variables and their current values. Useful for exploring available data and diagnosing block issues.
| Parameter | Type | Description |
|---|---|---|
filter | string (regex, optional) | Only return variables whose names match this regex. |
names_only | bool (optional) | If true, return only variable names without values. Saves tokens when exploring. |
human_description | string (optional) | Shown on the bar as a status update (updates mcp:value and mcp:recent). |
Variables matching hidden_variables patterns are excluded from the output.
get_var
Gets the current value of a single variable.
| Parameter | Type | Description |
|---|---|---|
name | string | Variable name (e.g., clock:time). |
human_description | string (optional) | Shown on the bar as a status update (updates mcp:value and mcp:recent). |
Returns an error if the variable matches hidden_variables.
set_var
Sets a variable to a new value. Variables do not need to exist beforehand — they are created on first write. The bar redraws immediately.
| Parameter | Type | Description |
|---|---|---|
name | string | Variable name. |
value | string | New value. Plain text; Pango markup rendering is controlled by the block that displays the variable, not the variable itself. |
human_description | string (optional) | Shown on the bar as a status update (updates mcp:value and mcp:recent). |
Returns an error if the variable matches hidden_variables.
poke
Forces an immediate refresh of one or all commands by interrupting their interval timer.
| Parameter | Type | Description |
|---|---|---|
command | string (optional) | Name of the command to poke. If omitted, pokes all commands. |
human_description | string (optional) | Shown on the bar as a status update (updates mcp:value and mcp:recent). |
report_status
Writes a short status string to mcp:value and sets mcp:recent to "1" for
recent_timeout_seconds. Intended for MCP clients to show live activity on the bar.
| Parameter | Type | Description |
|---|---|---|
status | string | Short status message (e.g., "Building..."). Sets mcp:value and triggers mcp:recent. |
restart_oatbar
Restarts the connected oatbar instance cleanly via IPC, preserving the original command line.
Use this after editing config.toml to apply changes without terminal disruption.
| Parameter | Type | Description |
|---|---|---|
human_description | string (optional) | Shown on the bar as a status update (updates mcp:value and mcp:recent). |
Connecting an MCP Client
Add the server to your MCP client configuration. The exact format depends on your client; generally it looks like:
{
"mcpServers": {
"oatbar": {
"command": "/path/to/oatbar-mcp",
"args": ["--instance-name", "oatbar"]
}
}
}
The server communicates over stdio and connects to oatbar via a Unix domain socket. The socket
path is resolved in order: $XDG_RUNTIME_DIR, then $XDG_STATE_HOME, then the system temp
directory — whichever exists first. The socket file is named oatbar/<instance_name>.sock
within that directory.
