English | Русский
Mostly written by AI. The QML, the installer, the docs and this README were produced with AI assistance and reviewed by a human. Treat it as a personal config that happens to be public, not as a supported product.
A complete Material 3 Expressive desktop shell for niri, built with Quickshell. One QML config replaces the bar, launcher, clipboard manager, notification daemon, quick settings, OSD, wallpaper manager and power menu.
The palette comes from the wallpaper through matugen and is written onto the rest of the desktop too: GTK3/GTK4 apps, Qt apps, ghostty and the niri border and focus ring.
The interface strings are currently Russian; the QML code and these docs are English (except for the Russian README).
- Bar — workspaces, window title, clock (timer/stopwatch) and date (weather), keyboard layout, volume, brightness, battery, notifications, the system tray and a power button. Three layouts: three floating islands, one floating plate or one plate flush to the screen walls.
- Launcher — applications, arithmetic and
Terminal=truedesktop entries, with a configurable terminal ($TERMINAL,alacrittyby default). - Clipboard history —
cliphist+wl-clipboard, with images and text. Ctrl+P opens the actions menu; pinning stores a decoded snapshot in the shell data directory, so pins survivecliphist wipe. - Notifications — Quickshell is the notification daemon; toast popups, history persisted across restarts, do-not-disturb, per-app mute and a notification center.
- Quick settings — volume, audio output switching, microphone mute, brightness, Wi-Fi, night light, do-not-disturb, MPRIS media, power profiles, UI language (RU/EN/DE), theme/style/colorscheme/font controls and the power menu.
- Lock screen — current wallpaper, clock card and a PAM password field on
ext-session-lock-v1; locks after 5 idle minutes and powers the monitors off after 7.5. - Wallpaper manager — recursive library, cached previews, favorites, random wallpaper, drag-and-drop from the desktop or the bar.
- Desktop pet — a draggable Tux that chats through NVIDIA NIM, answers time/date/weather/battery questions locally, sleeps when idle and reacts to notifications, low battery and mute.
- OSD and system popup — volume/brightness overlay (also for changes made
with
wpctl/brightnessctl), CPU, RAM, temperature and GPU rows (AMD via sysfs,nvidia-smifallback). - Seven style packs —
M3 Inspired,M3 Blur,New(graphite acrylic),Nothing(opaque graphite, hard corners),New Nothing(the same graphite with the acrylic dressing and blur),Old(retro gray/charcoal chrome with square corners and bevels) andCustom(your own palette and knobs: corner ladder, bar/chip radii, acrylic, shadow, outline and bevels). - Light and dark — a second matugen pass writes the light palette; the shell switches both its own colors and the desktop.
- Wayland with niri. Other compositors can run the QML, but workspaces,
window titles and the keyboard layout come from
niri msg. - Arch Linux for the installer (
pacman). The manual route works on other distributions with the same packages. - Quickshell 0.3.2 or newer (developed against 0.3.2 and niri 26.04).
| Weather | Calendar |
|---|---|
![]() |
![]() |
| Quick settings | Power menu |
|---|---|
![]() |
![]() |
| OSD | Keyboard layout |
|---|---|
![]() |
![]() |
| Launcher | Clipboard history |
|---|---|
![]() |
![]() |
| Settings menu | Wallpaper manager |
|---|---|
![]() |
![]() |
git clone https://github.com/cactozs/quickshell.git ~/.config/quickshell
cd ~/.config/quickshell
./install.shThe installer is deliberately conservative:
| step | what it does | skip it with |
|---|---|---|
| packages | installs the missing Arch packages with pacman -S --needed |
--no-packages |
| fonts | downloads Google Sans to ~/.local/share/fonts/ if it is missing; the Old font comes from gsfonts |
--no-fonts |
| matugen | writes ~/.config/matugen/config.toml, light.toml and the template with absolute paths to this checkout |
— |
| shim | installs /usr/local/bin/awww, backing up an existing file |
--no-shim |
| directories | creates ~/Pictures/Wallpapers and the config folders the theme writer uses |
— |
| niri | adds the quickshell.kdl include, autostart entries and QT_QPA_PLATFORMTHEME, with backups |
--no-apply |
| ghostty | adds theme = Quickshell if there is no theme yet |
--no-apply |
| qt6ct | adds color_scheme_path if it is not set yet |
--no-apply |
| keybinds | prints the binds; it never edits keybinds.kdl |
always |
Existing files are copied to file.bak.YYYYmmdd-HHMMSS.PID before an edit.
Running the installer twice is safe: it only writes when the content differs.
If you cloned somewhere other than ~/.config/quickshell, either run
./install.sh --link (creates the symlink when the path is free) or start the
shell with quickshell -p /path/to/quickshell. The matugen output paths are
always rewritten to the absolute checkout path, so --link is not required for
recoloring; it is only what makes plain quickshell and qs ipc find the config.
Check the environment at any time:
./install.sh --check # writes nothing
qs ipc show # live IPC targets of a running shell
qs log # stderr of the running shellUse this if you want to see every change (or are not on Arch).
git clone https://github.com/cactozs/quickshell.git ~/.config/quickshellQuickshell treats ~/.config/quickshell as its default config directory.
If you clone elsewhere, symlink it:
ln -s /path/to/quickshell ~/.config/quickshellor run quickshell -p /path/to/quickshell and pass toggleOn as shown in
keybinds.
sudo pacman -S --needed \
quickshell niri matugen awww \
fd ripgrep jq curl cliphist wl-clipboard brightnessctl \
pipewire wireplumber upower power-profiles-daemon networkmanager \
ttf-material-symbols-variable ttf-nerd-fonts-symbols ttf-jetbrains-mono-nerd \
alacritty gtk3 gtk4 qt6ct ghostty \
imagemagick wlsunset playerctlOptional:
sudo pacman -S papirus-icon-theme # app icons; hicolor comes with quickshell
sudo pacman -S nvidia-utils # GPU rows on NVIDIA; AMD needs no extra packageimagemagick only builds the 720 px wallpaper previews. Without it the manager
loads the original files, which is slow for 4K images but still works.
Material Symbols and Symbols Nerd Font come from pacman:
sudo pacman -S ttf-material-symbols-variable ttf-nerd-fonts-symbols ttf-jetbrains-mono-nerdThe default UI font, Google Sans, has no Arch package:
mkdir -p ~/.local/share/fonts
curl -L -o ~/.local/share/fonts/GoogleSans.ttf \
"https://raw.githubusercontent.com/google/fonts/main/ofl/googlesans/GoogleSans%5BGRAD%2Copsz%2Cwght%5D.ttf"
fc-cache -f
fc-match "Google Sans" # must print Google Sans, not a fallbackThe Monospace option in the settings menu is JetBrainsMono Nerd Font
Mono, installed by ttf-jetbrains-mono-nerd. If it is missing, monospace mode
falls back to the default sans.
The Old option is Nimbus Mono PS from gsfonts — a Courier-style
monospace with Cyrillic, installed by the packages step:
sudo pacman -S gsfonts
fc-match "Nimbus Mono PS" # must print Nimbus Mono PS, not a fallbackThe shell reads its colors from style/Colors.json (dark) and
style/ColorsLight.json (light). matugen writes them. The configs live in the
repository under install/matugen/:
mkdir -p ~/.config/matugen
cp -r install/matugen/. ~/.config/matugen/Then edit ~/.config/matugen/config.toml:
[config]
version_check = false
source_color_index = 0
fallback_color = "#6750A4"
[config.wallpaper]
set = true
command = "/usr/bin/awww img --transition-type center {{ image }}"
[templates.quickshell]
input_path = "/home/you/.config/matugen/templates/quickshell.json"
output_path = "/home/you/.config/quickshell/style/Colors.json"
type = "SchemeContent"light.toml is the same template with ColorsLight.json as the output and
no [config.wallpaper]: regenerating light colors must not re-apply the
wallpaper. matugen takes the mode from the command line only, so the light pass
is a separate matugen -m light invocation.
Generate the first palette from any image:
matugen image ~/Pictures/Wallpapers/your-wallpaper.pngThe absolute command path matters. The shim in /usr/local/bin/awww calls
matugen image, and matugen's wallpaper command calls /usr/bin/awww; a
relative awww there would loop.
awww img <file> must go through matugen, or the colors never follow the
wallpaper. /usr/local/bin is ahead of /usr/bin in the normal PATH:
sudo install -Dm755 install/awww /usr/local/bin/awww
command -v awww # must print /usr/local/bin/awwwThe shim intercepts only plain awww img <existing-file>. query, restore,
pause, extra flags and color values go straight to /usr/bin/awww.
Create the generated file before including it, otherwise niri refuses to start:
touch ~/.config/niri/quickshell.kdl~/.config/niri/config.kdl — the include must come after layout.kdl, because
niri merges includes positionally and the generated border colors have to win:
include "layout.kdl"
include "quickshell.kdl"~/.config/niri/autostart.kdl:
spawn-at-startup "quickshell"
spawn-at-startup "awww-daemon"
spawn-at-startup "sh" "-c" "wl-paste --type text --watch cliphist store"
spawn-at-startup "sh" "-c" "wl-paste --type image --watch cliphist store"Idle locking, monitor power-off and locking before suspend all live in the shell itself.
If you already launch Quickshell with environment variables, keep that line instead of adding a second one. For example:
spawn-at-startup "sh" "-c" "env QT_QUICK_BACKEND=software quickshell"~/.config/niri/environment.kdl:
environment {
QT_QPA_PLATFORMTHEME "qt6ct"
}~/.config/ghostty/config:
theme = Quickshell~/.config/qt6ct/qt6ct.conf (create it if qt6ct has no config yet):
[Appearance]
color_scheme_path=/home/you/.config/qt6ct/colors/matugen.conf
custom_palette=trueThe shell writes the file that path points at on its first run. Qt applications
read the palette at startup, so restart them after a theme change. The shell
reloads ghostty with SIGUSR2.
The other generated files are inert until the app is pointed at them once:
| generated file | one-time line |
|---|---|
~/.config/foot/quickshell.ini |
include=~/.config/foot/quickshell.ini in foot.ini |
~/.config/alacritty/quickshell.toml |
import = ["~/.config/alacritty/quickshell.toml"] in alacritty.toml |
~/.config/kitty/quickshell.conf |
include ~/.config/kitty/quickshell.conf in kitty.conf |
~/.config/nvim/colors/quickshell.lua |
colorscheme quickshell |
~/.config/helix/themes/quickshell.toml |
theme = "quickshell" in config.toml |
~/.config/zed/themes/quickshell.json |
pick "Quickshell" in Zed's theme selector |
~/.config/bash/quickshell.sh |
source ~/.config/bash/quickshell.sh in ~/.bashrc |
~/.config/fish/conf.d/quickshell.fish |
nothing: conf.d is sourced automatically |
~/.config/bat/themes/quickshell.tmTheme |
--theme="quickshell" in ~/.config/bat/config, or BAT_THEME=quickshell |
foot and kitty reload on SIGUSR1, Alacritty on save. Neovim picks the
colorscheme up on :colorscheme quickshell, the rest on their next start.
The power menu's Lock entry runs qs ipc call lock lock; bind the same
command to a key. The built-in lock screen shows the current wallpaper behind a
dim scrim, with a card holding the clock, date, user and a password field. It
takes a real ext-session-lock-v1 lock and checks the password against PAM's
system login stack, so there is no extra locker package and no /etc/pam.d
change.
The shell locks itself after 5 idle minutes, powers the monitors off after 7.5
(any input wakes them) and locks before suspend through logind's
PrepareForSleep, holding a delay inhibitor until the lock is up. No swayidle
is involved. There is deliberately no IPC to unlock — only the password ends
the lock.
mkdir -p ~/Pictures/WallpapersPut images there. Dropping one on the bare desktop or the bar applies it too.
Local edit points:
| what | where | default |
|---|---|---|
| wallpaper folder | services/Wallpapers.qml → rootDir |
~/Pictures/Wallpapers |
| weather coordinates | services/Weather.qml → lat, lon |
Moscow (55.75, 37.62) |
| terminal for desktop entries | $TERMINAL, then modules/Launcher.qml |
alacritty |
$TERMINAL is split on spaces, so TERMINAL="ghostty --class=foo" works.
quickshellRun it in the foreground first. There is no build and no test suite: QML errors
appear on stderr, and the shell reloads itself when a file changes. GitHub
Actions runs qmllint, bash -n install.sh and python3 scripts/check-qml.py
on pushes and pull requests.
Once it works, keep the niri autostart line and stop the manual instance.
Put these in ~/.config/niri/keybinds.kdl. They are qs ipc calls, so the
shell must be running. toggleOn opens the panel on the focused monitor; it
needs niri and jq.
Mod+A repeat=false { spawn-sh "qs ipc call launcher toggleOn $(niri msg --json focused-output | jq -r .name)"; }
Mod+V repeat=false { spawn-sh "qs ipc call clipboard toggleOn $(niri msg --json focused-output | jq -r .name)"; }
Mod+N repeat=false { spawn-sh "qs ipc call center toggleOn $(niri msg --json focused-output | jq -r .name)"; }
Mod+Ctrl+W repeat=false { spawn-sh "qs ipc call wallpaper toggleOn $(niri msg --json focused-output | jq -r .name)"; }
Mod+Shift+N repeat=false { spawn "qs" "ipc" "call" "notifs" "toggleDnd"; }
Mod+P repeat=false { spawn "qs" "ipc" "call" "bar" "toggle"; }
Mod+Alt+L repeat=false { spawn "qs" "ipc" "call" "lock" "lock"; }Without niri/jq, replace toggleOn $(…) with toggle; the panel then opens on
the first screen. Esc and a click outside close an open panel.
Volume, brightness and media binds do not go through the shell: use wpctl,
brightnessctl and playerctl directly. Lock does: qs ipc call lock lock.
qs ipc show prints the live list. The overlay targets share toggle,
toggleOn <output> and close; bar and notifs are separate:
| target | extra functions |
|---|---|
launcher |
— |
clipboard |
— |
center (notifications) |
— |
tray |
— |
weather |
— |
clock (timer and stopwatch) |
— |
system (monitoring) |
— |
settings (quick settings) |
launcherView <bool>, style <m3|blur|new|nothing|newNothing|old>, panel <islands|island|panel>, panelwidth <full|fit>, colorscheme <matugen|oled|gruvbox>, theme <dark|light>, view <full|minimal>, font <material|mono|old>, animations <normal|slow|off>, nirianimations <normal|slow|off>, windowsradius <0-24>, blur <wallpaper|window>, ambient <off|xmas>, language <ru|en|de> |
wallpaper (manager) |
random |
pet (desktop pet) |
— |
lock (lock screen) |
lock |
bar |
toggle |
notifs |
toggleDnd, dnd, clear, mute <app>, unmuteAll |
<output> is a monitor name ("eDP-1", see niri msg outputs), not a number.
Examples:
qs ipc call launcher toggle
qs ipc call settings theme light
qs ipc call settings colorscheme gruvbox
qs ipc call settings style new
qs ipc call wallpaper random
qs ipc call notifs toggleDnd
qs ipc call lock lockThe shell owns the palette, and services/SystemTheme.qml is the single writer:
~/.config/gtk-3.0/gtk.cssand~/.config/gtk-4.0/gtk.css~/.config/ghostty/themes/Quickshell(reloaded withpkill -USR2 -x ghostty)~/.config/qt6ct/colors/matugen.conf~/.config/foot/quickshell.ini,~/.config/alacritty/quickshell.tomland~/.config/kitty/quickshell.conf(foot and kitty reload onSIGUSR1)~/.config/nvim/colors/quickshell.lua,~/.config/helix/themes/quickshell.tomland~/.config/zed/themes/quickshell.json~/.config/bash/quickshell.shand~/.config/fish/conf.d/quickshell.fish~/.config/bat/themes/quickshell.tmTheme(the writer also runsbat cache --build)~/.config/niri/quickshell.kdl— niri border and focus-ring colors, plus thelayer-rulethat selects the blur source and theanimationsswitchgsettings set org.gnome.desktop.interface color-scheme …
A file is written only when its content changes. Hand edits survive until the next theme change, then they are regenerated.
Which of these targets are written at all is the "Applications" tab in the
settings menu (or qs ipc call settings app ghostty false). A disabled target
is left alone: its file keeps whatever colors it last got.
Light mode is a second matugen pass. The shell runs
matugen -m light image <wallpaper> -c ~/.config/matugen/light.toml when the
dark palette is newer than ColorsLight.json. The two configs must keep the
same source-color settings; light.toml intentionally has no wallpaper command.
Blur is niri policy. «Обои» (the default) is niri's xray blur: the wallpaper
alone. «Окна» writes xray false into the generated layer-rule, so windows
under the acrylic plates blur too. The choice is read back from
~/.config/niri/quickshell.kdl at shell start, so it survives a reboot.
Animations are two settings. «Анимации» is the shell's own motion
(qs ipc call settings animations <normal|slow|off>): Slow stretches every
duration by 1.6, Off zeroes them (the hardcoded ones go through Theme.dur()),
and the seasonal ambient dressing keeps its own. «Анимации niri» is the
compositor's (qs ipc call settings nirianimations <normal|slow|off>): Normal
writes nothing, so niri keeps the animations block from your own
animations.kdl; Slow writes slowdown 1.0, Off writes animations { off }.
Window rounding (qs ipc call settings windowsradius <0-24>) is a niri
window rule written into that same generated file. Untouched, it writes nothing,
so your own window-rules.kdl keeps its radius; the first slide takes over and
clips the windows to the rounded geometry too.
Style packs are switched in the settings menu or with
qs ipc call settings style …. M3 Inspired is the matugen palette with the
M3 shape scale, M3 Blur adds acrylic plates and compositor blur, New is
graphite acrylic, Nothing is opaque graphite with hard corners, and
New Nothing is that graphite and those corners with the New acrylic dressing —
rim, specular, drop shadow and blur. Old is the retro pack: gray/charcoal
plates with light and dark twins, square corners, a black hairline and
raised/sunken bevels, no blur, shadow or motion. The accent roles come from the
current matugen palette and the plates take a faint tint of its primary, so a
wallpaper change recolors the selection and buttons while the chrome stays gray.
It keeps the current font; pick Old for the Courier look.
Custom is the hand-built pack: the Custom settings tab edits one M3
palette for both light and dark (click a role to open an HSV picker; Готово
commits, Сброс restores that role) and the knobs for the corner ladder
(Скругление), bar and chip radii, acrylic and its opacity, shadow strength,
blur and offset, the outline and the bevels. The palette's surface luminance
decides whether the light terminal/desktop derivations apply. Everything is
stored sparsely in shell-state.json under custom; Сброс → Всё clears it.
The Тема and Цвета rows are hidden while Custom is active because the pack
owns both.
| what | file / key |
|---|---|
| overlays and screens | shell.qml (Variants per Quickshell.screens) |
| wallpaper folder | services/Wallpapers.qml → rootDir |
| weather location | services/Weather.qml → lat, lon |
| terminal | $TERMINAL; fallback in modules/Launcher.qml |
| fonts | style/Theme.qml → fontFamily, iconFont, nerdFont |
| UI translations | services/I18n.qml |
| style, palette, bar layout (islands/island/panel), bar position (top/left/bottom/right), light/dark, font mode, animations (shell and niri), language | settings menu, or qs ipc call settings … |
| Custom style palette and knobs | Custom tab in the settings menu; stored under custom in shell-state.json |
| matugen dark/light configs | ~/.config/matugen/config.toml, light.toml |
| generated niri colors | ~/.config/niri/quickshell.kdl |
| desktop theme writer | services/SystemTheme.qml |
| system stats | services/SystemMonitor.qml |
| notifications | services/Notifications.qml |
| pinned clipboard entries | services/ClipboardPins.qml (shell data directory) |
| IPC handlers | services/ShellState.qml |
launcher-usage.json, notifications.json, wallpaper-favorites.json,
shell-state.json and .qmlls.ini are local runtime state and are gitignored.
style/Colors.json and style/ColorsLight.json are generated palettes, but
they are tracked so a fresh clone has a usable fallback until matugen runs.
The paw button in the bar shows Tux on its screen (qs ipc call pet toggle
from a keybind). Drag him anywhere; the spot is saved per shell. Click to open
the chat, double-click to pet him, and he sleeps after five idle minutes. The
window hides itself while a fullscreen window is focused.
Answers come from NVIDIA NIM (integrate.api.nvidia.com), or from the shell
itself for time, date, weather and battery. The key is read from
NVIDIA_API_KEY in the shell's environment; there is no key field in the
settings menu:
set -Ux NVIDIA_API_KEY nvapi-...The shell inherits the variable when it starts, so restart it after exporting.
The pet remembers the last few messages and can use three tools:
run_diagnosticruns one read-only command: kernel, free space, disks, memory, network addresses, failed systemd units, the last lines ofqs log, or a listing of a folder inside your home. Hidden folders and anything outside$HOMEare refused; there is nocat,findor shell.grep_shellsearches this repository and answers withfile:line, so questions about the config are checked against the real files.shell_actionopens a panel, switches the theme, changes the volume, mutes the sound or takes a screenshot. It only runs when you ask for it.
Every question, every command output and every listed file name goes to NVIDIA. Without a key the pet still answers the local questions.
| symptom | cause | fix |
|---|---|---|
| text is the default sans | Google Sans is missing | install it (step 3), then fc-cache -f |
| icons are empty boxes | Material Symbols is missing | sudo pacman -S ttf-material-symbols-variable |
| the bar stays purple | matugen never ran, or output_path points elsewhere |
fix ~/.config/matugen/config.toml, run matugen image <file> |
| wallpaper changes, colors do not | the awww shim is missing or shadowed |
command -v awww must print /usr/local/bin/awww; reinstall the shim |
awww img does nothing |
awww-daemon is not running |
add spawn-at-startup "awww-daemon" |
| a panel opens on the wrong monitor | the bind uses toggle |
use toggleOn $(niri msg --json focused-output | jq -r .name) |
| blur does nothing | missing niri include, or the style paints no acrylic | step 6; try M3 Blur and «Окна» |
| Qt apps keep old colors | Qt reads the palette at startup | restart them |
| light mode does not update | light.toml missing or ColorsLight.json stale |
rerun ./install.sh, then change the wallpaper |
qs ipc cannot connect |
shell is not running, or the checkout is elsewhere | qs ipc show; run ./install.sh --link or quickshell -p <path> |
| the shell will not start | QML error | run quickshell in a terminal and read stderr; qs log for a running instance |
If you edit QML, keep a terminal with quickshell open. A syntax error leaves
the previous configuration on screen and prints the file and line in the log.
cd ~/.config/quickshell
git pull --ff-only
./install.shThe installer rewrites the matugen paths if the checkout moved, and writes a
config file only when its content differs or a required entry is missing. If
you use --no-apply, compare install/matugen/ with ~/.config/matugen/ and
the snippets in this README by hand.
Stop the shell and remove the autostart line first:
pkill -x quickshellThen remove what you no longer want:
sudo rm /usr/local/bin/awww # restore awww.bak.* if one exists
rm -rf ~/.config/quickshell # the checkout
rm -rf ~/.config/matugen # matugen configs
rm -f ~/.config/niri/quickshell.kdl
rm -f ~/.config/ghostty/themes/Quickshell
rm -f ~/.config/qt6ct/colors/matugen.conf
rm -f ~/.config/foot/quickshell.ini ~/.config/alacritty/quickshell.toml ~/.config/kitty/quickshell.conf
rm -f ~/.config/nvim/colors/quickshell.lua ~/.config/helix/themes/quickshell.toml ~/.config/zed/themes/quickshell.json
rm -f ~/.config/bash/quickshell.sh ~/.config/fish/conf.d/quickshell.fish
rm -f ~/.config/bat/themes/quickshell.tmTheme
rm -f ~/.config/gtk-3.0/gtk.css ~/.config/gtk-4.0/gtk.cssRemove include "quickshell.kdl" and the autostart lines from niri by hand. Backups made by the installer are next to the original files as
*.bak.YYYYmmdd-HHMMSS.PID. The wallpaper library and installed packages are
left alone.
- Quickshell — the shell toolkit and
qsIPC. - niri — the compositor this is built around.
- matugen — Material You palettes from images.
- awww — wallpaper daemon.
- Google Sans, Nimbus Mono PS and Material Symbols — UI fonts.
- Material 3 Expressive for the shape, color and motion language.
- The QML, installer and documentation were written with AI assistance and reviewed by cactozs.
MIT. See LICENSE.










