#!/bin/bash # omarchy:summary=Send an Omarchy desktop notification # omarchy:args=[--exec-arg ]... [--exec ] [--app-name ] [-g ] [-u ] [--image ] [description] [notify-send options] # omarchy:examples=omarchy notification send "Reminder" "5 minutes are up" -g 󰢌 set -euo pipefail headline="" description="" glyph= urgency="low" app_name="omarchy-action" image= exec_command= exec_args=() args=() parsed_option_args=0 parse_omarchy_option() { case $1 in -g | --glyph) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi glyph=$2 parsed_option_args=2 return 0 ;; -u | --urgency) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi urgency="$2" parsed_option_args=2 return 0 ;; --app-name) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi app_name=$2 parsed_option_args=2 return 0 ;; --image) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi image=$2 parsed_option_args=2 return 0 ;; --exec) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi exec_command=$2 parsed_option_args=2 return 0 ;; --exec-arg) if (($# < 2)); then echo "Missing value for $1" >&2 exit 1 fi # Each --exec-arg contributes one literal argument to the click command. # The shell runs the resulting argv vector directly (no shell), so callers # pass untrusted data as its own --exec-arg rather than quoting it into a # command string. Value is taken verbatim, even when it starts with "-". exec_args+=("$2") parsed_option_args=2 return 0 ;; esac return 1 } while (($# > 0)); do if parse_omarchy_option "$@"; then shift "$parsed_option_args" else break fi done if (($# < 1)); then echo "Usage: omarchy-notification-send [--exec-arg ]... [--exec ] [--app-name ] [-g ] [-u ] [--image ] [description] [notify-send options]" exit 1 fi headline=$1 shift if (($# > 0)) && [[ $1 != -* ]]; then description=$1 shift fi while (($# > 0)); do if parse_omarchy_option "$@"; then shift "$parsed_option_args" else args+=("$1") shift fi done # Tag as a user-action toast so it pops through DND. args+=("-a" "$app_name" "-u" "$urgency") if [[ -n $glyph ]]; then args+=("--hint=string:omarchy-glyph:$glyph") fi if [[ -n $image ]]; then args+=("--hint=string:image-path:$image") fi # The shell runs the click command itself, from a copy it keeps alongside the # on-screen popup. A libnotify action would instead keep this process blocked # until the click, and die unanswered whenever the shell restarts underneath it. # # --exec-arg builds an argv vector the shell runs directly, so a value carrying # untrusted data is only ever one argument and never reaches a shell. It wins # over the legacy free-form --exec string, which is run through `bash -lc` and # is only safe when the caller quoted every interpolated value itself. if ((${#exec_args[@]} > 0)); then # NUL-delimit the args into jq so every byte survives as data — jq's own # --args would eat a bare "--", and a title with a newline must stay one # element, not split the vector. exec_argv_json=$(printf '%s\0' "${exec_args[@]}" | jq -Rsc 'split("\u0000")[:-1]') args+=("--hint=string:omarchy-exec-argv:$exec_argv_json") elif [[ -n $exec_command ]]; then args+=("--hint=string:omarchy-exec:$exec_command") fi if [[ -n $description ]]; then notify-send "${args[@]}" "$headline" "$description" else notify-send "${args[@]}" "$headline" fi