Introduction

Noti is a simple yet powerful notification daemon, offering:

  • Native Wayland Support: Built from the ground up for Wayland environments
  • Highly-customizable: Tailor your notifications to suit your preferences
  • Per-App Styling: Unique notification styles for different applications
  • Modern Design: Clean, minimalist approach to desktop notifications

Whether you're a developer who wants to integrate notifications into your Wayland environment or a regular user looking for a more customizable desktop experience, Noti is here to provide a simple, powerful solution.


noti

Noti doesn’t just deliver notifications; it gives you control.

Getting Started with Noti

Prerequisites

Before installing Noti, ensure you have the following:

  • A Linux system with a Wayland compositor
  • Rust toolchain (rustc and cargo)
  • Basic understanding of terminal commands

Installation

The simplest way to install Noti is using Cargo, Rust's package manager:

# Install directly from GitHub
cargo install --git https://github.com/noti-rs/noti/

# Or clone and install locally
git clone https://github.com/noti-rs/noti.git
cd noti
cargo install --path .

Building from Source

# Clone the repository
git clone https://github.com/noti-rs/noti.git
cd noti

# Build in release mode
cargo build --release

# Install the binary
cargo install --path .

Running

To confirm Noti is installed correctly:

# Check Noti version
noti --version

# Display help information
noti --help

After installation, you can start Noti manually:

# Start Noti daemon
noti run

Automatic Startup

Session Startup Script

You can add noti run to your session startup script to ensure it starts automatically. For example, if you use Hyprland, include the following line in your Hyprland configuration:

exec-once = noti run

This is a simple way to start noti automatically when your session begins. However, note that this approach does not handle cases where the application crashes or encounters errors — it will not automatically restart noti in such situations.

Advanced Autostart

For more robust and advanced autostart setups, refer to the Autostart. This guide provides detailed instructions for setting up a reliable autostart configuration.

Autostart

Autostarting Noti using Systemd and D-Bus

This guide will help you configure autostart Noti using systemd user services and D-Bus activation.

WARNING: Specific configurations might vary slightly depending on your distribution and desktop setup.

Understanding Environment Variables

VariableDefault PathDescription
$XDG_DATA_HOME~/.local/shareUser-specific data directory
$XDG_CONFIG_HOME~/.configUser-specific configuration directory

Step 1: D-Bus Service Configuration

Create a D-Bus service to define how application should be launched:

mkdir -d $XDG_DATA_HOME/dbus-1/services
touch $XDG_DATA_HOME/dbus-1/services/org.freedesktop.Notifications.service

Add the following:

[D-Bus Service]
Name=org.freedesktop.Notifications
Exec=%h/.cargo/bin/noti run
SystemdService=noti.service

Step 2: Systemd User Service Configuration

Create a systemd unit to manage application's lifecycle:

mkdir -d $XDG_CONFIG_HOME/systemd/user
touch $XDG_CONFIG_HOME/systemd/user/noti.service

Add the following:

[Unit]
Description=Noti Application
PartOf=graphical-session.target
After=graphical-session.target

[Service]
Type=dbus
BusName=org.freedesktop.Notifications
Environment=XDG_CONFIG_HOME=%h/.config
Environment=NOTI_LOG=info
ExecStart=%h/.cargo/bin/noti run
Restart=always

[Install]
WantedBy=default.target

Unit Configuration Breakdown

ConfigurationPurpose
PartOf=graphical-session.targetEnsures the service is managed with the graphical session
Type=dbusEnables D-Bus activation
Restart=alwaysAutomatically restarts on failure
WantedBy=default.targetEnables autostart at user login

Step 3: Enable and Start the Service

# Reload systemd user configuration
systemctl --user daemon-reload

# Enable service to start on boot
systemctl --user enable noti.service

# Start service immediately
systemctl --user start noti.service

Troubleshooting

# View service status
systemctl --user status noti

# Follow live service logs
journalctl --user --unit noti --follow

Common Issues:

  • Ensure the executable path is correct
  • Check file permissions
  • Verify D-Bus and systemd configurations
  • Confirm environment variables are set correctly

