Compare commits

...
21 Commits
Author SHA1 Message Date
mathias2784 0d61477072 include kitty noctalia theme 2026-10-31 10:31:07 +01:00
mathias2784 4846325583 hyprpm and binds for hyprtasking 2026-10-28 22:38:31 +01:00
mathias2784 1d654cf470 hyprmod shortcut description 2026-10-28 21:22:27 +01:00
mathias2784 e9ad0175a1 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-10-21 08:45:21 +02:00
mathias2784 093cd97a96 ktouch has floating window 2026-10-21 08:44:43 +02:00
Mathias Schneider f9a44ac5aa some config tweaks 2026-10-19 08:17:24 +02:00
mathias2784 7302e87fa3 noctalia config 2026-10-15 11:28:18 +01:00
mathias2784 4434feed16 skripte 2026-10-15 10:24:18 +01:00
mathias2784 94fa42672c noctalia: start menu command fix 2026-10-15 10:48:11 +02:00
mathias2784 3c1b727a7e caelestia: show weather 2026-10-14 20:43:17 +02:00
mathias2784 c7bf98775b rusr bin on path 2026-10-13 21:22:04 +01:00
mathias2784 590e568061 bitwarden floating 2026-10-13 21:21:36 +01:00
mathias2784 0e7f76200c window rule for keybinds window 2026-10-12 21:11:47 +01:00
mathias2784 30cab18e60 some more hyprland tweaks 2026-10-12 15:15:02 +01:00
mathias2784 e5dd0039a2 yazi tweaks 2026-10-12 11:42:43 +01:00
mathias2784 00e4931d29 some tweaks 2026-10-12 11:40:05 +01:00
mathias2784 e5ce0d0957 remove custom screensavers (let shells handle it) 2026-10-11 22:37:45 +01:00
mathias2784 540baacb15 new and better keybinds window 2026-10-11 22:21:24 +01:00
mathias2784 99b0075ada define floating windows 2026-10-11 22:00:09 +01:00
mathias2784 049e9a9f5e some package fix 2026-10-11 11:52:26 +01:00
mathias2784 1d2878ad81 some package reorganization 2026-10-11 11:28:22 +01:00
31 changed files with 616 additions and 602 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ hypr/.config/hypr/env.conf
hypr/.config/hypr/env.sh
hypr/.config/hypr/monitor_setup.lua
# Noctalia template output (shell-owned, regenerated on palette change)
hypr/.config/kitty/themes/noctalia.conf
# hypr/.config/kitty/themes/noctalia.conf
# Caelestia runtime output (shell-owned, regenerated on scheme change)
hypr/.config/hypr/scheme/
*~lock.*#
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SERVER="buntes-monster"
SERVER_IP="192.168.0.4"
MOUNTS=(
"/mnt/anbau:/mnt/anbau"
"/mnt/bilder:/mnt/bilder"
)
# Re-run as root when needed.
if [[ "${EUID}" -ne 0 ]]; then
exec sudo "$0" "$@"
fi
# Preserve ownership for the user who invoked sudo.
OWNER="${SUDO_USER:-${USER}}"
OWNER_UID="$(id -u "$OWNER")"
OWNER_GID="$(id -g "$OWNER")"
for entry in "${MOUNTS[@]}"; do
IFS=":" read -r remote_path local_path <<< "$entry"
# Create the mount point if necessary and give it to the invoking user.
install -d \
-o "$OWNER_UID" \
-g "$OWNER_GID" \
-m 0755 \
"$local_path"
# Do not remount an already-mounted location.
if mountpoint -q "$local_path"; then
echo "Already mounted: $local_path"
continue
fi
echo "Mounting $SERVER:$remote_path at $local_path..."
mount \
-t nfs \
-o rw,sync,addr="$SERVER_IP" \
"$SERVER:$remote_path" \
"$local_path"
echo "Mounted: $local_path"
done
+3
View File
@@ -7,6 +7,9 @@
"background": {
"wallpaperEnabled": true
},
"dashboard": {
"showWeather": true
},
"general": {
"apps": {
"audio": [
-33
View File
@@ -1,33 +0,0 @@
# hypridle.conf — screensaver only.
# Managed by dotfiles (stow hypr).
#
# IDLE OWNERSHIP: Noctalia owns dim / lock / screen-off / suspend via its own
# [idle] block in ~/.config/noctalia/config.toml. hypridle is started only for
# the Noctalia shell (see hyprland.lua) and contributes just the tte
# screensaver. Do not re-add lock/DPMS listeners here — that duplication is
# what made two idle managers fight over the same transitions.
#
# lock_cmd goes through scripts/lock.sh, which resolves the active shell from
# env.lua. That keeps a manually started hypridle under Caelestia from calling
# `noctalia ...` (which would silently do nothing).
#
# WARNING — do not add DPMS dispatchers back to this file. This Hyprland
# (hyprmod Lua runtime, 0.56.x) evaluates `hyprctl dispatch` args as Lua, and
# the dpms dispatcher takes a TABLE, not a boolean:
# WRONG (silently becomes a TOGGLE, ignoring the argument):
# hyprctl dispatch 'hl.dsp.dpms(true)'
# RIGHT:
# hyprctl dispatch 'hl.dsp.dpms({action="enable"})'
# A toggle in after_sleep_cmd turns the display OFF on resume, and with
# misc:key_press_enables_dpms = false nothing could wake it again.
general {
lock_cmd = ~/.config/hypr/scripts/lock.sh
}
# Screensaver via backend wrappers (switch backend in modules/screensaver.lua).
listener {
timeout = 300
on-timeout = ~/.config/hypr/scripts/screensaver-start.sh
on-resume = ~/.config/hypr/scripts/screensaver-stop.sh
}
-6
View File
@@ -40,11 +40,6 @@ hl.on("hyprland.start", function()
else -- default: "noctalia"
hl.exec_cmd("noctalia &")
end
-- activate hypridle when not using Caelestia (has its own idle-ing mechnism)
if env.shell ~= "caelestia" then
hl.exec_cmd("hypridle &")
end
hl.exec_cmd("sh ~/.config/hypr/scripts/portal-host-override.sh")
hl.exec_cmd("sh ~/.config/hypr/scripts/ensure-mime-defaults.sh")
end)
@@ -74,4 +69,3 @@ if fid_gui2 then
fid_gui2:close()
require("hyprland-gui")
end
-40
View File
@@ -1,40 +0,0 @@
# hyprsaver.toml — chill playlist: calm shaders + cool palettes, cycling.
# Location: ~/.config/hypr/hyprsaver.toml (stowed from dotfiles).
# Names verified against `hyprsaver --list-shaders` / `--list-palettes`.
# Everything else uses hyprsaver's built-in defaults (see
# /usr/share/hyprsaver/examples/config.toml for the full reference).
[general]
fps = 30
shader = "cycle"
palette = "cycle"
shader_cycle_interval = 300
palette_cycle_interval = 60
cycle_order = "random"
synced = true
shader_playlist = "chill"
palette_playlist = "chill"
[playlists.chill]
shaders = [
"plasma",
"marble",
"snowfall",
"aurora",
"clouds",
"planet",
"starfield",
"fireflies",
]
palettes = [
"vaporwave",
"frost",
"ocean",
"aurora",
]
[behavior]
fade_in_ms = 800
fade_out_ms = 400
dismiss_on = ["key", "mouse_move", "mouse_click", "touch"]
exclusive_keyboard = true
@@ -0,0 +1,63 @@
-- keybind_registry.lua — wrap hl.bind so every keybind records a runnable
-- action alongside its chord/description.
--
-- Hyprland's Lua API registers binds through hl.bind(keys, dispatcher, opts),
-- and `hyprctl binds -j` reports those as `dispatcher: "__lua"` with an opaque
-- registry reference — the underlying command is not recoverable from the
-- compositor afterwards. This module captures the action at registration time:
--
-- exec(cmd) -> a tagged placeholder for the exec_cmd dispatcher (the final
-- command string is resolved here, so concatenations like
-- "hyprshot ... " .. var.screenshot_folder work automatically)
-- bind(...) -> same signature/behavior as hl.bind; converts the exec
-- placeholder back into a real dispatcher and records the row
-- write(path)-> dump the registry as "keys\x1fdescription\x1fcmd" TSV so the
-- show-keybinds picker can execute the selected entry
--
-- Pure dispatcher binds (hl.dsp.window.close(), hl.dsp.focus(...), ...) pass
-- through unchanged and are recorded with an empty cmd (informational only).
local registry = {}
local M = {}
--- Tag a shell command so bind() knows to turn it into an exec_cmd dispatcher.
function M.exec(cmd)
return { __cmd = cmd }
end
--- Register a keybind like hl.bind, but record it in the registry first.
--- @param keys string final key string (e.g. "SUPER + Q")
--- @param action any M.exec(cmd) or a real dispatcher from hl.dsp.*
--- @param opts? table hl.bind options (description, flags, ...)
function M.bind(keys, action, opts)
opts = opts or {}
local dsp = action
local cmd = ""
if type(action) == "table" and action.__cmd ~= nil then
cmd = action.__cmd
dsp = hl.dsp.exec_cmd(cmd)
end
table.insert(registry, { keys = keys, description = opts.description or "", cmd = cmd })
return hl.bind(keys, dsp, opts)
end
--- Dump the registry as "keys\x1fdescription\x1fcmd" lines (one per bind).
function M.write(path)
local f, err = io.open(path, "w")
if not f then
return nil, err
end
for _, b in ipairs(registry) do
f:write(b.keys, "\x1f", b.description, "\x1f", b.cmd, "\n")
end
f:close()
return registry
end
--- Access the raw registry rows ({keys, description, cmd}).
function M.get()
return registry
end
return M
+116 -109
View File
@@ -1,138 +1,142 @@
local var = require("modules.variables")
local command = require("modules.shell_commands")
local env = require("env")
-- bind/exec wrap hl.bind so each keybind also records a runnable command for
-- the show-keybinds picker (see modules/keybind_registry.lua).
local reg = require("modules.keybind_registry")
local bind = reg.bind
local exec = reg.exec
-- See https://wiki.hypr.land/Configuring/Keywords/
-- Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
hl.bind(var.mainMod .. " + RETURN", hl.dsp.exec_cmd(var.terminal), { description = "Open terminal (kitty)" })
hl.bind(var.mainMod .. " + ALT + RETURN", hl.dsp.exec_cmd(var.terminal_tmux), { description = "Open terminal (kitty)" })
hl.bind(var.mainMod .. " + Q", hl.dsp.window.close(), { description = "Close focused window" })
hl.bind(
bind(var.mainMod .. " + RETURN", exec(var.terminal), { description = "Open terminal" })
bind(var.mainMod .. " + ALT + RETURN", exec(var.terminal_tmux), { description = "Open tmux in terminal" })
bind(var.mainMod .. " + Q", hl.dsp.window.close(), { description = "Close focused window" })
bind(
var.mainMod .. " + ESCAPE",
hl.dsp.exec_cmd(command.session),
exec(command.session),
{ description = "Session menu (logout/reboot/shutdown)" }
)
hl.bind(var.mainMod .. " + SHIFT + ESCAPE", hl.dsp.exec_cmd(command.logout), { description = "Exit Hyprland" })
hl.bind(var.mainMod .. " + CTRL + ESCAPE", hl.dsp.exec_cmd(command.shutdown), { description = "Power off the system" })
hl.bind(var.mainMod .. " + ALT + ESCAPE", hl.dsp.exec_cmd(command.reboot), { description = "Reboot the system" })
hl.bind(var.mainMod .. " + E", hl.dsp.exec_cmd(var.fileManager), { description = "Open file manager (dolphin)" })
hl.bind(var.mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }), { description = "Toggle floating window" })
hl.bind(var.mainMod .. " + SPACE", hl.dsp.exec_cmd(command.menu), { description = "Open app launcher" })
hl.bind(var.mainMod .. " + P", hl.dsp.window.pseudo(), { description = "Toggle pseudo-tiling" })
hl.bind(var.mainMod .. " + SHIFT + J", hl.dsp.layout("togglesplit"), { description = "Toggle split layout" })
hl.bind(var.mainMod .. " + I", hl.dsp.exec_cmd("firefox &"), { description = "Open Firefox" })
hl.bind(var.mainMod .. " + n", hl.dsp.exec_cmd("nm-connection-editor"), { description = "Network manager" })
hl.bind(var.mainMod .. " + comma", hl.dsp.exec_cmd(command.settings), { description = "settings menu" })
hl.bind(var.mainMod .. " + S", hl.dsp.exec_cmd(command.control_center), { description = "Control center" })
bind(var.mainMod .. " + SHIFT + ESCAPE", exec(command.logout), { description = "Session: logout" })
bind(var.mainMod .. " + CTRL + ESCAPE", exec(command.shutdown), { description = "Session: power off" })
bind(var.mainMod .. " + ALT + ESCAPE", exec(command.reboot), { description = "Session: reboot" })
bind(var.mainMod .. " + E", exec(var.fileManager), { description = "Open file manager" })
bind(var.mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }), { description = "Floating window toggle" })
bind(var.mainMod .. " + SPACE", exec(command.menu), { description = "Open app launcher" })
bind(var.mainMod .. " + P", hl.dsp.window.pseudo(), { description = "Pseudo-tiling toggle" })
bind(var.mainMod .. " + SHIFT + J", hl.dsp.layout("togglesplit"), { description = "Split layout toggle" })
bind(var.mainMod .. " + I", exec(var.internet_explorer), { description = "Open internet explorer" })
bind(var.mainMod .. " + n", exec("nm-connection-editor"), { description = "Open network manager" })
bind(var.mainMod .. " + comma", exec(command.settings), { description = "Open settings" })
bind(var.mainMod .. " + S", exec(command.control_center), { description = "Open start menu" })
bind(var.mainMod .. " + ALT + H", exec("hyprmod"), {description = "Open hyprmod (hyprland config GUI)"})
bind(var.mainMod .. " + period", exec(command.emoji), { description = "Open emoji picker" })
bind(var.mainMod .. " + ALT + S", exec(var.systemmonitor), {description = "Open systemmonitor"})
bind(var.mainMod .. " + TAB", function()
hl.plugin.hyprtasking.toggle("cursor")
end, { description = "Workspace: overview" })
-- Move focus with mainMod or Vim-style
hl.bind(var.mainMod .. " + CTRL + h", hl.dsp.focus({ direction = "left" }), { description = "Focus window left" })
hl.bind(var.mainMod .. " + CTRL + l", hl.dsp.focus({ direction = "right" }), { description = "Focus window right" })
hl.bind(var.mainMod .. " + CTRL + k", hl.dsp.focus({ direction = "up" }), { description = "Focus window up" })
hl.bind(var.mainMod .. " + CTRL + j", hl.dsp.focus({ direction = "down" }), { description = "Focus window down" })
bind(var.mainMod .. " + CTRL + h", hl.dsp.focus({ direction = "left" }), { description = "Window: focus left" })
bind(var.mainMod .. " + CTRL + l", hl.dsp.focus({ direction = "right" }), { description = "Window: focus right" })
bind(var.mainMod .. " + CTRL + k", hl.dsp.focus({ direction = "up" }), { description = "Window: focus up" })
bind(var.mainMod .. " + CTRL + j", hl.dsp.focus({ direction = "down" }), { description = "Window: focus down" })
-- Move windows with mainMod or Vim-style
hl.bind(var.mainMod .. " + SHIFT + h", hl.dsp.window.move({ direction = "left" }), { description = "Move window left" })
hl.bind(
bind(var.mainMod .. " + SHIFT + h", hl.dsp.window.move({ direction = "left" }), { description = "Window: move left" })
bind(
var.mainMod .. " + SHIFT + l",
hl.dsp.window.move({ direction = "right" }),
{ description = "Move window right" }
{ description = "Window: move right" }
)
hl.bind(var.mainMod .. " + SHIFT + k", hl.dsp.window.move({ direction = "up" }), { description = "Move window up" })
hl.bind(var.mainMod .. " + SHIFT + j", hl.dsp.window.move({ direction = "down" }), { description = "Move window down" })
bind(var.mainMod .. " + SHIFT + k", hl.dsp.window.move({ direction = "up" }), { description = "Window: move up" })
bind(var.mainMod .. " + SHIFT + j", hl.dsp.window.move({ direction = "down" }), { description = "Window: move down" })
-- Resize focused window with mainMod + Arrows
hl.bind(
bind(
var.mainMod .. " + left",
hl.dsp.window.resize({ x = -10, y = 0, relative = true }),
{ description = "Resize window left" }
{ description = "Window: resize left" }
)
hl.bind(
bind(
var.mainMod .. " + right",
hl.dsp.window.resize({ x = 10, y = 0, relative = true }),
{ description = "Resize window right" }
{ description = "Window: resize right" }
)
hl.bind(
bind(
var.mainMod .. " + up",
hl.dsp.window.resize({ x = 0, y = -10, relative = true }),
{ description = "Resize window up" }
{ description = "Window: resize up" }
)
hl.bind(
bind(
var.mainMod .. " + down",
hl.dsp.window.resize({ x = 0, y = 10, relative = true }),
{ description = "Resize window down" }
{ description = "Window: resize down" }
)
-- Switch workspaces with mainMod + [0-9]
hl.bind(var.mainMod .. " + 1", hl.dsp.focus({ workspace = 1 }), { description = "Switch to workspace 1" })
hl.bind(var.mainMod .. " + 2", hl.dsp.focus({ workspace = 2 }), { description = "Switch to workspace 2" })
hl.bind(var.mainMod .. " + 3", hl.dsp.focus({ workspace = 3 }), { description = "Switch to workspace 3" })
hl.bind(var.mainMod .. " + 4", hl.dsp.focus({ workspace = 4 }), { description = "Switch to workspace 4" })
hl.bind(var.mainMod .. " + 5", hl.dsp.focus({ workspace = 5 }), { description = "Switch to workspace 5" })
hl.bind(var.mainMod .. " + 6", hl.dsp.focus({ workspace = 6 }), { description = "Switch to workspace 6" })
hl.bind(var.mainMod .. " + 7", hl.dsp.focus({ workspace = 7 }), { description = "Switch to workspace 7" })
hl.bind(var.mainMod .. " + 8", hl.dsp.focus({ workspace = 8 }), { description = "Switch to workspace 8" })
hl.bind(var.mainMod .. " + 9", hl.dsp.focus({ workspace = 9 }), { description = "Switch to workspace 9" })
hl.bind(var.mainMod .. " + 0", hl.dsp.focus({ workspace = 10 }), { description = "Switch to workspace 10" })
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"), {
repeating = true,
description = "Volume up",
})
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
locked = true,
description = "Volume down",
})
bind(var.mainMod .. " + 1", hl.dsp.focus({ workspace = 1 }), { description = "Workspace: switch to 1" })
bind(var.mainMod .. " + 2", hl.dsp.focus({ workspace = 2 }), { description = "Workspace: Switch to 2" })
bind(var.mainMod .. " + 3", hl.dsp.focus({ workspace = 3 }), { description = "Workspace: Switch to 3" })
bind(var.mainMod .. " + 4", hl.dsp.focus({ workspace = 4 }), { description = "Workspace: Switch to 4" })
bind(var.mainMod .. " + 5", hl.dsp.focus({ workspace = 5 }), { description = "Workspace: Switch to 5" })
bind(var.mainMod .. " + 6", hl.dsp.focus({ workspace = 6 }), { description = "Workspace: Switch to 6" })
bind(var.mainMod .. " + 7", hl.dsp.focus({ workspace = 7 }), { description = "Workspace: Switch to 7" })
bind(var.mainMod .. " + 8", hl.dsp.focus({ workspace = 8 }), { description = "Workspace: Switch to 8" })
bind(var.mainMod .. " + 9", hl.dsp.focus({ workspace = 9 }), { description = "Workspace: Switch to 9" })
bind(var.mainMod .. " + 0", hl.dsp.focus({ workspace = 10 }), { description = "Workspace: Switch to 10" })
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
hl.bind(
bind(
var.mainMod .. " + SHIFT + 1",
hl.dsp.window.move({ workspace = 1 }),
{ description = "Move window to workspace 1" }
{ description = "Window: move to workspace 1" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 2",
hl.dsp.window.move({ workspace = 2 }),
{ description = "Move window to workspace 2" }
{ description = "Window: move to workspace 2" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 3",
hl.dsp.window.move({ workspace = 3 }),
{ description = "Move window to workspace 3" }
{ description = "Window: move to workspace 3" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 4",
hl.dsp.window.move({ workspace = 4 }),
{ description = "Move window to workspace 4" }
{ description = "Window: move to workspace 4" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 5",
hl.dsp.window.move({ workspace = 5 }),
{ description = "Move window to workspace 5" }
{ description = "Window: move to workspace 5" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 6",
hl.dsp.window.move({ workspace = 6 }),
{ description = "Move window to workspace 6" }
{ description = "Window: move to workspace 6" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 7",
hl.dsp.window.move({ workspace = 7 }),
{ description = "Move window to workspace 7" }
{ description = "Window: move to workspace 7" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 8",
hl.dsp.window.move({ workspace = 8 }),
{ description = "Move window to workspace 8" }
{ description = "Window: move to workspace 8" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 9",
hl.dsp.window.move({ workspace = 9 }),
{ description = "Move window to workspace 9" }
{ description = "Window: move to workspace 9" }
)
hl.bind(
bind(
var.mainMod .. " + SHIFT + 0",
hl.dsp.window.move({ workspace = 10 }),
{ description = "Move window to workspace 10" }
{ description = "Window: move to workspace 10" }
)
-- # Example special workspace (scratchpad)
@@ -142,100 +146,103 @@ hl.bind(
-- bind = $mainMod SHIFT, S, movetoworkspace, special:magic
-- Scroll through existing workspaces with mainMod + scroll
hl.bind(
bind(
var.mainMod .. " + mouse_down",
hl.dsp.focus({ workspace = "e+1" }),
{ description = "Next workspace (scroll down)" }
{ description = "Workspace: next (scroll down)" }
)
hl.bind(
bind(
var.mainMod .. " + mouse_up",
hl.dsp.focus({ workspace = "e-1" }),
{ description = "Previous workspace (scroll up)" }
{ description = "Workspace: previous (scroll up)" }
)
-- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(var.mainMod .. " + mouse:272", hl.dsp.window.drag(), {
bind(var.mainMod .. " + mouse:272", hl.dsp.window.drag(), {
mouse = true,
description = "Move window with mouse",
description = "Window: move with mouse",
})
hl.bind(var.mainMod .. " + mouse:273", hl.dsp.window.resize(), {
bind(var.mainMod .. " + mouse:273", hl.dsp.window.resize(), {
mouse = true,
description = "Resize window with mouse",
description = "Window: resize with mouse",
})
-- Laptop multimedia keys for volume and LCD brightness
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), {
bind("XF86AudioRaiseVolume", exec("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), {
repeating = true,
locked = true,
description = "Volume up (repeat)",
description = "Media: volume up (repeat)",
})
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
bind("XF86AudioLowerVolume", exec("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
repeating = true,
locked = true,
description = "Volume down (repeat)",
description = "Media: volume down (repeat)",
})
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), {
bind("XF86AudioMute", exec("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), {
repeating = true,
locked = true,
description = "Mute audio",
description = "Media: mute audio",
})
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), {
bind("XF86AudioMicMute", exec("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), {
repeating = true,
locked = true,
description = "Mute microphone",
description = "Media: mute microphone",
})
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%+"), {
bind("XF86MonBrightnessUp", exec("brightnessctl -e4 -n2 set 5%+"), {
repeating = true,
locked = true,
description = "Brightness up",
})
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%-"), {
bind("XF86MonBrightnessDown", exec("brightnessctl -e4 -n2 set 5%-"), {
repeating = true,
locked = true,
description = "Brightness down",
})
-- Requires playerctl
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), {
bind("XF86AudioNext", exec("playerctl next"), {
locked = true,
description = "Next track",
description = "Media: next track",
})
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), {
bind("XF86AudioPause", exec("playerctl play-pause"), {
locked = true,
description = "Play/Pause",
description = "Media: play/pause",
})
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), {
bind("XF86AudioPlay", exec("playerctl play-pause"), {
locked = true,
description = "Play/Pause",
description = "Media: play/pause",
})
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), {
bind("XF86AudioPrev", exec("playerctl previous"), {
locked = true,
description = "Previous track",
description = "Media: previous track",
})
-- Screenshots
hl.bind("PRINT", hl.dsp.exec_cmd("hyprshot -m region --output-folder " .. var.screenshot_folder), {
bind("PRINT", exec("hyprshot -m region --output-folder " .. var.screenshot_folder), {
locked = true,
description = "Screenshot region",
})
hl.bind("CTRL + PRINT", hl.dsp.exec_cmd("hyprshot -m window --output-folder " .. var.screenshot_folder), {
bind("CTRL + PRINT", exec("hyprshot -m window --output-folder " .. var.screenshot_folder), {
locked = true,
description = "Screenshot window",
})
hl.bind("SHIFT + PRINT", hl.dsp.exec_cmd("hyprshot -m active --output-folder " .. var.screenshot_folder), {
bind("SHIFT + PRINT", exec("hyprshot -m active --output-folder " .. var.screenshot_folder), {
locked = true,
description = "Screenshot active monitor",
})
hl.bind(var.mainMod .. " + L", hl.dsp.exec_cmd(command.lock), { locked = true, description = "Lock screen" })
hl.bind(var.mainMod .. " + SHIFT + L", hl.dsp.exec_cmd(command.screensaver), { description = "Start screensaver (any key quits)" })
hl.bind(var.mainMod .. " + K", hl.dsp.exec_cmd(command.keybinds_show), { description = "Show keybind overview" })
hl.bind(var.mainMod .. " + W", hl.dsp.exec_cmd(command.wallpaper_select), { description = "Wallpaper select" })
bind(var.mainMod .. " + L", exec(command.lock), { locked = true, description = "Lock screen" })
bind(var.mainMod .. " + K", exec(command.keybinds_show), { description = "Keybinds: show overview" })
bind(var.mainMod .. " + W", exec(command.wallpaper_select), { description = "Wallpaper select" })
-- Caelestia extras
if env.shell == "caelestia" then
hl.bind(var.mainMod .. " + SHIFT + V", hl.dsp.exec_cmd(command.clipboard), { description = "Clipboard history" })
hl.bind(var.mainMod .. " + period", hl.dsp.exec_cmd(command.emoji), { description = "Emoji picker" })
hl.bind(var.mainMod .. " + SHIFT + K", hl.dsp.exec_cmd(command.show_all), { description = "Toggle utilities" })
hl.bind("CTRL + ALT + C", hl.dsp.exec_cmd(command.clear_notifications), { description = "Clear notifications" })
bind(var.mainMod .. " + SHIFT + V", exec(command.clipboard), { description = "Clipboard history" })
bind(var.mainMod .. " + SHIFT + K", exec(command.show_all), { description = "Toggle utilities" })
bind("CTRL + ALT + C", exec(command.clear_notifications), { description = "Clear notifications" })
end
-- Persist the registry for the show-keybinds picker
-- (scripts/show-keybinds.sh reads this TSV to resolve selected commands).
-- Runs on every config load/reload, keeping the picker in sync with the binds.
reg.write("/tmp/hypr-keybinds.tsv")
-33
View File
@@ -1,33 +0,0 @@
-- Single source of truth for "how do I lock this session", keyed on the
-- active shell in env.lua. Consumed by two callers:
-- * scripts/lock.sh (hypridle's lock_cmd) - loaded via plain dofile
-- * modules/shell_commands.lua (SUPER+L) - loaded via require
--
-- Both contexts load this file, so it must NOT use require("env"): Lua's
-- default package.path is cwd-relative, and that only resolves inside
-- ~/.config/hypr. hypridle's cwd is not guaranteed. Resolve by path instead.
local config_home = os.getenv("XDG_CONFIG_HOME") or os.getenv("HOME") .. "/.config"
local prefix = config_home .. "/hypr/"
local function load_env()
local fid = io.open(prefix .. "env.lua")
if fid then
fid:close()
return dofile(prefix .. "env.lua")
end
return dofile(prefix .. "env_example.lua")
end
local LOCK = {
noctalia = "noctalia msg session lock",
caelestia = "caelestia shell lock lock",
}
local env = load_env()
return {
shell = env.shell,
-- nil for an unknown shell; scripts/lock.sh treats that as a hard error
-- rather than reporting success while nothing is actually locked.
cmd = LOCK[env.shell],
}
-13
View File
@@ -1,13 +0,0 @@
-- Screensaver commands for idle (hypridle on-timeout/on-resume) and manual
-- (SUPER+SHIFT+L) use. scripts/screensaver-start.sh and
-- scripts/screensaver-stop.sh exec the .start / .stop command below.
--
-- Active backend: tte (fullscreen kitty + terminal-text-effects).
return {
start = "~/.config/hypr/scripts/launch-screensaver.sh",
stop = "~/.config/hypr/scripts/screensaver-stop.sh",
-- Alternative backend: hyprsaver (GPU fractal shaders, wlr-layer-shell).
-- Swap the commented pair in to switch (no other file changes needed).
-- start = "hyprsaver",
-- stop = "hyprsaver --quit",
}
+42 -43
View File
@@ -1,53 +1,52 @@
local env = require("env")
-- Shell-agnostic on purpose: the active shell is resolved inside lock.sh from
-- env.lua, so hypridle's lock_cmd and this bind cannot drift apart.
local LOCK = "~/.config/hypr/scripts/lock.sh"
local commands = {
session = "",
logout = "",
shutdown = "",
reboot = "",
menu = "",
settings = "",
control_center = "",
lock = LOCK,
screensaver = "sh ~/.config/hypr/scripts/screensaver-start.sh force",
wallpaper_select = "",
keybinds_show = "sh ~/.config/hypr/scripts/show-keybinds.sh",
clipboard = "",
clipboard_delete = "",
emoji = "",
show_all = "",
clear_notifications = "",
session = "",
logout = "",
shutdown = "",
reboot = "",
menu = "",
settings = "",
control_center = "",
lock = "",
wallpaper_select = "",
keybinds_show = "sh ~/.config/hypr/scripts/show-keybinds.sh",
clipboard = "",
clipboard_delete = "",
emoji = "",
show_all = "",
clear_notifications = "",
}
-- Noctalia
if env.shell == "noctalia" then
commands.session = "noctalia msg panel-toggle session"
commands.logout = "command -v noctalia >/dev/null 2>&1 && noctalia msg session logout || hyprctl dispatch exit"
commands.shutdown = "noctalia msg session shutdown"
commands.reboot = "noctalia msg session reboot"
commands.menu = "noctalia msg panel-toggle launcher"
commands.settings = "noctalia msg settings-toggle"
commands.control_center = "noctalia msg panel-toggle control-center"
commands.wallpaper_select = "noctalia msg panel-toggle wallpaper"
commands.session = "noctalia msg panel-toggle session"
commands.logout = "command -v noctalia >/dev/null 2>&1 && noctalia msg session logout || hyprctl dispatch exit"
commands.shutdown = "noctalia msg session shutdown"
commands.reboot = "noctalia msg session reboot"
commands.menu = "noctalia msg panel-toggle launcher"
commands.settings = "noctalia msg settings-toggle"
commands.control_center = "noctalia msg panel-toggle control-center"
commands.wallpaper_select = "noctalia msg panel-toggle wallpaper"
commands.lock = "noctalia msg session lock"
commands.emoji = "noctalia msg panel-toggle launcher '/emo '"
-- Caelesia
elseif env.shell == "caelestia" then
commands.session = "caelestia shell drawers toggle session"
commands.logout = "hyprctl dispatch exit"
commands.shutdown = "systemctl poweroff"
commands.reboot = "systemctl reboot"
commands.menu = "caelestia shell drawers toggle launcher"
commands.settings = "caelestia shell drawers toggle dashboard"
commands.control_center = "caelestia shell drawers toggle sidebar"
commands.wallpaper_select = "caelestia shell drawers toggle launcher && sleep 0.1 && wtype \">wallpaper \""
commands.clipboard = "caelestia clipboard"
commands.clipboard_delete = "caelestia clipboard -d"
commands.emoji = "caelestia emoji -p"
commands.show_all = "caelestia shell drawers toggle utilities"
commands.clear_notifications = "caelestia shell notifs clear"
commands.session = "caelestia shell drawers toggle session"
commands.logout = "hyprctl dispatch exit"
commands.shutdown = "systemctl poweroff"
commands.reboot = "systemctl reboot"
commands.menu = "caelestia shell drawers toggle launcher"
commands.settings = "caelestia shell drawers toggle dashboard"
commands.control_center = "caelestia shell drawers toggle sidebar"
commands.wallpaper_select = 'caelestia shell drawers toggle launcher && sleep 0.1 && wtype ">wallpaper "'
commands.clipboard = "caelestia clipboard"
commands.clipboard_delete = "caelestia clipboard -d"
commands.emoji = "caelestia emoji -p"
commands.show_all = "caelestia shell drawers toggle utilities"
commands.clear_notifications = "caelestia shell notifs clear"
commands.lock = "caelestia shell lock lock"
end
return commands
+3 -1
View File
@@ -18,6 +18,8 @@ return {
mainMod = "SUPER",
terminal = terminal,
terminal_tmux = terminal .. " -e tmux",
fileManager = "dolphin",
fileManager = terminal .. " -e yazi", -- "dolphin",
screenshot_folder = "~/Bilder/Screenshots/",
internet_explorer = "firefox",
systemmonitor = terminal .. " -e btop",
}
@@ -1,5 +1,3 @@
-- See https://wiki.hypr.land/Configuring/Window-Rules/ for more
-- See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
@@ -64,6 +62,12 @@ hl.window_rule({
},
opacity = "0.90 0.80",
})
hl.window_rule({
match = {
class = ".*keybinds.*",
},
opacity = "0.75 0.50",
})
-- Noctalia shell surfaces: blur bars/panels/dock/notifications and let
-- Noctalia run its own animations (no Hyprland layer animation on top).
hl.layer_rule({
@@ -103,36 +107,9 @@ hl.window_rule({
border_size = 0,
})
-- Manual tte screensaver preview (SUPER+SHIFT+L): fullscreen kitty.
hl.window_rule({
name = "screensaver-fullscreen",
match = {
class = "org.screensaver",
},
fullscreen = true,
name = "floating-windows",
float = true,
match = { class = "(imv|.*hyprmod|.*[Bb]itwarden.*|ktouch)" },
size = "1080 920",
})
-- windowrule {
-- name = no-rounding-steam-games
-- match:class = ^steam_app_.*$
-- rounding = 0
-- border_size = 0
-- }
-- windowrule {
-- name = no-rounding-gamescope
-- match:class = ^gamescope$
-- rounding = 0
-- border_size = 0
-- }
-4
View File
@@ -1,4 +0,0 @@
hyprland
// idle looks good on you
ArchTop is relaxing 🧘
@@ -1,51 +0,0 @@
#!/usr/bin/env bash
# launch-screensaver.sh — idle-triggered tte screensaver (hypridle on-timeout).
# Spawns one fullscreen kitty running screensaver-tte.sh per monitor.
# Usage: launch-screensaver.sh [force]
# force ignore the screensaver-off toggle file.
# Toggle off: touch ~/.local/state/hypr/screensaver-off
#
# Lessons baked in: no focus-restore after spawn (it raced the new window and
# caused flash-then-nothing), no pkill -f patterns anywhere (teardown is done
# by screensaver-stop.sh via exact client PIDs).
set -euo pipefail
SCREENSAVER_CLASS="org.screensaver"
TOGGLE_FILE="$HOME/.local/state/hypr/screensaver-off"
command -v tte >/dev/null 2>&1 || exit 1
command -v kitty >/dev/null 2>&1 || exit 1
# Already running (precise client check).
if hyprctl clients -j 2>/dev/null | jq -e '.[] | select(.class=="org.screensaver")' >/dev/null 2>&1; then
exit 0
fi
# Opt-out toggle (unless forced, e.g. manual invocation).
if [[ -f "$TOGGLE_FILE" ]] && [[ "${1:-}" != "force" ]]; then
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# hyprctl prints plain-text errors (not JSON) when HYPRLAND_INSTANCE_SIGNATURE
# is stale (e.g. shell/tmux predates the current compositor). Fail clearly
# instead of dumping a jq parse error.
monitors_json="$(hyprctl monitors -j 2>/dev/null)" || {
echo "launch-screensaver: hyprctl failed - is HYPRLAND_INSTANCE_SIGNATURE stale? (try a fresh terminal)" >&2
exit 1
}
echo "$monitors_json" | jq -e . >/dev/null 2>&1 || {
echo "launch-screensaver: hyprctl did not return JSON - stale HYPRLAND_INSTANCE_SIGNATURE?" >&2
exit 1
}
# NOTE: hyprmod Lua runtime (Hyprland 0.56.x) evaluates `hyprctl dispatch` args
# as Lua — classic `dispatch focusmonitor eDP-1` fails. Dispatchers must be
# hl.dsp.* expressions. Focus each monitor before exec so the saver kitty
# spawns there; deliberately no focus-restore afterwards (spawn race).
for m in $(echo "$monitors_json" | jq -r '.[] | .name'); do
hyprctl dispatch "hl.dsp.focus({monitor=\"$m\"})" >/dev/null 2>&1
hyprctl dispatch "hl.dsp.exec_cmd('kitty --class=\"$SCREENSAVER_CLASS\" --override font_size=18 --override window_padding_width=0 --override background_opacity=1 --override mouse_hide_wait=1.0 -e \"$SCRIPT_DIR/screensaver-tte.sh\"')" >/dev/null 2>&1
done
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# lock.sh — lock the session with the active shell's own lock command.
# The mapping lives in modules/lock.lua (reads env.lua), so neither hypridle's
# lock_cmd nor the SUPER+L bind hardcodes a shell. This is what keeps a
# manually started hypridle under Caelestia from failing on `noctalia ...`.
#
# Usage: lock.sh (no args; safe to call while locked)
set -euo pipefail
# Resolve from this script's own location so it works through the
# ~/.config/hypr symlink and does not depend on XDG_CONFIG_HOME.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCK_LUA="$(cd "$SCRIPT_DIR/.." && pwd)/modules/lock.lua"
CMD="$(LOCK_LUA="$LOCK_LUA" lua -e 'print(dofile(os.getenv("LOCK_LUA")).cmd)' 2>/dev/null)" || {
echo "lock: cannot read modules/lock.lua" >&2
exit 1
}
# Empty here means env.lua named a shell with no known lock command. Fail loudly
# instead of exiting 0, or a caller would think the session got locked.
[[ -n "$CMD" ]] || {
echo "lock: no lock command for the active shell (check env.lua)" >&2
exit 1
}
# Word-split on purpose: CMD is a command line, not a single path.
# shellcheck disable=SC2086
exec $CMD
@@ -1,24 +0,0 @@
#!/usr/bin/env bash
# screensaver-start.sh — exec the start command from modules/screensaver.lua.
# Usage: screensaver-start.sh [force] (forwarded to .sh targets, e.g. the tte
# launcher's toggle bypass; dropped for bare commands like `hyprsaver`).
set -euo pipefail
CMD="$(lua -e 'print(dofile(os.getenv("HOME").."/.config/hypr/modules/screensaver.lua").start)' 2>/dev/null)" || {
echo "screensaver-start: cannot read modules/screensaver.lua" >&2
exit 1
}
CMD="${CMD/#\~/$HOME}"
[[ -n "$CMD" ]] || {
echo "screensaver-start: empty start command" >&2
exit 1
}
if [[ "$CMD" == *.sh ]]; then
exec "$CMD" "$@"
else
# Bare command, may carry flags (e.g. "hyprsaver --quit").
# shellcheck disable=SC2086
exec $CMD
fi
@@ -1,36 +0,0 @@
#!/usr/bin/env bash
# screensaver-stop.sh — exec the stop command from modules/screensaver.lua.
# When it points back here (tte backend), run the built-in teardown below:
# kill saver windows by Hyprland client PID (class org.screensaver) plus
# stray tte processes (exact comm match). Never `pkill -f <pattern>`: a
# full-cmdline pattern also matches bystander shells merely mentioning it.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ME="$(realpath "$SCRIPT_DIR/screensaver-stop.sh")"
WANT="$(lua -e 'print(dofile(os.getenv("HOME").."/.config/hypr/modules/screensaver.lua").stop)' 2>/dev/null)" || {
echo "screensaver-stop: cannot read modules/screensaver.lua" >&2
exit 1
}
WANT="${WANT/#\~/$HOME}"
# Alternative backend configured (e.g. "hyprsaver --quit"): run it instead.
if [[ "$(realpath -m "$WANT")" != "$ME" ]]; then
# shellcheck disable=SC2086
exec $WANT
fi
# --- tte teardown ---
pids="$(hyprctl clients -j 2>/dev/null | jq -r '.[] | select(.class=="org.screensaver") | .pid' 2>/dev/null)" || pids=""
if [[ -n "${pids:-}" ]]; then
# shellcheck disable=SC2086
kill $pids 2>/dev/null || true
fi
# tte runs under comm "tte" (verified); -x matches the comm only, so this
# cannot hit bystander shells.
pkill -x tte 2>/dev/null || true
exit 0
@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# screensaver-tte.sh — runs inside each fullscreen tte kitty.
# Loops random tte effects until any keypress, then exits (kitty closes).
# Child teardown uses the exact PID ($!) — never pkill -f patterns, which
# would also match bystander shells mentioning the class name.
set -euo pipefail
SCREENSAVER_TEXT="$HOME/.config/hypr/screensaver.txt"
trap 'exit 0' SIGINT SIGTERM SIGHUP SIGQUIT
printf '\033]11;rgb:00/00/00\007' # black background
input="$SCREENSAVER_TEXT"
[[ -f "$input" ]] || input="/etc/hostname"
while true; do
tte -i <(fastfetch --pipe) \
--frame-rate 60 --canvas-width 0 --canvas-height 0 \
--anchor-canvas c --anchor-text c \
--random-effect --exclude-effects dev_worm \
--no-eol --no-restore-cursor &
TPID=$!
# Wait for tte to appear (Python startup) before polling it.
for _ in $(seq 1 10); do
kill -0 "$TPID" 2>/dev/null || break
pgrep -x tte >/dev/null 2>&1 && break
sleep 0.1
done
while kill -0 "$TPID" 2>/dev/null; do
if read -r -n1 -t 1; then
kill "$TPID" 2>/dev/null || true
wait "$TPID" 2>/dev/null || true
exit 0
fi
done
wait "$TPID" 2>/dev/null || true
# Throttle respawn if tte exits instantly (e.g. a sub-second effect).
sleep 0.2
done
+127 -59
View File
@@ -1,94 +1,162 @@
#!/usr/bin/env bash
# show-keybinds.sh — floating keybind overview popup.
# show-keybinds.sh — fuzzy, executable keybind picker.
#
# Reads the live keybind list from `hyprctl binds -j` and shows it in a
# floating kitty window (single column, sorted A-Z, searchable via less).
# Display-only: wofi/rofi are gone (the Noctalia launcher has no stdin-list
# mode), so picking a row to execute is not wired up.
# SUPER+K opens a floating kitty (class `keybinds`, see
# windows_and_workspaces.lua) running fzf over the keybind list:
# - start typing immediately: the fzf prompt is focused on open
# - arrow keys browse the (filtered) list
# - Enter executes the selected entry's command, then the picker closes
# - Esc closes without action
#
# Note: this config uses the Lua API (hl.bind), so `hyprctl binds -j` reports
# key + modmask + an opaque Lua registry reference instead of a readable
# dispatcher. The human-readable action comes from the `description` option
# added to each hl.bind(...) call - keep those descriptions up to date when
# you change a bind.
# Display data is read live from `hyprctl binds -j`. The command behind each
# entry cannot come from hyprctl — Lua-registered binds report as opaque
# `__lua` dispatchers there — so modules/keybind_registry.lua records every
# command at bind time and writes /tmp/hypr-keybinds.tsv on config load
# ("keys\x1fdescription\x1fcmd" lines). Entries without a recorded command
# (pure dispatchers like window.close) are informational only: Enter closes.
#
# Usage: show-keybinds.sh [--print]
# --print print the formatted list to stdout instead of opening kitty
# (handy for debugging / testing the formatting pipeline)
# Usage: show-keybinds.sh [--print | --internal-pick FILE]
# --print print the formatted list to stdout (debug/testing)
# --internal-pick F run fzf over picker file F and execute the selection
# (spawned by the default mode inside the kitty window)
set -euo pipefail
readonly SEP=$'\x1f'
# modmask bits reported by hyprctl (XKB modifiers), pairwise bit:name in
# display order. See https://wiki.hypr.land/Configuring/Binds/
readonly MOD_ORDER=(64 SUPER 8 ALT 4 CTRL 1 SHIFT 2 CAPS 16 MOD2 32 MOD3 128 MOD5)
readonly SIDECAR=/tmp/hypr-keybinds.tsv
if ! hyprctl binds -j >/dev/null 2>&1; then
echo "hyprctl binds failed - are you inside a Hyprland session?" >&2
# --internal-pick mode: runs INSIDE the floating kitty. fzf over the picker
# list (lines are "cmd\x1fdisplay"), then execute the chosen command and let
# the window close.
if [[ "${1:-}" == "--internal-pick" ]]; then
pick_file="${2:-}"
if [[ -z "$pick_file" || ! -s "$pick_file" ]]; then
echo "show-keybinds: --internal-pick needs an existing picker list file" >&2
exit 1
fi
# `|| rc=$?` keeps `set -e` from aborting before cleanup when fzf is
# cancelled (e.g. Esc) or filters to no match.
rc=0
sel="$(fzf --delimiter "$SEP" --with-nth 2.. --cycle --info=inline \
--prompt 'keybinds> ' --header 'type to filter · Enter executes · Esc closes' \
<"$pick_file")" || rc=$?
rm -f "$pick_file"
if ((rc != 0)) || [[ -z "$sel" ]]; then
exit 0
fi
cmd="${sel%%"$SEP"*}"
if [[ -n "$cmd" ]]; then
# Detach into its own session so the command outlives the picker
# window (kitty/pty teardown would otherwise signal its process group).
setsid -f sh -c "$cmd" >/dev/null 2>&1 </dev/null
fi
exit 0
fi
# ---------------------------------------------------------------------------
# Gather rows: prefer the sidecar (has commands); fall back to live `hyprctl
# binds -j` (display only, e.g. right after a fresh clone without a reload).
# ---------------------------------------------------------------------------
have_sidecar=0
if [[ -s "$SIDECAR" ]]; then
have_sidecar=1
fi
if ((! have_sidecar)) && ! hyprctl binds -j >/dev/null 2>&1; then
echo "show-keybinds: nothing to show: no keybind registry ($SIDECAR) and no Hyprland session" >&2
exit 1
fi
rows=()
cmds=()
keys=()
# modmask\x1fkey\x1fdescription\x1fsubmap\x1farg
# Fields are joined with the unit separator (0x1f): tabs are IFS-whitespace
# and `read` would collapse consecutive tabs, eating empty middle fields
# like the (usually empty) submap. \x1f is not whitespace, so every field
# keeps its position.
while IFS=$'\x1f' read -r modmask key description submap arg; do
# Humanize mouse-button binds.
case "$key" in
if ((have_sidecar)); then
while IFS="$SEP" read -r key description cmd; do
key="${key//mouse:272/LMB}"
key="${key//mouse:273/RMB}"
if [[ -z "$description" ]]; then
description="(no description)"
fi
rows+=("$(printf '%-26s %s' "$key" "$description")")
cmds+=("$cmd")
keys+=("$key")
done <"$SIDECAR"
else
while IFS="$SEP" read -r modmask key description submap; do
case "$key" in
mouse:272) key="LMB" ;;
mouse:273) key="RMB" ;;
esac
esac
# Build the chord, e.g. "SUPER + SHIFT + Q".
chord=""
i=0
while (( i < ${#MOD_ORDER[@]} )); do
bit=${MOD_ORDER[i]}
name=${MOD_ORDER[i + 1]}
if (( modmask & bit )); then
chord+="${name} + "
chord=""
# Rebuild "SUPER + SHIFT + Q" from the modmask bits.
i=0
while ((i < ${#MOD_ORDER[@]})); do
bit=${MOD_ORDER[i]}
name=${MOD_ORDER[i + 1]}
if ((modmask & bit)); then
chord+="${name} + "
fi
i=$((i + 2))
done
chord+="$key"
if [[ -n "$submap" ]]; then
chord="[$submap] $chord"
fi
i=$((i + 2))
done
chord+="$key"
if [[ -n "$submap" ]]; then
chord="[$submap] $chord"
fi
if [[ -n "$description" ]]; then
rows+=("$(printf '%-26s %s' "$chord" "$description")")
else
rows+=("$(printf '%-26s %s' "$chord" "(no description)")")
fi
cmds+=("")
keys+=("$chord")
done < <(hyprctl binds -j | jq -r '.[] | [(.modmask | tostring), .key, .description, .submap] | join("\u001f")')
fi
if [[ -n "$description" ]]; then
rows+=("$(printf '%-26s %s' "$chord" "$description")")
else
rows+=("$(printf '%-26s %s' "$chord" "(no description)")")
fi
done < <(hyprctl binds -j | jq -r '.[] | [(.modmask | tostring), .key, .description, .submap, .arg] | join("\u001f")')
if (( ${#rows[@]} == 0 )); then
echo "No keybinds found." >&2
if ((${#rows[@]} == 0)); then
echo "show-keybinds: no keybinds found." >&2
exit 1
fi
# Drop duplicate chords (e.g. media keys bound twice with different flags),
# then sort alphabetically (A-Z).
mapfile -t rows < <(printf '%s\n' "${rows[@]}" | awk '{ c = $0; sub(/ {2,}.*/, "", c); if (!seen[c]++) print }' | LC_ALL=C sort -f)
# keeping the first occurrence; carry the matching cmd along. Then sort by
# the display column A-Z.
declare -A seen=()
lines=()
for i in "${!rows[@]}"; do
if [[ -z "${seen[${keys[$i]}]:-}" ]]; then
seen["${keys[$i]}"]=1
lines+=("${cmds[$i]}$SEP${rows[$i]}")
fi
done
mapfile -t lines < <(printf '%s\n' "${lines[@]}" | LC_ALL=C sort -f -t "$SEP" -k2.29,2)
if [[ "${1:-}" == "--print" ]]; then
printf '%s\n' "${rows[@]}"
printf '%s\n' "${lines[@]}" | cut -d "$SEP" -f2-
exit 0
fi
# Floating display via kitty (class `keybinds` has a float rule in
# hyprland.lua). Rows go through a temp file rather than a stdin pipe so the
# popup does not depend on kitty forwarding stdin to its child; the previous
# run's file is cleaned up on the next invocation. q closes the window.
rm -f /tmp/keybinds.??????.txt 2>/dev/null || true
# ---------------------------------------------------------------------------
# Default mode: floating kitty + fzf. Rows go through a temp file; the inner
# --internal-pick run removes it when done, stale leftovers get cleaned on the
# next invocation.
# ---------------------------------------------------------------------------
rm -f /tmp/keybinds.??????.list 2>/dev/null || true
list="$(mktemp /tmp/keybinds.XXXXXX.list)"
printf '%s\n' "${lines[@]}" >"$list"
if command -v kitty >/dev/null 2>&1; then
tmp="$(mktemp /tmp/keybinds.XXXXXX.txt)"
printf '%s\n' "${rows[@]}" > "$tmp"
kitty --class keybinds --title Keybinds less -S "$tmp"
kitty --override font_size=16 --class keybinds --title Keybinds "$0" \
--internal-pick "$list"
exit 0
fi
printf '%s\n' "${rows[@]}"
# No kitty: fall back to a plain printout.
printf '%s\n' "${lines[@]}" | cut -d "$SEP" -f2-
rm -f "$list"
+32
View File
@@ -0,0 +1,32 @@
color0 #45475a
color1 #f38ba8
color2 #a6e3a1
color3 #f9e2af
color4 #89b4fa
color5 #f5c2e7
color6 #94e2d5
color7 #a6adc8
color8 #585b70
color9 #f37799
color10 #89d88b
color11 #ebd391
color12 #74a8fc
color13 #f2aede
color14 #6bd7ca
color15 #bac2de
cursor #f5e0dc
cursor_text_color #1e1e2e
background #1e1e2e
foreground #cdd6f4
selection_foreground #cdd6f4
selection_background #585b70
active_border_color #cba6f7
inactive_border_color #313244
url_color #cba6f7
active_tab_foreground #11111b
active_tab_background #cba6f7
inactive_tab_foreground #a3b4eb
inactive_tab_background #313244
cursor_trail_color #a3b4eb
+125 -40
View File
@@ -1,68 +1,153 @@
# Portable Noctalia config — generated by noctalia-sync-from-gui.sh.
# Validate with: noctalia config validate ~/.config/noctalia/config.toml
[bar.default]
background_opacity = 0.52
capsule = false
center = []
end = []
background_opacity = 0.50999998860061169
capsule = true
center = [ "session", "launcher", "control-center", "wallpaper" ]
end = [
"tray",
"notifications",
"network",
"bluetooth",
"volume",
"brightness",
"temp",
"cpu",
"sysmon",
"battery",
"clock"
]
margin_ends = 0
panel_overlap = 0
position = "bottom"
radius = 0
start = []
start = [ "workspaces", "media" ]
thickness = 28
[desktop_widgets]
enabled = false
schema_version = 2
widget_order = []
[desktop_widgets.grid]
cell_size = 16
major_interval = 4
visible = true
[desktop_widgets.widget]
[dock]
background_opacity = 0.68
background_opacity = 0.67999998480081558
[idle]
behavior_order = [ "backlight-dim", "kbd-off", "lock", "screen-off", "suspend" ]
[idle.behavior.backlight-dim]
action = "command"
command = "brightnessctl -s set 10"
enabled = true
resume_command = "brightnessctl -r"
timeout = 300.0
[idle.behavior.kbd-off]
action = "command"
command = "brightnessctl -sd rgb:kbd_backlight set 0"
enabled = true
resume_command = "brightnessctl -rd rgb:kbd_backlight"
timeout = 300.0
[idle.behavior.lock]
action = "lock"
enabled = true
timeout = 900.0
[idle.behavior.screen-off]
action = "screen_off"
enabled = false
timeout = 1800.0
[idle.behavior.suspend]
action = "lock_and_suspend"
enabled = true
timeout = 2700.0
[keybinds]
down = [ "Down", "Ctrl+j" ]
left = [ "Left", "Ctrl+h" ]
right = [ "Right", "Ctrl+l" ]
up = [ "Up", "Ctrl+k" ]
[location]
auto_locate = true
[lockscreen_widgets]
enabled = false
schema_version = 2
widget_order = [ "lockscreen-login-box@eDP-1" ]
[lockscreen_widgets.grid]
cell_size = 16
major_interval = 4
visible = true
[lockscreen_widgets.widget."lockscreen-login-box@eDP-1"]
box_height = 196.0
box_width = 810.0
cx = 960.0
cy = 898.0
output = "eDP-1"
placement_height = 1080.0
placement_width = 1920.0
rotation = 0.0
type = "login_box"
[lockscreen_widgets.widget."lockscreen-login-box@eDP-1".settings]
background_color = "surface_variant"
background_opacity = 0.88
background_radius = 12.0
center_password_text = false
input_opacity = 1.0
input_radius = 6.0
layout = "regular"
show_caps_lock = true
show_keyboard_layout = true
show_login_button = true
show_media = true
show_session_buttons = true
show_unlock_hint = true
show_weather = true
[shell]
font_family = "JetBrainsMono Nerd Font Mono"
telemetry_enabled = true
[shell.panel]
launcher_placement = "attached"
transparency_mode = "glass"
[shell.panel]
launcher_placement = "attached"
transparency_mode = "glass"
[theme]
mode = "dark"
builtin = "Catppuccin"
mode = "dark"
[theme.templates]
enable_builtin_templates = true
builtin_ids = []
[theme.templates]
builtin_ids = [ "gtk3", "gtk4", "hyprland", "kitty" ]
enable_builtin_templates = true
[wallpaper]
directory = ""
directory = "/home/mathias/nextcloud/Photos/Wallpapers"
[idle]
behavior_order = ["backlight-dim", "kbd-off", "lock", "screen-off", "suspend"]
[wallpaper.default]
path = "/home/mathias/nextcloud/Photos/Wallpapers/eigen/20180318-4825-Teneriffa_LaPalma-2.jpg"
[idle.behavior.backlight-dim]
timeout = 300
action = "command"
command = "brightnessctl -s set 10"
resume_command = "brightnessctl -r"
[wallpaper.last]
path = "/home/mathias/nextcloud/Photos/Wallpapers/eigen/20180318-4825-Teneriffa_LaPalma-2.jpg"
[idle.behavior.kbd-off]
timeout = 300
action = "command"
# This ThinkPad exposes the keyboard backlight as tpacpi::kbd_backlight;
# the previous rgb:kbd_backlight matched no device and errored out silently.
command = "brightnessctl -sd tpacpi::kbd_backlight set 0"
resume_command = "brightnessctl -rd tpacpi::kbd_backlight"
[wallpaper.monitors.eDP-1]
path = "/home/mathias/nextcloud/Photos/Wallpapers/eigen/20180318-4825-Teneriffa_LaPalma-2.jpg"
[idle.behavior.lock]
timeout = 900
action = "lock"
[widget.clock]
format = "{:%Y-%m-%d|%H:%M}"
[idle.behavior.screen-off]
timeout = 1800
action = "screen_off"
[widget.network]
show_label = false
[idle.behavior.suspend]
timeout = 2700
action = "lock_and_suspend"
[widget.sysmon]
stat = "gpu_usage"
+1
View File
@@ -19,6 +19,7 @@
@pacman
hyprland
hypridle
hyprpm
xdg-desktop-portal-hyprland
hyprpolkitagent
pantheon-shortcut-overlay
+2
View File
@@ -19,6 +19,8 @@ surge-xt
lsp-plugins
calf
dragonfly-reverb
darktable
gimp
@aur
numaplayer
+4 -3
View File
@@ -21,11 +21,12 @@ wine-mono
wine-gecko
winetricks
rbw
darktable
gimp
localsend
nfs-utils
code
imv
bitwarden
@aur
supersonic-desktop
subtui
localsend
+5
View File
@@ -46,3 +46,8 @@ fi
if command -v fastfetch >/dev/null 2>&1; then
fastfetch --config ~/.config/fastfetch/config.jsonc
fi
# Added by LM Studio CLI (lms)
export PATH="$PATH:/home/mathias/.lmstudio/bin"
# End of LM Studio CLI section
+6
View File
@@ -148,3 +148,9 @@ alias fdh="fd -H"
alias fdhi="fd -HI"
alias rgh="rg --hidden"
alias rghi="rg --hidden --no-ignore"
export PATH="$HOME/.cargo/bin:$PATH"
# Added by LM Studio CLI (lms)
set -gx PATH $PATH /home/mathias/.lmstudio/bin
# End of LM Studio CLI section
+12 -1
View File
@@ -17,11 +17,22 @@ return {
css = { "prettier" },
scss = { "prettier" },
yaml = { "prettier" },
markdown = { "prettier" },
markdown = { "prettier_markdown" },
sh = { "shfmt" },
bash = { "shfmt" },
rust = { "rustfmt" },
},
formatters = {
prettier_markdown = {
inherit = "prettier",
prepend_args = {
"--prose-wrap",
"always",
"--print-width",
"80",
},
},
},
})
vim.keymap.set({ "n", "v" }, "<leader>lf", function()
+1 -1
View File
@@ -63,7 +63,7 @@
"npm": "@ai-sdk/openai-compatible",
"name": "LM Studio (local)",
"options": {
"baseURL": "http://192.168.0.4:1234/v1"
"baseURL": "http://127.0.0.1:1234"
},
"models": {
"qwen3.5-35b-a3b": {
+14
View File
@@ -9,3 +9,17 @@ id = "git"
group = "git"
url = "*/"
run = "git"
[opener]
imv = [
{ run = "imv %s", orphan = true, spread = false, desc = "imv" },
]
mpv = [
{ run = "mpv %s", orphan = true, spread = false, desc = "mpv" },
]
[open]
prepend_rules = [
{ mime = "image/*", use = "imv" },
{ mime = "video/*", use = "mpv" },
]
+3 -1
View File
@@ -31,7 +31,9 @@ say_pass() { printf 'ok: %s\n' "$*"; PASS=$((PASS+1)); }
# Exclusions: never lint tpm or general vendored plugin trees, whose third-party
# scripts may legitimately fail `bash -n` and are not written/maintained here.
EXCLUDE_VENDOR='(/tpm/|/plugins/|/plugins$)'
mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -vE "$EXCLUDE_VENDOR")
mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -vE "$EXCLUDE_VENDOR" | while IFS= read -r script; do
[[ -f "$script" ]] && printf '%s\n' "$script"
done)
if [[ ${#SCRIPTS[@]} -eq 0 ]]; then
mapfile -t SCRIPTS < <(find . -name '*.sh' \
-not -path './.git/*' -not -path '*/.git/*' \