Warning
Experimental, vibe coded software. Works on my machine, but I'm no Rust/Wayland dev.
A desktop shell for cyberpunks. Wayland-native, built for Niri, runs on any Linux distro.
Philosophy:
- streamlined - all you need, nothing you don't
- ergonomic - first-class keyboard support
- isomorphic - GUI, CLI & keybinding parity
- focused - stays out of your way
- turnkey - doesn't take weeks to config
| Module | Description | Dependencies |
|---|---|---|
| calendar | Clock, weekly/monthly/yearly calendar navigation | — |
| window | Active window title | — |
| workspaces | Workspace indicators (hexagon toasts on switch) | — |
| system | CPU, memory, temperature, uptime | — |
| storage | Disk usage, alert when >80% full | — |
| notifications | Notification count, dismiss, clear | libnotify |
| outputs | Speaker volume & mute, device switching | wireplumber |
| inputs | Microphone volume & mute, denoise toggle | wireplumber, rnnoise |
| network | WiFi SSID/signal/IP, connect, scan | NetworkManager |
| bluetooth | Paired devices, connect/disconnect, scan | bluez |
| brightness | Screen brightness control | brightnessctl |
| media | MPRIS now playing, play/pause/skip | playerctl |
| session | Battery, suspend, shutdown, reboot, logout | upower |
| profiles | Power profiles (saver/balanced/performance) | power-profiles-daemon |
| weather | Temperature, humidity, conditions from wttr.in | curl |
| screenshot | Region or full-screen screenshot to clipboard | grim, slurp, wl-clipboard |
| recording | Screen recording with audio options | slurp, wl-screenrec |
| wallpaper | Wallpaper cycling via swww | swww |
| keyboard | Layout indicator, cycle layouts | swaymsg |
| clipboard | Clipboard history, paste, clear | wl-clipboard, cliphist |
| mounts | Removable device status | udisks2 |
- monitor management
- emoji module
- lockscreen
- greeter
Vision: all you need to work is Niri + Cyberdeck.
- Niri compositor (hardcoded IPC for window/workspace/app-launch)
- Linux with systemd
curl -fsSL https://raw.githubusercontent.com/ixxie/cyberdeck/main/install.sh | shThis installs the binary, Phosphor icons, a systemd service, and a default config. Then:
deck init # create ~/.config/cyberdeck/config.toml (if not already present)
# edit config.toml to enable modules, install their deps
systemctl --user enable --now cyberdeckAdd cyberdeck to your flake inputs:
# flake.nix
{
inputs.cyberdeck.url = "github:ixxie/cyberdeck";
}Import the NixOS module and enable modules:
# configuration.nix
{ inputs, ... }: {
imports = [ inputs.cyberdeck.nixosModules.default ];
services.cyberdeck = {
enable = true;
settings = {
font = "MonaspiceKr Nerd Font";
layout = "pills";
gap = 8;
};
mods = {
calendar.enable = true;
workspaces.enable = true;
window.enable = true;
network.enable = true;
outputs.enable = true;
inputs.enable = true;
bluetooth.enable = true;
brightness.enable = true;
session.enable = true;
system.enable = true;
notifications.enable = true;
media.enable = true;
weather = {
enable = true;
location = "London";
};
storage.enable = true;
screenshot.enable = true;
recording.enable = true;
wallpaper = {
enable = true;
dir = "~/Pictures/wallpapers";
};
};
};
}Rebuild your system and the bar will start automatically.
Add a toggle keybinding in your compositor config. For niri:
"Mod+Space".action.spawn = ["cyberdeck" "launcher"];For TOML config (non-NixOS), run deck init to generate a commented template at ~/.config/cyberdeck/config.toml.
[settings]
position = "top" # top or bottom
font = "monospace" # any monospace font
font-size = 14
layout = "pills" # classic, floating, pills, or transparent
gap = 6 # spacing between elements (px)
scale = 1.0 # global UI scale
icon-weight = "light" # regular, bold, thin, light, fill, or duotone
[settings.theme]
color = "#222222"
opacity = 0.8
radius = 6
padding = 6
[settings.theme.track] # bar background overrides (omit for no track)
# opacity = 1.0
[settings.theme.pill] # pill overrides
# radius = 8
[settings.monitors.DP-1]
scale = 0.8| Layout | Description |
|---|---|
| classic | Solid bar attached to screen edge, no margin |
| floating | Solid bar with margin, rounded corners |
| pills | Transparent bar, solid floating pills (default) |
| transparent | Everything transparent |
The gap parameter controls all visual spacing: margin, track padding, and inter-pill gaps.
Each module can define:
icon— module icon shown in the breadcrumb when focusedbadges— named map of badges shown on the root bar (see below)widget— content shown when the module is focusedhooks— trigger toasts or badge overrides on state changeskey-hints— keyboard shortcuts shown on the right when focusedtype— set to"calendar","bluetooth","wallpaper", or"actions"for interactive modules with rich keyboard navigation
Badges are the icons/text shown on the root bar for each module. Each module can define multiple named badges:
badges = {
# Always-visible badge (no condition)
battery = {
template = "{{ battery_pct }}%";
};
# Alert badge (only shown when condition is true)
low-battery = {
template = "{{ \"battery-warning\" | icon }} LOW";
condition = "{{ battery_pct < 20 }}";
highlight = "{{ \"battery-warning\" | icon }} {{ battery_pct }}%";
};
};- Badge — always visible on the root bar (no
condition) - Alert badge — only appears when its
conditiontemplate evaluates truthy
Each badge has:
template— Tera template for the badge contentcondition(optional) — if present, badge only shows when this renders truthyhighlight(optional) — alternative template used when a hook forces the badge visibleicon-scale(optional) — scale multiplier for icons within this badge
Hooks trigger actions when module state changes:
hooks = [
{
condition = "{{ changed(key=\"muted\") and muted }}";
action = "show-badge:muted"; # force-show a specific badge
timeout = 3;
}
{
condition = "{{ changed(key=\"volume\") }}";
action = "toast"; # show a transient notification
timeout = 3;
}
];Hook actions:
"toast"— show a transient message on the left of the bar"show-badge:<badge-name>"— force-show a specific badge temporarily (overrides condition)- Any other string — executed as a shell command
The bar exposes a Unix socket at $XDG_RUNTIME_DIR/cyberdeck.sock. Commands:
cyberdeck launcher # toggle the launcher
cyberdeck <module> <action> # run a module action
cyberdeck <module> open # navigate to a module
cyberdeck state # get current navigation state
cyberdeck dismiss # reset to rootUse cyberdeck <module> <action> in compositor keybindings for volume/brightness control with instant feedback.
src/
main.rs — entry point, CLI parsing, event loop
bar.rs — Wayland integration, BarApp struct, delegates
nav.rs — navigation state machine, input handling
view.rs — layout composition, text search
config.rs — configuration schema (JSON deserialization)
source.rs — data source management (poll, subscribe, file, native)
template.rs — Tera template engine, filters, functions
layout.rs — flex layout engine, hit area tracking
render.rs — text rendering (cosmic-text), icon compositing
icons.rs — SVG icon loading (Phosphor icons)
ipc.rs — Unix socket IPC protocol
color.rs — RGBA color type
mods/
mod.rs — InteractiveModule trait, native source registry
calendar.rs — clock + calendar navigation
launcher.rs — desktop application scanner
wallpaper.rs — wallpaper management via swww
... — audio, bluetooth, system, etc.
- Module (
ModuleDef) — a data source + display configuration. Defined inbar.modules. - Badge (
BadgeDef) — a small status element on the root bar. Each module can have multiple named badges. - Widget (
WidgetDef) — expanded content shown when a module is focused. - InteractiveModule — trait for modules with rich keyboard-driven navigation (calendar, bluetooth, wallpaper, actions).
- Source — how a module gets data:
poll(periodic command),subscribe(streaming),file(JSON files), ornative(Rust implementation). - Toast — transient notification shown on the left of the bar.
- Sources poll/stream data in background threads
- JSON updates sent to main thread via calloop channels
process_hooks()evaluates hook conditions on state changesmaybe_redraw()renders all bar instances via the template engine- Tera templates produce text + icon sequences
- Flex layout positions three groups (left, center, right)
- Renderer shapes text (cosmic-text) and composites icons (tiny-skia)
- Output copied to Wayland shared memory buffer
# NixOS
nix build # build the package
nix develop # dev shell with cargo, rust-analyzer
# Other distros (needs wayland, xkbcommon, fontconfig, freetype dev headers)
cargo build --releaseMIT
