Compare commits

...

2 Commits

Author SHA1 Message Date
Amir Alexander Abdelbaki bd4a935d51 docs: add Niri documentation and update DE references
- docs/md/niri.md: full reference for the Niri DE — overview table,
  config file map, Niri vs Hyprland comparison, complete keybindings
  reference, EWW bar, wallpaper/lock/idle, screen rotation, installer
  instructions
- docs/md/index.md: updated tagline (Hyprland → Wayland), added Niri
  to the doc index table, updated repo layout tree
- docs/md/installation.md: Niri added to DE list; answerfile example
  updated to hyprlua
- docs/md/modules.md: hyprlua and niri added to DE table with links;
  hyprlua marked as recommended
- docs/md/hyprland.md: cross-reference to Niri docs added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 16:40:37 +02:00
Amir Alexander Abdelbaki 7a40e9baec feat(niri): add Niri as a full desktop environment option
Adds Niri (scrollable-tiling Wayland compositor) as a first-class DE
alongside HyprLua, Hyprland, and Sway.

Config (desktopenvs/niri/):
- niri/config.kdl: full KDL config — input (de/caps:swapescape/
  left-handed), CyberQueer focus-ring colours, spring animations,
  environment vars, autostart, window rules, 100+ keybindings
  translated from binds.lua
- Shared configs (kitty, dunst, wofi, btop, walker, vicinae, etc.)
  symlinked from hyprlua to stay in sync
- EWW bar: three variants (PC/notebook/touch) with niri-native workspace
  widget driven by a niri msg event-stream listener script
- greetd-tuigreet: launches niri instead of Hyprland

Scripts adapted for niri IPC / niri toolchain:
- ewwstart.sh / togglebar.sh: niri msg outputs instead of hyprctl
- monitorhandler.sh: swaybg replaces hyprpaper
- caffeine.sh: swayidle replaces hypridle
- activewindow: niri msg -j focused-window
- unified-rotate.sh: niri msg action set-output-transform
- wallpaper-picker: swaybg-based rewrite
- windowswitcher: niri msg -j windows + wofi
- workspace: event-stream listener for eww literal widget
- drawer.sh / menu.sh: swaylock + niri msg action quit for power actions
- niri-toggle-touchpad.sh: xinput fallback (no niri IPC for touchpad)
- toggle-layout.sh: stub (niri has one scrollable layout)