Configuration

Understanding Noti's Configuration

Noti looks for configuration files in the following locations (in order of priority):

  1. $XDG_CONFIG_HOME/noti/config.toml
  2. $HOME/.config/noti/config.toml

Hot Reload

Noti supports hot-reloading configuration changes. Simply save your config file, and Noti will automatically apply the new settings.

Types

Before of all properties, need to understand a few primitive types. The complex types like array or table will be explained in place.

TypeDescription
boolA boolean value
u8An unsigned integer of 8 bit
u16An unsigned integer of 16 bit
StringA string,typically used as an enumeration variant
[...]An array containing various type. Used as tuple
[T; from..to]An array with elements of type T, where the number of elements can vary between from and to (inclusive)
ColorA color value. Either string or table
PathThe path to particular file or directory

Configuration Structure

The Noti configuration is divided into four main property groups:

Each of them belongs to specific idea. So the reading order is not matter. But we recommend you to go to through from the first one to the last one.

Importing

You can import configuration files from other files:

use = [
    "$XDG_CONFIG_HOME/noti/themes.toml",
    "~/.config/noti/layouts/layout.toml",
    "./apps/*"
]

Note:

  • Current configuration file takes precedence
  • Avoid importing the same file multiple time
  • Avoid circular dependencies
  • Merge behavior is not guaranteed, so declare configurations carefully

General Settings

General settings apply to the entire notification system and control global behavior.

Property nameDescriptionTypeDefault value
fontFontString"Noto Sans"
widthWidth of banner frameu16300
heightHeight of banner frameu16150
anchorScreen position where notifications appearString"top right"
gapSpace size between two banners. Measures in pxu810
offsetDistance from screen edges[u8, u8][0, 0]
sortingHow notifications are orderedString or Sorting"default"
idle_thresholdDuration that pauses notification timeouts during user inactivityString"5 sec"
limitMaximum number of notifications on the screenu80

Font

Accepts the font name, which may or may not be separated by spaces.

font = "JetBrainsMono Nerd Font"

Note:

The application can only use font names recognized by the fc-list command. Styled fonts like "Noto Sans Bold" can't be used directly, but the app may load the required styles internally.

Width & Height

The width and height properties define the dimensions of the notification banner frame in pixels. These settings control the visual size of the individual notification banners on the screen.

  • width: Sets the horizontal size of the banner.
  • height: Sets the vertical size of the banner.
width = 400
height = 200

Anchor

The anchor of the current monitor for the current window instance, which determines where the notification banners will appear. The possible values are:

top-lefttoptop-right
leftright
bottom-leftbottombottom-right
anchor = "top"

Note:

Dash between words is optional.

Gap

The gap property specifies the amount of space, measured in pixels, between two adjacent notification banners. This setting ensures that notifications do not overlap and maintains a consistent visual separation between them.

gap = 15

Offset

The offset from the edges for the window instance. The first value represents the offset along the x-axis, and the second value represents the offset along the y-axis.

For example, if you choose the "bottom-left" anchor and set an offset of [5, 10], the window instance will be positioned at the bottom-left edge of the current monitor, with a 5-pixel offset from the left edge and a 10-pixel offset from the bottom edge.

offset = [10, 10]

Sorting

The property that determines the banner sorting rule. This is particularly useful when you want to position banners with critical urgency at the top or bottom.

You can define a string for ascending sorting by default.

# with default ordering
sorting = "urgency"

However, to sort in descending order, you must define a table:

Possible values of the by property name:

  • "default" (alias to "time")
  • "time"
  • "id"
  • "urgency"

Possible values of the ordering property name:

  • "ascending" (also possible short name "asc")
  • "descending" (also possible short name "desc")
# with specific ordering
[general.sorting]
by = "id"
ordering = "descending"

Idle Threshold

When idle_threshold is set, notifications will not be removed or expired while the user is idle beyond the configured threshold. Once the user is active again, the timeout resumes. This setting accepts a human-readable duration format (e.g., "15 minutes", "30s").

If set to "none", the idle timeout behavior is disabled.

idle_threshold = "5 min"

