Drop bin/omarchy-menu; sweep JSONC actions to bare commands
The shell now owns the menu — both data and dispatch. The 821-line bash bin was carrying the open path (cold spawn + jq pipeline + tempfile dance + IPC + poll) and a grab-bag of helpers that only existed inside it. Both go away in this commit. New bins for the few composite helpers that were genuinely worth keeping: - omarchy-install-app NAME PKG - omarchy-install-and-launch NAME PKG DESKTOP_ID - omarchy-install-font LABEL PKG FAMILY - omarchy-launch-config-editor PATH - omarchy-reminder-set-interactive - omarchy-capture-screenrecording-with-webcam Sweep through default/omarchy/omarchy-menu.jsonc rewrites: - present_terminal X -> omarchy-launch-floating-terminal-with-presentation X - install_terminal X -> omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal X' - install / install_and_launch / install_font / open_in_editor -> new bins above - terminal X -> xdg-terminal-exec --app-id=org.omarchy.terminal X - default_browser_is X -> [[ "$(omarchy-default-browser)" == "X" ]] - default_terminal_is X / default_editor_is X / haptic_touchpad_is X same shape - $(hypr_config_file X) -> ~/.config/hypr/X.lua - show_custom_reminder_input -> omarchy-reminder-set-interactive - screenrecord_with_webcam -> omarchy-capture-screenrecording-with-webcam - stop_active_screenrecording -> omarchy-capture-screenrecording --stop-recording Hyprland bindings switch from `omarchy-menu X` to `omarchy-shell-ipc menu summon X` (the keybind hot path now skips bash entirely). The Bar.qml omarchy widget and the battery right-click do the same. ALT+PRINT becomes a one-liner: stop the recording if one is going, otherwise summon the screenrecord submenu. Measured: keybind-to-visible is ~30ms (was ~235ms). The shell's plugin keepLoaded:true stops being theoretical \u2014 the menu data lives in memory across opens, and the only work between keypress and paint is the IPC roundtrip and the layer-shell window mount.
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Pick a webcam and start a screen recording with it
|
||||
# omarchy:examples=omarchy capture screenrecording-with-webcam
|
||||
|
||||
set -o pipefail
|
||||
|
||||
webcam_list() {
|
||||
v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do
|
||||
if [[ $line != $'\t'* && -n $line ]]; then
|
||||
local name="$line"
|
||||
local device
|
||||
IFS= read -r device || break
|
||||
device=$(tr -d '\t' <<<"$device" | head -1)
|
||||
[[ -n $device ]] && echo "$device $name"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
devices=$(webcam_list)
|
||||
if [[ -z $devices ]]; then
|
||||
omarchy-notification-send "No webcam devices found" -u critical -t 3000
|
||||
exit 1
|
||||
fi
|
||||
|
||||
count=$(grep -c . <<<"$devices")
|
||||
if (( count == 1 )); then
|
||||
device="${devices%%[[:space:]]*}"
|
||||
else
|
||||
selection=$(omarchy-menu-select "Select Webcam" "$devices") || exit 1
|
||||
device="${selection%%[[:space:]]*}"
|
||||
fi
|
||||
|
||||
exec omarchy-capture-screenrecording \
|
||||
--with-desktop-audio --with-microphone-audio \
|
||||
--with-webcam --webcam-device="$device"
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install a packaged app and gtk-launch it once it finishes
|
||||
# omarchy:args=<display-name> <packages> <desktop-id>
|
||||
# omarchy:examples=omarchy install and-launch Cursor cursor-bin cursor
|
||||
|
||||
name="${1-}"
|
||||
packages="${2-}"
|
||||
desktop_id="${3-}"
|
||||
|
||||
if [[ -z $name || -z $packages || -z $desktop_id ]]; then
|
||||
echo "Usage: omarchy-install-and-launch <display-name> <packages> <desktop-id>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec omarchy-launch-floating-terminal-with-presentation \
|
||||
"echo 'Installing ${name}...'; omarchy-pkg-add ${packages} && setsid gtk-launch ${desktop_id}"
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install a packaged app, surfacing the install in a floating terminal
|
||||
# omarchy:args=<display-name> <packages>
|
||||
# omarchy:examples=omarchy install app 'LM Studio' lmstudio-bin
|
||||
|
||||
name="${1-}"
|
||||
packages="${2-}"
|
||||
|
||||
if [[ -z $name || -z $packages ]]; then
|
||||
echo "Usage: omarchy-install-app <display-name> <packages>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec omarchy-launch-floating-terminal-with-presentation "echo 'Installing ${name}...'; omarchy-pkg-add ${packages}"
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install a Nerd Font package and switch the system to it
|
||||
# omarchy:args=<display-name> <package> <family>
|
||||
# omarchy:examples=omarchy install font 'Cascadia Mono' ttf-cascadia-mono-nerd 'CaskaydiaMono Nerd Font'
|
||||
|
||||
name="${1-}"
|
||||
package="${2-}"
|
||||
family="${3-}"
|
||||
|
||||
if [[ -z $name || -z $package || -z $family ]]; then
|
||||
echo "Usage: omarchy-install-font <display-name> <package> <family>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec omarchy-launch-floating-terminal-with-presentation \
|
||||
"echo 'Installing ${name}...'; omarchy-pkg-add ${package} && sleep 2 && omarchy-font-set '${family}'"
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Open a config file in the user's editor and surface a toast
|
||||
# omarchy:args=<path>
|
||||
# omarchy:examples=omarchy launch config-editor ~/.config/hypr/hyprlock.conf
|
||||
|
||||
path="${1-}"
|
||||
|
||||
if [[ -z $path ]]; then
|
||||
echo "Usage: omarchy-launch-config-editor <path>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
omarchy-notification-send -u low "Editing config file" "$path"
|
||||
exec omarchy-launch-editor "$path"
|
||||
@@ -1,821 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Launch the Quickshell Omarchy menu or jump straight to a submenu/action
|
||||
# omarchy:args=[menu-or-action|--json|--provider <provider> [menu-id]]
|
||||
# omarchy:examples=omarchy menu | omarchy menu setup | omarchy menu style.theme | omarchy menu --json | omarchy menu --provider fonts style.font
|
||||
|
||||
set -o pipefail
|
||||
|
||||
OMARCHY_PATH=${OMARCHY_PATH:-$HOME/.local/share/omarchy}
|
||||
DEFAULT_MENU="$OMARCHY_PATH/default/omarchy/omarchy-menu.jsonc"
|
||||
USER_MENU="$HOME/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||
|
||||
# Menu hooks users may edit
|
||||
# Menu rows live in default/omarchy/omarchy-menu.jsonc.
|
||||
# Keep Bash here only for multi-step actions or truly runtime-generated lists.
|
||||
|
||||
# Action helpers available to omarchy-menu.jsonc
|
||||
|
||||
terminal() {
|
||||
xdg-terminal-exec --app-id=org.omarchy.terminal "$@"
|
||||
}
|
||||
|
||||
present_terminal() {
|
||||
omarchy-launch-floating-terminal-with-presentation "$*"
|
||||
}
|
||||
|
||||
open_in_editor() {
|
||||
omarchy-notification-send -u low "Editing config file" "$1"
|
||||
omarchy-launch-editor "$1"
|
||||
}
|
||||
|
||||
hypr_config_file() {
|
||||
local name="$1"
|
||||
printf '%s/.config/hypr/%s.lua' "$HOME" "$name"
|
||||
}
|
||||
|
||||
install() {
|
||||
present_terminal "echo 'Installing $1...'; omarchy-pkg-add $2"
|
||||
}
|
||||
|
||||
install_and_launch() {
|
||||
present_terminal "echo 'Installing $1...'; omarchy-pkg-add $2 && setsid gtk-launch $3"
|
||||
}
|
||||
|
||||
install_font() {
|
||||
present_terminal "echo 'Installing $1...'; omarchy-pkg-add $2 && sleep 2 && omarchy-font-set '$3'"
|
||||
}
|
||||
|
||||
install_terminal() {
|
||||
present_terminal "omarchy-install-terminal $1"
|
||||
}
|
||||
|
||||
show_custom_reminder_input() {
|
||||
local minutes
|
||||
minutes=$(omarchy-menu-input "Remind in minutes")
|
||||
|
||||
if [[ $minutes =~ ^[0-9]+$ ]] && (( minutes > 0 )); then
|
||||
show_reminder_message_input "$minutes"
|
||||
elif [[ -n $minutes ]]; then
|
||||
omarchy-notification-send -g "Invalid reminder" "Enter the number of minutes" -u critical
|
||||
show_custom_reminder_input
|
||||
fi
|
||||
}
|
||||
|
||||
show_reminder_message_input() {
|
||||
local minutes="$1"
|
||||
local message
|
||||
message=$(omarchy-menu-input "Reminder message")
|
||||
|
||||
if [[ -n $message ]]; then
|
||||
omarchy-reminder "$minutes" "$message"
|
||||
else
|
||||
omarchy-reminder "$minutes"
|
||||
fi
|
||||
}
|
||||
|
||||
get_webcam_list() {
|
||||
v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do
|
||||
if [[ $line != $'\t'* && -n $line ]]; then
|
||||
local name="$line"
|
||||
local device
|
||||
IFS= read -r device || break
|
||||
device=$(tr -d '\t' <<<"$device" | head -1)
|
||||
[[ -n $device ]] && echo "$device $name"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
show_webcam_select_menu() {
|
||||
local devices
|
||||
local count
|
||||
devices=$(get_webcam_list)
|
||||
if [[ -z $devices ]]; then
|
||||
count=0
|
||||
else
|
||||
count=$(grep -c . <<<"$devices")
|
||||
fi
|
||||
|
||||
if [[ -z $devices ]] || (( count == 0 )); then
|
||||
omarchy-notification-send "No webcam devices found" -u critical -t 3000
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (( count == 1 )); then
|
||||
printf '%s\n' "${devices%%[[:space:]]*}"
|
||||
else
|
||||
local selection
|
||||
selection=$(omarchy-menu-select "Select Webcam" "$devices") || return 1
|
||||
printf '%s\n' "${selection%%[[:space:]]*}"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_active_screenrecording() {
|
||||
omarchy-capture-screenrecording --stop-recording
|
||||
}
|
||||
|
||||
screenrecord_with_webcam() {
|
||||
local device
|
||||
device=$(show_webcam_select_menu) || return 1
|
||||
omarchy-capture-screenrecording --with-desktop-audio --with-microphone-audio --with-webcam --webcam-device="$device"
|
||||
}
|
||||
|
||||
|
||||
# Runtime-generated providers users may edit
|
||||
# Use provider:"fonts" only when a submenu calls provider_fonts (or a command)
|
||||
# to return JSON. Normal submenus are inferred from dotted JSONC ids.
|
||||
|
||||
provider_fonts() {
|
||||
local menu_id="$1"
|
||||
local current_font
|
||||
local index=0
|
||||
local font
|
||||
local label
|
||||
current_font=$(omarchy-font-current 2>/dev/null)
|
||||
|
||||
while IFS= read -r font; do
|
||||
[[ -n $font ]] || continue
|
||||
(( index++ ))
|
||||
label="$font$(current_marker "$font" "$current_font")"
|
||||
json_item "$menu_id.$index-$(slugify "$font")" "" "$label" "omarchy-font-set $(shell_quote "$font")" "$font typeface"
|
||||
done < <(omarchy-font-list 2>/dev/null)
|
||||
}
|
||||
|
||||
provider_power_profiles() {
|
||||
local menu_id="$1"
|
||||
local current_profile
|
||||
local profile
|
||||
current_profile=$(powerprofilesctl get 2>/dev/null)
|
||||
|
||||
while IFS= read -r profile; do
|
||||
[[ -n $profile ]] || continue
|
||||
json_item "$menu_id.$(slugify "$profile")" "" "$profile$(current_marker "$profile" "$current_profile")" "powerprofilesctl set $(shell_quote "$profile")" "$profile power profile"
|
||||
done < <(omarchy-powerprofiles-list 2>/dev/null)
|
||||
}
|
||||
|
||||
|
||||
# Framework helpers below this line
|
||||
# Most menu changes should not require editing below here.
|
||||
|
||||
# Generic helpers
|
||||
|
||||
shell_quote() {
|
||||
printf "'%s'" "${1//\'/\'\\\'\'}"
|
||||
}
|
||||
|
||||
slugify() {
|
||||
local value="${1,,}"
|
||||
value="${value//[^a-z0-9]/-}"
|
||||
|
||||
while [[ $value == *--* ]]; do
|
||||
value="${value//--/-}"
|
||||
done
|
||||
|
||||
while [[ $value == -* ]]; do
|
||||
value="${value#-}"
|
||||
done
|
||||
|
||||
while [[ $value == *- ]]; do
|
||||
value="${value%-}"
|
||||
done
|
||||
|
||||
printf '%s' "${value:-item}"
|
||||
}
|
||||
|
||||
current_marker() {
|
||||
if [[ $1 == $2 ]]; then
|
||||
printf ' ✓'
|
||||
fi
|
||||
}
|
||||
|
||||
declare -A CMD_PRESENT_CACHE=()
|
||||
declare -A STATUS_CACHE=()
|
||||
|
||||
cached_status() {
|
||||
local key="$1"
|
||||
shift
|
||||
|
||||
if [[ -z ${STATUS_CACHE[$key]+x} ]]; then
|
||||
"$@" >/dev/null 2>&1
|
||||
STATUS_CACHE[$key]=$?
|
||||
fi
|
||||
|
||||
return "${STATUS_CACHE[$key]}"
|
||||
}
|
||||
|
||||
omarchy-cmd-present() {
|
||||
local cmd
|
||||
|
||||
for cmd in "$@"; do
|
||||
if [[ -z ${CMD_PRESENT_CACHE[$cmd]+x} ]]; then
|
||||
if command -v "$cmd" >/dev/null 2>&1; then
|
||||
CMD_PRESENT_CACHE[$cmd]=0
|
||||
else
|
||||
CMD_PRESENT_CACHE[$cmd]=1
|
||||
fi
|
||||
fi
|
||||
|
||||
(( CMD_PRESENT_CACHE[$cmd] == 0 )) || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
omarchy-toggle-enabled() {
|
||||
cached_status "toggle:$*" command omarchy-toggle-enabled "$@"
|
||||
}
|
||||
|
||||
omarchy-hibernation-available() {
|
||||
cached_status hibernation command omarchy-hibernation-available
|
||||
}
|
||||
|
||||
omarchy-hw-hybrid-gpu() {
|
||||
cached_status hw-hybrid-gpu command omarchy-hw-hybrid-gpu
|
||||
}
|
||||
|
||||
omarchy-hw-touchpad() {
|
||||
cached_status hw-touchpad command omarchy-hw-touchpad
|
||||
}
|
||||
|
||||
omarchy-hw-touchscreen() {
|
||||
cached_status hw-touchscreen command omarchy-hw-touchscreen
|
||||
}
|
||||
|
||||
omarchy-hw-dell-xps-haptic-touchpad() {
|
||||
cached_status hw-dell-xps-haptic-touchpad command omarchy-hw-dell-xps-haptic-touchpad
|
||||
}
|
||||
|
||||
default_browser_is() {
|
||||
if [[ -z ${DEFAULT_BROWSER_CACHE+x} ]]; then
|
||||
DEFAULT_BROWSER_CACHE=$(command omarchy-default-browser 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
[[ $DEFAULT_BROWSER_CACHE == "$1" ]]
|
||||
}
|
||||
|
||||
default_terminal_is() {
|
||||
if [[ -z ${DEFAULT_TERMINAL_CACHE+x} ]]; then
|
||||
DEFAULT_TERMINAL_CACHE=$(command omarchy-default-terminal 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
[[ $DEFAULT_TERMINAL_CACHE == "$1" ]]
|
||||
}
|
||||
|
||||
default_editor_is() {
|
||||
if [[ -z ${DEFAULT_EDITOR_CACHE+x} ]]; then
|
||||
DEFAULT_EDITOR_CACHE=$(command omarchy-default-editor 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
[[ $DEFAULT_EDITOR_CACHE == "$1" ]]
|
||||
}
|
||||
|
||||
haptic_touchpad_is() {
|
||||
if [[ -z ${HAPTIC_TOUCHPAD_CACHE+x} ]]; then
|
||||
HAPTIC_TOUCHPAD_CACHE=$(command dell-xps-touchpad-haptics get 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
[[ $HAPTIC_TOUCHPAD_CACHE == "$1" ]]
|
||||
}
|
||||
|
||||
json_string() {
|
||||
local value="${1-}"
|
||||
value="${value//\\/\\\\}"
|
||||
value="${value//\"/\\\"}"
|
||||
value="${value//$'\t'/\\t}"
|
||||
value="${value//$'\r'/\\r}"
|
||||
value="${value//$'\n'/\\n}"
|
||||
printf '"%s"' "$value"
|
||||
}
|
||||
|
||||
json_item() {
|
||||
local id="$1"
|
||||
local icon="$2"
|
||||
local label="$3"
|
||||
local action="${4:-}"
|
||||
local keywords="${5:-}"
|
||||
local description="${6:-}"
|
||||
local target="${7:-}"
|
||||
local provider="${8:-}"
|
||||
|
||||
printf '{"id": '
|
||||
json_string "$id"
|
||||
printf ', "icon": '
|
||||
json_string "$icon"
|
||||
printf ', "label": '
|
||||
json_string "$label"
|
||||
|
||||
if [[ -n $action ]]; then
|
||||
printf ', "action": '
|
||||
json_string "$action"
|
||||
fi
|
||||
|
||||
if [[ -n $keywords ]]; then
|
||||
printf ', "keywords": '
|
||||
json_string "$keywords"
|
||||
fi
|
||||
|
||||
if [[ -n $description ]]; then
|
||||
printf ', "description": '
|
||||
json_string "$description"
|
||||
fi
|
||||
|
||||
if [[ -n $target ]]; then
|
||||
printf ', "target": '
|
||||
json_string "$target"
|
||||
fi
|
||||
|
||||
if [[ -n $provider ]]; then
|
||||
printf ', "provider": '
|
||||
json_string "$provider"
|
||||
fi
|
||||
|
||||
printf '}\n'
|
||||
}
|
||||
|
||||
|
||||
# Menu definition loading
|
||||
menu_sources() {
|
||||
local path
|
||||
|
||||
for path in "$DEFAULT_MENU" "$USER_MENU"; do
|
||||
[[ -f $path ]] || continue
|
||||
jq -Rrs '
|
||||
# Our menu files are JSONC: full-line // comments plus trailing commas.
|
||||
# Keep parsing in jq so the menu build path stays fast and dependency-free.
|
||||
gsub("(?m)^\\s*//[^\\n]*(\\n|$)"; "") |
|
||||
gsub(",(?<tail>\\s*[}\\]])"; "\(.tail)") |
|
||||
fromjson |
|
||||
if type == "object" then
|
||||
(if has("items") and (.items | type) == "object" then .items else . end)
|
||||
else
|
||||
{}
|
||||
end |
|
||||
to_entries[] |
|
||||
select(.value | type == "object") |
|
||||
.value + { id: .key }
|
||||
' "$path"
|
||||
done
|
||||
}
|
||||
|
||||
normalize_menu_json() {
|
||||
jq -sc '
|
||||
def aliases_array:
|
||||
if ((.aliases // []) | type) == "array" then (.aliases // [])
|
||||
elif ((.aliases // "") != "") then [(.aliases // "")]
|
||||
else [] end;
|
||||
|
||||
def normalize_item:
|
||||
def inferred_parent:
|
||||
if has("parent") then .parent
|
||||
elif (.id | contains(".")) then (.id | split(".")[:-1] | join("."))
|
||||
else "root" end;
|
||||
|
||||
def inferred_kind:
|
||||
if ((.action // "") != "") then "action"
|
||||
elif ((.target // "") != "") then "link"
|
||||
else "menu" end;
|
||||
|
||||
def id_keywords:
|
||||
.id | gsub("[._-]+"; " ");
|
||||
|
||||
def alias_keywords:
|
||||
aliases_array | map(gsub("[._-]+"; " ")) | join(" ");
|
||||
|
||||
def keyword_string:
|
||||
([id_keywords, alias_keywords, (.keywords // "")] | join(" ") | split(" ") | map(select(. != ""))) as $terms |
|
||||
reduce $terms[] as $term ([]; if index($term) then . else . + [$term] end) |
|
||||
join(" ");
|
||||
|
||||
{
|
||||
id,
|
||||
parent: (if .id == "root" then "" else inferred_parent end),
|
||||
kind: inferred_kind,
|
||||
icon: (.icon // ""),
|
||||
label: (.label // .id),
|
||||
target: (.target // ""),
|
||||
keywords: keyword_string,
|
||||
description: (.description // ""),
|
||||
action: (.action // ""),
|
||||
provider: (.provider // ""),
|
||||
aliases: aliases_array,
|
||||
when: (.when // ""),
|
||||
checked: (.checked // "")
|
||||
};
|
||||
|
||||
reduce (.[] | select(.id)) as $item ({ order: [], by_id: {} };
|
||||
if .by_id[$item.id] == null then .order += [$item.id] else . end |
|
||||
.by_id[$item.id] = ((.by_id[$item.id] // {}) + $item)
|
||||
) |
|
||||
{ items: [ .order[] as $id | (.by_id[$id] + { id: $id }) | normalize_item ] }
|
||||
'
|
||||
}
|
||||
|
||||
menu_all_json() {
|
||||
if [[ -z ${MENU_ALL_JSON_CACHE+x} ]]; then
|
||||
MENU_ALL_JSON_CACHE=$(menu_sources | normalize_menu_json) || return
|
||||
fi
|
||||
|
||||
printf '%s\n' "$MENU_ALL_JSON_CACHE"
|
||||
}
|
||||
|
||||
menu_static_json() {
|
||||
if [[ -z ${MENU_STATIC_JSON_CACHE+x} ]]; then
|
||||
MENU_STATIC_JSON_CACHE=$(menu_all_json | jq '
|
||||
(.items | map({ key: .id, value: . }) | from_entries) as $by_id |
|
||||
def ancestor_ids($id):
|
||||
($id | split(".")) as $parts |
|
||||
[range(1; $parts | length) | $parts[0:.] | join(".")];
|
||||
def has_provider_ancestor($id):
|
||||
any(ancestor_ids($id)[]; (($by_id[.] // {}).provider // "") != "");
|
||||
|
||||
{ items: [.items[] | select(has_provider_ancestor(.id) | not)] }
|
||||
') || return
|
||||
fi
|
||||
|
||||
printf '%s\n' "$MENU_STATIC_JSON_CACHE"
|
||||
}
|
||||
|
||||
emit_config_items() {
|
||||
local fields
|
||||
local id
|
||||
local icon
|
||||
local label
|
||||
local action
|
||||
local keywords
|
||||
local description
|
||||
local target
|
||||
local provider
|
||||
local when
|
||||
local checked
|
||||
|
||||
while IFS= read -r fields; do
|
||||
eval "set -- $fields"
|
||||
id="$1"
|
||||
icon="$2"
|
||||
label="$3"
|
||||
action="$4"
|
||||
keywords="$5"
|
||||
description="$6"
|
||||
target="$7"
|
||||
provider="$8"
|
||||
when="$9"
|
||||
checked="${10}"
|
||||
|
||||
[[ -n $id ]] || continue
|
||||
if [[ -n $when ]] && ! eval "$when" >/dev/null 2>&1; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n $checked ]] && eval "$checked" >/dev/null 2>&1; then
|
||||
label="$label ✓"
|
||||
fi
|
||||
|
||||
json_item "$id" "$icon" "$label" "$action" "$keywords" "$description" "$target" "$provider"
|
||||
done < <(jq -r '.items[] | [
|
||||
(.id // ""),
|
||||
(.icon // ""),
|
||||
(.label // .id // ""),
|
||||
(.action // ""),
|
||||
(.keywords // ""),
|
||||
(.description // ""),
|
||||
(.target // ""),
|
||||
(.provider // ""),
|
||||
(.when // ""),
|
||||
(.checked // "")
|
||||
] | @sh')
|
||||
}
|
||||
|
||||
menu_evaluated_json() {
|
||||
if [[ -z ${MENU_EVALUATED_JSON_CACHE+x} ]]; then
|
||||
MENU_EVALUATED_JSON_CACHE=$(menu_all_json | emit_config_items | normalize_menu_json) || return
|
||||
fi
|
||||
|
||||
printf '%s\n' "$MENU_EVALUATED_JSON_CACHE"
|
||||
}
|
||||
|
||||
menu_json() {
|
||||
if [[ -z ${MENU_JSON_CACHE+x} ]]; then
|
||||
MENU_JSON_CACHE=$(menu_static_json | emit_config_items | normalize_menu_json) || return
|
||||
fi
|
||||
|
||||
printf '%s\n' "$MENU_JSON_CACHE"
|
||||
}
|
||||
|
||||
# Provider dispatch
|
||||
provider_mount_id() {
|
||||
local provider="$1"
|
||||
|
||||
menu_all_json | jq -r --arg provider "$provider" '.items[] | select(.provider == $provider) | .id' | head -n 1
|
||||
}
|
||||
|
||||
provider_json() {
|
||||
local provider="$1"
|
||||
local menu_id="${2:-}"
|
||||
local provider_function
|
||||
|
||||
[[ -n $provider ]] || return 1
|
||||
menu_id=${menu_id:-$(provider_mount_id "$provider")}
|
||||
[[ -n $menu_id ]] || return 1
|
||||
|
||||
if [[ $provider =~ ^[a-z0-9-]+$ ]]; then
|
||||
provider_function="provider_${provider//-/_}"
|
||||
case "$provider_function" in
|
||||
provider_mount_id | provider_json) ;;
|
||||
*)
|
||||
if declare -F "$provider_function" >/dev/null; then
|
||||
"$provider_function" "$menu_id" | normalize_menu_json
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if omarchy-cmd-present "$provider"; then
|
||||
"$provider" "$menu_id" | normalize_menu_json
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "omarchy-menu: unknown provider '$provider'" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Routes and actions
|
||||
route_target() {
|
||||
local route="${1,,}"
|
||||
local target
|
||||
local all_json
|
||||
route="${route//_/-}"
|
||||
|
||||
if [[ -z $route || $route == "go" || $route == "menu" ]]; then
|
||||
printf 'root'
|
||||
return
|
||||
fi
|
||||
|
||||
all_json=$(menu_all_json) || return
|
||||
target=$(jq -r --arg route "$route" '
|
||||
def aliases:
|
||||
if (.aliases | type) == "array" then .aliases
|
||||
elif (.aliases | type) == "string" then [.aliases]
|
||||
else [] end;
|
||||
|
||||
first(.items[] | select(any(aliases[]?; (ascii_downcase | gsub("_"; "-")) == $route)) | .id) // empty
|
||||
' <<<"$all_json") || return
|
||||
|
||||
printf '%s' "${target:-$route}"
|
||||
}
|
||||
|
||||
route_field() {
|
||||
local target="$1"
|
||||
local field="$2"
|
||||
local all_json
|
||||
local fields
|
||||
local when
|
||||
local value
|
||||
|
||||
all_json=$(menu_all_json) || return
|
||||
fields=$(jq -r --arg id "$target" --arg field "$field" '
|
||||
first(.items[] | select(.id == $id) | [(.when // ""), (.[$field] // "")] | @sh) // empty
|
||||
' <<<"$all_json") || return
|
||||
[[ -n $fields ]] || return
|
||||
|
||||
eval "set -- $fields"
|
||||
when="$1"
|
||||
value="$2"
|
||||
|
||||
if [[ -n $when ]] && ! eval "$when" >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
route_kind() {
|
||||
local target="$1"
|
||||
|
||||
if [[ $target == "root" ]]; then
|
||||
printf 'menu'
|
||||
return
|
||||
fi
|
||||
|
||||
route_field "$target" kind
|
||||
}
|
||||
|
||||
route_link_target() {
|
||||
route_field "$1" target
|
||||
}
|
||||
|
||||
route_action() {
|
||||
route_field "$1" action
|
||||
}
|
||||
|
||||
run_action() {
|
||||
local id="$1"
|
||||
local action="${2:-}"
|
||||
|
||||
if [[ -z $action ]]; then
|
||||
action=$(route_action "$id") || return 0
|
||||
fi
|
||||
|
||||
[[ -n $action ]] || return 0
|
||||
eval "$action"
|
||||
}
|
||||
|
||||
|
||||
# Quickshell launcher
|
||||
ensure_menu_style_file() {
|
||||
local toggles_dir="$HOME/.local/state/omarchy/toggles"
|
||||
local style_file="$toggles_dir/quickshell-menu.json"
|
||||
local radius=0
|
||||
|
||||
[[ -f $style_file ]] && return
|
||||
|
||||
mkdir -p "$toggles_dir"
|
||||
if [[ -f $toggles_dir/walker.css ]] && grep -q 'border-radius: 6px;' "$toggles_dir/walker.css"; then
|
||||
radius=6
|
||||
fi
|
||||
|
||||
printf '{ "radius": %s }\n' "$radius" >"$style_file"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: omarchy-menu [menu-or-action]
|
||||
omarchy-menu --json
|
||||
omarchy-menu --provider <provider> [menu-id]
|
||||
|
||||
Launch the Quickshell Omarchy menu. Pass a menu name (install, style, system,
|
||||
capture, toggle, etc.) to open there, or an action id to run it directly.
|
||||
|
||||
Options:
|
||||
--json Print the static menu JSON used by Quickshell
|
||||
--provider <provider> [id] Print JSON rows from a runtime provider
|
||||
-h, --help Show this help
|
||||
USAGE
|
||||
}
|
||||
|
||||
menu_layer_visible() {
|
||||
hyprctl layers -j 2>/dev/null | jq -e '.. | objects | select(.namespace? == "omarchy-menu")' >/dev/null
|
||||
}
|
||||
|
||||
menu_lock_path() {
|
||||
printf '%s/omarchy-menu.lock' "${XDG_RUNTIME_DIR:-/run/user/$UID}"
|
||||
}
|
||||
|
||||
menu_payload_json() {
|
||||
local menu_file="$1"
|
||||
local initial_menu="$2"
|
||||
local selection_file="$3"
|
||||
local done_file="$4"
|
||||
local colors_payload_base64="$5"
|
||||
local font_family="$6"
|
||||
|
||||
jq -nc \
|
||||
--arg menu_file "$menu_file" \
|
||||
--arg initial_menu "$initial_menu" \
|
||||
--arg selection_file "$selection_file" \
|
||||
--arg done_file "$done_file" \
|
||||
--arg colors_payload_base64 "$colors_payload_base64" \
|
||||
--arg font_family "$font_family" \
|
||||
'{
|
||||
menuJsonFile: $menu_file,
|
||||
initialMenu: $initial_menu,
|
||||
selectionFile: $selection_file,
|
||||
doneFile: $done_file,
|
||||
colorsRawBase64: $colors_payload_base64,
|
||||
fontFamily: $font_family
|
||||
}'
|
||||
}
|
||||
|
||||
close_visible_quickshell_menu() {
|
||||
menu_layer_visible || return 1
|
||||
omarchy-shell-ipc shell hide omarchy.menu >/dev/null 2>&1 || return 1
|
||||
}
|
||||
|
||||
open_quickshell_menu() {
|
||||
local initial_menu="$1"
|
||||
local menu_payload
|
||||
local colors_file="$HOME/.config/omarchy/current/theme/colors.toml"
|
||||
local colors_payload_base64=""
|
||||
local font_family
|
||||
local menu_file=""
|
||||
local selection_file
|
||||
local done_file
|
||||
local selected_id
|
||||
local selected_action
|
||||
local menu_started=false
|
||||
local lock_path
|
||||
local lock_fd
|
||||
local summon_result
|
||||
local payload
|
||||
|
||||
lock_path=$(menu_lock_path)
|
||||
exec {lock_fd}>"$lock_path" || return 1
|
||||
if ! flock -n "$lock_fd"; then
|
||||
close_visible_quickshell_menu && return 0
|
||||
flock "$lock_fd" || return 1
|
||||
fi
|
||||
|
||||
if close_visible_quickshell_menu; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
ensure_menu_style_file
|
||||
|
||||
selection_file=$(mktemp)
|
||||
done_file=$(mktemp)
|
||||
rm -f "$done_file"
|
||||
trap "rm -f $(shell_quote "$selection_file") $(shell_quote "$done_file")" EXIT
|
||||
|
||||
menu_payload=$(menu_json) || return
|
||||
if [[ $initial_menu != "root" ]] && ! jq -e --arg id "$initial_menu" '.items[] | select(.id == $id)' <<<"$menu_payload" >/dev/null; then
|
||||
menu_payload=$(menu_evaluated_json) || return
|
||||
fi
|
||||
|
||||
if [[ -f $colors_file ]]; then
|
||||
colors_payload_base64=$(base64 -w0 "$colors_file")
|
||||
fi
|
||||
|
||||
font_family=$(omarchy-font-current 2>/dev/null || true)
|
||||
font_family=${font_family:-monospace}
|
||||
|
||||
menu_file=$(mktemp)
|
||||
printf '%s' "$menu_payload" >"$menu_file"
|
||||
trap "rm -f $(shell_quote "$menu_file") $(shell_quote "$selection_file") $(shell_quote "$done_file")" EXIT
|
||||
payload=$(menu_payload_json "$menu_file" "$initial_menu" "$selection_file" "$done_file" "$colors_payload_base64" "$font_family") || return
|
||||
|
||||
summon_result=$(omarchy-shell-ipc shell summon omarchy.menu "$payload" 2>/dev/null || true)
|
||||
if [[ $summon_result != "ok" ]]; then
|
||||
echo "omarchy-menu: Omarchy shell did not accept the menu request" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
for (( i = 0; i < 500; i++ )); do
|
||||
if [[ -e $done_file ]] || menu_layer_visible; then
|
||||
menu_started=true
|
||||
break
|
||||
fi
|
||||
sleep 0.01
|
||||
done
|
||||
|
||||
if [[ $menu_started != true ]]; then
|
||||
omarchy-shell-ipc shell hide omarchy.menu >/dev/null 2>&1 || true
|
||||
echo "omarchy-menu: Quickshell menu did not open" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
while [[ ! -e $done_file ]]; do
|
||||
menu_layer_visible || break
|
||||
sleep 0.01
|
||||
done
|
||||
|
||||
if [[ -s $selection_file ]]; then
|
||||
IFS=$'\t' read -r selected_id selected_action <"$selection_file"
|
||||
exec {lock_fd}>&-
|
||||
run_action "$selected_id" "$selected_action"
|
||||
fi
|
||||
}
|
||||
|
||||
# CLI dispatch
|
||||
case "${1:-}" in
|
||||
--help | -h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--json)
|
||||
menu_json || exit $?
|
||||
exit 0
|
||||
;;
|
||||
--provider)
|
||||
provider_json "${2:-}" "${3:-}"
|
||||
exit $?
|
||||
;;
|
||||
screenrecord | screenrecording | screen-record)
|
||||
close_visible_quickshell_menu || true
|
||||
stop_active_screenrecording && exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z ${1:-} ]]; then
|
||||
open_quickshell_menu root
|
||||
exit $?
|
||||
fi
|
||||
|
||||
target=$(route_target "${1:-}") || exit $?
|
||||
kind=$(route_kind "$target") || exit $?
|
||||
|
||||
case "$kind" in
|
||||
action)
|
||||
close_visible_quickshell_menu || true
|
||||
run_action "$target"
|
||||
;;
|
||||
menu)
|
||||
open_quickshell_menu "$target"
|
||||
;;
|
||||
link)
|
||||
open_quickshell_menu "$(route_link_target "$target")"
|
||||
;;
|
||||
*)
|
||||
open_quickshell_menu root
|
||||
;;
|
||||
esac
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Prompt for a reminder duration + message and schedule it
|
||||
# omarchy:examples=omarchy reminder set-interactive
|
||||
|
||||
set -e
|
||||
|
||||
prompt_minutes() {
|
||||
while :; do
|
||||
minutes=$(omarchy-menu-input "Remind in minutes") || exit 0
|
||||
if [[ $minutes =~ ^[0-9]+$ ]] && (( minutes > 0 )); then
|
||||
printf '%s' "$minutes"
|
||||
return 0
|
||||
fi
|
||||
[[ -z $minutes ]] && exit 0
|
||||
omarchy-notification-send "Invalid reminder" "Enter the number of minutes" -u critical
|
||||
done
|
||||
}
|
||||
|
||||
minutes=$(prompt_minutes)
|
||||
[[ -n $minutes ]] || exit 0
|
||||
|
||||
message=$(omarchy-menu-input "Reminder message") || exit 0
|
||||
if [[ -n $message ]]; then
|
||||
omarchy-reminder "$minutes" "$message"
|
||||
else
|
||||
omarchy-reminder "$minutes"
|
||||
fi
|
||||
Reference in New Issue
Block a user