Installer & integration:
- setup/modules/Desktop-Environments/niri.sh: full installer with
  niri-appropriate packages (swaybg, swaylock, swayidle, xfce-polkit,
  gammastep, nwg-drawer, xdg-desktop-portal-gnome; no nwg-dock-hyprland
  as niri doesn't support wlr-foreign-toplevel-management)
- setup/tui-install.sh: niri added to DE selection dialog
- apply-theme.sh: niri/config.kdl added to USER_FILES for colour theming

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 16:38:47 +02:00
80 changed files with 1584 additions and 10 deletions

View File

@ -55,6 +55,7 @@ USER_FILES=(
"qt-themes/cyberqueer/cyberqueer.conf|$HOME/.config/qt6ct/colors/cyberqueer.conf"
"qt-themes/cyberqueer/style-plugin/CyberQueerStyle.cpp|$DOTFILES/qt-themes/cyberqueer/style-plugin/CyberQueerStyle.cpp"
"qt-themes/cyberqueer/hyprpolkitagent-qt.conf|$HOME/.config/systemd/user/hyprpolkitagent.service.d/qt-theme.conf"
"desktopenvs/niri/niri/config.kdl|$HOME/.config/niri/config.kdl"
)
# System-owned paths — sed is run via sudo

1
desktopenvs/niri/CRT Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/CRT

1
desktopenvs/niri/Vencord Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/Vencord

1
desktopenvs/niri/alacritty Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/alacritty

1
desktopenvs/niri/btop Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/btop

View File

@ -0,0 +1 @@
../../hyprlua/config-updater/update-configs.sh

View File

@ -0,0 +1,37 @@
# Config updater — defines which dotfiles configs are deployed to ~/.config
#
# SOURCE_BASE base directory containing all configs in the dotfiles
#
# config <name> copy SOURCE_BASE/<name> to ~/.config/<name>
# flat <name> copy contents of SOURCE_BASE/<name> directly into ~/.config/
# ignore <name> present in source but intentionally not managed here
SOURCE_BASE = ~/Dotfiles/desktopenvs/niri
# ── deployed as ~/.config/<name> ─────────────────────────────────────────────
config alacritty
config btop
config dunst
config gtk-3.0
config kitty
config mimeapps.list
config niri
config nwg-drawer
config nwg-panel
config scripts
config ulauncher
config vicinae
config walker
config waybar
config wofi
config xfce4
# ── intentionally not managed here ───────────────────────────────────────────
ignore config-updater # the updater itself
ignore CRT # referenced from dotfiles path directly
ignore eww # eww bar variant selected and installed separately
ignore eww-nobattery
ignore eww-touch
ignore greetd-tuigreet # deployed to /etc/greetd/ at install time
ignore spicetify # managed separately
ignore Vencord # managed separately

1
desktopenvs/niri/dunst Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/dunst

View File

@ -0,0 +1 @@
../../hyprlua/eww/eww.scss

View File

@ -0,0 +1,92 @@
(defwindow bar [monitor]
:monitor monitor
:class "ewwbar"
:windowtype "dock"
:geometry (geometry :x "0%"
:y "1%"
:width "99%"
:height "20px"
:anchor "top center")
:exclusive true
(bar :monitor_ monitor))
(defwidget bar [monitor_]
(centerbox :orientation "h"
(winsworks :monitor monitor_)
(music)
(sidestuff)))
(defwidget winsworks [monitor]
(box :orientation "h" :space-evenly false :halign "start"
(workspaceWidget)
(button :onclick "~/.config/scripts/drawer.sh" :class "music" {" ${activewindow}"})
)
)
(defwidget sidestuff []
(box :class "sidestuff" :orientation "h" :space-evenly false :halign "end"
(box :class "music" {"󰛳 ${IP}"})
(metric :label "󰓃"
:value volume
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"
:onclick "killall pavucontrol || pavucontrol")
(box
:tooltip {disks}
(metric :label ""
:value {round((1 - (EWW_DISK["/"].free / EWW_DISK["/"].total)) * 100, 0)}
:onchange ""
:onclick ""))
(clock)
(systray :class "music" :orientation "h" :spacing 2 :space-evenly true)
))
(defwidget music []
(button :class "music"
:orientation "h"
:space-evenly false
:halign "center"
:onclick "~/.config/scripts/playpause.sh"
{music != "" ? " ${music}" : " None"}))
(defwidget metric [label value onchange onclick]
(box :orientation "h"
:class "metric"
:space-evenly false
(button :class "label" :onclick onclick label)
(scale :min 0
:max 101
:active {onchange != ""}
:value value
:onchange onchange)))
(deflisten workspace-niri "~/.config/scripts/workspace")
(defwidget workspaceWidget []
(eventbox
:onscroll "niri msg action $(echo {} | sed 's/up/focus-workspace-up/;s/down/focus-workspace-down/')"
(literal :content workspace-niri)))
(defpoll music :interval "0.5s"
"~/.config/scripts/playerget")
(defpoll activewindow :interval "0.5s"
"~/.config/scripts/activewindow")
(defpoll IP :interval "5s"
"~/.config/scripts/ip")
(defpoll volume :interval "0.5s"
"~/.config/scripts/getvol")
(defpoll time :interval "1s"
"date '+%H:%M:%S|%d.%m.%Y'")
(defwidget clock []
(box :class "clock"
:tooltip {calender}
(label :text {"${time}"})))
(defpoll calender :interval "600s"
"~/.config/scripts/calender-fix.sh")
(defpoll disks :interval "600s"
"~/.config/scripts/dysk-phydisks.sh")

View File

@ -0,0 +1 @@
../../hyprlua/eww-touch/eww.scss

View File

@ -0,0 +1,104 @@
(defwindow bar [monitor]
:monitor monitor
:class "ewwbar"
:windowtype "dock"
:geometry (geometry :x "0%"
:y "1%"
:width "99%"
:height "5%"
:anchor "top center")
:exclusive true
(bar :monitor_ monitor))
(defpoll battery :interval "2s"
"~/.config/scripts/batteryperc")
(defwidget bar [monitor_]
(centerbox :orientation "h"
(winsworks :monitor monitor_)
(screenrotate)
(sidestuff)))
(defwidget winsworks [monitor]
(box :orientation "h" :space-evenly false :halign "start"
(osk)
(button :onclick "~/.config/scripts/drawer.sh" :class "music" {"󱂬"})
(metric :label "󰓃"
:value volume
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"
:onclick "killall pavucontrol || pavucontrol")
)
)
(defwidget screenrotate []
(box :orientation "h" :space-evenly false :halign "center"
(button :class "music" :onclick "~/.config/scripts/screenrotationwcw.sh" "↻")
(workspaceWidget)
(button :class "music" :onclick "~/.config/scripts/screenrotationacw.sh" "↺")
)
)
(defwidget osk []
(button :class "music" :onclick "~/.config/scripts/onscreenkb.sh" "⌨"))
(defwidget sidestuff []
(box :class "sidestuff" :orientation "h" :space-evenly false :halign "end"
(box :class "music" {"${battery}"})
(box :class "music" {"󰛳 ${IP}"})
(box
:tooltip {disks}
(metric :label ""
:value {round((1 - (EWW_DISK["/"].free / EWW_DISK["/"].total)) * 100, 0)}
:onchange ""
:onclick ""))
(clock)
(systray :class "music" :orientation "h" :spacing 2 :space-evenly true)
))
(defwidget music []
(button :class "music"
:orientation "h"
:space-evenly false
:halign "center"
:onclick "~/.config/scripts/playpause.sh"
{music != "" ? " ${music}" : " None"}))
(defwidget metric [label value onchange onclick]
(box :orientation "h"
:class "metric"
:space-evenly false
(button :class "label" :onclick onclick label)
(scale :min 0
:max 101
:active {onchange != ""}
:value value
:onchange onchange)))
(deflisten workspace-niri "~/.config/scripts/workspace")
(defwidget workspaceWidget []
(eventbox
:onscroll "niri msg action $(echo {} | sed 's/up/focus-workspace-up/;s/down/focus-workspace-down/')"
(literal :content workspace-niri)))
(defpoll music :interval "0.5s"
"~/.config/scripts/playerget")
(defpoll IP :interval "5s"
"~/.config/scripts/ip")
(defpoll volume :interval "0.5s"
"~/.config/scripts/getvol")
(defpoll time :interval "1s"
"date '+%H:%M:%S|%d.%m.%Y'")
(defwidget clock []
(box :class "clock"
:tooltip {calender}
(label :text {"${time}"})))
(defpoll calender :interval "600s"
"~/.config/scripts/calender-fix.sh")
(defpoll disks :interval "600s"
"~/.config/scripts/dysk-phydisks.sh")

View File

@ -0,0 +1 @@
../../hyprlua/eww/eww.scss

View File

@ -0,0 +1,93 @@
(defwindow bar [monitor]
:monitor monitor
:class "ewwbar"
:windowtype "dock"
:geometry (geometry :x "0%"
:y "1%"
:width "99%"
:height "20px"
:anchor "top center")
:exclusive true
(bar :monitor_ monitor))
(defwidget bar [monitor_]
(centerbox :orientation "h"
(winsworks :monitor monitor_)
(music)
(sidestuff)))
(defwidget winsworks [monitor]
(box :orientation "h" :space-evenly false :halign "start"
(workspaceWidget)
(button :onclick "~/.config/scripts/drawer.sh" :class "music" {" ${activewindow}"})
)
)
(defwidget sidestuff []
(box :class "sidestuff" :orientation "h" :space-evenly false :halign "end"
(box :class "music" {"󰛳 ${IP}"})
(metric :label "󰓃"
:value volume
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"
:onclick "killall pavucontrol || pavucontrol")
(box
:tooltip {disks}
(metric :label ""
:value {round((1 - (EWW_DISK["/"].free / EWW_DISK["/"].total)) * 100, 0)}
:onchange ""
:onclick ""))
(clock)
(systray :class "music" :orientation "h" :spacing 2 :space-evenly true)
))
(defwidget music []
(button :class "music"
:orientation "h"
:space-evenly false
:halign "center"
:onclick "~/.config/scripts/playpause.sh"
{music != "" ? " ${music}" : " None"}))
(defwidget metric [label value onchange onclick]
(box :orientation "h"
:class "metric"
:space-evenly false
(button :class "label" :onclick onclick label)
(scale :min 0
:max 101
:active {onchange != ""}
:value value
:onchange onchange)))
// Niri workspace widget — polls via niri IPC event-stream script
(deflisten workspace-niri "~/.config/scripts/workspace")
(defwidget workspaceWidget []
(eventbox
:onscroll "niri msg action $(echo {} | sed 's/up/focus-workspace-up/;s/down/focus-workspace-down/')"
(literal :content workspace-niri)))
(defpoll music :interval "0.5s"
"~/.config/scripts/playerget")
(defpoll activewindow :interval "0.5s"
"~/.config/scripts/activewindow")
(defpoll IP :interval "5s"
"~/.config/scripts/ip")
(defpoll volume :interval "0.5s"
"~/.config/scripts/getvol")
(defpoll time :interval "1s"
"date '+%H:%M:%S|%d.%m.%Y'")
(defwidget clock []
(box :class "clock"
:tooltip {calender}
(label :text {"${time}"})))
(defpoll calender :interval "600s"
"~/.config/scripts/calender-fix.sh")
(defpoll disks :interval "600s"
"~/.config/scripts/dysk-phydisks.sh")

View File

@ -0,0 +1,6 @@
[terminal]
vt = 1
[default_session]
command = "tuigreet --cmd niri"
user = "greeter"

1
desktopenvs/niri/gtk-3.0 Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/gtk-3.0

1
desktopenvs/niri/kitty Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/kitty

View File

@ -0,0 +1 @@
../hyprlua/mimeapps.list

View File

@ -0,0 +1,421 @@
// ~/.config/niri/config.kdl
// Niri scrollable-tiling Wayland compositor — CyberQueer config
// Translated from hyprlua setup: https://github.com/the_miro/Dotfiles
// https://github.com/YaLTeR/niri/wiki/Configuration:-Overview
// ── Input ─────────────────────────────────────────────────────────────────────
input {
keyboard {
xkb {
layout "de"
options "caps:swapescape"
}
repeat-delay 300
repeat-rate 50
track-layout "window"
}
touchpad {
tap
natural-scroll
accel-speed 0.0
accel-profile "flat"
}
mouse {
left-handed
accel-speed 0.0
accel-profile "flat"
}
warp-mouse-to-focus
focus-follows-mouse max-scroll-amount="0%"
}
// ── Outputs ───────────────────────────────────────────────────────────────────
// Niri auto-detects monitors. Uncomment and adjust for fixed setups:
// output "eDP-1" {
// scale 2.0
// position x=0 y=0
// }
// output "HDMI-A-1" {
// scale 1.0
// position x=1920 y=0
// }
// ── Layout ────────────────────────────────────────────────────────────────────
layout {
gaps 6
center-focused-column "never"
// Preset widths cycled by Mod+Backspace
preset-column-widths {
proportion 0.333
proportion 0.5
proportion 0.667
proportion 1.0
}
default-column-width { proportion 0.5; }
// CyberQueer focus ring
focus-ring {
width 4
active-color "#E40046"
inactive-color "#5018DD"
active-gradient from="#E40046" to="#f50505" angle=35 relative-to="window"
inactive-gradient from="#5018DD" to="#5018DD" angle=35 relative-to="window"
}
border {
off
}
shadow {
on
softness 30
spread 5
offset x=0 y=5
color "#00000080"
}
// Reserve space for the eww bar
struts {
top 30
}
}
// ── Animations ────────────────────────────────────────────────────────────────
animations {
window-open {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
window-close {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
window-movement {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
horizontal-view-movement {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
workspace-switch {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
config-notification-open-close {
spring damping-ratio=0.9 stiffness=800 epsilon=0.0001
}
screenshot-ui-open {
duration-ms 200
curve "ease-out-cubic"
}
}
// ── Environment Variables ─────────────────────────────────────────────────────
environment {
GTK_THEME "cyberqueer"
XCURSOR_SIZE "40"
XCURSOR_THEME "Nordzy-cursors-lefthand"
QT_QPA_PLATFORMTHEME "gtk3"
QT_WAYLAND_DISABLE_WINDOWDECORATION "1"
MOZ_ENABLE_WAYLAND "1"
ELECTRON_OZONE_PLATFORM_HINT "auto"
NIXOS_OZONE_WL "1"
GDK_SCALE "2"
}
// ── Cursor ────────────────────────────────────────────────────────────────────
cursor {
xcursor-theme "Nordzy-cursors-lefthand"
xcursor-size 40
hide-after-inactive-ms 5000
hide-when-typing
}
// ── Prefer no client-side decorations ─────────────────────────────────────────
prefer-no-csd
// ── Screenshot path ───────────────────────────────────────────────────────────
screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
// ── Autostart ─────────────────────────────────────────────────────────────────
spawn-at-startup "bash" "-c" "~/.config/scripts/monitorhandler.sh"
spawn-at-startup "bash" "-c" "vicinae server"
spawn-at-startup "bash" "-c" "~/.config/scripts/bluetooth-applet.sh"
spawn-at-startup "bash" "-c" "~/.config/scripts/ulwatchdog.sh"
spawn-at-startup "udiskie" "-t" "-m" "nested" "-n"
spawn-at-startup "xfce-polkit"
spawn-at-startup "bash" "-c" "gammastep -O 4500"
spawn-at-startup "nm-applet"
spawn-at-startup "dunst"
spawn-at-startup "bash" "-c" "swayidle -w timeout 300 'swaylock -f' timeout 600 'systemctl suspend' before-sleep 'swaylock -f'"
spawn-at-startup "blueman-applet"
// ── Window Rules ──────────────────────────────────────────────────────────────
// Launcher / utility floating windows
window-rule {
match app-id=r"^ulauncher$"
open-floating true
}
window-rule {
match app-id=r"^vicinae$"
open-floating true
}
window-rule {
match app-id=r"^org\.gnome\.Calculator$"
open-floating true
default-column-width { fixed 400; }
}
window-rule {
match app-id=r"^qalculate-gtk$"
open-floating true
default-column-width { fixed 600; }
}
// Mixer — top-right float (approximating tag+mixer behavior)
window-rule {
match app-id=r"^pavucontrol$"
open-floating true
default-column-width { fixed 800; }
}
window-rule {
match app-id=r"^nm-connection-editor$"
open-floating true
default-column-width { fixed 800; }
}
window-rule {
match app-id=r"^blueman-manager$"
open-floating true
default-column-width { fixed 700; }
}
// File manager dialogs
window-rule {
match app-id=r"^thunar$" title=r"^(Copy|Move|Rename|Delete|Progress)"
open-floating true
}
window-rule {
match app-id=r"^xfce4-terminal$"
open-floating true
default-column-width { fixed 900; }
}
// Firefox — no transparency
window-rule {
match app-id=r"^firefox$"
opacity 1.0
}
window-rule {
match app-id=r"^zen$"
opacity 1.0
}
// Global inactive window dimming
window-rule {
min-opacity 0.85
}
// Screen sharing / video — exclude from dimming
window-rule {
match app-id=r"^vesktop$"
min-opacity 1.0
}
// ── Keybindings ───────────────────────────────────────────────────────────────
binds {
// ── Applications ──────────────────────────────────────────────────────────
Mod+T { spawn "kitty"; }
Mod+Shift+T { spawn "bash" "-c" "cool-retro-term -p ~/Dotfiles/desktopenvs/niri/CRT"; }
Mod+M { spawn "bash" "-c" "kitty -e nvim"; }
Mod+E { spawn "thunar"; }
Mod+Alt+E { spawn "pcmanfm-qt"; }
Mod+X { spawn "bash" "-c" "wofi --show=run"; }
Mod+N { spawn "nextcloud"; }
Mod+I { spawn "bash" "-c" "iwmenu --launcher walker"; }
Mod+Alt+I { spawn "bash" "-c" "bzmenu --launcher walker"; }
Mod+Shift+I { spawn "nm-connection-editor"; }
Mod+R { spawn "bash" "-c" "vicinae toggle"; }
Mod+Return { spawn "bash" "-c" "vicinae toggle"; }
Mod+Shift+R { spawn "bash" "-c" "wofi --show drun"; }
Ctrl+Shift+R { spawn "bash" "-c" "vicinae toggle"; }
Mod+F { spawn "bash" "-c" "~/.config/scripts/wofi-file-search.sh"; }
Mod+Shift+F { spawn "bash" "-c" "~/.config/scripts/foldersearch.sh"; }
Mod+Alt+F { spawn "wofi-calc"; }
Mod+S { spawn "pavucontrol"; }
Mod+U { spawn "bash" "-c" "kitty -e btop"; }
Mod+W { spawn "bash" "-c" "kitty -e ~/.config/scripts/wallpaper-picker ~/Pictures"; }
Mod+Ctrl+R { spawn "bash" "-c" "kitty -e ~/.config/scripts/amssh"; }
Mod+F1 { spawn "bash" "-c" "kitty -e ~/.config/scripts/helpmenu.sh"; }
Mod+Ctrl+T { spawn "bash" "-c" "kitty -e ~/.config/scripts/timer-pick"; }
// Edit niri config directly:
Mod+Shift+F1 { spawn "bash" "-c" "kitty -e nvim ~/.config/niri/config.kdl"; }
Mod+Ctrl+P { spawn "bash" "-c" "~/.config/scripts/screenrec.sh"; }
// App drawer
Mod+D { spawn "bash" "-c" "~/.config/scripts/drawer.sh"; }
Mod+Shift+A { spawn "bash" "-c" "~/.config/scripts/drawer.sh"; }
// ── Screenshots ───────────────────────────────────────────────────────────
Print { screenshot; }
Mod+P { screenshot; }
Mod+Shift+P { screenshot-screen; }
// Mod+Ctrl+P is screenrec (defined above)
// ── Window Management ─────────────────────────────────────────────────────
Mod+Q { close-window; }
Mod+Shift+Q { close-window; }
Mod+V { toggle-window-floating; }
Mod+Shift+V { center-column; }
Mod+C { center-column; }
// ── Niri-native layout actions ─────────────────────────────────────────────
Mod+Backspace { switch-preset-column-width; }
Mod+Shift+Backspace { maximize-column; }
Mod+Ctrl+Backspace { fullscreen-window; }
// Consume/expel from column (Niri's grouping analog)
Mod+A { consume-window-into-column; }
Mod+Y { expel-window-from-column; }
Mod+Alt+C { consume-or-expel-window-left; }
Mod+Alt+V { consume-or-expel-window-right; }
// ── Focus ─────────────────────────────────────────────────────────────────
Mod+H { focus-column-left; }
Mod+L { focus-column-right; }
Mod+J { focus-window-down; }
Mod+K { focus-window-up; }
Mod+Left { focus-column-left; }
Mod+Right { focus-column-right; }
Mod+Down { focus-window-down; }
Mod+Up { focus-window-up; }
Mod+Tab { focus-window-down-or-column-right; }
Mod+Shift+Tab { focus-window-up-or-column-left; }
// ── Move Window ───────────────────────────────────────────────────────────
Mod+Shift+H { move-column-left; }
Mod+Shift+L { move-column-right; }
Mod+Shift+J { move-window-down; }
Mod+Shift+K { move-window-up; }
Mod+Shift+Left { move-column-left; }
Mod+Shift+Right { move-column-right; }
Mod+Shift+Down { move-window-down; }
Mod+Shift+Up { move-window-up; }
// ── Resize ────────────────────────────────────────────────────────────────
Mod+Alt+L { set-column-width "+10%"; }
Mod+Alt+H { set-column-width "-10%"; }
Mod+Alt+K { set-window-height "-10%"; }
Mod+Alt+J { set-window-height "+10%"; }
Mod+Alt+Right { set-column-width "+10%"; }
Mod+Alt+Left { set-column-width "-10%"; }
Mod+Alt+Up { set-window-height "-10%"; }
Mod+Alt+Down { set-window-height "+10%"; }
// ── Workspaces ────────────────────────────────────────────────────────────
// Note: Niri workspaces are vertical (stacked top-to-bottom per output)
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+3 { focus-workspace 3; }
Mod+4 { focus-workspace 4; }
Mod+5 { focus-workspace 5; }
Mod+6 { focus-workspace 6; }
Mod+7 { focus-workspace 7; }
Mod+8 { focus-workspace 8; }
Mod+9 { focus-workspace 9; }
Mod+Shift+1 { move-column-to-workspace 1; }
Mod+Shift+2 { move-column-to-workspace 2; }
Mod+Shift+3 { move-column-to-workspace 3; }
Mod+Shift+4 { move-column-to-workspace 4; }
Mod+Shift+5 { move-column-to-workspace 5; }
Mod+Shift+6 { move-column-to-workspace 6; }
Mod+Shift+7 { move-column-to-workspace 7; }
Mod+Shift+8 { move-column-to-workspace 8; }
Mod+Shift+9 { move-column-to-workspace 9; }
// Relative workspace navigation (Ctrl+L/R → down/up in vertical stack)
Mod+Ctrl+Right { focus-workspace-down; }
Mod+Ctrl+Left { focus-workspace-up; }
Mod+Ctrl+L { focus-workspace-down; }
Mod+Ctrl+H { focus-workspace-up; }
Mod+Ctrl+Shift+Right { move-column-to-workspace-down; }
Mod+Ctrl+Shift+Left { move-column-to-workspace-up; }
Mod+Ctrl+Shift+L { move-column-to-workspace-down; }
Mod+Ctrl+Shift+H { move-column-to-workspace-up; }
// Scroll workspaces
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; }
Mod+Shift+WheelScrollDown { move-column-to-workspace-down; }
Mod+Shift+WheelScrollUp { move-column-to-workspace-up; }
// Horizontal scroll → horizontal column navigation
Mod+WheelScrollRight { focus-column-right; }
Mod+WheelScrollLeft { focus-column-left; }
// Volume keys as workspace nav (when Mod held)
Mod+XF86AudioRaiseVolume { focus-workspace-down; }
Mod+XF86AudioLowerVolume { focus-workspace-up; }
Mod+Shift+XF86AudioRaiseVolume { move-column-to-workspace-down; }
Mod+Shift+XF86AudioLowerVolume { move-column-to-workspace-up; }
// Scratchpad-equivalent: focus the most recently visited workspace
Mod+Space { focus-workspace-with-recent-window; }
Mod+Shift+Space { move-column-to-workspace-with-recent-window; }
// ── Bar / UI ──────────────────────────────────────────────────────────────
Mod+Z { spawn "bash" "-c" "~/.config/scripts/togglebar.sh"; }
Mod+Ctrl+B { spawn "bash" "-c" "eww reload"; }
// ── Audio ─────────────────────────────────────────────────────────────────
XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "-l" "1.4" "@DEFAULT_AUDIO_SINK@" "5%+"; }
XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "-l" "1.4" "@DEFAULT_AUDIO_SINK@" "5%-"; }
XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
XF86AudioPlay allow-when-locked=true { spawn "playerctl" "play-pause" "-p" "spotify,vlc"; }
// ── Brightness ────────────────────────────────────────────────────────────
XF86MonBrightnessUp allow-when-locked=true { spawn "bri" "--up"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "bri" "--down"; }
// ── Color temperature (gammastep presets) ─────────────────────────────────
Mod+Ctrl+X { spawn "bash" "-c" "killall gammastep; gammastep -O 6500"; } // neutral / reset
Mod+Ctrl+W { spawn "bash" "-c" "killall gammastep; gammastep -O 5000"; } // warm
Mod+Ctrl+A { spawn "bash" "-c" "killall gammastep; gammastep -O 4000"; } // warmer
Mod+Ctrl+Q { spawn "bash" "-c" "killall gammastep; gammastep -O 3000"; } // very warm
Mod+Ctrl+S { spawn "bash" "-c" "killall gammastep; gammastep -O 2700"; } // night mode
// ── Misc ──────────────────────────────────────────────────────────────────
Mod+Ctrl+I { spawn "chamel" "toggle"; }
Mod+Ctrl+U { spawn "chamel" "clear"; }
Mod+Ctrl+Z { spawn "chamel" "clear-and-deactivate"; }
Mod+Ctrl+C { spawn "dunstctl" "close-all"; }
Mod+Ctrl+G { spawn "bash" "-c" "~/.config/scripts/onscreenkb.sh"; }
Mod+Shift+C { spawn "bash" "-c" "~/.config/scripts/caffeine.sh"; }
Mod+Shift+X { spawn "bash" "-c" "~/.config/scripts/niri-toggle-touchpad.sh"; }
Mod+Ctrl+E { spawn "bash" "-c" "~/.config/scripts/screenrotationwcw.sh"; }
Mod+Ctrl+D { spawn "bash" "-c" "~/.config/scripts/screenrotationacw.sh"; }
// ── Lock / Exit ───────────────────────────────────────────────────────────
Mod+O { spawn "swaylock" "-f"; }
Mod+Shift+O { quit; }
Mod+Ctrl+O { spawn "systemctl" "poweroff"; }
Mod+Alt+O { spawn "bash" "-c" "~/.config/scripts/pwr-dmenu.sh"; }
Mod+Alt+Ctrl+Shift+End { quit; }
}

1
desktopenvs/niri/nwg-drawer Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/nwg-drawer

1
desktopenvs/niri/nwg-panel Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/nwg-panel

View File

@ -0,0 +1 @@
../../hyprlua/scripts/_toggle_Template.tmplsh

View File

@ -0,0 +1,13 @@
#!/bin/bash
trunc=16
sample=$(niri msg -j focused-window 2>/dev/null | jq -r '.title // ""' 2>/dev/null)
if [ ${#sample} -gt $trunc ]; then
echo "$sample" | head -c $trunc | sed 's/$/…/'
else
if [ ${#sample} -ne 0 ]; then
echo "$sample"
else
echo "None"
fi
fi

View File

@ -0,0 +1 @@
../../hyprlua/scripts/amssh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/batteryperc

View File

@ -0,0 +1,2 @@
#!/bin/bash
blueman-applet

View File

@ -0,0 +1,14 @@
#!/bin/bash
statecon=$(pidof swayidle | grep "[1234567890]")
if [ "$statecon" != "" ]; then
notify-send -t 1000 "Caffeine mode ON — idle inhibited"
killall swayidle
else
notify-send -t 1000 "Caffeine mode OFF — idle active"
swayidle -w \
timeout 300 'swaylock -f' \
timeout 600 'systemctl suspend' \
before-sleep 'swaylock -f' &
fi

View File

@ -0,0 +1 @@
../../hyprlua/scripts/calender-fix.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/date.sh

View File

@ -0,0 +1,9 @@
#!/bin/bash
nwg-drawer \
-fm "kitty -e yazi" \
-term kitty \
-mb 20 -ml 20 -mr 20 -mt 20 \
-pblock "swaylock -f" \
-pbpoweroff poweroff \
-pbexit "niri msg action quit" \
-pbreboot reboot

View File

@ -0,0 +1 @@
../../hyprlua/scripts/dysk-phydisks.sh

View File

@ -0,0 +1,13 @@
#!/bin/bash
killall eww 2>/dev/null || true
/usr/bin/eww daemon
GTK_THEME=cyberqueer
monitorsum=$(niri msg outputs | grep -c "Scale:" || echo 1)
for i in $(seq 1 "$monitorsum"); do
declare -i curmon=$i-1
/usr/bin/eww open bar --id "bar$curmon" --arg monitor=$curmon
done

View File

@ -0,0 +1 @@
../../hyprlua/scripts/foldersearch.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/frequentcommands.list

View File

@ -0,0 +1 @@
../../hyprlua/scripts/getispeed.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/getvol

View File

@ -0,0 +1 @@
../../hyprlua/scripts/helpmenu.sh

1
desktopenvs/niri/scripts/ip Symbolic link
View File

@ -0,0 +1 @@
../../hyprlua/scripts/ip

View File

@ -0,0 +1 @@
../../hyprlua/scripts/journal.sh

View File

@ -0,0 +1,9 @@
#!/bin/bash
nwg-menu \
-term kitty \
--ml 15 -mb 15 -mr 15 -mt 15 \
-cmd-lock "swaylock -f" \
-cmd-logout "niri msg action quit" \
-cmd-restart reboot \
-cmd-shutdown poweroff \
-fm "kitty -e yazi"

View File

@ -0,0 +1,13 @@
#!/bin/bash
# Start the wallpaper daemon and eww bar for niri.
# swaybg replaces hyprpaper; one instance per output.
WALLPAPER="${HOME}/Pictures/background.jpg"
if [ -f "$WALLPAPER" ]; then
swaybg -m fill -i "$WALLPAPER" &
fi
mkdir -p ~/Pictures/Screenshots
~/.config/scripts/ewwstart.sh

View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Toggle touchpad on/off. Niri doesn't expose a direct IPC for this;
# we fall back to libinput xinput if available, or notify with instructions.
STATUS_FILE="$XDG_RUNTIME_DIR/touchpad.status"
TOUCHPAD_ID=$(xinput --list --id-only "$(xinput --list --name-only 2>/dev/null | grep -i touchpad | head -1)" 2>/dev/null || echo "")
enable_touchpad() {
printf "true" > "$STATUS_FILE"
notify-send -u normal "Touchpad Enabled"
if [ -n "$TOUCHPAD_ID" ]; then
xinput enable "$TOUCHPAD_ID"
fi
}
disable_touchpad() {
printf "false" > "$STATUS_FILE"
notify-send -u normal "Touchpad Disabled"
if [ -n "$TOUCHPAD_ID" ]; then
xinput disable "$TOUCHPAD_ID"
fi
}
if [ -z "$TOUCHPAD_ID" ]; then
notify-send -u normal "Touchpad" "No touchpad found via xinput.\nAdjust device name in niri-toggle-touchpad.sh"
exit 0
fi
if [ ! -f "$STATUS_FILE" ]; then
enable_touchpad
else
if [ "$(cat "$STATUS_FILE")" = "true" ]; then
disable_touchpad
else
enable_touchpad
fi
fi

View File

@ -0,0 +1 @@
../../hyprlua/scripts/playerget

View File

@ -0,0 +1 @@
../../hyprlua/scripts/playpause.sh

View File

@ -0,0 +1,2 @@
#!/bin/zsh
cat ~/.config/scripts/frequentcommands.list | wofi --show=dmenu | zsh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/python

View File

@ -0,0 +1 @@
../../hyprlua/scripts/screenrec.sh

View File

@ -0,0 +1,2 @@
#!/bin/bash
~/.config/scripts/unified-rotate.sh ccw

View File

@ -0,0 +1,2 @@
#!/bin/bash
~/.config/scripts/unified-rotate.sh cw

View File

@ -0,0 +1 @@
../../hyprlua/scripts/screenshot.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/time.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/timer-notify.sh.old

View File

@ -0,0 +1 @@
../../hyprlua/scripts/timer-pick

View File

@ -0,0 +1 @@
../../hyprlua/scripts/timer-run

View File

@ -0,0 +1 @@
../../hyprlua/scripts/timer.sh.old

View File

@ -0,0 +1,3 @@
#!/bin/bash
# Niri uses a single scrollable-tiling layout — no layout toggle available.
notify-send -t 2000 "Niri" "Scrollable tiling — no layout toggle in niri."

View File

@ -0,0 +1,25 @@
#!/bin/bash
GTK_THEME=cyberqueer
monitorsum=$(niri msg outputs | grep -c "Scale:" || echo 1)
# niri doesn't expose a simple "focused output index" via IPC — default to 0
actmon=0
statecon=$(eww active-windows 2>/dev/null | grep "bar$actmon" || true)
if [ "$statecon" != '' ]; then
for i in $(seq 1 "$monitorsum"); do
declare -i curmon=$i-1
if [ "$curmon" -eq "$actmon" ]; then
/usr/bin/eww open --toggle "bar$curmon"
fi
done
else
for i in $(seq 1 "$monitorsum"); do
declare -i curmon=$i-1
if [ "$curmon" -eq "$actmon" ]; then
/usr/bin/eww open bar --id "bar$curmon" --arg monitor=$curmon
fi
done
fi

View File

@ -0,0 +1,9 @@
#!/bin/bash
zoxide import --from z ~/.z --merge 2>/dev/null || true
while true; do
statecon=$(pgrep ulauncher)
if [ "$statecon" != '' ]; then
ulauncher
fi
sleep 5
done

View File

@ -0,0 +1,49 @@
#!/bin/bash
# Screen rotation for niri using niri IPC.
# Usage: unified-rotate.sh [cw|ccw|0|90|180|270]
direction="$1"
if [[ "$direction" != "cw" && "$direction" != "ccw" && \
"$direction" != "0" && "$direction" != "90" && \
"$direction" != "180" && "$direction" != "270" ]]; then
echo "Usage: $0 [cw|ccw|0|90|180|270]"
exit 1
fi
# Get focused output name
outputs_json=$(niri msg -j outputs 2>/dev/null)
curmon=$(echo "$outputs_json" | jq -r 'to_entries[] | select(.value.is_active == true) | .key' 2>/dev/null | head -1)
if [ -z "$curmon" ]; then
# Fallback: use first output
curmon=$(echo "$outputs_json" | jq -r 'keys[0]' 2>/dev/null)
fi
if [ -z "$curmon" ]; then
notify-send -u critical "Screen Rotation" "Could not detect current output"
exit 1
fi
# Niri transform values: "normal" | "90" | "180" | "270" | "flipped" | "flipped-90" | "flipped-180" | "flipped-270"
currot=$(echo "$outputs_json" | jq -r ".\"$curmon\".transform // \"normal\"" 2>/dev/null)
declare -A rot_to_deg=([normal]="0" [90]="90" [180]="180" [270]="270")
declare -A deg_to_rot=([0]="normal" [90]="90" [180]="180" [270]="270")
curdeg="${rot_to_deg[$currot]:-0}"
if [[ "$direction" == "cw" ]]; then
newdeg=$(( (curdeg + 90) % 360 ))
elif [[ "$direction" == "ccw" ]]; then
newdeg=$(( (curdeg + 270) % 360 ))
else
newdeg="$direction"
fi
newrot="${deg_to_rot[$newdeg]:-normal}"
echo "Rotating '$curmon' from ${curdeg}° to ${newdeg}° (${newrot})"
niri msg action set-output-transform "$curmon" "$newrot"
eww reload 2>/dev/null || true

View File

@ -0,0 +1 @@
../../hyprlua/scripts/uptime.sh

View File

@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Wallpaper picker for niri using swaybg.
# Usage: wallpaper-picker [directory]
set -u
DIR="${1:-$HOME/Pictures}"
STATE_FILE="${HOME}/.config/niri-wallpaper"
IMAGES=()
while IFS= read -r -d '' f; do
IMAGES+=("$f")
done < <(find "$DIR" -maxdepth 1 -type f \
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \
-o -iname '*.webp' -o -iname '*.bmp' -o -iname '*.avif' \) \
-print0 | sort -z)
[[ ${#IMAGES[@]} -eq 0 ]] && { echo "No images found in $DIR"; exit 1; }
INDEX=0
icat() { kitty +kitten icat --stdin=no --silent --transfer-mode=memory "$@" 2>/dev/null; }
clear_images() { icat --clear; }
draw() {
local cols rows
cols=$(tput cols); rows=$(tput lines)
local n=${#IMAGES[@]}
local prev=$(( (INDEX - 1 + n) % n ))
local next=$(( (INDEX + 1) % n ))
local header_h=2
local footer_h=4
local img_h=$(( rows - header_h - footer_h ))
(( img_h < 5 )) && img_h=5
local side_w=$(( cols * 18 / 100 ))
local center_w=$(( cols - 2 * (side_w + 2) ))
local center_x=$(( side_w + 2 ))
local next_x=$(( center_x + center_w + 2 ))
printf '\033[2J\033[H'
clear_images
tput cup 0 0
printf ' Wallpaper Picker (niri/swaybg) [%d/%d] %s' \
$(( INDEX + 1 )) "$n" "$(basename "${IMAGES[$INDEX]}")"
icat --place "${side_w}x${img_h}@0x${header_h}" "${IMAGES[$prev]}"
icat --place "${center_w}x${img_h}@${center_x}x${header_h}" "${IMAGES[$INDEX]}"
icat --place "${side_w}x${img_h}@${next_x}x${header_h}" "${IMAGES[$next]}"
local row=$(( header_h + img_h ))
tput cup "$row" 0
printf ' h/←: prev l/→: next Enter/Space: apply q: quit\n'
}
apply() {
local path="$1"
killall swaybg 2>/dev/null || true
swaybg -m fill -i "$path" &
printf '%s\n' "$path" > "$STATE_FILE"
tput cup $(( $(tput lines) - 1 )) 0
printf '\033[2K Applied: %s' "$(basename "$path")"
}
KEY=""
read_key() {
KEY=""
IFS= read -rsn1 KEY
if [[ $KEY == $'\x1b' ]]; then
local rest=""
IFS= read -rsn2 -t 0.1 rest || true
KEY+="$rest"
fi
}
old_stty=$(stty -g)
trap 'stty "$old_stty"; clear_images; tput cnorm; printf "\033[2J\033[H"' EXIT
stty -echo -icanon -icrnl min 1 time 0
tput civis
draw
while true; do
read_key
case "$KEY" in
h|$'\x1b[D') INDEX=$(( (INDEX - 1 + ${#IMAGES[@]}) % ${#IMAGES[@]} )); draw ;;
l|$'\x1b[C') INDEX=$(( (INDEX + 1) % ${#IMAGES[@]} )); draw ;;
$'\r'|$'\n'|' ') apply "${IMAGES[$INDEX]}" ;;
q|Q) break ;;
esac
done

View File

@ -0,0 +1,12 @@
#!/bin/bash
# Window switcher for niri using wofi dmenu
focused=$(niri msg -j focused-window 2>/dev/null | jq -r '.id // ""')
selection=$(niri msg -j windows 2>/dev/null \
| jq -r '.[] | "\(.id) ┇ \(.app_id // "?") ┇ \(.title // "?")"' \
| wofi -dO alphabetical --prompt "Switch to:" \
| awk '{print $1}')
if [ -n "$selection" ]; then
niri msg action focus-window --id "$selection"
fi

View File

@ -0,0 +1 @@
../../hyprlua/scripts/wofi-file-search.sh

View File

@ -0,0 +1,29 @@
#!/bin/bash
# EWW workspace widget for niri (literal/listen mode).
# Outputs an eww Yuck literal widget, updates on workspace changes.
render() {
local ws_json
ws_json=$(niri msg -j workspaces 2>/dev/null) || ws_json="[]"
local out="(box :class \"workspaces\" :space-evenly false"
while IFS= read -r ws; do
local idx is_active
idx=$(echo "$ws" | jq -r '.idx')
is_active=$(echo "$ws" | jq -r '.is_focused')
local id
id=$(echo "$ws" | jq -r '.id')
local class="workspace-button"
[ "$is_active" = "true" ] && class="workspace-button workspace-active"
out+=" (button :class \"$class\" :width 20 :onclick \"niri msg action focus-workspace $idx\" \"$idx\")"
done < <(echo "$ws_json" | jq -c '.[]')
out+=")"
echo "$out"
}
render
# Watch for workspace changes via event-stream
niri msg event-stream 2>/dev/null | while IFS= read -r line; do
echo "$line" | grep -qE '"WorkspacesChanged"|"WorkspaceActivated"' && render
done

View File

@ -0,0 +1,2 @@
#!/bin/bash
niri msg action focus-workspace-up

View File

@ -0,0 +1,2 @@
#!/bin/bash
niri msg action focus-workspace-down

1
desktopenvs/niri/spicetify Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/spicetify

1
desktopenvs/niri/ulauncher Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/ulauncher

1
desktopenvs/niri/vicinae Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/vicinae

1
desktopenvs/niri/walker Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/walker

1
desktopenvs/niri/waybar Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/waybar

1
desktopenvs/niri/wofi Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/wofi

1
desktopenvs/niri/xfce4 Symbolic link
View File

@ -0,0 +1 @@
../hyprlua/xfce4

View File

@ -1,6 +1,8 @@
# Hyprland Desktop Environment
The Hyprland setup is the primary desktop environment — a Wayland compositor with a full ecosystem of bars, launchers, notification daemons, and theming tools.
The HyprLua setup is the primary Hyprland-based desktop environment — a Wayland compositor with a full ecosystem of bars, launchers, notification daemons, and theming tools.
> **See also:** [Niri](niri.md) — an alternative scrollable-tiling Wayland compositor with the same CyberQueer theme and most of the same tools.
---

View File

@ -1,8 +1,8 @@
# M-Archy Dotfiles
**Arch Linux · Hyprland · Wayland · CyberQueer**
**Arch Linux · Wayland · CyberQueer**
A production-grade Arch Linux configuration for network administration, development, and gaming — built around the Hyprland Wayland compositor and the CyberQueer colour theme.
A production-grade Arch Linux configuration for network administration, development, and gaming — built around Wayland compositors (HyprLua and Niri) and the CyberQueer colour theme.
---
@ -12,7 +12,8 @@ A production-grade Arch Linux configuration for network administration, developm
|------|-------------|
| [Installation](installation.md) | Interactive TUI installer, answerfile automation, ISO building |
| [Theming](theming.md) | CyberQueer colour system and `apply-theme.sh` |
| [Hyprland](hyprland.md) | Desktop environment, keybindings, bars, launchers |
| [Hyprland](hyprland.md) | HyprLua desktop environment — keybindings, bars, launchers |
| [Niri](niri.md) | Niri scrollable-tiling compositor — config, keybindings, tools |
| [Modules](modules.md) | Core modules and full optional-app catalogue |
| [Archiso](archiso.md) | Building the custom live installer ISO |
| [FreeIPA & Ansible](freeipa-ansible.md) | Identity management and automated config deployment |
@ -66,7 +67,9 @@ Dotfiles/
│ ├── install-modules.sh # Add optional modules to existing system
│ ├── archiso/ # Custom Arch live ISO builder
│ └── modules/ # Modular install scripts
├── desktopenvs/hyprland/ # All Hyprland / Wayland configs
├── desktopenvs/hyprlua/ # HyprLua (Lua-based Hyprland) configs
├── desktopenvs/niri/ # Niri scrollable-tiling compositor configs
├── desktopenvs/hyprland/ # Legacy Hyprlang Hyprland configs
├── gtk-themes/cyberqueer/ # GTK 3 & 4 theme
├── qt-themes/cyberqueer/ # Qt platform theme
├── nvim/ # Neovim config

View File

@ -35,7 +35,7 @@ The TUI walks you through:
- `core` — 100+ base system packages
- `svc` — core services (NetworkManager, cronie, fail2ban, greetd)
- `shell` — zsh, Neovim, Yazi, Micro, Starship
3. **Desktop Environment** — Hyprland, Sway, KDE Plasma, GNOME, COSMIC, XFCE, LXQt, or none
3. **Desktop Environment** — HyprLua, Niri, Hyprland, Sway, KDE Plasma, GNOME, COSMIC, XFCE, LXQt, or none
4. **Applications** — checklist of ~50 optional apps (see [Modules](modules.md))
5. **Colorway** — optional; enter hex values to customise the CyberQueer palette
@ -80,7 +80,7 @@ This dry-runs every installer dialog and saves your choices. **No software is in
"fido2_user": false,
"run_tui": true,
"components": ["pkg", "core", "svc", "shell"],
"desktop_environment": "hyprland",
"desktop_environment": "hyprlua",
"apps": ["firefox-browser", "vscodium", "docker"],
"colors": {
"COLOR_TEXT": "D6ABAB",

View File

@ -53,7 +53,9 @@ Also deploys `greetd-tuigreet` config from the dotfiles.
| ID | Name | Notes |
|----|------|-------|
| `hyprland` | Hyprland | Primary DE — see [Hyprland](hyprland.md) |
| `hyprlua` | HyprLua | Lua-based Hyprland — recommended — see [Hyprland](hyprland.md) |
| `niri` | Niri | Scrollable-tiling Wayland compositor — see [Niri](niri.md) |
| `hyprland` | Hyprland | Legacy Hyprlang config |
| `sway` | Sway | Wayland tiling WM, lighter |
| `kde-plasma` | KDE Plasma | Full-featured with sddm |
| `gnome` | GNOME | Modern Wayland DE with gdm |

278
docs/md/niri.md Normal file
View File

@ -0,0 +1,278 @@
# Niri Desktop Environment
Niri is a scrollable-tiling Wayland compositor written in Rust. Unlike Hyprland's dwindle/master layouts, Niri arranges windows in **columns that scroll horizontally** within each workspace — think a continuous horizontal strip instead of a fixed grid.
---
## Overview
| Component | Role |
|-----------|------|
| **Niri** | Wayland scrollable-tiling compositor |
| **Kitty** | Primary terminal emulator |
| **EWW** | Status bar (three device variants) |
| **Waybar** | Alternative status bar |
| **Wofi** | App launcher (keyboard-driven) |
| **Walker** | Fast CLI launcher |
| **uLauncher** | GUI app launcher |
| **Dunst** | Notification daemon |
| **swaylock** | Screen locker |
| **swaybg** | Wallpaper daemon |
| **swayidle** | Idle management (lock, suspend) |
| **gammastep** | Colour temperature (night light) |
| **nwg-drawer** | Application drawer |
| **nwg-panel** | Desktop menu |
| **Vicinae** | Gesture-triggered context launcher |
| **xfce-polkit** | PolicyKit authentication agent |
| **ly** | TUI login manager |
---
## Config File Map
```
desktopenvs/niri/
├── niri/
│ └── config.kdl # Single KDL config — everything in one file
├── greetd-tuigreet/
│ └── config.toml # Greeter → launches niri
├── scripts/ # Utility scripts (niri-adapted)
│ ├── ewwstart.sh # Start EWW bar (niri monitor detection)
│ ├── togglebar.sh # Show/hide EWW bar
│ ├── monitorhandler.sh # swaybg wallpaper + eww bar on startup
│ ├── caffeine.sh # Toggle swayidle (prevent sleep)
│ ├── activewindow # Focused window title for EWW
│ ├── workspace # Workspace widget (niri event-stream)
│ ├── wallpaper-picker # swaybg-based wallpaper picker
│ ├── unified-rotate.sh # Screen rotation via niri IPC
│ ├── niri-toggle-touchpad.sh # Touchpad on/off (xinput)
│ ├── windowswitcher # Window list via niri msg + wofi
│ ├── drawer.sh # nwg-drawer launcher
│ └── [shared with hyprlua] # screenshot.sh, screenrec.sh, etc.
├── eww/ # EWW bar (PC, no battery)
├── eww-nobattery/ # EWW bar (desktop alias)
├── eww-touch/ # EWW bar (laptop/tablet + battery)
├── config-updater/
│ └── updater.conf # Config sync manifest
│ # Shared configs — symlinked from desktopenvs/hyprlua/
├── kitty → ../hyprlua/kitty
├── dunst → ../hyprlua/dunst
├── wofi → ../hyprlua/wofi
├── btop → ../hyprlua/btop
├── walker → ../hyprlua/walker
├── vicinae → ../hyprlua/vicinae
└── [alacritty, gtk-3.0, ulauncher, xfce4, waybar, nwg-drawer, ...]
```
---
## Niri vs Hyprland — Key Differences
| Feature | HyprLua | Niri |
|---------|---------|------|
| Layout model | Dwindle / Master | Scrollable columns |
| Config format | Lua (`hyprland.lua`) | KDL (`config.kdl`) |
| Workspace direction | Horizontal (1→9) | Vertical (stacked) |
| Wallpaper | hyprpaper | swaybg |
| Screen locker | hyprlock | swaylock |
| Idle daemon | hypridle | swayidle |
| Colour temp | hyprsunset | gammastep |
| Polkit agent | hyprpolkitagent | xfce-polkit |
| Dock | nwg-dock-hyprland | — (not compatible) |
| IPC tool | `hyprctl` | `niri msg` |
> **No dock:** nwg-dock-hyprland uses the `wlr-foreign-toplevel-management` protocol which niri does not implement. The app drawer (`nwg-drawer`) and launcher (`vicinae`) serve the same role.
---
## Keybindings
All bindings are defined in `niri/config.kdl` under the `binds { }` section. Edit with `Mod+Shift+F1`.
### Applications
| Binding | Action |
|---------|--------|
| `Super + T` | Kitty terminal |
| `Super + Shift + T` | Cool Retro Term |
| `Super + M` | Kitty + Neovim |
| `Super + E` | Thunar file manager |
| `Super + Alt + E` | PCManFM-Qt |
| `Super + X` | Wofi run launcher |
| `Super + R` / `Super + Return` | Vicinae launcher |
| `Super + Shift + R` | Wofi drun |
| `Super + F` | File search |
| `Super + Shift + F` | Folder search |
| `Super + Alt + F` | Wofi calculator |
| `Super + S` | PulseAudio mixer (pavucontrol) |
| `Super + U` | btop system monitor |
| `Super + W` | Wallpaper picker |
| `Super + D` | App drawer (nwg-drawer) |
| `Super + I` | Network menu (iwmenu) |
| `Super + N` | Nextcloud |
### Window Management
| Binding | Action |
|---------|--------|
| `Super + Q` | Close window |
| `Super + V` | Toggle floating |
| `Super + Shift + V` / `Super + C` | Centre column |
| `Super + Backspace` | Cycle preset column width |
| `Super + Shift + Backspace` | Maximise column |
| `Super + Ctrl + Backspace` | Fullscreen |
| `Super + A` | Consume window into column |
| `Super + Y` | Expel window from column |
### Focus & Move
| Binding | Action |
|---------|--------|
| `Super + H / L / J / K` | Focus column left/right, window up/down |
| `Super + Arrow keys` | Same as above |
| `Super + Tab` | Focus next window/column |
| `Super + Shift + H/L/J/K` | Move column/window |
| `Super + Alt + H/L/J/K` | Resize column/window |
### Workspaces
> Niri workspaces are **vertical** — they stack top-to-bottom on each output.
> `Ctrl+L` goes **down** (next workspace), `Ctrl+H` goes **up** (previous).
| Binding | Action |
|---------|--------|
| `Super + [19]` | Switch to workspace N |
| `Super + Shift + [19]` | Move column to workspace N |
| `Super + Ctrl + L / Right` | Next workspace (down) |
| `Super + Ctrl + H / Left` | Previous workspace (up) |
| `Super + Ctrl + Shift + L/H` | Move column to next/prev workspace |
| `Super + Scroll up/down` | Navigate workspaces |
| `Super + Space` | Focus workspace with recent window |
### System
| Binding | Action |
|---------|--------|
| `Super + O` | Lock screen (swaylock) |
| `Super + Shift + O` | Quit niri |
| `Super + Ctrl + O` | Shutdown |
| `Super + Alt + O` | Power menu |
| `Super + Z` | Toggle EWW bar |
| `Super + Ctrl + B` | Reload EWW |
| `Super + Shift + C` | Toggle caffeine (swayidle) |
| `Super + Ctrl + P` | Start/stop screen recording |
| `Print` / `Super + P` | Screenshot (region) |
| `Super + Shift + P` | Screenshot full screen |
### Audio / Brightness
| Binding | Action |
|---------|--------|
| `XF86AudioRaiseVolume` | Volume +5% |
| `XF86AudioLowerVolume` | Volume -5% |
| `XF86AudioMute` | Mute toggle |
| `XF86AudioPlay` | Play/pause |
| `XF86MonBrightnessUp/Down` | Brightness |
### Colour Temperature (gammastep)
Unlike hyprsunset, gammastep has no runtime IPC. Each binding kills and restarts it at a preset:
| Binding | Temperature |
|---------|-------------|
| `Super + Ctrl + X` | 6500 K (neutral / reset) |
| `Super + Ctrl + W` | 5000 K (warm) |
| `Super + Ctrl + A` | 4000 K (warmer) |
| `Super + Ctrl + Q` | 3000 K (very warm) |
| `Super + Ctrl + S` | 2700 K (night mode) |
---
## Status Bar (EWW)
Three EWW bar variants — installed during setup:
| Variant | Target device | Battery widget |
|---------|---------------|----------------|
| `eww/` | Desktop PC | No |
| `eww-nobattery/` | Desktop PC | No |
| `eww-touch/` | Laptop / tablet | Yes |
The workspace widget uses `~/.config/scripts/workspace` — a bash script that listens to `niri msg event-stream` and outputs EWW Yuck literals whenever workspaces change.
The EWW style (`eww.scss`) is shared with hyprlua via symlink — CyberQueer colours apply identically.
---
## Wallpaper
The wallpaper is managed by `swaybg`. At startup `monitorhandler.sh` runs:
```bash
swaybg -m fill -i ~/Pictures/background.jpg &
```
To pick a wallpaper interactively:
```
Super + W
```
This opens `wallpaper-picker` in a Kitty terminal — navigate with `h/l`, apply with `Enter`, quit with `q`. The selected image is reloaded via `swaybg` and saved to `~/.config/niri-wallpaper`.
---
## Screen Lock & Idle
**swaylock** is the screen locker (`Super + O` or via swayidle timeout).
**swayidle** launches at startup with:
- 5 min idle → `swaylock -f`
- 10 min idle → `systemctl suspend`
- before sleep → `swaylock -f`
**Caffeine mode** (`Super + Shift + C`) kills swayidle and restarts it when toggled off.
---
## Screen Rotation
`Super + Ctrl + E` (clockwise) and `Super + Ctrl + D` (anti-clockwise) call `unified-rotate.sh`, which uses `niri msg action set-output-transform` on the currently focused output.
---
## Config Updater
The config updater syncs `desktopenvs/niri/` into `~/.config/`:
```bash
~/update-configs.sh
```
Config: `~/.config/config-updater/updater.conf`
Managed entries: `niri`, `kitty`, `dunst`, `wofi`, `scripts`, and more.
---
## Installing Niri
```bash
# Via the TUI installer
bash ~/Dotfiles/setup/tui-install.sh
# → select "Niri" in the Desktop Environment dialog
# Direct script (on an existing Arch system)
bash ~/Dotfiles/setup/modules/Desktop-Environments/niri.sh
```
The installer:
1. Installs all packages (niri, swaybg, swaylock, swayidle, gammastep, xfce-polkit, etc.)
2. Compiles EWW from source (requires Rust)
3. Copies all configs to `~/.config/`
4. Deploys greetd config to `/etc/greetd/config.toml`
5. Installs CyberQueer GTK theme and Nordzy-cursors-lefthand
6. Enables `ly@tty1`, `NetworkManager`, `bluetooth`, `iwd`, `udisks2`
7. Installs Python venv for scripts
8. Sets up the config-updater symlinks

View File

@ -0,0 +1,151 @@
#!/bin/bash
set -e
source "$(dirname "${BASH_SOURCE[0]}")/../lib/logging.sh"
log "Starting Niri installer (scrollable-tiling Wayland compositor)..."
# 1. Update system and install Flatpak
log "Updating system and installing Flatpak..."
sudo pacman -Syu --noconfirm --needed flatpak
# 2. Install required packages
log "Installing required packages..."
sudo pacman -Syu --noconfirm --needed \
niri xdg-desktop-portal-gnome wl-clipboard swaybg swaylock swayidle \
xfce-polkit gammastep \
wofi kitty dunst \
nwg-drawer nwg-menu nwg-look \
python cmake meson cpio pkgconf ruby-pkg-config \
nm-connection-editor network-manager-applet blueman bluez \
pipewire alsa-utils firefox greetd-tuigreet \
grim slurp gst-plugin-pipewire imagemagick \
nerd-fonts otf-font-awesome \
pipewire-alsa pipewire-jack pipewire-pulse \
qt5-wayland qt6-wayland swww ttf-jetbrains-mono wireplumber \
xdg-utils xorg-xinit papirus-icon-theme \
cool-retro-term qalculate-gtk iwd dbus \
thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \
pcmanfm-qt udisks2 ly kew \
pavucontrol playerctl wf-recorder sound-theme-freedesktop \
xinput jq
# 3. Enable essential services
log "Enabling essential services..."
sudo systemctl enable NetworkManager.service
sudo systemctl disable getty@tty1.service || true
sudo systemctl enable ly@tty1.service
sudo systemctl enable udisks2.service
# 4. Install AUR packages
log "Installing AUR packages..."
rustup default stable
yay -Syu --answerdiff None --answerclean All --noconfirm --needed \
vicinae-bin bluetuith wvkbd iwmenu pinta \
walker-bin ulauncher bzmenu udiskie \
wofi-calc bri chamel
# 5. EWW bar selection and compilation
log "Setting up EWW bar..."
rm -rf ~/.config/eww
read -n1 -p "Install eww bar for PC, Notebook or Tablet [P/N/T]: " doit
echo
case $doit in
n|N) cp -rf ~/Dotfiles/desktopenvs/niri/eww/ ~/.config/ ;;
p|P) cp -rf ~/Dotfiles/desktopenvs/niri/eww-nobattery/ ~/.config/eww ;;
t|T) cp -rf ~/Dotfiles/desktopenvs/niri/eww-touch/ ~/.config/eww ;;
*) warn "No valid choice — skipping EWW copy. Run manually later." ;;
esac
log "Compiling EWW..."
mkdir -p ~/install-tmp
cd ~/install-tmp
git clone https://github.com/elkowar/eww
cd eww
cargo build --release --no-default-features --features=wayland
chmod +x target/release/eww
sudo cp target/release/eww /usr/bin/
cd ~
# 6. Theme and icon setup
log "Installing themes and icons..."
sudo cp -r ~/Dotfiles/gtk-themes/cyberqueer /usr/share/themes
sudo cp ~/Dotfiles/desktopenvs/niri/btop/themes/cyberqueer.theme /usr/share/btop/themes
sudo cp -f ~/Dotfiles/etc-ly-config.ini /etc/ly/config.ini
sudo ln -sf /usr/bin/kitty /usr/bin/xdg-terminal-exec
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
# 7. Cursor setup
log "Installing cursor theme..."
mkdir -p ~/.icons
wget -O ~/install-tmp/Nordzy-cursors-lefthand.tar.gz \
https://github.com/guillaumeboehm/Nordzy-cursors/releases/download/v2.3.0/Nordzy-cursors-lefthand.tar.gz
tar -zxf ~/install-tmp/Nordzy-cursors-lefthand.tar.gz -C ~/.icons/
# 8. Enable Bluetooth and wireless services
log "Enabling Bluetooth and wireless services..."
sudo systemctl enable bluez
sudo systemctl enable bluetooth.service
sudo systemctl enable iwd.service
# 9. Copy configs
log "Copying configs..."
CONFIGS=(kitty mimeapps.list vicinae walker ulauncher niri nwg-drawer nwg-panel wofi dunst alacritty scripts btop gtk-3.0 waybar xfce4)
for cfg in "${CONFIGS[@]}"; do
rm -rf ~/.config/"$cfg"
cp -rL ~/Dotfiles/desktopenvs/niri/"$cfg" ~/.config/
done
cp ~/Dotfiles/colors.conf ~/.config/colors.conf
# 10. greetd — deploy niri session config
log "Deploying greetd config for niri..."
sudo mkdir -p /etc/greetd
sudo cp -f ~/Dotfiles/desktopenvs/niri/greetd-tuigreet/config.toml /etc/greetd/config.toml
sudo systemctl enable greetd.service
# 11. Wallpaper and resources
log "Copying wallpaper and resources..."
mkdir -p ~/Pictures ~/Pictures/Screenshots
cp ~/Dotfiles/resources/fflogo.svg ~/Pictures/fflogo.svg
wget "https://cloud.abdelbaki.eu/apps/theming/image/background?v=15" -O ~/Pictures/background.jpg
# 12. Python venv for scripts
log "Setting up Python venv for scripts..."
python -m venv ~/.config/python-script
~/.config/python-script/bin/pip install speedtest-cli requests pint simpleeval parsedatetime
# 13. Udiskie icon fix
log "Applying Udiskie icon fix..."
PAPIRUS_DIR="/usr/share/icons/Papirus-Dark/status"
HICOLOR_DIR="/usr/share/icons/hicolor/scalable/status"
if [ -d "$PAPIRUS_DIR" ]; then
sudo ln -sf "$PAPIRUS_DIR/checkbox-checked.svg" "$HICOLOR_DIR/udiskie-checkbox-checked.svg"
sudo ln -sf "$PAPIRUS_DIR/checkbox-unchecked.svg" "$HICOLOR_DIR/udiskie-checkbox-unchecked.svg"
sudo gtk-update-icon-cache -f -t /usr/share/icons/hicolor
else
warn "Papirus-Dark not found — skipping udiskie icon fix."
fi
# 14. Enable udiskie
log "Enabling udiskie service..."
sudo systemctl enable udiskie.service
sudo systemctl start udiskie.service
# 15. Make scripts executable
log "Setting script permissions..."
chmod +x ~/.config/scripts/*.sh ~/.config/scripts/activewindow \
~/.config/scripts/wallpaper-picker ~/.config/scripts/windowswitcher \
~/.config/scripts/workspace ~/.config/scripts/workspacefocus/*.sh \
~/.config/scripts/toggle-layout.sh ~/.config/scripts/timer-pick \
~/.config/scripts/timer-run ~/.config/scripts/ulwatchdog.sh \
2>/dev/null || true
# 16. Install config updater and theme script
log "Installing config updater and theme script..."
mkdir -p ~/.config/config-updater
ln -sf ~/Dotfiles/desktopenvs/niri/config-updater/updater.conf ~/.config/config-updater/updater.conf
ln -sf ~/Dotfiles/desktopenvs/niri/config-updater/update-configs.sh ~/update-configs.sh
cp ~/Dotfiles/apply-theme.sh ~/apply-theme.sh
chmod +x ~/apply-theme.sh
log "Niri installation complete. Reboot to start."

View File

@ -312,8 +312,9 @@ if $ANSWERFILE_MODE; then
else
DE=$(dialog --backtitle "$BACKTITLE" \
--title " Desktop Environment " \
--menu "Select a desktop environment · Esc / none to skip:" 22 72 10 \
--menu "Select a desktop environment · Esc / none to skip:" 24 72 11 \
"hyprlua" "HyprLua — Hyprland with Lua config (recommended)" \
"niri" "Niri — scrollable-tiling Wayland compositor" \
"hyprland" "Hyprland — Wayland WM, hyprlang config (legacy)" \
"sway" "Sway — Wayland tiling WM" \
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
@ -421,7 +422,7 @@ if ! $ANSWERFILE_MODE; then
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n"
[[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
[[ "$DE" != "none" && "$DE" != "" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
if [[ -n "$SELECTED_APPS" ]]; then
SUMMARY+="\n Applications:\n"
@ -518,6 +519,7 @@ count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
if [[ "$DE" != "none" ]]; then
case "$DE" in
hyprlua) run_module "HyprLua" "$MODULES/Desktop-Environments/hyprlua.sh" ;;
niri) run_module "Niri" "$MODULES/Desktop-Environments/niri.sh" ;;
hyprland) run_module "Hyprland" "$MODULES/Desktop-Environments/hyprland.sh" ;;
sway) run_module "Sway" "$MODULES/Desktop-Environments/sway.sh" ;;
kde-plasma) run_module "KDE Plasma" "$MODULES/Desktop-Environments/kde-plasma.sh" ;;