Limit

The maximum number of notifications that can be displayed at once. Once this limit is reached, any new notifications will be queued and shown once the currently displayed ones either time out or are manually dismissed.

A value of 0 means there is no limit.

limit = 3

Display Configuration

The display section allows you to customize the visual aspects of notifications.

Property nameDescriptionTypeDefault value
paddingSpacing from the banner's edge to inner contentu8 or [u8; 1..4] or Spacing0
marginSpacing from the banner's edge to inner contentu8 or [u8; 1..4] or Spacing0
imageImage propertiesImage-
iconsIcons propertiesIcons-
borderBorder propertiesBorder-
textText properties (alias for title and body)Text-
titleTitle text propertiesText-
bodyBody text propertiesText-
markupEnables HTML markupbooltrue
timeoutBanner timeoutu16 or Timeout0
layoutCustom layout pathPath"default"
themeTheme nameString-

Padding & Margin

In this application, padding and margin control spacing in different ways:

  • Padding: The space between an element's content (like text or an image) and its edges. It reduces the area available for the content inside the element.
  • Margin: The space between an element's outer edges and other surrounding elements. It separates elements from each other.

Declaring Padding and Margin Properties

CSS-like

If you familiar with CSS, you know that the padding or the margin can be applied in single row, in the TOML config file you can do it using array:

# Applies vertical and horizontal paddings respectively
padding = [0, 5]

# Applies top, horizontal and bottom paddings respectively
margin = [3, 2, 5]

# Applies top, right, bottom, left paddings respectively
padding = [1, 2, 3, 4]

# For all-directional padding or margin
padding = 10
margin = 5

Explicit

If you prefer a more detailed approach, you can use an explicit syntax with Spacing table:

KeyShort descriptionType
topSpacing from topu8
rightSpacing from rightu8
bottomSpacing from bottomu8
leftSpacing from leftu8
verticalSpacing from top and bottom togetheru8
horizontalSpacing from left and right togetheru8
# Sets only top padding
padding = { top = 3 }

# Sets only top and right padding
padding = { top = 5, right = 6 }

# Instead of
# padding = { top = 5, right = 6, bottom = 5 }
# You can write
padding = { vertical = 5, right = 6 }

# Conflicting values will result in an error due to ambiguity:
# padding = { top = 5, vertical = 6 } --- ERROR

# You can apply the same way for margin
margin = { top = 5, horizontal = 10 }

# For all-directional padding or margin
padding = 10
margin = 5

Warning:

  • horizontal is incompatible with left or right keys
  • vertical is incompatible with top or bottom keys

If content (like an image or text) isn't displaying correctly in a banner, check the padding or margin. Excessive padding or margin can reduce the space for content, causing it to overflow or not fit properly.

Image

Typically, the notification includes an image or icon, which is displayed on the left side of the banner.

Here's a table of Image properties:

Property nameDescriptionTypeDefault value
max_sizeSets the max size for image and resizes it when width or height of image exceeds this valueu1664
roundingValue used to round image cornersu160
marginSpacing around image. If there is no space for image, the image will be squished.u8 or [u8; 1..4] or Spacing0
resizing_methodResizing method for image when it exceeds max_size. Possible values: "gaussian", "nearest", "triandle", "catmull-rom", "lanczos3"String"gaussian"
[display.image]
max_size = 32
rounding = 10

Icons

If application doesn't provide any image but app-icon, then it will be treated as image.

The Icons table:

Property nameDescriptionTypeDefault value
themeIcon theme like Adwaita, Papirus...Strng"Adwaita"
sizeConcrete sizes of the icon. Will be picked the first one which found in system resources. Ordering matters - searches from first to last.[u16; 1..][64, 32]
[display.icons]
theme = "Papirus"
size = [64, 32, 16]

Note: Since app-icon threated as image, the image properties will be applied to icon, even the max_size.

Border

You can customize the notification banner with border styles, including border size and border radius:

  • Border size: The width of the border around the banner, which also reduces the inner space.
  • Border radius: How rounded the corners of the banner are.

The Border table:

