feat(menu): network/bluetooth/capture/ask modules + keybindings cheatsheet
Build out the rofi menu system roadmap item: - New nomarchy-menu modules: network (nmtui), bluetooth (blueman-manager), capture (grim/slurp submenu → clipboard/file), ask (free-text → claude CLI in a terminal), and keybinds (the SUPER+? cheatsheet). - modules/home/keybinds.nix is now the single source of truth for both the Hyprland bind strings and the cheatsheet, so the two can't drift — hyprland.nix maps it into `bind`, rofi.nix renders the padded two-column sheet (generated/mouse binds live in its `extra` rows). - New binds: SUPER+Space (quick launch), SUPER+M (main menu), SUPER+? (cheatsheet). SUPER+D stays rofi -show drun. - Ask Claude pulls claude-code fresh from npm via `npx @anthropic-ai/claude-code@latest` (the nixpkgs package lags model releases); nodejs is bundled for npx. nix flake check passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -261,7 +261,8 @@
|
||||
"Bash(pkill -f \"qemu-system-x86_64.*NOMARCHY\\\\|qemu-system-x86_64.*nomarchy\")",
|
||||
"Bash(NOMARCHY_PATH=__TRACKED_VAR__ python3 pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py --quiet apply summer-night --no-switch)",
|
||||
"Bash(NOMARCHY_PATH=__TRACKED_VAR__ python3 pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py --quiet apply summer-day --no-switch)",
|
||||
"Bash(xargs cat)"
|
||||
"Bash(xargs cat)",
|
||||
"Bash(nix-instantiate --parse modules/home/rofi.nix)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
22
README.md
22
README.md
@@ -274,14 +274,20 @@ close · `SUPER+1..9` workspaces · `Print` region screenshot.
|
||||
`theme` (SUPER+T) · `clipboard` (cliphist, SUPER+CTRL+V) · `calc`
|
||||
(qalc, copy/chain) · `files` (fd → xdg-open) · `web` (DuckDuckGo).
|
||||
SUPER+D is `rofi -show drun`.
|
||||
- next modules: Network ▸ nmtui · Bluetooth ▸ blueman · Capture ▸
|
||||
screenshot · **keybindings cheatsheet** (parse the bind list → rofi,
|
||||
so SUPER-? shows every shortcut) · **ask Claude**: free-text →
|
||||
`$TERMINAL -e claude "<question>"` — the claude CLI auths via OAuth
|
||||
against a Pro/Max subscription (no API key); REPL stays open for
|
||||
follow-ups (claude-code in nixpkgs is unfree — allowUnfree is on)
|
||||
- planned binds: `SUPER+Space` → `rofi -dmenu`/quick-launch and
|
||||
`SUPER+M` → `nomarchy-menu` (main menu); `SUPER+D` stays `-show drun`
|
||||
- ✓ shipped modules: `network` (nmtui in `$TERMINAL`) · `bluetooth`
|
||||
(blueman-manager) · `capture` (grim/slurp submenu: region/full →
|
||||
clipboard/file, saved to `~/Pictures/Screenshots`) · `keybinds` (the
|
||||
cheatsheet, see below) · `ask` (free-text → claude CLI in a terminal;
|
||||
auths via OAuth, no API key; pulled fresh via `npx
|
||||
@anthropic-ai/claude-code@latest` rather than the nixpkgs package, which
|
||||
lags model releases — `nodejs` is bundled for npx; REPL stays open)
|
||||
- ✓ keybindings cheatsheet: `modules/home/keybinds.nix` is the **single
|
||||
source** for both the Hyprland binds and the SUPER+? rofi list, so they
|
||||
can't drift; `nomarchy-menu keybinds` renders the padded two-column
|
||||
sheet (generated/mouse binds carried in its `extra` rows)
|
||||
- ✓ shipped binds: `SUPER+Space` → `rofi -show drun` (quick launch) ·
|
||||
`SUPER+M` → `nomarchy-menu` (main menu) · `SUPER+?` → the cheatsheet;
|
||||
`SUPER+D` stays `-show drun`
|
||||
- launcher icons: ✓ `show-icons` on, drawing from the theme's icon set
|
||||
(Papirus, via the icon-themes work below)
|
||||
- decision record: resolves the old Walker/Lua question — no GTK4
|
||||
|
||||
@@ -17,6 +17,11 @@ let
|
||||
"$mod SHIFT, ${ws}, movetoworkspace, ${ws}"
|
||||
])
|
||||
9);
|
||||
|
||||
# The keyboard binds — single source shared with the SUPER+? cheatsheet
|
||||
# (rofi.nix renders the same list). Edit them in ./keybinds.nix.
|
||||
keybinds = import ./keybinds.nix;
|
||||
mkBind = b: "${b.mods}, ${b.key}, ${b.action}";
|
||||
in
|
||||
{
|
||||
wayland.windowManager.hyprland = lib.mkIf config.nomarchy.hyprland.enable {
|
||||
@@ -125,41 +130,11 @@ in
|
||||
force_default_wallpaper = lib.mkDefault 0;
|
||||
};
|
||||
|
||||
bind = [
|
||||
"$mod, Return, exec, $terminal"
|
||||
"$mod, D, exec, rofi -show drun"
|
||||
"$mod, E, exec, $terminal -e yazi"
|
||||
"$mod, Q, killactive"
|
||||
"$mod, F, fullscreen"
|
||||
"$mod, V, togglefloating"
|
||||
"$mod SHIFT, E, exit"
|
||||
|
||||
# Theme picker (menu dispatcher): apply writes the state and
|
||||
# runs home-manager switch (progress via notify-send).
|
||||
"$mod, T, exec, nomarchy-menu theme"
|
||||
# Cycle the current theme's wallpapers (instant, no rebuild).
|
||||
"$mod SHIFT, T, exec, nomarchy-theme-sync bg next"
|
||||
|
||||
# Power menu (rofi dmenu via the nomarchy-menu dispatcher).
|
||||
# Not Escape: Super+Escape gets swallowed before reaching the
|
||||
# dispatcher on some setups (input methods / modal handling).
|
||||
"$mod, X, exec, nomarchy-menu power"
|
||||
|
||||
# Notification centre (swaync).
|
||||
"$mod, N, exec, swaync-client -t"
|
||||
|
||||
# Clipboard history (cliphist via the menu dispatcher).
|
||||
"$mod CTRL, V, exec, nomarchy-menu clipboard"
|
||||
|
||||
# Focus
|
||||
"$mod, H, movefocus, l"
|
||||
"$mod, L, movefocus, r"
|
||||
"$mod, K, movefocus, u"
|
||||
"$mod, J, movefocus, d"
|
||||
|
||||
# Screenshot region to clipboard
|
||||
", Print, exec, grim -g \"$(slurp)\" - | wl-copy"
|
||||
] ++ workspaceBinds;
|
||||
# Rendered from ./keybinds.nix (the cheatsheet reads the same list),
|
||||
# plus the generated per-workspace binds. Like exec-once above this
|
||||
# stays a normal-priority list, so a downstream `bind = [...]`
|
||||
# concatenates rather than replaces.
|
||||
bind = map mkBind keybinds.binds ++ workspaceBinds;
|
||||
|
||||
# Media keys via swayosd-client: it performs the action AND shows
|
||||
# the on-screen display (the nomarchy.osd module). e = repeat,
|
||||
|
||||
56
modules/home/keybinds.nix
Normal file
56
modules/home/keybinds.nix
Normal file
@@ -0,0 +1,56 @@
|
||||
# Canonical keybind list — the single source of truth for BOTH the
|
||||
# Hyprland `bind` strings (hyprland.nix) and the SUPER+? cheatsheet
|
||||
# rendered by nomarchy-menu (rofi.nix). One place, no second renderer to
|
||||
# keep in sync — the same rule the theme pipeline follows.
|
||||
#
|
||||
# mods — modifier string, Hyprland syntax ("$mod", "$mod SHIFT", "")
|
||||
# key — the key name
|
||||
# action — the Hyprland dispatcher (+args) that the bind runs
|
||||
# desc — human label, shown in the cheatsheet
|
||||
#
|
||||
# `extra` holds cheatsheet-only rows for binds generated elsewhere
|
||||
# (per-workspace numbers, mouse drags) so they still show up under SUPER+?.
|
||||
{
|
||||
binds = [
|
||||
{ mods = "$mod"; key = "Return"; action = "exec, $terminal"; desc = "Open terminal"; }
|
||||
{ mods = "$mod"; key = "Space"; action = "exec, rofi -show drun"; desc = "Quick launch (apps)"; }
|
||||
{ mods = "$mod"; key = "D"; action = "exec, rofi -show drun"; desc = "App launcher"; }
|
||||
{ mods = "$mod"; key = "M"; action = "exec, nomarchy-menu"; desc = "Main menu"; }
|
||||
{ mods = "$mod"; key = "E"; action = "exec, $terminal -e yazi"; desc = "File manager (yazi)"; }
|
||||
{ mods = "$mod"; key = "Q"; action = "killactive"; desc = "Close window"; }
|
||||
{ mods = "$mod"; key = "F"; action = "fullscreen"; desc = "Fullscreen"; }
|
||||
{ mods = "$mod"; key = "V"; action = "togglefloating"; desc = "Toggle floating"; }
|
||||
{ mods = "$mod SHIFT"; key = "E"; action = "exit"; desc = "Exit Hyprland"; }
|
||||
|
||||
# Theme picker (menu dispatcher): apply writes the state and runs
|
||||
# home-manager switch (progress via notify-send).
|
||||
{ mods = "$mod"; key = "T"; action = "exec, nomarchy-menu theme"; desc = "Theme picker"; }
|
||||
# Cycle the current theme's wallpapers (instant, no rebuild).
|
||||
{ mods = "$mod SHIFT"; key = "T"; action = "exec, nomarchy-theme-sync bg next"; desc = "Next wallpaper"; }
|
||||
|
||||
# Power menu via the dispatcher. Not Escape: Super+Escape gets
|
||||
# swallowed before reaching the dispatcher on some setups.
|
||||
{ mods = "$mod"; key = "X"; action = "exec, nomarchy-menu power"; desc = "Power menu"; }
|
||||
|
||||
{ mods = "$mod"; key = "N"; action = "exec, swaync-client -t"; desc = "Notification centre"; }
|
||||
{ mods = "$mod CTRL"; key = "V"; action = "exec, nomarchy-menu clipboard"; desc = "Clipboard history"; }
|
||||
# SUPER+? (the "question" keysym already implies Shift on most layouts).
|
||||
{ mods = "$mod"; key = "question"; action = "exec, nomarchy-menu keybinds"; desc = "Keybindings cheatsheet"; }
|
||||
|
||||
# Focus
|
||||
{ mods = "$mod"; key = "H"; action = "movefocus, l"; desc = "Focus left"; }
|
||||
{ mods = "$mod"; key = "L"; action = "movefocus, r"; desc = "Focus right"; }
|
||||
{ mods = "$mod"; key = "K"; action = "movefocus, u"; desc = "Focus up"; }
|
||||
{ mods = "$mod"; key = "J"; action = "movefocus, d"; desc = "Focus down"; }
|
||||
|
||||
# Screenshot region to clipboard (the menu's Capture module has more).
|
||||
{ mods = ""; key = "Print"; action = "exec, grim -g \"$(slurp)\" - | wl-copy"; desc = "Screenshot region → clipboard"; }
|
||||
];
|
||||
|
||||
extra = [
|
||||
{ keys = "SUPER + 1-9"; desc = "Switch to workspace 1-9"; }
|
||||
{ keys = "SUPER + SHIFT + 1-9"; desc = "Move window to workspace 1-9"; }
|
||||
{ keys = "SUPER + drag"; desc = "Move (LMB) / resize (RMB) window"; }
|
||||
{ keys = "Volume / Brightness"; desc = "Hardware keys, shown via the OSD"; }
|
||||
];
|
||||
}
|
||||
@@ -19,6 +19,25 @@ let
|
||||
rasiOverride = cfg.themesDir + "/${t.slug}/rofi.rasi";
|
||||
hasRasiOverride = builtins.pathExists rasiOverride;
|
||||
|
||||
# Keybindings cheatsheet (SUPER+? → the `keybinds` menu module). Built
|
||||
# from the SAME ./keybinds.nix that hyprland.nix binds, so it can never
|
||||
# drift from the live shortcuts. "$mod" reads as SUPER; rows are padded
|
||||
# into two columns. extra[] carries the generated/mouse binds.
|
||||
keybinds = import ./keybinds.nix;
|
||||
padRight = w: s:
|
||||
let n = w - builtins.stringLength s;
|
||||
in s + (if n > 0 then lib.concatStrings (builtins.genList (_: " ") n) else " ");
|
||||
prettyKeys = b:
|
||||
let m = lib.replaceStrings [ "$mod" ] [ "SUPER" ] b.mods;
|
||||
prefix = if m == "" then "" else lib.concatStringsSep " + " (lib.splitString " " m) + " + ";
|
||||
key = lib.replaceStrings [ "question" "slash" ] [ "?" "/" ] b.key;
|
||||
in prefix + key;
|
||||
cheatRows =
|
||||
map (b: padRight 22 (prettyKeys b) + b.desc) keybinds.binds
|
||||
++ map (e: padRight 22 e.keys + e.desc) keybinds.extra;
|
||||
cheatsheetFile = pkgs.writeText "nomarchy-keybinds.txt"
|
||||
(lib.concatStringsSep "\n" cheatRows + "\n");
|
||||
|
||||
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
|
||||
# Nomarchy menu dispatcher — thin presentation layer over
|
||||
# `rofi -dmenu`; actions delegate to nomarchy-theme-sync, systemctl,
|
||||
@@ -89,6 +108,45 @@ let
|
||||
[ -n "$q" ] || exit 0
|
||||
exec xdg-open "https://duckduckgo.com/?q=$(urlencode "$q")" ;;
|
||||
|
||||
network)
|
||||
# nmtui in a terminal (NetworkManager is the system network stack).
|
||||
exec ${cfg.terminal} -e nmtui ;;
|
||||
|
||||
bluetooth)
|
||||
# blueman-manager GUI (services.blueman.enable, system-side).
|
||||
exec blueman-manager ;;
|
||||
|
||||
capture)
|
||||
choice=$(printf '%s\n' \
|
||||
" Region → clipboard" \
|
||||
" Region → file" \
|
||||
" Full screen → clipboard" \
|
||||
" Full screen → file" \
|
||||
| rofi -dmenu -p capture) || exit 0
|
||||
dir="$HOME/Pictures/Screenshots"
|
||||
file="$dir/$(date +%Y%m%d-%H%M%S).png"
|
||||
case "$choice" in
|
||||
*"Region → clipboard"*) grim -g "$(slurp)" - | wl-copy ;;
|
||||
*"Region → file"*) mkdir -p "$dir"; grim -g "$(slurp)" "$file" \
|
||||
&& notify-send "Screenshot saved" "$file" ;;
|
||||
*"Full screen → clipboard"*) grim - | wl-copy ;;
|
||||
*"Full screen → file"*) mkdir -p "$dir"; grim "$file" \
|
||||
&& notify-send "Screenshot saved" "$file" ;;
|
||||
esac ;;
|
||||
|
||||
keybinds)
|
||||
# Read-only cheatsheet, generated from keybinds.nix at build time.
|
||||
rofi -dmenu -i -p keys < ${cheatsheetFile} >/dev/null || exit 0 ;;
|
||||
|
||||
ask)
|
||||
# Free-text question → claude CLI in a terminal (OAuth, no API key).
|
||||
# Pulled fresh from npm via npx rather than nixpkgs' claude-code,
|
||||
# which lags behind model releases; npx caches after first run. The
|
||||
# REPL stays open for follow-ups.
|
||||
q=$(rofi -dmenu -p claude < /dev/null) || exit 0
|
||||
[ -n "$q" ] || exit 0
|
||||
exec ${cfg.terminal} -e npx --yes @anthropic-ai/claude-code@latest "$q" ;;
|
||||
|
||||
"")
|
||||
choice=$(printf '%s\n' \
|
||||
" Apps" \
|
||||
@@ -97,6 +155,11 @@ let
|
||||
" Calculator" \
|
||||
" Files" \
|
||||
" Web search" \
|
||||
" Network" \
|
||||
" Bluetooth" \
|
||||
" Capture" \
|
||||
" Keybindings" \
|
||||
" Ask Claude" \
|
||||
" Power" \
|
||||
| rofi -dmenu -p menu) || exit 0
|
||||
case "$choice" in
|
||||
@@ -106,11 +169,16 @@ let
|
||||
*Calc*) exec "$0" calc ;;
|
||||
*Files*) exec "$0" files ;;
|
||||
*Web*) exec "$0" web ;;
|
||||
*Network*) exec "$0" network ;;
|
||||
*Bluetooth*) exec "$0" bluetooth ;;
|
||||
*Capture*) exec "$0" capture ;;
|
||||
*Keybindings*) exec "$0" keybinds ;;
|
||||
*Ask*) exec "$0" ask ;;
|
||||
*Power*) exec "$0" power ;;
|
||||
esac ;;
|
||||
|
||||
*)
|
||||
echo "usage: nomarchy-menu [power|theme|clipboard|calc|files|web]" >&2
|
||||
echo "usage: nomarchy-menu [power|theme|clipboard|calc|files|web|network|bluetooth|capture|keybinds|ask]" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
@@ -122,6 +190,7 @@ in
|
||||
pkgs.libqalculate # qalc, the calc module's engine
|
||||
pkgs.fd # files module
|
||||
pkgs.xdg-utils # xdg-open for files/web
|
||||
pkgs.nodejs # npx, for the Ask Claude module (claude-code from npm)
|
||||
];
|
||||
|
||||
programs.rofi = {
|
||||
|
||||
Reference in New Issue
Block a user