fix(menu): case-insensitive filter on all rofi -dmenu menus
All checks were successful
Check / eval (push) Successful in 2m53s

dmenu mode is case-sensitive by default (dmenu compat), so config.rasi
case-sensitive=false fixed apps/drun but not nomarchy-menu pickers.
rofi_menu helper always passes -i; networkmanager_dmenu gets rofi -i.

V0: flake check --no-build green. V3: type sys in root Menu → System.
This commit is contained in:
2026-07-10 13:08:33 +01:00
parent 79630314aa
commit 3bc8a46927
2 changed files with 44 additions and 35 deletions

View File

@@ -169,10 +169,11 @@ let
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
# Nomarchy menu dispatcher thin presentation layer over
# `rofi -dmenu`; actions delegate to nomarchy-theme-sync, systemctl,
# hyprctl and friends. Menu entries carry real icons from the theme's
# icon set (Papirus) via rofi's per-row icon protocol see row() below.
# `nomarchy-menu` with no argument shows the module picker.
# `rofi -dmenu` (via rofi_menu helper); actions delegate to
# nomarchy-theme-sync, systemctl, hyprctl and friends. Menu entries
# carry real icons from the theme's icon set (Papirus) via rofi's
# per-row icon protocol see row() below. `nomarchy-menu` with no
# argument shows the module picker.
# System batteries: type=Battery, not scope=Device same filter as
# nomarchy-battery-notify. Name-agnostic (BAT0, CMB0, test_battery)
@@ -229,6 +230,12 @@ let
BACK=" Back"
back() { printf '%s\n' "$BACK"; }
# dmenu mode is case-sensitive by default (dmenu compatibility), even
# when config.rasi has case-sensitive: false for drun/modi so
# SUPER+Space (apps) matched "ste"Steam while the main Menu filter
# did not match "sys"System. Always pass -i on menu pickers.
rofi_menu() { command rofi -dmenu -i "$@"; }
case "''${1:-}" in
power)
choice=$( {
@@ -239,7 +246,7 @@ let
row "Reboot" system-reboot
row "Shutdown" system-shutdown
back
} | rofi -dmenu -show-icons -p Power) || exit 0
} | rofi_menu -show-icons -p Power) || exit 0
case "$choice" in
"$BACK") exec "$0" ;;
*Lock) loginctl lock-session ;;
@@ -274,7 +281,7 @@ let
esac
done
back
} | rofi -dmenu -show-icons -p "profile (now: $cur)") || exit 0
} | rofi_menu -show-icons -p "profile (now: $cur)") || exit 0
[ "$choice" = "$BACK" ] && exec "$0" system
[ -n "$choice" ] && powerprofilesctl set "$choice" ;;
@@ -313,7 +320,7 @@ let
row "Charge limit: Off (100%)" battery-100
fi
back
} | rofi -dmenu -show-icons -p "Power profile: ''${curp:-n/a}, limit: ''${curc:-default}") || exit 0
} | rofi_menu -show-icons -p "Power profile: ''${curp:-n/a}, limit: ''${curc:-default}") || exit 0
# Persist in-flake + apply live to sysfs (udev makes the node
# group-writable for users Bernardo 2026-07-10).
set_limit() {
@@ -346,14 +353,14 @@ ${themeSlugMap}
choice=$( {
${themeRows}
back
} | rofi -dmenu -show-icons -p Theme -theme-str ${themeGridThemeStr}) || exit 0
} | rofi_menu -show-icons -p Theme -theme-str ${themeGridThemeStr}) || exit 0
[ "$choice" = "$BACK" ] && exec "$0"
choice="''${choice% }" # drop the active marker if present
slug="''${THEME_SLUG[$choice]:-}"
[ -n "$slug" ] && exec nomarchy-theme-sync apply "$slug" ;;
clipboard)
sel=$( { cliphist list; printf '%s\n' "$BACK"; } | rofi -dmenu -p clip) || exit 0
sel=$( { cliphist list; printf '%s\n' "$BACK"; } | rofi_menu -p clip) || exit 0
[ "$sel" = "$BACK" ] && exec "$0" tools
printf '%s' "$sel" | cliphist decode | wl-copy ;;
@@ -368,7 +375,7 @@ ${themeRows}
files)
sel=$( { fd . "$HOME" --type f --hidden --exclude .git --exclude .cache 2>/dev/null \
| head -n 50000 | sed "s|^$HOME/||"; printf '%s\n' "$BACK"; } \
| rofi -dmenu -p file) || exit 0
| rofi_menu -p file) || exit 0
[ "$sel" = "$BACK" ] && exec "$0" tools
[ -n "$sel" ] && exec xdg-open "$HOME/$sel" ;;
@@ -379,7 +386,7 @@ ${themeRows}
exec rofi -show emoji -modi emoji ;;
web)
q=$(rofi -dmenu -p search < /dev/null) || exit 0
q=$(rofi_menu -p search < /dev/null) || exit 0
[ -n "$q" ] || exit 0
exec xdg-open "https://www.google.com/search?q=$(urlencode "$q")" ;;
@@ -421,7 +428,7 @@ ${themeRows}
row "Output device" audio-volume-high
row "Input device" audio-input-microphone
back
} | rofi -dmenu -show-icons -p Audio) || exit 0
} | rofi_menu -show-icons -p Audio) || exit 0
case "$choice" in
"$BACK") exec "$0" system ;;
*Output*) exec rofi-pulse-select sink ;;
@@ -454,7 +461,7 @@ ${themeRows}
[ -n "$profs" ] && printf 'Profiles \n'
printf '%s' "$mons" | jq -r '.[] | "\(.name) · \(.width)x\(.height)@\(.refreshRate|round)Hz"'
printf '%s\n' "$BACK"
} | rofi -dmenu -p Display ) || exit 0
} | rofi_menu -p Display ) || exit 0
[ "$name" = "$BACK" ] && exec "$0" system
[ "$name" = "Profiles " ] && exec "$0" display-profile
name=''${name%% ·*} # strip the " · WxH@R" hint
@@ -474,7 +481,7 @@ ${themeRows}
printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|.availableModes[]' \
| sed 's/Hz$//' | awk -F@ '{ printf "%s@%d\n", $1, $2 + 0.5 }' | sort -rV -u
printf '%s\n' "$BACK"
} | rofi -dmenu -p "$name (now $cur)" ) || exit 0
} | rofi_menu -p "$name (now $cur)" ) || exit 0
[ "$mode" = "$BACK" ] && exec "$0" "$modeBack"
[ -n "$mode" ] || exit 0
@@ -509,7 +516,7 @@ ${themeRows}
fi
fi
back
} | rofi -dmenu -show-icons -markup-rows -p Capture) || exit 0
} | rofi_menu -show-icons -markup-rows -p Capture) || exit 0
dir="$HOME/Pictures/Screenshots"
file="$dir/$(date +%Y%m%d-%H%M%S).png"
case "$choice" in
@@ -551,7 +558,7 @@ ${themeRows}
else row "Night light (off)" weather-clear-night
fi
back
} | rofi -dmenu -show-icons -markup-rows -p "Look & Feel") || exit 0
} | rofi_menu -show-icons -markup-rows -p "Look & Feel") || exit 0
case "$choice" in
"$BACK") exec "$0" ;;
*Theme*) exec "$0" theme ;;
@@ -578,7 +585,7 @@ ${themeRows}
else printf 'Auto-switch (off)\n'
fi
printf '%s\n' "$BACK"
} | rofi -dmenu -p Profiles ) || exit 0
} | rofi_menu -p Profiles ) || exit 0
case "$choice" in
"$BACK") exec "$0" display ;;
"Base layout") exec nomarchy-display-profile base ;;
@@ -612,7 +619,7 @@ ${themeRows}
# Enter on a row is a deliberate no-op (nothing to launch); Back
# returns to the root picker, which is also where it's launched.
sel=$( { cat ${cheatsheetFile}; printf '%s\n' "$BACK"; } \
| rofi -dmenu -i -p keys ) || exit 0
| rofi_menu -p keys ) || exit 0
[ "$sel" = "$BACK" ] && exec "$0"
exit 0 ;;
@@ -621,7 +628,7 @@ ${themeRows}
# 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
q=$(rofi_menu -p claude < /dev/null) || exit 0
[ -n "$q" ] || exit 0
exec ${cfg.terminal} -e npx --yes @anthropic-ai/claude-code@latest "$q" ;;
@@ -728,7 +735,7 @@ ${themeRows}
row "Delete all" edit-delete
row "$pam_label" system-lock-screen
back
} | rofi -dmenu -show-icons -p Fingerprint) || exit 0
} | rofi_menu -show-icons -p Fingerprint) || exit 0
case "$choice" in
"$BACK") exec "$0" system ;;
*Enroll*)
@@ -794,7 +801,7 @@ ${themeRows}
&& row "System files Snapshots" timeshift
row "System config boot an older generation (how)" help-about
back
} | rofi -dmenu -show-icons -p Rollback) || exit 0
} | rofi_menu -show-icons -p Rollback) || exit 0
case "$choice" in
"$BACK") exec "$0" system ;;
"Desktop gen "*)
@@ -830,7 +837,7 @@ ${themeRows}
command -v hyprpicker >/dev/null 2>&1 && row "Color picker${menuHint "colorpicker"}" preferences-desktop-color
row "Ask Claude${menuHint "ask"}" internet-chat
back
} | rofi -dmenu -show-icons -markup-rows -p Tools) || exit 0
} | rofi_menu -show-icons -markup-rows -p Tools) || exit 0
case "$choice" in
"$BACK") exec "$0" ;;
*"Install Nomarchy"*)
@@ -888,7 +895,7 @@ ${themeRows}
# expose it), like Power profile above.
has_charge_threshold && row "Battery limit" battery-080
back
} | rofi -dmenu -show-icons -markup-rows -p System) || exit 0
} | rofi_menu -show-icons -markup-rows -p System) || exit 0
case "$choice" in
"$BACK") exec "$0" ;;
*Network*) exec "$0" network ;;
@@ -926,7 +933,7 @@ ${themeRows}
row "Off charge to 100%" battery-100
row "Custom" battery
back
} | rofi -dmenu -show-icons -p "Battery limit (now: ''${cur:-default})") || exit 0
} | rofi_menu -show-icons -p "Battery limit (now: ''${cur:-default})") || exit 0
set_limit() {
nomarchy-theme-sync --quiet set settings.power.batteryChargeLimit "$1" --no-switch
n="$1"; [ "$n" = null ] && n=100
@@ -945,7 +952,7 @@ ${themeRows}
"60%"*) set_limit 60 "Set to 60%" ;;
"Off"*) set_limit null "Off charges to 100%" ;;
"Custom"*)
limit=$(rofi -dmenu -p "Charge limit % (50100)" < /dev/null) || exit 0
limit=$(rofi_menu -p "Charge limit % (50100)" < /dev/null) || exit 0
[ -n "$limit" ] || exit 0
case "$limit" in
*[!0-9]*) notify-send "Battery limit" "Not a whole number: $limit"; exit 0 ;;
@@ -968,7 +975,7 @@ ${themeRows}
row "System" preferences-system
row "Power${menuHint "power"}" system-shutdown
row "Keybindings${menuHint "keybinds"}" preferences-desktop-keyboard
} | rofi -dmenu -show-icons -markup-rows -p Menu) || exit 0
} | rofi_menu -show-icons -markup-rows -p Menu) || exit 0
case "$choice" in
*Apps*) exec rofi -show drun -theme launcher ;;
*Look*) exec "$0" lookfeel ;;
@@ -1020,7 +1027,7 @@ ${themeRows}
file=$( { fd -e conf -e ovpn . "$HOME" --type f --hidden \
--exclude .git --exclude .cache 2>/dev/null \
| sed "s|^$HOME/||"; printf '%s\n' "$BACK"; } \
| rofi -dmenu -p "Import VPN config" ) || return
| rofi_menu -p "Import VPN config" ) || return
{ [ -z "$file" ] || [ "$file" = "$BACK" ]; } && return
case "$file" in *.ovpn) type=openvpn ;; *) type=wireguard ;; esac
if nmcli connection import type "$type" file "$HOME/$file" >/dev/null 2>&1; then
@@ -1040,7 +1047,7 @@ ${themeRows}
}
ts_exit_node() {
node=$( { printf 'none\n'; tailscale exit-node list 2>/dev/null | awk 'NR>1 && $2 {print $2}'; \
printf '%s\n' "$BACK"; } | rofi -dmenu -p "Exit node" ) || return
printf '%s\n' "$BACK"; } | rofi_menu -p "Exit node" ) || return
{ [ -z "$node" ] || [ "$node" = "$BACK" ]; } && return
[ "$node" = none ] && node=""
ts_priv set --exit-node="$node"
@@ -1052,7 +1059,7 @@ ${themeRows}
if [ "$st" = Running ]; then printf 'Disconnect\n'; else printf 'Connect\n'; fi
printf 'Exit node\n'
printf '%s\n' "$BACK"
} | rofi -dmenu -p "Tailscale ($st)" ) || return
} | rofi_menu -p "Tailscale ($st)" ) || return
case "$choice" in
"$BACK"|"") return ;;
# Authed-but-down bring it up inline; otherwise it needs an
@@ -1072,7 +1079,7 @@ ${themeRows}
printf 'Import config\n'
[ -n "$have_ts" ] && printf 'Tailscale \n'
printf '%s\n' "$BACK"
} | rofi -dmenu -p VPN ) || exit 0
} | rofi_menu -p VPN ) || exit 0
case "$choice" in
"$BACK"|"") exec nomarchy-menu system ;;
"Import config") import_config ;;
@@ -1114,7 +1121,9 @@ in
force = true;
text = ''
[dmenu]
dmenu_command = rofi
# -i: networkmanager_dmenu uses dmenu mode (case-sensitive by
# default); match nomarchy-menu's case-insensitive filter.
dmenu_command = rofi -i
rofi_highlight = True
compact = True
wifi_chars =