KeyShort descriptionTypeDefault value
sizeWidth of stroke which is outlines around the banneru80
radiusBorder radius for corner roundingu80
[display.border]
size = 2
radius = 12
margin = { right = 15 }

Note: If the border size is larger than the radius, the inner corners won’t be rounded.

Text

The application has title and body properties, both treated as Text. A new text property can be used for both the title and body at once. Use text when the same value applies to both, or set them separately if needed.

The Text table:

KeyShort descriptionTypeDefault value
wrapSets possibility to line breaking when text overflows a linebooltrue
ellipsize_atEllipsizes a text if it's totally overflows an area. Possible values: "end" and "middle". "end" - put ellipsis at end of word, "middle" - cut word at some middle of word and puts ellipsisString"end"
styleFont style. Possible values: "regular", "bold", "italic", "bold italic"String"regular"
marginText spacing from edges of remaining areaSpacing0
justificationText justification. Possible values: "left", "right", "center", "space-between"String"left"
line_spacingGap between wrapped text lines in pxu80
font_sizeFont size in pxu812
[display.text]
justification = "left"
wrap = false
ellipsize_at = "middle"
font_size = 18

[display.title]
style = "bold"

Property priority:

  1. Use title or body first.
  2. If missing, use the text property.
  3. If still missing, default values apply.

Markup

Enables text styling through HTML tags.

For the body, the markup property is applied, allowing the use of HTML-like tags, such as:

  • <b> - bold style
  • <i> - italic style
  • <u> - underline style
  • <a href="https://google.com"> - a hyperlink
  • <img src="path/to/image" alt="image description"> - an image embedded in the text

You can disable the markup property by setting it to false.

markup = false

Timeout

The time in milliseconds after which the notification banner will automatically close due to expiration, starting from when it is created.

Additionally, there is an extended timeout configuration based on the urgency of the banners, defined in the Timeout table.

The Timeout table:

KeyShort descriptionType
defaultSet default timeout for all urgencyu16
lowOverride timeout value for 'low' urgency bannersu16
normalOverride timeout value for 'normal' urgency bannersu16
criticalOverride timeout value for 'critical' urgency bannersu16
timeout = 5000

# or

[display.timeout]
default = 5000
critical = 0

Note: The value 0 means will never expired.

Colors

Fill

To declare fill color use a string in format "#RGB", "#RRGGBB" or "#RRGGBBAA":

[theme.normal]
background = "#FFF"
foreground = "#A2A2A2"
border = "#BEBEBEAA"

Gradient

When defining gradients, use the same property as you would for a single color, but provide a table value instead. All gradient configurations share these common properties:

KeyTypeDefault valueShort description
modeString-Specifies the gradient type
degreeu16-Angle of the gradient in degrees
colors[Color; 2..]-An array of color values (in hex format)

Supported gradient types

Linear Gradient

Linear gradients create a smooth transition between two or more colors along a straight line.background. The transition direction is determined by the degree value, where 0° points upward and the angle increases clockwise.

[theme.normal]
background = { mode = "linear-gradient", degree = 30, colors = ["#F00", "#0F0", "#00F"] }]

Themes

Define custom color schemes for different notification urgency levels:

The Theme table:

KeyTypeDefault valueShort description
lowColors-The colors for low urgency banner
normalColors-The colors for normal urgency banner
criticalColors-The colors for critical urgency banner

The Colors table:

KeyTypeDefault valueShort description
backgroundColor"#FFFFFF"The background color
foregroundColor"#000000" (but for critical: "#FF0000")The foreground color
borderColor"#000000" (but for critical: "#FF0000")The border stroke color
[[theme]]
name = "pastel"

[theme.normal]
background = "#1e1e2e"
foreground = "#99AEB3"
border = "#000"

[theme.critical]
background = "#EBA0AC"
foreground = "#1E1E2E"
border = "#000"

App-Specific Configuration

The Noti application includes an app-specific configuration feature, allowing you to override the display table for a particular application.

Here’s how the properties are selected:

  • Check if the property is defined in the app-specific configuration.
  • If not, use the general display property.
  • If it’s still not defined in the general display property, use the default value.

The App table:

KeyShort descriptionType
nameThe name of applicationString
displayThe display configuration table for this applicationDisplay
[[app]]
name = "Telegram Desktop"

[app.display]
theme = "telegram-theme"
padding = 10
border = { size = 2, radius = 12 }

[[app]]
name = "Spotify"

[app.display]
image = { max_size = 64 }
timeout = 2000

Custom Layout

The Noti application allows users to customize the layout of notification banners, giving you complete control over how your notifications are displayed. Whether you want to move an image to the right, swap the title and body, or pin the title to the top, Noti makes it possible through custom layout files.

To customize a layout, you define it in a .noti file and reference it in your main configuration file.

Setting Up a Custom Layout

To enable a custom layout, specify its path in your configuration file:

display.layout = "path/to/your/layout/file.noti"

This can be set globally or for specific applications. Once specified, create the layout file and define your custom design.

WARNING: When using a custom layout:

  • Border, padding, and margin settings are overridden
  • Only general configurations, colors, and non-styling settings remain active

Types

The available property types are limited to a few:

  • Literal: A continuous sequence of characters not enclosed in double quotes, always treated as a string.
  • UInt: An unsigned integer.
  • Type value: Refer to the linked section for details.

The Noti application includes intelligent conversion for Literal types, allowing them to represent boolean, color, type, or other supported formats as needed.

Layout Basics

Layouts in Noti are built using widgets and type values:

Widgets

Widgets are the building blocks of layouts. They are graphic elements with customizable properties. The available widgets are:

  • FlexContainer: A container that organizes its child widgets.
  • Text: Displays text, such as the title or body.
  • Image: Displays an image.

Type Values

Type values are configuration options used to adjust widget behavior. Examples include:

  • Alignment: Controls horizontal and vertical alignment.
  • Spacing: Defines padding or margins.
  • Border: Adds a border around widgets.

Writing a Layout File

General Syntax

Each layout file must have one root widget, typically a FlexContainer. Widgets and type values are declared using a constructor-like syntax:

WidgetName(
    property_name = value,
    another_property = true,
    nested_property = TypeValueName(
        type_value_property = 5,
    )
)

You can include trailing commas for readability. If a widget or type value has default properties, they can be omitted:

Image()

Adding Child Widgets

To nest widgets, use the children property inside a FlexContainer:

FlexContainer() {
    Text(kind = title)
    Image()
}

Child widgets are enclosed in curly braces, with whitespace separating them.


Widget Properties

FlexContainer

The FlexContainer organizes its child widgets either horizontally or vertically.

PropertyDescriptionTypeDefault
directionLayout direction (horizontal or vertical).Literal-
max_widthMaximum width of the container.UIntMAX
max_heightMaximum height of the container.UIntMAX
borderBorder around the container.BorderDefault border values
spacingSpacing between child widgets.SpacingDefault spacing values
alignmentHorizontal and vertical alignment of content (start, center, end).Alignment-
transparent_backgroundMake the background transparent (true or false)Literalfalse

Text

The Text widget is used for titles and body text. Specify the kind of text to display.

PropertyDescriptionTypeDefault
kindType of text (title/summary or body).Literal-

Image

The Image widget displays an image. It uses properties defined in the main configuration.


Supporting Type Values

Spacing

Defines padding or margin offsets. Currently, individual values (e.g., top, right) must be set explicitly.

PropertyDescriptionTypeDefault
topOffset from the top.UInt0
rightOffset from the right.UInt0
bottomOffset from the bottom.UInt0
leftOffset from the left.UInt0

Alignment

Controls how content is aligned within a widget.

PropertyDescriptionTypeDefault
horizontalHorizontal alignment (start, end, center, space-between).Literal-
verticalVertical alignment (start, end, center, space-between).Literal-

Editor Support

Tree-sitter Highlighting

If your editor supports Tree-sitter, you can enable syntax highlighting for .noti files by adding our Tree-sitter grammar:

Tree-sitter Noti

Neovim Plugin

For Neovim users, we offer a plugin that integrates Tree-sitter to enable syntax highlighting with minimal effort.

To install, use your favorite plugin manager, such as lazy.nvim:

{
    "noti-rs/noti.nvim",
    dependencies = {
      "nvim-treesitter/nvim-treesitter",
    },
    build = "python3 clone_queries.py",
    opts = {},
}

After adding the plugin to your configuration, run:

:TSInstall noti

Tips and Tricks

Blurred Transparent Background

If you're using Hyprland, you can create a blurred transparent background with the following steps:

  1. Make the background transparent in your theme configuration:
[theme.normal]
background = "#ffffff00" # the last two symbols control opacity
  1. Add these layerrule settings somewhere to your Hyprland config:
layerrule = blur, noti
layerrule = blurpopups, noti
layerrule = xray, noti
layerrule = ignorealpha 0, noti

Troubleshooting

You can change the log level by setting the NOTI_LOG environment variable. Supported log levels are info, debug, and trace. For example, to enable detailed logging, use:

NOTI_LOG=debug noti run

This will help you check the logs for specific error messages.

Examples

Example configurations

jsonmaf1a's setup

jsonmaf1a's setup

Source

Example scripts

Volume Control

This script allows you to adjust the volume (increase, decrease, or mute) and displays notifications for each action.

Click to reveal

Requirements:

  • pamixer: To control the audio volume.

Script:

#!/bin/bash

VOL_UP=$ICONS/volup.svg
VOL_DOWN=$ICONS/voldown.svg
VOL_MUTE=$ICONS/volmute.svg

URGENCY=low
APP_NAME=volume

ID_TMPFILE=/tmp/vol-noti-id

if [[ ! -f $ID_TMPFILE ]]; then
  touch $ID_TMPFILE
fi

ID=$(<$ID_TMPFILE)

send_notification() {
    local icon=$1
    local message=$2
    local urgency=${3:-$URGENCY}

    if [[ $ID ]]; then
        noti send -a "$APP_NAME" -u "$urgency" -i "$icon" -r "$ID" "$message"
    else
        noti send -a "$APP_NAME" -u "$urgency" -i "$icon" "$message" --print-id > "$ID_TMPFILE"
    fi
}

update_volume() {
    local change=$1
    pamixer $change 5 -u
    send_notification "$VOL_UP" "Volume: $(pamixer --get-volume)%"
}

toggle_mute() {
    pamixer -t
    if $(pamixer --get-mute); then
        send_notification "$VOL_MUTE" "Volume muted" "critical"
    else
        send_notification "$VOL_MUTE" "Volume unmuted" "critical"
    fi
}

case $1 in
up)
    update_volume "-i"
    ;;
down)
    update_volume "-d"
    ;;
mute)
    toggle_mute
    ;;
*)
    echo "Usage: $0 { 'up' | 'down' | 'mute' }"
    exit 1
    ;;
esac

Note: Replace the $ICONS/volup.svg, $ICONS/voldown.svg, and $ICONS/volmute.svg with the paths to your own icons for volume up, down, and mute.

Usage

./volume.sh up     # Increase volume
./volume.sh down   # Decrease volume
./volume.sh mute   # Toggle mute

Bind these commands to keyboard shortcuts for easier access.

Brightness Control

This script allows you to adjust the screen brightness and displays notifications for each action.

Click to reveal

Requirements:

  • brightnessctl: To control the screen brightness.

Script:

#!/bin/bash

ID_TMPFILE=/tmp/brightness-noti-id

if [[ ! -f $ID_TMPFILE ]]; then
  touch $ID_TMPFILE
fi

ID=$(<$ID_TMPFILE)

send_notification() {
    local value=$1

    if [[ $ID ]]; then
        noti send -a "brightness" -u low -i $ICONS/brightness.svg -r $ID "Brightness: $value%"
    else
        noti send -a "brightness" -u low -i $ICONS/brightness.svg "Brightness: $value%" > $ID_TMPFILE
    fi
}

calculate_percentage() {
    local value=$1

	percentage=$(echo "scale=0; $brightness * 100 / 1515" | bc -l)
	rounded_percentage=$(echo "($percentage + 5) / 10 * 10" | bc)

    echo $rounded_percentage
}

case $1 in
up)
	brightnessctl s +10%
	brightness=$(brightnessctl g)
    send_notification $(calculate_percentage brightness)
	;;
down)
	brightnessctl s 10%-
	brightness=$(brightnessctl g)
    send_notification $(calculate_percentage brightness)
	;;
*)
    echo "Usage: $0 {up|down}"
    exit 1
    ;;
esac

Note:
Replace $ICONS/brightness.svg with the path to your own brightness icon.

Usage

./brightness.sh up   # Increase brightness
./brightness.sh down # Decrease brightness

Bind these commands to keyboard shortcuts for quicker access.

Keyboard Layout Switcher

This script switches between keyboard layouts and displays a notification showing the current layout.

Click to reveal

Requirements:

  • hyprctl: To manage devices and switch keyboard layouts.
  • kb-switcher(optional): See hyprland_kb_switcher

Script:


#!/bin/sh

ID_TMPFILE=/tmp/kb-noti-id

if [[ ! -f $ID_TMPFILE ]]; then
  touch $ID_TMPFILE
fi

ID=$(<$ID_TMPFILE)

KEYBOARD="your-keyboard-name" # use hyprctl devices to see all connected keyboards

# NOTE: kb is https://github.com/JarKz/hyprland_kb_switcher/
# but you can just use `hyprctl switchxkblayout $keyboard next`
kb switch

VALUE=$(hyprctl devices | grep -i $KEYBOARD -A 2 | tail -n1 | cut -f3,4 -d' ')

if [[ $ID ]]; then
    noti send -a "kb" -u low -i $ICONS/kb.svg -r $ID "$VALUE"
else
    noti send -a "kb" -u low -i $ICONS/kb.svg "$VALUE" --print-id > $ID_TMPFILE
fi

Note:
Replace keyboard with your actual keyboard device name and $ICONS/kb.svg with the path to your keyboard icon.

Developer Guide

Welcome to the Noti Developer Guide! This chapter provides essential information for developers who want to contribute to the Noti project or understand its architecture and codebase.

Project Structure

The Noti project is organized into several crates, each serving a specific purpose:

  • app: Contains the command-line interface and main application logic.
  • backend: Handles the core functionality and runs as a daemon.
  • client: Manages client-side operations and interactions.
  • dbus: Implements D-Bus communication using the zbus crate.
  • render: Provides rendering capabilities for notifications.
  • config: Manages configuration settings and parsing.
  • macros: Contains procedural macros to simplify code generation.
  • shared: Includes shared utilities and types used across the project.

Setting Up the Development Environment

To set up your development environment, follow these steps:

  1. Install Rust Toolchain: Ensure you have the latest Rust toolchain installed. You can install it using rustup.

  2. Clone the Repository:

    git clone https://github.com/noti-rs/noti.git
    cd noti
    
  3. Run the Project: Use Cargo to build the project:

    cargo run -- run
    

Coding Guidelines

  • Rust Edition: The project uses Rust 2021 edition. Ensure your code is compatible with this edition.
  • Formatting: Use rustfmt to format your code. The configuration is specified in rustfmt.toml.
  • Linting: Run clippy to catch common mistakes and improve code quality:
    cargo clippy
    

Contributing

We welcome contributions from the community! Here’s how you can contribute:

  1. Fork the Repository: Create a fork of the repository on GitHub.

  2. Create a Branch: Create a new branch for your feature or bug fix:

    git checkout -b feat/my-feature
    
  3. Make Changes: Implement your changes, ensuring you follow the coding guidelines.

  4. Commit and Push: Commit your changes with a descriptive message and push to your fork:

    git commit -am "feat: add new feature"
    git push origin feature/my-feature
    
  5. Open a Pull Request: Submit a pull request to the main repository. Provide a clear description of your changes and any relevant context.

Commit messages

Write clear and concise commit messages. Use the following format:

type(scope): subject

body (optional)

For example:

feat(config): add support for custom themes

This feature allows users to define their own themes in the configuration file.